Browse Source

Rework of the cell-based pathfinding, check config for details.
DP update required.

_DS_ 15 years ago
parent
commit
f7a565ff4b

+ 6 - 7
L2_GameServer/java/com/l2jserver/gameserver/GeoData.java

@@ -20,7 +20,6 @@ import com.l2jserver.Config;
 import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.Location;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.pathfinding.Node;
 import com.l2jserver.util.Point3D;
 
 
@@ -140,7 +139,12 @@ public class GeoData
 	{
 		return 15;
 	}
-	
+
+	public short getHeightAndNSWE(int x, int y, int z)
+	{
+		return (short)((z << 1) | 15);
+	}
+
 	/**
 	 * @param x
 	 * @param y
@@ -185,11 +189,6 @@ public class GeoData
 		return false;
 	}
 	
-	public Node[] getNeighbors(Node n)
-	{
-		return null;
-	}
-	
 	@SuppressWarnings("synthetic-access")
 	private static class SingletonHolder
 	{

+ 58 - 148
L2_GameServer/java/com/l2jserver/gameserver/GeoEngine.java

@@ -14,6 +14,8 @@
  */
 package com.l2jserver.gameserver;
 
+import gnu.trove.TShortObjectHashMap;
+
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.File;
@@ -26,8 +28,6 @@ import java.nio.ByteOrder;
 import java.nio.IntBuffer;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
-import java.util.List;
-import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.logging.Logger;
 
@@ -39,13 +39,8 @@ import com.l2jserver.gameserver.model.Location;
 import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.pathfinding.Node;
-import com.l2jserver.gameserver.pathfinding.cellnodes.CellPathFinding;
 import com.l2jserver.util.Point3D;
 
-import javolution.util.FastList;
-import javolution.util.FastMap;
-
 /**
  *
  * @author -Nemesiss-
@@ -53,12 +48,13 @@ import javolution.util.FastMap;
 public class GeoEngine extends GeoData
 {
 	private static Logger _log = Logger.getLogger(GeoData.class.getName());
-	private final static byte _e = 1;
-	private final static byte _w = 2;
-	private final static byte _s = 4;
-	private final static byte _n = 8;
-	private static Map<Short, MappedByteBuffer> _geodata = new FastMap<Short, MappedByteBuffer>();
-	private static Map<Short, IntBuffer> _geodataIndex = new FastMap<Short, IntBuffer>();
+	private static final byte EAST = 1;
+	private static final byte WEST = 2;
+	private static final byte SOUTH = 4;
+	private static final byte NORTH = 8;
+	private static final byte NSWE_ALL = 15;
+	private static TShortObjectHashMap<MappedByteBuffer> _geodata = new TShortObjectHashMap<MappedByteBuffer>();
+	private static TShortObjectHashMap<IntBuffer> _geodataIndex = new TShortObjectHashMap<IntBuffer>();
 	private static BufferedOutputStream _geoBugsOut;
 	
 	public static GeoEngine getInstance()
@@ -251,7 +247,7 @@ public class GeoEngine extends GeoData
 		int gx = (x - L2World.MAP_MIN_X) >> 4;
 		int gy = (y - L2World.MAP_MIN_Y) >> 4;
 		short region = getRegionOffset(gx, gy);
-		if (_geodata.get(region) != null)
+		if (_geodata.contains(region))
 			return true;
 		return false;
 	}
@@ -279,7 +275,7 @@ public class GeoEngine extends GeoData
 				short region = getRegionOffset(x, y);
 				// geodata is loaded for region and mobs should have correct Z coordinate...
 				// so there would likely be a floor in between the two
-				if (_geodata.get(region) != null)
+				if (_geodata.contains(region))
 					return false;
 			}
 			return true;
@@ -821,12 +817,13 @@ public class GeoEngine extends GeoData
 		int blockX = getBlock(x);
 		int blockY = getBlock(y);
 		int index = 0;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current geodata region
 		else
-			index = _geodataIndex.get(region).get((blockX << 8) + blockY);
+			index = idx.get((blockX << 8) + blockY);
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -850,12 +847,13 @@ public class GeoEngine extends GeoData
 		int blockX = getBlock(geox);
 		int blockY = getBlock(geoy);
 		int cellX, cellY, index;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -928,11 +926,12 @@ public class GeoEngine extends GeoData
 		int blockY = getBlock(geoy);
 		int cellX, cellY, index;
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		final IntBuffer idx = _geodataIndex.get(region);
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -1007,12 +1006,13 @@ public class GeoEngine extends GeoData
 		int blockY = getBlock(geoy);
 		int cellX, cellY, index;
 		short temph = Short.MIN_VALUE;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -1104,12 +1104,13 @@ public class GeoEngine extends GeoData
 		short NSWE = 0;
 		
 		int index = 0;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -1201,12 +1202,13 @@ public class GeoEngine extends GeoData
 		short NSWE = 0;
 		
 		int index;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -1344,7 +1346,7 @@ public class GeoEngine extends GeoData
 	 * @param z
 	 * @return NSWE: 0-15
 	 */
-	private short nGetNSWE(int x, int y, int z)
+	private static short nGetNSWE(int x, int y, int z)
 	{
 		short region = getRegionOffset(x, y);
 		int blockX = getBlock(x);
@@ -1353,12 +1355,13 @@ public class GeoEngine extends GeoData
 		short NSWE = 0;
 		
 		int index = 0;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
@@ -1419,115 +1422,48 @@ public class GeoEngine extends GeoData
 		}
 		return NSWE;
 	}
-	
+
 	/**
 	 * @param x
 	 * @param y
 	 * @param z
-	 * @return NSWE: 0-15
+	 * @return array [0] - height, [1] - NSWE
 	 */
 	@Override
-	public Node[] getNeighbors(Node n)
+	public short getHeightAndNSWE(int x, int y, int z)
 	{
-		List<Node> Neighbors = new FastList<Node>(4);
-		Node newNode;
-		int x = n.getLoc().getNodeX();
-		int y = n.getLoc().getNodeY();
-		int parentdirection = 0;
-		if (n.getParent() != null) // check for not adding parent again
-		{
-			if (n.getParent().getLoc().getNodeX() > x)
-				parentdirection = 1;
-			if (n.getParent().getLoc().getNodeX() < x)
-				parentdirection = -1;
-			if (n.getParent().getLoc().getNodeY() > y)
-				parentdirection = 2;
-			if (n.getParent().getLoc().getNodeY() < y)
-				parentdirection = -2;
-		}
-		short z = n.getLoc().getZ();
 		short region = getRegionOffset(x, y);
 		int blockX = getBlock(x);
 		int blockY = getBlock(y);
 		int cellX, cellY;
-		short NSWE = 0;
+		
 		int index = 0;
+		final IntBuffer idx = _geodataIndex.get(region);
 		//Geodata without index - it is just empty so index can be calculated on the fly
-		if (_geodataIndex.get(region) == null)
+		if (idx == null)
 			index = ((blockX << 8) + blockY) * 3;
 		//Get Index for current block of current region geodata
 		else
-			index = _geodataIndex.get(region).get(((blockX << 8)) + (blockY));
+			index = idx.get(((blockX << 8)) + (blockY));
 		//Buffer that Contains current Region GeoData
 		ByteBuffer geo = _geodata.get(region);
 		if (geo == null)
 		{
 			if (Config.DEBUG)
 				_log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!");
-			return null;
+			return (short)((z << 1) | NSWE_ALL);
 		}
 		//Read current block type: 0-flat,1-complex,2-multilevel
 		byte type = geo.get(index);
 		index++;
 		if (type == 0)//flat
-		{
-			short height = geo.getShort(index);
-			n.getLoc().setZ(height);
-			if (parentdirection != 1)
-			{
-				newNode = CellPathFinding.getInstance().readNode(x + 1, y, height);
-				//newNode.setCost(0);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != 2)
-			{
-				newNode = CellPathFinding.getInstance().readNode(x, y + 1, height);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != -2)
-			{
-				newNode = CellPathFinding.getInstance().readNode(x, y - 1, height);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != -1)
-			{
-				newNode = CellPathFinding.getInstance().readNode(x - 1, y, height);
-				Neighbors.add(newNode);
-			}
-		}
+			return (short)((geo.getShort(index) << 1) | NSWE_ALL);
 		else if (type == 1)//complex
 		{
 			cellX = getCell(x);
 			cellY = getCell(y);
 			index += ((cellX << 3) + cellY) << 1;
-			short height = geo.getShort(index);
-			NSWE = (short) (height & 0x0F);
-			height = (short) (height & 0x0fff0);
-			height = (short) (height >> 1); //height / 2
-			n.getLoc().setZ(height);
-			if (NSWE != 15 && parentdirection != 0)
-				return null; // no node with a block will be used
-			if (parentdirection != 1 && checkNSWE(NSWE, x, y, x + 1, y))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x + 1, y, height);
-				//newNode.setCost(basecost+50);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != 2 && checkNSWE(NSWE, x, y, x, y + 1))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x, y + 1, height);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != -2 && checkNSWE(NSWE, x, y, x, y - 1))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x, y - 1, height);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != -1 && checkNSWE(NSWE, x, y, x - 1, y))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x - 1, y, height);
-				Neighbors.add(newNode);
-			}
+			return geo.getShort(index);
 		}
 		else
 		//multilevel
