2
0
Эх сурвалжийг харах

Geodata:
1) Pathfinding new option added.
2) Client validate position packet handling changed and therefore changes to client-server coordinate synchronization.

New pathfinding explained:
Previously we've been using pathnode files which are basically
NSWE values for a single block. We've had problems with this
approximation and their accuracy, but they're good considering
performance. However, the system should be improved before it's of
real use inside castles and other complex locations.
This option does not disappear with this patch.

Now we add cell level pathfinding (thanks l2jfree for bringing
up the idea after a long time) to calculate things at a more
accurate level: straight from geodata files and their NSWE values.
Basically this means 8-64x more cpu load and this option hasn't
been considered before. However with certain approximations the
performance isn't that bad (to be measured) and the routes are
naturally much better. I've used an approximation where calculation
moves directly towards target and a path of 3 cells wide needs
to be open.
Instead of previous max ~500 nodes to check the max value now
is 3500 (and it's about that much heavier maybe). Maybe it could
be little less.
Uses less server memory because of no pathnode files used.

See general.properties for new settings: to activate this system
you need geodata = 2, cell pathfinding enabled and coordinate sync = 2.

Part 2: rewritten client validate position packet handling. Basically client
should not set server side coordinates but this has long been the case
since we didn't have geodata. Now with geodata enabled server will
only send updates to the client if it wonders too far from the
"real" coordinates. I also tried to keep the old -1 and 1 coord
sync option as they were.
This part intends to fix some/many situations where mob cannot be seen because
geodata Z and client Z are too far from each other.
Also less network load since your location isn't broadcasted to
party EVERY time, only when you move a certain distance from previous
broadcast loc.

This has been tested only locally by far.

Sami 16 жил өмнө
parent
commit
66739fee16

+ 19 - 14
L2_GameServer/java/config/General.properties

@@ -224,14 +224,6 @@ MaxNPCAnimation = 20
 MinMonsterAnimation = 5
 MaxMonsterAnimation = 20
 
-# This is setting of experimental Client <--> Server Player coordinates synchronization,
-#   0 - no synchronization at all
-#   1 - partial synchronization Client --> Server only * using this option it is difficult for players to bypass obstacles
-#   2 - partial synchronization Server --> Client only
-#   3 - full synchronization Client <--> Server
-#  -1 - Will synchronize Z only. Suggested with geodata.
-CoordSynchronize = -1
-
 # Knownlist (the list of things a character sees) update method. Default is currently
 # time based updating, which also makes it possible to use config options for guards 
 # to see moving monsters. Alternatively move based update can be used
@@ -244,10 +236,6 @@ MoveBasedKnownlist = False
 #          may not update knownlists properly, the default value is chosen experimentally
 KnownListUpdateInterval = 1250
 
-#============================================================#
-#                         Geodata                            #
-#============================================================#
-
 #Grid options: Grids can now turn themselves on and off.  This also affects
 #the loading and processing of all AI tasks and (in the future) geodata 
 #within this grid.
@@ -262,21 +250,38 @@ GridsAlwaysOn = False
 GridNeighborTurnOnTime = 1
 GridNeighborTurnOffTime = 90
 
+#============================================================#
+#                         Geodata                            #
+#============================================================#
+
 # GeoData options: (no recommendations, for each his own) 
 # 0 = GeoData and PathFinding OFF (default)
 # 1 = GeoData is used to check Line Of Sight (LOS) targeting and 
 #     L2Playable movement. You need to download files for data/geodata folder.
 #     Monsters can pass walls but not aggro (no line of sight) through them.
 # 2 = Full GeoData enabled. Includes PathFinding (requires also /data/pathnode 
-#     files) and all character moves go through geodata checks (if a mob 
-#     passes a wall, pathfinding didn't find a route but we allow attack).
+#     files if CellPathFinding not enabled) and all character moves go through 
+#     geodata checks (if a mob passes a wall, pathfinding didn't find a route 
+#     but we allow attack and returning home).
 #     Recommended server memory minimum 2 GB, rather 3 GB.
 GeoData = 0
+# Cell-level pathfinding, produces more accurate routes but is (maybe 10x) 
+# heavier to calculate. Recommended for small servers at least. If False,
+# pathnode files are used. Uses a max nr of nodes in calculation which can
+# be adjusted in the algorithm if it needs to be faster.
+CellPathFinding = 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.
 ForceGeodata = True
 
+# This is setting of Client <--> Server Player coordinates synchronization,
+#  -1 - Will synchronize only Z from Client --> Server. Default when no geodata.
+#   1 - Synchronization Client --> Server only. Using this option (without geodata) it is more difficult for players to bypass obstacles
+#   2 - Intended for geodata (at least when cell-level pathfinding, otherwise can try -1 also)! 
+#       Server sends validation packet if client goes too far from server calculated coordinates.
+CoordSynchronize = -1
+
 #============================================================#
 #                         Features                           #
 #============================================================#

+ 2 - 0
L2_GameServer/java/net/sf/l2j/Config.java

@@ -377,6 +377,7 @@ public final class Config
     public static int		GRID_NEIGHBOR_TURNON_TIME;
     public static int		GRID_NEIGHBOR_TURNOFF_TIME;
     public static int		GEODATA;
+    public static boolean	GEODATA_CELLFINDING;
     public static boolean	FORCE_GEODATA;
     public static boolean 	MOVE_BASED_KNOWNLIST;
     public static long		KNOWNLIST_UPDATE_INTERVAL;
@@ -1441,6 +1442,7 @@ public final class Config
 	                GRID_NEIGHBOR_TURNON_TIME					= Integer.parseInt(General.getProperty("GridNeighborTurnOnTime", "1"));
 	                GRID_NEIGHBOR_TURNOFF_TIME					= Integer.parseInt(General.getProperty("GridNeighborTurnOffTime", "90"));
 	                GEODATA										= Integer.parseInt(General.getProperty("GeoData", "0"));
+	                GEODATA_CELLFINDING							= Boolean.parseBoolean(General.getProperty("CellPathFinding", "False"));
 	                FORCE_GEODATA								= Boolean.parseBoolean(General.getProperty("ForceGeoData", "True"));
 	                MOVE_BASED_KNOWNLIST						= Boolean.parseBoolean(General.getProperty("MoveBasedKnownlist", "False"));
 	                KNOWNLIST_UPDATE_INTERVAL					= Long.parseLong(General.getProperty("KnownListUpdateInterval", "1250"));

+ 2 - 2
L2_GameServer/java/net/sf/l2j/gameserver/GameServer.java

@@ -101,7 +101,7 @@ import net.sf.l2j.gameserver.model.entity.Hero;
 import net.sf.l2j.gameserver.model.entity.TvTManager;
 import net.sf.l2j.gameserver.network.L2GameClient;
 import net.sf.l2j.gameserver.network.L2GamePacketHandler;
-import net.sf.l2j.gameserver.pathfinding.geonodes.GeoPathFinding;
+import net.sf.l2j.gameserver.pathfinding.PathFinding;
 import net.sf.l2j.gameserver.script.faenor.FaenorScriptEngine;
 import net.sf.l2j.gameserver.scripting.CompiledScriptCache;
 import net.sf.l2j.gameserver.scripting.L2ScriptEngineManager;
@@ -277,7 +277,7 @@ public class GameServer
 		
 		GeoData.getInstance();
 		if (Config.GEODATA == 2)
-			GeoPathFinding.getInstance();
+			PathFinding.getInstance();
 		
 		CastleManager.getInstance();
 		SiegeManager.getInstance();

+ 11 - 0
L2_GameServer/java/net/sf/l2j/gameserver/GeoData.java

@@ -20,6 +20,7 @@ import net.sf.l2j.Config;
 import net.sf.l2j.gameserver.model.L2Object;
 import net.sf.l2j.gameserver.model.Location;
 import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+import net.sf.l2j.gameserver.pathfinding.Node;
 import net.sf.l2j.util.Point3D;
 
 /**
@@ -145,6 +146,10 @@ public class GeoData
     {
         return new Location(tx,ty,tz);
     }
+    public boolean canMoveFromToTarget(int x, int y, int z, int tx, int ty, int tz)
+    {
+    	return true;
+    }
     /**
      * @param gm
      * @param comment
@@ -167,4 +172,10 @@ public class GeoData
     {
     	return false;
     }
+    
+    public Node[] getNeighbors(Node n)
+    {
+    	return null;
+    }
+    
 }

+ 171 - 0
L2_GameServer/java/net/sf/l2j/gameserver/GeoEngine.java

@@ -26,10 +26,12 @@ 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;
 
+import javolution.util.FastList;
 import javolution.util.FastMap;
 import net.sf.l2j.Config;
 import net.sf.l2j.gameserver.datatables.DoorTable;
@@ -39,6 +41,8 @@ import net.sf.l2j.gameserver.model.Location;
 import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
 import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
 import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance;
+import net.sf.l2j.gameserver.pathfinding.Node;
+import net.sf.l2j.gameserver.pathfinding.cellnodes.CellPathFinding;
 import net.sf.l2j.util.Point3D;
 
 /**
@@ -173,6 +177,12 @@ public class GeoEngine extends GeoData
     {
         return nGetNSWE((x - L2World.MAP_MIN_X) >> 4,(y - L2World.MAP_MIN_Y) >> 4,z);
     }
+    @Override
+    public boolean canMoveFromToTarget(int x, int y, int z, int tx, int ty, int tz)
+    {
+    	Location destiny = moveCheck(x,y,z,tx,ty,tz);
+    	return (destiny.getX() == tx && destiny.getY() == ty && destiny.getZ() == tz);
+    }
     /**
      * @see net.sf.l2j.gameserver.GeoData#moveCheck(int, int, int, int, int, int)
      */
@@ -1318,6 +1328,167 @@ public class GeoEngine extends GeoData
 	    }
 	    return NSWE;
 	}
