From 6e6f60ee27ec9a663bb064a423cadc8c8b683842 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Sun, 11 Sep 2022 13:44:54 +0000 Subject: [PATCH] Added the ability to award credits from a CSV file with an ingame command (ADMIN) Added the ability to modify an individual user's credits (PMOD) Added ContentAPI methods for working with credits Corrected a source of syntactic confusion in the login decoder (no actual change in behavior) --- Server/src/main/kotlin/api/ContentAPI.kt | 26 ++++++ .../main/kotlin/rs09/auth/UserAccountInfo.kt | 5 +- .../command/sets/ModerationCommandSet.kt | 80 ++++++++++++++++++- .../main/kotlin/rs09/net/packet/in/Login.kt | 4 +- 4 files changed, 110 insertions(+), 5 deletions(-) diff --git a/Server/src/main/kotlin/api/ContentAPI.kt b/Server/src/main/kotlin/api/ContentAPI.kt index 274d64855..66d5cd8d1 100644 --- a/Server/src/main/kotlin/api/ContentAPI.kt +++ b/Server/src/main/kotlin/api/ContentAPI.kt @@ -2099,4 +2099,30 @@ fun equipSlot(item: Int) : EquipmentSlot? { return EquipmentSlot .values() .getOrNull(itemDefinition(item).getConfiguration(ItemConfigParser.EQUIP_SLOT, -1)) +} + +/** + * Adjusts the user's credits by the given amount + * @param player the player whose credits to adjust + * @param amt the amount to adjust by - add with positive, remove with negative. + * @return true if successful. Success is defined as always true when adding, but can be false if we are trying to remove, and it would put the amount below 0. + */ +fun updateCredits(player: Player, amount: Int) : Boolean { + val creds = getCredits(player) + amount + + if (creds < 0) + return false + else + player.details.accountInfo.credits = creds + + return true +} + +/** + * Gets the number of credits a user has. + * @param player the player to check + * @return the number of credits they have + */ +fun getCredits(player: Player) : Int { + return player.details.accountInfo.credits } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/auth/UserAccountInfo.kt b/Server/src/main/kotlin/rs09/auth/UserAccountInfo.kt index 0195858da..5d420680d 100644 --- a/Server/src/main/kotlin/rs09/auth/UserAccountInfo.kt +++ b/Server/src/main/kotlin/rs09/auth/UserAccountInfo.kt @@ -23,6 +23,7 @@ class UserAccountInfo( var joinDate: Timestamp ) { companion object { + val default = createDefault() @JvmStatic fun createDefault() : UserAccountInfo { return UserAccountInfo("", "", 0, 0, 0, "", "", 0L, 0L, "", "", "", "", "1,0,8,9", 0L, 0L, false, joinDate = Timestamp(System.currentTimeMillis())).also { it.setInitialReferenceValues() } } @@ -101,5 +102,7 @@ class UserAccountInfo( return result } - + fun isDefault() : Boolean { + return this == default + } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt index 5c4db9bab..0022c7ce1 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt @@ -1,16 +1,27 @@ package rs09.game.system.command.sets +import api.sendMessage import core.game.node.entity.player.Player import core.game.node.entity.player.info.Rights import core.game.system.task.Pulse -import rs09.game.world.GameWorld import core.game.world.map.Location -import rs09.game.world.repository.Repository import core.plugin.Initializable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import rs09.ServerConstants import rs09.ServerStore import rs09.ServerStore.Companion.addToList +import rs09.auth.UserAccountInfo +import rs09.game.system.command.CommandMapping import rs09.game.system.command.Privilege +import rs09.game.world.GameWorld +import rs09.game.world.repository.Repository +import java.io.File +import java.io.FileReader import java.util.concurrent.TimeUnit +import kotlin.math.abs @Initializable /** @@ -233,5 +244,70 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ /** * ============================================================================================================= */ + + define("modcr", Privilege.MODERATOR, "::modcr user_name amount", "Modifies user_name's credits by the given amount.") {player, args -> + val username = (args.getOrNull(1) ?: "").lowercase() + val amount = args.getOrNull(2)?.toIntOrNull() ?: Integer.MIN_VALUE + + if (username.isEmpty()) { + reject(player, "Usage: ::modcr user_name amount") + return@define + } + + if (amount == Integer.MIN_VALUE) { + reject(player, "Usage ::modcr user_name amount") + return@define + } + + val p = Repository.getPlayerByName(username) + val info: UserAccountInfo = if (p == null) + GameWorld.accountStorage.getAccountInfo(username) + else + p.details.accountInfo + + if (info.isDefault()) { + reject(player, "The user you specified ($username) does not exist.") + return@define + } + + info.credits += amount + if (p == null) + GameWorld.accountStorage.update(info) + else + sendMessage(p, "You have been ${if (amount > 0) "granted" else "penalized"} ${abs(amount)} credits.") + + notify(player, "Updated $username's credits to ${info.credits} by ${if (amount > 0) "adding" else "removing"} ${abs(amount)}.") + } + + define("csvmodcr", Privilege.ADMIN, "::csvmodcr filename", "Awards credits based on a csv list from a file. Relative to data dir.") {player, args -> + val filename = args.getOrNull(1) ?: "" + + if (filename.isEmpty()) { + reject(player, "Usage: ::csvmodcr filename") + return@define + } + + GlobalScope.launch { + val f = File(ServerConstants.DATA_PATH + File.separator + filename) + if (!f.exists()) { + notify(player, "Unable to locate file $filename.") + notify(player, "Note: The file must be in ${ServerConstants.DATA_PATH}") + return@launch + } + val lines: List + withContext(Dispatchers.IO) { + FileReader(f).use { + lines = f.readLines() + } + } + for (line in lines) { + if (line.startsWith("#")) continue + val tokens = line.split(",") + val username = tokens[0].lowercase().trim().replace(" ", "_") + val amount = tokens[1].trim() + CommandMapping.get("modcr")?.handle?.invoke(player, arrayOf("", username, amount)) + } + } + } } } diff --git a/Server/src/main/kotlin/rs09/net/packet/in/Login.kt b/Server/src/main/kotlin/rs09/net/packet/in/Login.kt index 9fdaf0d6d..6955c6024 100644 --- a/Server/src/main/kotlin/rs09/net/packet/in/Login.kt +++ b/Server/src/main/kotlin/rs09/net/packet/in/Login.kt @@ -108,7 +108,7 @@ object Login { return try { val numBytes = buffer.get().toInt() and 0xFF val encryptedBytes = ByteArray(numBytes) - buffer[encryptedBytes] + buffer.get(encryptedBytes) val encryptedBigInt = BigInteger(encryptedBytes) ByteBuffer.wrap(encryptedBigInt.modPow(exponent, modulus).toByteArray()) @@ -117,7 +117,7 @@ object Login { } } - private fun noop(buffer: ByteBuffer, amount: Int = 1) {buffer[ByteArray(amount)]} + private fun noop(buffer: ByteBuffer, amount: Int = 1) {buffer.get(ByteArray(amount))} fun proceedWith(session: IoSession, details: PlayerDetails, opcode: Int) { if (!Repository.LOGGED_IN_PLAYERS.contains(details.username))