@@ -1546,54 +1482,28 @@ public class GeoEngine extends GeoData
 			short height = -1;
 			if (layers <= 0 || layers > 125)
 			{
-				_log.warning("Broken geofile (case5), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y);
-				return null;
+				_log.warning("Broken geofile (case1), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y);
+				return (short)((z << 1) | NSWE_ALL);
 			}
-			short tempz = Short.MIN_VALUE;
+			short temph = Short.MIN_VALUE;
+			short result = 0;
 			while (layers > 0)
 			{
-				height = geo.getShort(index);
-				height = (short) (height & 0x0fff0);
+				short block = geo.getShort(index);
+				height = (short) (block & 0x0fff0);
 				height = (short) (height >> 1); //height / 2
-				
-				if ((z - tempz) * (z - tempz) > (z - height) * (z - height))
+				if ((z - temph) * (z - temph) > (z - height) * (z - height))
 				{
-					tempz = height;
-					NSWE = geo.get(index);
-					NSWE = (short) (NSWE & 0x0F);
+					temph = height;
+					result = block;
 				}
 				layers--;
 				index += 2;
 			}
-			n.getLoc().setZ(tempz);
-			if (NSWE != 15 && parentdirection != 0)
-				return null; // no node with a block will be used
-			if (parentdirection != 1 && checkNSWE(NSWE, x, y, x + 1, y))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x + 1, y, tempz);
-				//newNode.setCost(basecost+50);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != 2 && checkNSWE(NSWE, x, y, x, y + 1))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x, y + 1, tempz);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != -2 && checkNSWE(NSWE, x, y, x, y - 1))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x, y - 1, tempz);
-				Neighbors.add(newNode);
-			}
-			if (parentdirection != -1 && checkNSWE(NSWE, x, y, x - 1, y))
-			{
-				newNode = CellPathFinding.getInstance().readNode(x - 1, y, tempz);
-				Neighbors.add(newNode);
-			}
+			return result;
 		}
-		Node[] result = new Node[Neighbors.size()];
-		return Neighbors.toArray(result);
 	}
-	
+
 	/**
 	 * @param NSWE
 	 * @param x
@@ -1609,22 +1519,22 @@ public class GeoEngine extends GeoData
 			return true;
 		if (tx > x)//E
 		{
-			if ((NSWE & _e) == 0)
+			if ((NSWE & EAST) == 0)
 				return false;
 		}
 		else if (tx < x)//W
 		{
-			if ((NSWE & _w) == 0)
+			if ((NSWE & WEST) == 0)
 				return false;
 		}
 		if (ty > y)//S
 		{
-			if ((NSWE & _s) == 0)
+			if ((NSWE & SOUTH) == 0)
 				return false;
 		}
 		else if (ty < y)//N
 		{
-			if ((NSWE & _n) == 0)
+			if ((NSWE & NORTH) == 0)
 				return false;
 		}
 		return true;

+ 13 - 19
L2_GameServer/java/com/l2jserver/gameserver/model/actor/L2Character.java

@@ -62,11 +62,8 @@ import com.l2jserver.gameserver.model.L2Skill.SkillTargetType;
 import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2BoatInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
-import com.l2jserver.gameserver.model.actor.instance.L2MinionInstance;
-import com.l2jserver.gameserver.model.actor.instance.L2NpcWalkerInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PetInstance;
-import com.l2jserver.gameserver.model.actor.instance.L2RiftInvaderInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance.SkillDat;
 import com.l2jserver.gameserver.model.actor.knownlist.CharKnownList;
 import com.l2jserver.gameserver.model.actor.position.CharPosition;
@@ -4489,7 +4486,7 @@ public abstract class L2Character extends L2Object
 		if (Config.GEODATA > 0 
 			&& !isFlying() // flying chars not checked - even canSeeTarget doesn't work yet
 			&& (!isInsideZone(ZONE_WATER) || isInsideZone(ZONE_SIEGE)) // swimming also not checked unless in siege zone - but distance is limited
-			&& !(this instanceof L2NpcWalkerInstance)) // npc walkers not checked
+			&& !isInstanceType(InstanceType.L2NpcWalkerInstance)) // npc walkers not checked
 		{
 			double originalDistance = distance;
 			int originalX = x;
@@ -4501,11 +4498,10 @@ public abstract class L2Character extends L2Object
 			// Movement checks:
 			// when geodata == 2, for all characters except mobs returning home (could be changed later to teleport if pathfinding fails)
 			// when geodata == 1, for l2playableinstance and l2riftinstance only
-			if ((Config.GEODATA == 2 &&	!(this instanceof L2Attackable && ((L2Attackable)this).isReturningToSpawnPoint())) 
-					|| this instanceof L2PcInstance 
-					|| (this instanceof L2Summon && !(this.getAI().getIntention() == AI_INTENTION_FOLLOW)) // assuming intention_follow only when following owner
-					|| isAfraid()
-					|| this instanceof L2RiftInvaderInstance)
+			if ((Config.GEODATA == 2 &&	!(isInstanceType(InstanceType.L2Attackable) && ((L2Attackable)this).isReturningToSpawnPoint())) 
+					|| isInstanceTypes(InstanceType.L2Playable, InstanceType.L2RiftInvaderInstance) 
+					|| (isInstanceType(InstanceType.L2Summon) && !(this.getAI().getIntention() == AI_INTENTION_FOLLOW)) // assuming intention_follow only when following owner
+					|| isAfraid())
 			{
 				if (isOnGeodataPath())
 				{
@@ -4545,10 +4541,10 @@ public abstract class L2Character extends L2Object
 			{
 				// Path calculation
 				// Overrides previous movement check
-				if(this instanceof L2Playable || this.isInCombat() || this instanceof L2MinionInstance)
+				if(isInstanceTypes(InstanceType.L2Playable, InstanceType.L2MinionInstance) || this.isInCombat())
 				{
 		
-					m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId());
+					m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isInstanceType(InstanceType.L2Playable));
                 	if (m.geoPath == null || m.geoPath.size() < 2) // No path found
                 	{
                 		// * Even though there's no path found (remember geonodes aren't perfect), 
@@ -4559,11 +4555,10 @@ public abstract class L2Character extends L2Object
                 		// * Summons will follow their masters no matter what.
                 		// * Currently minions also must move freely since L2AttackableAI commands
                 		// them to move along with their leader
-                		if (this instanceof L2PcInstance 
-                				|| (!(this instanceof L2Playable) 
-                						&& !(this instanceof L2MinionInstance)
+                		if (isInstanceType(InstanceType.L2PcInstance) 
+                				|| (!isInstanceTypes(InstanceType.L2Playable, InstanceType.L2MinionInstance) 
                 						&& Math.abs(z - curZ) > 140)
-                				|| (this instanceof L2Summon && !((L2Summon)this).getFollowStatus())) 
+                				|| (isInstanceType(InstanceType.L2Summon) && !((L2Summon)this).getFollowStatus())) 
                 		{
                 			getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                 			return;
@@ -4616,11 +4611,10 @@ public abstract class L2Character extends L2Object
 			}
 			// If no distance to go through, the movement is canceled
 			if (distance < 1 && (Config.GEODATA == 2 
-					|| this instanceof L2Playable
-					|| this.isAfraid()
-					|| this instanceof L2RiftInvaderInstance))
+					|| isInstanceTypes(InstanceType.L2Playable, InstanceType.L2RiftInvaderInstance) 
+					|| this.isAfraid()))
 			{
-				if(this instanceof L2Summon) ((L2Summon)this).setFollowStatus(false);
+				if(isInstanceType(InstanceType.L2Summon)) ((L2Summon)this).setFollowStatus(false);
 				getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
 				return;
 			}

+ 12 - 38
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/Node.java → L2_GameServer/java/com/l2jserver/gameserver/pathfinding/AbstractNode.java

@@ -14,47 +14,22 @@
  */
 package com.l2jserver.gameserver.pathfinding;
 
