Refactored components of the ScriptAPI to more efficiently utilize pathfinding

Implemented a global limit on pathfinding distance (server.max_pathfind_dist)
Pathfinding limit is enforced at the packet level as well as elsewhere (ContentAPI, etc)
This commit is contained in:
Ceikry
2023-04-24 14:16:59 +00:00
committed by Ryan
parent 5aded2b349
commit 4b20cdd001
15 changed files with 369 additions and 326 deletions
+1 -1
View File
@@ -285,7 +285,7 @@ exceptions:
- 'Throwable' - 'Throwable'
allowedExceptionNameRegex: '_|(ignore|expected).*' allowedExceptionNameRegex: '_|(ignore|expected).*'
TooGenericExceptionThrown: TooGenericExceptionThrown:
active: true active: false
exceptionNames: exceptionNames:
- 'Error' - 'Error'
- 'Exception' - 'Exception'
@@ -97,9 +97,9 @@ class LobsterCatcher : Script() {
state = State.FISHING state = State.FISHING
} else { } else {
if (bot.location.x < 2837) { if (bot.location.x < 2837) {
Pathfinder.find(bot, Location.create(2837, 3435, 0)).walk(bot) scriptAPI.walkTo(Location.create(2837, 3435, 0))
} else { } 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() return newInstance()
} }
} }
+202 -199
View File
@@ -13,270 +13,273 @@ import java.math.BigInteger
* @author Ceikry * @author Ceikry
*/ */
class ServerConstants { class ServerConstants {
companion object { companion object {
var NOAUTH_DEFAULT_ADMIN: Boolean = true var NOAUTH_DEFAULT_ADMIN: Boolean = true
@JvmField @JvmField
var DAILY_ACCOUNT_LIMIT = 3 var DAILY_ACCOUNT_LIMIT = 3
@JvmField @JvmField
var REVENANT_POPULATION: Int = 30 var REVENANT_POPULATION: Int = 30
@JvmField @JvmField
var BOTS_INFLUENCE_PRICE_INDEX = true var BOTS_INFLUENCE_PRICE_INDEX = true
@JvmField @JvmField
var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook())
@JvmField
var ALLOW_GUI: Boolean = false
@JvmField @JvmField
var DATA_PATH: String? = null var ALLOW_GUI: Boolean = false
//path to the cache @JvmField
@JvmField var DATA_PATH: String? = null
var CACHE_PATH: String? = null
//path for the server store //path to the cache
@JvmField @JvmField
var STORE_PATH: String? = null var CACHE_PATH: String? = null
//path for player saves //path for the server store
@JvmField @JvmField
var PLAYER_SAVE_PATH: String? = null var STORE_PATH: String? = null
@JvmField //path for player saves
var PLAYER_ATTRIBUTE_PATH = "ish"; @JvmField
var PLAYER_SAVE_PATH: String? = null
//path to the various config files, such as npc_spawns.json @JvmField
var CONFIG_PATH: String? = null var PLAYER_ATTRIBUTE_PATH = "ish";
@JvmField //path to the various config files, such as npc_spawns.json
var GRAND_EXCHANGE_DATA_PATH: String? = null var CONFIG_PATH: String? = null
@JvmField @JvmField
var RDT_DATA_PATH: String? = null var GRAND_EXCHANGE_DATA_PATH: String? = null
@JvmField @JvmField
var OBJECT_PARSER_PATH: String? = null var RDT_DATA_PATH: String? = null
@JvmField @JvmField
var SCRIPTS_PATH: String? = null var OBJECT_PARSER_PATH: String? = null
@JvmField @JvmField
var DIALOGUE_SCRIPTS_PATH: String? = null var SCRIPTS_PATH: String? = null
@JvmField @JvmField
var LOGS_PATH: String? = null var DIALOGUE_SCRIPTS_PATH: String? = null
@JvmField @JvmField
var BOT_DATA_PATH: String? = null var LOGS_PATH: String? = null
@JvmField @JvmField
var CELEDT_DATA_PATH: String? = null var BOT_DATA_PATH: String? = null
@JvmField @JvmField
var USDT_DATA_PATH: String? = null var CELEDT_DATA_PATH: String? = null
@JvmField @JvmField
var HDT_DATA_PATH: String? = null var USDT_DATA_PATH: String? = null
@JvmField @JvmField
var GDT_DATA_PATH: String? = null var HDT_DATA_PATH: String? = null
@JvmField @JvmField
var RSDT_DATA_PATH: String? = null var GDT_DATA_PATH: String? = null
@JvmField @JvmField
var ASDT_DATA_PATH: String? = null var RSDT_DATA_PATH: String? = null
//the max number of players. @JvmField
@JvmField var ASDT_DATA_PATH: String? = null
var MAX_PLAYERS = 2000
//the max number of NPCs //the max number of players.
@JvmField @JvmField
var MAX_NPCS = 32000 var MAX_PLAYERS = 2000
//the location where new players are placed on login. //the max number of NPCs
@JvmField @JvmField
var START_LOCATION: Location? = null var MAX_NPCS = 32000
//Location for all home teleports/respawn location //the location where new players are placed on login.
@JvmField @JvmField
var HOME_LOCATION: Location? = null var START_LOCATION: Location? = null
//the name for the database //Location for all home teleports/respawn location
@JvmField @JvmField
var DATABASE_NAME: String? = null var HOME_LOCATION: Location? = null
//username for the database //the name for the database
@JvmField @JvmField
var DATABASE_USER: String? = null var DATABASE_NAME: String? = null
//password for the database //username for the database
@JvmField @JvmField
var DATABASE_PASS: String? = null var DATABASE_USER: String? = null
//address for the database //password for the database
@JvmField @JvmField
var DATABASE_ADDRESS: String? = null var DATABASE_PASS: String? = null
@JvmField //address for the database
var DATABASE_PORT: String? = null @JvmField
var DATABASE_ADDRESS: String? = null
@JvmField @JvmField
var WRITE_LOGS: Boolean = false var DATABASE_PORT: String? = null
@JvmField @JvmField
var BANK_SIZE: Int = 496 var WRITE_LOGS: Boolean = false
@JvmField @JvmField
var BANK_BOOTH_QUICK_OPEN: Boolean = false var BANK_SIZE: Int = 496
@JvmField @JvmField
var BANK_BOOTH_NOTE_ENABLED: Boolean = true var BANK_BOOTH_QUICK_OPEN: Boolean = false
@JvmField @JvmField
var BANK_BOOTH_NOTE_UIM: Boolean = true var BANK_BOOTH_NOTE_ENABLED: Boolean = true
@JvmField @JvmField
var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour var BANK_BOOTH_NOTE_UIM: Boolean = true
@JvmField @JvmField
var GE_AUTOSTOCK_ENABLED = false var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour
@JvmField @JvmField
var MS_SECRET_KEY = "" var GE_AUTOSTOCK_ENABLED = false
@JvmField @JvmField
var LOG_CUTSCENE = true var MS_SECRET_KEY = ""
@JvmField @JvmField
var RULES_AND_INFO_ENABLED = true var LOG_CUTSCENE = true
@JvmField @JvmField
var WATCHDOG_ENABLED = true var RULES_AND_INFO_ENABLED = true
@JvmField @JvmField
var I_AM_A_CHEATER = false var WATCHDOG_ENABLED = true
@JvmField @JvmField
var LOG_LEVEL = LogLevel.VERBOSE var I_AM_A_CHEATER = false
@JvmField @JvmField
var JAVA_VERSION = 8 var LOG_LEVEL = LogLevel.VERBOSE
//location names for the ::to command. @JvmField
val TELEPORT_DESTINATIONS = arrayOf( var JAVA_VERSION = 8
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 //location names for the ::to command.
var DATABASE: Database? = null 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
@JvmField var DATABASE: Database? = null
var MYSQL = true
//the server name //if SQL is enabled
@JvmField @JvmField
var SERVER_NAME: String = "" var MYSQL = true
//the server's grand exchange name //the server name
@JvmField @JvmField
var SERVER_GE_NAME: String = "" var SERVER_NAME: String = ""
//The RSA_KEY for the server. //the server's grand exchange name
@JvmField @JvmField
var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241") var SERVER_GE_NAME: String = ""
//The MODULUS for the server. //The RSA_KEY for the server.
@JvmField @JvmField
var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241")
@JvmField //The MODULUS for the server.
var DAILY_RESTART = false @JvmField
var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051")
@JvmField @JvmField
var DISCORD_GE_WEBHOOK = "" var DAILY_RESTART = false
@JvmField @JvmField
var DISCORD_MOD_WEBHOOK = "" var DISCORD_GE_WEBHOOK = ""
@JvmField @JvmField
var DISCORD_OPENRSC_HOOK = "" var DISCORD_MOD_WEBHOOK = ""
@JvmField @JvmField
var PRELOAD_MAP = false var DISCORD_OPENRSC_HOOK = ""
@JvmField @JvmField
var USE_AUTH = false var PRELOAD_MAP = false
@JvmField @JvmField
var PERSIST_ACCOUNTS = false var USE_AUTH = false
@JvmField @JvmField
var DRAGON_AXE_USE_OSRS_SPEC = false var PERSIST_ACCOUNTS = false
@JvmField @JvmField
var ENABLE_GLOBALCHAT = false var DRAGON_AXE_USE_OSRS_SPEC = false
}
@JvmField
var ENABLE_GLOBALCHAT = false
@JvmField
var MAX_PATHFIND_DISTANCE = 25
}
} }
+8 -4
View File
@@ -70,8 +70,10 @@ import core.game.world.repository.Repository
import core.game.consumable.* import core.game.consumable.*
import core.tools.Log import core.tools.Log
import core.tools.tick import core.tools.tick
import core.ServerConstants
import core.api.utils.Vector
import java.util.regex.* 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. * 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 { fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Location, maxAttempts: Int = 3): Location {
val swCorner = center.transform(-radius, -radius, center.z) var maxRadius = Vector.deriveWithEqualComponents(ServerConstants.MAX_PATHFIND_DISTANCE.toDouble()).x - 1
val neCorner = center.transform(radius, radius, center.z) 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) val borders = ZoneBorders(swCorner.x, swCorner.y, neCorner.x, neCorner.y, center.z)
var attempts = maxAttempts var attempts = maxAttempts
var success: Boolean var success: Boolean
while (attempts-- > 0) { while (attempts-- > 0) {
val dest = borders.randomLoc val dest = borders.randomLoc
val path = Pathfinder.find(target, dest) val path = Pathfinder.find(center, dest, target.size())
success = path.isSuccessful && !path.isMoveNear success = path.isSuccessful && !path.isMoveNear
if (success) return dest if (success) return dest
} }
+20
View File
@@ -25,11 +25,31 @@ class Vector (val x: Double, val y: Double) {
return Vector(this.x * other, this.y * other) 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 { companion object {
@JvmStatic fun betweenLocs (from: Location, to: Location) : Vector { @JvmStatic fun betweenLocs (from: Location, to: Location) : Vector {
val xDiff = to.x - from.x val xDiff = to.x - from.x
val yDiff = to.y - from.y val yDiff = to.y - from.y
return Vector (xDiff.toDouble(), yDiff.toDouble()) return Vector (xDiff.toDouble(), yDiff.toDouble())
} }
@JvmStatic fun deriveWithEqualComponents (magnitude: Double) : Vector {
var sideLength = sqrt(magnitude.pow(2.0) / 2)
return Vector(sideLength, sideLength)
}
} }
} }
@@ -40,7 +40,7 @@ class GeneralBotCreator {
randomDelay -= 1 randomDelay -= 1
return false return false
} }
if (!botScript.bot.pulseManager.hasPulseRunning()) { if (!botScript.bot.pulseManager.hasPulseRunning() && botScript.bot.scripts.getActiveScript() == null) {
/*if (ticks++ >= RandomFunction.random(90000,120000)) { /*if (ticks++ >= RandomFunction.random(90000,120000)) {
AIPlayer.deregister(botScript.bot.uid) AIPlayer.deregister(botScript.bot.uid)
@@ -77,4 +77,4 @@ class GeneralBotCreator {
return true return true
} }
} }
} }
+62 -107
View File
@@ -49,6 +49,8 @@ import java.util.concurrent.CountDownLatch
import kotlin.math.max import kotlin.math.max
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
import core.ServerConstants
import core.api.utils.Vector
class ScriptAPI(private val bot: Player) { class ScriptAPI(private val bot: Player) {
val GRAPHICSUP = Graphics(1576) 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) 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) { fun sendChat(message: String) {
bot.sendChat(message) bot.sendChat(message)
bot.updateMasks.register(ChatFlag(ChatMessage(bot, message, 0, 0))) 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 * @return the nearest node with a matching name or null
* @author Ceikry * @author Ceikry
*/ */
fun getNearestNodeFromList(acceptedNames: List<String>, isObject: Boolean): Node? { fun getNearestNodeFromList(acceptedNames: List<String>, isObject: Boolean): Node? {
if (isObject) { if (isObject)
var entity: Node? = null return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = acceptedNames)
var minDistance = Double.MAX_VALUE else
for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = acceptedNames)
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
}
} }
/** /**
@@ -151,30 +113,21 @@ class ScriptAPI(private val bot: Player) {
* @return the closest node with matching id or null. * @return the closest node with matching id or null.
* @author Ceikry * @author Ceikry
*/ */
fun getNearestNode(id: Int, `object`: Boolean): Node? { fun getNearestNode(id: Int, isObject: Boolean): Node? {
if (`object`) { if (isObject)
var entity: Node? = null return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedId = id)
var minDistance = Double.MAX_VALUE else
for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedId = id)
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) * 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
return if(entity == null) null else entity as Scenery */
} else { fun getNearestNode(entityName: String): Node? {
var entity: Node? = null return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(entityName))
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
}
} }
/** /**
@@ -184,30 +137,38 @@ class ScriptAPI(private val bot: Player) {
* @return the nearest matching node or null. * @return the nearest matching node or null.
* @author Ceikry * @author Ceikry
*/ */
fun getNearestNode(name: String, `object`: Boolean): Node? { fun getNearestNode(name: String, isObject: Boolean): Node? {
if (`object`) { if (isObject)
var entity: Node? = null return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = listOf(name))
var minDistance = Double.MAX_VALUE else
for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(name))
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 fun evaluateViability (e: Node?, minDistance: Double, maxDistance: Double, acceptedNames: List<String>? = null, acceptedId: Int = -1): Boolean {
minDistance = distance(bot, e) 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<Node>, acceptedName: List<String>? = 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){ fun walkTo(loc: Location){
if(!bot.walkingQueue.isMoving) { 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) { fun randomWalkTo(loc: Location, radius: Int) {
if(!bot.walkingQueue.isMoving) { if(!bot.walkingQueue.isMoving) {
GlobalScope.launch { var newloc = loc.transform(RandomFunction.random(radius,-radius),RandomFunction.random(radius,-radius), 0)
var newloc = loc.transform(RandomFunction.random(radius,-radius), walkToIterator(newloc)
RandomFunction.random(radius,-radius), 0)
walkToIterator(newloc)
}
} }
} }
@@ -408,15 +364,12 @@ class ScriptAPI(private val bot: Player) {
private fun walkToIterator(loc: Location){ private fun walkToIterator(loc: Location){
var diffX = loc.x - bot.location.x var diffX = loc.x - bot.location.x
var diffY = loc.y - bot.location.y var diffY = loc.y - bot.location.y
while(!bot.location.transform(diffX, diffY, 0).withinDistance(bot.location)) {
diffX /= 2 val vec = Vector.betweenLocs(bot.location, loc)
diffY /= 2 val norm = vec.normalized()
} val tiles = kotlin.math.min(kotlin.math.floor(vec.magnitude()).toInt(), ServerConstants.MAX_PATHFIND_DISTANCE - 1)
GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { val loc = bot.location.transform(norm * tiles)
override fun pulse(): Boolean { bot.pulseManager.run(object : MovementPulse(bot, loc) { override fun pulse() : Boolean { return true } })
return true
}
})
} }
/** /**
@@ -469,6 +422,7 @@ class ScriptAPI(private val bot: Player) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
bot.unlock() bot.unlock()
bot.properties.teleportLocation = location bot.properties.teleportLocation = location
bot.pulseManager.clear()
bot.animator.reset() bot.animator.reset()
return true return true
} }
@@ -599,6 +553,7 @@ class ScriptAPI(private val bot: Player) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
bot.unlock() bot.unlock()
bot.properties.teleportLocation = location bot.properties.teleportLocation = location
bot.pulseManager.clear()
bot.animator.reset() bot.animator.reset()
return true return true
} }
@@ -78,6 +78,8 @@ public abstract class MovementPulse extends Pulse {
private Function2<Entity,Node,Location> overrideMethod; private Function2<Entity,Node,Location> overrideMethod;
private Location previousLoc;
/** /**
* Constructs a new {@code MovementPulse} {@code Object}. * Constructs a new {@code MovementPulse} {@code Object}.
* *
@@ -263,7 +265,7 @@ public abstract class MovementPulse extends Pulse {
else if (inside) { else if (inside) {
loc = findBorderLocation(); loc = findBorderLocation();
} }
} } else if (loc == previousLoc) return;
if (destination == null) { if (destination == null) {
return; return;
@@ -293,6 +295,7 @@ public abstract class MovementPulse extends Pulse {
} }
Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder); Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder);
loc = destination.getLocation();
near = !path.isSuccessful() || path.isMoveNear(); near = !path.isSuccessful() || path.isMoveNear();
interactLocation = mover.getLocation(); interactLocation = mover.getLocation();
boolean canMove = true; boolean canMove = true;
@@ -330,6 +333,7 @@ public abstract class MovementPulse extends Pulse {
} }
} }
} }
previousLoc = loc;
} }
last = destination.getLocation(); last = destination.getLocation();
} }
@@ -45,11 +45,11 @@ class ScriptProcessor(val entity: Entity) {
if (entity !is Player) return if (entity !is Player) return
if (!entity.delayed() && canProcess && interactTarget != null) { if (!entity.delayed() && canProcess && interactTarget != null) {
if (opScript != null && inOperableDistance()) { if (opScript != null && inOperableDistance()) {
face(entity, interactTarget?.centerLocation ?: return) face(entity, interactTarget?.getFaceLocation(entity.location) ?: return)
processInteractScript(opScript ?: return) processInteractScript(opScript ?: return)
} }
else if (apScript != null && inApproachDistance(apScript ?: return)) { else if (apScript != null && inApproachDistance(apScript ?: return)) {
face(entity, interactTarget?.centerLocation ?: return) face(entity, interactTarget?.getFaceLocation(entity.location) ?: return)
processInteractScript(apScript ?: return) processInteractScript(apScript ?: return)
} }
else if (apScript == null && opScript == null && inOperableDistance()) { else if (apScript == null && opScript == null && inOperableDistance()) {
+17 -1
View File
@@ -9,6 +9,7 @@ import core.game.node.scenery.Scenery;
import core.game.world.map.Direction; import core.game.world.map.Direction;
import core.game.world.map.Location; import core.game.world.map.Location;
import core.tools.StringUtils; import core.tools.StringUtils;
import core.api.utils.Vector;
/** /**
* Represents a node which is anything that is interactable in Keldagrim. * 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); 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. * Gets the name of this node.
* @return The name. * @return The name.
@@ -266,4 +282,4 @@ public abstract class Node {
public void setRenderable(boolean renderable) { public void setRenderable(boolean renderable) {
this.renderable = renderable; this.renderable = renderable;
} }
} }
@@ -150,6 +150,7 @@ object ServerConfigParser {
ServerConstants.DRAGON_AXE_USE_OSRS_SPEC = data.getBoolean("world.dragon_axe_use_osrs_spec", false) 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.DISCORD_OPENRSC_HOOK = data.getString("server.openrsc_integration_webhook", "")
ServerConstants.ENABLE_GLOBALCHAT = data.getBoolean("world.enable_globalchat", true) 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() val logLevel = data.getString("server.log_level", "VERBOSE").uppercase()
ServerConstants.LOG_LEVEL = parseEnumEntry<LogLevel>(logLevel) ?: LogLevel.VERBOSE ServerConstants.LOG_LEVEL = parseEnumEntry<LogLevel>(logLevel) ?: LogLevel.VERBOSE
@@ -201,6 +201,17 @@ public final class RegionPlane {
public Scenery[][] getObjects() { public Scenery[][] getObjects() {
return objects; return objects;
} }
public List<Scenery> getObjectList() {
ArrayList<Scenery> 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. * Clears this region plane.
@@ -422,4 +433,4 @@ public final class RegionPlane {
return chunks; return chunks;
} }
} }
@@ -151,6 +151,10 @@ public abstract class Pathfinder {
return find(start, destination, true, SMART); 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. * Finds a path from the start location to the end location.
* @param destination The destination node. * @param destination The destination node.
@@ -4,6 +4,8 @@ import core.game.world.GameWorld
import core.game.world.map.Direction import core.game.world.map.Direction
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.map.Point import core.game.world.map.Point
import core.api.utils.Vector
import core.ServerConstants
import java.util.Comparator import java.util.Comparator
import java.util.PriorityQueue 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() 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() val path = Path()
foundPath = false foundPath = false
for (x in 0..103) { for (x in 0..103) {
@@ -226,6 +241,8 @@ internal constructor() : Pathfinder() {
path.points.add(Point(absX, absY)) path.points.add(Point(absX, absY))
} }
path.setSuccesful(true) path.setSuccesful(true)
if (end != dest)
path.isMoveNear = true
return path return path
} }
@@ -55,6 +55,7 @@ import core.net.packet.`in`.Packet
import core.net.packet.`in`.RunScript import core.net.packet.`in`.RunScript
import core.tools.Log import core.tools.Log
import core.worker.ManagementEvents import core.worker.ManagementEvents
import core.api.utils.Vector
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.lang.Math.min import java.lang.Math.min
@@ -452,15 +453,22 @@ object PacketProcessor {
//there's more data in this packet, we're just not using it //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 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) if (canWalk && player.interfaceManager.isOpened && !player.interfaceManager.opened.definition.isWalkable)
canWalk = canWalk && player.interfaceManager.close() canWalk = canWalk && player.interfaceManager.close()
if (canWalk && player.interfaceManager.hasChatbox() && !player.interfaceManager.chatbox.definition.isWalkable) if (canWalk && player.interfaceManager.hasChatbox() && !player.interfaceManager.chatbox.definition.isWalkable)
player.interfaceManager.closeChatbox() player.interfaceManager.closeChatbox()
if (!canWalk || !player.dialogueInterpreter.close()) { 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) return sendClearMinimap(player)
} }
@@ -474,7 +482,7 @@ object PacketProcessor {
player.faceLocation(null) player.faceLocation(null)
player.scripts.reset() 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 { override fun pulse(): Boolean {
if (isRunning) if (isRunning)
player.walkingQueue.isRunning = false player.walkingQueue.isRunning = false
@@ -798,4 +806,4 @@ object PacketProcessor {
} }
const val BASE_CHAT_CUTOFF = 81 const val BASE_CHAT_CUTOFF = 81
} }