Added additional abstraction for better squad overview.

This commit is contained in:
yenon 2023-12-01 18:39:18 +01:00
parent 78b102dfbf
commit 72625833cd
5 changed files with 206 additions and 54 deletions

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="17" project-jdk-type="JavaSDK">

View File

@ -5,17 +5,44 @@ object Css {
width: 100%; width: 100%;
display: flex; display: flex;
} }
.teamA{ .team{
width: 50%; width: 50%;
margin: 6px;
padding: 6px;
} }
.teamB{ .colorBluFor{
width: 50%; background-color: #bbffff;
}
.colorRedFor{
background-color: #ffbbbb;
}
.squad{
background-color: #00000020;
margin: 6px;
} }
.squadTable{ .squadTable{
width: 100%; width: 100%;
} }
.squadHeader{
background-color: #00000050
}
.leader{
background-color: #CD7F32
}
.playerClass{
width: 15%;
}
.playerName{
width: 35%;
}
.playerId{
width: 35%;
}
.playerButtons{
width: 15%;
}
tr:nth-child(even) { tr:nth-child(even) {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.1);
} }
""".trimIndent() """.trimIndent()
} }

View File

@ -74,8 +74,47 @@ fun main() {
} }
get("/overview") { get("/overview") {
val players = abstraction.getCurrentPlayers() val players = abstraction.getCurrentPlayers()
val squadInfo = abstraction.getSquadList()
call.respondHtmlTemplate(OverviewTemplate(players)) {} call.respondHtmlTemplate(OverviewTemplate(players, squadInfo)) {}
}
get("/overviewDemo") {
//val players = abstraction.getCurrentPlayers()
//val squadInfo = abstraction.getSquadList()
val players = ListPlayersOutput(
arrayListOf(
Player(1, 86868686868686868L, "newSlMan", 1, 1, true, "SL"),
Player(2, 31337313373133711L, "IStayed(TM)", 2, 1, true, "SL"),
Player(1, 1L, "Man1", 1, 1, false, "RM"),
Player(1, 2L, "Man2", 1, 1, false, "LAT"),
Player(1, 3L, "Man3", 1, 1, false, "HAT"),
Player(1, 4L, "Man4", 1, 1, false, "MED"),
Player(1, 5L, "Man5", 1, 1, false, "GR"),
Player(1, 6L, "OtherSL1", 2, 2, true, "SL"),
Player(1, 7L, "OtherMan2", 2, 2, false, "MED"),
Player(1, 8L, "OtherMan3", 2, 0, false, "RM"),
Player(1, 9L, "OtherMan4", 2, 0, false, "RM"),
Player(1, 10L, "OtherMan5", 2, 0, false, "RM"),
), arrayListOf()
)
val squadInfo = Pair(
RconAbstraction.SquadListFaction(
"1st Battalion, 1st Marines", hashMapOf(
1 to RconAbstraction.SquadListSquad("HERPS", 5, true, "leaver9000", 69696969696969696L)
)
),
RconAbstraction.SquadListFaction(
"Insurgent Rebel Federation", hashMapOf(
1 to RconAbstraction.SquadListSquad("DERPS", 1, true, "IStayed(TM)", 31337313373133711L),
2 to RconAbstraction.SquadListSquad("NOOBS WELCOME", 2, false, "OtherSL1", 6L)
)
)
)
call.respondHtmlTemplate(OverviewTemplate(players, squadInfo)) {
}
} }
get("/css") { get("/css") {
call.respondText(contentType = ContentType.Text.CSS, text = Css.cssString) call.respondText(contentType = ContentType.Text.CSS, text = Css.cssString)

View File

@ -1,7 +1,10 @@
import io.ktor.server.html.* import io.ktor.server.html.*
import kotlinx.html.* import kotlinx.html.*
class OverviewTemplate(private val players: ListPlayersOutput) : Template<HTML> { class OverviewTemplate(
private val players: ListPlayersOutput,
private val squadInfo: Pair<RconAbstraction.SquadListFaction, RconAbstraction.SquadListFaction>
) : Template<HTML> {
private fun TR.makePlayer(player: Player) { private fun TR.makePlayer(player: Player) {
td { +player.role } td { +player.role }
td { +player.name } td { +player.name }
@ -11,7 +14,7 @@ class OverviewTemplate(private val players: ListPlayersOutput) : Template<HTML>
+"" +""
} }
button {//Kick button {//Kick
+"\uD83E\uDDB6" +""
} }
button {//Ban button {//Ban
+"" +""
@ -19,17 +22,38 @@ class OverviewTemplate(private val players: ListPlayersOutput) : Template<HTML>
} }
} }
private fun FlowContent.makeSquad(squad: Map.Entry<Int, List<Player>>) { private fun FlowContent.makeSquad(
squad: Map.Entry<Int, List<Player>>,
squadListSquad: RconAbstraction.SquadListSquad
) {
val leader = squad.value.find { it.leader } val leader = squad.value.find { it.leader }
+if (squad.key != 0) { div("squad") {
"Squad ${squad.key}" div("squadHeader") {
if (squad.key != 0) {
+squadListSquad.name
} else { } else {
"Unassigned" +"Unassigned"
} }
if (squadListSquad.locked) {
+" \uD83D\uDD12"
}
if (squadListSquad.creatorSteamId != (leader?.steamId ?: 0) && squadListSquad.creatorSteamId != 0L) {
+" (originally created by ${squadListSquad.creatorName} ${squadListSquad.creatorSteamId})"
}
}
table("squadTable") { table("squadTable") {
colGroup {
col("playerClass")
col("playerName")
col("playerId")
col("playerButtons")
}
leader?.let { leader?.let {
thead { thead {
tr { tr("leader") {
makePlayer(leader) makePlayer(leader)
} }
} }
@ -43,14 +67,23 @@ class OverviewTemplate(private val players: ListPlayersOutput) : Template<HTML>
} }
} }
} }
}
private fun FlowContent.makeSide(team: Map<Int, List<Player>>) { private fun FlowContent.makeSide(team: Map<Int, List<Player>>, squadListFaction: RconAbstraction.SquadListFaction) {
div("teamHeader") {
+squadListFaction.name
}
if (team.keys.isEmpty()) { if (team.keys.isEmpty()) {
+"empty" +"empty"
return return
} }
team.forEach { team.forEach {
makeSquad(it) if (it.key != 0) {
makeSquad(it, squadListFaction.squads[it.key]!!)
}
}
team.filter { it.key == 0 }.forEach {
makeSquad(it, RconAbstraction.SquadListSquad("Unassigned", it.value.size, false, "default", 0L))
} }
} }
@ -64,11 +97,11 @@ class OverviewTemplate(private val players: ListPlayersOutput) : Template<HTML>
body { body {
div("teamView") { div("teamView") {
div("teamA") { div("team colorRedFor") {
makeSide(teamA) makeSide(teamA, squadInfo.first)
} }
div("teamB") { div("team colorBluFor") {
makeSide(teamB) makeSide(teamB, squadInfo.second)
} }
} }
} }