-/**
- *
- * @author -Nemesiss-
- */
-public class Node
+public abstract class AbstractNode
 {
-	private final AbstractNodeLoc _loc;
-	private final int _neighborsIdx;
-	private Node[] _neighbors;
-	private Node _parent;
-	private short _cost;
-
+	private AbstractNodeLoc _loc;
+	private AbstractNode _parent;
 
-	public Node(AbstractNodeLoc Loc, int Neighbors_idx)
+	public AbstractNode(AbstractNodeLoc loc)
 	{
-		_loc = Loc;
-		_neighborsIdx = Neighbors_idx;
+		_loc = loc;
 	}
 
-	public void setParent(Node p)
+	public void setParent(AbstractNode p)
 	{
 		_parent = p;
 	}
 
-	public void setCost(int cost)
-	{
-		_cost = (short)cost;
-	}
-
-	public void attachNeighbors()
-	{
-		if(_loc == null) _neighbors = null;
-		else _neighbors = PathFinding.getInstance().readNeighbors(this, _neighborsIdx);
-	}
-
-	public Node[] getNeighbors()
-	{
-		return _neighbors;
-	}
-
-	public Node getParent()
+	public AbstractNode getParent()
 	{
 		return _parent;
 	}
@@ -64,9 +39,9 @@ public class Node
 		return _loc;
 	}
 
-	public short getCost()
+	public void setLoc(AbstractNodeLoc l)
 	{
-		return _cost;
+		_loc = l;
 	}
 
 	/**
@@ -91,9 +66,9 @@ public class Node
 		    return true;
 	    if (obj == null)
 		    return false;
-	    if (!(obj instanceof Node))
+	    if (!(obj instanceof AbstractNode))
 		    return false;
-	    final Node other = (Node) obj;
+	    final AbstractNode other = (AbstractNode) obj;
 	    if (_loc == null)
 	    {
 		    if (other._loc != null)
@@ -103,5 +78,4 @@ public class Node
 		    return false;
 	    return true;
     }
-	
-}
+}

+ 18 - 256
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/PathFinding.java

@@ -14,19 +14,12 @@
  */
 package com.l2jserver.gameserver.pathfinding;
 
-import java.util.LinkedList;
 import java.util.List;
 
 import com.l2jserver.Config;
-import com.l2jserver.gameserver.GeoData;
 import com.l2jserver.gameserver.model.L2World;
 import com.l2jserver.gameserver.pathfinding.cellnodes.CellPathFinding;
 import com.l2jserver.gameserver.pathfinding.geonodes.GeoPathFinding;
