Abstraction Layer GeoEngine.

This commit is contained in:
MobiusDevelopment
2022-07-16 06:04:42 +00:00
parent 534f5dbf33
commit beb1aa0b77
1220 changed files with 92917 additions and 72972 deletions

View File

@ -61,7 +61,6 @@ import org.l2jmobius.commons.util.PropertiesParser;
import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.enums.ClassId;
import org.l2jmobius.gameserver.enums.GeoType;
import org.l2jmobius.gameserver.enums.IllegalActionPunishmentType;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.holders.ItemHolder;
@ -919,18 +918,16 @@ public class Config
// GeoEngine
// --------------------------------------------------
public static Path GEODATA_PATH;
public static GeoType GEODATA_TYPE;
public static boolean PATHFINDING;
public static Path PATHNODE_PATH;
public static int PATHFINDING;
public static String PATHFIND_BUFFERS;
public static int MOVE_WEIGHT;
public static int MOVE_WEIGHT_DIAG;
public static int OBSTACLE_WEIGHT;
public static int OBSTACLE_WEIGHT_DIAG;
public static int HEURISTIC_WEIGHT;
public static int HEURISTIC_WEIGHT_DIAG;
public static int MAX_ITERATIONS;
public static int PART_OF_CHARACTER_HEIGHT;
public static int MAX_OBSTACLE_HEIGHT;
public static float LOW_WEIGHT;
public static float MEDIUM_WEIGHT;
public static float HIGH_WEIGHT;
public static boolean ADVANCED_DIAGONAL_STRATEGY;
public static float DIAGONAL_WEIGHT;
public static int MAX_POSTFILTER_PASSES;
public static boolean DEBUG_PATH;
/** Attribute System */
public static int S_WEAPON_STONE;
@ -2429,19 +2426,17 @@ public class Config
// Load GeoEngine config file (if exists)
final PropertiesParser geoEngineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE);
GEODATA_PATH = Paths.get(geoEngineConfig.getString("GeoDataPath", "./data/geodata"));
GEODATA_TYPE = Enum.valueOf(GeoType.class, geoEngineConfig.getString("GeoDataType", "L2J"));
PATHFINDING = geoEngineConfig.getBoolean("PathFinding", true);
PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "1200x10;2000x10;3000x5;5000x3;10000x3");
MOVE_WEIGHT = geoEngineConfig.getInt("MoveWeight", 10);
MOVE_WEIGHT_DIAG = geoEngineConfig.getInt("MoveWeightDiag", 14);
OBSTACLE_WEIGHT = geoEngineConfig.getInt("ObstacleWeight", 30);
OBSTACLE_WEIGHT_DIAG = (int) (OBSTACLE_WEIGHT * Math.sqrt(2));
HEURISTIC_WEIGHT = geoEngineConfig.getInt("HeuristicWeight", 12);
HEURISTIC_WEIGHT_DIAG = geoEngineConfig.getInt("HeuristicWeightDiag", 18);
MAX_ITERATIONS = geoEngineConfig.getInt("MaxIterations", 3500);
PART_OF_CHARACTER_HEIGHT = geoEngineConfig.getInt("PartOfCharacterHeight", 75);
MAX_OBSTACLE_HEIGHT = geoEngineConfig.getInt("MaxObstacleHeight", 32);
GEODATA_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("GeoDataPath", "geodata"));
PATHNODE_PATH = Paths.get(Config.DATAPACK_ROOT.getPath() + "/" + geoEngineConfig.getString("PathnodePath", "pathnode"));
PATHFINDING = geoEngineConfig.getInt("PathFinding", 0);
PATHFIND_BUFFERS = geoEngineConfig.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
LOW_WEIGHT = geoEngineConfig.getFloat("LowWeight", 0.5f);
MEDIUM_WEIGHT = geoEngineConfig.getFloat("MediumWeight", 2);
HIGH_WEIGHT = geoEngineConfig.getFloat("HighWeight", 3);
ADVANCED_DIAGONAL_STRATEGY = geoEngineConfig.getBoolean("AdvancedDiagonalStrategy", true);
DIAGONAL_WEIGHT = geoEngineConfig.getFloat("DiagonalWeight", 0.707f);
MAX_POSTFILTER_PASSES = geoEngineConfig.getInt("MaxPostfilterPasses", 3);
DEBUG_PATH = geoEngineConfig.getBoolean("DebugPath", false);
// Load AllowedPlayerRaces config file (if exists)
final PropertiesParser allowedPlayerRacesConfig = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE);

View File

@ -1,144 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.enums;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
/**
* Container of movement constants used for various geodata and movement checks.
*/
public enum MoveDirectionType
{
N(0, -1),
S(0, 1),
W(-1, 0),
E(1, 0),
NW(-1, -1),
SW(-1, 1),
NE(1, -1),
SE(1, 1);
// Step and signum.
private final int _stepX;
private final int _stepY;
private final int _signumX;
private final int _signumY;
// Cell offset.
private final int _offsetX;
private final int _offsetY;
// Direction flags.
private final byte _directionX;
private final byte _directionY;
private final String _symbolX;
private final String _symbolY;
private MoveDirectionType(int signumX, int signumY)
{
// Get step (world -16, 0, 16) and signum (geodata -1, 0, 1) coordinates.
_stepX = signumX * GeoStructure.CELL_SIZE;
_stepY = signumY * GeoStructure.CELL_SIZE;
_signumX = signumX;
_signumY = signumY;
// Get border offsets in a direction of iteration.
_offsetX = signumX >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
_offsetY = signumY >= 0 ? GeoStructure.CELL_SIZE - 1 : 0;
// Get direction NSWE flag and symbol.
_directionX = signumX < 0 ? GeoStructure.CELL_FLAG_W : signumX == 0 ? 0 : GeoStructure.CELL_FLAG_E;
_directionY = signumY < 0 ? GeoStructure.CELL_FLAG_N : signumY == 0 ? 0 : GeoStructure.CELL_FLAG_S;
_symbolX = signumX < 0 ? "W" : signumX == 0 ? "-" : "E";
_symbolY = signumY < 0 ? "N" : signumY == 0 ? "-" : "S";
}
public int getStepX()
{
return _stepX;
}
public int getStepY()
{
return _stepY;
}
public int getSignumX()
{
return _signumX;
}
public int getSignumY()
{
return _signumY;
}
public int getOffsetX()
{
return _offsetX;
}
public int getOffsetY()
{
return _offsetY;
}
public byte getDirectionX()
{
return _directionX;
}
public byte getDirectionY()
{
return _directionY;
}
public String getSymbolX()
{
return _symbolX;
}
public String getSymbolY()
{
return _symbolY;
}
/**
* @param gdx : Geodata X delta coordinate.
* @param gdy : Geodata Y delta coordinate.
* @return {@link MoveDirectionType} based on given geodata dx and dy delta coordinates.
*/
public static MoveDirectionType getDirection(int gdx, int gdy)
{
if (gdx == 0)
{
return (gdy < 0) ? MoveDirectionType.N : MoveDirectionType.S;
}
if (gdy == 0)
{
return (gdx < 0) ? MoveDirectionType.W : MoveDirectionType.E;
}
if (gdx > 0)
{
return (gdy < 0) ? MoveDirectionType.NE : MoveDirectionType.SE;
}
return (gdy < 0) ? MoveDirectionType.NW : MoveDirectionType.SW;
}
}

View File

