diff --git a/Server/detekt.yml b/Server/detekt.yml index 6a18854c9..f66960704 100644 --- a/Server/detekt.yml +++ b/Server/detekt.yml @@ -285,7 +285,7 @@ exceptions: - 'Throwable' allowedExceptionNameRegex: '_|(ignore|expected).*' TooGenericExceptionThrown: - active: true + active: false exceptionNames: - 'Error' - 'Exception' diff --git a/Server/src/main/content/global/bots/LobsterCatcher.kt b/Server/src/main/content/global/bots/LobsterCatcher.kt index 1f3a494a1..9661d41f1 100644 --- a/Server/src/main/content/global/bots/LobsterCatcher.kt +++ b/Server/src/main/content/global/bots/LobsterCatcher.kt @@ -97,9 +97,9 @@ class LobsterCatcher : Script() { state = State.FISHING } else { if (bot.location.x < 2837) { - Pathfinder.find(bot, Location.create(2837, 3435, 0)).walk(bot) + scriptAPI.walkTo(Location.create(2837, 3435, 0)) } else { - Pathfinder.find(bot, Location.create(2854, 3427, 0)).walk(bot) + scriptAPI.walkTo(Location.create(2854, 3427, 0)) } } } @@ -194,4 +194,4 @@ class LobsterCatcher : Script() { } return newInstance() } -} \ No newline at end of file +} diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index 5998ed181..afe6f0157 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -13,270 +13,273 @@ import java.math.BigInteger * @author Ceikry */ class ServerConstants { - companion object { - var NOAUTH_DEFAULT_ADMIN: Boolean = true + companion object { + var NOAUTH_DEFAULT_ADMIN: Boolean = true @JvmField - var DAILY_ACCOUNT_LIMIT = 3 + var DAILY_ACCOUNT_LIMIT = 3 @JvmField - var REVENANT_POPULATION: Int = 30 + var REVENANT_POPULATION: Int = 30 @JvmField - var BOTS_INFLUENCE_PRICE_INDEX = true + var BOTS_INFLUENCE_PRICE_INDEX = true @JvmField - var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) - - @JvmField - var ALLOW_GUI: Boolean = false + var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) @JvmField - var DATA_PATH: String? = null + var ALLOW_GUI: Boolean = false - //path to the cache - @JvmField - var CACHE_PATH: String? = null + @JvmField + var DATA_PATH: String? = null - //path for the server store - @JvmField - var STORE_PATH: String? = null + //path to the cache + @JvmField + var CACHE_PATH: String? = null - //path for player saves - @JvmField - var PLAYER_SAVE_PATH: String? = null + //path for the server store + @JvmField + var STORE_PATH: String? = null - @JvmField - var PLAYER_ATTRIBUTE_PATH = "ish"; + //path for player saves + @JvmField + var PLAYER_SAVE_PATH: String? = null - //path to the various config files, such as npc_spawns.json - var CONFIG_PATH: String? = null + @JvmField + var PLAYER_ATTRIBUTE_PATH = "ish"; - @JvmField - var GRAND_EXCHANGE_DATA_PATH: String? = null + //path to the various config files, such as npc_spawns.json + var CONFIG_PATH: String? = null - @JvmField - var RDT_DATA_PATH: String? = null + @JvmField + var GRAND_EXCHANGE_DATA_PATH: String? = null - @JvmField - var OBJECT_PARSER_PATH: String? = null + @JvmField + var RDT_DATA_PATH: String? = null - @JvmField - var SCRIPTS_PATH: String? = null + @JvmField + var OBJECT_PARSER_PATH: String? = null - @JvmField - var DIALOGUE_SCRIPTS_PATH: String? = null + @JvmField + var SCRIPTS_PATH: String? = null - @JvmField - var LOGS_PATH: String? = null + @JvmField + var DIALOGUE_SCRIPTS_PATH: String? = null - @JvmField - var BOT_DATA_PATH: String? = null + @JvmField + var LOGS_PATH: String? = null - @JvmField - var CELEDT_DATA_PATH: String? = null + @JvmField + var BOT_DATA_PATH: String? = null - @JvmField - var USDT_DATA_PATH: String? = null + @JvmField + var CELEDT_DATA_PATH: String? = null - @JvmField - var HDT_DATA_PATH: String? = null + @JvmField + var USDT_DATA_PATH: String? = null - @JvmField - var GDT_DATA_PATH: String? = null + @JvmField + var HDT_DATA_PATH: String? = null - @JvmField - var RSDT_DATA_PATH: String? = null + @JvmField + var GDT_DATA_PATH: String? = null - @JvmField - var ASDT_DATA_PATH: String? = null + @JvmField + var RSDT_DATA_PATH: String? = null - //the max number of players. - @JvmField - var MAX_PLAYERS = 2000 + @JvmField + var ASDT_DATA_PATH: String? = null - //the max number of NPCs - @JvmField - var MAX_NPCS = 32000 + //the max number of players. + @JvmField + var MAX_PLAYERS = 2000 - //the location where new players are placed on login. - @JvmField - var START_LOCATION: Location? = null + //the max number of NPCs + @JvmField + var MAX_NPCS = 32000 - //Location for all home teleports/respawn location - @JvmField - var HOME_LOCATION: Location? = null + //the location where new players are placed on login. + @JvmField + var START_LOCATION: Location? = null - //the name for the database - @JvmField - var DATABASE_NAME: String? = null + //Location for all home teleports/respawn location + @JvmField + var HOME_LOCATION: Location? = null - //username for the database - @JvmField - var DATABASE_USER: String? = null + //the name for the database + @JvmField + var DATABASE_NAME: String? = null - //password for the database - @JvmField - var DATABASE_PASS: String? = null + //username for the database + @JvmField + var DATABASE_USER: String? = null - //address for the database - @JvmField - var DATABASE_ADDRESS: String? = null + //password for the database + @JvmField + var DATABASE_PASS: String? = null - @JvmField - var DATABASE_PORT: String? = null + //address for the database + @JvmField + var DATABASE_ADDRESS: String? = null - @JvmField - var WRITE_LOGS: Boolean = false + @JvmField + var DATABASE_PORT: String? = null - @JvmField - var BANK_SIZE: Int = 496 + @JvmField + var WRITE_LOGS: Boolean = false - @JvmField - var BANK_BOOTH_QUICK_OPEN: Boolean = false + @JvmField + var BANK_SIZE: Int = 496 - @JvmField - var BANK_BOOTH_NOTE_ENABLED: Boolean = true + @JvmField + var BANK_BOOTH_QUICK_OPEN: Boolean = false - @JvmField - var BANK_BOOTH_NOTE_UIM: Boolean = true + @JvmField + var BANK_BOOTH_NOTE_ENABLED: Boolean = true - @JvmField - var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour + @JvmField + var BANK_BOOTH_NOTE_UIM: Boolean = true - @JvmField - var GE_AUTOSTOCK_ENABLED = false + @JvmField + var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour - @JvmField - var MS_SECRET_KEY = "" + @JvmField + var GE_AUTOSTOCK_ENABLED = false - @JvmField - var LOG_CUTSCENE = true + @JvmField + var MS_SECRET_KEY = "" - @JvmField - var RULES_AND_INFO_ENABLED = true + @JvmField + var LOG_CUTSCENE = true - @JvmField - var WATCHDOG_ENABLED = true + @JvmField + var RULES_AND_INFO_ENABLED = true - @JvmField - var I_AM_A_CHEATER = false + @JvmField + var WATCHDOG_ENABLED = true - @JvmField - var LOG_LEVEL = LogLevel.VERBOSE + @JvmField + var I_AM_A_CHEATER = false - @JvmField - var JAVA_VERSION = 8 + @JvmField + var LOG_LEVEL = LogLevel.VERBOSE - //location names for the ::to command. - val TELEPORT_DESTINATIONS = arrayOf( - arrayOf(Location.create(2974, 4383, 2), "corp", "corporal", "corporeal"), - arrayOf(Location.create(2659, 2649, 0), "pc", "pest control", "pest"), - arrayOf(Location.create(3293, 3184, 0), "al kharid", "alkharid", "kharid"), - arrayOf(Location.create(3222, 3217, 0), "lumbridge", "lumby"), - arrayOf(Location.create(3110, 3168, 0), "wizard tower", "wizards tower", "tower", "wizards"), - arrayOf(Location.create(3083, 3249, 0), "draynor", "draynor village"), - arrayOf(Location.create(3019, 3244, 0), "port sarim", "sarim"), - arrayOf(Location.create(2956, 3209, 0), "rimmington"), - arrayOf(Location.create(2965, 3380, 0), "fally", "falador"), - arrayOf(Location.create(2895, 3436, 0), "taverley"), - arrayOf(Location.create(3080, 3423, 0), "barbarian village", "barb"), - arrayOf(Location.create(3213, 3428, 0), "varrock"), - arrayOf(Location.create(3164, 3485, 0), "grand exchange", "ge"), - arrayOf(Location.create(2917, 3175, 0), "karamja"), - arrayOf(Location.create(2450, 5165, 0), "tzhaar"), - arrayOf(Location.create(2795, 3177, 0), "brimhaven"), - arrayOf(Location.create(2849, 2961, 0), "shilo village", "shilo"), - arrayOf(Location.create(2605, 3093, 0), "yanille"), - arrayOf(Location.create(2663, 3305, 0), "ardougne", "ardy"), - arrayOf(Location.create(2450, 3422, 0), "gnome stronghold", "gnome"), - arrayOf(Location.create(2730, 3485, 0), "camelot", "cammy", "seers"), - arrayOf(Location.create(2805, 3435, 0), "catherby"), - arrayOf(Location.create(2659, 3657, 0), "rellekka"), - arrayOf(Location.create(2890, 3676, 0), "trollheim"), - arrayOf(Location.create(2914, 3746, 0), "godwars", "gwd", "god wars"), - arrayOf(Location.create(3180, 3684, 0), "bounty hunter", "bh"), - arrayOf(Location.create(3272, 3687, 0), "clan wars", "clw"), - arrayOf(Location.create(3090, 3957, 0), "mage arena", "mage", "magearena", "arena"), - arrayOf(Location.create(3069, 10257, 0), "king black dragon", "kbd"), - arrayOf(Location.create(3359, 3416, 0), "digsite"), - arrayOf(Location.create(3488, 3489, 0), "canifis"), - arrayOf(Location.create(3428, 3526, 0), "slayer tower", "slayer"), - arrayOf(Location.create(3502, 9483, 2), "kalphite queen", "kq", "kalphite hive", "kalphite"), - arrayOf(Location.create(3233, 2913, 0), "pyramid"), - arrayOf(Location.create(3419, 2917, 0), "nardah"), - arrayOf(Location.create(3482, 3090, 0), "uzer"), - arrayOf(Location.create(3358, 2970, 0), "pollnivneach", "poln"), - arrayOf(Location.create(3305, 2788, 0), "sophanem"), - arrayOf(Location.create(2898, 3544, 0), "burthorpe", "burthorp"), - arrayOf(Location.create(3088, 3491, 0), "edge", "edgeville"), - arrayOf(Location.create(3169, 3034, 0), "bedabin"), - arrayOf(Location.create(3565, 3289, 0), "barrows"), - arrayOf(Location.create(3016, 3513, 0), "bkf", "black knights fortress"), - arrayOf(Location.create(3052, 3481, 0), "monastery"), - arrayOf(Location.create(1945, 4959, 0), "blast furnace", "blast"), - arrayOf(Location.create(2408, 4449, 0), "zanaris"), - arrayOf(Location.create(3656, 3517, 0), "ectofuntus", "ecto"), - arrayOf(Location.create(2408, 4449, 0), "tower of life lower"), - arrayOf(Location.create(2894, 4756, 0), "test area"), - arrayOf(Location.create(2722, 4886, 0), "quest the golem 1"), - arrayOf(Location.create(2704, 5349, 0), "dorgeshuun", "dorg"), - arrayOf(Location.create(2711, 10132, 0), "brine rats"), - arrayOf(Location.create(2328, 3677, 0), "piscatoris"), - arrayOf(Location.create(2660, 3158, 0), "fishing trawler", "trawler"), - arrayOf(Location.create(2800, 3667, 0), "mountain camp"), - arrayOf(Location.create(2575, 3250, 0), "clocktower"), - arrayOf(CastleWars.lobbyBankArea.randomLoc, "cwars", "castle wars", "castle war", "castlewars", "castlewar", "castle") - ) + @JvmField + var JAVA_VERSION = 8 - @JvmField - var DATABASE: Database? = null + //location names for the ::to command. + val TELEPORT_DESTINATIONS = arrayOf( + arrayOf(Location.create(2974, 4383, 2), "corp", "corporal", "corporeal"), + arrayOf(Location.create(2659, 2649, 0), "pc", "pest control", "pest"), + arrayOf(Location.create(3293, 3184, 0), "al kharid", "alkharid", "kharid"), + arrayOf(Location.create(3222, 3217, 0), "lumbridge", "lumby"), + arrayOf(Location.create(3110, 3168, 0), "wizard tower", "wizards tower", "tower", "wizards"), + arrayOf(Location.create(3083, 3249, 0), "draynor", "draynor village"), + arrayOf(Location.create(3019, 3244, 0), "port sarim", "sarim"), + arrayOf(Location.create(2956, 3209, 0), "rimmington"), + arrayOf(Location.create(2965, 3380, 0), "fally", "falador"), + arrayOf(Location.create(2895, 3436, 0), "taverley"), + arrayOf(Location.create(3080, 3423, 0), "barbarian village", "barb"), + arrayOf(Location.create(3213, 3428, 0), "varrock"), + arrayOf(Location.create(3164, 3485, 0), "grand exchange", "ge"), + arrayOf(Location.create(2917, 3175, 0), "karamja"), + arrayOf(Location.create(2450, 5165, 0), "tzhaar"), + arrayOf(Location.create(2795, 3177, 0), "brimhaven"), + arrayOf(Location.create(2849, 2961, 0), "shilo village", "shilo"), + arrayOf(Location.create(2605, 3093, 0), "yanille"), + arrayOf(Location.create(2663, 3305, 0), "ardougne", "ardy"), + arrayOf(Location.create(2450, 3422, 0), "gnome stronghold", "gnome"), + arrayOf(Location.create(2730, 3485, 0), "camelot", "cammy", "seers"), + arrayOf(Location.create(2805, 3435, 0), "catherby"), + arrayOf(Location.create(2659, 3657, 0), "rellekka"), + arrayOf(Location.create(2890, 3676, 0), "trollheim"), + arrayOf(Location.create(2914, 3746, 0), "godwars", "gwd", "god wars"), + arrayOf(Location.create(3180, 3684, 0), "bounty hunter", "bh"), + arrayOf(Location.create(3272, 3687, 0), "clan wars", "clw"), + arrayOf(Location.create(3090, 3957, 0), "mage arena", "mage", "magearena", "arena"), + arrayOf(Location.create(3069, 10257, 0), "king black dragon", "kbd"), + arrayOf(Location.create(3359, 3416, 0), "digsite"), + arrayOf(Location.create(3488, 3489, 0), "canifis"), + arrayOf(Location.create(3428, 3526, 0), "slayer tower", "slayer"), + arrayOf(Location.create(3502, 9483, 2), "kalphite queen", "kq", "kalphite hive", "kalphite"), + arrayOf(Location.create(3233, 2913, 0), "pyramid"), + arrayOf(Location.create(3419, 2917, 0), "nardah"), + arrayOf(Location.create(3482, 3090, 0), "uzer"), + arrayOf(Location.create(3358, 2970, 0), "pollnivneach", "poln"), + arrayOf(Location.create(3305, 2788, 0), "sophanem"), + arrayOf(Location.create(2898, 3544, 0), "burthorpe", "burthorp"), + arrayOf(Location.create(3088, 3491, 0), "edge", "edgeville"), + arrayOf(Location.create(3169, 3034, 0), "bedabin"), + arrayOf(Location.create(3565, 3289, 0), "barrows"), + arrayOf(Location.create(3016, 3513, 0), "bkf", "black knights fortress"), + arrayOf(Location.create(3052, 3481, 0), "monastery"), + arrayOf(Location.create(1945, 4959, 0), "blast furnace", "blast"), + arrayOf(Location.create(2408, 4449, 0), "zanaris"), + arrayOf(Location.create(3656, 3517, 0), "ectofuntus", "ecto"), + arrayOf(Location.create(2408, 4449, 0), "tower of life lower"), + arrayOf(Location.create(2894, 4756, 0), "test area"), + arrayOf(Location.create(2722, 4886, 0), "quest the golem 1"), + arrayOf(Location.create(2704, 5349, 0), "dorgeshuun", "dorg"), + arrayOf(Location.create(2711, 10132, 0), "brine rats"), + arrayOf(Location.create(2328, 3677, 0), "piscatoris"), + arrayOf(Location.create(2660, 3158, 0), "fishing trawler", "trawler"), + arrayOf(Location.create(2800, 3667, 0), "mountain camp"), + arrayOf(Location.create(2575, 3250, 0), "clocktower"), + arrayOf(CastleWars.lobbyBankArea.randomLoc, "cwars", "castle wars", "castle war", "castlewars", "castlewar", "castle") + ) - //if SQL is enabled - @JvmField - var MYSQL = true + @JvmField + var DATABASE: Database? = null - //the server name - @JvmField - var SERVER_NAME: String = "" + //if SQL is enabled + @JvmField + var MYSQL = true - //the server's grand exchange name - @JvmField - var SERVER_GE_NAME: String = "" + //the server name + @JvmField + var SERVER_NAME: String = "" - //The RSA_KEY for the server. - @JvmField - var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241") + //the server's grand exchange name + @JvmField + var SERVER_GE_NAME: String = "" - //The MODULUS for the server. - @JvmField - var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") + //The RSA_KEY for the server. + @JvmField + var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241") - @JvmField - var DAILY_RESTART = false + //The MODULUS for the server. + @JvmField + var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") - @JvmField - var DISCORD_GE_WEBHOOK = "" + @JvmField + var DAILY_RESTART = false - @JvmField - var DISCORD_MOD_WEBHOOK = "" + @JvmField + var DISCORD_GE_WEBHOOK = "" - @JvmField - var DISCORD_OPENRSC_HOOK = "" + @JvmField + var DISCORD_MOD_WEBHOOK = "" - @JvmField - var PRELOAD_MAP = false + @JvmField + var DISCORD_OPENRSC_HOOK = "" - @JvmField - var USE_AUTH = false + @JvmField + var PRELOAD_MAP = false - @JvmField - var PERSIST_ACCOUNTS = false + @JvmField + var USE_AUTH = false - @JvmField - var DRAGON_AXE_USE_OSRS_SPEC = false + @JvmField + var PERSIST_ACCOUNTS = false - @JvmField - var ENABLE_GLOBALCHAT = false - } + @JvmField + var DRAGON_AXE_USE_OSRS_SPEC = false + + @JvmField + var ENABLE_GLOBALCHAT = false + + @JvmField + var MAX_PATHFIND_DISTANCE = 25 + } } diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index e2bab4e26..1ef6b9a1e 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -70,8 +70,10 @@ import core.game.world.repository.Repository import core.game.consumable.* import core.tools.Log import core.tools.tick +import core.ServerConstants +import core.api.utils.Vector import java.util.regex.* -import kotlin.math.absoluteValue +import kotlin.math.* /** * Gets a skilling tool which the player has the level to use and is in their inventory. @@ -1770,15 +1772,17 @@ fun getServerConfig(): Toml { } fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Location, maxAttempts: Int = 3): Location { - val swCorner = center.transform(-radius, -radius, center.z) - val neCorner = center.transform(radius, radius, center.z) + var maxRadius = Vector.deriveWithEqualComponents(ServerConstants.MAX_PATHFIND_DISTANCE.toDouble()).x - 1 + var effectiveRadius = min(radius, maxRadius.toInt()) + val swCorner = center.transform(-effectiveRadius, -effectiveRadius, center.z) + val neCorner = center.transform(effectiveRadius, effectiveRadius, center.z) val borders = ZoneBorders(swCorner.x, swCorner.y, neCorner.x, neCorner.y, center.z) var attempts = maxAttempts var success: Boolean while (attempts-- > 0) { val dest = borders.randomLoc - val path = Pathfinder.find(target, dest) + val path = Pathfinder.find(center, dest, target.size()) success = path.isSuccessful && !path.isMoveNear if (success) return dest } diff --git a/Server/src/main/core/api/utils/Vector.kt b/Server/src/main/core/api/utils/Vector.kt index 8c3365f22..4c2c3ffed 100644 --- a/Server/src/main/core/api/utils/Vector.kt +++ b/Server/src/main/core/api/utils/Vector.kt @@ -25,11 +25,31 @@ class Vector (val x: Double, val y: Double) { return Vector(this.x * other, this.y * other) } + operator fun plus (other: Vector) : Vector { + return Vector(this.x + other.x, this.y + other.y) + } + + operator fun minus (other: Vector) : Vector { + return Vector(this.x - other.x, this.y - other.y) + } + + override fun toString() : String { + return "{$x,$y}" + } + + fun invert() : Vector { + return -this + } + companion object { @JvmStatic fun betweenLocs (from: Location, to: Location) : Vector { val xDiff = to.x - from.x val yDiff = to.y - from.y return Vector (xDiff.toDouble(), yDiff.toDouble()) } + @JvmStatic fun deriveWithEqualComponents (magnitude: Double) : Vector { + var sideLength = sqrt(magnitude.pow(2.0) / 2) + return Vector(sideLength, sideLength) + } } } diff --git a/Server/src/main/core/game/bots/GeneralBotCreator.kt b/Server/src/main/core/game/bots/GeneralBotCreator.kt index 5aa7147b9..4f32de3d5 100644 --- a/Server/src/main/core/game/bots/GeneralBotCreator.kt +++ b/Server/src/main/core/game/bots/GeneralBotCreator.kt @@ -40,7 +40,7 @@ class GeneralBotCreator { randomDelay -= 1 return false } - if (!botScript.bot.pulseManager.hasPulseRunning()) { + if (!botScript.bot.pulseManager.hasPulseRunning() && botScript.bot.scripts.getActiveScript() == null) { /*if (ticks++ >= RandomFunction.random(90000,120000)) { AIPlayer.deregister(botScript.bot.uid) @@ -77,4 +77,4 @@ class GeneralBotCreator { return true } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/bots/ScriptAPI.kt b/Server/src/main/core/game/bots/ScriptAPI.kt index 7ada80ca7..4400f66e5 100644 --- a/Server/src/main/core/game/bots/ScriptAPI.kt +++ b/Server/src/main/core/game/bots/ScriptAPI.kt @@ -49,6 +49,8 @@ import java.util.concurrent.CountDownLatch import kotlin.math.max import kotlin.math.pow import kotlin.math.sqrt +import core.ServerConstants +import core.api.utils.Vector class ScriptAPI(private val bot: Player) { val GRAPHICSUP = Graphics(1576) @@ -86,24 +88,6 @@ class ScriptAPI(private val bot: Player) { if(!InteractionListeners.run(node.id, type, option, bot, node)) node.interaction.handle(bot, opt) } - /** - * Gets the nearest node with name entityName - * @param entityName the name of the node to look for - * @return the nearest node with a matching name or null - * @author Ceikry - */ - fun getNearestNode(entityName: String): Node? { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (node in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - if (node != null && node.name == entityName && distance(bot, node) < minDistance && !Pathfinder.find(bot, node).isMoveNear) { - entity = node - minDistance = distance(bot, node) - } - } - return entity - } - fun sendChat(message: String) { bot.sendChat(message) bot.updateMasks.register(ChatFlag(ChatMessage(bot, message, 0, 0))) @@ -115,33 +99,11 @@ class ScriptAPI(private val bot: Player) { * @return the nearest node with a matching name or null * @author Ceikry */ - fun getNearestNodeFromList(acceptedNames: List, isObject: Boolean): Node? { - if (isObject) { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { - for(e in objects) { - val name = e?.name - if (e != null && acceptedNames.contains(name) && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) { - entity = e - minDistance = distance(bot, e) - } - } - } - return if(entity == null) null else entity as Scenery - } else { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - val name = e?.name - if (e != null && acceptedNames.contains(name) && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) { - entity = e - minDistance = distance(bot, e) - } - } - return entity - } + if (isObject) + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = acceptedNames) + else + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = acceptedNames) } /** @@ -151,30 +113,21 @@ class ScriptAPI(private val bot: Player) { * @return the closest node with matching id or null. * @author Ceikry */ - fun getNearestNode(id: Int, `object`: Boolean): Node? { - if (`object`) { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { - for(e in objects) { - if (e != null && e.id == id && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) { - entity = e - minDistance = distance(bot, e) - } - } - } - return if(entity == null) null else entity as Scenery - } else { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - if (e != null && e.id == id && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) { - entity = e - minDistance = distance(bot, e) - } - } - return entity - } + fun getNearestNode(id: Int, isObject: Boolean): Node? { + if (isObject) + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedId = id) + else + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedId = id) + } + + /** + * Gets the nearest node with name entityName + * @param entityName the name of the node to look for + * @return the nearest node with a matching name or null + * @author Ceikry + */ + fun getNearestNode(entityName: String): Node? { + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(entityName)) } /** @@ -184,30 +137,38 @@ class ScriptAPI(private val bot: Player) { * @return the nearest matching node or null. * @author Ceikry */ - fun getNearestNode(name: String, `object`: Boolean): Node? { - if (`object`) { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { - for(e in objects) { - if (e != null && e.name.toLowerCase() == name.toLowerCase() && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) { - entity = e - minDistance = distance(bot, e) - } - } + fun getNearestNode(name: String, isObject: Boolean): Node? { + if (isObject) + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = listOf(name)) + else + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(name)) + } + + fun evaluateViability (e: Node?, minDistance: Double, maxDistance: Double, acceptedNames: List? = null, acceptedId: Int = -1): Boolean { + if (e == null || !e.isActive) + return false + if (acceptedId != -1 && e.id != acceptedId) + return false + + val dist = distance(bot, e) + if (dist > maxDistance || dist > minDistance) + return false + + val name = e?.name + return (acceptedNames?.contains(name) ?: true && !Pathfinder.find(bot, e).isMoveNear) + } + + fun processEvaluationList (list: List, acceptedName: List? = null, acceptedId: Int = -1): Node? { + var entity: Node? = null + var minDistance = Double.MAX_VALUE + val maxDistance = ServerConstants.MAX_PATHFIND_DISTANCE.toDouble() + for (e in list) { + if (evaluateViability(e, minDistance, maxDistance, acceptedName, acceptedId)) { + entity = e + minDistance = distance(bot, e) } - return if(entity == null) null else entity as Scenery - } else { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - if (e != null && e.name.toLowerCase() == name.toLowerCase() && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) { - entity = e - minDistance = distance(bot, e) - } - } - return entity } + return entity } /** @@ -350,9 +311,7 @@ class ScriptAPI(private val bot: Player) { */ fun walkTo(loc: Location){ if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - walkToIterator(loc) - } + walkToIterator(loc) } } @@ -391,11 +350,8 @@ class ScriptAPI(private val bot: Player) { */ fun randomWalkTo(loc: Location, radius: Int) { if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - var newloc = loc.transform(RandomFunction.random(radius,-radius), - RandomFunction.random(radius,-radius), 0) - walkToIterator(newloc) - } + var newloc = loc.transform(RandomFunction.random(radius,-radius),RandomFunction.random(radius,-radius), 0) + walkToIterator(newloc) } } @@ -408,15 +364,12 @@ class ScriptAPI(private val bot: Player) { private fun walkToIterator(loc: Location){ var diffX = loc.x - bot.location.x var diffY = loc.y - bot.location.y - while(!bot.location.transform(diffX, diffY, 0).withinDistance(bot.location)) { - diffX /= 2 - diffY /= 2 - } - GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { - override fun pulse(): Boolean { - return true - } - }) + + val vec = Vector.betweenLocs(bot.location, loc) + val norm = vec.normalized() + val tiles = kotlin.math.min(kotlin.math.floor(vec.magnitude()).toInt(), ServerConstants.MAX_PATHFIND_DISTANCE - 1) + val loc = bot.location.transform(norm * tiles) + bot.pulseManager.run(object : MovementPulse(bot, loc) { override fun pulse() : Boolean { return true } }) } /** @@ -469,6 +422,7 @@ class ScriptAPI(private val bot: Player) { override fun pulse(): Boolean { bot.unlock() bot.properties.teleportLocation = location + bot.pulseManager.clear() bot.animator.reset() return true } @@ -599,6 +553,7 @@ class ScriptAPI(private val bot: Player) { override fun pulse(): Boolean { bot.unlock() bot.properties.teleportLocation = location + bot.pulseManager.clear() bot.animator.reset() return true } diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java index 647d6e86f..11f727dd9 100644 --- a/Server/src/main/core/game/interaction/MovementPulse.java +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -78,6 +78,8 @@ public abstract class MovementPulse extends Pulse { private Function2 overrideMethod; + private Location previousLoc; + /** * Constructs a new {@code MovementPulse} {@code Object}. * @@ -263,7 +265,7 @@ public abstract class MovementPulse extends Pulse { else if (inside) { loc = findBorderLocation(); } - } + } else if (loc == previousLoc) return; if (destination == null) { return; @@ -293,6 +295,7 @@ public abstract class MovementPulse extends Pulse { } Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder); + loc = destination.getLocation(); near = !path.isSuccessful() || path.isMoveNear(); interactLocation = mover.getLocation(); boolean canMove = true; @@ -330,6 +333,7 @@ public abstract class MovementPulse extends Pulse { } } } + previousLoc = loc; } last = destination.getLocation(); } diff --git a/Server/src/main/core/game/interaction/ScriptProcessor.kt b/Server/src/main/core/game/interaction/ScriptProcessor.kt index 8967dce10..dd8e4792c 100644 --- a/Server/src/main/core/game/interaction/ScriptProcessor.kt +++ b/Server/src/main/core/game/interaction/ScriptProcessor.kt @@ -45,11 +45,11 @@ class ScriptProcessor(val entity: Entity) { if (entity !is Player) return if (!entity.delayed() && canProcess && interactTarget != null) { if (opScript != null && inOperableDistance()) { - face(entity, interactTarget?.centerLocation ?: return) + face(entity, interactTarget?.getFaceLocation(entity.location) ?: return) processInteractScript(opScript ?: return) } else if (apScript != null && inApproachDistance(apScript ?: return)) { - face(entity, interactTarget?.centerLocation ?: return) + face(entity, interactTarget?.getFaceLocation(entity.location) ?: return) processInteractScript(apScript ?: return) } else if (apScript == null && opScript == null && inOperableDistance()) { diff --git a/Server/src/main/core/game/node/Node.java b/Server/src/main/core/game/node/Node.java index 4eb3676d1..4310044d2 100644 --- a/Server/src/main/core/game/node/Node.java +++ b/Server/src/main/core/game/node/Node.java @@ -9,6 +9,7 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Direction; import core.game.world.map.Location; import core.tools.StringUtils; +import core.api.utils.Vector; /** * Represents a node which is anything that is interactable in Keldagrim. @@ -120,6 +121,21 @@ public abstract class Node { return location.transform(offset, offset, 0); } + public Vector getMathematicalCenter() { + Location topRight = location.transform(size - 1, size - 1, 0); + double x = ((double) location.getX() + (double) topRight.getX()) / 2.0; + double y = ((double) location.getY() + (double) topRight.getY()) / 2.0; + return new Vector(x, y); + } + + public Location getFaceLocation (Location fromLoc) { + Vector center = getMathematicalCenter(); + Vector fromVec = new Vector((double) fromLoc.getX(), (double) fromLoc.getY()); + Vector difference = fromVec.minus(center); + Vector end = center.plus(difference.invert()); + return Location.create((int)end.getX(), (int)end.getY(), fromLoc.getZ()); + } + /** * Gets the name of this node. * @return The name. @@ -266,4 +282,4 @@ public abstract class Node { public void setRenderable(boolean renderable) { this.renderable = renderable; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt index d72eaec4e..af22fb205 100644 --- a/Server/src/main/core/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -150,6 +150,7 @@ object ServerConfigParser { ServerConstants.DRAGON_AXE_USE_OSRS_SPEC = data.getBoolean("world.dragon_axe_use_osrs_spec", false) ServerConstants.DISCORD_OPENRSC_HOOK = data.getString("server.openrsc_integration_webhook", "") ServerConstants.ENABLE_GLOBALCHAT = data.getBoolean("world.enable_globalchat", true) + ServerConstants.MAX_PATHFIND_DISTANCE = data.getLong("server.max_pathfind_dist", 25L).toInt() val logLevel = data.getString("server.log_level", "VERBOSE").uppercase() ServerConstants.LOG_LEVEL = parseEnumEntry(logLevel) ?: LogLevel.VERBOSE diff --git a/Server/src/main/core/game/world/map/RegionPlane.java b/Server/src/main/core/game/world/map/RegionPlane.java index 2a3f2ab0d..6557bf62b 100644 --- a/Server/src/main/core/game/world/map/RegionPlane.java +++ b/Server/src/main/core/game/world/map/RegionPlane.java @@ -201,6 +201,17 @@ public final class RegionPlane { public Scenery[][] getObjects() { return objects; } + + public List getObjectList() { + ArrayList list = new ArrayList(); + for (int x = 0; x < REGION_SIZE; x++) { + for (int y = 0; y < REGION_SIZE; y++) { + if (objects[x][y] != null) + list.add(objects[x][y]); + } + } + return list; + } /** * Clears this region plane. @@ -422,4 +433,4 @@ public final class RegionPlane { return chunks; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/path/Pathfinder.java b/Server/src/main/core/game/world/map/path/Pathfinder.java index 72261150a..9fce3a782 100644 --- a/Server/src/main/core/game/world/map/path/Pathfinder.java +++ b/Server/src/main/core/game/world/map/path/Pathfinder.java @@ -151,6 +151,10 @@ public abstract class Pathfinder { return find(start, destination, true, SMART); } + public static Path find(Location start, Node destination, int moverSize) { + return find(start, moverSize, destination, true, SMART, RegionManager::getClippingFlag); + } + /** * Finds a path from the start location to the end location. * @param destination The destination node. diff --git a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt index a4e95789d..b0a52a033 100644 --- a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt +++ b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt @@ -4,6 +4,8 @@ import core.game.world.GameWorld import core.game.world.map.Direction import core.game.world.map.Location import core.game.world.map.Point +import core.api.utils.Vector +import core.ServerConstants import java.util.Comparator import java.util.PriorityQueue @@ -95,9 +97,22 @@ internal constructor() : Pathfinder() { } } - override fun find(start: Location?, moverSize: Int, end: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path { + override fun find(start: Location?, moverSize: Int, dest: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path { reset() - assert(start != null && end != null) + assert(start != null && dest != null) + var vec = Vector.betweenLocs(start!!, dest!!) + var mag = kotlin.math.floor(vec.magnitude()) + var end = dest!! + if (mag > ServerConstants.MAX_PATHFIND_DISTANCE) { + try { + if (mag < 50.0) { //truncate the path if it's realistically long + vec = vec.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1) + end = start!!.transform(vec) + } else throw Exception("Pathfinding distance exceeds server max! -> " + mag.toString() + " {" + start + "->" + end + "}") + } catch (e: Exception) { + e.printStackTrace() + } + } val path = Path() foundPath = false for (x in 0..103) { @@ -226,6 +241,8 @@ internal constructor() : Pathfinder() { path.points.add(Point(absX, absY)) } path.setSuccesful(true) + if (end != dest) + path.isMoveNear = true return path } diff --git a/Server/src/main/core/net/packet/PacketProcessor.kt b/Server/src/main/core/net/packet/PacketProcessor.kt index ec84e5a18..be0c04150 100644 --- a/Server/src/main/core/net/packet/PacketProcessor.kt +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -55,6 +55,7 @@ import core.net.packet.`in`.Packet import core.net.packet.`in`.RunScript import core.tools.Log import core.worker.ManagementEvents +import core.api.utils.Vector import java.io.PrintWriter import java.io.StringWriter import java.lang.Math.min @@ -452,15 +453,22 @@ object PacketProcessor { //there's more data in this packet, we're just not using it } + var loc = Location.create(x,y,player.location.z) var canWalk = !player.locks.isMovementLocked + val vec = Vector.betweenLocs(player.location, loc) + if (vec.magnitude() > ServerConstants.MAX_PATHFIND_DISTANCE) { + val newVec = vec.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1) + loc = player.location.transform(newVec) + } + if (canWalk && player.interfaceManager.isOpened && !player.interfaceManager.opened.definition.isWalkable) canWalk = canWalk && player.interfaceManager.close() if (canWalk && player.interfaceManager.hasChatbox() && !player.interfaceManager.chatbox.definition.isWalkable) player.interfaceManager.closeChatbox() if (!canWalk || !player.dialogueInterpreter.close()) { - player.debug("[WALK ACTION]-- NO HANDLE: PLAYER LOCKED OR INTERFACES SAY NO") + player.debug("[WALK ACTION]-- Action canceled. Either player is locked, interfaces can't close, or distance is beyond server pathfinding limit.") return sendClearMinimap(player) } @@ -474,7 +482,7 @@ object PacketProcessor { player.faceLocation(null) player.scripts.reset() - player.pulseManager.run(object : MovementPulse(player, Location.create(x,y,player.location.z), isRunning) { + player.pulseManager.run(object : MovementPulse(player, loc, isRunning) { override fun pulse(): Boolean { if (isRunning) player.walkingQueue.isRunning = false @@ -798,4 +806,4 @@ object PacketProcessor { } const val BASE_CHAT_CUTOFF = 81 -} \ No newline at end of file +}