diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini b/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java index 91b5557351..1b056eb984 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java @@ -941,6 +941,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2483,10 +2484,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini b/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java index 5d87ef7f1b..c36e415da3 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java @@ -951,6 +951,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2505,10 +2506,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini b/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java index 43595a4f9f..7015651005 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java @@ -964,6 +964,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2527,10 +2528,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java index 8853f3ca51..f8c86c53b9 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java @@ -951,6 +951,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2502,10 +2503,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini b/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java index fb0f4cc830..010215bd9f 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java @@ -946,6 +946,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2502,10 +2503,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini b/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java index 06929fa8c6..4677f4b611 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java @@ -953,6 +953,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2514,10 +2515,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini b/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java index 045ebf4e32..d44af08a57 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java @@ -965,6 +965,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2557,10 +2558,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java index ea441f8ef6..39e07030df 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java @@ -971,6 +971,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2569,10 +2570,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini b/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java index 15d9a5bd19..e3afcc3990 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java @@ -969,6 +969,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2565,10 +2566,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java index 9187c9fa16..e641f71fc7 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java @@ -969,6 +969,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2565,10 +2566,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/dist/game/config/GeoEngine.ini b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/Config.java b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/Config.java index 9187c9fa16..e641f71fc7 100644 --- a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/Config.java @@ -969,6 +969,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2565,10 +2566,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_9.2_ReturnOfTheQueenAnt_Ch2/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini index 6e4b5db075..5889c6caf0 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini +++ b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java index 942c9cec05..713f9df1f1 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java @@ -943,6 +943,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2520,10 +2521,11 @@ public class Config GEODATA_PATH = Paths.get(geoengineConfig.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, geoengineConfig.getString("GeoDataType", "L2J")); PATHFINDING = geoengineConfig.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = geoengineConfig.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = geoengineConfig.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = geoengineConfig.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = geoengineConfig.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = geoengineConfig.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = geoengineConfig.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 35c7db082e..4ca71be781 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -151,22 +152,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -948,7 +939,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1025,18 +1021,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini b/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini index 6e4b5db075..5889c6caf0 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini +++ b/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java index 2c0785f79c..53c5416081 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java @@ -979,6 +979,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2583,10 +2584,11 @@ public class Config GEODATA_PATH = Paths.get(geoengineConfig.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, geoengineConfig.getString("GeoDataType", "L2J")); PATHFINDING = geoengineConfig.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = geoengineConfig.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = geoengineConfig.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = geoengineConfig.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = geoengineConfig.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = geoengineConfig.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = geoengineConfig.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 35c7db082e..4ca71be781 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -151,22 +152,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -948,7 +939,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1025,18 +1021,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java index 348b218961..6b358bb40b 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java @@ -1069,6 +1069,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2599,10 +2600,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index bf5be64a93..04619ff4c2 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -151,22 +152,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -948,7 +939,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1025,18 +1021,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java index c41157ae87..a047c771f9 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java @@ -1069,6 +1069,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2608,10 +2609,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index bf5be64a93..04619ff4c2 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -151,22 +152,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -948,7 +939,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1025,18 +1021,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java index 3b0f7e9b75..71e7798910 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java @@ -900,6 +900,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2411,10 +2412,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java index 79adc2014d..c951df0e27 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java @@ -904,6 +904,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2417,10 +2418,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java index 79adc2014d..c951df0e27 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java @@ -904,6 +904,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2417,10 +2418,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java index 74744f3380..0f668ffc37 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java @@ -904,6 +904,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2418,10 +2419,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java index 6154136af2..9303eff0a9 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java @@ -909,6 +909,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2427,10 +2428,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java index 868657f35e..9ad673c051 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java @@ -912,6 +912,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2432,10 +2433,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java index f5144631be..9b1a3fbe4f 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java @@ -916,6 +916,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2431,10 +2432,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java index 77fbd8b90b..cc849383b8 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java @@ -933,6 +933,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2477,10 +2478,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini b/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java index b918eff25f..40fd982904 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java @@ -933,6 +933,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2480,10 +2481,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/config/GeoEngine.ini b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/config/GeoEngine.ini index e740c678dd..e9c22abb03 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/config/GeoEngine.ini @@ -19,8 +19,8 @@ GeoDataType = L2J # an alternative path (e.g. walk around obstacle), default: True PathFinding = True -# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 -PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 +# Pathfinding array buffers configuration, default: 1200x10;2000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 1200x10;2000x10;3000x5;5000x3;10000x3 # Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 MoveWeight = 10 diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/Config.java index b918eff25f..40fd982904 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/Config.java @@ -933,6 +933,7 @@ public class Config public static int MOVE_WEIGHT; public static int MOVE_WEIGHT_DIAG; public static int OBSTACLE_WEIGHT; + public static int OBSTACLE_WEIGHT_DIAG; public static int HEURISTIC_WEIGHT; public static int HEURISTIC_WEIGHT_DIAG; public static int MAX_ITERATIONS; @@ -2480,10 +2481,11 @@ public class Config GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3"); MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2)); HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index f85addd5a5..54f9de6e9b 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -23,10 +23,11 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.l2jmobius.Config; @@ -152,22 +153,12 @@ public class GeoEngine continue; } - // Find unlocked NodeBuffer. - for (NodeBuffer buffer : holder._buffer) + // Get NodeBuffer. + current = holder.getBuffer(); + if (current != null) { - if (!buffer.isLocked()) - { - continue; - } - - return buffer; + return current; } - - LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); - - // NodeBuffer not found, allocate temporary buffer. - current = new NodeBuffer(holder._size); - current.isLocked(); } return current; @@ -949,7 +940,12 @@ public class GeoEngine int gtz = getHeightNearest(gtx, gty, tz); // Prepare buffer for pathfinding calculations. - NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + int dx = Math.abs(gox - gtx); + int dy = Math.abs(goy - gty); + int dz = Math.abs(goz - gtz) / 8; + int total = dx + dy + dz; + int size = 1000 + (10 * total); + NodeBuffer buffer = getBuffer(size); if (buffer == null) { return Collections.emptyList(); @@ -1026,18 +1022,51 @@ public class GeoEngine private static class BufferHolder { final int _size; - final List _buffer; + final int _count; + final Set _buffer; public BufferHolder(int size, int count) { _size = size; - _buffer = new ArrayList<>(count); + _count = count * 4; + _buffer = ConcurrentHashMap.newKeySet(_count); for (int i = 0; i < count; i++) { _buffer.add(new NodeBuffer(size)); } } + + public NodeBuffer getBuffer() + { + // Get available free NodeBuffer. + for (NodeBuffer buffer : _buffer) + { + if (!buffer.isLocked()) + { + continue; + } + + return buffer; + } + + // No free NodeBuffer found, try allocate new buffer. + if (_buffer.size() < _count) + { + NodeBuffer buffer = new NodeBuffer(_size); + buffer.isLocked(); + _buffer.add(buffer); + + if (_buffer.size() == _count) + { + LOGGER.warning("NodeBuffer holder with " + _size + " size reached max capacity."); + } + + return buffer; + } + + return null; + } } /** diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java index 691b164e0c..87073479af 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -26,8 +26,24 @@ public final class GeoStructure public static final byte CELL_FLAG_W = 0x02; public static final byte CELL_FLAG_S = 0x04; public static final byte CELL_FLAG_N = 0x08; + public static final byte CELL_FLAG_SE = 0x10; + public static final byte CELL_FLAG_SW = 0x20; + public static final byte CELL_FLAG_NE = 0x40; + public static final byte CELL_FLAG_NW = (byte) 0x80; public static final byte CELL_FLAG_ALL = 0x0F; + // Geo cell expansion flags + public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW; + public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE; + public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE; + public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E; + public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W; + public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E; + public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W; + // public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW; + public static final byte CELL_EXPANSION_ALL = (byte) 0xFF; + // Geo cell height constants. public static final int CELL_SIZE = 16; public static final int CELL_HEIGHT = 8; diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java index fa5ca43c2f..b837e2007a 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -26,6 +26,7 @@ public class Node extends Location implements Comparable private int _geoX; private int _geoY; private byte _nswe; + private byte _nsweExpand; // The cost G (movement cost done) and cost H (estimated cost to target). private int _costG; @@ -48,6 +49,7 @@ public class Node extends Location implements Comparable _geoX = 0; _geoY = 0; _nswe = GeoStructure.CELL_FLAG_NONE; + _nsweExpand = GeoStructure.CELL_FLAG_NONE; _costG = 0; _costH = 0; @@ -56,13 +58,14 @@ public class Node extends Location implements Comparable _parent = null; } - public void setGeo(int gx, int gy, int gz, byte nswe) + public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand) { super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); _geoX = gx; _geoY = gy; _nswe = nswe; + _nsweExpand = nsweExpand; } public void setCost(Node parent, int weight, int costH) @@ -93,6 +96,11 @@ public class Node extends Location implements Comparable return _nswe; } + public byte getNsweExpand() + { + return _nsweExpand; + } + public int getCostF() { return _costF; diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java index b464212c96..e1d4790d43 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -16,7 +16,6 @@ */ package org.l2jmobius.gameserver.geoengine.pathfinding; -import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -29,7 +28,6 @@ import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.geoengine.geodata.ABlock; import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.Location; -import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; public class NodeBuffer { @@ -97,7 +95,7 @@ public class NodeBuffer _current = _buffer[_bufferIndex++]; // Set node geodata coordinates and movement cost. - _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz)); + _current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL); _current.setCost(null, 0, getCostH(gox, goy, goz)); int count = 0; @@ -166,25 +164,6 @@ public class NodeBuffer return path; } - /** - * Creates list of Nodes to show debug path. - * @param debug : The debug packet to add debug informations in. - */ - public void debugPath(ExServerPrimitive debug) - { - // Add all opened node as yellow points. - for (Node n : _opened) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); - } - - // Add all opened node as blue points. - for (Node n : _closed) - { - debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); - } - } - public boolean isLocked() { return _lock.tryLock(); @@ -219,7 +198,8 @@ public class NodeBuffer { // Movement is blocked, skip. final byte nswe = _current.getNSWE(); - if (nswe == GeoStructure.CELL_FLAG_NONE) + byte expand = _current.getNsweExpand(); + if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE) { return; } @@ -235,53 +215,230 @@ public class NodeBuffer byte nsweW = GeoStructure.CELL_FLAG_NONE; byte nsweE = GeoStructure.CELL_FLAG_NONE; - // Can move north, expand. - if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + switch (expand) { - nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + case GeoStructure.CELL_EXPANSION_N: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_S: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + + if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_W: + { + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_E: + { + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + + if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0)) + { + if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + } + + if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0)) + { + if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + } + } + break; + } + case GeoStructure.CELL_EXPANSION_NW: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_NE: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SW: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_SE: + { + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + case GeoStructure.CELL_EXPANSION_ALL: + { + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false); + } + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false); + } + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false); + } + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false); + } + + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true); + } + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true); + } + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true); + } + break; + } + } + } + + private static byte getNodeNswe(int gx, int gy, int gz) + { + // Check new node is out of geodata grid (world coordinates). + if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) + { + return GeoStructure.CELL_FLAG_NONE; } - // Can move south, expand. - if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + // Get geodata block and check if there is a layer at given coordinates. + ABlock block = GeoEngine.getInstance().getBlock(gx, gy); + final int index = block.getIndexBelow(gx, gy, gz); + if (index < 0) { - nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + return GeoStructure.CELL_FLAG_NONE; } - // Can move west, expand. - if ((nswe & GeoStructure.CELL_FLAG_W) != 0) - { - nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move east, expand. - if ((nswe & GeoStructure.CELL_FLAG_E) != 0) - { - nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); - } - - // Can move north-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move north-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-west, expand. - if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) - { - addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } - - // Can move south-east, expand. - if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) - { - addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); - } + // Get node geodata nswe. + return block.getNswe(index); } /** @@ -289,10 +446,11 @@ public class NodeBuffer * @param gx : The new node X geodata coordinate. * @param gy : The new node Y geodata coordinate. * @param gzValue : The new node Z geodata coordinate. - * @param weight : The weight of movement to the new node. + * @param nsweExpand : The allowed directions to be expanded on the new node. + * @param diagonal : The new node is being explored in diagonal direction. * @return The nswe of the added node. Blank, if not added. */ - private byte addNode(int gx, int gy, int gzValue, int weight) + private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal) { // Check new node is out of geodata grid (world coordinates). if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y)) @@ -321,8 +479,19 @@ public class NodeBuffer // Get node from current index (don't move index yet). Node node = _buffer[_bufferIndex]; + // Calculate node weight. + int weight; + if (nswe == GeoStructure.CELL_FLAG_ALL) + { + weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT; + } + else + { + weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT; + } + // Set node geodata coordinates. - node.setGeo(gx, gy, gz, nswe); + node.setGeo(gx, gy, gz, nswe, nsweExpand); // Node is already added to opened list, return. if (_opened.contains(node)) @@ -337,7 +506,7 @@ public class NodeBuffer } // The node is to be used. Set node movement cost and add it to opened list. Move the buffer index. - node.setCost(_current, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + node.setCost(_current, weight, getCostH(gx, gy, gz)); _opened.add(node); _bufferIndex++; return nswe; @@ -354,15 +523,34 @@ public class NodeBuffer private int getCostH(int gx, int gy, int gz) { // Get differences to the target. - final int dx = Math.abs(gx - _gtx); - final int dy = Math.abs(gy - _gty); - final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + int dx = Math.abs(gx - _gtx); + int dy = Math.abs(gy - _gty); + int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; // Get diagonal and axial differences to the target. - final int dd = Math.min(dx, dy); - final int da = Math.max(dx, dy) - dd; + final int ds = Math.min(dx, Math.min(dy, dz)); + dx -= ds; + dy -= ds; + dz -= ds; + int dd; + int d; + if (dx == 0) + { + dd = Math.min(dy, dz); + d = Math.max(dy, dz) - dd; + } + else if (dy == 0) + { + dd = Math.min(dx, dz); + d = Math.max(dx, dz) - dd; + } + else + { + dd = Math.min(dx, dy); + d = Math.max(dx, dy) - dd; + } // Calculate the diagonal distance of the node to the target. - return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2); } } \ No newline at end of file