+	
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 * @return NSWE: 0-15
+	 */
+	@Override
+	public Node[] getNeighbors(Node n)
+	{
+		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;
+		//Geodata without index - it is just empty so index can be calculated on the fly
+		if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
+		//Get Index for current block of current region geodata
+		else index = _geodataIndex.get(region).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;
+		}
+		//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);
+			}
+	    }
+	    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);
+			}
+	    }
+	    else//multilevel
+	    {
+	    	cellX = getCell(x);
+			cellY = getCell(y);
+	        int offset = (cellX << 3) + cellY;
+	        while(offset > 0)
+	        {
+	        	byte lc = geo.get(index);
+	            index += (lc << 1) + 1;
+	            offset--;
+	        }
+	        byte layers = geo.get(index);
+	        index++;
+	        short height=-1;
+	        if(layers <= 0 || layers > 125)
+	        {
+	        	_log.warning("Broken geofile (case5), region: "+region+" - invalid layer count: "+layers+" at: "+x+" "+y);
+	            return null;
+	        }
+	        short tempz = Short.MIN_VALUE;
+	        while(layers > 0)
+	        {
+	            height = geo.getShort(index);
+	            height = (short)(height&0x0fff0);
+				height = (short)(height >> 1); //height / 2
+
+	            if ((z-tempz)*(z-tempz) > (z-height)*(z-height))
+	            {
+	                tempz = height;
+	                NSWE = geo.get(index);
+	                NSWE = (short)(NSWE&0x0F);
+	            }
+	            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);
+			}	    
+		}
+		Node[] result = new Node[Neighbors.size()];
+		return Neighbors.toArray(result);
+	}
 
 	/**
 	 * @param NSWE

+ 2 - 7
L2_GameServer/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminPathNode.java

@@ -18,10 +18,9 @@ import java.util.List;
 
 import net.sf.l2j.Config;
 import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
-import net.sf.l2j.gameserver.model.L2World;
 import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
 import net.sf.l2j.gameserver.pathfinding.AbstractNodeLoc;
-import net.sf.l2j.gameserver.pathfinding.geonodes.GeoPathFinding;
+import net.sf.l2j.gameserver.pathfinding.PathFinding;
 
 public class AdminPathNode implements IAdminCommandHandler
 {
@@ -61,11 +60,7 @@ public class AdminPathNode implements IAdminCommandHandler
 			}
 			if (activeChar.getTarget() != null)
 			{
-				int gx = (activeChar.getX() - L2World.MAP_MIN_X) >> 4;
-				int gy = (activeChar.getY() - L2World.MAP_MIN_Y) >> 4;
-				int gtx = (activeChar.getTarget().getX() - L2World.MAP_MIN_X) >> 4;
-				int gty = (activeChar.getTarget().getY() - L2World.MAP_MIN_Y) >> 4;
-				List<AbstractNodeLoc> path = GeoPathFinding.getInstance().findPath(gx, gy, (short) activeChar.getZ(), gtx, gty, (short) activeChar.getTarget().getZ());
+				List<AbstractNodeLoc> path = PathFinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ());
 				if (path == null)
 				{
 					activeChar.sendMessage("No Route!");

+ 2 - 2
L2_GameServer/java/net/sf/l2j/gameserver/model/L2Character.java

@@ -95,7 +95,7 @@ import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
 import net.sf.l2j.gameserver.network.serverpackets.TeleportToLocation;
 import net.sf.l2j.gameserver.network.serverpackets.FlyToLocation.FlyType;
 import net.sf.l2j.gameserver.pathfinding.AbstractNodeLoc;
-import net.sf.l2j.gameserver.pathfinding.geonodes.GeoPathFinding;
+import net.sf.l2j.gameserver.pathfinding.PathFinding;
 import net.sf.l2j.gameserver.skills.Calculator;
 import net.sf.l2j.gameserver.skills.Formulas;
 import net.sf.l2j.gameserver.skills.Stats;
@@ -4240,7 +4240,7 @@ public abstract class L2Character extends L2Object
 				if(this instanceof L2PlayableInstance || this.isInCombat())
 				{
 		
-					m.geoPath = GeoPathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ);
+					m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ);
                 	if (m.geoPath == null || m.geoPath.size() < 2) // No path found
                 	{
                 		// Even though there's no path found (remember geonodes aren't perfect), 

+ 14 - 17
L2_GameServer/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java

@@ -429,18 +429,20 @@ public final class L2PcInstance extends L2PlayableInstance
 	private boolean _observerMode = false;
 
 	/** Stored from last ValidatePosition **/
