/* * 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); } } }