diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java
index 40f9ca9..8e05174 100644
--- a/src/de/steamwar/sql/SteamwarUser.java
+++ b/src/de/steamwar/sql/SteamwarUser.java
@@ -1,349 +1,349 @@
-/*
- * This file is a part of the SteamWar software.
- *
- * Copyright (C) 2022 SteamWar.de-Serverteam
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package de.steamwar.sql;
-
-import de.steamwar.sql.internal.*;
-import lombok.Getter;
-import lombok.SneakyThrows;
-
-import javax.crypto.Mac;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.sql.Timestamp;
-import java.util.*;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.logging.Level;
-import java.util.stream.Collectors;
-
-public class SteamwarUser {
-
- static {
- 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 table = new Table<>(SteamwarUser.class, "UserData");
- private static final Statement insert = table.insertFields("UUID", "UserName");
- private static final SelectStatement byID = table.selectFields("id");
- private static final SelectStatement byUUID = table.selectFields("UUID");
- private static final SelectStatement byName = table.selectFields("UserName");
- private static final SelectStatement byDiscord = table.selectFields("DiscordId");
- private static final SelectStatement byTeam = table.selectFields("Team");
- private static final SelectStatement getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
-
- 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 Map usersById = new HashMap<>();
- private static final Map usersByUUID = new HashMap<>();
- private static final Map usersByName = new HashMap<>();
- private static final Map 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 newPlayer, BiConsumer nameUpdate) {
- SteamwarUser user = get(uuid);
-
- if (user != null) {
- if (!user.userName.equals(name)) {
- updateName.update(name, user.id);
- nameUpdate.accept(user.userName, name);
- user.userName = name;
- }
-
- return user;
- } else {
- insert.update(uuid, name);
- newPlayer.accept(uuid);
- return get(uuid);
- }
- }
-
- public static List getServerTeam() {
- return getServerTeam.listSelect();
- }
-
- public static List getTeam(int teamId) {
- return byTeam.listSelect(teamId);
- }
-
- public static void batchCache(Set ids) {
- ids.removeIf(usersById::containsKey);
- if(ids.isEmpty())
- return;
-
- try (SelectStatement 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 punishments = null;
- private Set 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 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 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);
- }
- }
-
- @SneakyThrows
- public void setPassword(String password) {
- SecureRandom random = new SecureRandom();
- byte[] salt = new byte[16];
- random.nextBytes(salt);
- String saltString = Base64.getEncoder().encodeToString(salt);
-
- PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
- SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
- byte[] hash = factory.generateSecret(spec).getEncoded();
-
- String hashString = Base64.getEncoder().encodeToString(hash);
-
- this.password = hashString + ":" + saltString;
- updatePassword.update(this.password, id);
- }
-
- @SneakyThrows
- public boolean verifyPassword(String password) {
- if (this.password == null) {
- 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);
-
- PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
- SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
- byte[] hash = factory.generateSecret(spec).getEncoded();
-
- return Arrays.equals(realHash, hash);
- }
-
- 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);
- }
-}
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.sql;
+
+import de.steamwar.sql.internal.*;
+import lombok.Getter;
+import lombok.SneakyThrows;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.sql.Timestamp;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+public class SteamwarUser {
+
+ static {
+ 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 table = new Table<>(SteamwarUser.class, "UserData");
+ private static final Statement insert = table.insertFields("UUID", "UserName");
+ private static final SelectStatement byID = table.selectFields("id");
+ private static final SelectStatement byUUID = table.selectFields("UUID");
+ private static final SelectStatement byName = table.selectFields("UserName");
+ private static final SelectStatement byDiscord = table.selectFields("DiscordId");
+ private static final SelectStatement byTeam = table.selectFields("Team");
+ private static final SelectStatement getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
+
+ 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 Map usersById = new HashMap<>();
+ private static final Map usersByUUID = new HashMap<>();
+ private static final Map usersByName = new HashMap<>();
+ private static final Map 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 newPlayer, BiConsumer nameUpdate) {
+ SteamwarUser user = get(uuid);
+
+ if (user != null) {
+ if (!user.userName.equals(name)) {
+ updateName.update(name, user.id);
+ nameUpdate.accept(user.userName, name);
+ user.userName = name;
+ }
+
+ return user;
+ } else {
+ insert.update(uuid, name);
+ newPlayer.accept(uuid);
+ return get(uuid);
+ }
+ }
+
+ public static List getServerTeam() {
+ return getServerTeam.listSelect();
+ }
+
+ public static List getTeam(int teamId) {
+ return byTeam.listSelect(teamId);
+ }
+
+ public static void batchCache(Set ids) {
+ ids.removeIf(usersById::containsKey);
+ if(ids.isEmpty())
+ return;
+
+ try (SelectStatement 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 punishments = null;
+ private Set 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 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 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);
+ }
+ }
+
+ @SneakyThrows
+ public void setPassword(String password) {
+ SecureRandom random = new SecureRandom();
+ byte[] salt = new byte[16];
+ random.nextBytes(salt);
+ String saltString = Base64.getEncoder().encodeToString(salt);
+
+ PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
+ byte[] hash = factory.generateSecret(spec).getEncoded();
+
+ String hashString = Base64.getEncoder().encodeToString(hash);
+
+ this.password = hashString + ":" + saltString;
+ updatePassword.update(this.password, id);
+ }
+
+ @SneakyThrows
+ public boolean verifyPassword(String password) {
+ if (this.password == null) {
+ 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);
+
+ PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
+ byte[] hash = factory.generateSecret(spec).getEncoded();
+
+ return Arrays.equals(realHash, hash);
+ }
+
+ 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);
+ }
+}