-	private Point3D _lastClientPosition = new Point3D(0, 0, 0);
 	private Point3D _lastServerPosition = new Point3D(0, 0, 0);
 
-	/** The number of recommandation obtained by the L2PcInstance */
+	/** Previous coordinate sent to party in ValidatePosition **/
+	private Point3D _lastPartyPosition = new Point3D(0, 0, 0);
+	
+	/** The number of recommendation obtained by the L2PcInstance */
 	private int _recomHave; // how much I was recommended by others
 
-	/** The number of recommandation that the L2PcInstance can give */
-	private int _recomLeft; // how many recomendations I can give to others
+	/** The number of recommendation that the L2PcInstance can give */
+	private int _recomLeft; // how many recommendations I can give to others
 
-	/** Date when recom points were updated last time */
+	/** Date when recommendation points were updated last time */
 	private long _lastRecomUpdate;
-	/** List with the recomendations that I've give */
+	/** List with the recommendations that I've give */
 	private List<Integer> _recomChars = new FastList<Integer>();
 
 	/** The random number of the L2PcInstance */
@@ -10034,21 +10036,16 @@ public final class L2PcInstance extends L2PlayableInstance
 		return (distFraction > 1);
 	}
 
-	public void setLastClientPosition(int x, int y, int z)
-	{
-		_lastClientPosition.setXYZ(x,y,z);
-	}
-
-	public boolean checkLastClientPosition(int x, int y, int z)
+	public void setLastPartyPosition(int x, int y, int z)
 	{
-		return _lastClientPosition.equals(x,y,z);
+		_lastPartyPosition.setXYZ(x,y,z);
 	}
 
