mirror of https://github.com/hykilpikonna/AquaDX
[+] Recruit lobby
parent
8f7f422b28
commit
146e4bac0f
|
@ -15,12 +15,14 @@ class PathProps {
|
||||||
var mai2Plays: String = "data/upload/mai2/plays"
|
var mai2Plays: String = "data/upload/mai2/plays"
|
||||||
var mai2Portrait: String = "data/upload/mai2/portrait"
|
var mai2Portrait: String = "data/upload/mai2/portrait"
|
||||||
var aquaNetPortrait: String = "data/upload/net/portrait"
|
var aquaNetPortrait: String = "data/upload/net/portrait"
|
||||||
|
var recruitLog: String = "data/futari/recruit.log"
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
fun init() {
|
fun init() {
|
||||||
mai2Plays = mai2Plays.path().apply { toFile().mkdirs() }.toString()
|
mai2Plays = mai2Plays.path().apply { toFile().mkdirs() }.toString()
|
||||||
mai2Portrait = mai2Portrait.path().apply { toFile().mkdirs() }.toString()
|
mai2Portrait = mai2Portrait.path().apply { toFile().mkdirs() }.toString()
|
||||||
aquaNetPortrait = aquaNetPortrait.path().apply { toFile().mkdirs() }.toString()
|
aquaNetPortrait = aquaNetPortrait.path().apply { toFile().mkdirs() }.toString()
|
||||||
|
recruitLog = recruitLog.path().apply { toFile().parentFile.mkdirs() }.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
|
||||||
|
package icu.samnyan.aqua.sega.maimai2.worldslink
|
||||||
|
|
||||||
|
import ext.*
|
||||||
|
import icu.samnyan.aqua.net.utils.PathProps
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import java.io.BufferedWriter
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
|
||||||
|
// KotlinX Serialization
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
private val KJson = Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
isLenient = true
|
||||||
|
explicitNulls = false
|
||||||
|
coerceInputValues = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum time to live for a recruit record
|
||||||
|
const val MAX_TTL = 3 * 60 * 1000
|
||||||
|
|
||||||
|
data class RecruitRecord(val d: RecruitInfo, val time: Long = millis())
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(path = ["/mai2-futari"])
|
||||||
|
class FutariLobby(paths: PathProps) {
|
||||||
|
// <IP Address, RecruitInfo>
|
||||||
|
val recruits = mutableMapOf<UInt, RecruitRecord>()
|
||||||
|
// Append writer
|
||||||
|
lateinit var writer: BufferedWriter
|
||||||
|
val mutex = ReentrantLock()
|
||||||
|
val log = logger()
|
||||||
|
|
||||||
|
init {
|
||||||
|
paths.init()
|
||||||
|
writer = FileOutputStream(File(paths.recruitLog), true).bufferedWriter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun log(data: String) = mutex.withLock {
|
||||||
|
log.info(data)
|
||||||
|
writer.write(data)
|
||||||
|
writer.newLine()
|
||||||
|
writer.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun log(data: StartRecruit, msg: String) =
|
||||||
|
log("${LocalDateTime.now().isoDateTime()}: $msg: ${data.RecruitInfo.toJson()}")
|
||||||
|
|
||||||
|
val StartRecruit.ip get() = RecruitInfo.MechaInfo.IpAddress
|
||||||
|
|
||||||
|
@API("recruit/start")
|
||||||
|
fun startRecruit(@RB data: String) {
|
||||||
|
val d = parsing { KJson.decodeFromString<StartRecruit>(data) }
|
||||||
|
val exists = d.ip in recruits
|
||||||
|
recruits[d.ip] = RecruitRecord(d.RecruitInfo)
|
||||||
|
|
||||||
|
if (!exists) log(d, "StartRecruit")
|
||||||
|
}
|
||||||
|
|
||||||
|
@API("recruit/finish")
|
||||||
|
fun finishRecruit(@RB data: String) {
|
||||||
|
val d = parsing { KJson.decodeFromString<StartRecruit>(data) }
|
||||||
|
if (d.ip !in recruits) 400 - "Recruit not found"
|
||||||
|
recruits.remove(d.ip)
|
||||||
|
log(d, "EndRecruit")
|
||||||
|
}
|
||||||
|
|
||||||
|
@API("recruit/list")
|
||||||
|
fun listRecruit(): String {
|
||||||
|
val time = millis()
|
||||||
|
recruits.filterValues { time - it.time > MAX_TTL }.keys.forEach { recruits.remove(it) }
|
||||||
|
return recruits.values.toList().joinToString("\n") { KJson.encodeToString(it.d) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val json = """{"RecruitInfo":{"MechaInfo":{"IsJoin":true,"IpAddress":1820162433,"MusicID":11692,"Entrys":[true,false],"UserIDs":[281474976710657,281474976710657],"UserNames":["GUEST","GUEST"],"IconIDs":[1,1],"FumenDifs":[0,-1],"Rateing":[0,0],"ClassValue":[0,0],"MaxClassValue":[0,0],"UserType":[0,0]},"MusicID":11692,"GroupID":0,"EventModeID":false,"JoinNumber":1,"PartyStance":0,"_startTimeTicks":638725464510308001,"_recvTimeTicks":0}}"""
|
||||||
|
println(json.jsonMap().toJson())
|
||||||
|
val data = KJson.decodeFromString<StartRecruit>(json)
|
||||||
|
println(json)
|
||||||
|
println(KJson.encodeToString(StartRecruit.serializer(), data))
|
||||||
|
println(data)
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
|
package icu.samnyan.aqua.sega.maimai2.worldslink
|
||||||
|
|
||||||
|
import ext.Bool
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MechaInfo(
|
||||||
|
val IsJoin: Bool,
|
||||||
|
val IpAddress: UInt,
|
||||||
|
val MusicID: Int,
|
||||||
|
val Entrys: List<Bool>,
|
||||||
|
val UserIDs: List<Long>,
|
||||||
|
val UserNames: List<String>,
|
||||||
|
val IconIDs: List<Int>,
|
||||||
|
val FumenDifs: List<Int>,
|
||||||
|
val Rateing: List<Int>,
|
||||||
|
val ClassValue: List<Int>,
|
||||||
|
val MaxClassValue: List<Int>,
|
||||||
|
val UserType: List<Int>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RecruitInfo(
|
||||||
|
val MechaInfo: MechaInfo,
|
||||||
|
val MusicID: Int,
|
||||||
|
val GroupID: Int,
|
||||||
|
val EventModeID: Boolean,
|
||||||
|
val JoinNumber: Int,
|
||||||
|
val PartyStance: Int,
|
||||||
|
val _startTimeTicks: Long,
|
||||||
|
val _recvTimeTicks: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StartRecruit(
|
||||||
|
val RecruitInfo: RecruitInfo,
|
||||||
|
)
|
Loading…
Reference in New Issue