diff --git a/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.java b/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.java
deleted file mode 100644
index 9376ed18..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.java
+++ /dev/null
@@ -1,77 +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 .
- */
-
-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 lombok.Setter;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@AllArgsConstructor
-public class PollAnswer {
-
- @Getter
- @Setter
- private static String currentPoll;
-
- private static final Table table = new Table<>(PollAnswer.class);
-
- private static final SelectStatement get = table.select(Table.PRIMARY);
- private static final Statement getResults = new Statement("SELECT Count(UserID) AS Times, Answer FROM PollAnswer WHERE Question = ? GROUP BY Answer ORDER BY Times ASC");
- private static final Statement insert = table.insertAll();
-
- @Field(keys = {Table.PRIMARY})
- private final int userID;
- @Field(keys = {Table.PRIMARY})
- private final String question;
- @Field(def = "0")
- private int answer;
-
- public static PollAnswer get(int userID) {
- PollAnswer answer = get.select(userID, currentPoll);
- if(answer == null)
- return new PollAnswer(userID, currentPoll, 0);
- return answer;
- }
-
- public static Map getCurrentResults() {
- return getResults.select(rs -> {
- Map retMap = new HashMap<>();
- while (rs.next())
- retMap.put(rs.getInt("Answer")-1, rs.getInt("Times"));
- return retMap;
- }, currentPoll);
- }
-
- public boolean hasAnswered(){
- return answer != 0;
- }
-
- public void setAnswer(int answer){
- this.answer = answer;
- insert.update(userID, question, answer);
- }
-}
diff --git a/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.kt b/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.kt
new file mode 100644
index 00000000..a4c20b26
--- /dev/null
+++ b/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.kt
@@ -0,0 +1,78 @@
+/*
+ * 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 .
+ */
+
+package de.steamwar.sql
+
+import de.steamwar.sql.internal.useDb
+import org.jetbrains.exposed.v1.core.VarCharColumnType
+import org.jetbrains.exposed.v1.core.and
+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.eq
+import org.jetbrains.exposed.v1.dao.CompositeEntity
+import org.jetbrains.exposed.v1.dao.CompositeEntityClass
+
+object PollAnswerTable: CompositeIdTable("PollAnswer") {
+ val userId = reference("UserID", SteamwarUserTable)
+ val question = varchar("Question", 150)
+ val answer = integer("Answer")
+}
+
+class PollAnswer(id: EntityID): CompositeEntity(id) {
+ var userId by PollAnswerTable.userId
+ private set
+ var question by PollAnswerTable.question
+ private set
+ private var answerId by PollAnswerTable.answer
+ var answer: Int
+ get() = answerId
+ set(value) = useDb {
+ answerId = value
+ }
+
+ companion object: CompositeEntityClass(PollAnswerTable) {
+ @JvmStatic
+ var currentPoll: String? = null
+
+ @JvmStatic
+ fun get(userId: Int) = useDb {
+ find { (PollAnswerTable.userId eq userId) and (PollAnswerTable.question eq currentPoll!!) }.firstOrNull()
+ ?: new {
+ this.userId = EntityID(userId, SteamwarUserTable)
+ this.question = currentPoll!!
+ this.answerId = 0
+ }
+ }
+
+ @JvmStatic
+ fun getCurrentResults(): Map = useDb {
+ exec("SELECT Count(UserID) AS Times, Answer FROM PollAnswer WHERE Question = ? GROUP BY Answer ORDER BY Times ASC",
+ args = listOf(VarCharColumnType() to currentPoll!!)) {
+ val result = mutableMapOf()
+ while (it.next()) {
+ result[it.getInt("Answer")] = it.getInt("Times")
+ }
+ result
+ } ?: emptyMap()
+ }
+ }
+
+ fun hasAnswered() = answerId != 0
+}
\ No newline at end of file
diff --git a/CommonCore/SQL/src/de/steamwar/sql/Punishment.java b/CommonCore/SQL/src/de/steamwar/sql/Punishment.java
deleted file mode 100644
index f5813792..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/Punishment.java
+++ /dev/null
@@ -1,140 +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 .
- */
-
-package de.steamwar.sql;
-
-import de.steamwar.sql.internal.*;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-import java.sql.Timestamp;
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-@AllArgsConstructor
-public class Punishment {
-
- static {
- SqlTypeMapper.nameEnumMapper(PunishmentType.class);
- }
-
- public static final Timestamp PERMA_TIME = Timestamp.from(Instant.ofEpochSecond(946674800));
-
- private static final Table table = new Table<>(Punishment.class, "Punishments");
- private static final SelectStatement getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)");
- private static final SelectStatement getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1");
- private static final SelectStatement getAllPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? ORDER BY `PunishmentId` DESC");
- private static final Statement insert = table.insertFields(true, "UserId", "Punisher", "Type", "EndTime", "Perma", "Reason");
-
- public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
- return getPunishment.select(user, type);
- }
-
- public static Map getPunishmentsOfPlayer(int user) {
- return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
- }
-
- public static List getAllPunishmentsOfPlayer(int user) {
- return getAllPunishments.listSelect(user);
- }
-
- public static boolean isPunished(SteamwarUser user, PunishmentType type, Consumer callback) {
- Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
- if(punishment == null || !punishment.isCurrent()) {
- return false;
- } else {
- callback.accept(punishment);
- return true;
- }
- }
-
- public static Punishment createPunishment(int user, int executor, PunishmentType type, String reason, Timestamp endTime, boolean perma) {
- if(perma && !endTime.equals(PERMA_TIME)) {
- throw new IllegalArgumentException("Permanent punishments must have an end time of `Punishment.PERMA_TIME`");
- }
- int punishmentId = insert.insertGetKey(user, executor, type.name(), endTime, perma, reason);
- return new Punishment(punishmentId, user, executor, type, Timestamp.from(Instant.now()), endTime, perma, reason);
- }
-
- @Field(keys = {Table.PRIMARY}, autoincrement = true)
- private final int punishmentId;
- @Field
- @Getter
- private final int userId;
- @Field
- @Getter
- private final int punisher;
- @Field
- @Getter
- private final PunishmentType type;
- @Field
- @Getter
- private final Timestamp startTime;
- @Field
- @Getter
- private final Timestamp endTime;
- @Field
- @Getter
- private final boolean perma;
- @Field
- @Getter
- private final String reason;
-
- @Deprecated // Not multiling, misleading title
- public String getBantime(Timestamp endTime, boolean perma) {
- if (perma) {
- return "permanent";
- } else {
- return endTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
- }
- }
-
- public boolean isCurrent() {
- return isPerma() || getEndTime().after(new Date());
- }
-
- @AllArgsConstructor
- @RequiredArgsConstructor
- @Getter
- public enum PunishmentType {
- Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
- Mute( false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"),
- NoSchemReceiving(true, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"),
- NoSchemSharing(true, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"),
- NoSchemSubmitting(false, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"),
- NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"),
- NoFightServer(true, "NOFIGHTSERVER_TEAM", "NOFIGHTSERVER_PERMA", "NOFIGHTSERVER_UNTIL", "UNNOFIGHTSERVER_ERROR", "UNNOFIGHTSERVER"),
- NoTeamServer(true, "NOTEAMSERVER_TEAM", "NOTEAMSERVER_PERMA", "NOTEAMSERVER_UNTIL", "UNNOTEAMSERVER_ERROR", "UNNOTEAMSERVER"),
- Note(false, "NOTE_TEAM", null, null, null, null, true);
-
- private final boolean needsAdmin;
- private final String teamMessage;
- private final String playerMessagePerma;
- private final String playerMessageUntil;
- private final String usageNotPunished;
- private final String unpunishmentMessage;
- private boolean multi = false;
- }
-}
diff --git a/CommonCore/SQL/src/de/steamwar/sql/Punishment.kt b/CommonCore/SQL/src/de/steamwar/sql/Punishment.kt
new file mode 100644
index 00000000..a5fa6baa
--- /dev/null
+++ b/CommonCore/SQL/src/de/steamwar/sql/Punishment.kt
@@ -0,0 +1,190 @@
+/*
+ * 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 .
+ */
+
+package de.steamwar.sql
+
+import de.steamwar.sql.internal.useDb
+import org.jetbrains.exposed.v1.core.SortOrder
+import org.jetbrains.exposed.v1.core.and
+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.core.inSubQuery
+import org.jetbrains.exposed.v1.core.max
+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.select
+import java.sql.Timestamp
+import java.time.Instant
+import java.util.Date
+import java.util.function.Consumer
+
+object PunishmentTable : IntIdTable("Punishment", "PunishmentId") {
+ val userId = reference("UserId", SteamwarUserTable)
+ val punisher = reference("Punisher", SteamwarUserTable)
+ val type = enumerationByName("Type", 32, Punishment.PunishmentType::class)
+ val startTime = timestamp("StartTime")
+ val endTime = timestamp("EndTime")
+ val perma = bool("Perma")
+ val reason = text("Reason")
+}
+
+class Punishment(id: EntityID) : IntEntity(id) {
+ companion object : IntEntityClass(PunishmentTable) {
+ @JvmField
+ val PERMA_TIME: Timestamp = Timestamp.from(Instant.ofEpochSecond(946674800))
+
+ @JvmStatic
+ fun getPunsihmentOfPlayer(user: Int, type: PunishmentType) = useDb {
+ find { (PunishmentTable.userId eq user) and (PunishmentTable.type eq type) }.orderBy(PunishmentTable.id to SortOrder.DESC).firstOrNull()
+ }
+
+ @JvmStatic
+ fun getPunishmentsOfPlayer(user: Int) = useDb {
+ find {
+ PunishmentTable.id inSubQuery PunishmentTable.select(PunishmentTable.id.max())
+ .where { PunishmentTable.userId eq user }.groupBy(
+ PunishmentTable.type
+ )
+ }.associateBy { it.type }.toMutableMap()
+ }
+
+ @JvmStatic
+ fun getAllPunishmentsOfPlayer(user: Int) = useDb {
+ find { PunishmentTable.userId eq user }.orderBy(PunishmentTable.id to SortOrder.DESC).toList()
+ }
+
+ @JvmStatic
+ fun isPunished(user: SteamwarUser, type: PunishmentType, callback: Consumer): Boolean = useDb {
+ val punishment = getPunsihmentOfPlayer(user.id.value, type) ?: return@useDb false
+
+ if (punishment.isCurrent()) {
+ callback.accept(punishment)
+ return@useDb true
+ }
+
+ return@useDb false
+ }
+
+ @JvmStatic
+ fun createPunishment(
+ user: Int,
+ executor: Int,
+ type: PunishmentType,
+ reason: String,
+ endTime: Timestamp,
+ perma: Boolean
+ ) = useDb {
+ new {
+ this.userId = user
+ this.punisher = executor
+ this.type = type
+ this.startTime = Timestamp.from(Instant.now())
+ this.endTime = endTime
+ this.perma = perma
+ this.reason = reason
+ }
+ }
+ }
+
+ var userId by PunishmentTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
+ private set
+ var punisher by PunishmentTable.punisher.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
+ private set
+ var type by PunishmentTable.type
+ private set
+ var startTime by PunishmentTable.startTime.transform({ it.toInstant() }, { Timestamp.from(it) })
+ private set
+ var endTime by PunishmentTable.endTime.transform({ it.toInstant() }, { Timestamp.from(it) })
+ private set
+ var perma by PunishmentTable.perma
+ private set
+ var reason by PunishmentTable.reason
+ private set
+
+ fun isPerma() = perma
+
+ fun isCurrent() = perma || endTime.after(Date())
+
+ enum class PunishmentType(
+ val needsAdmin: Boolean,
+ val teamMessage: String?,
+ val playerMessagePerma: String?,
+ val playerMessageUntil: String?,
+ val usageNotPunished: String?,
+ val unpunishmentMessage: String?,
+ val multi: Boolean = false
+ ) {
+ Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
+ Mute(false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"),
+ NoSchemReceiving(
+ true,
+ "NOSCHEMRECEIVING_TEAM",
+ "NOSCHEMRECEIVING_PERMA",
+ "NOSCHEMRECEIVING_UNTIL",
+ "UNNOSCHEMRECEIVING_ERROR",
+ "UNNOSCHEMRECEIVING"
+ ),
+ NoSchemSharing(
+ true,
+ "NOSCHEMSHARING_TEAM",
+ "NOSCHEMSHARING_PERMA",
+ "NOSCHEMSHARING_UNTIL",
+ "UNNOSCHEMSHARING_ERROR",
+ "UNNOSCHEMSHARING"
+ ),
+ NoSchemSubmitting(
+ false,
+ "NOSCHEMSUBMITTING_TEAM",
+ "NOSCHEMSUBMITTING_PERMA",
+ "NOSCHEMSUBMITTING_UNTIL",
+ "UNNOSCHEMSUBMITTING_ERROR",
+ "UNNOSCHEMSUBMITTING"
+ ),
+ NoDevServer(
+ true,
+ "NODEVSERVER_TEAM",
+ "NODEVSERVER_PERMA",
+ "NODEVSERVER_UNTIL",
+ "UNNODEVSERVER_ERROR",
+ "UNNODEVSERVER"
+ ),
+ NoFightServer(
+ true,
+ "NOFIGHTSERVER_TEAM",
+ "NOFIGHTSERVER_PERMA",
+ "NOFIGHTSERVER_UNTIL",
+ "UNNOFIGHTSERVER_ERROR",
+ "UNNOFIGHTSERVER"
+ ),
+ NoTeamServer(
+ true,
+ "NOTEAMSERVER_TEAM",
+ "NOTEAMSERVER_PERMA",
+ "NOTEAMSERVER_UNTIL",
+ "UNNOTEAMSERVER_ERROR",
+ "UNNOTEAMSERVER"
+ ),
+ Note(false, "NOTE_TEAM", null, null, null, null, true);
+
+ fun isNeedsAdmin() = needsAdmin
+ fun isMulti() = multi
+ }
+}
\ No newline at end of file
diff --git a/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt b/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt
index 3ea8aa6e..a4dc42ba 100644
--- a/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt
+++ b/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt
@@ -208,7 +208,7 @@ class SteamwarUser(id: EntityID): IntEntity(id) {
fun getPunishment(punishment: Punishment.PunishmentType) = punishments[punishment]
fun isPunished(punishment: Punishment.PunishmentType) = getPunishment(punishment)
?.let {
- if (!it.isCurrent) {
+ if (!it.isCurrent()) {
if (punishment == Punishment.PunishmentType.Ban) {
BannedUserIPs.unbanIPs(id.value)
}