-import com.l2jserver.gameserver.pathfinding.utils.BinaryNodeHeap;
-import com.l2jserver.gameserver.pathfinding.utils.CellNodeMap;
-import com.l2jserver.gameserver.pathfinding.utils.FastNodeList;
-
-import javolution.util.FastList;
 
 /**
  *
@@ -50,27 +43,26 @@ public abstract class PathFinding
 	
 	public abstract boolean pathNodesExist(short regionoffset);
 	
-	public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId);
-	
-	public abstract Node[] readNeighbors(Node n, int idx);
+	public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId, boolean playable);
 	
-	public List<AbstractNodeLoc> search(Node start, Node end)
+/*
+	public List<AbstractNodeLoc> search(AbstractNode start, AbstractNode end, int instanceId)
 	{
 		// The simplest grid-based pathfinding.
 		// Drawback is not having higher cost for diagonal movement (means funny routes)
 		// Could be optimized e.g. not to calculate backwards as far as forwards.
 		
 		// List of Visited Nodes
-		LinkedList<Node> visited = new LinkedList<Node>();
+		LinkedList<AbstractNode> visited = new LinkedList<AbstractNode>();
 		
 		// List of Nodes to Visit
-		LinkedList<Node> to_visit = new LinkedList<Node>();
+		LinkedList<AbstractNode> to_visit = new LinkedList<AbstractNode>();
 		to_visit.add(start);
 		
 		int i = 0;
 		while (i < 800)
 		{
-			Node node;
+			AbstractNode node;
 			try
 			{
 				node = to_visit.removeFirst();
@@ -81,7 +73,7 @@ public abstract class PathFinding
 				return null;
 			}
 			if (node.equals(end)) //path found!
-				return constructPath(node);
+				return constructPath(node, instanceId);
 			else
 			{
 				i++;
@@ -103,167 +95,9 @@ public abstract class PathFinding
 		//No Path found
 		return null;
 	}
-	
-	public List<AbstractNodeLoc> searchByClosest(Node start, Node end)
-	{
-		// Note: This is the version for cell-based calculation, harder 
-		// on cpu than from block-based pathnode files. However produces better routes.
-		
-		// Always continues checking from the closest to target non-blocked
-		// node from to_visit list. There's extra length in path if needed
-		// to go backwards/sideways but when moving generally forwards, this is extra fast
-		// and accurate. And can reach insane distances (try it with 8000 nodes..).
-		// Minimum required node count would be around 300-400.
-		// Generally returns a bit (only a bit) more intelligent looking routes than
-		// the basic version. Not a true distance image (which would increase CPU
-		// load) level of intelligence though.
-		
-		// List of Visited Nodes
-		CellNodeMap known = new CellNodeMap();
-		
-		// List of Nodes to Visit
-		LinkedList<Node> to_visit = new LinkedList<Node>();
-		to_visit.add(start);
-		known.add(start);
-		int targetx = end.getLoc().getNodeX();
-		int targety = end.getLoc().getNodeY();
-		int targetz = end.getLoc().getZ();
-		
-		int dx, dy, dz;
-		boolean added;
-		int i = 0;
-		while (i < 3500)
-		{
-			Node node;
-			try
-			{
-				node = to_visit.removeFirst();
-			}
-			catch (Exception e)
-			{
-				// No Path found
-				return null;
-			}
-			i++;
-			
-			node.attachNeighbors();
-			if (node.equals(end))
-			{
-				//path found! note that node z coordinate is updated only in attach
-				//to improve performance (alternative: much more checks)
-				//System.out.println("path found, i:"+i);
-				return constructPath(node);
-			}
-			
-			Node[] neighbors = node.getNeighbors();
-			if (neighbors == null)
-				continue;
-			for (Node n : neighbors)
-			{
-				if (!known.contains(n))
-				{
-					
-					added = false;
-					n.setParent(node);
-					dx = targetx - n.getLoc().getNodeX();
-					dy = targety - n.getLoc().getNodeY();
-					dz = targetz - n.getLoc().getZ();
-					n.setCost(dx * dx + dy * dy + dz / 2 * dz/*+n.getCost()*/);
-					for (int index = 0; index < to_visit.size(); index++)
-					{
-						// supposed to find it quite early..
-						if (to_visit.get(index).getCost() > n.getCost())
-						{
-							to_visit.add(index, n);
-							added = true;
-							break;
-						}
-					}
-					if (!added)
-						to_visit.addLast(n);
-					known.add(n);
-				}
-			}
-		}
-		//No Path found
-		//System.out.println("no path found");
-		return null;
-	}
-	
-	public List<AbstractNodeLoc> searchByClosest2(Node start, Node end)
-	{
-		// Always continues checking from the closest to target non-blocked
-		// node from to_visit list. There's extra length in path if needed
-		// to go backwards/sideways but when moving generally forwards, this is extra fast
-		// and accurate. And can reach insane distances (try it with 800 nodes..).
-		// Minimum required node count would be around 300-400.
-		// Generally returns a bit (only a bit) more intelligent looking routes than
-		// the basic version. Not a true distance image (which would increase CPU
-		// load) level of intelligence though.
-		
-		// List of Visited Nodes
-		FastNodeList visited = new FastNodeList(550);
-		
-		// List of Nodes to Visit
-		LinkedList<Node> to_visit = new LinkedList<Node>();
-		to_visit.add(start);
-		int targetx = end.getLoc().getNodeX();
-		int targety = end.getLoc().getNodeY();
-		int dx, dy;
-		boolean added;
-		int i = 0;
-		while (i < 550)
-		{
-			Node node;
-			try
-			{
-				node = to_visit.removeFirst();
-			}
-			catch (Exception e)
-			{
-				// No Path found
-				return null;
-			}
-			if (node.equals(end)) //path found!
-				return constructPath2(node);
-			else
-			{
-				i++;
-				visited.add(node);
-				node.attachNeighbors();
-				Node[] neighbors = node.getNeighbors();
-				if (neighbors == null)
-					continue;
-				for (Node n : neighbors)
-				{
-					if (!visited.containsRev(n) && !to_visit.contains(n))
-					{
-						added = false;
-						n.setParent(node);
-						dx = targetx - n.getLoc().getNodeX();
-						dy = targety - n.getLoc().getNodeY();
-						n.setCost(dx * dx + dy * dy);
-						for (int index = 0; index < to_visit.size(); index++)
-						{
-							// supposed to find it quite early..
-							if (to_visit.get(index).getCost() > n.getCost())
-							{
-								to_visit.add(index, n);
-								added = true;
-								break;
-							}
-						}
-						if (!added)
-							to_visit.addLast(n);
-					}
-				}
-			}
-		}
-		//No Path found
-		return null;
-	}
-	
-	public List<AbstractNodeLoc> searchAStar(Node start, Node end)
+*/
+/*
+	public List<AbstractNodeLoc> searchAStar(Node start, Node end, int instanceId)
 	{
 		// Not operational yet?
 		int start_x = start.getLoc().getX();
@@ -280,7 +114,7 @@ public abstract class PathFinding
 		int i = 0;
 		while (i < 800)//TODO! Add limit to cfg
 		{
-			Node node;
+			AbstractNode node;
 			try
 			{
 				node = to_visit.removeFirst();
@@ -291,7 +125,7 @@ public abstract class PathFinding
 				return null;
 			}
 			if (node.equals(end)) //path found!
-				return constructPath(node);
+				return constructPath(node, instanceId);
 			else
 			{
 				visited.add(node);
@@ -312,84 +146,7 @@ public abstract class PathFinding
 		//No Path found
 		return null;
 	}
-	
-	public List<AbstractNodeLoc> constructPath(Node node)
-	{
-		LinkedList<AbstractNodeLoc> path = new LinkedList<AbstractNodeLoc>();
-		int previousdirectionx = -1000;
-		int previousdirectiony = -1000;
-		int directionx;
-		int directiony;
-		while (node.getParent() != null)
-		{
-			// only add a new route point if moving direction changes
-			if (node.getParent().getParent() != null // to check and clean diagonal movement
-					&& Math.abs(node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX()) == 1
-					&& Math.abs(node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY()) == 1)
-			{
-				directionx = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX();
-				directiony = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY();
-			}
-			else
-			{
-				directionx = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
-				directiony = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
-			}
-			if (directionx != previousdirectionx || directiony != previousdirectiony)
-			{
-				previousdirectionx = directionx;
-				previousdirectiony = directiony;
-				path.addFirst(node.getLoc());
-			}
-			node = node.getParent();
-		}
-		// then LOS based filtering to reduce the number of route points
-		if (path.size() > 4)
-		{
-			//System.out.println("pathsize:"+path.size());
-			List<Integer> valueList = new FastList<Integer>();
-			for (int index = 0; index < path.size() - 3; index = index + 3)
-			{
-				//System.out.println("Attempt filter");
-				if (GeoData.getInstance().canMoveFromToTarget(path.get(index).getX(), path.get(index).getY(), path.get(index).getZ(), path.get(index + 3).getX(), path.get(index + 3).getY(), path.get(index + 3).getZ(), 0))
-				{
-					//System.out.println("filtering i:"+(index+1));
-					valueList.add(index + 1);
-					valueList.add(index + 2);
-				}
-			}
-			for (int index = valueList.size() - 1; index >= 0; index--)
-			{
-				path.remove(valueList.get(index).intValue());
-			}
-			//System.out.println("pathsize:"+path.size());
-		}
-		return path;
-	}
-	
-	public List<AbstractNodeLoc> constructPath2(Node node)
-	{
-		LinkedList<AbstractNodeLoc> path = new LinkedList<AbstractNodeLoc>();
-		int previousdirectionx = -1000;
-		int previousdirectiony = -1000;
-		int directionx;
-		int directiony;
-		while (node.getParent() != null)
-		{
-			// only add a new route point if moving direction changes
-			directionx = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
-			directiony = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
-			if (directionx != previousdirectionx || directiony != previousdirectiony)
-			{
-				previousdirectionx = directionx;
-				previousdirectiony = directiony;
-				path.addFirst(node.getLoc());
-			}
-			node = node.getParent();
-		}
-		return path;
-	}
-	
+*/
 	/**
 	 * Convert geodata position to pathnode position
 	 * @param geo_pos
@@ -444,4 +201,9 @@ public abstract class PathFinding
 	{
 		return L2World.MAP_MIN_Y + node_y * 128 + 48;
 	}
+
+	public String[] getStat()
+	{
+		return null;
+	}
 }

+ 68 - 0
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/cellnodes/CellNode.java

@@ -0,0 +1,68 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.pathfinding.cellnodes;
+
+import com.l2jserver.gameserver.pathfinding.AbstractNode;
+import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
+
+public class CellNode extends AbstractNode
+{
+	private CellNode _next = null;
+	private boolean _isInUse = true;
+	private float _cost = -1000;
+
+	public CellNode(AbstractNodeLoc loc)
+	{
+		super(loc);
+	}
+
+	public boolean isInUse()
+	{
+		return _isInUse;
+	}
+
+	public void setInUse()
+	{
+		_isInUse = true;
+	}
+
+	public CellNode getNext()
+	{
+		return _next;
+	}
+
+	public void setNext(CellNode next)
+	{
+		_next = next;
+	}
+
+	public float getCost()
+	{
+		return _cost;
+	}
+
+	public void setCost(double cost)
+	{
+		_cost = (float)cost;
+	}
+
+	public void free()
+	{
+		setParent(null);
+		_cost = -1000;
+		_isInUse = false;
+		_next = null;
+	}
+}

+ 316 - 0
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/cellnodes/CellNodeBuffer.java

@@ -0,0 +1,316 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.pathfinding.cellnodes;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+import javolution.util.FastList;
+
+import com.l2jserver.Config;
+
+/**
+ * 
+ * @author DS
+ * Credits to Diamond
+ *
+ */
+public class CellNodeBuffer
+{
+	private static final byte EAST = 1;
+	private static final byte WEST = 2;
+	private static final byte SOUTH = 4;
+	private static final byte NORTH = 8;
+	private static final byte NSWE_ALL = 15;
+	private static final byte NSWE_NONE = 0;
+
+	private static final int MAX_ITERATIONS = 3500;
+
+	private final ReentrantLock _lock = new ReentrantLock();
+	private final int _mapSize;
+	private final CellNode[][] _buffer;
+
+	private int _baseX = 0;
+	private int _baseY = 0;
+
+	private int _targetX = 0;
+	private int _targetY = 0;
+	private short _targetZ = 0;
+
+	private long _timeStamp = 0;
+	private long _lastElapsedTime = 0;
+
+	private CellNode _current = null;
+
+	public CellNodeBuffer(int size)
+	{
+		_mapSize = size;
+		_buffer = new CellNode[_mapSize][_mapSize];
+	}
+
+	public final boolean lock()
+	{
+		return _lock.tryLock();
+	}
+
+	public final CellNode findPath(int x, int y, short z, int tx, int ty, short tz)
+	{
+		_timeStamp = System.currentTimeMillis();
+		_baseX = x + (tx - x - _mapSize) / 2; // middle of the line (x,y) - (tx,ty)
+		_baseY = y + (ty - y - _mapSize) / 2; // will be in the center of the buffer
+		_targetX = tx;
+		_targetY = ty;
+		_targetZ = tz;
+		_current = getNode(x, y, z);
+		_current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
+
+		for (int count = 0; count < MAX_ITERATIONS; count++)
+		{
+			if (_current.getLoc().getNodeX() == _targetX
+					&& _current.getLoc().getNodeY() == _targetY
+					&& Math.abs(_current.getLoc().getZ() - _targetZ) < 64)
+				return _current; // found
+
+			getNeighbors();
+			if (_current.getNext() == null)
+				return null; // no more ways
+
+			_current = _current.getNext();
+		}
+		return null;
+	}
+
+	public final void free()
+	{
+		_current = null;
+
+		CellNode node;
+		for (int i = 0; i < _mapSize; i++)
+			for (int j = 0; j < _mapSize; j++)
+			{
+				node = _buffer[i][j];
+				if (node != null)
+					node.free();
+			}
+
+		_lock.unlock();
+		_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
+	}
+
+	public final long getElapsedTime()
+	{
+		return _lastElapsedTime;
+	}
+
+	public final FastList<CellNode> debugPath()
+	{
+		FastList<CellNode> result = new FastList<CellNode>();
+
+		for (CellNode n = _current; n.getParent() != null; n = (CellNode)n.getParent())
+		{
+			result.add(n);
+			n.setCost(-n.getCost());
+		}
+
+		for (int i = 0; i < _mapSize; i++)
+			for (int j = 0; j < _mapSize; j++)
+			{
+				CellNode n = _buffer[i][j];
+				if (n == null || !n.isInUse() || n.getCost() <= 0)
+					continue;
+
+				result.add(n);
+			}
+
+		return result;
+	}
+
+	private final void getNeighbors()
+	{
+		final short NSWE = ((NodeLoc)_current.getLoc()).getNSWE();
+		if (NSWE == NSWE_NONE)
+			return;
+
+		final int x = _current.getLoc().getNodeX();
+		final int y = _current.getLoc().getNodeY();
+		final short z = _current.getLoc().getZ();
+
+		CellNode nodeE = null;
+		CellNode nodeS = null;
+		CellNode nodeW = null;
+		CellNode nodeN = null;
+
+		// East
+		if ((NSWE & EAST) != 0)
+			nodeE = addNode(x + 1, y, z, false);
+
+		// South
+		if ((NSWE & SOUTH) != 0)
+			nodeS = addNode(x, y + 1, z, false);
+
+		// West
+		if ((NSWE & WEST) != 0)
+			nodeW = addNode(x - 1, y, z, false);
+
+		// North
+		if ((NSWE & NORTH) != 0)
+			nodeN = addNode(x, y - 1, z, false);
+
+		if (Config.ADVANCED_DIAGONAL_STRATEGY)
+		{
+			// SouthEast
+			if (nodeE != null && nodeS != null)
+			{
+				if ((((NodeLoc)nodeE.getLoc()).getNSWE() & SOUTH) != 0
+						&& (((NodeLoc)nodeS.getLoc()).getNSWE() & EAST) != 0)
+					addNode(x + 1, y + 1, z, true);
+			}
+
+			// SouthWest
+			if (nodeS != null && nodeW != null)
+			{
+				if ((((NodeLoc)nodeW.getLoc()).getNSWE() & SOUTH) != 0
+						&& (((NodeLoc)nodeS.getLoc()).getNSWE() & WEST) != 0)
+					addNode(x - 1, y + 1, z, true);
+			}
+
+			// NorthEast
+			if (nodeN != null && nodeE != null)
+			{
+				if ((((NodeLoc)nodeE.getLoc()).getNSWE() & NORTH) != 0
+						&& (((NodeLoc)nodeN.getLoc()).getNSWE() & EAST) != 0)
+					addNode(x + 1, y - 1, z, true);
+			}
+
+			// NorthWest
+			if (nodeN != null && nodeW != null)
+			{
+				if ((((NodeLoc)nodeW.getLoc()).getNSWE() & NORTH) != 0
+						&& (((NodeLoc)nodeN.getLoc()).getNSWE() & WEST) != 0)
+					addNode(x - 1, y - 1, z, true);
+			}
+		}
+	}
+
+	private final CellNode getNode(int x, int y, short z)
+	{
+		final int aX = x - _baseX;
+		if (aX < 0 || aX >= _mapSize)
+			return null;
+
+		final int aY = y - _baseY;
+		if (aY < 0 || aY >= _mapSize)
+			return null;
+
+		CellNode result = _buffer[aX][aY];
+		if (result == null)
+		{
+			result = new CellNode(new NodeLoc(x, y, z));
+			_buffer[aX][aY] = result;
+		}
+		else if (!result.isInUse())
+		{
+			result.setInUse();
+			// reinit node if needed
+			if (result.getLoc() != null)
+				((NodeLoc)result.getLoc()).set(x, y, z);
+			else
+				result.setLoc(new NodeLoc(x, y, z));
+		}
+
+		return result;
+	}
+
+	private final CellNode addNode(int x, int y, short z, boolean diagonal)
+	{
+		CellNode newNode = getNode(x, y, z);
+		if (newNode == null)
+			return null;
+		if (newNode.getCost() >= 0)
+			return newNode;
+
+		final short geoZ = newNode.getLoc().getZ();
+
+		final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
+		float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
+
+		if (((NodeLoc)newNode.getLoc()).getNSWE() != NSWE_ALL || stepZ > 16)
+			weight = Config.HIGH_WEIGHT;
+		else
+		{
+			if (isHighWeight(x + 1, y, geoZ))
+				weight = Config.MEDIUM_WEIGHT;
+			else if (isHighWeight(x - 1, y, geoZ))
+				weight = Config.MEDIUM_WEIGHT;
+			else if (isHighWeight(x, y + 1, geoZ))
+				weight = Config.MEDIUM_WEIGHT;
+			else if (isHighWeight(x, y -1, geoZ))
+				weight = Config.MEDIUM_WEIGHT;
+		}
+
+		newNode.setParent(_current);
+		newNode.setCost(getCost(x, y, geoZ, weight));
+
+		CellNode node = _current;
+		int count = 0;
+		while (node.getNext() != null && count < MAX_ITERATIONS * 4)
+		{
+			count++;
+			if (node.getNext().getCost() > newNode.getCost())
+			{
+				// insert node into a chain
+				newNode.setNext(node.getNext());
+				break;
+			}
+			else
+				node = node.getNext();
+		}
+		if (count == MAX_ITERATIONS * 4)
+			System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
+
+		node.setNext(newNode); // add last
+
+		return newNode;
+	}
+
+	private final boolean isHighWeight(int x, int y, short z)
+	{
+		final CellNode result = getNode(x, y, z);
+		if (result == null)
+			return true;
+
+		if (((NodeLoc)result.getLoc()).getNSWE() != NSWE_ALL)
+			return true;
+		if (Math.abs(result.getLoc().getZ() - z)  > 16)
+			return true;
+
+		return false;
+	}
+
+	private final double getCost(int x, int y, short z, float weight)
+	{
+		final int dX = x - _targetX;
+		final int dY = y - _targetY;
+		final int dZ = z - _targetZ;
+		// Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16
+		double result = Math.sqrt(dX * dX + dY * dY + dZ * dZ /256);
+		if (result > weight)
+			result += weight;
+
+		if (result > Float.MAX_VALUE)
+			result = Float.MAX_VALUE;
+
+		return result;
+	}
+}