-	public int getLastClientDistance(int x, int y, int z)
+	public int getLastPartyPositionDistance(int x, int y, int z)
 	{
-		double dx = (x - _lastClientPosition.getX());
-		double dy = (y - _lastClientPosition.getY());
-		double dz = (z - _lastClientPosition.getZ());
+		double dx = (x - _lastPartyPosition.getX());
+		double dy = (y - _lastPartyPosition.getY());
+		double dz = (z - _lastPartyPosition.getZ());
 
 		return (int)Math.sqrt(dx*dx + dy*dy + dz*dz);
 	}

+ 73 - 124
L2_GameServer/java/net/sf/l2j/gameserver/network/clientpackets/ValidatePosition.java

@@ -17,11 +17,9 @@ package net.sf.l2j.gameserver.network.clientpackets;
 import java.util.logging.Logger;
 
 import net.sf.l2j.Config;
-import net.sf.l2j.gameserver.GeoData;
 import net.sf.l2j.gameserver.TaskPriority;
 import net.sf.l2j.gameserver.Universe;
 import net.sf.l2j.gameserver.geoeditorcon.GeoEditorListener;
-import net.sf.l2j.gameserver.model.L2Character;
 import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
 import net.sf.l2j.gameserver.network.serverpackets.PartyMemberPosition;
 import net.sf.l2j.gameserver.network.serverpackets.ValidateLocation;
@@ -37,7 +35,7 @@ public class ValidatePosition extends L2GameClientPacket
     private static Logger _log = Logger.getLogger(ValidatePosition.class.getName());
     private static final String _C__48_VALIDATEPOSITION = "[C] 48 ValidatePosition";
 
-    /** urgent messages, execute immediatly */
+    /** urgent messages, execute immediately */
     public TaskPriority getPriority() { return TaskPriority.PR_HIGH; }
 
     private int _x;
@@ -64,131 +62,86 @@ public class ValidatePosition extends L2GameClientPacket
         L2PcInstance activeChar = getClient().getActiveChar();
         if (activeChar == null || activeChar.isTeleporting()) return;
 
-        if (Config.GEODATA > 0 
-        		&& (activeChar.isInOlympiadMode() || activeChar.isInsideZone(L2Character.ZONE_SIEGE))
-        		&& !activeChar.isFlying()
-        		&& GeoData.getInstance().hasGeo(_x, _y))
+        activeChar.setClientX(_x);
+        activeChar.setClientY(_y);
+        activeChar.setClientZ(_z);
+        activeChar.setClientHeading(_heading); // No real need to validate heading.
+        int realX = activeChar.getX();
+        int realY = activeChar.getY();
+        int realZ = activeChar.getZ();
+        activeChar.setLastServerPosition(realX, realY, realZ);
+        
+        if(activeChar.getParty() != null && activeChar.getLastPartyPositionDistance(_x, _y, _z) > 150)
         {
-        	// check Z coordinate sent by client
-        	short geoHeight = GeoData.getInstance().getSpawnHeight(_x, _y, activeChar.getZ()-30, activeChar.getZ()+30, activeChar.getObjectId());
-        	if (Math.abs(geoHeight - _z) > 15)
-        	{
-        		// causes mild flashing in the middle of a drop from a castle wall for example
-        		_z = geoHeight;
-        		// System.out.println("Spawnheight validation diff="+Math.abs(geoHeight - _z));
-        	}
+            activeChar.setLastPartyPosition(_x, _y, _z);
+        	activeChar.getParty().broadcastToPartyMembers(activeChar,new PartyMemberPosition(activeChar));
         }
