Skip to content

Commit

Permalink
Run file server earlier on startup & rename forceWalk to exactMove (#467
Browse files Browse the repository at this point in the history
)

* Remove unused VisualEncoder variable

* Rename ForceMovement to ExactMovement

* Rename forceWalk to exactMove

* Split ConnectionGatekeeper into ClientManager and LoginManager to allow loading the Game and File server before Login for faster client connection startup times closes #465
  • Loading branch information
GregHib committed Mar 6, 2024
1 parent 074840b commit b7793e1
Show file tree
Hide file tree
Showing 29 changed files with 345 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import org.koin.dsl.module
import org.rsmod.game.pathfinder.LineValidator
import org.rsmod.game.pathfinder.PathFinder
import org.rsmod.game.pathfinder.StepValidator
import world.gregs.voidps.engine.client.ConnectionGatekeeper
import world.gregs.voidps.engine.client.ConnectionQueue
import world.gregs.voidps.engine.client.LoginManager
import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates
import world.gregs.voidps.engine.data.PlayerAccounts
import world.gregs.voidps.engine.data.definition.*
Expand Down Expand Up @@ -48,7 +48,7 @@ val engineModule = module {
single {
ConnectionQueue(getIntProperty("connectionPerTickCap", 1))
}
single { ConnectionGatekeeper(get()) }
single { LoginManager(get<Players>().indexer) }
single(createdAtStart = true) { GameObjectCollision(get()) }
// Collision
single { Collisions() }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package world.gregs.voidps.engine.client

import world.gregs.voidps.network.SessionManager
import java.util.concurrent.ConcurrentHashMap

/**
* Keeps track of number of clients per ip address
*/
class ClientManager : SessionManager {
private val connections = ConcurrentHashMap<String, Int>()

override fun count(key: String) = connections[key] ?: 0

override fun add(key: String): Int? {
connections[key] = count(key) + 1
return null
}

override fun remove(key: String) {
val count = count(key) - 1
if (count <= 0) {
connections.remove(key)
} else {
connections[key] = count
}
}

override fun clear() {
connections.clear()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package world.gregs.voidps.engine.client

import world.gregs.voidps.engine.client.ui.chat.toInt
import world.gregs.voidps.engine.entity.character.IndexAllocator
import world.gregs.voidps.network.SessionManager
import java.util.concurrent.ConcurrentHashMap

/**
* Keeps track of the players online, prevents duplicate login attempts
*/
class LoginManager(
private val indices: IndexAllocator
) : SessionManager {

private val online = ConcurrentHashMap.newKeySet<String>()

override fun count(key: String) = online.contains(key).toInt()

override fun add(key: String): Int? {
if (!online.add(key)) {
return null
}
return indices.obtain()
}

override fun remove(key: String) {
online.remove(key)
}

override fun clear() {
indices.clear()
online.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@ import world.gregs.voidps.engine.entity.character.move.tele
import world.gregs.voidps.engine.entity.character.npc.NPC
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.entity.character.player.appearance
import world.gregs.voidps.engine.entity.character.player.movementType
import world.gregs.voidps.engine.entity.character.player.skill.Skill
import world.gregs.voidps.engine.entity.obj.GameObject
import world.gregs.voidps.engine.entity.obj.ObjectShape
import world.gregs.voidps.engine.get
import world.gregs.voidps.engine.queue.strongQueue
import world.gregs.voidps.network.visual.VisualMask
import world.gregs.voidps.network.visual.Visuals
import world.gregs.voidps.network.visual.update.Hitsplat
import world.gregs.voidps.network.visual.update.Turn
import world.gregs.voidps.network.visual.update.player.MoveType
import world.gregs.voidps.network.visual.update.player.TemporaryMoveType
import world.gregs.voidps.type.Delta
import world.gregs.voidps.type.Direction
import world.gregs.voidps.type.Distance
Expand All @@ -34,7 +30,7 @@ fun Character.flagForceChat() = visuals.flag(if (this is Player) VisualMask.PLAY

fun Character.flagHits() = visuals.flag(if (this is Player) VisualMask.PLAYER_HITS_MASK else VisualMask.NPC_HITS_MASK)

fun Character.flagForceMovement() = visuals.flag(if (this is Player) VisualMask.PLAYER_FORCE_MOVEMENT_MASK else VisualMask.NPC_FORCE_MOVEMENT_MASK)
fun Character.flagExactMovement() = visuals.flag(if (this is Player) VisualMask.PLAYER_EXACT_MOVEMENT_MASK else VisualMask.NPC_EXACT_MOVEMENT_MASK)

fun Character.flagTurn() = visuals.flag(if (this is Player) VisualMask.PLAYER_TURN_MASK else VisualMask.NPC_TURN_MASK)

Expand Down Expand Up @@ -176,14 +172,14 @@ private fun watchIndex(character: Character) = if (character is Player) characte
* @param startDelay Client ticks until starting the movement
* @param direction The cardinal direction to face during movement
*/
fun Character.setForceMovement(
fun Character.setExactMovement(
endDelta: Delta = Delta.EMPTY,
endDelay: Int = 0,
startDelta: Delta = Delta.EMPTY,
startDelay: Int = 0,
direction: Direction = Direction.NONE
) {
val move = visuals.forceMovement
val move = visuals.exactMovement
check(endDelay > startDelay) { "End delay ($endDelay) must be after start delay ($startDelay)." }
move.startX = startDelta.x
move.startY = startDelta.y
Expand All @@ -192,19 +188,19 @@ fun Character.setForceMovement(
move.endY = endDelta.y
move.endDelay = endDelay
move.direction = direction.ordinal
flagForceMovement()
flagExactMovement()
}

fun Character.forceWalk(delta: Delta, delay: Int = tile.distanceTo(tile.add(delta)) * 30, direction: Direction = Direction.NONE) {
fun Character.exactMove(delta: Delta, delay: Int = tile.distanceTo(tile.add(delta)) * 30, direction: Direction = Direction.NONE) {
val start = tile
tele(delta)
setForceMovement(Delta.EMPTY, delay, start.delta(tile), direction = direction)
setExactMovement(Delta.EMPTY, delay, start.delta(tile), direction = direction)
}

fun Character.forceWalk(target: Tile, delay: Int = tile.distanceTo(target) * 30, direction: Direction = Direction.NONE) {
fun Character.exactMove(target: Tile, delay: Int = tile.distanceTo(target) * 30, direction: Direction = Direction.NONE) {
val start = tile
tele(target)
setForceMovement(Delta.EMPTY, delay, start.delta(tile), direction = direction)
setExactMovement(Delta.EMPTY, delay, start.delta(tile), direction = direction)
}

val Character.turn: Delta
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ suspend fun CharacterContext.pauseForever() {
}

/**
* Movement delay, typically used by interactions that perform animations or force movements
* Movement delay, typically used by interactions that perform animations or exact movements
*/
suspend fun CharacterContext.arriveDelay() {
val delay = character.remaining("last_movement")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package world.gregs.voidps.engine.client

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

class ClientManagerTest {

private lateinit var manager: ClientManager

@BeforeEach
fun setup() {
manager = ClientManager()
}

@Test
fun `Same addresses are counted`() {
val address = "123.456.789"
assertEquals(0, manager.count(address))
manager.add(address)
assertEquals(1, manager.count(address))
manager.add(address)
assertEquals(2, manager.count(address))
manager.add(address)
assertEquals(3, manager.count(address))
}

@Test
fun `Different addresses are separate`() {
assertEquals(0, manager.count("123.456.789"))
manager.add("123.456.789")
assertEquals(0, manager.count("100.000.000"))
manager.add("100.000.000")

assertEquals(1, manager.count("100.000.000"))
assertEquals(1, manager.count("123.456.789"))
}

@Test
fun `Disconnections aren't counted`() {
val address = "123.456.789"
assertEquals(0, manager.count(address))
manager.add(address)
manager.add(address)
assertEquals(2, manager.count(address))
manager.remove(address)
assertEquals(1, manager.count(address))
}

@Test
fun `Too many disconnections isn't negative`() {
val address = "123.456.789"
assertEquals(0, manager.count(address))
manager.add(address)
assertEquals(1, manager.count(address))
manager.remove(address)
assertEquals(0, manager.count(address))
manager.remove(address)
assertEquals(0, manager.count(address))
}

@Test
fun `Clearing removes all counts`() {
val address = "123.456.789"
manager.add(address)
assertEquals(1, manager.count(address))
manager.clear()
assertEquals(0, manager.count(address))
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ internal class ConnectionQueueTest : KoinMock() {
single {
ConnectionQueue(getIntProperty("connectionPerTickCap", 1))
}
single { ConnectionGatekeeper(get()) }
}
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package world.gregs.voidps.engine.client

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import world.gregs.voidps.engine.entity.character.IndexAllocator

class LoginManagerTest {

private lateinit var manager: LoginManager
private lateinit var indices: IndexAllocator

@BeforeEach
fun setup() {
indices = IndexAllocator(5)
manager = LoginManager(indices)
}

@Test
fun `Same names can't be added twice`() {
val name = "player"
assertEquals(0, manager.count(name))
assertEquals(1, manager.add(name))
assertEquals(1, manager.count(name))
assertNull(manager.add(name))
assertEquals(1, manager.count(name))
}

@Test
fun `Different names different count`() {
val name1 = "player1"
val name2 = "player2"
assertEquals(0, manager.count(name1))
assertEquals(1, manager.add(name1))
assertEquals(1, manager.count(name1))
assertEquals(0, manager.count(name2))
assertEquals(2, manager.add(name2))
assertEquals(1, manager.count(name2))
}

@Test
fun `Removed named clear count`() {
val name = "player"
assertEquals(1, manager.add(name))
assertEquals(1, manager.count(name))
manager.remove(name)
assertEquals(0, manager.count(name))
}

@Test
fun `Removing name twice doesn't cause negative count`() {
val name = "player"
assertEquals(1, manager.add(name))
assertEquals(1, manager.count(name))
manager.remove(name)
assertEquals(0, manager.count(name))
manager.remove(name)
assertEquals(0, manager.count(name))
}

@Test
fun `Clear names removes count`() {
val name = "player"
assertEquals(1, manager.add(name))
assertEquals(1, manager.count(name))
manager.clear()
assertEquals(0, manager.count(name))
assertEquals(1, manager.add(name))
}
}
Loading

0 comments on commit b7793e1

Please sign in to comment.