@ -1,85 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
public abstract class ABlock
{
/**
* Checks the block for having geodata.
* @return boolean : True, when block has geodata (Flat, Complex, Multilayer).
*/
public abstract boolean hasGeoPos();
/**
* Returns the height of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell geodata Z coordinate, nearest to given coordinates.
*/
public abstract short getHeightNearest(int geoX, int geoY, int worldZ);
/**
* Returns the NSWE flag byte of cell, which is closest to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return short : Cell NSWE flag byte, nearest to given coordinates.
*/
public abstract byte getNsweNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is closes layer to given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index.
*/
public abstract int getIndexNearest(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer above given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexAbove(int geoX, int geoY, int worldZ);
/**
* Returns index to data of the cell, which is first layer below given coordinates.<br>
* @param geoX : Cell geodata X coordinate.
* @param geoY : Cell geodata Y coordinate.
* @param worldZ : Cell world Z coordinate.
* @return {@code int} : Cell index. -1..when no layer available below given Z coordinate.
*/
public abstract int getIndexBelow(int geoX, int geoY, int worldZ);
/**
* Returns the height of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract short getHeight(int index);
/**
* Returns the NSWE flag byte of cell given by cell index.<br>
* @param index : Index of the cell.
* @return short : Cell geodata Z coordinate, below given coordinates.
*/
public abstract byte getNswe(int index);
}

View File

@ -1,130 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
public class BlockComplex extends ABlock
{
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockComplex()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates ComplexBlock.
* @param bb : Input byte buffer.
*/
public BlockComplex(ByteBuffer bb)
{
// Initialize buffer.
_buffer = new byte[GeoStructure.BLOCK_CELLS * 3];
// Load data.
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
{
// Get data.
short data = bb.getShort();
// Get nswe.
_buffer[i * 3] = (byte) (data & 0x000F);
// Get height.
data = (short) ((short) (data & 0xFFF0) >> 1);
_buffer[(i * 3) + 1] = (byte) (data & 0x00FF);
_buffer[(i * 3) + 2] = (byte) (data >> 8);
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get nswe.
return _buffer[index];
}
@Override
public final int getIndexNearest(int geoX, int geoY, int worldZ)
{
return (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height > worldZ ? index : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
// Get height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Check height and return nswe.
return height < worldZ ? index : -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@ -1,95 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockFlat extends ABlock
{
protected final short _height;
protected byte _nswe;
/**
* Creates FlatBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockFlat(ByteBuffer bb, GeoType type)
{
// Get height and nswe.
_height = bb.getShort();
_nswe = GeoStructure.CELL_FLAG_ALL;
// Read dummy data.
if (type == GeoType.L2OFF)
{
bb.getShort();
}
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return _nswe;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height > worldZ ? 0 : -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Check height and return index.
return _height < worldZ ? 0 : -1;
}
@Override
public short getHeight(int index)
{
return _height;
}
@Override
public byte getNswe(int index)
{
return _nswe;
}
}

View File

@ -1,248 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import org.l2jmobius.gameserver.enums.GeoType;
public class BlockMultilayer extends ABlock
{
private static final int MAX_LAYERS = Byte.MAX_VALUE;
private static ByteBuffer _temp;
/**
* Initializes the temporary buffer.
*/
public static final void initialize()
{
// Initialize temporary buffer and sorting mechanism.
_temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3);
_temp.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Releases temporary buffer.
*/
public static final void release()
{
_temp = null;
}
protected byte[] _buffer;
/**
* Implicit constructor for children class.
*/
protected BlockMultilayer()
{
// Buffer is initialized in children class.
_buffer = null;
}
/**
* Creates MultilayerBlock.
* @param bb : Input byte buffer.
* @param type : The type of loaded geodata.
*/
public BlockMultilayer(ByteBuffer bb, GeoType type)
{
// Move buffer pointer to end of MultilayerBlock.
for (int cell = 0; cell < GeoStructure.BLOCK_CELLS; cell++)
{
// Get layer count for this cell.
final byte layers = type != GeoType.L2OFF ? bb.get() : (byte) bb.getShort();
if ((layers <= 0) || (layers > MAX_LAYERS))
{
throw new RuntimeException("Invalid layer count for MultilayerBlock");
}
// Add layers count.
_temp.put(layers);
// Loop over layers.
for (byte layer = 0; layer < layers; layer++)
{
// Get data.
final short data = bb.getShort();
// Add nswe and height.
_temp.put((byte) (data & 0x000F));
_temp.putShort((short) ((short) (data & 0xFFF0) >> 1));
}
}
// Initialize buffer.
_buffer = Arrays.copyOf(_temp.array(), _temp.position());
// Clear temp buffer.
_temp.clear();
}
@Override
public boolean hasGeoPos()
{
return true;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
// Get cell index.
final int index = getIndexNearest(geoX, geoY, worldZ);
// Get nswe.
return _buffer[index];
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
// Loop though all cell layers, find closest layer to given worldZ.
int limit = Integer.MAX_VALUE;
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Get Z distance and compare with limit.
// Note: When 2 layers have same distance to worldZ (worldZ is in the middle of them):
// > Returns bottom layer.
// >= Returns upper layer.
final int distance = Math.abs(height - worldZ);
if (distance > limit)
{
break;
}
// Update limit and move to next layer.
limit = distance;
index += 3;
}
// Return layer index.
return index - 3;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to last layer data (first from bottom).
byte layers = _buffer[index++];
index += (layers - 1) * 3;
// Loop though all layers, find first layer above worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is higher than worldZ, return layer index.
if (height > worldZ)
{
return index;
}
// Move index to next layer.
index -= 3;
}
// No layer found.
return -1;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
// Move index to the cell given by coordinates.
int index = 0;
for (int i = 0; i < (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)); i++)
{
// Move index by amount of layers for this cell.
index += (_buffer[index] * 3) + 1;
}
// Get layers count and shift to first layer data (first from top).
byte layers = _buffer[index++];
// Loop though all layers, find first layer below worldZ.
while (layers-- > 0)
{
// Get layer height.
final int height = (_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8);
// Layer height is lower than worldZ, return layer index.
if (height < worldZ)
{
return index;
}
// Move index to next layer.
index += 3;
}
// No layer found.
return -1;
}
@Override
public short getHeight(int index)
{
// Get height.
return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8));
}
@Override
public byte getNswe(int index)
{
// Get nswe.
return _buffer[index];
}
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public final class Cell
{
/** East NSWE flag */
public static final byte NSWE_EAST = 1 << 0;
/** West NSWE flag */
public static final byte NSWE_WEST = 1 << 1;
/** South NSWE flag */
public static final byte NSWE_SOUTH = 1 << 2;
/** North NSWE flag */
public static final byte NSWE_NORTH = 1 << 3;
/** North-East NSWE flags */
public static final byte NSWE_NORTH_EAST = NSWE_NORTH | NSWE_EAST;
/** North-West NSWE flags */
public static final byte NSWE_NORTH_WEST = NSWE_NORTH | NSWE_WEST;
/** South-East NSWE flags */
public static final byte NSWE_SOUTH_EAST = NSWE_SOUTH | NSWE_EAST;
/** South-West NSWE flags */
public static final byte NSWE_SOUTH_WEST = NSWE_SOUTH | NSWE_WEST;
/** All directions NSWE flags */
public static final byte NSWE_ALL = NSWE_EAST | NSWE_WEST | NSWE_SOUTH | NSWE_NORTH;
private Cell()
{
}
}

View File

@ -0,0 +1,162 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.l2jmobius.gameserver.geoengine.geodata.regions.NullRegion;
import org.l2jmobius.gameserver.geoengine.geodata.regions.Region;
/**
* @author HorridoJoho
*/
public final class GeoData
{
// world dimensions: 1048576 * 1048576 = 1099511627776
private static final int WORLD_MIN_X = -655360;
private static final int WORLD_MAX_X = 393215;
private static final int WORLD_MIN_Y = -589824;
private static final int WORLD_MAX_Y = 458751;
/** Regions in the world on the x axis */
public static final int GEO_REGIONS_X = 32;
/** Regions in the world on the y axis */
public static final int GEO_REGIONS_Y = 32;
/** Region in the world */
public static final int GEO_REGIONS = GEO_REGIONS_X * GEO_REGIONS_Y;
/** Blocks in the world on the x axis */
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * IRegion.REGION_BLOCKS_X;
/** Blocks in the world on the y axis */
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * IRegion.REGION_BLOCKS_Y;
/** Blocks in the world */
public static final int GEO_BLOCKS = GEO_REGIONS * IRegion.REGION_BLOCKS;
/** Cells in the world on the x axis */
public static final int GEO_CELLS_X = GEO_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in the world in the y axis */
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** The regions array */
private final AtomicReferenceArray<IRegion> _regions = new AtomicReferenceArray<>(GEO_REGIONS);
public GeoData()
{
for (int i = 0; i < _regions.length(); i++)
{
_regions.set(i, NullRegion.INSTANCE);
}
}
private void checkGeoX(int geoX)
{
if ((geoX < 0) || (geoX >= GEO_CELLS_X))
{
throw new IllegalArgumentException();
}
}
private void checkGeoY(int geoY)
{
if ((geoY < 0) || (geoY >= GEO_CELLS_Y))
{
throw new IllegalArgumentException();
}
}
private IRegion getRegion(int geoX, int geoY)
{
checkGeoX(geoX);
checkGeoY(geoY);
return _regions.get(((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y));
}
public void loadRegion(Path filePath, int regionX, int regionY) throws IOException
{
final int regionOffset = (regionX * GEO_REGIONS_Y) + regionY;
try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r"))
{
_regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).order(ByteOrder.LITTLE_ENDIAN)));
}
}
public void unloadRegion(int regionX, int regionY)
{
_regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE);
}
public boolean hasGeoPos(int geoX, int geoY)
{
return getRegion(geoX, geoY).hasGeo();
}
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getRegion(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getRegion(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
public int getGeoX(int worldX)
{
if ((worldX < WORLD_MIN_X) || (worldX > WORLD_MAX_X))
{
throw new IllegalArgumentException();
}
return (worldX - WORLD_MIN_X) / 16;
}
public int getGeoY(int worldY)
{
if ((worldY < WORLD_MIN_Y) || (worldY > WORLD_MAX_Y))
{
throw new IllegalArgumentException();
}
return (worldY - WORLD_MIN_Y) / 16;
}
public int getWorldX(int geoX)
{
checkGeoX(geoX);
return (geoX * 16) + WORLD_MIN_X + 8;
}
public int getWorldY(int geoY)
{
checkGeoY(geoY);
return (geoY * 16) + WORLD_MIN_Y + 8;
}
}

View File

@ -1,81 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
import org.l2jmobius.gameserver.model.World;
public final class GeoStructure
{
// Geo cell direction (nswe) flags.
public static final byte CELL_FLAG_NONE = 0x00;
public static final byte CELL_FLAG_E = 0x01;
public static final byte CELL_FLAG_W = 0x02;
public static final byte CELL_FLAG_S = 0x04;
public static final byte CELL_FLAG_N = 0x08;
public static final byte CELL_FLAG_SE = 0x10;
public static final byte CELL_FLAG_SW = 0x20;
public static final byte CELL_FLAG_NE = 0x40;
public static final byte CELL_FLAG_NW = (byte) 0x80;
public static final byte CELL_FLAG_ALL = 0x0F;
// Geo cell expansion flags
public static final byte CELL_EXPANSION_E = CELL_FLAG_E | CELL_FLAG_NE | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_W = CELL_FLAG_W | CELL_FLAG_NW | CELL_FLAG_SW;
public static final byte CELL_EXPANSION_S = CELL_FLAG_S | CELL_FLAG_SW | CELL_FLAG_SE;
public static final byte CELL_EXPANSION_N = CELL_FLAG_N | CELL_FLAG_NW | CELL_FLAG_NE;
public static final byte CELL_EXPANSION_SE = CELL_FLAG_SE | CELL_FLAG_S | CELL_FLAG_E;
public static final byte CELL_EXPANSION_SW = CELL_FLAG_SW | CELL_FLAG_S | CELL_FLAG_W;
public static final byte CELL_EXPANSION_NE = CELL_FLAG_NE | CELL_FLAG_N | CELL_FLAG_E;
public static final byte CELL_EXPANSION_NW = CELL_FLAG_NW | CELL_FLAG_N | CELL_FLAG_W;
// public static final byte CELL_EXPANSION_MASK = CELL_FLAG_SE | CELL_FLAG_SW | CELL_FLAG_NE | CELL_FLAG_NW;
public static final byte CELL_EXPANSION_ALL = (byte) 0xFF;
// Geo cell height constants.
public static final int CELL_SIZE = 16;
public static final int CELL_HEIGHT = 8;
public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6;
// Geo block type identification.
public static final byte TYPE_FLAT_L2J_L2OFF = 0;
public static final byte TYPE_COMPLEX_L2J = 1;
public static final byte TYPE_COMPLEX_L2OFF = 0x40;
public static final byte TYPE_MULTILAYER_L2J = 2;
// public static final byte TYPE_MULTILAYER_L2OFF = 0x41; // officially not does exist, is anything above complex block (0x41 - 0xFFFF)
// Geo block dimensions.
public static final int BLOCK_CELLS_X = 8;
public static final int BLOCK_CELLS_Y = 8;
public static final int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
// Geo region dimensions.
public static final int REGION_BLOCKS_X = 256;
public static final int REGION_BLOCKS_Y = 256;
public static final int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
public static final int REGION_CELLS_X = REGION_BLOCKS_X * BLOCK_CELLS_X;
public static final int REGION_CELLS_Y = REGION_BLOCKS_Y * BLOCK_CELLS_Y;
// Geo world dimensions.
public static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1);
public static final int GEO_REGIONS_Y = ((World.TILE_Y_MAX - World.TILE_Y_MIN) + 1);
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * REGION_BLOCKS_X;
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * REGION_BLOCKS_Y;
public static final int GEO_CELLS_X = GEO_BLOCKS_X * BLOCK_CELLS_X;
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * BLOCK_CELLS_Y;
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IBlock
{
int TYPE_FLAT = 0;
int TYPE_COMPLEX = 1;
int TYPE_MULTILAYER = 2;
/** Cells in a block on the x axis */
int BLOCK_CELLS_X = 8;
/** Cells in a block on the y axis */
int BLOCK_CELLS_Y = 8;
/** Cells in a block */
int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata;
/**
* @author HorridoJoho
*/
public interface IRegion
{
/** Blocks in a region on the x axis. */
int REGION_BLOCKS_X = 256;
/** Blocks in a region on the y axis. */
int REGION_BLOCKS_Y = 256;
/** Blocks in a region. */
int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
/** Cells in a region on the x axis. */
int REGION_CELLS_X = REGION_BLOCKS_X * IBlock.BLOCK_CELLS_X;
/** Cells in a regioin on the y axis. */
int REGION_CELLS_Y = REGION_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
/** Cells in a region. */
int REGION_CELLS = REGION_CELLS_X * REGION_CELLS_Y;
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
int getNearestZ(int geoX, int geoY, int worldZ);
int getNextLowerZ(int geoX, int geoY, int worldZ);
int getNextHigherZ(int geoX, int geoY, int worldZ);
boolean hasGeo();
}

View File

@ -0,0 +1,79 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public final class ComplexBlock implements IBlock
{
private final short[] _data;
public ComplexBlock(ByteBuffer bb)
{
_data = new short[IBlock.BLOCK_CELLS];
for (int cellOffset = 0; cellOffset < IBlock.BLOCK_CELLS; cellOffset++)
{
_data[cellOffset] = bb.getShort();
}
}
private short getCellData(int geoX, int geoY)
{
return _data[((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y)];
}
private byte getCellNSWE(int geoX, int geoY)
{
return (byte) (getCellData(geoX, geoY) & 0x000F);
}
private int getCellHeight(int geoX, int geoY)
{
return (short) (getCellData(geoX, geoY) & 0x0FFF0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (getCellNSWE(geoX, geoY) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getCellHeight(geoX, geoY);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = getCellHeight(geoX, geoY);
return cellHeight <= worldZ ? cellHeight : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int cellHeight = getCellHeight(geoX, geoY);
return cellHeight >= worldZ ? cellHeight : worldZ;
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public class FlatBlock implements IBlock
{
private final short _height;
public FlatBlock(ByteBuffer bb)
{
_height = bb.getShort();
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return _height;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return _height <= worldZ ? _height : worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return _height >= worldZ ? _height : worldZ;
}
}

View File

@ -0,0 +1,185 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata.blocks;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
/**
* @author HorridoJoho
*/
public class MultilayerBlock implements IBlock
{
private final byte[] _data;
/**
* Initializes a new instance of this block reading the specified buffer.
* @param bb the buffer
*/
public MultilayerBlock(ByteBuffer bb)
{
final int start = bb.position();
for (int blockCellOffset = 0; blockCellOffset < IBlock.BLOCK_CELLS; blockCellOffset++)
{
final byte nLayers = bb.get();
if ((nLayers <= 0) || (nLayers > 125))
{
throw new RuntimeException("L2JGeoDriver: Geo file corrupted! Invalid layers count!");
}
bb.position(bb.position() + (nLayers * 2));
}
_data = new byte[bb.position() - start];
bb.position(start);
bb.get(_data);
}
private short getNearestLayer(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
// One layer at least was required on loading so this is set at least once on the loop below.
int nearestDZ = 0;
short nearestData = 0;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerData;
}
final int layerDZ = Math.abs(layerZ - worldZ);
if ((offset == (startOffset + 1)) || (layerDZ < nearestDZ))
{
nearestDZ = layerDZ;
nearestData = layerData;
}
}
return nearestData;
}
private int getCellDataOffset(int geoX, int geoY)
{
final int cellLocalOffset = ((geoX % IBlock.BLOCK_CELLS_X) * IBlock.BLOCK_CELLS_Y) + (geoY % IBlock.BLOCK_CELLS_Y);
int cellDataOffset = 0;
// Move index to cell, we need to parse on each request, OR we parse on creation and save indexes.
for (int i = 0; i < cellLocalOffset; i++)
{
cellDataOffset += 1 + (_data[cellDataOffset] * 2);
}
// Now the index points to the cell we need.
return cellDataOffset;
}
private short extractLayerData(int dataOffset)
{
return (short) ((_data[dataOffset] & 0xFF) | (_data[dataOffset + 1] << 8));
}
private int getNearestNSWE(int geoX, int geoY, int worldZ)
{
return extractLayerNswe(getNearestLayer(geoX, geoY, worldZ));
}
private int extractLayerNswe(short layer)
{
return (byte) (layer & 0x000F);
}
private int extractLayerHeight(short layer)
{
return (short) (layer & 0x0fff0) >> 1;
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return (getNearestNSWE(geoX, geoY, worldZ) & nswe) == nswe;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return extractLayerHeight(getNearestLayer(geoX, geoY, worldZ));
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int lowerZ = Integer.MIN_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerZ;
}
if ((layerZ < worldZ) && (layerZ > lowerZ))
{
lowerZ = layerZ;
}
}
return lowerZ == Integer.MIN_VALUE ? worldZ : lowerZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
final int startOffset = getCellDataOffset(geoX, geoY);
final byte nLayers = _data[startOffset];
final int endOffset = startOffset + 1 + (nLayers * 2);
int higherZ = Integer.MAX_VALUE;
for (int offset = startOffset + 1; offset < endOffset; offset += 2)
{
final short layerData = extractLayerData(offset);
final int layerZ = extractLayerHeight(layerData);
if (layerZ == worldZ)
{
// Exact z.
return layerZ;
}
if ((layerZ > worldZ) && (layerZ < higherZ))
{
higherZ = layerZ;
}
}
return higherZ == Integer.MAX_VALUE ? worldZ : higherZ;
}
}

View File

@ -14,55 +14,44 @@
* 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 org.l2jmobius.gameserver.geoengine.geodata;
package org.l2jmobius.gameserver.geoengine.geodata.regions;
public class BlockNull extends ABlock
import org.l2jmobius.gameserver.geoengine.geodata.IRegion;
/**
* @author HorridoJoho
*/
public final class NullRegion implements IRegion
{
public static final NullRegion INSTANCE = new NullRegion();
@Override
public boolean hasGeoPos()
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return true;
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return worldZ;
}
@Override
public boolean hasGeo()
{
return false;
}
@Override
public short getHeightNearest(int geoX, int geoY, int worldZ)
{
return (short) worldZ;
}
@Override
public byte getNsweNearest(int geoX, int geoY, int worldZ)
{
return GeoStructure.CELL_FLAG_ALL;
}
@Override
public int getIndexNearest(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexAbove(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public int getIndexBelow(int geoX, int geoY, int worldZ)
{
return 0;
}
@Override
public short getHeight(int index)
{
return 0;
}
@Override
public byte getNswe(int index)
{
return GeoStructure.CELL_FLAG_ALL;
}
}
}

View File

@ -0,0 +1,98 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.geodata.regions;
import java.nio.ByteBuffer;
import org.l2jmobius.gameserver.geoengine.geodata.IBlock;
import org.l2jmobius.gameserver.geoengine.geodata.IRegion;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.ComplexBlock;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.FlatBlock;
import org.l2jmobius.gameserver.geoengine.geodata.blocks.MultilayerBlock;
/**
* @author HorridoJoho
*/
public final class Region implements IRegion
{
private final IBlock[] _blocks = new IBlock[IRegion.REGION_BLOCKS];
public Region(ByteBuffer bb)
{
for (int blockOffset = 0; blockOffset < IRegion.REGION_BLOCKS; blockOffset++)
{
final int blockType = bb.get();
switch (blockType)
{
case IBlock.TYPE_FLAT:
{
_blocks[blockOffset] = new FlatBlock(bb);
break;
}
case IBlock.TYPE_COMPLEX:
{
_blocks[blockOffset] = new ComplexBlock(bb);
break;
}
case IBlock.TYPE_MULTILAYER:
{
_blocks[blockOffset] = new MultilayerBlock(bb);
break;
}
default:
{
throw new RuntimeException("Invalid block type " + blockType + "!");
}
}
}
}
private IBlock getBlock(int geoX, int geoY)
{
return _blocks[(((geoX / IBlock.BLOCK_CELLS_X) % IRegion.REGION_BLOCKS_X) * IRegion.REGION_BLOCKS_Y) + ((geoY / IBlock.BLOCK_CELLS_Y) % IRegion.REGION_BLOCKS_Y)];
}
@Override
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
{
return getBlock(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
}
@Override
public int getNearestZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
}
@Override
public int getNextLowerZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
}
@Override
public int getNextHigherZ(int geoX, int geoY, int worldZ)
{
return getBlock(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
}
@Override
public boolean hasGeo()
{
return true;
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding;
public abstract class AbstractNode<Loc extends AbstractNodeLoc>
{
private Loc _loc;
private AbstractNode<Loc> _parent;
public AbstractNode(Loc loc)
{
_loc = loc;
}
public void setParent(AbstractNode<Loc> p)
{
_parent = p;
}
public AbstractNode<Loc> getParent()
{
return _parent;
}
public Loc getLoc()
{
return _loc;
}
public void setLoc(Loc l)
{
_loc = l;
}
@Override
public int hashCode()
{
return (31 * 1) + ((_loc == null) ? 0 : _loc.hashCode());
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof AbstractNode))
{
return false;
}
final AbstractNode<?> other = (AbstractNode<?>) obj;
if (_loc == null)
{
if (other._loc != null)
{
return false;
}
}
else if (!_loc.equals(other._loc))
{
return false;
}
return true;
}
}

View File

@ -14,22 +14,22 @@
* 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 org.l2jmobius.gameserver.enums;
package org.l2jmobius.gameserver.geoengine.pathfinding;
public enum GeoType
/**
* @author -Nemesiss-
*/
public abstract class AbstractNodeLoc
{
L2J("%d_%d.l2j"),
L2OFF("%d_%d_conv.dat");
public abstract int getX();
private final String _filename;
public abstract int getY();
private GeoType(String filename)
{
_filename = filename;
}
public abstract int getZ();
public String getFilename()
{
return _filename;
}
}
public abstract void setZ(short z);
public abstract int getNodeX();
public abstract int getNodeY();
}

View File

@ -1,119 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.model.Location;
public class Node extends Location implements Comparable<Node>
{
// Node geodata values.
private int _geoX;
private int _geoY;
private byte _nswe;
private byte _nsweExpand;
// The cost G (movement cost done) and cost H (estimated cost to target).
private int _costG;
private int _costH;
private int _costF;
// Node parent (reverse path construction).
private Node _parent;
public Node()
{
super(0, 0, 0);
}
@Override
public void clean()
{
super.clean();
_geoX = 0;
_geoY = 0;
_nswe = GeoStructure.CELL_FLAG_NONE;
_nsweExpand = GeoStructure.CELL_FLAG_NONE;
_costG = 0;
_costH = 0;
_costF = 0;
_parent = null;
}
public void setGeo(int gx, int gy, int gz, byte nswe, byte nsweExpand)
{
super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz);
_geoX = gx;
_geoY = gy;
_nswe = nswe;
_nsweExpand = nsweExpand;
}
public void setCost(Node parent, int weight, int costH)
{
_costG = weight;
if (parent != null)
{
_costG += parent._costG;
}
_costH = costH;
_costF = _costG + _costH;
_parent = parent;
}
public int getGeoX()
{
return _geoX;
}
public int getGeoY()
{
return _geoY;
}
public byte getNSWE()
{
return _nswe;
}
public byte getNsweExpand()
{
return _nsweExpand;
}
public int getCostF()
{
return _costF;
}
public Node getParent()
{
return _parent;
}
@Override
public int compareTo(Node o)
{
return _costF - o._costF;
}
}

View File

@ -1,543 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.locks.ReentrantLock;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.ABlock;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.model.Location;
public class NodeBuffer
{
// Locking NodeBuffer to ensure thread-safe operations.
private final ReentrantLock _lock = new ReentrantLock();
// Container holding all available Nodes to be used.
private final Node[] _buffer;
private int _bufferIndex;
// Container (binary-heap) holding Nodes to be explored.
private final PriorityQueue<Node> _opened;
// Container holding Nodes already explored.
private final List<Node> _closed;
// Target coordinates.
private int _gtx;
private int _gty;
private int _gtz;
private Node _current;
/**
* Constructor of NodeBuffer.
* @param size : The total size buffer. Determines the amount of {@link Node}s to be used for pathfinding.
*/
public NodeBuffer(int size)
{
// Create buffers based on given size.
_buffer = new Node[size];
_opened = new PriorityQueue<>(size);
_closed = new ArrayList<>(size);
// Create Nodes.
for (int i = 0; i < size; i++)
{
_buffer[i] = new Node();
}
}
/**
* Find path consisting of Nodes. Starts at origin coordinates, ends in target coordinates.
* @param gox : origin point x
* @param goy : origin point y
* @param goz : origin point z
* @param gtx : target point x
* @param gty : target point y
* @param gtz : target point z
* @return The list of {@link Location} for the path. Empty, if path not found.
*/
public List<Location> findPath(int gox, int goy, int goz, int gtx, int gty, int gtz)
{
// Set target coordinates.
_gtx = gtx;
_gty = gty;
_gtz = gtz;
// Get node from buffer.
_current = _buffer[_bufferIndex++];
// Set node geodata coordinates and movement cost.
_current.setGeo(gox, goy, goz, GeoEngine.getInstance().getNsweNearest(gox, goy, goz), GeoStructure.CELL_EXPANSION_ALL);
_current.setCost(null, 0, getCostH(gox, goy, goz));
int count = 0;
do
{
// Move node to closed list.
_closed.add(_current);
// Target reached, calculate path and return.
if ((_current.getGeoX() == _gtx) && (_current.getGeoY() == _gty) && (_current.getZ() == _gtz))
{
return constructPath();
}
// Expand current node.
expand();
// Get next node to expand.
_current = _opened.poll();
}
while ((_current != null) && (_bufferIndex < _buffer.length) && (++count < Config.MAX_ITERATIONS));
// Iteration failed, return empty path.
return Collections.emptyList();
}
/**
* Build the path from subsequent nodes. Skip nodes in straight directions, keep only corner nodes.
* @return List of {@link Node}s representing the path.
*/
private List<Location> constructPath()
{
// Create result.
final LinkedList<Location> path = new LinkedList<>();
// Clear X/Y direction.
int dx = 0;
int dy = 0;
// Get parent node.
Node parent = _current.getParent();
// While parent exists.
while (parent != null)
{
// Get parent node to current node X/Y direction.
final int nx = parent.getGeoX() - _current.getGeoX();
final int ny = parent.getGeoY() - _current.getGeoY();
// Direction has changed?
if ((dx != nx) || (dy != ny))
{
// Add current node to the beginning of the path (Node must be cloned, as NodeBuffer reuses them).
path.addFirst(_current.clone());
// Update X/Y direction.
dx = nx;
dy = ny;
}
// Move current node and update its parent.
_current = parent;
parent = _current.getParent();
}
return path;
}
public boolean isLocked()
{
return _lock.tryLock();
}
public void free()
{
_opened.clear();
_closed.clear();
for (int i = 0; i < (_bufferIndex - 1); i++)
{
_buffer[i].clean();
}
_bufferIndex = 0;
_current = null;
_lock.unlock();
}
/**
* Expand the current {@link Node} by exploring its neighbors (axially and diagonally).
*/
private void expand()
{
// Movement is blocked, skip.
final byte nswe = _current.getNSWE();
byte expand = _current.getNsweExpand();
if ((nswe & expand) == GeoStructure.CELL_FLAG_NONE)
{
return;
}
// Get geo coordinates of the node to be expanded.
// Note: Z coord shifted up to avoid dual-layer issues.
final int x = _current.getGeoX();
final int y = _current.getGeoY();
final int z = _current.getZ() + GeoStructure.CELL_IGNORE_HEIGHT;
byte nsweN = GeoStructure.CELL_FLAG_NONE;
byte nsweS = GeoStructure.CELL_FLAG_NONE;
byte nsweW = GeoStructure.CELL_FLAG_NONE;
byte nsweE = GeoStructure.CELL_FLAG_NONE;
switch (expand)
{
case GeoStructure.CELL_EXPANSION_N:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_N) != 0)
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_S:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
if (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
if ((getNodeNswe(x - 1, y, z) & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
if ((getNodeNswe(x + 1, y, z) & GeoStructure.CELL_FLAG_S) != 0)
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_W:
{
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))
{
if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))
{
if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_W) != 0)
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_E:
{
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
if (((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))
{
if ((getNodeNswe(x, y - 1, z) & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
}
if (((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))
{
if ((getNodeNswe(x, y + 1, z) & GeoStructure.CELL_FLAG_E) != 0)
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
}
}
break;
}
case GeoStructure.CELL_EXPANSION_NW:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_NE:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_SW:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_SE:
{
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
break;
}
case GeoStructure.CELL_EXPANSION_ALL:
{
if ((nswe & GeoStructure.CELL_FLAG_N) != 0)
{
nsweN = addNode(x, y - 1, z, GeoStructure.CELL_EXPANSION_N, false);
}
if ((nswe & GeoStructure.CELL_FLAG_S) != 0)
{
nsweS = addNode(x, y + 1, z, GeoStructure.CELL_EXPANSION_S, false);
}
if ((nswe & GeoStructure.CELL_FLAG_W) != 0)
{
nsweW = addNode(x - 1, y, z, GeoStructure.CELL_EXPANSION_W, false);
}
if ((nswe & GeoStructure.CELL_FLAG_E) != 0)
{
nsweE = addNode(x + 1, y, z, GeoStructure.CELL_EXPANSION_E, false);
}
if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y - 1, z, GeoStructure.CELL_EXPANSION_NW, true);
}
if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y - 1, z, GeoStructure.CELL_EXPANSION_NE, true);
}
if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0))
{
addNode(x - 1, y + 1, z, GeoStructure.CELL_EXPANSION_SW, true);
}
if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0))
{
addNode(x + 1, y + 1, z, GeoStructure.CELL_EXPANSION_SE, true);
}
break;
}
}
}
private static byte getNodeNswe(int gx, int gy, int gz)
{
// Check new node is out of geodata grid (world coordinates).
if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y))
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get geodata block and check if there is a layer at given coordinates.
ABlock block = GeoEngine.getInstance().getBlock(gx, gy);
final int index = block.getIndexBelow(gx, gy, gz);
if (index < 0)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get node geodata nswe.
return block.getNswe(index);
}
/**
* Take {@link Node} from buffer, validate it and add to opened list.
* @param gx : The new node X geodata coordinate.
* @param gy : The new node Y geodata coordinate.
* @param gzValue : The new node Z geodata coordinate.
* @param nsweExpand : The allowed directions to be expanded on the new node.
* @param diagonal : The new node is being explored in diagonal direction.
* @return The nswe of the added node. Blank, if not added.
*/
private byte addNode(int gx, int gy, int gzValue, byte nsweExpand, boolean diagonal)
{
// Check new node is out of geodata grid (world coordinates).
if ((gx < 0) || (gx >= GeoStructure.GEO_CELLS_X) || (gy < 0) || (gy >= GeoStructure.GEO_CELLS_Y))
{
return GeoStructure.CELL_FLAG_NONE;
}
// Check buffer has reached capacity.
if (_bufferIndex >= _buffer.length)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get geodata block and check if there is a layer at given coordinates.
final ABlock block = GeoEngine.getInstance().getBlock(gx, gy);
final int index = block.getIndexBelow(gx, gy, gzValue);
if (index < 0)
{
return GeoStructure.CELL_FLAG_NONE;
}
// Get node geodata Z and nswe.
final int gz = block.getHeight(index);
final byte nswe = block.getNswe(index);
// Get node from current index (don't move index yet).
Node node = _buffer[_bufferIndex];
// Calculate node weight.
int weight;
if (nswe == GeoStructure.CELL_FLAG_ALL)
{
weight = diagonal ? Config.MOVE_WEIGHT_DIAG : Config.MOVE_WEIGHT;
}
else
{
weight = diagonal ? Config.OBSTACLE_WEIGHT_DIAG : Config.OBSTACLE_WEIGHT;
}
// Set node geodata coordinates.
node.setGeo(gx, gy, gz, nswe, nsweExpand);
// Node is already added to opened list, return.
if (_opened.contains(node))
{
return nswe;
}
// Node was already expanded, return.
if (_closed.contains(node))
{
return nswe;
}
// The node is to be used. Set node movement cost and add it to opened list. Move the buffer index.
node.setCost(_current, weight, getCostH(gx, gy, gz));
_opened.add(node);
_bufferIndex++;
return nswe;
}
/**
* Calculate cost H value, calculated using diagonal distance method.<br>
* Note: Manhattan distance is too simple, causing to explore more unwanted cells.
* @param gx : The node geodata X coordinate.
* @param gy : The node geodata Y coordinate.
* @param gz : The node geodata Z coordinate.
* @return The cost H value (estimated cost to reach the target).
*/
private int getCostH(int gx, int gy, int gz)
{
// Get differences to the target.
int dx = Math.abs(gx - _gtx);
int dy = Math.abs(gy - _gty);
int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT;
// Get diagonal and axial differences to the target.
final int ds = Math.min(dx, Math.min(dy, dz));
dx -= ds;
dy -= ds;
dz -= ds;
int dd;
int d;
if (dx == 0)
{
dd = Math.min(dy, dz);
d = Math.max(dy, dz) - dd;
}
else if (dy == 0)
{
dd = Math.min(dx, dz);
d = Math.max(dx, dz) - dd;
}
else
{
dd = Math.min(dx, dy);
d = Math.max(dx, dy) - dd;
}
// Calculate the diagonal distance of the node to the target.
return (int) (((int) (ds * Math.sqrt(3)) + (dd * Config.HEURISTIC_WEIGHT_DIAG) + (d * Config.HEURISTIC_WEIGHT)) * 1.2);
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding;
import java.util.List;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes.CellPathFinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.geonodes.GeoPathFinding;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author -Nemesiss-
*/
public abstract class PathFinding
{
public static PathFinding getInstance()
{
return Config.PATHFINDING == 1 ? GeoPathFinding.getInstance() : CellPathFinding.getInstance();
}
public abstract boolean pathNodesExist(short regionoffset);
public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable);
/**
* Convert geodata position to pathnode position
* @param geoPos
* @return pathnode position
*/
public short getNodePos(int geoPos)
{
return (short) (geoPos >> 3); // OK?
}
/**
* Convert node position to pathnode block position
* @param nodePos
* @return pathnode block position (0...255)
*/
public short getNodeBlock(int nodePos)
{
return (short) (nodePos % 256);
}
public byte getRegionX(int nodePos)
{
return (byte) ((nodePos >> 8) + World.TILE_X_MIN);
}
public byte getRegionY(int nodePos)
{
return (byte) ((nodePos >> 8) + World.TILE_Y_MIN);
}
public short getRegionOffset(byte rx, byte ry)
{
return (short) ((rx << 5) + ry);
}
/**
* Convert pathnode x to World x position
* @param nodeX rx
* @return
*/
public int calculateWorldX(short nodeX)
{
return World.WORLD_X_MIN + (nodeX * 128) + 48;
}
/**
* Convert pathnode y to World y position
* @param nodeY
* @return
*/
public int calculateWorldY(short nodeY)
{
return World.WORLD_Y_MIN + (nodeY * 128) + 48;
}
public String[] getStat()
{
return null;
}
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
public class CellNode extends AbstractNode<NodeLoc>
{
private CellNode _next = null;
private boolean _isInUse = true;
private float _cost = -1000;
public CellNode(NodeLoc 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;
}
}

View File

@ -0,0 +1,335 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.l2jmobius.Config;
/**
* @author DS Credits to Diamond
*/
public class CellNodeBuffer
{
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 int _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, int z, int tx, int ty, int 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 List<CellNode> debugPath()
{
final List<CellNode> result = new LinkedList<>();
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++)
{
final CellNode n = _buffer[i][j];
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
{
continue;
}
result.add(n);
}
}
return result;
}
private final void getNeighbors()
{
if (_current.getLoc().canGoNone())
{
return;
}
final int x = _current.getLoc().getNodeX();
final int y = _current.getLoc().getNodeY();
final int z = _current.getLoc().getZ();
CellNode nodeE = null;
CellNode nodeS = null;
CellNode nodeW = null;
CellNode nodeN = null;
// East
if (_current.getLoc().canGoEast())
{
nodeE = addNode(x + 1, y, z, false);
}
// South
if (_current.getLoc().canGoSouth())
{
nodeS = addNode(x, y + 1, z, false);
}
// West
if (_current.getLoc().canGoWest())
{
nodeW = addNode(x - 1, y, z, false);
}
// North
if (_current.getLoc().canGoNorth())
{
nodeN = addNode(x, y - 1, z, false);
}
if (!Config.ADVANCED_DIAGONAL_STRATEGY)
{
return;
}
// SouthEast
if ((nodeE != null) && (nodeS != null) && nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast())
{
addNode(x + 1, y + 1, z, true);
}
// SouthWest
if ((nodeS != null) && (nodeW != null) && nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest())
{
addNode(x - 1, y + 1, z, true);
}
// NorthEast
if ((nodeN != null) && (nodeE != null) && nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast())
{
addNode(x + 1, y - 1, z, true);
}
// NorthWest
if ((nodeN != null) && (nodeW != null) && nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest())
{
addNode(x - 1, y - 1, z, true);
}
}
private final CellNode getNode(int x, int y, int 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();
// Re-init node if needed.
if (result.getLoc() != null)
{
result.getLoc().set(x, y, z);
}
else
{
result.setLoc(new NodeLoc(x, y, z));
}
}
return result;
}
private final CellNode addNode(int x, int y, int z, boolean diagonal)
{
final CellNode newNode = getNode(x, y, z);
if (newNode == null)
{
return null;
}
if (newNode.getCost() >= 0)
{
return newNode;
}
final int geoZ = newNode.getLoc().getZ();
final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
if (!newNode.getLoc().canGoAll() || (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;
}
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, int z)
{
final CellNode result = getNode(x, y, z);
return (result == null) || !result.getLoc().canGoAll() || (Math.abs(result.getLoc().getZ() - z) > 16);
}
private final double getCost(int x, int y, int 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.0));
if (result > weight)
{
result += weight;
}
if (result > Float.MAX_VALUE)
{
result = Float.MAX_VALUE;
}
return result;
}
}