-        if (Config.COORD_SYNCHRONIZE > 0)
+        
+        double dx = _x - realX;
+        double dy = _y - realY;
+        double dz = Math.abs(_z - realZ);
+        double diffSq = (dx*dx + dy*dy);
+        
+        if (Config.DEVELOPER) {
+            _log.fine("client pos: "+ _x + " "+ _y + " "+ _z +" head "+ _heading);
+            _log.fine("server pos: "+ realX + " "+realY+ " "+realZ +" head "+activeChar.getHeading());
+        }
+        if (Config.ACCEPT_GEOEDITOR_CONN)
+            if (GeoEditorListener.getInstance().getThread() != null  
+            		&& GeoEditorListener.getInstance().getThread().isWorking()  
+            		&& GeoEditorListener.getInstance().getThread().isSend(activeChar))
+            	GeoEditorListener.getInstance().getThread().sendGmPosition(_x,_y,(short)_z);
+        
+        if (Config.ACTIVATE_POSITION_RECORDER 
+        		&& !activeChar.isFlying() 
+        		&& Universe.getInstance().shouldLog(activeChar.getObjectId()))
+            Universe.getInstance().registerHeight(realX, realY, _z);
+       
+        if (activeChar.isFlying())
         {
-            activeChar.setClientX(_x);
-            activeChar.setClientY(_y);
-            activeChar.setClientZ(_z);
-            activeChar.setClientHeading(_heading);
-            int realX = activeChar.getX();
-            int realY = activeChar.getY();
-            // int realZ = activeChar.getZ();
-
-            double dx = _x - realX;
-            double dy = _y - realY;
-            double diffSq = (dx*dx + dy*dy);
-
-            /*
-            if (Config.DEVELOPER && false)
-            {
-            	int dxs = (_x - activeChar._lastClientPosition.x);
-            	int dys = (_y - activeChar._lastClientPosition.y);
-            	int dist = (int)Math.sqrt(dxs*dxs + dys*dys);
-            	int heading = dist > 0 ? (int)(Math.atan2(-dys/dist, -dxs/dist) * 10430.378350470452724949566316381) + 32768 : 0;
-                _log.info("Client X:" + _x + ", Y:" + _y + ", Z:" + _z + ", H:" + _heading + ", Dist:" + activeChar.getLastClientDistance(_x, _y, _z));
-                _log.info("Server X:" + realX + ", Y:" + realY + ", Z:" + realZ + ", H:" + activeChar.getHeading() + ", Dist:" + activeChar.getLastServerDistance(realX, realY, realZ));
-            }
-        	*/
-
-            if (diffSq > 0 && diffSq < 250000) // if too large, messes observation
-            {
-                if ((Config.COORD_SYNCHRONIZE & 1) == 1
-                    && (!activeChar.isMoving() // character is not moving, take coordinates from client
-                    || !activeChar.validateMovementHeading(_heading))) // Heading changed on client = possible obstacle
-                {
-                    if (Config.DEVELOPER)
-                        _log.info(activeChar.getName() + ": Synchronizing position Client --> Server" + (activeChar.isMoving()?" (collision)":" (stay sync)"));
-                    
-                    if (diffSq < 2500) // 50*50 - attack won't work fluently if even small differences are corrected
-                    	activeChar.setXYZ(realX, realY, _z);
-                    else
-                    	activeChar.setXYZ(_x, _y, _z);
-                    activeChar.setHeading(_heading);
-                }
-                else if ((Config.COORD_SYNCHRONIZE & 2) == 2
-                        && diffSq > 10000) // more than can be considered to be result of latency
-                {
-                    if (Config.DEVELOPER)
-                        _log.info(activeChar.getName() + ": Synchronizing position Server --> Client");
-                    if (activeChar.isInBoat())
-                    {
-                        sendPacket(new ValidateLocationInVehicle(activeChar));
-                    }
-                    else
-                    {
-                    	activeChar.sendPacket(new ValidateLocation(activeChar));
-                    }
-                }
-            }
-            activeChar.setLastClientPosition(_x, _y, _z);
-            activeChar.setLastServerPosition(activeChar.getX(), activeChar.getY(), activeChar.getZ());
+        	activeChar.setXYZ(_x, _y, _z);
         }
