Some code reformatting with ktfmt (Kotlinlang)

This commit is contained in:
dam 2026-06-20 17:24:28 +03:00
parent 2caba4e6dd
commit 5857cba851
No known key found for this signature in database
GPG key ID: 4AF4E722399663FB
7 changed files with 1160 additions and 523 deletions

View file

@ -9,19 +9,19 @@ import core.game.world.map.Point
import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder
/**
* Plans combat-specific chase targets without mutating walking queues.
*/
/** Plans combat-specific chase targets without mutating walking queues. */
object CombatMovementPlanner {
data class Plan(
val targetLocation: Location, val attackTile: Location?
val targetLocation: Location,
val attackTile: Location?,
)
@JvmStatic
fun plan(attacker: Entity, target: Entity): Plan {
val targetLocation = predictedTargetLocation(target)
return Plan(
targetLocation = targetLocation, attackTile = chooseTargetBorderTile(attacker, target, targetLocation)
targetLocation = targetLocation,
attackTile = chooseTargetBorderTile(attacker, target, targetLocation),
)
}
@ -31,7 +31,8 @@ object CombatMovementPlanner {
return true
}
val targetTile = target.getClosestOccupiedTile(attacker.location)
return attacker.location.getDistance(targetTile) > ServerConstants.MAX_PATHFIND_DISTANCE * 2.0
return attacker.location.getDistance(targetTile) >
ServerConstants.MAX_PATHFIND_DISTANCE * 2.0
}
@JvmStatic
@ -114,28 +115,46 @@ object CombatMovementPlanner {
}
@JvmStatic
fun chooseTargetBorderTile(attacker: Entity, target: Entity, targetLocation: Location): Location? {
fun chooseTargetBorderTile(
attacker: Entity,
target: Entity,
targetLocation: Location,
): Location? {
return candidateAttackTiles(attacker, target, targetLocation).firstOrNull()
}
@JvmStatic
fun candidateAttackTiles(attacker: Entity, target: Entity, targetLocation: Location): List<Location> {
fun candidateAttackTiles(
attacker: Entity,
target: Entity,
targetLocation: Location,
): List<Location> {
val candidates = borderTiles(target, targetLocation, attacker.size())
val walkable = candidates.filter { RegionManager.isTeleportPermitted(it) }
val attackable = walkable.filter { canInteractFrom(attacker, it, target, targetLocation) }
return (attackable.ifEmpty { walkable.ifEmpty { candidates } }).sortedWith(compareBy<Location> {
it.getDistance(
attacker.location
)
}.thenBy { it.x }.thenBy { it.y })
return (attackable.ifEmpty { walkable.ifEmpty { candidates } }).sortedWith(
compareBy<Location> {
it.getDistance(attacker.location)
}
.thenBy { it.x }
.thenBy { it.y }
)
}
private fun canInteractFrom(
attacker: Entity, location: Location, target: Entity, targetLocation: Location
attacker: Entity,
location: Location,
target: Entity,
targetLocation: Location,
): Boolean {
if (attacker.size() == 1 && target.size() == 1) {
val direction = Direction.getDirection(location, targetLocation) ?: return false
return direction.canMoveFrom(location.z, location.x, location.y, RegionManager::getClippingFlag)
return direction.canMoveFrom(
location.z,
location.x,
location.y,
RegionManager::getClippingFlag,
)
}
return Pathfinder.canInteract(
location.x,
@ -146,8 +165,10 @@ object CombatMovementPlanner {
target.size(),
target.size(),
0,
targetLocation.z
) { z, x, y -> RegionManager.getClippingFlag(z, x, y) }
targetLocation.z,
) { z, x, y ->
RegionManager.getClippingFlag(z, x, y)
}
}
@JvmStatic
@ -159,10 +180,18 @@ object CombatMovementPlanner {
val maxOffset = targetSize - 1
for (offset in minOffset..maxOffset) {
border.add(Location.create(targetLocation.x - attackerSize, targetLocation.y + offset, plane))
border.add(Location.create(targetLocation.x + targetSize, targetLocation.y + offset, plane))
border.add(Location.create(targetLocation.x + offset, targetLocation.y - attackerSize, plane))
border.add(Location.create(targetLocation.x + offset, targetLocation.y + targetSize, plane))
border.add(
Location.create(targetLocation.x - attackerSize, targetLocation.y + offset, plane)
)
border.add(
Location.create(targetLocation.x + targetSize, targetLocation.y + offset, plane)
)
border.add(
Location.create(targetLocation.x + offset, targetLocation.y - attackerSize, plane)
)
border.add(
Location.create(targetLocation.x + offset, targetLocation.y + targetSize, plane)
)
}
return border.toList()

View file

@ -10,9 +10,7 @@ import core.game.world.map.RegionManager.getClippingFlag
import core.game.world.map.path.Pathfinder
import core.game.world.map.path.Pathfinder.*
/**
* Shared combat reach calculations.
*/
/** Shared combat reach calculations. */
object CombatReach {
@JvmStatic
fun isUsingHalberd(entity: Entity): Boolean {
@ -49,26 +47,74 @@ object CombatReach {
val size = entity.size()
if (distance == 1) {
for (i in 0 until size) {
if (Pathfinder.isStandingIn(e.x - 1, e.y + i, 1, 1, x, y, victim.size(), victim.size())) {
if (
Pathfinder.isStandingIn(
e.x - 1,
e.y + i,
1,
1,
x,
y,
victim.size(),
victim.size(),
)
) {
return true
}
if (Pathfinder.isStandingIn(e.x + size, e.y + i, 1, 1, x, y, victim.size(), victim.size())) {
if (
Pathfinder.isStandingIn(
e.x + size,
e.y + i,
1,
1,
x,
y,
victim.size(),
victim.size(),
)
) {
return true
}
if (Pathfinder.isStandingIn(e.x + i, e.y - 1, 1, 1, x, y, victim.size(), victim.size())) {
if (
Pathfinder.isStandingIn(
e.x + i,
e.y - 1,
1,
1,
x,
y,
victim.size(),
victim.size(),
)
) {
return true
}
if (Pathfinder.isStandingIn(e.x + i, e.y + size, 1, 1, x, y, victim.size(), victim.size())) {
if (
Pathfinder.isStandingIn(
e.x + i,
e.y + size,
1,
1,
x,
y,
victim.size(),
victim.size(),
)
) {
return true
}
}
return victim.getSwingHandler(false).type == CombatStyle.MELEE && e.withinDistance(
victim.location,
1
) && victim.properties.combatPulse.getVictim() === entity && entity.index < victim.index
return victim.getSwingHandler(false).type == CombatStyle.MELEE &&
e.withinDistance(
victim.location,
1,
) &&
victim.properties.combatPulse.getVictim() === entity &&
entity.index < victim.index
}
return entity.centerLocation.withinDistance(
victim.centerLocation, distance + (size shr 1) + (victim.size() shr 1)
victim.centerLocation,
distance + (size shr 1) + (victim.size() shr 1),
)
}
@ -81,7 +127,7 @@ object CombatReach {
second.location.x,
second.location.y,
second.size(),
second.size()
second.size(),
)
}
@ -103,7 +149,9 @@ object CombatReach {
fun canStepTowards(entity: Entity, victim: Entity): InteractionType {
val closestVictimTile = victim.getClosestOccupiedTile(entity.location)
val closestEntityTile = entity.getClosestOccupiedTile(closestVictimTile)
val dir = closestEntityTile.deriveDirection(closestVictimTile) ?: return InteractionType.STILL_INTERACT
val dir =
closestEntityTile.deriveDirection(closestVictimTile)
?: return InteractionType.STILL_INTERACT
var next = closestEntityTile
// A fixed-direction walk can pass beside an oblique target without converging, so
@ -136,37 +184,49 @@ object CombatReach {
val components = next.getStepComponents(dir)
when (dir) {
Direction.NORTH -> if (getClippingFlag(next) and PREVENT_NORTH != 0) return InteractionType.NO_INTERACT
Direction.EAST -> if (getClippingFlag(next) and PREVENT_EAST != 0) return InteractionType.NO_INTERACT
Direction.SOUTH -> if (getClippingFlag(next) and PREVENT_SOUTH != 0) return InteractionType.NO_INTERACT
Direction.WEST -> if (getClippingFlag(next) and PREVENT_WEST != 0) return InteractionType.NO_INTERACT
Direction.NORTH ->
if (getClippingFlag(next) and PREVENT_NORTH != 0) return InteractionType.NO_INTERACT
Direction.EAST ->
if (getClippingFlag(next) and PREVENT_EAST != 0) return InteractionType.NO_INTERACT
Direction.SOUTH ->
if (getClippingFlag(next) and PREVENT_SOUTH != 0) return InteractionType.NO_INTERACT
Direction.WEST ->
if (getClippingFlag(next) and PREVENT_WEST != 0) return InteractionType.NO_INTERACT
Direction.NORTH_EAST -> {
if (getClippingFlag(components[0]) and PREVENT_EAST != 0 || getClippingFlag(components[1]) and PREVENT_NORTH != 0 || getClippingFlag(
next
) and PREVENT_NORTHEAST != 0
) return InteractionType.NO_INTERACT
if (
getClippingFlag(components[0]) and PREVENT_EAST != 0 ||
getClippingFlag(components[1]) and PREVENT_NORTH != 0 ||
getClippingFlag(next) and PREVENT_NORTHEAST != 0
)
return InteractionType.NO_INTERACT
}
Direction.NORTH_WEST -> {
if (getClippingFlag(components[0]) and PREVENT_WEST != 0 || getClippingFlag(components[1]) and PREVENT_NORTH != 0 || getClippingFlag(
next
) and PREVENT_NORTHWEST != 0
) return InteractionType.NO_INTERACT
if (
getClippingFlag(components[0]) and PREVENT_WEST != 0 ||
getClippingFlag(components[1]) and PREVENT_NORTH != 0 ||
getClippingFlag(next) and PREVENT_NORTHWEST != 0
)
return InteractionType.NO_INTERACT
}
Direction.SOUTH_EAST -> {
if (getClippingFlag(components[0]) and PREVENT_EAST != 0 || getClippingFlag(components[1]) and PREVENT_SOUTH != 0 || getClippingFlag(
next
) and PREVENT_SOUTHEAST != 0
) return InteractionType.NO_INTERACT
if (
getClippingFlag(components[0]) and PREVENT_EAST != 0 ||
getClippingFlag(components[1]) and PREVENT_SOUTH != 0 ||
getClippingFlag(next) and PREVENT_SOUTHEAST != 0
)
return InteractionType.NO_INTERACT
}
Direction.SOUTH_WEST -> {
if (getClippingFlag(components[0]) and PREVENT_WEST != 0 || getClippingFlag(components[1]) and PREVENT_SOUTH != 0 || getClippingFlag(
next
) and PREVENT_SOUTHWEST != 0
) return InteractionType.NO_INTERACT
if (
getClippingFlag(components[0]) and PREVENT_WEST != 0 ||
getClippingFlag(components[1]) and PREVENT_SOUTH != 0 ||
getClippingFlag(next) and PREVENT_SOUTHWEST != 0
)
return InteractionType.NO_INTERACT
}
}

View file

@ -16,9 +16,7 @@ import kotlin.math.floor
private const val SEARCH_MAP_SIZE = 128
private const val RING_BUFFER_SIZE = 4096
class RsmodPathfinder(
private val maxWaypoints: Int = 25
) : Pathfinder() {
class RsmodPathfinder(private val maxWaypoints: Int = 25) : Pathfinder() {
private val defaultFinder = ThreadLocal.withInitial {
PathFinder(RegionManager.RSMOD_CLIPPING_FLAGS, SEARCH_MAP_SIZE, RING_BUFFER_SIZE)
@ -35,7 +33,7 @@ class RsmodPathfinder(
type: Int,
walkingFlag: Int,
near: Boolean,
clipMaskSupplier: ClipMaskSupplier?
clipMaskSupplier: ClipMaskSupplier?,
): Path {
val source = requireNotNull(start)
val destination = requireNotNull(dest)
@ -46,7 +44,10 @@ class RsmodPathfinder(
if (magnitude > ServerConstants.MAX_PATHFIND_DISTANCE) {
if (canAttempt(source, destination)) {
end = source.transform(vector.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1))
end =
source.transform(
vector.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1)
)
} else {
path.isMoveNear = true
return path
@ -54,29 +55,31 @@ class RsmodPathfinder(
}
val shape = routeShape(type, sizeX, sizeY)
val finder = if (clipMaskSupplier == null) {
RegionManager.loadClippingWindow(source, SEARCH_MAP_SIZE)
defaultFinder.get()
} else {
val state = suppliedFinder.get()
state.loadCollisionWindow(source, clipMaskSupplier)
state.finder
}
val route = finder.findPath(
level = source.z,
srcX = source.x,
srcZ = source.y,
destX = end.x,
destZ = end.y,
srcSize = moverSize,
destWidth = if (sizeX == 0) 1 else sizeX,
destHeight = if (sizeY == 0) 1 else sizeY,
objRot = rotation,
objShape = shape,
moveNear = near,
blockAccessFlags = walkingFlag,
maxWaypoints = maxWaypoints
)
val finder =
if (clipMaskSupplier == null) {
RegionManager.loadClippingWindow(source, SEARCH_MAP_SIZE)
defaultFinder.get()
} else {
val state = suppliedFinder.get()
state.loadCollisionWindow(source, clipMaskSupplier)
state.finder
}
val route =
finder.findPath(
level = source.z,
srcX = source.x,
srcZ = source.y,
destX = end.x,
destZ = end.y,
srcSize = moverSize,
destWidth = if (sizeX == 0) 1 else sizeX,
destHeight = if (sizeY == 0) 1 else sizeY,
objRot = rotation,
objShape = shape,
moveNear = near,
blockAccessFlags = walkingFlag,
maxWaypoints = maxWaypoints,
)
if (route.failed) {
return path
@ -117,7 +120,7 @@ class RsmodPathfinder(
type: Int,
walkingFlag: Int,
z: Int,
clipMaskSupplier: ClipMaskSupplier?
clipMaskSupplier: ClipMaskSupplier?,
): Boolean {
if (clipMaskSupplier == null) {
RegionManager.loadClippingWindow(Location.create(srcX, srcY, z), SEARCH_MAP_SIZE)
@ -133,14 +136,15 @@ class RsmodPathfinder(
srcSize = moverSize,
objRot = rotation,
objShape = routeShape(type, destWidth, destHeight),
blockAccessFlags = walkingFlag
blockAccessFlags = walkingFlag,
)
}
// Reach checks only read tiles within the source/destination rectangles and
// their cross-axis combinations, so loading just their padded bounding box
// into a reused map produces the same reads as a freshly allocated full map.
val flags = suppliedReachFlags.get()
val destSize = maxOf(if (destWidth == 0) 1 else destWidth, if (destHeight == 0) 1 else destHeight)
val destSize =
maxOf(if (destWidth == 0) 1 else destWidth, if (destHeight == 0) 1 else destHeight)
val minX = maxOf(0, minOf(srcX, destX) - 1)
val minY = maxOf(0, minOf(srcY, destY) - 1)
val maxX = maxOf(srcX + moverSize, destX + destSize) + 1
@ -163,7 +167,7 @@ class RsmodPathfinder(
srcSize = moverSize,
objRot = rotation,
objShape = routeShape(type, destWidth, destHeight),
blockAccessFlags = walkingFlag
blockAccessFlags = walkingFlag,
)
} finally {
for (zoneX in (minX shr 3)..(maxX shr 3)) {
@ -176,25 +180,35 @@ class RsmodPathfinder(
@JvmStatic
fun lineOfSight(
start: Location, dest: Location, moverSize: Int, destWidth: Int, destHeight: Int
start: Location,
dest: Location,
moverSize: Int,
destWidth: Int,
destHeight: Int,
): RayCast {
RegionManager.loadClippingWindow(start, SEARCH_MAP_SIZE)
return lineOfSightLoaded(start, dest, moverSize, destWidth, destHeight)
}
private fun lineOfSightLoaded(
start: Location, dest: Location, moverSize: Int, destWidth: Int, destHeight: Int
start: Location,
dest: Location,
moverSize: Int,
destWidth: Int,
destHeight: Int,
): RayCast {
return projectileLineFinder.get().lineOfSight(
level = start.z,
srcX = start.x,
srcZ = start.y,
destX = dest.x,
destZ = dest.y,
srcSize = moverSize,
destWidth = destWidth,
destHeight = destHeight
)
return projectileLineFinder
.get()
.lineOfSight(
level = start.z,
srcX = start.x,
srcZ = start.y,
destX = dest.x,
destZ = dest.y,
srcSize = moverSize,
destWidth = destWidth,
destHeight = destHeight,
)
}
@JvmStatic
@ -204,7 +218,7 @@ class RsmodPathfinder(
moverSize: Int,
destWidth: Int,
destHeight: Int,
maxRaySteps: Int = Int.MAX_VALUE
maxRaySteps: Int = Int.MAX_VALUE,
): Boolean {
val rayCast = lineOfSight(start, dest, moverSize, destWidth, destHeight)
return rayCast.success && rayCast.coordinates.size <= maxRaySteps
@ -216,10 +230,16 @@ class RsmodPathfinder(
sourceSize: Int,
targetLocation: Location,
targetSize: Int,
maxRaySteps: Int = Int.MAX_VALUE
maxRaySteps: Int = Int.MAX_VALUE,
): Boolean {
RegionManager.loadClippingWindow(sourceLocation, SEARCH_MAP_SIZE)
return hasLineOfSightBetweenLoaded(sourceLocation, sourceSize, targetLocation, targetSize, maxRaySteps)
return hasLineOfSightBetweenLoaded(
sourceLocation,
sourceSize,
targetLocation,
targetSize,
maxRaySteps,
)
}
@JvmStatic
@ -233,7 +253,7 @@ class RsmodPathfinder(
sourceSize: Int,
targetLocation: Location,
targetSize: Int,
maxRaySteps: Int = Int.MAX_VALUE
maxRaySteps: Int = Int.MAX_VALUE,
): Boolean {
if (sourceSize == 1 && targetSize == 1) {
if (maxRaySteps == Int.MAX_VALUE) {
@ -256,7 +276,8 @@ class RsmodPathfinder(
// step, so tiles further than one orthogonal step apart can
// never satisfy a single-step ray.
val manhattan =
kotlin.math.abs(source.x - (targetLocation.x + targetX)) + kotlin.math.abs(source.y - (targetLocation.y + targetY))
kotlin.math.abs(source.x - (targetLocation.x + targetX)) +
kotlin.math.abs(source.y - (targetLocation.y + targetY))
if (manhattan > 1) {
continue
}
@ -273,17 +294,22 @@ class RsmodPathfinder(
return false
}
private fun hasSingleTileLineOfSight(sourceLocation: Location, targetLocation: Location): Boolean {
return projectileLineValidator.get().hasLineOfSight(
level = sourceLocation.z,
srcX = sourceLocation.x,
srcZ = sourceLocation.y,
destX = targetLocation.x,
destZ = targetLocation.y,
srcSize = 1,
destWidth = 1,
destHeight = 1
)
private fun hasSingleTileLineOfSight(
sourceLocation: Location,
targetLocation: Location,
): Boolean {
return projectileLineValidator
.get()
.hasLineOfSight(
level = sourceLocation.z,
srcX = sourceLocation.x,
srcZ = sourceLocation.y,
destX = targetLocation.x,
destZ = targetLocation.y,
srcSize = 1,
destWidth = 1,
destHeight = 1,
)
}
private fun manhattanDistance(first: Location, second: Location): Int {
@ -292,11 +318,12 @@ class RsmodPathfinder(
return dx + dy
}
private fun routeShape(type: Int, sizeX: Int, sizeY: Int): Int = when {
type >= 0 -> type
sizeX != 0 && sizeY != 0 -> 10
else -> -1
}
private fun routeShape(type: Int, sizeX: Int, sizeY: Int): Int =
when {
type >= 0 -> type
sizeX != 0 && sizeY != 0 -> 10
else -> -1
}
private val suppliedReachFlags = ThreadLocal.withInitial { CollisionFlagMap() }
@ -309,7 +336,9 @@ class RsmodPathfinder(
}
private fun loadCollisionWindow(
flags: CollisionFlagMap, start: Location, supplier: ClipMaskSupplier
flags: CollisionFlagMap,
start: Location,
supplier: ClipMaskSupplier,
) {
val baseX = start.x - (SEARCH_MAP_SIZE / 2)
val baseY = start.y - (SEARCH_MAP_SIZE / 2)

View file

@ -14,13 +14,18 @@ class RsmodProjectilePathfinder : Pathfinder() {
type: Int,
walkingFlag: Int,
near: Boolean,
clipMaskSupplier: ClipMaskSupplier?
clipMaskSupplier: ClipMaskSupplier?,
): Path {
val source = requireNotNull(start)
val destination = requireNotNull(end)
val rayCast = RsmodPathfinder.lineOfSight(
start = source, dest = destination, moverSize = size, destWidth = sizeX, destHeight = sizeY
)
val rayCast =
RsmodPathfinder.lineOfSight(
start = source,
dest = destination,
moverSize = size,
destWidth = sizeX,
destHeight = sizeY,
)
val path = Path()
if (!rayCast.success) {
path.isMoveNear = rayCast.alternative

File diff suppressed because it is too large Load diff

View file

@ -37,9 +37,10 @@ class CombatPerformanceTests {
measureCombatTick(load, it)
}
val durations = LongArray(MEASURED_TICKS) { tick ->
measureCombatTick(load, tick + WARMUP_TICKS)
}
val durations =
LongArray(MEASURED_TICKS) { tick ->
measureCombatTick(load, tick + WARMUP_TICKS)
}
val sorted = durations.sorted()
val p90Index = ((sorted.size * 9 + 9) / 10 - 1).coerceIn(0, sorted.lastIndex)
val p90 = sorted[p90Index]
@ -50,12 +51,12 @@ class CombatPerformanceTests {
p90 <= HEADROOM_TICK_BUDGET_MILLIS,
"650-player combat p90 tick time should leave room for slower live hardware. " +
"durations=${durationText}ms, p90=${p90}ms, " +
"budget=${HEADROOM_TICK_BUDGET_MILLIS}ms"
"budget=${HEADROOM_TICK_BUDGET_MILLIS}ms",
)
assertTrue(
max <= LIVE_TICK_BUDGET_MILLIS,
"650-player combat tick should remain under the live 600ms server tick budget. " +
"durations=${durationText}ms, max=${max}ms"
"durations=${durationText}ms, max=${max}ms",
)
} finally {
load?.close()
@ -83,9 +84,10 @@ class CombatPerformanceTests {
configureMeleePlayer(player)
}
val pairs = players.chunked(2).mapIndexed { index, pair ->
CombatPair(pair[0], pair[1], pairOrigin(index))
}
val pairs =
players.chunked(2).mapIndexed { index, pair ->
CombatPair(pair[0], pair[1], pairOrigin(index))
}
val load = CombatLoad(players, pairs, previousWildPvp)
GameWorld.settings!!.wild_pvp_enabled = true
@ -105,7 +107,7 @@ class CombatPerformanceTests {
assertEquals(
TOTAL_PLAYER_COUNT,
CombatMovementIntents.pendingCount(),
"The performance fixture should exercise one combat movement intent per loaded player."
"The performance fixture should exercise one combat movement intent per loaded player.",
)
val start = System.nanoTime()
@ -114,10 +116,11 @@ class CombatPerformanceTests {
}
private fun configureMeleePlayer(player: Player) {
player.properties.attackStyle = WeaponInterface.AttackStyle(
WeaponInterface.STYLE_AGGRESSIVE,
WeaponInterface.BONUS_CRUSH
)
player.properties.attackStyle =
WeaponInterface.AttackStyle(
WeaponInterface.STYLE_AGGRESSIVE,
WeaponInterface.BONUS_CRUSH,
)
player.properties.combatPulse.updateStyle()
player.properties.combatLevel = 126
player.skills.setStaticLevel(Skills.HITPOINTS, 10_000)
@ -131,13 +134,13 @@ class CombatPerformanceTests {
private data class CombatPair(
val first: Player,
val second: Player,
val origin: Location
val origin: Location,
)
private class CombatLoad(
val players: List<Player>,
private val pairs: List<CombatPair>,
private val previousWildPvp: Boolean
private val previousWildPvp: Boolean,
) : AutoCloseable {
fun resetPairPositionsAndMovement(tick: Int) {