diff --git a/CommonCore/SQL/src/de/steamwar/sql/PersonalKit.kt b/CommonCore/SQL/src/de/steamwar/sql/PersonalKit.kt
new file mode 100644
index 00000000..c970845d
--- /dev/null
+++ b/CommonCore/SQL/src/de/steamwar/sql/PersonalKit.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.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 PersonalKitTable: CompositeIdTable("PersonalKit") {
+ val userId = reference("UserId", SteamwarUserTable)
+ val gamemode = varchar("Gamemode", 64)
+ val kitName = varchar("KitName", 64)
+ val inventory = text("Inventory")
+ val armor = text("Armor")
+ val inUse = bool("InUse")
+}
+
+class InternalKit(id: EntityID): CompositeEntity(id) {
+ companion object: CompositeEntityClass(PersonalKitTable) {
+ @JvmStatic
+ fun get(userId: Int, gamemode: String) = useDb {
+ find { PersonalKitTable.userId eq userId and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.inUse eq true) }
+ .toList()
+ }
+
+ @JvmStatic
+ fun get(userId: Int, gamemode: String, kitName: String) = useDb {
+ find { PersonalKitTable.userId eq userId and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.kitName eq kitName) and (PersonalKitTable.inUse eq true) }
+ .firstOrNull()
+ }
+
+ @JvmStatic
+ fun create(userId: Int, gamemode: String, kitName: String, rawInventory: String, rawArmor: String) = useDb {
+ new {
+ this.userID = userId
+ this.gamemode = gamemode
+ this.name = kitName
+ this.inUse = true
+ this.rawInventory = rawInventory
+ this.rawArmor = rawArmor
+ }
+ }
+
+ @JvmStatic
+ fun getKitInUse(userId: Int, gamemode: String) = useDb {
+ find { PersonalKitTable.userId eq userId and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.inUse eq true) }
+ .firstOrNull()
+ }
+ }
+
+ var userID by PersonalKitTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
+ private set
+ var gamemode by PersonalKitTable.gamemode
+ private set
+ var name by PersonalKitTable.kitName
+ private set
+ var rawInventory by PersonalKitTable.inventory
+ private set
+ var rawArmor by PersonalKitTable.armor
+ private set
+ var inUse by PersonalKitTable.inUse
+ private set
+
+ var inventory: String
+ get() = rawInventory
+ set(value) = useDb { rawInventory = value }
+ var armor: String
+ get() = rawArmor
+ set(value) = useDb { rawArmor = value }
+
+ fun setDefault() = useDb {
+ find { PersonalKitTable.userId eq userID and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.inUse eq true) }
+ .forEach { it.inUse = false }
+ inUse = true
+ }
+
+ override fun delete() = useDb {
+ super.delete()
+ }
+}
\ No newline at end of file
diff --git a/CommonCore/SQL/src/de/steamwar/sql/UserElo.java b/CommonCore/SQL/src/de/steamwar/sql/UserElo.java
deleted file mode 100644
index a02ba03a..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/UserElo.java
+++ /dev/null
@@ -1,152 +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 java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-@AllArgsConstructor
-public class UserElo {
-
- private static final int ELO_DEFAULT = 0;
-
- private static final Map>> gameModeUserEloCache = new ConcurrentHashMap<>();
- private static final Map emblemCache = new ConcurrentHashMap<>();
-
- public static void clear() {
- gameModeUserEloCache.clear();
- emblemCache.clear();
- }
-
- private static final Table table = new Table<>(UserElo.class);
- private static final SelectStatement getElo = table.select(Table.PRIMARY);
- private static final Statement setElo = table.insertAll();
-
- private static final Statement place = new Statement("SELECT COUNT(*) AS Place FROM UserElo WHERE GameMode = ? AND Elo > ? AND Season = ?");
- private static final Statement fightsOfSeason = new Statement("SELECT COUNT(*) AS Fights FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode = ? AND UNIX_TIMESTAMP(StartTime) + Duration >= UNIX_TIMESTAMP(?)");
-
- @Field(keys = {Table.PRIMARY})
- private final int season;
- @Field(keys = {Table.PRIMARY})
- private final String gameMode;
- @Field(keys = {Table.PRIMARY})
- private final int userId;
- @Field
- private final int elo;
-
- public static int getEloOrDefault(int userID, String gameMode) {
- return getElo(userID, gameMode).orElse(ELO_DEFAULT);
- }
-
- public static Optional getElo(int userID, String gameMode) {
- return gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).computeIfAbsent(userID, uid -> Optional.ofNullable(getElo.select(Season.getSeason(), gameMode, userID)).map(userElo -> userElo.elo));
- }
-
- public static int getFightsOfSeason(int userID, String gameMode) {
- return fightsOfSeason.select(rs -> {
- if (rs.next())
- return rs.getInt("Fights");
- return 0;
- }, userID, gameMode, Season.getSeasonStart());
- }
-
- public static void setElo(int userId, String gameMode, int elo) {
- emblemCache.remove(userId);
- gameModeUserEloCache.getOrDefault(gameMode, Collections.emptyMap()).remove(userId);
- setElo.update(Season.getSeason(), gameMode, userId, elo);
- }
-
- public static int getPlacement(int elo, String gameMode) {
- return place.select(rs -> {
- if (rs.next())
- return rs.getInt("Place") + 1;
- return -1;
- }, gameMode, elo, Season.getSeason());
- }
-
- public static String getEmblem(SteamwarUser user, List rankedModes) {
- return emblemCache.computeIfAbsent(user.getId(), userId -> {
- int emblemProgression = -1;
- for (String mode : rankedModes) {
- if (UserElo.getFightsOfSeason(userId, mode) == 0) continue;
- int progression = getProgression(userId, mode);
- if (progression > emblemProgression) {
- emblemProgression = progression;
- }
- }
- return toEmblem(emblemProgression);
- });
- }
-
- public static String getEmblemProgression(String gameMode, int userId) {
- switch (getProgression(userId, gameMode)) {
- case -1:
- return "§8❱❱❱❱ ❂";
- case 0:
- return "§e❱§8❱❱❱ ❂";
- case 1:
- return "§e❱❱§8❱❱ ❂";
- case 2:
- return "§e❱❱❱§8❱ ❂";
- case 3:
- return "§e❱❱❱❱§8 ❂";
- case 4:
- return "§8❱❱❱❱ §5❂";
- default:
- throw new SecurityException("Progression is not in range");
- }
- }
-
- public static int getProgression(int userId, String gameMode) {
- int elo = getElo(userId, gameMode).orElse(-1);
- if (elo < 0) return -1;
-
- if (elo < 150) return 0;
- if (elo < 350) return 1;
- if (elo < 600) return 2;
- if (elo < 900) return 3;
- return 4;
- }
-
- public static String toEmblem(int progression) {
- switch(progression) {
- case -1:
- return "";
- case 0:
- return "§e❱ ";
- case 1:
- return "§e❱❱ ";
- case 2:
- return "§e❱❱❱ ";
- case 3:
- return "§e❱❱❱❱ ";
- case 4:
- return "§5❂ ";
- default:
- throw new SecurityException("Progression out of range");
- }
- }
-}
diff --git a/CommonCore/SQL/src/de/steamwar/sql/UserElo.kt b/CommonCore/SQL/src/de/steamwar/sql/UserElo.kt
new file mode 100644
index 00000000..31b1b34a
--- /dev/null
+++ b/CommonCore/SQL/src/de/steamwar/sql/UserElo.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.*
+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.dao.CompositeEntity
+import org.jetbrains.exposed.v1.dao.CompositeEntityClass
+import org.jetbrains.exposed.v1.jdbc.insertIgnore
+import org.jetbrains.exposed.v1.jdbc.select
+import java.util.concurrent.ConcurrentHashMap
+
+object UserEloTable : CompositeIdTable("UserElo") {
+ val season = integer("Season").entityId()
+ val gameMode = varchar("GameMode", 16).entityId()
+ val userId = reference("UserID", SteamwarUserTable)
+ val elo = integer("Elo")
+
+ override val primaryKey = PrimaryKey(season, gameMode, userId)
+
+ init {
+ addIdColumn(season)
+ addIdColumn(gameMode)
+ }
+}
+
+class UserElo(id: EntityID) : CompositeEntity(id) {
+ companion object : CompositeEntityClass(UserEloTable) {
+ private const val ELO_DEFAULT = 0
+
+ private val gameModeUserEloCache: MutableMap> = ConcurrentHashMap()
+ private val emblemCache: MutableMap = ConcurrentHashMap()
+
+ @JvmStatic
+ fun clear() {
+ gameModeUserEloCache.clear()
+ emblemCache.clear()
+ }
+
+ @JvmStatic
+ fun getEloOrDefault(userId: Int, gameMode: String) =
+ getElo(userId, gameMode) ?: ELO_DEFAULT
+
+ @JvmStatic
+ fun getElo(userId: Int, gameMode: String) =
+ gameModeUserEloCache.getOrPut(gameMode) { mutableMapOf() }
+ .getOrPut(userId) { getEloFromDb(userId, gameMode)?.elo }
+
+ private fun getEloFromDb(userId: Int, gameMode: String) = useDb {
+ find { (UserEloTable.userId eq userId) and (UserEloTable.gameMode eq gameMode) }.firstOrNull()
+ }
+
+ @JvmStatic
+ fun getFightsOfSeason(userId: Int, gamemode: String) = useDb {
+ exec(
+ "SELECT COUNT(*) AS Fights FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode = ? AND UNIX_TIMESTAMP(StartTime) + Duration >= UNIX_TIMESTAMP(?)",
+ args = listOf(
+ IntegerColumnType() to userId,
+ VarCharColumnType() to gamemode,
+ VarCharColumnType() to Season.getSeasonStart()
+ )
+ ) {
+ return@exec if (it.next()) {
+ it.getInt("Fights")
+ } else {
+ 0
+ }
+ } ?: 0
+ }
+
+ @JvmStatic
+ fun setElo(userId: Int, gameMode: String, elo: Int) {
+ emblemCache.remove(userId)
+ gameModeUserEloCache.getOrDefault(gameMode, mutableMapOf()).remove(userId)
+ useDb {
+ findByIdAndUpdate(CompositeID {
+ it[UserEloTable.userId] = userId
+ it[UserEloTable.gameMode] = gameMode
+ it[UserEloTable.season] = Season.getSeason()
+ }) {
+ it.elo = elo
+ } ?: UserEloTable.insertIgnore {
+ it[UserEloTable.userId] = userId
+ it[UserEloTable.gameMode] = gameMode
+ it[UserEloTable.season] = Season.getSeason()
+ it[UserEloTable.elo] = elo
+ }
+ }
+ }
+
+ @JvmStatic
+ fun getPlacement(elo: Int, gamemode: String) = useDb {
+ UserEloTable.select(UserEloTable.userId.count()).where {
+ (UserEloTable.gameMode eq gamemode) and (UserEloTable.elo greater elo) and (UserEloTable.season eq Season.getSeason())
+ }.firstOrNull()?.get(UserEloTable.userId.count())?.let { it + 1 }?.toInt() ?: -1
+ }
+
+ @JvmStatic
+ fun getEmblem(user: SteamwarUser, rankedModes: List) =
+ emblemCache.getOrPut(user.id.value) {
+ var emblemProgression = -1
+ for (mode in rankedModes) {
+ if (getFightsOfSeason(user.id.value, mode) == 0) continue
+ val progression = getProgression(user.id.value, mode)
+ if (progression > emblemProgression) {
+ emblemProgression = progression
+ }
+ }
+ return toEmblem(emblemProgression)
+ }
+
+ @JvmStatic
+ fun getEmblemProgression(gameMode: String, userId: Int): String =
+ when (getProgression(userId, gameMode)) {
+ -1 -> "§8❱❱❱❱ ❂"
+ 0 -> "§e❱§8❱❱❱ ❂"
+ 1 -> "§e❱❱§8❱❱ ❂"
+ 2 -> "§e❱❱❱§8❱ ❂"
+ 3 -> "§e❱❱❱❱§8 ❂"
+ 4 -> "§8❱❱❱❱ §5❂"
+ else -> throw SecurityException("Progression is not in range")
+ }
+
+
+ @JvmStatic
+ fun getProgression(userId: Int, gameMode: String) = useDb { getElo(userId, gameMode) ?: -1 }.let {
+ when {
+ it < 0 -> -1
+ it < 150 -> 0
+ it < 350 -> 1
+ it < 600 -> 2
+ it < 900 -> 3
+ else -> 4
+ }
+ }
+
+ @JvmStatic
+ fun toEmblem(progression: Int) = when (progression) {
+ -1 -> ""
+ 0 -> "§e❱ "
+ 1 -> "§e❱❱ "
+ 2 -> "§e❱❱❱ "
+ 3 -> "§e❱❱❱❱ "
+ 4 -> "§5❂ "
+ else -> throw SecurityException("Progression out of range")
+ }
+ }
+
+ var elo by UserEloTable.elo
+}
\ No newline at end of file
diff --git a/CommonCore/SQL/src/de/steamwar/sql/internal/SelectStatement.java b/CommonCore/SQL/src/de/steamwar/sql/internal/SelectStatement.java
deleted file mode 100644
index 259989bd..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/internal/SelectStatement.java
+++ /dev/null
@@ -1,72 +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.internal;
-
-import java.lang.reflect.InvocationTargetException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public class SelectStatement extends Statement {
- private final Table table;
-
- SelectStatement(Table table, String... kfields) {
- this(table, "SELECT " + Arrays.stream(table.fields).map(f -> f.identifier).collect(Collectors.joining(", ")) + " FROM " + table.name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
- }
-
- public SelectStatement(Table table, String sql) {
- super(sql);
- this.table = table;
- }
-
- public T select(Object... values) {
- return select(rs -> {
- if (rs.next())
- return read(rs);
- return null;
- }, values);
- }
-
- public List listSelect(Object... values) {
- return select(rs -> {
- List result = new ArrayList<>();
- while (rs.next())
- result.add(read(rs));
-
- return result;
- }, values);
- }
-
- private T read(ResultSet rs) throws SQLException {
- Object[] params = new Object[table.fields.length];
- for(int i = 0; i < params.length; i++) {
- params[i] = table.fields[i].read(rs);
- }
-
- try {
- return table.constructor.newInstance(params);
- } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
- throw new SecurityException(e);
- }
- }
-}
diff --git a/CommonCore/SQL/src/de/steamwar/sql/internal/SqlTypeMapper.java b/CommonCore/SQL/src/de/steamwar/sql/internal/SqlTypeMapper.java
deleted file mode 100644
index 8ac8ba66..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/internal/SqlTypeMapper.java
+++ /dev/null
@@ -1,115 +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.internal;
-
-import java.io.InputStream;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Arrays;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-public final class SqlTypeMapper {
- private static final Map, SqlTypeMapper>> mappers = new IdentityHashMap<>();
-
- public static SqlTypeMapper getMapper(Class> clazz) {
- SqlTypeMapper result = (SqlTypeMapper) mappers.get(clazz);
-
- if(result == null)
- throw new SecurityException("Unregistered mapper requested: " + clazz.getName());
-
- return result;
- }
-
- public static > void ordinalEnumMapper(Class type) {
- T[] enumConstants = type.getEnumConstants();
- new SqlTypeMapper<>(
- type,
- "INTEGER(" + (int)Math.ceil(enumConstants.length/256.0) + ")",
- (rs, identifier) -> enumConstants[rs.getInt(identifier)],
- (st, index, value) -> st.setInt(index, value.ordinal())
- );
- }
-
- public static > void nameEnumMapper(Class type) {
- new SqlTypeMapper<>(
- type,
- "VARCHAR(" + Arrays.stream(type.getEnumConstants()).map(e -> e.name().length()).max(Integer::compareTo).orElse(0) + ")",
- (rs, identifier) -> Enum.valueOf(type, rs.getString(identifier)),
- (st, index, value) -> st.setString(index, value.name())
- );
- }
-
- static {
- primitiveMapper(boolean.class, Boolean.class, "BOOLEAN", ResultSet::getBoolean, PreparedStatement::setBoolean);
- primitiveMapper(byte.class, Byte.class, "INTEGER(1)", ResultSet::getByte, PreparedStatement::setByte);
- primitiveMapper(short.class, Short.class, "INTEGER(2)", ResultSet::getShort, PreparedStatement::setShort);
- primitiveMapper(int.class, Integer.class, "INTEGER", ResultSet::getInt, PreparedStatement::setInt);
- primitiveMapper(long.class, Long.class, "INTEGER(8)", ResultSet::getLong, PreparedStatement::setLong);
- primitiveMapper(float.class, Float.class, "REAL", ResultSet::getFloat, PreparedStatement::setFloat);
- primitiveMapper(double.class, Double.class, "REAL", ResultSet::getDouble, PreparedStatement::setDouble);
- new SqlTypeMapper<>(String.class, "TEXT", ResultSet::getString, PreparedStatement::setString);
- new SqlTypeMapper<>(Timestamp.class, "TIMESTAMP", ResultSet::getTimestamp, PreparedStatement::setTimestamp);
- new SqlTypeMapper<>(InputStream.class, "BLOB", ResultSet::getBinaryStream, PreparedStatement::setBinaryStream);
- }
-
- private static void primitiveMapper(Class primitive, Class wrapped, String sqlType, SQLReader reader, SQLWriter writer) {
- new SqlTypeMapper<>(primitive, sqlType, reader, writer);
- new SqlTypeMapper<>(wrapped, sqlType, (rs, identifier) -> {
- T value = reader.read(rs, identifier);
- return rs.wasNull() ? null : value;
- }, writer);
- }
-
- private final String sqlType;
- private final SQLReader reader;
- private final SQLWriter writer;
-
- public SqlTypeMapper(Class clazz, String sqlType, SQLReader reader, SQLWriter writer) {
- this.sqlType = sqlType;
- this.reader = reader;
- this.writer = writer;
- mappers.put(clazz, this);
- }
-
- public T read(ResultSet rs, String identifier) throws SQLException {
- return reader.read(rs, identifier);
- }
-
- public void write(PreparedStatement st, int index, Object value) throws SQLException {
- writer.write(st, index, (T) value);
- }
-
- public String sqlType() {
- return sqlType;
- }
-
- @FunctionalInterface
- public interface SQLReader {
- T read(ResultSet rs, String identifier) throws SQLException;
- }
-
- @FunctionalInterface
- public interface SQLWriter {
- void write(PreparedStatement st, int index, T value) throws SQLException;
- }
-}
diff --git a/CommonCore/SQL/src/de/steamwar/sql/internal/Statement.java b/CommonCore/SQL/src/de/steamwar/sql/internal/Statement.java
deleted file mode 100644
index 5bcd3fcf..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/internal/Statement.java
+++ /dev/null
@@ -1,298 +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.internal;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.sql.*;
-import java.util.*;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-import java.util.function.UnaryOperator;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class Statement implements AutoCloseable {
-
- private static final Logger logger = SQLConfig.impl.getLogger();
-
- private static final List statements = new ArrayList<>();
- private static final Deque connections = new ArrayDeque<>();
- private static final int MAX_CONNECTIONS;
- private static final Supplier conProvider;
- static final Consumer> schemaCreator;
- static final String ON_DUPLICATE_KEY;
- static final UnaryOperator upsertWrapper;
- public static final String NULL_SAFE_EQUALS;
-
- private static final boolean MYSQL_MODE;
- private static final boolean PRODUCTION_DATABASE;
-
- static {
- File file = new File(System.getProperty("user.home"), "mysql.properties");
- MYSQL_MODE = file.exists();
-
- if(MYSQL_MODE) {
- Properties properties = new Properties();
- try {
- properties.load(new FileReader(file));
- } catch (IOException e) {
- throw new SecurityException("Could not load SQL connection", e);
- }
-
- String url = "jdbc:mysql://" + properties.getProperty("host") + ":" + properties.getProperty("port") + "/" + properties.getProperty("database") + "?useServerPrepStmts=true";
- String user = properties.getProperty("user");
- String password = properties.getProperty("password");
-
- PRODUCTION_DATABASE = "production".equals(properties.getProperty("database"));
- MAX_CONNECTIONS = SQLConfig.impl.maxConnections();
- conProvider = () -> {
- try {
- return DriverManager.getConnection(url, user, password);
- } catch (SQLException e) {
- throw new SecurityException("Could not create MySQL connection", e);
- }
- };
- schemaCreator = table -> {};
- ON_DUPLICATE_KEY = " ON DUPLICATE KEY UPDATE ";
- upsertWrapper = f -> f + " = VALUES(" + f + ")";
- NULL_SAFE_EQUALS = " <=> ";
- } else {
- Connection connection;
-
- try {
- Class.forName("org.sqlite.JDBC");
- connection = DriverManager.getConnection("jdbc:sqlite:" + System.getProperty("user.home") + "/standalone.db");
- } catch (SQLException | ClassNotFoundException e) {
- throw new SecurityException("Could not create sqlite connection", e);
- }
-
- PRODUCTION_DATABASE = false;
- MAX_CONNECTIONS = 1;
- conProvider = () -> connection;
- schemaCreator = Table::ensureExistanceInSqlite;
- ON_DUPLICATE_KEY = " ON CONFLICT DO UPDATE SET ";
- upsertWrapper = f -> f + " = " + f;
- NULL_SAFE_EQUALS = " IS ";
- }
- }
-
- private static volatile int connectionBudget = MAX_CONNECTIONS;
-
- public static void closeAll() {
- synchronized (connections) {
- while(connectionBudget < MAX_CONNECTIONS) {
- if(connections.isEmpty())
- waitOnConnections();
- else
- closeConnection(aquireConnection());
- }
- }
- }
-
- public static boolean mysqlMode() {
- return MYSQL_MODE;
- }
-
- public static boolean productionDatabase() {
- return PRODUCTION_DATABASE;
- }
-
- private final boolean returnGeneratedKeys;
- private final String sql;
- private final Map cachedStatements = new HashMap<>();
-
- public Statement(String sql) {
- this(sql, false);
- }
-
- public Statement(String sql, boolean returnGeneratedKeys) {
- this.sql = sql;
- this.returnGeneratedKeys = returnGeneratedKeys;
- synchronized (statements) {
- statements.add(this);
- }
- }
-
- public T select(ResultSetUser user, Object... objects) {
- return withConnection(st -> {
- boolean res = st.execute();
- if(!res) {
- throw new SecurityException("No result set for select statement");
- }
- ResultSet rs = st.getResultSet();
- T result = user.use(rs);
- rs.close();
- return result;
- }, objects);
- }
-
- public void update(Object... objects) {
- withConnection(PreparedStatement::executeUpdate, objects);
- }
-
- public int insertGetKey(Object... objects) {
- return withConnection(st -> {
- st.executeUpdate();
- ResultSet rs = st.getGeneratedKeys();
- rs.next();
- return rs.getInt(1);
- }, objects);
- }
-
- public String getSql() {
- return sql;
- }
-
- private T withConnection(SQLRunnable runnable, Object... objects) {
- Connection connection = aquireConnection();
- T result;
-
- try {
- result = tryWithConnection(connection, runnable, objects);
- } catch (Throwable e) {
- if(connectionInvalid(connection)) {
- closeConnection(connection);
-
- return withConnection(runnable, objects);
- } else {
- synchronized (connections) {
- connections.push(connection);
- connections.notify();
- }
-
- throw new SecurityException("Failing sql statement", e);
- }
- }
-
- synchronized (connections) {
- connections.push(connection);
- connections.notify();
- }
-
- return result;
- }
-
- private boolean connectionInvalid(Connection connection) {
- try {
- return connection.isClosed() || !connection.isValid(1);
- } catch (SQLException e) {
- logger.log(Level.INFO, "Could not check SQL connection status", e); // No database logging possible at this state
- return true;
- }
- }
-
- private T tryWithConnection(Connection connection, SQLRunnable runnable, Object... objects) throws SQLException {
- PreparedStatement st = cachedStatements.get(connection);
- if(st == null) {
- if(returnGeneratedKeys)
- st = connection.prepareStatement(sql, java.sql.Statement.RETURN_GENERATED_KEYS);
- else
- st = connection.prepareStatement(sql);
- cachedStatements.put(connection, st);
- }
-
- for (int i = 0; i < objects.length; i++) {
- Object o = objects[i];
- if(o != null)
- SqlTypeMapper.getMapper(o.getClass()).write(st, i+1, o);
- else
- st.setNull(i+1, Types.NULL);
- }
-
- return runnable.run(st);
- }
-
- @Override
- public void close() {
- cachedStatements.values().forEach(st -> closeStatement(st, false));
- cachedStatements.clear();
- synchronized (statements) {
- statements.remove(this);
- }
- }
-
- private void close(Connection connection) {
- PreparedStatement st = cachedStatements.remove(connection);
- if(st != null)
- closeStatement(st, true);
- }
-
- private static Connection aquireConnection() {
- synchronized (connections) {
- while(connections.isEmpty() && connectionBudget <= 0)
- waitOnConnections();
-
- if(!connections.isEmpty()) {
- return connections.pop();
- } else {
- Connection connection = conProvider.get();
- connectionBudget--;
- return connection;
- }
- }
- }
-
- private static void closeConnection(Connection connection) {
- synchronized (statements) {
- for (Statement statement : statements) {
- statement.close(connection);
- }
- }
- try {
- connection.close();
- } catch (SQLException e) {
- logger.log(Level.INFO, "Could not close connection", e);
- }
-
- synchronized (connections) {
- connectionBudget++;
- connections.notify();
- }
- }
-
- private static void waitOnConnections() {
- synchronized (connections) {
- try {
- connections.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
-
- private static void closeStatement(PreparedStatement st, boolean silent) {
- try {
- st.close();
- } catch (SQLException e) {
- if(!silent)
- logger.log(Level.INFO, "Could not close statement", e);
- }
- }
-
- public interface ResultSetUser {
- T use(ResultSet rs) throws SQLException;
- }
-
- private interface SQLRunnable {
- T run(PreparedStatement st) throws SQLException;
- }
-}
diff --git a/CommonCore/SQL/src/de/steamwar/sql/internal/Field.java b/CommonCore/SQL/src/de/steamwar/sql/internal/Statement.kt
similarity index 64%
rename from CommonCore/SQL/src/de/steamwar/sql/internal/Field.java
rename to CommonCore/SQL/src/de/steamwar/sql/internal/Statement.kt
index 512484d8..9ce038bc 100644
--- a/CommonCore/SQL/src/de/steamwar/sql/internal/Field.java
+++ b/CommonCore/SQL/src/de/steamwar/sql/internal/Statement.kt
@@ -17,18 +17,17 @@
* along with this program. If not, see .
*/
-package de.steamwar.sql.internal;
+package de.steamwar.sql.internal
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.jetbrains.exposed.v1.jdbc.name
+import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
-@Target(ElementType.FIELD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Field {
- String[] keys() default {};
- String def() default "";
- boolean nullable() default false;
- boolean autoincrement() default false;
-}
+class Statement {
+ companion object {
+ @JvmStatic
+ fun closeAll() = TransactionManager.defaultDatabase?.let { TransactionManager.closeAndUnregister(it) }
+
+ @JvmStatic
+ fun productionDatabase() = TransactionManager.defaultDatabase?.name == "production"
+ }
+}
\ No newline at end of file
diff --git a/CommonCore/SQL/src/de/steamwar/sql/internal/Table.java b/CommonCore/SQL/src/de/steamwar/sql/internal/Table.java
deleted file mode 100644
index b0f156e4..00000000
--- a/CommonCore/SQL/src/de/steamwar/sql/internal/Table.java
+++ /dev/null
@@ -1,141 +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.internal;
-
-import java.lang.reflect.Constructor;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.*;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-public class Table {
- public static final String PRIMARY = "primary";
-
- final String name;
- final TableField>[] fields;
- private final Map> fieldsByIdentifier = new HashMap<>();
- final Constructor constructor;
-
- private final Map[]> keys;
-
-
- public Table(Class clazz) {
- this(clazz, clazz.getSimpleName());
- }
-
- public Table(Class clazz, String name) {
- this.name = name;
- this.fields = Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Field.class)).map(TableField::new).toArray(TableField[]::new);
- try {
- this.constructor = clazz.getDeclaredConstructor(Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Field.class)).map(java.lang.reflect.Field::getType).toArray(Class[]::new));
- } catch (NoSuchMethodException e) {
- throw new SecurityException(e);
- }
-
- keys = Arrays.stream(fields).flatMap(field -> Arrays.stream(field.field.keys())).distinct().collect(Collectors.toMap(Function.identity(), key -> Arrays.stream(fields).filter(field -> Arrays.asList(field.field.keys()).contains(key)).toArray(TableField[]::new)));
-
- for (TableField> field : fields) {
- fieldsByIdentifier.put(field.identifier.toLowerCase(), field);
- }
-
- Statement.schemaCreator.accept(this);
- }
-
- public SelectStatement select(String name) {
- return selectFields(keyFields(name));
- }
- public SelectStatement selectFields(String... kfields) {
- return new SelectStatement<>(this, kfields);
- }
-
- public Statement update(String name, String... fields) {
- return updateFields(fields, keyFields(name));
- }
-
- public Statement updateField(String field, String... kfields) {
- return updateFields(new String[]{field}, kfields);
- }
-
- public Statement updateFields(String[] fields, String... kfields) {
- return new Statement("UPDATE " + name + " SET " + Arrays.stream(fields).map(f -> f + " = ?").collect(Collectors.joining(", ")) + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
- }
-
- public Statement insert(String name) {
- return insertFields(keyFields(name));
- }
-
- public Statement insertAll() {
- return insertAll(false);
- }
-
- public Statement insertAll(boolean returnGeneratedKeys) {
- return insertFields(returnGeneratedKeys, Arrays.stream(fields).map(f -> f.identifier).toArray(String[]::new));
- }
-
- public Statement insertFields(String... fields) {
- return insertFields(false, fields);
- }
-
- public Statement insertFields(boolean returnGeneratedKeys, String... fields) {
- List nonKeyFields = Arrays.stream(fields).filter(f -> fieldsByIdentifier.get(f.toLowerCase()).field.keys().length == 0).collect(Collectors.toList());
- return new Statement("INSERT INTO " + name + " (" + String.join(", ", fields) + ") VALUES (" + Arrays.stream(fields).map(f -> "?").collect(Collectors.joining(", ")) + ")" + (nonKeyFields.isEmpty() ? "" : Statement.ON_DUPLICATE_KEY + nonKeyFields.stream().map(Statement.upsertWrapper).collect(Collectors.joining(", "))), returnGeneratedKeys);
- }
-
- public Statement delete(String name) {
- return deleteFields(keyFields(name));
- }
-
- public Statement deleteFields(String... kfields) {
- return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
- }
-
- void ensureExistanceInSqlite() {
- try (Statement statement = new Statement(
- "CREATE TABLE IF NOT EXISTS " + name + "(" +
- Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def())).collect(Collectors.joining(", ")) +
- keys.entrySet().stream().map(key -> (key.getKey().equals(PRIMARY) ? ", PRIMARY KEY(" : ", UNIQUE (") + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) +
- ")")) {
- statement.update();
- }
- }
-
- private String[] keyFields(String name) {
- return Arrays.stream(keys.get(name)).map(f -> f.identifier).toArray(String[]::new);
- }
-
- static class TableField {
-
- final String identifier;
-
- final SqlTypeMapper mapper;
- private final Field field;
-
- private TableField(java.lang.reflect.Field field) {
- this.identifier = field.getName();
- this.mapper = SqlTypeMapper.getMapper(field.getType());
- this.field = field.getAnnotation(Field.class);
- }
-
- T read(ResultSet rs) throws SQLException {
- return mapper.read(rs, identifier);
- }
- }
-}
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/PersonalKit.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/PersonalKit.java
index 13b05f37..ec1a9b91 100644
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/PersonalKit.java
+++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/PersonalKit.java
@@ -19,119 +19,79 @@
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 org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.ItemStack;
import java.io.StringReader;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
@AllArgsConstructor
public class PersonalKit {
-
- private static final Table table = new Table<>(PersonalKit.class);
- private static final SelectStatement getKits = table.selectFields("UserID", "GameMode");
- private static final SelectStatement getKit = table.select(Table.PRIMARY);
- private static final SelectStatement getKitInUse = table.selectFields("UserID", "GameMode", "InUse");
- private static final Statement update = table.insertAll();
- private static final Statement delete = table.delete(Table.PRIMARY);
-
- @Getter
- @Field(keys = {Table.PRIMARY})
- private final int userID;
- @Getter
- @Field(keys = {Table.PRIMARY})
- private final String gamemode;
- @Getter
- @Field(keys = {Table.PRIMARY})
- private final String name;
- @Field
- private String inventory;
- @Field
- private String armor;
- @Getter
- @Field(def = "1")
- private boolean inUse;
+ private final InternalKit kit;
public String getRawInventory() {
- return inventory;
+ return kit.getRawInventory();
}
public String getRawArmor() {
- return armor;
+ return kit.getRawArmor();
}
public ItemStack[] getInventory(){
- YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(inventory));
+ YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(getRawInventory()));
return Objects.requireNonNull(config.getList("Inventory")).toArray(new ItemStack[0]);
}
public ItemStack[] getArmor(){
- YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(armor));
+ YamlConfiguration config = YamlConfiguration.loadConfiguration(new StringReader(getRawArmor()));
return Objects.requireNonNull(config.getList("Armor")).toArray(new ItemStack[0]);
}
public void setInUse() {
- PersonalKit kit = getKitInUse(userID, gamemode);
- if(kit != null)
- kit.setUse(false);
- setUse(true);
- }
-
- private void setUse(boolean inUse) {
- this.inUse = inUse;
- update();
+ kit.setDefault();
}
public void setInventory(ItemStack[] inventory) {
- this.inventory = saveInvConfig("Inventory", inventory);
- update();
+ kit.setInventory(saveInvConfig("Inventory", inventory));
}
public void setArmor(ItemStack[] armor) {
- this.armor = saveInvConfig("Armor", armor);
- update();
+ kit.setArmor(saveInvConfig("Armor", armor));
}
public void setContainer(ItemStack[] inventory, ItemStack[] armor) {
- this.armor = saveInvConfig("Armor", armor);
- this.inventory = saveInvConfig("Inventory", inventory);
- update();
+ setInventory(inventory);
+ setArmor(armor);
}
public void delete() {
- delete.update(userID, gamemode, name);
- }
-
- private void update() {
- update.update(userID, gamemode, name, inventory, armor, inUse);
+ kit.delete();
}
public static List get(int userID, String gamemode){
- return getKits.listSelect(userID, gamemode);
+ return InternalKit.get(userID, gamemode).stream().map(PersonalKit::new).collect(Collectors.toList());
}
public static PersonalKit get(int userID, String gamemode, String name) {
- return getKit.select(userID, gamemode, name);
+ InternalKit kit = InternalKit.get(userID, gamemode, name);
+ if(kit == null)
+ return null;
+ return new PersonalKit(kit);
}
public static PersonalKit create(int userID, String gamemode, String name, ItemStack[] inventory, ItemStack[] armor){
- if(armor == null) {
- armor = new ItemStack[]{null, null, null, null};
- }
- PersonalKit kit = new PersonalKit(userID, gamemode, name, saveInvConfig("Inventory", inventory), saveInvConfig("Armor", armor), true);
- kit.update();
- return kit;
+ InternalKit kit = InternalKit.create(userID, gamemode, name, saveInvConfig("Inventory", inventory), saveInvConfig("Armor", armor));
+ return new PersonalKit(kit);
}
public static PersonalKit getKitInUse(int userID, String gamemode) {
- return getKitInUse.select(userID, gamemode, true);
+ InternalKit kit = InternalKit.getKitInUse(userID, gamemode);
+ if(kit == null)
+ return null;
+ return new PersonalKit(kit);
}
private static String saveInvConfig(String name, ItemStack[] inv) {
diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java
index 007c7a2c..72fd070f 100644
--- a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java
+++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SchematicData.java
@@ -20,19 +20,12 @@
package de.steamwar.sql;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
-import de.steamwar.core.Core;
import de.steamwar.core.WorldEditWrapper;
-import de.steamwar.sql.internal.SqlTypeMapper;
-import de.steamwar.sql.internal.Statement;
import org.bukkit.entity.Player;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.PipedInputStream;
-import java.sql.Blob;
-import java.sql.PreparedStatement;
-import java.util.zip.GZIPInputStream;
public class SchematicData {
diff --git a/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java b/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java
index c8150a1b..74efe014 100644
--- a/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java
+++ b/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java
@@ -119,13 +119,6 @@ public class VelocityCore implements ReloadablePlugin {
config = Config.load();
MAIN_SERVER = proxyServer.getBoundAddress().getPort() == 25565;
- try {
- sqlDriver = Statement.mysqlMode() ? new com.mysql.cj.jdbc.Driver() : new org.sqlite.JDBC();
- DriverManager.registerDriver(sqlDriver);
- } catch (SQLException e) {
- throw new SecurityException(e);
- }
-
errorLogger = new ErrorLogger();
SWCommandUtils.init((SWTypeMapperCreator, Chatter, Object>) (mapper, tabCompleter) -> new TypeMapper<>() {
diff --git a/VelocityCore/src/de/steamwar/velocitycore/commands/GDPRQuery.java b/VelocityCore/src/de/steamwar/velocitycore/commands/GDPRQuery.java
deleted file mode 100644
index d5756890..00000000
--- a/VelocityCore/src/de/steamwar/velocitycore/commands/GDPRQuery.java
+++ /dev/null
@@ -1,286 +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.velocitycore.commands;
-
-import de.steamwar.linkage.Linked;
-import de.steamwar.velocitycore.VelocityCore;
-import de.steamwar.command.SWCommand;
-import de.steamwar.messages.Chatter;
-import de.steamwar.messages.PlayerChatter;
-import de.steamwar.sql.SteamwarUser;
-import de.steamwar.sql.UserPerm;
-import de.steamwar.sql.internal.Statement;
-
-import java.io.*;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-@Linked
-public class GDPRQuery extends SWCommand {
-
- public GDPRQuery() {
- super("gdprquery", UserPerm.ADMINISTRATION);
- }
-
- @Register
- public void generate(PlayerChatter sender) {
- generate(sender, sender.user());
- }
-
- @Register
- public void generate(Chatter sender, SteamwarUser user) {
- VelocityCore.schedule(() -> {
- try {
- createZip(sender, user);
- } catch (IOException e) {
- throw new SecurityException("Could not create zip", e);
- }
- }).schedule();
- }
-
- private void createZip(Chatter sender, SteamwarUser user) throws IOException {
- sender.system("GDPR_STATUS_WEBSITE");
-
- ZipOutputStream out = new ZipOutputStream(new FileOutputStream(user.getUserName() + ".zip"));
-
- copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.md");
- copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt");
-
- sender.system("GDPR_STATUS_WORLD");
- copyBauwelt(user, out, "/home/minecraft/userworlds/" + user.getUUID().toString(), "BuildWorld12");
- copyBauwelt(user, out, "/home/minecraft/userworlds15/" + user.getId(), "BuildWorld15");
-
- sender.system("GDPR_STATUS_INVENTORIES");
- copyPlayerdata(user, out, "/home/minecraft/userworlds", "BuildInventories12");
- copyPlayerdata(user, out, "/home/minecraft/userworlds15", "BuildInventories15");
-
- sender.system("GDPR_STATUS_DATABASE");
- sqlCSV(user, out, bannedIPs, "BannedIPs.csv");
- sqlCSV(user, out, bauweltMember, "BuildMember.csv");
- sqlCSV(user, out, bauweltMembers, "BuildMembers.csv");
- sqlCSV(user, out, checkedSchems, "SchematicChecksessions.csv");
- sqlCSV(user, out, userElo, "UserElo.csv");
- sqlCSV(user, out, fights, "Fights.csv");
- sqlCSV(user, out, ignoredPlayers, "IgnoredPlayers.csv");
- sqlCSV(user, out, ignoringPlayers, "IgnoringPlayers.csv");
- sqlCSV(user, out, schematicMember, "SchematicMember.csv");
- sqlCSV(user, out, schematicMembers, "SchematicMembers.csv");
- sqlCSV(user, out, pollAnswers, "PollAnswers.csv");
- sqlCSV(user, out, punishments, "Punishments.csv");
- sqlCSV(user, out, sessions, "Sessions.csv");
- sqlCSV(user, out, userData, "UserData.csv");
- sqlCSV(user, out, personalKits, "PersonalKits.csv");
- sqlCSV(user, out, schematics, "Schematics.csv");
-
- personalKits(user, out);
- schematics(user, out);
- userConfig(user, out);
-
- sender.system("GDPR_STATUS_LOGS");
- copyLogs(user, out, new File("/logs"), "logs");
-
- out.close();
- sender.system("GDPR_STATUS_FINISHED");
- }
-
- private static final Statement bannedIPs = new Statement("SELECT Timestamp, IP FROM BannedUserIPs WHERE UserID = ?");
- private static final Statement bauweltMember = new Statement("SELECT BauweltID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
- private static final Statement bauweltMembers = new Statement("SELECT u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id WHERE m.BauweltID = ?");
- private static final Statement checkedSchems = new Statement("SELECT NodeName AS Schematic, StartTime, EndTime, DeclineReason AS Result FROM CheckedSchematic WHERE NodeOwner = ? ORDER BY StartTime ASC");
- private static final Statement userElo = new Statement("SELECT GameMode, Elo, Season FROM Elo WHERE UserID = ?");
- private static final Statement fights = new Statement("SELECT p.Team AS Team, p.Kit AS Kit, p.Kills AS Kills, p.IsOut AS Died, f.GameMode AS GameMode, f.Server AS Server, f.StartTime AS StartTime, f.Duration AS Duration, (f.BlueLeader = p.UserID) AS IsBlueLeader, (f.RedLeader = p.UserID) AS IsRedLeader, f.Win AS Winner, f.WinCondition AS WinCondition FROM Fight f INNER JOIN FightPlayer p ON f.FightID = p.FightID WHERE p.UserID = ? ORDER BY StartTime ASC");
- private static final Statement ignoredPlayers = new Statement("SELECT u.UserName AS IgnoredPlayer FROM IgnoredPlayers i INNER JOIN UserData u ON i.Ignored = u.id WHERE Ignorer = ?");
- private static final Statement ignoringPlayers = new Statement("SELECT Ignorer AS IgnoringPlayers FROM IgnoredPlayers WHERE Ignored = ?");
- private static final Statement schematicMember = new Statement("SELECT s.NodeName AS SchematicName, u.UserName AS SchematicOwner FROM NodeMember m INNER JOIN SchematicNode s ON m.NodeId = s.NodeId INNER JOIN UserData u ON s.NodeOwner = u.id WHERE m.UserId = ?");
- private static final Statement schematicMembers = new Statement("SELECT s.NodeName AS SchematicName, u.UserName AS Member FROM NodeMember m INNER JOIN SchematicNode s ON m.NodeId = s.NodeId INNER JOIN UserData u ON m.UserId = u.id WHERE s.NodeOwner = ?");
- private static final Statement pollAnswers = new Statement("SELECT Question, Answer FROM PollAnswer WHERE UserID = ?");
- private static final Statement punishments = new Statement("SELECT Type, StartTime, EndTime, Perma, Reason FROM Punishments WHERE UserId = ?");
- private static final Statement sessions = new Statement("SELECT StartTime, EndTime FROM Session WHERE UserID = ?");
- private static final Statement userData = new Statement("SELECT * FROM UserData WHERE id = ?");
- private static final Statement personalKits = new Statement("SELECT GameMode, Name, InUse FROM PersonalKit WHERE UserID = ?");
- private static final Statement personalKitData = new Statement("SELECT GameMode, Name, Inventory, Armor FROM PersonalKit WHERE UserID = ?");
- private static final Statement schematics = new Statement("SELECT NodeName AS SchematicName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank FROM SchematicNode WHERE NodeOwner = ?");
- private static final Statement schematicData = new Statement("SELECT NodeName, ParentNode, NodeFormat, NodeData FROM SchematicNode WHERE NodeOwner = ?");
- private static final Statement userConfig = new Statement("SELECT * FROM UserConfig WHERE User = ?");
-
- private void sqlCSV(SteamwarUser user, ZipOutputStream out, Statement statement, String path) throws IOException {
- write(stream -> statement.select(rs -> {
- try {
- OutputStreamWriter writer = new OutputStreamWriter(stream);
- int columns = rs.getMetaData().getColumnCount();
-
- for(int i = 1; i <= columns; i++) {
- writer.write(rs.getMetaData().getColumnLabel(i));
- writer.write(";");
- }
- writer.write("\n");
-
- while(rs.next()) {
- for(int i = 1; i <= columns; i++) {
- try {
- writer.write(rs.getString(i));
- } catch (NullPointerException e) {
- // ignored
- }
- writer.write(";");
- }
- writer.write("\n");
- }
- writer.flush();
- } catch (IOException e) {
- throw new SecurityException("Could not write file", e);
- }
- return null;
- }, user.getId()), out, path);
- }
-
- private void personalKits(SteamwarUser user, ZipOutputStream out) {
- personalKitData.select(rs -> {
- while(rs.next()) {
- try {
- String path = "PersonalKit/" + rs.getString("GameMode") + "/" + rs.getString("Name");
- try(InputStream data = rs.getBinaryStream("Inventory")) {
- copy(data, out, path + ".Inventory.yml");
- }
- try(InputStream data = rs.getBinaryStream("Armor")) {
- copy(data, out, path + ".Armor.yml");
- }
- } catch (IOException e) {
- throw new SecurityException("Could not export PersonalKits", e);
- }
- }
- return null;
- }, user.getId());
- }
-
- private void schematics(SteamwarUser user, ZipOutputStream out) {
- schematicData.select(rs -> {
- while(rs.next()) {
- String name = (rs.getString("ParentNode") != null ? rs.getString("ParentNode") : "") + ":" + rs.getString("NodeName");
- boolean format = rs.getBoolean("NodeFormat");
- try(InputStream data = rs.getBinaryStream("NodeData")) {
- copy(data, out, "Schematics/" + name + (format ? ".schem" : ".schematic"));
- } catch (IOException e) {
- throw new SecurityException("Could not export Schematic", e);
- }
- }
- return null;
- }, user.getId());
- }
-
- private void userConfig(SteamwarUser user, ZipOutputStream out) {
- userConfig.select(rs -> {
- while(rs.next()) {
- String name = rs.getString("Config");
- try(InputStream data = rs.getBinaryStream("Value")) {
- copy(data, out, name + ".yapion");
- } catch (IOException e) {
- throw new SecurityException("Could not export UserConfig", e);
- }
- }
- return null;
- }, user.getId());
- }
-
- private void copyLogs(SteamwarUser user, ZipOutputStream out, File log, String outFile) throws IOException {
- if (log.isDirectory()) {
- for(File logfile : log.listFiles()) {
- copyLogs(user, out, logfile, outFile + "/" + logfile.getName().replace(".gz", ""));
- }
- } else {
- Process reader = new ProcessBuilder("zgrep", "^.*" + user.getUserName() + "\\( issued server command:\\| moved too quickly!\\| executed command:\\| lost connection:\\||\\|»\\|\\[\\|\\]\\).*$", log.getPath()).start();
- copy(reader.getInputStream(), out, outFile);
- }
- }
-
- private void copyBauwelt(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
- File world = new File(inDir);
- if(!world.exists())
- return;
-
- copy(new File(world, "level.dat"), out, outDir + "/level.dat");
-
- File region = new File(world, "region");
- for(File regionfile : region.listFiles()) {
- copy(regionfile, out, outDir + "/region/" + regionfile.getName());
- }
-
- File poi = new File(world, "poi");
- if(poi.exists()) {
- for(File regionfile : poi.listFiles()) {
- copy(regionfile, out, outDir + "/poi/" + regionfile.getName());
- }
- }
-
- File playerdata = new File(world, "playerdata/" + user.getUUID().toString() + ".dat");
- if(playerdata.exists())
- copy(playerdata, out, outDir + "/playerdata/" + user.getUUID().toString() + ".dat");
- }
-
- private void copyPlayerdata(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
- File worlds = new File(inDir);
- String path = "playerdata/" + user.getUUID().toString() + ".dat";
-
- int i = 0;
- for(File world : worlds.listFiles()) {
- File playerdata = new File(world, path);
- if(!playerdata.exists())
- continue;
-
- copy(playerdata, out, outDir + "/" + (i++) + "/" + user.getUUID().toString() + ".dat");
- }
- }
-
- private void copy(File file, ZipOutputStream out, String path) throws IOException {
- try(FileInputStream in = new FileInputStream(file)) {
- copy(in, out, path);
- }
- }
-
- private void copy(InputStream in, ZipOutputStream out, String path) throws IOException {
- boolean initialized = false;
-
- int bytes;
- for(byte[] buf = new byte[8192]; (bytes = in.read(buf)) > 0; ) {
- if(!initialized) {
- ZipEntry entry = new ZipEntry(path);
- out.putNextEntry(entry);
- initialized = true;
- }
-
- out.write(buf, 0, bytes);
- }
-
- if(initialized) {
- out.closeEntry();
- }
- }
-
- private void write(Writer writer, ZipOutputStream out, String path) throws IOException {
- ZipEntry entry = new ZipEntry(path);
- out.putNextEntry(entry);
- writer.accept(out);
- out.closeEntry();
- }
-
- private interface Writer {
- void accept(OutputStream stream) throws IOException;
- }
-}
diff --git a/VelocityCore/src/de/steamwar/velocitycore/commands/RankCommand.java b/VelocityCore/src/de/steamwar/velocitycore/commands/RankCommand.java
index b0b3e944..49ac0a9e 100644
--- a/VelocityCore/src/de/steamwar/velocitycore/commands/RankCommand.java
+++ b/VelocityCore/src/de/steamwar/velocitycore/commands/RankCommand.java
@@ -52,11 +52,11 @@ public class RankCommand extends SWCommand {
if (!mode.Server.Ranked)
continue;
- Optional elo = UserElo.getElo(user.getId(), mode.getSchemTypeOrInternalName());
+ Integer elo = UserElo.getElo(user.getId(), mode.getSchemTypeOrInternalName());
Message eloMsg;
- if (elo.isPresent()) {
- int placement = UserElo.getPlacement(elo.get(), mode.getSchemTypeOrInternalName());
- eloMsg = new Message("RANK_PLACED", placement, elo.get());
+ if (elo != null) {
+ int placement = UserElo.getPlacement(elo, mode.getSchemTypeOrInternalName());
+ eloMsg = new Message("RANK_PLACED", placement, elo);
} else {
eloMsg = new Message("RANK_UNPLACED");
}
diff --git a/WebsiteBackend/src/de/steamwar/sql/Stats.kt b/WebsiteBackend/src/de/steamwar/sql/Stats.kt
index 0080a58b..9cb546dc 100644
--- a/WebsiteBackend/src/de/steamwar/sql/Stats.kt
+++ b/WebsiteBackend/src/de/steamwar/sql/Stats.kt
@@ -19,49 +19,70 @@
package de.steamwar.sql
-import de.steamwar.sql.internal.Statement
-import de.steamwar.sql.internal.Statement.ResultSetUser
+import de.steamwar.sql.internal.useDb
+import org.jetbrains.exposed.v1.core.IntegerColumnType
+import org.jetbrains.exposed.v1.core.VarCharColumnType
+import java.sql.ResultSet
-private val getNum: ResultSetUser = ResultSetUser { res ->
- if (res.next()) {
- res.getInt("num")
- } else {
- null
- }
+private val getNum: (ResultSet) -> Int? = {
+ if (it.next()) it.getInt("num") else null
}
-private val eventFightParticipation = Statement("SELECT FightPlayer.UserID, COUNT(UserID) as num FROM FightPlayer INNER JOIN Fight on FightPlayer.FightID = Fight.FightID WHERE Fight.Server LIKE '%vs%' AND FightPlayer.UserID = ? GROUP BY FightPlayer.UserID")
+private const val eventFightParticipation =
+ "SELECT FightPlayer.UserID, COUNT(UserID) as num FROM FightPlayer INNER JOIN Fight on FightPlayer.FightID = Fight.FightID WHERE Fight.Server LIKE '%vs%' AND FightPlayer.UserID = ? GROUP BY FightPlayer.UserID"
-fun getEventFightParticipation(user: SteamwarUser): Int? = eventFightParticipation.select(getNum, user.id)
+fun getEventFightParticipation(user: SteamwarUser) = useDb {
+ exec(
+ eventFightParticipation, args = listOf(
+ IntegerColumnType() to user.getId()
+ )
+ ) { getNum(it) }
+}
-private val eventParticipation = Statement("SELECT FightPlayer.UserID, COUNT(DISTINCT EventID) as num FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID INNER JOIN EventFight EF on F.FightID = EF.Fight WHERE F.FightID = FightPlayer.FightID AND FightPlayer.FightID = EF.Fight AND F.Server LIKE '%vs%' AND FightPlayer.UserID = ? GROUP BY FightPlayer.UserID")
+private const val eventParticipation =
+ "SELECT FightPlayer.UserID, COUNT(DISTINCT EventID) as num FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID INNER JOIN EventFight EF on F.FightID = EF.Fight WHERE F.FightID = FightPlayer.FightID AND FightPlayer.FightID = EF.Fight AND F.Server LIKE '%vs%' AND FightPlayer.UserID = ? GROUP BY FightPlayer.UserID"
-fun getEventParticipation(user: SteamwarUser): Int? = eventParticipation.select(getNum, user.id)
+fun getEventParticipation(user: SteamwarUser) = useDb {
+ exec(
+ eventParticipation, args = listOf(
+ IntegerColumnType() to user.getId()
+ )
+ ) { getNum(it) }
+}
-private val acceptedSchematics = Statement("SELECT NodeOwner, COUNT(DISTINCT NodeId) AS num FROM SchematicNode WHERE NodeType != 'normal' AND NodeType IS NOT NULL AND NodeType NOT LIKE 'c%' AND NodeOwner = ?")
+private const val acceptedSchematics =
+ "SELECT NodeOwner, COUNT(DISTINCT NodeId) AS num FROM SchematicNode WHERE NodeType != 'normal' AND NodeType IS NOT NULL AND NodeType NOT LIKE 'c%' AND NodeOwner = ?"
-fun getAcceptedSchematics(user: SteamwarUser): Int? = acceptedSchematics.select(getNum, user.id)
+fun getAcceptedSchematics(user: SteamwarUser) =
+ useDb { exec(acceptedSchematics, args = listOf(IntegerColumnType() to user.getId())) { getNum(it) } }
-private val fightCount = Statement("SELECT COUNT(*) AS num FROM FightPlayer WHERE UserID = ?")
+private val fightCount = "SELECT COUNT(*) AS num FROM FightPlayer WHERE UserID = ?"
-fun getFightCount(user: SteamwarUser): Int? = fightCount.select(getNum, user.id)
+fun getFightCount(user: SteamwarUser) =
+ useDb { exec(fightCount, args = listOf(IntegerColumnType() to user.getId())) { getNum(it) } }
-private val rankedList = Statement("SELECT UserName, Elo FROM UserData, UserElo WHERE UserID = id AND GameMode = ? AND Season = ? ORDER BY Elo DESC")
+private const val rankedList =
+ "SELECT UserName, Elo FROM UserData, UserElo WHERE UserID = id AND GameMode = ? AND Season = ? ORDER BY Elo DESC"
-fun getRankedList(gamemode: String): List> = rankedList.select({ res ->
- val list = mutableListOf>()
- while (res.next()) {
- list.add(res.getString("UserName") to res.getInt("Elo"))
+fun getRankedList(gamemode: String): List> = useDb {
+ exec(rankedList, args = listOf(VarCharColumnType() to gamemode, IntegerColumnType() to Season.getSeason())) {
+ val list = mutableListOf>()
+ while (it.next()) {
+ list.add(it.getString("UserName") to it.getInt("Elo"))
+ }
+ list
+ } ?: emptyList()
+}
+
+private const val fightList =
+ "SELECT DATE(StartTime) AS Datum, GameMode AS Modus, COUNT(*) AS Anzahl FROM Fight WHERE DATE(StartTime) >= DATE(NOW()) - INTERVAL 1 WEEK GROUP BY Datum, GameMode ORDER BY Datum ASC"
+
+fun getFightList(): List> = useDb {
+ exec(fightList) {
+ val list = mutableListOf>()
+ while (it.next()) {
+ list.add(Triple(it.getString("Datum"), it.getString("Modus"), it.getInt("Anzahl")))
+ }
+ list
}
- list
-}, gamemode, Season.getSeason())
-
-private val fightList = Statement("SELECT DATE(StartTime) AS Datum, GameMode AS Modus, COUNT(*) AS Anzahl FROM Fight WHERE DATE(StartTime) >= DATE(NOW()) - INTERVAL 1 WEEK GROUP BY Datum, GameMode ORDER BY Datum ASC")
-
-fun getFightList(): List> = fightList.select({ res ->
- val list = mutableListOf>()
- while (res.next()) {
- list.add(Triple(res.getString("Datum"), res.getString("Modus"), res.getInt("Anzahl")))
- }
- list
-})
+} ?: emptyList()