import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.serialization.json.Json
-import kotlin.math.abs
import kotlin.math.roundToInt
-import kotlin.math.sign
-import kotlin.math.sqrt
val jsonSerializer = Json {
classDiscriminator = "\$ktClass"
import starshipfights.game.AdmiralRank
import starshipfights.game.Faction
import starshipfights.info.*
+import java.time.Instant
+import java.time.temporal.ChronoUnit
interface AuthProvider {
fun installApplication(app: Application) = Unit
get("/logout") {
call.getUserSession()?.let { sess ->
launch {
- val newTime = System.currentTimeMillis() - 100
- UserSession.update(UserSession::id eq sess.id, setValue(UserSession::expirationMillis, newTime))
+ val newTime = Instant.now().minusMillis(100)
+ UserSession.update(UserSession::id eq sess.id, setValue(UserSession::expiration, newTime))
}
}
val id = Id<UserSession>(call.parameters.getOrFail("id"))
call.getUserSession()?.let { sess ->
launch {
- val newTime = System.currentTimeMillis() - 100
- UserSession.update(and(UserSession::id eq id, UserSession::user eq sess.user), setValue(UserSession::expirationMillis, newTime))
+ val newTime = Instant.now().minusMillis(100)
+ UserSession.update(and(UserSession::id eq id, UserSession::user eq sess.user), setValue(UserSession::expiration, newTime))
}
}
get("/logout-all") {
call.getUserSession()?.let { sess ->
launch {
- val newTime = System.currentTimeMillis() - 100
- UserSession.update(and(UserSession::user eq sess.user, UserSession::id ne sess.id), setValue(UserSession::expirationMillis, newTime))
+ val newTime = Instant.now().minusMillis(100)
+ UserSession.update(and(UserSession::user eq sess.user, UserSession::id ne sess.id), setValue(UserSession::expiration, newTime))
}
}
user = user.id,
clientAddresses = listOf(originAddress),
userAgent = userAgent,
- expirationMillis = System.currentTimeMillis() + 3_600_000L
+ expiration = Instant.now().plus(1, ChronoUnit.DAYS)
).also {
UserSession.put(it)
}
user = user.id,
clientAddresses = listOf(call.request.origin.remoteHost),
userAgent = userAgent,
- expirationMillis = System.currentTimeMillis() + EXPIRATION_TIME
+ expiration = Instant.now().plus(1, ChronoUnit.DAYS)
)
launch { User.put(user) }
import starshipfights.data.Id
import starshipfights.data.auth.User
import starshipfights.data.auth.UserSession
-
-const val EXPIRATION_TIME = 86_400_000
+import java.time.Instant
+import java.time.temporal.ChronoUnit
suspend fun Id<UserSession>.resolve(userAgent: String) = UserSession.get(this)?.takeIf { session ->
- session.userAgent == userAgent && session.expirationMillis > System.currentTimeMillis()
+ session.userAgent == userAgent && session.expiration.isAfter(Instant.now())
}
suspend fun UserSession.renewed(clientAddress: String) = copy(
- expirationMillis = System.currentTimeMillis() + EXPIRATION_TIME,
+ expiration = Instant.now().plus(1, ChronoUnit.DAYS),
clientAddresses = if (clientAddresses.last() != clientAddress) clientAddresses + clientAddress else clientAddresses
).also { UserSession.put(it) }
@SerialName("_id")
override val id: Id<BattleRecord> = Id(),
+ val whenStarted: @Contextual Instant,
val whenEnded: @Contextual Instant,
val hostUser: Id<User>,
package starshipfights.data.auth
import io.ktor.auth.*
+import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import starshipfights.data.DataDocument
import starshipfights.data.DocumentTable
import starshipfights.data.Id
import starshipfights.data.invoke
+import java.time.Instant
@Serializable
data class User(
val user: Id<User>,
val clientAddresses: List<String>,
val userAgent: String,
- val expirationMillis: Long
+ val expiration: @Contextual Instant
) : DataDocument<UserSession>, Principal {
companion object Table : DocumentTable<UserSession> by DocumentTable.create({
index(UserSession::user)
val session = GameSession(gameState)
DocumentTable.launch {
+ session.gameStart.join()
+ val startedAt = Instant.now()
+
val end = session.gameEnd.await()
+ val endedAt = Instant.now()
- val now = Instant.now()
- val destroyedShipStatus = DrydockStatus.InRepair(now.plus(12, ChronoUnit.HOURS))
- val damagedShipStatus = DrydockStatus.InRepair(now.plus(9, ChronoUnit.HOURS))
- val intactShipStatus = DrydockStatus.InRepair(now.plus(6, ChronoUnit.HOURS))
- val escapedShipStatus = DrydockStatus.InRepair(now.plus(3, ChronoUnit.HOURS))
+ val destroyedShipStatus = DrydockStatus.InRepair(endedAt.plus(12, ChronoUnit.HOURS))
+ val damagedShipStatus = DrydockStatus.InRepair(endedAt.plus(9, ChronoUnit.HOURS))
+ val intactShipStatus = DrydockStatus.InRepair(endedAt.plus(6, ChronoUnit.HOURS))
+ val escapedShipStatus = DrydockStatus.InRepair(endedAt.plus(3, ChronoUnit.HOURS))
val shipWrecks = session.state.value.destroyedShips
val destroyedShips = shipWrecks.filterValues { !it.isEscape }.keys.map { it.reinterpret<ShipInDrydock>() }.toSet()
}
val battleRecord = BattleRecord(
- whenEnded = now,
+ whenStarted = startedAt,
+ whenEnded = endedAt,
hostUser = hostInfo.user.id.reinterpret(),
guestUser = guestInfo.user.id.reinterpret(),
true
} ?: false
}
- }.also { if (!it) onPacket(player.other, PlayerAction.TimeOut) }
+ }.also {
+ if (it)
+ _gameStart.complete()
+ else
+ onPacket(player.other, PlayerAction.TimeOut)
+ }
+
+ private val _gameStart = Job()
+ val gameStart: Job
+ get() = _gameStart
private val stateMutable = MutableStateFlow(gameState)
private val stateMutex = Mutex()
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ClosedSendChannelException
import kotlinx.coroutines.launch
-import starshipfights.auth.getUser
import starshipfights.data.admiralty.getInGameAdmiral
import starshipfights.data.auth.User
th { +"Client IPs" }
th { +Entities.nbsp }
}
- val now = System.currentTimeMillis()
+ val now = Instant.now()
val expiredSessions = mutableListOf<UserSession>()
allUserSessions.forEach { session ->
- if (session.expirationMillis < now) {
+ if (session.expiration.isBefore(now)) {
expiredSessions += session
return@forEach
}
+"Expired at "
span(classes = "moment") {
style = "display:none"
- +session.expirationMillis.toString()
+ +session.expiration.toEpochMilli().toString()
}
}
}
)
) {
section {
- h1 { +admiral.fullName }
+ h1 { +admiral.name }
p {
+admiral.fullName
+" is a flag officer of the "
}
}
td {
- val now = Instant.now()
- +when (ship.status) {
- DrydockStatus.Ready -> "Ready"
+ when (ship.status) {
+ DrydockStatus.Ready -> +"Ready"
is DrydockStatus.InRepair -> {
- val distance = (ship.status.until.epochSecond - now.epochSecond) / 3600 + 1
- "Repairing (ready in ${distance}h)"
+ +"Repairing"
+ br
+ +"Will be ready at "
+ span(classes = "moment") {
+ style = "display:none"
+ +ship.status.until.toEpochMilli().toString()
+ }
}
}
}
records.sortedBy { it.whenEnded }.forEach { record ->
tr {
td {
+ +"Started at "
+ span(classes = "moment") {
+ style = "display:none"
+ +record.whenStarted.toEpochMilli().toString()
+ }
+ br
+ +"Ended at "
span(classes = "moment") {
style = "display:none"
+record.whenEnded.toEpochMilli().toString()