View File

@ -0,0 +1,403 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.util.StringUtil;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.instancemanager.IdManager;
import org.l2jmobius.gameserver.model.instancezone.Instance;
import org.l2jmobius.gameserver.model.item.instance.Item;
/**
* @author Sami, DS Credits to Diamond
*/
public class CellPathFinding extends PathFinding
{
private static final Logger LOGGER = 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 List<Item> _debugItems = null;
protected CellPathFinding()
{
try
{
final 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)
{
LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e);
throw new Error("CellPathFinding: load aborted");
}
}
@Override
public boolean pathNodesExist(short regionoffset)
{
return false;
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
{
final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getInstance().getGeoY(y);
if (!GeoEngine.getInstance().hasGeo(x, y))
{
return null;
}
final int gz = GeoEngine.getInstance().getHeight(x, y, z);
final int gtx = GeoEngine.getInstance().getGeoX(tx);
final int gty = GeoEngine.getInstance().getGeoY(ty);
if (!GeoEngine.getInstance().hasGeo(tx, ty))
{
return null;
}
final int gtz = GeoEngine.getInstance().getHeight(tx, ty, tz);
final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty))), playable);
if (buffer == null)
{
return null;
}
final boolean debug = Config.DEBUG_PATH && playable;
if (debug)
{
if (_debugItems == null)
{
_debugItems = new CopyOnWriteArrayList<>();
}
else
{
for (Item item : _debugItems)
{
item.decayMe();
}
_debugItems.clear();
}
}
List<AbstractNodeLoc> path = null;
try
{
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
if (debug)
{
for (CellNode n : buffer.debugPath())
{
if (n.getCost() < 0)
{
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)
{
LOGGER.log(Level.WARNING, "", e);
return null;
}
finally
{
buffer.free();
}
if ((path.size() < 3) || (Config.MAX_POSTFILTER_PASSES <= 0))
{
_findSuccess++;
return path;
}
final long timeStamp = System.currentTimeMillis();
_postFilterUses++;
if (playable)
{
_postFilterPlayableUses++;
}
boolean remove;
int pass = 0;
do
{
pass++;
_postFilterPasses++;
remove = false;
final Iterator<AbstractNodeLoc> endPoint = path.iterator();
endPoint.next();
int currentX = x;
int currentY = y;
int currentZ = z;
int midPoint = 0;
while (endPoint.hasNext())
{
final AbstractNodeLoc locMiddle = path.get(midPoint);
final AbstractNodeLoc locEnd = endPoint.next();
if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance))
{
path.remove(midPoint);
remove = true;
if (debug)
{
dropDebugItem(735, 1, locMiddle);
}
}
else
{
currentX = locMiddle.getX();
currentY = locMiddle.getY();
currentZ = locMiddle.getZ();
midPoint++;
}
}
}
// Only one postfilter pass for AI.
while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES));
if (debug)
{
path.forEach(n -> dropDebugItem(65, 1, n));
}
_findSuccess++;
_postFilterElapsed += System.currentTimeMillis() - timeStamp;
return path;
}
private List<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> node)
{
final List<AbstractNodeLoc> path = new CopyOnWriteArrayList<>();
int previousDirectionX = Integer.MIN_VALUE;
int previousDirectionY = Integer.MIN_VALUE;
int directionX;
int directionY;
AbstractNode<NodeLoc> tempNode = node;
while (tempNode.getParent() != null)
{
if (!Config.ADVANCED_DIAGONAL_STRATEGY && (tempNode.getParent().getParent() != null))
{
final int tmpX = tempNode.getLoc().getNodeX() - tempNode.getParent().getParent().getLoc().getNodeX();
final int tmpY = tempNode.getLoc().getNodeY() - tempNode.getParent().getParent().getLoc().getNodeY();
if (Math.abs(tmpX) == Math.abs(tmpY))
{
directionX = tmpX;
directionY = tmpY;
}
else
{
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
}
}
else
{
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
}
// Only add a new route point if moving direction changes.
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.add(0, tempNode.getLoc());
tempNode.setLoc(null);
}
tempNode = tempNode.getParent();
}
return path;
}
private final CellNodeBuffer alloc(int size, boolean playable)
{
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;
}
i.overflows++;
if (playable)
{
i.playableOverflows++;
// System.err.println("Overflow, size requested: " + size + " playable:"+playable);
}
}
}
return current;
}
private final void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
{
final Item item = new Item(IdManager.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;
List<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<>(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), Config.EOL);
}
StringUtil.append(stat, "Pathfind success/fail:", String.valueOf(_findSuccess), "/", String.valueOf(_findFails));
result[result.length - 1] = stat.toString();
return result;
}
public static CellPathFinding getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final CellPathFinding INSTANCE = new CellPathFinding();
}
}

