Compare commits
2 Commits
5a53fd5f68
...
a9ea232e13
| Author | SHA1 | Date | |
|---|---|---|---|
| a9ea232e13 | |||
| be03f5fb96 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -183,3 +183,4 @@ gradle-app.setting
|
|||||||
# End of https://www.toptal.com/developers/gitignore/api/intellij,java,gradle,kotlin
|
# End of https://www.toptal.com/developers/gitignore/api/intellij,java,gradle,kotlin
|
||||||
|
|
||||||
data.json
|
data.json
|
||||||
|
token
|
||||||
22
src/main/java/Discord.kt
Normal file
22
src/main/java/Discord.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import dev.kord.core.Kord
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
object Discord {
|
||||||
|
|
||||||
|
private lateinit var kord: Kord
|
||||||
|
suspend fun init(token: String) {
|
||||||
|
|
||||||
|
kord = Kord(token)
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
kord.login()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun sendMessage(message: String) {
|
||||||
|
kord.rest.channel.createMessage(Settings.instance.discordChannelId) {
|
||||||
|
this.content = "<@&${Settings.instance.discordGroupId}>$message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,18 @@
|
|||||||
import dev.kord.common.entity.Snowflake
|
|
||||||
import dev.kord.core.Kord
|
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.cio.*
|
import io.ktor.server.cio.*
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
|
||||||
suspend fun main() {
|
suspend fun main() {
|
||||||
|
Settings.load()
|
||||||
val path = Path("data.json")
|
val path = Path("data.json")
|
||||||
|
|
||||||
if (Files.isRegularFile(path)) {
|
if (Files.isRegularFile(path)) {
|
||||||
@ -19,25 +22,32 @@ suspend fun main() {
|
|||||||
RaceHolder.save(path)
|
RaceHolder.save(path)
|
||||||
})
|
})
|
||||||
|
|
||||||
val kord = Kord("MTE3MTIwODc1MDE5MTg5MDQ4Mw.GOUedL.i3zD6IG5B6fFRvaSOotWwJ5KBRK2whC9xr0vL8")
|
Discord.init(withContext(Dispatchers.IO) {
|
||||||
|
Files.readString(Path("token"))
|
||||||
|
})
|
||||||
|
|
||||||
|
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
|
||||||
|
runBlocking {
|
||||||
|
RaceHolder.races.forEach {
|
||||||
|
it.rescanAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 5, TimeUnit.MINUTES)
|
||||||
|
|
||||||
val server = embeddedServer(CIO, port = 8080){
|
val server = embeddedServer(CIO, port = 8080){
|
||||||
routing {
|
routing {
|
||||||
get("/"){
|
get("/"){
|
||||||
call.respondRedirect("/search")
|
call.respondRedirect("/search")
|
||||||
}
|
}
|
||||||
|
settingsPage()
|
||||||
searchPage()
|
searchPage()
|
||||||
trackPage()
|
trackPage()
|
||||||
racePage()
|
racePage()
|
||||||
get("/hi") {
|
get("/hi") {
|
||||||
kord.rest.channel.createMessage(Snowflake("1040400994355392522")) {
|
Discord.sendMessage("Herro!")
|
||||||
this.content = "<@&978289601506586624> Herro!"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println("connection to localhost:8080 now possible.")
|
println("connection to localhost:8080 now possible.")
|
||||||
server.start(false)
|
server.start(true)
|
||||||
kord.login()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.toInstant
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -5,49 +9,57 @@ import java.nio.file.Files
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardOpenOption
|
import java.nio.file.StandardOpenOption
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Track(
|
||||||
|
val trackId: Long,
|
||||||
|
val endTime: LocalDateTime? = null,
|
||||||
|
var leaderboard: Velocidrone.Leaderboard = Velocidrone.Leaderboard(
|
||||||
|
true, "", "",
|
||||||
|
arrayOf()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class RaceData(var name: String, var description: String) {
|
data class RaceData(var name: String, var description: String) {
|
||||||
private val trackList = arrayListOf<Long>()
|
private val trackList = arrayListOf<Track>()
|
||||||
private val leaderboardMap = hashMapOf<Long, Velocidrone.Leaderboard>()
|
|
||||||
private var totalScores = arrayListOf<Pair<String, Long>>()
|
private var totalScores = arrayListOf<Pair<String, Long>>()
|
||||||
|
|
||||||
fun addTrack(trackId: Long) {
|
fun addTrack(track: Track) {
|
||||||
trackList.add(trackId)
|
trackList.add(track)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeTrack(trackId: Long) {
|
fun removeTrack(track: Track) {
|
||||||
trackList.remove(trackId)
|
trackList.remove(track)
|
||||||
leaderboardMap.remove(trackId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun rescanLeaderboard(trackId: Long, newLeaderboardCallback: (Velocidrone.Leaderboard) -> Unit) {
|
private suspend fun rescanLeaderboard(track: Track, newLeaderboardCallback: (Velocidrone.Leaderboard) -> Unit) {
|
||||||
if (trackList.contains(trackId)) {
|
Velocidrone.getLeaderboardForId(track.trackId).getOrNull()?.let { newLeaderboard ->
|
||||||
Velocidrone.getLeaderboardForId(trackId).getOrNull()?.let { newLeaderboard ->
|
if (track.leaderboard != newLeaderboard) {
|
||||||
if (leaderboardMap[trackId]?.equals(newLeaderboard) == false) {
|
track.leaderboard = newLeaderboard
|
||||||
leaderboardMap[trackId] = newLeaderboard
|
|
||||||
newLeaderboardCallback(newLeaderboard)
|
newLeaderboardCallback(newLeaderboard)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun rescanAll() {
|
suspend fun rescanAll() {
|
||||||
var changes = false
|
var changes = false
|
||||||
trackList.forEach {
|
trackList.forEach {
|
||||||
|
if ((it.endTime?.toInstant(TimeZone.UTC)?.compareTo(Clock.System.now()) ?: -1) < 0) {
|
||||||
rescanLeaderboard(it) {
|
rescanLeaderboard(it) {
|
||||||
changes = true
|
changes = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (changes) {
|
if (changes) {
|
||||||
calculateTotalScores()
|
calculateTotalScores()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateTotalScores() {
|
private fun calculateTotalScores() {
|
||||||
val scoreMap = hashMapOf<String, Long>()
|
val scoreMap = hashMapOf<String, Long>()
|
||||||
leaderboardMap.forEach { leaderboardEntry ->
|
trackList.forEach { track ->
|
||||||
leaderboardEntry.value.tracktimes.sortedBy { it.lap_time }.forEach {
|
track.leaderboard.trackTimes.sortedBy { it.lapTime }.forEachIndexed { index, it ->
|
||||||
scoreMap[it.playername] = scoreMap.getOrDefault(it.playername, 0L) + 1
|
scoreMap[it.playerName] = scoreMap.getOrDefault(it.playerName, 0L) + 1 + index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized(totalScores) {
|
synchronized(totalScores) {
|
||||||
@ -55,6 +67,10 @@ data class RaceData(var name: String, var description: String) {
|
|||||||
scoreMap.asIterable().sortedByDescending { it.value }.map { it.toPair() }.toCollection(totalScores)
|
scoreMap.asIterable().sortedByDescending { it.value }.map { it.toPair() }.toCollection(totalScores)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getTracks(): ArrayList<Track> {
|
||||||
|
return trackList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object RaceHolder {
|
object RaceHolder {
|
||||||
|
|||||||
@ -9,13 +9,33 @@ fun Routing.racePage() {
|
|||||||
get("/race/list") {
|
get("/race/list") {
|
||||||
raceOverviewPage(RaceHolder.races)
|
raceOverviewPage(RaceHolder.races)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get("/races/{id}/show") {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/races/{raceId}/remove/{trackId}") {
|
||||||
|
val raceId = call.parameters["raceId"]
|
||||||
|
val trackId = call.parameters["trackId"]?.toLong()
|
||||||
|
|
||||||
|
RaceHolder.races.find { it.name == raceId }?.let { raceData ->
|
||||||
|
val toBeRemovedTrack = raceData.getTracks().find { it.trackId == trackId }
|
||||||
|
toBeRemovedTrack?.let {
|
||||||
|
raceData.removeTrack(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call.respondRedirect("/races/$raceId/show")
|
||||||
|
}
|
||||||
|
|
||||||
get("/race/new") {
|
get("/race/new") {
|
||||||
newRacePage()
|
newRacePage()
|
||||||
}
|
}
|
||||||
|
|
||||||
post("/race/new") {
|
post("/race/new") {
|
||||||
val parameters = call.receiveParameters()
|
val parameters = call.receiveParameters()
|
||||||
val name = parameters["name"]
|
val name = parameters["name"]
|
||||||
val description = parameters["description"]
|
val description = parameters["description"]
|
||||||
|
|
||||||
if (name == null || description == null) {
|
if (name == null || description == null) {
|
||||||
newRacePage("Not all parameters given.", name ?: "", description ?: "")
|
newRacePage("Not all parameters given.", name ?: "", description ?: "")
|
||||||
return@post
|
return@post
|
||||||
@ -24,6 +44,7 @@ fun Routing.racePage() {
|
|||||||
newRacePage("Race with the same name already exists.", name, description)
|
newRacePage("Race with the same name already exists.", name, description)
|
||||||
return@post
|
return@post
|
||||||
}
|
}
|
||||||
|
|
||||||
RaceHolder.races.add(RaceData(name, description))
|
RaceHolder.races.add(RaceData(name, description))
|
||||||
call.respondRedirect("/race/list")
|
call.respondRedirect("/race/list")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,7 @@ private suspend fun PipelineContext<Unit,ApplicationCall>.searchResultPage(track
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackResults.user_tracks.forEach {
|
trackResults.userTracks.forEach {
|
||||||
tr {
|
tr {
|
||||||
td {
|
td {
|
||||||
form("/track/" + it.id + "/add") {
|
form("/track/" + it.id + "/add") {
|
||||||
@ -81,10 +81,10 @@ private suspend fun PipelineContext<Unit,ApplicationCall>.searchResultPage(track
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
+it.track_name
|
+it.trackName
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
+it.playername
|
+it.playerName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/main/java/Settings.kt
Normal file
32
src/main/java/Settings.kt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import dev.kord.common.entity.Snowflake
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
const val SETTINGS = "settings.json"
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Settings {
|
||||||
|
companion object {
|
||||||
|
var instance = Settings()
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
if (Files.isRegularFile(Path(SETTINGS))) {
|
||||||
|
val reader = Files.newBufferedReader(Path(SETTINGS))
|
||||||
|
instance = Json.decodeFromString<Settings>(reader.readText())
|
||||||
|
reader.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
val writer = Files.newBufferedWriter(Path(SETTINGS))
|
||||||
|
writer.write(Json.encodeToString(instance))
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var discordChannelId = Snowflake(0)
|
||||||
|
var discordGroupId = Snowflake(0)
|
||||||
|
}
|
||||||
41
src/main/java/SettingsPage.kt
Normal file
41
src/main/java/SettingsPage.kt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import dev.kord.common.entity.Snowflake
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.html.*
|
||||||
|
|
||||||
|
fun Routing.settingsPage() {
|
||||||
|
get("/settings") {
|
||||||
|
respondThemedHtml("Settings") {
|
||||||
|
form("/settings", encType = FormEncType.applicationXWwwFormUrlEncoded, method = FormMethod.post) {
|
||||||
|
p {
|
||||||
|
+"GroupID:"
|
||||||
|
textInput(name = "groupId") {
|
||||||
|
value = Settings.instance.discordGroupId.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
+"ChannelID:"
|
||||||
|
textInput(name = "channelId") {
|
||||||
|
value = Settings.instance.discordChannelId.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitInput { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post("/settings") {
|
||||||
|
val params = call.receiveParameters()
|
||||||
|
params["groupId"]?.let { Settings.instance.discordGroupId = Snowflake(it) }
|
||||||
|
params["channelId"]?.let { Settings.instance.discordChannelId = Snowflake(it) }
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
Settings.save()
|
||||||
|
}
|
||||||
|
call.respondRedirect("/settings")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import io.ktor.server.html.*
|
|||||||
import io.ktor.server.request.*
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import io.ktor.util.pipeline.*
|
import io.ktor.util.pipeline.*
|
||||||
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
@ -22,11 +23,12 @@ fun Routing.trackPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
post("/track/{id}/add") {
|
post("/track/{id}/add") {
|
||||||
|
val endTime = call.receiveParameters()["endTime"]?.toLocalDateTime()
|
||||||
call.parameters["id"]?.toLong()?.let { trackId ->
|
call.parameters["id"]?.toLong()?.let { trackId ->
|
||||||
val param = call.receiveParameters()
|
val param = call.receiveParameters()
|
||||||
param["race"]?.let { raceString ->
|
param["race"]?.let { raceString ->
|
||||||
val decoded = URLDecoder.decode(raceString, Charsets.UTF_8)
|
val decoded = URLDecoder.decode(raceString, Charsets.UTF_8)
|
||||||
RaceHolder.races.find { it.name == decoded }?.addTrack(trackId)
|
RaceHolder.races.find { it.name == decoded }?.addTrack(Track(trackId, endTime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,10 +49,10 @@ private suspend fun PipelineContext<Unit, ApplicationCall>.trackPage(leaderboard
|
|||||||
td { +"Name" }
|
td { +"Name" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
leaderboard.tracktimes.sortedBy { it.lap_time }.forEach {
|
leaderboard.trackTimes.sortedBy { it.lapTime }.forEach {
|
||||||
tr {
|
tr {
|
||||||
td { +it.lap_time.toString() }
|
td { +it.lapTime.toString() }
|
||||||
td { +it.playername }
|
td { +it.playerName }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,6 +72,10 @@ private suspend fun PipelineContext<Unit, ApplicationCall>.addTrackPage(trackId:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p {
|
||||||
|
+"Optional end time:"
|
||||||
|
dateTimeLocalInput(name = "endTime")
|
||||||
|
}
|
||||||
submitInput()
|
submitInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import io.ktor.client.*
|
|||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.*
|
||||||
import io.ktor.client.engine.cio.*
|
import io.ktor.client.engine.cio.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
@ -13,38 +14,67 @@ import javax.crypto.spec.SecretKeySpec
|
|||||||
object Velocidrone{
|
object Velocidrone{
|
||||||
private val key = "BatCaveGGevaCtaB".toByteArray(Charset.defaultCharset())
|
private val key = "BatCaveGGevaCtaB".toByteArray(Charset.defaultCharset())
|
||||||
|
|
||||||
fun decodeVeloBase64(input: ByteArray): Result<String> {
|
private fun decodeVeloBase64(input: ByteArray): Result<String> {
|
||||||
val decoded = Base64.getDecoder().decode(input)
|
val decoded = Base64.getDecoder().decode(input)
|
||||||
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
|
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
|
||||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"))
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"))
|
||||||
return Result.success(String(cipher.doFinal(decoded)))
|
return Result.success(String(cipher.doFinal(decoded)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeVeloBase64(input: String): ByteArray {
|
private fun encodeVeloBase64(input: String): ByteArray {
|
||||||
val encoded: ByteArray = input.toByteArray()
|
val encoded: ByteArray = input.toByteArray()
|
||||||
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
|
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"))
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"))
|
||||||
return Base64.getEncoder().encode(cipher.doFinal(encoded))
|
return Base64.getEncoder().encode(cipher.doFinal(encoded))
|
||||||
}
|
}
|
||||||
|
|
||||||
val client = HttpClient(CIO)
|
private val client = HttpClient(CIO)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Leaderboard(
|
data class Leaderboard(
|
||||||
val success:Boolean,
|
val success:Boolean,
|
||||||
val message_title:String,
|
@SerialName("message_title")
|
||||||
|
val messageTitle: String,
|
||||||
val message:String,
|
val message:String,
|
||||||
val tracktimes:Array<TrackTime>
|
@SerialName("tracktimes")
|
||||||
)
|
val trackTimes: Array<TrackTime>
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Leaderboard
|
||||||
|
|
||||||
|
if (success != other.success) return false
|
||||||
|
if (messageTitle != other.messageTitle) return false
|
||||||
|
if (message != other.message) return false
|
||||||
|
if (!trackTimes.contentEquals(other.trackTimes)) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = success.hashCode()
|
||||||
|
result = 31 * result + messageTitle.hashCode()
|
||||||
|
result = 31 * result + message.hashCode()
|
||||||
|
result = 31 * result + trackTimes.contentHashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TrackTime(
|
data class TrackTime(
|
||||||
val lap_time:Float,
|
@SerialName("lap_time")
|
||||||
val playername: String,
|
val lapTime: Float,
|
||||||
val model_id: Long,
|
@SerialName("playername")
|
||||||
|
val playerName: String,
|
||||||
|
@SerialName("model_id")
|
||||||
|
val modelId: Long,
|
||||||
val country: String,
|
val country: String,
|
||||||
val sim_version: String,
|
@SerialName("sim_version")
|
||||||
val device_type: Long
|
val simVersion: String,
|
||||||
|
@SerialName("device_type")
|
||||||
|
val deviceType: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun getLeaderboardForId(id:Long): Result<Leaderboard> {
|
suspend fun getLeaderboardForId(id:Long): Result<Leaderboard> {
|
||||||
@ -66,17 +96,44 @@ object Velocidrone{
|
|||||||
data class TrackResults(
|
data class TrackResults(
|
||||||
val success:Boolean,
|
val success:Boolean,
|
||||||
val detail:String,
|
val detail:String,
|
||||||
val user_tracks:Array<Track>
|
@SerialName("user_tracks")
|
||||||
)
|
val userTracks: Array<Track>
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as TrackResults
|
||||||
|
|
||||||
|
if (success != other.success) return false
|
||||||
|
if (detail != other.detail) return false
|
||||||
|
if (!userTracks.contentEquals(other.userTracks)) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = success.hashCode()
|
||||||
|
result = 31 * result + detail.hashCode()
|
||||||
|
result = 31 * result + userTracks.contentHashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Track(
|
data class Track(
|
||||||
val id:Long,
|
val id:Long,
|
||||||
val scenery_id:Long,
|
@SerialName("scenery_id")
|
||||||
val track_name:String,
|
val sceneryId: Long,
|
||||||
val track_type:String,
|
@SerialName("track_name")
|
||||||
val playername:String,
|
val trackName: String,
|
||||||
|
@SerialName("track_type")
|
||||||
|
val trackType: String,
|
||||||
|
@SerialName("playername")
|
||||||
|
val playerName: String,
|
||||||
val rating:Float,
|
val rating:Float,
|
||||||
val total_ratings:Long,
|
@SerialName("total_ratings")
|
||||||
|
val totalRatings: Long,
|
||||||
val date:String
|
val date:String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user