diff --git a/data/client/client.scripts.toml b/data/client/client.scripts.toml index f454839062..2ba7861534 100644 --- a/data/client/client.scripts.toml +++ b/data/client/client.scripts.toml @@ -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 diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/client/EncodeExtensions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/client/EncodeExtensions.kt index 4f43ab9f6a..74543bc3d3 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/client/EncodeExtensions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/client/EncodeExtensions.kt @@ -36,7 +36,7 @@ fun Character.message( getOrPut("messages") { FixedSizeQueue(100) }.add(text) val font = get().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())) } } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoader.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoader.kt index e7f72efe22..657d182beb 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoader.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoader.kt @@ -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 diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitions.kt index 9559df733b..1b619486e5 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitions.kt @@ -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 @@ -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 diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoaderTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoaderTest.kt index d650eca223..981d87f91d 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoaderTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/client/PlayerAccountLoaderTest.kt @@ -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")) } diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/data/AccountDefinitionsReloaderTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/data/AccountDefinitionsReloaderTest.kt index 46ed948767..85f39dbfda 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/data/AccountDefinitionsReloaderTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/data/AccountDefinitionsReloaderTest.kt @@ -36,7 +36,7 @@ class AccountDefinitionsReloaderTest { assertTrue(started) assertEquals(1, completed) - assertEquals("hash", definitions.get("account")?.passwordHash) + assertEquals("hash", definitions.get("Name")?.passwordHash) } @Test @@ -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 @@ -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 diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitionsTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitionsTest.kt index b0b7bd043d..e28066803a 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitionsTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/data/definition/AccountDefinitionsTest.kt @@ -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"]) } @@ -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) @@ -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 @@ -73,7 +73,7 @@ class AccountDefinitionsTest { definitions.merge(emptyMap(), emptyMap()) { false } - assertEquals("hash", definitions.get("account")?.passwordHash) + assertEquals("hash", definitions.get("Name")?.passwordHash) } @Test @@ -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 } diff --git a/game/src/main/kotlin/content/entity/player/Introduction.kt b/game/src/main/kotlin/content/entity/player/Introduction.kt index 1e46daee17..915d27cd1c 100644 --- a/game/src/main/kotlin/content/entity/player/Introduction.kt +++ b/game/src/main/kotlin/content/entity/player/Introduction.kt @@ -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") diff --git a/network/src/main/kotlin/world/gregs/voidps/network/login/AccountLoader.kt b/network/src/main/kotlin/world/gregs/voidps/network/login/AccountLoader.kt index 2db832a876..fac756c084 100644 --- a/network/src/main/kotlin/world/gregs/voidps/network/login/AccountLoader.kt +++ b/network/src/main/kotlin/world/gregs/voidps/network/login/AccountLoader.kt @@ -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? diff --git a/network/src/main/kotlin/world/gregs/voidps/network/login/PasswordManager.kt b/network/src/main/kotlin/world/gregs/voidps/network/login/PasswordManager.kt index cb08a20603..593bc8b245 100644 --- a/network/src/main/kotlin/world/gregs/voidps/network/login/PasswordManager.kt +++ b/network/src/main/kotlin/world/gregs/voidps/network/login/PasswordManager.kt @@ -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) diff --git a/network/src/test/kotlin/world/gregs/voidps/network/LoginServerTest.kt b/network/src/test/kotlin/world/gregs/voidps/network/LoginServerTest.kt index bacab22dc0..5a2489aefe 100644 --- a/network/src/test/kotlin/world/gregs/voidps/network/LoginServerTest.kt +++ b/network/src/test/kotlin/world/gregs/voidps/network/LoginServerTest.kt @@ -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 @@ -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 diff --git a/network/src/test/kotlin/world/gregs/voidps/network/PasswordManagerTest.kt b/network/src/test/kotlin/world/gregs/voidps/network/PasswordManagerTest.kt index 062f0f3544..ca13ddaf86 100644 --- a/network/src/test/kotlin/world/gregs/voidps/network/PasswordManagerTest.kt +++ b/network/src/test/kotlin/world/gregs/voidps/network/PasswordManagerTest.kt @@ -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" @@ -103,6 +114,9 @@ class PasswordManagerTest { private class TestAccountLoader : AccountLoader { val accountMap = mutableMapOf() var exists = true + var used = false + + override fun used(username: String): Boolean = used override fun exists(username: String): Boolean = exists