diff --git a/L2J_Mobius_C4/.classpath b/L2J_Mobius_C4/.classpath index cc58ca923e..4ba780a682 100644 --- a/L2J_Mobius_C4/.classpath +++ b/L2J_Mobius_C4/.classpath @@ -9,8 +9,6 @@ - - diff --git a/L2J_Mobius_C4/dist/game/config/GeoData.ini b/L2J_Mobius_C4/dist/game/config/GeoData.ini new file mode 100644 index 0000000000..52ef701461 --- /dev/null +++ b/L2J_Mobius_C4/dist/game/config/GeoData.ini @@ -0,0 +1,75 @@ +# --------------------------------------------------------------------------- +# GeoData +# --------------------------------------------------------------------------- + +# Pathfinding options: +# 0 = Disabled +# 1 = Enabled using path node files +# 2 = Enabled using geodata cells at runtime +# Default: 0 +PathFinding = 0 + +# Pathnode directory +# Default: data/pathnode +PathnodeDirectory = data/pathnode + +# Pathfinding array buffers configuration +PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 + +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 + +# Weight for nodes near walls +MediumWeight = 2 + +# Weight for nodes with obstacles +HighWeight = 3 + +# Angle paths will be more "smart", but in cost of higher CPU utilization +AdvancedDiagonalStrategy = True + +# Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 + +# Maximum number of LOS postfilter passes, 0 will disable postfilter. +# Default: 3 +MaxPostfilterPasses = 3 + +# Path debug function. +# Nodes known to pathfinder will be displayed as adena, constructed path as antidots. +# Number of the items show node cost * 10 +# Potions display path after first stage filter +# Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter +# This function FOR DEBUG PURPOSES ONLY, never use it on the live server ! +DebugPath = False + +# True = Loads GeoData buffer's content into physical memory. +# False = Does not necessarily imply that the GeoData buffer's content is not resident in physical memory. +# Default: True +ForceGeoData = True + +# This setting controls Client <--> Server Player coordinates synchronization: +# -1 - Will synchronize only Z from Client --> Server. Default when no geodata. +# 1 - Synchronization Client --> Server only. Using this option (without geodata) makes it more difficult for players to bypass obstacles. +# 2 - Intended for geodata (at least with cell-level pathfinding, otherwise can you try -1). +# Server sends validation packet if client goes too far from server calculated coordinates. +# Default: -1 +CoordSynchronize = -1 + +# Geodata files folder +GeoDataPath = ./data/geodata + +# True: Try to load regions not specified below(won't disturb server startup when file does not exist) +# False: Don't load any regions other than the ones specified with True below +TryLoadUnspecifiedRegions = True + +# List of regions to be required to load +# eg.: +# Both regions required +# 22_22=True +# 19_20=true +# Exclude region from loading +# 25_26=false +# True: Region is required for the server to startup +# False: Region is not considered to be loaded diff --git a/L2J_Mobius_C4/dist/game/config/GeoDriver.ini b/L2J_Mobius_C4/dist/game/config/GeoDriver.ini deleted file mode 100644 index 007a38481d..0000000000 --- a/L2J_Mobius_C4/dist/game/config/GeoDriver.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Geodata files folder -geodataPath = ./data/geodata - -# True: Try to load regions not specified below(won't disturb server startup when file does not exist) -# False: Don't load any regions other than the ones specified with True below -tryLoadUnspecifiedRegions = True - -# List of regions to be required to load -# eg.: -# Both regions required -# 22_22=True -# 19_20=true -# Exclude region from loading -# 25_26=false -# True: Region is required for the server to startup -# False: Region is not considered to be loaded \ No newline at end of file diff --git a/L2J_Mobius_C4/dist/game/config/command-privileges.ini b/L2J_Mobius_C4/dist/game/config/command-privileges.ini index 54b937bfc9..a22e46bb19 100644 --- a/L2J_Mobius_C4/dist/game/config/command-privileges.ini +++ b/L2J_Mobius_C4/dist/game/config/command-privileges.ini @@ -376,9 +376,8 @@ admin_geo_pos = 100 admin_geo_spawn_pos = 100 admin_geo_can_see = 100 admin_geo_can_move = 100 -admin_geoeditor_connect = 100 -admin_geoeditor_join = 100 -admin_geoeditor_leave = 100 +admin_geogrid = 100 +admin_geomap =100 ############### ### MANOR ### diff --git a/L2J_Mobius_C4/dist/game/config/options.ini b/L2J_Mobius_C4/dist/game/config/options.ini index 126d9a728a..23d865b2b3 100644 --- a/L2J_Mobius_C4/dist/game/config/options.ini +++ b/L2J_Mobius_C4/dist/game/config/options.ini @@ -5,7 +5,6 @@ Debug = False Assert = False Developer = False -AcceptGeoeditorConn = False # if true the server will be a test server (listed by clients setted up to list testserver) TestServer = False @@ -253,77 +252,6 @@ GridsAlwaysOn = False GridNeighborTurnOnTime = 1 GridNeighborTurnOffTime = 90 -# ================================================================= -# GeoData & PathNode -# ================================================================= - -# GeoData options: -# 0 = GeoData and PathFinding OFF (default) -# 1 = GeoData used to check Line Of Sight (LOS) targetting and -# L2Playable movement. You need to download files for data/geodata folder. -# Monsters can pass walls but not aggro through them. -# 2 = Full GeoData enabled. Includes PathFinding (requires also /data/pathnode -# files if CellPathFinding not enabled) and all character moves go through -# geodata checks (if a mob passes a wall, pathfinding didn't find a route -# but we allow attack and returning home). -# Recommended server memory minimum 2 GB, rather 3 GB. -GeoData = 0 - -# GeoData driver to use -# Default: com.l2j.geodriver.GeoDriver -GeoDataDriver = com.l2j.geodriver.GeoDriver - -# Cell-level pathfinding, produces more accurate routes but is (maybe 10x) -# heavier to calculate. Recommended for small servers at least. If False, -# pathnode files are used. Uses a max nr of nodes in calculation which can -# be adjusted in the algorithm if it needs to be faster. -CellPathFinding = False - -# Pathnode directory folder -PathnodeDirectory = ./data/pathnode - -# Pathfinding array buffers configuration -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 - -# Weight for nodes near walls -MediumWeight = 2 - -# Weight for nodes with obstacles -HighWeight = 3 - -# Angle paths will be more "smart", but in cost of higher CPU utilization -AdvancedDiagonalStrategy = True - -# Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 - -# Maximum number of LOS postfilter passes, 0 will disable postfilter. -# Default: 3 -MaxPostfilterPasses = 3 - -# Path debug function. -# Nodes known to pathfinder will be displayed as adena, constructed path as antidots. -# Number of the items show node cost * 10 -# Potions display path after first stage filter -# Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter -# This function FOR DEBUG PURPOSES ONLY, never use it on the live server! -DebugPath = False - -#[True]Loads GeoData buffer's content into physical memory. -#[False] Does not necessarily imply that the GeoData buffer's content is not resident in physical memory. -ForceGeodata = True - -# This is setting of Client <--> Server Player coordinates synchronization, -# -1 - Will synchronize only Z from Client --> Server. Default when no geodata. -# 1 - Synchronization Client --> Server only. Using this option (without geodata) it is more difficult for players to bypass obstacles -# 2 - Intended for geodata (at least when cell-level pathfinding, otherwise can try -1 also)! -# Server sends validation packet if client goes too far from server calculated coordinates. -CoordSynchronize = -1 - # Falling Damage # --------------------------------------------------------------------------- # Allow characters to receive damage from falling. diff --git a/L2J_Mobius_C4/dist/game/data/scripts/ai/bosses/Baium.java b/L2J_Mobius_C4/dist/game/data/scripts/ai/bosses/Baium.java index 90d7c1ee28..f95f7a304f 100644 --- a/L2J_Mobius_C4/dist/game/data/scripts/ai/bosses/Baium.java +++ b/L2J_Mobius_C4/dist/game/data/scripts/ai/bosses/Baium.java @@ -23,9 +23,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ThreadPoolManager; import com.l2jmobius.gameserver.datatables.SkillTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.instancemanager.GrandBossManager; import com.l2jmobius.gameserver.model.L2Character; import com.l2jmobius.gameserver.model.L2Effect; @@ -165,24 +165,20 @@ public class Baium extends Quest final L2GrandBossInstance baium = (L2GrandBossInstance) addSpawn(LIVE_BAIUM, loc_x, loc_y, loc_z, heading, false, 0); GrandBossManager.getInstance().addBoss(baium); final L2NpcInstance _baium = baium; - ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() + ThreadPoolManager.getInstance().scheduleGeneral(() -> { - @Override - public void run() + try { - try - { - _baium.setCurrentHpMp(hp, mp); - _baium.setIsInvul(true); - _baium.setIsImmobilized(true); - _baium.setRunning(); - _baium.broadcastPacket(new SocialAction(_baium.getObjectId(), 2)); - startQuestTimer("baium_wakeup", 15000, _baium, null); - } - catch (final Exception e) - { - e.printStackTrace(); - } + _baium.setCurrentHpMp(hp, mp); + _baium.setIsInvul(true); + _baium.setIsImmobilized(true); + _baium.setRunning(); + _baium.broadcastPacket(new SocialAction(_baium.getObjectId(), 2)); + startQuestTimer("baium_wakeup", 15000, _baium, null); + } + catch (final Exception e) + { + e.printStackTrace(); } }, 100L); } @@ -219,20 +215,16 @@ public class Baium extends Quest startQuestTimer("baium_despawn", 60000, npc, null, true); startQuestTimer("skill_range", 500, npc, null, true); final L2NpcInstance baium = npc; - ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() + ThreadPoolManager.getInstance().scheduleGeneral(() -> { - @Override - public void run() + try { - try - { - baium.setIsInvul(false); - baium.setIsImmobilized(false); - } - catch (final Exception e) - { - e.printStackTrace(); - } + baium.setIsInvul(false); + baium.setIsImmobilized(false); + } + catch (final Exception e) + { + e.printStackTrace(); } }, 11100L); @@ -313,23 +305,19 @@ public class Baium extends Quest final L2GrandBossInstance baium = (L2GrandBossInstance) addSpawn(LIVE_BAIUM, npc); GrandBossManager.getInstance().addBoss(baium); final L2NpcInstance _baium = baium; - ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() + ThreadPoolManager.getInstance().scheduleGeneral(() -> { - @Override - public void run() + try { - try - { - _baium.setIsInvul(true); - _baium.setIsImmobilized(true); - _baium.setRunning(); - _baium.broadcastPacket(new SocialAction(_baium.getObjectId(), 2)); - startQuestTimer("baium_wakeup", 15000, _baium, null); - } - catch (final Exception e) - { - e.printStackTrace(); - } + _baium.setIsInvul(true); + _baium.setIsImmobilized(true); + _baium.setRunning(); + _baium.broadcastPacket(new SocialAction(_baium.getObjectId(), 2)); + startQuestTimer("baium_wakeup", 15000, _baium, null); + } + catch (final Exception e) + { + e.printStackTrace(); } }, 100L); } diff --git a/L2J_Mobius_C4/dist/game/data/scripts/ai/individual/Elpy.java b/L2J_Mobius_C4/dist/game/data/scripts/ai/individual/Elpy.java index 3c93b6afdd..5ac0da9627 100644 --- a/L2J_Mobius_C4/dist/game/data/scripts/ai/individual/Elpy.java +++ b/L2J_Mobius_C4/dist/game/data/scripts/ai/individual/Elpy.java @@ -16,8 +16,8 @@ */ package ai.individual; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ai.CtrlIntention; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.L2CharPosition; import com.l2jmobius.gameserver.model.L2Character; import com.l2jmobius.gameserver.model.L2Summon; diff --git a/L2J_Mobius_C4/dist/libs/L2J_GeoAbstraction.jar b/L2J_Mobius_C4/dist/libs/L2J_GeoAbstraction.jar deleted file mode 100644 index af841f999e..0000000000 Binary files a/L2J_Mobius_C4/dist/libs/L2J_GeoAbstraction.jar and /dev/null differ diff --git a/L2J_Mobius_C4/dist/libs/L2J_GeoDriver.jar b/L2J_Mobius_C4/dist/libs/L2J_GeoDriver.jar deleted file mode 100644 index 2abd4fc5f6..0000000000 Binary files a/L2J_Mobius_C4/dist/libs/L2J_GeoDriver.jar and /dev/null differ diff --git a/L2J_Mobius_C4/java/com/l2jmobius/Config.java b/L2J_Mobius_C4/java/com/l2jmobius/Config.java index 314b55dc2d..57dc06bc98 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/Config.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/Config.java @@ -22,12 +22,16 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.logging.Logger; +import com.l2jmobius.gameserver.model.L2World; import com.l2jmobius.gameserver.model.base.Experience; import com.l2jmobius.gameserver.util.FloodProtectorConfig; import com.l2jmobius.util.StringUtil; @@ -46,6 +50,9 @@ import javolution.util.FastMap; public final class Config { protected static Logger _log = Logger.getLogger(Config.class.getName()); + + public static final String EOL = System.lineSeparator(); + /** Debug/release mode */ public static boolean DEBUG; /** Enable/disable assertions */ @@ -578,8 +585,6 @@ public final class Config /** Accept multi-items drop ? */ public static boolean MULTIPLE_ITEM_DROP; - /** Coord Synchronization */ - public static int COORD_SYNCHRONIZE; /** Falling Damage */ public static boolean ENABLE_FALLING_DAMAGE; @@ -730,6 +735,8 @@ public final class Config public static final String OLYMPIAD_CONFIGURATION_FILE = "config/olympiad.ini"; /** Properties file for extensions configurations */ public static final String EXTENSIONS_CONFIGURATION_FILE = "config/extensions.ini"; + /** Properties file for GeoData configurations */ + public static final String GEODATA_CONFIGURATION_FILE = "config/GeoData.ini"; /** Text file containing hexadecimal value of server ID */ public static final String HEXID_FILE = "./config/hexid.txt"; /** @@ -1269,12 +1276,8 @@ public final class Config public static int CS_SUPPORT3_FEE; public static int CS_SUPPORT4_FEE; - /** GeoData 0/1/2 */ - public static int GEODATA; - public static String GEODATA_DRIVER; - - /** Cell PathFinding */ - public static boolean GEODATA_CELLFINDING; + /** GeoData Settings */ + public static int PATHFINDING; public static File PATHNODE_DIR; public static String PATHFIND_BUFFERS; public static float LOW_WEIGHT; @@ -1284,10 +1287,11 @@ public final class Config public static float DIAGONAL_WEIGHT; public static int MAX_POSTFILTER_PASSES; public static boolean DEBUG_PATH; - - /** Force loading GeoData to physical memory */ public static boolean FORCE_GEODATA; - public static boolean ACCEPT_GEOEDITOR_CONN; + public static int COORD_SYNCHRONIZE; + public static Path GEODATA_PATH; + public static boolean TRY_LOAD_UNSPECIFIED_REGIONS; + public static Map GEODATA_REGIONS; /** Max number of buffs */ public static byte BUFFS_MAX_AMOUNT; @@ -1376,6 +1380,45 @@ public final class Config throw new Error("MinProtocolRevision is bigger than MaxProtocolRevision in server configuration file."); } + _log.info("Loading GeoData Configuration Files."); + + final Properties geoData = new Properties(); + try (InputStream is = new FileInputStream(new File(GEODATA_CONFIGURATION_FILE))) + { + geoData.load(is); + } + catch (final Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + GEODATA_CONFIGURATION_FILE + " File."); + } + + PATHFINDING = Integer.parseInt(geoData.getProperty("PathFinding", "0")); + PATHFIND_BUFFERS = geoData.getProperty("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); + LOW_WEIGHT = Float.parseFloat(geoData.getProperty("LowWeight", "0.5f")); + MEDIUM_WEIGHT = Float.parseFloat(geoData.getProperty("MediumWeight", "2")); + HIGH_WEIGHT = Float.parseFloat(geoData.getProperty("HighWeight", "3")); + ADVANCED_DIAGONAL_STRATEGY = Boolean.parseBoolean(geoData.getProperty("AdvancedDiagonalStrategy", "true")); + DIAGONAL_WEIGHT = Float.parseFloat(geoData.getProperty("DiagonalWeight", "0.707f")); + MAX_POSTFILTER_PASSES = Integer.parseInt(geoData.getProperty("MaxPostfilterPasses", "3")); + DEBUG_PATH = Boolean.parseBoolean(geoData.getProperty("DebugPath", "false")); + FORCE_GEODATA = Boolean.parseBoolean(geoData.getProperty("ForceGeoData", "true")); + COORD_SYNCHRONIZE = Integer.parseInt(geoData.getProperty("CoordSynchronize", "-1")); + GEODATA_PATH = Paths.get(geoData.getProperty("GeoDataPath", "./data/geodata")); + TRY_LOAD_UNSPECIFIED_REGIONS = Boolean.parseBoolean(geoData.getProperty("TryLoadUnspecifiedRegions", "true")); + GEODATA_REGIONS = new HashMap<>(); + for (int regionX = L2World.TILE_X_MIN; regionX <= L2World.TILE_X_MAX; regionX++) + { + for (int regionY = L2World.TILE_Y_MIN; regionY <= L2World.TILE_Y_MAX; regionY++) + { + final String key = regionX + "_" + regionY; + if (geoData.containsKey(regionX + "_" + regionY)) + { + GEODATA_REGIONS.put(key, Boolean.parseBoolean(geoData.getProperty(key, "false"))); + } + } + } + final Properties optionsSettings = new Properties(); try (InputStream is = new FileInputStream(new File(OPTIONS_FILE))) { @@ -1392,7 +1435,6 @@ public final class Config DEBUG = Boolean.parseBoolean(optionsSettings.getProperty("Debug", "false")); ASSERT = Boolean.parseBoolean(optionsSettings.getProperty("Assert", "false")); DEVELOPER = Boolean.parseBoolean(optionsSettings.getProperty("Developer", "false")); - ACCEPT_GEOEDITOR_CONN = Boolean.parseBoolean(optionsSettings.getProperty("AcceptGeoeditorConn", "False")); TEST_SERVER = Boolean.parseBoolean(optionsSettings.getProperty("TestServer", "false")); SERVER_LIST_TESTSERVER = Boolean.parseBoolean(optionsSettings.getProperty("TestServer", "false")); @@ -1422,7 +1464,7 @@ public final class Config MULTIPLE_ITEM_DROP = Boolean.valueOf(optionsSettings.getProperty("MultipleItemDrop", "True")); final String str = optionsSettings.getProperty("EnableFallingDamage", "auto"); - ENABLE_FALLING_DAMAGE = "auto".equalsIgnoreCase(str) ? GEODATA > 0 : Boolean.parseBoolean(str); + ENABLE_FALLING_DAMAGE = "auto".equalsIgnoreCase(str) ? PATHFINDING > 0 : Boolean.parseBoolean(str); ALLOW_WAREHOUSE = Boolean.valueOf(optionsSettings.getProperty("AllowWarehouse", "True")); WAREHOUSE_CACHE = Boolean.valueOf(optionsSettings.getProperty("WarehouseCache", "False")); @@ -1511,31 +1553,6 @@ public final class Config GRID_NEIGHBOR_TURNON_TIME = Integer.parseInt(optionsSettings.getProperty("GridNeighborTurnOnTime", "1")); GRID_NEIGHBOR_TURNOFF_TIME = Integer.parseInt(optionsSettings.getProperty("GridNeighborTurnOffTime", "90")); - GEODATA = Integer.parseInt(optionsSettings.getProperty("GeoData", "0")); - GEODATA_DRIVER = optionsSettings.getProperty("GeoDataDriver", "com.l2j.geodriver.GeoDriver"); - GEODATA_CELLFINDING = Boolean.parseBoolean(optionsSettings.getProperty("CellPathFinding", "False")); - - try - { - PATHNODE_DIR = new File(optionsSettings.getProperty("PathnodeDirectory", "./data/pathnode").replaceAll("\\\\", "/")).getCanonicalFile(); - } - catch (final Exception e) - { - _log.warning("Error setting pathnode directory!"); - PATHNODE_DIR = new File("./data/pathnode"); - } - - PATHFIND_BUFFERS = optionsSettings.getProperty("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = Float.parseFloat(optionsSettings.getProperty("LowWeight", "0.5")); - MEDIUM_WEIGHT = Float.parseFloat(optionsSettings.getProperty("MediumWeight", "2")); - HIGH_WEIGHT = Float.parseFloat(optionsSettings.getProperty("HighWeight", "3")); - ADVANCED_DIAGONAL_STRATEGY = Boolean.parseBoolean(optionsSettings.getProperty("AdvancedDiagonalStrategy", "True")); - DIAGONAL_WEIGHT = Float.parseFloat(optionsSettings.getProperty("DiagonalWeight", "0.707")); - MAX_POSTFILTER_PASSES = Integer.parseInt(optionsSettings.getProperty("MaxPostfilterPasses", "3")); - DEBUG_PATH = Boolean.parseBoolean(optionsSettings.getProperty("DebugPath", "False")); - FORCE_GEODATA = Boolean.parseBoolean(optionsSettings.getProperty("ForceGeoData", "True")); - COORD_SYNCHRONIZE = Integer.parseInt(optionsSettings.getProperty("CoordSynchronize", "-1")); - // --------------------------------------------------- // Configuration values not found in config files // --------------------------------------------------- diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GameServer.java index b4864940e0..e391a84724 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GameServer.java @@ -63,7 +63,8 @@ import com.l2jmobius.gameserver.datatables.StaticObjects; import com.l2jmobius.gameserver.datatables.SummonItemsData; import com.l2jmobius.gameserver.datatables.TeleportLocationTable; import com.l2jmobius.gameserver.datatables.ZoneTable; -import com.l2jmobius.gameserver.geoeditorcon.GeoEditorListener; +import com.l2jmobius.gameserver.geodata.GeoData; +import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding; import com.l2jmobius.gameserver.handler.AdminCommandHandler; import com.l2jmobius.gameserver.handler.ItemHandler; import com.l2jmobius.gameserver.handler.SkillHandler; @@ -87,7 +88,6 @@ import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminEnchant; import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminEventEngine; import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminExpSp; import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminFightCalculator; -import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminGeoEditor; import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminGeodata; import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminGm; import com.l2jmobius.gameserver.handler.admincommandhandlers.AdminGmChat; @@ -223,7 +223,6 @@ import com.l2jmobius.gameserver.model.entity.AutoRewarder; import com.l2jmobius.gameserver.model.entity.Hero; import com.l2jmobius.gameserver.network.L2GameClient; import com.l2jmobius.gameserver.network.L2GamePacketHandler; -import com.l2jmobius.gameserver.pathfinding.PathFinding; import com.l2jmobius.gameserver.script.faenor.FaenorScriptEngine; import com.l2jmobius.gameserver.scripting.L2ScriptEngineManager; import com.l2jmobius.gameserver.taskmanager.AutoAnnounceTaskManager; @@ -359,7 +358,7 @@ public class GameServer ClanTable.getInstance(); GeoData.getInstance(); - if (Config.GEODATA == 2) + if (Config.PATHFINDING > 0) { PathFinding.getInstance(); } @@ -588,7 +587,6 @@ public class GameServer _adminCommandHandler.registerAdminCommandHandler(new AdminQuest()); _adminCommandHandler.registerAdminCommandHandler(new AdminZone()); _adminCommandHandler.registerAdminCommandHandler(new AdminGeodata()); - _adminCommandHandler.registerAdminCommandHandler(new AdminGeoEditor()); _adminCommandHandler.registerAdminCommandHandler(new AdminManor()); // _adminCommandHandler.registerAdminCommandHandler(new AdminRadar()); @@ -630,11 +628,6 @@ public class GameServer // read pet stats from db L2PetDataTable.getInstance().loadPetsData(); - if (Config.ACCEPT_GEOEDITOR_CONN) - { - GeoEditorListener.getInstance(); - } - _shutdownHandler = Shutdown.getInstance(); Runtime.getRuntime().addShutdownHook(_shutdownHandler); diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GeoData.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GeoData.java deleted file mode 100644 index cccc913157..0000000000 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/GeoData.java +++ /dev/null @@ -1,577 +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 . - */ -package com.l2jmobius.gameserver; - -import java.io.FileInputStream; -import java.lang.reflect.Constructor; -import java.nio.file.Paths; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.datatables.DoorTable; -import com.l2jmobius.gameserver.model.L2Object; -import com.l2jmobius.gameserver.model.Location; -import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance; -import com.l2jmobius.gameserver.util.GeoUtils; -import com.l2jmobius.gameserver.util.LinePointIterator; -import com.l2jmobius.gameserver.util.LinePointIterator3D; -import com.l2jserver.gameserver.geoengine.Direction; -import com.l2jserver.gameserver.geoengine.NullDriver; -import com.l2jserver.gameserver.geoengine.abstraction.IGeoDriver; - -/** - * @author -Nemesiss-, FBIagent - */ -public class GeoData implements IGeoDriver -{ - private static Logger LOGGER = Logger.getLogger(GeoData.class.getName()); - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - - private final IGeoDriver _driver; - private static GeoData _instance; - - public static GeoData getInstance() - { - if (_instance == null) - { - _instance = new GeoData(); - } - return _instance; - } - - protected GeoData() - { - if (Config.GEODATA > 0) - { - IGeoDriver driver = null; - try - { - final Class cls = Class.forName(Config.GEODATA_DRIVER); - if (!IGeoDriver.class.isAssignableFrom(cls)) - { - throw new ClassCastException("Geodata driver class needs to implement IGeoDriver!"); - } - - final Constructor ctor = cls.getConstructor(Properties.class); - final Properties props = new Properties(); - try (FileInputStream fis = new FileInputStream(Paths.get("config", "GeoDriver.ini").toString())) - { - props.load(fis); - } - driver = (IGeoDriver) ctor.newInstance(props); - } - catch (final Exception ex) - { - LOGGER.log(Level.SEVERE, "Failed to load geodata driver!", ex); - System.exit(1); - } - // we do it this way so it's predictable for the compiler - _driver = driver; - } - else - { - _driver = new NullDriver(null); - } - } - - public boolean isNullDriver() - { - return _driver instanceof NullDriver; - } - - @Override - public int getGeoX(int worldX) - { - return _driver.getGeoX(worldX); - } - - @Override - public int getGeoY(int worldY) - { - return _driver.getGeoY(worldY); - } - - @Override - public int getWorldX(int geoX) - { - return _driver.getWorldX(geoX); - } - - @Override - public int getWorldY(int geoY) - { - return _driver.getWorldY(geoY); - } - - @Override - public boolean hasGeoPos(int geoX, int geoY) - { - return _driver.hasGeoPos(geoX, geoY); - } - - @Override - public int getNearestZ(int geoX, int geoY, int worldZ) - { - return _driver.getNearestZ(geoX, geoY, worldZ); - } - - @Override - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - return _driver.getNextLowerZ(geoX, geoY, worldZ); - } - - @Override - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - return _driver.getNextHigherZ(geoX, geoY, worldZ); - } - - @Override - public boolean canEnterNeighbors(int geoX, int geoY, int worldZ, Direction first, Direction... more) - { - return _driver.canEnterNeighbors(geoX, geoY, worldZ, first, more); - } - - @Override - public boolean canEnterAllNeighbors(int geoX, int geoY, int worldZ) - { - return _driver.canEnterAllNeighbors(geoX, geoY, worldZ); - } - - /** - * @param x - * @param y - * @param z - * @return Nearles Z - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param x - * @param y - * @param zmin - * @param zmax - * @return - */ - public int getSpawnHeight(int x, int y, int zmin, int zmax) - { - // + 30, defend against defective geodata and invalid spawn z :( - return getNextLowerZ(getGeoX(x), getGeoY(y), zmax + 30); - } - - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, Direction dir) - { - boolean can = true; - - switch (dir) - { - case NORTH_EAST: - can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.NORTH); - break; - case NORTH_WEST: - can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.NORTH); - break; - case SOUTH_EAST: - can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.SOUTH); - break; - case SOUTH_WEST: - can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.SOUTH); - break; - } - - if (can && canEnterNeighbors(prevX, prevY, prevGeoZ, dir)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha - * @param target - * @return True if cha can see target (LOS) - */ - public boolean canSeeTarget(L2Object cha, L2Object target) - { - if (target instanceof L2DoorInstance) - { - // can always see doors :o - return true; - } - - if (DoorTable.getInstance().checkIfDoorsBetween(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ())) - { - return false; - } - - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ()); - } - - /** - * Can see target. Does not check doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz) - { - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - z = getNearestZ(geoX, geoY, z); - tz = getNearestZ(tGeoX, tGeoY, tz); - - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // first point is guaranteed to be available, skip it, we can always see our own position - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) - { - continue; - } - - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // the current position has geodata - if (hasGeoPos(curX, curY)) - { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final Direction dir = GeoUtils.computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, dir); - - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) - { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; - } - - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) - { - switch (dir) - { - case NORTH_EAST: - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Direction.EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Direction.NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - break; - } - case NORTH_WEST: - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Direction.WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Direction.NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - break; - } - case SOUTH_EAST: - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Direction.EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Direction.SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - break; - } - case SOUTH_WEST: - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Direction.WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Direction.SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - break; - } - default: - { - canSeeThrough = true; - break; - } - } - } - - if (!canSeeThrough) - { - return false; - } - } - - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; - } - return true; - } - - /** - * @param x - * @param y - * @param z - * @param tx - * @param ty - * @param tz - * @return Last Location (x,y,z) where player can walk - just before wall - */ - public Location moveCheck(int x, int y, int z, int tx, int ty, int tz) - { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - z = getNearestZ(geoX, geoY, z); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - tz = getNearestZ(tGeoX, tGeoY, tz); - - if (DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) - { - return new Location(x, y, getHeight(x, y, z)); - } - - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // first point is guaranteed to be available - pointIter.next(); - - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); - - if (hasGeoPos(prevX, prevY)) - { - final Direction dir = GeoUtils.computeDirection(prevX, prevY, curX, curY); - boolean canEnter = false; - if (canEnterNeighbors(prevX, prevY, prevZ, dir)) - { - // check diagonal movement - switch (dir) - { - case NORTH_EAST: - canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.NORTH); - break; - case NORTH_WEST: - canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.NORTH); - break; - case SOUTH_EAST: - canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.SOUTH); - break; - case SOUTH_WEST: - canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.SOUTH); - break; - default: - canEnter = true; - break; - } - } - - if (!canEnter) - { - // can't move, return previous location - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } - } - - prevX = curX; - prevY = curY; - prevZ = curZ; - } - - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // different floors, return start location - return new Location(x, y, z); - } - return new Location(tx, ty, tz); - } - - public int traceTerrainZ(int x, int y, int z, int tx, int ty) - { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - z = getNearestZ(geoX, geoY, z); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // first point is guaranteed to be available - pointIter.next(); - int prevZ = z; - - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); - - prevZ = curZ; - } - - return prevZ; - } - - /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZ the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZ the Z coordinate to end checking at - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise - */ - public boolean canMove(int fromX, int fromY, int fromZ, int toX, int toY, int toZ) - { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - fromZ = getNearestZ(geoX, geoY, fromZ); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - toZ = getNearestZ(tGeoX, tGeoY, toZ); - - if (DoorTable.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ)) - { - return false; - } - - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - - // first point is guaranteed to be available - pointIter.next(); - - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); - - if (hasGeoPos(prevX, prevY)) - { - final Direction dir = GeoUtils.computeDirection(prevX, prevY, curX, curY); - boolean canEnter = false; - if (canEnterNeighbors(prevX, prevY, prevZ, dir)) - { - // check diagonal movement - switch (dir) - { - case NORTH_EAST: - canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.NORTH); - break; - case NORTH_WEST: - canEnter = canEnterNeighbors(prevX, prevY - 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.NORTH); - break; - case SOUTH_EAST: - canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevZ, Direction.SOUTH); - break; - case SOUTH_WEST: - canEnter = canEnterNeighbors(prevX, prevY + 1, prevZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevZ, Direction.SOUTH); - break; - default: - canEnter = true; - break; - } - } - - if (!canEnter) - { - return false; - } - } - - prevX = curX; - prevY = curY; - prevZ = curZ; - } - - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // different floors - return false; - } - return true; - } - - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java index d448e1e5a6..36f88667b0 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java @@ -25,9 +25,9 @@ import java.util.concurrent.Future; import com.l2jmobius.Config; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.Territory; import com.l2jmobius.gameserver.ThreadPoolManager; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.instancemanager.DimensionalRiftManager; import com.l2jmobius.gameserver.model.L2Attackable; import com.l2jmobius.gameserver.model.L2CharPosition; @@ -976,7 +976,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable } // Considering, if bigger range will be attempted - if ((dist2 < (10000 + (combinedCollision * combinedCollision))) && !_selfAnalysis.isFighter && !_selfAnalysis.isBalanced && (_selfAnalysis.hasLongRangeSkills || _selfAnalysis.isArcher) && (_mostHatedAnalysis.isBalanced || _mostHatedAnalysis.isFighter) && (_mostHatedAnalysis.character.isRooted() || _mostHatedAnalysis.isSlower) && ((Config.GEODATA == 2 ? 20 : 12) >= Rnd.get(100))) // chance + if ((dist2 < (10000 + (combinedCollision * combinedCollision))) && !_selfAnalysis.isFighter && !_selfAnalysis.isBalanced && (_selfAnalysis.hasLongRangeSkills || _selfAnalysis.isArcher) && (_mostHatedAnalysis.isBalanced || _mostHatedAnalysis.isFighter) && (_mostHatedAnalysis.character.isRooted() || _mostHatedAnalysis.isSlower) && ((Config.PATHFINDING == 2 ? 20 : 12) >= Rnd.get(100))) // chance { int posX = _actor.getX(); int posY = _actor.getY(); diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java index 682acdea08..a3a490db5d 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java @@ -24,8 +24,8 @@ import java.util.concurrent.Future; import com.l2jmobius.Config; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ThreadPoolManager; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.L2Attackable; import com.l2jmobius.gameserver.model.L2Character; import com.l2jmobius.gameserver.model.L2Effect; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/datatables/DoorTable.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/datatables/DoorTable.java index 1b5ad2d3b5..1054c5418c 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/datatables/DoorTable.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/datatables/DoorTable.java @@ -27,11 +27,11 @@ import java.util.StringTokenizer; import java.util.logging.Logger; import com.l2jmobius.Config; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc; import com.l2jmobius.gameserver.idfactory.IdFactory; import com.l2jmobius.gameserver.instancemanager.ClanHallManager; import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance; import com.l2jmobius.gameserver.model.entity.ClanHall; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; import com.l2jmobius.gameserver.templates.L2CharTemplate; import com.l2jmobius.gameserver.templates.StatsSet; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/GeoData.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/GeoData.java new file mode 100644 index 0000000000..46872a5597 --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/GeoData.java @@ -0,0 +1,559 @@ +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.datatables.DoorTable; +import com.l2jmobius.gameserver.geodata.geodriver.Cell; +import com.l2jmobius.gameserver.geodata.geodriver.GeoDriver; +import com.l2jmobius.gameserver.model.L2Object; +import com.l2jmobius.gameserver.model.L2World; +import com.l2jmobius.gameserver.model.Location; +import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jmobius.gameserver.util.GeoUtils; +import com.l2jmobius.gameserver.util.LinePointIterator; +import com.l2jmobius.gameserver.util.LinePointIterator3D; + +/** + * @author -Nemesiss-, HorridoJoho + */ +public class GeoData +{ + private static final Logger LOGGER = Logger.getLogger(GeoData.class.getName()); + private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; + private static final int ELEVATED_SEE_OVER_DISTANCE = 2; + private static final int MAX_SEE_OVER_HEIGHT = 48; + private static final int SPAWN_Z_DELTA_LIMIT = 100; + + private final GeoDriver _driver = new GeoDriver(); + + protected GeoData() + { + int loadedRegions = 0; + try + { + for (int regionX = L2World.TILE_X_MIN; regionX <= L2World.TILE_X_MAX; regionX++) + { + for (int regionY = L2World.TILE_Y_MIN; regionY <= L2World.TILE_Y_MAX; regionY++) + { + final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); + final Boolean loadFile = Config.GEODATA_REGIONS.get(regionX + "_" + regionY); + if (loadFile != null) + { + if (loadFile) + { + LOGGER.info(getClass().getSimpleName() + ": Loading " + geoFilePath.getFileName() + "..."); + _driver.loadRegion(geoFilePath, regionX, regionY); + loadedRegions++; + } + } + else if (Config.TRY_LOAD_UNSPECIFIED_REGIONS && Files.exists(geoFilePath)) + { + try + { + LOGGER.info(getClass().getSimpleName() + ": Loading " + geoFilePath.getFileName() + "..."); + _driver.loadRegion(geoFilePath, regionX, regionY); + loadedRegions++; + } + catch (Exception e) + { + LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Failed to load " + geoFilePath.getFileName() + "!", e); + } + } + } + } + } + catch (Exception e) + { + LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Failed to load geodata!", e); + System.exit(1); + } + + LOGGER.info(getClass().getSimpleName() + ": Loaded " + loadedRegions + " regions."); + } + + public boolean hasGeoPos(int geoX, int geoY) + { + return _driver.hasGeoPos(geoX, geoY); + } + + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) + { + return _driver.checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) + { + boolean can = true; + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + { + // can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.NORTH); + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); + } + + if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) + { + // can = canEnterNeighbors(prevX, prevY - 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.NORTH); + can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); + } + + if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) + { + // can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.EAST) && canEnterNeighbors(prevX + 1, prevY, prevGeoZ, Direction.SOUTH); + can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + + if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) + { + // can = canEnterNeighbors(prevX, prevY + 1, prevGeoZ, Direction.WEST) && canEnterNeighbors(prevX - 1, prevY, prevGeoZ, Direction.SOUTH); + can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); + } + + return can && checkNearestNswe(geoX, geoY, worldZ, nswe); + } + + public int getNearestZ(int geoX, int geoY, int worldZ) + { + return _driver.getNearestZ(geoX, geoY, worldZ); + } + + public int getNextLowerZ(int geoX, int geoY, int worldZ) + { + return _driver.getNextLowerZ(geoX, geoY, worldZ); + } + + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + return _driver.getNextHigherZ(geoX, geoY, worldZ); + } + + public int getGeoX(int worldX) + { + return _driver.getGeoX(worldX); + } + + public int getGeoY(int worldY) + { + return _driver.getGeoY(worldY); + } + + public int getGeoZ(int worldZ) + { + return _driver.getGeoZ(worldZ); + } + + public int getWorldX(int geoX) + { + return _driver.getWorldX(geoX); + } + + public int getWorldY(int geoY) + { + return _driver.getWorldY(geoY); + } + + public int getWorldZ(int geoZ) + { + return _driver.getWorldZ(geoZ); + } + + // /////////////////// + // L2J METHODS + /** + * Gets the height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the height + */ + public int getHeight(int x, int y, int z) + { + return getNearestZ(getGeoX(x), getGeoY(y), z); + } + + /** + * Gets the spawn height. + * @param x the x coordinate + * @param y the y coordinate + * @param z the the z coordinate + * @return the spawn height + */ + public int getSpawnHeight(int x, int y, int z) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + + if (!hasGeoPos(geoX, geoY)) + { + return z; + } + + final int nextLowerZ = getNextLowerZ(geoX, geoY, z + 100); + return Math.abs(nextLowerZ - z) <= SPAWN_Z_DELTA_LIMIT ? nextLowerZ : z; + } + + /** + * Gets the spawn height. + * @param location the location + * @return the spawn height + */ + public int getSpawnHeight(Location location) + { + return getSpawnHeight(location.getX(), location.getY(), location.getZ()); + } + + /** + * Can see target. Doors as target always return true. Checks doors between. + * @param cha the character + * @param target the target + * @return {@code true} if the character can see the target (LOS), {@code false} otherwise + */ + public boolean canSeeTarget(L2Object cha, L2Object target) + { + if (target instanceof L2DoorInstance) + { + // can always see doors :o + return true; + } + + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ()); + } + + private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) + { + if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) + { + throw new RuntimeException("Multiple directions!"); + } + + if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) + { + return getNearestZ(curX, curY, prevGeoZ); + } + return getNextHigherZ(curX, curY, prevGeoZ); + } + + /** + * Can see target. Does not check doors between. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise + */ + public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz) + { + int geoX = getGeoX(x); + int geoY = getGeoY(y); + int tGeoX = getGeoX(tx); + int tGeoY = getGeoY(ty); + + z = getNearestZ(geoX, geoY, z); + tz = getNearestZ(tGeoX, tGeoY, tz); + + // fastpath + if ((geoX == tGeoX) && (geoY == tGeoY)) + { + if (hasGeoPos(tGeoX, tGeoY)) + { + return z == tz; + } + + return true; + } + + if (tz > z) + { + int tmp = tx; + tx = x; + x = tmp; + + tmp = ty; + ty = y; + y = tmp; + + tmp = tz; + tz = z; + z = tmp; + + tmp = tGeoX; + tGeoX = geoX; + geoX = tmp; + + tmp = tGeoY; + tGeoY = geoY; + geoY = tmp; + } + + final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); + // first point is guaranteed to be available, skip it, we can always see our own position + pointIter.next(); + int prevX = pointIter.x(); + int prevY = pointIter.y(); + final int prevZ = pointIter.z(); + int prevGeoZ = prevZ; + int ptIndex = 0; + while (pointIter.next()) + { + final int curX = pointIter.x(); + final int curY = pointIter.y(); + + if ((curX == prevX) && (curY == prevY)) + { + continue; + } + + final int beeCurZ = pointIter.z(); + int curGeoZ = prevGeoZ; + + // check if the position has geodata + if (hasGeoPos(curX, curY)) + { + final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); + final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); + curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); + int maxHeight; + if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + { + maxHeight = z + MAX_SEE_OVER_HEIGHT; + } + else + { + maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + } + + boolean canSeeThrough = false; + if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + { + if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + { + final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); + final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); + canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); + } + else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) + { + final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); + final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); + canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + } + else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) + { + final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); + final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); + canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); + } + else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) + { + final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); + final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); + canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + } + else + { + canSeeThrough = true; + } + } + + if (!canSeeThrough) + { + return false; + } + } + + prevX = curX; + prevY = curY; + prevGeoZ = curGeoZ; + ++ptIndex; + } + + return true; + } + + /** + * Move check. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param tx the target's x coordinate + * @param ty the target's y coordinate + * @param tz the target's z coordinate + * @return the last Location (x,y,z) where player can walk - just before wall + */ + public Location moveCheck(int x, int y, int z, int tx, int ty, int tz) + { + final int geoX = getGeoX(x); + final int geoY = getGeoY(y); + z = getNearestZ(geoX, geoY, z); + final int tGeoX = getGeoX(tx); + final int tGeoY = getGeoY(ty); + tz = getNearestZ(tGeoX, tGeoY, tz); + + if (DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) + { + return new Location(x, y, getHeight(x, y, z)); + } + + final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); + // first point is guaranteed to be available + pointIter.next(); + int prevX = pointIter.x(); + int prevY = pointIter.y(); + int prevZ = z; + + while (pointIter.next()) + { + final int curX = pointIter.x(); + final int curY = pointIter.y(); + final int curZ = getNearestZ(curX, curY, prevZ); + + if (hasGeoPos(prevX, prevY)) + { + final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); + if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) + { + // can't move, return previous location + return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); + } + } + + prevX = curX; + prevY = curY; + prevZ = curZ; + } + + if (hasGeoPos(prevX, prevY) && (prevZ != tz)) + { + // different floors, return start location + return new Location(x, y, z); + } + + return new Location(tx, ty, tz); + } + + /** + * Checks if its possible to move from one location to another. + * @param fromX the X coordinate to start checking from + * @param fromY the Y coordinate to start checking from + * @param fromZ the Z coordinate to start checking from + * @param toX the X coordinate to end checking at + * @param toY the Y coordinate to end checking at + * @param toZ the Z coordinate to end checking at + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMove(int fromX, int fromY, int fromZ, int toX, int toY, int toZ) + { + final int geoX = getGeoX(fromX); + final int geoY = getGeoY(fromY); + fromZ = getNearestZ(geoX, geoY, fromZ); + final int tGeoX = getGeoX(toX); + final int tGeoY = getGeoY(toY); + toZ = getNearestZ(tGeoX, tGeoY, toZ); + + if (DoorTable.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ)) + { + return false; + } + + final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); + // first point is guaranteed to be available + pointIter.next(); + int prevX = pointIter.x(); + int prevY = pointIter.y(); + int prevZ = fromZ; + + while (pointIter.next()) + { + final int curX = pointIter.x(); + final int curY = pointIter.y(); + final int curZ = getNearestZ(curX, curY, prevZ); + + if (hasGeoPos(prevX, prevY)) + { + final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); + if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) + { + return false; + } + } + + prevX = curX; + prevY = curY; + prevZ = curZ; + } + + if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) + { + // different floors + return false; + } + + return true; + } + + /** + * Checks if its possible to move from one location to another. + * @param from the {@code WorldObject} to start checking from + * @param toX the X coordinate to end checking at + * @param toY the Y coordinate to end checking at + * @param toZ the Z coordinate to end checking at + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMove(L2Object from, int toX, int toY, int toZ) + { + return canMove(from.getX(), from.getY(), from.getZ(), toX, toY, toZ); + } + + /** + * Checks if its possible to move from one location to another. + * @param from the {@code WorldObject} to start checking from + * @param to the {@code WorldObject} to end checking at + * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + */ + public boolean canMove(L2Object from, L2Object to) + { + return canMove(from, to.getX(), to.getY(), to.getZ()); + } + + /** + * Checks the specified position for available geodata. + * @param x the X coordinate + * @param y the Y coordinate + * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise + */ + public boolean hasGeo(int x, int y) + { + return hasGeoPos(getGeoX(x), getGeoY(y)); + } + + public static GeoData getInstance() + { + return SingletonHolder._instance; + } + + private static class SingletonHolder + { + protected static final GeoData _instance = new GeoData(); + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/Cell.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/Cell.java new file mode 100644 index 0000000000..4914b1d72e --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/Cell.java @@ -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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver; + +/** + * @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() + { + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/GeoDriver.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/GeoDriver.java new file mode 100644 index 0000000000..ffef644f31 --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/GeoDriver.java @@ -0,0 +1,189 @@ +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver; + +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 com.l2jmobius.gameserver.geodata.geodriver.regions.NullRegion; +import com.l2jmobius.gameserver.geodata.geodriver.regions.Region; + +/** + * @author HorridoJoho + */ +public final class GeoDriver +{ + // 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; + private static final int WORLD_MIN_Z = -16384; + private static final int WORLD_MAX_Z = 16384; + + /** 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; + /** Cells in the world in the z axis */ + public static final int GEO_CELLS_Z = (Math.abs(WORLD_MIN_Z) + Math.abs(WORLD_MAX_Z)) / 16; + + /** The regions array */ + private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + + public GeoDriver() + { + 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 void checkGeoZ(int geoZ) + { + if ((geoZ < 0) || (geoZ >= GEO_CELLS_Z)) + { + 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()).load().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 getGeoZ(int worldZ) + { + if ((worldZ < WORLD_MIN_Z) || (worldZ > WORLD_MAX_Z)) + { + throw new IllegalArgumentException(); + } + return (worldZ - WORLD_MIN_Z) / 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; + } + + public int getWorldZ(int geoZ) + { + checkGeoZ(geoZ); + return (geoZ * 16) + WORLD_MIN_Z + 8; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IBlock.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IBlock.java new file mode 100644 index 0000000000..21df97c40d --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IBlock.java @@ -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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver; + +/** + * @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); +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IRegion.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IRegion.java new file mode 100644 index 0000000000..3eb23da03e --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IRegion.java @@ -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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver; + +/** + * @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(); +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/ComplexBlock.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/ComplexBlock.java new file mode 100644 index 0000000000..4d6410672e --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/ComplexBlock.java @@ -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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver.blocks; + +import java.nio.ByteBuffer; + +import com.l2jmobius.gameserver.geodata.geodriver.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; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/FlatBlock.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/FlatBlock.java new file mode 100644 index 0000000000..d5bcff094b --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/FlatBlock.java @@ -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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver.blocks; + +import java.nio.ByteBuffer; + +import com.l2jmobius.gameserver.geodata.geodriver.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; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/MultilayerBlock.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/MultilayerBlock.java new file mode 100644 index 0000000000..313131bd5a --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/MultilayerBlock.java @@ -0,0 +1,186 @@ +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver.blocks; + +import java.nio.ByteBuffer; + +import com.l2jmobius.gameserver.geodata.geodriver.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); + + // 1 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) + { + layer = (short) (layer & 0x0fff0); + return layer >> 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; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/utils/FastNodeList.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/NullRegion.java similarity index 51% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/utils/FastNodeList.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/NullRegion.java index 83a7f7d9e4..60ecac873a 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/utils/FastNodeList.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/NullRegion.java @@ -14,50 +14,44 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.l2jmobius.gameserver.pathfinding.utils; +package com.l2jmobius.gameserver.geodata.geodriver.regions; -import com.l2jmobius.gameserver.pathfinding.AbstractNode; +import com.l2jmobius.gameserver.geodata.geodriver.IRegion; /** - * @author -Nemesiss- + * @author HorridoJoho */ -public class FastNodeList +public final class NullRegion implements IRegion { - private final AbstractNode[] _list; - private int _size; + public static final NullRegion INSTANCE = new NullRegion(); - public FastNodeList(int size) + @Override + public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) { - _list = new AbstractNode[size]; + return true; } - public void add(AbstractNode n) + @Override + public int getNearestZ(int geoX, int geoY, int worldZ) { - _list[_size] = n; - _size++; + return worldZ; } - public boolean contains(AbstractNode n) + @Override + public int getNextLowerZ(int geoX, int geoY, int worldZ) { - for (int i = 0; i < _size; i++) - { - if (_list[i].equals(n)) - { - return true; - } - } - return false; + return worldZ; } - public boolean containsRev(AbstractNode n) + @Override + public int getNextHigherZ(int geoX, int geoY, int worldZ) + { + return worldZ; + } + + @Override + public boolean hasGeo() { - for (int i = _size - 1; i >= 0; i--) - { - if (_list[i].equals(n)) - { - return true; - } - } return false; } } diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/Region.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/Region.java new file mode 100644 index 0000000000..d2d02481ed --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/Region.java @@ -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 . + */ +package com.l2jmobius.gameserver.geodata.geodriver.regions; + +import java.nio.ByteBuffer; + +import com.l2jmobius.gameserver.geodata.geodriver.IBlock; +import com.l2jmobius.gameserver.geodata.geodriver.IRegion; +import com.l2jmobius.gameserver.geodata.geodriver.blocks.ComplexBlock; +import com.l2jmobius.gameserver.geodata.geodriver.blocks.FlatBlock; +import com.l2jmobius.gameserver.geodata.geodriver.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; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/AbstractNode.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/AbstractNode.java similarity index 70% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/AbstractNode.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/AbstractNode.java index 8cfe369e34..e3476f28c2 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/AbstractNode.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/AbstractNode.java @@ -1,93 +1,87 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding; - -public abstract class AbstractNode -{ - private AbstractNodeLoc _loc; - private AbstractNode _parent; - - public AbstractNode(AbstractNodeLoc loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public AbstractNodeLoc getLoc() - { - return _loc; - } - - public void setLoc(AbstractNodeLoc l) - { - _loc = l; - } - - /** - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - @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; - } +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding; + +public abstract class AbstractNode +{ + private T _loc; + private AbstractNode _parent; + + public AbstractNode(T loc) + { + _loc = loc; + } + + public void setParent(AbstractNode p) + { + _parent = p; + } + + public AbstractNode getParent() + { + return _parent; + } + + public T getLoc() + { + return _loc; + } + + public void setLoc(T l) + { + _loc = l; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); + return result; + } + + @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; + } } \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/AbstractNodeLoc.java similarity index 88% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/AbstractNodeLoc.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/AbstractNodeLoc.java index b398fe2e5e..ec3bd23d44 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/AbstractNodeLoc.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/AbstractNodeLoc.java @@ -1,35 +1,33 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract void setZ(short z); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding; + +/** + * @author -Nemesiss- + */ +public abstract class AbstractNodeLoc +{ + public abstract int getX(); + + public abstract int getY(); + + public abstract int getZ(); + + public abstract int getNodeX(); + + public abstract int getNodeY(); +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/PathFinding.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/PathFinding.java new file mode 100644 index 0000000000..37b74dc4a2 --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/PathFinding.java @@ -0,0 +1,210 @@ +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding; + +import java.util.List; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.geodata.pathfinding.cellnodes.CellPathFinding; +import com.l2jmobius.gameserver.geodata.pathfinding.geonodes.GeoPathFinding; +import com.l2jmobius.gameserver.model.L2World; + +/** + * @author -Nemesiss- + */ +public abstract class PathFinding +{ + public static PathFinding getInstance() + { + if (Config.PATHFINDING == 1) + { + // Higher Memory Usage, Smaller Cpu Usage + return GeoPathFinding.getInstance(); + } + // Cell pathfinding, calculated directly from geodata files + return CellPathFinding.getInstance(); + } + + public abstract boolean pathNodesExist(short regionoffset); + + public abstract List findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable); + + // @formatter:off + /* + public List search(AbstractNode start, AbstractNode end, int instanceId) + { + // The simplest grid-based pathfinding. + // Drawback is not having higher cost for diagonal movement (means funny routes) + // Could be optimized e.g. not to calculate backwards as far as forwards. + + // List of Visited Nodes + LinkedList visited = new LinkedList(); + + // List of Nodes to Visit + LinkedList to_visit = new LinkedList(); + to_visit.add(start); + + int i = 0; + while (i < 800) + { + AbstractNode node; + try + { + node = to_visit.removeFirst(); + } + catch (Exception e) + { + // No Path found + return null; + } + if (node.equals(end)) //path found! + return constructPath(node, instanceId); + else + { + i++; + visited.add(node); + node.attachNeighbors(); + Node[] neighbors = node.getNeighbors(); + if (neighbors == null) + continue; + for (Node n : neighbors) + { + if (!visited.contains(n) && !to_visit.contains(n)) + { + n.setParent(node); + to_visit.add(n); + } + } + } + } + //No Path found + return null; + } + */ + /* + public List searchAStar(Node start, Node end, int instanceId) + { + // Not operational yet? + int start_x = start.getLoc().getX(); + int start_y = start.getLoc().getY(); + int end_x = end.getLoc().getX(); + int end_y = end.getLoc().getY(); + //List of Visited Nodes + FastNodeList visited = new FastNodeList(800);//TODO! Add limit to cfg + + // List of Nodes to Visit + BinaryNodeHeap to_visit = new BinaryNodeHeap(800); + to_visit.add(start); + + int i = 0; + while (i < 800)//TODO! Add limit to cfg + { + AbstractNode node; + try + { + node = to_visit.removeFirst(); + } + catch (Exception e) + { + // No Path found + return null; + } + if (node.equals(end)) //path found! + return constructPath(node, instanceId); + else + { + visited.add(node); + node.attachNeighbors(); + for (Node n : node.getNeighbors()) + { + if (!visited.contains(n) && !to_visit.contains(n)) + { + i++; + n.setParent(node); + n.setCost(Math.abs(start_x - n.getLoc().getNodeX()) + Math.abs(start_y - n.getLoc().getNodeY()) + + Math.abs(end_x - n.getLoc().getNodeX()) + Math.abs(end_y - n.getLoc().getNodeY())); + to_visit.add(n); + } + } + } + } + //No Path found + return null; + } + */ + // @formatter:on + + /** + * Convert geodata position to pathnode position + * @param geo_pos + * @return pathnode position + */ + public short getNodePos(int geo_pos) + { + return (short) (geo_pos >> 3); // OK? + } + + /** + * Convert node position to pathnode block position + * @param node_pos + * @return pathnode block position (0...255) + */ + public short getNodeBlock(int node_pos) + { + return (short) (node_pos % 256); + } + + public byte getRegionX(int node_pos) + { + return (byte) ((node_pos >> 8) + L2World.TILE_X_MIN); + } + + public byte getRegionY(int node_pos) + { + return (byte) ((node_pos >> 8) + L2World.TILE_Y_MIN); + } + + public short getRegionOffset(byte rx, byte ry) + { + return (short) ((rx << 5) + ry); + } + + /** + * Convert pathnode x to World x position + * @param node_x rx + * @return + */ + public int calculateWorldX(short node_x) + { + return L2World.MAP_MIN_X + (node_x * 128) + 48; + } + + /** + * Convert pathnode y to World y position + * @param node_y + * @return + */ + public int calculateWorldY(short node_y) + { + return L2World.MAP_MIN_Y + (node_y * 128) + 48; + } + + public String[] getStat() + { + return null; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellNode.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellNode.java similarity index 79% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellNode.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellNode.java index 1c56cb9773..dd2c0f2d8e 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellNode.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellNode.java @@ -1,70 +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 . - */ -package com.l2jmobius.gameserver.pathfinding.cellnodes; - -import com.l2jmobius.gameserver.pathfinding.AbstractNode; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; - -public class CellNode extends AbstractNode -{ - private CellNode _next = null; - private boolean _isInUse = true; - private float _cost = -1000; - - public CellNode(AbstractNodeLoc loc) - { - super(loc); - } - - public boolean isInUse() - { - return _isInUse; - } - - public void setInUse() - { - _isInUse = true; - } - - public CellNode getNext() - { - return _next; - } - - public void setNext(CellNode next) - { - _next = next; - } - - public float getCost() - { - return _cost; - } - - public void setCost(double cost) - { - _cost = (float) cost; - } - - public void free() - { - setParent(null); - _cost = -1000; - _isInUse = false; - _next = null; - } +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes; + +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode; + +public class CellNode extends AbstractNode +{ + 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; + } } \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellNodeBuffer.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellNodeBuffer.java similarity index 76% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellNodeBuffer.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellNodeBuffer.java index 08aa8ca1d1..5747e430ea 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellNodeBuffer.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellNodeBuffer.java @@ -1,364 +1,361 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding.cellnodes; - -import java.util.concurrent.locks.ReentrantLock; - -import com.l2jmobius.Config; - -import javolution.util.FastList; - -/** - * @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 FastList debugPath() - { - final FastList result = new FastList<>(); - - 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 (((NodeLoc) _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 (((NodeLoc) _current.getLoc()).canGoEast()) - { - nodeE = addNode(x + 1, y, z, false); - } - - // South - if (((NodeLoc) _current.getLoc()).canGoSouth()) - { - nodeS = addNode(x, y + 1, z, false); - } - - // West - if (((NodeLoc) _current.getLoc()).canGoWest()) - { - nodeW = addNode(x - 1, y, z, false); - } - - // North - if (((NodeLoc) _current.getLoc()).canGoNorth()) - { - nodeN = addNode(x, y - 1, z, false); - } - - if (Config.ADVANCED_DIAGONAL_STRATEGY) - { - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (((NodeLoc) nodeE.getLoc()).canGoSouth() && ((NodeLoc) nodeS.getLoc()).canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (((NodeLoc) nodeW.getLoc()).canGoSouth() && ((NodeLoc) nodeS.getLoc()).canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (((NodeLoc) nodeE.getLoc()).canGoNorth() && ((NodeLoc) nodeN.getLoc()).canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (((NodeLoc) nodeW.getLoc()).canGoNorth() && ((NodeLoc) 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(); - // reinit node if needed - if (result.getLoc() != null) - { - ((NodeLoc) result.getLoc()).set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private final CellNode addNode(int x, int y, 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 (!((NodeLoc) 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); - if (result == null) - { - return true; - } - - if (!((NodeLoc) result.getLoc()).canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - 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)); - if (result > weight) - { - result += weight; - } - - if (result > Float.MAX_VALUE) - { - result = Float.MAX_VALUE; - } - - return result; - } +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import com.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 debugPath() + { + final List 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 void getNeighbors() + { + if (!_current.getLoc().canGoAll()) + { + 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) + { + // SouthEast + if ((nodeE != null) && (nodeS != null)) + { + if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) + { + addNode(x + 1, y + 1, z, true); + } + } + + // SouthWest + if ((nodeS != null) && (nodeW != null)) + { + if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) + { + addNode(x - 1, y + 1, z, true); + } + } + + // NorthEast + if ((nodeN != null) && (nodeE != null)) + { + if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) + { + addNode(x + 1, y - 1, z, true); + } + } + + // NorthWest + if ((nodeN != null) && (nodeW != null)) + { + if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) + { + addNode(x - 1, y - 1, z, true); + } + } + } + } + + private 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(); + // reinit node if needed + if (result.getLoc() != null) + { + result.getLoc().set(x, y, z); + } + else + { + result.setLoc(new NodeLoc(x, y, z)); + } + } + + return result; + } + + private 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 boolean isHighWeight(int x, int y, int z) + { + final CellNode result = getNode(x, y, z); + if (result == null) + { + return true; + } + + if (!result.getLoc().canGoAll()) + { + return true; + } + if (Math.abs(result.getLoc().getZ() - z) > 16) + { + return true; + } + + return false; + } + + private 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; + } } \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellPathFinding.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellPathFinding.java similarity index 69% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellPathFinding.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellPathFinding.java index df28c26345..aacee44b2d 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/CellPathFinding.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/CellPathFinding.java @@ -1,413 +1,439 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding.cellnodes; - -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Logger; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; -import com.l2jmobius.gameserver.idfactory.IdFactory; -import com.l2jmobius.gameserver.model.L2ItemInstance; -import com.l2jmobius.gameserver.pathfinding.AbstractNode; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; -import com.l2jmobius.gameserver.pathfinding.PathFinding; -import com.l2jmobius.util.StringUtil; - -import javolution.util.FastList; - -/** - * @author Sami, DS Credits to Diamond - */ -public class CellPathFinding extends PathFinding -{ - private static final Logger _log = Logger.getLogger(CellPathFinding.class.getName()); - private BufferInfo[] _allBuffers; - private int _findSuccess = 0; - private int _findFails = 0; - private int _postFilterUses = 0; - private int _postFilterPlayableUses = 0; - private int _postFilterPasses = 0; - private long _postFilterElapsed = 0; - - private FastList _debugItems = null; - - private static CellPathFinding _instance; - - public static CellPathFinding getInstance() - { - if (_instance == null) - { - _instance = new CellPathFinding(); - } - return _instance; - } - - private 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 (final Exception e) - { - _log.warning("CellPathFinding: Problem during buffer init: " + e.getMessage()); - throw new Error("CellPathFinding: load aborted"); - } - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.PathFinding#pathNodesExist(short) - */ - @Override - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - @Override - public List findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable) - { - final int gx = GeoData.getInstance().getGeoX(x); - final int gy = GeoData.getInstance().getGeoY(y); - if (!GeoData.getInstance().hasGeo(x, y)) - { - return null; - } - final int gz = GeoData.getInstance().getHeight(x, y, z); - final int gtx = GeoData.getInstance().getGeoX(tx); - final int gty = GeoData.getInstance().getGeoY(ty); - if (!GeoData.getInstance().hasGeo(tx, ty)) - { - return null; - } - final int gtz = GeoData.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 = playable && Config.DEBUG_PATH; - - if (debug) - { - if (_debugItems == null) - { - _debugItems = new FastList<>(); - } - else - { - for (final L2ItemInstance item : _debugItems) - { - if (item == null) - { - continue; - } - item.decayMe(); - } - - _debugItems.clear(); - } - } - - FastList path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (debug) - { - for (final CellNode n : buffer.debugPath()) - { - if (n.getCost() < 0) - { - dropDebugItem(1831, (int) (-n.getCost() * 10), n.getLoc()); - } - else - { - dropDebugItem(57, (int) (n.getCost() * 10), n.getLoc()); - } - } - } - - if (result == null) - { - _findFails++; - return null; - } - - path = constructPath(result); - } - catch (final Exception e) - { - e.printStackTrace(); - 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++; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint, endPoint; - AbstractNodeLoc locMiddle, locEnd; - boolean remove; - int pass = 0; - do - { - pass++; - _postFilterPasses++; - - remove = false; - middlePoint = path.listIterator(); - endPoint = path.listIterator(1); - locEnd = null; - currentX = x; - currentY = y; - currentZ = z; - - while (endPoint.hasNext()) - { - locEnd = endPoint.next(); - locMiddle = middlePoint.next(); - if (GeoData.getInstance().canMove(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ())) - { - middlePoint.remove(); - remove = true; - if (debug) - { - dropDebugItem(735, 1, locMiddle); - } - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - } - // only one postfilter pass for AI - while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES)); - - if (debug) - { - middlePoint = path.listIterator(); - while (middlePoint.hasNext()) - { - locMiddle = middlePoint.next(); - dropDebugItem(65, 1, locMiddle); - } - } - - _findSuccess++; - _postFilterElapsed += System.currentTimeMillis() - timeStamp; - return path; - } - - private FastList constructPath(AbstractNode node) - { - final FastList path = new FastList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - while (node.getParent() != null) - { - if (!Config.ADVANCED_DIAGONAL_STRATEGY && (node.getParent().getParent() != null)) - { - final int tmpX = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX(); - final int tmpY = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY(); - if (Math.abs(tmpX) == Math.abs(tmpY)) - { - directionX = tmpX; - directionY = tmpY; - } - else - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - } - } - else - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - } - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private final CellNodeBuffer alloc(int size, boolean playable) - { - CellNodeBuffer current = null; - for (final BufferInfo i : _allBuffers) - { - if (i.mapSize >= size) - { - for (final 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++; - } - } - } - - return current; - } - - private final void dropDebugItem(int itemId, int num, AbstractNodeLoc loc) - { - final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId); - item.setCount(num); - item.spawnMe(loc.getX(), loc.getY(), loc.getZ()); - _debugItems.add(item); - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList 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), "\r\n"); - } - StringUtil.append(stat, "Pathfind success/fail:", String.valueOf(_findSuccess), "/", String.valueOf(_findFails)); - result[result.length - 1] = stat.toString(); - - return result; - } -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.geodata.GeoData; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc; +import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding; +import com.l2jmobius.gameserver.idfactory.IdFactory; +import com.l2jmobius.gameserver.model.Inventory; +import com.l2jmobius.gameserver.model.L2ItemInstance; + +/** + * @author Sami, DS Credits to Diamond + */ +public class CellPathFinding extends PathFinding +{ + private static final Logger _log = Logger.getLogger(CellPathFinding.class.getName()); + private BufferInfo[] _allBuffers; + private int _findSuccess = 0; + private int _findFails = 0; + private int _postFilterUses = 0; + private int _postFilterPlayableUses = 0; + private int _postFilterPasses = 0; + private long _postFilterElapsed = 0; + + private List _debugItems = null; + + public static CellPathFinding getInstance() + { + return SingletonHolder._instance; + } + + 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) + { + _log.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 findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable) + { + final int gx = GeoData.getInstance().getGeoX(x); + final int gy = GeoData.getInstance().getGeoY(y); + if (!GeoData.getInstance().hasGeo(x, y)) + { + return null; + } + final int gz = GeoData.getInstance().getHeight(x, y, z); + final int gtx = GeoData.getInstance().getGeoX(tx); + final int gty = GeoData.getInstance().getGeoY(ty); + if (!GeoData.getInstance().hasGeo(tx, ty)) + { + return null; + } + final int gtz = GeoData.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 = playable && Config.DEBUG_PATH; + + if (debug) + { + if (_debugItems == null) + { + _debugItems = new CopyOnWriteArrayList<>(); + } + else + { + for (L2ItemInstance item : _debugItems) + { + if (item == null) + { + continue; + } + item.decayMe(); + } + + _debugItems.clear(); + } + } + + List 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(Inventory.ADENA_ID, (int) (n.getCost() * 10), n.getLoc()); + } + } + } + + if (result == null) + { + _findFails++; + return null; + } + + path = constructPath(result); + } + catch (Exception e) + { + _log.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++; + } + + int currentX, currentY, currentZ; + ListIterator middlePoint; + boolean remove; + int pass = 0; + do + { + pass++; + _postFilterPasses++; + + remove = false; + middlePoint = path.listIterator(); + currentX = x; + currentY = y; + currentZ = z; + + while (middlePoint.hasNext()) + { + final AbstractNodeLoc locMiddle = middlePoint.next(); + if (!middlePoint.hasNext()) + { + break; + } + + final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); + if (GeoData.getInstance().canMove(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ())) + { + middlePoint.remove(); + remove = true; + if (debug) + { + dropDebugItem(735, 1, locMiddle); + } + } + else + { + currentX = locMiddle.getX(); + currentY = locMiddle.getY(); + currentZ = locMiddle.getZ(); + } + } + } + // only one postfilter pass for AI + while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES)); + + if (debug) + { + path.forEach(n -> dropDebugItem(65, 1, n)); + } + + _findSuccess++; + _postFilterElapsed += System.currentTimeMillis() - timeStamp; + return path; + } + + private List constructPath(AbstractNode node) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = Integer.MIN_VALUE; + int previousDirectionY = Integer.MIN_VALUE; + int directionX, directionY; + + while (node.getParent() != null) + { + if (!Config.ADVANCED_DIAGONAL_STRATEGY && (node.getParent().getParent() != null)) + { + final int tmpX = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX(); + final int tmpY = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY(); + if (Math.abs(tmpX) == Math.abs(tmpY)) + { + directionX = tmpX; + directionY = tmpY; + } + else + { + directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); + directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); + } + } + else + { + directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); + directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); + } + + // only add a new route point if moving direction changes + if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) + { + previousDirectionX = directionX; + previousDirectionY = directionY; + + path.addFirst(node.getLoc()); + node.setLoc(null); + } + + node = node.getParent(); + } + + return path; + } + + private 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 void dropDebugItem(int itemId, int num, AbstractNodeLoc loc) + { + final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId); + item.setCount(num); + item.spawnMe(loc.getX(), loc.getY(), loc.getZ()); + _debugItems.add(item); + } + + private static final class BufferInfo + { + final int mapSize; + final int count; + ArrayList 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 sb = new StringBuilder(100); + sb.append(mapSize); + sb.append("x"); + sb.append(mapSize); + sb.append(" num:"); + sb.append(bufs.size()); + sb.append("/"); + sb.append(count); + sb.append(" uses:"); + sb.append(uses); + sb.append("/"); + sb.append(playableUses); + if (uses > 0) + { + sb.append(" total/avg(ms):"); + sb.append(elapsed); + sb.append("/"); + sb.append(String.format("%1.2f", (double) elapsed / uses)); + } + + sb.append(" ovf:"); + sb.append(overflows); + sb.append("/"); + sb.append(playableOverflows); + + return sb.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 sb = new StringBuilder(128); + sb.append("LOS postfilter uses:"); + sb.append(_postFilterUses); + sb.append("/"); + sb.append(_postFilterPlayableUses); + if (_postFilterUses > 0) + { + sb.append(" total/avg(ms):"); + sb.append(_postFilterElapsed); + sb.append("/"); + sb.append(String.format("%1.2f", (double) _postFilterElapsed / _postFilterUses)); + sb.append(" passes total/avg:"); + sb.append(_postFilterPasses); + sb.append("/"); + sb.append(String.format("%1.1f", (double) _postFilterPasses / _postFilterUses)); + sb.append(Config.EOL); + } + sb.append("Pathfind success/fail:"); + sb.append(_findSuccess); + sb.append("/"); + sb.append(_findFails); + result[result.length - 1] = sb.toString(); + + return result; + } + + private static class SingletonHolder + { + protected static final CellPathFinding _instance = new CellPathFinding(); + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/NodeLoc.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/NodeLoc.java similarity index 63% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/NodeLoc.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/NodeLoc.java index f837412e63..f0720ae1ae 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/cellnodes/NodeLoc.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/cellnodes/NodeLoc.java @@ -1,215 +1,184 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding.cellnodes; - -import com.l2jmobius.gameserver.GeoData; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; -import com.l2jserver.gameserver.geoengine.Direction; - -/** - * @author -Nemesiss-, FBIagent - */ -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 = GeoData.getInstance().canEnterNeighbors(x, y, z, Direction.NORTH); - _goEast = GeoData.getInstance().canEnterNeighbors(x, y, z, Direction.EAST); - _goSouth = GeoData.getInstance().canEnterNeighbors(x, y, z, Direction.SOUTH); - _goWest = GeoData.getInstance().canEnterNeighbors(x, y, z, Direction.WEST); - _geoHeight = GeoData.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(); - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getX() - */ - @Override - public int getX() - { - return GeoData.getInstance().getWorldX(_x); - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getY() - */ - @Override - public int getY() - { - return GeoData.getInstance().getWorldY(_y); - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getZ() - */ - @Override - public int getZ() - { - return _geoHeight; - } - - @Override - public void setZ(short z) - { - // do nothing here - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getNodeX() - */ - @Override - public int getNodeX() - { - return _x; - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getNodeY() - */ - @Override - public int getNodeY() - { - return _y; - } - - /** - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + _x; - result = (prime * result) + _y; - - byte nswe = 0; - if (canGoNorth()) - { - nswe |= 1; - } - if (canGoEast()) - { - nswe |= 1 << 1; - } - if (canGoSouth()) - { - nswe |= 1 << 2; - } - if (canGoEast()) - { - nswe |= 1 << 3; - } - - result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe); - return result; - } - - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.cellnodes; + +import com.l2jmobius.gameserver.geodata.GeoData; +import com.l2jmobius.gameserver.geodata.geodriver.Cell; +import com.l2jmobius.gameserver.geodata.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 = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH); + _goEast = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST); + _goSouth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH); + _goWest = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST); + _geoHeight = GeoData.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 canGoAll() + { + return canGoNorth() && canGoEast() && canGoSouth() && canGoWest(); + } + + @Override + public int getX() + { + return GeoData.getInstance().getWorldX(_x); + } + + @Override + public int getY() + { + return GeoData.getInstance().getWorldY(_y); + } + + @Override + public int getZ() + { + return _geoHeight; + } + + @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; + // return super.hashCode(); + } + + @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; + if (_x != other._x) + { + return false; + } + if (_y != other._y) + { + return false; + } + if (_goNorth != other._goNorth) + { + return false; + } + if (_goEast != other._goEast) + { + return false; + } + if (_goSouth != other._goSouth) + { + return false; + } + if (_goWest != other._goWest) + { + return false; + } + if (_geoHeight != other._geoHeight) + { + return false; + } + return true; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoNode.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoNode.java similarity index 78% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoNode.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoNode.java index 5a933e26f4..e9461b122a 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoNode.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoNode.java @@ -1,63 +1,62 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding.geonodes; - -import com.l2jmobius.gameserver.pathfinding.AbstractNode; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; - -/** - * @author -Nemesiss- - */ -public class GeoNode extends AbstractNode -{ - private final int _neighborsIdx; - private short _cost; - private GeoNode[] _neighbors; - - public GeoNode(AbstractNodeLoc Loc, int Neighbors_idx) - { - super(Loc); - _neighborsIdx = Neighbors_idx; - } - - public short getCost() - { - return _cost; - } - - public void setCost(int cost) - { - _cost = (short) cost; - } - - public GeoNode[] getNeighbors() - { - return _neighbors; - } - - public void attachNeighbors() - { - if (getLoc() == null) - { - _neighbors = null; - } - else - { - _neighbors = GeoPathFinding.getInstance().readNeighbors(this, _neighborsIdx); - } - } -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.geonodes; + +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode; + +/** + * @author -Nemesiss- + */ +public class GeoNode extends AbstractNode +{ + private final int _neighborsIdx; + private short _cost; + private GeoNode[] _neighbors; + + public GeoNode(GeoNodeLoc Loc, int Neighbors_idx) + { + super(Loc); + _neighborsIdx = Neighbors_idx; + } + + public short getCost() + { + return _cost; + } + + public void setCost(int cost) + { + _cost = (short) cost; + } + + public GeoNode[] getNeighbors() + { + return _neighbors; + } + + public void attachNeighbors() + { + if (getLoc() == null) + { + _neighbors = null; + } + else + { + _neighbors = GeoPathFinding.getInstance().readNeighbors(this, _neighborsIdx); + } + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoNodeLoc.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoNodeLoc.java similarity index 74% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoNodeLoc.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoNodeLoc.java index f63e92de85..3721b3a75e 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoNodeLoc.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoNodeLoc.java @@ -1,130 +1,109 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding.geonodes; - -import com.l2jmobius.gameserver.model.L2World; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; - -/** - * @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; - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getX() - */ - @Override - public int getX() - { - return L2World.MAP_MIN_X + (_x * 128) + 48; - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getY() - */ - @Override - public int getY() - { - return L2World.MAP_MIN_Y + (_y * 128) + 48; - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc#getZ() - */ - @Override - public int getZ() - { - return _z; - } - - @Override - public void setZ(short z) - { - // Overriden - } - - @Override - public int getNodeX() - { - return _x; - } - - @Override - public int getNodeY() - { - return _y; - } - - /** - * @see java.lang.Object#hashCode() - */ - @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; - } - - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_z != other._z) - { - return false; - } - return true; - } -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.geonodes; + +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc; +import com.l2jmobius.gameserver.model.L2World; + +/** + * @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 L2World.MAP_MIN_X + (_x * 128) + 48; + } + + @Override + public int getY() + { + return L2World.MAP_MIN_Y + (_y * 128) + 48; + } + + @Override + public int getZ() + { + return _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; + if (_x != other._x) + { + return false; + } + if (_y != other._y) + { + return false; + } + if (_z != other._z) + { + return false; + } + return true; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoPathFinding.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoPathFinding.java similarity index 71% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoPathFinding.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoPathFinding.java index c0f5a43d16..c6a4ca3ab7 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/geonodes/GeoPathFinding.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/geonodes/GeoPathFinding.java @@ -1,470 +1,469 @@ -/* - * 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 . - */ -package com.l2jmobius.gameserver.pathfinding.geonodes; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.LineNumberReader; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.logging.Logger; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; -import com.l2jmobius.gameserver.model.L2World; -import com.l2jmobius.gameserver.model.Location; -import com.l2jmobius.gameserver.pathfinding.AbstractNode; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; -import com.l2jmobius.gameserver.pathfinding.PathFinding; -import com.l2jmobius.gameserver.pathfinding.utils.FastNodeList; - -import javolution.util.FastList; -import javolution.util.FastMap; - -/** - * @author -Nemesiss- - */ -public class GeoPathFinding extends PathFinding -{ - private static Logger _log = Logger.getLogger(GeoPathFinding.class.getName()); - private static GeoPathFinding _instance; - private static Map pathNodes = new FastMap<>(); - private static Map pathNodes_index = new FastMap<>(); - - public static GeoPathFinding getInstance() - { - if (_instance == null) - { - _instance = new GeoPathFinding(); - } - return _instance; - } - - /** - * @see com.l2jmobius.gameserver.pathfinding.PathFinding#pathNodesExist(short) - */ - @Override - public boolean pathNodesExist(short regionoffset) - { - return pathNodes_index.containsKey(regionoffset); - } - - @Override - public List findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable) - { - final int gx = (x - L2World.MAP_MIN_X) >> 4; - final int gy = (y - L2World.MAP_MIN_Y) >> 4; - final short gz = (short) z; - final int gtx = (tx - L2World.MAP_MIN_X) >> 4; - final int gty = (ty - L2World.MAP_MIN_Y) >> 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; - } - Location temp = GeoData.getInstance().moveCheck(x, y, z, start.getLoc().getX(), start.getLoc().getY(), start.getLoc().getZ()); - if ((temp.getX() != start.getLoc().getX()) || (temp.getY() != start.getLoc().getY())) - { - return null; // cannot reach closest... - } - - temp = GeoData.getInstance().moveCheck(tx, ty, tz, end.getLoc().getX(), end.getLoc().getY(), end.getLoc().getZ()); - if ((temp.getX() != end.getLoc().getX()) || (temp.getY() != end.getLoc().getY())) - { - return null; // cannot reach closest... - } - - return searchByClosest2(start, end); - } - - public List 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 to_visit = new LinkedList<>(); - to_visit.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 = to_visit.removeFirst(); - } - catch (final Exception e) - { - // No Path found - return null; - } - - if (node.equals(end)) - { - return constructPath2(node); - } - - i++; - visited.add(node); - node.attachNeighbors(); - final GeoNode[] neighbors = node.getNeighbors(); - if (neighbors == null) - { - continue; - } - for (final GeoNode n : neighbors) - { - if (!visited.containsRev(n) && !to_visit.contains(n)) - { - added = false; - n.setParent(node); - dx = targetX - n.getLoc().getNodeX(); - dy = targetY - n.getLoc().getNodeY(); - n.setCost((dx * dx) + (dy * dy)); - for (int index = 0; index < to_visit.size(); index++) - { - // supposed to find it quite early.. - if (to_visit.get(index).getCost() > n.getCost()) - { - to_visit.add(index, n); - added = true; - break; - } - } - if (!added) - { - to_visit.addLast(n); - } - } - } - } - // No Path found - return null; - } - - public List constructPath2(AbstractNode node) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = -1000; - int previousDirectionY = -1000; - int directionX; - int directionY; - - while (node.getParent() != null) - { - // only add a new route point if moving direction changes - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - path.addFirst(node.getLoc()); - } - node = node.getParent(); - } - return path; - } - - public GeoNode[] readNeighbors(GeoNode n, int idx) - { - final int node_x = n.getLoc().getNodeX(); - final int node_y = n.getLoc().getNodeY(); - - final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y)); - final ByteBuffer pn = pathNodes.get(regoffset); - - final List Neighbors = new FastList<>(8); - - GeoNode newNode; - short new_node_x, new_node_y; - - // Region for sure will change, we must read from correct file - byte neighbor = pn.get(idx); // N - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) node_x; - new_node_y = (short) (node_y - 1); - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // NE - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) (node_x + 1); - new_node_y = (short) (node_y - 1); - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // E - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) (node_x + 1); - new_node_y = (short) node_y; - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // SE - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) (node_x + 1); - new_node_y = (short) (node_y + 1); - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // S - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) node_x; - new_node_y = (short) (node_y + 1); - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // SW - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) (node_x - 1); - new_node_y = (short) (node_y + 1); - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // W - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) (node_x - 1); - new_node_y = (short) node_y; - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - neighbor = pn.get(idx); // NW - idx++; - if (neighbor > 0) - { - neighbor--; - new_node_x = (short) (node_x - 1); - new_node_y = (short) (node_y - 1); - newNode = readNode(new_node_x, new_node_y, neighbor); - if (newNode != null) - { - Neighbors.add(newNode); - } - } - final GeoNode[] result = new GeoNode[Neighbors.size()]; - return Neighbors.toArray(result); - } - - private GeoNode readNode(short node_x, short node_y, byte layer) - { - final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y)); - if (!pathNodesExist(regoffset)) - { - return null; - } - final short nbx = getNodeBlock(node_x); - final short nby = getNodeBlock(node_y); - int idx = pathNodes_index.get(regoffset).get((nby << 8) + nbx); - final ByteBuffer pn = pathNodes.get(regoffset); - // reading - final byte nodes = pn.get(idx); - idx += (layer * 10) + 1;// byte + layer*10byte - if (nodes < layer) - { - _log.warning("SmthWrong!"); - } - final short node_z = pn.getShort(idx); - idx += 2; - return new GeoNode(new GeoNodeLoc(node_x, node_y, node_z), idx); - } - - private GeoNode readNode(int gx, int gy, short z) - { - final short node_x = getNodePos(gx); - final short node_y = getNodePos(gy); - final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y)); - if (!pathNodesExist(regoffset)) - { - return null; - } - final short nbx = getNodeBlock(node_x); - final short nby = getNodeBlock(node_y); - int idx = pathNodes_index.get(regoffset).get((nby << 8) + nbx); - final ByteBuffer pn = pathNodes.get(regoffset); - // reading - byte nodes = pn.get(idx); - idx++;// byte - int idx2 = 0; // create index to nearlest node by z - short last_z = Short.MIN_VALUE; - while (nodes > 0) - { - final short node_z = pn.getShort(idx); - if (Math.abs(last_z - z) > Math.abs(node_z - z)) - { - last_z = node_z; - idx2 = idx + 2; - } - idx += 10; // short + 8 byte - nodes--; - } - return new GeoNode(new GeoNodeLoc(node_x, node_y, last_z), idx2); - } - - private GeoPathFinding() - { - final File Data = new File(Config.PATHNODE_DIR, "pn_index.txt"); - try (FileReader fr = new FileReader(Data); - BufferedReader br = new BufferedReader(fr); - LineNumberReader lnr = new LineNumberReader(br)) - { - _log.info("PathFinding Engine: - Loading Path Nodes..."); - String line; - - while ((line = lnr.readLine()) != null) - { - if (line.trim().length() == 0) - { - continue; - } - final StringTokenizer st = new StringTokenizer(line, "_"); - final byte rx = Byte.parseByte(st.nextToken()); - final byte ry = Byte.parseByte(st.nextToken()); - LoadPathNodeFile(rx, ry); - } - } - catch (final Exception e) - { - e.printStackTrace(); - throw new Error("Failed to Read pn_index File."); - } - } - - private void LoadPathNodeFile(byte rx, byte ry) - { - final short regionoffset = getRegionOffset(rx, ry); - final File pn = new File(Config.PATHNODE_DIR, rx + "_" + ry + ".pn"); - _log.info("PathFinding Engine: - Loading: " + pn.getName() + " -> region offset: " + regionoffset + "X: " + rx + " Y: " + ry); - int node = 0, size, index = 0; - - // Create a read-only memory-mapped file - try (RandomAccessFile raf = new RandomAccessFile(pn, "r"); - FileChannel roChannel = raf.getChannel()) - { - size = (int) roChannel.size(); - MappedByteBuffer nodes; - if (Config.FORCE_GEODATA) // Force O/S to Loads this buffer's content into physical memory. - { - // it is not guarantee, because the underlying operating system may have paged out some of the buffer's data - nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load(); - } - else - { - nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size); - } - - // 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); - node++; - index += (layer * 10) + 1; - } - pathNodes_index.put(regionoffset, indexs); - pathNodes.put(regionoffset, nodes); - } - catch (final Exception e) - { - e.printStackTrace(); - _log.warning("Failed to Load PathNode File: " + pn.getAbsolutePath() + "\n"); - } - } -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.geonodes; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.geodata.GeoData; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNode; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc; +import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding; +import com.l2jmobius.gameserver.model.L2World; +import com.l2jmobius.gameserver.model.Location; +import com.l2jmobius.gameserver.util.Util; + +/** + * @author -Nemesiss- + */ +public class GeoPathFinding extends PathFinding +{ + private static Logger _log = Logger.getLogger(GeoPathFinding.class.getName()); + private static Map _pathNodes = new ConcurrentHashMap<>(); + private static Map _pathNodesIndex = new ConcurrentHashMap<>(); + + public static GeoPathFinding getInstance() + { + return SingletonHolder._instance; + } + + @Override + public boolean pathNodesExist(short regionoffset) + { + return _pathNodesIndex.containsKey(regionoffset); + } + + @Override + public List findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable) + { + final int gx = (x - L2World.MAP_MIN_X) >> 4; + final int gy = (y - L2World.MAP_MIN_Y) >> 4; + final short gz = (short) z; + final int gtx = (tx - L2World.MAP_MIN_X) >> 4; + final int gty = (ty - L2World.MAP_MIN_Y) >> 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 = GeoData.getInstance().moveCheck(x, y, z, start.getLoc().getX(), start.getLoc().getY(), start.getLoc().getZ()); + 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 = GeoData.getInstance().moveCheck(tx, ty, tz, end.getLoc().getX(), end.getLoc().getY(), end.getLoc().getZ()); + if ((temp.getX() != end.getLoc().getX()) || (temp.getY() != end.getLoc().getY())) + { + return null; // cannot reach closest... + } + + // return searchAStar(start, end); + return searchByClosest2(start, end); + } + + public List 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 List visited = new ArrayList<>(550); + + // List of Nodes to Visit + final LinkedList to_visit = new LinkedList<>(); + to_visit.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 = to_visit.removeFirst(); + } + catch (Exception e) + { + // No Path found + return null; + } + if (node.equals(end)) + { + return constructPath2(node); + } + + i++; + visited.add(node); + node.attachNeighbors(); + final GeoNode[] neighbors = node.getNeighbors(); + if (neighbors == null) + { + continue; + } + for (GeoNode n : neighbors) + { + if ((visited.lastIndexOf(n) == -1) && !to_visit.contains(n)) + { + added = false; + n.setParent(node); + dx = targetX - n.getLoc().getNodeX(); + dy = targetY - n.getLoc().getNodeY(); + n.setCost((dx * dx) + (dy * dy)); + for (int index = 0; index < to_visit.size(); index++) + { + // supposed to find it quite early.. + if (to_visit.get(index).getCost() > n.getCost()) + { + to_visit.add(index, n); + added = true; + break; + } + } + if (!added) + { + to_visit.addLast(n); + } + } + } + } + // No Path found + return null; + } + + public List constructPath2(AbstractNode node) + { + final LinkedList path = new LinkedList<>(); + int previousDirectionX = -1000; + int previousDirectionY = -1000; + int directionX; + int directionY; + + while (node.getParent() != null) + { + // only add a new route point if moving direction changes + directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); + directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); + + if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) + { + previousDirectionX = directionX; + previousDirectionY = directionY; + path.addFirst(node.getLoc()); + } + node = node.getParent(); + } + return path; + } + + public GeoNode[] readNeighbors(GeoNode n, int idx) + { + final int node_x = n.getLoc().getNodeX(); + final int node_y = n.getLoc().getNodeY(); + // short node_z = n.getLoc().getZ(); + + final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y)); + final ByteBuffer pn = _pathNodes.get(regoffset); + + final List> Neighbors = new ArrayList<>(8); + GeoNode newNode; + short new_node_x, new_node_y; + + // Region for sure will change, we must read from correct file + byte neighbor = pn.get(idx++); // N + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) node_x; + new_node_y = (short) (node_y - 1); + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // NE + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) (node_x + 1); + new_node_y = (short) (node_y - 1); + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // E + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) (node_x + 1); + new_node_y = (short) node_y; + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // SE + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) (node_x + 1); + new_node_y = (short) (node_y + 1); + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // S + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) node_x; + new_node_y = (short) (node_y + 1); + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // SW + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) (node_x - 1); + new_node_y = (short) (node_y + 1); + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // W + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) (node_x - 1); + new_node_y = (short) node_y; + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + neighbor = pn.get(idx++); // NW + if (neighbor > 0) + { + neighbor--; + new_node_x = (short) (node_x - 1); + new_node_y = (short) (node_y - 1); + newNode = readNode(new_node_x, new_node_y, neighbor); + if (newNode != null) + { + Neighbors.add(newNode); + } + } + final GeoNode[] result = new GeoNode[Neighbors.size()]; + return Neighbors.toArray(result); + } + + // Private + + private GeoNode readNode(short node_x, short node_y, byte layer) + { + final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y)); + if (!pathNodesExist(regoffset)) + { + return null; + } + final short nbx = getNodeBlock(node_x); + final short nby = getNodeBlock(node_y); + int idx = _pathNodesIndex.get(regoffset).get((nby << 8) + nbx); + final ByteBuffer pn = _pathNodes.get(regoffset); + // reading + final byte nodes = pn.get(idx); + idx += (layer * 10) + 1; // byte + layer*10byte + if (nodes < layer) + { + _log.warning("SmthWrong!"); + } + final short node_z = pn.getShort(idx); + idx += 2; + return new GeoNode(new GeoNodeLoc(node_x, node_y, node_z), idx); + } + + private GeoNode readNode(int gx, int gy, short z) + { + final short node_x = getNodePos(gx); + final short node_y = getNodePos(gy); + final short regoffset = getRegionOffset(getRegionX(node_x), getRegionY(node_y)); + if (!pathNodesExist(regoffset)) + { + return null; + } + final short nbx = getNodeBlock(node_x); + final short nby = getNodeBlock(node_y); + int idx = _pathNodesIndex.get(regoffset).get((nby << 8) + nbx); + final ByteBuffer pn = _pathNodes.get(regoffset); + // reading + byte nodes = pn.get(idx++); + int idx2 = 0; // create index to nearlest node by z + short last_z = Short.MIN_VALUE; + while (nodes > 0) + { + final short node_z = pn.getShort(idx); + if (Math.abs(last_z - z) > Math.abs(node_z - z)) + { + last_z = node_z; + idx2 = idx + 2; + } + idx += 10; // short + 8 byte + nodes--; + } + return new GeoNode(new GeoNodeLoc(node_x, node_y, last_z), idx2); + } + + protected GeoPathFinding() + { + try + { + _log.info("Path Engine: - Loading Path Nodes..."); + //@formatter:off + Files.lines(Paths.get(Config.PATHNODE_DIR.getPath(), "pn_index.txt"), StandardCharsets.UTF_8) + .map(String::trim) + .filter(l -> !l.isEmpty()) + .forEach(line -> { + final String[] parts = line.split("_"); + + if ((parts.length < 2) + || !Util.isDigit(parts[0]) + || !Util.isDigit(parts[1])) + { + _log.warning("Invalid pathnode entry: '" + line + "', must be in format 'XX_YY', where X and Y - integers"); + return; + } + + final byte rx = Byte.parseByte(parts[0]); + final byte ry = Byte.parseByte(parts[1]); + LoadPathNodeFile(rx, ry); + }); + //@formatter:on + } + catch (IOException e) + { + _log.log(Level.WARNING, "", e); + throw new Error("Failed to read pn_index file."); + } + } + + private void LoadPathNodeFile(byte rx, byte ry) + { + if ((rx < L2World.TILE_X_MIN) || (rx > L2World.TILE_X_MAX) || (ry < L2World.TILE_Y_MIN) || (ry > L2World.TILE_Y_MAX)) + { + _log.warning("Failed to Load PathNode File: invalid region " + rx + "," + ry + Config.EOL); + return; + } + final short regionoffset = getRegionOffset(rx, ry); + final File file = new File(Config.PATHNODE_DIR, rx + "_" + ry + ".pn"); + _log.info("Path Engine: - Loading: " + file.getName() + " -> region offset: " + regionoffset + " X: " + rx + " Y: " + ry); + int node = 0, size, index = 0; + + // Create a read-only memory-mapped file + try (RandomAccessFile raf = new RandomAccessFile(file, "r"); + FileChannel roChannel = raf.getChannel()) + { + size = (int) roChannel.size(); + MappedByteBuffer nodes; + if (Config.FORCE_GEODATA) + { + // it is not guarantee, because the underlying operating system may have paged out some of the buffer's data + nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load(); + } + else + { + nodes = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size); + } + + // 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; + } + _pathNodesIndex.put(regionoffset, indexs); + _pathNodes.put(regionoffset, nodes); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Failed to Load PathNode File: " + file.getAbsolutePath() + " : " + e.getMessage(), e); + } + } + + private static class SingletonHolder + { + protected static final GeoPathFinding _instance = new GeoPathFinding(); + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/utils/BinaryNodeHeap.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/utils/BinaryNodeHeap.java similarity index 90% rename from L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/utils/BinaryNodeHeap.java rename to L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/utils/BinaryNodeHeap.java index 866122a675..a6aae4c662 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/utils/BinaryNodeHeap.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/utils/BinaryNodeHeap.java @@ -1,124 +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 . - */ -package com.l2jmobius.gameserver.pathfinding.utils; - -import com.l2jmobius.gameserver.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; - } -} \ No newline at end of file +/* + * 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 . + */ +package com.l2jmobius.gameserver.geodata.pathfinding.utils; + +import com.l2jmobius.gameserver.geodata.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; + } +} diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorListener.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorListener.java deleted file mode 100644 index f037547a53..0000000000 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorListener.java +++ /dev/null @@ -1,111 +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 . - */ -package com.l2jmobius.gameserver.geoeditorcon; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Dezmond - */ -public class GeoEditorListener extends Thread -{ - private static GeoEditorListener _instance; - private static final int PORT = 9011; - private static Logger _log = Logger.getLogger(GeoEditorListener.class.getName()); - private final ServerSocket _serverSocket; - private static GeoEditorThread _geoEditor; - - public static GeoEditorListener getInstance() - { - if (_instance == null) - { - try - { - _instance = new GeoEditorListener(); - _instance.start(); - _log.info("GeoEditorListener Initialized."); - } - catch (final IOException e) - { - _log.severe("Error creating geoeditor listener! " + e.getMessage()); - System.exit(1); - } - } - return _instance; - } - - private GeoEditorListener() throws IOException - { - _serverSocket = new ServerSocket(PORT); - } - - public GeoEditorThread getThread() - { - return _geoEditor; - } - - public String getStatus() - { - if ((_geoEditor != null) && _geoEditor.isWorking()) - { - return "Geoeditor connected."; - } - - return "Geoeditor not connected."; - } - - @Override - public void run() - { - try (Socket connection = _serverSocket.accept()) - { - while (true) - { - if ((_geoEditor != null) && _geoEditor.isWorking()) - { - _log.warning("Geoeditor already connected!"); - connection.close(); - continue; - } - - _log.info("Received geoeditor connection from: " + connection.getInetAddress().getHostAddress()); - _geoEditor = new GeoEditorThread(connection); - _geoEditor.start(); - } - } - catch (final Exception e) - { - _log.info("GeoEditorListener: " + e.getMessage()); - } - finally - { - try - { - _serverSocket.close(); - } - catch (final IOException io) - { - _log.log(Level.INFO, "", io); - } - _log.warning("GeoEditorListener Closed!"); - } - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorThread.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorThread.java deleted file mode 100644 index 04296e371d..0000000000 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorThread.java +++ /dev/null @@ -1,300 +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 . - */ -package com.l2jmobius.gameserver.geoeditorcon; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.Socket; -import java.net.SocketException; -import java.util.logging.Logger; - -import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; - -import javolution.util.FastList; - -/** - * @author Luno, Dezmond - */ -public class GeoEditorThread extends Thread -{ - private static Logger _log = Logger.getLogger(GeoEditorThread.class.getName()); - - private boolean _working = false; - - private int _mode = 0; // 0 - don't send coords, 1 - send each - - // validateposition from client, 2 - send in - // intervals of _sendDelay ms. - private int _sendDelay = 1000; // default - once in second - - private final Socket _geSocket; - - private OutputStream _out; - - private final FastList _gms; - - public GeoEditorThread(Socket ge) - { - _geSocket = ge; - _working = true; - _gms = new FastList<>(); - } - - @Override - public void interrupt() - { - try - { - _geSocket.close(); - } - catch (final Exception e) - { - } - super.interrupt(); - } - - @Override - public void run() - { - try - { - _out = _geSocket.getOutputStream(); - int timer = 0; - - while (_working) - { - if (!isConnected()) - { - _working = false; - } - - if ((_mode == 2) && (timer > _sendDelay)) - { - for (final L2PcInstance gm : _gms) - { - if (gm == null) - { - continue; - } - - if (!gm.getClient().getConnection().isClosed()) - { - sendGmPosition(gm); - } - else - { - _gms.remove(gm); - } - } - - timer = 0; - } - - try - { - sleep(100); - if (_mode == 2) - { - timer += 100; - } - } - catch (final Exception e) - { - } - } - } - catch (final SocketException e) - { - _log.warning("GeoEditor disconnected. " + e.getMessage()); - } - catch (final Exception e) - { - e.printStackTrace(); - } - finally - { - try - { - _geSocket.close(); - } - catch (final Exception e) - { - } - _working = false; - } - } - - public void sendGmPosition(int gx, int gy, short z) - { - if (!isConnected()) - { - return; - } - - try - { - synchronized (_out) - { - writeC(0x0b); // length 11 bytes! - writeC(0x01); // Cmd = save cell; - writeD(gx); // Global coord X; - writeD(gy); // Global coord Y; - writeH(z); // Coord Z; - _out.flush(); - } - } - catch (final SocketException e) - { - _log.warning("GeoEditor disconnected. " + e.getMessage()); - _working = false; - } - catch (final Exception e) - { - e.printStackTrace(); - try - { - _geSocket.close(); - } - catch (final Exception ex) - { - } - _working = false; - } - } - - public void sendGmPosition(L2PcInstance _gm) - { - sendGmPosition(_gm.getX(), _gm.getY(), (short) _gm.getZ()); - } - - public void sendPing() - { - if (!isConnected()) - { - return; - } - - try - { - synchronized (_out) - { - writeC(0x01); // length 1 byte! - writeC(0x02); // Cmd = ping (dummy packet for connection - _out.flush(); - } - } - catch (final SocketException e) - { - _log.warning("GeoEditor disconnected. " + e.getMessage()); - _working = false; - } - catch (final Exception e) - { - e.printStackTrace(); - try - { - _geSocket.close(); - } - catch (final Exception ex) - { - } - _working = false; - } - } - - private void writeD(int value) throws IOException - { - _out.write(value & 0xff); - _out.write((value >> 8) & 0xff); - _out.write((value >> 16) & 0xff); - _out.write((value >> 24) & 0xff); - } - - private void writeH(int value) throws IOException - { - _out.write(value & 0xff); - _out.write((value >> 8) & 0xff); - } - - private void writeC(int value) throws IOException - { - _out.write(value & 0xff); - } - - public void setMode(int value) - { - _mode = value; - } - - public void setTimer(int value) - { - if (value < 500) - { - _sendDelay = 500; // maximum - 2 times per second! - } - else if (value > 60000) - { - _sendDelay = 60000; // Minimum - 1 time per minute. - } - else - { - _sendDelay = value; - } - } - - public void addGM(L2PcInstance gm) - { - if (!_gms.contains(gm)) - { - _gms.add(gm); - } - } - - public void removeGM(L2PcInstance gm) - { - if (_gms.contains(gm)) - { - _gms.remove(gm); - } - } - - public boolean isSend(L2PcInstance gm) - { - if ((_mode == 1) && _gms.contains(gm)) - { - return true; - } - - return false; - } - - private boolean isConnected() - { - return _geSocket.isConnected() && !_geSocket.isClosed(); - } - - public boolean isWorking() - { - sendPing(); - return _working; - } - - public int getMode() - { - return _mode; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeoEditor.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeoEditor.java deleted file mode 100644 index 369b76a096..0000000000 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeoEditor.java +++ /dev/null @@ -1,134 +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 . - */ -package com.l2jmobius.gameserver.handler.admincommandhandlers; - -import java.util.StringTokenizer; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.geoeditorcon.GeoEditorListener; -import com.l2jmobius.gameserver.handler.IAdminCommandHandler; -import com.l2jmobius.gameserver.model.GMAudit; -import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; - -/** - * @author Luno, Dezmond - */ -public class AdminGeoEditor implements IAdminCommandHandler -{ - private static String[] _adminCommands = - { - "admin_ge_status", - "admin_ge_mode", - "admin_ge_join", - "admin_ge_leave" - }; - - private static final int REQUIRED_LEVEL = Config.GM_MIN; - - @Override - public boolean useAdminCommand(String command, L2PcInstance activeChar) - { - if (!Config.ALT_PRIVILEGES_ADMIN) - { - if (!(checkLevel(activeChar.getAccessLevel()) && activeChar.isGM())) - { - return false; - } - } - - final String target = (activeChar.getTarget() != null) ? activeChar.getTarget().getName() : "no-target"; - GMAudit.auditGMAction(activeChar.getName(), command, target, ""); - - if (!Config.ACCEPT_GEOEDITOR_CONN) - { - activeChar.sendMessage("Server does not accept geoeditor connections now."); - return true; - } - - if (command.startsWith("admin_ge_status")) - { - activeChar.sendMessage(GeoEditorListener.getInstance().getStatus()); - } - else if (command.startsWith("admin_ge_mode")) - { - if (GeoEditorListener.getInstance().getThread() == null) - { - activeChar.sendMessage("Geoeditor is not connected."); - return true; - } - - try - { - final String val = command.substring("admin_ge_mode".length()); - final StringTokenizer st = new StringTokenizer(val); - - if (st.countTokens() < 1) - { - activeChar.sendMessage("Usage: //ge_mode X"); - activeChar.sendMessage("Mode 0: Don't send coordinates to geoeditor."); - activeChar.sendMessage("Mode 1: Send coordinates at ValidatePosition from clients."); - activeChar.sendMessage("Mode 2: Send coordinates each second."); - return true; - } - - final int m = Integer.parseInt(st.nextToken()); - GeoEditorListener.getInstance().getThread().setMode(m); - activeChar.sendMessage("Geoeditor connection mode set to " + m + "."); - } - catch (final Exception e) - { - activeChar.sendMessage("Usage: //ge_mode X"); - activeChar.sendMessage("Mode 0: Don't send coordinates to geoeditor."); - activeChar.sendMessage("Mode 1: Send coordinates at ValidatePosition from clients."); - activeChar.sendMessage("Mode 2: Send coordinates each second."); - e.printStackTrace(); - } - } - else if (command.equals("admin_ge_join")) - { - if (GeoEditorListener.getInstance().getThread() == null) - { - activeChar.sendMessage("Geoeditor is not connected."); - return true; - } - GeoEditorListener.getInstance().getThread().addGM(activeChar); - activeChar.sendMessage("You have been added to geoeditor's list."); - } - else if (command.equals("admin_ge_leave")) - { - if (GeoEditorListener.getInstance().getThread() == null) - { - activeChar.sendMessage("Geoeditor is not connected."); - return true; - } - GeoEditorListener.getInstance().getThread().removeGM(activeChar); - activeChar.sendMessage("You have been removed from geoeditor's list."); - } - return true; - } - - @Override - public String[] getAdminCommandList() - { - return _adminCommands; - } - - private boolean checkLevel(int level) - { - return (level >= REQUIRED_LEVEL); - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java index 8d2c01a48c..90046925c9 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java @@ -17,10 +17,12 @@ package com.l2jmobius.gameserver.handler.admincommandhandlers; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.handler.IAdminCommandHandler; import com.l2jmobius.gameserver.model.L2Object; +import com.l2jmobius.gameserver.model.L2World; import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; +import com.l2jmobius.gameserver.util.GeoUtils; /** * @author -Nemesiss- @@ -32,7 +34,9 @@ public class AdminGeodata implements IAdminCommandHandler "admin_geo_pos", "admin_geo_spawn_pos", "admin_geo_can_move", - "admin_geo_can_see" + "admin_geo_can_see", + "admin_geogrid", + "admin_geomap" }; private static final int REQUIRED_LEVEL = Config.GM_MIN; @@ -75,7 +79,7 @@ public class AdminGeodata implements IAdminCommandHandler if (GeoData.getInstance().hasGeoPos(geoX, geoY)) { - activeChar.sendMessage("WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoData.getInstance().getSpawnHeight(worldX, worldY, worldZ, worldZ)); + activeChar.sendMessage("WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoData.getInstance().getSpawnHeight(worldX, worldY, worldZ)); } else { @@ -120,6 +124,16 @@ public class AdminGeodata implements IAdminCommandHandler activeChar.sendMessage("Incorrect Target."); } } + else if (command.equals("admin_geogrid")) + { + GeoUtils.debugGrid(activeChar); + } + else if (command.equals("admin_geomap")) + { + final int x = ((activeChar.getX() - L2World.MAP_MIN_X) >> 15) + L2World.TILE_X_MIN; + final int y = ((activeChar.getY() - L2World.MAP_MIN_Y) >> 15) + L2World.TILE_Y_MIN; + activeChar.sendMessage("GeoMap: " + x + "_" + y); + } else { return false; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminPathNode.java index 18fbd4610e..4058a2b5fa 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminPathNode.java @@ -19,10 +19,10 @@ package com.l2jmobius.gameserver.handler.admincommandhandlers; import java.util.List; import com.l2jmobius.Config; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc; +import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding; import com.l2jmobius.gameserver.handler.IAdminCommandHandler; import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; -import com.l2jmobius.gameserver.pathfinding.PathFinding; public class AdminPathNode implements IAdminCommandHandler { @@ -79,7 +79,7 @@ public class AdminPathNode implements IAdminCommandHandler } else if (command.equals("admin_find_path")) { - if (Config.GEODATA < 2) + if (Config.PATHFINDING < 2) { activeChar.sendMessage("PathFinding has not been enabled."); return true; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/skillhandlers/Fishing.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/skillhandlers/Fishing.java index d9084507c2..5f7d40b693 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/skillhandlers/Fishing.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/skillhandlers/Fishing.java @@ -17,8 +17,8 @@ package com.l2jmobius.gameserver.handler.skillhandlers; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.datatables.ZoneTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.handler.ISkillHandler; import com.l2jmobius.gameserver.model.Inventory; import com.l2jmobius.gameserver.model.L2Character; @@ -169,7 +169,7 @@ public class Fishing implements ISkillHandler if (aimingTo != null) { // fishing zone found, we can fish here - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { // geodata enabled, checking if we can see end of the pole if (GeoData.getInstance().canSeeTarget(player.getX(), player.getY(), z, x, y, z)) diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/Inventory.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/Inventory.java index fc95817aa5..37ac7552e1 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/Inventory.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/Inventory.java @@ -72,6 +72,8 @@ public abstract class Inventory extends ItemContainer // Speed percentage mods public static final double MAX_ARMOR_WEIGHT = 12000; + public static final int ADENA_ID = 57; + private final L2ItemInstance[] _paperdoll; private final List _paperdollListeners; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Character.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Character.java index 04b9ea93bf..fc34af77c5 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Character.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Character.java @@ -26,7 +26,6 @@ import java.util.logging.Logger; import com.l2jmobius.Config; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ThreadPoolManager; import com.l2jmobius.gameserver.ai.CtrlEvent; import com.l2jmobius.gameserver.ai.CtrlIntention; @@ -36,6 +35,9 @@ import com.l2jmobius.gameserver.datatables.DoorTable; import com.l2jmobius.gameserver.datatables.MapRegionTable; import com.l2jmobius.gameserver.datatables.MapRegionTable.TeleportWhereType; import com.l2jmobius.gameserver.datatables.SkillTable; +import com.l2jmobius.gameserver.geodata.GeoData; +import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc; +import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding; import com.l2jmobius.gameserver.handler.ISkillHandler; import com.l2jmobius.gameserver.handler.SkillHandler; import com.l2jmobius.gameserver.instancemanager.TownManager; @@ -80,8 +82,6 @@ import com.l2jmobius.gameserver.network.serverpackets.StatusUpdate; import com.l2jmobius.gameserver.network.serverpackets.StopMove; import com.l2jmobius.gameserver.network.serverpackets.SystemMessage; import com.l2jmobius.gameserver.network.serverpackets.TeleportToLocation; -import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc; -import com.l2jmobius.gameserver.pathfinding.PathFinding; import com.l2jmobius.gameserver.skills.Calculator; import com.l2jmobius.gameserver.skills.Formulas; import com.l2jmobius.gameserver.skills.Stats; @@ -4407,10 +4407,10 @@ public abstract class L2Character extends L2Object final boolean isFloating = isFlying() || isInsideZone(L2Character.ZONE_WATER); // Z coordinate will follow geodata or client values - if ((Config.GEODATA > 0) && (Config.COORD_SYNCHRONIZE == 2) && !isFloating && !m.disregardingGeodata && !(this instanceof L2BoatInstance) && ((GameTimeController.getGameTicks() % 10) == 0 // once a second to reduce possible cpu load + if ((Config.PATHFINDING > 0) && (Config.COORD_SYNCHRONIZE == 2) && !isFloating && !m.disregardingGeodata && !(this instanceof L2BoatInstance) && ((GameTimeController.getGameTicks() % 10) == 0 // once a second to reduce possible cpu load ) && GeoData.getInstance().hasGeo(xPrev, yPrev)) { - final int geoHeight = GeoData.getInstance().getSpawnHeight(xPrev, yPrev, zPrev - 30, zPrev + 30); + final int geoHeight = GeoData.getInstance().getSpawnHeight(xPrev, yPrev, zPrev - 30); dz = m._zDestination - geoHeight; // quite a big difference, compare to validatePosition packet if ((this instanceof L2PcInstance) && (Math.abs(((L2PcInstance) this).getClientZ() - geoHeight) > 200) && (Math.abs(((L2PcInstance) this).getClientZ() - geoHeight) < 1500)) @@ -4650,7 +4650,7 @@ public abstract class L2Character extends L2Object // make water move short and use no geodata checks for swimming chars // distance in a click can easily be over 3000 - if ((Config.GEODATA > 0) && !(this instanceof L2BoatInstance) && isInsideZone(ZONE_WATER) && (distance > 700)) + if ((Config.PATHFINDING > 0) && !(this instanceof L2BoatInstance) && isInsideZone(ZONE_WATER) && (distance > 700)) { final double divider = 700 / distance; x = curX + (int) (divider * dx); @@ -4728,7 +4728,7 @@ public abstract class L2Character extends L2Object m.onGeodataPathIndex = -1; // Initialize not on geodata path m.disregardingGeodata = false; - if ((Config.GEODATA > 0) && !isFlying() // currently flying characters not checked + if ((Config.PATHFINDING > 0) && !isFlying() // currently flying characters not checked && !(this instanceof L2NpcWalkerInstance) && !(this instanceof L2BoatInstance) && (!isInsideZone(ZONE_WATER) || isInsideZone(ZONE_SIEGE))) // swimming also not checked - but distance is limited { final boolean isInBoat = (this instanceof L2PcInstance) && ((L2PcInstance) this).isInBoat(); @@ -4747,12 +4747,7 @@ public abstract class L2Character extends L2Object // Movement checks: // when geodata == 2, for all characters except mobs returning home (could be changed later to teleport if pathfinding fails) // when geodata == 1, for l2playableinstance and l2riftinvaderinstance - if (((Config.GEODATA == 2) && !((this instanceof L2Attackable) && ((L2Attackable) this).isReturningToSpawnPoint())) || ((this instanceof L2PcInstance) && !(isInBoat && (distance > 1500))) || isAfraid() || (this instanceof L2RiftInvaderInstance) || ((this instanceof L2Summon) && !(getAI().getIntention() == CtrlIntention.AI_INTENTION_FOLLOW))) // assuming - // intention_follow - // only - // when - // following - // owner + if (((Config.PATHFINDING == 2) && !((this instanceof L2Attackable) && ((L2Attackable) this).isReturningToSpawnPoint())) || ((this instanceof L2PcInstance) && !(isInBoat && (distance > 1500))) || isAfraid() || (this instanceof L2RiftInvaderInstance) || ((this instanceof L2Summon) && !(getAI().getIntention() == CtrlIntention.AI_INTENTION_FOLLOW))) // assuming { if (isOnGeodataPath()) { @@ -4806,7 +4801,7 @@ public abstract class L2Character extends L2Object // Pathfinding checks. Only when geodata setting is 2, the LoS check gives shorter result // than the original movement was and the LoS gives a shorter distance than 2000 // This way of detecting need for pathfinding could be changed. - if ((Config.GEODATA == 2) && ((originalDistance - distance) > 30) && (distance < 2000) && !isAfraid()) + if ((Config.PATHFINDING == 2) && ((originalDistance - distance) > 30) && (distance < 2000) && !isAfraid()) { // Path calculation // Overrides previous movement check @@ -4876,7 +4871,7 @@ public abstract class L2Character extends L2Object } // If no distance to go through, the movement is cancelled - if ((distance < 1) && ((Config.GEODATA == 2) || (this instanceof L2PlayableInstance) || isAfraid() || (this instanceof L2RiftInvaderInstance))) + if ((distance < 1) && ((Config.PATHFINDING == 2) || (this instanceof L2PlayableInstance) || isAfraid() || (this instanceof L2RiftInvaderInstance))) { if (this instanceof L2Summon) { diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2ItemInstance.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2ItemInstance.java index 02d9a288f0..021f9ad168 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2ItemInstance.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2ItemInstance.java @@ -27,10 +27,10 @@ import java.util.logging.Logger; import com.l2jmobius.Config; import com.l2jmobius.L2DatabaseFactory; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ThreadPoolManager; import com.l2jmobius.gameserver.ai.CtrlIntention; import com.l2jmobius.gameserver.datatables.ItemTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; import com.l2jmobius.gameserver.instancemanager.MercTicketManager; import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; @@ -931,7 +931,7 @@ public final class L2ItemInstance extends L2Object assert getPosition().getWorldRegion() == null; } - if ((Config.GEODATA > 0) && (dropper != null)) + if ((Config.PATHFINDING > 0) && (dropper != null)) { final Location dropDest = GeoData.getInstance().moveCheck(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z); x = dropDest.getX(); diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Skill.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Skill.java index d5db5a5bdb..8c9949675c 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Skill.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Skill.java @@ -23,8 +23,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.datatables.SkillTreeTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.actor.instance.L2ArtefactInstance; import com.l2jmobius.gameserver.model.actor.instance.L2ChestInstance; import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Spawn.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Spawn.java index 2c2c124cae..5cfea800b1 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Spawn.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Spawn.java @@ -22,9 +22,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.Territory; import com.l2jmobius.gameserver.ThreadPoolManager; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.idfactory.IdFactory; import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance; import com.l2jmobius.gameserver.model.actor.instance.L2NpcInstance; @@ -581,7 +581,7 @@ public class L2Spawn // don't correct z of flying npc's if (!mob.isFlying()) { - newlocz = GeoData.getInstance().getSpawnHeight(newlocx, newlocy, newlocz, newlocz); + newlocz = GeoData.getInstance().getSpawnHeight(newlocx, newlocy, newlocz); } for (final L2Effect f : mob.getAllEffects()) diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Summon.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Summon.java index 3a637cfcd7..2f71f61025 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Summon.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2Summon.java @@ -17,11 +17,11 @@ package com.l2jmobius.gameserver.model; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ai.CtrlIntention; import com.l2jmobius.gameserver.ai.L2CharacterAI; import com.l2jmobius.gameserver.ai.L2SummonAI; import com.l2jmobius.gameserver.datatables.SkillTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.L2Attackable.AggroInfo; import com.l2jmobius.gameserver.model.L2Skill.SkillTargetType; import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance; @@ -213,7 +213,7 @@ public abstract class L2Summon extends L2PlayableInstance player.sendPacket(new ValidateLocation(this)); if (isAutoAttackable(player)) { - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { if (GeoData.getInstance().canSeeTarget(player, this)) { @@ -231,7 +231,7 @@ public abstract class L2Summon extends L2PlayableInstance { // This Action Failed packet avoids player getting stuck when clicking three or more times player.sendPacket(new ActionFailed()); - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { if (GeoData.getInstance().canSeeTarget(player, this)) { diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2World.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2World.java index 3cd4273da3..1eeb6f5151 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2World.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/L2World.java @@ -47,6 +47,10 @@ public final class L2World public static final int SHIFT_BY = 12; /** Map dimensions */ + public static final int TILE_X_MIN = 11; + public static final int TILE_Y_MIN = 10; + public static final int TILE_X_MAX = 28; + public static final int TILE_Y_MAX = 26; public static final int MAP_MIN_X = -131072; public static final int MAP_MAX_X = 228608; public static final int MAP_MIN_Y = -262144; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2ControlTowerInstance.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2ControlTowerInstance.java index 3bd56ea966..9f6e842ff4 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2ControlTowerInstance.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2ControlTowerInstance.java @@ -18,8 +18,8 @@ package com.l2jmobius.gameserver.model.actor.instance; import java.util.List; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ai.CtrlIntention; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.L2Character; import com.l2jmobius.gameserver.model.L2Spawn; import com.l2jmobius.gameserver.network.serverpackets.ActionFailed; diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PcInstance.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PcInstance.java index ccf6abb1a9..de144f5ce5 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PcInstance.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PcInstance.java @@ -35,7 +35,6 @@ import java.util.logging.Level; import com.l2jmobius.Config; import com.l2jmobius.L2DatabaseFactory; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ItemsAutoDestroy; import com.l2jmobius.gameserver.LoginServerThread; import com.l2jmobius.gameserver.Olympiad; @@ -63,6 +62,7 @@ import com.l2jmobius.gameserver.datatables.NobleSkillTable; import com.l2jmobius.gameserver.datatables.NpcTable; import com.l2jmobius.gameserver.datatables.SkillTable; import com.l2jmobius.gameserver.datatables.SkillTreeTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.handler.IItemHandler; import com.l2jmobius.gameserver.handler.ItemHandler; import com.l2jmobius.gameserver.instancemanager.CastleManager; @@ -3996,7 +3996,7 @@ public final class L2PcInstance extends L2PlayableInstance { - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { @@ -4030,7 +4030,7 @@ public final class L2PcInstance extends L2PlayableInstance // This Action Failed packet avoids player getting stuck when clicking three or more times player.sendPacket(new ActionFailed()); - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PetInstance.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PetInstance.java index 034c2e4687..9a3f838879 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PetInstance.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/model/actor/instance/L2PetInstance.java @@ -24,10 +24,10 @@ import java.util.logging.Logger; import com.l2jmobius.Config; import com.l2jmobius.L2DatabaseFactory; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ThreadPoolManager; import com.l2jmobius.gameserver.ai.CtrlIntention; import com.l2jmobius.gameserver.datatables.SkillTable; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.handler.IItemHandler; import com.l2jmobius.gameserver.handler.ItemHandler; import com.l2jmobius.gameserver.idfactory.IdFactory; @@ -316,7 +316,7 @@ public class L2PetInstance extends L2Summon // Check if the pet is attackable (without a forced attack) and isn't dead if (isAutoAttackable(player) && !isOwner) { - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { if (GeoData.getInstance().canSeeTarget(player, this)) { @@ -333,7 +333,7 @@ public class L2PetInstance extends L2Summon } else if (!isInsideRadius(player, 150, false, false)) { - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { if (GeoData.getInstance().canSeeTarget(player, this)) { diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index 6301a26f95..6ba7b1e74a 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -104,7 +104,7 @@ public class MoveBackwardToLocation extends L2GameClientPacket } // Disable keyboard movement when geodata is not enabled and player is not flying. - if ((_moveMovement == 0) && (Config.GEODATA < 1) && !activeChar.isFlying()) + if ((_moveMovement == 0) && (Config.PATHFINDING < 1) && !activeChar.isFlying()) { activeChar.sendPacket(new ActionFailed()); } diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index 7137d30311..a3b61d335c 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -19,7 +19,6 @@ package com.l2jmobius.gameserver.network.clientpackets; import java.util.logging.Logger; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.geoeditorcon.GeoEditorListener; import com.l2jmobius.gameserver.model.L2Character; import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; import com.l2jmobius.gameserver.network.serverpackets.GetOnVehicle; @@ -113,14 +112,6 @@ public class ValidatePosition extends L2GameClientPacket activeChar.getParty().broadcastToPartyMembers(activeChar, new PartyMemberPosition(activeChar)); } - if (Config.ACCEPT_GEOEDITOR_CONN) - { - if ((GeoEditorListener.getInstance().getThread() != null) && GeoEditorListener.getInstance().getThread().isWorking() && GeoEditorListener.getInstance().getThread().isSend(activeChar)) - { - GeoEditorListener.getInstance().getThread().sendGmPosition(_x, _y, (short) _z); - } - } - if (activeChar.isFlying() || activeChar.isInsideZone(L2Character.ZONE_WATER)) { activeChar.setXYZ(realX, realY, _z); @@ -165,7 +156,7 @@ public class ValidatePosition extends L2GameClientPacket // intended for geodata. Sends a validation packet to client // when too far from server calculated true coordinate. // Due to geodata "holes", some Z axis checks are made. - if ((Config.GEODATA > 0) && ((diffSq > 250000) || (Math.abs(dz) > 200))) + if ((Config.PATHFINDING > 0) && ((diffSq > 250000) || (Math.abs(dz) > 200))) { if ((Math.abs(dz) > 200) && (Math.abs(dz) < 1500) && (Math.abs(_z - activeChar.getClientZ()) < 800)) { diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/serverpackets/ExServerPrimitive.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/serverpackets/ExServerPrimitive.java new file mode 100644 index 0000000000..0f6089c70f --- /dev/null +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/serverpackets/ExServerPrimitive.java @@ -0,0 +1,327 @@ +/* + * 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 . + */ +package com.l2jmobius.gameserver.network.serverpackets; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +/** + * A packet used to draw points and lines on client.
+ * Note: Names in points and lines are bugged they will appear even when not looking at them. + * @author NosBit + */ +public class ExServerPrimitive extends L2GameServerPacket +{ + private static final String _SM_EX_SERVER_PRIMITIVE = "[S] FE:24 ExServerPrimitive"; + + private final String _name; + private final int _x; + private final int _y; + private final int _z; + private final List _points = new ArrayList<>(); + private final List _lines = new ArrayList<>(); + + /** + * @param name A unique name this will be used to replace lines if second packet is sent + * @param x the x coordinate usually middle of drawing area + * @param y the y coordinate usually middle of drawing area + * @param z the z coordinate usually middle of drawing area + */ + public ExServerPrimitive(String name, int x, int y, int z) + { + _name = name; + _x = x; + _y = y; + _z = z; + } + + /** + * Adds a point to be displayed on client. + * @param name the name that will be displayed over the point + * @param color the color + * @param isNameColored if {@code true} name will be colored as well. + * @param x the x coordinate for this point + * @param y the y coordinate for this point + * @param z the z coordinate for this point + */ + public void addPoint(String name, int color, boolean isNameColored, int x, int y, int z) + { + _points.add(new Point(name, color, isNameColored, x, y, z)); + } + + /** + * Adds a point to be displayed on client. + * @param color the color + * @param x the x coordinate for this point + * @param y the y coordinate for this point + * @param z the z coordinate for this point + */ + public void addPoint(int color, int x, int y, int z) + { + addPoint("", color, false, x, y, z); + } + + /** + * Adds a point to be displayed on client. + * @param name the name that will be displayed over the point + * @param color the color + * @param isNameColored if {@code true} name will be colored as well. + * @param x the x coordinate for this point + * @param y the y coordinate for this point + * @param z the z coordinate for this point + */ + public void addPoint(String name, Color color, boolean isNameColored, int x, int y, int z) + { + addPoint(name, color.getRGB(), isNameColored, x, y, z); + } + + /** + * Adds a point to be displayed on client. + * @param color the color + * @param x the x coordinate for this point + * @param y the y coordinate for this point + * @param z the z coordinate for this point + */ + public void addPoint(Color color, int x, int y, int z) + { + addPoint("", color, false, x, y, z); + } + + /** + * Adds a line to be displayed on client + * @param name the name that will be displayed over the middle of line + * @param color the color + * @param isNameColored if {@code true} name will be colored as well. + * @param x the x coordinate for this line start point + * @param y the y coordinate for this line start point + * @param z the z coordinate for this line start point + * @param x2 the x coordinate for this line end point + * @param y2 the y coordinate for this line end point + * @param z2 the z coordinate for this line end point + */ + public void addLine(String name, int color, boolean isNameColored, int x, int y, int z, int x2, int y2, int z2) + { + _lines.add(new Line(name, color, isNameColored, x, y, z, x2, y2, z2)); + } + + /** + * Adds a line to be displayed on client + * @param color the color + * @param x the x coordinate for this line start point + * @param y the y coordinate for this line start point + * @param z the z coordinate for this line start point + * @param x2 the x coordinate for this line end point + * @param y2 the y coordinate for this line end point + * @param z2 the z coordinate for this line end point + */ + public void addLine(int color, int x, int y, int z, int x2, int y2, int z2) + { + addLine("", color, false, x, y, z, x2, y2, z2); + } + + /** + * Adds a line to be displayed on client + * @param name the name that will be displayed over the middle of line + * @param color the color + * @param isNameColored if {@code true} name will be colored as well. + * @param x the x coordinate for this line start point + * @param y the y coordinate for this line start point + * @param z the z coordinate for this line start point + * @param x2 the x coordinate for this line end point + * @param y2 the y coordinate for this line end point + * @param z2 the z coordinate for this line end point + */ + public void addLine(String name, Color color, boolean isNameColored, int x, int y, int z, int x2, int y2, int z2) + { + addLine(name, color.getRGB(), isNameColored, x, y, z, x2, y2, z2); + } + + /** + * Adds a line to be displayed on client + * @param color the color + * @param x the x coordinate for this line start point + * @param y the y coordinate for this line start point + * @param z the z coordinate for this line start point + * @param x2 the x coordinate for this line end point + * @param y2 the y coordinate for this line end point + * @param z2 the z coordinate for this line end point + */ + public void addLine(Color color, int x, int y, int z, int x2, int y2, int z2) + { + addLine("", color, false, x, y, z, x2, y2, z2); + } + + @Override + protected void writeImpl() + { + writeC(0xFE); + writeH(0x24); // Changed at Kamael to 11 + writeS(_name); + writeD(_x); + writeD(_y); + writeD(_z); + writeD(65535); // has to do something with display range and angle + writeD(65535); // has to do something with display range and angle + + writeD(_points.size() + _lines.size()); + + for (Point point : _points) + { + writeC(1); // Its the type in this case Point + writeS(point.getName()); + final int color = point.getColor(); + writeD((color >> 16) & 0xFF); // R + writeD((color >> 8) & 0xFF); // G + writeD(color & 0xFF); // B + writeD(point.isNameColored() ? 1 : 0); + writeD(point.getX()); + writeD(point.getY()); + writeD(point.getZ()); + } + + for (Line line : _lines) + { + writeC(2); // Its the type in this case Line + writeS(line.getName()); + final int color = line.getColor(); + writeD((color >> 16) & 0xFF); // R + writeD((color >> 8) & 0xFF); // G + writeD(color & 0xFF); // B + writeD(line.isNameColored() ? 1 : 0); + writeD(line.getX()); + writeD(line.getY()); + writeD(line.getZ()); + writeD(line.getX2()); + writeD(line.getY2()); + writeD(line.getZ2()); + } + } + + private static class Point + { + private final String _name; + private final int _color; + private final boolean _isNameColored; + private final int _x; + private final int _y; + private final int _z; + + public Point(String name, int color, boolean isNameColored, int x, int y, int z) + { + _name = name; + _color = color; + _isNameColored = isNameColored; + _x = x; + _y = y; + _z = z; + } + + /** + * @return the name + */ + public String getName() + { + return _name; + } + + /** + * @return the color + */ + public int getColor() + { + return _color; + } + + /** + * @return the isNameColored + */ + public boolean isNameColored() + { + return _isNameColored; + } + + /** + * @return the x + */ + public int getX() + { + return _x; + } + + /** + * @return the y + */ + public int getY() + { + return _y; + } + + /** + * @return the z + */ + public int getZ() + { + return _z; + } + } + + private static class Line extends Point + { + private final int _x2; + private final int _y2; + private final int _z2; + + public Line(String name, int color, boolean isNameColored, int x, int y, int z, int x2, int y2, int z2) + { + super(name, color, isNameColored, x, y, z); + _x2 = x2; + _y2 = y2; + _z2 = z2; + } + + /** + * @return the x2 + */ + public int getX2() + { + return _x2; + } + + /** + * @return the y2 + */ + public int getY2() + { + return _y2; + } + + /** + * @return the z2 + */ + public int getZ2() + { + return _z2; + } + } + + @Override + public String getType() + { + return _SM_EX_SERVER_PRIMITIVE; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/PathFinding.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/PathFinding.java deleted file mode 100644 index 94e28bd1a2..0000000000 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/PathFinding.java +++ /dev/null @@ -1,110 +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 . - */ -package com.l2jmobius.gameserver.pathfinding; - -import java.util.List; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.L2World; -import com.l2jmobius.gameserver.pathfinding.cellnodes.CellPathFinding; -import com.l2jmobius.gameserver.pathfinding.geonodes.GeoPathFinding; - -/** - * @author -Nemesiss- - */ -public abstract class PathFinding -{ - private static PathFinding _instance; - - public static PathFinding getInstance() - { - if (_instance == null) - { - if (!Config.GEODATA_CELLFINDING) - { - // Higher Memory Usage, Smaller Cpu Usage - return GeoPathFinding.getInstance(); - } - return CellPathFinding.getInstance(); - } - return _instance; - } - - public abstract boolean pathNodesExist(short regionoffset); - - public abstract List findPath(int x, int y, int z, int tx, int ty, int tz, boolean playable); - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + 16); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 8) + 10); - } - - public short getRegionOffset(byte rx, byte ry) - { - return (short) ((rx << 5) + ry); - } - - /** - * Convert pathnode x to World x position - * @param node_x - * @return - */ - public int calculateWorldX(short node_x) - { - return L2World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int CalculateWorldY(short node_y) - { - return L2World.MAP_MIN_Y + (node_y * 128) + 48; - } - - public String[] getStat() - { - return null; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectFear.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectFear.java index 19330a3dca..84a1722456 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectFear.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectFear.java @@ -17,8 +17,8 @@ package com.l2jmobius.gameserver.skills.effects; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; import com.l2jmobius.gameserver.ai.CtrlIntention; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.L2CharPosition; import com.l2jmobius.gameserver.model.L2Effect; import com.l2jmobius.gameserver.model.Location; @@ -110,7 +110,7 @@ final class EffectFear extends L2Effect posX += _dX * FEAR_RANGE; posY += _dY * FEAR_RANGE; - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { final Location destiny = GeoData.getInstance().moveCheck(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ); posX = destiny.getX(); diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectThrowUp.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectThrowUp.java index aaee244d74..04654307da 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectThrowUp.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/skills/effects/EffectThrowUp.java @@ -19,7 +19,7 @@ package com.l2jmobius.gameserver.skills.effects; import java.util.logging.Logger; import com.l2jmobius.Config; -import com.l2jmobius.gameserver.GeoData; +import com.l2jmobius.gameserver.geodata.GeoData; import com.l2jmobius.gameserver.model.L2Effect; import com.l2jmobius.gameserver.model.Location; import com.l2jmobius.gameserver.network.serverpackets.FlyToLocation; @@ -92,7 +92,7 @@ final class EffectThrowUp extends L2Effect _y = getEffector().getY() - (int) (offset * sin); _z = getEffected().getZ(); - if (Config.GEODATA > 0) + if (Config.PATHFINDING > 0) { final Location destiny = GeoData.getInstance().moveCheck(getEffected().getX(), getEffected().getY(), getEffected().getZ(), _x, _y, _z); _x = destiny.getX(); diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/GeoUtils.java index 9ea6f3588b..7004d8cccc 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/GeoUtils.java @@ -16,25 +16,165 @@ */ package com.l2jmobius.gameserver.util; -import com.l2jserver.gameserver.geoengine.Direction; +import java.awt.Color; + +import com.l2jmobius.gameserver.geodata.GeoData; +import com.l2jmobius.gameserver.geodata.geodriver.Cell; +import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; +import com.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; /** - * @author FBIagent + * @author HorridoJoho */ public final class GeoUtils { - public interface PointListener + public static void debug2DLine(L2PcInstance player, int x, int y, int tx, int ty, int z) { - /** - * @param x - * @param y - * @return true proceed, false abort - */ - boolean onPoint(int x, int y); + final int gx = GeoData.getInstance().getGeoX(x); + final int gy = GeoData.getInstance().getGeoY(y); + + final int tgx = GeoData.getInstance().getGeoX(tx); + final int tgy = GeoData.getInstance().getGeoY(ty); + + final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); + prim.addLine(Color.BLUE, GeoData.getInstance().getWorldX(gx), GeoData.getInstance().getWorldY(gy), z, GeoData.getInstance().getWorldX(tgx), GeoData.getInstance().getWorldY(tgy), z); + + final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); + + while (iter.next()) + { + final int wx = GeoData.getInstance().getWorldX(iter.x()); + final int wy = GeoData.getInstance().getWorldY(iter.y()); + + prim.addPoint(Color.RED, wx, wy, z); + } + player.sendPacket(prim); + } + + public static void debug3DLine(L2PcInstance player, int x, int y, int z, int tx, int ty, int tz) + { + final int gx = GeoData.getInstance().getGeoX(x); + final int gy = GeoData.getInstance().getGeoY(y); + + final int tgx = GeoData.getInstance().getGeoX(tx); + final int tgy = GeoData.getInstance().getGeoY(ty); + + final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); + prim.addLine(Color.BLUE, GeoData.getInstance().getWorldX(gx), GeoData.getInstance().getWorldY(gy), z, GeoData.getInstance().getWorldX(tgx), GeoData.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 = GeoData.getInstance().getWorldX(prevX); + int wy = GeoData.getInstance().getWorldY(prevY); + int wz = iter.z(); + prim.addPoint(Color.RED, wx, wy, wz); + + while (iter.next()) + { + final int curX = iter.x(); + final int curY = iter.y(); + + if ((curX != prevX) || (curY != prevY)) + { + wx = GeoData.getInstance().getWorldX(curX); + wy = GeoData.getInstance().getWorldY(curY); + wz = iter.z(); + + prim.addPoint(Color.RED, wx, wy, wz); + + prevX = curX; + prevY = curY; + } + } + player.sendPacket(prim); + } + + private static Color getDirectionColor(int x, int y, int z, int nswe) + { + if (GeoData.getInstance().checkNearestNswe(x, y, z, nswe)) + { + return Color.GREEN; + } + return Color.RED; + } + + public static void debugGrid(L2PcInstance player) + { + final int geoRadius = 20; + final int blocksPerPacket = 40; + + int iBlock = blocksPerPacket; + int iPacket = 0; + + ExServerPrimitive exsp = null; + final GeoData gd = GeoData.getInstance(); + final int playerGx = gd.getGeoX(player.getX()); + final int playerGy = gd.getGeoY(player.getY()); + for (int dx = -geoRadius; dx <= geoRadius; ++dx) + { + for (int dy = -geoRadius; dy <= geoRadius; ++dy) + { + if (iBlock >= blocksPerPacket) + { + iBlock = 0; + if (exsp != null) + { + ++iPacket; + player.sendPacket(exsp); + } + exsp = new ExServerPrimitive("DebugGrid_" + iPacket, player.getX(), player.getY(), -16000); + } + + if (exsp == null) + { + throw new IllegalStateException(); + } + + final int gx = playerGx + dx; + final int gy = playerGy + dy; + + final int x = gd.getWorldX(gx); + final int y = gd.getWorldY(gy); + final int z = gd.getNearestZ(gx, gy, player.getZ()); + + // north arrow + 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, 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, 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, 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); + exsp.addLine(col, x - 4, y - 4, z, x - 4, y + 4, z); + + ++iBlock; + } + } + + player.sendPacket(exsp); } /** - * difference between x values: never abover 1
+ * difference between x values: never above 1
* difference between y values: never above 1 * @param lastX * @param lastY @@ -42,36 +182,36 @@ public final class GeoUtils * @param y * @return */ - public static Direction computeDirection(int lastX, int lastY, int x, int y) + public static int computeNswe(int lastX, int lastY, int x, int y) { if (x > lastX) // east { if (y > lastY) { - return Direction.SOUTH_EAST; + return Cell.NSWE_SOUTH_EAST; // Direction.SOUTH_EAST; } else if (y < lastY) { - return Direction.NORTH_EAST; + return Cell.NSWE_NORTH_EAST; // Direction.NORTH_EAST; } else { - return Direction.EAST; + return Cell.NSWE_EAST; // Direction.EAST; } } else if (x < lastX) // west { if (y > lastY) { - return Direction.SOUTH_WEST; + return Cell.NSWE_SOUTH_WEST; // Direction.SOUTH_WEST; } else if (y < lastY) { - return Direction.NORTH_WEST; + return Cell.NSWE_NORTH_WEST; // Direction.NORTH_WEST; } else { - return Direction.WEST; + return Cell.NSWE_WEST; // Direction.WEST; } } else @@ -79,15 +219,15 @@ public final class GeoUtils { if (y > lastY) { - return Direction.SOUTH; + return Cell.NSWE_SOUTH; // Direction.SOUTH; } else if (y < lastY) { - return Direction.NORTH; + return Cell.NSWE_NORTH; // Direction.NORTH; } else { - return null;// error, should never happen, TODO: Logging + throw new RuntimeException(); } } } diff --git a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/Util.java b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/Util.java index 74b5feff6e..c1d30c50b4 100644 --- a/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/Util.java +++ b/L2J_Mobius_C4/java/com/l2jmobius/gameserver/util/Util.java @@ -325,4 +325,24 @@ public final class Util } return result; } + + /** + * @param text - the text to check + * @return {@code true} if {@code text} contains only numbers, {@code false} otherwise + */ + public static boolean isDigit(String text) + { + if ((text == null) || text.isEmpty()) + { + return false; + } + for (char c : text.toCharArray()) + { + if (!Character.isDigit(c)) + { + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/L2J_Mobius_C4/launcher/Gameserver.launch b/L2J_Mobius_C4/launcher/Gameserver.launch index 2418e5fd37..3c9ff6f4d8 100644 --- a/L2J_Mobius_C4/launcher/Gameserver.launch +++ b/L2J_Mobius_C4/launcher/Gameserver.launch @@ -7,7 +7,7 @@ - + diff --git a/L2J_Mobius_C4/launcher/Loginserver.launch b/L2J_Mobius_C4/launcher/Loginserver.launch index e5c80b5561..96757f326a 100644 --- a/L2J_Mobius_C4/launcher/Loginserver.launch +++ b/L2J_Mobius_C4/launcher/Loginserver.launch @@ -7,7 +7,7 @@ - +