Migrate Fight, FightPlayer, and SchematicType classes to Kotlin and remove old Java implementations. Update references across modules.

Signed-off-by: Chaoscaot <max@maxsp.de>
This commit is contained in:
2025-11-01 13:52:26 +01:00
parent 4e6933f2fd
commit 0fbbcdacea
7 changed files with 343 additions and 343 deletions
@@ -1,134 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Fight {
private static final Table<Fight> table = new Table<>(Fight.class);
private static final SelectStatement<Fight> getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?");
private static final SelectStatement<Fight> getById = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId WHERE FightId = ?");
private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition");
private static final Statement updateReplayAvailable = table.update(Table.PRIMARY, "ReplayAvailable");
public static List<Fight> getPage(int page, int elementsPerPage) {
List<Fight> fights = getPage.listSelect(page * elementsPerPage, elementsPerPage);
List<FightPlayer> fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID));
for(Fight fight : fights) {
fight.initPlayers(fightPlayers);
}
SteamwarUser.batchCache(fightPlayers.stream().map(FightPlayer::getUserID).collect(Collectors.toSet()));
return fights;
}
public static Fight getById(int fightID) {
return getById.select(fightID);
}
public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){
return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition);
}
public static void markReplayAvailable(int fightID) {
updateReplayAvailable.update(true, fightID);
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int fightID;
@Field
private final String gameMode;
@Getter
@Field
private final String server;
@Getter
@Field
private final Timestamp startTime;
@Field
private final int duration;
@Field
private final int blueLeader;
@Field
private final int redLeader;
@Field(nullable = true)
private final Integer blueSchem;
@Field(nullable = true)
private final Integer redSchem;
@Getter
@Field
private final int win;
@Field
private final String wincondition;
@Field
private final boolean replayAvailable;
@Field // Virtual field for easy select
private final boolean replayAllowed;
@Getter
private final List<FightPlayer> bluePlayers = new ArrayList<>();
@Getter
private final List<FightPlayer> redPlayers = new ArrayList<>();
public SchematicType getSchemType() {
return SchematicType.fromDB(gameMode);
}
public SteamwarUser getBlueLeader() {
return SteamwarUser.byId(blueLeader);
}
public SteamwarUser getRedLeader() {
return SteamwarUser.byId(redLeader);
}
public boolean replayAllowed() {
return replayExists() && replayAllowed;
}
public boolean replayExists() {
return getSchemType() != null && replayAvailable;
}
private void initPlayers(List<FightPlayer> fightPlayers) {
for(FightPlayer fp : fightPlayers) {
if(fp.getFightID() != fightID)
continue;
if(fp.getTeam() == 1)
bluePlayers.add(fp);
else
redPlayers.add(fp);
}
}
}
+155
View File
@@ -0,0 +1,155 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.IntegerColumnType
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.javatime.timestamp
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
import org.jetbrains.exposed.v1.jdbc.update
import java.sql.Timestamp
object FightTable : IntIdTable("Fight", "FightId") {
val gamemode = varchar("Gamemode", 30)
val server = text("Server")
val startTime = timestamp("StartTime")
val duration = integer("Duration")
val blueLeader = reference("BlueLeader", SteamwarUserTable)
val redLeader = reference("RedLeader", SteamwarUserTable)
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val win = integer("Win")
val winCondition = varchar("WinCondition", 100)
val replayAvailable = bool("ReplayAvailable")
}
class Fight(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Fight>(FightTable) {
@JvmStatic
fun getById(id: Int) = useDb { get(id) }
@JvmStatic
fun create(
gamemode: String,
server: String,
starttime: Timestamp,
duration: Int,
blueleader: Int,
redleader: Int,
blueschem: Int?,
redschem: Int?,
win: Int,
wincondition: String
): Int = useDb {
FightTable.insertAndGetId {
it[FightTable.gamemode] = gamemode
it[FightTable.server] = server
it[FightTable.startTime] = starttime.toInstant()
it[FightTable.duration] = duration
it[FightTable.blueLeader] = EntityID(blueleader, SteamwarUserTable)
it[FightTable.redLeader] = EntityID(redleader, SteamwarUserTable)
it[FightTable.blueSchem] = blueschem?.let { EntityID(it, SchematicNodeTable) }
it[FightTable.redSchem] = redschem?.let { EntityID(it, SchematicNodeTable) }
it[FightTable.win] = win
it[FightTable.winCondition] = wincondition
}.value
}
@JvmStatic
fun getPage(page: Int, pageSize: Int): List<Fight> = useDb {
val fights = all().limit(pageSize).offset((pageSize * page).toLong())
val fightPlayer = FightPlayer.batchGet(fights.map { it.id.value })
for (fight in fights) {
fight.initPlayers(fightPlayer)
}
SteamwarUser.batchCache(fightPlayer.map { it.userID }.toMutableSet())
fights.toList()
}
@JvmStatic
fun markReplayAvailable(id: Int) = useDb {
FightTable.update({ FightTable.id eq id }) {
it[replayAvailable] = true
}
}
}
val fightID by FightTable.id.transform({ EntityID(it, FightTable) }, { it.value })
val gameMode by FightTable.gamemode
val server by FightTable.server
val startTime by FightTable.startTime.transform({ it.toInstant() }, { Timestamp.from(it) })
val duration by FightTable.duration
val blueLeaderId by FightTable.blueLeader
val blueLeader by lazy { useDb { SteamwarUser[blueLeaderId] } }
val redLeaderId by FightTable.redLeader
val redLeader by lazy { useDb { SteamwarUser[redLeaderId] } }
val blueSchem by FightTable.blueSchem
val redSchem by FightTable.redSchem
val win by FightTable.win
val winCondition by FightTable.winCondition
val replayAvailable by FightTable.replayAvailable
val schemType: SchematicType?
get() = SchematicType.fromDB(gameMode)
val replayAllowed by lazy {
replayExists() && useDb {
exec(
"SELECT (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId WHERE FightId = ?",
args = listOf(IntegerColumnType() to this@Fight.id)
) {
if (it.next()) {
it.getBoolean("ReplayAllowed")
} else {
false
}
} ?: false
}
}
fun replayExists() = schemType != null && replayAvailable
fun replayAllowed() = replayAllowed
lateinit var bluePlayers: List<FightPlayer>
lateinit var redPlayers: List<FightPlayer>
private fun initPlayers(fightPlayers: List<FightPlayer>) {
val blue = mutableListOf<FightPlayer>()
val red = mutableListOf<FightPlayer>()
for (player in fightPlayers.filter { it.fightID == id.value }) {
if (player.team == 1)
blue.add(player)
else
red.add(player)
}
bluePlayers = blue
redPlayers = red
}
}
@@ -1,65 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@AllArgsConstructor
public class FightPlayer {
private static final Table<FightPlayer> table = new Table<>(FightPlayer.class);
private static final Statement create = table.insertAll();
private static final SelectStatement<FightPlayer> batchGet = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN ?");
@Getter
@Field(keys = {Table.PRIMARY})
private final int fightID;
@Getter
@Field(keys = {Table.PRIMARY})
private final int userID;
@Getter
@Field
private final int team;
@Field
private final String kit;
@Field
private final int kills;
@Field
private final boolean isOut;
public static void create(int fightID, int userID, boolean blue, String kit, int kills, boolean isOut) {
create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut);
}
public static List<FightPlayer> batchGet(Stream<Integer> fightIds) {
try (SelectStatement<FightPlayer> batch = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN (" + fightIds.map(Object::toString).collect(Collectors.joining(", ")) + ")")) {
return batch.listSelect();
}
}
}
@@ -0,0 +1,80 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.inList
import org.jetbrains.exposed.v1.dao.CompositeEntity
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import org.jetbrains.exposed.v1.jdbc.insertIgnore
object FightPlayerTable : CompositeIdTable("FightPlayer") {
val fightId = reference("FightId", FightTable)
val userId = reference("UserId", SteamwarUserTable)
val team = integer("Team")
val kit = varchar("Kit", 64)
val kills = integer("Kills")
val out = bool("IsOut")
init {
addIdColumn(fightId)
addIdColumn(userId)
}
}
class FightPlayer(id: EntityID<CompositeID>) : CompositeEntity(id) {
companion object : CompositeEntityClass<FightPlayer>(FightPlayerTable) {
@JvmStatic
fun create(
fightId: Int,
userId: Int,
blue: Boolean,
kit: String,
kills: Int,
out: Boolean
) = useDb {
FightPlayerTable.insertIgnore {
it[this.fightId] = fightId
it[this.userId] = userId
it[this.team] = if (blue) 1 else 2
it[this.kit] = kit
it[this.kills] = kills
it[this.out] = out
}
}
@JvmStatic
fun batchGet(fightIds: List<Int>) = useDb {
find { FightPlayerTable.fightId inList fightIds.toList() }.toList()
}
}
val fightID by FightPlayerTable.fightId.transform({ EntityID(it, FightTable) }, { it.value })
val userID by FightPlayerTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
val team by FightPlayerTable.team
val kit by FightPlayerTable.kit
val kills by FightPlayerTable.kills
val out by FightPlayerTable.out
fun isOut() = out
}
@@ -328,7 +328,7 @@ class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
} }
private var nodeType by SchematicNodeTable.type private var nodeType by SchematicNodeTable.type
var schemtype: SchematicType var schemtype: SchematicType
get() = checkDir { SchematicType.fromDB(nodeType) } get() = checkDir { SchematicType.fromDB(nodeType!!) }
set(value) = checkDir { useDb { nodeType = value.toDB() } } set(value) = checkDir { useDb { nodeType = value.toDB() } }
var config by SchematicNodeTable.config var config by SchematicNodeTable.config
@@ -1,143 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.SqlTypeMapper;
import lombok.Getter;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
public class SchematicType {
public static final SchematicType Normal = new SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false);
private static final Map<String, SchematicType> fromDB;
private static final List<SchematicType> types;
static {
List<SchematicType> tmpTypes = new LinkedList<>();
Map<String, SchematicType> tmpFromDB = new HashMap<>();
tmpTypes.add(Normal);
tmpFromDB.put(Normal.name().toLowerCase(), Normal);
File folder = SQLWrapper.impl.getSchemTypesFolder();
if (folder.exists()) {
for (File configFile : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().collect(Collectors.toList())) {
GameModeConfig<?, String> gameModeConfig = SQLWrapper.impl.loadGameModeConfig(configFile);
if (gameModeConfig.Schematic.Type == null) continue;
if (tmpFromDB.containsKey(gameModeConfig.Schematic.Type.toDB())) continue;
SchematicType current = gameModeConfig.Schematic.Type;
if (!gameModeConfig.CheckQuestions.isEmpty()) {
SchematicType checkType = current.checkType;
tmpTypes.add(checkType);
tmpFromDB.put(checkType.toDB(), checkType);
}
tmpTypes.add(current);
tmpFromDB.put(current.toDB(), current);
SQLWrapper.impl.processSchematicType(gameModeConfig);
}
}
fromDB = Collections.unmodifiableMap(tmpFromDB);
types = Collections.unmodifiableList(tmpTypes);
}
static {
new SqlTypeMapper<>(SchematicType.class, "VARCHAR(16)", (rs, identifier) -> {
String t = rs.getString(identifier);
return t != null ? fromDB.get(t) : null;
}, (st, index, value) -> st.setString(index, value.toDB()));
}
private final String name;
@Getter
private final String kuerzel;
private final Type type;
private final SchematicType checkType;
@Getter
private final String material;
@Getter
private final Date deadline;
@Getter
private final boolean manualCheck;
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, boolean manualCheck){
this(name, kuerzel, type, checkType, material, null, manualCheck);
}
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, Date deadline, boolean manualCheck){
this.name = name;
this.kuerzel = kuerzel;
this.type = type;
this.checkType = checkType;
this.material = material;
this.deadline = deadline;
this.manualCheck = manualCheck;
}
public boolean isAssignable(){
return type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck;
}
public SchematicType checkType(){
if (!manualCheck) {
return this;
}
return checkType;
}
public boolean check(){
return type == Type.CHECK_TYPE;
}
public boolean fightType(){
return type == Type.FIGHT_TYPE;
}
public boolean writeable(){
return type == Type.NORMAL;
}
public String name(){
return name;
}
public String toDB(){
return name.toLowerCase();
}
public static SchematicType fromDB(String input) {
if (fromDB == null) return null;
return fromDB.get(input.toLowerCase());
}
public static List<SchematicType> values(){
return types;
}
enum Type{
NORMAL,
CHECK_TYPE,
FIGHT_TYPE
}
}
@@ -0,0 +1,107 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql
import java.io.File
import java.util.*
import java.util.stream.Collectors
data class SchematicType(
val name: String,
val kuerzel: String,
val type: Type,
val checkType: SchematicType?,
val material: String,
val deadline: Date?,
val manualCheck: Boolean,
) {
constructor(
name: String,
kuerzel: String,
type: Type,
checkType: SchematicType?,
material: String,
manualCheck: Boolean
) : this(name, kuerzel, type, checkType, material, null, manualCheck)
companion object {
@JvmField
val Normal = SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false)
private val types: List<SchematicType>
private val fromDB: Map<String, SchematicType>
init {
val tmpTypes = mutableListOf<SchematicType>()
val tmpFromDB = mutableMapOf<String, SchematicType>()
tmpTypes.add(Normal)
tmpFromDB[Normal.toDB()] = Normal
val folder = SQLWrapper.impl.schemTypesFolder
if (folder.exists()) {
for (configFile in Arrays.stream<File?>(folder.listFiles { _, name ->
name.endsWith(
".yml"
) && !name.endsWith(".kits.yml")
}).sorted().collect(Collectors.toList())) {
val gameModeConfig = SQLWrapper.impl.loadGameModeConfig(configFile)
if (gameModeConfig.Schematic.Type == null) continue
if (tmpFromDB.containsKey(gameModeConfig.Schematic.Type.toDB())) continue
val current = gameModeConfig.Schematic.Type
if (gameModeConfig.CheckQuestions.isNotEmpty()) {
val checkType = current.checkType
tmpTypes.add(checkType!!)
tmpFromDB[checkType.toDB()] = checkType
}
tmpTypes.add(current)
tmpFromDB[current.toDB()] = current
SQLWrapper.impl.processSchematicType(gameModeConfig)
}
}
types = tmpTypes.toList()
fromDB = tmpFromDB.toMap()
}
@JvmStatic
fun values() = types
@JvmStatic
fun fromDB(value: String) =
fromDB[value.lowercase()] ?: throw IllegalArgumentException("Unknown SchematicType: $value")
}
fun name() = name
fun toDB() = name.lowercase()
fun check() = type == Type.CHECK_TYPE
fun fightType() = type == Type.FIGHT_TYPE
fun writeable() = type == Type.NORMAL
fun checkType() = if (manualCheck) checkType else this
fun isAssignable() = type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck
enum class Type {
NORMAL,
CHECK_TYPE,
FIGHT_TYPE
}
}