From 40563ace6cc251db6e07bee82bdaadfc6d52be2e Mon Sep 17 00:00:00 2001 From: MobiusDev <8391001+MobiusDevelopment@users.noreply.github.com> Date: Wed, 19 Jul 2017 23:56:27 +0000 Subject: [PATCH] Replaced GeoData engine with newest version. --- L2J_Mobius_C4/.classpath | 2 - L2J_Mobius_C4/dist/game/config/GeoData.ini | 75 ++ L2J_Mobius_C4/dist/game/config/GeoDriver.ini | 16 - .../dist/game/config/command-privileges.ini | 5 +- L2J_Mobius_C4/dist/game/config/options.ini | 72 -- .../game/data/scripts/ai/bosses/Baium.java | 76 +- .../game/data/scripts/ai/individual/Elpy.java | 2 +- .../dist/libs/L2J_GeoAbstraction.jar | Bin 7691 -> 0 bytes L2J_Mobius_C4/dist/libs/L2J_GeoDriver.jar | Bin 27381 -> 0 bytes L2J_Mobius_C4/java/com/l2jmobius/Config.java | 93 +- .../com/l2jmobius/gameserver/GameServer.java | 13 +- .../com/l2jmobius/gameserver/GeoData.java | 577 ----------- .../gameserver/ai/L2AttackableAI.java | 4 +- .../gameserver/ai/L2SiegeGuardAI.java | 2 +- .../gameserver/datatables/DoorTable.java | 2 +- .../l2jmobius/gameserver/geodata/GeoData.java | 559 +++++++++++ .../gameserver/geodata/geodriver/Cell.java | 48 + .../geodata/geodriver/GeoDriver.java | 189 ++++ .../gameserver/geodata/geodriver/IBlock.java | 42 + .../gameserver/geodata/geodriver/IRegion.java | 47 + .../geodriver/blocks/ComplexBlock.java | 79 ++ .../geodata/geodriver/blocks/FlatBlock.java | 58 ++ .../geodriver/blocks/MultilayerBlock.java | 186 ++++ .../geodriver/regions/NullRegion.java} | 50 +- .../geodata/geodriver/regions/Region.java | 98 ++ .../pathfinding/AbstractNode.java | 178 ++-- .../pathfinding/AbstractNodeLoc.java | 68 +- .../geodata/pathfinding/PathFinding.java | 210 ++++ .../pathfinding/cellnodes/CellNode.java | 137 ++- .../pathfinding/cellnodes/CellNodeBuffer.java | 723 +++++++------- .../cellnodes/CellPathFinding.java | 852 ++++++++-------- .../pathfinding/cellnodes/NodeLoc.java | 399 ++++---- .../pathfinding/geonodes/GeoNode.java | 125 ++- .../pathfinding/geonodes/GeoNodeLoc.java | 239 ++--- .../pathfinding/geonodes/GeoPathFinding.java | 939 +++++++++--------- .../pathfinding/utils/BinaryNodeHeap.java | 248 ++--- .../geoeditorcon/GeoEditorListener.java | 111 --- .../geoeditorcon/GeoEditorThread.java | 300 ------ .../admincommandhandlers/AdminGeoEditor.java | 134 --- .../admincommandhandlers/AdminGeodata.java | 20 +- .../admincommandhandlers/AdminPathNode.java | 6 +- .../handler/skillhandlers/Fishing.java | 4 +- .../l2jmobius/gameserver/model/Inventory.java | 2 + .../gameserver/model/L2Character.java | 25 +- .../gameserver/model/L2ItemInstance.java | 4 +- .../l2jmobius/gameserver/model/L2Skill.java | 2 +- .../l2jmobius/gameserver/model/L2Spawn.java | 4 +- .../l2jmobius/gameserver/model/L2Summon.java | 6 +- .../l2jmobius/gameserver/model/L2World.java | 4 + .../instance/L2ControlTowerInstance.java | 2 +- .../model/actor/instance/L2PcInstance.java | 6 +- .../model/actor/instance/L2PetInstance.java | 6 +- .../clientpackets/MoveBackwardToLocation.java | 2 +- .../clientpackets/ValidatePosition.java | 11 +- .../serverpackets/ExServerPrimitive.java | 327 ++++++ .../gameserver/pathfinding/PathFinding.java | 110 -- .../gameserver/skills/effects/EffectFear.java | 4 +- .../skills/effects/EffectThrowUp.java | 4 +- .../l2jmobius/gameserver/util/GeoUtils.java | 180 +++- .../com/l2jmobius/gameserver/util/Util.java | 20 + L2J_Mobius_C4/launcher/Gameserver.launch | 2 +- L2J_Mobius_C4/launcher/Loginserver.launch | 2 +- 62 files changed, 4212 insertions(+), 3499 deletions(-) create mode 100644 L2J_Mobius_C4/dist/game/config/GeoData.ini delete mode 100644 L2J_Mobius_C4/dist/game/config/GeoDriver.ini delete mode 100644 L2J_Mobius_C4/dist/libs/L2J_GeoAbstraction.jar delete mode 100644 L2J_Mobius_C4/dist/libs/L2J_GeoDriver.jar delete mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/GeoData.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/GeoData.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/Cell.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/GeoDriver.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IBlock.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/IRegion.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/ComplexBlock.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/FlatBlock.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/blocks/MultilayerBlock.java rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{pathfinding/utils/FastNodeList.java => geodata/geodriver/regions/NullRegion.java} (51%) create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/geodriver/regions/Region.java rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/AbstractNode.java (70%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/AbstractNodeLoc.java (88%) create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geodata/pathfinding/PathFinding.java rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/cellnodes/CellNode.java (79%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/cellnodes/CellNodeBuffer.java (76%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/cellnodes/CellPathFinding.java (69%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/cellnodes/NodeLoc.java (63%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/geonodes/GeoNode.java (78%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/geonodes/GeoNodeLoc.java (74%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/geonodes/GeoPathFinding.java (71%) rename L2J_Mobius_C4/java/com/l2jmobius/gameserver/{ => geodata}/pathfinding/utils/BinaryNodeHeap.java (90%) delete mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorListener.java delete mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/geoeditorcon/GeoEditorThread.java delete mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeoEditor.java create mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/network/serverpackets/ExServerPrimitive.java delete mode 100644 L2J_Mobius_C4/java/com/l2jmobius/gameserver/pathfinding/PathFinding.java 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 af841f999ee125448ea690457bc52c726a7c4420..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7691 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 U2OT~D04e%KgVz40>CV3WAJ`*`n*aa+ 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 2abd4fc5f6ce3c4c345b2fb3a5eebbd5f55836b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27381 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%0Atlr&^@nnDbPmNwQXgeQm@oovhL9=wlJ8SXr{H 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 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 @@ - +