develop-chatReply #5
| @@ -26,6 +26,7 @@ dependencies { | |||||||
|     compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT' |     compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT' | ||||||
|     implementation 'org.apache.httpcomponents:httpclient:4.5.14' |     implementation 'org.apache.httpcomponents:httpclient:4.5.14' | ||||||
|     implementation 'com.sparkjava:spark-core:2.9.4' |     implementation 'com.sparkjava:spark-core:2.9.4' | ||||||
|  |     implementation 'org.reflections:reflections:0.10.2' | ||||||
| } | } | ||||||
|  |  | ||||||
| def targetJavaVersion = 21 | def targetJavaVersion = 21 | ||||||
|   | |||||||
| @@ -2,39 +2,16 @@ package eu.mhsl.craftattack.spawn; | |||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.api.HttpServer; | import eu.mhsl.craftattack.spawn.api.HttpServer; | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.antiSignEdit.AntiSignEdit; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.autoShulker.AutoShulker; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.chatMention.ChatMention; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.customAdvancements.CustomAdvancements; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.debug.Debug; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.event.Event; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.fleischerchest.Fleischerchest; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.help.Help; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.hotbarRefill.HotbarRefill; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.kick.Kick; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.panicBan.PanicBan; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.portableCrafting.PortableCrafting; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.privateMessage.PrivateMessage; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.projectStart.ProjectStart; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.report.Report; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.restart.Restart; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.tablist.Tablist; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.titleClear.TitleClear; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.whitelist.Whitelist; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.worldmuseum.WorldMuseum; |  | ||||||
| import eu.mhsl.craftattack.spawn.config.Configuration; | import eu.mhsl.craftattack.spawn.config.Configuration; | ||||||
| import org.bukkit.Bukkit; | import org.bukkit.Bukkit; | ||||||
| import org.bukkit.event.HandlerList; | import org.bukkit.event.HandlerList; | ||||||
| import org.bukkit.plugin.java.JavaPlugin; | import org.bukkit.plugin.java.JavaPlugin; | ||||||
|  | import org.reflections.Reflections; | ||||||
|  |  | ||||||
| import java.lang.reflect.ParameterizedType; | import java.lang.reflect.ParameterizedType; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.logging.Level; | ||||||
| import java.util.logging.Logger; | import java.util.logging.Logger; | ||||||
|  |  | ||||||
| public final class Main extends JavaPlugin { | public final class Main extends JavaPlugin { | ||||||
| @@ -49,49 +26,46 @@ public final class Main extends JavaPlugin { | |||||||
|         instance = this; |         instance = this; | ||||||
|         logger = instance().getLogger(); |         logger = instance().getLogger(); | ||||||
|         saveDefaultConfig(); |         saveDefaultConfig(); | ||||||
|         Configuration.readConfig(); |         try { | ||||||
|  |             this.wrappedEnable(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             Main.logger().log(Level.SEVERE, "Error while initializing Spawn plugin, shutting down!", e); | ||||||
|  |             Bukkit.shutdown(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|         appliances = List.of( |     private void wrappedEnable() { | ||||||
|             new AdminMarker(), |         Configuration.readConfig(); | ||||||
|             new WorldMuseum(), |         List<String> disabledAppliances = Configuration.pluginConfig.getStringList("disabledAppliances"); | ||||||
|             new TitleClear(), |  | ||||||
|             new ProjectStart(), |  | ||||||
|             new Tablist(), |  | ||||||
|             new ChatMessages(), |  | ||||||
|             new Report(), |  | ||||||
|             new Event(), |  | ||||||
|             new Help(), |  | ||||||
|             new PlayerLimit(), |  | ||||||
|             new Whitelist(), |  | ||||||
|             new Restart(), |  | ||||||
|             new Kick(), |  | ||||||
|             new PanicBan(), |  | ||||||
|             new Outlawed(), |  | ||||||
|             new DisplayName(), |  | ||||||
|             new Debug(), |  | ||||||
|             new Fleischerchest(), |  | ||||||
|             new CustomAdvancements(), |  | ||||||
|             new Settings(), |  | ||||||
|             new PortableCrafting(), |  | ||||||
|             new AutoShulker(), |  | ||||||
|             new AntiSignEdit(), |  | ||||||
|             new HotbarRefill(), |  | ||||||
|             new ChatMention(), |  | ||||||
|             new PrivateMessage() |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         Main.logger.info("Loading appliances..."); |         Main.logger.info("Loading appliances..."); | ||||||
|         appliances.forEach(appliance -> { |         Reflections reflections = new Reflections(this.getClass().getPackageName()); | ||||||
|             Main.logger().info("Enabling " + appliance.getClass().getSimpleName()); |         Set<Class<? extends Appliance>> applianceClasses = reflections.getSubTypesOf(Appliance.class); | ||||||
|  |  | ||||||
|  |         this.appliances = applianceClasses.stream() | ||||||
|  |             .filter(applianceClass -> !disabledAppliances.contains(applianceClass.getSimpleName())) | ||||||
|  |             .map(applianceClass -> { | ||||||
|  |                 try { | ||||||
|  |                     return (Appliance) applianceClass.getDeclaredConstructor().newInstance(); | ||||||
|  |                 } catch (Exception e) { | ||||||
|  |                     throw new RuntimeException(String.format("Failed to create instance of '%s'", applianceClass.getName()), e); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             .toList(); | ||||||
|  |         Main.logger().info(String.format("Loaded %d appliances!", appliances.size())); | ||||||
|  |  | ||||||
|  |         Main.logger().info("Initializing appliances..."); | ||||||
|  |         this.appliances.forEach(appliance -> { | ||||||
|             appliance.onEnable(); |             appliance.onEnable(); | ||||||
|             appliance.initialize(this); |             appliance.initialize(this); | ||||||
|         }); |         }); | ||||||
|         Main.logger().info("Loaded " + appliances.size() + " appliances!"); |         Main.logger().info(String.format("Initialized %d appliances!", appliances.size())); | ||||||
|  |  | ||||||
|         Main.logger().info("Starting HTTP API"); |         Main.logger().info("Starting HTTP API..."); | ||||||
|         this.httpApi = new HttpServer(); |         this.httpApi = new HttpServer(); | ||||||
|  |  | ||||||
|         getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); |         getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); | ||||||
|  |         Main.logger().info("Startup complete!"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ public abstract class Appliance { | |||||||
|      * @return List of listeners |      * @return List of listeners | ||||||
|      */ |      */ | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return new ArrayList<>(); |         return new ArrayList<>(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -72,7 +72,8 @@ public abstract class Appliance { | |||||||
|      */ |      */ | ||||||
|     @NotNull |     @NotNull | ||||||
|     public ConfigurationSection localConfig() { |     public ConfigurationSection localConfig() { | ||||||
|         return Optional.ofNullable(Configuration.cfg.getConfigurationSection(localConfigPath)).orElse(Configuration.cfg); |         return Optional.ofNullable(Configuration.cfg.getConfigurationSection(localConfigPath)) | ||||||
|  |             .orElseGet(() -> Configuration.cfg.createSection(localConfigPath)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void onEnable() { |     public void onEnable() { | ||||||
| @@ -82,7 +83,7 @@ public abstract class Appliance { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void initialize(@NotNull JavaPlugin plugin) { |     public void initialize(@NotNull JavaPlugin plugin) { | ||||||
|         this.listeners = eventHandlers(); |         this.listeners = listeners(); | ||||||
|         this.commands = commands(); |         this.commands = commands(); | ||||||
|  |  | ||||||
|         listeners.forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, plugin)); |         listeners.forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, plugin)); | ||||||
| @@ -93,7 +94,7 @@ public abstract class Appliance { | |||||||
|         listeners.forEach(HandlerList::unregisterAll); |         listeners.forEach(HandlerList::unregisterAll); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public <T extends Appliance> T queryAppliance(Class<T> clazz) { |     protected static <T extends Appliance> T queryAppliance(Class<T> clazz) { | ||||||
|         return Main.instance().getAppliance(clazz); |         return Main.instance().getAppliance(clazz); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ import java.util.Optional; | |||||||
| /** | /** | ||||||
|  * Utility class which enables command name definition over a constructor. |  * Utility class which enables command name definition over a constructor. | ||||||
|  */ |  */ | ||||||
| public abstract class ApplianceCommand<T extends Appliance> extends ApplianceSupplier<T> implements TabCompleter, CommandExecutor { | public abstract class ApplianceCommand<T extends Appliance> extends CachedApplianceSupplier<T> implements TabCompleter, CommandExecutor { | ||||||
|     public String commandName; |     public String commandName; | ||||||
|     protected Component errorMessage = Component.text("Fehler: ").color(NamedTextColor.RED); |     protected Component errorMessage = Component.text("Fehler: ").color(NamedTextColor.RED); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,6 +8,6 @@ import org.bukkit.event.Listener; | |||||||
|  * |  * | ||||||
|  * @param <T> the type of your appliance |  * @param <T> the type of your appliance | ||||||
|  */ |  */ | ||||||
| public abstract class ApplianceListener<T extends Appliance> extends ApplianceSupplier<T> implements Listener { | public abstract class ApplianceListener<T extends Appliance> extends CachedApplianceSupplier<T> implements Listener { | ||||||
|  |  | ||||||
| } | } | ||||||
| @@ -2,10 +2,10 @@ package eu.mhsl.craftattack.spawn.appliance; | |||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.Main; | import eu.mhsl.craftattack.spawn.Main; | ||||||
| 
 | 
 | ||||||
| public class ApplianceSupplier<T extends Appliance> implements IApplianceSupplier<T> { | public class CachedApplianceSupplier<T extends Appliance> implements IApplianceSupplier<T> { | ||||||
|     private final T appliance; |     private final T appliance; | ||||||
| 
 | 
 | ||||||
|     public ApplianceSupplier() { |     public CachedApplianceSupplier() { | ||||||
|         this.appliance = Main.instance().getAppliance(Main.getApplianceType(getClass())); |         this.appliance = Main.instance().getAppliance(Main.getApplianceType(getClass())); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -18,7 +18,7 @@ public class AdminMarker extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new AdminMarkerListener()); |         return List.of(new AdminMarkerListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ package eu.mhsl.craftattack.spawn.appliances.antiSignEdit; | |||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.settings.SignEditSetting; |  | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
| import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; | import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; | ||||||
| @@ -15,6 +14,11 @@ import org.jetbrains.annotations.NotNull; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class AntiSignEdit extends Appliance { | public class AntiSignEdit extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(SignEditSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public boolean preventSignEdit(Player p, SignSide sign) { |     public boolean preventSignEdit(Player p, SignSide sign) { | ||||||
|         SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.SignEdit, SelectSetting.Options.Option.class); |         SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.SignEdit, SelectSetting.Options.Option.class); | ||||||
|         if(setting.is(SignEditSetting.editable)) return false; |         if(setting.is(SignEditSetting.editable)) return false; | ||||||
| @@ -37,7 +41,7 @@ public class AntiSignEdit extends Appliance { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected @NotNull List<Listener> eventHandlers() { |     protected @NotNull List<Listener> listeners() { | ||||||
|         return List.of(new OnSignEditListener()); |         return List.of(new OnSignEditListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.antiSignEdit; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.autoShulker; | package eu.mhsl.craftattack.spawn.appliances.autoShulker; | ||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
| import org.bukkit.Material; | import org.bukkit.Material; | ||||||
| @@ -16,6 +17,11 @@ import java.util.HashMap; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class AutoShulker extends Appliance { | public class AutoShulker extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(AutoShulkerSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public boolean tryAutoShulker(Player p, Item item) { |     public boolean tryAutoShulker(Player p, Item item) { | ||||||
|         ItemStack itemStack = item.getItemStack(); |         ItemStack itemStack = item.getItemStack(); | ||||||
|         ItemStack offhandStack = p.getInventory().getItemInOffHand(); |         ItemStack offhandStack = p.getInventory().getItemInOffHand(); | ||||||
| @@ -40,7 +46,7 @@ public class AutoShulker extends Appliance { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected @NotNull List<Listener> eventHandlers() { |     protected @NotNull List<Listener> listeners() { | ||||||
|         return List.of(new ItemPickupListener()); |         return List.of(new ItemPickupListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.autoShulker; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -3,7 +3,6 @@ package eu.mhsl.craftattack.spawn.appliances.autoShulker; | |||||||
| import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.settings.AutoShulkerSetting; |  | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.event.EventHandler; | import org.bukkit.event.EventHandler; | ||||||
| import org.bukkit.event.entity.EntityPickupItemEvent; | import org.bukkit.event.entity.EntityPickupItemEvent; | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ package eu.mhsl.craftattack.spawn.appliances.chatMention; | |||||||
| import eu.mhsl.craftattack.spawn.Main; | import eu.mhsl.craftattack.spawn.Main; | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.settings.ChatMentionSetting; |  | ||||||
| import net.kyori.adventure.sound.Sound; | import net.kyori.adventure.sound.Sound; | ||||||
| import org.bukkit.Bukkit; | import org.bukkit.Bukkit; | ||||||
| import org.bukkit.OfflinePlayer; | import org.bukkit.OfflinePlayer; | ||||||
| @@ -56,11 +55,12 @@ public class ChatMention extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onEnable() { |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(ChatMentionSetting.class); | ||||||
|         refreshPlayers(); |         refreshPlayers(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected @NotNull List<Listener> eventHandlers() { |     protected @NotNull List<Listener> listeners() { | ||||||
|         return List.of(new ChatMentionListener()); |         return List.of(new ChatMentionListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import eu.mhsl.craftattack.spawn.Main; | |||||||
| import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages; | import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.settings.ChatMentionSetting; | import eu.mhsl.craftattack.spawn.util.text.ComponentUtil; | ||||||
| import io.papermc.paper.event.player.AsyncChatDecorateEvent; | import io.papermc.paper.event.player.AsyncChatDecorateEvent; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
| @@ -42,7 +42,7 @@ public class ChatMentionListener extends ApplianceListener<ChatMention> { | |||||||
|                     return Component.text(word); |                     return Component.text(word); | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|             .reduce((a, b) -> a.append(Component.text(" ")).append(b)) |             .reduce(ComponentUtil::appendWithSpace) | ||||||
|             .orElseThrow(); |             .orElseThrow(); | ||||||
|  |  | ||||||
|         getAppliance().notifyPlayers(mentioned); |         getAppliance().notifyPlayers(mentioned); | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.chatMention; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -9,7 +9,7 @@ import org.bukkit.Material; | |||||||
| public class ChatMentionSetting extends MultiBoolSetting<ChatMentionSetting.ChatMentionConfig> implements CategorizedSetting { | public class ChatMentionSetting extends MultiBoolSetting<ChatMentionSetting.ChatMentionConfig> implements CategorizedSetting { | ||||||
|     @Override |     @Override | ||||||
|     public SettingCategory category() { |     public SettingCategory category() { | ||||||
|         return SettingCategory.Chat; |         return SettingCategory.Visuals; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public record ChatMentionConfig( |     public record ChatMentionConfig( | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.chatMessages; | package eu.mhsl.craftattack.spawn.appliances.chatMessages; | ||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.event.ClickEvent; | import net.kyori.adventure.text.event.ClickEvent; | ||||||
| import net.kyori.adventure.text.event.HoverEvent; | import net.kyori.adventure.text.event.HoverEvent; | ||||||
| @@ -12,6 +13,11 @@ import org.jetbrains.annotations.NotNull; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class ChatMessages extends Appliance { | public class ChatMessages extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(ShowJoinAndLeaveMessagesSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public Component getReportablePlayerName(Player player) { |     public Component getReportablePlayerName(Player player) { | ||||||
|         return addReportActions(player.displayName(), player.getName()); |         return addReportActions(player.displayName(), player.getName()); | ||||||
|     } |     } | ||||||
| @@ -24,7 +30,7 @@ public class ChatMessages extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new ChatMessagesListener()); |         return List.of(new ChatMessagesListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.chatMessages; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -33,6 +33,6 @@ public class ShowJoinAndLeaveMessagesSetting extends BoolSetting implements Cate | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public SettingCategory category() { |     public SettingCategory category() { | ||||||
|         return SettingCategory.Chat; |         return SettingCategory.Visuals; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -17,7 +17,7 @@ public class CustomAdvancements extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new CustomAdvancementsDamageEntityListener()); |         return List.of(new CustomAdvancementsDamageEntityListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.customAdvancements; | package eu.mhsl.craftattack.spawn.appliances.customAdvancements; | ||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
| import org.bukkit.entity.Entity; | import org.bukkit.Material; | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.event.EventHandler; | import org.bukkit.event.EventHandler; | ||||||
| import org.bukkit.event.entity.EntityDamageByEntityEvent; | import org.bukkit.event.entity.EntityDamageByEntityEvent; | ||||||
| @@ -9,12 +9,11 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; | |||||||
| public class CustomAdvancementsDamageEntityListener extends ApplianceListener<CustomAdvancements> { | public class CustomAdvancementsDamageEntityListener extends ApplianceListener<CustomAdvancements> { | ||||||
|     @EventHandler |     @EventHandler | ||||||
|     public void onEntityDamageEntity(EntityDamageByEntityEvent event) { |     public void onEntityDamageEntity(EntityDamageByEntityEvent event) { | ||||||
|         Entity damaged = event.getEntity(); |         if(!(event.getEntity() instanceof Player damaged)) return; | ||||||
|         if(!(damaged instanceof Player)) return; |         if(!(event.getDamager() instanceof Player damager)) return; | ||||||
|         Entity damager = event.getDamager(); |         if(!damager.getInventory().getItemInMainHand().getType().equals(Material.AIR)) return; | ||||||
|         if(!(damager instanceof Player)) return; |         if(!damaged.hasPermission("admin")) return; | ||||||
|         if(damaged.hasPermission("admin")) { |  | ||||||
|             getAppliance().grantAdvancement("search_trouble", (Player) damager); |         getAppliance().grantAdvancement("search_trouble", damager); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,11 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.displayName; | package eu.mhsl.craftattack.spawn.appliances.displayName; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; | import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarkerListener; | import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarkerListener; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; | import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.yearRank.YearRank; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.ComponentBuilder; | import net.kyori.adventure.text.ComponentBuilder; | ||||||
| import net.kyori.adventure.text.TextComponent; | import net.kyori.adventure.text.TextComponent; | ||||||
| @@ -14,19 +16,22 @@ import org.jetbrains.annotations.NotNull; | |||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.function.Supplier; | import java.util.function.Supplier; | ||||||
|  | import java.util.logging.Level; | ||||||
|  |  | ||||||
| public class DisplayName extends Appliance { | public class DisplayName extends Appliance { | ||||||
|     public void update(Player player) { |     public void update(Player player) { | ||||||
|         TextColor playerColor = queryAppliance(AdminMarker.class).getPlayerColor(player); |         TextColor playerColor = queryAppliance(AdminMarker.class).getPlayerColor(player); | ||||||
|         List<Supplier<Component>> prefixes = List.of( |         List<Supplier<Component>> prefixes = List.of( | ||||||
|             () -> queryAppliance(Outlawed.class).getNamePrefix(player) |             () -> queryAppliance(Outlawed.class).getNamePrefix(player), | ||||||
|  |             () -> queryAppliance(YearRank.class).getNamePrefix(player) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text(); |         ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text(); | ||||||
|         prefixes.forEach(supplier -> { |         prefixes.forEach(supplier -> { | ||||||
|             Component prefix = supplier.get(); |             Component prefix = supplier.get(); | ||||||
|             if(prefix == null) return; |             if(prefix == null) return; | ||||||
|             playerName.append(prefix).append(Component.text(" ")); |             playerName.append(prefix).append( | ||||||
|  |                 Component.text(" ").hoverEvent(Component.empty().asHoverEvent())); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         playerName.append(Component.text(player.getName(), playerColor)); |         playerName.append(Component.text(player.getName(), playerColor)); | ||||||
| @@ -41,14 +46,14 @@ public class DisplayName extends Appliance { | |||||||
|             player.playerListName(component); |             player.playerListName(component); | ||||||
|         } catch(Exception e) { |         } catch(Exception e) { | ||||||
|             //TODO this throws often exceptions, but still works, don't know why |             //TODO this throws often exceptions, but still works, don't know why | ||||||
|             //Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage); |             Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new AdminMarkerListener()); |         return List.of(new AdminMarkerListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,50 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.doubeDoor; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
|  | import org.bukkit.block.Block; | ||||||
|  | import org.bukkit.block.BlockFace; | ||||||
|  | import org.bukkit.block.data.BlockData; | ||||||
|  | import org.bukkit.block.data.type.Door; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class DoubleDoor extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(DoubleDoorSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void openNextDoor(Block block) { | ||||||
|  |         BlockData clickedData = block.getBlockData(); | ||||||
|  |         if (!(clickedData instanceof Door clickedDoor)) return; | ||||||
|  |  | ||||||
|  |         BlockFace neighborFace = getNeighborFace(clickedDoor.getFacing(), clickedDoor.getHinge()); | ||||||
|  |         Block neighbourBlock = block.getRelative(neighborFace); | ||||||
|  |         BlockData neighbourData = neighbourBlock.getBlockData(); | ||||||
|  |  | ||||||
|  |         if(!(neighbourData instanceof Door neighbourDoor)) return; | ||||||
|  |         if(!(neighbourDoor.getFacing() == clickedDoor.getFacing())) return; | ||||||
|  |         if(neighbourDoor.getHinge() == clickedDoor.getHinge()) return; | ||||||
|  |  | ||||||
|  |         neighbourDoor.setOpen(!clickedDoor.isOpen()); | ||||||
|  |         neighbourBlock.setBlockData(neighbourDoor); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private @NotNull BlockFace getNeighborFace(BlockFace face, Door.Hinge hinge) { | ||||||
|  |         return switch(face) { | ||||||
|  |             case EAST -> (hinge == Door.Hinge.RIGHT) ? BlockFace.NORTH : BlockFace.SOUTH; | ||||||
|  |             case WEST -> (hinge == Door.Hinge.RIGHT) ? BlockFace.SOUTH : BlockFace.NORTH; | ||||||
|  |             case SOUTH -> (hinge == Door.Hinge.RIGHT) ? BlockFace.EAST : BlockFace.WEST; | ||||||
|  |             case NORTH -> (hinge == Door.Hinge.RIGHT) ? BlockFace.WEST : BlockFace.EAST; | ||||||
|  |             default -> throw new IllegalStateException(String.format("BlockFace '%s' of clicked door is not valid!", face)); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<Listener> listeners() { | ||||||
|  |         return List.of(new OnDoorInteractListener()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.doubeDoor; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting; | ||||||
|  | import org.bukkit.Material; | ||||||
|  |  | ||||||
|  | public class DoubleDoorSetting extends BoolSetting implements CategorizedSetting { | ||||||
|  |     public DoubleDoorSetting() { | ||||||
|  |         super(Settings.Key.DoubleDoors); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected String title() { | ||||||
|  |         return "Automatische Doppeltüren"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected String description() { | ||||||
|  |         return "Öffnet und schließt die zweite Hälfte einer Doppeltür automatisch"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Material icon() { | ||||||
|  |         return Material.OAK_DOOR; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Boolean defaultValue() { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public SettingCategory category() { | ||||||
|  |         return SettingCategory.Gameplay; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.doubeDoor; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.block.Block; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.block.Action; | ||||||
|  | import org.bukkit.event.player.PlayerInteractEvent; | ||||||
|  | import org.bukkit.inventory.EquipmentSlot; | ||||||
|  |  | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | public class OnDoorInteractListener extends ApplianceListener<DoubleDoor> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onPlayerInteract(PlayerInteractEvent event) { | ||||||
|  |         if(!event.hasBlock()) return; | ||||||
|  |         if(!Objects.equals(event.getHand(), EquipmentSlot.HAND)) return; | ||||||
|  |         if(!event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) return; | ||||||
|  |         if(event.getPlayer().isSneaking()) return; | ||||||
|  |         Block clickedBlock = event.getClickedBlock(); | ||||||
|  |         if(clickedBlock == null) return; | ||||||
|  |         if(clickedBlock.getType().equals(Material.IRON_DOOR)) return; | ||||||
|  |         if(!Settings.instance().getSetting(event.getPlayer(), Settings.Key.DoubleDoors, Boolean.class)) return; | ||||||
|  |         getAppliance().openNextDoor(clickedBlock); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -201,17 +201,15 @@ public class Event extends Appliance { | |||||||
|     public void advertise() { |     public void advertise() { | ||||||
|         advertiseCountdown.cancelIfRunning(); |         advertiseCountdown.cancelIfRunning(); | ||||||
|         this.advertiseStatus = AdvertisementStatus.ADVERTISED; |         this.advertiseStatus = AdvertisementStatus.ADVERTISED; | ||||||
|         IteratorUtil.onlinePlayers(player -> { |         IteratorUtil.onlinePlayers(player -> player.sendMessage( | ||||||
|             player.sendMessage( |             Component.text() | ||||||
|                 Component.text() |                 .append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline() | ||||||
|                     .append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline() |                 .append(Component.text("Ein Event wurde gestartet!", NamedTextColor.GOLD)).appendNewline() | ||||||
|                     .append(Component.text("Ein Event wurde gestartet!", NamedTextColor.GOLD)).appendNewline() |                 .append(Component.text("Nutze ")) | ||||||
|                     .append(Component.text("Nutze ")) |                 .append(Component.text("/event", NamedTextColor.AQUA)) | ||||||
|                     .append(Component.text("/event", NamedTextColor.AQUA)) |                 .append(Component.text(", um dem Event beizutreten!")).appendNewline() | ||||||
|                     .append(Component.text(", um dem Event beizutreten!")).appendNewline() |                 .append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline() | ||||||
|                     .append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline() |         )); | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         advertiseCountdown.start(); |         advertiseCountdown.start(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -237,7 +235,7 @@ public class Event extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new ApplyPendingRewardsListener(), |             new ApplyPendingRewardsListener(), | ||||||
|             new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> joinEvent(playerInteractAtEntityEvent.getPlayer())), |             new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> joinEvent(playerInteractAtEntityEvent.getPlayer())), | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ public class Fleischerchest extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new FleischerchestCraftItemListener()); |         return List.of(new FleischerchestCraftItemListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.glowingBerries; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import net.kyori.adventure.sound.Sound; | ||||||
|  | import net.kyori.adventure.util.Ticks; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.bukkit.potion.PotionEffect; | ||||||
|  | import org.bukkit.potion.PotionEffectType; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class GlowingBerries extends Appliance { | ||||||
|  |     private static final PotionEffect glowEffect = new PotionEffect( | ||||||
|  |         PotionEffectType.GLOWING, | ||||||
|  |         Ticks.TICKS_PER_SECOND * 15, | ||||||
|  |         1, | ||||||
|  |         false, | ||||||
|  |         true, | ||||||
|  |         true | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     public void letPlayerGlow(Player player) { | ||||||
|  |         player.addPotionEffect(glowEffect); | ||||||
|  |         Sound sound = Sound.sound(org.bukkit.Sound.BLOCK_AMETHYST_BLOCK_CHIME.key(), Sound.Source.PLAYER, 1f, 1f); | ||||||
|  |         player.stopSound(sound); | ||||||
|  |         player.playSound(sound, Sound.Emitter.self()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<Listener> listeners() { | ||||||
|  |         return List.of(new OnBerryEaten()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.glowingBerries; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.player.PlayerItemConsumeEvent; | ||||||
|  |  | ||||||
|  | public class OnBerryEaten extends ApplianceListener<GlowingBerries> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onEat(PlayerItemConsumeEvent event) { | ||||||
|  |         if(event.getItem().getType().equals(Material.GLOW_BERRIES)) getAppliance().letPlayerGlow(event.getPlayer()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -2,6 +2,7 @@ package eu.mhsl.craftattack.spawn.appliances.hotbarRefill; | |||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.Main; | import eu.mhsl.craftattack.spawn.Main; | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
| import org.bukkit.Bukkit; | import org.bukkit.Bukkit; | ||||||
| @@ -16,6 +17,11 @@ import java.util.List; | |||||||
| import java.util.NoSuchElementException; | import java.util.NoSuchElementException; | ||||||
|  |  | ||||||
| public class HotbarRefill extends Appliance { | public class HotbarRefill extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(HotbarRefillSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void handleHotbarChange(Player player, ItemStack item) { |     public void handleHotbarChange(Player player, ItemStack item) { | ||||||
|         if(player.getGameMode().equals(GameMode.CREATIVE)) return; |         if(player.getGameMode().equals(GameMode.CREATIVE)) return; | ||||||
|         if(item.getAmount() != 1) return; |         if(item.getAmount() != 1) return; | ||||||
| @@ -46,7 +52,7 @@ public class HotbarRefill extends Appliance { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected @NotNull List<Listener> eventHandlers() { |     protected @NotNull List<Listener> listeners() { | ||||||
|         return List.of(new ItemRefillListener()); |         return List.of(new HotbarRefillListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ package eu.mhsl.craftattack.spawn.appliances.hotbarRefill; | |||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.settings.HotbarReplaceSetting; |  | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.event.EventHandler; | import org.bukkit.event.EventHandler; | ||||||
| import org.bukkit.event.block.BlockPlaceEvent; | import org.bukkit.event.block.BlockPlaceEvent; | ||||||
| @@ -10,12 +9,12 @@ import org.bukkit.event.player.PlayerItemBreakEvent; | |||||||
| import org.bukkit.event.player.PlayerItemConsumeEvent; | import org.bukkit.event.player.PlayerItemConsumeEvent; | ||||||
| import org.bukkit.inventory.ItemStack; | import org.bukkit.inventory.ItemStack; | ||||||
| 
 | 
 | ||||||
| public class ItemRefillListener extends ApplianceListener<HotbarRefill> { | public class HotbarRefillListener extends ApplianceListener<HotbarRefill> { | ||||||
|     private HotbarReplaceSetting.HotbarReplaceConfig getPlayerSetting(Player player) { |     private HotbarRefillSetting.HotbarReplaceConfig getPlayerSetting(Player player) { | ||||||
|         return Settings.instance().getSetting( |         return Settings.instance().getSetting( | ||||||
|             player, |             player, | ||||||
|             Settings.Key.HotbarReplacer, |             Settings.Key.HotbarReplacer, | ||||||
|             HotbarReplaceSetting.HotbarReplaceConfig.class |             HotbarRefillSetting.HotbarReplaceConfig.class | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.hotbarRefill; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -6,7 +6,7 @@ import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | |||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.MultiBoolSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.MultiBoolSetting; | ||||||
| import org.bukkit.Material; | import org.bukkit.Material; | ||||||
| 
 | 
 | ||||||
| public class HotbarReplaceSetting extends MultiBoolSetting<HotbarReplaceSetting.HotbarReplaceConfig> implements CategorizedSetting { | public class HotbarRefillSetting extends MultiBoolSetting<HotbarRefillSetting.HotbarReplaceConfig> implements CategorizedSetting { | ||||||
|     @Override |     @Override | ||||||
|     public SettingCategory category() { |     public SettingCategory category() { | ||||||
|         return SettingCategory.Gameplay; |         return SettingCategory.Gameplay; | ||||||
| @@ -18,7 +18,7 @@ public class HotbarReplaceSetting extends MultiBoolSetting<HotbarReplaceSetting. | |||||||
|         @DisplayName("Essen") boolean onConsumable |         @DisplayName("Essen") boolean onConsumable | ||||||
|     ) {} |     ) {} | ||||||
| 
 | 
 | ||||||
|     public HotbarReplaceSetting() { |     public HotbarRefillSetting() { | ||||||
|         super(Settings.Key.HotbarReplacer); |         super(Settings.Key.HotbarReplacer); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -0,0 +1,65 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.knockDoor; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; | ||||||
|  | import org.bukkit.*; | ||||||
|  | import org.bukkit.block.Block; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.bukkit.metadata.FixedMetadataValue; | ||||||
|  | import org.bukkit.scheduler.BukkitRunnable; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class KnockDoor extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(KnockDoorSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void knockAtDoor(Player knockingPlayer, Block knockedBlock) { | ||||||
|  |         SelectSetting.Options.Option setting = Settings.instance().getSetting(knockingPlayer, Settings.Key.KnockDoors, SelectSetting.Options.Option.class); | ||||||
|  |         if(setting.is(KnockDoorSetting.disabled)) return; | ||||||
|  |  | ||||||
|  |         if(setting.is(KnockDoorSetting.knockSingleTime)) { | ||||||
|  |             playSound(knockedBlock); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(setting.is(KnockDoorSetting.knockThreeTimes)) { | ||||||
|  |             String metadataKey = new NamespacedKey(Main.instance(), KnockDoor.class.getName()).getNamespace(); | ||||||
|  |             if(knockingPlayer.hasMetadata(metadataKey)) return; | ||||||
|  |             knockingPlayer.setMetadata(metadataKey, new FixedMetadataValue(Main.instance(), 0)); | ||||||
|  |  | ||||||
|  |             new BukkitRunnable() { | ||||||
|  |                 @Override | ||||||
|  |                 public void run() { | ||||||
|  |                     int timesKnocked = knockingPlayer.getMetadata(metadataKey).getFirst().asInt(); | ||||||
|  |                     if(timesKnocked >= 3) { | ||||||
|  |                         knockingPlayer.removeMetadata(metadataKey, Main.instance()); | ||||||
|  |                         cancel(); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |                     playSound(knockedBlock); | ||||||
|  |                     knockingPlayer.setMetadata(metadataKey, new FixedMetadataValue(Main.instance(), timesKnocked + 1)); | ||||||
|  |                 } | ||||||
|  |             }.runTaskTimer(Main.instance(), 0, 8); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void playSound(Block knockedBlock) { | ||||||
|  |         Location knockLocation = knockedBlock.getLocation(); | ||||||
|  |         Sound sound = knockedBlock.getType() == Material.IRON_DOOR | ||||||
|  |             ? Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR | ||||||
|  |             : Sound.ITEM_SHIELD_BLOCK; | ||||||
|  |  | ||||||
|  |         knockLocation.getWorld().playSound(knockLocation, sound, SoundCategory.PLAYERS, 1f, 1f); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<Listener> listeners() { | ||||||
|  |         return List.of(new KnockDoorListener()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.knockDoor; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import org.bukkit.GameMode; | ||||||
|  | import org.bukkit.block.Block; | ||||||
|  | import org.bukkit.block.data.type.Door; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.block.BlockDamageAbortEvent; | ||||||
|  |  | ||||||
|  | public class KnockDoorListener extends ApplianceListener<KnockDoor> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onKnock(BlockDamageAbortEvent event) { | ||||||
|  |         if(event.getPlayer().getGameMode() != GameMode.SURVIVAL) return; | ||||||
|  |         Block block = event.getBlock(); | ||||||
|  |         if(!(block.getBlockData() instanceof Door)) return; | ||||||
|  |         getAppliance().knockAtDoor(event.getPlayer(), block); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,50 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.knockDoor; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.NamespacedKey; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Locale; | ||||||
|  |  | ||||||
|  | public class KnockDoorSetting extends SelectSetting implements CategorizedSetting { | ||||||
|  |     private static final String namespace = KnockDoorSetting.class.getSimpleName().toLowerCase(Locale.ROOT); | ||||||
|  |     public static Options.Option disabled = new Options.Option("Deaktiviert", new NamespacedKey(namespace, "disabled")); | ||||||
|  |     public static Options.Option knockSingleTime = new Options.Option("Einmal an der Tür anklopfen", new NamespacedKey(namespace, "single")); | ||||||
|  |     public static Options.Option knockThreeTimes = new Options.Option("Dreimal an der Tür anklopfen", new NamespacedKey(namespace, "three")); | ||||||
|  |  | ||||||
|  |     public KnockDoorSetting() { | ||||||
|  |         super( | ||||||
|  |             Settings.Key.KnockDoors, | ||||||
|  |             new Options(List.of(disabled, knockSingleTime, knockThreeTimes)) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public SettingCategory category() { | ||||||
|  |         return SettingCategory.Gameplay; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected String title() { | ||||||
|  |         return "Klopfen an Türen"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected String description() { | ||||||
|  |         return "Klopft durch das schlagen an eine Tür an."; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Material icon() { | ||||||
|  |         return Material.BELL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Options.Option defaultValue() { | ||||||
|  |         return disabled; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.maintenance; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
|  | import eu.mhsl.craftattack.spawn.config.Configuration; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class Maintenance extends Appliance { | ||||||
|  |     private boolean isInMaintenance; | ||||||
|  |     private final String configKey = "enabled"; | ||||||
|  |  | ||||||
|  |     public Maintenance() { | ||||||
|  |         super("maintenance"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         this.isInMaintenance = localConfig().getBoolean(configKey, false); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setState(boolean enabled) { | ||||||
|  |         this.isInMaintenance = enabled; | ||||||
|  |         localConfig().set(configKey, enabled); | ||||||
|  |         Configuration.saveChanges(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean isInMaintenance() { | ||||||
|  |         return isInMaintenance; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<ApplianceCommand<?>> commands() { | ||||||
|  |         return List.of(new MaintenanceCommand()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<Listener> listeners() { | ||||||
|  |         return List.of(new PreventMaintenanceJoinListener()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,30 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.maintenance; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
|  | import org.bukkit.command.Command; | ||||||
|  | import org.bukkit.command.CommandSender; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | public class MaintenanceCommand extends ApplianceCommand<Maintenance> { | ||||||
|  |     Map<String, Boolean> arguments = Map.of("enable", true, "disable", false); | ||||||
|  |  | ||||||
|  |     public MaintenanceCommand() { | ||||||
|  |         super("maintanance"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { | ||||||
|  |         if(args.length != 1 || !arguments.containsKey(args[0])) throw new Error("Argument 'enable' oder 'disable' gefordert!"); | ||||||
|  |         getAppliance().setState(arguments.get(args[0])); | ||||||
|  |         sender.sendMessage(String.format("Maintanance: %b", getAppliance().isInMaintenance())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { | ||||||
|  |         return arguments.keySet().stream().toList(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.maintenance; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.player.PlayerLoginEvent; | ||||||
|  |  | ||||||
|  | public class PreventMaintenanceJoinListener extends ApplianceListener<Maintenance> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onJoin(PlayerLoginEvent event) { | ||||||
|  |         if(!getAppliance().isInMaintenance()) return; | ||||||
|  |         if(event.getPlayer().hasPermission("bypassMaintainance")) return; | ||||||
|  |  | ||||||
|  |         DisconnectInfo disconnectInfo = new DisconnectInfo( | ||||||
|  |             "Wartunsarbeiten", | ||||||
|  |             "Zurzeit können nur Admins dem Server beitreten!", | ||||||
|  |             "Bitte warte bis die Warungsarbeiten wieder deaktiviert werden.", | ||||||
|  |             event.getPlayer().getUniqueId() | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         event.disallow(PlayerLoginEvent.Result.KICK_OTHER, disconnectInfo.getComponent()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,63 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.optionLinks; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.Bukkit; | ||||||
|  | import org.bukkit.ServerLinks; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.net.URI; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.function.Function; | ||||||
|  | import java.util.logging.Level; | ||||||
|  |  | ||||||
|  | @SuppressWarnings("UnstableApiUsage") | ||||||
|  | public class OptionLinks extends Appliance { | ||||||
|  |     record ComponentSupplier() {} | ||||||
|  |     record UriSupplier(Player player) {} | ||||||
|  |     record UriConsumer(String uri) {} | ||||||
|  |     record SuppliedLink(Function<ComponentSupplier, Component> component, Function<UriSupplier, UriConsumer> uri) {} | ||||||
|  |  | ||||||
|  |     List<SuppliedLink> links = List.of( | ||||||
|  |         new SuppliedLink( | ||||||
|  |             componentSupplier -> Component.text("CraftAttack Homepage", NamedTextColor.GOLD), | ||||||
|  |             uriSupplier -> new UriConsumer("https://mhsl.eu/craftattack") | ||||||
|  |         ), | ||||||
|  |         new SuppliedLink( | ||||||
|  |             componentSupplier -> Component.text("Regeln", NamedTextColor.GOLD), | ||||||
|  |             uriSupplier -> new UriConsumer("https://mhsl.eu/craftattack/rules") | ||||||
|  |         ) | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Bukkit.getServer().getServerLinks().getLinks() | ||||||
|  |             .forEach(serverLink -> Bukkit.getServer().getServerLinks().removeLink(serverLink)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setServerLinks(Player player) { | ||||||
|  |         ServerLinks playerLinks = Bukkit.getServerLinks().copy(); | ||||||
|  |          Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> { | ||||||
|  |              this.links.forEach(suppliedLink -> { | ||||||
|  |                  Component component = suppliedLink.component.apply(new ComponentSupplier()); | ||||||
|  |                  String uri = suppliedLink.uri.apply(new UriSupplier(player)).uri; | ||||||
|  |  | ||||||
|  |                  try { | ||||||
|  |                      playerLinks.addLink(component, URI.create(uri)); | ||||||
|  |                  } catch(IllegalArgumentException e) { | ||||||
|  |                      Main.logger().log(Level.INFO, String.format("Failed to create OptionLink '%s' for player '%s'", uri, player.getName()), e); | ||||||
|  |                  } | ||||||
|  |              }); | ||||||
|  |              player.sendLinks(playerLinks); | ||||||
|  |          }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<Listener> listeners() { | ||||||
|  |         return List.of(new UpdateLinksListener()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.optionLinks; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.player.PlayerJoinEvent; | ||||||
|  |  | ||||||
|  | public class UpdateLinksListener extends ApplianceListener<OptionLinks> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onJoin(PlayerJoinEvent event) { | ||||||
|  |         getAppliance().setServerLinks(event.getPlayer()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -38,7 +38,7 @@ public class Outlawed extends Appliance { | |||||||
|                 if(!player.isOnline()) return; |                 if(!player.isOnline()) return; | ||||||
|                 if(status != Status.FORCED) return; |                 if(status != Status.FORCED) return; | ||||||
|                 try { |                 try { | ||||||
|                     Main.instance().getAppliance(Whitelist.class).integrityCheck(player); |                     queryAppliance(Whitelist.class).integrityCheck(player); | ||||||
|                 } catch(DisconnectInfo.Throwable e) { |                 } catch(DisconnectInfo.Throwable e) { | ||||||
|                     Bukkit.getScheduler().runTask(Main.instance(), () -> e.getDisconnectScreen().applyKick(player)); |                     Bukkit.getScheduler().runTask(Main.instance(), () -> e.getDisconnectScreen().applyKick(player)); | ||||||
|                 } |                 } | ||||||
| @@ -75,7 +75,7 @@ public class Outlawed extends Appliance { | |||||||
|  |  | ||||||
|     private void setLawStatus(Player player, Status status) { |     private void setLawStatus(Player player, Status status) { | ||||||
|         playerStatusMap.put(player, status); |         playerStatusMap.put(player, status); | ||||||
|         Main.instance().getAppliance(DisplayName.class).update(player); |         queryAppliance(DisplayName.class).update(player); | ||||||
|  |  | ||||||
|         List<String> newList = localConfig().getStringList(voluntarilyEntry); |         List<String> newList = localConfig().getStringList(voluntarilyEntry); | ||||||
|         if(status.equals(Status.VOLUNTARILY)) { |         if(status.equals(Status.VOLUNTARILY)) { | ||||||
| @@ -93,7 +93,7 @@ public class Outlawed extends Appliance { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private boolean isTimeout(Player player) { |     private boolean isTimeout(Player player) { | ||||||
|         return timeouts.get(player.getUniqueId()) < System.currentTimeMillis() - timeoutInMs; |         return timeouts.getOrDefault(player.getUniqueId(), 0L) > System.currentTimeMillis() - timeoutInMs; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void setTimeout(Player player) { |     private void setTimeout(Player player) { | ||||||
| @@ -127,7 +127,7 @@ public class Outlawed extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new OutlawedReminderListener()); |         return List.of(new OutlawedReminderListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
|  | import org.bukkit.command.Command; | ||||||
|  | import org.bukkit.command.CommandSender; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | public class ChangePackCommand extends ApplianceCommand.PlayerChecked<PackSelect> { | ||||||
|  |     public static final String commandName = "texturepack"; | ||||||
|  |     public ChangePackCommand() { | ||||||
|  |         super(commandName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { | ||||||
|  |         getAppliance().openPackInventory(getPlayer()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,94 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect; | ||||||
|  |  | ||||||
|  | import com.google.gson.*; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.CachedApplianceSupplier; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.inventory.HeadBuilder; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.text.ComponentUtil; | ||||||
|  | import net.kyori.adventure.resource.ResourcePackInfo; | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; | ||||||
|  | import org.bukkit.inventory.ItemStack; | ||||||
|  | import org.bukkit.inventory.meta.ItemMeta; | ||||||
|  |  | ||||||
|  | import java.util.*; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | public class PackConfiguration extends CachedApplianceSupplier<PackSelect> { | ||||||
|  |     public record Pack(UUID id, String name, String description, String author, ResourcePackInfo info, String icon) { | ||||||
|  |         public ItemStack buildItem() { | ||||||
|  |             ItemStack stack = HeadBuilder.getCustomTextureHead(icon); | ||||||
|  |             ItemMeta meta = stack.getItemMeta(); | ||||||
|  |             meta.itemName(Component.text(id.toString())); | ||||||
|  |             meta.displayName(Component.text(name(), NamedTextColor.GOLD)); | ||||||
|  |             List<Component> lore = new ArrayList<>(); | ||||||
|  |             lore.add(Component.text("Autor: ", NamedTextColor.DARK_GRAY).append(Component.text(author))); | ||||||
|  |             lore.add(Component.text(" ")); | ||||||
|  |             lore.addAll( | ||||||
|  |                 ComponentUtil.lineBreak(description()) | ||||||
|  |                     .map(s -> Component.text(s, NamedTextColor.GRAY)) | ||||||
|  |                     .toList() | ||||||
|  |             ); | ||||||
|  |             lore.add(Component.text(" ")); | ||||||
|  |             meta.lore(lore); | ||||||
|  |             stack.setItemMeta(meta); | ||||||
|  |             return stack; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public boolean equalsItem(ItemStack other) { | ||||||
|  |             String itemName = PlainTextComponentSerializer.plainText().serialize(other.getItemMeta().itemName()); | ||||||
|  |             try { | ||||||
|  |                 return UUID.fromString(itemName).equals(id); | ||||||
|  |             } catch(IllegalArgumentException ignored) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     public record PackList(List<Pack> packs) { | ||||||
|  |         public Optional<Pack> findFromItem(ItemStack itemStack) { | ||||||
|  |             return packs.stream() | ||||||
|  |                 .filter(pack -> pack.equalsItem(itemStack)) | ||||||
|  |                 .findFirst(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     public record SerializedPackList(List<UUID> packs) {} | ||||||
|  |  | ||||||
|  |     private final PackList packList; | ||||||
|  |     private final Gson gson = new GsonBuilder().create(); | ||||||
|  |  | ||||||
|  |     private PackConfiguration() { | ||||||
|  |         this.packList = new PackList(new ArrayList<>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private PackConfiguration(String data) { | ||||||
|  |         SerializedPackList serializedData = gson.fromJson(data, SerializedPackList.class); | ||||||
|  |  | ||||||
|  |         var availablePackMap = getAppliance().availablePacks.packs().stream() | ||||||
|  |             .collect(Collectors.toMap(PackConfiguration.Pack::id, pack -> pack)); | ||||||
|  |  | ||||||
|  |         this.packList = new PackList( | ||||||
|  |             serializedData.packs().stream() | ||||||
|  |                 .map(availablePackMap::get) | ||||||
|  |                 .filter(Objects::nonNull) | ||||||
|  |                 .collect(Collectors.toList()) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         if (this.packList.packs().isEmpty()) throw new IllegalArgumentException("Serialized data does not contain any valid data!"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static PackConfiguration deserialize(String data) { | ||||||
|  |         return new PackConfiguration(data); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static PackConfiguration empty() { | ||||||
|  |         return new PackConfiguration(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String serialize() { | ||||||
|  |         return gson.toJson(new SerializedPackList(this.packList.packs().stream().map(Pack::id).toList())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public List<Pack> getPackList() { | ||||||
|  |         return packList.packs(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,171 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.CachedApplianceSupplier; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.IteratorUtil; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.inventory.ItemBuilder; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.inventory.PlaceholderItems; | ||||||
|  | import eu.mhsl.craftattack.spawn.util.world.InteractSounds; | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.Bukkit; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.inventory.ClickType; | ||||||
|  | import org.bukkit.inventory.Inventory; | ||||||
|  | import org.bukkit.inventory.ItemStack; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
|  |  | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.IntStream; | ||||||
|  |  | ||||||
|  | public class PackConfigurationInventory extends CachedApplianceSupplier<PackSelect> { | ||||||
|  |     private final PackConfiguration packConfiguration; | ||||||
|  |     private final Player inventoryOwner; | ||||||
|  |     private final Inventory inventory; | ||||||
|  |  | ||||||
|  |     private final ItemStack reset = ItemBuilder.of(Material.BARRIER) | ||||||
|  |         .displayName(Component.text("Zurücksetzen", NamedTextColor.RED)) | ||||||
|  |         .lore("Alle gewählten Texturepacks werden entfernt") | ||||||
|  |         .build(); | ||||||
|  |  | ||||||
|  |     private final ItemStack info = ItemBuilder.of(Material.OAK_HANGING_SIGN) | ||||||
|  |         .displayName(Component.text("Texturepacks", NamedTextColor.GOLD)) | ||||||
|  |         .lore( | ||||||
|  |             "Wähle aus der oberen Liste eine oder mehrere Texturepacks aus. " + | ||||||
|  |             "Ändere die anzuwendende Reihenfolge durch Links/Rechtsklick in der unteren Liste. " + | ||||||
|  |             "Klicke auf Speichern um die Änderungen anzuwenden!" | ||||||
|  |         ) | ||||||
|  |         .build(); | ||||||
|  |  | ||||||
|  |     private final ItemStack save = ItemBuilder.of(Material.GREEN_WOOL) | ||||||
|  |         .displayName(Component.text("Anwenden", NamedTextColor.GREEN)) | ||||||
|  |         .lore("Die Ausgewählten Texturepacks werden in der entsprechenden Reihenfolge angewandt. Das Anwenden kann durch den Download je nach Internetgeschwindigkeit eine Weile dauern.") | ||||||
|  |         .build(); | ||||||
|  |  | ||||||
|  |     private final ItemStack unusedSlot = ItemBuilder.of(Material.LIME_STAINED_GLASS_PANE) | ||||||
|  |         .displayName(Component.text("Freier Slot", NamedTextColor.GREEN)) | ||||||
|  |         .lore("Klicke auf ein Texturepack um es hinzuzufügen.") | ||||||
|  |         .build(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public PackConfigurationInventory(PackConfiguration packConfiguration, Player inventoryOwner) { | ||||||
|  |         this.packConfiguration = packConfiguration; | ||||||
|  |         this.inventoryOwner = inventoryOwner; | ||||||
|  |         this.inventory = Bukkit.createInventory(null, 9 * 6, Component.text("Texturepacks")); | ||||||
|  |         this.draw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Inventory getInventory() { | ||||||
|  |         return inventory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handleClick(@Nullable ItemStack clickedItem, int slot, ClickType clickType) { | ||||||
|  |         if(clickedItem == null) return; | ||||||
|  |         if(clickedItem.equals(reset)) reset(); | ||||||
|  |         if(clickedItem.equals(save)) apply(); | ||||||
|  |  | ||||||
|  |         if(slot >= 9 && slot < 9 * 4) { | ||||||
|  |             getAppliance().availablePacks.findFromItem(clickedItem) | ||||||
|  |                 .ifPresent(this::toggle); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(slot >= 9 * 5) { | ||||||
|  |             getAppliance().availablePacks.findFromItem(clickedItem) | ||||||
|  |                 .ifPresent(pack -> { | ||||||
|  |                     switch(clickType) { | ||||||
|  |                         case RIGHT -> move(pack, true); | ||||||
|  |                         case LEFT -> move(pack, false); | ||||||
|  |                         case MIDDLE -> toggle(pack); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void move(PackConfiguration.Pack pack, boolean moveToRight) { | ||||||
|  |         List<PackConfiguration.Pack> packs = packConfiguration.getPackList(); | ||||||
|  |         int index = packs.indexOf(pack); | ||||||
|  |  | ||||||
|  |         if (index != -1) { | ||||||
|  |             int newIndex = moveToRight ? index + 1 : index - 1; | ||||||
|  |             if (newIndex >= 0 && newIndex < packs.size()) Collections.swap(packs, index, newIndex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         InteractSounds.of(inventoryOwner).click(); | ||||||
|  |         draw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void toggle(PackConfiguration.Pack pack) { | ||||||
|  |         if(packConfiguration.getPackList().contains(pack)) { | ||||||
|  |             packConfiguration.getPackList().remove(pack); | ||||||
|  |         } else { | ||||||
|  |             packConfiguration.getPackList().add(pack); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         InteractSounds.of(inventoryOwner).click(); | ||||||
|  |         draw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void reset() { | ||||||
|  |         packConfiguration.getPackList().clear(); | ||||||
|  |         InteractSounds.of(inventoryOwner).delete(); | ||||||
|  |         draw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void apply() { | ||||||
|  |         inventoryOwner.closeInventory(); | ||||||
|  |         InteractSounds.of(inventoryOwner).success(); | ||||||
|  |         Bukkit.getScheduler().runTask(Main.instance(), () -> getAppliance().setPack(inventoryOwner, packConfiguration)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void draw() { | ||||||
|  |         inventory.clear(); | ||||||
|  |  | ||||||
|  |         inventory.setItem(0, packConfiguration.getPackList().isEmpty() ? PlaceholderItems.grayStainedGlassPane : reset); | ||||||
|  |         IteratorUtil.times(3, () -> inventory.addItem(PlaceholderItems.grayStainedGlassPane)); | ||||||
|  |         inventory.setItem(4, info); | ||||||
|  |         IteratorUtil.times(3, () -> inventory.addItem(PlaceholderItems.grayStainedGlassPane)); | ||||||
|  |         inventory.setItem(8, save); | ||||||
|  |  | ||||||
|  |         IteratorUtil.iterateListInGlobal( | ||||||
|  |             9, | ||||||
|  |             getAppliance().availablePacks.packs().stream() | ||||||
|  |                 .filter(pack -> !packConfiguration.getPackList().contains(pack)) | ||||||
|  |                 .limit(9 * 3) | ||||||
|  |                 .map(pack -> { | ||||||
|  |                     ItemBuilder stack = ItemBuilder.of(pack.buildItem()); | ||||||
|  |                     if(packConfiguration.getPackList().contains(pack)) stack.glint(); | ||||||
|  |                     return stack.build(); | ||||||
|  |                 }) | ||||||
|  |                 .toList(), | ||||||
|  |             inventory::setItem | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         IntStream.range(9 * 4, 9 * 5) | ||||||
|  |                 .forEach(slot -> inventory.setItem(slot, PlaceholderItems.grayStainedGlassPane)); | ||||||
|  |  | ||||||
|  |         IteratorUtil.iterateListInGlobal( | ||||||
|  |             9 * 5, | ||||||
|  |             IteratorUtil.expandList( | ||||||
|  |                 IntStream.range(0, Math.min(packConfiguration.getPackList().size(), 9)) | ||||||
|  |                     .mapToObj(i -> { | ||||||
|  |                         PackConfiguration.Pack pack = packConfiguration.getPackList().get(i); | ||||||
|  |                         ItemBuilder builder = ItemBuilder.of(pack.buildItem()); | ||||||
|  |  | ||||||
|  |                         builder | ||||||
|  |                             .displayName(existing -> Component.text(String.format("#%s ", i + 1), NamedTextColor.LIGHT_PURPLE).append(existing)) | ||||||
|  |                             .appendLore(Component.text("➡ Rechtsklick um nach Rechts zu verschieben", NamedTextColor.AQUA)) | ||||||
|  |                             .appendLore(Component.text("⬅ Linksklick um nach Links zu verschieben", NamedTextColor.AQUA)) | ||||||
|  |                             .appendLore(Component.text("\uD83D\uDDD1 Mausradklick um zu entfernen", NamedTextColor.AQUA)); | ||||||
|  |                         return builder.build(); | ||||||
|  |                     }) | ||||||
|  |                     .toList(), | ||||||
|  |                 9, | ||||||
|  |                 unusedSlot | ||||||
|  |             ), | ||||||
|  |             inventory::setItem | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,139 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.packSelect.listeners.ClosePackInventoryListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.packSelect.listeners.ClickPackInventoryListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.packSelect.listeners.SetPacksOnJoinListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
|  | import net.kyori.adventure.resource.ResourcePackInfo; | ||||||
|  | import net.kyori.adventure.resource.ResourcePackRequest; | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.Bukkit; | ||||||
|  | import org.bukkit.NamespacedKey; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.bukkit.inventory.Inventory; | ||||||
|  | import org.bukkit.persistence.PersistentDataContainer; | ||||||
|  | import org.bukkit.persistence.PersistentDataType; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.net.URI; | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | public class PackSelect extends Appliance { | ||||||
|  |     private static final NamespacedKey packKey = new NamespacedKey(Main.instance(), PackSelect.class.getName().toLowerCase(Locale.ROOT)); | ||||||
|  |  | ||||||
|  |     public final PackConfiguration.PackList availablePacks = new PackConfiguration.PackList(new ArrayList<>()); | ||||||
|  |     public final Map<Player, PackConfigurationInventory> openInventories = new WeakHashMap<>(); | ||||||
|  |  | ||||||
|  |     public PackSelect() { | ||||||
|  |         super("packselect"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(PackSelectSetting.class); | ||||||
|  |  | ||||||
|  |         List<Map<?, ?>> packs = localConfig().getMapList("packs"); | ||||||
|  |         Bukkit.getScheduler().runTaskAsynchronously( | ||||||
|  |             Main.instance(), | ||||||
|  |             () -> packs.stream() | ||||||
|  |                 .flatMap(pack -> pack.entrySet().stream()) | ||||||
|  |                 .forEach(pack -> { | ||||||
|  |                     @SuppressWarnings("unchecked") Map<String, String> packData = (Map<String, String>) pack.getValue(); | ||||||
|  |  | ||||||
|  |                     try { | ||||||
|  |                         ResourcePackInfo resourcePackInfo = ResourcePackInfoFactory | ||||||
|  |                             .createResourcePackInfo(URI.create(packData.get("url")), packData.get("hash")) | ||||||
|  |                             .join(); | ||||||
|  |  | ||||||
|  |                         PackConfiguration.Pack packToAdd = new PackConfiguration.Pack( | ||||||
|  |                             UUID.nameUUIDFromBytes(pack.getKey().toString().getBytes()), | ||||||
|  |                             packData.get("name"), | ||||||
|  |                             packData.get("description"), | ||||||
|  |                             packData.get("author"), | ||||||
|  |                             resourcePackInfo, | ||||||
|  |                             packData.get("icon") | ||||||
|  |                         ); | ||||||
|  |                         availablePacks.packs().add(packToAdd); | ||||||
|  |                     } catch (Exception e) { | ||||||
|  |                         Main.logger().warning(String.format("Failed to add pack %s: %s", packData.get("name"), e.getMessage())); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public PackConfiguration getPackConfigurationForPlayer(Player player) { | ||||||
|  |         PersistentDataContainer persistentDataContainer = player.getPersistentDataContainer(); | ||||||
|  |         try { | ||||||
|  |             String serialized = persistentDataContainer.get(packKey, PersistentDataType.STRING); | ||||||
|  |             Objects.requireNonNull(serialized); | ||||||
|  |             return PackConfiguration.deserialize(serialized); | ||||||
|  |         } catch(IllegalArgumentException | NullPointerException exception) { | ||||||
|  |             return PackConfiguration.empty(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void openPackInventory(Player player) { | ||||||
|  |         PackConfigurationInventory packInventory = new PackConfigurationInventory(getPackConfigurationForPlayer(player), player); | ||||||
|  |         player.openInventory(packInventory.getInventory()); | ||||||
|  |         openInventories.put(player, packInventory); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setPack(Player player, PackConfiguration packConfiguration) { | ||||||
|  |         player.getPersistentDataContainer().set(packKey, PersistentDataType.STRING, packConfiguration.serialize()); | ||||||
|  |  | ||||||
|  |         int packCount = packConfiguration.getPackList().size(); | ||||||
|  |         if(packCount > 0) { | ||||||
|  |             player.sendMessage( | ||||||
|  |                 Component.text( | ||||||
|  |                     String.format("%s heruntergeladen und hinzugefügt...", packCount > 1 ? "Texturenpakete werden" : "Texturenpaket wird"), | ||||||
|  |                     NamedTextColor.DARK_GREEN | ||||||
|  |                 ) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         player.sendResourcePacks( | ||||||
|  |             ResourcePackRequest.resourcePackRequest() | ||||||
|  |                 .packs( | ||||||
|  |                     packConfiguration.getPackList().stream() | ||||||
|  |                         .map(PackConfiguration.Pack::info) | ||||||
|  |                         .toList() | ||||||
|  |                         .reversed() | ||||||
|  |                 ) | ||||||
|  |                 .replace(true) | ||||||
|  |                 .required(true) | ||||||
|  |                 .prompt( | ||||||
|  |                     Component.text() | ||||||
|  |                         .append(Component.text("Bestätige um fortzufahren! Du kannst deine Entscheidung jederzeit mit ", NamedTextColor.GRAY)) | ||||||
|  |                         .append(Component.text(String.format("/%s ", ChangePackCommand.commandName), NamedTextColor.GOLD)) | ||||||
|  |                         .append(Component.text("ändern.", NamedTextColor.GRAY)) | ||||||
|  |                         .build() | ||||||
|  |                 ) | ||||||
|  |                 .build() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean isNotPackInventory(Player player, Inventory inventory) { | ||||||
|  |         PackConfigurationInventory packConfigurationInventory = this.openInventories.get(player); | ||||||
|  |         if(packConfigurationInventory == null) return true; | ||||||
|  |         return packConfigurationInventory.getInventory() != inventory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<ApplianceCommand<?>> commands() { | ||||||
|  |         return List.of(new ChangePackCommand()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<Listener> listeners() { | ||||||
|  |         return List.of( | ||||||
|  |             new ClosePackInventoryListener(), | ||||||
|  |             new ClickPackInventoryListener(), | ||||||
|  |             new SetPacksOnJoinListener() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.ActionSetting; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.inventory.ClickType; | ||||||
|  |  | ||||||
|  | public class PackSelectSetting extends ActionSetting implements CategorizedSetting { | ||||||
|  |     @Override | ||||||
|  |     protected String title() { | ||||||
|  |         return "Texturepacks"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected String description() { | ||||||
|  |         return "Stelle dein persönliches Texturepack aus einer kuratierten Auswahl zusammen und gestalte deine Spielerfahrung individuell."; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Material icon() { | ||||||
|  |         return Material.PAINTING; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void onAction(Player player, ClickType clickType) { | ||||||
|  |         Main.instance().getAppliance(PackSelect.class).openPackInventory(player); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public SettingCategory category() { | ||||||
|  |         return SettingCategory.Visuals; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,62 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import net.kyori.adventure.resource.ResourcePackInfo; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
|  |  | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.net.HttpURLConnection; | ||||||
|  | import java.net.URI; | ||||||
|  | import java.security.MessageDigest; | ||||||
|  | import java.util.UUID; | ||||||
|  | import java.util.concurrent.CompletableFuture; | ||||||
|  |  | ||||||
|  | public class ResourcePackInfoFactory { | ||||||
|  |  | ||||||
|  |     private static boolean isValidHash(@Nullable String hash) { | ||||||
|  |         return hash != null && hash.length() == 40; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static @NotNull CompletableFuture<ResourcePackInfo> createResourcePackInfo(@NotNull URI resourcePackUrl, @Nullable String hash) { | ||||||
|  |         if (isValidHash(hash)) { | ||||||
|  |             return CompletableFuture.completedFuture( | ||||||
|  |                 ResourcePackInfo.resourcePackInfo(UUID.nameUUIDFromBytes(hash.getBytes()), resourcePackUrl, hash) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return CompletableFuture.supplyAsync(() -> { | ||||||
|  |             try { | ||||||
|  |                 Main.logger().info(String.format("Start calculating SHA1 Hash of %s", resourcePackUrl)); | ||||||
|  |                 HttpURLConnection connection = (HttpURLConnection) resourcePackUrl.toURL().openConnection(); | ||||||
|  |                 connection.setRequestMethod("GET"); | ||||||
|  |                 connection.connect(); | ||||||
|  |  | ||||||
|  |                 try (InputStream inputStream = connection.getInputStream()) { | ||||||
|  |                     MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1"); | ||||||
|  |                     byte[] buffer = new byte[1024]; | ||||||
|  |                     int bytesRead; | ||||||
|  |                     while ((bytesRead = inputStream.read(buffer)) != -1) { | ||||||
|  |                         sha1Digest.update(buffer, 0, bytesRead); | ||||||
|  |                     } | ||||||
|  |                     String sha1Hex = bytesToHex(sha1Digest.digest()); | ||||||
|  |  | ||||||
|  |                     Main.logger().info(String.format("Calculating SHA1 Hash of %s completed: %s", resourcePackUrl, sha1Hex)); | ||||||
|  |                     return ResourcePackInfo.resourcePackInfo(UUID.nameUUIDFromBytes(sha1Hex.getBytes()), resourcePackUrl, sha1Hex); | ||||||
|  |                 } | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 String error = String.format("Error whilst SHA1 calculation of %s: %s", resourcePackUrl, e.getMessage()); | ||||||
|  |                 Main.logger().warning(error); | ||||||
|  |                 throw new RuntimeException(error); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static String bytesToHex(byte[] bytes) { | ||||||
|  |         StringBuilder hexString = new StringBuilder(bytes.length * 2); | ||||||
|  |         for (byte b : bytes) { | ||||||
|  |             hexString.append(String.format("%02x", b)); | ||||||
|  |         } | ||||||
|  |         return hexString.toString(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect.listeners; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.packSelect.PackSelect; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.inventory.InventoryClickEvent; | ||||||
|  |  | ||||||
|  | public class ClickPackInventoryListener extends ApplianceListener<PackSelect> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void interact(InventoryClickEvent event) { | ||||||
|  |         if(!(event.getWhoClicked() instanceof Player player)) return; | ||||||
|  |         if(getAppliance().isNotPackInventory(player, event.getInventory())) return; | ||||||
|  |         event.setCancelled(true); | ||||||
|  |  | ||||||
|  |         getAppliance().openInventories.get(player).handleClick( | ||||||
|  |             event.getCurrentItem(), | ||||||
|  |             event.getSlot(), | ||||||
|  |             event.getClick() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect.listeners; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.packSelect.PackSelect; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.inventory.InventoryCloseEvent; | ||||||
|  |  | ||||||
|  | public class ClosePackInventoryListener extends ApplianceListener<PackSelect> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onClose(InventoryCloseEvent event) { | ||||||
|  |         if(!(event.getPlayer() instanceof Player player)) return; | ||||||
|  |         if(getAppliance().isNotPackInventory(player, event.getInventory())) return; | ||||||
|  |         getAppliance().openInventories.remove(player); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.packSelect.listeners; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliances.packSelect.PackSelect; | ||||||
|  | import org.bukkit.Bukkit; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.player.PlayerJoinEvent; | ||||||
|  |  | ||||||
|  | public class SetPacksOnJoinListener extends ApplianceListener<PackSelect> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onJoin(PlayerJoinEvent event) { | ||||||
|  |         Bukkit.getScheduler().runTask( | ||||||
|  |             Main.instance(), | ||||||
|  |             () -> getAppliance().setPack(event.getPlayer(), getAppliance().getPackConfigurationForPlayer(event.getPlayer())) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -50,7 +50,7 @@ public class PanicBan extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new PanicBanJoinListener()); |         return List.of(new PanicBanJoinListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ public class PlayerLimit extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new PlayerLimiterListener() |             new PlayerLimiterListener() | ||||||
|         ); |         ); | ||||||
|   | |||||||
| @@ -3,11 +3,13 @@ package eu.mhsl.craftattack.spawn.appliances.portableCrafting; | |||||||
| import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
| import org.bukkit.Material; | import org.bukkit.Material; | ||||||
| import org.bukkit.event.EventHandler; | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.block.Action; | ||||||
| import org.bukkit.event.player.PlayerInteractEvent; | import org.bukkit.event.player.PlayerInteractEvent; | ||||||
|  |  | ||||||
| public class OnCraftingTableUseListener extends ApplianceListener<PortableCrafting> { | public class OnCraftingTableUseListener extends ApplianceListener<PortableCrafting> { | ||||||
|     @EventHandler |     @EventHandler | ||||||
|     public void inInteract(PlayerInteractEvent event) { |     public void inInteract(PlayerInteractEvent event) { | ||||||
|  |         if(!event.getAction().equals(Action.RIGHT_CLICK_AIR)) return; | ||||||
|         if(!event.getMaterial().equals(Material.CRAFTING_TABLE)) return; |         if(!event.getMaterial().equals(Material.CRAFTING_TABLE)) return; | ||||||
|         getAppliance().openFor(event.getPlayer()); |         getAppliance().openFor(event.getPlayer()); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,13 +9,18 @@ import org.jetbrains.annotations.NotNull; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class PortableCrafting extends Appliance { | public class PortableCrafting extends Appliance { | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(PortableCraftingSetting.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void openFor(Player player) { |     public void openFor(Player player) { | ||||||
|         if(!Settings.instance().getSetting(player, Settings.Key.EnablePortableCrafting, Boolean.class)) return; |         if(!Settings.instance().getSetting(player, Settings.Key.EnablePortableCrafting, Boolean.class)) return; | ||||||
|         player.openWorkbench(null, true); |         player.openWorkbench(null, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected @NotNull List<Listener> eventHandlers() { |     protected @NotNull List<Listener> listeners() { | ||||||
|         return List.of(new OnCraftingTableUseListener()); |         return List.of(new OnCraftingTableUseListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.portableCrafting; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -175,7 +175,7 @@ public class ProjectStart extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new PlayerInvincibleListener(), |             new PlayerInvincibleListener(), | ||||||
|             new NoAdvancementsListener() |             new NoAdvancementsListener() | ||||||
|   | |||||||
| @@ -2,6 +2,6 @@ package eu.mhsl.craftattack.spawn.appliances.settings; | |||||||
|  |  | ||||||
| public enum SettingCategory { | public enum SettingCategory { | ||||||
|     Gameplay, |     Gameplay, | ||||||
|     Chat, |     Visuals, | ||||||
|     Misc, |     Misc, | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings; | package eu.mhsl.craftattack.spawn.appliances.settings; | ||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.Main; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliance.Appliance; | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
| import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.Setting; | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.Setting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.listeners.OpenSettingsShortcutListener; | import eu.mhsl.craftattack.spawn.appliances.settings.listeners.OpenSettingsShortcutListener; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.listeners.SettingsInventoryListener; | import eu.mhsl.craftattack.spawn.appliances.settings.listeners.SettingsInventoryListener; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.settings.*; |  | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import org.bukkit.Bukkit; | import org.bukkit.Bukkit; | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
| @@ -14,13 +12,14 @@ import org.bukkit.event.Listener; | |||||||
| import org.bukkit.inventory.Inventory; | import org.bukkit.inventory.Inventory; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
| import java.util.Arrays; | import java.lang.reflect.InvocationTargetException; | ||||||
| import java.util.List; | import java.util.*; | ||||||
| import java.util.WeakHashMap; |  | ||||||
| import java.util.concurrent.atomic.AtomicInteger; | import java.util.concurrent.atomic.AtomicInteger; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| public class Settings extends Appliance { | public class Settings extends Appliance { | ||||||
|     private static Settings settingsInstance; |     private static Settings settingsInstance; | ||||||
|  |     private final Set<Class<? extends Setting<?>>> declaredSettings = new HashSet<>(); | ||||||
|  |  | ||||||
|     public enum Key { |     public enum Key { | ||||||
|         TechnicalTab, |         TechnicalTab, | ||||||
| @@ -31,13 +30,24 @@ public class Settings extends Appliance { | |||||||
|         SignEdit, |         SignEdit, | ||||||
|         HotbarReplacer, |         HotbarReplacer, | ||||||
|         ChatMentions, |         ChatMentions, | ||||||
|  |         DoubleDoors, | ||||||
|  |         KnockDoors, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Settings instance() { |     public static Settings instance() { | ||||||
|         if(settingsInstance != null) return settingsInstance; |         if(settingsInstance != null) return settingsInstance; | ||||||
|         Settings instance = Main.instance().getAppliance(Settings.class); |         Settings.settingsInstance = queryAppliance(Settings.class); | ||||||
|         Settings.settingsInstance = instance; |         return settingsInstance; | ||||||
|         return instance; |     } | ||||||
|  |  | ||||||
|  |     public void declareSetting(Class<? extends Setting<?>> setting) { | ||||||
|  |         this.declaredSettings.add(setting); | ||||||
|  |         this.settingsCache.clear(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(SettingsShortcutSetting.class); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public record OpenSettingsInventory(Inventory inventory, List<Setting<?>> settings) { |     public record OpenSettingsInventory(Inventory inventory, List<Setting<?>> settings) { | ||||||
| @@ -49,25 +59,31 @@ public class Settings extends Appliance { | |||||||
|     private List<Setting<?>> getSettings(Player player) { |     private List<Setting<?>> getSettings(Player player) { | ||||||
|         if(settingsCache.containsKey(player)) return settingsCache.get(player); |         if(settingsCache.containsKey(player)) return settingsCache.get(player); | ||||||
|  |  | ||||||
|         List<Setting<?>> settings = List.of( |         List<Setting<?>> settings = this.declaredSettings.stream() | ||||||
|             new PortableCraftingSetting(), |             .map(clazz -> { | ||||||
|             new AutoShulkerSetting(), |                 try { | ||||||
|             new SignEditSetting(), |                     return clazz.getDeclaredConstructor(); | ||||||
|             new HotbarReplaceSetting(), |                 } catch (NoSuchMethodException e) { | ||||||
|             new ChatMentionSetting(), |                     throw new RuntimeException(String.format("Setting '%s' does not have an accessible constructor", clazz.getName()), e); | ||||||
|             new ShowJoinAndLeaveMessagesSetting(), |                 } | ||||||
|             new TechnicalTablistSetting(), |             }) | ||||||
|             new SettingsShortcutSetting() |             .map(constructor -> { | ||||||
|         ); |                 try { | ||||||
|  |                     return constructor.newInstance(); | ||||||
|  |                 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { | ||||||
|  |                     throw new RuntimeException(String.format("Failed to create instance of setting '%s'", constructor.getDeclaringClass().getName()), e); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             .peek(setting -> setting.initializeFromPlayer(player)) | ||||||
|  |             .collect(Collectors.toList()); | ||||||
|  |  | ||||||
|         settings.forEach(setting -> setting.initializeFromPlayer(player)); |  | ||||||
|         this.settingsCache.put(player, settings); |         this.settingsCache.put(player, settings); | ||||||
|         return settings; |         return settings; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public <T> T getSetting(Player player, Key key, Class<T> clazz) { |     public <T> T getSetting(Player player, Key key, Class<T> clazz) { | ||||||
|         Setting<?> setting = getSettings(player).stream() |         Setting<?> setting = getSettings(player).stream() | ||||||
|             .filter(s -> s.getKey().equals(key)) |             .filter(s -> Objects.equals(s.getKey(), key)) | ||||||
|             .findFirst() |             .findFirst() | ||||||
|             .orElseThrow(); |             .orElseThrow(); | ||||||
|  |  | ||||||
| @@ -119,11 +135,15 @@ public class Settings extends Appliance { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int calculateInvSize(List<Setting<?>> settings) { |     private int calculateInvSize(List<Setting<?>> settings) { | ||||||
|         int countOfUncategorized = (int) settings.stream().filter(setting -> !(setting instanceof CategorizedSetting)).count(); |         int countOfUncategorized = (int) settings.stream() | ||||||
|  |             .filter(setting -> !(setting instanceof CategorizedSetting)) | ||||||
|  |             .count(); | ||||||
|  |  | ||||||
|         return Arrays.stream(SettingCategory.values()) |         return Arrays.stream(SettingCategory.values()) | ||||||
|             .map(settingCategory -> settings.stream() |             .map(settingCategory -> settings.stream() | ||||||
|                 .filter(setting -> setting instanceof CategorizedSetting) |                 .filter(setting -> setting instanceof CategorizedSetting) | ||||||
|                 .filter(setting -> ((CategorizedSetting) setting).category().equals(settingCategory)) |                 .map(setting -> (CategorizedSetting) setting) | ||||||
|  |                 .filter(categorizedSetting -> categorizedSetting.category().equals(settingCategory)) | ||||||
|                 .count()) |                 .count()) | ||||||
|             .map(itemCount -> itemCount + countOfUncategorized) |             .map(itemCount -> itemCount + countOfUncategorized) | ||||||
|             .map(itemCount -> (int) Math.ceil((double) itemCount / 9)) |             .map(itemCount -> (int) Math.ceil((double) itemCount / 9)) | ||||||
| @@ -148,7 +168,7 @@ public class Settings extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new SettingsInventoryListener(), |             new SettingsInventoryListener(), | ||||||
|             new OpenSettingsShortcutListener() |             new OpenSettingsShortcutListener() | ||||||
|   | |||||||
| @@ -1,8 +1,5 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.settings; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; |  | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting; | ||||||
| import org.bukkit.Material; | import org.bukkit.Material; | ||||||
| 
 | 
 | ||||||
| @@ -0,0 +1,50 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.settings.datatypes; | ||||||
|  |  | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.bukkit.event.inventory.ClickType; | ||||||
|  | import org.bukkit.inventory.meta.ItemMeta; | ||||||
|  | import org.bukkit.persistence.PersistentDataContainer; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | public abstract class ActionSetting extends Setting<Void> { | ||||||
|  |     public ActionSetting() { | ||||||
|  |         super(null); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected abstract void onAction(Player player, ClickType clickType); | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ItemMeta buildMeta(ItemMeta meta) { | ||||||
|  |         meta.displayName(Component.text(title(), NamedTextColor.WHITE)); | ||||||
|  |         meta.lore(buildDescription(description())); | ||||||
|  |         return meta; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void change(Player player, ClickType clickType) { | ||||||
|  |         onAction(player, clickType); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Void defaultValue() { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void fromStorage(PersistentDataContainer container) {} | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void toStorage(PersistentDataContainer container, Void value) {} | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Class<?> dataType() { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Void state() { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -3,6 +3,7 @@ package eu.mhsl.craftattack.spawn.appliances.settings.datatypes; | |||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.event.inventory.ClickType; | import org.bukkit.event.inventory.ClickType; | ||||||
| import org.bukkit.inventory.meta.ItemMeta; | import org.bukkit.inventory.meta.ItemMeta; | ||||||
| import org.bukkit.persistence.PersistentDataContainer; | import org.bukkit.persistence.PersistentDataContainer; | ||||||
| @@ -47,7 +48,7 @@ public abstract class BoolSetting extends Setting<Boolean> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void change(ClickType clickType) { |     protected void change(Player player, ClickType clickType) { | ||||||
|         this.state = !this.state; |         this.state = !this.state; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import com.google.gson.Gson; | |||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | import eu.mhsl.craftattack.spawn.appliances.settings.Settings; | ||||||
| import net.kyori.adventure.text.Component; | import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.event.inventory.ClickType; | import org.bukkit.event.inventory.ClickType; | ||||||
| import org.bukkit.inventory.meta.ItemMeta; | import org.bukkit.inventory.meta.ItemMeta; | ||||||
| import org.bukkit.persistence.PersistentDataContainer; | import org.bukkit.persistence.PersistentDataContainer; | ||||||
| @@ -87,7 +88,7 @@ public abstract class MultiBoolSetting<T> extends Setting<T> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void change(ClickType clickType) { |     protected void change(Player player, ClickType clickType) { | ||||||
|         var recordComponents = this.state.getClass().getRecordComponents(); |         var recordComponents = this.state.getClass().getRecordComponents(); | ||||||
|  |  | ||||||
|         int currentIndex = IntStream.range(0, recordComponents.length) |         int currentIndex = IntStream.range(0, recordComponents.length) | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import net.kyori.adventure.text.Component; | |||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
| import org.bukkit.Material; | import org.bukkit.Material; | ||||||
| import org.bukkit.NamespacedKey; | import org.bukkit.NamespacedKey; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.event.inventory.ClickType; | import org.bukkit.event.inventory.ClickType; | ||||||
| import org.bukkit.inventory.meta.ItemMeta; | import org.bukkit.inventory.meta.ItemMeta; | ||||||
| import org.bukkit.persistence.PersistentDataContainer; | import org.bukkit.persistence.PersistentDataContainer; | ||||||
| @@ -57,7 +58,7 @@ public abstract class SelectSetting extends Setting<SelectSetting.Options.Option | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void change(ClickType clickType) { |     protected void change(Player player, ClickType clickType) { | ||||||
|         int optionModifier = clickType.equals(ClickType.LEFT) ? 1 : -1; |         int optionModifier = clickType.equals(ClickType.LEFT) ? 1 : -1; | ||||||
|         List<Options.Option> options = this.options.options; |         List<Options.Option> options = this.options.options; | ||||||
|         this.state = IntStream.range(0, options.size()) |         this.state = IntStream.range(0, options.size()) | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ public abstract class Setting<TDataType> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void triggerChange(Player p, ClickType clickType) { |     public void triggerChange(Player p, ClickType clickType) { | ||||||
|         this.change(clickType); |         this.change(p, clickType); | ||||||
|         toStorage(p.getPersistentDataContainer(), this.state()); |         toStorage(p.getPersistentDataContainer(), this.state()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -57,7 +57,7 @@ public abstract class Setting<TDataType> { | |||||||
|     protected abstract String description(); |     protected abstract String description(); | ||||||
|     protected abstract Material icon(); |     protected abstract Material icon(); | ||||||
|     public abstract ItemMeta buildMeta(ItemMeta meta); |     public abstract ItemMeta buildMeta(ItemMeta meta); | ||||||
|     protected abstract void change(ClickType clickType); |     protected abstract void change(Player player, ClickType clickType); | ||||||
|     protected abstract TDataType defaultValue(); |     protected abstract TDataType defaultValue(); | ||||||
|     protected abstract void fromStorage(PersistentDataContainer container); |     protected abstract void fromStorage(PersistentDataContainer container); | ||||||
|     protected abstract void toStorage(PersistentDataContainer container, TDataType value); |     protected abstract void toStorage(PersistentDataContainer container, TDataType value); | ||||||
|   | |||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.snowballKnockback; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import org.bukkit.entity.Entity; | ||||||
|  | import org.bukkit.entity.LivingEntity; | ||||||
|  | import org.bukkit.event.Listener; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class SnowballKnockback extends Appliance { | ||||||
|  |     public void dealSnowballKnockback(LivingEntity entity, Entity snowball) { | ||||||
|  |         entity.damage(0.1); | ||||||
|  |         entity.knockback(0.4, -snowball.getVelocity().getX(), -snowball.getVelocity().getZ()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @NotNull | ||||||
|  |     protected List<Listener> listeners() { | ||||||
|  |         return List.of(new SnowballKnockbackListener()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.snowballKnockback; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; | ||||||
|  | import org.bukkit.entity.*; | ||||||
|  | import org.bukkit.event.EventHandler; | ||||||
|  | import org.bukkit.event.entity.ProjectileHitEvent; | ||||||
|  |  | ||||||
|  | public class SnowballKnockbackListener extends ApplianceListener<SnowballKnockback> { | ||||||
|  |     @EventHandler | ||||||
|  |     public void onSnowballHit(ProjectileHitEvent event) { | ||||||
|  |         if(event.getHitEntity() == null) return; | ||||||
|  |         if(!event.getEntityType().equals(EntityType.SNOWBALL)) return; | ||||||
|  |         if(!(event.getHitEntity() instanceof LivingEntity hitEntity)) return; | ||||||
|  |  | ||||||
|  |         Entity snowball = event.getEntity(); | ||||||
|  |         getAppliance().dealSnowballKnockback(hitEntity, snowball); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -16,6 +16,8 @@ import org.bukkit.entity.Player; | |||||||
| import org.bukkit.event.Listener; | import org.bukkit.event.Listener; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.lang.management.ManagementFactory; | ||||||
|  | import java.lang.management.OperatingSystemMXBean; | ||||||
| import java.time.Duration; | import java.time.Duration; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| @@ -23,6 +25,7 @@ import java.util.List; | |||||||
| public class Tablist extends Appliance { | public class Tablist extends Appliance { | ||||||
|     private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 7 ", 7, 3); |     private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 7 ", 7, 3); | ||||||
|     private NetworkMonitor networkMonitor; |     private NetworkMonitor networkMonitor; | ||||||
|  |     private OperatingSystemMXBean systemMonitor; | ||||||
|  |  | ||||||
|     public Tablist() { |     public Tablist() { | ||||||
|         super("tablist"); |         super("tablist"); | ||||||
| @@ -30,8 +33,12 @@ public class Tablist extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onEnable() { |     public void onEnable() { | ||||||
|  |         Settings.instance().declareSetting(TechnicalTablistSetting.class); | ||||||
|  |  | ||||||
|         int tabRefreshRate = 3; |         int tabRefreshRate = 3; | ||||||
|         this.networkMonitor = new NetworkMonitor(localConfig().getString("interface"), Duration.ofSeconds(1)); |         this.networkMonitor = new NetworkMonitor(localConfig().getString("interface"), Duration.ofSeconds(1)); | ||||||
|  |         this.systemMonitor = ManagementFactory.getOperatingSystemMXBean(); | ||||||
|  |  | ||||||
|         Bukkit.getScheduler().runTaskTimerAsynchronously( |         Bukkit.getScheduler().runTaskTimerAsynchronously( | ||||||
|             Main.instance(), |             Main.instance(), | ||||||
|             () -> IteratorUtil.onlinePlayers(this::updateHeader), |             () -> IteratorUtil.onlinePlayers(this::updateHeader), | ||||||
| @@ -63,8 +70,9 @@ public class Tablist extends Appliance { | |||||||
|                 .append(ComponentUtil.getFormattedPing(player)).appendNewline() |                 .append(ComponentUtil.getFormattedPing(player)).appendNewline() | ||||||
|                 .append(ComponentUtil.getFormattedNetworkStats( |                 .append(ComponentUtil.getFormattedNetworkStats( | ||||||
|                     this.networkMonitor.getTraffic(), |                     this.networkMonitor.getTraffic(), | ||||||
|                     this.networkMonitor.getPackets()) |                     this.networkMonitor.getPackets() | ||||||
|                 ).appendNewline(); |                 )).appendNewline() | ||||||
|  |                 .append(ComponentUtil.getFormattedSystemStats(this.systemMonitor)).appendNewline(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         player.sendPlayerListHeader(header); |         player.sendPlayerListHeader(header); | ||||||
| @@ -76,7 +84,7 @@ public class Tablist extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of(new TablistListener()); |         return List.of(new TablistListener()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package eu.mhsl.craftattack.spawn.appliances.settings.settings; | package eu.mhsl.craftattack.spawn.appliances.tablist; | ||||||
| 
 | 
 | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; | ||||||
| import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; | ||||||
| @@ -14,7 +14,7 @@ public class TitleClear extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new TitleClearListener() |             new TitleClearListener() | ||||||
|         ); |         ); | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ public class Whitelist extends Appliance { | |||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             Main.instance().getAppliance(Outlawed.class).updateForcedStatus(player, timestampRelevant(user.outlawed_until)); |             queryAppliance(Outlawed.class).updateForcedStatus(player, timestampRelevant(user.outlawed_until)); | ||||||
|  |  | ||||||
|             String purePlayerName = Floodgate.isBedrock(player) |             String purePlayerName = Floodgate.isBedrock(player) | ||||||
|                 ? Floodgate.getBedrockPlayer(player).getUsername() |                 ? Floodgate.getBedrockPlayer(player).getUsername() | ||||||
| @@ -144,7 +144,7 @@ public class Whitelist extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new PlayerJoinListener() |             new PlayerJoinListener() | ||||||
|         ); |         ); | ||||||
|   | |||||||
| @@ -44,15 +44,13 @@ public class WorldMuseum extends Appliance { | |||||||
|  |  | ||||||
|     public void handleVillagerInteraction(Player player) { |     public void handleVillagerInteraction(Player player) { | ||||||
|         if(Floodgate.isBedrock(player)) { |         if(Floodgate.isBedrock(player)) { | ||||||
|             Floodgate.runBedrockOnly(player, floodgatePlayer -> { |             Floodgate.runBedrockOnly(player, floodgatePlayer -> floodgatePlayer.sendForm( | ||||||
|                 floodgatePlayer.sendForm( |                 SimpleForm.builder() | ||||||
|                     SimpleForm.builder() |                     .title("Nicht unterstützt") | ||||||
|                         .title("Nicht unterstützt") |                     .content("Bedrock-Spieler werden derzeit für das Weltenmuseum aus Kompatiblitätsgründen nicht zugelassen! Tut uns Leid.") | ||||||
|                         .content("Bedrock-Spieler werden derzeit für das Weltenmuseum aus Kompatiblitätsgründen nicht zugelassen! Tut uns Leid.") |                     .button("Ok") | ||||||
|                         .button("Ok") |                     .build() | ||||||
|                         .build() |             )); | ||||||
|                 ); |  | ||||||
|             }); |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -68,7 +66,7 @@ public class WorldMuseum extends Appliance { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NotNull |     @NotNull | ||||||
|     protected List<Listener> eventHandlers() { |     protected List<Listener> listeners() { | ||||||
|         return List.of( |         return List.of( | ||||||
|             new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> handleVillagerInteraction(playerInteractAtEntityEvent.getPlayer())), |             new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> handleVillagerInteraction(playerInteractAtEntityEvent.getPlayer())), | ||||||
|             new DismissInventoryOpenFromHolder(this.villager.getUniqueId()) |             new DismissInventoryOpenFromHolder(this.villager.getUniqueId()) | ||||||
|   | |||||||
| @@ -0,0 +1,99 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.yearRank; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.Appliance; | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.TextComponent; | ||||||
|  | import net.kyori.adventure.text.event.ClickEvent; | ||||||
|  | import net.kyori.adventure.text.event.HoverEvent; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
|  |  | ||||||
|  | import java.io.File; | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | public class YearRank extends Appliance { | ||||||
|  |     record CraftAttackYear(String name) {} | ||||||
|  |     private final Map<UUID, List<CraftAttackYear>> rankMap = new HashMap<>(); | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() { | ||||||
|  |         File folder = new File(Main.instance().getDataFolder(), "yearRank"); | ||||||
|  |  | ||||||
|  |         //noinspection ResultOfMethodCallIgnored | ||||||
|  |         folder.mkdirs(); | ||||||
|  |  | ||||||
|  |         Optional<File[]> dataFolders = Optional.ofNullable(folder.listFiles()); | ||||||
|  |         if(dataFolders.isEmpty()) return; | ||||||
|  |  | ||||||
|  |         List.of(dataFolders.get()).forEach(playerDataFolder -> { | ||||||
|  |             Optional<File[]> datFiles = Optional.ofNullable(playerDataFolder.listFiles()); | ||||||
|  |             if(datFiles.isEmpty()) return; | ||||||
|  |  | ||||||
|  |             CraftAttackYear craftAttackYear = new CraftAttackYear(playerDataFolder.getName()); | ||||||
|  |  | ||||||
|  |             Arrays.stream(datFiles.get()) | ||||||
|  |                 .map(file -> file.getName().split("\\.")[0]) | ||||||
|  |                 .distinct() | ||||||
|  |                 .map(UUID::fromString) | ||||||
|  |                 .peek(uuid -> rankMap.computeIfAbsent(uuid, p -> new ArrayList<>())) | ||||||
|  |                 .forEach(uuid -> rankMap.get(uuid).add(craftAttackYear)); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public @Nullable Component getNamePrefix(Player player) { | ||||||
|  |         if(!rankMap.containsKey(player.getUniqueId())) return null; | ||||||
|  |         int yearCount = rankMap.get(player.getUniqueId()).size(); | ||||||
|  |         if(yearCount <= 3) return null; | ||||||
|  |  | ||||||
|  |         return Component.text() | ||||||
|  |             .append(Component.text("[\uD83C\uDF1F]", NamedTextColor.GOLD)) | ||||||
|  |             .hoverEvent(HoverEvent.showText( | ||||||
|  |                 Component.text(String.format("Langzeitspieler: %s ist bereits seit %d Jahren dabei!", player.getName(), yearCount)) | ||||||
|  |             )) | ||||||
|  |             .build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Component listYearRanks() { | ||||||
|  |         TextComponent.Builder builder = Component.text(); | ||||||
|  |         builder.append(Component.text("Top 30 Spieler: ", NamedTextColor.GOLD)); | ||||||
|  |         rankMap.keySet().stream() | ||||||
|  |             .map(uuid -> Map.entry(uuid, rankMap.get(uuid).size())) | ||||||
|  |             .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) | ||||||
|  |             .limit(30) | ||||||
|  |             .forEach(entry -> builder | ||||||
|  |                 .appendNewline() | ||||||
|  |                 .append(Component.text(entry.getKey().toString(), NamedTextColor.GRAY) | ||||||
|  |                     .hoverEvent(HoverEvent.showText(Component.text(entry.getKey().toString()))) | ||||||
|  |                     .clickEvent(ClickEvent.copyToClipboard(entry.getKey().toString()))) | ||||||
|  |                 .append(Component.text(": ")) | ||||||
|  |                 .append(Component.text(entry.getValue(), NamedTextColor.GOLD))); | ||||||
|  |  | ||||||
|  |         builder | ||||||
|  |             .appendNewline() | ||||||
|  |             .appendNewline() | ||||||
|  |             .append(Component.text("Übersischt:", NamedTextColor.GOLD)); | ||||||
|  |  | ||||||
|  |         rankMap.values().stream() | ||||||
|  |             .mapMulti(Iterable::forEach) | ||||||
|  |             .filter(o -> o instanceof CraftAttackYear) | ||||||
|  |             .map(o -> (CraftAttackYear) o) | ||||||
|  |             .distinct() | ||||||
|  |             .forEach(craftAttackYear -> builder | ||||||
|  |                 .appendNewline() | ||||||
|  |                 .append(Component.text(craftAttackYear.name, NamedTextColor.GRAY)) | ||||||
|  |                 .append(Component.text(": ")) | ||||||
|  |                 .append(Component.text(rankMap.keySet().stream() | ||||||
|  |                     .filter(uuid -> rankMap.get(uuid).contains(craftAttackYear)).count()))); | ||||||
|  |  | ||||||
|  |         return builder.build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected @NotNull List<ApplianceCommand<?>> commands() { | ||||||
|  |         return List.of(new YearRankCommand()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.appliances.yearRank; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; | ||||||
|  | import org.bukkit.command.Command; | ||||||
|  | import org.bukkit.command.CommandSender; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | public class YearRankCommand extends ApplianceCommand<YearRank> { | ||||||
|  |     public YearRankCommand() { | ||||||
|  |         super("yearRank"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { | ||||||
|  |         sender.sendMessage(getAppliance().listYearRanks()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package eu.mhsl.craftattack.spawn.config; | package eu.mhsl.craftattack.spawn.config; | ||||||
|  |  | ||||||
| import eu.mhsl.craftattack.spawn.Main; | import eu.mhsl.craftattack.spawn.Main; | ||||||
|  | import org.bukkit.configuration.ConfigurationSection; | ||||||
| import org.bukkit.configuration.file.FileConfiguration; | import org.bukkit.configuration.file.FileConfiguration; | ||||||
| import org.bukkit.configuration.file.YamlConfiguration; | import org.bukkit.configuration.file.YamlConfiguration; | ||||||
|  |  | ||||||
| @@ -10,9 +11,11 @@ public class Configuration { | |||||||
|     private static final String configName = "config.yml"; |     private static final String configName = "config.yml"; | ||||||
|     private static final File configFile = new File(Main.instance().getDataFolder().getAbsolutePath() + "/" + configName); |     private static final File configFile = new File(Main.instance().getDataFolder().getAbsolutePath() + "/" + configName); | ||||||
|     public static FileConfiguration cfg; |     public static FileConfiguration cfg; | ||||||
|  |     public static ConfigurationSection pluginConfig; | ||||||
|  |  | ||||||
|     public static void readConfig() { |     public static void readConfig() { | ||||||
|         cfg = YamlConfiguration.loadConfiguration(configFile); |         cfg = YamlConfiguration.loadConfiguration(configFile); | ||||||
|  |         pluginConfig = cfg.getConfigurationSection("plugin"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void saveChanges() { |     public static void saveChanges() { | ||||||
|   | |||||||
| @@ -5,9 +5,14 @@ import org.bukkit.GameRule; | |||||||
| import org.bukkit.World; | import org.bukkit.World; | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  | import java.util.function.BiConsumer; | ||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
| import java.util.function.Function; | import java.util.function.Function; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  | import java.util.stream.IntStream; | ||||||
|  | import java.util.stream.Stream; | ||||||
|  |  | ||||||
| public class IteratorUtil { | public class IteratorUtil { | ||||||
|     public static void worlds(Consumer<World> world) { |     public static void worlds(Consumer<World> world) { | ||||||
| @@ -25,4 +30,25 @@ public class IteratorUtil { | |||||||
|     public static void setGameRules(Map<GameRule<Boolean>, Boolean> rules, boolean inverse) { |     public static void setGameRules(Map<GameRule<Boolean>, Boolean> rules, boolean inverse) { | ||||||
|         rules.forEach((gameRule, value) -> IteratorUtil.worlds(world -> world.setGameRule(gameRule, value ^ inverse))); |         rules.forEach((gameRule, value) -> IteratorUtil.worlds(world -> world.setGameRule(gameRule, value ^ inverse))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static void times(int times, Runnable callback) { | ||||||
|  |         IntStream.range(0, times).forEach(value -> callback.run()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static <T> void iterateListInGlobal(int sectionStart, List<T> list, BiConsumer<Integer, T> callback) { | ||||||
|  |         IntStream.range(sectionStart, sectionStart + list.size()) | ||||||
|  |             .forEach(value -> callback.accept(value, list.get(value - sectionStart))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void iterateLocalInGlobal(int sectionStart, int localLength, BiConsumer<Integer, Integer> callback) { | ||||||
|  |         IntStream.range(sectionStart, sectionStart + localLength) | ||||||
|  |             .forEach(value -> callback.accept(value, value + sectionStart)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static <T> List<T> expandList(List<T> list, int targetSize, T defaultValue) { | ||||||
|  |         return Stream.concat( | ||||||
|  |             list.stream(), | ||||||
|  |             Stream.generate(() -> defaultValue).limit(Math.max(0, targetSize - list.size())) | ||||||
|  |         ).collect(Collectors.toList()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.util.inventory; | ||||||
|  |  | ||||||
|  | import com.destroystokyo.paper.profile.PlayerProfile; | ||||||
|  | import com.destroystokyo.paper.profile.ProfileProperty; | ||||||
|  | import org.bukkit.Bukkit; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.inventory.ItemStack; | ||||||
|  | import org.bukkit.inventory.meta.SkullMeta; | ||||||
|  |  | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
|  | public class HeadBuilder { | ||||||
|  |     public static ItemStack getCustomTextureHead(String base64) { | ||||||
|  |         ItemStack head = new ItemStack(Material.PLAYER_HEAD); | ||||||
|  |         SkullMeta meta = (SkullMeta) head.getItemMeta(); | ||||||
|  |         PlayerProfile profile = Bukkit.createProfile(UUID.nameUUIDFromBytes(base64.getBytes()), null); | ||||||
|  |         profile.setProperty(new ProfileProperty("textures", base64)); | ||||||
|  |         meta.setPlayerProfile(profile); | ||||||
|  |         head.setItemMeta(meta); | ||||||
|  |         return head; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,92 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.util.inventory; | ||||||
|  |  | ||||||
|  | import eu.mhsl.craftattack.spawn.util.text.ComponentUtil; | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.inventory.ItemStack; | ||||||
|  | import org.bukkit.inventory.meta.ItemMeta; | ||||||
|  | import org.jetbrains.annotations.Contract; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Objects; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  | import java.util.function.Function; | ||||||
|  |  | ||||||
|  | public class ItemBuilder { | ||||||
|  |     private final ItemStack itemStack; | ||||||
|  |  | ||||||
|  |     @Contract(value = "_ -> new", pure = true) | ||||||
|  |     public static @NotNull ItemBuilder of(Material material) { | ||||||
|  |         return new ItemBuilder(material); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Contract(value = "_ -> new", pure = true) | ||||||
|  |     public static @NotNull ItemBuilder of(ItemStack itemStack) { | ||||||
|  |         return new ItemBuilder(itemStack); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private ItemBuilder(Material material) { | ||||||
|  |         this.itemStack = ItemStack.of(material); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private ItemBuilder(ItemStack itemStack) { | ||||||
|  |         this.itemStack = itemStack; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder displayName(Component displayName) { | ||||||
|  |         return this.withMeta(itemMeta -> itemMeta.displayName(displayName)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder displayName(Function<Component, Component> process) { | ||||||
|  |         return this.displayName(process.apply(itemStack.displayName())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder lore(String text) { | ||||||
|  |         return this.lore(text, 50, NamedTextColor.GRAY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder lore(String text, NamedTextColor color) { | ||||||
|  |         return this.lore(text, 50, color); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder lore(String text, int linebreak, NamedTextColor color) { | ||||||
|  |         return this.withMeta(itemMeta -> itemMeta.lore( | ||||||
|  |             ComponentUtil.lineBreak(text, linebreak) | ||||||
|  |                 .map(s -> Component.text(s, color)) | ||||||
|  |                 .toList() | ||||||
|  |         )); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder appendLore(Component text) { | ||||||
|  |         List<Component> lore = itemStack.lore(); | ||||||
|  |         Objects.requireNonNull(lore, "Cannot append lore to Item without lore"); | ||||||
|  |         lore.add(text); | ||||||
|  |         return this.withMeta(itemMeta -> itemMeta.lore(lore)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder noStacking() { | ||||||
|  |         return this.withMeta(itemMeta -> itemMeta.setMaxStackSize(1)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder glint() { | ||||||
|  |         return this.withMeta(itemMeta -> itemMeta.setEnchantmentGlintOverride(true)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder amount(int amount) { | ||||||
|  |         this.itemStack.setAmount(amount); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemBuilder withMeta(Consumer<ItemMeta> callback) { | ||||||
|  |         ItemMeta meta = this.itemStack.getItemMeta(); | ||||||
|  |         callback.accept(meta); | ||||||
|  |         this.itemStack.setItemMeta(meta); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ItemStack build() { | ||||||
|  |         return this.itemStack; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.util.inventory; | ||||||
|  |  | ||||||
|  | import net.kyori.adventure.text.Component; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.inventory.ItemStack; | ||||||
|  |  | ||||||
|  | public class PlaceholderItems { | ||||||
|  |     private static final Component emptyName = Component.text(" "); | ||||||
|  |     public static final ItemStack grayStainedGlassPane = ItemBuilder.of(Material.GRAY_STAINED_GLASS_PANE) | ||||||
|  |         .displayName(emptyName) | ||||||
|  |         .noStacking() | ||||||
|  |         .build(); | ||||||
|  | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package eu.mhsl.craftattack.spawn.util.server; | package eu.mhsl.craftattack.spawn.util.server; | ||||||
|  |  | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
|  | import org.geysermc.cumulus.form.SimpleForm; | ||||||
| import org.geysermc.floodgate.api.FloodgateApi; | import org.geysermc.floodgate.api.FloodgateApi; | ||||||
| import org.geysermc.floodgate.api.player.FloodgatePlayer; | import org.geysermc.floodgate.api.player.FloodgatePlayer; | ||||||
|  |  | ||||||
| @@ -24,4 +25,22 @@ public class Floodgate { | |||||||
|     public static void runJavaOnly(Player p, Consumer<Player> callback) { |     public static void runJavaOnly(Player p, Consumer<Player> callback) { | ||||||
|         if(!isBedrock(p)) callback.accept(p); |         if(!isBedrock(p)) callback.accept(p); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static void throwWithMessageWhenBedrock(Player player) { | ||||||
|  |         if(isBedrock(player)) { | ||||||
|  |             SimpleForm.builder() | ||||||
|  |                 .title("Nicht unterstützt") | ||||||
|  |                 .content("Bedrock-Spieler werden derzeit für diese Aktion unterstützt! Tut uns Leid.") | ||||||
|  |                 .button("Ok") | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |             throw new BedrockNotSupportedException(player); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class BedrockNotSupportedException extends RuntimeException { | ||||||
|  |         public BedrockNotSupportedException(Player player) { | ||||||
|  |             super(String.format("Bedrock player '%s' tried using an Operation which is unsupported.", player.getName())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import org.bukkit.Bukkit; | |||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
|  |  | ||||||
| import java.awt.*; | import java.awt.*; | ||||||
|  | import java.lang.management.OperatingSystemMXBean; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -21,6 +22,13 @@ public class ComponentUtil { | |||||||
|         return Component.text().append(a.appendNewline().append(b)).build(); |         return Component.text().append(a.appendNewline().append(b)).build(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static TextComponent appendWithSpace(Component a, Component b) { | ||||||
|  |         return Component.text().append(a).append(Component.text(" ")).append(b).build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Stream<String> lineBreak(String text) { | ||||||
|  |         return lineBreak(text, 50); | ||||||
|  |     } | ||||||
|     public static Stream<String> lineBreak(String text, int charactersPerLine) { |     public static Stream<String> lineBreak(String text, int charactersPerLine) { | ||||||
|         List<String> lines = new ArrayList<>(); |         List<String> lines = new ArrayList<>(); | ||||||
|         String[] words = text.split(" "); |         String[] words = text.split(" "); | ||||||
| @@ -46,34 +54,6 @@ public class ComponentUtil { | |||||||
|         return lines.collect(Collectors.joining("\n")); |         return lines.collect(Collectors.joining("\n")); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Component getFormattedTPS() { |  | ||||||
|         double[] tpsValues = Bukkit.getTPS(); |  | ||||||
|  |  | ||||||
|         double min1 = Math.min(1.0, Math.max(0.0, tpsValues[0] / 20.0)); |  | ||||||
|         double min2 = Math.min(1.0, Math.max(0.0, tpsValues[1] / 20.0)); |  | ||||||
|         double min3 = Math.min(1.0, Math.max(0.0, tpsValues[2] / 20.0)); |  | ||||||
|  |  | ||||||
|         int red1 = (int) (255 * (1.0 - min1)); |  | ||||||
|         int green1 = (int) (255 * min1); |  | ||||||
|         int red2 = (int) (255 * (1.0 - min2)); |  | ||||||
|         int green2 = (int) (255 * min2); |  | ||||||
|         int red3 = (int) (255 * (1.0 - min3)); |  | ||||||
|         int green3 = (int) (255 * min3); |  | ||||||
|  |  | ||||||
|         TextColor tpsColor1 = TextColor.color(red1, green1, 0); |  | ||||||
|         TextColor tpsColor2 = TextColor.color(red2, green2, 0); |  | ||||||
|         TextColor tpsColor3 = TextColor.color(red3, green3, 0); |  | ||||||
|  |  | ||||||
|         return Component.text() |  | ||||||
|             .append(Component.text("TPS 1, 5, 15m: ", NamedTextColor.GRAY)) |  | ||||||
|             .append(Component.text(String.format("%.2f", tpsValues[0]), tpsColor1)) |  | ||||||
|             .append(Component.text(", ")) |  | ||||||
|             .append(Component.text(String.format("%.2f", tpsValues[1]), tpsColor2)) |  | ||||||
|             .append(Component.text(", ")) |  | ||||||
|             .append(Component.text(String.format("%.2f", tpsValues[2]), tpsColor3)) |  | ||||||
|             .build(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static Component getFormattedTickTimes(boolean detailed) { |     public static Component getFormattedTickTimes(boolean detailed) { | ||||||
|         long[] times = Bukkit.getServer().getTickTimes(); |         long[] times = Bukkit.getServer().getTickTimes(); | ||||||
|         float mspt = ((float) Arrays.stream(times).sum() / times.length) * 1.0E-6f; |         float mspt = ((float) Arrays.stream(times).sum() / times.length) * 1.0E-6f; | ||||||
| @@ -132,14 +112,72 @@ public class ComponentUtil { | |||||||
|     public static Component getFormattedNetworkStats(NetworkMonitor.Traffic traffic, NetworkMonitor.Packets packets) { |     public static Component getFormattedNetworkStats(NetworkMonitor.Traffic traffic, NetworkMonitor.Packets packets) { | ||||||
|         return Component.text() |         return Component.text() | ||||||
|             .append(Component.text( |             .append(Component.text( | ||||||
|                 DataSizeConverter.convertBytesToHumanReadable(traffic.rxBytes()) + " ↓ " + NumberAbbreviation.abbreviateNumber(packets.rxCount()) + "pps", |                 DataSizeConverter.convertBytesPerSecond(traffic.rxBytes()) + " ↓ " + NumberAbbreviation.abbreviateNumber(packets.rxCount()) + "pps", | ||||||
|                 NamedTextColor.GREEN |                 NamedTextColor.GREEN | ||||||
|             )) |             )) | ||||||
|             .append(Component.text(" | ", NamedTextColor.GRAY)) |             .append(Component.text(" | ", NamedTextColor.GRAY)) | ||||||
|             .append(Component.text( |             .append(Component.text( | ||||||
|                 DataSizeConverter.convertBytesToHumanReadable(traffic.txBytes()) + " ↑ " + NumberAbbreviation.abbreviateNumber(packets.rxCount()) + "pps", |                 DataSizeConverter.convertBytesPerSecond(traffic.txBytes()) + " ↑ " + NumberAbbreviation.abbreviateNumber(packets.rxCount()) + "pps", | ||||||
|                 NamedTextColor.RED |                 NamedTextColor.RED | ||||||
|             )) |             )) | ||||||
|             .build(); |             .build(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static Component getFormattedSystemStats(OperatingSystemMXBean systemMonitor) { | ||||||
|  |         if(!(systemMonitor instanceof com.sun.management.OperatingSystemMXBean monitor)) | ||||||
|  |             return Component.text("Could not get System information", NamedTextColor.DARK_GRAY); | ||||||
|  |  | ||||||
|  |         return Component.text() | ||||||
|  |             .append(Component.text("proc: ", NamedTextColor.GRAY)) | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format("%.0f%%cpu", monitor.getProcessCpuLoad() * 100), | ||||||
|  |                 NamedTextColor.GOLD | ||||||
|  |             )) | ||||||
|  |             .append(Component.text(" | ", NamedTextColor.GRAY)) | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format("%s time", DataSizeConverter.formatCpuTimeToHumanReadable(monitor.getProcessCpuTime())), | ||||||
|  |                 NamedTextColor.LIGHT_PURPLE | ||||||
|  |             )) | ||||||
|  |             .append(Component.text(" | ", NamedTextColor.GRAY)) | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format( | ||||||
|  |                     "%s free, %s committed RAM", | ||||||
|  |                     DataSizeConverter.formatBytesToHumanReadable(monitor.getFreeMemorySize()), | ||||||
|  |                     DataSizeConverter.formatBytesToHumanReadable(monitor.getCommittedVirtualMemorySize()) | ||||||
|  |                 ), | ||||||
|  |                 NamedTextColor.DARK_AQUA | ||||||
|  |             )) | ||||||
|  |             .appendNewline() | ||||||
|  |             .append(Component.text("sys: ", NamedTextColor.GRAY)) | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format("%.0f%%cpu", monitor.getCpuLoad() * 100), | ||||||
|  |                 NamedTextColor.GOLD | ||||||
|  |             )) | ||||||
|  |             .append(Component.text(" | ", NamedTextColor.GRAY)) | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format( | ||||||
|  |                     "1min %.2f load avg (%.0f%%)", | ||||||
|  |                     monitor.getSystemLoadAverage(), | ||||||
|  |                     (monitor.getSystemLoadAverage() / monitor.getAvailableProcessors()) * 100 | ||||||
|  |                 ), | ||||||
|  |                 NamedTextColor.LIGHT_PURPLE | ||||||
|  |             )) | ||||||
|  |             .append(Component.text(" | ", NamedTextColor.GRAY)) | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format("%s total RAM", DataSizeConverter.formatBytesToHumanReadable(monitor.getTotalMemorySize())), | ||||||
|  |                 NamedTextColor.DARK_AQUA | ||||||
|  |             )) | ||||||
|  |             .appendNewline() | ||||||
|  |             .append(Component.text( | ||||||
|  |                 String.format( | ||||||
|  |                     "%s(%s) \uD83D\uDE80 on %s with %s cpu(s)", | ||||||
|  |                     monitor.getName(), | ||||||
|  |                     monitor.getVersion(), | ||||||
|  |                     monitor.getArch(), | ||||||
|  |                     monitor.getAvailableProcessors() | ||||||
|  |                 ), | ||||||
|  |                 NamedTextColor.GRAY | ||||||
|  |             )) | ||||||
|  |             .build(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package eu.mhsl.craftattack.spawn.util.text; | package eu.mhsl.craftattack.spawn.util.text; | ||||||
|  |  | ||||||
| public class DataSizeConverter { | public class DataSizeConverter { | ||||||
|     public static String convertBytesToHumanReadable(long bytes) { |     public static String convertBytesPerSecond(long bytes) { | ||||||
|         double kbits = bytes * 8.0 / 1000.0; |         double kbits = bytes * 8.0 / 1000.0; | ||||||
|         double mbits = kbits / 1000.0; |         double mbits = kbits / 1000.0; | ||||||
|  |  | ||||||
| @@ -11,4 +11,31 @@ public class DataSizeConverter { | |||||||
|             return String.format("%.2f Kbit", kbits); |             return String.format("%.2f Kbit", kbits); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static String formatBytesToHumanReadable(long bytes) { | ||||||
|  |         String[] units = {"B", "KB", "MB", "GB", "TB", "PB", "EB"}; | ||||||
|  |         int unitIndex = 0; | ||||||
|  |         double readableSize = bytes; | ||||||
|  |  | ||||||
|  |         while (readableSize >= 1024 && unitIndex < units.length - 1) { | ||||||
|  |             readableSize /= 1024; | ||||||
|  |             unitIndex++; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return String.format("%.2f%s", readableSize, units[unitIndex]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static String formatCpuTimeToHumanReadable(long nanoseconds) { | ||||||
|  |         if (nanoseconds < 0) return "unsupported"; | ||||||
|  |  | ||||||
|  |         long seconds = nanoseconds / 1_000_000_000; | ||||||
|  |         long minutes = seconds / 60; | ||||||
|  |         long hours = minutes / 60; | ||||||
|  |         long days = hours / 24; | ||||||
|  |  | ||||||
|  |         seconds %= 60; | ||||||
|  |         minutes %= 60; | ||||||
|  |         hours %= 60; | ||||||
|  |         return String.format("%dd%dh%dm%ds", days, hours, minutes, seconds); | ||||||
|  |     } | ||||||
| } | } | ||||||
| @@ -6,12 +6,12 @@ import net.kyori.adventure.text.format.TextColor; | |||||||
| import java.awt.*; | import java.awt.*; | ||||||
|  |  | ||||||
| public class RainbowComponent { | public class RainbowComponent { | ||||||
|     private int hueOffset = 0; |     private float hueOffset = 0; | ||||||
|     private final String text; |     private final String text; | ||||||
|     private final int density; |     private final int density; | ||||||
|     private final int speed; |     private final float speed; | ||||||
|  |  | ||||||
|     public RainbowComponent(String text, int density, int speed) { |     public RainbowComponent(String text, int density, float speed) { | ||||||
|         this.text = text; |         this.text = text; | ||||||
|         this.density = density; |         this.density = density; | ||||||
|         this.speed = speed; |         this.speed = speed; | ||||||
| @@ -19,15 +19,16 @@ public class RainbowComponent { | |||||||
|  |  | ||||||
|     public Component getRainbowState() { |     public Component getRainbowState() { | ||||||
|         Component builder = Component.empty(); |         Component builder = Component.empty(); | ||||||
|         int hue = this.hueOffset; |         float hue = this.hueOffset; | ||||||
|         for(char c : text.toCharArray()) { |         for (char c : text.toCharArray()) { | ||||||
|             TextColor color = TextColor.color(Color.getHSBColor((float) hue / 360, 1, 1).getRGB()); |             float normalizedHue = (hue % 360) / 360; | ||||||
|  |             TextColor color = TextColor.color(Color.getHSBColor(normalizedHue, 1, 1).getRGB()); | ||||||
|             builder = builder.append(Component.text(c).color(color)); |             builder = builder.append(Component.text(c).color(color)); | ||||||
|             hue += density; |             hue += density; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if(this.hueOffset > Byte.MAX_VALUE - speed) this.hueOffset = 0; |         this.hueOffset = (this.hueOffset + speed) % 360; | ||||||
|         this.hueOffset += (byte) speed; |  | ||||||
|         return builder; |         return builder; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | package eu.mhsl.craftattack.spawn.util.world; | ||||||
|  |  | ||||||
|  | import net.kyori.adventure.key.Key; | ||||||
|  | import net.kyori.adventure.sound.Sound; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  |  | ||||||
|  | public class InteractSounds { | ||||||
|  |     private final Player player; | ||||||
|  |  | ||||||
|  |     public static InteractSounds of(Player player) { | ||||||
|  |         return new InteractSounds(player); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private InteractSounds(Player player) { | ||||||
|  |         this.player = player; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void playSound(org.bukkit.Sound sound) { | ||||||
|  |         player.playSound(getSound(sound.key()), Sound.Emitter.self()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private Sound getSound(Key soundKey) { | ||||||
|  |         return Sound.sound(soundKey, Sound.Source.PLAYER, 1f, 1f); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void click() { | ||||||
|  |         playSound(org.bukkit.Sound.UI_BUTTON_CLICK); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void success() { | ||||||
|  |         playSound(org.bukkit.Sound.ENTITY_PLAYER_LEVELUP); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void delete() { | ||||||
|  |         playSound(org.bukkit.Sound.ENTITY_SILVERFISH_DEATH); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,3 +1,7 @@ | |||||||
|  | plugin: | ||||||
|  |   disabledAppliances: | ||||||
|  |     - NameOfApplianceClass | ||||||
|  |  | ||||||
| worldMuseum: | worldMuseum: | ||||||
|   uuid: |   uuid: | ||||||
|   connect-server-name: worldmuseum |   connect-server-name: worldmuseum | ||||||
| @@ -42,7 +46,7 @@ help: | |||||||
|   spawn: "Der Weltspawn befindet sich bei x:0 y:0 z:0" |   spawn: "Der Weltspawn befindet sich bei x:0 y:0 z:0" | ||||||
|  |  | ||||||
| playerLimit: | playerLimit: | ||||||
|   maxPlayers: 100 |   maxPlayers: 10 | ||||||
|  |  | ||||||
| whitelist: | whitelist: | ||||||
|   overrideIntegrityCheck: false |   overrideIntegrityCheck: false | ||||||
| @@ -50,3 +54,16 @@ whitelist: | |||||||
|  |  | ||||||
| tablist: | tablist: | ||||||
|   interface: eth0 |   interface: eth0 | ||||||
|  |  | ||||||
|  | outlawed: | ||||||
|  |   voluntarily: [] | ||||||
|  |  | ||||||
|  | packselect: | ||||||
|  |   packs: | ||||||
|  |     - somepack: | ||||||
|  |         name: "Texture pack name" | ||||||
|  |         description: "Texture pack description" | ||||||
|  |         author: "Pack Author(s)" | ||||||
|  |         url: "https://example.com/download/pack.zip" | ||||||
|  |         hash: "" # SHA1 hash of ZIP file (will be auto determined by the server on startup when not set) | ||||||
|  |         icon: "" # base64 player-head texture, can be obtained from sites like https://minecraft-heads.com/ under developers > Value | ||||||
| @@ -37,5 +37,8 @@ commands: | |||||||
|   panicBan: |   panicBan: | ||||||
|   vogelfrei: |   vogelfrei: | ||||||
|   settings: |   settings: | ||||||
|  |   texturepack: | ||||||
|  |   maintanance: | ||||||
|  |   yearRank: | ||||||
|   msg: |   msg: | ||||||
|   r: |   r: | ||||||
		Reference in New Issue
	
	Block a user