diff --git a/BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/PlayerMovementWrapper21.java b/BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/PlayerMovementWrapper21.java new file mode 100644 index 00000000..e62e4591 --- /dev/null +++ b/BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/PlayerMovementWrapper21.java @@ -0,0 +1,46 @@ +/* + * 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 . + */ + +package de.steamwar.bausystem.utils; + +import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; + +public class PlayerMovementWrapper21 implements PlayerMovementWrapper { + + @Override + public void setPosition(Player player, Object object) { + ServerboundMovePlayerPacket packetPlayInFlying = ((ServerboundMovePlayerPacket) object); + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + if (packetPlayInFlying.hasPos) { + serverPlayer.setPosRaw(packetPlayInFlying.x, packetPlayInFlying.y, packetPlayInFlying.z); + } + if (packetPlayInFlying.hasRot) { + serverPlayer.setXRot(packetPlayInFlying.xRot); + serverPlayer.setYRot(packetPlayInFlying.yRot); + } + } + + @Override + public Object convertToOut(Player player, Object object) { + return object; + } +} diff --git a/BauSystem/BauSystem_Main/src/BauSystem.properties b/BauSystem/BauSystem_Main/src/BauSystem.properties index b90933b6..e6c490d7 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem.properties @@ -1030,6 +1030,7 @@ SCHEMATIC_GUI_ITEM=§eSchematics # TNTListener TLS_MESSAGE_79=§7TLS§8> §7Tick §e{0} §8- §7TNT §e{1} TLS_MESSAGE_80=§7TLS§8> §7Tick §e{0} §8- §7TNT §e{1} §8(§e{2} §7with Fuse 80§8) -TLS_START_HELP=§8/§etls start §8: §7Start the TNT Listener -TLS_STOP_HELP=§8/§etls stop §8: §7Stop the TNT Listener -TLS_SCOREBOARD_ELEMENT=§eTLS§8: §aon \ No newline at end of file +TLS_START_HELP=§8/§etls start §8- §7Start the TNT Listener +TLS_STOP_HELP=§8/§etls stop §8- §7Stop the TNT Listener +TLS_SCOREBOARD_ELEMENT=§eTLS§8: §aon +TLS_TOGGLE_HELP=§8/§etls§8: §7Toggle the TNT Listener \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/BauSystem_de.properties b/BauSystem/BauSystem_Main/src/BauSystem_de.properties index 453b4079..f0d55c5c 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem_de.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem_de.properties @@ -961,6 +961,7 @@ TYPEREPLACE_HELP=§8//§etyreplace §8[§7type§8] §8[§7type§8] §8- §7Erset # Schematics SCHEMATIC_GUI_ITEM=§eSchematics TLS_MESSAGE_80=§7TLS§8> §7Tick §e{0} §8- §7TNT §e{1} §8(§e{2} §7mit Fuse 80§8) -TLS_START_HELP=§8/§etls start §8: §7Starte den TNT Listener -TLS_STOP_HELP=§8/§etls stop §8: §7Stope den TNT Listener -TLS_SCOREBOARD_ELEMENT=§eTLS§8: §aan \ No newline at end of file +TLS_START_HELP=§8/§etls start §8- §7Starte den TNT Listener +TLS_STOP_HELP=§8/§etls stop §8- §7Stope den TNT Listener +TLS_SCOREBOARD_ELEMENT=§eTLS§8: §aan +TLS_TOGGLE_HELP=§8/§etls §8: §7Toggle den TNT Listener \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java index ecc1df44..1cbb5d9f 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java @@ -27,8 +27,6 @@ import de.steamwar.sql.SteamwarUser; import lombok.AllArgsConstructor; import org.bukkit.entity.Player; -import java.util.HashSet; -import java.util.Set; import java.util.function.Predicate; @AllArgsConstructor @@ -39,13 +37,12 @@ public enum Permission { return bauweltMember.isSupervisor(); }), BUILD(bauweltMember -> { - if (isTempOnlySpectator(bauweltMember)) return false; return bauweltMember.isBuild() || SUPERVISOR.permissionPredicate.test(bauweltMember); }), /** * Only used for {@link BauMemberUpdate} */ - REAL_SPECTATOR(bauweltMember -> { + SPECTATOR(bauweltMember -> { return !bauweltMember.isBuild() && !bauweltMember.isSupervisor(); }), /** @@ -55,28 +52,6 @@ public enum Permission { return true; }); - private static final Set TEMP_ONLY_SPECTATOR = new HashSet<>(); - - private static boolean isTempOnlySpectator(BauweltMember bauweltMember) { - return TEMP_ONLY_SPECTATOR.contains(bauweltMember.getMemberID()); - } - - public static boolean isTempOnlySpectator(Player player) { - return TEMP_ONLY_SPECTATOR.contains(SteamwarUser.get(player.getUniqueId()).getId()); - } - - public static void forceOnlySpectator(Player player) { - TEMP_ONLY_SPECTATOR.add(SteamwarUser.get(player.getUniqueId()).getId()); - BauMemberUpdate.baumemberUpdate(); - } - - /** - * Only used by {@link BauMemberUpdate} - */ - public static void removeForceOnlySpectator(Player player) { - TEMP_ONLY_SPECTATOR.remove(SteamwarUser.get(player.getUniqueId()).getId()); - } - private final Predicate permissionPredicate; public boolean hasPermission(BauweltMember bauweltMember) { @@ -86,10 +61,10 @@ public enum Permission { public boolean hasPermission(Player member) { if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) { - return this != REAL_SPECTATOR; + return this != SPECTATOR; } BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId()); - if (bauweltMember == null) return this == REAL_SPECTATOR; + if (bauweltMember == null) return this == SPECTATOR; return permissionPredicate.test(bauweltMember); } } \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java index fea634a2..67fd9e52 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java @@ -36,7 +36,7 @@ public class BauServer { private Integer owner; public UUID getOwner() { - return SteamwarUser.get(getOwnerID()).getUUID(); + return SteamwarUser.byId(getOwnerID()).getUUID(); } public int getOwnerID() { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java index d45a7029..efa7f199 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java @@ -32,6 +32,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.data.type.Chest; import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityExplodeEvent; @@ -120,6 +121,7 @@ public class AutostartListener implements Listener { @EventHandler public void onEntityExplode(EntityExplodeEvent event) { + if (!(event.getEntity() instanceof TNTPrimed)) return; if (regionStartTime.isEmpty()) { return; } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java deleted file mode 100644 index a4e79c2f..00000000 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.bausystem.features.bau; - -import de.steamwar.bausystem.Permission; -import de.steamwar.command.PreviousArguments; -import de.steamwar.command.SWCommand; -import de.steamwar.command.TypeMapper; -import de.steamwar.linkage.Linked; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.util.Collection; -import java.util.stream.Collectors; - -@Linked -public class ForceSpectatorCommand extends SWCommand { - - public ForceSpectatorCommand() { - super("forcespectator"); - } - - @Register - public void forceSpectator(@Validator("supervisor") Player player, @Mapper("builder") Player other) { - Permission.forceOnlySpectator(other); - } - - @Mapper("builder") - public TypeMapper spectatorMapper() { - return new TypeMapper<>() { - @Override - public Player map(CommandSender commandSender, String[] previousArguments, String s) { - Player player = Bukkit.getPlayer(s); - if (player == null) { - return null; - } - if (Permission.BUILD.hasPermission(player) && !Permission.SUPERVISOR.hasPermission(player)) { - return player; - } - return null; - } - - @Override - public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { - return Bukkit.getOnlinePlayers().stream() - .filter(Permission.BUILD::hasPermission) - .filter(player -> !Permission.SUPERVISOR.hasPermission(player)) - .map(Player::getName) - .collect(Collectors.toList()); - } - }; - } -} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java index adeb4c45..e5bce542 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java @@ -47,7 +47,7 @@ public class InfoCommand extends SWCommand { @Register(description = "BAU_INFO_COMMAND_HELP") public void genericCommand(Player p) { - BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.get(bauServer.getOwnerID()).getUserName()); + BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName()); Region region = Region.getRegion(p.getLocation()); for (Flag flag : Flag.getFlags()) { if (!region.getFlags().has(flag).isApplicable()) continue; @@ -97,7 +97,7 @@ public class InfoCommand extends SWCommand { st.append("§8, "); } st.append("§7"); - st.append(SteamwarUser.get(bauweltMembers.get(i).getMemberID()).getUserName()); + st.append(SteamwarUser.byId(bauweltMembers.get(i).getMemberID()).getUserName()); } return st.toString(); } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java index bcd13a7f..0fbaf1b7 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java @@ -21,9 +21,10 @@ package de.steamwar.bausystem.features.countingwand; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.region.Point; -import de.steamwar.bausystem.shared.Pair; import de.steamwar.bausystem.utils.ItemUtils; +import de.steamwar.core.SWPlayer; import de.steamwar.inventory.SWItem; +import lombok.Data; import lombok.experimental.UtilityClass; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -31,13 +32,32 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - @UtilityClass public class Countingwand { + @Data + public class CountingWandComponent implements SWPlayer.Component { + private Point pos1; + private Point pos2; + + public boolean setPosition(boolean pos1, Point point) { + if (this.pos1 == null || this.pos2 == null) { + this.pos1 = point; + this.pos2 = point; + return true; + } + Point current = pos1 ? this.pos1 : this.pos2; + if (current.equals(point)) return false; + if (pos1) { + this.pos1 = point; + } else { + this.pos2 = point; + } + return true; + } + } + public static ItemStack getWandItem(Player player) { ItemStack itemStack = new SWItem(Material.STICK, BauSystem.MESSAGE.parse("COUNTINGWAND_ITEM_NAME", player), Arrays.asList(BauSystem.MESSAGE.parse("COUNTINGWAND_ITEM_LORE1", player), BauSystem.MESSAGE.parse("COUNTINGWAND_ITEM_LORE2", player)), false, null).getItemStack(); ItemUtils.setItem(itemStack, "countingwand"); @@ -47,61 +67,20 @@ public class Countingwand { return itemStack; } - private final Map> selections = new HashMap<>(); + public boolean isCountingwand(ItemStack itemStack) { + return ItemUtils.isItem(itemStack, "countingwand"); + } - public boolean isCountingwand(ItemStack itemStack) { - return ItemUtils.isItem(itemStack, "countingwand"); - } - - public void checkSelection(final Point point, final boolean pos1, final Player p) { - Pair selection = selections.get(p.getUniqueId().toString()); - final boolean newPos; - if (selection != null) { - if (pos1) { - newPos = !point.equals(selection.setKey(point)); - } else { - newPos = !point.equals(selection.setValue(point)); - } - } else { - if (pos1) { - selection = new Pair<>(point, null); - } else { - selection = new Pair<>(null, point); - } - selections.put(p.getUniqueId().toString(), selection); - newPos = true; - } - - if (newPos) { - String dimension = getDimensions(p, selection.getKey(), selection.getValue()); - String volume = getVolume(p, selection.getKey(), selection.getValue()); - if (pos1) { - BauSystem.MESSAGE.send("COUNTINGWAND_MESSAGE_RCLICK", p, point.getX(), point.getY(), point.getZ(), dimension, volume); - } else { - BauSystem.MESSAGE.send("COUNTINGWAND_MESSAGE_LCLICK", p, point.getX(), point.getY(), point.getZ(), dimension, volume); - } - } - } - - public void removePlayer(Player p) { - selections.remove(p.getUniqueId().toString()); - } - - public String getVolume(Player player, final Point point1, final Point point2) { - if (point1 == null || point2 == null) { - return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_VOLUME", player, 0); - } - return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_VOLUME", player, getAmount(point1, point2)); - } - - public String getDimensions(Player player, final Point point1, final Point point2) { - if (point1 == null || point2 == null) { - return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_DIMENSION", player, 0, 0, 0); - } - return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_DIMENSION", player, Math.abs(point1.getX() - point2.getX()) + 1, Math.abs(point1.getY() - point2.getY()) + 1, Math.abs(point1.getZ() - point2.getZ()) + 1); - } - - public int getAmount(final Point point1, final Point point2) { - return (Math.abs(point1.getX() - point2.getX()) + 1) * (Math.abs(point1.getY() - point2.getY()) + 1) * (Math.abs(point1.getZ() - point2.getZ()) + 1); - } + public void checkSelection(final Point point, final boolean pos1, final Player p) { + SWPlayer player = SWPlayer.of(p); + CountingWandComponent countingWandComponent = player.getComponentOrDefault(CountingWandComponent.class, CountingWandComponent::new); + if (countingWandComponent.setPosition(pos1, point)) { + Point point1 = countingWandComponent.pos1; + Point point2 = countingWandComponent.pos2; + int amount = (Math.abs(point1.getX() - point2.getX()) + 1) * (Math.abs(point1.getY() - point2.getY()) + 1) * (Math.abs(point1.getZ() - point2.getZ()) + 1); + String dimension = player.using(BauSystem.MESSAGE).parse("COUNTINGWAND_MESSAGE_VOLUME", amount); + String volume = player.parse("COUNTINGWAND_MESSAGE_DIMENSION", Math.abs(point1.getX() - point2.getX()) + 1, Math.abs(point1.getY() - point2.getY()) + 1, Math.abs(point1.getZ() - point2.getZ()) + 1); + player.sendMessage(pos1 ? "COUNTINGWAND_MESSAGE_RCLICK" : "COUNTINGWAND_MESSAGE_LCLICK", point.getX(), point.getY(), point.getZ(), dimension, volume); + } + } } \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java index 127bb964..9daab3f0 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java @@ -27,7 +27,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.util.RayTraceResult; import java.util.Objects; @@ -67,9 +66,4 @@ public class CountingwandListener implements Listener { event.setCancelled(true); Countingwand.checkSelection(Point.fromLocation(Objects.requireNonNull(event.getClickedBlock()).getLocation()), false, event.getPlayer()); } - - @EventHandler - public void onLeave(PlayerQuitEvent event) { - Countingwand.removePlayer(event.getPlayer()); - } } \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java index 5ba2bca8..51d5e4e5 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java @@ -25,8 +25,11 @@ import de.steamwar.bausystem.configplayer.Config; import de.steamwar.bausystem.features.autostart.AutostartListener; import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage; import de.steamwar.bausystem.features.detonator.storage.ItemStorage; +import de.steamwar.core.SWPlayer; import de.steamwar.entity.REntityServer; import de.steamwar.entity.RFallingBlockEntity; +import lombok.Getter; +import lombok.Setter; import lombok.experimental.UtilityClass; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -45,7 +48,28 @@ import java.util.*; @UtilityClass public class Detonator { - private static final Map ENTITIES_MAP = new HashMap<>(); + public class DetonatorComponent implements SWPlayer.Component { + private final REntityServer entities = new REntityServer(); + + @Getter + @Setter + private boolean hasUpdated = false; + + @Override + public void onMount(SWPlayer player) { + entities.addPlayer(player.getPlayer()); + entities.setCallback((player1, entity, action) -> { + Vector vector = new Vector(entity.getX(), entity.getY(), entity.getZ()); + DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player1); + }); + } + + @Override + public void onUnmount(SWPlayer player) { + entities.close(); + } + } + private static final Vector HALF = new Vector(0.5, 0, 0.5); public static boolean isDetonator(ItemStack itemStack) { @@ -53,28 +77,19 @@ public class Detonator { } public static void showDetonator(Player p, List locs) { - if (ENTITIES_MAP.containsKey(p)) return; - REntityServer entities = new REntityServer(); - entities.setCallback((player, rEntity, entityAction) -> { - Vector vector = new Vector(rEntity.getX(), rEntity.getY(), rEntity.getZ()); - DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player); - DetonatorListener.HAS_UPDATED.add(player); - }); - entities.addPlayer(p); - ENTITIES_MAP.put(p, entities); - + DetonatorComponent detonatorComponent = SWPlayer.of(p).getComponentOrDefault(DetonatorComponent.class, DetonatorComponent::new); locs.forEach(location -> { - RFallingBlockEntity entity = new RFallingBlockEntity(entities, location.clone().add(HALF), Material.RED_STAINED_GLASS); + RFallingBlockEntity entity = new RFallingBlockEntity(detonatorComponent.entities, location.clone().add(HALF), Material.RED_STAINED_GLASS); entity.setNoGravity(true); }); } public static void hideDetonator(Player p) { - ENTITIES_MAP.remove(p).close(); + SWPlayer.of(p).removeComponent(DetonatorComponent.class); } public static boolean hasActiveDetonatorShow(Player p) { - return ENTITIES_MAP.containsKey(p); + return SWPlayer.of(p).hasComponent(DetonatorComponent.class); } public static void activateDetonator(DetonatorStorage detonator) { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java index 9d70d11a..d8cd57e3 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java @@ -24,6 +24,7 @@ import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.SWUtils; import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage; import de.steamwar.bausystem.features.detonator.storage.ItemStorage; +import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -36,15 +37,11 @@ import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerSwapHandItemsEvent; -import java.util.HashSet; -import java.util.Set; - @Linked public class DetonatorListener implements Listener { - static final Set HAS_UPDATED = new HashSet<>(); - static void addLocationToDetonator(Location location, Player p) { + SWPlayer.of(p).getComponent(Detonator.DetonatorComponent.class).ifPresent(detonatorComponent -> detonatorComponent.setHasUpdated(true)); Detoblock detoblock = Detonator.getBlock(location.getBlock()); if (detoblock == Detoblock.INVALID) { SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_INVALID_BLOCK", p)); @@ -66,64 +63,59 @@ public class DetonatorListener implements Listener { @EventHandler public void onBlockBreak(BlockBreakEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; Player p = event.getPlayer(); if (Detonator.isDetonator(p.getInventory().getItemInMainHand())) { event.setCancelled(true); addLocationToDetonator(event.getBlock().getLocation(), p); - HAS_UPDATED.add(event.getPlayer()); } } @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (!Detonator.isDetonator(event.getItem())) { - return; - } + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!Detonator.isDetonator(event.getItem())) return; if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) { event.setCancelled(true); DetonatorStorage detonator = new ItemStorage(event.getPlayer()); Detonator.activateDetonator(detonator); - HAS_UPDATED.add(event.getPlayer()); + SWPlayer.of(event.getPlayer()).getComponent(Detonator.DetonatorComponent.class).ifPresent(detonatorComponent -> detonatorComponent.setHasUpdated(true)); } } @EventHandler(ignoreCancelled = true) public void onPlayerMove(PlayerMoveEvent event) { - if (!Permission.BUILD.hasPermission(event.getPlayer()) ||!Detonator.isDetonator(event.getPlayer().getInventory().getItemInMainHand())) { + if (!Permission.BUILD.hasPermission(event.getPlayer()) || !Detonator.isDetonator(event.getPlayer().getInventory().getItemInMainHand())) { if (Detonator.hasActiveDetonatorShow(event.getPlayer())) { Detonator.hideDetonator(event.getPlayer()); } - } else { - if (!Detonator.hasActiveDetonatorShow(event.getPlayer())) { - Detonator.showDetonator(event.getPlayer(), new ItemStorage(event.getPlayer()).getLocations()); - } + return; } - - if (HAS_UPDATED.contains(event.getPlayer())) { - HAS_UPDATED.remove(event.getPlayer()); - if (Detonator.hasActiveDetonatorShow(event.getPlayer())) { - Detonator.hideDetonator(event.getPlayer()); - Detonator.showDetonator(event.getPlayer(), new ItemStorage(event.getPlayer()).getLocations()); - } + if (!Detonator.hasActiveDetonatorShow(event.getPlayer())) { + Detonator.showDetonator(event.getPlayer(), new ItemStorage(event.getPlayer()).getLocations()); + return; } + Detonator.DetonatorComponent component = SWPlayer.of(event.getPlayer()) + .getComponentAndFilter(Detonator.DetonatorComponent.class, Detonator.DetonatorComponent::isHasUpdated) + .orElse(null); + if (component == null) return; + component.setHasUpdated(false); + Detonator.hideDetonator(event.getPlayer()); + Detonator.showDetonator(event.getPlayer(), new ItemStorage(event.getPlayer()).getLocations()); } @EventHandler public void onPlayerItemHeld(PlayerItemHeldEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (Detonator.isDetonator(event.getPlayer().getInventory().getItemInMainHand())) { - HAS_UPDATED.add(event.getPlayer()); - } + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!Detonator.isDetonator(event.getPlayer().getInventory().getItemInMainHand())) return; + SWPlayer.of(event.getPlayer()).getComponent(Detonator.DetonatorComponent.class).ifPresent(detonatorComponent -> detonatorComponent.setHasUpdated(true)); } @EventHandler public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (Detonator.isDetonator(event.getMainHandItem()) || Detonator.isDetonator(event.getOffHandItem())) { - HAS_UPDATED.add(event.getPlayer()); - } + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!(Detonator.isDetonator(event.getMainHandItem()) || Detonator.isDetonator(event.getOffHandItem()))) return; + SWPlayer.of(event.getPlayer()).getComponent(Detonator.DetonatorComponent.class).ifPresent(detonatorComponent -> detonatorComponent.setHasUpdated(true)); } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java index 491f45aa..4f990052 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java @@ -26,6 +26,7 @@ import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement; import de.steamwar.bausystem.features.loader.elements.impl.LoaderTNT; import de.steamwar.bausystem.features.loader.elements.impl.LoaderWait; import de.steamwar.bausystem.shared.EnumDisplay; +import de.steamwar.core.SWPlayer; import de.steamwar.inventory.SWAnvilInv; import de.steamwar.inventory.SWItem; import de.steamwar.inventory.SWListInv; @@ -40,21 +41,17 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.event.player.PlayerQuitEvent; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -public class Loader implements Listener { - - private static final Map LOADER_MAP = new HashMap<>(); +public class Loader implements Listener, SWPlayer.Component { public static Loader getLoader(Player player) { - return LOADER_MAP.get(player); + return SWPlayer.of(player).getComponent(Loader.class).orElse(null); } public static void newLoader(Player player) { - LOADER_MAP.put(player, new Loader(player)); + SWPlayer.of(player).setComponent(new Loader(player)); } private final Player p; @@ -145,7 +142,7 @@ public class Loader implements Listener { recorder = null; } elements.clear(); - LOADER_MAP.remove(p); + SWPlayer.of(p).removeComponent(Loader.class); } public boolean setTicksBetweenShots(int delay) { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java index f1a42e1a..a7d642fe 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java @@ -29,6 +29,7 @@ import org.bukkit.boss.BarColor; import org.bukkit.boss.BarStyle; import org.bukkit.boss.BossBar; import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; @@ -156,6 +157,7 @@ public class Loadtimer implements Listener { } public void onTntExplode(EntityExplodeEvent event) { + if (!(event.getEntity() instanceof TNTPrimed)) return; if (region.getBuildArea().inRegion(event.getLocation(), true) && stage == Stage.IGNITION) { stage = Stage.END; explode = TPSUtils.currentRealTick.get(); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java index ad0bca1b..6dad9e00 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java @@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.observer; import de.steamwar.Reflection; import de.steamwar.bausystem.region.Point; +import de.steamwar.core.SWPlayer; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; @@ -34,7 +35,7 @@ import org.bukkit.entity.Player; import java.util.*; -public class ObserverTracer { +public class ObserverTracer implements SWPlayer.Component { private static final Set ALLOWED = EnumSet.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerCommand.java index 7cfadf2c..e88e4ca0 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerCommand.java @@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.observer; import de.steamwar.bausystem.BauSystem; import de.steamwar.command.SWCommand; +import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; import org.bukkit.entity.Player; @@ -41,23 +42,23 @@ public class ObserverTracerCommand extends SWCommand { @Register(value = "disable", description = "OBSERVER_HELP_DISABLE") public void disable(Player p) { ObserverTracerListener.enabled.remove(p); - ObserverTracerListener.observerTracerMap.remove(p); + SWPlayer.of(p).removeComponent(ObserverTracer.class); BauSystem.MESSAGE.send("OBSERVER_DISABLE", p); } @Register(value = "delete", description = "OBSERVER_HELP_DELETE") public void delete(@Validator Player p) { - ObserverTracerListener.observerTracerMap.remove(p); + SWPlayer.of(p).removeComponent(ObserverTracer.class); BauSystem.MESSAGE.send("OBSERVER_DELETE", p); } @Register(value = "retrace", description = "OBSERVER_HELP_RETRACE") public void retrace(@Validator Player p) { - if (ObserverTracerListener.observerTracerMap.containsKey(p)) { + if (SWPlayer.of(p).hasComponent(ObserverTracer.class)) { BauSystem.MESSAGE.send("OBSERVER_RETRACE_NO_TRACE", p); return; } - ObserverTracerListener.observerTracerMap.get(p).trace(); + SWPlayer.of(p).getComponent(ObserverTracer.class).ifPresent(ObserverTracer::trace); BauSystem.MESSAGE.send("OBSERVER_RETRACE_DONE", p); } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerListener.java index 9ef56909..0528527e 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracerListener.java @@ -22,6 +22,7 @@ package de.steamwar.bausystem.features.observer; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; +import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -34,23 +35,19 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; @Linked public class ObserverTracerListener implements Listener { static Set enabled = new HashSet<>(); - static Map observerTracerMap = new HashMap<>(); public ObserverTracerListener() { Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { - observerTracerMap.forEach((player, observerTracer) -> { - if (player.getGameMode() == GameMode.SPECTATOR) { - observerTracer.show(); - } + SWPlayer.allWithSingleComponent(ObserverTracer.class).forEach(pair -> { + if (pair.getKey().getGameMode() != GameMode.SPECTATOR) return; + pair.getValue().show(); }); }, 15L, 15L); } @@ -58,20 +55,16 @@ public class ObserverTracerListener implements Listener { @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (!enabled.contains(event.getPlayer())) { - return; - } - if (event.getClickedBlock() == null) { - return; - } - if (observerTracerMap.containsKey(event.getPlayer())) { - ObserverTracer observerTracer = observerTracerMap.get(event.getPlayer()); + if (!enabled.contains(event.getPlayer())) return; + if (event.getClickedBlock() == null) return; + ObserverTracer observerTracer = SWPlayer.of(event.getPlayer()).getComponent(ObserverTracer.class).orElse(null); + if (observerTracer != null) { Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { if (!observerTracer.trace()) { createNew(event); } - observerTracerMap.forEach((player, o) -> { - o.trace(); + SWPlayer.allWithSingleComponent(ObserverTracer.class).forEach(pair -> { + pair.getValue().trace(); }); }, 1L); } else { @@ -86,13 +79,13 @@ public class ObserverTracerListener implements Listener { if (event.getClickedBlock().getType() == Material.OBSERVER) { ObserverTracer observerTracer = new ObserverTracer(event.getPlayer(), event.getClickedBlock()); observerTracer.trace(); - observerTracerMap.put(event.getPlayer(), observerTracer); + SWPlayer.of(event.getPlayer()).setComponent(observerTracer); } } @EventHandler public void onBauMemberUpdate(BauMemberUpdateEvent event) { - event.getNewSpectator().forEach(observerTracerMap::remove); + event.getNewBuilder().forEach(player -> SWPlayer.of(player).removeComponent(ObserverTracer.class)); } @EventHandler @@ -103,6 +96,5 @@ public class ObserverTracerListener implements Listener { @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { enabled.remove(event.getPlayer()); - observerTracerMap.remove(event.getPlayer()); } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java index a8676ae2..22eabcaa 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java @@ -26,8 +26,10 @@ import de.steamwar.bausystem.region.flags.Flag; import de.steamwar.bausystem.region.flags.TNTMode; import de.steamwar.bausystem.utils.ScoreboardElement; import de.steamwar.linkage.Linked; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -39,11 +41,14 @@ import java.util.List; @Linked public class TNTListener implements Listener, ScoreboardElement { - private void explode(List blockList) { + private void explode(List blockList, boolean destroy) { blockList.removeIf(block -> { Region region = Region.getRegion(block.getLocation()); TNTMode value = region.getFlags().get(Flag.TNT).getWithDefault(); if (value == TNTMode.ALLOW) { + if (destroy && block.getType() != Material.TNT) { + block.setType(Material.AIR); + } return false; } else if (value == TNTMode.ONLY_TB) { if (region.getBuildArea().inRegion(block.getLocation(), true)) { @@ -57,12 +62,16 @@ public class TNTListener implements Listener, ScoreboardElement { @EventHandler public void onBlockExplode(BlockExplodeEvent event) { - explode(event.blockList()); + explode(event.blockList(), false); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onExplode(EntityExplodeEvent event) { - explode(event.blockList()); + if (!(event.getEntity() instanceof TNTPrimed)) { + event.blockList().clear(); + return; + } + explode(event.blockList(), true); } @Override diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TestblockCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TestblockCommand.java index d09c06a0..52c53a63 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TestblockCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/region/TestblockCommand.java @@ -31,7 +31,6 @@ import de.steamwar.command.SWCommand; import de.steamwar.command.TypeMapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; -import de.steamwar.sql.BauweltMember; import de.steamwar.sql.Punishment; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; @@ -73,6 +72,14 @@ public class TestblockCommand extends SWCommand { resetRegion(p, node, isExtension ? RegionExtensionType.EXTENSION : RegionExtensionType.NORMAL, isIgnoreAir, isOnlyColor, replaceTNT, replaceWater); } + @Register(value="default") + public void setTestblockDefault(Player p) { + Region region = regionCheck(p); + if (region == null) return; + region.getRegionData().setTestblockSchematic(null); + resetRegion(p, null, RegionExtensionType.EXTENSION, false, false, false, false); + } + private void resetRegion(Player p, SchematicNode node, RegionExtensionType regionExtensionType, boolean ignoreAir, boolean onlyColors, boolean removeTNT, boolean removeWater) { Region region = regionCheck(p); if (region == null) return; @@ -93,29 +100,10 @@ public class TestblockCommand extends SWCommand { } } - // Beta Tester enabled - switch (BauServer.getInstance().getOwnerID()) { - case 245: - case 403: - case 1898: - case 3320: - case 4603: - case 5262: - case 5399: - case 6032: - case 7862: - case 11077: - case 11888: - case 12258: - case 16009: - if (node == null) { - node = region.getRegionData().getTestblockSchematic(); - } else { - region.getRegionData().setTestblockSchematic(node); - } - break; - default: - break; + if (node == null) { + node = region.getRegionData().getTestblockSchematic(); + } else { + region.getRegionData().setTestblockSchematic(node); } PasteBuilder.ClipboardProvider clipboardProvider; @@ -198,7 +186,7 @@ public class TestblockCommand extends SWCommand { @Override public List tabCompletes(CommandSender commandSender, PreviousArguments previousArguments, String s) { List stringList = new ArrayList<>(SchematicNode.getNodeTabcomplete(SteamwarUser.get(((Player) commandSender).getUniqueId()), s)); - stringList.addAll(SchematicNode.getNodeTabcomplete(SteamwarUser.get(0), s)); + stringList.addAll(SchematicNode.getNodeTabcomplete(SteamwarUser.byId(0), s)); return stringList; } @@ -206,7 +194,7 @@ public class TestblockCommand extends SWCommand { public SchematicNode map(CommandSender commandSender, PreviousArguments previousArguments, String s) { SchematicNode node = SchematicNode.getNodeFromPath(SteamwarUser.get(((Player) commandSender).getUniqueId()), s); if (node == null) { - node = SchematicNode.getNodeFromPath(SteamwarUser.get(0), s); + node = SchematicNode.getNodeFromPath(SteamwarUser.byId(0), s); } return node; } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptHelper.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptHelper.java index fd4bc826..bb486680 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptHelper.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptHelper.java @@ -38,7 +38,7 @@ public class ScriptHelper { BookMeta meta = (BookMeta) itemStack.getItemMeta(); if(!writeable) { meta.setTitle(script.getName()); - meta.setAuthor(SteamwarUser.get(script.getUserId()).getUserName()); + meta.setAuthor(SteamwarUser.byId(script.getUserId()).getUserName()); } meta.setPages(getScriptPages(script)); itemStack.setItemMeta(meta); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java index 0eadc3ed..2f444561 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java @@ -67,7 +67,7 @@ public class ScriptListener implements Listener { @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { - ScriptRunner.remove(event.getPlayer()); + playerSet.remove(event.getPlayer()); } @EventHandler diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptRunner.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptRunner.java index 2a506e12..ee10536e 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptRunner.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptRunner.java @@ -23,8 +23,11 @@ import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.features.script.lua.CommandRegister; import de.steamwar.bausystem.features.script.lua.SteamWarGlobalLuaPlugin; import de.steamwar.bausystem.features.script.lua.SteamWarPlatform; +import de.steamwar.core.SWPlayer; import de.steamwar.sql.Script; import de.steamwar.sql.SteamwarUser; +import lombok.Getter; +import lombok.Setter; import lombok.experimental.UtilityClass; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -44,12 +47,25 @@ public class ScriptRunner { // Key -> bau-script- // Value -> - private static final Map>> EVENT_MAP = new HashMap<>(); - private static final Map>> HOTKEY_MAP = new HashMap<>(); - private static final Map> COMMAND_MAP = new HashMap<>(); + public class ScriptData implements SWPlayer.Component { + private Map> events = new EnumMap<>(SteamWarGlobalLuaPlugin.EventType.class); + private Map> hotkeys = new HashMap<>(); + private Map commands = new HashMap<>(); + + @Getter + private Set calledCommands = new HashSet<>(); + + @Getter + @Setter + private Long lastF = Long.MAX_VALUE; + + @Getter + @Setter + private boolean ignore; + } public Set getCommandsOfPlayer(Player player) { - return COMMAND_MAP.getOrDefault(player, new HashMap<>()).keySet(); + return SWPlayer.of(player).getComponentOrDefault(ScriptData.class, ScriptData::new).commands.keySet(); } public static void runScript(String script, Player player) { @@ -64,10 +80,12 @@ public class ScriptRunner { public static void createGlobalScript(List scripts, Player player) { remove(player); + SWPlayer swPlayer = SWPlayer.of(player); + ScriptData scriptData = swPlayer.getComponentOrDefault(ScriptData.class, ScriptData::new); Globals globals = SteamWarPlatform.createGlobalGlobals(player, - (s, luaFunction) -> EVENT_MAP.computeIfAbsent(player, player1 -> new EnumMap<>(SteamWarGlobalLuaPlugin.EventType.class)).computeIfAbsent(s, s1 -> new ArrayList<>()).add(luaFunction), - (s, luaFunction) -> HOTKEY_MAP.computeIfAbsent(player, player1 -> new HashMap<>()).computeIfAbsent(Hotkey.fromString(s), s1 -> new ArrayList<>()).add(luaFunction), - commandRegister -> COMMAND_MAP.computeIfAbsent(player, player1 -> new HashMap<>()).put(commandRegister.getName(), commandRegister)); + (s, luaFunction) -> scriptData.events.computeIfAbsent(s, s1 -> new ArrayList<>()).add(luaFunction), + (s, luaFunction) -> scriptData.hotkeys.computeIfAbsent(Hotkey.fromString(s), s1 -> new ArrayList<>()).add(luaFunction), + commandRegister -> scriptData.commands.put(commandRegister.getName(), commandRegister)); for (String script : scripts) { catchScript("SCRIPT_ERROR_GLOBAL", player, () -> globals.load(script).call()); @@ -75,13 +93,14 @@ public class ScriptRunner { } public static void remove(Player player) { - EVENT_MAP.remove(player); - COMMAND_MAP.remove(player); - HOTKEY_MAP.remove(player); + SWPlayer.of(player).removeComponent(ScriptData.class); } public static void callEvent(Player player, SteamWarGlobalLuaPlugin.EventType event, LuaValue eventValue, Event wrappedEvent) { - List luaFunctions = EVENT_MAP.getOrDefault(player, Collections.emptyMap()).getOrDefault(event, Collections.emptyList()); + List luaFunctions = SWPlayer.of(player).getComponent(ScriptData.class) + .map(scriptData -> scriptData.events) + .orElse(Collections.emptyMap()) + .getOrDefault(event, Collections.emptyList()); if (luaFunctions.isEmpty()) { if(event == SteamWarGlobalLuaPlugin.EventType.DoubleSwap) { player.performCommand("gui"); @@ -124,7 +143,10 @@ public class ScriptRunner { } public static boolean callCommand(Player player, String command, String[] argsArray) { - CommandRegister commandRegister = COMMAND_MAP.getOrDefault(player, Collections.emptyMap()).get(command); + CommandRegister commandRegister = SWPlayer.of(player).getComponent(ScriptData.class) + .map(scriptData -> scriptData.commands) + .orElse(Collections.emptyMap()) + .get(command); if (commandRegister == null) { return false; } @@ -188,7 +210,11 @@ public class ScriptRunner { public static void callHotkey(int mods, int key, Player player, boolean pressed) { Hotkey hotkey = Hotkey.fromChar(key, mods); - catchScript("SCRIPT_ERROR_GLOBAL", player, () -> HOTKEY_MAP.getOrDefault(player, Collections.emptyMap()).getOrDefault(hotkey, Collections.emptyList()).forEach(luaFunction -> luaFunction.call(LuaValue.valueOf(pressed)))); + List hotkeys = SWPlayer.of(player).getComponent(ScriptData.class) + .map(scriptData -> scriptData.hotkeys) + .orElse(Collections.emptyMap()) + .getOrDefault(hotkey, Collections.emptyList()); + catchScript("SCRIPT_ERROR_GLOBAL", player, () -> hotkeys.forEach(luaFunction -> luaFunction.call(LuaValue.valueOf(pressed)))); } public static void catchScript(String errorMsg, Player player, Runnable run) { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/CommandListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/CommandListener.java index d52e4d9b..f79d4ac3 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/CommandListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/CommandListener.java @@ -21,44 +21,24 @@ package de.steamwar.bausystem.features.script.event; import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.features.script.ScriptRunner; +import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; @Linked public class CommandListener implements Listener { - private Map> calledCommands = new HashMap<>(); - @EventHandler public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { if(!Permission.BUILD.hasPermission(event.getPlayer())) return; String[] split = event.getMessage().split(" "); - if (calledCommands.getOrDefault(event.getPlayer(), new HashSet<>()).contains(split[0])) { - return; - } + ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new); + if (scriptData.getCalledCommands().contains(split[0])) return; - calledCommands.getOrDefault(event.getPlayer(), new HashSet<>()).add(split[0]); + scriptData.getCalledCommands().add(split[0]); event.setCancelled(ScriptRunner.callCommand(event.getPlayer(), split[0].substring(1), split)); - calledCommands.getOrDefault(event.getPlayer(), new HashSet<>()).remove(split[0]); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - calledCommands.put(event.getPlayer(), new HashSet<>()); - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - calledCommands.remove(event.getPlayer()); + scriptData.getCalledCommands().remove(split[0]); } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/EventListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/EventListener.java index 8aade642..3490ea2e 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/EventListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/event/EventListener.java @@ -25,6 +25,7 @@ import de.steamwar.bausystem.features.script.ScriptRunner; import de.steamwar.bausystem.features.script.lua.SteamWarGlobalLuaPlugin; import de.steamwar.bausystem.features.script.lua.libs.StorageLib; import de.steamwar.bausystem.region.Region; +import de.steamwar.core.SWPlayer; import de.steamwar.core.TrickyTrialsWrapper; import de.steamwar.linkage.Linked; import org.bukkit.Bukkit; @@ -43,32 +44,27 @@ import org.bukkit.event.player.*; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - @Linked public class EventListener implements Listener { - private static final Map LAST_FS = new HashMap<>(); - static { Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { long millis = System.currentTimeMillis(); - LAST_FS.entrySet().removeIf(entry -> millis - entry.getValue() > 200); + SWPlayer.allWithSingleComponent(ScriptRunner.ScriptData.class) + .filter(pair -> millis - pair.getValue().getLastF() > 200) + .forEach(pair -> pair.getValue().setLastF(Long.MAX_VALUE)); }, 1, 1); } @EventHandler(priority = EventPriority.MONITOR) public void onPlayerJoin(PlayerJoinEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.SelfJoin, LuaValue.NIL, event); } @EventHandler(priority = EventPriority.HIGH) public void onPlayerQuit(PlayerQuitEvent event) { - if(Permission.BUILD.hasPermission(event.getPlayer())) { + if (Permission.BUILD.hasPermission(event.getPlayer())) { ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.SelfLeave, LuaValue.NIL, event); } StorageLib.removePlayer(event.getPlayer()); @@ -76,19 +72,20 @@ public class EventListener implements Listener { @EventHandler(priority = EventPriority.HIGH) public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (LAST_FS.containsKey(event.getPlayer())) { + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new); + if (scriptData.getLastF() != Long.MAX_VALUE) { Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.DoubleSwap, LuaValue.NIL, event); }, 1); } else { - LAST_FS.put(event.getPlayer(), System.currentTimeMillis()); + scriptData.setLastF(System.currentTimeMillis()); } } @EventHandler(priority = EventPriority.HIGH) public void onBlockPlace(BlockPlaceEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; LuaTable table = new LuaTable(); table.set("x", event.getBlock().getX()); table.set("y", event.getBlock().getY()); @@ -99,7 +96,7 @@ public class EventListener implements Listener { @EventHandler(priority = EventPriority.HIGH) public void onBlockBreak(BlockBreakEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; LuaTable table = new LuaTable(); table.set("x", event.getBlock().getX()); table.set("y", event.getBlock().getY()); @@ -108,12 +105,12 @@ public class EventListener implements Listener { ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.BreakBlock, table, event); } - private final Set ignore = new HashSet<>(); - @EventHandler(priority = EventPriority.LOW) public void onPlayerInteract(PlayerInteractEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (ignore.remove(event.getPlayer())) { + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new); + if (scriptData.isIgnore()) { + scriptData.setIgnore(false); return; } LuaTable table = new LuaTable(); @@ -124,7 +121,7 @@ public class EventListener implements Listener { table.set("hand", event.getHand().name()); } table.set("block", event.getItem() == null ? Material.AIR.name() : event.getItem().getType().name()); - if(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK) { + if (event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK) { table.set("hasBlock", LuaValue.valueOf(true)); table.set("blockX", event.getClickedBlock().getX()); table.set("blockY", event.getClickedBlock().getY()); @@ -150,7 +147,7 @@ public class EventListener implements Listener { Region tntRegion = Region.getRegion(event.getLocation()); for (Player player : Bukkit.getOnlinePlayers()) { - if(!Permission.BUILD.hasPermission(player)) continue; + if (!Permission.BUILD.hasPermission(player)) continue; if (tntRegion.getArea().inRegion(player.getLocation(), false)) { ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.TNTSpawn, LuaValue.NIL, event); } @@ -172,7 +169,7 @@ public class EventListener implements Listener { boolean inBuild = event.blockList().stream().anyMatch(block -> tntRegion.getBuildArea().inRegion(block.getLocation(), true)); for (Player player : Bukkit.getOnlinePlayers()) { - if(!Permission.BUILD.hasPermission(player)) continue; + if (!Permission.BUILD.hasPermission(player)) continue; if (tntRegion.getArea().inRegion(player.getLocation(), false)) { ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.TNTExplode, table, event); if (inBuild) { @@ -184,8 +181,9 @@ public class EventListener implements Listener { @EventHandler(priority = EventPriority.HIGH) public void onPlayerDropItem(PlayerDropItemEvent event) { - if(!Permission.BUILD.hasPermission(event.getPlayer())) return; - ignore.add(event.getPlayer()); + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new); + scriptData.setIgnore(true); LuaTable table = new LuaTable(); table.set("type", event.getItemDrop().getItemStack().getType().name()); ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.DropItem, table, event); @@ -194,7 +192,7 @@ public class EventListener implements Listener { @EventHandler(priority = EventPriority.HIGH) public void onEntityDeath(EntityDeathEvent event) { for (Player player : Bukkit.getOnlinePlayers()) { - if(!Permission.BUILD.hasPermission(player)) continue; + if (!Permission.BUILD.hasPermission(player)) continue; LuaTable table = new LuaTable(); table.set("type", event.getEntityType().name()); ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.EntityDeath, table, event); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/SteamWarLuaPlugin.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/SteamWarLuaPlugin.java index df6fc706..74225e62 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/SteamWarLuaPlugin.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/SteamWarLuaPlugin.java @@ -48,6 +48,7 @@ import java.util.logging.Level; public class SteamWarLuaPlugin extends TwoArgFunction { private static final boolean hasFAWE = Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") != null; + private static final Set ignoreCommandFromWorldEdit = Set.of("select", "schem", "/schem", "schematic", "/schematic"); protected static final Map, List> LUA_LIBS = new HashMap<>(); @@ -116,7 +117,7 @@ public class SteamWarLuaPlugin extends TwoArgFunction { command = preprocessEvent.getMessage().substring(1); Bukkit.getLogger().log(Level.INFO, player.getName() + " dispatched command: " + command); String[] commandSplit = command.split(" "); - if (!commandSplit[0].equals("select") && hasFAWE && WorldEditListener.isWorldEditCommand("/" + commandSplit[0])) { + if (!ignoreCommandFromWorldEdit.contains(commandSplit[0]) && hasFAWE && WorldEditListener.isWorldEditCommand("/" + commandSplit[0])) { EditSession editSession = WorldEditUtils.getEditSession(player); Actor actor = BukkitAdapter.adapt(player); WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().handleCommandOnCurrentThread(new CommandEvent(actor, command, editSession)); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/libs/StorageLib.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/libs/StorageLib.java index c1646d41..04cde3f0 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/libs/StorageLib.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/script/lua/libs/StorageLib.java @@ -89,7 +89,7 @@ public class StorageLib implements LuaLib, Enable, Disable { jsonObject.keySet().forEach(key -> { map.put(key, fromJson(jsonObject.get(key))); }); - SteamwarUser steamwarUser = SteamwarUser.get(Integer.parseInt(playerStorage.getName().substring(0, playerStorage.getName().length() - ".json".length()))); + SteamwarUser steamwarUser = SteamwarUser.byId(Integer.parseInt(playerStorage.getName().substring(0, playerStorage.getName().length() - ".json".length()))); PLAYER_STORAGE.put(steamwarUser.getUUID(), map); } catch (Exception e) {} } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/execute/StabGenerator.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/execute/StabGenerator.java index 809358b6..37d1a5f0 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/execute/StabGenerator.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/execute/StabGenerator.java @@ -26,6 +26,7 @@ import de.steamwar.bausystem.utils.PasteBuilder; import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; @@ -51,6 +52,7 @@ public class StabGenerator extends StabStep implements Listener { @EventHandler public void onEntityExplode(EntityExplodeEvent event) { + if (!(event.getEntity() instanceof TNTPrimed)) return; if (Region.getRegion(event.getEntity().getLocation()) == data.region) { event.blockList().forEach(block -> { if (!data.region.getTestblockArea().inRegion(block.getLocation(), true)) diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/storage/YAPIONFormatSimulatorLoader.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/storage/YAPIONFormatSimulatorLoader.java index c138ecb1..6111eb32 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/storage/YAPIONFormatSimulatorLoader.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/storage/YAPIONFormatSimulatorLoader.java @@ -54,7 +54,7 @@ public class YAPIONFormatSimulatorLoader implements SimulatorLoader { } String name = file.getName().substring(0, file.getName().length() - 7); - SteamwarUser steamwarUser = SteamwarUser.get(Integer.parseInt(name)); + SteamwarUser steamwarUser = SteamwarUser.byId(Integer.parseInt(name)); List simulators = new ArrayList<>(); for (String s : yapionObject.getKeys()) { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/LaufbauUtils.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/LaufbauUtils.java index 4eb6c382..a642c22d 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/LaufbauUtils.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/LaufbauUtils.java @@ -20,17 +20,14 @@ package de.steamwar.bausystem.features.slaves.laufbau; import de.steamwar.bausystem.BauSystem; +import de.steamwar.core.SWPlayer; import de.steamwar.inventory.SWItem; -import de.steamwar.linkage.Linked; import de.steamwar.sql.UserConfig; import lombok.Cleanup; import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; import org.bukkit.Material; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.meta.ItemMeta; import yapion.hierarchy.output.StreamOutput; import yapion.hierarchy.types.YAPIONObject; @@ -39,44 +36,45 @@ import yapion.parser.options.StreamOptions; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -@Linked -public class LaufbauUtils implements Listener { +@UtilityClass +public class LaufbauUtils { - private static Map yapionObjectMap = new HashMap<>(); + public static class LaufbauComponent implements SWPlayer.Component { - @EventHandler - @SneakyThrows - public void onPlayerJoin(PlayerJoinEvent event) { - String config = UserConfig.getConfig(event.getPlayer().getUniqueId(), "bausystem-laufbau"); - if (config == null) { - return; - } - byte[] bytes = Base64.getDecoder().decode(config); - @Cleanup GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(bytes)); - YAPIONObject yapionObject = new YAPIONParser(gzipInputStream, new StreamOptions().stopOnStreamEnd(true)).parse().result(); - yapionObjectMap.put(event.getPlayer(), yapionObject); - } + private YAPIONObject yapionObject = new YAPIONObject(); - @EventHandler - @SneakyThrows - public void onPlayerQuit(PlayerQuitEvent event) { - YAPIONObject yapionObject = yapionObjectMap.get(event.getPlayer()); - if (yapionObject == null) { - return; + @Override + @SneakyThrows + public void onMount(SWPlayer player) { + String config = UserConfig.getConfig(player.getPlayer().getUniqueId(), "bausystem-laufbau"); + if (config == null) { + return; + } + byte[] bytes = Base64.getDecoder().decode(config); + @Cleanup GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(bytes)); + yapionObject = new YAPIONParser(gzipInputStream, new StreamOptions().stopOnStreamEnd(true)).parse().result(); } - if (yapionObject.isEmpty()) { - UserConfig.removePlayerConfig(event.getPlayer().getUniqueId(), "bausystem-laufbau"); - return; + + @Override + @SneakyThrows + public void onUnmount(SWPlayer player) { + if (yapionObject.isEmpty()) { + UserConfig.removePlayerConfig(player.getPlayer().getUniqueId(), "bausystem-laufbau"); + return; + } + @Cleanup ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + @Cleanup GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); + yapionObject.toYAPION(new StreamOutput(gzipOutputStream)).close(); + UserConfig.updatePlayerConfig(player.getPlayer().getUniqueId(), "bausystem-laufbau", Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray())); } - @Cleanup ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - @Cleanup GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); - yapionObject.toYAPION(new StreamOutput(gzipOutputStream)).close(); - UserConfig.updatePlayerConfig(event.getPlayer().getUniqueId(), "bausystem-laufbau", Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray())); } public static Cuboid pixelCuboid(double pixelX, double pixelY, double pixelZ, double pixelDX, double pixelDY, double pixelDZ) { @@ -125,11 +123,7 @@ public class LaufbauUtils implements Listener { return false; } String identifier = identifier(bb); - YAPIONObject yapionObject = yapionObjectMap.get(p); - if (yapionObject == null) { - return false; - } - return yapionObject.containsKey(identifier); + return SWPlayer.of(p).getComponentOrDefault(LaufbauComponent.class, LaufbauComponent::new).yapionObject.containsKey(identifier); } @SneakyThrows @@ -138,11 +132,7 @@ public class LaufbauUtils implements Listener { return; } String identifier = identifier(bb); - YAPIONObject yapionObject = yapionObjectMap.get(p); - if (yapionObject == null) { - yapionObject = new YAPIONObject(); - yapionObjectMap.put(p, yapionObject); - } + YAPIONObject yapionObject = SWPlayer.of(p).getComponentOrDefault(LaufbauComponent.class, LaufbauComponent::new).yapionObject; if (yapionObject.containsKey(identifier)) { yapionObject.remove(identifier); } else { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java index 7cb18b50..74888bec 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java @@ -107,7 +107,7 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE @Override public Player map(CommandSender commandSender, String[] previousArguments, String s) { Player player = Bukkit.getPlayer(s); - if (player != null && Permission.REAL_SPECTATOR.hasPermission(player)) { + if (player != null && Permission.SPECTATOR.hasPermission(player)) { return player; } return null; @@ -116,7 +116,7 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE @Override public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { return Bukkit.getOnlinePlayers().stream() - .filter(Permission.REAL_SPECTATOR::hasPermission) + .filter(Permission.SPECTATOR::hasPermission) .map(Player::getName) .collect(Collectors.toList()); } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/testblock/blockcounter/BlockCounterListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/testblock/blockcounter/BlockCounterListener.java index c4f711d8..beb693eb 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/testblock/blockcounter/BlockCounterListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/testblock/blockcounter/BlockCounterListener.java @@ -22,6 +22,7 @@ package de.steamwar.bausystem.features.testblock.blockcounter; import de.steamwar.bausystem.region.Region; import de.steamwar.linkage.Linked; import org.bukkit.block.Block; +import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityExplodeEvent; @@ -34,6 +35,7 @@ public class BlockCounterListener implements Listener { @EventHandler public void onEntityExplode(EntityExplodeEvent event) { + if (!(event.getEntity() instanceof TNTPrimed)) return; Region region = Region.getRegion(event.getLocation()); if (region.getType().isGlobal()) { return; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSCommand.java similarity index 82% rename from BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.java rename to BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSCommand.java index 355803dc..252482b6 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSCommand.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.steamwar.bausystem.features.tntlistener; +package de.steamwar.bausystem.features.tls; import de.steamwar.command.SWCommand; import de.steamwar.linkage.Linked; @@ -43,4 +43,13 @@ public class TLSCommand extends SWCommand { public void stop(@Validator Player player) { listener.stopListening(player); } + + @Register(description = "TLS_TOGGLE_HELP") + public void toggle(@Validator Player player) { + if (listener.isActiveFor(player)) { + listener.stopListening(player); + } else { + listener.startListening(player); + } + } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSListener.java similarity index 98% rename from BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java rename to BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSListener.java index 9b587d8f..20e7f161 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSListener.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.steamwar.bausystem.features.tntlistener; +package de.steamwar.bausystem.features.tls; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.features.tpslimit.TPSUtils; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSScoreboardElement.java similarity index 96% rename from BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java rename to BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSScoreboardElement.java index 0dec6609..8744e1f0 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tls/TLSScoreboardElement.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.steamwar.bausystem.features.tntlistener; +package de.steamwar.bausystem.features.tls; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.Permission; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/GamemodeCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/GamemodeCommand.java index 9e3da950..86ecf6ca 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/GamemodeCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/GamemodeCommand.java @@ -20,6 +20,7 @@ package de.steamwar.bausystem.features.util; import de.steamwar.bausystem.BauSystem; import de.steamwar.command.SWCommand; +import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; import org.bukkit.GameMode; import org.bukkit.entity.Player; @@ -39,10 +40,11 @@ public class GamemodeCommand extends SWCommand { @Register public void genericCommand(final Player p) { - if (NoClipCommand.getNOCLIPS().contains(p)) { - p.performCommand("noclip"); - return; - } + SWPlayer swPlayer = SWPlayer.of(p); + if (swPlayer.hasComponent(NoClipCommand.NoClipData.class)) { + swPlayer.removeComponent(NoClipCommand.NoClipData.class); + return; + } if (p.getGameMode() == GameMode.CREATIVE) { p.setGameMode(GameMode.SPECTATOR); } else { @@ -52,9 +54,7 @@ public class GamemodeCommand extends SWCommand { @Register public void gamemodeCommand(final Player p, final GameMode gameMode) { - if (NoClipCommand.getNOCLIPS().contains(p)) { - p.performCommand("noclip"); - } + SWPlayer.of(p).removeComponent(NoClipCommand.NoClipData.class); p.setGameMode(gameMode); } } \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialCommand.java index 69b53897..52a588f9 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialCommand.java @@ -26,6 +26,7 @@ import de.steamwar.bausystem.shared.EnumDisplay; import de.steamwar.command.PreviousArguments; import de.steamwar.command.SWCommand; import de.steamwar.command.TypeMapper; +import de.steamwar.core.SWPlayer; import de.steamwar.data.CMDs; import de.steamwar.inventory.SWAnvilInv; import de.steamwar.inventory.SWInventory; @@ -56,11 +57,9 @@ public class MaterialCommand extends SWCommand implements Listener { WorldEditListener.addCommandExclusion("material"); } - private Map searchMap = new HashMap<>(); - @Getter @Setter - static class Search { + public class SearchParameter implements SWPlayer.Component { SearchType transparent = SearchType.IGNORE; SearchType solid = SearchType.IGNORE; SearchType gravity = SearchType.IGNORE; @@ -104,10 +103,10 @@ public class MaterialCommand extends SWCommand implements Listener { @Register public void materialGUI(Player p) { - materialGUI(p, searchMap.get(p)); + materialGUI(p, SWPlayer.of(p).getComponentOrDefault(SearchParameter.class, SearchParameter::new)); } - private static final Map> elements = new HashMap<>(); + private static final Map> elements = new HashMap<>(); static { elements.put("-transparent", (search, searchType) -> search.transparent = searchType); elements.put("-solid", (search, searchType) -> search.solid = searchType); @@ -122,15 +121,15 @@ public class MaterialCommand extends SWCommand implements Listener { @Register public void search(Player p, @Mapper("search") String... searches) { - Search search = new Search(); + SearchParameter searchParameter = SWPlayer.of(p).getComponentOrDefault(SearchParameter.class, SearchParameter::new); for (String s : searches) { boolean has = false; - for (Map.Entry> element: elements.entrySet()) { + for (Map.Entry> element: elements.entrySet()) { if (s.startsWith(element.getKey() + ":")) { - element.getValue().accept(search, SearchType.valueOf(s.substring(element.getKey().length() + 1).toUpperCase())); + element.getValue().accept(searchParameter, SearchType.valueOf(s.substring(element.getKey().length() + 1).toUpperCase())); has = true; } else if (s.startsWith(element.getKey().substring(0, 2) + ":")) { - element.getValue().accept(search, SearchType.valueOf(s.substring(element.getKey().substring(0, 2).length() + 1).toUpperCase())); + element.getValue().accept(searchParameter, SearchType.valueOf(s.substring(element.getKey().substring(0, 2).length() + 1).toUpperCase())); has = true; } if (has) break; @@ -141,15 +140,15 @@ public class MaterialCommand extends SWCommand implements Listener { case "-blastresistance:": s = s.substring(s.indexOf(':') + 1).replace('_', ' '); if (s.isEmpty() || s.matches("((([><]=?)|!|=)\\d+(\\.|,\\d+)?)( ((([><]=?)|!|=)\\d+(\\.|,\\d+)?))*")) { - search.blastResistance = s; + searchParameter.blastResistance = s; } break; default: - search.name = s; + searchParameter.name = s; break; } } - materialGUI(p, search); + materialGUI(p, searchParameter); } @Mapper(value = "search", local = true) @@ -182,7 +181,7 @@ public class MaterialCommand extends SWCommand implements Listener { }; } - public void materialGUI(Player p, Search search) { + public void materialGUI(Player p, SearchParameter search) { List> swListEntries = new ArrayList<>(); MaterialLazyInit.materialData.forEach(data -> { if (data.is(search)) { @@ -202,59 +201,59 @@ public class MaterialCommand extends SWCommand implements Listener { private void searchGUI(Player p) { SWInventory swInventory = new SWInventory(p, 54, BauSystem.MESSAGE.parse("MATERIAL_SEARCH", p)); - Search search = searchMap.get(p); + SearchParameter searchParameter = SWPlayer.of(p).getComponentOrDefault(SearchParameter.class, SearchParameter::new); swInventory.setItem(0, new SWItem(Material.ARROW, BauSystem.MESSAGE.parse("MATERIAL_BACK", p), clickType -> { materialGUI(p); }).setCustomModelData(CMDs.BACK)); - swInventory.setItem(10, new SWItem(Material.NAME_TAG, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_NAME", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.name), clickType -> { - SWAnvilInv swAnvilInv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_NAME", p), search.name); + swInventory.setItem(10, new SWItem(Material.NAME_TAG, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_NAME", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, searchParameter.name), clickType -> { + SWAnvilInv swAnvilInv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_NAME", p), searchParameter.name); swAnvilInv.setCallback(s -> { - search.name = s; + searchParameter.name = s; searchGUI(p); }); swAnvilInv.open(); })); - swInventory.setItem(19, new SWItem(Material.GLASS, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_TRANSPARENT", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.transparent.getChatValue(), p)), clickType -> { - search.transparent = search.transparent.next(); + swInventory.setItem(19, new SWItem(Material.GLASS, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_TRANSPARENT", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.transparent.getChatValue(), p)), clickType -> { + searchParameter.transparent = searchParameter.transparent.next(); searchGUI(p); })); - swInventory.setItem(20, new SWItem(Material.BRICK, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_SOLID", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.solid.getChatValue(), p)), clickType -> { - search.solid = search.solid.next(); + swInventory.setItem(20, new SWItem(Material.BRICK, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_SOLID", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.solid.getChatValue(), p)), clickType -> { + searchParameter.solid = searchParameter.solid.next(); searchGUI(p); })); - swInventory.setItem(21, new SWItem(Material.BLACK_CONCRETE_POWDER, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_GRAVITY", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.gravity.getChatValue(), p)), clickType -> { - search.gravity = search.gravity.next(); + swInventory.setItem(21, new SWItem(Material.BLACK_CONCRETE_POWDER, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_GRAVITY", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.gravity.getChatValue(), p)), clickType -> { + searchParameter.gravity = searchParameter.gravity.next(); searchGUI(p); })); - swInventory.setItem(22, new SWItem(Material.BIRCH_LOG, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_OCCLUDING", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.occluding.getChatValue(), p)), clickType -> { - search.occluding = search.occluding.next(); + swInventory.setItem(22, new SWItem(Material.BIRCH_LOG, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_OCCLUDING", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.occluding.getChatValue(), p)), clickType -> { + searchParameter.occluding = searchParameter.occluding.next(); searchGUI(p); })); - swInventory.setItem(23, new SWItem(Material.OAK_BUTTON, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_INTERACTEABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.interacteable.getChatValue(), p)), clickType -> { - search.interacteable = search.interacteable.next(); + swInventory.setItem(23, new SWItem(Material.OAK_BUTTON, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_INTERACTEABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.interacteable.getChatValue(), p)), clickType -> { + searchParameter.interacteable = searchParameter.interacteable.next(); searchGUI(p); })); - swInventory.setItem(24, new SWItem(Material.FLINT_AND_STEEL, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_FLAMMABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.flammable.getChatValue(), p)), clickType -> { - search.flammable = search.flammable.next(); + swInventory.setItem(24, new SWItem(Material.FLINT_AND_STEEL, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_FLAMMABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.flammable.getChatValue(), p)), clickType -> { + searchParameter.flammable = searchParameter.flammable.next(); searchGUI(p); })); - swInventory.setItem(25, new SWItem(Material.LAVA_BUCKET, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_BURNABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.burnable.getChatValue(), p)), clickType -> { - search.burnable = search.burnable.next(); + swInventory.setItem(25, new SWItem(Material.LAVA_BUCKET, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_BURNABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.burnable.getChatValue(), p)), clickType -> { + searchParameter.burnable = searchParameter.burnable.next(); searchGUI(p); })); - swInventory.setItem(28, new SWItem(Material.WATER_BUCKET, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_WATERLOGGABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.waterloggable.getChatValue(), p)), clickType -> { - search.waterloggable = search.waterloggable.next(); + swInventory.setItem(28, new SWItem(Material.WATER_BUCKET, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_WATERLOGGABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.waterloggable.getChatValue(), p)), clickType -> { + searchParameter.waterloggable = searchParameter.waterloggable.next(); searchGUI(p); })); - swInventory.setItem(29, new SWItem(Material.PISTON, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_UNMOVEABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(search.unmoveable.getChatValue(), p)), clickType -> { - search.unmoveable = search.unmoveable.next(); + swInventory.setItem(29, new SWItem(Material.PISTON, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_UNMOVEABLE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, BauSystem.MESSAGE.parse(searchParameter.unmoveable.getChatValue(), p)), clickType -> { + searchParameter.unmoveable = searchParameter.unmoveable.next(); searchGUI(p); })); - swInventory.setItem(34, new SWItem(Material.NETHER_BRICK, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, search.blastResistance), clickType -> { - SWAnvilInv swAnvilInv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p), search.blastResistance); + swInventory.setItem(34, new SWItem(Material.NETHER_BRICK, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p) + BauSystem.MESSAGE.parse("MATERIAL_SEARCH_VALUE", p, searchParameter.blastResistance), clickType -> { + SWAnvilInv swAnvilInv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("MATERIAL_SEARCH_BLASTRESISTANCE", p), searchParameter.blastResistance); swAnvilInv.setCallback(s -> { if (s.isEmpty() || s.matches("((([><]=?)|!|=)\\d+(\\.|,\\d+)?)( ((([><]=?)|!|=)\\d+(\\.|,\\d+)?))*")) { - search.blastResistance = s; + searchParameter.blastResistance = s; } searchGUI(p); }); @@ -262,14 +261,4 @@ public class MaterialCommand extends SWCommand implements Listener { })); swInventory.open(); } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - searchMap.put(event.getPlayer(), new Search()); - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - searchMap.remove(event.getPlayer()); - } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialLazyInit.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialLazyInit.java index 4f174c12..02ffc609 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialLazyInit.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/MaterialLazyInit.java @@ -133,7 +133,7 @@ public class MaterialLazyInit { }), originalMaterial); } - public boolean is(MaterialCommand.Search search) { + public boolean is(MaterialCommand.SearchParameter search) { boolean result = true; result &= search.transparent.test(transparent); result &= search.solid.test(solid); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java index ad97dac0..61fdecdc 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java @@ -19,17 +19,17 @@ package de.steamwar.bausystem.features.util; -import de.steamwar.Reflection; import com.comphenix.tinyprotocol.TinyProtocol; import com.mojang.authlib.GameProfile; +import de.steamwar.Reflection; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.features.tpslimit.TPSUtils; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.bausystem.utils.NMSWrapper; import de.steamwar.command.SWCommand; import de.steamwar.core.ProtocolWrapper; +import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; -import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.entity.Player; @@ -40,10 +40,6 @@ import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerToggleFlightEvent; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.function.BiFunction; @Linked @@ -59,29 +55,34 @@ public class NoClipCommand extends SWCommand implements Listener { private static final Class windowClick = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundContainerClickPacket"); private static final Class setSlotStack = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket"); - @Getter - private static final List NOCLIPS = new ArrayList<>(); - private static final Map LAST_TICKS = new HashMap<>(); + public static class NoClipData implements SWPlayer.Component { + private long lastTick = -1; + + @Override + public void onUnmount(SWPlayer player) { + player.setGameMode(GameMode.CREATIVE); + } + } public NoClipCommand() { super("noclip", "nc"); BiFunction first = (player, o) -> { - if (NOCLIPS.contains(player)) { - if (LAST_TICKS.getOrDefault(player, -1L).equals(TPSUtils.currentTick.get())) return o; - NMSWrapper.impl.setInternalGameMode(player, GameMode.SPECTATOR); - LAST_TICKS.put(player, TPSUtils.currentTick.get()); - } + NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null); + if (noClipData == null) return o; + if (noClipData.lastTick == TPSUtils.currentTick.get()) return o; + NMSWrapper.impl.setInternalGameMode(player, GameMode.SPECTATOR); + noClipData.lastTick = TPSUtils.currentTick.get(); return o; }; TinyProtocol.instance.addFilter(position, first); TinyProtocol.instance.addFilter(positionLook, first); BiFunction second = (player, o) -> { - if (NOCLIPS.contains(player)) { - NMSWrapper.impl.setInternalGameMode(player, GameMode.CREATIVE); - LAST_TICKS.put(player, TPSUtils.currentTick.get()); - } + NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null); + if (noClipData == null) return o; + NMSWrapper.impl.setInternalGameMode(player, GameMode.CREATIVE); + noClipData.lastTick = TPSUtils.currentTick.get(); return o; }; TinyProtocol.instance.addFilter(useItem, second); @@ -89,7 +90,7 @@ public class NoClipCommand extends SWCommand implements Listener { TinyProtocol.instance.addFilter(windowClick, second); BiFunction third = (player, o) -> { - if (NOCLIPS.contains(player)) { + if (SWPlayer.of(player).hasComponent(NoClipData.class)) { NMSWrapper.impl.setSlotToItemStack(player, o); } return o; @@ -99,9 +100,9 @@ public class NoClipCommand extends SWCommand implements Listener { @Register(help = true) public void genericCommand(@Validator Player player) { - if (NOCLIPS.contains(player)) { - NOCLIPS.remove(player); - player.setGameMode(GameMode.CREATIVE); + SWPlayer swPlayer = SWPlayer.of(player); + if (swPlayer.hasComponent(NoClipData.class)) { + swPlayer.removeComponent(NoClipData.class); } else { player.setGameMode(GameMode.SPECTATOR); NMSWrapper.impl.setPlayerBuildAbilities(player); @@ -109,8 +110,8 @@ public class NoClipCommand extends SWCommand implements Listener { Object gameStateChangeObject = Reflection.newInstance(gameStateChange); NMSWrapper.impl.setGameStateChangeReason(gameStateChangeObject); floatFieldAccessor.set(gameStateChangeObject, 1F); - - NOCLIPS.add(player); + + swPlayer.setComponent(new NoClipData()); BauSystem.MESSAGE.send("OTHER_NOCLIP_SLOT_INFO", player); TinyProtocol.instance.sendPacket(player, gameStateChangeObject); pseudoGameMode(player, GameMode.SPECTATOR); @@ -120,30 +121,27 @@ public class NoClipCommand extends SWCommand implements Listener { @EventHandler public void onBauMemberUpdate(BauMemberUpdateEvent event) { event.getNewSpectator().forEach(player -> { - if (NOCLIPS.contains(player)) { - NOCLIPS.remove(player); - player.setGameMode(GameMode.CREATIVE); - } + SWPlayer.of(player).removeComponent(NoClipData.class); }); } @EventHandler(ignoreCancelled = true) public void onPlayerGameModeChange(PlayerGameModeChangeEvent event) { - if (NOCLIPS.contains(event.getPlayer())) { + if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onBlock(BlockCanBuildEvent event) { - if (NOCLIPS.contains(event.getPlayer())) { + if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) { event.setBuildable(true); } } @EventHandler(ignoreCancelled = true) public void onPlayerToggleFlight(PlayerToggleFlightEvent event) { - if (NOCLIPS.contains(event.getPlayer())) { + if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) { event.setCancelled(true); event.getPlayer().setFlying(true); } @@ -154,7 +152,7 @@ public class NoClipCommand extends SWCommand implements Listener { if (event.getCause() != PlayerTeleportEvent.TeleportCause.SPECTATE) { return; } - if (NOCLIPS.contains(event.getPlayer())) { + if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) { event.setCancelled(true); Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { event.getPlayer().setSpectatorTarget(null); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/items/SelectBauGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/items/SelectBauGuiItem.java index 78669a8d..43b9cb85 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/items/SelectBauGuiItem.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/items/SelectBauGuiItem.java @@ -24,6 +24,7 @@ import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.linkage.BauGuiItem; import de.steamwar.bausystem.region.utils.RegionExtensionType; import de.steamwar.bausystem.region.utils.RegionType; +import de.steamwar.core.SWPlayer; import de.steamwar.inventory.SWInventory; import de.steamwar.inventory.SWItem; import de.steamwar.linkage.Linked; @@ -35,15 +36,11 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import java.util.Arrays; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; @Linked public class SelectBauGuiItem extends BauGuiItem { - private static final Map LAST_SELECT_MAP = new HashMap<>(); - public SelectBauGuiItem() { super(13); } @@ -58,13 +55,13 @@ public class SelectBauGuiItem extends BauGuiItem { private static void selectFinish(Player p, RegionType type, RegionExtensionType extensionType) { p.closeInventory(); - LAST_SELECT_MAP.put(p, new LastSelect(type, extensionType)); + SWPlayer.of(p).setComponent(new LastSelect(type, extensionType)); p.performCommand("select " + type.name() + " " + extensionType.toString()); } @Override public ItemStack getItem(Player player) { - LastSelect last = LAST_SELECT_MAP.getOrDefault(player, new LastSelect(RegionType.BUILD, RegionExtensionType.NORMAL)); + LastSelect last = SWPlayer.of(player).getComponentOrDefault(LastSelect.class, () -> new LastSelect(RegionType.BUILD, RegionExtensionType.NORMAL)); return new SWItem(Material.SCAFFOLDING, BauSystem.MESSAGE.parse("SELECT_ITEM_SELECT", player), Arrays.asList(BauSystem.MESSAGE.parse("SELECT_ITEM_AUSWAHL", player, BauSystem.MESSAGE.parse(last.type.getChatValue(), player), last.extensionType.name()), BauSystem.MESSAGE.parse("SELECT_ITEM_RIGHT_CLICK", player)), false, clickType -> { }).getItemStack(); } @@ -80,7 +77,7 @@ public class SelectBauGuiItem extends BauGuiItem { inv.open(); } else { p.closeInventory(); - LastSelect last = LAST_SELECT_MAP.getOrDefault(p, new LastSelect(RegionType.BUILD, RegionExtensionType.NORMAL)); + LastSelect last = SWPlayer.of(p).getComponentOrDefault(LastSelect.class, () -> new LastSelect(RegionType.BUILD, RegionExtensionType.NORMAL)); p.performCommand("select " + last.getType().name() + " " + last.getExtensionType().toString()); } return false; @@ -92,7 +89,7 @@ public class SelectBauGuiItem extends BauGuiItem { } @AllArgsConstructor - private static class LastSelect { + private static class LastSelect implements SWPlayer.Component { @Getter private final RegionType type; @Getter diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/warp/WarpListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/warp/WarpListener.java index 60121c90..f57ff726 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/warp/WarpListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/warp/WarpListener.java @@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.warp; import de.steamwar.bausystem.region.Region; import de.steamwar.core.Core; +import de.steamwar.core.SWPlayer; import de.steamwar.entity.RArmorStand; import de.steamwar.entity.REntityServer; import de.steamwar.linkage.Linked; @@ -30,7 +31,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.player.*; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; @@ -42,8 +46,21 @@ import java.util.Map; @Linked public class WarpListener implements Listener { - private Map warpEntityServer = new HashMap<>(); - private Map> selected = new HashMap<>(); + public static class WarpComponent implements SWPlayer.Component { + private REntityServer server; + private List selected = new ArrayList<>(); + + @Override + public void onMount(SWPlayer player) { + server = new REntityServer(); + server.addPlayer(player.getPlayer()); + } + + @Override + public void onUnmount(SWPlayer player) { + server.close(); + } + } @EventHandler public void onPlayerItemHeld(PlayerItemHeldEvent e) { @@ -65,20 +82,11 @@ public class WarpListener implements Listener { } private void reshow(Player p, Material material, boolean sneaking) { - REntityServer entityServer = warpEntityServer.get(p); - if (entityServer != null) { - entityServer.close(); - } - if (material != Material.COMPASS) { - warpEntityServer.remove(p); - return; - } - - selected.remove(p); - entityServer = new REntityServer(); - entityServer.addPlayer(p); - warpEntityServer.put(p, entityServer); + SWPlayer swPlayer = SWPlayer.of(p); + swPlayer.removeComponent(WarpComponent.class); + if (material != Material.COMPASS) return; + WarpComponent warpComponent = swPlayer.getComponentOrDefault(WarpComponent.class, WarpComponent::new); Vector current = p.getLocation().clone().add(p.getLocation().getDirection().multiply(5)).toVector(); Map locations = new HashMap<>(); @@ -99,12 +107,9 @@ public class WarpListener implements Listener { } } - REntityServer finalEntityServer = entityServer; locations.forEach((name, location) -> { Vector vector = location.toVector().subtract(p.getLocation().toVector()); - if (vector.getX() * vector.getX() + vector.getZ() * vector.getZ() < 25) { - return; - } + if (vector.getX() * vector.getX() + vector.getZ() * vector.getZ() < 25) return; vector.setY(0); Vector position = p.getLocation().toVector().clone().add(vector.normalize().multiply(5)); @@ -112,9 +117,9 @@ public class WarpListener implements Listener { if ((position.getX() - current.getX()) * (position.getX() - current.getX()) + (position.getZ() - current.getZ()) * (position.getZ() - current.getZ()) < 0.1) { name = "§a§l" + name; - selected.computeIfAbsent(p, player -> new ArrayList<>()).add(location); + warpComponent.selected.add(location); } - RArmorStand armorStand = new RArmorStand(finalEntityServer, position.toLocation(p.getWorld()), RArmorStand.Size.MARKER); + RArmorStand armorStand = new RArmorStand(warpComponent.server, position.toLocation(p.getWorld()), RArmorStand.Size.MARKER); armorStand.setDisplayName(name); armorStand.setNoGravity(true); armorStand.setInvisible(true); @@ -123,28 +128,13 @@ public class WarpListener implements Listener { @EventHandler(priority = EventPriority.LOWEST) public void onPlayerInteract(PlayerInteractEvent event) { - if (event.getPlayer().getInventory().getItemInMainHand().getType() != Material.COMPASS) { - return; - } - if (event.getAction() != Action.RIGHT_CLICK_AIR) { - return; - } - List locations = selected.getOrDefault(event.getPlayer(), new ArrayList<>()); - if (locations.size() != 1) { - return; - } - Location location = locations.get(0); + if (event.getPlayer().getInventory().getItemInMainHand().getType() != Material.COMPASS) return; + if (event.getAction() != Action.RIGHT_CLICK_AIR) return; + WarpComponent warpComponent = SWPlayer.of(event.getPlayer()).getComponent(WarpComponent.class).orElse(null); + if (warpComponent == null || warpComponent.selected.size() != 1) return; + Location location = warpComponent.selected.get(0); event.getPlayer().teleport(location); event.getPlayer().playSound(location, Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 1, 1); event.setCancelled(true); } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - warpEntityServer.computeIfPresent(event.getPlayer(), (player, rEntityServer) -> { - rEntityServer.close(); - return null; - }); - selected.remove(event.getPlayer()); - } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/AntiBauAddMemberFix.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/AntiBauAddMemberFix.java index c8c40cba..da75fe7c 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/AntiBauAddMemberFix.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/AntiBauAddMemberFix.java @@ -41,7 +41,7 @@ public class AntiBauAddMemberFix implements Listener { } if (BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId()) == null) { event.getPlayer().kickPlayer(""); - throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.get(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!"); + throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!"); } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java index a60e2958..1568ba5f 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java @@ -59,18 +59,14 @@ public class BauMemberUpdate extends PacketHandler implements Listener { Set newSpectator = new HashSet<>(); Set newBuilder = new HashSet<>(); Bukkit.getOnlinePlayers().forEach(player -> { - if (Permission.REAL_SPECTATOR.hasPermission(player)) { + if (Permission.SPECTATOR.hasPermission(player)) { if (!SPECTATORS.contains(player)) { SPECTATORS.add(player); - Permission.removeForceOnlySpectator(player); newSpectator.add(player); player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, -1, 1, false,false, false)); showSpectatorNotice(player); } } else { - if (Permission.SUPERVISOR.hasPermission(player)) { - Permission.removeForceOnlySpectator(player); - } if (SPECTATORS.contains(player)) { SPECTATORS.remove(player); newBuilder.add(player); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/OtherTNTListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/OtherTNTListener.java deleted file mode 100644 index 4deb1e5d..00000000 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/OtherTNTListener.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.bausystem.features.world; - -import de.steamwar.linkage.Linked; -import org.bukkit.Material; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityExplodeEvent; - -@Linked -public class OtherTNTListener implements Listener { - - @EventHandler(priority = EventPriority.MONITOR) - public void onExplosion(EntityExplodeEvent e) { - e.blockList().removeIf(block -> { - if(block.getType() == Material.TNT) { - return false; - } else { - block.setType(Material.AIR); - return true; - } - }); - } -} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java index 9874c1fd..cfdc6fe3 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java @@ -49,6 +49,7 @@ import java.util.Set; @Linked public class SpectatorListener implements Listener { + private static final TechHider techHider; private static final Set NO_TECHHIDER = new HashSet<>(); static { @@ -100,10 +101,20 @@ public class SpectatorListener implements Listener { materials.add(Material.WATER); materials.remove(Material.BARRIER); materials.remove(Material.STONE); - TechHider techHider = new TechHider((TechHider.LocationEvaluator) (player, i, i1) -> { - return Permission.BUILD.hasPermission(player) || Permission.isTempOnlySpectator(player) || NO_TECHHIDER.contains(player); + techHider = new TechHider((TechHider.LocationEvaluator) (player, i, i1) -> { + return Permission.BUILD.hasPermission(player) || NO_TECHHIDER.contains(player); }, Material.END_STONE, materials, new HashSet<>()); - techHider.enable(); + } + + private static void enableOrDisableTechhider() { + boolean enable = Bukkit.getOnlinePlayers() + .stream() + .anyMatch(player -> !(Permission.BUILD.hasPermission(player) || NO_TECHHIDER.contains(player))); + if (enable) { + techHider.enable(); + } else { + techHider.disable(); + } } public static void toggleNoTechHider(Player player) { @@ -113,6 +124,7 @@ public class SpectatorListener implements Listener { NO_TECHHIDER.add(player); } resendChunks(player); + enableOrDisableTechhider(); } private static void resendChunks(Player player) { @@ -131,6 +143,7 @@ public class SpectatorListener implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { + enableOrDisableTechhider(); if (BauSystem.DEV_SERVER) return; if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) { return; @@ -152,6 +165,7 @@ public class SpectatorListener implements Listener { @EventHandler public void onBauMemberUpdate(BauMemberUpdateEvent event) { + enableOrDisableTechhider(); if (!anySupervisorOnline(null) && !BauSystem.DEV_SERVER) { Bukkit.getOnlinePlayers().forEach(player -> { player.kickPlayer(""); @@ -161,7 +175,6 @@ public class SpectatorListener implements Listener { event.getChanged().forEach(player -> { NO_TECHHIDER.remove(player); - if (Permission.isTempOnlySpectator(player)) return; resendChunks(player); }); } @@ -169,6 +182,7 @@ public class SpectatorListener implements Listener { @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { NO_TECHHIDER.remove(event.getPlayer()); + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), SpectatorListener::enableOrDisableTechhider, 1); if (BauSystem.DEV_SERVER) return; if (!anySupervisorOnline(event.getPlayer())) { Bukkit.getOnlinePlayers().forEach(player -> { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java index 518ca00c..3f6ca8f2 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java @@ -23,6 +23,7 @@ import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.region.Point; import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.utils.FlatteningWrapper; +import de.steamwar.core.SWPlayer; import de.steamwar.core.WorldEditRenderer; import de.steamwar.linkage.Linked; import de.steamwar.linkage.MinVersion; @@ -35,13 +36,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -71,32 +69,23 @@ public class SelectAdjacent implements Listener { new Vector(0, -1, -1), }; - private Map selectors = new HashMap<>(); - @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { if (!event.hasItem()) return; if (event.getItem().getType() != Material.WOODEN_AXE) return; - Selector selector = selectors.get(event.getPlayer()); - if (selector != null) selector.cancel(); if (!event.getPlayer().isSneaking()) return; if (event.getAction() != Action.LEFT_CLICK_BLOCK) return; Material material = event.getPlayer().getInventory().getItemInOffHand().getType(); + Selector selector; if (material.isAir()) { selector = new Selector(event.getClickedBlock(), event.getPlayer(), __ -> true); } else { selector = new Selector(event.getClickedBlock(), event.getPlayer(), type -> type == material); } - selectors.put(event.getPlayer(), selector); + SWPlayer.of(event.getPlayer()).setComponent(selector); } - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - Selector selector = selectors.remove(event.getPlayer()); - if (selector != null) selector.cancel(); - } - - private class Selector { + private class Selector implements SWPlayer.Component { private static final int MAX_BLOCKS = 500_000; @@ -154,6 +143,7 @@ public class SelectAdjacent implements Listener { if (toCalc.isEmpty() || seen.size() > MAX_BLOCKS) { bukkitTask.cancel(); player.sendTitle("§aDone", "§e" + volume + " §7Blocks", 0, 20, 5); + SWPlayer.of(player).removeComponent(Selector.class); } }, 1, 1); } @@ -190,5 +180,10 @@ public class SelectAdjacent implements Listener { } } } + + @Override + public void onUnmount(SWPlayer player) { + cancel(); + } } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java index babd0d88..b128e27a 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java @@ -104,7 +104,7 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen if (hidden.containsKey(region) && hidden.get(region).contains(player)) { Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { PlayerMovementWrapper.impl.setPosition(player, o); - }, 1L); + }, 0); return null; } return o; diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index aeade561..df33b12e 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -50,5 +50,6 @@ tasks.register("DevBau21") { description = "Run a 1.21 Dev Bau" dependsOn(":SpigotCore:shadowJar") dependsOn(":BauSystem:shadowJar") + dependsOn(":SchematicSystem:shadowJar") template = "Bau21" } diff --git a/CommonCore/SQL/build.gradle.kts b/CommonCore/SQL/build.gradle.kts index dc171dde..09528759 100644 --- a/CommonCore/SQL/build.gradle.kts +++ b/CommonCore/SQL/build.gradle.kts @@ -18,11 +18,26 @@ */ plugins { - steamwar.java + steamwar.kotlin +} + +kotlin { + jvmToolchain(8) +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { compileOnly(libs.sqlite) implementation("org.yaml:snakeyaml:2.2") -} \ No newline at end of file + + compileOnlyApi("org.jetbrains.kotlin:kotlin-stdlib:2.2.21") + compileOnlyApi(libs.exposedCore) + compileOnlyApi(libs.exposedDao) + compileOnlyApi(libs.exposedJdbc) + compileOnlyApi(libs.exposedTime) +} diff --git a/CommonCore/SQL/src/de/steamwar/sql/AuditLog.java b/CommonCore/SQL/src/de/steamwar/sql/AuditLog.java deleted file mode 100644 index 04f87d9d..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/AuditLog.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 . - */ - -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 java.sql.Timestamp; -import java.time.Instant; - -@AllArgsConstructor -public class AuditLog { - - static { - SqlTypeMapper.nameEnumMapper(AuditLog.Type.class); - } - - public static final String SERVER_NAME_VELOCITY = "Velocity"; - - private static final Table 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 { - 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); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/AuditLog.kt b/CommonCore/SQL/src/de/steamwar/sql/AuditLog.kt new file mode 100644 index 00000000..7ab0c8c4 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/AuditLog.kt @@ -0,0 +1,121 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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 + +object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") { + val time = timestamp("Time") + val server = varchar("ServerName", 255) + val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable() + val actor = reference("Actor", SteamwarUserTable) + val action = enumerationByName("ActionType", 255, AuditLog.Type::class) + val actionText = text("ActionText") +} + +class AuditLog(id: EntityID): IntEntity(id) { + companion object: IntEntityClass(AuditLogTable) { + const val SERVER_NAME_VELOCITY: String = "Velocity" + + 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 + this.actor = actor.id + 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) + } + + 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 + + enum class Type { + JOIN, + LEAVE, + COMMAND, + SENSITIVE_COMMAND, + CHAT, + GUI_OPEN, + GUI_CLOSE, + GUI_CLICK, + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.java b/CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.java deleted file mode 100644 index f16e0343..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 . - */ - -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 java.sql.Timestamp; -import java.time.Instant; -import java.util.List; - -@AllArgsConstructor -public class BannedUserIPs { - - private static final Table table = new Table<>(BannedUserIPs.class); - - private static final SelectStatement getByID = table.selectFields("UserID"); - private static final SelectStatement 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 get(int userID) { - return getByID.listSelect(userID); - } - - public static List 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); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt b/CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt new file mode 100644 index 00000000..495238ad --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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 + +object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") { + val userId = reference("UserID", SteamwarUserTable) + val timestamp = timestamp("Timestamp") + val ip = varchar("IP", 45) + + override val primaryKey = PrimaryKey(userId, ip) +} + +class BannedUserIPs(id: EntityID): CompositeEntity(id) { + companion object: CompositeEntityClass(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() + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/BauweltMember.java b/CommonCore/SQL/src/de/steamwar/sql/BauweltMember.java deleted file mode 100644 index 71fa3a46..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/BauweltMember.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 . - */ - -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 java.util.*; - -public class BauweltMember { - private static final Map memberCache = new HashMap<>(); - - public static void clear() { - memberCache.clear(); - } - - private static final Table table = new Table<>(BauweltMember.class); - private static final SelectStatement getMember = table.select(Table.PRIMARY); - private static final SelectStatement 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 getMembers(UUID bauweltID){ - return getMembers(SteamwarUser.get(bauweltID).getId()); - } - - public static List 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(); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/BauweltMember.kt b/CommonCore/SQL/src/de/steamwar/sql/BauweltMember.kt new file mode 100644 index 00000000..66e3bb8b --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/BauweltMember.kt @@ -0,0 +1,130 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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.* + +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") + + override val primaryKey = PrimaryKey(bauweltId, memberId) + + init { + addIdColumn(bauweltId) + addIdColumn(memberId) + } +} + +class BauweltMember(id: EntityID): CompositeEntity(id) { + companion object: CompositeEntityClass(BauweltMemberTable) { + private val cache = mutableMapOf() + + 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, newMemberId: EntityID) = 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() = build + fun isSupervisor() = world + fun isWorldEdit() = build + fun isWorld() = world + + fun remove() = useDb { + delete() + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/CheckedSchematic.java b/CommonCore/SQL/src/de/steamwar/sql/CheckedSchematic.java deleted file mode 100644 index 5a0e553c..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/CheckedSchematic.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 . - */ - -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 java.sql.Timestamp; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -@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"); - private static final SelectStatement nodeHistory = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); - private static final Statement insert = table.insertAll(); - - private static final SelectStatement getUnseen = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE Seen = 0 AND NodeOwner = ? ORDER BY StartTime DESC"); - private static final Statement updateSeen = new Statement("UPDATE CheckedSchematic SET Seen = ? WHERE StartTime = ? AND EndTime = ? AND NodeName = ?"); - - public static void create(SchematicNode node, int validator, Timestamp startTime, Timestamp endTime, String reason, boolean seen) { - insert.update(node.getId(), node.getOwner(), node.getName(), validator, startTime, endTime, reason, seen, node.getSchemtype().toDB().substring(1)); - } - - public static List getLastDeclinedOfNode(int node) { - return statusOfNode.listSelect(node); - } - - public static List previousChecks(SchematicNode node) { - return nodeHistory.listSelect(node.getId()); - } - - public static List getUnseen(SteamwarUser owner) { - return getUnseen.listSelect(owner); - } - - @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; - @Getter - @Field - private boolean seen; - @Getter - @Field - private final String nodeType; - - public int getNode() { - return nodeId; - } - - public String getSchemName() { - return nodeName; - } - - public int getSchemOwner() { - return nodeOwner; - } - - public void setSeen(boolean seen) { - this.seen = seen; - updateSeen.update(seen, startTime, endTime, nodeName); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/CheckedSchematic.kt b/CommonCore/SQL/src/de/steamwar/sql/CheckedSchematic.kt new file mode 100644 index 00000000..3e51d24e --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/CheckedSchematic.kt @@ -0,0 +1,100 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.SortOrder +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.core.neq +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.insertIgnore +import java.sql.Timestamp + +object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") { + val nodeId = optReference("NodeId", SchematicNodeTable) + val nodeOwner = reference("NodeOwner", SteamwarUserTable) + val nodeName = varchar("NodeName", 64).entityId() + val validator = reference("Validator", SteamwarUserTable) + val startTime = timestamp("StartTime").entityId() + val endTime = timestamp("EndTime") + val declineReason = text("DeclineReason") + val seen = bool("Seen") + val nodeType = varchar("NodeType", 16) + + init { + addIdColumn(nodeOwner) + addIdColumn(nodeName) + } +} + +class CheckedSchematic(id: EntityID): CompositeEntity(id) { + companion object: CompositeEntityClass(CheckedSchematicTable) { + @JvmStatic + fun create(node: SchematicNode, validator: Int, startTime: Timestamp, endTime: Timestamp, reason: String, seen: Boolean) = useDb { + CheckedSchematicTable.insertIgnore { + it[this.nodeId] = node.id + it[this.nodeOwner] = EntityID(node.owner, SteamwarUserTable) + it[this.nodeName] = node.name + it[this.validator] = EntityID(validator, SteamwarUserTable) + it[this.startTime] = startTime.toInstant() + it[this.endTime] = endTime.toInstant() + it[this.declineReason] = reason + it[this.seen] = seen + it[this.nodeType] = node.schemtype.toDB().substring(1) + } + } + + @JvmStatic + fun getLastDeclinedOfNode(node: Int) = useDb { + find { (CheckedSchematicTable.nodeId eq node) and (CheckedSchematicTable.declineReason neq "Prüfvorgang abgebrochen") }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList() + } + + @JvmStatic + fun previousChecks(node: SchematicNode) = useDb { + find { (CheckedSchematicTable.nodeId eq node.id) and (CheckedSchematicTable.declineReason neq "") and (CheckedSchematicTable.declineReason neq "Prüfvorgang abgebrochen") }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList() + } + + @JvmStatic + fun getUnseen(owner: SteamwarUser) = useDb { + find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.seen eq false) }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList() + } + } + + val node by CheckedSchematicTable.nodeId.transform({ it?.let { EntityID(it, SchematicNodeTable) } }, { it?.value }) + val schemOwner by CheckedSchematicTable.nodeOwner.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + val nodeName by CheckedSchematicTable.nodeName + val schemName get() = nodeName.value + val validator by CheckedSchematicTable.validator.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + val startTimeId by CheckedSchematicTable.startTime + val startTime get() = Timestamp.from(startTimeId.value) + val endTime by CheckedSchematicTable.endTime.transform({ it.toInstant() }, { Timestamp.from(it) }) + val declineReason by CheckedSchematicTable.declineReason + private var wasSeen by CheckedSchematicTable.seen + var seen: Boolean + get() = wasSeen + set(value) = useDb { wasSeen = value } + val nodeType by CheckedSchematicTable.nodeType +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/Event.java b/CommonCore/SQL/src/de/steamwar/sql/Event.java deleted file mode 100644 index e65a1533..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/Event.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 . - */ - -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 java.sql.Timestamp; -import java.time.Instant; -import java.util.List; - -@AllArgsConstructor -public class Event { - - static { - SchematicType.Normal.name(); // Ensure SchematicType is loaded. - } - - private static final Table table = new Table<>(Event.class); - - private static final SelectStatement byCurrent = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start < now() AND End > now()"); - private static final SelectStatement byId = table.select(Table.PRIMARY); - private static final SelectStatement byName = table.select("eventName"); - private static final SelectStatement byComing = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start > now()"); - private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM Event"); - - private static final Statement create = table.insertFields(true, "eventName", "deadline", "start", "end", "maximumTeamMembers", "publicSchemsOnly"); - private static final Statement update = table.update(Table.PRIMARY, "eventName", "deadline", "start", "end", "schemType", "maximumTeamMembers", "publicSchemsOnly"); - private static final Statement delete = table.delete(Table.PRIMARY); - - private static Event current = null; - - public static Event get(){ - if(current != null && current.now()) - return current; - - current = byCurrent.select(); - return current; - } - - public static List getAll(){ - return all.listSelect(); - } - - public static Event create(String eventName, Timestamp start, Timestamp end){ - return get(create.insertGetKey(eventName, start, start, end, 5, false)); - } - - public static Event get(int eventID){ - return byId.select(eventID); - } - - public static Event get(String eventName) { - return byName.select(eventName); - } - - public static List getComing() { - return byComing.listSelect(); - } - - @Getter - @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int eventID; - @Getter - @Field(keys = {"eventName"}) - private final String eventName; - @Getter - @Field - private final Timestamp deadline; - @Getter - @Field - private final Timestamp start; - @Getter - @Field - private final Timestamp end; - @Getter - @Field - private final int maximumTeamMembers; - @Field(nullable = true) - private final SchematicType schemType; - @Field - private final boolean publicSchemsOnly; - - public boolean publicSchemsOnly() { - return publicSchemsOnly; - } - - public SchematicType getSchematicType() { - return schemType; - } - - private boolean now() { - Instant now = Instant.now(); - return now.isAfter(start.toInstant()) && now.isBefore(end.toInstant()); - } - - public void update(String eventName, Timestamp deadline, Timestamp start, Timestamp end, SchematicType schemType, int maximumTeamMembers, boolean publicSchemsOnly) { - update.update(eventName, deadline, start, end, schemType, maximumTeamMembers, publicSchemsOnly, eventID); - } - - public void delete() { - delete.update(eventID); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Event.kt b/CommonCore/SQL/src/de/steamwar/sql/Event.kt new file mode 100644 index 00000000..ffc08b8d --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/Event.kt @@ -0,0 +1,121 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.and +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.greater +import org.jetbrains.exposed.v1.core.lessEq +import org.jetbrains.exposed.v1.dao.IntEntity +import org.jetbrains.exposed.v1.dao.IntEntityClass +import org.jetbrains.exposed.v1.javatime.timestamp +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import java.sql.Timestamp +import java.time.Instant + +object EventTable : IntIdTable("Event", "EventId") { + val name = varchar("EventName", 100).uniqueIndex() + val deadline = timestamp("Deadline") + val start = timestamp("Start") + val end = timestamp("End") + val maxPlayers = integer("MaximumTeamMembers") + val schemType = varchar("SchemType", 16).nullable() + val publicsOnly = bool("PublicSchemsOnly") +} + +class Event(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(EventTable) { + private var current: Event? = null + + @JvmStatic + fun get(): Event? = if (current?.now() == true) { + current + } else useDb { + find { EventTable.start.lessEq(Instant.now()) and EventTable.end.greater(Instant.now()) }.firstOrNull() + ?.also { current == it } + } + + @JvmStatic + fun getAll() = useDb { all().toList() } + + @JvmStatic + fun create(name: String, start: Timestamp, end: Timestamp) = useDb { + EventTable.insertAndGetId { + it[this.name] = name + it[this.deadline] = start.toInstant() + it[this.start] = start.toInstant() + it[this.end] = end.toInstant() + it[this.maxPlayers] = 5 + it[this.publicsOnly] = false + }.let { get(it) } + } + + @JvmStatic + fun byId(id: Int) = useDb { findById(id) } + + @JvmStatic + fun get(name: String) = useDb { find { EventTable.name eq name }.firstOrNull() } + + @JvmStatic + fun getComing() = useDb { find { EventTable.start greater Instant.now() }.toList() } + } + + val eventID by EventTable.id.transform({ EntityID(it, EventTable) }, { it.value }) + var eventName by EventTable.name + private set + var deadline: Timestamp by EventTable.deadline.transform({ it.toInstant() }, { Timestamp.from(it) }) + private set + var start: Timestamp by EventTable.start.transform({ it.toInstant() }, { Timestamp.from(it) }) + private set + var end: Timestamp by EventTable.end.transform({ it.toInstant() }, { Timestamp.from(it) }) + private set + var maximumTeamMembers by EventTable.maxPlayers + private set + var schematicType by EventTable.schemType.transform({ it?.toDB() }, { it?.let { SchematicType.fromDB(it) } }) + private set + var publicSchemsOnly by EventTable.publicsOnly + private set + + fun publicSchemsOnly() = publicSchemsOnly + fun now() = Instant.now().let { it.isAfter(start.toInstant()) && it.isBefore(end.toInstant()) } + + fun update( + name: String, + deadline: Timestamp, + start: Timestamp, + end: Timestamp, + schematicType: SchematicType?, + maxPlayers: Int, + publicSchemsOnly: Boolean + ) = useDb { + this@Event.eventName = name + this@Event.deadline = deadline + this@Event.start = start + this@Event.end = end + this@Event.maximumTeamMembers = maxPlayers + this@Event.schematicType = schematicType + this@Event.publicSchemsOnly = publicSchemsOnly + } + + override fun delete() = useDb { super.delete() } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/EventFight.java b/CommonCore/SQL/src/de/steamwar/sql/EventFight.java deleted file mode 100644 index a1472918..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/EventFight.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * 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 . - */ - -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 lombok.Setter; - -import java.sql.Timestamp; -import java.util.*; - -import static java.time.temporal.ChronoUnit.SECONDS; - -@AllArgsConstructor -public class EventFight implements Comparable { - - private static final Table table = new Table<>(EventFight.class); - private static final SelectStatement byId = table.select(Table.PRIMARY); - private static final SelectStatement byGroup = new SelectStatement(table, "SELECT * FROM EventFight WHERE GroupID = ? ORDER BY StartTime ASC"); - private static final SelectStatement byGroupLast = new SelectStatement(table, "SELECT * FROM EventFight WHERE GroupID = ? ORDER BY StartTime DESC LIMIT 1"); - private static final SelectStatement allComing = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE StartTime > now() ORDER BY StartTime ASC"); - private static final SelectStatement event = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE EventID = ? ORDER BY StartTime ASC"); - private static final SelectStatement activeFights = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE EventID IN (SELECT EventID FROM Event WHERE Start < now() and End > now()) AND Fight IS NULL AND StartTime < now()"); - private static final Statement reschedule = table.update(Table.PRIMARY, "StartTime"); - private static final Statement setResult = table.update(Table.PRIMARY, "Ergebnis"); - private static final Statement setFight = table.update(Table.PRIMARY, "Fight"); - - private static final Statement create = table.insertFields(true, "eventID", "startTime", "spielmodus", "map", "teamBlue", "teamRed", "spectatePort"); - private static final Statement update = table.update(Table.PRIMARY, "startTime", "spielModus", "map", "teamBlue", "teamRed", "spectatePort"); - private static final Statement setGroup = table.update(Table.PRIMARY, "GroupID"); - private static final Statement delete = table.delete(Table.PRIMARY); - - @Getter - private static final Queue fights = new PriorityQueue<>(); - - public static EventFight get(int fightID) { - return byId.select(fightID); - } - - public static List get(EventGroup group) { - return byGroup.listSelect(group.getId()); - } - - public static Optional getLast(EventGroup group) { - return Optional.ofNullable(byGroupLast.select(group.getId())); - } - - public static void loadAllComingFights() { - fights.clear(); - fights.addAll(allComing.listSelect()); - } - - public static List getEvent(int eventID) { - return event.listSelect(eventID); - } - - private static List activeFightsCache = null; - - public static void clearActiveFightsCache() { - activeFightsCache = null; - } - - public static List getActiveFights() { - if (activeFightsCache == null) { - activeFightsCache = activeFights.listSelect(); - } - return activeFightsCache; - } - - public static EventFight create(int event, Timestamp from, String spielmodus, String map, int blueTeam, int redTeam, Integer spectatePort) { - return get(create.insertGetKey(event, from, spielmodus, map, blueTeam, redTeam, spectatePort)); - } - - @Getter - @Field - private final int eventID; - @Getter - @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int fightID; - @Getter - @Setter - @Field(nullable = true, def = "null") - private Integer groupId; - @Getter - @Setter - @Field - private Timestamp startTime; - @Getter - @Setter - @Field - private String spielmodus; - @Getter - @Setter - @Field - private String map; - @Getter - @Setter - @Field - private int teamBlue; - @Getter - @Setter - @Field - private int teamRed; - @Getter - @Setter - @Field(nullable = true) - private Integer spectatePort; - @Getter - @Setter - @Field(def = "1") - private int bestOf; - @Getter - @Field(def = "0") - private int ergebnis; - @Field(nullable = true) - private int fight; - - public Optional getGroup() { - return Optional.ofNullable(groupId).flatMap(EventGroup::get); - } - - public Optional getWinner() { - if(ergebnis == 0) - return Optional.empty(); - return Optional.ofNullable(ergebnis == 1 ? Team.get(teamBlue) : Team.get(teamRed)); - } - - public Optional getLosser() { - if(ergebnis == 0) - return Optional.empty(); - return Optional.ofNullable(ergebnis == 1 ? Team.get(teamRed) : Team.get(teamBlue)); - } - - public List getDependents() { - return EventRelation.getFightRelations(this); - } - - public void setErgebnis(int winner) { - this.ergebnis = winner; - setResult.update(winner, fightID); - } - - public void setFight(int fight) { - //Fight.FightID, not EventFight.FightID - this.fight = fight; - setFight.update(fight, fightID); - } - - public void setGroup(Integer group) { - setGroup.update(group, fightID); - this.groupId = group; - } - - public boolean hasFinished() { - return fight != 0 || ergebnis != 0; - } - - public void reschedule() { - startTime = Timestamp.from(new Date().toInstant().plus(30, SECONDS)); - reschedule.update(startTime, fightID); - } - - @Override - public int hashCode(){ - return fightID; - } - - @Override - public boolean equals(Object o){ - if(o == null) - return false; - if(!(o instanceof EventFight)) - return false; - return fightID == ((EventFight) o).fightID; - } - - @Override - public int compareTo(EventFight o) { - return startTime.compareTo(o.startTime); - } - - public void update(Timestamp startTime, String spielmodus, String map, int teamBlue, int teamRed, Integer spectatePort) { - update.update(startTime, spielmodus, map, teamBlue, teamRed, spectatePort, fightID); - this.startTime = startTime; - this.spielmodus = spielmodus; - this.map = map; - this.teamBlue = teamBlue; - this.teamRed = teamRed; - this.spectatePort = spectatePort; - } - - public void delete() { - delete.update(fightID); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/EventFight.kt b/CommonCore/SQL/src/de/steamwar/sql/EventFight.kt new file mode 100644 index 00000000..f4c60248 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/EventFight.kt @@ -0,0 +1,188 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.* +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 org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.select +import java.sql.Timestamp +import java.time.Instant +import java.util.* + +object EventFightTable : IntIdTable("EventFight", "FightID") { + val eventId = reference("EventID", EventTable) + val startTime = timestamp("StartTime") + val gamemode = text("Spielmodus") + val map = text("Map") + val groupId = optReference("GroupId", EventGroupTable) + val teamBlue = reference("TeamBlue", TeamTable) + val teamRed = reference("TeamRed", TeamTable) + val spectatePort = integer("SpectatePort").nullable() + val bestOf = integer("BestOf") + val ergebnis = integer("Ergebnis") + val fight = optReference("Fight", FightTable) +} + +class EventFight(id: EntityID) : IntEntity(id), Comparable { + companion object : IntEntityClass(EventFightTable) { + val fights: Queue = PriorityQueue() + @JvmStatic get + + @JvmStatic + fun byId(fightId: Int) = useDb { findById(fightId) } + + @JvmStatic + fun byId(group: EventGroup) = useDb { + find { EventFightTable.groupId eq group.id }.orderBy(EventFightTable.startTime to SortOrder.DESC).toList() + } + + @JvmStatic + fun getLast(group: EventGroup) = useDb { + Optional.ofNullable( + find { EventFightTable.groupId eq group.id }.orderBy(EventFightTable.startTime to SortOrder.DESC) + .firstOrNull() + ) + } + + @JvmStatic + fun loadAllComingFights() = useDb { + fights.clear() + fights.addAll(find { EventFightTable.startTime greaterEq Instant.now() }.orderBy(EventFightTable.startTime to SortOrder.ASC)) + } + + @JvmStatic + fun getEvent(eventId: Int) = useDb { + find { EventFightTable.eventId eq eventId }.orderBy(EventFightTable.startTime to SortOrder.ASC).toList() + } + + private var activeFightsCache: List? = null + + @JvmStatic + fun clearActiveFightsCache() { + activeFightsCache = null + } + + @JvmStatic + fun getActiveFights(): List { + if (activeFightsCache == null) { + activeFightsCache = useDb { + find { + EventFightTable.fight.isNull() and (EventFightTable.startTime less Instant.now()) and (EventFightTable.eventId.inSubQuery( + EventTable.select( + EventTable.id + ) + .where { (EventTable.start less Instant.now()) and (EventTable.end greater Instant.now()) })) + }.orderBy(EventFightTable.startTime to SortOrder.ASC).toList() + } + } + + return activeFightsCache!! + } + + @JvmStatic + fun create( + event: Int, + from: Timestamp, + spielmodus: String, + map: String, + blueTeam: Int, + redTeam: Int, + spectatePort: Int? + ) = useDb { + get( + EventFightTable.insertAndGetId { + it[eventId] = EntityID(event, EventTable) + it[startTime] = from.toInstant() + it[gamemode] = spielmodus + it[EventFightTable.map] = map + it[teamBlue] = EntityID(blueTeam, TeamTable) + it[teamRed] = EntityID(redTeam, TeamTable) + it[EventFightTable.spectatePort] = spectatePort + } + ) + } + } + + val fightID by EventFightTable.id.transform({ EntityID(it, EventFightTable) }, { it.value }) + var teamBlue by EventFightTable.teamBlue.transform({ EntityID(it, TeamTable) }, { it.value }) + var teamRed by EventFightTable.teamRed.transform({ EntityID(it, TeamTable) }, { it.value }) + private var fightErgebnis by EventFightTable.ergebnis + var ergebnis: Int + get() = fightErgebnis + set(value) = useDb { + fightErgebnis = value + } + var eventID by EventFightTable.eventId.transform({ EntityID(it, EventTable) }, { it.value }) + var startTime by EventFightTable.startTime.transform({ it.toInstant() }, { Timestamp.from(it) }) + var spielmodus by EventFightTable.gamemode + var map by EventFightTable.map + var groupId by EventFightTable.groupId + val group by lazy { useDb { Optional.ofNullable(groupId).map { EventGroup[it] } } } + var spectatePort by EventFightTable.spectatePort + private var fightStat by EventFightTable.fight.transform({ it?.let { EntityID(it, EventFightTable) } }, { it?.value }) + var fight: Int? + get() = fightStat + set(value) = useDb { + fightStat = value + } + val dependents by lazy { useDb { EventRelation.getFightRelations(this@EventFight).toList() } } + + val winner: Team? + get() = useDb { if (ergebnis == 1) Team[teamBlue] else if (ergebnis == 2) Team[teamRed] else null } + val losser: Team? + get() = useDb { if (ergebnis == 1) Team[teamRed] else if (ergebnis == 2) Team[teamBlue] else null } + + fun setGroup(group: Int?) = useDb { groupId = group?.let { EntityID(it, EventGroupTable) } } + fun hasFinished() = fight != null || ergebnis != 0 + + fun reschedule() = useDb { + startTime = Timestamp.from(Instant.now().plusSeconds(30)) + } + + override fun hashCode() = fightID + override fun equals(other: Any?) = other is EventFight && other.fightID == fightID + override fun compareTo(other: EventFight): Int = startTime.compareTo(other.startTime) + + fun update( + startTime: Timestamp, + spielmodus: String, + map: String, + teamBlue: Int, + teamRed: Int, + spectatePort: Int? + ) = useDb { + this@EventFight.startTime = startTime + this@EventFight.spielmodus = spielmodus + this@EventFight.map = map + this@EventFight.teamBlue = teamBlue + this@EventFight.teamRed = teamRed + this@EventFight.spectatePort = spectatePort + } + + override fun delete() = useDb { + super.delete() + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/EventGroup.java b/CommonCore/SQL/src/de/steamwar/sql/EventGroup.java deleted file mode 100644 index 68934765..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/EventGroup.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.*; -import lombok.Getter; -import lombok.Setter; - -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@Getter -@Setter -public class EventGroup { - static { - SqlTypeMapper.ordinalEnumMapper(EventGroupType.class); - } - - private static final Table table = new Table<>(EventGroup.class); - - private static final SelectStatement get = table.select(Table.PRIMARY); - private static final SelectStatement byEvent = new SelectStatement<>(table, "SELECT * FROM EventGroup WHERE EventID = ?"); - - private static final Statement insert = table.insertFields(true, "EventID", "Name", "Type"); - private static final Statement update = table.update(Table.PRIMARY, "Name", "Type", "PointsPerWin", "PointsPerLoss", "PointsPerDraw"); - private static final Statement delete = table.delete(Table.PRIMARY); - - public static List get(Event eventID) { - return byEvent.listSelect(eventID.getEventID()); - } - - public static EventGroup create(Event event, String name, EventGroupType type) { - int key = insert.insertGetKey(event.getEventID(), name, type); - return EventGroup.get(key).get(); - } - - public static Optional get(int id) { - return Optional.ofNullable(get.select(id)); - } - - @Field(keys = Table.PRIMARY) - private final int id; - - @Field(keys = "EVENT_NAME") - private int eventID; - - @Field(keys = "EVENT_NAME") - private String name; - - @Field - private EventGroupType type; - - @Field - private int pointsPerWin; - - @Field - private int pointsPerLoss; - - @Field - private int pointsPerDraw; - - public EventGroup(int id, int eventID, String name, EventGroupType type, int pointsPerWin, int pointsPerLoss, int pointsPerDraw) { - this.id = id; - this.eventID = eventID; - this.name = name; - this.type = type; - this.pointsPerWin = pointsPerWin; - this.pointsPerLoss = pointsPerLoss; - this.pointsPerDraw = pointsPerDraw; - } - - private Map points; - - public List getFights() { - return EventFight.get(this); - } - - public Set getTeamsId() { - return getFights().stream().flatMap(fight -> Stream.of(fight.getTeamBlue(), fight.getTeamRed())) - .collect(Collectors.toSet()); - } - - public Set getTeams() { - return getTeamsId().stream().map(Team::get).collect(Collectors.toSet()); - } - - public Optional getLastFight() { - return EventFight.getLast(this); - } - - public List getDependents() { - return EventRelation.getGroupRelations(this); - } - - public Map calculatePoints() { - if (points == null) { - Map p = getTeamsId().stream().collect(Collectors.toMap(team -> team, team -> 0)); - - for (EventFight fight : getFights()) { - int blueTeamAdd = 0; - int redTeamAdd = 0; - - if (!fight.hasFinished()) { - continue; - } - - switch (fight.getErgebnis()) { - case 1: - blueTeamAdd += pointsPerWin; - redTeamAdd += pointsPerLoss; - break; - case 2: - blueTeamAdd += pointsPerLoss; - redTeamAdd += pointsPerWin; - break; - case 0: - if (fight.getFightID() != 0) { - blueTeamAdd += pointsPerDraw; - redTeamAdd += pointsPerDraw; - } - break; - } - - p.put(fight.getTeamBlue(), p.get(fight.getTeamBlue()) + blueTeamAdd); - p.put(fight.getTeamRed(), p.get(fight.getTeamRed()) + redTeamAdd); - } - - points = p.entrySet().stream().collect(Collectors.toMap(integerIntegerEntry -> Team.get(integerIntegerEntry.getKey()), Map.Entry::getValue)); - } - - return points; - } - - public void update(String name, EventGroupType type, int pointsPerWin, int pointsPerLoss, int pointsPerDraw) { - update.update(name, type, pointsPerWin, pointsPerLoss, pointsPerDraw, id); - this.name = name; - this.type = type; - this.pointsPerWin = pointsPerWin; - this.pointsPerLoss = pointsPerLoss; - this.pointsPerDraw = pointsPerDraw; - } - - public boolean needsTieBreak() { - return calculatePoints().values().stream().sorted().limit(2).distinct().count() < 2; - } - - public void delete() { - delete.update(id); - } - - public static enum EventGroupType { - GROUP_STAGE, - ELIMINATION_STAGE - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/EventGroup.kt b/CommonCore/SQL/src/de/steamwar/sql/EventGroup.kt new file mode 100644 index 00000000..5db01dbf --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/EventGroup.kt @@ -0,0 +1,153 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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.core.eq +import org.jetbrains.exposed.v1.dao.IntEntity +import org.jetbrains.exposed.v1.dao.IntEntityClass +import java.util.* + +object EventGroupTable : IntIdTable("EventGroup", "Id") { + val event = reference("EventID", EventTable) + val name = varchar("Name", 64) + val type = enumeration("Type", EventGroup.EventGroupType::class) + val pointsPerWin = integer("PointsPerWin").default(3) + val pointsPerLoss = integer("PointsPerLoss").default(0) + val pointsPerDraw = integer("PointsPerDraw").default(1) +} + +class EventGroup(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(EventGroupTable) { + + @JvmStatic + fun get(event: Event) = useDb { find { EventGroupTable.event eq event.id }.toList() } + + @JvmStatic + fun byId(groupId: Int) = useDb { Optional.ofNullable(findById(groupId)) } + + @JvmStatic + fun create(event: Event, name: String, type: EventGroupType) = useDb { + new { + this.eventID = event.id.value + this.groupName = name + this.groupType = type + } + } + } + + var eventID by EventGroupTable.event.transform({ EntityID(it, EventTable) }, { it.value }) + private set + private var groupName by EventGroupTable.name + private var groupType by EventGroupTable.type + private var groupPointsPerWin by EventGroupTable.pointsPerWin + private var groupPointsPerLoss by EventGroupTable.pointsPerLoss + private var groupPointsPerDraw by EventGroupTable.pointsPerDraw + val fights by lazy { useDb { EventFight.find { EventFightTable.groupId eq id }.toList() } } + val teamsId by lazy { fights.flatMap { listOf(it.teamBlue, it.teamRed) }.toSet() } + + var name: String + get() = groupName + set(value) { + groupName = value + } + var type: EventGroupType + get() = groupType + set(value) { + groupType = value + } + var pointsPerWin: Int + get() = groupPointsPerWin + set(value) { + groupPointsPerWin = value + } + var pointsPerLoss: Int + get() = groupPointsPerLoss + set(value) { + groupPointsPerLoss = value + } + var pointsPerDraw: Int + get() = groupPointsPerDraw + set(value) { + groupPointsPerDraw = value + } + val dependents by lazy { EventRelation.getGroupRelations(this).toList() } + val lastFight by lazy { Optional.ofNullable(fights.maxByOrNull { it.startTime }) } + + fun getId() = id.value + + private var points: Map? = null + + fun calculatePoints(): Map { + if (points == null) { + val p: MutableMap = teamsId.associateWith { 0 }.toMutableMap() + + for (fight in fights) { + var blueTeamAdd = 0 + var redTeamAdd = 0 + + if (!fight.hasFinished()) { + continue + } + + when (fight.ergebnis) { + 1 -> { + blueTeamAdd += pointsPerWin + redTeamAdd += pointsPerLoss + } + 2 -> { + blueTeamAdd += pointsPerLoss + redTeamAdd += pointsPerWin + } + 0 -> if (fight.fight != null) { + blueTeamAdd += pointsPerDraw + redTeamAdd += pointsPerDraw + } + } + + p[fight.teamBlue] = p[fight.teamBlue]?.plus(blueTeamAdd) ?: blueTeamAdd + p[fight.teamRed] = p[fight.teamRed]?.plus(redTeamAdd) ?: redTeamAdd + } + + return p.mapKeys { Team.byId(it.key) }.also { points = it } + } else { + return points!! + } + } + + fun needsTieBreak() = calculatePoints().values.let { it.size == it.toSet().size } + + fun update(name: String, type: EventGroupType, pointsPerWin: Int, pointsPerLoss: Int, pointsPerDraw: Int) = useDb { + this@EventGroup.name = name + this@EventGroup.type = type + this@EventGroup.pointsPerWin = pointsPerWin + this@EventGroup.pointsPerLoss = pointsPerLoss + this@EventGroup.pointsPerDraw = pointsPerDraw + } + + override fun delete() = useDb { super.delete() } + + enum class EventGroupType { + GROUP_STAGE, + ELIMINATION_STAGE + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/EventRelation.java b/CommonCore/SQL/src/de/steamwar/sql/EventRelation.java deleted file mode 100644 index 539908de..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/EventRelation.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -@AllArgsConstructor -@Getter -@Setter -public class EventRelation { - - static { - SqlTypeMapper.ordinalEnumMapper(FightTeam.class); - SqlTypeMapper.ordinalEnumMapper(FromType.class); - } - - private static final Table table = new Table<>(EventRelation.class); - - private static final SelectStatement get = new SelectStatement<>(table, "SELECT * FROM EventRelation WHERE FromType = ? AND FromId = ?"); - private static final SelectStatement byId = new SelectStatement<>(table, "SELECT * FROM EventRelation WHERE id = ?"); - private static final SelectStatement byEvent = new SelectStatement<>(table, "SELECT ER.* FROM EventRelation ER JOIN EventFight EF ON EF.fightId = ER.fightId WHERE EF.EventID = ?"); - private static final Statement insert = table.insertFields(true, "fightId", "fightTeam", "fromType", "fromId", "fromPlace"); - private static final Statement update = table.update(Table.PRIMARY, "fromType", "fromId", "fromPlace"); - private static final Statement updateTeam = table.update(Table.PRIMARY, "fightTeam"); - private static final Statement delete = table.delete(Table.PRIMARY); - - public static List get(Event event) { - return byEvent.listSelect(event.getEventID()); - } - - public static EventRelation get(int id) { - return byId.select(id); - } - - public static List getFightRelations(EventFight fight) { - return get.listSelect(FromType.FIGHT, fight.getFightID()); - } - - public static List getGroupRelations(EventGroup group) { - return get.listSelect(FromType.GROUP, group.getId()); - } - - public static EventRelation create(EventFight fight, FightTeam fightTeam, FromType fromType, int fromId, int fromPlace) { - int id = insert.insertGetKey(fight.getFightID(), fightTeam, fromType, fromId, fromPlace); - return get(id); - } - - @Field(keys = Table.PRIMARY) - private final int id; - - @Field - private int fightId; - - @Field - private FightTeam fightTeam; - - @Field - private FromType fromType; - - @Field - private int fromId; - - @Field - private int fromPlace; - - public EventFight getFight() { - return EventFight.get(fightId); - } - - public Optional getFromFight() { - if(fromType == FromType.FIGHT) { - return Optional.of(EventFight.get(fromId)); - } else { - return Optional.empty(); - } - } - - public Optional getFromGroup() { - if(fromType == FromType.GROUP) { - return EventGroup.get(fromId); - } else { - return Optional.empty(); - } - } - - public void delete() { - delete.update(id); - } - - public void setUpdateTeam(FightTeam team) { - updateTeam.update(id, team); - this.fightTeam = team; - } - - public void setFromFight(EventFight fight, int place) { - setFrom(fight.getFightID(), place, FromType.FIGHT); - } - - public void setFromGroup(EventGroup group, int place) { - setFrom(group.getId(), place, FromType.GROUP); - } - - private void setFrom(int fightId, int place, FromType type) { - update.update(type, fightId, place, id); - this.fromType = type; - this.fromId = fightId; - this.fromPlace = place; - } - - public Optional getAdvancingTeam() { - if (fromType == FromType.FIGHT) { - if (fromPlace == 0) { - return getFromFight().flatMap(EventFight::getWinner); - } else { - return getFromFight().flatMap(EventFight::getLosser); - } - } else if (fromType == FromType.GROUP) { - return getFromGroup().map(EventGroup::calculatePoints) - .flatMap(points -> points.entrySet().stream() - .sorted(Map.Entry.comparingByValue().reversed()) - .skip(fromPlace) - .findFirst() - .map(Map.Entry::getKey)); - } else { - return Optional.empty(); - } - } - - public boolean apply() { - Optional team = getAdvancingTeam().map(Team::getTeamId); - if(!team.isPresent()) - return false; - - EventFight fight = getFight(); - if(fightTeam == FightTeam.RED) { - fight.update( - fight.getStartTime(), - fight.getSpielmodus(), - fight.getMap(), - fight.getTeamBlue(), - team.get(), - fight.getSpectatePort() - ); - } else { - fight.update( - fight.getStartTime(), - fight.getSpielmodus(), - fight.getMap(), - team.get(), - fight.getTeamRed(), - fight.getSpectatePort() - ); - } - - return true; - } - - public static enum FightTeam { - BLUE, - RED - } - - public static enum FromType { - FIGHT, - GROUP - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/EventRelation.kt b/CommonCore/SQL/src/de/steamwar/sql/EventRelation.kt new file mode 100644 index 00000000..0ca7be22 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/EventRelation.kt @@ -0,0 +1,149 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.and +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.dao.IntEntity +import org.jetbrains.exposed.v1.dao.IntEntityClass +import org.jetbrains.exposed.v1.jdbc.select + +object EventRelationTable : IntIdTable("EventRelation") { + val fightId = reference("FightId", EventFightTable) + val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class) + val fromType = enumeration("FromType", EventRelation.FromType::class) + val fromId = integer("FromId") + val fromPlace = integer("FromPlace") +} + +class EventRelation(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(EventRelationTable) { + @JvmStatic + fun get(event: Event) = useDb { + EventRelationTable.innerJoin(EventFightTable) + .select(EventRelationTable.columns) + .where { EventFightTable.eventId eq event.id } + .map { wrapRow(it) } + } + + @JvmStatic + fun byId(id: Int) = useDb { findById(id) } + + @JvmStatic + fun getFightRelations(fight: EventFight) = + useDb { find { (EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT) } } + + @JvmStatic + fun getGroupRelations(group: EventGroup) = + useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) } } + + @JvmStatic + fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) = useDb { + new { + this.fightEntityId = fight.id + this.fightTeam = fightTeam + this.fromType = fromType + this.fromId = fromId + this.fromPlace = fromPlace + } + } + } + + var fightEntityId by EventRelationTable.fightId + var fightId: Int + get() = fightEntityId.value + set(value) = useDb { fightEntityId = EntityID(value, EventFightTable) } + val fight by lazy { useDb { EventFight[fightEntityId] } } + var fightTeam by EventRelationTable.fightTeam + private set + var fromType by EventRelationTable.fromType + private set + var fromId by EventRelationTable.fromId + private set + + var fromFightId: Int? + get() = if (fromType == FromType.FIGHT) fromId else null + set(value) = useDb { + fromType = FromType.FIGHT + fromId = value!! + } + val fromFight: EventFight? + get() = fromFightId?.let { useDb { EventFight[it] } } + var fromGroupId: Int? + get() = if (fromType == FromType.GROUP) fromId else null + set(value) = useDb { + fromType = FromType.GROUP + fromId = value!! + } + val fromGroup: EventGroup? + get() = fromGroupId?.let { useDb { EventGroup[it] } } + var fromPlace by EventRelationTable.fromPlace + private set + + fun getId() = id.value + fun setUpdateTeam(team: FightTeam) = useDb { + fightTeam = team + } + + fun setFromFight(fight: EventFight, place: Int) = useDb { + fromType = FromType.FIGHT + fromId = fight.id.value + fromPlace = place + } + + fun setFromGroup(group: EventGroup, place: Int) = useDb { + fromType = FromType.GROUP + fromId = group.id.value + fromPlace = place + } + + fun getAdvancingTeam(): Team? = useDb { when(fromType) { + FromType.FIGHT -> if (fromPlace == 0) fromFight?.winner else fromFight?.losser + FromType.GROUP -> fromGroup?.calculatePoints()?.toList()?.sortedBy { (_, v) -> v }?.reversed()?.elementAt(fromPlace)?.first + } } + + fun apply(): Boolean { + val team = getAdvancingTeam() ?: return false + + useDb { + when(fightTeam) { + FightTeam.BLUE -> fight.teamBlue = team.teamId + FightTeam.RED -> fight.teamRed = team.teamId + } + } + + return true + } + + override fun delete() = useDb { + super.delete() + } + + enum class FightTeam { + BLUE, RED + } + + enum class FromType { + FIGHT, GROUP + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/Fight.java b/CommonCore/SQL/src/de/steamwar/sql/Fight.java deleted file mode 100644 index b1f122b2..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/Fight.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 . - */ - -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 java.sql.Timestamp; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -@AllArgsConstructor -public class Fight { - - private static final Table table = new Table<>(Fight.class); - private static final SelectStatement getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?"); - private static final SelectStatement getById = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId WHERE FightId = ?"); - private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition"); - private static final Statement updateReplayAvailable = table.update(Table.PRIMARY, "ReplayAvailable"); - - public static List getPage(int page, int elementsPerPage) { - List fights = getPage.listSelect(page * elementsPerPage, elementsPerPage); - - List fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID)); - for(Fight fight : fights) { - fight.initPlayers(fightPlayers); - } - - SteamwarUser.batchCache(fightPlayers.stream().map(FightPlayer::getUserID).collect(Collectors.toSet())); - return fights; - } - - public static Fight getById(int fightID) { - return getById.select(fightID); - } - - public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){ - return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition); - } - - public static void markReplayAvailable(int fightID) { - updateReplayAvailable.update(true, fightID); - } - - @Getter - @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int fightID; - @Field - private final String gameMode; - @Getter - @Field - private final String server; - @Getter - @Field - private final Timestamp startTime; - @Field - private final int duration; - @Field - private final int blueLeader; - @Field - private final int redLeader; - @Field(nullable = true) - private final Integer blueSchem; - @Field(nullable = true) - private final Integer redSchem; - @Getter - @Field - private final int win; - @Field - private final String wincondition; - @Field - private final boolean replayAvailable; - @Field // Virtual field for easy select - private final boolean replayAllowed; - - @Getter - private final List bluePlayers = new ArrayList<>(); - @Getter - private final List redPlayers = new ArrayList<>(); - - public SchematicType getSchemType() { - return SchematicType.fromDB(gameMode); - } - - public SteamwarUser getBlueLeader() { - return SteamwarUser.get(blueLeader); - } - - public SteamwarUser getRedLeader() { - return SteamwarUser.get(redLeader); - } - - public boolean replayAllowed() { - return replayExists() && replayAllowed; - } - - public boolean replayExists() { - return getSchemType() != null && replayAvailable; - } - - private void initPlayers(List fightPlayers) { - for(FightPlayer fp : fightPlayers) { - if(fp.getFightID() != fightID) - continue; - - if(fp.getTeam() == 1) - bluePlayers.add(fp); - else - redPlayers.add(fp); - } - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Fight.kt b/CommonCore/SQL/src/de/steamwar/sql/Fight.kt new file mode 100644 index 00000000..d64d7583 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/Fight.kt @@ -0,0 +1,162 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.IntegerColumnType +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.SortOrder +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.dao.IntEntity +import org.jetbrains.exposed.v1.dao.IntEntityClass +import org.jetbrains.exposed.v1.javatime.timestamp +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.update +import java.sql.Timestamp + +object FightTable : IntIdTable("Fight", "FightId") { + val gamemode = varchar("Gamemode", 30) + val server = text("Server") + val startTime = timestamp("StartTime") + val duration = integer("Duration") + val blueLeader = reference("BlueLeader", SteamwarUserTable) + val redLeader = reference("RedLeader", SteamwarUserTable) + val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL) + val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL) + val win = enumeration("Win", Fight.WinningTeam::class) + val winCondition = varchar("WinCondition", 100) + val replayAvailable = bool("ReplayAvailable") +} + +class Fight(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(FightTable) { + @JvmStatic + fun getById(id: Int) = useDb { get(id) } + + @JvmStatic + fun create( + gamemode: String, + server: String, + starttime: Timestamp, + duration: Int, + blueleader: Int, + redleader: Int, + blueschem: Int?, + redschem: Int?, + win: Int, + wincondition: String + ): Int = useDb { + FightTable.insertAndGetId { + it[FightTable.gamemode] = gamemode + it[FightTable.server] = server + it[FightTable.startTime] = starttime.toInstant() + it[FightTable.duration] = duration + it[FightTable.blueLeader] = EntityID(blueleader, SteamwarUserTable) + it[FightTable.redLeader] = EntityID(redleader, SteamwarUserTable) + it[FightTable.blueSchem] = blueschem?.let { EntityID(it, SchematicNodeTable) } + it[FightTable.redSchem] = redschem?.let { EntityID(it, SchematicNodeTable) } + it[FightTable.win] = WinningTeam.entries[win] + it[FightTable.winCondition] = wincondition + }.value + } + + @JvmStatic + fun getPage(page: Int, pageSize: Int): List = useDb { + val fights = all().orderBy(FightTable.startTime to SortOrder.DESC).limit(pageSize).offset((pageSize * page).toLong()) + + val fightPlayer = FightPlayer.batchGet(fights.map { it.id.value }) + for (fight in fights) { + fight.initPlayers(fightPlayer) + } + + SteamwarUser.batchCache(fightPlayer.map { it.userID }.toMutableSet()) + fights.toList() + } + + @JvmStatic + fun markReplayAvailable(id: Int) = useDb { + FightTable.update({ FightTable.id eq id }) { + it[replayAvailable] = true + } + } + } + + val fightID by FightTable.id.transform({ EntityID(it, FightTable) }, { it.value }) + val gameMode by FightTable.gamemode + val server by FightTable.server + val startTime by FightTable.startTime.transform({ it.toInstant() }, { Timestamp.from(it) }) + val duration by FightTable.duration + val blueLeaderId by FightTable.blueLeader + val blueLeader by lazy { useDb { SteamwarUser[blueLeaderId] } } + val redLeaderId by FightTable.redLeader + val redLeader by lazy { useDb { SteamwarUser[redLeaderId] } } + val blueSchem by FightTable.blueSchem + val redSchem by FightTable.redSchem + val winner by FightTable.win + val win: Int + get() = winner.ordinal + val winCondition by FightTable.winCondition + val replayAvailable by FightTable.replayAvailable + + val schemType: SchematicType? + get() = SchematicType.fromDB(gameMode) + + val replayAllowed by lazy { + replayExists() && useDb { + exec( + "SELECT (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId WHERE FightId = ?", + args = listOf(IntegerColumnType() to this@Fight.id.value) + ) { + if (it.next()) { + it.getBoolean("ReplayAllowed") + } else { + false + } + } ?: false + } + } + + fun replayExists() = schemType != null && replayAvailable + fun replayAllowed() = replayAllowed + + lateinit var bluePlayers: List + lateinit var redPlayers: List + + private fun initPlayers(fightPlayers: List) { + val blue = mutableListOf() + val red = mutableListOf() + + for (player in fightPlayers.filter { it.fightID == id.value }) { + if (player.team == 1) + blue.add(player) + else + red.add(player) + } + + bluePlayers = blue + redPlayers = red + } + + enum class WinningTeam { + NONE, BLUE, RED; + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/FightPlayer.java b/CommonCore/SQL/src/de/steamwar/sql/FightPlayer.java deleted file mode 100644 index c45ec806..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/FightPlayer.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 . - */ - -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 java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@AllArgsConstructor -public class FightPlayer { - - private static final Table table = new Table<>(FightPlayer.class); - private static final Statement create = table.insertAll(); - private static final SelectStatement batchGet = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN ?"); - - @Getter - @Field(keys = {Table.PRIMARY}) - private final int fightID; - @Getter - @Field(keys = {Table.PRIMARY}) - private final int userID; - @Getter - @Field - private final int team; - @Field - private final String kit; - @Field - private final int kills; - @Field - private final boolean isOut; - - public static void create(int fightID, int userID, boolean blue, String kit, int kills, boolean isOut) { - create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut); - } - - public static List batchGet(Stream fightIds) { - try (SelectStatement batch = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN (" + fightIds.map(Object::toString).collect(Collectors.joining(", ")) + ")")) { - return batch.listSelect(); - } - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/FightPlayer.kt b/CommonCore/SQL/src/de/steamwar/sql/FightPlayer.kt new file mode 100644 index 00000000..d0238c96 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/FightPlayer.kt @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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.inList +import org.jetbrains.exposed.v1.dao.CompositeEntity +import org.jetbrains.exposed.v1.dao.CompositeEntityClass +import org.jetbrains.exposed.v1.jdbc.insertIgnore + +object FightPlayerTable : CompositeIdTable("FightPlayer") { + val fightId = reference("FightId", FightTable) + val userId = reference("UserId", SteamwarUserTable) + val team = integer("Team") + val kit = varchar("Kit", 64) + val kills = integer("Kills") + val out = bool("IsOut") + + init { + addIdColumn(fightId) + addIdColumn(userId) + } +} + +class FightPlayer(id: EntityID) : CompositeEntity(id) { + companion object : CompositeEntityClass(FightPlayerTable) { + @JvmStatic + fun create( + fightId: Int, + userId: Int, + blue: Boolean, + kit: String, + kills: Int, + out: Boolean + ) = useDb { + FightPlayerTable.insertIgnore { + it[this.fightId] = fightId + it[this.userId] = userId + it[this.team] = if (blue) 1 else 2 + it[this.kit] = kit + it[this.kills] = kills + it[this.out] = out + } + } + + @JvmStatic + fun batchGet(fightIds: List) = useDb { + find { FightPlayerTable.fightId inList fightIds.toList() }.toList() + } + } + + val fightID by FightPlayerTable.fightId.transform({ EntityID(it, FightTable) }, { it.value }) + val userID by FightPlayerTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + val team by FightPlayerTable.team + val kit by FightPlayerTable.kit + val kills by FightPlayerTable.kills + val out by FightPlayerTable.out + + fun isOut() = out +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java b/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java index c7e9ecf8..2a737508 100644 --- a/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java +++ b/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java @@ -35,11 +35,19 @@ import java.util.stream.Collectors; public final class GameModeConfig { public static final Function ToString = Function.identity(); - public static final Function ToStaticWarGear = __ -> "WarGear"; - public static final Function ToInternalName = file -> file != null ? file.getName().replace(".yml", "") : "WarGear"; + public static final Function ToStaticWarGear = GameModeConfig::constWarGear; + public static final Function 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 != null ? f.getName().replace(".yml", "") : constWarGear(null); + } + private static final Map> byFileName; private static final Map> byGameName; private static final Map> bySchematicType; @@ -63,11 +71,15 @@ public final class GameModeConfig { } private static final Field Schematic_TypeField; + private static final Field Schematic_SubTypesField; static { try { Schematic_TypeField = SchematicConfig.class.getDeclaredField("Type"); Schematic_TypeField.setAccessible(true); + + Schematic_SubTypesField = SchematicConfig.class.getDeclaredField("SubTypes"); + Schematic_SubTypesField.setAccessible(true); } catch (NoSuchFieldException e) { throw new SecurityException(e.getMessage(), e); } @@ -77,6 +89,18 @@ public final class GameModeConfig { bySchematicType = new HashMap<>(); SchematicType.values(); DEFAULTS = SQLWrapper.impl.loadGameModeConfig(null); + + byFileName.values().forEach(gameModeConfig -> { + List subTypes = Collections.unmodifiableList(gameModeConfig.Schematic.SubTypesStrings.stream() + .map(SchematicType::fromDB) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + try { + Schematic_SubTypesField.set(gameModeConfig.Schematic, subTypes); + } catch (IllegalAccessException e) { + throw new SecurityException(e.getMessage(), e); + } + }); } public final boolean loaded; @@ -516,6 +540,8 @@ public final class GameModeConfig { */ public final SchematicType Type; + private final List SubTypesStrings; + /** * The schematic types that are also allowed to be chosen in this arena */ @@ -605,6 +631,13 @@ public final class GameModeConfig { */ public final int MaxDispenserItems; + /** + * Maximal blast resistance for the blocks + * + * @implSpec {@code Double.MAX_VALUE} by default + */ + public final double MaxBlastResistance; + /** * Maximal blast resistance for the design blocks * @@ -623,6 +656,7 @@ public final class GameModeConfig { Size = new SizeConfig(loader.with("Size")); Inset = new InsetConfig(loader.with("Inset")); Type = loader.getSchematicType("Type", "Normal"); + SubTypesStrings = loader.getStringList("SubTypes"); SubTypes = loader.getSchematicTypeList("SubTypes"); Shortcut = loader.getString("Shortcut", ""); Material = loader.getMaterial("Material", "STONE_BUTTON"); @@ -636,6 +670,7 @@ public final class GameModeConfig { UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false); MaxBlocks = loader.getInt("MaxBlocks", 0); MaxDispenserItems = loader.getInt("MaxDispenserItems", 128); + MaxBlastResistance = loader.getDouble("MaxBlastResistance", Double.MAX_VALUE); MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", Double.MAX_VALUE); Map, Integer> Limited = new HashMap<>(); @@ -650,6 +685,10 @@ public final class GameModeConfig { Limited.put(Collections.unmodifiableSet(materials.stream().map(String::toUpperCase).map(loader.materialMapper).collect(Collectors.toSet())), amount); } } + SQLWrapper.impl.getMaterialWithGreaterBlastResistance(MaxBlastResistance).forEach(material -> { + if (Limited.entrySet().stream().anyMatch(entry -> entry.getKey().contains(material))) return; + Limited.put(Collections.singleton((M) material), 0); + }); this.Limited = Collections.unmodifiableMap(Limited); } diff --git a/CommonCore/SQL/src/de/steamwar/sql/IgnoreSystem.java b/CommonCore/SQL/src/de/steamwar/sql/IgnoreSystem.java deleted file mode 100644 index c0c38cc8..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/IgnoreSystem.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 . - */ - -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.sql.ResultSet; -import java.util.UUID; - -@AllArgsConstructor -public class IgnoreSystem { - - private static final Table table = new Table<>(IgnoreSystem.class, "IgnoredPlayers"); - 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 ignorer; - @Field(keys = {Table.PRIMARY}) - private final int ignored; - - public static boolean isIgnored(UUID ignorer, UUID ignored){ - return isIgnored(SteamwarUser.get(ignorer), SteamwarUser.get(ignored)); - } - - public static boolean isIgnored(SteamwarUser ignorer, SteamwarUser ignored) { - return select.select(ResultSet::next, ignorer.getId(), ignored.getId()); - } - - public static void ignore(SteamwarUser ignorer, SteamwarUser ignored) { - insert.update(ignorer.getId(), ignored.getId()); - } - - public static void unIgnore(SteamwarUser ignorer, SteamwarUser ignored) { - delete.update(ignorer.getId(), ignored.getId()); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/IgnoreSystem.kt b/CommonCore/SQL/src/de/steamwar/sql/IgnoreSystem.kt new file mode 100644 index 00000000..9df3a8cf --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/IgnoreSystem.kt @@ -0,0 +1,74 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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 java.util.* + +object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") { + val ignorer = reference("Ignorer", SteamwarUserTable) + val ignored = reference("Ignored", SteamwarUserTable) + + override val primaryKey = PrimaryKey(ignorer, ignored) + + init { + addIdColumn(ignorer) + addIdColumn(ignored) + } +} + +class IgnoreSystem(id: EntityID) : CompositeEntity(id) { + var ignorer by IgnoreSystemTable.ignorer + var ignored by IgnoreSystemTable.ignored + + companion object : CompositeEntityClass(IgnoreSystemTable) { + @JvmStatic + fun isIgnored(ignorer: UUID, ignored: UUID) = useDb { isIgnored(SteamwarUser.get(ignorer)!!, SteamwarUser.get(ignored)!!) } + + @JvmStatic + fun isIgnored(ignorer: SteamwarUser, ignored: SteamwarUser) = useDb { + find { + (IgnoreSystemTable.ignorer eq ignorer.id) and (IgnoreSystemTable.ignored eq ignored.id) + }.firstOrNull() != null + } + + @JvmStatic + fun ignore(ignorer: SteamwarUser, ignored: SteamwarUser) = useDb { + new { + this.ignorer = ignorer.id + this.ignored = ignored.id + } + } + + @JvmStatic + fun unIgnore(ignorer: SteamwarUser, ignored: SteamwarUser) = useDb { + find { + (IgnoreSystemTable.ignorer eq ignorer.id) and (IgnoreSystemTable.ignored eq ignored.id) + }.firstOrNull()?.delete() + } + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/Leaderboard.kt b/CommonCore/SQL/src/de/steamwar/sql/Leaderboard.kt new file mode 100644 index 00000000..8a6e3e47 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/Leaderboard.kt @@ -0,0 +1,94 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.count +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.core.lessSubQuery +import org.jetbrains.exposed.v1.dao.CompositeEntity +import org.jetbrains.exposed.v1.dao.CompositeEntityClass +import org.jetbrains.exposed.v1.dao.flushCache +import org.jetbrains.exposed.v1.javatime.CurrentTimestamp +import org.jetbrains.exposed.v1.javatime.timestamp +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.upsert + +object LeaderboardTable : CompositeIdTable("Leaderboard") { + val userId = reference("UserId", SteamwarUserTable) + val name = varchar("LeaderboardName", 64).entityId() + val time = long("Time") + val updatedAt = timestamp("UpdatedAt").defaultExpression(CurrentTimestamp) + val bestTime = bool("BestTime") + + override val primaryKey = PrimaryKey(userId, name) + + init { + addIdColumn(userId) + } +} + +class Leaderboard(id: EntityID) : CompositeEntity(id) { + companion object : CompositeEntityClass(LeaderboardTable) { + @JvmStatic + fun getLeaderboard(name: String) = useDb { + find { LeaderboardTable.name eq name }.orderBy(LeaderboardTable.time to SortOrder.ASC).limit(5).toList() + } + + @JvmStatic + fun getPlayerTime(user: SteamwarUser, name: String) = useDb { + find { (LeaderboardTable.userId eq user.id.value) and (LeaderboardTable.name eq name) }.firstOrNull() + } + + @JvmStatic + fun getPlayerPlacement(user: SteamwarUser, name: String) = useDb { + LeaderboardTable.select(LeaderboardTable.time.count()) + .where { + (LeaderboardTable.name eq name) and (LeaderboardTable.time lessSubQuery LeaderboardTable.select( + LeaderboardTable.time + ).where { (LeaderboardTable.userId eq user.id.value) and (LeaderboardTable.name eq name) }) + } + .firstOrNull()?.get(LeaderboardTable.time.count())?.toInt() ?: Int.MAX_VALUE + } + + @JvmStatic + fun upsert(userId: Int, name: String, time: Long, bestTime: Boolean) = useDb { + LeaderboardTable.upsert( + onUpdateExclude = listOf(LeaderboardTable.updatedAt, LeaderboardTable.userId, LeaderboardTable.name) + ) { + it[LeaderboardTable.userId] = userId + it[LeaderboardTable.name] = name + it[LeaderboardTable.time] = time + it[LeaderboardTable.bestTime] = bestTime + } + } + } + + val user by LeaderboardTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + val name by LeaderboardTable.name + var time by LeaderboardTable.time + var updatedAt by LeaderboardTable.updatedAt + var bestTime by LeaderboardTable.bestTime +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/Mod.java b/CommonCore/SQL/src/de/steamwar/sql/Mod.java deleted file mode 100644 index 4d8fcd9d..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/Mod.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.*; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.List; - -@AllArgsConstructor -public class Mod { - - static { - SqlTypeMapper.ordinalEnumMapper(Platform.class); - SqlTypeMapper.ordinalEnumMapper(ModType.class); - } - - private static final Table table = new Table<>(Mod.class, "Mods"); - private static final SelectStatement get = table.select(Table.PRIMARY); - private static final SelectStatement findFirst = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = 0 LIMIT 1"); - private static final SelectStatement getPageOfType = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = ? ORDER BY ModName DESC LIMIT ?, ?"); - private static final Statement insert = table.insert(Table.PRIMARY); - private static final Statement set = table.update(Table.PRIMARY, "ModType"); - - public static Mod get(String name, Platform platform) { - return get.select(platform, name); - } - - public static Mod getOrCreate(String name, Platform platform) { - Mod mod = get(name, platform); - if(mod != null) - return mod; - - insert.update(platform, name); - return new Mod(platform, name, ModType.UNKLASSIFIED); - } - - public static List getAllModsFiltered(int page, int elementsPerPage, ModType filter) { - return Mod.getPageOfType.listSelect(filter, page * elementsPerPage, elementsPerPage); - } - - public static Mod findFirstMod() { - return findFirst.select(); - } - - @Getter - @Field(keys = {Table.PRIMARY}) - private final Platform platform; - @Getter - @Field(keys = {Table.PRIMARY}) - private final String modName; - @Getter - @Field(def = "0") - private ModType modType; - - public void setModType(ModType modType) { - set.update(modType, platform, modName); - this.modType = modType; - } - - public enum Platform { - FORGE, - LABYMOD, - FABRIC - } - - public enum ModType { - UNKLASSIFIED("7"), - GREEN("a"), - YELLOW("e"), - RED("c"), - YOUTUBER_ONLY("6"); - - ModType(String colorcode) { - this.colorcode = colorcode; - } - private final String colorcode; - - public String getColorCode() { - return colorcode; - } - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Mod.kt b/CommonCore/SQL/src/de/steamwar/sql/Mod.kt new file mode 100644 index 00000000..9e3b4d50 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/Mod.kt @@ -0,0 +1,88 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.SortOrder +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 + +object ModTable : CompositeIdTable("Mods") { + val platform = enumeration("Platform", Mod.Platform::class) + val modName = varchar("ModName", 100) + val modeType = enumerationByName("ModType", 10, Mod.ModType::class) +} + +class Mod(id: EntityID) : CompositeEntity(id) { + companion object : CompositeEntityClass(ModTable) { + @JvmStatic + fun get(modName: String, platform: Platform) = useDb { + find { ModTable.platform eq platform and (ModTable.modName eq modName) }.firstOrNull() + } + + @JvmStatic + fun getOrCreate(modName: String, platform: Platform) = useDb { + get(modName, platform) ?: new { + this.platform = platform + this.modName = modName + this.type = ModType.UNKLASSIFIED + } + } + + @JvmStatic + fun getAllModsFiltered(page: Int, elementsPerPage: Int, filter: ModType) = useDb { + find { ModTable.modeType eq filter }.limit(elementsPerPage).offset((elementsPerPage * page).toLong()) + .orderBy( + ModTable.modName to SortOrder.DESC + ).toList() + } + + @JvmStatic + fun findFirstMod() = useDb { + find { ModTable.modeType eq ModType.UNKLASSIFIED }.limit(1).firstOrNull() + } + } + + var platform by ModTable.platform + var modName by ModTable.modName + private var type by ModTable.modeType + var modType: ModType + get() = type + set(value) = useDb { type = value } + + enum class Platform { + FORGE, + LABYMOD, + FABRIC + } + + enum class ModType(val colorCode: String) { + UNKLASSIFIED("7"), + GREEN("a"), + YELLOW("e"), + RED("c"), + YOUTUBER_ONLY("6"); + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/NodeData.java b/CommonCore/SQL/src/de/steamwar/sql/NodeData.java deleted file mode 100644 index be0bbc95..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/NodeData.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.*; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import javax.swing.plaf.nimbus.State; -import java.io.*; -import java.sql.PreparedStatement; -import java.sql.Timestamp; -import java.time.Instant; -import java.util.List; -import java.util.Optional; -import java.util.zip.GZIPInputStream; - -@AllArgsConstructor -@Getter -public class NodeData { - static { - new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); - new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); - new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); - - SqlTypeMapper.ordinalEnumMapper(SchematicFormat.class); - } - - private static final Table table = new Table<>(NodeData.class); - - private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?)", true); - private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ? AND CreatedAt = ?"); - private static final Statement delete = table.delete(Table.PRIMARY); - - private static final SelectStatement get = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt "); - private static final Statement getRevisions = new Statement("SELECT COUNT(DISTINCT CreatedAt) as CNT FROM NodeData WHERE NodeId = ?"); - private static final SelectStatement getLatest = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt DESC LIMIT 1"); - - public static NodeData getLatest(SchematicNode node) { - if (node.isDir()) throw new IllegalArgumentException("Node is dir"); - return Optional.ofNullable(getLatest.select(node)).orElseGet(() -> new NodeData(node.getId(), Timestamp.from(Instant.now()), SchematicFormat.MCEDIT)); - } - - public static List get(SchematicNode node) { - return get.listSelect(node); - } - - public static NodeData get(SchematicNode node, int revision) { - return get.listSelect(node).get(revision - 1); - } - - public static int getRevisions(SchematicNode node) { - return getRevisions.select(rs -> { - if (rs.next()) { - return rs.getInt("CNT"); - } else { - return 0; - } - }, node); - } - - public static void saveFromStream(SchematicNode node, InputStream blob, SchematicFormat format) { - updateDatabase.update(node.getId(), format, blob); - } - - @Field(keys = {Table.PRIMARY}) - private final int nodeId; - - @Field(keys = {Table.PRIMARY}) - private Timestamp createdAt; - - @Field - private SchematicFormat nodeFormat; - - public InputStream schemData() throws IOException { - return schemData(true); - } - - public InputStream schemData(boolean decompress) throws IOException { - try { - return selSchemData.select(rs -> { - rs.next(); - InputStream schemData = rs.getBinaryStream("SchemData"); - try { - if(rs.wasNull() || schemData.available() == 0) { - throw new SecurityException("SchemData is null"); - } - if (decompress) { - return new GZIPInputStream(schemData); - } else { - return schemData; - } - } catch (IOException e) { - throw new SecurityException("SchemData is wrong", e); - } - }, nodeId, createdAt); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Deprecated - public void saveFromStream(InputStream blob, SchematicFormat newFormat) { - saveFromStream(SchematicNode.getSchematicNode(nodeId), blob, newFormat); - } - - public void delete() { - delete.update(nodeId, createdAt); - } - - @AllArgsConstructor - @Getter - public enum SchematicFormat { - MCEDIT(".schematic"), - SPONGE_V2(".schem"), - SPONGE_V3(".schem"); - - private final String fileEnding; - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/NodeData.kt b/CommonCore/SQL/src/de/steamwar/sql/NodeData.kt new file mode 100644 index 00000000..69552ac0 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/NodeData.kt @@ -0,0 +1,100 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.SortOrder +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.core.statements.api.ExposedBlob +import org.jetbrains.exposed.v1.dao.CompositeEntity +import org.jetbrains.exposed.v1.dao.CompositeEntityClass +import org.jetbrains.exposed.v1.javatime.CurrentTimestamp +import org.jetbrains.exposed.v1.javatime.timestamp +import org.jetbrains.exposed.v1.jdbc.insert +import java.io.InputStream +import java.util.zip.GZIPInputStream + +object NodeDataTable: CompositeIdTable("NodeData") { + val nodeId = reference("NodeId", SchematicNodeTable) + val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId() + val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class) + val schemData = blob("SchemData") + + override val primaryKey = PrimaryKey(nodeId, createdAt) + + init { + addIdColumn(nodeId) + } +} + +class NodeData(id: EntityID): CompositeEntity(id) { + val nodeId by NodeDataTable.nodeId + val createdAt by NodeDataTable.createdAt + val nodeFormat by NodeDataTable.nodeFormat + val schemData by NodeDataTable.schemData + + companion object: CompositeEntityClass(NodeDataTable) { + @JvmStatic + fun getLatest(node: SchematicNode) = useDb { + find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.DESC).firstOrNull() + } + + @JvmStatic + fun get(node: SchematicNode) = useDb { + find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList() + } + + @JvmStatic + fun get(node: SchematicNode, revision: Int) = useDb { + find { NodeDataTable.nodeId eq node.nodeId }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList().get(revision - 1) + } + + @JvmStatic + fun getRevisions(node: SchematicNode) = useDb { + count(NodeDataTable.nodeId eq node.nodeId).toInt() + } + + @JvmStatic + fun saveFromStream(node: SchematicNode, blob: InputStream, format: SchematicFormat) = useDb { + NodeDataTable.insert { + it[NodeDataTable.nodeId] = EntityID(node.getId(), SchematicNodeTable) + it[NodeDataTable.nodeFormat] = format + it[NodeDataTable.schemData] = ExposedBlob(blob.readBytes()) + } + } + } + + fun schemData(decompress: Boolean) = useDb { + schemData.inputStream.let { if(decompress) GZIPInputStream(it) else it } + } + + fun schemData() = schemData(true) + + override fun delete() = useDb { super.delete() } + + enum class SchematicFormat(val fileEnding: String) { + MCEDIT(".schematic"), + SPONGE_V2(".schem"), + SPONGE_V3(".schem"); + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/NodeDownload.java b/CommonCore/SQL/src/de/steamwar/sql/NodeDownload.java deleted file mode 100644 index 6580b589..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/NodeDownload.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 . - */ - -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 java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.sql.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://api.steamwar.de/download/"; - - private static final Table table = new Table<>(NodeDownload.class); - private static final Statement insert = table.insertFields("NodeId", "Link"); - - private static final SelectStatement select = table.selectFields("link"); - - private static final Statement delete = table.delete(Table.PRIMARY); - - public static NodeDownload get(String link) { - return select.select(link); - } - - @Getter - @Field(keys = {Table.PRIMARY}) - private final int nodeId; - @Field - private final String link; - @Field(def = "CURRENT_TIMESTAMP") - @Getter - 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) & 0xF]).append(HEX[b & 0xF]); - return hexBuffer.toString(); - } - - public void delete() { - delete.update(nodeId); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/NodeDownload.kt b/CommonCore/SQL/src/de/steamwar/sql/NodeDownload.kt new file mode 100644 index 00000000..62de6038 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/NodeDownload.kt @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.dao.id.IdTable +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.dao.IntEntity +import org.jetbrains.exposed.v1.dao.IntEntityClass +import org.jetbrains.exposed.v1.javatime.CurrentTimestamp +import org.jetbrains.exposed.v1.javatime.timestamp +import java.security.MessageDigest +import java.sql.Timestamp +import java.time.Instant + +object NodeDownloadTable: IdTable("NodeDownload") { + override val id = reference("NodeId", SchematicNodeTable).uniqueIndex() + val link = varchar("Link", 255) + val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp) +} + +class NodeDownload(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(NodeDownloadTable) { + const val LINK_BASE = "https://api.steamwar.de/download/" + + @JvmStatic + fun getLink(schem: SchematicNode): String { + if (schem.isDir()) + throw IllegalArgumentException("Cannot get link for directory") + + val digest = MessageDigest.getInstance("SHA-1") + digest.update("${Instant.now()}${schem.owner}${schem.nodeId}".toByteArray()) + val hash = digest.digest().joinToString("") { "%02x".format(it) } + useDb { + findByIdAndUpdate(schem.id.value) { + it.link = hash + } ?: new { + nodeId = schem.id.value + link = hash + } + } + return "${LINK_BASE}${hash}" + } + + @JvmStatic + fun get(link: String) = useDb { + find { NodeDownloadTable.link eq link }.firstOrNull() + } + } + + var nodeId by NodeDownloadTable.id.transform({ EntityID(it, SchematicNodeTable) }, { it.value }) + var link by NodeDownloadTable.link + var timestamp by NodeDownloadTable.timestamp.transform({ it.toInstant() }, { Timestamp.from(it) }) + + override fun delete() = useDb { + super.delete() + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/NodeMember.java b/CommonCore/SQL/src/de/steamwar/sql/NodeMember.java deleted file mode 100644 index 8b40b8be..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/NodeMember.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 . - */ - -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.HashSet; -import java.util.Optional; -import java.util.Set; - -@AllArgsConstructor -public class NodeMember { - - public static void init() { - // enforce class initialization - } - - private static final Table table = new Table<>(NodeMember.class); - private static final SelectStatement getNodeMember = table.select(Table.PRIMARY); - private static final SelectStatement getNodeMembers = table.selectFields("NodeId"); - private static final SelectStatement getSchematics = table.selectFields("UserId"); - private static final Statement create = table.insert(Table.PRIMARY); - private static final Statement delete = table.delete(Table.PRIMARY); - private static final Statement updateParent = table.update(Table.PRIMARY, "ParentId"); - - @Field(keys = {Table.PRIMARY}) - private final int nodeId; - @Field(keys = {Table.PRIMARY}) - private final int userId; - @Field(nullable = true, def = "null") - private Integer parentId; - - public int getNode() { - return nodeId; - } - - public int getMember() { - return userId; - } - - public Optional getParent() { - return Optional.ofNullable(parentId); - } - - public void delete() { - delete.update(nodeId, userId); - } - - public static NodeMember createNodeMember(int node, int member) { - create.update(node, member); - return new NodeMember(node, member, null); - } - - public static NodeMember getNodeMember(int node, SteamwarUser member) { - return getNodeMember(node, member.getId()); - } - - public static NodeMember getNodeMember(int node, int member) { - return getNodeMember.select(node, member); - } - - public static Set getNodeMembers(int node) { - return new HashSet<>(getNodeMembers.listSelect(node)); - } - - public static Set getSchematics(int member) { - return new HashSet<>(getSchematics.listSelect(member)); - } - - public void setParentId(Integer parentId) { - this.parentId = parentId; - updateParent.update(this.parentId, nodeId, userId); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/NodeMember.kt b/CommonCore/SQL/src/de/steamwar/sql/NodeMember.kt new file mode 100644 index 00000000..cf6d951e --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/NodeMember.kt @@ -0,0 +1,104 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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.* +import kotlin.jvm.optionals.getOrNull + +object NodeMemberTable : CompositeIdTable("NodeMember") { + val node = reference("NodeId", SchematicNodeTable) + val userId = reference("UserId", SteamwarUserTable) + val parentNode = optReference("ParentId", SchematicNodeTable) + + override val primaryKey = PrimaryKey(node, userId) + + init { + addIdColumn(node) + addIdColumn(userId) + } +} + +class NodeMember(id: EntityID) : CompositeEntity(id) { + companion object : CompositeEntityClass(NodeMemberTable) { + @JvmStatic + fun createNodeMember(node: Int, member: Int): NodeMember = useDb { + NodeMemberTable.insertIgnore { + it[this.node] = EntityID(node, SchematicNodeTable) + it[this.userId] = EntityID(member, SteamwarUserTable) + } + getNodeMember(node, member) ?: throw IllegalStateException("NodeMember not created") + } + + @JvmStatic + fun createNodeMember(node: Int, member: Int, parent: SchematicNode): NodeMember = useDb { + if (!parent.isDir()) throw IllegalStateException("Parent must be a directory") + NodeMemberTable.insertIgnore { + it[this.node] = EntityID(node, SchematicNodeTable) + it[this.userId] = EntityID(member, SteamwarUserTable) + it[NodeMemberTable.parentNode] = parent.getId() + } + getNodeMember(node, member) ?: throw IllegalStateException("NodeMember not created") + } + + @JvmStatic + fun createNodeMember(node: Int, member: SteamwarUser) = createNodeMember(node, member.id.value) + + @JvmStatic + fun getNodeMember(node: Int, member: Int) = useDb { + find { (NodeMemberTable.node eq node) and (NodeMemberTable.userId eq member) }.firstOrNull() + } + + @JvmStatic + fun getNodeMember(node: Int, member: SteamwarUser) = getNodeMember(node, member.id.value) + + @JvmStatic + fun getNodeMembers(node: Int) = useDb { find { NodeMemberTable.node eq node }.toSet() } + + @JvmStatic + fun getSchematics(member: Int) = useDb { find { NodeMemberTable.userId eq member }.toSet() } + + @JvmStatic + fun init() = Unit + } + + val node by NodeMemberTable.node.transform({ EntityID(it, SchematicNodeTable) }, { it.value }) + val member by NodeMemberTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + var parent by NodeMemberTable.parentNode.transform( + { it.map { EntityID(it, SchematicNodeTable) }.getOrNull() }, + { Optional.ofNullable(it?.value) }) + private set + + fun setParentId(id: Int?) { + parent = Optional.ofNullable(id) + } + + override fun delete() = useDb { + super.delete() + } +} diff --git a/CommonCore/SQL/src/de/steamwar/sql/PersonalKit.kt b/CommonCore/SQL/src/de/steamwar/sql/PersonalKit.kt new file mode 100644 index 00000000..cd774d3c --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/PersonalKit.kt @@ -0,0 +1,117 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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.insert + +object PersonalKitTable: CompositeIdTable("PersonalKit") { + val userId = reference("UserId", SteamwarUserTable) + val gamemode = varchar("Gamemode", 64).entityId() + val kitName = varchar("Name", 64).entityId() + val inventory = text("Inventory") + val armor = text("Armor") + val inUse = bool("InUse") + + override val primaryKey = PrimaryKey(userId, gamemode, kitName) + + init { + addIdColumn(userId) + } +} + +class InternalKit(id: EntityID): CompositeEntity(id) { + companion object: CompositeEntityClass(PersonalKitTable) { + @JvmStatic + fun get(userId: Int, gamemode: String) = useDb { + find { PersonalKitTable.userId eq userId and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.inUse eq true) } + .toList() + } + + @JvmStatic + fun get(userId: Int, gamemode: String, kitName: String) = useDb { + find { PersonalKitTable.userId eq userId and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.kitName eq kitName) and (PersonalKitTable.inUse eq true) } + .firstOrNull() + } + + @JvmStatic + fun create(userId: Int, gamemode: String, kitName: String, rawInventory: String, rawArmor: String) = useDb { + new( + CompositeID { + it[PersonalKitTable.userId] = EntityID(userId, SteamwarUserTable) + it[PersonalKitTable.gamemode] = gamemode + it[PersonalKitTable.kitName] = kitName + } + ) { + this.inventory = rawInventory + this.armor = rawArmor + this.inUse = true + } + } + + @JvmStatic + fun getKitInUse(userId: Int, gamemode: String) = useDb { + find { PersonalKitTable.userId eq userId and (PersonalKitTable.gamemode eq gamemode) and (PersonalKitTable.inUse eq true) } + .firstOrNull() + } + } + + var userID by PersonalKitTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + private set + private var gameMode by PersonalKitTable.gamemode + val gamemode: String + get() = gameMode.value + + var kitName by PersonalKitTable.kitName + private set + val name: String + get() = kitName.value + + var rawInventory by PersonalKitTable.inventory + private set + var rawArmor by PersonalKitTable.armor + private set + var inUse by PersonalKitTable.inUse + private set + + var inventory: String + get() = rawInventory + set(value) = useDb { rawInventory = value } + var armor: String + get() = rawArmor + set(value) = useDb { rawArmor = value } + + fun setDefault() = useDb { + find { PersonalKitTable.userId eq userID and (PersonalKitTable.gamemode eq gameMode) and (PersonalKitTable.inUse eq true) } + .forEach { it.inUse = false } + inUse = true + } + + override fun delete() = useDb { + super.delete() + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.java b/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.java deleted file mode 100644 index 9376ed18..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/PollAnswer.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 . - */ - -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 lombok.Setter; - -import java.util.HashMap; -import java.util.Map; - -@AllArgsConstructor -public class PollAnswer { - - @Getter - @Setter - private static String currentPoll; - - private static final Table table = new Table<>(PollAnswer.class); - - private static final SelectStatement get = table.select(Table.PRIMARY); - private static final Statement getResults = new Statement("SELECT Count(UserID) AS Times, Answer FROM PollAnswer WHERE Question = ? GROUP BY Answer ORDER BY Times ASC"); - private static final Statement insert = table.insertAll(); - - @Field(keys = {Table.PRIMARY}) - private final int userID; - @Field(keys = {Table.PRIMARY}) - private final String question; - @Field(def = "0") - private int answer; - - public static PollAnswer get(int userID) { - PollAnswer answer = get.select(userID, currentPoll); - if(answer == null) - return new PollAnswer(userID, currentPoll, 0); - return answer; - } - - public static Map getCurrentResults() { - return getResults.select(rs -> { - Map retMap = new HashMap<>(); - while (rs.next()) - retMap.put(rs.getInt("Answer")-1, rs.getInt("Times")); - return retMap; - }, currentPoll); - } - - public boolean hasAnswered(){ - return answer != 0; - } - - public void setAnswer(int answer){ - this.answer = answer; - insert.update(userID, question, answer); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Punishment.java b/CommonCore/SQL/src/de/steamwar/sql/Punishment.java deleted file mode 100644 index 1cd61078..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/Punishment.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.sql.Timestamp; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -@AllArgsConstructor -public class Punishment { - - static { - SqlTypeMapper.nameEnumMapper(PunishmentType.class); - } - - public static final Timestamp PERMA_TIME = Timestamp.from(Instant.ofEpochSecond(946674800)); - - 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"); - private static final SelectStatement getAllPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? ORDER BY `PunishmentId` DESC"); - private static final Statement insert = table.insertFields(true, "UserId", "Punisher", "Type", "EndTime", "Perma", "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 List getAllPunishmentsOfPlayer(int user) { - return getAllPunishments.listSelect(user); - } - - public static boolean isPunished(SteamwarUser user, PunishmentType type, Consumer callback) { - Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type); - if(punishment == null || !punishment.isCurrent()) { - return false; - } else { - callback.accept(punishment); - return true; - } - } - - public static Punishment createPunishment(int user, int executor, PunishmentType type, String reason, Timestamp endTime, boolean perma) { - if(perma && !endTime.equals(PERMA_TIME)) { - throw new IllegalArgumentException("Permanent punishments must have an end time of `Punishment.PERMA_TIME`"); - } - int punishmentId = insert.insertGetKey(user, executor, type.name(), endTime, perma, reason); - return new Punishment(punishmentId, user, executor, type, Timestamp.from(Instant.now()), endTime, perma, reason); - } - - @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int punishmentId; - @Field - @Getter - private final int userId; - @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; - - @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 - @RequiredArgsConstructor - @Getter - public enum PunishmentType { - Ban(UserPerm.TEAM, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"), - Mute( UserPerm.TEAM, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"), - NoSchemReceiving(UserPerm.MODERATION, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"), - NoSchemSharing(UserPerm.MODERATION, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"), - NoSchemSubmitting(UserPerm.TEAM, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"), - NoDevServer(UserPerm.PREFIX_DEVELOPER, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"), - NoFightServer(UserPerm.MODERATION, "NOFIGHTSERVER_TEAM", "NOFIGHTSERVER_PERMA", "NOFIGHTSERVER_UNTIL", "UNNOFIGHTSERVER_ERROR", "UNNOFIGHTSERVER"), - NoTeamServer(UserPerm.MODERATION, "NOTEAMSERVER_TEAM", "NOTEAMSERVER_PERMA", "NOTEAMSERVER_UNTIL", "UNNOTEAMSERVER_ERROR", "UNNOTEAMSERVER"), - Note(UserPerm.TEAM, "NOTE_TEAM", null, null, null, null, true); - - private final UserPerm userPerm; - private final String teamMessage; - private final String playerMessagePerma; - private final String playerMessageUntil; - private final String usageNotPunished; - private final String unpunishmentMessage; - private boolean multi = false; - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Punishment.kt b/CommonCore/SQL/src/de/steamwar/sql/Punishment.kt new file mode 100644 index 00000000..d2ad06ba --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/Punishment.kt @@ -0,0 +1,186 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.* +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 org.jetbrains.exposed.v1.jdbc.select +import java.sql.Timestamp +import java.time.Instant +import java.util.* +import java.util.function.Consumer + +object PunishmentTable : IntIdTable("Punishments", "PunishmentId") { + val userId = reference("UserId", SteamwarUserTable) + val punisher = reference("Punisher", SteamwarUserTable) + val type = enumerationByName("Type", 32, Punishment.PunishmentType::class) + val startTime = timestamp("StartTime") + val endTime = timestamp("EndTime") + val perma = bool("Perma") + val reason = text("Reason") +} + +class Punishment(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(PunishmentTable) { + @JvmField + val PERMA_TIME: Timestamp = Timestamp.from(Instant.ofEpochSecond(946674800)) + + @JvmStatic + fun getPunsihmentOfPlayer(user: Int, type: PunishmentType) = useDb { + find { (PunishmentTable.userId eq user) and (PunishmentTable.type eq type) }.orderBy(PunishmentTable.id to SortOrder.DESC) + .firstOrNull() + } + + @JvmStatic + fun getPunishmentsOfPlayer(user: Int) = useDb { + find { + PunishmentTable.id inSubQuery PunishmentTable.select(PunishmentTable.id.max()) + .where { PunishmentTable.userId eq user }.groupBy( + PunishmentTable.type + ) + }.associateBy { it.type }.toMutableMap() + } + + @JvmStatic + fun getAllPunishmentsOfPlayer(user: Int) = useDb { + find { PunishmentTable.userId eq user }.orderBy(PunishmentTable.id to SortOrder.DESC).toList() + } + + @JvmStatic + fun isPunished(user: SteamwarUser, type: PunishmentType, callback: Consumer): Boolean = useDb { + val punishment = getPunsihmentOfPlayer(user.id.value, type) ?: return@useDb false + + if (punishment.isCurrent()) { + callback.accept(punishment) + return@useDb true + } + + return@useDb false + } + + @JvmStatic + fun createPunishment( + user: Int, + executor: Int, + type: PunishmentType, + reason: String, + endTime: Timestamp, + perma: Boolean + ) = useDb { + new { + this.userId = user + this.punisher = executor + this.type = type + this.startTime = Timestamp.from(Instant.now()) + this.endTime = endTime + this.perma = perma + this.reason = reason + } + } + } + + var userId by PunishmentTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + private set + var punisher by PunishmentTable.punisher.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) + private set + var type by PunishmentTable.type + private set + var startTime by PunishmentTable.startTime.transform({ it.toInstant() }, { Timestamp.from(it) }) + private set + var endTime by PunishmentTable.endTime.transform({ it.toInstant() }, { Timestamp.from(it) }) + private set + var perma by PunishmentTable.perma + private set + var reason by PunishmentTable.reason + private set + + fun isPerma() = perma + + fun isCurrent() = perma || endTime.after(Date()) + + enum class PunishmentType( + val teamMessage: String?, + val playerMessagePerma: String?, + val playerMessageUntil: String?, + val usageNotPunished: String?, + val unpunishmentMessage: String?, + val userPerm: UserPerm, + val multi: Boolean = false + ) { + Ban("BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN", UserPerm.PUNISHMENTS), + Mute("MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE", UserPerm.PUNISHMENTS), + NoSchemReceiving( + "NOSCHEMRECEIVING_TEAM", + "NOSCHEMRECEIVING_PERMA", + "NOSCHEMRECEIVING_UNTIL", + "UNNOSCHEMRECEIVING_ERROR", + "UNNOSCHEMRECEIVING", + UserPerm.MODERATION + ), + NoSchemSharing( + "NOSCHEMSHARING_TEAM", + "NOSCHEMSHARING_PERMA", + "NOSCHEMSHARING_UNTIL", + "UNNOSCHEMSHARING_ERROR", + "UNNOSCHEMSHARING", + UserPerm.MODERATION + ), + NoSchemSubmitting( + "NOSCHEMSUBMITTING_TEAM", + "NOSCHEMSUBMITTING_PERMA", + "NOSCHEMSUBMITTING_UNTIL", + "UNNOSCHEMSUBMITTING_ERROR", + "UNNOSCHEMSUBMITTING", + UserPerm.CHECK + ), + NoDevServer( + "NODEVSERVER_TEAM", + "NODEVSERVER_PERMA", + "NODEVSERVER_UNTIL", + "UNNODEVSERVER_ERROR", + "UNNODEVSERVER", + UserPerm.PREFIX_DEVELOPER + ), + NoFightServer( + "NOFIGHTSERVER_TEAM", + "NOFIGHTSERVER_PERMA", + "NOFIGHTSERVER_UNTIL", + "UNNOFIGHTSERVER_ERROR", + "UNNOFIGHTSERVER", + UserPerm.MODERATION + ), + NoTeamServer( + "NOTEAMSERVER_TEAM", + "NOTEAMSERVER_PERMA", + "NOTEAMSERVER_UNTIL", + "UNNOTEAMSERVER_ERROR", + "UNNOTEAMSERVER", + UserPerm.MODERATION + ), + Note("NOTE_TEAM", null, null, null, null, UserPerm.PUNISHMENTS, true); + + fun isMulti() = multi + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/Referee.java b/CommonCore/SQL/src/de/steamwar/sql/Referee.java deleted file mode 100644 index 3f909643..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/Referee.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 . - */ - -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.Set; -import java.util.stream.Collectors; - -@AllArgsConstructor -public class Referee { - - private static final Table table = new Table<>(Referee.class); - private static final SelectStatement byEvent = table.selectFields("eventID"); - - private static final Statement insert = table.insertAll(); - private static final Statement delete = table.delete("eventReferee"); - - public static void add(int eventID, int userID) { - insert.update(eventID, userID); - } - - public static void remove(int eventID, int userID) { - delete.update(eventID, userID); - } - - public static Set get(int eventID) { - return byEvent.listSelect(eventID).stream().map(referee -> referee.userID).collect(Collectors.toSet()); - } - - @Field(keys = {"eventReferee"}) - private final int eventID; - @Field(keys = {"eventReferee"}) - private final int userID; -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Referee.kt b/CommonCore/SQL/src/de/steamwar/sql/Referee.kt new file mode 100644 index 00000000..594b12cd --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/Referee.kt @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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 + +object RefereeTable: CompositeIdTable("Referee") { + val eventId = reference("EventId", EventTable) + val userId = reference("UserId", SteamwarUserTable) + + override val primaryKey = PrimaryKey(eventId, userId) + + init { + addIdColumn(eventId) + addIdColumn(userId) + } +} + +class Referee(id: EntityID): CompositeEntity(id) { + companion object: CompositeEntityClass(RefereeTable) { + @JvmStatic + fun add(eventId: Int, userId: Int) = useDb { + new { + this.eventID = eventId + this.userID = userId + } + } + + @JvmStatic + fun remove(eventId: Int, userId: Int) = useDb { + find { (RefereeTable.eventId eq eventId) and (RefereeTable.userId eq userId) }.firstOrNull()?.delete() + } + + @JvmStatic + fun get(event: Int) = useDb { + find { RefereeTable.eventId eq event }.map { it.userID }.toSet() + } + } + + var eventID by RefereeTable.eventId.transform({ EntityID(it, EventTable) }, { it.value }) + var userID by RefereeTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java b/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java index 4fbe2541..47331406 100644 --- a/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java +++ b/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java @@ -22,6 +22,8 @@ package de.steamwar.sql; import de.steamwar.ImplementationProvider; import java.io.File; +import java.util.Collections; +import java.util.List; public interface SQLWrapper { SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl"); @@ -30,6 +32,10 @@ public interface SQLWrapper { GameModeConfig loadGameModeConfig(File file); + default List getMaterialWithGreaterBlastResistance(double maxBlastResistance) { + return Collections.emptyList(); + } + default void processSchematicType(GameModeConfig gameModeConfig) { } diff --git a/CommonCore/SQL/src/de/steamwar/sql/SWException.java b/CommonCore/SQL/src/de/steamwar/sql/SWException.java deleted file mode 100644 index 945743f7..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/SWException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 . - */ - -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; -import java.sql.Timestamp; - -@AllArgsConstructor -public class SWException { - - public static void init() { - // force class initialialisation - } - - 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.insertFields(true, "server", "message", "stacktrace"); - - @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int id; - @Field(def = "CURRENT_TIMESTAMP") - private final Timestamp time; - @Field - private final String server; - @Field - private final String message; - @Field - private final String stacktrace; - - public static void log(String message, String stacktrace){ - insert.update(SERVER_NAME, generateMessage(message), stacktrace); - } - - public static int logGetId(String message, String stacktrace) { - return insert.insertGetKey(SERVER_NAME, generateMessage(message), stacktrace); - } - - private static String generateMessage(String message) { - StringBuilder msgBuilder = new StringBuilder(message); - SQLWrapper.impl.additionalExceptionMetadata(msgBuilder); - msgBuilder.append("\nCWD: ").append(CWD); - return msgBuilder.toString(); - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/SWException.kt b/CommonCore/SQL/src/de/steamwar/sql/SWException.kt new file mode 100644 index 00000000..fb57e24f --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/SWException.kt @@ -0,0 +1,72 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.javatime.CurrentTimestamp +import org.jetbrains.exposed.v1.javatime.timestamp +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import java.io.File + +object ExceptionTable: IntIdTable("Exception") { + val time = timestamp("Time").defaultExpression(CurrentTimestamp) + val server = text("Server") + val message = text("Message") + val stackTrace = text("StackTrace") +} + +class SWException { + companion object { + val cwd = System.getProperty("user.dir") + val serverName = File(cwd).name + + @JvmStatic + fun init() = Unit + + @JvmStatic + fun log(message: String, stacktrace: String) = useDb { + ExceptionTable.insert { + it[ExceptionTable.server] = serverName + it[ExceptionTable.message] = generateMessage(message) + it[ExceptionTable.stackTrace] = stacktrace + } + + Unit + } + + @JvmStatic + fun logGetId(message: String, stacktrace: String) = useDb { + ExceptionTable.insertAndGetId { + it[ExceptionTable.server] = serverName + it[ExceptionTable.message] = generateMessage(message) + it[ExceptionTable.stackTrace] = stacktrace + }.value + } + + fun generateMessage(message: String): String { + val msgBuilder = StringBuilder(message) + SQLWrapper.impl.additionalExceptionMetadata(msgBuilder) + msgBuilder.append("\nCWD: ").append(cwd) + return msgBuilder.toString() + } + } +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchemElo.java b/CommonCore/SQL/src/de/steamwar/sql/SchemElo.java deleted file mode 100644 index 0a8641b0..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/SchemElo.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 . - */ - -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; - -@AllArgsConstructor -public class SchemElo { - private static final int ELO_DEFAULT = 1000; - - private static final Table table = new Table<>(SchemElo.class); - private static final SelectStatement select = table.select(Table.PRIMARY); - private static final Statement setElo = table.insertAll(); - - public static int getElo(SchematicNode node, int season) { - SchemElo elo = select.select(node, season); - return elo != null ? elo.elo : 0; - } - - public static int getCurrentElo(int schemID) { - SchemElo elo = select.select(schemID, Season.getSeason()); - return elo != null ? elo.elo : ELO_DEFAULT; - } - - public static void setElo(int schemID, int elo) { - setElo.update(schemID, elo, Season.getSeason()); - } - - @Field(keys = {Table.PRIMARY}) - private final int schemId; - @Field - private final int elo; - @Field(keys = {Table.PRIMARY}) - private final int season; -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchemElo.kt b/CommonCore/SQL/src/de/steamwar/sql/SchemElo.kt new file mode 100644 index 00000000..b187186b --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/SchemElo.kt @@ -0,0 +1,74 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +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 + +object SchemEloTable: CompositeIdTable("SchemElo") { + val schemId = reference("SchemId", SchematicNodeTable) + val season = integer("Season").entityId() + val elo = integer("Elo") + + override val primaryKey = PrimaryKey(schemId, season) + + init { + addIdColumn(schemId) + } +} + +class SchemElo(id: EntityID): CompositeEntity(id) { + companion object: CompositeEntityClass(SchemEloTable) { + @JvmStatic + fun getElo(node: SchematicNode, season: Int, defaultElo: Int = 0) = useDb { + find { (SchemEloTable.schemId eq node.id) and (SchemEloTable.season eq season) }.firstOrNull()?.elo ?: defaultElo + } + + @JvmStatic + fun getCurrentElo(schemId: Int) = getElo(SchematicNode.byId(schemId)!!, Season.getSeason()) + + @JvmStatic + fun setElo(node: Int, elo: Int) = useDb { + findByIdAndUpdate(CompositeID { + it[SchemEloTable.schemId] = node + it[SchemEloTable.season] = Season.getSeason() + }) { + it.elo = elo + } ?: SchemEloTable.insertIgnore { + it[SchemEloTable.schemId] = node + it[SchemEloTable.season] = Season.getSeason() + it[SchemEloTable.elo] = elo + } + + return@useDb + } + } + + var node by SchemEloTable.schemId + var season by SchemEloTable.season + var elo by SchemEloTable.elo +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.java b/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.java deleted file mode 100644 index bac7929f..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.java +++ /dev/null @@ -1,634 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.*; -import lombok.Getter; - -import java.sql.Timestamp; -import java.time.Instant; -import java.util.*; -import java.util.function.Predicate; - -public class SchematicNode { - - static { - SchematicType.Normal.name(); // Ensure SchematicType is loaded. - 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 nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config 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.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem", - "NodeType", "NodeRank", "Config"); - private static final Statement delete = table.delete(Table.PRIMARY); - - private static final SelectStatement byId = new SelectStatement<>(table, - nodeSelector + "WHERE NodeId = ?"); - private static final SelectStatement byOwnerNameParent = new SelectStatement<>(table, - nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode " + Statement.NULL_SAFE_EQUALS + "?"); - private static final SelectStatement byParent = new SelectStatement<>(table, - nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); - private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelector - + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); - private static final SelectStatement byOwnerType = new SelectStatement<>(table, - nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); - private static final SelectStatement byType = new SelectStatement<>(table, - nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); - private static final SelectStatement all = new SelectStatement<>(table, - "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId"); - private static final SelectStatement list = new SelectStatement<>(table, - "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " - + Statement.NULL_SAFE_EQUALS - + "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName"); - private static final SelectStatement byParentName = new SelectStatement<>(table, - "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " - + Statement.NULL_SAFE_EQUALS - + "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?"); - private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, - "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeId = ?"); - private static final SelectStatement accessibleByUserTypeInParent = new SelectStatement<>(table, - "WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId" - + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); - private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, - "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?"); - private static final SelectStatement byIdAndUser = new SelectStatement<>(table, - "SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE NodeId = ?"); - private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, - "WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.Config FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); - - static { - NodeMember.init(); - } - - @Field(keys = { Table.PRIMARY }, autoincrement = true) - private final int nodeId; - @Field(keys = { "OwnerNameParent" }) - private final int nodeOwner; - @Field(def = "0") - @Getter - private final int effectiveOwner; - @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 - private int config; - - private String brCache; - - public SchematicNode( - int nodeId, - int nodeOwner, - int effectiveOwner, - String nodeName, - Integer parentNode, - Timestamp lastUpdate, - String nodeItem, - SchematicType nodeType, - int nodeRank, - int config) { - this.nodeId = nodeId; - this.nodeOwner = nodeOwner; - this.effectiveOwner = effectiveOwner; - this.nodeName = nodeName; - this.parentNode = parentNode; - this.nodeItem = nodeItem; - this.nodeType = nodeType; - this.lastUpdate = lastUpdate; - this.nodeRank = nodeRank; - this.config = config; - } - - public static List getAll(SteamwarUser user) { - return all.listSelect(user, user, user); - } - - public static Map> getAllMap(SteamwarUser user) { - return map(getAll(user)); - } - - public static List list(SteamwarUser user, Integer schematicId) { - return list.listSelect(user, schematicId, user, user, schematicId, user, schematicId, schematicId); - } - - public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { - return byParentName.select(user, schematicId, user, name, user, schematicId, user, schematicId, schematicId, - name); - } - - public static List accessibleByUserType(SteamwarUser user, SchematicType type) { - return accessibleByUserType.listSelect(user, user, user, type); - } - - public static Map> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) { - return map(accessibleByUserType(user, type)); - } - - public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) { - return schematicAccessibleForUser.select(user, user, user, schematicId) != null; - } - - public static List accessibleByUserTypeParent(SteamwarUser user, SchematicType type, - Integer parentId) { - return accessibleByUserTypeInParent.listSelect(user, user, type, user, parentId); - } - - public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) { - return byIdAndUser.select(user, id); - } - - public static List parentsOfNode(SteamwarUser user, Integer id) { - return allParentsOfNode.listSelect(id, user, user, user); - } - - private static Map> map(List in) { - Map> map = new HashMap<>(); - for (SchematicNode effectiveSchematicNode : in) { - map.computeIfAbsent(effectiveSchematicNode.getOptionalParent().orElse(0), k -> new ArrayList<>()) - .add(effectiveSchematicNode); - } - return map; - } - - 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, item, type); - 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) { - return byOwnerNameParent.select(owner, name, parent); - } - - public static List getSchematicNodeInNode(SchematicNode parent) { - return getSchematicNodeInNode(parent.getId()); - } - - public static List getSchematicNodeInNode(Integer parent) { - return byParent.listSelect(parent); - } - - public static List getSchematicDirectoryInNode(Integer parent) { - 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) { - 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) { - return accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent); - } - - public static List getAllAccessibleSchematicsOfType(int user, String schemType) { - return accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(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; - } - - @Deprecated - public static List getSchematicsAccessibleByUser(int user, Integer parent) { - return list(SteamwarUser.get(user), parent); - } - - @Deprecated - public static List getAllSchematicsAccessibleByUser(int user) { - return getAll(SteamwarUser.get(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.endsWith("/")) { - s = s.substring(0, s.length() - 1); - } - if (s.isEmpty()) { - return null; - } - if (s.contains("/")) { - String[] layers = s.split("/"); - Optional currentNode = Optional - .ofNullable(SchematicNode.byParentName(user, null, layers[0])); - for (int i = 1; i < layers.length; i++) { - int finalI = i; - Optional node = currentNode.map(effectiveSchematicNode -> SchematicNode - .byParentName(user, effectiveSchematicNode.getId(), layers[finalI])); - if (!node.isPresent()) { - return null; - } else { - currentNode = node; - if (!currentNode.map(SchematicNode::isDir).orElse(false) && i != layers.length - 1) { - return null; - } - } - } - return currentNode.orElse(null); - } else { - return SchematicNode.byParentName(user, null, s); - } - } - - 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; - } - - 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 Optional getOptionalParent() { - return Optional.ofNullable(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 String getFileEnding() { - if (isDir()) - throw new SecurityException("Node is Directory"); - return NodeData.getLatest(this).getNodeFormat().getFileEnding(); - } - - 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 getConfig(ConfigFlags.REPLACE_COLOR); - } - - public void setReplaceColor(boolean replaceColor) { - if (isDir()) - throw new SecurityException("Is Directory"); - setConfig(ConfigFlags.REPLACE_COLOR, replaceColor); - } - - public boolean allowReplay() { - return getConfig(ConfigFlags.ALLOW_REPLAY); - } - - public void setAllowReplay(boolean allowReplay) { - if (isDir()) - throw new SecurityException("Is Directory"); - setConfig(ConfigFlags.ALLOW_REPLAY, allowReplay); - } - - public boolean isPrepared() { - return getConfig(ConfigFlags.IS_PREPARED); - } - - public void setPrepared(boolean prepared) { - if (isDir()) - throw new SecurityException("Is Directory"); - setConfig(ConfigFlags.IS_PREPARED, prepared); - } - - public boolean getConfig(ConfigFlags flag) { - return (config & (1 << flag.ordinal())) != 0; - } - - public void setConfig(ConfigFlags flag, boolean value) { - if (value) { - config |= (1 << flag.ordinal()); - } else { - config &= ~(1 << flag.ordinal()); - } - 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(SteamwarUser 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(nodeName, parentNode, nodeItem, nodeType, nodeRank, config, nodeId); - 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 byIdAndUser(user, nodeId).generateBreadcrumbs(); - } - - public String generateBreadcrumbs(String split, SteamwarUser user) { - return byIdAndUser(user, nodeId).generateBreadcrumbs(split); - } - - public String generateBreadcrumbs() { - if (brCache == null) { - brCache = generateBreadcrumbs("/"); - } - return brCache; - } - - public String generateBreadcrumbs(String split) { - StringBuilder builder = new StringBuilder(getName()); - Optional currentNode = Optional.of(this); - if (currentNode.map(SchematicNode::isDir).orElse(false)) { - builder.append(split); - } - while (currentNode.isPresent()) { - currentNode = currentNode - .flatMap(schematicNode -> Optional - .ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner)) - .map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())) - .map(SchematicNode::getSchematicNode); - currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName())); - } - return builder.toString(); - } - - public List> generateBreadcrumbsMap(SteamwarUser user) { - List> map = new ArrayList<>(); - Optional currentNode = Optional.of(this); - if (currentNode.map(SchematicNode::isDir).orElse(false)) { - map.add(new AbstractMap.SimpleEntry<>(getName(), getId())); - } - while (currentNode.isPresent()) { - currentNode = currentNode - .flatMap(schematicNode -> Optional - .ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner)) - .map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())) - .map(SchematicNode::getSchematicNode); - currentNode.ifPresent(node -> map.add(0, new AbstractMap.SimpleEntry<>(node.getName(), node.getId()))); - } - return map; - } - - 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 new ArrayList<>(); - List nodes = SchematicNode.list(user, pa.getId()); - String br = pa.generateBreadcrumbs(); - nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : ""))); - } else { - List nodes = SchematicNode.list(user, null); - 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; - } - - public static enum ConfigFlags { - REPLACE_COLOR, - ALLOW_REPLAY, - IS_PREPARED - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt b/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt new file mode 100644 index 00000000..96fd0901 --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt @@ -0,0 +1,435 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import de.steamwar.sql.internal.fromSql +import de.steamwar.sql.internal.useDb +import org.jetbrains.exposed.v1.core.* +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.CurrentTimestamp +import org.jetbrains.exposed.v1.javatime.timestamp +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import java.sql.Timestamp +import java.util.* +import java.util.function.Consumer + +object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") { + val owner = reference("NodeOwner", SteamwarUserTable) + val name = varchar("NodeName", 64) + val parent = optReference("ParentNode", SchematicNodeTable) + val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp) + val item = text("NodeItem") + val type = varchar("NodeType", 16).nullable() + val config = integer("Config") +} + +class SchematicNode(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(SchematicNodeTable) { + private val fieldIndex: Map, Int> = + SchematicNodeTable.columns.mapIndexed { index, column -> column to index }.toMap() + + private val FORBIDDEN_NAMES = listOf("public") + private val FORBIDDEN_CHARS = listOf('/', '\\', '<', '>', '^', '°', '\'', '"', ' ') + + val tabCache = mutableMapOf>>() + + @JvmStatic + fun clear() = tabCache.clear() + + private fun List.mapToIds(): Map = this.associateBy { it.nodeId } + + @JvmStatic + fun getAll(user: SteamwarUser) = fromSql( + "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.* FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId", + listOf( + IntegerColumnType() to user.getId(), + IntegerColumnType() to user.getId() + ), + fieldIndex + ) + + @JvmStatic + fun getAllMap(user: SteamwarUser) = getAll(user).mapToIds() + + @JvmStatic + fun list(user: SteamwarUser, schematicId: Int?) = fromSql( + "SELECT SchematicNode.NodeId, NodeOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId <=> ? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName", + listOf( + IntegerColumnType() to schematicId, + IntegerColumnType() to user.getId(), + IntegerColumnType() to schematicId, + IntegerColumnType() to user.getId(), + IntegerColumnType() to schematicId, + IntegerColumnType() to schematicId, + ), fieldIndex + ) + + @JvmStatic + fun byParentName(user: SteamwarUser, schematicId: Int?, name: String) = fromSql( + "SELECT SchematicNode.NodeId, NodeOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId <=> ? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?", + listOf( + IntegerColumnType() to schematicId, + IntegerColumnType() to user.getId(), + VarCharColumnType() to name, + IntegerColumnType() to schematicId, + IntegerColumnType() to user.getId(), + IntegerColumnType() to schematicId, + IntegerColumnType() to schematicId, + VarCharColumnType() to name, + ), fieldIndex + ).firstOrNull() + + @JvmStatic + fun accessibleByUserType(user: SteamwarUser, type: SchematicType) = fromSql( + "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?", + listOf( + IntegerColumnType() to user.getId(), + IntegerColumnType() to user.getId(), + IntegerColumnType() to user.getId(), + VarCharColumnType() to type.toDB(), + ), fieldIndex + ) + + @JvmStatic + fun accessibleByUserTypeMap(user: SteamwarUser, type: SchematicType) = + accessibleByUserType(user, type).mapToIds() + + @JvmStatic + fun schematicAccessibleForUser(user: SteamwarUser, schematicId: Int?) = fromSql( + "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeId = ?", + listOf( + IntegerColumnType() to user.getId(), + IntegerColumnType() to user.getId(), + IntegerColumnType() to user.getId(), + IntegerColumnType() to schematicId, + ), + fieldIndex + ).isNotEmpty() + + @JvmStatic + fun accessibleByUserTypeParent(user: SteamwarUser, type: SchematicType, parentId: Int?) = fromSql( + "WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId <=> ? ORDER BY NodeName", + listOf( + IntegerColumnType() to user.getId(), + IntegerColumnType() to user.getId(), + VarCharColumnType() to type.toDB(), + IntegerColumnType() to user.getId(), + IntegerColumnType() to parentId, + ), fieldIndex + ) + + @JvmStatic + @Deprecated("Use byId") + fun byIdAndUser(ignored: SteamwarUser, id: Int) = useDb { + findById(id) + } + + @JvmStatic + fun parentsOfNode(user: SteamwarUser, id: Int) = fromSql( + "WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.Config FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId", + listOf( + IntegerColumnType() to id, + IntegerColumnType() to user.getId() + ), fieldIndex + ) + + @JvmStatic + fun createSchematic(owner: Int, name: String, parent: Int?) = createSchematicNode( + owner, name, parent, + SchematicType.Normal.toDB(), "" + ) + + @JvmStatic + fun createSchematicDirectory(owner: Int, name: String, parent: Int?) = + createSchematicNode(owner, name, parent, null, "") + + @JvmStatic + fun createSchematicNode(owner: Int, name: String, parent: Int?, type: String?, item: String) = useDb { + val id = SchematicNodeTable.insertAndGetId { + it[this.owner] = EntityID(owner, SteamwarUserTable) + it[this.name] = name + it[this.parent] = + parent?.let { p -> if (p == 0) null else p }?.let { p -> EntityID(p, SchematicNodeTable) } + it[this.item] = item + it[this.type] = type + } + return@useDb findById(id) ?: throw IllegalStateException("SchematicNode $id not found") + } + + @JvmStatic + fun byId(id: Int) = useDb { findById(id) } + + @JvmStatic + fun getSchematicNode(owner: Int, name: String, parent: Int?) = useDb { + find { (SchematicNodeTable.owner eq owner) and (SchematicNodeTable.name eq name) and (SchematicNodeTable.parent eq parent) }.firstOrNull() + } + + @JvmStatic + fun getSchematicNode(owner: Int, name: String, parent: SchematicNode) = + getSchematicNode(owner, name, parent.nodeId) + + @JvmStatic + fun getSchematicNodeInNode(parent: Int?) = useDb { + find { (SchematicNodeTable.parent eq parent) }.orderBy(SchematicNodeTable.name to SortOrder.ASC).toList() + } + + @JvmStatic + fun getSchematicNodeInNode(parent: SchematicNode) = getSchematicNodeInNode(parent.nodeId) + + @JvmStatic + fun getSchematicNode(name: String, parent: Int?) = useDb { + find { (SchematicNodeTable.name eq name) and (SchematicNodeTable.parent eq parent) }.firstOrNull() + } + + @JvmStatic + fun getSchematicNode(id: Int) = byId(id) + + @JvmStatic + fun getAllAccessibleSchematicsOfType(user: Int, type: String) = + accessibleByUserType(SteamwarUser.byId(user)!!, SchematicType.fromDB(type)!!) + + @JvmStatic + fun getAllSchematicsAccessibleByUser(user: Int) = getAll(SteamwarUser.byId(user)!!) + + @JvmStatic + fun getAllSchematicsOfType(owner: Int, type: String) = useDb { + find { (SchematicNodeTable.owner eq owner) and (SchematicNodeTable.type eq type) }.orderBy( + SchematicNodeTable.name to SortOrder.ASC + ).toList() + } + + @JvmStatic + fun getAllSchematicsOfType(type: SchematicType) = useDb { + find { (SchematicNodeTable.type eq type.toDB()) }.orderBy(SchematicNodeTable.name to SortOrder.ASC).toList() + } + + @JvmStatic + fun deepGet(parent: Int?, filter: (node: SchematicNode) -> Boolean): List = + getSchematicNodeInNode(parent) + .flatMap { + return@flatMap if (it.isDir()) { + deepGet(it.nodeId, filter) + } else { + if (filter(it)) listOf(it) else listOf() + } + } + + @JvmStatic + fun getNodeFromPath(user: SteamwarUser, path: String): SchematicNode? { + var s = path + if (s.startsWith("/")) { + s = s.drop(1) + } + if (s.endsWith("/")) { + s = s.dropLast(1) + } + if (s.isEmpty()) return null + if (s.contains("/")) { + val layers: Array = s.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + var currentNode = byParentName(user, null, layers[0]!!) + for (i in 1.. byParentName(user, n.getId(), layers[i]!!) } + if (node == null) { + return null + } else { + currentNode = node + if (!currentNode.isDir() && i != layers.size - 1 + ) { + return null + } + } + } + return currentNode + } else { + return byParentName(user, null, s) + } + } + + @JvmStatic + fun invalidSchemName(layers: Array) = layers.any { + it.isEmpty() || FORBIDDEN_CHARS.any { c -> c in it } || FORBIDDEN_NAMES.any { n -> n == it.lowercase() } + } + + @JvmStatic + fun getNodeTabcomplete(user: SteamwarUser, s: String): List { + var s = s + val sws = s.startsWith("/") + if (sws) { + s = s.substring(1) + } + val index = s.lastIndexOf("/") + val cacheKey = if (index == -1) "" else s.take(index) + tabCache[user.getId()]?.get(cacheKey)?.also { return it.toMutableList() } + + val list = mutableListOf() + if (s.contains("/")) { + val preTab = s.take(s.lastIndexOf("/") + 1) + val pa = getNodeFromPath(user, preTab) ?: return emptyList() + val nodes: List = list(user, pa.getId()) + val br = pa.generateBreadcrumbs(user) + nodes.forEach(Consumer { node: SchematicNode? -> list.add((if (sws) "/" else "") + br + node!!.name + (if (node.isDir()) "/" else "")) }) + } else { + val nodes: List = list(user, null) + nodes.forEach(Consumer { node: SchematicNode? -> list.add((if (sws) "/" else "") + node!!.name + (if (node.isDir()) "/" else "")) }) + } + list.remove("//copy") + tabCache.computeIfAbsent(user.getId()) { i -> mutableMapOf() }.putIfAbsent(cacheKey, list) + return list + } + } + + val nodeId by SchematicNodeTable.id.transform({ EntityID(it, SchematicNodeTable) }, { it.value }) + val nodeOwner by SchematicNodeTable.owner + val owner: Int get() = nodeOwner.value + private var nodeName by SchematicNodeTable.name + var name: String + get() = nodeName + set(value) = useDb { + nodeName = value + } + private var parentNodeId by SchematicNodeTable.parent + var parent: Int? + get() = parentNodeId?.value + set(value) = useDb { + parentNodeId = value?.let { EntityID(it, SchematicNodeTable) } + } + val parentNode: SchematicNode? + get() = useDb { parent?.let { findById(it) } } + + val optionalParent: Optional get() = Optional.ofNullable(parent) + var lastUpdate by SchematicNodeTable.lastUpdate.transform({ it.toInstant() }, { Timestamp.from(it) }) + private var nodeItem by SchematicNodeTable.item + var item: String + get() = nodeItem.ifEmpty { + if (isDir()) "CHEST" else "CAULDRON_ITEM" + } + set(value) = useDb { + nodeItem = value + } + private var nodeType by SchematicNodeTable.type + var schemtype: SchematicType + get() = checkDir { SchematicType.fromDB(nodeType!!) ?: SchematicType.Normal } + set(value) = checkDir { useDb { nodeType = value.toDB() } } + var config by SchematicNodeTable.config + + val members: Set by lazy { NodeMember.getNodeMembers(nodeId) } + private lateinit var breadcrumbs: String + + fun getFileEnding(): String = checkDir { NodeData.getLatest(this)!!.nodeFormat.fileEnding } + fun getId() = nodeId + fun isDir() = nodeType == null + + private fun checkDir(block: () -> T): T { + if (isDir()) { + throw IllegalStateException("Node is a directory") + } + + return block() + } + + fun replaceColor() = getConfig(ConfigFlags.REPLACE_COLOR) + fun setReplaceColor(value: Boolean) = setConfig(ConfigFlags.REPLACE_COLOR, value) + + fun allowReplay() = getConfig(ConfigFlags.ALLOW_REPLAY) + fun setAllowReplay(value: Boolean) = setConfig(ConfigFlags.ALLOW_REPLAY, value) + + fun isPrepared() = getConfig(ConfigFlags.IS_PREPARED) + fun setPrepared(value: Boolean) = setConfig(ConfigFlags.IS_PREPARED, value) + + fun getConfig(flag: ConfigFlags) = config and (1 shl flag.ordinal) != 0 + fun setConfig(flag: ConfigFlags, value: Boolean) = useDb { + config = if (value) { + config or (1 shl flag.ordinal) + } else { + config and (1 shl flag.ordinal).inv() + } + } + + fun getElo(season: Int) = SchemElo.getElo(this, season) + + override fun delete() = useDb { + super.delete() + } + + override fun hashCode() = nodeId + + override fun equals(other: Any?): Boolean { + if (other !is SchematicNode) return false + return nodeId == other.nodeId + } + + fun generateBreadcrumbs(user: SteamwarUser): String { + if (!this::breadcrumbs.isInitialized) { + breadcrumbs = generateBreadcrumbs("/", user) + } + return breadcrumbs + } + + fun generateBreadcrumbs(split: String, user: SteamwarUser): String = useDb { + val builder: StringBuilder = StringBuilder(name) + if (isDir()) { + builder.append(split) + } + var currentNode: SchematicNode? = this@SchematicNode + while (currentNode != null) { + currentNode = currentNode + .let { + NodeMember.getNodeMember(it.nodeId, user.getId()) + ?.parent?.orElse(null) ?: it.parent + } + ?.let { findById(it) } + ?.also { + builder.insert(0, split).insert(0, it.name) + } + } + return@useDb builder.toString() + } + + fun generateBreadcrumbsMap(user: SteamwarUser): List> = useDb { + val map = mutableListOf>() + var currentNode: SchematicNode? = this@SchematicNode + while (currentNode != null) { + currentNode = currentNode + .also { + map.add(0, Pair(it.name, it.nodeId)) + } + .let { + NodeMember.getNodeMember(it.nodeId, user.getId()) + ?.parent?.orElse(null) ?: it.parent + } + ?.let { findById(it) } + } + return@useDb map + } + + fun accessibleByUser(user: SteamwarUser) = schematicAccessibleForUser(user, nodeId) + + enum class ConfigFlags { + REPLACE_COLOR, + ALLOW_REPLAY, + IS_PREPARED + } + + @Deprecated("Removed") + var rank = 0 +} diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchematicType.java b/CommonCore/SQL/src/de/steamwar/sql/SchematicType.java deleted file mode 100644 index 6abaffca..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/SchematicType.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.sql; - -import de.steamwar.sql.internal.SqlTypeMapper; -import lombok.Getter; - -import java.io.File; -import java.util.*; -import java.util.stream.Collectors; - -public class SchematicType { - - public static final SchematicType Normal = new SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false); - - private static final Map fromDB; - private static final List types; - - static { - List tmpTypes = new LinkedList<>(); - Map tmpFromDB = new HashMap<>(); - - tmpTypes.add(Normal); - tmpFromDB.put(Normal.name().toLowerCase(), Normal); - - File folder = SQLWrapper.impl.getSchemTypesFolder(); - if (folder.exists()) { - for (File configFile : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().collect(Collectors.toList())) { - GameModeConfig gameModeConfig = SQLWrapper.impl.loadGameModeConfig(configFile); - if (gameModeConfig.Schematic.Type == null) continue; - if (tmpFromDB.containsKey(gameModeConfig.Schematic.Type.toDB())) continue; - SchematicType current = gameModeConfig.Schematic.Type; - if (!gameModeConfig.CheckQuestions.isEmpty()) { - SchematicType checkType = current.checkType; - tmpTypes.add(checkType); - tmpFromDB.put(checkType.toDB(), checkType); - } - tmpTypes.add(current); - tmpFromDB.put(current.toDB(), current); - SQLWrapper.impl.processSchematicType(gameModeConfig); - } - } - - fromDB = Collections.unmodifiableMap(tmpFromDB); - types = Collections.unmodifiableList(tmpTypes); - } - - static { - new SqlTypeMapper<>(SchematicType.class, "VARCHAR(16)", (rs, identifier) -> { - String t = rs.getString(identifier); - return t != null ? fromDB.get(t) : null; - }, (st, index, value) -> st.setString(index, value.toDB())); - } - - private final String name; - @Getter - private final String kuerzel; - private final Type type; - private final SchematicType checkType; - @Getter - private final String material; - @Getter - private final Date deadline; - @Getter - private final boolean manualCheck; - - SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, boolean manualCheck){ - this(name, kuerzel, type, checkType, material, null, manualCheck); - } - - SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, Date deadline, boolean manualCheck){ - this.name = name; - this.kuerzel = kuerzel; - this.type = type; - this.checkType = checkType; - this.material = material; - this.deadline = deadline; - this.manualCheck = manualCheck; - } - - public boolean isAssignable(){ - return type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck; - } - - public SchematicType checkType(){ - if (!manualCheck) { - return this; - } - return checkType; - } - - public boolean check(){ - return type == Type.CHECK_TYPE; - } - - public boolean fightType(){ - return type == Type.FIGHT_TYPE; - } - - public boolean writeable(){ - return type == Type.NORMAL; - } - - public String name(){ - return name; - } - - public String toDB(){ - return name.toLowerCase(); - } - - public static SchematicType fromDB(String input) { - if (fromDB == null) return null; - return fromDB.get(input.toLowerCase()); - } - - public static List values(){ - return types; - } - - enum Type{ - NORMAL, - CHECK_TYPE, - FIGHT_TYPE - } -} diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchematicType.kt b/CommonCore/SQL/src/de/steamwar/sql/SchematicType.kt new file mode 100644 index 00000000..b7f7d5be --- /dev/null +++ b/CommonCore/SQL/src/de/steamwar/sql/SchematicType.kt @@ -0,0 +1,108 @@ +/* + * 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 . + */ + +package de.steamwar.sql + +import java.io.File +import java.util.* +import java.util.Locale +import java.util.Locale.getDefault +import java.util.stream.Collectors + +data class SchematicType( + val name: String, + val kuerzel: String, + val type: Type, + val checkType: SchematicType?, + val material: String, + val deadline: Date?, + val manualCheck: Boolean, +) { + constructor( + name: String, + kuerzel: String, + type: Type, + checkType: SchematicType?, + material: String, + manualCheck: Boolean + ) : this(name, kuerzel, type, checkType, material, null, manualCheck) + + companion object { + @JvmField + val Normal = SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false) + + private val types: List + private val fromDB: Map? + + init { + val tmpTypes = mutableListOf() + val tmpFromDB = mutableMapOf() + + tmpTypes.add(Normal) + tmpFromDB[Normal.toDB()] = Normal + + val folder = SQLWrapper.impl.schemTypesFolder + if (folder.exists()) { + for (configFile in Arrays.stream(folder.listFiles { _, name -> + name.endsWith( + ".yml" + ) && !name.endsWith(".kits.yml") + }).sorted().collect(Collectors.toList())) { + val gameModeConfig = SQLWrapper.impl.loadGameModeConfig(configFile) + if (gameModeConfig.Schematic.Type == null) continue + if (tmpFromDB.containsKey(gameModeConfig.Schematic.Type.toDB())) continue + val current = gameModeConfig.Schematic.Type + if (gameModeConfig.CheckQuestions.isNotEmpty()) { + val checkType = current.checkType + tmpTypes.add(checkType!!) + tmpFromDB[checkType.toDB()] = checkType + } + tmpTypes.add(current) + tmpFromDB[current.toDB()] = current + SQLWrapper.impl.processSchematicType(gameModeConfig) + } + } + + types = tmpTypes.toList() + fromDB = tmpFromDB.toMap() + } + + @JvmStatic + fun values() = types + + @JvmStatic + fun fromDB(value: String) = fromDB?.let { it[value.lowercase()] } + } + + fun name() = name + fun toDB() = name.lowercase() + + fun check() = type == Type.CHECK_TYPE + fun fightType() = type == Type.FIGHT_TYPE + fun writeable() = type == Type.NORMAL + + fun checkType() = if (manualCheck) checkType else this + fun isAssignable() = type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck + + enum class Type { + NORMAL, + CHECK_TYPE, + FIGHT_TYPE + } +} diff --git a/CommonCore/SQL/src/de/steamwar/sql/Script.java b/CommonCore/SQL/src/de/steamwar/sql/Script.java deleted file mode 100644 index 1a5ccd25..00000000 --- a/CommonCore/SQL/src/de/steamwar/sql/Script.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 . - */ - -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 java.util.List; - -@AllArgsConstructor -@Getter -public class Script { - - private static final Table