-        else if (Config.COORD_SYNCHRONIZE == -1)
+        else if (diffSq < 250000) // if too large, messes observation
         {
-            activeChar.setClientX(_x);
-            activeChar.setClientY(_y);
-            activeChar.setClientZ(_z);
-            activeChar.setClientHeading(_heading); // No real need to validate heading.
-            int realX = activeChar.getX();
-            int realY = activeChar.getY();
-            int realZ = activeChar.getZ();
-
-            double dx = _x - realX;
-            double dy = _y - realY;
-            double diffSq = (dx*dx + dy*dy);
-            if (diffSq < 250000)
-                activeChar.setXYZ(realX,realY,_z);
-
-            //TODO: do we need to validate?
-            /*double dx = (_x - realX);
-             double dy = (_y - realY);
-             double dist = Math.sqrt(dx*dx + dy*dy);
-             if ((dist < 500)&&(dist > 2)) //check it wasnt teleportation, and char isn't there yet
-             activeChar.sendPacket(new CharMoveToLocation(activeChar));*/
-
-            if (Config.DEBUG) {
-                _log.fine("client pos: "+ _x + " "+ _y + " "+ _z +" head "+ _heading);
-                _log.fine("server pos: "+ realX + " "+realY+ " "+realZ +" head "+activeChar.getHeading());
+            if (Config.COORD_SYNCHRONIZE == -1) // Only Z coordinate synched to server, mainly used when no geodata
+            {
+            	activeChar.setXYZ(realX,realY,_z);
+            	return;
             }
-
-            if (Config.ACTIVATE_POSITION_RECORDER && !activeChar.isFlying() && Universe.getInstance().shouldLog(activeChar.getObjectId()))
-                Universe.getInstance().registerHeight(realX, realY, _z);
-
-            if (Config.DEVELOPER)
+            if (Config.COORD_SYNCHRONIZE == 1 && !activeChar.isMoving()) // Trusting client coordinates (should not be used with geodata)
             {
-                if (diffSq > 1000000) {
-                    if (Config.DEBUG) _log.fine("client/server dist diff "+ (int)Math.sqrt(diffSq));
-                    if (activeChar.isInBoat())
-                    {
-                        sendPacket(new ValidateLocationInVehicle(activeChar));
-                    }
-                    else
-                    {
-                    	activeChar.sendPacket(new ValidateLocation(activeChar));
-                    }
-                }
+            	// character is not moving, take coordinates from client
+            	if (diffSq < 2500) // 50*50 - attack won't work fluently if even small differences are corrected
+            		activeChar.setXYZ(realX, realY, _z);
+            	else
+            		activeChar.setXYZ(_x, _y, _z);
+            	activeChar.setHeading(_heading);
+            	return;
             }
+        	if (Config.GEODATA > 0 
+        		&& (diffSq > 10000 || dz > 500)) // Sync 2 (or other), 
+        										 // intended for geodata. Only sends a validation packet to client 
+            {								     // when too far from server calculated true coordinate.
+        		// a new geodata check seems unnecessary
+            	//short geoHeight = GeoData.getInstance().getSpawnHeight(realX, realY, realZ-30, realZ+30, activeChar.getObjectId()); 
+            	//if (realZ != geoHeight) System.out.println("realZ:"+realZ+" spawnheight:"+geoHeight);
+            	//activeChar.setXYZ(realX, realY, geoHeight);
+
+            	//System.out.println("validating pos diffsq:"+diffSq+" dz:"+dz);
+            	if (Config.DEVELOPER)
+                     _log.info(activeChar.getName() + ": Synchronizing position Server --> Client");
+            	if (activeChar.isInBoat())
+            	{
+            		sendPacket(new ValidateLocationInVehicle(activeChar));
+            	}
+            	else
+            	{
+            		activeChar.sendPacket(new ValidateLocation(activeChar));
+            	}
+            }
+        	
         }
-		if(activeChar.getParty() != null)
-			activeChar.getParty().broadcastToPartyMembers(activeChar,new PartyMemberPosition(activeChar));
-
-		if (Config.ACCEPT_GEOEDITOR_CONN)
-            if (GeoEditorListener.getInstance().getThread() != null  && GeoEditorListener.getInstance().getThread().isWorking()  && GeoEditorListener.getInstance().getThread().isSend(activeChar))
-            	GeoEditorListener.getInstance().getThread().sendGmPosition(_x,_y,(short)_z);
+		
     }
 
     /* (non-Javadoc)
@@ -200,9 +153,5 @@ public class ValidatePosition extends L2GameClientPacket
         return _C__48_VALIDATEPOSITION;
     }
 
-    @Deprecated
-    public boolean equal(ValidatePosition pos)
-    {
-        return _x == pos._x && _y == pos._y && _z == pos._z && _heading == pos._heading;
-    }
+ 
 }

+ 3 - 2
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/AbstractNodeLoc.java

@@ -23,6 +23,7 @@ public abstract class AbstractNodeLoc
 	public abstract int getX();
 	public abstract int getY();
 	public abstract short getZ();
-	public abstract short getNodeX();
-	public abstract short getNodeY();
+	public abstract void setZ(short z);
+	public abstract int getNodeX();
+	public abstract int getNodeY();
 }

+ 3 - 3
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/Node.java

@@ -43,10 +43,10 @@ public class Node
 		_cost = (short)cost;
 	}
 
-	public void attacheNeighbors()
+	public void attachNeighbors()
 	{
 		if(_loc == null) _neighbors = null;
-		else _neighbors = PathFinding.getInstance().readNeighbors(_loc.getNodeX(),_loc.getNodeY(), _neighborsIdx);
+		else _neighbors = PathFinding.getInstance().readNeighbors(this, _neighborsIdx);
 	}
 
 	public Node[] getNeighbors()
@@ -78,7 +78,7 @@ public class Node
 		if(!(arg0 instanceof Node))
 			return false;
 		Node n = (Node)arg0;
-		//Check if x,y,z are the same
+		
 		return _loc.getX() == n.getLoc().getX() && _loc.getY() == n.getLoc().getY()
 		&& _loc.getZ() == n.getLoc().getZ();
 	}

+ 153 - 13
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/PathFinding.java

@@ -17,7 +17,12 @@ package net.sf.l2j.gameserver.pathfinding;
 import java.util.LinkedList;
 import java.util.List;
 
+import javolution.util.FastList;
+
+import net.sf.l2j.Config;
+import net.sf.l2j.gameserver.GeoData;
 import net.sf.l2j.gameserver.model.L2World;
+import net.sf.l2j.gameserver.pathfinding.cellnodes.CellPathFinding;
 import net.sf.l2j.gameserver.pathfinding.geonodes.GeoPathFinding;
 import net.sf.l2j.gameserver.pathfinding.utils.BinaryNodeHeap;
 import net.sf.l2j.gameserver.pathfinding.utils.FastNodeList;
@@ -34,14 +39,14 @@ public abstract class PathFinding
 	{
 		if (_instance == null)
 		{
-			if (true /*Config.GEODATA_PATHFINDING*/)
+			if (!Config.GEODATA_CELLFINDING)
 			{
-				//Smaler Memory Usage, Higher Cpu Usage (CalculatedOnTheFly)
+				//Higher Memory Usage, Smaller Cpu Usage
 				return GeoPathFinding.getInstance();
 			}
-			else // WORLD_PATHFINDING
+			else // Cell pathfinding, calculated directly from geodata files
 			{
-				//Higher Memoru Usage, Lower Cpu Usage (PreCalculated)
+				return CellPathFinding.getInstance();
 			}
 		}
 		return _instance;