+ 329 - 22
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/cellnodes/CellPathFinding.java

@@ -14,26 +14,75 @@
  */
 package com.l2jserver.gameserver.pathfinding.cellnodes;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.ListIterator;
+import java.util.logging.Logger;
 
+import javolution.util.FastList;
+
+import com.l2jserver.Config;
 import com.l2jserver.gameserver.GeoData;
+import com.l2jserver.gameserver.idfactory.IdFactory;
+import com.l2jserver.gameserver.model.L2ItemInstance;
 import com.l2jserver.gameserver.model.L2World;
+import com.l2jserver.gameserver.pathfinding.AbstractNode;
 import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
-import com.l2jserver.gameserver.pathfinding.Node;
 import com.l2jserver.gameserver.pathfinding.PathFinding;
+import com.l2jserver.util.StringUtil;
 
 
 /**
  *
- * @author Sami
+ * @author Sami, DS
+ * Credits to Diamond
  */
 public class CellPathFinding extends PathFinding
 {
+	private static final Logger _log = Logger.getLogger(CellPathFinding.class.getName());
+	private BufferInfo[] _allBuffers;
+	private int _findSuccess = 0;
+	private int _findFails = 0;
+	private int _postFilterUses = 0;
+	private int _postFilterPlayableUses = 0;
+	private int _postFilterPasses = 0;
+	private long _postFilterElapsed = 0;
+
+	private FastList<L2ItemInstance> _debugItems = null;
+
+
 	public static CellPathFinding getInstance()
 	{
 		return SingletonHolder._instance;
 	}
 	
+	private CellPathFinding()
+	{
+		try
+		{
+			String[] array = Config.PATHFIND_BUFFERS.split(";");
+
+			_allBuffers = new BufferInfo[array.length];
+
+			String buf;
+			String[] args;
+			for (int i = 0; i < array.length; i++)
+			{
+				buf = array[i];
+				args = buf.split("x");
+				if (args.length != 2)
+					throw new Exception("Invalid buffer definition: " + buf);
+
+				_allBuffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
+			}
+		}
+		catch (Exception e)
+		{
+			_log.warning("CellPathFinding: Problem during buffer init: " + e.getMessage());
+			throw new Error("CellPathFinding: load aborted");
+		}
+	}
+
 	/**
 	 * @see com.l2jserver.gameserver.pathfinding.PathFinding#PathNodesExist(short)
 	 */
@@ -47,7 +96,7 @@ public class CellPathFinding extends PathFinding
 	 * @see com.l2jserver.gameserver.pathfinding.PathFinding#FindPath(int, int, short, int, int, short)
 	 */
 	@Override
-	public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId)
+	public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId, boolean playable)
 	{
 		int gx = (x - L2World.MAP_MIN_X) >> 4;
 		int gy = (y - L2World.MAP_MIN_Y) >> 4;
@@ -59,32 +108,290 @@ public class CellPathFinding extends PathFinding
 		if (!GeoData.getInstance().hasGeo(tx, ty))
 			return null;
 		short gtz = GeoData.getInstance().getHeight(tx, ty, tz);
-		Node start = readNode(gx, gy, gz);
-		Node end = readNode(gtx, gty, gtz);
-		return searchByClosest(start, end);
+		CellNodeBuffer buffer = alloc(64 + 2*Math.max(Math.abs(gx - gtx), Math.abs(gy - gty)), playable);
+		if (buffer == null)
+			return null;
+
+		boolean debug = playable && Config.DEBUG_PATH;
+
+		if (debug)
+		{
+			if (_debugItems == null)
+				_debugItems = new FastList<L2ItemInstance>();
+			else
+			{
+				for (L2ItemInstance item : _debugItems)
+				{
+					if (item == null)
+						continue;
+					item.decayMe();
+				}
+
+				_debugItems.clear();
+			}
+		}
+
+		FastList<AbstractNodeLoc> path = null;
+		try
+		{
+			CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
+
+			if (debug)
+			{
+				for (CellNode n : buffer.debugPath())
+				{
+					if (n.getCost() < 0) // calculated path
+						dropDebugItem(1831, (int)(-n.getCost() * 10), n.getLoc());
+					else // known nodes
+						dropDebugItem(57, (int)(n.getCost() * 10), n.getLoc());
+				}
+			}
+
+			if (result == null)
+			{
+				_findFails++;
+				return null;
+			}
+
+			path = constructPath(result);
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+			return null;
+		}
+		finally
+		{
+			buffer.free();
+		}
+
+		if (path.size() < 3 || Config.MAX_POSTFILTER_PASSES <= 0)
+		{
+			_findSuccess++;
+			return path;
+		}
+
+		long timeStamp = System.currentTimeMillis();
+		_postFilterUses++;
+		if (playable)
+			_postFilterPlayableUses++;
+
+		int currentX, currentY, currentZ;
+		ListIterator<AbstractNodeLoc> middlePoint, endPoint;
+		AbstractNodeLoc locMiddle, locEnd;
+		boolean remove;
+		int pass = 0;
+		do
+		{
+			pass++;
+			_postFilterPasses++;
+
+			remove = false;
+			middlePoint = path.listIterator();
+			endPoint = path.listIterator(1);
+			locEnd = null;
+			currentX = x;
+			currentY = y;
+			currentZ = z;
+
+			while (endPoint.hasNext())
+			{
+				locEnd = endPoint.next();
+				locMiddle = middlePoint.next();
+				if (GeoData.getInstance().canMoveFromToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId))
+				{
+					middlePoint.remove();
+					remove = true;
+					if (debug)
+						dropDebugItem(735,1,locMiddle);
+				}
+				else
+				{
+					currentX = locMiddle.getX();
+					currentY = locMiddle.getY();
+					currentZ = locMiddle.getZ();
+				}
+			}
+		}
+		// only one postfilter pass for AI
+		while (playable && remove && path.size() > 2 && pass < Config.MAX_POSTFILTER_PASSES);
+
+		if (debug)
+		{
+			middlePoint = path.listIterator();
+			while (middlePoint.hasNext())
+			{
+				locMiddle = middlePoint.next();
+				dropDebugItem(65, 1, locMiddle);
+			}
+		}
+
+		_findSuccess++;
+		_postFilterElapsed += System.currentTimeMillis() - timeStamp;
+		return path;
 	}
