Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions data/client/client.scripts.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ id = 571

[primary_options]
id = 150
params = [
"component_id",
"inventory_id",
"inv_width",
"inv_height",
"drag_type", # 1=swap, 2=shuffle
"inv_drag_iface",
"options",
]

[secondary_options]
id = 695
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fun Character.message(
getOrPut("messages") { FixedSizeQueue<String>(100) }.add(text)
val font = get<FontDefinitions>().get("p12_full")
for (line in font.splitLines(Colours.replaceCustomTags(text), if (type == ChatType.Console) 600 else 484)) {
client?.message(line, type.id, tile, name, name?.lowercase(Locale.getDefault())?.replace(' ', '_'))
client?.message(line, type.id, tile, name, name?.lowercase(Locale.getDefault()))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ class PlayerAccountLoader(

var update: Boolean = false

override fun used(username: String) = accountDefinitions.get(username) != null

override fun exists(username: String): Boolean = storage.exists(username)

override fun password(username: String): String? = accountDefinitions.get(username)?.passwordHash
override fun password(username: String): String? = accountDefinitions.getByAccount(username)?.passwordHash

/**
* @return flow of instructions for the player to be controlled with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ class AccountDefinitions(

fun clan(displayName: String) = clans[displayName.lowercase()]

fun getByAccount(account: String): AccountDefinition? {
return get(displayNames[account.lowercase()] ?: return null)
fun getByAccount(accountName: String): AccountDefinition? {
return get(displayNames[accountName.lowercase()] ?: return null)
}

fun get(key: String) = definitions[key.lowercase()]
fun get(displayName: String) = definitions[displayName.lowercase()]

fun getValue(key: String) = definitions.getValue(key.lowercase())
fun getValue(displayName: String) = definitions.getValue(displayName.lowercase())

fun load(storage: Storage = get()): AccountDefinitions {
timedLoad("account") {
for ((name, definition) in storage.names()) {
definitions[name.lowercase()] = definition
for ((_, definition) in storage.names()) {
definitions[definition.displayName.lowercase()] = definition
}
for (def in definitions.values) {
displayNames[def.accountName.lowercase()] = def.displayName
Expand All @@ -85,16 +85,19 @@ class AccountDefinitions(
skip: (accountName: String) -> Boolean,
): Int {
var count = 0
for ((name, definition) in names) {
for ((_, definition) in names) {
if (skip(definition.accountName)) {
continue
}
val existing = definitions[name.lowercase()]
val displayName = displayNames[definition.accountName.lowercase()]?.lowercase() ?: definition.displayName.lowercase()
val existing = definitions[displayName]
if (existing == null) {
definitions[name.lowercase()] = definition
definitions[displayName] = definition
} else if (existing == definition) {
continue
} else {
definitions.remove(displayName)
definitions[definition.displayName.lowercase()] = existing
existing.displayName = definition.displayName
existing.previousName = definition.previousName
existing.passwordHash = definition.passwordHash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ internal class PlayerAccountLoaderTest : KoinMock() {
override fun load(accountName: String): PlayerSave? = playerSave
}
saveQueue = SaveQueue(storage, scope = TestScope())
definitions = AccountDefinitions(mutableMapOf("name" to AccountDefinition("name", "oldName", "", "hash")))
definitions = AccountDefinitions(mutableMapOf("name" to AccountDefinition("name", "oldName", "", "hash")), mutableMapOf("accountname" to "name"))
accounts = mockk(relaxed = true)
loader = PlayerAccountLoader(queue, storage, accounts, saveQueue, definitions, UnconfinedTestDispatcher())
}

@Test
fun `Get password`() {
assertEquals("hash", loader.password("name"))
assertEquals("hash", loader.password("accountName"))
assertNull(loader.password("name2"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class AccountDefinitionsReloaderTest {

assertTrue(started)
assertEquals(1, completed)
assertEquals("hash", definitions.get("account")?.passwordHash)
assertEquals("hash", definitions.get("Name")?.passwordHash)
}

@Test
Expand All @@ -46,11 +46,11 @@ class AccountDefinitionsReloaderTest {

assertTrue(reloader.reload())

assertEquals("old_hash", definitions.get("account")?.passwordHash)
assertEquals("old_hash", definitions.get("Name")?.passwordHash)
every { storage.names() } returns mapOf("account" to AccountDefinition("account", "Name", "", "new_hash"))
every { storage.clans() } returns emptyMap()
assertTrue(reloader.reload())
assertEquals("new_hash", definitions.get("account")?.passwordHash)
assertEquals("new_hash", definitions.get("Name")?.passwordHash)
}

@Test
Expand All @@ -66,7 +66,7 @@ class AccountDefinitionsReloaderTest {
assertTrue(reloader.reload { count -> completed = count })

assertEquals(0, completed)
assertEquals("memory_hash", definitions.get("account")?.passwordHash)
assertEquals("memory_hash", definitions.get("Name")?.passwordHash)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class AccountDefinitionsTest {
val count = definitions.merge(mapOf("new_account" to definition), emptyMap()) { false }

assertEquals(1, count)
assertSame(definition, definitions.get("new_account"))
assertSame(definition, definitions.get("Newbie"))
assertEquals("Newbie", definitions.displayNames["new_account"])
}

Expand All @@ -38,7 +38,7 @@ class AccountDefinitionsTest {
val count = definitions.merge(mapOf("account" to definition("account", "New name", hash = "new_hash")), emptyMap()) { false }

assertEquals(1, count)
val updated = definitions.get("account")
val updated = definitions.get("New name")
assertSame(existing, updated)
assertEquals("New name", updated?.displayName)
assertEquals("new_hash", updated?.passwordHash)
Expand All @@ -64,7 +64,7 @@ class AccountDefinitionsTest {
}

assertEquals(0, count)
assertEquals("memory_hash", definitions.get("online_account")?.passwordHash)
assertEquals("memory_hash", definitions.get("Online")?.passwordHash)
}

@Test
Expand All @@ -73,7 +73,7 @@ class AccountDefinitionsTest {

definitions.merge(emptyMap(), emptyMap()) { false }

assertEquals("hash", definitions.get("account")?.passwordHash)
assertEquals("hash", definitions.get("Name")?.passwordHash)
}

@Test
Expand All @@ -88,7 +88,7 @@ class AccountDefinitionsTest {
@Test
fun `Merge updates existing clan in place preserving members`() {
val existing = clan("owner_account", "Owner")
val member: Player = Player(accountName = "member_account")
val member = Player(accountName = "member_account")
existing.members.add(member)
definitions.merge(emptyMap(), mapOf("owner_account" to existing)) { false }

Expand Down
1 change: 1 addition & 0 deletions game/src/main/kotlin/content/entity/player/Introduction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Introduction : Script {
return
}
if (Settings["world.start.creation", true] && !player.isBot) {
player.sendVariable("movement")
player["delay"] = -1
World.queue("welcome_${player.name}", 1) {
player.open("character_creation")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import world.gregs.voidps.network.client.Instruction
* Loads and checks existing accounts
*/
interface AccountLoader {
fun used(username: String): Boolean

fun exists(username: String): Boolean

fun password(username: String): String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class PasswordManager(private val account: AccountLoader) {
return Response.LOGIN_SERVER_REJECTED_SESSION
}
val passwordHash = account.password(username)
if (passwordHash == null && account.used(username)) {
// Username already in use as a display name
return Response.INVALID_CREDENTIALS
}
if (!account.exists(username)) {
if (passwordHash != null) {
// Failed to find account file despite AccountDefinition exists in memory (aka existed on startup)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ internal class LoginServerTest {
password = "\$2a\$10${"$"}iIdTrtrJ5ibgFcJToZW7ueGkymDed2Ws2FoE8JnrXPGiY2YNVa9y6"
instructions = Channel(capacity = 1)
accounts = object : AccountLoader {
override fun used(username: String) = false

override fun exists(username: String) = true

override fun password(username: String) = password
Expand Down Expand Up @@ -230,6 +232,8 @@ internal class LoginServerTest {
val writeChannel = ByteChannel(autoFlush = true)
accounts = object : AccountLoader {

override fun used(username: String) = false

override fun exists(username: String) = false

override fun password(username: String) = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ class PasswordManagerTest {
assertEquals(Response.SUCCESS, result)
}

@Test
fun `Can't create new player if username is in use`() {
val username = "test"
val password = "password"
accountLoader.used = true

val result = passwordManager.validate(username, password)

assertEquals(Response.INVALID_CREDENTIALS, result)
}

@Test
fun `Existing player with no password is disabled`() {
val username = "test"
Expand Down Expand Up @@ -103,6 +114,9 @@ class PasswordManagerTest {
private class TestAccountLoader : AccountLoader {
val accountMap = mutableMapOf<String, String>()
var exists = true
var used = false

override fun used(username: String): Boolean = used

override fun exists(username: String): Boolean = exists

Expand Down
Loading