@@ -49,7 +54,7 @@ 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);
-	public abstract Node[] readNeighbors(short node_x,short node_y, int idx);
+	public abstract Node[] readNeighbors(Node n, int idx);
 
 	public List<AbstractNodeLoc> search(Node start, Node end)
 	{
@@ -65,7 +70,7 @@ public abstract class PathFinding
 		to_visit.add(start);
 
 		int i = 0;
-		while (i < 800)//TODO! Add limit to cfg. Worst case max distance is 1810..
+		while (i < 800)
 		{
 			Node node;
 			try
@@ -83,7 +88,7 @@ public abstract class PathFinding
 			{
 				i++;
 				visited.add(node);
-				node.attacheNeighbors();
+				node.attachNeighbors();
 				Node[] neighbors = node.getNeighbors();
 				if (neighbors == null) continue;
 				for (Node n : neighbors)
@@ -101,6 +106,87 @@ public abstract class PathFinding
 	}
 
 	public List<AbstractNodeLoc> searchByClosest(Node start, Node end)
+	{
+		// Note: This is the version for cell-based calculation, maybe 10x 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
+		FastNodeList visited = new FastNodeList(3500);
+
+		// 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 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++;
+			visited.add(node);
+			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 (!visited.containsRev(n) && !to_visit.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*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);
+				}
+			}
+		}
+		//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
@@ -117,8 +203,8 @@ public abstract class PathFinding
 		// List of Nodes to Visit
 		LinkedList<Node> to_visit = new LinkedList<Node>();
 		to_visit.add(start);
-		short targetx = end.getLoc().getNodeX();
-		short targety = end.getLoc().getNodeY();
+		int targetx = end.getLoc().getNodeX();
+		int targety = end.getLoc().getNodeY();
 		int dx, dy;
 		boolean added;
 		int i = 0;
@@ -135,12 +221,12 @@ public abstract class PathFinding
 				return null;
 			}
 			if (node.equals(end)) //path found!
-				return constructPath(node);
+				return constructPath2(node);
 			else
 			{
 				i++;
 				visited.add(node);
-				node.attacheNeighbors();
+				node.attachNeighbors();
 				Node[] neighbors = node.getNeighbors();
 				if (neighbors == null) continue;
 				for (Node n : neighbors)
@@ -171,7 +257,7 @@ public abstract class PathFinding
 		return null;
 	}
 
-
+	
 	public List<AbstractNodeLoc> searchAStar(Node start, Node end)
 	{
 		// Not operational yet?
@@ -204,7 +290,7 @@ public abstract class PathFinding
 			else
 			{
 				visited.add(node);
-				node.attacheNeighbors();
+				node.attachNeighbors();
 				for (Node n : node.getNeighbors())
 				{
 					if (!visited.contains(n) && !to_visit.contains(n))
@@ -223,6 +309,60 @@ public abstract class PathFinding
 	}
 
 	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).getNodeX(), path.get(index).getNodeY(), path.get(index).getZ(), path.get(index+3).getNodeX(), path.get(index+3).getNodeY(), path.get(index+3).getZ()))
+				{
+					//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;

+ 33 - 22
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/worldnodes/WorldPathFinding.java → L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/cellnodes/CellPathFinding.java

@@ -12,34 +12,31 @@
  * 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 net.sf.l2j.gameserver.pathfinding.worldnodes;
+package net.sf.l2j.gameserver.pathfinding.cellnodes;
+
 
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
 import java.util.List;
-import java.util.Map;
 
-import javolution.util.FastMap;
+import net.sf.l2j.gameserver.GeoData;
+import net.sf.l2j.gameserver.model.L2World;
+
 import net.sf.l2j.gameserver.pathfinding.AbstractNodeLoc;
 import net.sf.l2j.gameserver.pathfinding.Node;
 import net.sf.l2j.gameserver.pathfinding.PathFinding;
 
 /**
  *
- * @author -Nemesiss-
+ * @author Sami
  */
-public class WorldPathFinding extends PathFinding
+public class CellPathFinding extends PathFinding
 {
 	//private static Logger _log = Logger.getLogger(WorldPathFinding.class.getName());
-	private static WorldPathFinding _instance;
-    @SuppressWarnings("unused")
-	private static Map<Short, ByteBuffer> _pathNodes = new FastMap<Short, ByteBuffer>();
-	private static Map<Short, IntBuffer> _pathNodesIndex = new FastMap<Short, IntBuffer>();
-
-	public static WorldPathFinding getInstance()
+	private static CellPathFinding _instance;
+  	
+	public static CellPathFinding getInstance()
 	{
 		if (_instance == null)
-			_instance = new WorldPathFinding();
+			_instance = new CellPathFinding();
 		return _instance;
 	}
 
@@ -49,33 +46,47 @@ public class WorldPathFinding extends PathFinding
 	@Override
 	public boolean pathNodesExist(short regionoffset)
 	{
-		return _pathNodesIndex.containsKey(regionoffset);
+		return false;
 	}
 
-    //TODO! [Nemesiss]
 	/**
 	 * @see net.sf.l2j.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)
 	{
-		return null;
+		int gx = (x - L2World.MAP_MIN_X) >> 4;
+		int gy = (y - L2World.MAP_MIN_Y) >> 4;
+		if (!GeoData.getInstance().hasGeo(x, y)) return null;
+		short gz = GeoData.getInstance().getHeight(x, y, z); 
+		int gtx = (tx - L2World.MAP_MIN_X) >> 4;
+		int gty = (ty - L2World.MAP_MIN_Y) >> 4;
+		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);
 	}
 
 	/**
 	 * @see net.sf.l2j.gameserver.pathfinding.PathFinding#ReadNeighbors(short, short)
 	 */
 	@Override
-	public Node[] readNeighbors(short node_x,short node_y, int idx)
+	public Node[] readNeighbors(Node n, int idx)
 	{
-		// TODO Auto-generated method stub
-		return null;
+		return GeoData.getInstance().getNeighbors(n);
 	}
 
+	
 	//Private
 
-	private WorldPathFinding()
+	public Node readNode(int gx, int gy, short z)
+	{
+		return new Node(new NodeLoc(gx,gy,z), 0);
+	}
+	
+	private CellPathFinding()
 	{
-		//TODO! {Nemesiss] Load PathNodes.
+		//
 	}
 }

+ 15 - 9
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/worldnodes/NodeLoc.java → L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/cellnodes/NodeLoc.java

@@ -12,8 +12,8 @@
  * 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 net.sf.l2j.gameserver.pathfinding.worldnodes;
-
+package net.sf.l2j.gameserver.pathfinding.cellnodes;
+import net.sf.l2j.gameserver.model.L2World;
 import net.sf.l2j.gameserver.pathfinding.AbstractNodeLoc;
 
 /**
@@ -24,7 +24,7 @@ public class NodeLoc extends AbstractNodeLoc
 {
 	private final int _x;
 	private final int _y;
-	private final short _z;
+	private short _z;
 
 	public NodeLoc(int x, int y, short z)
 	{
@@ -39,7 +39,7 @@ public class NodeLoc extends AbstractNodeLoc
 	@Override
 	public int getX()
 	{
-		return _x;
+		return (_x << 4) + L2World.MAP_MIN_X;
 	}
 
 	/**
@@ -48,7 +48,7 @@ public class NodeLoc extends AbstractNodeLoc
 	@Override
 	public int getY()
 	{
-		return _y;
+		return (_y << 4) + L2World.MAP_MIN_Y;
 	}
 
 	/**
@@ -60,24 +60,30 @@ public class NodeLoc extends AbstractNodeLoc
 		return _z;
 	}
 
+	@Override
+	public void setZ(short z)
+	{
+		_z = z;
+	}
+	
 	/**
 	 * @see net.sf.l2j.gameserver.pathfinding.AbstractNodeLoc#getNodeX()
 	 */
 	@Override
-	public short getNodeX()
+	public int getNodeX()
 	{
 		// TODO Auto-generated method stub
-		return 0;
+		return _x;
 	}
 
 	/**
 	 * @see net.sf.l2j.gameserver.pathfinding.AbstractNodeLoc#getNodeY()
 	 */
 	@Override
-	public short getNodeY()
+	public int getNodeY()
 	{
 		// TODO Auto-generated method stub
-		return 0;
+		return _y;
 	}
 
 }