-	
-	/**
-	 * @see com.l2jserver.gameserver.pathfinding.PathFinding#ReadNeighbors(short, short)
-	 */
-	@Override
-	public Node[] readNeighbors(Node n, int idx)
+
+	private FastList<AbstractNodeLoc> constructPath(AbstractNode node)
 	{
-		return GeoData.getInstance().getNeighbors(n);
+		FastList<AbstractNodeLoc> path = new FastList<AbstractNodeLoc>();
+		int previousDirectionX = Integer.MIN_VALUE;
+		int previousDirectionY = Integer.MIN_VALUE;
+		int directionX, directionY;
+
+		while (node.getParent() != null)
+		{
+			if (!Config.ADVANCED_DIAGONAL_STRATEGY && node.getParent().getParent() != null)
+			{
+				int tmpX = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX();
+				int tmpY = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY();
+				if (Math.abs(tmpX) == Math.abs(tmpY))
+				{
+					directionX = tmpX;
+					directionY = tmpY;
+				}
+				else
+				{
+					directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
+					directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
+				}
+			}
+			else
+			{
+				directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
+				directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
+			}
+
+			// only add a new route point if moving direction changes
+			if (directionX != previousDirectionX || directionY != previousDirectionY)
+			{
+				previousDirectionX = directionX;
+				previousDirectionY = directionY;
+
+				path.addFirst(node.getLoc());
+				node.setLoc(null);
+			}
+
+			node = node.getParent();
+		}
+
+		return path;
 	}
-	
-	//Private
-	
-	public Node readNode(int gx, int gy, short z)
+
+	private final CellNodeBuffer alloc(int size, boolean playable)
 	{
-		return new Node(new NodeLoc(gx, gy, z), 0);
+		CellNodeBuffer current = null;
+		for (BufferInfo i : _allBuffers)
+		{
+			if (i.mapSize >= size)
+			{
+				for (CellNodeBuffer buf : i.bufs)
+				{
+					if (buf.lock())
+					{
+						i.uses++;
+						if (playable)
+							i.playableUses++;
+						i.elapsed += buf.getElapsedTime();
+						current = buf;
+						break;
+					}
+				}
+				if (current != null)
+					break;
+
+				// not found, allocate temporary buffer
+				current = new CellNodeBuffer(i.mapSize);
+				current.lock();
+				if (i.bufs.size() < i.count)
+				{
+					i.bufs.add(current);
+					i.uses++;
+					if (playable)
+						i.playableUses++;
+					break;
+				}
+				else
+				{
+					i.overflows++;
+					if (playable)
+						i.playableOverflows++;
+					//System.err.println("Overflow, size requested: " + size + " playable:"+playable);
+				}
+			}
+		}
+
+		return current;
 	}
-	
-	private CellPathFinding()
+
+	private final void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
 	{
-		//
+		final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
+		item.setCount(num);
+		item.spawnMe(loc.getX(), loc.getY(), loc.getZ());
+		_debugItems.add(item);
 	}