View File

@ -1,11 +1,4 @@
/*
Format for "ListPlayers"
----- Active Players -----
ID: 0 | SteamID: 76561198037144702 | Name: [PACK] yenon | Team ID: 1 | Squad ID: N/A | Is Leader: False | Role: USA_Rifleman_01
----- Recently Disconnected Players [Max of 15] -----
ID: 1 | SteamID: 76561199074475884 | Since Disconnect: 03m.56s | Name: RoeveNn
ID: 2 | SteamID: 76561199262966526 | Since Disconnect: 03m.51s | Name: Aliko
*/
data class ListPlayersOutput(val players: ArrayList<Player>, val disconnected: ArrayList<DisconnectedPlayer>) data class ListPlayersOutput(val players: ArrayList<Player>, val disconnected: ArrayList<DisconnectedPlayer>)
data class Player( data class Player(
@ -30,12 +23,19 @@ val inactivePlayerInListRegex = Regex(
) )
class RconAbstraction(private val connection: RconConnection) { class RconAbstraction(private val connection: RconConnection) {
/*
Format for "ListPlayers"
----- Active Players -----
ID: 0 | SteamID: 76561198037144702 | Name: [PACK] yenon | Team ID: 1 | Squad ID: N/A | Is Leader: False | Role: USA_Rifleman_01
----- Recently Disconnected Players [Max of 15] -----
ID: 1 | SteamID: 76561199074475884 | Since Disconnect: 03m.56s | Name: RoeveNn
ID: 2 | SteamID: 76561199262966526 | Since Disconnect: 03m.51s | Name: Aliko
*/
suspend fun getCurrentPlayers(): ListPlayersOutput { suspend fun getCurrentPlayers(): ListPlayersOutput {
val active = arrayListOf<Player>() val active = arrayListOf<Player>()
val disconnected = arrayListOf<DisconnectedPlayer>() val disconnected = arrayListOf<DisconnectedPlayer>()
connection.sendCommand("ListPlayers").getOrNull()?.let { connection.sendCommand("ListPlayers").getOrNull()?.let {
println(it)
activePlayerInListRegex.findAll(it).forEach { match -> activePlayerInListRegex.findAll(it).forEach { match ->
val playerId = match.groupValues[1].toInt() val playerId = match.groupValues[1].toInt()
val steamId = match.groupValues[2].toLong() val steamId = match.groupValues[2].toLong()
@ -57,6 +57,60 @@ class RconAbstraction(private val connection: RconConnection) {
return ListPlayersOutput(active, disconnected) return ListPlayersOutput(active, disconnected)
} }
/*
----- Active Squads -----
Team ID: 1 (3rd Division)
ID: 1 | Name: HERPDERP2 THE RECONING | Size: 1 | Locked: True | Creator Name: yenon | Creator Steam ID: 76561198037144702
Team ID: 2 (Insurgent Rebel Federation)
*/
data class SquadListSquad(
val name: String,
val size: Int,
val locked: Boolean,
val creatorName: String,
val creatorSteamId: Long
)
data class SquadListFaction(val name: String, val squads: HashMap<Int, SquadListSquad>)
suspend fun getSquadList(): Pair<SquadListFaction, SquadListFaction> {
val lines = connection.sendCommand("ListSquads").getOrNull()!!.split("\n")
val teamRegex = Regex("""^Team ID: ([12]) \(([^)]+)\)$""")
val squadRegex =
Regex("""^ID: (\d+) \| Name: (.*) \| Size: (\d+) \| Locked: (True|False) \| Creator Name: (.*) \| Creator Steam ID: (\d{17})${'$'}""")
lateinit var faction1: SquadListFaction
lateinit var faction2: SquadListFaction
lateinit var currentFaction: SquadListFaction
lines.forEach { line ->
teamRegex.find(line)?.let {
if (it.groupValues[1].toInt() == 1) {
faction1 = SquadListFaction(it.groupValues[2], hashMapOf())
currentFaction = faction1
} else {
faction2 = SquadListFaction(it.groupValues[2], hashMapOf())
currentFaction = faction2
}
}
squadRegex.find(line)?.let {
val squadId = it.groupValues[1].toInt()
val name = it.groupValues[2]
val size = it.groupValues[3].toInt()
val locked = it.groupValues[4].lowercase().toBooleanStrict()
val creatorName = it.groupValues[5]
val creatorSteamId = it.groupValues[6].toLong()
currentFaction.squads[squadId] = SquadListSquad(name, size, locked, creatorName, creatorSteamId)
}
}
return Pair(faction1, faction2)
}
suspend fun kickPlayer(steamId: Long, reason: String) { suspend fun kickPlayer(steamId: Long, reason: String) {
connection.sendCommand("AdminKickById $steamId \"$reason\"") connection.sendCommand("AdminKickById $steamId \"$reason\"")
} }
@ -66,6 +120,6 @@ class RconAbstraction(private val connection: RconConnection) {
} }
suspend fun messagePlayer(steamId: Long, message: String) { suspend fun messagePlayer(steamId: Long, message: String) {
connection.sendCommand("AdminKickById $steamId \"$message\"") connection.sendCommand("AdminMessageById $steamId \"$message\"")
} }
} }