diff --git a/BauSystem_Main/src/de/steamwar/bausystem/Permission.java b/BauSystem_Main/src/de/steamwar/bausystem/Permission.java index 778bf4ca..6ab9d142 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/Permission.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/Permission.java @@ -21,18 +21,31 @@ package de.steamwar.bausystem; import de.steamwar.bausystem.config.BauServer; import de.steamwar.sql.BauweltMember; +import de.steamwar.sql.SteamwarUser; import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import java.util.function.BiConsumer; import java.util.function.Predicate; @AllArgsConstructor +@RequiredArgsConstructor public enum Permission { - WORLD(BauweltMember::isWorld), - WORLDEDIT(BauweltMember::isWorldEdit), + + WORLD(BauweltMember::isWorld, (player, target) -> { + target.setWorld(!target.isWorld()); + sendMessages(player, target.isWorld(), target, "Einstellungen vornehmen"); + }), + WORLDEDIT(BauweltMember::isWorldEdit, (player, target) -> { + target.setWorldEdit(!target.isWorldEdit()); + sendMessages(player, target.isWorldEdit(), target, "WorldEdit verwenden"); + }), MEMBER(bauweltMember -> true); - private Predicate permissionPredicate; + private final Predicate permissionPredicate; + private BiConsumer permissionChange = (player, bauweltMember) -> {}; public boolean hasPermission(Player member) { if (member.getUniqueId().equals(BauServer.getInstance().getOwner())) { @@ -50,4 +63,28 @@ public enum Permission { public static boolean hasPermission(Player member, Permission permission) { return permission.hasPermission(member); } + + private static void sendMessages(Player player, boolean ableTo, BauweltMember target, String what) { + Player targetPlayer = Bukkit.getPlayer(SteamwarUser.get(target.getMemberID()).getUUID()); + if (targetPlayer != null) { + if (ableTo) { + targetPlayer.sendMessage(BauSystem.PREFIX + "§aDu kannst nun auf der Welt von §6" + player.getName() + "§a " + what); + } else { + targetPlayer.sendMessage(BauSystem.PREFIX + "§cDu kannst nun nicht mehr auf der Welt von §6" + player.getName() + "§c " + what); + } + } + if (ableTo) { + player.sendMessage(BauSystem.PREFIX + "§aDer Spieler darf nun " + what); + } else { + player.sendMessage(BauSystem.PREFIX + "§cDer Spieler darf nun nicht mehr " + what); + } + } + + public void toggle(Player player, BauweltMember target) { + permissionChange.accept(player, target); + } + + public static void toggle(Player player, BauweltMember target, Permission permission) { + permission.toggle(player, target); + } } \ No newline at end of file diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauCommand.java new file mode 100644 index 00000000..40f9fa4f --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauCommand.java @@ -0,0 +1,133 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.bau; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.config.BauServer; +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import de.steamwar.command.SWCommand; +import de.steamwar.command.SWCommandUtils; +import de.steamwar.command.TypeMapper; +import de.steamwar.sql.BauweltMember; +import de.steamwar.sql.SteamwarUser; +import org.bukkit.entity.Player; + +import java.util.stream.Collectors; + +@Linked(LinkageType.COMMAND) +public class BauCommand extends SWCommand { + + public BauCommand() { + super("bau", "b", "gs"); + } + + @Register(help = true) + public void genericHelp(Player p, String... args) { + p.sendMessage("§8/§ebau togglewe §8[§7Player§8] §8- §7Editiere die WorldEdit Rechte eines Spielers"); + p.sendMessage("§8/§ebau toggleworld §8[§7Player§8] §8- §7Editiere die Werlt Rechte eines Spielers"); + } + + @Register("info") + public void infoCommand(Player p) { + // TODO: Implement this + // CommandInfo.sendBauInfo(p); + } + + @Register("togglewe") + public void toggleWECommand(Player p, SteamwarUser user) { + if (!permissionCheck(p)) { + return; + } + onToggleWE(p, user); + } + + @Register("toggleworld") + public void toggleWorldCommand(Player p, SteamwarUser user) { + if (!permissionCheck(p)) { + return; + } + onToggleWorld(p, user); + } + + private void onToggleWE(Player p, SteamwarUser id) { + if (negativeToggleCheck(p, id)) { + return; + } + + BauweltMember target = BauweltMember.getBauMember(BauServer.getInstance().getOwnerID(), id.getId()); + Permission.WORLDEDIT.toggle(p, target); + } + + private void onToggleWorld(Player p, SteamwarUser id) { + if (negativeToggleCheck(p, id)) { + return; + } + + BauweltMember target = BauweltMember.getBauMember(BauServer.getInstance().getOwnerID(), id.getId()); + Permission.WORLD.toggle(p, target); + } + + private boolean negativeToggleCheck(Player p, SteamwarUser id) { + if (id == null) { + p.sendMessage(BauSystem.PREFIX + "§cUnbekannter Spieler"); + return true; + } + + BauweltMember target = BauweltMember.getBauMember(BauServer.getInstance().getOwnerID(), id.getId()); + if (target == null) { + p.sendMessage(BauSystem.PREFIX + "§cDer Spieler ist kein Mitglied deiner Welt!"); + return true; + } + return false; + } + + + private boolean permissionCheck(Player p) { + if (!BauServer.getInstance().getOwner().equals(p.getUniqueId())) { + p.sendMessage(BauSystem.PREFIX + "§cDies ist nicht deine Welt!"); + return false; + } else { + return true; + } + } + + + @ClassMapper(value = SteamwarUser.class, local = true) + private TypeMapper steamwarUserTypeMapper() { + return SWCommandUtils.createMapper(s -> BauweltMember.getMembers(BauServer.getInstance().getOwnerID()) + .stream() + .map(m -> SteamwarUser.get(m.getMemberID())) + .filter(u -> u.getUserName().equals(s)) + .findFirst() + .orElse(null), + (c, s) -> { + if (!(c instanceof Player)) { + return null; + } + Player p = (Player) c; + return BauweltMember.getMembers(SteamwarUser.get(p.getUniqueId()).getId()) + .stream() + .map(m -> SteamwarUser.get(m.getMemberID()).getUserName()) + .collect(Collectors.toList()); + }); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/util/NightVisionCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/util/NightVisionCommand.java index 61084a03..78ca6ee7 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/util/NightVisionCommand.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/util/NightVisionCommand.java @@ -31,7 +31,7 @@ import org.bukkit.potion.PotionEffectType; @Linked(LinkageType.COMMAND) public class NightVisionCommand extends SWCommand { - protected NightVisionCommand() { + public NightVisionCommand() { super("nightvision", "nv", "light"); } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/AFKStopperListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/AFKStopperListener.java new file mode 100644 index 00000000..e0994afe --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/AFKStopperListener.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.world; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +@Linked(LinkageType.LISTENER) +public class AFKStopperListener implements Listener { + + private static final String afkWarning = BauSystem.PREFIX + "§cDieser Server wird bei weiterer Inaktivität in einer Minute gestoppt"; + + private int minutesAfk; + + public AFKStopperListener() { + minutesAfk = 0; + Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance()); + Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { + switch (minutesAfk) { + case 5: + for (Player p : Bukkit.getOnlinePlayers()) + p.kickPlayer("§cAuf diesem Server ist seit 5 Minuten nichts passiert."); + break; + case 4: + Bukkit.broadcastMessage(afkWarning); + default: + minutesAfk++; + } + }, 1200, 1200); //every minute + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + minutesAfk = 0; + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/AutoShutdownListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/AutoShutdownListener.java new file mode 100644 index 00000000..8e9a2602 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/AutoShutdownListener.java @@ -0,0 +1,90 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.world; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.features.tpslimit.TPSLimitUtils; +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import de.steamwar.core.Core; +import de.steamwar.scoreboard.SWScoreboard; +import org.bukkit.Bukkit; +import org.bukkit.GameRule; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.logging.Level; + +@Linked(LinkageType.LISTENER) +public class AutoShutdownListener implements Listener { + + private BukkitTask autoShutdown; + + @EventHandler + public void onJoin(PlayerLoginEvent e) { + if (autoShutdown != null) { + autoShutdown.cancel(); + autoShutdown = null; + } + + Player p = e.getPlayer(); + p.setOp(true); + + if (Core.getVersion() == 15) { + Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, false); + } + } + + @EventHandler + public void onLeave(PlayerQuitEvent e) { + Player p = e.getPlayer(); + SWScoreboard.removeScoreboard(p); + if (Bukkit.getOnlinePlayers().isEmpty() || (Bukkit.getOnlinePlayers().size() == 1 && Bukkit.getOnlinePlayers().contains(p))) { + if (autoShutdown != null) { + autoShutdown.cancel(); + } + TPSLimitUtils.setTPS(20.0); + autoShutdown = Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), new Runnable() { + int count = 0; + + @Override + public void run() { + if (count >= 300) { + Bukkit.shutdown(); + return; + } + count++; + try { + if (RamUsage.getUsage() > 0.8) { + Bukkit.shutdown(); + } + } catch (Throwable throwable) { + Bukkit.getLogger().log(Level.WARNING, throwable.getMessage(), throwable); + Bukkit.shutdown(); + } + } + }, 20, 20); + } + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/ClipboardListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/ClipboardListener.java new file mode 100644 index 00000000..64b10d8d --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/ClipboardListener.java @@ -0,0 +1,69 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.world; + +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import de.steamwar.sql.Schematic; +import de.steamwar.sql.SchematicType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.UUID; + +@Linked(LinkageType.LISTENER) +public class ClipboardListener implements Listener { + + private static final String CLIPBOARD_SCHEMNAME = "//copy"; + + @EventHandler + public void onLogin(PlayerJoinEvent e) { + try { + Schematic schematic = Schematic.getSchemFromDB(CLIPBOARD_SCHEMNAME, e.getPlayer().getUniqueId()); + if (schematic != null) { + schematic.loadToPlayer(e.getPlayer()); + } + } catch (Exception ex) { + // ignore cause players do all kind of stuff with schematics.... like massively oversized schems + } + } + + @EventHandler + public void onLogout(PlayerQuitEvent e) { + UUID playerUUID = e.getPlayer().getUniqueId(); + Schematic schematic = Schematic.getSchemFromDB(CLIPBOARD_SCHEMNAME, playerUUID); + boolean newSchem = false; + if (schematic == null) { + Schematic.createSchem(CLIPBOARD_SCHEMNAME, playerUUID, "", SchematicType.Normal); + schematic = Schematic.getSchemFromDB(CLIPBOARD_SCHEMNAME, playerUUID); + newSchem = true; + } + + try { + schematic.saveFromPlayer(e.getPlayer()); + } catch (Exception ex) { + if (newSchem) { + schematic.remove(); + } + } + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java new file mode 100644 index 00000000..a4e2490d --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java @@ -0,0 +1,71 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.world; + +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import de.steamwar.core.Core; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +@Linked(LinkageType.LISTENER) +public class InventoryListener implements Listener { + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + ItemStack stack = e.getCursor(); + if (stack == null || !stack.hasItemMeta()) { + return; + } + assert stack.getItemMeta() != null; + if (stack.getItemMeta().hasEnchants()) { + for (Enchantment en : Enchantment.values()) { + if (stack.getEnchantmentLevel(en) > en.getMaxLevel()) { + stack.removeEnchantment(en); + } + } + } + + Material material = stack.getType(); + if (material == Material.POTION || material == Material.SPLASH_POTION || material == Material.LINGERING_POTION) { + stack.setType(Material.MILK_BUCKET); + } + + if (Core.getVersion() < 15) { + e.setCurrentItem(stack); + return; + } + + if (stack.getItemMeta().hasAttributeModifiers()) { + ItemMeta meta = stack.getItemMeta(); + for (Attribute a : Attribute.values()) { + meta.removeAttributeModifier(a); + } + stack.setItemMeta(meta); + } + e.setCurrentItem(stack); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/ItemFrameListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/ItemFrameListener.java new file mode 100644 index 00000000..3858b4cf --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/ItemFrameListener.java @@ -0,0 +1,54 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.world; + +import de.steamwar.bausystem.SWUtils; +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import org.bukkit.Material; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; + +@Linked(LinkageType.LISTENER) +public class ItemFrameListener implements Listener { + + @EventHandler + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (!(event.getDamager() instanceof Player)) { + return; + } + if (!(event.getEntity() instanceof ItemFrame)) { + return; + } + event.setCancelled(true); + ItemFrame itemFrame = (ItemFrame) event.getEntity(); + ItemStack itemStack = itemFrame.getItem(); + if (itemStack.getType() != Material.AIR) { + SWUtils.giveItemToPlayer((Player) event.getDamager(), itemFrame.getItem()); + itemFrame.setItem(null); + } else { + itemFrame.remove(); + } + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/RamUsage.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/RamUsage.java new file mode 100644 index 00000000..35f8c81a --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/RamUsage.java @@ -0,0 +1,42 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.world; + +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; + +import java.lang.management.ManagementFactory; +import java.util.logging.Level; + +@UtilityClass +public class RamUsage { + + public double getUsage() { + try { + long memorySize = ((com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean()).getTotalPhysicalMemorySize(); + long freeMemory = ((com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean()).getFreePhysicalMemorySize(); + return (memorySize - freeMemory) / (double) memorySize; + } catch (Throwable throwable) { + Bukkit.getLogger().log(Level.WARNING, throwable.getMessage(), throwable); + return 1D; + } + } + +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/linkage/LinkageUtils.java b/BauSystem_Main/src/de/steamwar/bausystem/linkage/LinkageUtils.java index 56b8be54..33969266 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/linkage/LinkageUtils.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/linkage/LinkageUtils.java @@ -59,14 +59,14 @@ public class LinkageUtils { Set linkageTypeSet = new HashSet<>(); for (Linked linked : linkages) { if (linked == null) { - return; + continue; } LinkageType linkageType = linked.value(); if (linkageType.getLinkagePredicate().test(clazz)) { linkageTypeSet.add(linked.value()); } } - + if (linkageTypeSet.isEmpty()) { return; } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/linkage/Linked.java b/BauSystem_Main/src/de/steamwar/bausystem/linkage/Linked.java index 430e589c..2096d1ac 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/linkage/Linked.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/linkage/Linked.java @@ -24,13 +24,13 @@ import org.atteo.classindex.IndexAnnotated; import java.lang.annotation.*; @IndexAnnotated -@Retention(RetentionPolicy.CLASS) +@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Repeatable(Linked.Linkages.class) public @interface Linked { LinkageType value(); - @Retention(RetentionPolicy.CLASS) + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @interface Linkages { @SuppressWarnings("unused") Linked[] value() default {}; diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/.gitkeep b/BauSystem_Main/src/de/steamwar/bausystem/utils/.gitkeep new file mode 100644 index 00000000..e69de29b