-	
+
+	private static final class BufferInfo
+	{
+		final int mapSize;
+		final int count;
+		ArrayList<CellNodeBuffer> bufs;
+		int uses = 0;
+		int playableUses = 0;
+		int overflows = 0;
+		int playableOverflows = 0;
+		long elapsed = 0;
+
+		public BufferInfo(int size, int cnt)
+		{
+			mapSize = size;
+			count = cnt;
+			bufs = new ArrayList<CellNodeBuffer>(count);
+		}
+
+		@Override
+		public String toString()
+		{
+			final StringBuilder stat = new StringBuilder(100);
+			StringUtil.append(stat,
+					String.valueOf(mapSize), "x", String.valueOf(mapSize),
+					" num:", String.valueOf(bufs.size()), "/" ,String.valueOf(count),
+					" uses:", String.valueOf(uses), "/", String.valueOf(playableUses));
+			if (uses > 0)
+				StringUtil.append(stat, " total/avg(ms):",
+						String.valueOf(elapsed), "/", String.format("%1.2f", (double)elapsed/uses));				
+
+			StringUtil.append(stat, " ovf:", String.valueOf(overflows), "/",
+					String.valueOf(playableOverflows));
+
+			return stat.toString();
+		}
+	}
+
+	@Override
+	public String[] getStat()
+	{
+		final String[] result = new String[_allBuffers.length + 1];
+		for (int i = 0; i < _allBuffers.length; i++)
+			result[i] = _allBuffers[i].toString();
+
+		final StringBuilder stat = new StringBuilder(100);
+		StringUtil.append(stat,
+				"LOS postfilter uses:", String.valueOf(_postFilterUses),
+				"/", String.valueOf(_postFilterPlayableUses));
+		if (_postFilterUses > 0)
+			StringUtil.append(stat, " total/avg(ms):",
+					String.valueOf(_postFilterElapsed), "/",
+					String.format("%1.2f", (double)_postFilterElapsed/_postFilterUses),
+					" passes total/avg:", String.valueOf(_postFilterPasses), "/",
+					String.format("%1.1f", (double)_postFilterPasses/_postFilterUses),"\r\n");
+		StringUtil.append(stat, "Pathfind success/fail:", String.valueOf(_findSuccess),
+				"/", String.valueOf(_findFails));
+		result[result.length - 1] = stat.toString();
+
+		return result;
+	}
+
 	@SuppressWarnings("synthetic-access")
 	private static class SingletonHolder
 	{

+ 22 - 11
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/cellnodes/NodeLoc.java

@@ -13,6 +13,7 @@
  * this program. If not, see <http://www.gnu.org/licenses/>.
  */
 package com.l2jserver.gameserver.pathfinding.cellnodes;
+import com.l2jserver.gameserver.GeoData;
 import com.l2jserver.gameserver.model.L2World;
 import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
 
@@ -22,15 +23,27 @@ import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
  */
 public class NodeLoc extends AbstractNodeLoc
 {
-	private final int _x;
-	private final int _y;
-	private short _z;
+	private int _x;
+	private int _y;
+	private short _geoHeightAndNSWE;
 
 	public NodeLoc(int x, int y, short z)
 	{
 		_x = x;
 		_y = y;
-		_z = z;
+		_geoHeightAndNSWE = GeoData.getInstance().getHeightAndNSWE(x, y, z);
+	}
+
+	public void set(int x, int y, short z)
+	{
+		_x = x;
+		_y = y;
+		_geoHeightAndNSWE = GeoData.getInstance().getHeightAndNSWE(x, y, z);
+	}
+
+	public short getNSWE()
+	{
+		return (short)(_geoHeightAndNSWE & 0x0f);
 	}
 
 	/**
@@ -57,13 +70,14 @@ public class NodeLoc extends AbstractNodeLoc
 	@Override
 	public short getZ()
 	{
-		return _z;
+		short height = (short)(_geoHeightAndNSWE & 0x0fff0);
+		return (short)(height >> 1);
 	}
 
 	@Override
 	public void setZ(short z)
 	{
-		_z = z;
+		//
 	}
 	
 	/**
@@ -72,7 +86,6 @@ public class NodeLoc extends AbstractNodeLoc
 	@Override
 	public int getNodeX()
 	{
-		// TODO Auto-generated method stub
 		return _x;
 	}
 
@@ -82,7 +95,6 @@ public class NodeLoc extends AbstractNodeLoc
 	@Override
 	public int getNodeY()
 	{
-		// TODO Auto-generated method stub
 		return _y;
 	}
 
@@ -96,7 +108,7 @@ public class NodeLoc extends AbstractNodeLoc
 	    int result = 1;
 	    result = prime * result + _x;
 	    result = prime * result + _y;
-	    result = prime * result + _z;
+	    result = prime * result + _geoHeightAndNSWE;
 	    return result;
     }
 
@@ -117,9 +129,8 @@ public class NodeLoc extends AbstractNodeLoc
 		    return false;
 	    if (_y != other._y)
 		    return false;
-	    if (_z != other._z)
+	    if (_geoHeightAndNSWE != other._geoHeightAndNSWE)
 		    return false;
 	    return true;
     }
-
 }

+ 57 - 0
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/geonodes/GeoNode.java

@@ -0,0 +1,57 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.pathfinding.geonodes;
+
+import com.l2jserver.gameserver.pathfinding.AbstractNode;
+import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
+
+/**
+ *
+ * @author -Nemesiss-
+ */
+public class GeoNode extends AbstractNode
+{
+	private final int _neighborsIdx;
+	private short _cost;
+	private GeoNode[] _neighbors;
+
+	public GeoNode(AbstractNodeLoc Loc, int Neighbors_idx)
+	{
+		super(Loc);
+		_neighborsIdx = Neighbors_idx;
+	}
+
+	public short getCost()
+	{
+		return _cost;
+	}
+
+	public void setCost(int cost)
+	{
+		_cost = (short)cost;
+	}
+
+	public GeoNode[] getNeighbors()
+	{
+		return _neighbors;
+	}
+
+	public void attachNeighbors()
+	{
+		if(getLoc() == null) _neighbors = null;
+		else _neighbors = GeoPathFinding.getInstance().readNeighbors(this, _neighborsIdx);
+	}
+	
+}

+ 113 - 16
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/geonodes/GeoPathFinding.java

@@ -23,6 +23,7 @@ import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -32,9 +33,10 @@ import com.l2jserver.Config;
 import com.l2jserver.gameserver.GeoData;
 import com.l2jserver.gameserver.model.L2World;
 import com.l2jserver.gameserver.model.Location;
+import com.l2jserver.gameserver.pathfinding.AbstractNode;
 import com.l2jserver.gameserver.pathfinding.AbstractNodeLoc;
-import com.l2jserver.gameserver.pathfinding.Node;
 import com.l2jserver.gameserver.pathfinding.PathFinding;
+import com.l2jserver.gameserver.pathfinding.utils.FastNodeList;
 
 import javolution.util.FastList;
 import javolution.util.FastMap;
@@ -67,7 +69,7 @@ public class GeoPathFinding extends PathFinding
 	 * @see com.l2jserver.gameserver.pathfinding.PathFinding#FindPath(int, int, short, int, int, short)
 	 */
 	@Override
-	public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId)
+	public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId, boolean playable)
 	{
 		int gx = (x - L2World.MAP_MIN_X) >> 4;
 		int gy = (y - L2World.MAP_MIN_Y) >> 4;
@@ -76,8 +78,8 @@ public class GeoPathFinding extends PathFinding
 		int gty = (ty - L2World.MAP_MIN_Y) >> 4;
 		short gtz = (short) tz;
 		
-		Node start = readNode(gx, gy, gz);
-		Node end = readNode(gtx, gty, gtz);
+		GeoNode start = readNode(gx, gy, gz);
+		GeoNode end = readNode(gtx, gty, gtz);
 		if (start == null || end == null)
 			return null;
 		if (Math.abs(start.getLoc().getZ() - z) > 55)
@@ -101,11 +103,106 @@ public class GeoPathFinding extends PathFinding
 		return searchByClosest2(start, end);
 	}
 	
-	/**
-	 * @see com.l2jserver.gameserver.pathfinding.PathFinding#ReadNeighbors(short, short)
-	 */
-	@Override
-	public Node[] readNeighbors(Node n, int idx)
+	public List<AbstractNodeLoc> searchByClosest2(GeoNode start, GeoNode end)
+	{
+		// Always continues checking from the closest to target non-blocked
+		// node from to_visit list. There's extra length in path if needed
+		// to go backwards/sideways but when moving generally forwards, this is extra fast
+		// and accurate. And can reach insane distances (try it with 800 nodes..).
+		// Minimum required node count would be around 300-400.
+		// Generally returns a bit (only a bit) more intelligent looking routes than
+		// the basic version. Not a true distance image (which would increase CPU
+		// load) level of intelligence though.
+		
+		// List of Visited Nodes
+		FastNodeList visited = new FastNodeList(550);
+		
+		// List of Nodes to Visit
+		LinkedList<GeoNode> to_visit = new LinkedList<GeoNode>();
+		to_visit.add(start);
+		int targetX = end.getLoc().getNodeX();
+		int targetY = end.getLoc().getNodeY();
+
+		int dx, dy;
+		boolean added;
+		int i = 0;
+		while (i < 550)
+		{
+			GeoNode node;
+			try
+			{
+				node = to_visit.removeFirst();
+			}
+			catch (Exception e)
+			{
+				// No Path found
+				return null;
+			}
+			if (node.equals(end)) //path found!
+				return constructPath2(node);
+			else
+			{
+				i++;
+				visited.add(node);
+				node.attachNeighbors();
+				GeoNode[] neighbors = node.getNeighbors();
+				if (neighbors == null)
+					continue;
+				for (GeoNode n : neighbors)
+				{
+					if (!visited.containsRev(n) && !to_visit.contains(n))
+					{
+						added = false;
+						n.setParent(node);
+						dx = targetX - n.getLoc().getNodeX();
+						dy = targetY - n.getLoc().getNodeY();
+						n.setCost(dx * dx + dy * dy);
+						for (int index = 0; index < to_visit.size(); index++)
+						{
+							// supposed to find it quite early..
+							if (to_visit.get(index).getCost() > n.getCost())
+							{
+								to_visit.add(index, n);
+								added = true;
+								break;
+							}
+						}
+						if (!added)
+							to_visit.addLast(n);
+					}
+				}
+			}
+		}
+		//No Path found
+		return null;
+	}
+
+	public List<AbstractNodeLoc> constructPath2(AbstractNode node)
+	{
+		LinkedList<AbstractNodeLoc> path = new LinkedList<AbstractNodeLoc>();
+		int previousDirectionX = -1000;
+		int previousDirectionY = -1000;
+		int directionX;
+		int directionY;
+
+		while (node.getParent() != null)
+		{
+			// only add a new route point if moving direction changes
+			directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
+			directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
+
+			if (directionX != previousDirectionX || directionY != previousDirectionY)
+			{
+				previousDirectionX = directionX;
+				previousDirectionY = directionY;
+				path.addFirst(node.getLoc());
+			}
+			node = node.getParent();
+		}
+		return path;
+	}
+
+	public GeoNode[] readNeighbors(GeoNode n, int idx)
 	{
 		int node_x = n.getLoc().getNodeX();
 		int node_y = n.getLoc().getNodeY();
@@ -114,8 +211,8 @@ public class GeoPathFinding extends PathFinding
 		short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y));
 		ByteBuffer pn = _pathNodes.get(regoffset);
 		
