Various refactoring
authorTheSaminator <TheSaminator@users.noreply.github.com>
Wed, 23 Feb 2022 15:00:05 +0000 (10:00 -0500)
committerTheSaminator <TheSaminator@users.noreply.github.com>
Wed, 23 Feb 2022 15:00:05 +0000 (10:00 -0500)
14 files changed:
run [deleted file]
src/commonMain/kotlin/starshipfights/game/ship_factions.kt
src/jvmMain/kotlin/starshipfights/auth/providers.kt
src/jvmMain/kotlin/starshipfights/auth/ratelimit.kt
src/jvmMain/kotlin/starshipfights/auth/utils.kt
src/jvmMain/kotlin/starshipfights/data/admiralty/admiral_names.kt
src/jvmMain/kotlin/starshipfights/data/admiralty/ship_names.kt
src/jvmMain/kotlin/starshipfights/data/auth/user_trophies.kt
src/jvmMain/kotlin/starshipfights/info/views_error.kt
src/jvmMain/kotlin/starshipfights/info/views_user.kt
src/jvmMain/kotlin/starshipfights/server.kt
src/jvmMain/kotlin/starshipfights/server_utils.kt
src/jvmMain/resources/static/images/embed-logo.png [deleted file]
update

diff --git a/run b/run
deleted file mode 100755 (executable)
index c1cd6d7..0000000
--- a/run
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env sh
-
-screen -dmS starshipfights gradle runShadow
index d6431f561b78f9456922ee81979a01c361befc59..45e01d5409d33b2781610c11be2e457022640a6e 100644 (file)
@@ -20,7 +20,7 @@ enum class Faction(
                navyName = "Mechyrdian Star Fleet",
                polityName = "Empire of Mechyrdia",
                demonymSingular = "Mechyrdian",
-               currencyName = "throne",
+               currencyName = "thrones",
                shipPrefix = "CMS ", // Ciarstuos Mehurdiasi Štelnau
                blurbDesc = {
                        p {
@@ -46,7 +46,7 @@ enum class Faction(
                navyName = "Masra Draetsen Khoy'qan",
                polityName = "Diadochus Masra Draetsen",
                demonymSingular = "Diadochi",
-               currencyName = "sylaph",
+               currencyName = "sylaphs",
                shipPrefix = "", // The Diadochi don't use ship prefixes
                blurbDesc = {
                        p {
@@ -66,8 +66,8 @@ enum class Faction(
                navyName = "Isarnareyksk Styurnamariyn",
                polityName = "Isarnareyksk Federation",
                demonymSingular = "Isarnareyksk",
-               currencyName = "mark",
-               shipPrefix = "ISMS ", // Isarnareyksk StyurnaMariyn nu Skyf
+               currencyName = "marks",
+               shipPrefix = "ISS ", // Isarnareyksk Styurnamariyn nu Skyf
                blurbDesc = {
                        p {
                                +"The Isarnareyksk Federation is the largest and most populous successor state to the Fulkreyksk Authoritariat. A shadow of its former glory, Isarnareykk is led by Faurasitand Demeter Ursalia and ruled by dissenting factions such as the tech barons and the revanchist military, that hate each other more than they hate Ursalia."
@@ -92,8 +92,8 @@ enum class Faction(
                navyName = "Imperial States Space Force",
                polityName = "Imperial States of America",
                demonymSingular = "American",
-               currencyName = "dollar",
-               shipPrefix = "ISFC ", // Imperial Space Force Craft
+               currencyName = "dollars",
+               shipPrefix = "ASC ", // American Space Craft
                blurbDesc = {
                        p {
                                +"The Imperial States of America was once the political hyperpower of Earth and beyond, and the ideological bulwark of the Caesarism of its time. They were strong, they were proud... they were hated. Hated to the point that entire nations fled from Earth and colonized the stars just to escape American hegemony."
index 23a971410929f767babaf75b728a69a76446845b..caf1697ae1dc78e8da75e1e026fb77f2f4e88fac 100644 (file)
@@ -161,7 +161,7 @@ interface AuthProvider {
                                        val admiralId = call.parameters["id"]?.let { Id<Admiral>(it) }!!
                                        val admiral = Admiral.get(admiralId)!!
                                        
-                                       if (admiral.owningUser != currentUser) throw ForbiddenException()
+                                       if (admiral.owningUser != currentUser) forbid()
                                        
                                        val newAdmiral = admiral.copy(
                                                name = form["name"]?.takeIf { it.isNotBlank() } ?: admiral.name,
@@ -189,8 +189,8 @@ interface AuthProvider {
                                                admiral.await() to ship.await()
                                        }
                                        
-                                       if (admiral.owningUser != currentUser) throw ForbiddenException()
-                                       if (ship.owningAdmiral != admiralId) throw ForbiddenException()
+                                       if (admiral.owningUser != currentUser) forbid()
+                                       if (ship.owningAdmiral != admiralId) forbid()
                                        
                                        val newName = formParams["name"]?.takeIf { it.isNotBlank() && it.length <= SHIP_NAME_MAX_LENGTH } ?: redirect("/admiral/${admiralId}/manage")
                                        ShipInDrydock.set(shipId, setValue(ShipInDrydock::name, newName))
@@ -216,8 +216,8 @@ interface AuthProvider {
                                                admiral.await() to ship.await()
                                        }
                                        
-                                       if (admiral.owningUser != currentUser) throw ForbiddenException()
-                                       if (ship.owningAdmiral != admiralId) throw ForbiddenException()
+                                       if (admiral.owningUser != currentUser) forbid()
+                                       if (ship.owningAdmiral != admiralId) forbid()
                                        
                                        if (ship.status != DrydockStatus.Ready) redirect("/admiral/${admiralId}/manage")
                                        if (ship.shipType.weightClass.isUnique) redirect("/admiral/${admiralId}/manage")
@@ -241,7 +241,7 @@ interface AuthProvider {
                                        val admiralId = call.parameters["id"]?.let { Id<Admiral>(it) }!!
                                        val admiral = Admiral.get(admiralId)!!
                                        
-                                       if (admiral.owningUser != currentUser) throw ForbiddenException()
+                                       if (admiral.owningUser != currentUser) forbid()
                                        
                                        val shipType = call.parameters["ship"]?.let { param -> ShipType.values().singleOrNull { it.toUrlSlug() == param } }!!
                                        
@@ -288,7 +288,7 @@ interface AuthProvider {
                                        val admiralId = call.parameters["id"]?.let { Id<Admiral>(it) }!!
                                        val admiral = Admiral.get(admiralId)!!
                                        
-                                       if (admiral.owningUser != currentUser) throw ForbiddenException()
+                                       if (admiral.owningUser != currentUser) forbid()
                                        
                                        coroutineScope {
                                                launch { Admiral.del(admiralId) }
@@ -555,7 +555,7 @@ class ProductionAuthProvider(private val discordLogin: DiscordLogin) : AuthProvi
                                }
                                
                                get("/login/discord/callback") {
-                                       val userAgent = call.request.userAgent() ?: throw ForbiddenException()
+                                       val userAgent = call.request.userAgent() ?: forbid()
                                        val principal: OAuthAccessTokenResponse.OAuth2 = call.principal() ?: redirect("/login")
                                        val userInfoJson = httpClient.get<String>("https://discord.com/api/users/@me") {
                                                headers {
index 987c3d583a0152d44bf9b2297cc549a64bdcfc82..6440690c1ba74479fd6afb45ff9cd86a791ae51f 100644 (file)
@@ -11,6 +11,7 @@ import kotlinx.coroutines.delay
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
+import starshipfights.rateLimit
 import kotlin.math.roundToLong
 
 class RateLimit(
@@ -46,6 +47,8 @@ class RateLimit(
                                        val jsonBody = context.response.receive<String>()
                                        val rateLimitedResponse = feature.jsonCodec.decodeFromString(RateLimitedResponse.serializer(), jsonBody)
                                        feature.resetAfter = rateLimitedResponse.retryAfter
+                                       
+                                       rateLimit()
                                } else {
                                        context.response.headers[feature.remainingHeader]?.toIntOrNull()?.let {
                                                feature.remainingRequests = it
index 2df424873d142e20deb89a3e5560cbd95d469ad3..9c9c1ef4ba09847a5c62155a88c1e9d4ea1574e7 100644 (file)
@@ -6,7 +6,7 @@ import io.ktor.http.*
 import io.ktor.request.*
 import io.ktor.sessions.*
 import io.ktor.util.*
-import starshipfights.ForbiddenException
+import starshipfights.forbid
 import starshipfights.data.Id
 import starshipfights.data.auth.User
 import starshipfights.data.auth.UserSession
@@ -73,5 +73,5 @@ suspend fun ApplicationCall.receiveValidatedParameters(): Parameters {
        if (CsrfProtector.verifyNonce(csrfToken, sessionId, request.uri))
                return formInput
        else
-               throw ForbiddenException()
+               forbid()
 }
index 9e9a12e6c2440ded50de89f5ee0119079e9b2637..137f5bffa394708edbe3a804bc184533e21d66fb 100644 (file)
@@ -229,7 +229,7 @@ object AdmiralNames {
        
        private val caliboreseVowels = "aeiouy".toSet()
        private fun randomCaliboreseName(isFemale: Boolean) = caliboreseNames.filter {
-               it.length < 8 && (if (isFemale) it.last() in caliboreseVowels else it.last() !in caliboreseVowels)
+               it.length < 8 && (isFemale == (it.last() in caliboreseVowels))
        }.random() + " " + caliboreseNames.filter { it.length > 7 }.random()
        
        private val diadochiMaleNames = listOf(
@@ -294,13 +294,13 @@ object AdmiralNames {
                "Murder",
                "Gore",
                "Daemon",
-               "Talon"
+               "Talon",
        )
        
        private fun randomDiadochiName(isFemale: Boolean) = (if (isFemale) diadochiFemaleNames else diadochiMaleNames).random() + " " + diadochiEpithetParts.random() + diadochiEpithetParts.random().lowercase()
        
        private val thedishMaleNames = listOf(
-               "Prethoris",
+               "Praethoris",
                "Severus",
                "Augast",
                "Dagobar",
@@ -313,7 +313,9 @@ object AdmiralNames {
                "Toval",
                "Ivon",
                "Belis",
-               "Jorh"
+               "Jorh",
+               "Svar",
+               "Alaric",
        )
        
        private val thedishFemaleNames = listOf(
@@ -330,6 +332,7 @@ object AdmiralNames {
                "Amberli",
                "Alysia",
                "Lenera",
+               "Demeter",
        )
        
        private val thedishSurnames = listOf(
@@ -349,7 +352,10 @@ object AdmiralNames {
                "Arvi",
                "Galvus",
                "Voss",
-               "Mandanof"
+               "Mandanof",
+               "Ursali",
+               "Vytunn",
+               "Quesrinn",
        )
        
        private fun randomThedishName(isFemale: Boolean) = (if (isFemale) thedishFemaleNames else thedishMaleNames).random() + " " + thedishSurnames.random()
index 507d0862ae85b02144ea5e70674e296430df7a15..ac207158cceccd34130eac17879d0b25cf70fb64 100644 (file)
@@ -253,6 +253,7 @@ object ShipNames {
                "Catonsville",
                "Ocean City",
                "Philadelphia",
+               "Somerset",
                "Pittsburgh",
                
                "Las Vegas",
index a2885e5406ca2df057805e543e196ad479a092bd..303b5c029e1c2563c50414e470c733ee6fcab52b 100644 (file)
@@ -36,7 +36,7 @@ object SiteDeveloperTrophy : UserTrophy() {
        override fun ASIDE.render() {
                p {
                        style = "text-align:center;border:2px solid #62a;padding:3px;background-color:#93f;color:#315;font-variant:small-caps;font-family:'Orbitron',sans-serif"
-                       title = "This person helps with coding the site"
+                       title = "This person helps with coding the game"
                        +"Site Developer"
                }
        }
@@ -80,9 +80,11 @@ data class SiteSupporterTrophy(val amountInUsCents: Int) : UserTrophy() {
                get() = 3
 }
 
-fun User.getTrophies(): List<UserTrophy> =
+fun User.getTrophiesUnsorted(): Set<UserTrophy> =
        (if (discordId == CurrentConfiguration.discordClient?.ownerId)
-               listOf(SiteOwnerTrophy)
-       else emptyList()) + (if (amountDonatedInUsCents > 0)
-               listOf(SiteSupporterTrophy(amountDonatedInUsCents))
-       else emptyList())
+               setOf(SiteOwnerTrophy)
+       else emptySet()) + (if (amountDonatedInUsCents > 0)
+               setOf(SiteSupporterTrophy(amountDonatedInUsCents))
+       else emptySet())
+
+fun User.getTrophies(): List<UserTrophy> = getTrophiesUnsorted().sorted()
index da89eeb95dc93499eab72c73ae043cc5c593f8e6..83f9bc290e1207855f986de170cb16dc5d256276 100644 (file)
@@ -40,6 +40,14 @@ suspend fun ApplicationCall.error404(): HTML.() -> Unit = page("Not Found", stan
        devModeCallId(callId)
 }
 
+suspend fun ApplicationCall.error429(): HTML.() -> Unit = page("Too Many Requests", standardNavBar()) {
+       section {
+               h1 { +"Too Many Requests" }
+               p { +"Our server is being bogged down in a quagmire of HTTP requests. Please try again later." }
+       }
+       devModeCallId(callId)
+}
+
 suspend fun ApplicationCall.error503(): HTML.() -> Unit = page("Internal Error", standardNavBar()) {
        section {
                h1 { +"Internal Error" }
index b337c4af8f15b65628776264b80207a4412af1c4..807927f576d41b7b9e1d1c835a316ccf8c2e0299 100644 (file)
@@ -10,7 +10,7 @@ import org.litote.kmongo.and
 import org.litote.kmongo.eq
 import org.litote.kmongo.gt
 import org.litote.kmongo.or
-import starshipfights.ForbiddenException
+import starshipfights.forbid
 import starshipfights.auth.*
 import starshipfights.data.Id
 import starshipfights.data.admiralty.*
@@ -558,7 +558,7 @@ suspend fun ApplicationCall.manageAdmiralPage(): HTML.() -> Unit {
        val admiralId = parameters["id"]?.let { Id<Admiral>(it) }!!
        val admiral = Admiral.get(admiralId)!!
        
-       if (admiral.owningUser != currentUser) throw ForbiddenException()
+       if (admiral.owningUser != currentUser) forbid()
        
        val ownedShips = ShipInDrydock.filter(ShipInDrydock::owningAdmiral eq admiralId).toList()
        
@@ -640,7 +640,7 @@ suspend fun ApplicationCall.manageAdmiralPage(): HTML.() -> Unit {
                section {
                        h2 { +"Manage Fleet" }
                        p {
-                               +"${admiral.fullName} currently owns ${admiral.money} ${admiral.faction.currencyName}s, and earns ${admiral.rank.dailyWage} ${admiral.faction.currencyName}s every day."
+                               +"${admiral.fullName} currently owns ${admiral.money} ${admiral.faction.currencyName}, and earns ${admiral.rank.dailyWage} ${admiral.faction.currencyName}s every day."
                        }
                        table {
                                tr {
@@ -679,7 +679,6 @@ suspend fun ApplicationCall.manageAdmiralPage(): HTML.() -> Unit {
                                                        +ship.shipType.weightClass.sellPrice.toString()
                                                        +" "
                                                        +admiral.faction.currencyName
-                                                       +"s"
                                                        if (ship.status == DrydockStatus.Ready && !ship.shipType.weightClass.isUnique) {
                                                                br
                                                                a(href = "/admiral/${admiralId}/sell/${ship.id}") { +"Sell" }
@@ -703,7 +702,6 @@ suspend fun ApplicationCall.manageAdmiralPage(): HTML.() -> Unit {
                                                        +st.weightClass.buyPrice.toString()
                                                        +" "
                                                        +admiral.faction.currencyName
-                                                       +"s"
                                                        br
                                                        a(href = "/admiral/${admiralId}/buy/${st.toUrlSlug()}") { +"Buy" }
                                                }
@@ -727,8 +725,8 @@ suspend fun ApplicationCall.renameShipPage(): HTML.() -> Unit {
                admiral.await() to ship.await()
        }
        
-       if (admiral.owningUser != currentUser) throw ForbiddenException()
-       if (ship.owningAdmiral != admiralId) throw ForbiddenException()
+       if (admiral.owningUser != currentUser) forbid()
+       if (ship.owningAdmiral != admiralId) forbid()
        
        return page("Renaming Ship", null, null) {
                section {
@@ -777,8 +775,8 @@ suspend fun ApplicationCall.sellShipConfirmPage(): HTML.() -> Unit {
                admiral.await() to ship.await()
        }
        
-       if (admiral.owningUser != currentUser) throw ForbiddenException()
-       if (ship.owningAdmiral != admiralId) throw ForbiddenException()
+       if (admiral.owningUser != currentUser) forbid()
+       if (ship.owningAdmiral != admiralId) forbid()
        
        if (ship.status != DrydockStatus.Ready) redirect("/admiral/${admiralId}/manage")
        if (ship.shipType.weightClass.isUnique) redirect("/admiral/${admiralId}/manage")
@@ -789,7 +787,7 @@ suspend fun ApplicationCall.sellShipConfirmPage(): HTML.() -> Unit {
                section {
                        h1 { +"Are You Sure?" }
                        p {
-                               +"${admiral.fullName} is about to sell the ${ship.shipType.fullDisplayName} ${ship.shipData.fullName} for ${ship.shipType.weightClass.sellPrice} ${admiral.faction.currencyName}s."
+                               +"${admiral.fullName} is about to sell the ${ship.shipType.fullDisplayName} ${ship.shipData.fullName} for ${ship.shipType.weightClass.sellPrice} ${admiral.faction.currencyName}."
                        }
                        form(method = FormMethod.get, action = "/admiral/${admiral.id}/manage") {
                                submitInput {
@@ -813,7 +811,7 @@ suspend fun ApplicationCall.buyShipConfirmPage(): HTML.() -> Unit {
        val admiralId = parameters["id"]?.let { Id<Admiral>(it) }!!
        val admiral = Admiral.get(admiralId)!!
        
-       if (admiral.owningUser != currentUser) throw ForbiddenException()
+       if (admiral.owningUser != currentUser) forbid()
        
        val shipType = parameters["ship"]?.let { param -> ShipType.values().singleOrNull { it.toUrlSlug() == param } }!!
        
@@ -827,7 +825,7 @@ suspend fun ApplicationCall.buyShipConfirmPage(): HTML.() -> Unit {
                        section {
                                h1 { +"Too Expensive" }
                                p {
-                                       +"Unfortunately, the ${shipType.fullDisplayName} is out of ${admiral.fullName}'s budget. It costs ${shipType.weightClass.buyPrice} ${admiral.faction.currencyName}s, and ${admiral.name} only has ${admiral.money} ${admiral.faction.currencyName}s."
+                                       +"Unfortunately, the ${shipType.fullDisplayName} is out of ${admiral.fullName}'s budget. It costs ${shipType.weightClass.buyPrice} ${admiral.faction.currencyName}, and ${admiral.name} only has ${admiral.money} ${admiral.faction.currencyName}s."
                                }
                                form(method = FormMethod.get, action = "/admiral/${admiral.id}/manage") {
                                        submitInput {
@@ -844,7 +842,7 @@ suspend fun ApplicationCall.buyShipConfirmPage(): HTML.() -> Unit {
                section {
                        h1 { +"Are You Sure?" }
                        p {
-                               +"${admiral.fullName} is about to buy a ${shipType.fullDisplayName} for ${shipType.weightClass.buyPrice} ${admiral.faction.currencyName}s."
+                               +"${admiral.fullName} is about to buy a ${shipType.fullDisplayName} for ${shipType.weightClass.buyPrice} ${admiral.faction.currencyName}."
                        }
                        form(method = FormMethod.get, action = "/admiral/${admiral.id}/manage") {
                                submitInput {
@@ -868,7 +866,7 @@ suspend fun ApplicationCall.deleteAdmiralConfirmPage(): HTML.() -> Unit {
        val admiralId = parameters["id"]?.let { Id<Admiral>(it) }!!
        val admiral = Admiral.get(admiralId)!!
        
-       if (admiral.owningUser != currentUser) throw ForbiddenException()
+       if (admiral.owningUser != currentUser) forbid()
        
        return page(
                "Are You Sure?", null, null
index 49c0c9665ca8d96b2efc39bac80173f028795dfe..3453643134baac60a00b65ebcd09e9188a463888 100644 (file)
@@ -73,6 +73,10 @@ fun main() {
                        exception<NullPointerException> {
                                call.respondHtml(HttpStatusCode.NotFound, call.error404())
                        }
+                       exception<RateLimitException> {
+                               call.respondHtml(HttpStatusCode.TooManyRequests, call.error429())
+                       }
+                       
                        exception<Throwable> {
                                call.respondHtml(HttpStatusCode.InternalServerError, call.error503())
                                throw it
index 8fbd578e6642ac7a6f0dae5b8218aac3b73e044a..3d0b138248dc0c12a79096cbd0c26808b72feef0 100644 (file)
@@ -4,8 +4,12 @@ import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
 class ForbiddenException : IllegalArgumentException()
+fun forbid(): Nothing = throw ForbiddenException()
 
 data class HttpRedirectException(val url: String, val permanent: Boolean) : RuntimeException()
 fun redirect(url: String, permanent: Boolean = false): Nothing = throw HttpRedirectException(url, permanent)
 
+class RateLimitException : RuntimeException()
+fun rateLimit(): Nothing = throw RateLimitException()
+
 val sfLogger: Logger = LoggerFactory.getLogger("StarshipFights")
diff --git a/src/jvmMain/resources/static/images/embed-logo.png b/src/jvmMain/resources/static/images/embed-logo.png
deleted file mode 100644 (file)
index b81c35f..0000000
Binary files a/src/jvmMain/resources/static/images/embed-logo.png and /dev/null differ
diff --git a/update b/update
index f40ecd140bfb9dfad6644aeb5cbc791ca1ec2005..7bd0ef65c4a76c2c01f4e9a3de7f6aac0395e111 100755 (executable)
--- a/update
+++ b/update
@@ -2,5 +2,5 @@
 
 screen -S starshipfights -X quit
 git pull
-gradle shadowJar
+gradle clean shadowJar
 screen -dmS starshipfights gradle runShadow