View File

@ -0,0 +1,165 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.cellnodes;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.Cell;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
/**
* @author -Nemesiss-, HorridoJoho
*/
public class NodeLoc extends AbstractNodeLoc
{
private int _x;
private int _y;
private boolean _goNorth;
private boolean _goEast;
private boolean _goSouth;
private boolean _goWest;
private int _geoHeight;
public NodeLoc(int x, int y, int z)
{
set(x, y, z);
}
public void set(int x, int y, int z)
{
_x = x;
_y = y;
_goNorth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH);
_goEast = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST);
_goSouth = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH);
_goWest = GeoEngine.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST);
_geoHeight = GeoEngine.getInstance().getNearestZ(x, y, z);
}
public boolean canGoNorth()
{
return _goNorth;
}
public boolean canGoEast()
{
return _goEast;
}
public boolean canGoSouth()
{
return _goSouth;
}
public boolean canGoWest()
{
return _goWest;
}
public boolean canGoNone()
{
return !canGoNorth() && !canGoEast() && !canGoSouth() && !canGoWest();
}
public boolean canGoAll()
{
return canGoNorth() && canGoEast() && canGoSouth() && canGoWest();
}
@Override
public int getX()
{
return GeoEngine.getInstance().getWorldX(_x);
}
@Override
public int getY()
{
return GeoEngine.getInstance().getWorldY(_y);
}
@Override
public int getZ()
{
return _geoHeight;
}
@Override
public void setZ(short z)
{
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
int nswe = 0;
if (canGoNorth())
{
nswe |= Cell.NSWE_NORTH;
}
if (canGoEast())
{
nswe |= Cell.NSWE_EAST;
}
if (canGoSouth())
{
nswe |= Cell.NSWE_SOUTH;
}
if (canGoWest())
{
nswe |= Cell.NSWE_WEST;
}
result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe);
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof NodeLoc))
{
return false;
}
final NodeLoc other = (NodeLoc) obj;
return (_x == other._x) && (_y == other._y) && (!_goNorth == !other._goNorth) && (!_goEast == !other._goEast) && (!_goSouth == !other._goSouth) && (!_goWest == !other._goWest) && (_geoHeight == other._geoHeight);
}
}