-		List<Node> Neighbors = new FastList<Node>(8);
-		Node newNode;
+		List<AbstractNode> Neighbors = new FastList<AbstractNode>(8);
+		GeoNode newNode;
 		short new_node_x, new_node_y;
 		
 		//Region for sure will change, we must read from correct file
@@ -199,13 +296,13 @@ public class GeoPathFinding extends PathFinding
 			if (newNode != null)
 				Neighbors.add(newNode);
 		}
-		Node[] result = new Node[Neighbors.size()];
+		GeoNode[] result = new GeoNode[Neighbors.size()];
 		return Neighbors.toArray(result);
 	}
 	
 	//Private
 	
-	private Node readNode(short node_x, short node_y, byte layer)
+	private GeoNode readNode(short node_x, short node_y, byte layer)
 	{
 		short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y));
 		if (!this.pathNodesExist(regoffset))
@@ -223,10 +320,10 @@ public class GeoPathFinding extends PathFinding
 		}
 		short node_z = pn.getShort(idx);
 		idx += 2;
-		return new Node(new GeoNodeLoc(node_x, node_y, node_z), idx);
+		return new GeoNode(new GeoNodeLoc(node_x, node_y, node_z), idx);
 	}
 	
-	private Node readNode(int gx, int gy, short z)
+	private GeoNode readNode(int gx, int gy, short z)
 	{
 		short node_x = getNodePos(gx);
 		short node_y = getNodePos(gy);
@@ -252,7 +349,7 @@ public class GeoPathFinding extends PathFinding
 			idx += 10; //short + 8 byte
 			nodes--;
 		}
-		return new Node(new GeoNodeLoc(node_x, node_y, last_z), idx2);
+		return new GeoNode(new GeoNodeLoc(node_x, node_y, last_z), idx2);
 	}
 	
 	private GeoPathFinding()

+ 9 - 9
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/utils/BinaryNodeHeap.java

@@ -14,7 +14,7 @@
  */
 package com.l2jserver.gameserver.pathfinding.utils;
 
-import com.l2jserver.gameserver.pathfinding.Node;
+import com.l2jserver.gameserver.pathfinding.geonodes.GeoNode;
 
 /**
  *
@@ -22,15 +22,15 @@ import com.l2jserver.gameserver.pathfinding.Node;
  */
 public class BinaryNodeHeap
 {
-	private final Node[] _list;
+	private final GeoNode[] _list;
 	private int _size;
 
 	public BinaryNodeHeap(int size)
 	{
-		_list = new Node[size+1];
+		_list = new GeoNode[size+1];
 		_size = 0;
 	}
-	public void add(Node n) {
+	public void add(GeoNode n) {
 		_size++;
 		int pos  = _size;
 		_list[pos] = n;
@@ -39,7 +39,7 @@ public class BinaryNodeHeap
 			int p2 = pos/2;
 			if (_list[pos].getCost() <= _list[p2].getCost())
 			{
-				Node temp = _list[p2];
+				GeoNode temp = _list[p2];
 				_list[p2] = _list[pos];
 				_list[pos] = temp;
 		        pos = p2;
@@ -48,16 +48,16 @@ public class BinaryNodeHeap
 				break;
 		}
 	}
-	public Node removeFirst()
+	public GeoNode removeFirst()
 	{
-		Node first = _list[1];
+		GeoNode first = _list[1];
 		_list[1] = _list[_size];
 		_list[_size] = null;
 		_size--;
 		int pos = 1;
 		int cpos;
 		int dblcpos;
-		Node temp;
+		GeoNode temp;
 		while(true)
 		{
 			cpos = pos;
@@ -86,7 +86,7 @@ public class BinaryNodeHeap
 		}
 		return first;
 	}
-	public boolean contains(Node n)
+	public boolean contains(GeoNode n)
 	{
 		if (_size == 0)
 			return false;

+ 0 - 62
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/utils/CellNodeMap.java

@@ -1,62 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.pathfinding.utils;
-
-import java.util.ArrayList;
-import java.util.Map;
-
-import com.l2jserver.gameserver.pathfinding.Node;
-
-import javolution.util.FastMap;
-
-/**
- *
- * @author Sami
- */
-public class CellNodeMap
-{
-	private Map<Integer, ArrayList<Node>> _cellIndex;
-	
-	public CellNodeMap()
-	{
-		_cellIndex = new FastMap<Integer, ArrayList<Node>>();
-	}
-	
-	public void add(Node n)
-	{
-		if (_cellIndex.containsKey(n.getLoc().getY()))
-		{
-			_cellIndex.get(n.getLoc().getY()).add(n);
-		}
-		else
-		{
-			ArrayList<Node> array = new ArrayList<Node>(5);
-			array.add(n);
-			_cellIndex.put(n.getLoc().getY(), array);
-		}
-	}
-	public boolean contains(Node n)
-	{
-		ArrayList<Node> array = _cellIndex.get(n.getLoc().getY());
-		if (array == null) 
-		{
-			return false;
-		}
-		for (Node node : array)
-			if(node.equals(n))
-				return true;
-		return false;
-	}
-}

+ 6 - 6
L2_GameServer/java/com/l2jserver/gameserver/pathfinding/utils/FastNodeList.java

@@ -14,7 +14,7 @@
  */
 package com.l2jserver.gameserver.pathfinding.utils;
 
-import com.l2jserver.gameserver.pathfinding.Node;
+import com.l2jserver.gameserver.pathfinding.AbstractNode;
 
 /**
  *
@@ -22,25 +22,25 @@ import com.l2jserver.gameserver.pathfinding.Node;
  */
 public class FastNodeList
 {
-	private Node[] _list;
+	private AbstractNode[] _list;
 	private int _size;
 
 	public FastNodeList(int size)
 	{
-		_list = new Node[size];
+		_list = new AbstractNode[size];
 	}
-	public void add(Node n)
+	public void add(AbstractNode n)
 	{
 		_list[_size++] = n;
 	}
-	public boolean contains(Node n)
+	public boolean contains(AbstractNode n)
 	{
 		for (int i =0; i < _size; i++)
 			if(_list[i].equals(n))
 				return true;
 		return false;
 	}
-	public boolean containsRev(Node n)
+	public boolean containsRev(AbstractNode n)
 	{
 		for (int i=_size-1; i >= 0; i--)
 			if(_list[i].equals(n))

+ 31 - 0
L2_GameServer/java/config/General.properties

@@ -331,6 +331,37 @@ GeoData = 0
 # Default: False
 CellPathFinding = False
 
+# Pathfinding array buffers configuration
+PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
+
+# Weight for nodes without obstacles far from walls
+LowWeight = 0.5
+
+# Weight for nodes near walls
+MediumWeight = 2
+
+# Weight for nodes with obstacles
+HighWeight = 3
+
+# Angle paths will be more "smart", but in cost of higher CPU utilization
+AdvancedDiagonalStrategy = True
+
+# Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True
+# Default: LowWeight * sqrt(2) 
+DiagonalWeight = 0.707
+
+# Maximum number of LOS postfilter passes, 0 will disable postfilter.
+# Default: 3
+MaxPostfilterPasses = 3
+
+# Path debug function.
+# Nodes known to pathfinder will be displayed as adena, constructed path as antidots.
+# Number of the items show node cost * 10
+# Potions display path after first stage filter
+# Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter
+# This function FOR DEBUG PURPOSES ONLY, never use it on the live server !
+DebugPath = False
+
 # True = Loads GeoData buffer's content into physical memory.
 # False = Does not necessarily imply that the GeoData buffer's content is not resident in physical memory.
 # Default: True