forked from SteamWar/SteamWar
Starting...
This commit is contained in:
@@ -19,10 +19,51 @@
|
||||
|
||||
plugins {
|
||||
steamwar.java
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(8)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs("src/")
|
||||
exclude("**/*.kt")
|
||||
}
|
||||
kotlin {
|
||||
srcDirs("src/")
|
||||
exclude("**/*.java")
|
||||
}
|
||||
resources {
|
||||
srcDirs("src/")
|
||||
exclude("**/*.java", "**/*.kt")
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs("testsrc/")
|
||||
exclude("**/*.kt")
|
||||
}
|
||||
kotlin {
|
||||
srcDirs("testsrc/")
|
||||
exclude("**/*.java")
|
||||
}
|
||||
resources {
|
||||
srcDirs("testsrc/")
|
||||
exclude("**/*.java", "**/*.kt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.sqlite)
|
||||
|
||||
implementation("org.yaml:snakeyaml:2.2")
|
||||
|
||||
api(libs.exposedCore)
|
||||
api(libs.exposedDao)
|
||||
api(libs.exposedJdbc)
|
||||
api(libs.exposedTime)
|
||||
}
|
||||
@@ -17,109 +17,105 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.sql;
|
||||
package de.steamwar.sql
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SqlTypeMapper;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import de.steamwar.sql.internal.useDb
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.v1.javatime.timestamp
|
||||
import java.time.Instant
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
|
||||
val time = timestamp("Time")
|
||||
val server = varchar("ServerName", 255)
|
||||
val serverOwner = integer("ServerOwner").nullable()
|
||||
val actor = integer("Actor")
|
||||
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
|
||||
val actionText = text("ActionText")
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public class AuditLog {
|
||||
class AuditLog(id: EntityID<Int>): IntEntity(id) {
|
||||
companion object: IntEntityClass<AuditLog>(AuditLogTable) {
|
||||
const val SERVER_NAME_VELOCITY: String = "Velocity"
|
||||
|
||||
static {
|
||||
SqlTypeMapper.nameEnumMapper(AuditLog.Type.class);
|
||||
private fun create(
|
||||
serverName: String,
|
||||
serverOwner: SteamwarUser?,
|
||||
actor: SteamwarUser,
|
||||
actionType: Type,
|
||||
text: String = ""
|
||||
) = useDb {
|
||||
new {
|
||||
this.time = Instant.now()
|
||||
this.server = serverName
|
||||
this.serverOwner = serverOwner?.id?.value
|
||||
this.actor = actor.id.value
|
||||
this.action = actionType
|
||||
this.actionText = text
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createJoin(jointServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(jointServerName, serverOwner, joinedPlayer, Type.JOIN)
|
||||
|
||||
@JvmStatic
|
||||
fun createLeave(leftServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(leftServerName, serverOwner, joinedPlayer, Type.LEAVE)
|
||||
|
||||
@JvmStatic
|
||||
fun createCommand(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser?, command: String) = player?.let { create(serverName, serverOwner, it, Type.COMMAND, command) }
|
||||
|
||||
@JvmStatic
|
||||
fun createSensitiveCommand(
|
||||
serverName: String,
|
||||
serverOwner: SteamwarUser?,
|
||||
player: SteamwarUser?,
|
||||
command: String
|
||||
) = player?.let { create(serverName, serverOwner, it, Type.SENSITIVE_COMMAND, command) }
|
||||
|
||||
@JvmStatic
|
||||
fun createChat(serverName: String, serverOwner: SteamwarUser?, chatter: SteamwarUser, chat: String) = create(serverName, serverOwner, chatter, Type.CHAT, chat)
|
||||
|
||||
@JvmStatic
|
||||
fun createGuiOpen(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_OPEN, guiName)
|
||||
|
||||
@JvmStatic
|
||||
fun createGuiClick(
|
||||
serverName: String,
|
||||
serverOwner: SteamwarUser?,
|
||||
player: SteamwarUser,
|
||||
guiName: String,
|
||||
clickType: String,
|
||||
slot: Int,
|
||||
itemName: String
|
||||
) = create(
|
||||
serverName,
|
||||
serverOwner,
|
||||
player,
|
||||
Type.GUI_CLICK,
|
||||
"Gui: $guiName\nSlot: $slot\nClickType: $clickType\nItemName: $itemName"
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun createGuiClose(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_CLOSE, guiName)
|
||||
}
|
||||
|
||||
public static final String SERVER_NAME_VELOCITY = "Velocity";
|
||||
var time by AuditLogTable.time
|
||||
var server by AuditLogTable.server
|
||||
var serverOwner by AuditLogTable.serverOwner
|
||||
var actor by AuditLogTable.actor
|
||||
var action by AuditLogTable.action
|
||||
var actionText by AuditLogTable.actionText
|
||||
|
||||
private static final Table<AuditLog> table = new Table<>(AuditLog.class);
|
||||
|
||||
private static final Statement create = table.insertFields(true, "time", "serverName", "serverOwner", "actor", "actionType", "actionText");
|
||||
|
||||
@Getter
|
||||
@Field
|
||||
private final Timestamp time;
|
||||
|
||||
@Getter
|
||||
@Field
|
||||
private final String serverName;
|
||||
|
||||
@Field(nullable = true)
|
||||
private final int serverOwner;
|
||||
|
||||
@Field
|
||||
private final int actor;
|
||||
|
||||
@Getter
|
||||
@Field
|
||||
private final Type actionType;
|
||||
|
||||
@Getter
|
||||
@Field
|
||||
private final String actionText;
|
||||
|
||||
public enum Type {
|
||||
enum class Type {
|
||||
JOIN,
|
||||
LEAVE,
|
||||
COMMAND,
|
||||
SENSITIVE_COMMAND,
|
||||
|
||||
CHAT,
|
||||
GUI_OPEN,
|
||||
GUI_CLOSE,
|
||||
GUI_CLICK,
|
||||
}
|
||||
|
||||
private static void create(String serverName, SteamwarUser serverOwner, SteamwarUser actor, Type actionType, String text) {
|
||||
create.insertGetKey(Timestamp.from(Instant.now()), serverName, serverOwner, actor, actionType, text);
|
||||
}
|
||||
|
||||
public static void createJoin(@NonNull String jointServerName, SteamwarUser serverOwner, @NonNull SteamwarUser joinedPlayer) {
|
||||
create(jointServerName, serverOwner, joinedPlayer, Type.JOIN, "");
|
||||
}
|
||||
|
||||
public static void createLeave(@NonNull String leftServerName, SteamwarUser serverOwner, @NonNull SteamwarUser joinedPlayer) {
|
||||
create(leftServerName, serverOwner, joinedPlayer, Type.LEAVE, "");
|
||||
}
|
||||
|
||||
public static void createCommand(@NonNull String serverName, SteamwarUser serverOwner, SteamwarUser player, @NonNull String command) {
|
||||
if (player == null) return;
|
||||
create(serverName, serverOwner, player, Type.COMMAND, command);
|
||||
}
|
||||
|
||||
public static void createSensitiveCommand(@NonNull String serverName, SteamwarUser serverOwner, SteamwarUser player, @NonNull String command) {
|
||||
if (player == null) return;
|
||||
create(serverName, serverOwner, player, Type.SENSITIVE_COMMAND, command);
|
||||
}
|
||||
|
||||
public static void createChat(@NonNull String serverName, SteamwarUser serverOwner, @NonNull SteamwarUser chatter, @NonNull String chat) {
|
||||
create(serverName, serverOwner, chatter, Type.CHAT, chat);
|
||||
}
|
||||
|
||||
public static void createGuiOpen(@NonNull String serverName, SteamwarUser serverOwner, @NonNull SteamwarUser player, @NonNull String guiName) {
|
||||
create(serverName, serverOwner, player, Type.GUI_OPEN, guiName);
|
||||
}
|
||||
|
||||
public static void createGuiClick(@NonNull String serverName, SteamwarUser serverOwner, @NonNull SteamwarUser player, @NonNull String guiName, @NonNull String clickType, int slot, @NonNull String itemName) {
|
||||
create(serverName, serverOwner, player, Type.GUI_CLICK, "Gui: " + guiName + "\nSlot: " + slot + "\nClickType: " + clickType + "\nItemName: " + itemName);
|
||||
}
|
||||
|
||||
public static void createGuiClose(@NonNull String serverName, SteamwarUser serverOwner, @NonNull SteamwarUser player, @NonNull String guiName) {
|
||||
create(serverName, serverOwner, player, Type.GUI_CLOSE, guiName);
|
||||
}
|
||||
|
||||
public SteamwarUser getServerOwner() {
|
||||
return SteamwarUser.get(serverOwner);
|
||||
}
|
||||
|
||||
public SteamwarUser getActor() {
|
||||
return SteamwarUser.get(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,51 +17,62 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.Getter;
|
||||
import de.steamwar.sql.internal.useDb
|
||||
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
|
||||
import org.jetbrains.exposed.v1.javatime.timestamp
|
||||
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||
import java.sql.Timestamp
|
||||
import java.time.Instant
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") {
|
||||
val userId = reference("UserID", SteamwarUserTable)
|
||||
val timestamp = timestamp("Timestamp")
|
||||
val ip = varchar("IP", 45)
|
||||
|
||||
@AllArgsConstructor
|
||||
public class BannedUserIPs {
|
||||
|
||||
private static final Table<BannedUserIPs> table = new Table<>(BannedUserIPs.class);
|
||||
|
||||
private static final SelectStatement<BannedUserIPs> getByID = table.selectFields("UserID");
|
||||
private static final SelectStatement<BannedUserIPs> getByIP = new SelectStatement<>(table, "SELECT * FROM BannedUserIPs WHERE IP = ? ORDER BY Timestamp DESC");
|
||||
private static final Statement banIP = table.insertAll();
|
||||
private static final Statement unbanIPs = table.deleteFields("UserID");
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int userID;
|
||||
@Getter
|
||||
@Field(def = "CURRENT_TIMESTAMP")
|
||||
private final Timestamp timestamp;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final String ip;
|
||||
|
||||
public static List<BannedUserIPs> get(int userID) {
|
||||
return getByID.listSelect(userID);
|
||||
}
|
||||
|
||||
public static List<BannedUserIPs> get(String ip) {
|
||||
return getByIP.listSelect(ip);
|
||||
}
|
||||
|
||||
public static void banIP(int userID, String ip){
|
||||
banIP.update(userID, Timestamp.from(Instant.now()), ip);
|
||||
}
|
||||
|
||||
public static void unbanIPs(int userID) {
|
||||
unbanIPs.update(userID);
|
||||
}
|
||||
override val primaryKey = PrimaryKey(userId, ip)
|
||||
}
|
||||
|
||||
class BannedUserIPs(id: EntityID<CompositeID>): CompositeEntity(id) {
|
||||
companion object: CompositeEntityClass<BannedUserIPs>(BannedUserIPsTable) {
|
||||
|
||||
@JvmStatic
|
||||
fun get(userId: Int) = useDb {
|
||||
find { BannedUserIPsTable.userId eq userId }.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun get(ip: String) = useDb {
|
||||
find { BannedUserIPsTable.ip eq ip }.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun banIP(userId: Int, ip: String) = useDb {
|
||||
BannedUserIPsTable.insertIgnore {
|
||||
it[BannedUserIPsTable.userId] = userId
|
||||
it[BannedUserIPsTable.ip] = ip
|
||||
it[BannedUserIPsTable.timestamp] = Instant.now()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun unbanIPs(userId: Int) = useDb {
|
||||
BannedUserIPsTable.deleteWhere { BannedUserIPsTable.userId eq userId }
|
||||
}
|
||||
}
|
||||
|
||||
val userID by BannedUserIPsTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
|
||||
val timestamp: Timestamp by BannedUserIPsTable.timestamp.transform({ it.toInstant() }, { Timestamp.from(it) })
|
||||
val ip by BannedUserIPsTable.ip
|
||||
|
||||
fun remove() = useDb {
|
||||
delete()
|
||||
}
|
||||
}
|
||||
@@ -17,107 +17,109 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.Getter;
|
||||
import de.steamwar.sql.BauweltMemberTable.bauweltId
|
||||
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
|
||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||
import java.util.UUID
|
||||
|
||||
import java.util.*;
|
||||
object BauweltMemberTable: CompositeIdTable("BauweltMember") {
|
||||
val bauweltId = reference("BauweltID", SteamwarUserTable)
|
||||
val memberId = reference("MemberID", SteamwarUserTable)
|
||||
val build = bool("Build")
|
||||
val worldEdit = bool("WorldEdit")
|
||||
val world = bool("World")
|
||||
|
||||
public class BauweltMember {
|
||||
private static final Map<Integer, BauweltMember> memberCache = new HashMap<>();
|
||||
|
||||
public static void clear() {
|
||||
memberCache.clear();
|
||||
}
|
||||
|
||||
private static final Table<BauweltMember> table = new Table<>(BauweltMember.class);
|
||||
private static final SelectStatement<BauweltMember> getMember = table.select(Table.PRIMARY);
|
||||
private static final SelectStatement<BauweltMember> getMembers = table.selectFields("BauweltID");
|
||||
private static final Statement update = table.insertAll();
|
||||
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||
|
||||
public static void addMember(UUID ownerID, UUID memberID) {
|
||||
new BauweltMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId(), false, false).updateDB();
|
||||
}
|
||||
|
||||
public static BauweltMember getBauMember(UUID ownerID, UUID memberID){
|
||||
return getBauMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId());
|
||||
}
|
||||
|
||||
public static BauweltMember getBauMember(int ownerID, int memberID){
|
||||
BauweltMember member = memberCache.get(memberID);
|
||||
if(member != null && member.bauweltID == ownerID)
|
||||
return member;
|
||||
return getMember.select(ownerID, memberID);
|
||||
}
|
||||
|
||||
public static List<BauweltMember> getMembers(UUID bauweltID){
|
||||
return getMembers(SteamwarUser.get(bauweltID).getId());
|
||||
}
|
||||
|
||||
public static List<BauweltMember> getMembers(int bauweltID){
|
||||
return getMembers.listSelect(bauweltID);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int bauweltID;
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int memberID;
|
||||
@Getter
|
||||
@Field(def = "0")
|
||||
private boolean worldEdit;
|
||||
@Getter
|
||||
@Field(def = "0")
|
||||
private boolean world;
|
||||
|
||||
public BauweltMember(int bauweltID, int memberID, boolean worldEdit, boolean world) {
|
||||
this.bauweltID = bauweltID;
|
||||
this.memberID = memberID;
|
||||
this.worldEdit = worldEdit;
|
||||
this.world = world;
|
||||
memberCache.put(memberID, this);
|
||||
}
|
||||
|
||||
public void setWorldEdit(boolean worldEdit) {
|
||||
this.worldEdit = worldEdit;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public void setWorld(boolean world) {
|
||||
this.world = world;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
private void updateDB(){
|
||||
update.update(bauweltID, memberID, worldEdit, world);
|
||||
}
|
||||
|
||||
public void remove(){
|
||||
delete.update(bauweltID, memberID);
|
||||
memberCache.remove(memberID);
|
||||
}
|
||||
|
||||
public boolean isBuild() {
|
||||
return worldEdit;
|
||||
}
|
||||
|
||||
public boolean isSupervisor() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public void setBuild(boolean build) {
|
||||
this.worldEdit = build;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public void setSupervisor(boolean supervisor) {
|
||||
this.world = supervisor;
|
||||
updateDB();
|
||||
}
|
||||
override val primaryKey = PrimaryKey(bauweltId, memberId)
|
||||
}
|
||||
|
||||
class BauweltMember(id: EntityID<CompositeID>): CompositeEntity(id) {
|
||||
companion object: CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
|
||||
private val cache = mutableMapOf<Int, BauweltMember>()
|
||||
|
||||
private fun cache(member: BauweltMember) = cache.put(member.memberID, member)
|
||||
|
||||
@JvmStatic
|
||||
fun clear() = cache.clear()
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use addMember(ownerId: Int, newMemberId: Int)")
|
||||
fun addMember(ownerId: UUID, newMemberId: UUID) = addMember(SteamwarUser.get(ownerId)!!.id, SteamwarUser.get(newMemberId)!!.id)
|
||||
|
||||
@JvmStatic
|
||||
fun addMember(ownerId: Int, newMemberId: Int) = addMember(EntityID(ownerId, SteamwarUserTable), EntityID(newMemberId, SteamwarUserTable))
|
||||
|
||||
fun addMember(ownerId: EntityID<Int>, newMemberId: EntityID<Int>) = useDb {
|
||||
BauweltMemberTable.insertIgnore {
|
||||
it[bauweltId] = ownerId
|
||||
it[memberId] = newMemberId
|
||||
it[build] = false
|
||||
it[worldEdit] = false
|
||||
it[world] = false
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
||||
fun getBauMember(bauwelt: UUID, member: UUID) = useDb {
|
||||
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBauMember(bauwelt: Int, member: Int) = useDb {
|
||||
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use getMembers(bauwelt: Int)")
|
||||
fun getMembers(bauwelt: UUID) = getMembers(SteamwarUser.get(bauwelt)!!.id.value)
|
||||
|
||||
@JvmStatic
|
||||
fun getMembers(bauwelt: Int) = useDb {
|
||||
find { bauweltId eq bauwelt }.toList().also { it.forEach { cache(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
val bauweltID by BauweltMemberTable.bauweltId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
|
||||
val memberID by BauweltMemberTable.memberId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
|
||||
private var worldEditInternal by BauweltMemberTable.worldEdit
|
||||
var worldEdit: Boolean
|
||||
get() = worldEditInternal
|
||||
set(value) = useDb {
|
||||
worldEditInternal = value
|
||||
}
|
||||
private var buildInternal by BauweltMemberTable.build
|
||||
var build: Boolean
|
||||
get() = buildInternal
|
||||
set(value) = useDb {
|
||||
buildInternal = value
|
||||
}
|
||||
private var worldInternal by BauweltMemberTable.world
|
||||
var world: Boolean
|
||||
get() = worldInternal
|
||||
set(value) = useDb {
|
||||
worldInternal = value
|
||||
}
|
||||
var supervisor: Boolean
|
||||
get() = world
|
||||
set(value) = useDb {
|
||||
world = value
|
||||
}
|
||||
|
||||
fun isBuild() = worldEdit
|
||||
fun isSupervisor() = world
|
||||
fun isWorldEdit() = build
|
||||
fun isWorld() = world
|
||||
|
||||
fun remove() = useDb {
|
||||
delete()
|
||||
}
|
||||
}
|
||||
@@ -105,11 +105,11 @@ public class Fight {
|
||||
}
|
||||
|
||||
public SteamwarUser getBlueLeader() {
|
||||
return SteamwarUser.get(blueLeader);
|
||||
return SteamwarUser.byId(blueLeader);
|
||||
}
|
||||
|
||||
public SteamwarUser getRedLeader() {
|
||||
return SteamwarUser.get(redLeader);
|
||||
return SteamwarUser.byId(redLeader);
|
||||
}
|
||||
|
||||
public boolean replayAllowed() {
|
||||
|
||||
@@ -35,11 +35,19 @@ import java.util.stream.Collectors;
|
||||
public final class GameModeConfig<M, W> {
|
||||
|
||||
public static final Function<String, String> ToString = Function.identity();
|
||||
public static final Function<File, String> ToStaticWarGear = __ -> "WarGear";
|
||||
public static final Function<File, String> ToInternalName = file -> file.getName().replace(".yml", "");
|
||||
public static final Function<File, String> ToStaticWarGear = GameModeConfig::constWarGear;
|
||||
public static final Function<File, String> ToInternalName = GameModeConfig::internalName;
|
||||
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
private static final Random random = new Random();
|
||||
|
||||
private static String constWarGear(File f) {
|
||||
return "WarGear";
|
||||
}
|
||||
|
||||
private static String internalName(File f) {
|
||||
return f.getName().replace(".yml", "");
|
||||
}
|
||||
|
||||
private static final Map<String, GameModeConfig<?, String>> byFileName;
|
||||
private static final Map<String, GameModeConfig<?, String>> byGameName;
|
||||
private static final Map<SchematicType, GameModeConfig<?, String>> bySchematicType;
|
||||
|
||||
@@ -242,11 +242,11 @@ public class SchematicNode {
|
||||
|
||||
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType,
|
||||
Integer parent) {
|
||||
return accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent);
|
||||
return accessibleByUserTypeParent(SteamwarUser.byId(owner), SchematicType.fromDB(schemType), parent);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) {
|
||||
return accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType));
|
||||
return accessibleByUserType(SteamwarUser.byId(user), SchematicType.fromDB(schemType));
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) {
|
||||
@@ -278,12 +278,12 @@ public class SchematicNode {
|
||||
|
||||
@Deprecated
|
||||
public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) {
|
||||
return list(SteamwarUser.get(user), parent);
|
||||
return list(SteamwarUser.byId(user), parent);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) {
|
||||
return getAll(SteamwarUser.get(user));
|
||||
return getAll(SteamwarUser.byId(user));
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
|
||||
|
||||
@@ -17,373 +17,273 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.sql;
|
||||
package de.steamwar.sql
|
||||
|
||||
import de.steamwar.sql.internal.*;
|
||||
import lombok.Getter;
|
||||
import de.steamwar.sql.internal.useDb
|
||||
import org.jetbrains.exposed.v1.core.JoinType
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.inList
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.select
|
||||
import java.security.SecureRandom
|
||||
import java.sql.Timestamp
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import javax.crypto.SecretKeyFactory
|
||||
import javax.crypto.spec.PBEKeySpec
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SteamwarUser {
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
private static final SecretKeyFactory factory;
|
||||
|
||||
static {
|
||||
try {
|
||||
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
|
||||
new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString()));
|
||||
new SqlTypeMapper<>(Locale.class, "VARCHAR(32)", (rs, identifier) -> {
|
||||
String l = rs.getString(identifier);
|
||||
return l != null ? Locale.forLanguageTag(l) : null;
|
||||
}, (st, index, value) -> st.setString(index, value.toLanguageTag()));
|
||||
new SqlTypeMapper<>(SteamwarUser.class, null, (rs, identifier) -> { throw new SecurityException("SteamwarUser cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.id));
|
||||
}
|
||||
|
||||
private static final Table<SteamwarUser> table = new Table<>(SteamwarUser.class, "UserData");
|
||||
private static final Statement insert = table.insertFields("UUID", "UserName");
|
||||
private static final SelectStatement<SteamwarUser> byID = table.selectFields("id");
|
||||
private static final SelectStatement<SteamwarUser> byUUID = table.selectFields("UUID");
|
||||
private static final SelectStatement<SteamwarUser> byName = table.selectFields("UserName");
|
||||
private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId");
|
||||
private static final SelectStatement<SteamwarUser> byTeam = table.selectFields("Team");
|
||||
private static final SelectStatement<SteamwarUser> getUsersWithPerm = new SelectStatement<>(table, "SELECT S.* FROM UserData S JOIN UserPerm P ON S.id = P.User WHERE P.Perm = ?");
|
||||
private static final SelectStatement<SteamwarUser> getAll = new SelectStatement<SteamwarUser>(table, "SELECT * FROM UserData");
|
||||
|
||||
private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
|
||||
private static final Statement updatePassword = table.update(Table.PRIMARY, "Password");
|
||||
private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale");
|
||||
private static final Statement updateTeam = table.update(Table.PRIMARY, "Team");
|
||||
private static final Statement updateLeader = table.update(Table.PRIMARY, "Leader");
|
||||
private static final Statement updateDiscord = table.update(Table.PRIMARY, "DiscordId");
|
||||
|
||||
private static final Statement getPlaytime = new Statement("SELECT SUM(UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime)) as Playtime FROM Session WHERE UserID = ?");
|
||||
private static final Statement getFirstjoin = new Statement("SELECT MIN(StartTime) AS FirstJoin FROM Session WHERE UserID = ?");
|
||||
private static final Statement getLastonline = new Statement("SELECT MAX(EndTime) AS LastOnline FROM Session WHERE UserID = ?");
|
||||
|
||||
private static final Map<Integer, SteamwarUser> usersById = new HashMap<>();
|
||||
private static final Map<UUID, SteamwarUser> usersByUUID = new HashMap<>();
|
||||
private static final Map<String, SteamwarUser> usersByName = new HashMap<>();
|
||||
private static final Map<Long, SteamwarUser> usersByDiscord = new HashMap<>();
|
||||
public static void clear() {
|
||||
usersById.clear();
|
||||
usersByName.clear();
|
||||
usersByUUID.clear();
|
||||
usersByDiscord.clear();
|
||||
}
|
||||
|
||||
public static void invalidate(int userId) {
|
||||
SteamwarUser user = usersById.remove(userId);
|
||||
if (user == null)
|
||||
return;
|
||||
usersByName.remove(user.getUserName());
|
||||
usersByUUID.remove(user.getUUID());
|
||||
usersByDiscord.remove(user.getDiscordId());
|
||||
}
|
||||
|
||||
public static SteamwarUser get(String userName){
|
||||
SteamwarUser user = usersByName.get(userName.toLowerCase());
|
||||
if(user != null)
|
||||
return user;
|
||||
return byName.select(userName);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(UUID uuid){
|
||||
SteamwarUser user = usersByUUID.get(uuid);
|
||||
if(user != null)
|
||||
return user;
|
||||
return byUUID.select(uuid);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(int id) {
|
||||
SteamwarUser user = usersById.get(id);
|
||||
if(user != null)
|
||||
return user;
|
||||
return byID.select(id);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(Long discordId) {
|
||||
if(usersByDiscord.containsKey(discordId))
|
||||
return usersByDiscord.get(discordId);
|
||||
return byDiscord.select(discordId);
|
||||
}
|
||||
|
||||
public static SteamwarUser getOrCreate(UUID uuid, String name, Consumer<UUID> newPlayer) {
|
||||
SteamwarUser user = get(uuid);
|
||||
|
||||
if (user != null) {
|
||||
if (!user.userName.equals(name)) {
|
||||
updateName.update(name, user.id);
|
||||
user.userName = name;
|
||||
}
|
||||
|
||||
return user;
|
||||
} else {
|
||||
insert.update(uuid, name);
|
||||
newPlayer.accept(uuid);
|
||||
return get(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<SteamwarUser> getUsersWithPerm(UserPerm userPerm) {
|
||||
return getUsersWithPerm.listSelect(userPerm);
|
||||
}
|
||||
|
||||
public static List<SteamwarUser> getServerTeam() {
|
||||
return Stream.of(getUsersWithPerm(UserPerm.PREFIX_ADMIN),
|
||||
getUsersWithPerm(UserPerm.PREFIX_DEVELOPER),
|
||||
getUsersWithPerm(UserPerm.PREFIX_MODERATOR),
|
||||
getUsersWithPerm(UserPerm.PREFIX_SUPPORTER),
|
||||
getUsersWithPerm(UserPerm.PREFIX_BUILDER)
|
||||
)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<SteamwarUser> getTeam(int teamId) {
|
||||
return byTeam.listSelect(teamId);
|
||||
}
|
||||
|
||||
public static void batchCache(Set<Integer> ids) {
|
||||
ids.removeIf(usersById::containsKey);
|
||||
if(ids.isEmpty())
|
||||
return;
|
||||
|
||||
try (SelectStatement<SteamwarUser> batch = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN (" + ids.stream().map(Object::toString).collect(Collectors.joining(", ")) + ")")) {
|
||||
batch.listSelect();
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int id;
|
||||
@Field(keys = {"uuid"})
|
||||
private final UUID uuid;
|
||||
@Getter
|
||||
@Field
|
||||
private String userName;
|
||||
@Field(nullable = true)
|
||||
private String password;
|
||||
@Getter
|
||||
@Field(def = "0")
|
||||
private int team;
|
||||
@Getter
|
||||
@Field(def = "0")
|
||||
private boolean leader;
|
||||
@Field(nullable = true)
|
||||
private Locale locale;
|
||||
@Field(def = "0")
|
||||
private boolean manualLocale;
|
||||
@Getter
|
||||
@Field(keys = {"discordId"}, nullable = true)
|
||||
private Long discordId;
|
||||
|
||||
private Map<Punishment.PunishmentType, Punishment> punishments = null;
|
||||
private Set<UserPerm> permissions = null;
|
||||
private UserPerm.Prefix prefix = null;
|
||||
|
||||
public SteamwarUser(int id, UUID uuid, String userName, String password, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) {
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.userName = userName;
|
||||
this.password = password;
|
||||
this.team = team;
|
||||
this.leader = leader;
|
||||
this.locale = locale;
|
||||
this.manualLocale = manualLocale;
|
||||
this.discordId = discordId != null && discordId != 0 ? discordId : null;
|
||||
|
||||
usersById.put(id, this);
|
||||
usersByName.put(userName.toLowerCase(), this);
|
||||
usersByUUID.put(uuid, this);
|
||||
if (this.discordId != null) {
|
||||
usersByDiscord.put(discordId, this);
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
if(locale != null)
|
||||
return locale;
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
public Punishment getPunishment(Punishment.PunishmentType type) {
|
||||
initPunishments();
|
||||
return punishments.getOrDefault(type, null);
|
||||
}
|
||||
|
||||
public boolean isPunished(Punishment.PunishmentType punishment) {
|
||||
initPunishments();
|
||||
if (!punishments.containsKey(punishment)) {
|
||||
return false;
|
||||
}
|
||||
if (!punishments.get(punishment).isCurrent()) {
|
||||
if (punishment == Punishment.PunishmentType.Ban) {
|
||||
BannedUserIPs.unbanIPs(id);
|
||||
}
|
||||
punishments.remove(punishment);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasPerm(UserPerm perm) {
|
||||
initPerms();
|
||||
return permissions.contains(perm);
|
||||
}
|
||||
|
||||
public Set<UserPerm> perms() {
|
||||
initPerms();
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public UserPerm.Prefix prefix() {
|
||||
initPerms();
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public double getOnlinetime() {
|
||||
return getPlaytime.select(rs -> {
|
||||
if (rs.next() && rs.getBigDecimal("Playtime") != null)
|
||||
return rs.getBigDecimal("Playtime").doubleValue();
|
||||
return 0.0;
|
||||
}, id);
|
||||
}
|
||||
|
||||
public Timestamp getFirstjoin() {
|
||||
return getFirstjoin.select(rs -> {
|
||||
if (rs.next())
|
||||
return rs.getTimestamp("FirstJoin");
|
||||
return null;
|
||||
}, id);
|
||||
}
|
||||
|
||||
public Timestamp getLastOnline() {
|
||||
return getLastonline.select(rs -> {
|
||||
if (rs.next())
|
||||
return rs.getTimestamp("LastOnline");
|
||||
return null;
|
||||
}, id);
|
||||
}
|
||||
|
||||
public void punish(Punishment.PunishmentType punishment, Timestamp time, String banReason, int from, boolean perma) {
|
||||
initPunishments();
|
||||
punishments.remove(punishment);
|
||||
punishments.put(punishment, Punishment.createPunishment(id, from, punishment, banReason, time, perma));
|
||||
}
|
||||
|
||||
public void setTeam(int team) {
|
||||
this.team = team;
|
||||
updateTeam.update(team, id);
|
||||
setLeader(false);
|
||||
}
|
||||
|
||||
public void setLeader(boolean leader) {
|
||||
this.leader = leader;
|
||||
updateLeader.update(leader, id);
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale, boolean manualLocale) {
|
||||
if (locale == null || (this.manualLocale && !manualLocale))
|
||||
return;
|
||||
|
||||
this.locale = locale;
|
||||
this.manualLocale = manualLocale;
|
||||
updateLocale.update(locale.toLanguageTag(), manualLocale, id);
|
||||
}
|
||||
|
||||
public void setDiscordId(Long discordId) {
|
||||
usersByDiscord.remove(this.discordId);
|
||||
this.discordId = discordId;
|
||||
updateDiscord.update(discordId, id);
|
||||
if (discordId != null) {
|
||||
usersByDiscord.put(discordId, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
try {
|
||||
byte[] salt = new byte[16];
|
||||
random.nextBytes(salt);
|
||||
String saltString = Base64.getEncoder().encodeToString(salt);
|
||||
|
||||
byte[] hash = generateHash(password, salt);
|
||||
String hashString = Base64.getEncoder().encodeToString(hash);
|
||||
this.password = hashString + ":" + saltString;
|
||||
updatePassword.update(this.password, id);
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifyPassword(String password) {
|
||||
try {
|
||||
if (!hasPassword()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] parts = this.password.split(":");
|
||||
if (parts.length != 2) {
|
||||
SQLConfig.impl.getLogger().log(Level.SEVERE ,"Invalid password hash for user {0} ({1})", new Object[]{userName, id});
|
||||
return false;
|
||||
}
|
||||
|
||||
String hashString = parts[0];
|
||||
byte[] realHash = Base64.getDecoder().decode(hashString);
|
||||
String saltString = parts[1];
|
||||
byte[] salt = Base64.getDecoder().decode(saltString);
|
||||
byte[] hash = generateHash(password, salt);
|
||||
return Arrays.equals(realHash, hash);
|
||||
} catch (Exception e) {
|
||||
SQLConfig.impl.getLogger().log(Level.SEVERE, "Error while verifying password for user " + userName + " (" + id + ")", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPassword() {
|
||||
return this.password != null;
|
||||
}
|
||||
|
||||
private byte[] generateHash(String password, byte[] salt)
|
||||
throws InvalidKeySpecException {
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
|
||||
return factory.generateSecret(spec).getEncoded();
|
||||
|
||||
}
|
||||
|
||||
private void initPunishments() {
|
||||
if(punishments != null)
|
||||
return;
|
||||
|
||||
punishments = Punishment.getPunishmentsOfPlayer(id);
|
||||
}
|
||||
|
||||
private void initPerms() {
|
||||
if(permissions != null)
|
||||
return;
|
||||
|
||||
permissions = UserPerm.getPerms(id);
|
||||
prefix = permissions.stream().filter(UserPerm.prefixes::containsKey).findAny().map(UserPerm.prefixes::get).orElse(UserPerm.emptyPrefix);
|
||||
}
|
||||
|
||||
public static List<SteamwarUser> getAll() {
|
||||
return getAll.listSelect();
|
||||
}
|
||||
object SteamwarUserTable : IntIdTable("UserData", "id") {
|
||||
val uuid = varchar("UUID", 36)
|
||||
val username = varchar("UserName", 32)
|
||||
val team = integer("Team")
|
||||
val leader = bool("Leader")
|
||||
val locale = varchar("Locale", 16).nullable()
|
||||
val manualeLocale = bool("ManualeLocale")
|
||||
val bedrock = bool("Bedrock")
|
||||
val password = text("Password").nullable()
|
||||
val discordId = long("DiscordId").nullable()
|
||||
}
|
||||
|
||||
class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
|
||||
companion object: IntEntityClass<SteamwarUser>(SteamwarUserTable) {
|
||||
private val random = SecureRandom()
|
||||
private val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
|
||||
|
||||
private val byId = mutableMapOf<Int, SteamwarUser>()
|
||||
private val byUUID = mutableMapOf<UUID, SteamwarUser>()
|
||||
private val byDiscordId = mutableMapOf<Long, SteamwarUser>()
|
||||
private val byUsername = mutableMapOf<String, SteamwarUser>()
|
||||
|
||||
@JvmStatic
|
||||
fun clear() {
|
||||
byId.clear()
|
||||
byUUID.clear()
|
||||
byDiscordId.clear()
|
||||
byUsername.clear()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun invalidate(userId: Int) {
|
||||
val user = byId.remove(userId)
|
||||
if(user != null) {
|
||||
byUUID.remove(user.getUUID())
|
||||
byDiscordId.remove(user.discordId)
|
||||
byUsername.remove(user.userName)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun byId(id: Int) = byId[id] ?: useDb { findById(id)?.also { cache(it) } }
|
||||
|
||||
@JvmStatic
|
||||
fun get(uuid: UUID) = byUUID[uuid] ?: useDb { find { SteamwarUserTable.uuid eq uuid.toString() }.firstOrNull()?.also { cache(it) } }
|
||||
|
||||
@JvmStatic
|
||||
fun get(discordId: Long) = byDiscordId[discordId] ?: useDb { find { SteamwarUserTable.discordId eq discordId }.firstOrNull()?.also { cache(it) } }
|
||||
|
||||
@JvmStatic
|
||||
fun get(username: String) = byUsername[username] ?: useDb { find { SteamwarUserTable.username eq username }.firstOrNull()?.also { cache(it) } }
|
||||
|
||||
private fun cache(user: SteamwarUser) {
|
||||
byId[user.getId()] = user
|
||||
byUUID[user.getUUID()] = user
|
||||
user.discordId?.let { byDiscordId[it] = user }
|
||||
byUsername[user.userName] = user
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getOrCreate(uuid: UUID, name: String, newPlayer: Consumer<UUID>): SteamwarUser {
|
||||
val user = get(uuid)
|
||||
|
||||
return if (user != null) {
|
||||
if (user.userName != name) {
|
||||
useDb {
|
||||
user.userName = name
|
||||
}
|
||||
}
|
||||
|
||||
user
|
||||
} else {
|
||||
useDb {
|
||||
SteamwarUserTable.insert {
|
||||
it[SteamwarUserTable.uuid] = uuid.toString()
|
||||
it[username] = name
|
||||
}
|
||||
}
|
||||
|
||||
newPlayer.accept(uuid)
|
||||
get(uuid) ?: error("User $uuid not found after creation!")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getUsersWithPerm(perm: UserPerm) = useDb {
|
||||
UserPermTable.join(SteamwarUserTable, JoinType.INNER, UserPermTable.user, SteamwarUserTable.id)
|
||||
.select(SteamwarUserTable.fields).where { UserPermTable.perm eq perm }.map { wrapRow(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getServerTeam() = useDb { listOf(
|
||||
getUsersWithPerm(UserPerm.PREFIX_ADMIN),
|
||||
getUsersWithPerm(UserPerm.PREFIX_DEVELOPER),
|
||||
getUsersWithPerm(UserPerm.PREFIX_MODERATOR),
|
||||
getUsersWithPerm(UserPerm.PREFIX_SUPPORTER),
|
||||
getUsersWithPerm(UserPerm.PREFIX_BUILDER),
|
||||
).flatten() }
|
||||
|
||||
@JvmStatic
|
||||
fun getTeam(teamId: Int) = useDb {
|
||||
find { SteamwarUserTable.team eq teamId }.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun batchCache(ids: MutableSet<Int>) {
|
||||
ids.removeIf(byId::containsKey)
|
||||
if(ids.isEmpty()) return
|
||||
|
||||
useDb {
|
||||
find { SteamwarUserTable.id inList ids }.forEach { cache(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val uuid: UUID by SteamwarUserTable.uuid.transform({ it.toString() }, { UUID.fromString(it) })
|
||||
var userName by SteamwarUserTable.username
|
||||
private var teamInternal by SteamwarUserTable.team
|
||||
var team: Int
|
||||
get() = teamInternal
|
||||
set(value) = useDb {
|
||||
teamInternal = value
|
||||
leaderInternal = false
|
||||
}
|
||||
|
||||
private var leaderInternal by SteamwarUserTable.leader
|
||||
var leader: Boolean
|
||||
get() = leaderInternal
|
||||
set(value) = useDb {
|
||||
leaderInternal = value
|
||||
}
|
||||
fun isLeader() = leader
|
||||
|
||||
var locale: Locale by SteamwarUserTable.locale
|
||||
.transform({ it.toLanguageTag() }, { it?.let { Locale.forLanguageTag(it) } ?: Locale.getDefault()})
|
||||
var manualLocale by SteamwarUserTable.manualeLocale
|
||||
var bedrock by SteamwarUserTable.bedrock
|
||||
private var passwordInternal by SteamwarUserTable.password
|
||||
var password: String?
|
||||
get() = passwordInternal
|
||||
set(value) {
|
||||
if(value == null) {
|
||||
useDb {
|
||||
passwordInternal = null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val salt = ByteArray(16)
|
||||
random.nextBytes(salt)
|
||||
val saltString = Base64.getEncoder().encode(salt)
|
||||
|
||||
val hash = generateHash(value, salt)
|
||||
val hashString = Base64.getEncoder().encode(hash)
|
||||
|
||||
useDb {
|
||||
passwordInternal = "$hashString:$saltString"
|
||||
}
|
||||
}
|
||||
|
||||
var discordId by SteamwarUserTable.discordId
|
||||
|
||||
private val punishments by lazy { Punishment.getPunishmentsOfPlayer(id.value) }
|
||||
private val perms by lazy { UserPerm.getPerms(id.value) }
|
||||
private val prefix by lazy { perms.firstOrNull { UserPerm.prefixes.containsKey(it) }?.let { UserPerm.prefixes[it]} ?: UserPerm.emptyPrefix }
|
||||
|
||||
fun getUUID(): UUID = uuid
|
||||
fun getId() = id.value
|
||||
|
||||
fun getPunishment(punishment: Punishment.PunishmentType) = punishments[punishment]
|
||||
fun isPunished(punishment: Punishment.PunishmentType) = getPunishment(punishment)
|
||||
?.let {
|
||||
if (!it.isCurrent) {
|
||||
if (punishment == Punishment.PunishmentType.Ban) {
|
||||
BannedUserIPs.unbanIPs(id.value)
|
||||
}
|
||||
punishments.remove(punishment)
|
||||
return@let false
|
||||
}
|
||||
|
||||
return@let true
|
||||
} ?: false
|
||||
|
||||
fun hasPerm(perm: UserPerm) = perms.contains(perm)
|
||||
fun perms() = perms
|
||||
fun prefix() = prefix
|
||||
|
||||
fun getOnlinetime() = useDb {
|
||||
exec("SELECT SUM(UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime)) as Playtime FROM Session WHERE UserID = ${this@SteamwarUser.id.value}") { rs ->
|
||||
return@exec if (rs.next()) {
|
||||
rs.getBigDecimal("Playtime").toDouble()
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} ?: 0.0
|
||||
}
|
||||
|
||||
fun getFirstjoin() = useDb {
|
||||
exec("SELECT MIN(StartTime) AS FirstJoin FROM Session WHERE UserID = ${this@SteamwarUser.id.value}") { rs ->
|
||||
return@exec if (rs.next()) {
|
||||
rs.getTimestamp("FirstJoin")
|
||||
} else { null }
|
||||
}
|
||||
}
|
||||
|
||||
fun getLastOnline() = useDb {
|
||||
exec("SELECT MAX(EndTime) AS LastOnline FROM Session WHERE UserID = ${this@SteamwarUser.id.value}") { rs ->
|
||||
return@exec if (rs.next()) {
|
||||
rs.getTimestamp("LastOnline")
|
||||
} else { null }
|
||||
}
|
||||
}
|
||||
|
||||
fun punish(punishment: Punishment.PunishmentType, time: Timestamp, reason: String, from: Int, perma: Boolean) = useDb {
|
||||
punishments.remove(punishment)
|
||||
punishments[punishment] = Punishment.createPunishment(this@SteamwarUser.id.value, from, punishment, reason, time, perma)
|
||||
}
|
||||
|
||||
fun setLocale(locale: Locale?, manualeLocale: Boolean) {
|
||||
if (locale == null || (this.manualLocale && !manualLocale)) return
|
||||
|
||||
useDb {
|
||||
this@SteamwarUser.locale = locale
|
||||
this@SteamwarUser.manualLocale = manualeLocale
|
||||
}
|
||||
}
|
||||
|
||||
fun setDiscordId(discordId: Long) {
|
||||
byDiscordId.remove(this.discordId)
|
||||
useDb {
|
||||
this@SteamwarUser.discordId = discordId
|
||||
}
|
||||
byDiscordId[discordId] = this
|
||||
}
|
||||
|
||||
fun verifyPassword(password: String): Boolean {
|
||||
if (!hasPassword()) return false
|
||||
|
||||
val (hashString, saltString) = this.password!!.split(':')
|
||||
|
||||
val hash = Base64.getDecoder().decode(hashString)
|
||||
val salt = Base64.getDecoder().decode(saltString)
|
||||
|
||||
return hash.contentEquals(generateHash(password, salt))
|
||||
}
|
||||
|
||||
fun hasPassword() = password != null
|
||||
|
||||
fun generateHash(password: String, salt: ByteArray): ByteArray =
|
||||
PBEKeySpec(password.toCharArray(), salt, 65536, 128).let { factory.generateSecret(it).encoded }
|
||||
}
|
||||
@@ -105,6 +105,6 @@ public class Token {
|
||||
}
|
||||
|
||||
public SteamwarUser getOwner() {
|
||||
return SteamwarUser.get(owner);
|
||||
return SteamwarUser.byId(owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,16 +17,24 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.sql;
|
||||
package de.steamwar.sql
|
||||
|
||||
import de.steamwar.sql.internal.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import de.steamwar.sql.internal.useDb
|
||||
import org.jetbrains.exposed.v1.core.Table
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
object UserPermTable: Table("UserPerm") {
|
||||
val user = reference("User", SteamwarUserTable.id)
|
||||
val perm = enumerationByName("Perm", 32, UserPerm::class)
|
||||
|
||||
public enum UserPerm {
|
||||
override val primaryKey = PrimaryKey(user, perm)
|
||||
}
|
||||
|
||||
enum class UserPerm {
|
||||
PREFIX_NONE, // special value, not stored in database
|
||||
PREFIX_YOUTUBER,
|
||||
PREFIX_GUIDE,
|
||||
@@ -45,54 +53,41 @@ public enum UserPerm {
|
||||
MODERATION,
|
||||
ADMINISTRATION;
|
||||
|
||||
public static final Map<UserPerm, Prefix> prefixes;
|
||||
public static final Prefix emptyPrefix;
|
||||
static {
|
||||
// https://www.digminecraft.com/lists/color_list_pc.php
|
||||
SqlTypeMapper.nameEnumMapper(UserPerm.class);
|
||||
Map<UserPerm, Prefix> p = new EnumMap<>(UserPerm.class);
|
||||
emptyPrefix = new Prefix("§7", "");
|
||||
p.put(PREFIX_NONE, emptyPrefix);
|
||||
p.put(PREFIX_YOUTUBER, new Prefix("§7", "YT"));
|
||||
p.put(PREFIX_GUIDE, new Prefix("§x§e§7§6§2§e§d", "Guide")); // E762ED
|
||||
companion object {
|
||||
@JvmField
|
||||
val emptyPrefix = Prefix("§7", "")
|
||||
@JvmField
|
||||
val prefixes = mapOf(
|
||||
PREFIX_NONE to emptyPrefix,
|
||||
PREFIX_YOUTUBER to Prefix("§7", "YT"),
|
||||
PREFIX_GUIDE to Prefix("§x§e§7§6§2§e§d", "Guide"), // E762ED
|
||||
PREFIX_SUPPORTER to Prefix("§x§6§0§9§5§F§B", "Sup"), // 6095FB
|
||||
PREFIX_MODERATOR to Prefix("§x§F§F§A§2§5§C", "Mod"), // FFA25C
|
||||
PREFIX_BUILDER to Prefix("§x§6§0§F§F§6§A", "Arch"), // 60FF6A
|
||||
PREFIX_DEVELOPER to Prefix("§x§0§B§B§C§B§3", "Dev"), // 0BBCB3
|
||||
PREFIX_ADMIN to Prefix("§x§F§F§2§B§2§4", "Admin"), // FF2B24
|
||||
)
|
||||
|
||||
p.put(PREFIX_SUPPORTER, new Prefix("§x§6§0§9§5§F§B", "Sup")); // 6095FB
|
||||
p.put(PREFIX_MODERATOR, new Prefix("§x§F§F§A§2§5§C", "Mod")); // FFA25C
|
||||
p.put(PREFIX_BUILDER, new Prefix("§x§6§0§F§F§6§A", "Arch")); // 60FF6A
|
||||
p.put(PREFIX_DEVELOPER, new Prefix("§x§0§B§B§C§B§3", "Dev")); // 0BBCB3
|
||||
p.put(PREFIX_ADMIN, new Prefix("§x§F§F§2§B§2§4", "Admin")); // FF2B24
|
||||
prefixes = Collections.unmodifiableMap(p);
|
||||
@JvmStatic
|
||||
fun getPerms(id: Int) = useDb {
|
||||
UserPermTable.selectAll().where { UserPermTable.user eq id }.map { it[UserPermTable.perm] }.toSet()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun addPerm(user: SteamwarUser, perm: UserPerm) = useDb {
|
||||
UserPermTable.insert {
|
||||
it[UserPermTable.user] = user.getId()
|
||||
it[UserPermTable.perm] = perm
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removePerm(user: SteamwarUser, perm: UserPerm) = useDb {
|
||||
UserPermTable.deleteWhere {
|
||||
(UserPermTable.user eq user.getId()) and (UserPermTable.perm eq perm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Table<UserPermTable> table = new Table<>(UserPermTable.class, "UserPerm");
|
||||
private static final SelectStatement<UserPermTable> getPerms = table.selectFields("user");
|
||||
private static final Statement addPerm = table.insertAll();
|
||||
private static final Statement removePerm = table.delete(Table.PRIMARY);
|
||||
|
||||
public static Set<UserPerm> getPerms(int user) {
|
||||
return getPerms.listSelect(user).stream().map(up -> up.perm).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static void addPerm(SteamwarUser user, UserPerm perm) {
|
||||
addPerm.update(user, perm);
|
||||
}
|
||||
|
||||
public static void removePerm(SteamwarUser user, UserPerm perm) {
|
||||
removePerm.update(user, perm);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Prefix {
|
||||
private final String colorCode;
|
||||
private final String chatPrefix;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public static class UserPermTable {
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int user;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final UserPerm perm;
|
||||
}
|
||||
}
|
||||
data class Prefix(val colorCode: String, val chatPrefix: String)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
|
||||
object KotlinDatabase {
|
||||
lateinit var db: Database
|
||||
|
||||
fun ensureConnected() {
|
||||
if(KotlinDatabase::db.isInitialized) return
|
||||
|
||||
val file = File(System.getProperty("user.home"), "mysql.properties")
|
||||
|
||||
val properties = Properties()
|
||||
properties.load(file.inputStream())
|
||||
|
||||
val host = properties.getProperty("host")
|
||||
val port = properties.getProperty("port")
|
||||
val database = properties.getProperty("database")
|
||||
val username = properties.getProperty("user")
|
||||
val password = properties.getProperty("password")
|
||||
|
||||
db = Database.connect(
|
||||
"jdbc:mysql://$host:$port/$database",
|
||||
driver = "com.mysql.cj.jdbc.Driver",
|
||||
user = username,
|
||||
password = password
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
|
||||
KotlinDatabase.ensureConnected()
|
||||
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db, statement)
|
||||
}
|
||||
Reference in New Issue
Block a user