185 lines
6.6 KiB
Kotlin
185 lines
6.6 KiB
Kotlin
package de.arindy.dicetower
|
|
|
|
import io.quarkus.runtime.annotations.RegisterForReflection
|
|
import jakarta.enterprise.context.ApplicationScoped
|
|
import jakarta.ws.rs.Consumes
|
|
import jakarta.ws.rs.GET
|
|
import jakarta.ws.rs.POST
|
|
import jakarta.ws.rs.Path
|
|
import jakarta.ws.rs.PathParam
|
|
import jakarta.ws.rs.Produces
|
|
import jakarta.ws.rs.core.Context
|
|
import jakarta.ws.rs.core.MediaType
|
|
import jakarta.ws.rs.sse.OutboundSseEvent
|
|
import jakarta.ws.rs.sse.Sse
|
|
import jakarta.ws.rs.sse.SseBroadcaster
|
|
import jakarta.ws.rs.sse.SseEventSink
|
|
import org.eclipse.microprofile.config.inject.ConfigProperty
|
|
import java.util.Optional
|
|
import java.util.UUID
|
|
|
|
private const val LIMIT = 30
|
|
|
|
@Path("/dice/{id}")
|
|
@ApplicationScoped
|
|
final class DiceResource(@Context val sse: Sse) {
|
|
|
|
@ConfigProperty(name = "dice.limit")
|
|
private lateinit var diceLimit: Optional<Int>
|
|
|
|
private var eventBuilder: OutboundSseEvent.Builder = sse.newEventBuilder()
|
|
private var sseBroadcasters: MutableMap<String, SseBroadcaster> = HashMap()
|
|
|
|
@POST
|
|
@Consumes(MediaType.APPLICATION_JSON)
|
|
fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
|
|
data.room = id.split(":")[0]
|
|
data.user = id.split(":")[1]
|
|
|
|
val results = ArrayList<Results>()
|
|
var numberOfDice = 0
|
|
data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>().forEach { command ->
|
|
val dice = command.split("d")
|
|
var amount = dice[0].toInt()
|
|
val limit = diceLimit.orElse(LIMIT)
|
|
if (limit < numberOfDice + amount) {
|
|
amount = limit - numberOfDice
|
|
}
|
|
numberOfDice += amount
|
|
if (amount > 0) {
|
|
val result = IntArray(amount)
|
|
val sides = dice[1].split("+", "-")
|
|
val modifier = if (dice[1].contains("+")) sides[1].toInt() else if (dice[1].contains("+")) -1 * sides[1].toInt() else 0
|
|
repeat(amount) { index ->
|
|
result[index] = (Math.random() * sides[0].toInt() + 1).toInt()
|
|
}
|
|
results.add(Results(sides[0].toInt(), modifier, result.sum() + modifier, result.map { Roll(it) }.toTypedArray()))
|
|
}
|
|
}
|
|
val map = results.map { r ->
|
|
"${r.rolls.size}d${r.sides}@${
|
|
if (r.sides == 100) r.rolls.map { roll -> Roll(roll.value / 10 * 10) }
|
|
.joinToString(",") else r.rolls.joinToString(",")
|
|
}"
|
|
}.toTypedArray()
|
|
|
|
data.roll = map + results.filter { it.sides == 100 }.map { r -> "${r.rolls.size}d10@${r.rolls.map { roll -> Roll(roll.value % 10) }.joinToString(",")}"}.toTypedArray()
|
|
sseBroadcasters[id]?.broadcast(
|
|
eventBuilder.id((UUID.randomUUID()).toString())
|
|
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build()
|
|
)
|
|
if (data.roll.all { it.trim().isNotEmpty() }) {
|
|
results(data.room!!, Result(data.name, data.user!!, data.faceColor, null))
|
|
}
|
|
Thread.sleep(1000)
|
|
results(data.room!!, Result(data.name, data.user!!, data.faceColor, results.toTypedArray()))
|
|
}
|
|
|
|
@POST
|
|
@Path("/register")
|
|
@Consumes(MediaType.APPLICATION_JSON)
|
|
fun register(@PathParam("id") id: String, data: Any) {
|
|
sseBroadcasters["register:$id"]?.broadcast(
|
|
eventBuilder.id((UUID.randomUUID()).toString())
|
|
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build())
|
|
|
|
}
|
|
|
|
@POST
|
|
@Path("/results")
|
|
@Consumes(MediaType.APPLICATION_JSON)
|
|
fun results(@PathParam("id") id: String, data: Any) {
|
|
sseBroadcasters[id]?.broadcast(
|
|
eventBuilder.id((UUID.randomUUID()).toString())
|
|
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build()
|
|
)
|
|
}
|
|
|
|
@GET
|
|
@Path("/stream")
|
|
@Produces(MediaType.SERVER_SENT_EVENTS)
|
|
fun stream(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
|
if (!sseBroadcasters.containsKey(id)) {
|
|
sseBroadcasters[id] = sse.newBroadcaster()
|
|
}
|
|
sseBroadcasters[id]?.register(sseEventSink)
|
|
}
|
|
|
|
@GET
|
|
@Path("/results")
|
|
@Produces(MediaType.SERVER_SENT_EVENTS)
|
|
fun results(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
|
if (!sseBroadcasters.containsKey(id)) {
|
|
sseBroadcasters[id] = sse.newBroadcaster()
|
|
}
|
|
sseBroadcasters[id]?.register(sseEventSink)
|
|
}
|
|
|
|
@GET
|
|
@Path("/users")
|
|
@Produces(MediaType.SERVER_SENT_EVENTS)
|
|
fun users(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
|
if (!sseBroadcasters.containsKey("register:$id")) {
|
|
sseBroadcasters["register:$id"] = sse.newBroadcaster()
|
|
}
|
|
sseBroadcasters["register:$id"]?.register(sseEventSink)
|
|
}
|
|
|
|
@RegisterForReflection
|
|
data class Result(val name: String, val user: String, val faceColor: String, val results: Array<Results>?) {
|
|
override fun equals(other: Any?): Boolean {
|
|
if (this === other) return true
|
|
if (javaClass != other?.javaClass) return false
|
|
|
|
other as Result
|
|
|
|
if (name != other.name) return false
|
|
if (user != other.user) return false
|
|
if (faceColor != other.faceColor) return false
|
|
if (results != null) {
|
|
if (other.results == null) return false
|
|
if (!results.contentEquals(other.results)) return false
|
|
} else if (other.results != null) return false
|
|
|
|
return true
|
|
}
|
|
|
|
override fun hashCode(): Int {
|
|
var result = name.hashCode()
|
|
result = 31 * result + user.hashCode()
|
|
result = 31 * result + faceColor.hashCode()
|
|
result = 31 * result + (results?.contentHashCode() ?: 0)
|
|
return result
|
|
}
|
|
}
|
|
|
|
@RegisterForReflection
|
|
data class Results(val sides: Int, val modifier: Int, val value: Int, val rolls: Array<Roll>) {
|
|
override fun equals(other: Any?): Boolean {
|
|
if (this === other) return true
|
|
if (javaClass != other?.javaClass) return false
|
|
|
|
other as Results
|
|
|
|
if (sides != other.sides) return false
|
|
if (modifier != other.modifier) return false
|
|
if (value != other.value) return false
|
|
if (!rolls.contentEquals(other.rolls)) return false
|
|
|
|
return true
|
|
}
|
|
|
|
override fun hashCode(): Int {
|
|
var result = sides
|
|
result = 31 * result + modifier
|
|
result = 31 * result + value
|
|
result = 31 * result + rolls.contentHashCode()
|
|
return result
|
|
}
|
|
}
|
|
|
|
@RegisterForReflection
|
|
data class Roll(val value: Int)
|
|
|
|
}
|