/*
* 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 java.sql.Timestamp;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
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()));
SqlTypeMapper.nameEnumMapper(UserGroup.class);
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 SelectStatement batchGet = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN ?");
private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
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());
}
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 = SteamwarUser.get(uuid);
if (user != null) {
if (!user.userName.equals(name)) {
updateName.update(name, user.id);
nameUpdate.accept(user.userName, name);
user.userName = name;
}
} else {
insert.update(uuid, name);
newPlayer.accept(uuid);
return get(uuid);
}
return user;
}
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;
batchGet.listSelect((Object) ids.toArray());
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int id;
@Field(keys = {"uuid"})
private final UUID uuid;
@Getter
@Field
private String userName;
@Getter
@Field(def = "'Member'")
private final UserGroup userGroup;
@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 final Map punishments;
public SteamwarUser(int id, UUID uuid, String userName, UserGroup userGroup, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) {
this.id = id;
this.uuid = uuid;
this.userName = userName;
this.userGroup = userGroup;
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);
}
punishments = Punishment.getPunishmentsOfPlayer(id); //TODO load always on Subservers?
}
public UUID getUUID() {
return uuid;
}
public Locale getLocale() {
if(locale != null)
return locale;
return Locale.getDefault();
}
public Punishment getPunishment(Punishment.PunishmentType type) {
return punishments.getOrDefault(type, null);
}
public boolean isPunished(Punishment.PunishmentType punishment) {
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 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) {
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);
}
}
}