View File

@ -0,0 +1,60 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class GeoNode extends AbstractNode<GeoNodeLoc>
{
private final int _neighborsIdx;
private short _cost;
private GeoNode[] _neighbors;
public GeoNode(GeoNodeLoc loc, int neighborsIdx)
{
super(loc);
_neighborsIdx = neighborsIdx;
}
public short getCost()
{
return _cost;
}
public void setCost(int cost)
{
_cost = (short) cost;
}
public GeoNode[] getNeighbors()
{
return _neighbors;
}
public void attachNeighbors(GeoNode[] neighbors)
{
_neighbors = neighbors;
}
public int getNeighborsIdx()
{
return _neighborsIdx;
}
}

View File

@ -0,0 +1,102 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.model.World;
/**
* @author -Nemesiss-
*/
public class GeoNodeLoc extends AbstractNodeLoc
{
private final short _x;
private final short _y;
private final short _z;
public GeoNodeLoc(short x, short y, short z)
{
_x = x;
_y = y;
_z = z;
}
@Override
public int getX()
{
return World.WORLD_X_MIN + (_x * 128) + 48;
}
@Override
public int getY()
{
return World.WORLD_Y_MIN + (_y * 128) + 48;
}
@Override
public int getZ()
{
return _z;
}
@Override
public void setZ(short z)
{
}
@Override
public int getNodeX()
{
return _x;
}
@Override
public int getNodeY()
{
return _y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + _x;
result = (prime * result) + _y;
result = (prime * result) + _z;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof GeoNodeLoc))
{
return false;
}
final GeoNodeLoc other = (GeoNodeLoc) obj;
return (_x == other._x) && (_y == other._y) && (_z == other._z);
}
}

