Skip to content

Commit

Permalink
Move login and logout out of Player and into PlayerAccounts #497
Browse files Browse the repository at this point in the history
  • Loading branch information
GregHib committed Mar 23, 2024
1 parent a002b03 commit 5627b5b
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class PlayerAccountLoader(
withContext(gameContext) {
queue.await()
logger.info { "Player logged in $username index $index." }
player.login(client, displayMode)
accounts.login(player, client, displayMode)
}
return player.instructions
} catch (e: IllegalStateException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.mindrot.jbcrypt.BCrypt
import world.gregs.voidps.engine.client.ConnectionQueue
import world.gregs.voidps.engine.client.ui.InterfaceOptions
import world.gregs.voidps.engine.client.ui.Interfaces
import world.gregs.voidps.engine.client.ui.chat.plural
import world.gregs.voidps.engine.client.update.view.Viewport
import world.gregs.voidps.engine.client.variable.PlayerVariables
import world.gregs.voidps.engine.data.definition.*
import world.gregs.voidps.engine.entity.Despawn
import world.gregs.voidps.engine.entity.Spawn
import world.gregs.voidps.engine.entity.World
import world.gregs.voidps.engine.entity.character.mode.move.AreaEntered
import world.gregs.voidps.engine.entity.character.mode.move.AreaExited
import world.gregs.voidps.engine.entity.character.move.previousTile
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.entity.character.player.PlayerOptions
import world.gregs.voidps.engine.entity.character.player.appearance
import world.gregs.voidps.engine.entity.character.player.name
import world.gregs.voidps.engine.entity.character.player.*
import world.gregs.voidps.engine.entity.character.player.skill.level.PlayerLevels
import world.gregs.voidps.engine.get
import world.gregs.voidps.engine.inv.equipment
import world.gregs.voidps.engine.inv.restrict.ValidItemRestriction
import world.gregs.voidps.engine.inv.stack.DependentOnItem
import world.gregs.voidps.engine.map.collision.CollisionStrategyProvider
import world.gregs.voidps.engine.map.zone.RegionLoad
import world.gregs.voidps.engine.queue.strongQueue
import world.gregs.voidps.network.client.Client
import world.gregs.voidps.network.encode.login
import world.gregs.voidps.network.encode.logout
import world.gregs.voidps.network.visual.PlayerVisuals
import world.gregs.voidps.type.Direction
import world.gregs.voidps.type.Tile
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.CoroutineContext
import kotlin.system.measureTimeMillis

class PlayerAccounts(
private val interfaceDefinitions: InterfaceDefinitions,
Expand All @@ -36,32 +49,37 @@ class PlayerAccounts(
) : Runnable, CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.IO
private val validItems = ValidItemRestriction(itemDefinitions)
private val saveQueue = mutableMapOf<String, PlayerSave>()
private val pending = ConcurrentHashMap<String, PlayerSave>()
private val logger = InlineLogger()

override fun run() {
if (saveQueue.isEmpty()) {
if (pending.isEmpty()) {
return
}
val accounts = saveQueue.values.toList()
saveQueue.clear()
val accounts = pending.values.toList()
launch {
try {
storage.save(accounts)
val took = measureTimeMillis {
storage.save(accounts)
for (account in accounts) {
pending.remove(account.name)
}
}
logger.info { "Saved ${accounts.size} ${"account".plural(accounts.size)} in ${took}ms" }
} catch (e: Exception) {
logger.error(e) { "Error saving players!" }
}
}
}

fun queueSave(player: Player) {
fun save(player: Player) {
if (player.contains("bot")) {
return
}
saveQueue[player.accountName] = player.copy()
pending[player.accountName] = player.copy()
}

fun saving(name: String) = saveQueue.containsKey(name)
fun saving(name: String) = pending.containsKey(name)

fun getOrElse(name: String, index: Int, block: () -> Player): Player {
val player = storage.load(name)?.toPlayer() ?: block()
Expand Down Expand Up @@ -102,4 +120,59 @@ class PlayerAccounts(
player.collision = collisionStrategyProvider.get(character = player)
}

fun login(player: Player, client: Client? = null, displayMode: Int = 0) {
player.interfaces.displayMode = displayMode
if (client != null) {
player.viewport = Viewport()
client.login(player.name, player.index, player.rights.ordinal, membersWorld = World.members)
player.client = client
player.interfaces.client = client
(player.variables as PlayerVariables).client = client
client.onDisconnecting {
logout(player, false)
}
}
player.emit(RegionLoad)
player.emit(Spawn)
val definitions = get<AreaDefinitions>()
for (def in definitions.get(player.tile.zone)) {
if (player.tile in def.area) {
player.emit(AreaEntered(player, def.name, def.tags, def.area))
}
}
}

fun logout(player: Player, safely: Boolean) {
if (player["logged_out", false]) {
return
}
player["logged_out"] = true
if (safely) {
player.client?.logout()
player.strongQueue("logout") {
// Make sure nothing else starts
}
}
player.client?.disconnect()
val queue: ConnectionQueue = get()
queue.disconnect {
val players: Players = get()
World.queue("logout", 1) {
players.remove(player)
players.removeIndex(player)
players.releaseIndex(player)
}
val definitions = get<AreaDefinitions>()
for (def in definitions.get(player.tile.zone)) {
if (player.tile in def.area) {
player.emit(AreaExited(player, def.name, def.tags, def.area))
}
}
player.emit(Despawn)
player.queue.logout()
player.softTimers.stopAll()
player.timers.stopAll()
save(player)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,26 @@ package world.gregs.voidps.engine.entity.character.player

import kotlinx.coroutines.flow.MutableSharedFlow
import org.rsmod.game.pathfinder.collision.CollisionStrategy
import world.gregs.voidps.engine.client.ConnectionQueue
import world.gregs.voidps.engine.client.ui.InterfaceOptions
import world.gregs.voidps.engine.client.ui.Interfaces
import world.gregs.voidps.engine.client.update.view.Viewport
import world.gregs.voidps.engine.client.variable.PlayerVariables
import world.gregs.voidps.engine.client.variable.Variables
import world.gregs.voidps.engine.data.PlayerAccounts
import world.gregs.voidps.engine.data.definition.AreaDefinitions
import world.gregs.voidps.engine.entity.Despawn
import world.gregs.voidps.engine.entity.Spawn
import world.gregs.voidps.engine.entity.World
import world.gregs.voidps.engine.entity.character.Character
import world.gregs.voidps.engine.entity.character.mode.EmptyMode
import world.gregs.voidps.engine.entity.character.mode.Mode
import world.gregs.voidps.engine.entity.character.mode.move.AreaEntered
import world.gregs.voidps.engine.entity.character.mode.move.AreaExited
import world.gregs.voidps.engine.entity.character.mode.move.Steps
import world.gregs.voidps.engine.entity.character.player.chat.clan.ClanRank
import world.gregs.voidps.engine.entity.character.player.equip.BodyParts
import world.gregs.voidps.engine.entity.character.player.skill.exp.Experience
import world.gregs.voidps.engine.entity.character.player.skill.level.Levels
import world.gregs.voidps.engine.get
import world.gregs.voidps.engine.inv.Inventories
import world.gregs.voidps.engine.map.zone.RegionLoad
import world.gregs.voidps.engine.queue.ActionQueue
import world.gregs.voidps.engine.queue.strongQueue
import world.gregs.voidps.engine.suspend.Suspension
import world.gregs.voidps.engine.timer.TimerQueue
import world.gregs.voidps.engine.timer.Timers
import world.gregs.voidps.network.Instruction
import world.gregs.voidps.network.client.Client
import world.gregs.voidps.network.encode.login
import world.gregs.voidps.network.encode.logout
import world.gregs.voidps.network.visual.PlayerVisuals
import world.gregs.voidps.type.Tile
import kotlin.coroutines.Continuation
Expand Down Expand Up @@ -105,66 +92,6 @@ class Player(

override val steps = Steps(this)

fun login(client: Client? = null, displayMode: Int = 0) {
interfaces.displayMode = displayMode
if (client != null) {
this.viewport = Viewport()
client.login(name, index, rights.ordinal, membersWorld = World.members)
this.client = client
interfaces.client = client
(variables as PlayerVariables).client = client
client.onDisconnecting {
logout(false)
}
}
emit(RegionLoad)
emit(Spawn)
val definitions = get<AreaDefinitions>()
for (def in definitions.get(tile.zone)) {
if (tile in def.area) {
emit(AreaEntered(this, def.name, def.tags, def.area))
}
}
}

fun logout(safely: Boolean) {
if (this["logged_out", false]) {
return
}
this["logged_out"] = true
if (safely) {
client?.logout()
strongQueue("logout") {
// Make sure nothing else starts
}
}
disconnect()
}

private fun disconnect() {
client?.disconnect()
val queue: ConnectionQueue = get()
queue.disconnect {
val players: Players = get()
World.queue("logout", 1) {
players.remove(this@Player)
players.removeIndex(this@Player)
players.releaseIndex(this@Player)
}
val definitions = get<AreaDefinitions>()
for (def in definitions.get(tile.zone)) {
if (tile in def.area) {
emit(AreaExited(this@Player, def.name, def.tags, def.area))
}
}
emit(Despawn)
this.queue.logout()
softTimers.stopAll()
timers.stopAll()
get<PlayerAccounts>().queueSave(this)
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ internal class PlayerAccountLoaderTest : KoinMock() {
loader.load(client, "name", "pass", 2, 3)

coVerify {
player.login(client, 3)
factory.login(player, client, 3)
}
}

Expand Down
5 changes: 3 additions & 2 deletions game/src/main/kotlin/world/gregs/voidps/bot/BotSpawns.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,16 @@ fun spawn() {
GlobalScope.launch(Contexts.Game) {
val name = "Bot ${++counter}"
val index = manager.add(name)!!
val bot = accounts.getOrElse(name, index) { Player(index = index, tile = lumbridge.random(), accountName = name) }
val bot = Player(index = index, tile = lumbridge.random(), accountName = name)
accounts.initPlayer(bot, index)
setAppearance(bot)
queue.await()
if (bot.inventory.isEmpty()) {
bot.inventory.add("coins", 10000)
}
val client = if (TaskManager.DEBUG) DummyClient() else null
bot.initBot()
bot.login(client, 0)
accounts.login(bot, client, 0)
bot.emit(StartBot)
bot.viewport?.loaded = true
delay(3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ adminCommand("npc") {

modCommand("save") {
val account: PlayerAccounts = get()
players.forEach(account::queueSave)
players.forEach(account::save)
}

val definitions: ItemDefinitions by inject()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ package world.gregs.voidps.world.interact.entity.player.spawn
import world.gregs.voidps.engine.client.message
import world.gregs.voidps.engine.client.ui.interfaceOption
import world.gregs.voidps.engine.client.ui.open
import world.gregs.voidps.engine.data.PlayerAccounts
import world.gregs.voidps.engine.inject
import world.gregs.voidps.world.interact.entity.combat.inCombat

interfaceOption("Exit", "logout", "toplevel*") {
player.open("logout")
}

val accounts: PlayerAccounts by inject()

interfaceOption(id = "logout") {
if (player.inCombat) {
player.message("You can't log out until 8 seconds after the end of combat.")
return@interfaceOption
}
player.logout(true)
accounts.logout(player, true)
}
2 changes: 1 addition & 1 deletion game/src/main/resources/game.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ admin=Greg
loginLimit=10
randomWalk=true
characterCollision=true
loadUnusedObjects=true
loadUnusedObjects=false
connectionPerTickCap=25
# Events
shootingStars=true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ abstract class WorldTest : KoinTest {
tick()
player["creation"] = -1
player["skip_level_up"] = true
player.login(null, 0)
accounts.login(player, null, 0)
player.softTimers.clear("restore_stats")
player.softTimers.clear("restore_hitpoints")
tick()
Expand Down

0 comments on commit 5627b5b

Please sign in to comment.