+ 8 - 2
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/geonodes/GeoNodeLoc.java

@@ -60,15 +60,21 @@ public class GeoNodeLoc extends AbstractNodeLoc
 	{
 		return _z;
 	}
+	
+	@Override
+	public void setZ(short z)
+	{
+		//
+	}
 
 	@Override
-	public short getNodeX()
+	public int getNodeX()
 	{
 		return _x;
 	}
 
 	@Override
-	public short getNodeY()
+	public int getNodeY()
 	{
 		return _y;
 	}

+ 10 - 6
L2_GameServer/java/net/sf/l2j/gameserver/pathfinding/geonodes/GeoPathFinding.java

@@ -98,15 +98,19 @@ public class GeoPathFinding extends PathFinding
 			return null; //   cannot reach closest...
 
 		//return searchAStar(start, end);
-		return searchByClosest(start, end);
+		return searchByClosest2(start, end);
 	}
 
 	/**
 	 * @see net.sf.l2j.gameserver.pathfinding.PathFinding#ReadNeighbors(short, short)
 	 */
 	@Override
-	public Node[] readNeighbors(short node_x,short node_y, int idx)
+	public Node[] readNeighbors(Node n, int idx)
 	{
+		int node_x = n.getLoc().getNodeX();
+		int node_y = n.getLoc().getNodeY();
+		//short node_z = n.getLoc().getZ();
+			
 		short regoffset = getRegionOffset(getRegionX(node_x),getRegionY(node_y));
 		ByteBuffer pn = _pathNodes.get(regoffset);
 
@@ -120,7 +124,7 @@ public class GeoPathFinding extends PathFinding
 		if(neighbor > 0)
 		{
 			neighbor--;
-			new_node_x = node_x;
+			new_node_x = (short)node_x;
 			new_node_y = (short)(node_y-1);
 			newNode = readNode(new_node_x,new_node_y,neighbor);
 			if (newNode != null) Neighbors.add(newNode);
@@ -141,7 +145,7 @@ public class GeoPathFinding extends PathFinding
 		{
 			neighbor--;
 			new_node_x = (short)(node_x+1);
-			new_node_y = node_y;
+			new_node_y = (short)node_y;
 			newNode = readNode(new_node_x,new_node_y,neighbor);
 			if (newNode != null) Neighbors.add(newNode);
 		}
@@ -160,7 +164,7 @@ public class GeoPathFinding extends PathFinding
 		if(neighbor > 0)
 		{
 			neighbor--;
-			new_node_x = node_x;
+			new_node_x = (short)node_x;
 			new_node_y = (short)(node_y+1);
 			newNode = readNode(new_node_x,new_node_y,neighbor);
 			if (newNode != null) Neighbors.add(newNode);
@@ -181,7 +185,7 @@ public class GeoPathFinding extends PathFinding
 		{
 			neighbor--;
 			new_node_x = (short)(node_x-1);
-			new_node_y = node_y;
+			new_node_y = (short)node_y;
 			newNode = readNode(new_node_x,new_node_y,neighbor);
 			if (newNode != null) Neighbors.add(newNode);
 		}