View File

@ -0,0 +1,445 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.geonodes;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.utils.FastNodeList;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author -Nemesiss-
*/
public class GeoPathFinding extends PathFinding
{
private static final Logger LOGGER = Logger.getLogger(GeoPathFinding.class.getName());
private static final Map<Short, ByteBuffer> PATH_NODES = new HashMap<>();
private static final Map<Short, IntBuffer> PATH_NODE_INDEX = new HashMap<>();
@Override
public boolean pathNodesExist(short regionoffset)
{
return PATH_NODE_INDEX.containsKey(regionoffset);
}
@Override
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
{
final int gx = (x - World.WORLD_X_MIN) >> 4;
final int gy = (y - World.WORLD_Y_MIN) >> 4;
final short gz = (short) z;
final int gtx = (tx - World.WORLD_X_MIN) >> 4;
final int gty = (ty - World.WORLD_Y_MIN) >> 4;
final short gtz = (short) tz;
final GeoNode start = readNode(gx, gy, gz);
final GeoNode end = readNode(gtx, gty, gtz);
if ((start == null) || (end == null))
{
return null;
}
if (Math.abs(start.getLoc().getZ() - z) > 55)
{
return null; // Not correct layer.
}
if (Math.abs(end.getLoc().getZ() - tz) > 55)
{
return null; // Not correct layer.
}
if (start == end)
{
return null;
}
// TODO: Find closest path node we CAN access. Now only checks if we can not reach the closest.
Location temp = GeoEngine.getInstance().getValidLocation(x, y, z, start.getLoc().getX(), start.getLoc().getY(), start.getLoc().getZ(), instance);
if ((temp.getX() != start.getLoc().getX()) || (temp.getY() != start.getLoc().getY()))
{
return null; // Cannot reach closest...
}
// TODO: Find closest path node around target, now only checks if final location can be reached.
temp = GeoEngine.getInstance().getValidLocation(tx, ty, tz, end.getLoc().getX(), end.getLoc().getY(), end.getLoc().getZ(), instance);
if ((temp.getX() != end.getLoc().getX()) || (temp.getY() != end.getLoc().getY()))
{
return null; // Cannot reach closest...
}
return searchByClosest2(start, end);
}
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.
final FastNodeList visited = new FastNodeList(550);
// List of Nodes to Visit.
final LinkedList<GeoNode> toVisit = new LinkedList<>();
toVisit.add(start);
final int targetX = end.getLoc().getNodeX();
final int targetY = end.getLoc().getNodeY();
int dx, dy;
boolean added;
int i = 0;
while (i < 550)
{
GeoNode node;
try
{
node = toVisit.removeFirst();
}
catch (Exception e)
{
// No Path found
return null;
}
if (node.equals(end))
{
return constructPath2(node);
}
i++;
visited.add(node);
node.attachNeighbors(readNeighbors(node));
final GeoNode[] neighbors = node.getNeighbors();
if (neighbors == null)
{
continue;
}
for (GeoNode n : neighbors)
{
if (!visited.containsRev(n) && !toVisit.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 < toVisit.size(); index++)
{
// Supposed to find it quite early.
if (toVisit.get(index).getCost() > n.getCost())
{
toVisit.add(index, n);
added = true;
break;
}
}
if (!added)
{
toVisit.addLast(n);
}
}
}
}
// No Path found.
return null;
}
public List<AbstractNodeLoc> constructPath2(AbstractNode<GeoNodeLoc> node)
{
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
int previousDirectionX = -1000;
int previousDirectionY = -1000;
int directionX;
int directionY;
AbstractNode<GeoNodeLoc> tempNode = node;
while (tempNode.getParent() != null)
{
// Only add a new route point if moving direction changes.
directionX = tempNode.getLoc().getNodeX() - tempNode.getParent().getLoc().getNodeX();
directionY = tempNode.getLoc().getNodeY() - tempNode.getParent().getLoc().getNodeY();
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
{
previousDirectionX = directionX;
previousDirectionY = directionY;
path.addFirst(tempNode.getLoc());
}
tempNode = tempNode.getParent();
}
return path;
}
private GeoNode[] readNeighbors(GeoNode n)
{
if (n.getLoc() == null)
{
return null;
}
int idx = n.getNeighborsIdx();
final int nodeX = n.getLoc().getNodeX();
final int nodeY = n.getLoc().getNodeY();
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
final ByteBuffer pn = PATH_NODES.get(regoffset);
final List<AbstractNode<GeoNodeLoc>> neighbors = new ArrayList<>(8);
GeoNode newNode;
short newNodeX;
short newNodeY;
// Region for sure will change, we must read from correct file
byte neighbor = pn.get(idx++); // N
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) nodeX;
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NE
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // E
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) nodeY;
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SE
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX + 1);
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // S
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) nodeX;
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // SW
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) (nodeY + 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // W
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) nodeY;
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
neighbor = pn.get(idx++); // NW
if (neighbor > 0)
{
neighbor--;
newNodeX = (short) (nodeX - 1);
newNodeY = (short) (nodeY - 1);
newNode = readNode(newNodeX, newNodeY, neighbor);
if (newNode != null)
{
neighbors.add(newNode);
}
}
return neighbors.toArray(new GeoNode[neighbors.size()]);
}
// Private
private GeoNode readNode(short nodeX, short nodeY, byte layer)
{
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(nodeX);
final short nby = getNodeBlock(nodeY);
int idx = PATH_NODE_INDEX.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = PATH_NODES.get(regoffset);
// Reading.
final byte nodes = pn.get(idx);
idx += (layer * 10) + 1; // byte + layer*10byte
if (nodes < layer)
{
LOGGER.warning("SmthWrong!");
}
final short node_z = pn.getShort(idx);
idx += 2;
return new GeoNode(new GeoNodeLoc(nodeX, nodeY, node_z), idx);
}
private GeoNode readNode(int gx, int gy, short z)
{
final short nodeX = getNodePos(gx);
final short nodeY = getNodePos(gy);
final short regoffset = getRegionOffset(getRegionX(nodeX), getRegionY(nodeY));
if (!pathNodesExist(regoffset))
{
return null;
}
final short nbx = getNodeBlock(nodeX);
final short nby = getNodeBlock(nodeY);
int idx = PATH_NODE_INDEX.get(regoffset).get((nby << 8) + nbx);
final ByteBuffer pn = PATH_NODES.get(regoffset);
// Reading.
byte nodes = pn.get(idx++);
int idx2 = 0; // Create index to nearlest node by z.
short lastZ = Short.MIN_VALUE;
while (nodes > 0)
{
final short node_z = pn.getShort(idx);
if (Math.abs(lastZ - z) > Math.abs(node_z - z))
{
lastZ = node_z;
idx2 = idx + 2;
}
idx += 10; // short + 8 byte
nodes--;
}
return new GeoNode(new GeoNodeLoc(nodeX, nodeY, lastZ), idx2);
}
protected GeoPathFinding()
{
for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++)
{
for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++)
{
loadPathNodeFile((byte) regionX, (byte) regionY);
}
}
}
private void loadPathNodeFile(byte rx, byte ry)
{
final short regionoffset = getRegionOffset(rx, ry);
final File file = new File(Config.PATHNODE_PATH.toString(), rx + "_" + ry + ".pn");
if (!file.exists())
{
return;
}
// LOGGER.info("Path Engine: - Loading: " + file.getName() + " -> region offset: " + regionoffset + " X: " + rx + " Y: " + ry);
int node = 0;
int size = 0;
int index = 0;
// Create a read-only memory-mapped file.
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel roChannel = raf.getChannel())
{
size = (int) roChannel.size();
final MappedByteBuffer nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
// Indexing pathnode files, so we will know where each block starts.
final IntBuffer indexs = IntBuffer.allocate(65536);
while (node < 65536)
{
final byte layer = nodes.get(index);
indexs.put(node++, index);
index += (layer * 10) + 1;
}
PATH_NODE_INDEX.put(regionoffset, indexs);
PATH_NODES.put(regionoffset, nodes);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to Load PathNode File: " + file.getAbsolutePath() + " : " + e.getMessage(), e);
}
}
public static GeoPathFinding getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final GeoPathFinding INSTANCE = new GeoPathFinding();
}
}

