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()