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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+12
-13
@@ -17,18 +17,17 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 org.jetbrains.exposed.v1.jdbc.name
|
||||||
import java.lang.annotation.Retention;
|
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Target(ElementType.FIELD)
|
class Statement {
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
companion object {
|
||||||
public @interface Field {
|
@JvmStatic
|
||||||
String[] keys() default {};
|
fun closeAll() = TransactionManager.defaultDatabase?.let { TransactionManager.closeAndUnregister(it) }
|
||||||
String def() default "";
|
|
||||||
boolean nullable() default false;
|
@JvmStatic
|
||||||
boolean autoincrement() default false;
|
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;
|
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.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class PersonalKit {
|
public class PersonalKit {
|
||||||
|
private final InternalKit kit;
|
||||||
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;
|
|
||||||
|
|
||||||
public String getRawInventory() {
|
public String getRawInventory() {
|
||||||
return inventory;
|
return kit.getRawInventory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRawArmor() {
|
public String getRawArmor() {
|
||||||
return armor;
|
return kit.getRawArmor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack[] getInventory(){
|
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]);
|
return Objects.requireNonNull(config.getList("Inventory")).toArray(new ItemStack[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack[] getArmor(){
|
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]);
|
return Objects.requireNonNull(config.getList("Armor")).toArray(new ItemStack[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInUse() {
|
public void setInUse() {
|
||||||
PersonalKit kit = getKitInUse(userID, gamemode);
|
kit.setDefault();
|
||||||
if(kit != null)
|
|
||||||
kit.setUse(false);
|
|
||||||
setUse(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUse(boolean inUse) {
|
|
||||||
this.inUse = inUse;
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInventory(ItemStack[] inventory) {
|
public void setInventory(ItemStack[] inventory) {
|
||||||
this.inventory = saveInvConfig("Inventory", inventory);
|
kit.setInventory(saveInvConfig("Inventory", inventory));
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setArmor(ItemStack[] armor) {
|
public void setArmor(ItemStack[] armor) {
|
||||||
this.armor = saveInvConfig("Armor", armor);
|
kit.setArmor(saveInvConfig("Armor", armor));
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContainer(ItemStack[] inventory, ItemStack[] armor) {
|
public void setContainer(ItemStack[] inventory, ItemStack[] armor) {
|
||||||
this.armor = saveInvConfig("Armor", armor);
|
setInventory(inventory);
|
||||||
this.inventory = saveInvConfig("Inventory", inventory);
|
setArmor(armor);
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
delete.update(userID, gamemode, name);
|
kit.delete();
|
||||||
}
|
|
||||||
|
|
||||||
private void update() {
|
|
||||||
update.update(userID, gamemode, name, inventory, armor, inUse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<PersonalKit> get(int userID, String gamemode){
|
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) {
|
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){
|
public static PersonalKit create(int userID, String gamemode, String name, ItemStack[] inventory, ItemStack[] armor){
|
||||||
if(armor == null) {
|
InternalKit kit = InternalKit.create(userID, gamemode, name, saveInvConfig("Inventory", inventory), saveInvConfig("Armor", armor));
|
||||||
armor = new ItemStack[]{null, null, null, null};
|
return new PersonalKit(kit);
|
||||||
}
|
|
||||||
PersonalKit kit = new PersonalKit(userID, gamemode, name, saveInvConfig("Inventory", inventory), saveInvConfig("Armor", armor), true);
|
|
||||||
kit.update();
|
|
||||||
return kit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PersonalKit getKitInUse(int userID, String gamemode) {
|
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) {
|
private static String saveInvConfig(String name, ItemStack[] inv) {
|
||||||
|
|||||||
@@ -20,19 +20,12 @@
|
|||||||
package de.steamwar.sql;
|
package de.steamwar.sql;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
import de.steamwar.core.Core;
|
|
||||||
import de.steamwar.core.WorldEditWrapper;
|
import de.steamwar.core.WorldEditWrapper;
|
||||||
import de.steamwar.sql.internal.SqlTypeMapper;
|
|
||||||
import de.steamwar.sql.internal.Statement;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PipedInputStream;
|
|
||||||
import java.sql.Blob;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
public class SchematicData {
|
public class SchematicData {
|
||||||
|
|
||||||
|
|||||||
@@ -119,13 +119,6 @@ public class VelocityCore implements ReloadablePlugin {
|
|||||||
config = Config.load();
|
config = Config.load();
|
||||||
MAIN_SERVER = proxyServer.getBoundAddress().getPort() == 25565;
|
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();
|
errorLogger = new ErrorLogger();
|
||||||
|
|
||||||
SWCommandUtils.init((SWTypeMapperCreator<TypeMapper<Object>, Chatter, Object>) (mapper, tabCompleter) -> new TypeMapper<>() {
|
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)
|
if (!mode.Server.Ranked)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Optional<Integer> elo = UserElo.getElo(user.getId(), mode.getSchemTypeOrInternalName());
|
Integer elo = UserElo.getElo(user.getId(), mode.getSchemTypeOrInternalName());
|
||||||
Message eloMsg;
|
Message eloMsg;
|
||||||
if (elo.isPresent()) {
|
if (elo != null) {
|
||||||
int placement = UserElo.getPlacement(elo.get(), mode.getSchemTypeOrInternalName());
|
int placement = UserElo.getPlacement(elo, mode.getSchemTypeOrInternalName());
|
||||||
eloMsg = new Message("RANK_PLACED", placement, elo.get());
|
eloMsg = new Message("RANK_PLACED", placement, elo);
|
||||||
} else {
|
} else {
|
||||||
eloMsg = new Message("RANK_UNPLACED");
|
eloMsg = new Message("RANK_UNPLACED");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,49 +19,70 @@
|
|||||||
|
|
||||||
package de.steamwar.sql
|
package de.steamwar.sql
|
||||||
|
|
||||||
import de.steamwar.sql.internal.Statement
|
import de.steamwar.sql.internal.useDb
|
||||||
import de.steamwar.sql.internal.Statement.ResultSetUser
|
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 ->
|
private val getNum: (ResultSet) -> Int? = {
|
||||||
if (res.next()) {
|
if (it.next()) it.getInt("num") else null
|
||||||
res.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 ->
|
fun getRankedList(gamemode: String): List<Pair<String, Int>> = useDb {
|
||||||
val list = mutableListOf<Pair<String, Int>>()
|
exec(rankedList, args = listOf(VarCharColumnType() to gamemode, IntegerColumnType() to Season.getSeason())) {
|
||||||
while (res.next()) {
|
val list = mutableListOf<Pair<String, Int>>()
|
||||||
list.add(res.getString("UserName") to res.getInt("Elo"))
|
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
|
} ?: emptyList()
|
||||||
}, 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
|
|
||||||
})
|
|
||||||
|
|||||||
Reference in New Issue
Block a user