View File

@ -0,0 +1,124 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.utils;
import org.l2jmobius.gameserver.geoengine.pathfinding.geonodes.GeoNode;
/**
* @author -Nemesiss-
*/
public class BinaryNodeHeap
{
private final GeoNode[] _list;
private int _size;
public BinaryNodeHeap(int size)
{
_list = new GeoNode[size + 1];
_size = 0;
}
public void add(GeoNode n)
{
_size++;
int pos = _size;
_list[pos] = n;
while (pos != 1)
{
final int p2 = pos / 2;
if (_list[pos].getCost() <= _list[p2].getCost())
{
final GeoNode temp = _list[p2];
_list[p2] = _list[pos];
_list[pos] = temp;
pos = p2;
}
else
{
break;
}
}
}
public GeoNode removeFirst()
{
final GeoNode first = _list[1];
_list[1] = _list[_size];
_list[_size] = null;
_size--;
int pos = 1;
int cpos;
int dblcpos;
GeoNode temp;
while (true)
{
cpos = pos;
dblcpos = cpos * 2;
if ((dblcpos + 1) <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
if (_list[pos].getCost() >= _list[dblcpos + 1].getCost())
{
pos = dblcpos + 1;
}
}
else if (dblcpos <= _size)
{
if (_list[cpos].getCost() >= _list[dblcpos].getCost())
{
pos = dblcpos;
}
}
if (cpos != pos)
{
temp = _list[cpos];
_list[cpos] = _list[pos];
_list[pos] = temp;
}
else
{
break;
}
}
return first;
}
public boolean contains(GeoNode n)
{
if (_size == 0)
{
return false;
}
for (int i = 1; i <= _size; i++)
{
if (_list[i].equals(n))
{
return true;
}
}
return false;
}
public boolean isEmpty()
{
return _size == 0;
}
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 org.l2jmobius.gameserver.geoengine.pathfinding.utils;
import java.util.ArrayList;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode;
/**
* @author -Nemesiss-
*/
public class FastNodeList
{
private final ArrayList<AbstractNode<?>> _list;
public FastNodeList(int size)
{
_list = new ArrayList<>(size);
}
public void add(AbstractNode<?> n)
{
_list.add(n);
}
public boolean contains(AbstractNode<?> n)
{
return _list.contains(n);
}
public boolean containsRev(AbstractNode<?> n)
{
return _list.lastIndexOf(n) != -1;
}
}

View File

@ -400,7 +400,7 @@ public class Spawn extends Location implements IIdentifiable, INamable
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
if (GeoEngine.getInstance().canMoveToTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()) //
&& GeoEngine.getInstance().canSee(newlocx, newlocy, newlocz, npc.getCollisionHeight(), randX, randY, newlocz, 0))
&& GeoEngine.getInstance().canSeeTarget(newlocx, newlocy, newlocz, randX, randY, newlocz, npc.getInstanceWorld()))
{
newlocx = randX;
newlocy = randY;

View File

@ -67,6 +67,8 @@ import org.l2jmobius.gameserver.enums.Team;
import org.l2jmobius.gameserver.enums.TeleportWhereType;
import org.l2jmobius.gameserver.enums.UserInfoType;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.geoengine.pathfinding.PathFinding;
import org.l2jmobius.gameserver.instancemanager.IdManager;
import org.l2jmobius.gameserver.instancemanager.MapRegionManager;
import org.l2jmobius.gameserver.instancemanager.QuestManager;
@ -821,7 +823,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
{
int x = xValue;
int y = yValue;
int z = zValue;
int z = _isFlying ? zValue : GeoEngine.getInstance().getHeight(x, y, zValue);
int heading = headingValue;
Instance instance = instanceValue;
@ -2688,7 +2690,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
public boolean disregardingGeodata;
public int onGeodataPathIndex;
public List<Location> geoPath;
public List<AbstractNodeLoc> geoPath;
public int geoPathAccurateTx;
public int geoPathAccurateTy;
public int geoPathGtx;
@ -3465,7 +3467,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
}
// Movement checks.
if (Config.PATHFINDING && !(this instanceof FriendlyNpc))
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
{
final double originalDistance = distance;
final int originalX = x;
@ -3509,7 +3511,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle)
{
// Path calculation -- overrides previous movement check
m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld());
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
{
if (isPlayer() && !_isFlying && !isInWater)
@ -3553,7 +3555,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
}
// If no distance to go through, the movement is canceled
if ((distance < 1) && (Config.PATHFINDING || isPlayable()))
if ((distance < 1) && ((Config.PATHFINDING > 0) || isPlayable()))
{
if (isSummon())
{

View File

@ -19,7 +19,7 @@ package org.l2jmobius.gameserver.util;
import java.awt.Color;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
import org.l2jmobius.gameserver.geoengine.geodata.Cell;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive;
@ -30,21 +30,21 @@ public final class GeoUtils
{
public static void debug2DLine(Player player, int x, int y, int tx, int ty, int z)
{
final int gx = GeoEngine.getGeoX(x);
final int gy = GeoEngine.getGeoY(y);
final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getInstance().getGeoY(y);
final int tgx = GeoEngine.getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty);
final int tgx = GeoEngine.getInstance().getGeoX(tx);
final int tgy = GeoEngine.getInstance().getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z);
prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z);
final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy);
while (iter.next())
{
final int wx = GeoEngine.getWorldX(iter.x());
final int wy = GeoEngine.getWorldY(iter.y());
final int wx = GeoEngine.getInstance().getWorldX(iter.x());
final int wy = GeoEngine.getInstance().getWorldY(iter.y());
prim.addPoint(Color.RED, wx, wy, z);
}
@ -53,21 +53,21 @@ public final class GeoUtils
public static void debug3DLine(Player player, int x, int y, int z, int tx, int ty, int tz)
{
final int gx = GeoEngine.getGeoX(x);
final int gy = GeoEngine.getGeoY(y);
final int gx = GeoEngine.getInstance().getGeoX(x);
final int gy = GeoEngine.getInstance().getGeoY(y);
final int tgx = GeoEngine.getGeoX(tx);
final int tgy = GeoEngine.getGeoY(ty);
final int tgx = GeoEngine.getInstance().getGeoX(tx);
final int tgy = GeoEngine.getInstance().getGeoY(ty);
final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z);
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz);
prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz);
final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz);
iter.next();
int prevX = iter.x();
int prevY = iter.y();
int wx = GeoEngine.getWorldX(prevX);
int wy = GeoEngine.getWorldY(prevY);
int wx = GeoEngine.getInstance().getWorldX(prevX);
int wy = GeoEngine.getInstance().getWorldY(prevY);
int wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz);
@ -78,8 +78,8 @@ public final class GeoUtils
if ((curX != prevX) || (curY != prevY))
{
wx = GeoEngine.getWorldX(curX);
wy = GeoEngine.getWorldY(curY);
wx = GeoEngine.getInstance().getWorldX(curX);
wy = GeoEngine.getInstance().getWorldY(curY);
wz = iter.z();
prim.addPoint(Color.RED, wx, wy, wz);
@ -93,7 +93,7 @@ public final class GeoUtils
private static Color getDirectionColor(int x, int y, int z, int nswe)
{
if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0)
if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe))
{
return Color.GREEN;
}
@ -109,8 +109,9 @@ public final class GeoUtils
int iPacket = 0;
ExServerPrimitive exsp = null;
final int playerGx = GeoEngine.getGeoX(player.getX());
final int playerGy = GeoEngine.getGeoY(player.getY());
final GeoEngine ge = GeoEngine.getInstance();
final int playerGx = ge.getGeoX(player.getX());
final int playerGy = ge.getGeoY(player.getY());
for (int dx = -geoRadius; dx <= geoRadius; ++dx)
{
for (int dy = -geoRadius; dy <= geoRadius; ++dy)
@ -134,32 +135,32 @@ public final class GeoUtils
final int gx = playerGx + dx;
final int gy = playerGy + dy;
final int x = GeoEngine.getWorldX(gx);
final int y = GeoEngine.getWorldY(gy);
final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ());
final int x = ge.getWorldX(gx);
final int y = ge.getWorldY(gy);
final int z = ge.getNearestZ(gx, gy, player.getZ());
// north arrow
Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N);
Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH);
exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z);
exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z);
exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z);
exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z);
// east arrow
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E);
col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST);
exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z);
exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z);
exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z);
exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z);
// south arrow
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S);
col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH);
exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z);
exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z);
exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z);
exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z);
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W);
col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST);
exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z);
exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z);
exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z);
@ -187,41 +188,41 @@ public final class GeoUtils
{
if (y > lastY)
{
return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E;
return Cell.NSWE_SOUTH_EAST;
}
else if (y < lastY)
{
return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E;
return Cell.NSWE_NORTH_EAST;
}
else
{
return GeoStructure.CELL_FLAG_E;
return Cell.NSWE_EAST;
}
}
else if (x < lastX) // west
{
if (y > lastY)
{
return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W;
return Cell.NSWE_SOUTH_WEST;
}
else if (y < lastY)
{
return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W;
return Cell.NSWE_NORTH_WEST;
}
else
{
return GeoStructure.CELL_FLAG_W;
return Cell.NSWE_WEST;
}
}
else // unchanged x
{
if (y > lastY)
{
return GeoStructure.CELL_FLAG_S;
return Cell.NSWE_SOUTH;
}
else if (y < lastY)
{
return GeoStructure.CELL_FLAG_N;
return Cell.NSWE_NORTH;
}
else
{