forked from SteamWar/SteamWar
Remove outdated SQL-related Java classes (GDPRQuery, Field, SelectStatement, SqlTypeMapper, Statement) and update references across modules.
Signed-off-by: Chaoscaot <max@maxsp.de>
This commit is contained in:
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<CompositeID>): CompositeEntity(id) {
|
||||
companion object: CompositeEntityClass<InternalKit>(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()
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class UserElo {
|
||||
|
||||
private static final int ELO_DEFAULT = 0;
|
||||
|
||||
private static final Map<String, Map<Integer, Optional<Integer>>> gameModeUserEloCache = new ConcurrentHashMap<>();
|
||||
private static final Map<Integer, String> emblemCache = new ConcurrentHashMap<>();
|
||||
|
||||
public static void clear() {
|
||||
gameModeUserEloCache.clear();
|
||||
emblemCache.clear();
|
||||
}
|
||||
|
||||
private static final Table<UserElo> table = new Table<>(UserElo.class);
|
||||
private static final SelectStatement<UserElo> 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<Integer> 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<String> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<CompositeID>) : CompositeEntity(id) {
|
||||
companion object : CompositeEntityClass<UserElo>(UserEloTable) {
|
||||
private const val ELO_DEFAULT = 0
|
||||
|
||||
private val gameModeUserEloCache: MutableMap<String, MutableMap<Int, Int?>> = ConcurrentHashMap()
|
||||
private val emblemCache: MutableMap<Int, String> = 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<String>) =
|
||||
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
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<T> extends Statement {
|
||||
private final Table<T> table;
|
||||
|
||||
SelectStatement(Table<T> 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<T> 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<T> listSelect(Object... values) {
|
||||
return select(rs -> {
|
||||
List<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<T> {
|
||||
private static final Map<Class<?>, SqlTypeMapper<?>> mappers = new IdentityHashMap<>();
|
||||
|
||||
public static <T> SqlTypeMapper<T> getMapper(Class<?> clazz) {
|
||||
SqlTypeMapper<T> result = (SqlTypeMapper<T>) mappers.get(clazz);
|
||||
|
||||
if(result == null)
|
||||
throw new SecurityException("Unregistered mapper requested: " + clazz.getName());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends Enum<T>> void ordinalEnumMapper(Class<T> 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 <T extends Enum<T>> void nameEnumMapper(Class<T> 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 <T> void primitiveMapper(Class<T> primitive, Class<T> wrapped, String sqlType, SQLReader<T> reader, SQLWriter<T> 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<T> reader;
|
||||
private final SQLWriter<T> writer;
|
||||
|
||||
public SqlTypeMapper(Class<T> clazz, String sqlType, SQLReader<T> reader, SQLWriter<T> 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> {
|
||||
T read(ResultSet rs, String identifier) throws SQLException;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SQLWriter<T> {
|
||||
void write(PreparedStatement st, int index, T value) throws SQLException;
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Statement> statements = new ArrayList<>();
|
||||
private static final Deque<Connection> connections = new ArrayDeque<>();
|
||||
private static final int MAX_CONNECTIONS;
|
||||
private static final Supplier<Connection> conProvider;
|
||||
static final Consumer<Table<?>> schemaCreator;
|
||||
static final String ON_DUPLICATE_KEY;
|
||||
static final UnaryOperator<String> 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<Connection, PreparedStatement> 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> T select(ResultSetUser<T> 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> T withConnection(SQLRunnable<T> 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> T tryWithConnection(Connection connection, SQLRunnable<T> 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> {
|
||||
T use(ResultSet rs) throws SQLException;
|
||||
}
|
||||
|
||||
private interface SQLRunnable<T> {
|
||||
T run(PreparedStatement st) throws SQLException;
|
||||
}
|
||||
}
|
||||
+11
-12
@@ -17,18 +17,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<T> {
|
||||
public static final String PRIMARY = "primary";
|
||||
|
||||
final String name;
|
||||
final TableField<?>[] fields;
|
||||
private final Map<String, TableField<?>> fieldsByIdentifier = new HashMap<>();
|
||||
final Constructor<T> constructor;
|
||||
|
||||
private final Map<String, TableField<?>[]> keys;
|
||||
|
||||
|
||||
public Table(Class<T> clazz) {
|
||||
this(clazz, clazz.getSimpleName());
|
||||
}
|
||||
|
||||
public Table(Class<T> 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<T> select(String name) {
|
||||
return selectFields(keyFields(name));
|
||||
}
|
||||
public SelectStatement<T> 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<String> 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<T> {
|
||||
|
||||
final String identifier;
|
||||
|
||||
final SqlTypeMapper<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PersonalKit> table = new Table<>(PersonalKit.class);
|
||||
private static final SelectStatement<PersonalKit> getKits = table.selectFields("UserID", "GameMode");
|
||||
private static final SelectStatement<PersonalKit> getKit = table.select(Table.PRIMARY);
|
||||
private static final SelectStatement<PersonalKit> 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<PersonalKit> 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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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<TypeMapper<Object>, Chatter, Object>) (mapper, tabCompleter) -> new TypeMapper<>() {
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -52,11 +52,11 @@ public class RankCommand extends SWCommand {
|
||||
if (!mode.Server.Ranked)
|
||||
continue;
|
||||
|
||||
Optional<Integer> 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");
|
||||
}
|
||||
|
||||
@@ -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<Int?> = 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<Pair<String, Int>> = rankedList.select({ res ->
|
||||
val list = mutableListOf<Pair<String, Int>>()
|
||||
while (res.next()) {
|
||||
list.add(res.getString("UserName") to res.getInt("Elo"))
|
||||
fun getRankedList(gamemode: String): List<Pair<String, Int>> = useDb {
|
||||
exec(rankedList, args = listOf(VarCharColumnType() to gamemode, IntegerColumnType() to Season.getSeason())) {
|
||||
val list = mutableListOf<Pair<String, Int>>()
|
||||
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<Triple<String, String, Int>> = useDb {
|
||||
exec(fightList) {
|
||||
val list = mutableListOf<Triple<String, String, Int>>()
|
||||
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<Triple<String, String, Int>> = fightList.select({ res ->
|
||||
val list = mutableListOf<Triple<String, String, Int>>()
|
||||
while (res.next()) {
|
||||
list.add(Triple(res.getString("Datum"), res.getString("Modus"), res.getInt("Anzahl")))
|
||||
}
|
||||
list
|
||||
})
|
||||
} ?: emptyList()
|
||||
|
||||
Reference in New Issue
Block a user