diff --git a/src/de/steamwar/sql/CheckedSchematic.java b/src/de/steamwar/sql/CheckedSchematic.java new file mode 100644 index 0000000..71fe24a --- /dev/null +++ b/src/de/steamwar/sql/CheckedSchematic.java @@ -0,0 +1,71 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 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.Field; +import de.steamwar.sql.internal.SelectStatement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.sql.Timestamp; +import java.util.List; + +@AllArgsConstructor +public class CheckedSchematic { + + private static final Table table = new Table<>(CheckedSchematic.class); + private static final SelectStatement statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); + + public static List getLastDeclinedOfNode(int node){ + return statusOfNode.listSelect(node); + } + + @Field(nullable = true) + private final Integer nodeId; + @Field + private final int nodeOwner; + @Field + private final String nodeName; + @Getter + @Field + private final int validator; + @Getter + @Field + private final Timestamp startTime; + @Getter + @Field + private final Timestamp endTime; + @Getter + @Field + private final String declineReason; + + public int getNode() { + return nodeId; + } + + public String getSchemName() { + return nodeName; + } + + public int getSchemOwner() { + return nodeOwner; + } +} diff --git a/src/de/steamwar/sql/NodeDownload.java b/src/de/steamwar/sql/NodeDownload.java new file mode 100644 index 0000000..0984096 --- /dev/null +++ b/src/de/steamwar/sql/NodeDownload.java @@ -0,0 +1,69 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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.Field; +import de.steamwar.sql.internal.Statement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Timestamp; +import java.time.Instant; + +@AllArgsConstructor +public class NodeDownload { + + private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + private static final String LINK_BASE = "https://steamwar.de/download.php?schem="; + + private static final Table table = new Table<>(NodeDownload.class); + private static final Statement insert = table.insertFields("NodeId", "Link"); + + @Field(keys = {Table.PRIMARY}) + private final int nodeId; + @Field + private final String link; + @Field(def = "CURRENT_TIMESTAMP") + private final Timestamp timestamp; + + public static String getLink(SchematicNode schem){ + if(schem.isDir()) + throw new SecurityException("Can not Download Directorys"); + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new SecurityException(e); + } + digest.reset(); + digest.update((Instant.now().toString() + schem.getOwner() + schem.getId()).getBytes()); + String hash = base16encode(digest.digest()); + insert.update(schem.getId(), hash); + return LINK_BASE + hash; + } + public static String base16encode(byte[] byteArray) { + StringBuilder hexBuffer = new StringBuilder(byteArray.length * 2); + for (byte b : byteArray) + hexBuffer.append(HEX[b >> 4]).append(HEX[b & 0xF]); + return hexBuffer.toString(); + } +} diff --git a/src/de/steamwar/sql/Punishment.java b/src/de/steamwar/sql/Punishment.java new file mode 100644 index 0000000..22f54ce --- /dev/null +++ b/src/de/steamwar/sql/Punishment.java @@ -0,0 +1,119 @@ +/* + * 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.Field; +import de.steamwar.sql.internal.SelectStatement; +import de.steamwar.sql.internal.SqlTypeMapper; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.sql.Timestamp; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class Punishment { + + static { + SqlTypeMapper.nameEnumMapper(PunishmentType.class); + } + + private static final Table table = new Table<>(Punishment.class, "Punishments"); + private static final SelectStatement getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)"); + private static final SelectStatement getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1"); + + @Field(keys = {Table.PRIMARY}, autoincrement = true) + private final int punishmentId; + @Field + @Getter + private final int user; + @Field + @Getter + private final int punisher; + @Field + @Getter + private final PunishmentType type; + @Field + @Getter + private final Timestamp startTime; + @Field + @Getter + private final Timestamp endTime; + @Field + @Getter + private final boolean perma; + @Field + @Getter + private final String reason; + + public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) { + return getPunishment.select(user, type); + } + + public static Map getPunishmentsOfPlayer(int user) { + return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment)); + } + + public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer callback) { + Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type); + if(punishment == null || !punishment.isCurrent()) { + return false; + } else { + callback.accept(punishment); + return true; + } + } + + @Deprecated // Not multiling, misleading title + public String getBantime(Timestamp endTime, boolean perma) { + if (perma) { + return "permanent"; + } else { + return endTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")); + } + } + + public boolean isCurrent() { + return isPerma() || getEndTime().after(new Date()); + } + + @AllArgsConstructor + @Getter + public enum PunishmentType { + Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"), + Mute( false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"), + NoSchemReceiving(false, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"), + NoSchemSharing(false, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"), + NoSchemSubmitting(true, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"), + NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"); + + private final boolean needsAdmin; + private final String teamMessage; + private final String playerMessagePerma; + private final String playerMessageUntil; + private final String usageNotPunished; + private final String unpunishmentMessage; + } +} diff --git a/src/de/steamwar/sql/SQLWrapper.java b/src/de/steamwar/sql/SQLWrapper.java index 81abe61..cff7dec 100644 --- a/src/de/steamwar/sql/SQLWrapper.java +++ b/src/de/steamwar/sql/SQLWrapper.java @@ -28,4 +28,6 @@ public interface SQLWrapper { SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl"); void loadSchemTypes(List tmpTypes, Map tmpFromDB); + + void additionalExceptionMetadata(StringBuilder builder); } diff --git a/src/de/steamwar/sql/SWException.java b/src/de/steamwar/sql/SWException.java new file mode 100644 index 0000000..af1f9c1 --- /dev/null +++ b/src/de/steamwar/sql/SWException.java @@ -0,0 +1,52 @@ +/* + * 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.Field; +import de.steamwar.sql.internal.Statement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; + +import java.io.File; + +@AllArgsConstructor +public class SWException { + + private static final String CWD = System.getProperty("user.dir"); + private static final String SERVER_NAME = new File(CWD).getName(); + + private static final Table table = new Table<>(SWException.class, "Exception"); + private static final Statement insert = table.insertAll(); + + @Field + private final String server; + @Field + private final String message; + @Field + private final String stacktrace; + + public static void log(String message, String stacktrace){ + StringBuilder msgBuilder = new StringBuilder(message); + SQLWrapper.impl.additionalExceptionMetadata(msgBuilder); + msgBuilder.append("\nCWD: ").append(CWD); + + insert.update(SERVER_NAME, msgBuilder.toString(), stacktrace); + } +} diff --git a/src/de/steamwar/sql/SchemElo.java b/src/de/steamwar/sql/SchemElo.java new file mode 100644 index 0000000..3aedaba --- /dev/null +++ b/src/de/steamwar/sql/SchemElo.java @@ -0,0 +1,43 @@ +/* + * 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.Field; +import de.steamwar.sql.internal.SelectStatement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class SchemElo { + + private static final Table table = new Table<>(SchemElo.class); + private static final SelectStatement select = table.select(Table.PRIMARY); + + @Field(keys = {Table.PRIMARY}) + private final int schemId; + @Field + private final int elo; + @Field(keys = {Table.PRIMARY}) + private final int season; + + public static int getElo(SchematicNode node, int season) { + return select.select(node, season).elo; + } +} diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java new file mode 100644 index 0000000..cc62acc --- /dev/null +++ b/src/de/steamwar/sql/SchematicNode.java @@ -0,0 +1,543 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.AccessLevel; +import lombok.Setter; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class SchematicNode { + + static { + new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId)); + } + + private static final Map>> TAB_CACHE = new HashMap<>(); + public static void clear() { + TAB_CACHE.clear(); + } + + private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"}; + private static String nodeSelectCreator(String itemPrefix) { + return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + " FROM SchematicNode "; + } + + private static final Table table = new Table<>(SchematicNode.class); + private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType"); + private static final Statement update = table.insertAll(); + private static final Statement delete = table.delete(Table.PRIMARY); + + private static final SelectStatement byId = table.select(Table.PRIMARY); + private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); + private static final SelectStatement byOwnerNameParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode is NULL"); + private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? ORDER BY NodeName"); + private static final SelectStatement byParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL ORDER BY NodeName"); + private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName"); + private static final SelectStatement dirsByParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName"); + private static final SelectStatement byParentName = table.selectFields("NodeName", "ParentNode"); + private static final SelectStatement byParentName_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeName = ? AND ParentNode is NULL"); + private static final SelectStatement accessibleByUserTypeParent = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName"); + private static final SelectStatement accessibleByUserTypeParent_Null = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode is null ORDER BY NodeName"); + private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName"); + private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); + private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); + private static final SelectStatement accessibleByUser = new SelectStatement<>(table, nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName"); + private static final Statement schematicAccessibleForUser = new Statement("WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1"); + private static final SelectStatement allAccessibleByUser = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + + @Field(keys = {Table.PRIMARY}, autoincrement = true) + private final int nodeId; + @Field(keys = {"OwnerNameParent"}) + private final int nodeOwner; + @Field(keys = {"OwnerNameParent"}) + private String nodeName; + @Field(keys = {"OwnerNameParent"}, nullable = true) + private Integer parentNode; + @Field(def = "CURRENT_TIMESTAMP") + private Timestamp lastUpdate; + @Field(def = "''") + private String nodeItem; + @Field(def = "'normal'", nullable = true) + private SchematicType nodeType; + @Field(def = "0") + private int nodeRank; + @Field(def = "1") + private boolean replaceColor; + @Field(def = "1") + private boolean allowReplay; + @Setter(AccessLevel.PACKAGE) + @Field(def = "1") + private boolean nodeFormat; + + private final Map brCache = new HashMap<>(); + + public SchematicNode( + int nodeId, + int nodeOwner, + String nodeName, + Integer parentNode, + Timestamp lastUpdate, + String nodeItem, + SchematicType nodeType, + int nodeRank, + boolean replaceColor, + boolean allowReplay, + boolean nodeFormat + ) { + this.nodeId = nodeId; + this.nodeOwner = nodeOwner; + this.nodeName = nodeName; + this.parentNode = parentNode; + this.nodeItem = nodeItem; + this.nodeType = nodeType; + this.lastUpdate = lastUpdate; + this.nodeRank = nodeRank; + this.replaceColor = replaceColor; + this.allowReplay = allowReplay; + this.nodeFormat = nodeFormat; + } + + public static SchematicNode createSchematic(int owner, String name, Integer parent) { + return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), ""); + } + + public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) { + return createSchematicNode(owner, name, parent, null, ""); + } + + public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) { + if (parent != null && parent == 0) + parent = null; + int nodeId = create.insertGetKey(owner, name, parent, type, item); + return getSchematicNode(nodeId); + } + + public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) { + return getSchematicNode(owner, name, parent.getId()); + } + + public static SchematicNode getSchematicNode(int owner, String name, Integer parent) { + if (parent == null || parent == 0) + return byOwnerNameParent_null.select(owner, name); + return byOwnerNameParent.select(owner, name, parent); + } + + public static List getSchematicNodeInNode(SchematicNode parent) { + return getSchematicNodeInNode(parent.getId()); + } + + public static List getSchematicNodeInNode(Integer parent) { + if(parent == null || parent == 0) { + rootWarning(); + return byParent_null.listSelect(); + } + + return byParent.listSelect(parent); + } + + public static List getSchematicDirectoryInNode(Integer parent) { + if(parent == null || parent == 0) { + rootWarning(); + return dirsByParent_null.listSelect(); + } + return dirsByParent.listSelect(parent); + } + + @Deprecated + public static SchematicNode getSchematicDirectory(String name, SchematicNode parent) { + return getSchematicNode(name, parent.getId()); + } + + @Deprecated + public static SchematicNode getSchematicDirectory(String name, Integer parent) { + return getSchematicNode(name, parent); + } + + public static SchematicNode getSchematicNode(String name, Integer parent) { + if(parent == null || parent == 0) { + rootWarning(); + return byParentName_null.select(name); + } + return byParentName.select(name, parent); + } + + public static SchematicNode getSchematicNode(int id) { + return byId.select(id); + } + + public static List getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) { + if(parent == null || parent == 0) + return accessibleByUserTypeParent_Null.listSelect(owner, owner, schemType); + return accessibleByUserTypeParent.listSelect(owner, owner, schemType, parent); + } + + public static List getAllAccessibleSchematicsOfType(int user, String schemType) { + return accessibleByUserType.listSelect(user, user, schemType); + } + + public static List getAllSchematicsOfType(int owner, String schemType) { + return byOwnerType.listSelect(owner, schemType); + } + + @Deprecated + public static List getAllSchematicsOfType(String schemType) { + return byType.listSelect(schemType); + } + + public static List getAllSchematicsOfType(SchematicType schemType) { + return byType.listSelect(schemType); + } + + public static List deepGet(Integer parent, Predicate filter) { + List finalList = new ArrayList<>(); + List nodes = SchematicNode.getSchematicNodeInNode(parent); + nodes.forEach(node -> { + if (node.isDir()) { + finalList.addAll(deepGet(node.getId(), filter)); + } else { + if (filter.test(node)) + finalList.add(node); + } + }); + return finalList; + } + + public static List getSchematicsAccessibleByUser(int user, Integer parent) { + if (parent == null || parent == 0) + return accessibleByUser.listSelect(user, user, user, user); + + if(schematicAccessibleForUser.select(rs -> { + rs.next(); + return rs.getInt("Accessible") > 0; + }, parent, user, user)) + return getSchematicNodeInNode(parent); + + return Collections.emptyList(); + } + + public static List getAllSchematicsAccessibleByUser(int user) { + return allAccessibleByUser.listSelect(user, user); + } + + public static List getAllParentsOfNode(SchematicNode node) { + return getAllParentsOfNode(node.getId()); + } + + public static List getAllParentsOfNode(int node) { + return allParentsOfNode.listSelect(node); + } + + public static SchematicNode getNodeFromPath(SteamwarUser user, String s) { + if (s.startsWith("/")) { + s = s.substring(1); + } + if (s.isEmpty()) { + return null; + } + if (s.contains("/")) { + String[] layers = s.split("/"); + SchematicNode currentNode = null; + for (int i = 0; i < layers.length; i++) { + int finalI = i; + Optional node; + if (currentNode == null) { + node = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny(); + } else { + node = Optional.ofNullable(SchematicNode.getSchematicNode(layers[i], currentNode.getId())); + } + if (!node.isPresent()) { + return null; + } else { + currentNode = node.get(); + if (!currentNode.isDir() && i != layers.length - 1) { + return null; + } + } + } + return currentNode; + } else { + String finalS = s; + return SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(finalS)).findAny().orElse(null); + } + } + + public static List filterSchems(int user, Predicate filter) { + List finalList = new ArrayList<>(); + List nodes = getSchematicsAccessibleByUser(user, null); + nodes.forEach(node -> { + if (node.isDir()) { + finalList.addAll(deepGet(node.getId(), filter)); + } else { + if (filter.test(node)) + finalList.add(node); + } + }); + return finalList; + } + + @Deprecated + public static Integer countNodes() { + return -1; + } + + public int getId() { + return nodeId; + } + + public int getOwner() { + return nodeOwner; + } + + public String getName() { + return nodeName; + } + + public void setName(String name) { + this.nodeName = name; + updateDB(); + } + + public Integer getParent() { + return parentNode; + } + + public void setParent(Integer parent) { + this.parentNode = parent; + updateDB(); + } + + public String getItem() { + if (nodeItem.isEmpty()) { + return isDir() ? "CHEST" : "CAULDRON_ITEM"; + } + return nodeItem; + } + + public void setItem(String item) { + this.nodeItem = item; + updateDB(); + } + + @Deprecated + public String getType() { + return nodeType.name(); + } + + @Deprecated + public void setType(String type) { + if(isDir()) + throw new SecurityException("Node is Directory"); + this.nodeType = SchematicType.fromDB(type); + updateDB(); + } + + public boolean isDir() { + return nodeType == null; + } + + public boolean getSchemFormat() { + if(isDir()) + throw new SecurityException("Node is Directory"); + return nodeFormat; + } + + public int getRank() { + if(isDir()) + throw new SecurityException("Node is Directory"); + return nodeRank; + } + + @Deprecated + public int getRankUnsafe() { + return nodeRank; + } + + public void setRank(int rank) { + if(isDir()) + throw new SecurityException("Node is Directory"); + this.nodeRank = rank; + } + + public SchematicType getSchemtype() { + if(isDir()) + throw new SecurityException("Is Directory"); + return nodeType; + } + + public void setSchemtype(SchematicType type) { + if(isDir()) + throw new SecurityException("Is Directory"); + this.nodeType = type; + updateDB(); + } + + public boolean replaceColor() { + return replaceColor; + } + + public void setReplaceColor(boolean replaceColor) { + if(isDir()) + throw new SecurityException("Is Directory"); + this.replaceColor = replaceColor; + updateDB(); + } + + public boolean allowReplay() { + return allowReplay; + } + + public void setAllowReplay(boolean allowReplay) { + if(isDir()) + throw new SecurityException("Is Directory"); + this.allowReplay = allowReplay; + updateDB(); + } + + public SchematicNode getParentNode() { + if(parentNode == null) return null; + return SchematicNode.getSchematicNode(parentNode); + } + + public int getElo(int season) { + return SchemElo.getElo(this, season); + } + + public boolean accessibleByUser(int user) { + return NodeMember.getNodeMember(nodeId, user) != null; + } + + public Set getMembers() { + return NodeMember.getNodeMembers(nodeId); + } + + public Timestamp getLastUpdate() { + return lastUpdate; + } + + private void updateDB() { + this.lastUpdate = Timestamp.from(Instant.now()); + update.update(nodeId, nodeOwner, nodeName, parentNode, nodeItem, nodeType, lastUpdate, nodeRank, replaceColor, allowReplay, nodeFormat); + this.brCache.clear(); + TAB_CACHE.clear(); + } + + public void delete() { + delete.update(nodeId); + } + + @Override + public int hashCode() { + return nodeId; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SchematicNode)) + return false; + + return ((SchematicNode) obj).getId() == nodeId; + } + + public String generateBreadcrumbs(SteamwarUser user) { + return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user)); + } + + public String generateBreadcrumbs(String split, SteamwarUser user) { + StringBuilder builder = new StringBuilder(getName()); + SchematicNode currentNode = this; + if (currentNode.isDir()) builder.append("/"); + final Set nodeMembers = NodeMember.getSchematics(user.getId()); + AtomicInteger i = new AtomicInteger(); + i.set(currentNode.getId()); + while (currentNode.getParentNode() != null && nodeMembers.stream().noneMatch(nodeMember -> nodeMember.getNode() == i.get())) { + currentNode = currentNode.getParentNode(); + i.set(currentNode.getId()); + builder.insert(0, split) + .insert(0, currentNode.getName()); + } + return builder.toString(); + } + + private static final List FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public")); + public static boolean invalidSchemName(String[] layers) { + for (String layer : layers) { + if (layer.isEmpty()) { + return true; + } + if (layer.contains("/") || + layer.contains("\\") || + layer.contains("<") || + layer.contains(">") || + layer.contains("^") || + layer.contains("°") || + layer.contains("'") || + layer.contains("\"") || + layer.contains(" ")) { + return true; + } + if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) { + return true; + } + } + return false; + } + + public static List getNodeTabcomplete(SteamwarUser user, String s) { + boolean sws = s.startsWith("/"); + if (sws) { + s = s.substring(1); + } + int index = s.lastIndexOf("/"); + String cacheKey = index == -1 ? "" : s.substring(0, index); + if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) { + return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey)); + } + List list = new ArrayList<>(); + if (s.contains("/")) { + String preTab = s.substring(0, s.lastIndexOf("/") + 1); + SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab); + if (pa == null) return Collections.emptyList(); + List nodes = SchematicNode.getSchematicNodeInNode(pa); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user))); + } else { + List nodes = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); + } + list.remove("//copy"); + TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list); + return list; + } + + private static void rootWarning() { + ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream(); + new Throwable().printStackTrace(new PrintStream(stacktraceOutput)); + SWException.log("PERFORMANCE!!! Getting all/weird subset of schematic nodes with parent NULL", stacktraceOutput.toString()); + } +} diff --git a/src/de/steamwar/sql/SchematicType.java b/src/de/steamwar/sql/SchematicType.java index 56c46ad..d87065c 100644 --- a/src/de/steamwar/sql/SchematicType.java +++ b/src/de/steamwar/sql/SchematicType.java @@ -101,7 +101,7 @@ public class SchematicType { } public static SchematicType fromDB(String input){ - return fromDB.getOrDefault(input.toLowerCase(), null); + return fromDB.get(input.toLowerCase()); } public static List values(){ diff --git a/src/de/steamwar/sql/Season.java b/src/de/steamwar/sql/Season.java new file mode 100644 index 0000000..8768ad8 --- /dev/null +++ b/src/de/steamwar/sql/Season.java @@ -0,0 +1,54 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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 java.util.Calendar; + +public class Season { + private Season() {} + + public static int getSeason() { + Calendar calendar = Calendar.getInstance(); + int yearIndex = calendar.get(Calendar.MONTH) / 4; + return (calendar.get(Calendar.YEAR) * 3 + yearIndex); + } + + public static String getSeasonStart() { + Calendar calendar = Calendar.getInstance(); + return calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) / 4 * 3 + 1) + "-1"; + } + + public static String convertSeasonToString(int season){ + if (season == -1) return ""; + int yearSeason = season % 3; + int year = (season - yearSeason) / 3; + return String.format("%d-%d", year, yearSeason); + } + + public static int convertSeasonToNumber(String season){ + if (season.isEmpty()) return -1; + String[] split = season.split("-"); + try { + return Integer.parseInt(split[0]) * 3 + Integer.parseInt(split[1]); + } catch (NumberFormatException e) { + return -1; + } + } +} diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index 7245db7..c5ccbd5 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -42,6 +42,7 @@ public class SteamwarUser { 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 updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale"); @@ -147,7 +148,15 @@ public class SteamwarUser { return byDiscord.select(discordId); } + public static void createOrUpdateUsername(UUID uuid, String userName) { + insert.update(uuid, userName); + } + public static List getServerTeam() { return getServerTeam.listSelect(); } + + public static List getTeam(int teamId) { + return byTeam.listSelect(teamId); + } } diff --git a/src/de/steamwar/sql/Team.java b/src/de/steamwar/sql/Team.java new file mode 100644 index 0000000..10e6892 --- /dev/null +++ b/src/de/steamwar/sql/Team.java @@ -0,0 +1,61 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 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.Field; +import de.steamwar.sql.internal.SelectStatement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class Team { + + private static final Table table = new Table<>(Team.class); + private static final SelectStatement select = table.select(Table.PRIMARY); + + @Field(keys = {Table.PRIMARY}) + @Getter + private final int teamId; + @Field + @Getter + private final String teamKuerzel; + @Field + @Getter + private final String teamName; + @Field(def = "'8'") + @Getter + private final String teamColor; + + private static final Team pub = new Team(0, "PUB", "Öffentlich", "8"); + + public static Team get(int id) { + if(id == 0) + return pub; + return select.select(id); + } + + public List getMembers(){ + return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList()); + } +} diff --git a/src/de/steamwar/sql/TeamTeilnahme.java b/src/de/steamwar/sql/TeamTeilnahme.java new file mode 100644 index 0000000..533b830 --- /dev/null +++ b/src/de/steamwar/sql/TeamTeilnahme.java @@ -0,0 +1,54 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.Field; +import de.steamwar.sql.internal.SelectStatement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; + +import java.util.Set; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class TeamTeilnahme { + + private static final Table table = new Table<>(TeamTeilnahme.class); + private static final SelectStatement select = table.select(Table.PRIMARY); + private static final SelectStatement selectTeams = table.selectFields("EventID"); + private static final SelectStatement selectEvents = table.selectFields("TeamID"); + + @Field(keys = {Table.PRIMARY}) + private final int teamId; + @Field(keys = {Table.PRIMARY}) + private final int eventId; + + public static boolean nimmtTeil(int teamID, int eventID){ + return select.select(teamID, eventID) != null; + } + + public static Set getTeams(int eventID){ + return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries) + } + + public static Set getEvents(int teamID){ + return selectEvents.listSelect(teamID).stream().map(tt -> Event.get(tt.eventId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries) + } +} diff --git a/src/de/steamwar/sql/UserConfig.java b/src/de/steamwar/sql/UserConfig.java new file mode 100644 index 0000000..0b45f1f --- /dev/null +++ b/src/de/steamwar/sql/UserConfig.java @@ -0,0 +1,72 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.Field; +import de.steamwar.sql.internal.SelectStatement; +import de.steamwar.sql.internal.Statement; +import de.steamwar.sql.internal.Table; +import lombok.AllArgsConstructor; + +import java.util.UUID; + +@AllArgsConstructor +public class UserConfig { + + private static final Table table = new Table<>(UserConfig.class); + private static final SelectStatement select = table.select(Table.PRIMARY); + private static final Statement insert = table.insertAll(); + private static final Statement delete = table.delete(Table.PRIMARY); + + @Field(keys = {Table.PRIMARY}) + private final int user; + @Field(keys = {Table.PRIMARY}) + private final String config; + @Field + private final String value; + + public static String getConfig(UUID player, String config) { + return getConfig(SteamwarUser.get(player).getId(), config); + } + + public static String getConfig(int player, String config) { + return select.select(player, config).value; + } + + public static void updatePlayerConfig(UUID uuid, String config, String value) { + updatePlayerConfig(SteamwarUser.get(uuid).getId(), config, value); + } + + public static void updatePlayerConfig(int id, String config, String value) { + if (value == null) { + removePlayerConfig(id, config); + return; + } + insert.update(id, config, value); + } + + public static void removePlayerConfig(UUID uuid, String config) { + removePlayerConfig(SteamwarUser.get(uuid).getId(), config); + } + + public static void removePlayerConfig(int id, String config) { + delete.update(id, config); + } +} diff --git a/src/de/steamwar/sql/internal/Statement.java b/src/de/steamwar/sql/internal/Statement.java index 2e9edb8..c436fc2 100644 --- a/src/de/steamwar/sql/internal/Statement.java +++ b/src/de/steamwar/sql/internal/Statement.java @@ -39,10 +39,14 @@ public class Statement implements AutoCloseable { private static final Supplier conProvider; static final Consumer> schemaCreator; + private static final boolean mysqlMode; + private static final boolean productionDatabase; + static { File file = new File(System.getProperty("user.home"), "mysql.properties"); + mysqlMode = file.exists(); - if(file.exists()) { + if(mysqlMode) { Properties properties = new Properties(); try { properties.load(new FileReader(file)); @@ -54,6 +58,7 @@ public class Statement implements AutoCloseable { String user = properties.getProperty("user"); String password = properties.getProperty("password"); + productionDatabase = "core".equals(properties.getProperty("database")); MAX_CONNECTIONS = SQLConfig.impl.maxConnections(); conProvider = () -> { try { @@ -64,7 +69,6 @@ public class Statement implements AutoCloseable { }; schemaCreator = table -> {}; } else { - MAX_CONNECTIONS = 1; Connection connection; try { @@ -74,6 +78,8 @@ public class Statement implements AutoCloseable { throw new SecurityException("Could not create sqlite connection", e); } + productionDatabase = false; + MAX_CONNECTIONS = 1; conProvider = () -> connection; schemaCreator = Table::ensureExistanceInSqlite; } @@ -92,6 +98,14 @@ public class Statement implements AutoCloseable { } } + public static boolean mysqlMode() { + return mysqlMode; + } + + public static boolean productionDatabase() { + return productionDatabase; + } + private final boolean returnGeneratedKeys; private final String sql; private final Map cachedStatements = new HashMap<>(); diff --git a/src/de/steamwar/sql/internal/Table.java b/src/de/steamwar/sql/internal/Table.java index ffef01c..937f54d 100644 --- a/src/de/steamwar/sql/internal/Table.java +++ b/src/de/steamwar/sql/internal/Table.java @@ -59,7 +59,6 @@ public class Table { Statement.schemaCreator.accept(this); } - public SelectStatement select(String name) { return selectFields(keyFields(name)); } @@ -108,7 +107,7 @@ public class Table { List> primaryKey = keys.containsKey(PRIMARY) ? Arrays.asList(keys.get(PRIMARY)) : Collections.emptyList(); try (Statement statement = new Statement( "CREATE TABLE IF NOT EXISTS " + name + "(" + - Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? "" : " NOT NULL DEFAULT NULL") + (!field.field.nullable() && field.field.def().equals("") ? "" : " DEFAULT " + field.field.def()) + (primaryKey.contains(field) ? " PRIMARY KEY" : "") + (field.field.autoincrement() ? " AUTOINCREMENT" : "")).collect(Collectors.joining(", ")) + + Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def()) + (primaryKey.contains(field) ? " PRIMARY KEY" : "") + (field.field.autoincrement() ? " AUTOINCREMENT" : "")).collect(Collectors.joining(", ")) + keys.entrySet().stream().filter(entry -> !entry.getKey().equals(PRIMARY)).map(key -> ", UNIQUE (" + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) + ") STRICT, WITHOUT ROWID")) { statement.update();