Fixed NodeBuffer exhaustion.

This commit is contained in:
MobiusDevelopment 2021-07-09 22:06:22 +00:00
parent 200c1483df
commit 8e8084e8dd
150 changed files with 8475 additions and 2400 deletions

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

View File

@ -26,6 +26,7 @@ public class Node extends Location implements Comparable<Node>
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<Node>
_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<Node>
_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<Node>
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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<NodeBuffer> _buffer;
final int _count;
final Set<NodeBuffer> _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;
}
}
/**

View File

@ -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;

Some files were not shown because too many files have changed in this diff Show More