6 Commits

Author SHA1 Message Date
d140c785e1 adds dice to queue
All checks were successful
CI / deploy (push) Successful in 5m59s
2025-04-07 21:20:00 +02:00
gitea
18bb994a0f [no ci] prepare new Version 2025-03-01 11:21:52 +00:00
gitea
0e0406fa39 [no ci] release 1.2.6 2025-03-01 11:21:43 +00:00
37ce450f2c Merge pull request 'adds the ability to roll results externally' (#41) from allows-external-results into main
All checks were successful
CI / deploy (push) Successful in 6m3s
Reviewed-on: #41
2025-03-01 12:15:47 +01:00
152511b203 adds the ability to roll results externally
All checks were successful
CI / deploy (push) Successful in 7m26s
CI / deploy (pull_request) Successful in 5m50s
2025-03-01 11:44:09 +01:00
gitea
9e7bb02832 [no ci] prepare new Version 2025-02-22 13:56:35 +00:00
7 changed files with 88 additions and 42 deletions

View File

@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>de.arindy</groupId> <groupId>de.arindy</groupId>
<artifactId>dice-tower</artifactId> <artifactId>dice-tower</artifactId>
<version>1.2.5</version> <version>1.2.7-SNAPSHOT</version>
<properties> <properties>
<compiler-plugin.version>3.13.0</compiler-plugin.version> <compiler-plugin.version>3.13.0</compiler-plugin.version>

View File

@@ -35,27 +35,31 @@ final class DiceResource(@Context val sse: Sse) {
fun parseCommand(@PathParam("id") id: String, data: RollPayload) { fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
data.room = id.split(":")[0] data.room = id.split(":")[0]
data.user = id.split(":")[1] data.user = id.split(":")[1]
val results = ArrayList<Results>() val results = ArrayList<Results>()
var numberOfDice = 0 if (data.results == null) {
data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>().forEach { command -> var numberOfDice = 0
val dice = command.split("d") data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>().forEach { command ->
var amount = dice[0].toInt() val dice = command.split("d")
val limit = diceLimit.orElse(LIMIT) var amount = dice[0].toInt()
if (limit < numberOfDice + amount) { val limit = diceLimit.orElse(LIMIT)
amount = limit - numberOfDice if (limit < numberOfDice + amount) {
} amount = limit - numberOfDice
numberOfDice += amount }
if (amount > 0) { numberOfDice += amount
val result = IntArray(amount) if (amount > 0) {
val sides = dice[1].split("+", "-") val result = IntArray(amount)
val modifier = if (dice[1].contains("+")) sides[1].toInt() else if (dice[1].contains("+")) -1 * sides[1].toInt() else 0 val sides = dice[1].split("+", "-")
repeat(amount) { index -> val modifier = if (dice[1].contains("+")) sides[1].toInt() else if (dice[1].contains("+")) -1 * sides[1].toInt() else 0
result[index] = (Math.random() * sides[0].toInt() + 1).toInt() 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()))
} }
results.add(Results(sides[0].toInt(), modifier, result.sum() + modifier, result.map { Roll(it) }.toTypedArray()))
} }
} else {
results.addAll(data.results)
} }
val map = results.map { r -> val map = results.map { r ->
"${r.rolls.size}d${r.sides}@${ "${r.rolls.size}d${r.sides}@${
if (r.sides == 100) r.rolls.map { roll -> Roll(roll.value / 10 * 10) } if (r.sides == 100) r.rolls.map { roll -> Roll(roll.value / 10 * 10) }
@@ -64,13 +68,13 @@ final class DiceResource(@Context val sse: Sse) {
}.toTypedArray() }.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() data.roll = map + results.filter { it.sides == 100 }.map { r -> "${r.rolls.size}d10@${r.rolls.map { roll -> Roll(roll.value % 10) }.joinToString(",")}"}.toTypedArray()
if (data.roll.all { it.trim().isNotEmpty() }) {
results(data.room!!, Result(data.name, data.user!!, data.faceColor, null))
}
sseBroadcasters[id]?.broadcast( sseBroadcasters[id]?.broadcast(
eventBuilder.id((UUID.randomUUID()).toString()) eventBuilder.id((UUID.randomUUID()).toString())
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build() .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) Thread.sleep(1000)
results(data.room!!, Result(data.name, data.user!!, data.faceColor, results.toTypedArray())) results(data.room!!, Result(data.name, data.user!!, data.faceColor, results.toTypedArray()))
} }

View File

@@ -20,7 +20,7 @@ class OverlayResource {
@GET @GET
@Path("/results") @Path("/results")
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
fun results(@PathParam("diceid") room: String, @QueryParam("name") name: String?, @QueryParam("user") user: String?): TemplateInstance { fun results(@PathParam("diceid") room: String, @QueryParam("name") name: String?): TemplateInstance {
return Templates.results(room, name ?: "all", user ?: "all") return Templates.results(room, name ?: "all")
} }
} }

View File

@@ -6,7 +6,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection
data class RollPayload( data class RollPayload(
val name: String, val name: String,
var command: String, var command: String,
var predetermined: Boolean? = false, val results: Array<DiceResource.Results>?,
val faceColor: String = "white", val faceColor: String = "white",
val numberColor: String = "white", val numberColor: String = "white",
val theme: String = "default", val theme: String = "default",

View File

@@ -9,7 +9,7 @@ object Templates {
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
@JvmStatic @JvmStatic
external fun results(room: String, name: String?, user: String?): TemplateInstance external fun results(room: String, name: String?): TemplateInstance
@JvmStatic @JvmStatic
external fun index(version: String): TemplateInstance external fun index(version: String): TemplateInstance

View File

@@ -9,13 +9,14 @@
<body> <body>
<div id="dice-box"></div> <div id="dice-box"></div>
<script type="module"> <script type="module">
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
function url() { function url() {
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : ''); return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
} }
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
const evtSource = new EventSource(url() + "/dice/{diceid??}/stream"); const evtSource = new EventSource(url() + "/dice/{diceid??}/stream");
const diceBox = new DiceBox("#dice-box", { const diceBox = new DiceBox("#dice-box", {
assetPath: "/vendor/dice-box/", assetPath: "/vendor/dice-box/",
light_intensity: 2, light_intensity: 2,
@@ -23,26 +24,67 @@
baseScale: {scale} * 10, baseScale: {scale} * 10,
strength: Math.floor(Math.random() * 4), strength: Math.floor(Math.random() * 4),
}); });
diceBox.initialize(); diceBox.initialize();
let timeout = 0;
evtSource.addEventListener("message", function (event) { let rollQueue = [];
clearInterval(timeout); let rolling = false;
let data = JSON.parse(event.data); let clearTimeoutId = null;
diceBox.onRollComplete = () => { let lastRollTime = Date.now();
if ({clearAfter} > 0) {
timeout = setTimeout(() => diceBox.clearDice(), {clearAfter} * 1000) function scheduleClear() {
if (clearTimeoutId) clearTimeout(clearTimeoutId);
clearTimeoutId = setTimeout(() => {
const now = Date.now();
if (now - lastRollTime >= ({clearAfter} * 1000)) {
diceBox.clearDice();
rolling = false;
processQueue();
} else {
scheduleClear(); // try again later
} }
}, {clearAfter} * 1000);
}
async function processQueue() {
if (rolling || rollQueue.length === 0) return;
rolling = true;
const data = rollQueue.shift();
const rollString = data.roll
.filter(it => it.split('@')[0].split('d')[1] !== "2")
.join('&');
if (!rollString) {
rolling = false;
return;
} }
diceBox.updateConfig({
lastRollTime = Date.now();
await diceBox.updateConfig({
theme_customColorset: { theme_customColorset: {
texture: data.theme, texture: data.theme,
background: data.faceColor, background: data.faceColor,
foreground: data.numberColor foreground: data.numberColor
} }
}).then(() => { });
diceBox.roll(data.roll.filter(it => it.split('@')[0].split('d')[1] !== "2").join('&'));
}) await diceBox.add(rollString);
}) scheduleClear();
rolling = false;
void processQueue();
}
evtSource.addEventListener("message", function (event) {
const data = JSON.parse(event.data);
rollQueue.push(data);
processQueue();
});
</script> </script>
</body> </body>
</html> </html>

View File

@@ -17,9 +17,9 @@
const evtSource = new EventSource(url() + '/dice/{room}/results'); const evtSource = new EventSource(url() + '/dice/{room}/results');
evtSource.addEventListener('message', function (event) { evtSource.addEventListener('message', function (event) {
let data = JSON.parse(event.data); let data = JSON.parse(event.data);
if ("{name}" === "all" && "{user}" === "all" || "{name}" === data.name && "{user}" === data.user || "{name}" === "all" && "{user}" === data.user || "{name}" === data.name && "{user}" === "all") { if ("{name}" === "all" || "{name}" === data.name) {
let name = document.getElementById(data.user + '-' + data.name) ?? document.createElement('div'); let name = document.getElementById(data.name) ?? document.createElement('div');
name.id = data.user + '-' + data.name; name.id = data.name;
name.replaceChildren(...[]); name.replaceChildren(...[]);
let node = document.createElement('p'); let node = document.createElement('p');
let resultText = '' let resultText = ''