diff --git a/src/main/java/me/trouper/alias/Alias.java b/src/main/java/me/trouper/alias/Alias.java deleted file mode 100644 index f221e1a..0000000 --- a/src/main/java/me/trouper/alias/Alias.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.trouper.alias; - -import me.trouper.alias.data.Common; -import me.trouper.alias.server.AutoRegistrar; -import me.trouper.alias.server.commands.QuickCommand; -import me.trouper.alias.server.events.GuiListener; -import me.trouper.alias.server.events.QuickListener; -import me.trouper.alias.update.AutoUpdater; -import org.bukkit.plugin.java.JavaPlugin; - -public final class Alias extends JavaPlugin { - - private static Class host; - private static AutoRegistrar autoRegistrar; - private static Common common; - private static boolean enabled; - - - public static synchronized void register(JavaPlugin plugin, Common common) { - if (plugin == null || enabled) return; - Alias.host = plugin.getClass(); - Alias.common = common; - - AutoUpdater.checkUpdate(plugin,common); - - autoRegistrar = new AutoRegistrar(plugin); - autoRegistrar.getQuickListeners().add(new GuiListener()); - autoRegistrar.loadAll(common.getPackageName()); - - enabled = true; - } - - public static synchronized void stop(JavaPlugin plugin, Common common) { - autoRegistrar.getQuickListeners().forEach(QuickListener::unregister); - autoRegistrar.getQuickCommands().forEach(QuickCommand::disable); - AutoUpdater.checkUpdate(plugin,common); - } - - public static Class getHost() { - return host; - } - - public static AutoRegistrar getAutoRegistrar() { - return autoRegistrar; - } - - public static Common getCommon() { - return common; - } - - public static void updateCommon(Common common) { - Alias.common = common; - } -} diff --git a/src/main/java/me/trouper/alias/AliasContext.java b/src/main/java/me/trouper/alias/AliasContext.java new file mode 100644 index 0000000..4af82c9 --- /dev/null +++ b/src/main/java/me/trouper/alias/AliasContext.java @@ -0,0 +1,99 @@ +package me.trouper.alias; + +import me.trouper.alias.data.Common; +import me.trouper.alias.data.DataManager; +import me.trouper.alias.data.JsonSerializable; +import me.trouper.alias.server.AutoRegistrar; +import me.trouper.alias.server.events.listeners.GuiListener; +import me.trouper.alias.server.events.listeners.SpawnListener; +import me.trouper.alias.server.events.listeners.WandListener; +import me.trouper.alias.server.systems.TaskManager; +import me.trouper.alias.server.systems.Text; +import me.trouper.alias.server.systems.Verbose; +import me.trouper.alias.server.systems.display.DisplayManager; +import me.trouper.alias.server.update.AutoUpdater; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.ArrayList; +import java.util.List; + +public class AliasContext { + private final JavaPlugin plugin; + private final Common common; + private final AutoRegistrar autoRegistrar; + private final AutoUpdater autoUpdater; + private final DataManager dataManager; + private final Text text; + private final Verbose verbose; + private final DisplayManager displayManager; + private boolean enabled = false; + + public AliasContext(JavaPlugin plugin, Common common) { + this.plugin = plugin; + this.common = common; + this.autoRegistrar = new AutoRegistrar(this); + this.autoUpdater = new AutoUpdater(this); + this.dataManager = new DataManager(this); + this.text = new Text(this); + this.verbose = new Verbose(this); + this.displayManager = new DisplayManager(this); + } + + /** + * Initialize the Alias context and register all components. + * This must be called before using any Alias features. + * Alias should be registered first. + * See {@link AliasContextProvider#registerContext(JavaPlugin, AliasContext)} + */ + public synchronized void initialize() { + if (enabled) return; + + plugin.getLogger().info("Initializing Alias context for " + plugin.getName()); + + autoUpdater.checkUpdate(); + + autoRegistrar.loadAll(common.getPackageName()); + Bukkit.getPluginManager().registerEvents(new GuiListener(),getPlugin()); + Bukkit.getPluginManager().registerEvents(new SpawnListener(this),getPlugin()); + Bukkit.getPluginManager().registerEvents(new WandListener(this),getPlugin()); + List> copy = new ArrayList<>(autoRegistrar.getSerializables()); + for (JsonSerializable serializable : copy) { + dataManager.load(serializable.getClass()); + } + + enabled = true; + plugin.getLogger().info("Alias context initialized successfully"); + } + + /** + * Shutdown the Alias context, save any {@link me.trouper.alias.data.JsonSerializable} and release resources. + * This should be called when the plugin is shutting down. + */ + public synchronized void shutdown() { + if (!enabled) return; + + plugin.getLogger().info("Shutting down Alias context for " + plugin.getName()); + + autoRegistrar.getSerializables().forEach(jsonSerializable -> { + dataManager.save(jsonSerializable.getClass()); + }); + autoRegistrar.unregisterAll(); + + autoUpdater.checkUpdate(); + + enabled = false; + plugin.getLogger().info("Alias context shutdown complete"); + } + + public JavaPlugin getPlugin() { return plugin; } + public Common getCommon() { return common; } + public AutoRegistrar getAutoRegistrar() { return autoRegistrar; } + public Text getText() { return text; } + public boolean isEnabled() { return enabled; } + public TaskManager createTaskManager() { return new TaskManager(this); } + public Verbose getVerbose() { return verbose; } + public DisplayManager getDisplayManager() { return displayManager; } + public DataManager getDataManager() { return dataManager; } + public AutoUpdater getAutoUpdater() { return autoUpdater; } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/alias/AliasContextProvider.java b/src/main/java/me/trouper/alias/AliasContextProvider.java new file mode 100644 index 0000000..6f18e6a --- /dev/null +++ b/src/main/java/me/trouper/alias/AliasContextProvider.java @@ -0,0 +1,44 @@ +package me.trouper.alias; + +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class AliasContextProvider { + private static final Map, AliasContext> contexts = new ConcurrentHashMap<>(); + + /** + * Register a context for a plugin + * This must be called BEFORE {@link AliasContext#initialize()}. + */ + public static void registerContext(JavaPlugin plugin, AliasContext context) { + contexts.put(plugin.getClass(), context); + } + + /** + * Get context for a plugin class + */ + public static AliasContext getContext(Class pluginClass) { + AliasContext context = contexts.get(pluginClass); + if (context == null) { + throw new RuntimeException("No Alias context registered for " + pluginClass.getSimpleName() + + ". Make sure to call AliasContext.initialize() in your plugin's onEnable() method!"); + } + return context; + } + + /** + * Remove context for a plugin + */ + public static void removeContext(Class pluginClass) { + contexts.remove(pluginClass); + } + + /** + * Check if context exists for a plugin + */ + public static boolean hasContext(Class pluginClass) { + return contexts.containsKey(pluginClass); + } +} diff --git a/src/main/java/me/trouper/alias/data/DataManager.java b/src/main/java/me/trouper/alias/data/DataManager.java new file mode 100644 index 0000000..460f37f --- /dev/null +++ b/src/main/java/me/trouper/alias/data/DataManager.java @@ -0,0 +1,72 @@ +package me.trouper.alias.data; + +import me.trouper.alias.AliasContext; +import me.trouper.alias.server.AutoRegistrar; + +import java.util.List; +import java.util.Optional; + +/** + * Manages loading, retrieving, and saving of JsonSerializable instances + * registered via AutoRegistrar. + */ +public class DataManager { + private final AutoRegistrar registrar; + + public DataManager(AliasContext context) { + this.registrar = context.getAutoRegistrar(); + } + + /** + * Loads the data for the given JsonSerializable type from file, updates the registry, + * and returns the loaded instance (or fallback if loading failed). + * + * @param clazz the class type to load + * @param type extending JsonSerializable + * @return loaded or fallback instance + */ + public > T load(Class clazz) { + Optional opt = getOptional(clazz); + if (opt.isEmpty()) { + return null; + } + T instance = opt.get(); + T loaded = JsonSerializable.load(instance.getFile(), clazz, instance); + + if (loaded != instance) { + List> list = registrar.getSerializables(); + list.remove(instance); + list.add(loaded); + } + return loaded; + } + + /** + * Retrieves the registered JsonSerializable instance without loading. + * + * @param clazz the class type to retrieve + * @param type extending JsonSerializable + * @return instance or null if not found + */ + public > T get(Class clazz) { + return getOptional(clazz).orElse(null); + } + + private > Optional getOptional(Class clazz) { + return registrar.getSerializables().stream() + .filter(clazz::isInstance) + .map(clazz::cast) + .findFirst(); + } + + /** + * Saves the registered JsonSerializable instance to its file. + * Does nothing if the type is not registered. + * + * @param clazz the class type to save + * @param type extending JsonSerializable + */ + public > void save(Class clazz) { + getOptional(clazz).ifPresent(JsonSerializable::save); + } +} diff --git a/src/main/java/me/trouper/alias/data/JsonSerializable.java b/src/main/java/me/trouper/alias/data/JsonSerializable.java index b44dd66..26ff814 100755 --- a/src/main/java/me/trouper/alias/data/JsonSerializable.java +++ b/src/main/java/me/trouper/alias/data/JsonSerializable.java @@ -1,6 +1,9 @@ package me.trouper.alias.data; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import me.trouper.alias.utils.misc.FileValidationUtils; import java.io.*; diff --git a/src/main/java/me/trouper/alias/server/AutoRegistrar.java b/src/main/java/me/trouper/alias/server/AutoRegistrar.java index ef6f2c3..8f7e213 100644 --- a/src/main/java/me/trouper/alias/server/AutoRegistrar.java +++ b/src/main/java/me/trouper/alias/server/AutoRegistrar.java @@ -1,5 +1,8 @@ package me.trouper.alias.server; +import me.trouper.alias.AliasContext; +import me.trouper.alias.data.DataManager; +import me.trouper.alias.data.JsonSerializable; import me.trouper.alias.server.commands.QuickCommand; import me.trouper.alias.server.events.QuickListener; import me.trouper.alias.server.systems.AbstractWand; @@ -13,66 +16,77 @@ import java.util.Set; import java.util.logging.Level; public class AutoRegistrar { - private final JavaPlugin plugin; private final List quickCommands = new ArrayList<>(); private final List quickListeners = new ArrayList<>(); private final List wands = new ArrayList<>(); + private final List> serializables = new ArrayList<>(); - public AutoRegistrar(JavaPlugin plugin) { - this.plugin = plugin; + public AutoRegistrar(AliasContext context) { + this.plugin = context.getPlugin(); } public void loadAll(String basePackage) { Set> classes = ReflectionUtils.getClassesInPackage(plugin, basePackage); for (Class clazz : classes) { - if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()) || clazz.isEnum() || clazz.isAnnotation()) { - continue; - } + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()) || + clazz.isEnum() || clazz.isAnnotation()) continue; boolean isCommand = QuickCommand.class.isAssignableFrom(clazz); boolean isWand = AbstractWand.class.isAssignableFrom(clazz); boolean isListener = QuickListener.class.isAssignableFrom(clazz); + boolean isSerializable = JsonSerializable.class.isAssignableFrom(clazz); - if (!isCommand && !isWand && !isListener) continue; + if (!isCommand && !isWand && !isListener && !isSerializable) continue; + + Registrar registrarFlags = clazz.getAnnotation(Registrar.class); + if (registrarFlags != null && registrarFlags.exclude()) { + plugin.getLogger().info("Excluding " + clazz.getSimpleName() + " from the Registrar."); + continue; + } try { Object instance = clazz.getDeclaredConstructor().newInstance(); if (instance instanceof QuickCommand command) { - command.register(); quickCommands.add(command); - plugin.getLogger().info("Registered QuickCommand: " + clazz.getSimpleName()); + plugin.getLogger().info("Found QuickCommand: " + clazz.getSimpleName()); + } + + if (instance instanceof QuickListener listener && !(instance instanceof AbstractWand)) { + quickListeners.add(listener); + plugin.getLogger().info("Found QuickListener: " + clazz.getSimpleName()); } if (instance instanceof AbstractWand wand) { - wand.register(); wands.add(wand); - plugin.getLogger().info("Registered AbstractWand: " + clazz.getSimpleName()); + plugin.getLogger().info("Found AbstractWand: " + clazz.getSimpleName()); } - else if (instance instanceof QuickListener listener) { - listener.register(); - quickListeners.add(listener); - plugin.getLogger().info("Registered QuickListener: " + clazz.getSimpleName()); + if (instance instanceof JsonSerializable js) { + serializables.add(js); + plugin.getLogger().info("Found JsonSerializable: " + clazz.getSimpleName()); } } catch (Throwable t) { plugin.getLogger().log(Level.WARNING, "Failed to instantiate: " + clazz.getName(), t); } } + + quickListeners.forEach(QuickListener::register); + quickCommands.forEach(QuickCommand::register); } - public List getQuickCommands() { - return quickCommands; + public void unregisterAll() { + quickListeners.forEach(QuickListener::unregister); + quickListeners.clear(); + quickCommands.forEach(QuickCommand::disable); + quickCommands.clear(); } - public List getQuickListeners() { - return quickListeners; - } - - public List getWands() { - return wands; - } + public List getQuickCommands() { return quickCommands; } + public List getQuickListeners() { return quickListeners; } + public List getWands() { return wands; } + public List> getSerializables() { return serializables; } } diff --git a/src/main/java/me/trouper/alias/server/ContextAware.java b/src/main/java/me/trouper/alias/server/ContextAware.java new file mode 100644 index 0000000..95edf08 --- /dev/null +++ b/src/main/java/me/trouper/alias/server/ContextAware.java @@ -0,0 +1,104 @@ +package me.trouper.alias.server; + +import me.trouper.alias.AliasContext; +import me.trouper.alias.AliasContextProvider; +import me.trouper.alias.data.Common; +import me.trouper.alias.data.DataManager; +import me.trouper.alias.server.systems.Text; +import me.trouper.alias.server.systems.Verbose; +import me.trouper.alias.server.systems.display.DisplayManager; +import me.trouper.alias.utils.misc.Randomizer; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Random; + +public interface ContextAware { + + /** + * Get the plugin class this component belongs to. + * Easiest thing to avoid repeating this method is to make a "PluginContext" interface and fill out this method. + */ + Class getPluginClass(); + + default AliasContext getContext() { + return AliasContextProvider.getContext(getPluginClass()); + } + + default JavaPlugin getPlugin() { + return getContext().getPlugin(); + } + + default Common getCommon() { + return getContext().getCommon(); + } + + default Text getTextSystem() { + return getContext().getText(); + } + + default DisplayManager getDisplayManager() { + return getContext().getDisplayManager(); + } + + default Verbose getVerbose() { + return getContext().getVerbose(); + } + + default AutoRegistrar getAutoRegistrar() { + return getContext().getAutoRegistrar(); + } + + default DataManager getDataManager() { + return getContext().getDataManager(); + } + + default void info(Audience audience, Component message, Component... args) { + getTextSystem().message(Text.Pallet.INFO, audience, message, args); + } + + default void error(Audience audience, Component message, Component... args) { + getTextSystem().message(Text.Pallet.ERROR, audience, message, args); + } + + default void warning(Audience audience, Component message, Component... args) { + getTextSystem().message(Text.Pallet.WARNING, audience, message, args); + } + + default void success(Audience audience, Component message, Component... args) { + getTextSystem().message(Text.Pallet.SUCCESS, audience, message, args); + } + + default void message(Audience audience, Component message, Component... args) { + getTextSystem().message(Text.Pallet.NEUTRAL, audience, message, args); + } + + default void infoAny(Audience audience, String message, Object... args) { + getTextSystem().messageAny(Text.Pallet.INFO, audience, message, args); + } + + default void errorAny(Audience audience, String message, Object... args) { + getTextSystem().messageAny(Text.Pallet.ERROR, audience, message, args); + } + + default void warningAny(Audience audience, String message, Object... args) { + getTextSystem().messageAny(Text.Pallet.WARNING, audience, message, args); + } + + default void successAny(Audience audience, String message, Object... args) { + getTextSystem().messageAny(Text.Pallet.SUCCESS, audience, message, args); + } + + default void messageAny(Audience audience, String message, Object... args) { + getTextSystem().messageAny(Text.Pallet.NEUTRAL, audience, message, args); + } + + default Random random() { + return new Random(); + } + + default Randomizer randomizer() { + return new Randomizer(); + } +} diff --git a/src/main/java/me/trouper/alias/server/Main.java b/src/main/java/me/trouper/alias/server/Main.java deleted file mode 100644 index eb01b89..0000000 --- a/src/main/java/me/trouper/alias/server/Main.java +++ /dev/null @@ -1,83 +0,0 @@ -package me.trouper.alias.server; - -import io.papermc.paper.registry.RegistryAccess; -import me.trouper.alias.Alias; -import me.trouper.alias.data.Common; -import me.trouper.alias.server.systems.Text; -import me.trouper.alias.utils.misc.Randomizer; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.Component; -import org.bukkit.plugin.java.JavaPlugin; - -import java.util.Random; - -public interface Main { - Main main = new Main() {}; - - default RegistryAccess getRegistryAccess() { - return RegistryAccess.registryAccess(); - } - - default JavaPlugin getPlugin() { - Class host = Alias.getHost(); - if (host == null) throw new RuntimeException("Alias is not enabled. Make sure to call Alias#register() in your JavaPlugin#onLoad() method!"); - return getPlugin(host); - } - - default T getPlugin(Class pluginClass) { - return JavaPlugin.getPlugin(pluginClass); - } - - default Common getCommon() { - return Alias.getCommon(); - } - - default void infoAny(Audience player, String message, Object... args) { - Text.messageAny(Text.Pallet.INFO, player, message, args); - } - - default void errorAny(Audience player, String message, Object... args) { - Text.messageAny(Text.Pallet.ERROR,player, message, args); - } - - default void warningAny(Audience player, String message, Object... args) { - Text.messageAny(Text.Pallet.WARNING, player, message, args); - } - - default void successAny(Audience player, String message, Object... args) { - Text.messageAny(Text.Pallet.SUCCESS, player, message, args); - } - - default void messageAny(Audience player, String message, Object... args) { - Text.messageAny(Text.Pallet.NEUTRAL, player, message, args); - } - - default void info(Audience player, Component message, Component... args) { - Text.message(Text.Pallet.INFO, player, message, args); - } - - default void error(Audience player, Component message, Component... args) { - Text.message(Text.Pallet.ERROR,player, message, args); - } - - default void warning(Audience player, Component message, Component... args) { - Text.message(Text.Pallet.WARNING, player, message, args); - } - - default void success(Audience player, Component message, Component... args) { - Text.message(Text.Pallet.SUCCESS, player, message, args); - } - - default void message(Audience player, Component message, Component... args) { - Text.message(Text.Pallet.NEUTRAL, player, message, args); - } - - default Random random() { - return new Random(); - } - - default Randomizer randomizer() { - return new Randomizer(); - } - -} diff --git a/src/main/java/me/trouper/alias/server/Registrar.java b/src/main/java/me/trouper/alias/server/Registrar.java new file mode 100644 index 0000000..abfefef --- /dev/null +++ b/src/main/java/me/trouper/alias/server/Registrar.java @@ -0,0 +1,12 @@ +package me.trouper.alias.server; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Registrar { + boolean exclude() default false; +} diff --git a/src/main/java/me/trouper/alias/server/commands/QuickCommand.java b/src/main/java/me/trouper/alias/server/commands/QuickCommand.java index dd0f81c..cc8e5a8 100644 --- a/src/main/java/me/trouper/alias/server/commands/QuickCommand.java +++ b/src/main/java/me/trouper/alias/server/commands/QuickCommand.java @@ -1,6 +1,6 @@ package me.trouper.alias.server.commands; -import me.trouper.alias.server.Main; +import me.trouper.alias.server.ContextAware; import me.trouper.alias.server.commands.completions.CompletionBuilder; import me.trouper.alias.server.commands.completions.CompletionNode; import net.kyori.adventure.text.Component; @@ -12,8 +12,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public interface QuickCommand extends TabExecutor, Main { - +public interface QuickCommand extends TabExecutor, ContextAware { void handleCommand(CommandSender sender, Command command, String label, Args args); void handleCompletion(CommandSender sender, Command command, String label, Args args, CompletionBuilder b); @@ -23,6 +22,7 @@ public interface QuickCommand extends TabExecutor, Main { PluginCommand command = getPlugin().getCommand(registry.value()); if (command != null) { + getPlugin().getLogger().info("Registering Command from " + this.getClass().getSimpleName()); command.setExecutor(this); command.setTabCompleter(this); } diff --git a/src/main/java/me/trouper/alias/server/commands/QuickCommandListener.java b/src/main/java/me/trouper/alias/server/commands/QuickCommandListener.java index f3390e3..f3a9852 100644 --- a/src/main/java/me/trouper/alias/server/commands/QuickCommandListener.java +++ b/src/main/java/me/trouper/alias/server/commands/QuickCommandListener.java @@ -3,6 +3,7 @@ package me.trouper.alias.server.commands; import me.trouper.alias.server.events.QuickListener; public interface QuickCommandListener extends QuickCommand, QuickListener { + @Override default void register() { QuickCommand.super.register(); diff --git a/src/main/java/me/trouper/alias/server/events/QuickListener.java b/src/main/java/me/trouper/alias/server/events/QuickListener.java index 6c6e453..1baea0e 100644 --- a/src/main/java/me/trouper/alias/server/events/QuickListener.java +++ b/src/main/java/me/trouper/alias/server/events/QuickListener.java @@ -1,13 +1,15 @@ package me.trouper.alias.server.events; -import me.trouper.alias.server.Main; +import me.trouper.alias.server.ContextAware; import org.bukkit.Bukkit; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -public interface QuickListener extends Listener, Main { +public interface QuickListener extends Listener, ContextAware { + default void register() { - Bukkit.getPluginManager().registerEvents(this,main.getPlugin()); + getPlugin().getLogger().info("Registering Listeners from " + this.getClass().getSimpleName()); + Bukkit.getPluginManager().registerEvents(this,getPlugin()); } default void unregister() { diff --git a/src/main/java/me/trouper/alias/server/events/custom/PlayerSpawnEntityEvent.java b/src/main/java/me/trouper/alias/server/events/custom/PlayerSpawnEntityEvent.java new file mode 100644 index 0000000..fcffa87 --- /dev/null +++ b/src/main/java/me/trouper/alias/server/events/custom/PlayerSpawnEntityEvent.java @@ -0,0 +1,46 @@ +package me.trouper.alias.server.events.custom; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class PlayerSpawnEntityEvent extends Event implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private final Player player; + private final Entity spawnedEntity; + private boolean cancelled = false; + + public PlayerSpawnEntityEvent(Player player, Entity spawnedEntity) { + this.player = player; + this.spawnedEntity = spawnedEntity; + } + + public Player getPlayer() { + return player; + } + + public Entity getSpawnedEntity() { + return spawnedEntity; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/alias/server/events/GuiListener.java b/src/main/java/me/trouper/alias/server/events/listeners/GuiListener.java similarity index 82% rename from src/main/java/me/trouper/alias/server/events/GuiListener.java rename to src/main/java/me/trouper/alias/server/events/listeners/GuiListener.java index 6953b93..fc7b14c 100644 --- a/src/main/java/me/trouper/alias/server/events/GuiListener.java +++ b/src/main/java/me/trouper/alias/server/events/listeners/GuiListener.java @@ -1,14 +1,17 @@ -package me.trouper.alias.server.events; +package me.trouper.alias.server.events.listeners; +import me.trouper.alias.server.events.QuickListener; import me.trouper.alias.server.systems.gui.QuickGui; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; -public class GuiListener implements QuickListener { +public class GuiListener implements Listener { @EventHandler(priority = EventPriority.NORMAL) public void onInventoryClick(InventoryClickEvent event) { @@ -31,5 +34,4 @@ public class GuiListener implements QuickListener { gui.getViewers().remove(event.getPlayer()); }); } - } diff --git a/src/main/java/me/trouper/alias/server/events/listeners/SpawnListener.java b/src/main/java/me/trouper/alias/server/events/listeners/SpawnListener.java new file mode 100644 index 0000000..bb4ff42 --- /dev/null +++ b/src/main/java/me/trouper/alias/server/events/listeners/SpawnListener.java @@ -0,0 +1,102 @@ +package me.trouper.alias.server.events.listeners; + +import me.trouper.alias.AliasContext; +import me.trouper.alias.server.events.custom.PlayerSpawnEntityEvent; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.entity.ProjectileLaunchEvent; + +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class SpawnListener implements Listener { + + private final AliasContext context; + private final ConcurrentLinkedQueue recentBlocks = new ConcurrentLinkedQueue<>(); + private final Map pearlOwners = new ConcurrentHashMap<>(); + + public SpawnListener(AliasContext context) { + this.context = context; + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), this::cleanup, 20, 20); + } + + private void cleanup() { + long cutoff = System.currentTimeMillis() - 2000; + Iterator it = recentBlocks.iterator(); + while (it.hasNext() && it.next().time < cutoff) { + it.remove(); + } + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + recentBlocks.add(new Placed(e.getPlayer().getUniqueId(), e.getBlockPlaced().getLocation(), System.currentTimeMillis())); + } + + @EventHandler + public void onProjectileLaunch(ProjectileLaunchEvent e) { + if (!(e.getEntity() instanceof EnderPearl)) return; + if (!(e.getEntity().getShooter() instanceof Player p)) return; + pearlOwners.put(e.getEntity().getUniqueId(), p.getUniqueId()); + Bukkit.getScheduler().runTaskLaterAsynchronously( + context.getPlugin(), + () -> pearlOwners.remove(e.getEntity().getUniqueId()), + 100L + ); + } + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent e) { + SpawnReason reason = e.getSpawnReason(); + Player spawner = null; + + if (reason == SpawnReason.BUILD_IRONGOLEM + || reason == SpawnReason.BUILD_SNOWMAN + || reason == SpawnReason.BUILD_WITHER) { + + Location spawnLoc = e.getEntity().getLocation(); + long now = System.currentTimeMillis(); + for (Placed p : recentBlocks) { + if (p.time > now - 2000 && p.loc.getWorld().equals(spawnLoc.getWorld()) + && p.loc.distanceSquared(spawnLoc) < 4) { + spawner = Bukkit.getPlayer(p.playerId); + break; + } + } + } else if (reason == SpawnReason.ENDER_PEARL) { + for (Map.Entry en : pearlOwners.entrySet()) { + Entity pearl = Bukkit.getEntity(en.getKey()); + if (pearl != null && pearl.getLocation().distanceSquared(e.getEntity().getLocation()) < 4) { + spawner = Bukkit.getPlayer(en.getValue()); + break; + } + } + } + + if (spawner == null) return; + + PlayerSpawnEntityEvent pse = new PlayerSpawnEntityEvent(spawner, e.getEntity()); + Bukkit.getPluginManager().callEvent(pse); + if (pse.isCancelled()) { + e.setCancelled(true); + } + } + + private static class Placed { + final UUID playerId; + final Location loc; + final long time; + Placed(UUID p, Location l, long t) { playerId = p; loc = l; time = t; } + } +} diff --git a/src/main/java/me/trouper/alias/server/events/listeners/WandListener.java b/src/main/java/me/trouper/alias/server/events/listeners/WandListener.java new file mode 100644 index 0000000..6cb0ae3 --- /dev/null +++ b/src/main/java/me/trouper/alias/server/events/listeners/WandListener.java @@ -0,0 +1,168 @@ +package me.trouper.alias.server.events.listeners; + +import me.trouper.alias.AliasContext; +import me.trouper.alias.server.systems.AbstractWand; +import me.trouper.alias.utils.misc.Cooldown; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.*; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.UUID; + +public class WandListener implements Listener { + + private final AliasContext context; + private final Cooldown debounce = new Cooldown<>(); + + public WandListener(AliasContext context) { + this.context = context; + } + + private List getWands() { + return context.getAutoRegistrar().getWands(); + } + + private AbstractWand getWandForItem(ItemStack item) { + if (item == null) return null; + + for (AbstractWand wand : getWands()) { + if (item.isSimilar(wand.getWandItem())) { + return wand; + } + } + return null; + } + + private AbstractWand getWandForPlayer(Player p) { + ItemStack inMain = p.getInventory().getItemInMainHand(); + ItemStack inOff = p.getInventory().getItemInOffHand(); + + AbstractWand wand = getWandForItem(inMain); + if (wand != null) return wand; + + return getWandForItem(inOff); + } + + @EventHandler + public final void onSwapHands(PlayerSwapHandItemsEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + e.setCancelled(true); + + wand.swapHand(p); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onInteract(PlayerInteractEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + Action action = e.getAction(); + + switch (action) { + case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> { + e.setCancelled(true); + + if (debounce.isOnCooldown(p.getUniqueId())) return; + debounce.setCooldown(p.getUniqueId(), 100); + + wand.rightClick(p); + } + case LEFT_CLICK_AIR, LEFT_CLICK_BLOCK -> { + e.setCancelled(true); + wand.leftClick(p); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onEntityDamage(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p)) return; + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + e.setCancelled(true); + wand.leftClick(p); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onEntityInteract(PlayerInteractEntityEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + e.setCancelled(true); + + if (debounce.isOnCooldown(p.getUniqueId())) return; + debounce.setCooldown(p.getUniqueId(), 100); + + wand.rightClick(p); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onEntityInteractAt(PlayerInteractAtEntityEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + if (debounce.isOnCooldown(p.getUniqueId())) return; + debounce.setCooldown(p.getUniqueId(), 100); + + e.setCancelled(true); + wand.rightClick(p); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onBlockPlace(BlockPlaceEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForItem(e.getItemInHand()); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public final void onItemDrop(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForItem(e.getItemDrop().getItemStack()); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + e.setCancelled(true); + } + + @EventHandler + public final void onScroll(PlayerItemHeldEvent e) { + Player p = e.getPlayer(); + AbstractWand wand = getWandForPlayer(p); + if (wand == null || !p.hasPermission(wand.getUsePermission())) return; + + int prev = e.getPreviousSlot(); + int curr = e.getNewSlot(); + + if (!p.isSneaking() || getWandForItem(p.getInventory().getItem(prev)) == null) return; + e.setCancelled(true); + + if (curr == 8 && prev == 0) wand.onScrollUp(p); + else if (curr == 0 && prev == 8) wand.onScrollDown(p); + else if (curr < prev) wand.onScrollUp(p); + else if (curr > prev) wand.onScrollDown(p); + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/alias/server/systems/AbstractWand.java b/src/main/java/me/trouper/alias/server/systems/AbstractWand.java index 7dc36ac..9d81762 100644 --- a/src/main/java/me/trouper/alias/server/systems/AbstractWand.java +++ b/src/main/java/me/trouper/alias/server/systems/AbstractWand.java @@ -1,27 +1,12 @@ package me.trouper.alias.server.systems; -import me.trouper.alias.server.Main; -import me.trouper.alias.server.events.QuickListener; -import me.trouper.alias.utils.misc.Cooldown; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; -import java.util.UUID; - -public abstract class AbstractWand implements QuickListener, Main { +public abstract class AbstractWand { private final String usePermission; private final ItemStack wandItem; - private final Cooldown debounce = new Cooldown<>(); public AbstractWand(String usePermission, ItemStack wandItem) { this.wandItem = wandItem.clone(); @@ -31,126 +16,11 @@ public abstract class AbstractWand implements QuickListener, Main { public String getUsePermission() { return usePermission; } + public ItemStack getWandItem() { return wandItem.clone(); } - private boolean isWand(ItemStack item) { - return item != null && item.isSimilar(wandItem); - } - - private boolean isHoldingWand(Player p) { - ItemStack inMain = p.getInventory().getItemInMainHand(); - ItemStack inOff = p.getInventory().getItemInOffHand(); - return isWand(inMain) || isWand(inOff); - } - - @EventHandler - public final void onSwapHands(PlayerSwapHandItemsEvent e) { - Player p = e.getPlayer(); - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - e.setCancelled(true); - - swapHand(p); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onInteract(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - - Action action = e.getAction(); - - switch (action) { - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> { - e.setCancelled(true); - - if (debounce.isOnCooldown(p.getUniqueId())) return; - debounce.setCooldown(p.getUniqueId(),100); - - rightClick(p); - } - case LEFT_CLICK_AIR, LEFT_CLICK_BLOCK -> { - e.setCancelled(true); - leftClick(p); - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onEntityDamage(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p)) return; - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - - e.setCancelled(true); - leftClick(p); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onEntityInteract(PlayerInteractEntityEvent e) { - Player p = e.getPlayer(); - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - e.setCancelled(true); - - if (debounce.isOnCooldown(p.getUniqueId())) return; - debounce.setCooldown(p.getUniqueId(),100); - - rightClick(p); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onEntityInteractAt(PlayerInteractAtEntityEvent e) { - Player p = e.getPlayer(); - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - - if (debounce.isOnCooldown(p.getUniqueId())) return; - debounce.setCooldown(p.getUniqueId(),100); - - e.setCancelled(true); - rightClick(p); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onBlockBreak(BlockBreakEvent e) { - Player p = e.getPlayer(); - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - - e.setCancelled(true); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onBlockPlace(BlockPlaceEvent e) { - Player p = e.getPlayer(); - if (!isWand(e.getItemInHand()) || !p.hasPermission(getUsePermission())) return; - - e.setCancelled(true); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public final void onItemDrop(PlayerDropItemEvent e) { - Player p = e.getPlayer(); - if (!isWand(e.getItemDrop().getItemStack()) || !p.hasPermission(getUsePermission())) return; - - e.setCancelled(true); - } - - @EventHandler - public final void onScroll(PlayerItemHeldEvent e) { - Player p = e.getPlayer(); - if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return; - - int prev = e.getPreviousSlot(); - int curr = e.getNewSlot(); - - if (!p.isSneaking() || !isWand(p.getInventory().getItem(prev))) return; - e.setCancelled(true); - - if (curr == 8 && prev == 0) onScrollUp(p); - else if (curr == 0 && prev == 8) onScrollDown(p); - else if (curr < prev) onScrollUp(p); - else if (curr > prev) onScrollDown(p); - } - public final void swapHand(Player p) { if (p.isSneaking()) onSwapHandSneak(p); else onSwapHand(p); @@ -166,20 +36,20 @@ public abstract class AbstractWand implements QuickListener, Main { else onRightClick(p); } - protected void onSwapHand(Player player) {} - protected void onSwapHandSneak(Player player) {} - protected void onRightClick(Player player) {} - protected void onRightClickSneak(Player player) {} - protected void onLeftClick(Player player) {} - protected void onLeftClickSneak(Player player) {} + public void onSwapHand(Player player) {} + public void onSwapHandSneak(Player player) {} + public void onRightClick(Player player) {} + public void onRightClickSneak(Player player) {} + public void onLeftClick(Player player) {} + public void onLeftClickSneak(Player player) {} /** * The player must be sneaking to scroll the wand. */ - protected void onScrollUp(Player player) {} + public void onScrollUp(Player player) {} /** * The player must be sneaking to scroll the wand. */ - protected void onScrollDown(Player player) {} + public void onScrollDown(Player player) {} } \ No newline at end of file diff --git a/src/main/java/me/trouper/alias/server/systems/TaskManager.java b/src/main/java/me/trouper/alias/server/systems/TaskManager.java index dc2b076..0562be0 100644 --- a/src/main/java/me/trouper/alias/server/systems/TaskManager.java +++ b/src/main/java/me/trouper/alias/server/systems/TaskManager.java @@ -1,20 +1,25 @@ package me.trouper.alias.server.systems; -import me.trouper.alias.server.Main; +import me.trouper.alias.AliasContext; import org.bukkit.Bukkit; import java.io.Closeable; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.HashMap; +import java.util.Map; -public class TaskManager implements Closeable, Main { +public class TaskManager implements Closeable { + private final AliasContext context; private final Map tasks = new HashMap<>(); private volatile boolean closed = false; + public TaskManager(AliasContext context) { + this.context = context; + } + public int scheduleTask(Runnable task, long delay) { if (closed) return -1; - int taskId = Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> { + int taskId = Bukkit.getScheduler().runTaskLater(context.getPlugin(), () -> { if (!closed) { task.run(); } diff --git a/src/main/java/me/trouper/alias/server/systems/Text.java b/src/main/java/me/trouper/alias/server/systems/Text.java index a3babef..6ae0579 100644 --- a/src/main/java/me/trouper/alias/server/systems/Text.java +++ b/src/main/java/me/trouper/alias/server/systems/Text.java @@ -1,7 +1,6 @@ package me.trouper.alias.server.systems; -import me.trouper.alias.Alias; -import me.trouper.alias.server.Main; +import me.trouper.alias.AliasContext; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; @@ -18,8 +17,12 @@ import org.bukkit.entity.Player; import java.util.*; -public class Text implements Main { +public class Text { + private final AliasContext context; + public Text(AliasContext context) { + this.context = context; + } /** * Messages an audience applying pallet formatting to the text and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}... * Supports both flat messages and fancy wrapped messages based on Alias configuration. @@ -29,7 +32,7 @@ public class Text implements Main { * @param text The message to format * @param args Qualified placeholders to color. */ - public static void messageAny(Pallet pallet, boolean playSound, Audience audience, String text, Object... args) { + public void messageAny(Pallet pallet, boolean playSound, Audience audience, String text, Object... args) { message( pallet, playSound, @@ -49,7 +52,7 @@ public class Text implements Main { * @param text The message to format * @param args Qualified placeholders to color. */ - public static void messageAny(Pallet pallet, Audience audience, String text, Object... args) { + public void messageAny(Pallet pallet, Audience audience, String text, Object... args) { messageAny(pallet,true,audience,text,args); } @@ -63,7 +66,7 @@ public class Text implements Main { * @param text The component message to format * @param args Qualified placeholders to color. */ - public static void message(Pallet pallet, boolean playSound, Audience audience, ComponentLike text, ComponentLike... args) { + public void message(Pallet pallet, boolean playSound, Audience audience, ComponentLike text, ComponentLike... args) { Component message = getMessage(pallet, text, args); audience.sendMessage(message); if (playSound && audience instanceof Player p) p.playSound(p.getLocation(), pallet.sound.sound, SoundCategory.VOICE, 10f, pallet.sound.pitch); @@ -78,7 +81,7 @@ public class Text implements Main { * @param text The component message to format * @param args Qualified placeholders to color. */ - public static void message(Pallet pallet, Audience audience, ComponentLike text, ComponentLike... args) { + public void message(Pallet pallet, Audience audience, ComponentLike text, ComponentLike... args) { message(pallet,true,audience,text,args); } @@ -89,7 +92,7 @@ public class Text implements Main { * @param args Qualified placeholders to color. * @return The final component, formatted according to flat/fancy setting. */ - public static Component getMessageAny(Pallet pallet, String text, Object... args) { + public Component getMessageAny(Pallet pallet, String text, Object... args) { return getMessage( pallet, color(text), @@ -108,10 +111,10 @@ public class Text implements Main { * @param args Qualified placeholders to color. * @return The final component, formatted according to flat/fancy setting. */ - public static Component getMessage(Pallet pallet, ComponentLike text, ComponentLike... args) { + public Component getMessage(Pallet pallet, ComponentLike text, ComponentLike... args) { Component formattedMessage = format(pallet, text, args); - if (main.getCommon().useFlat()) { + if (context.getCommon().useFlat()) { return formatFlatMessage(formattedMessage); } else { return formatFancyMessage(formattedMessage); @@ -123,8 +126,8 @@ public class Text implements Main { * @param message The formatted message component * @return The message with flat prefix applied */ - private static Component formatFlatMessage(Component message) { - Component prefix = color(main.getCommon().getFlatPrefix()); + private Component formatFlatMessage(Component message) { + Component prefix = color(context.getCommon().getFlatPrefix()); return prefix.append(message); } @@ -134,8 +137,8 @@ public class Text implements Main { * @param message The formatted message component * @return The message with fancy formatting and line wrapping */ - private static Component formatFancyMessage(Component message) { - List wrappedLines = wrapComponent(message, 50, (int) Math.round((main.getCommon().getPluginName().length() + 3) * 1.3)); + private Component formatFancyMessage(Component message) { + List wrappedLines = wrapComponent(message, 50, (int) Math.round((context.getCommon().getPluginName().length() + 3) * 1.3)); // 50 is slightly below the average character width of someone's minecraft chat. The 3 is to account for the bolded "| " and the 1.3 is to account for bolding the plugin name. if (wrappedLines.isEmpty()) { wrappedLines.add(Component.empty()); @@ -144,15 +147,15 @@ public class Text implements Main { Component result = Component.empty().appendNewline(); Component firstLine = Component.empty() - .append(Component.text("| ", TextColor.color(main.getCommon().getSecondaryColor())).decorate(TextDecoration.BOLD)) - .append(Component.text(main.getCommon().getPluginName() + " ", TextColor.color(main.getCommon().getMainColor()), TextDecoration.BOLD)) + .append(Component.text("| ", TextColor.color(context.getCommon().getSecondaryColor())).decorate(TextDecoration.BOLD)) + .append(Component.text(context.getCommon().getPluginName() + " ", TextColor.color(context.getCommon().getMainColor()), TextDecoration.BOLD)) .append(wrappedLines.get(0)); result = result.append(firstLine); for (int i = 1; i < wrappedLines.size(); i++) { Component line = Component.empty() - .append(Component.text("| ", TextColor.color(main.getCommon().getSecondaryColor())).decorate(TextDecoration.BOLD)) + .append(Component.text("| ", TextColor.color(context.getCommon().getSecondaryColor())).decorate(TextDecoration.BOLD)) .append(wrappedLines.get(i)); result = result.appendNewline().append(line); @@ -170,7 +173,7 @@ public class Text implements Main { * @param firstLineOffset Offset for the first line (plugin name length) * @return List of wrapped component lines */ - private static List wrapComponent(Component component, int maxLineLength, int firstLineOffset) { + private List wrapComponent(Component component, int maxLineLength, int firstLineOffset) { List lines = new ArrayList<>(); List words = extractWords(component); @@ -218,7 +221,7 @@ public class Text implements Main { * @param word The word to check * @return true if the word starts with punctuation */ - private static boolean startsWithPunctuation(ComponentWord word) { + private boolean startsWithPunctuation(ComponentWord word) { String text = PlainTextComponentSerializer.plainText().serialize(word.component()); return !text.isEmpty() && ".,!?;:)]}".indexOf(text.charAt(0)) != -1; } @@ -228,7 +231,7 @@ public class Text implements Main { * @param component The component to extract words from * @return List of ComponentWord objects */ - private static List extractWords(Component component) { + private List extractWords(Component component) { List words = new ArrayList<>(); extractWordsRecursive(component, Style.empty(), words); return words; @@ -240,7 +243,7 @@ public class Text implements Main { * @param inheritedStyle The style inherited from parent components * @param words The list to add words to */ - private static void extractWordsRecursive(Component component, Style inheritedStyle, List words) { + private void extractWordsRecursive(Component component, Style inheritedStyle, List words) { Style currentStyle = inheritedStyle.merge(component.style()); if (component instanceof TextComponent textComponent) { @@ -267,7 +270,7 @@ public class Text implements Main { * @param text The text to measure * @return The visible character count */ - private static int getVisibleLength(String text) { + private int getVisibleLength(String text) { return PlainTextComponentSerializer.plainText().serialize(Component.text(text)).length(); } @@ -276,7 +279,7 @@ public class Text implements Main { * @param msg the legacy text * @return The deserialized component */ - public static Component color(String msg) { + public Component color(String msg) { if (msg.contains("§")) return LegacyComponentSerializer.legacySection().deserialize(msg); return LegacyComponentSerializer.legacyAmpersand().deserialize(msg); } @@ -286,7 +289,7 @@ public class Text implements Main { * @param ampersands String with ampersand codes * @return String with section codes */ - public static String legacyAmpersandColor(String ampersands) { + public String legacyAmpersandColor(String ampersands) { return ampersands.replaceAll("&","§"); } @@ -297,7 +300,7 @@ public class Text implements Main { * @param args Arguments to replace placeholders * @return Formatted component */ - public static Component format(Pallet pallet, String text, Object... args) { + public Component format(Pallet pallet, String text, Object... args) { return format(pallet, Component.text(text), Arrays.stream(args).map(arg->Component.text(arg.toString())).toArray(Component[]::new)); } @@ -309,7 +312,7 @@ public class Text implements Main { * @param args Argument components to replace placeholders * @return Formatted component with colors applied */ - public static Component format(Pallet pallet, ComponentLike text, ComponentLike... args) { + public Component format(Pallet pallet, ComponentLike text, ComponentLike... args) { Component resultComponent = text.asComponent().color(pallet.mainText); if (args == null || args.length == 0) { @@ -339,7 +342,7 @@ public class Text implements Main { * @param component The component to check. * @return Currently always returns true, indicating recoloring should occur. */ - private static boolean shouldRecolor(Component component) { + private boolean shouldRecolor(Component component) { Set colors = new HashSet<>(); collectColors(component,colors); return colors.size() <= 1; @@ -350,7 +353,7 @@ public class Text implements Main { * @param component The component to collect. * @param colors A mutable HashSet of colors. */ - private static void collectColors(Component component, Set colors) { + private void collectColors(Component component, Set colors) { if (component.color() != null) { colors.add(component.color()); } @@ -364,7 +367,7 @@ public class Text implements Main { * @param input The input string * @return String with color codes removed */ - public static String removeColors(String input) { + public String removeColors(String input) { if (input == null) return null; input = input.replaceAll("(?i)[&§][0-9a-fk-or]", ""); // Legacy colors @@ -379,7 +382,7 @@ public class Text implements Main { * @param input The input component * @return Component with plain text only */ - public static Component removeColors(ComponentLike input) { + public Component removeColors(ComponentLike input) { if (input == null) return Component.text(""); String plainText = PlainTextComponentSerializer.plainText().serialize(input.asComponent()); @@ -392,7 +395,7 @@ public class Text implements Main { * @param argIndex The argument index (0-indexed) * @return The appropriate TextColor for the argument */ - private static TextColor getArgColor(Pallet pallet, int argIndex) { + private TextColor getArgColor(Pallet pallet, int argIndex) { return switch (argIndex) { case 1 -> pallet.arg2; case 2 -> pallet.arg3; @@ -403,52 +406,49 @@ public class Text implements Main { /** * Represents a word extracted from a component with its formatting preserved. */ - private static record ComponentWord(Component component, int visibleLength) {} + private record ComponentWord(Component component, int visibleLength) {} - /** - * Color pallets for different message types with appropriate colors and sounds. - */ public enum Pallet { ERROR( - TextColor.color(0xD3A6A4), // Soft red for main text - TextColor.color(0xFFF1AE), // Light yellow for default args - TextColor.color(0xFF796D), // Coral for second arg - TextColor.color(0xC62828), // Dark red for third arg + TextColor.color(0xD3A6A4), + TextColor.color(0xFFF1AE), + TextColor.color(0xFF796D), + TextColor.color(0xC62828), new SoundData(Sound.BLOCK_NOTE_BLOCK_BASS, 1) ), WARNING( - TextColor.color(0xFFF3CD), // Light yellow for main text - TextColor.color(0xFFF9F5), // Very light cream for default args - TextColor.color(0xFFD54F), // Gold for second arg - TextColor.color(0xFFA000), // Orange for third arg + TextColor.color(0xFFF3CD), + TextColor.color(0xFFF9F5), + TextColor.color(0xFFD54F), + TextColor.color(0xFFA000), new SoundData(Sound.BLOCK_NOTE_BLOCK_BIT, 0.5F) ), INFO( - TextColor.color(0xBBDEFB), // Light blue for main text - TextColor.color(0xD2D0EA), // Light lavender for default args - TextColor.color(0x64B5F6), // Medium blue for second arg - TextColor.color(0x1976D2), // Dark blue for third arg + TextColor.color(0xBBDEFB), + TextColor.color(0xD2D0EA), + TextColor.color(0x64B5F6), + TextColor.color(0x1976D2), new SoundData(Sound.BLOCK_NOTE_BLOCK_CHIME, 0.7F) ), SUCCESS( - TextColor.color(0xCDFFC7), // Light green for main text - TextColor.color(0xFFFFFF), // White for default args - TextColor.color(0xB0FFE3), // Light mint for second arg - TextColor.color(0x63CD83), // Medium green for third arg + TextColor.color(0xCDFFC7), + TextColor.color(0xFFFFFF), + TextColor.color(0xB0FFE3), + TextColor.color(0x63CD83), new SoundData(Sound.BLOCK_NOTE_BLOCK_PLING, 1.5F) ), NEUTRAL( - TextColor.color(0xD3D3D3), // Light gray for main text - TextColor.color(0xFFFFFF), // White for default args - TextColor.color(0xFFB3F8), // Light pink for second arg - TextColor.color(0xE280FF), // Purple for third arg + TextColor.color(0xD3D3D3), + TextColor.color(0xFFFFFF), + TextColor.color(0xFFB3F8), + TextColor.color(0xE280FF), new SoundData(Sound.BLOCK_NOTE_BLOCK_BELL, 1) ), LOCATION( - TextColor.color(0xAAAAAA), // Gray for main text - TextColor.color(0xFFB0C1), // Light pink for default args - TextColor.color(0xB6F5B6), // Light green for second arg - TextColor.color(0xB0C1FF), // Light blue for third arg + TextColor.color(0xAAAAAA), + TextColor.color(0xFFB0C1), + TextColor.color(0xB6F5B6), + TextColor.color(0xB0C1FF), new SoundData(Sound.UI_TOAST_IN, 2) ); diff --git a/src/main/java/me/trouper/alias/server/systems/Verbose.java b/src/main/java/me/trouper/alias/server/systems/Verbose.java index 4e4e3ce..6dcd04d 100644 --- a/src/main/java/me/trouper/alias/server/systems/Verbose.java +++ b/src/main/java/me/trouper/alias/server/systems/Verbose.java @@ -1,22 +1,28 @@ package me.trouper.alias.server.systems; -import me.trouper.alias.server.Main; +import me.trouper.alias.AliasContext; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.util.Vector; -public class Verbose implements Main { +public class Verbose { + + private final AliasContext context; + + public Verbose(AliasContext context) { + this.context = context; + } /** - * A dynamic verbose system which uses the format from the {@link Text} system. + * A Uses the format from the {@link Text} system. * @param backtrace The number of calls up the stacktrace to go. * @param verbose A message with 0 indexed curly brace placeholders. {0}, {1}, {2}... * @param args Qualified placeholder values. */ - public static void send(int backtrace, String verbose, Object... args) { - if (!main.getCommon().getDebugMode()) return; + public void send(int backtrace, String verbose, Object... args) { + if (!context.getCommon().getDebugMode()) return; String callerInfo = "Unknown Caller"; StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); @@ -28,20 +34,20 @@ public class Verbose implements Main { if (className.contains("-")) callerInfo = "Protected"; else callerInfo = className + "." + caller.getMethodName(); - if (main.getCommon().getDebuggerExclusions().contains(callerInfo)) return; + if (context.getCommon().getDebuggerExclusions().contains(callerInfo)) return; } Object[] processedArgs = processArgs(args); - Component message = Text.format(Text.Pallet.INFO, verbose, processedArgs); - message = Text.format(Text.Pallet.INFO, + Component message = context.getText().format(Text.Pallet.INFO, verbose, processedArgs); + message = context.getText().format(Text.Pallet.INFO, Component.text("{0} [DEBUG ^ {1}] [{2}] » {3}"), - Component.text(main.getCommon().getPluginName()), + Component.text(context.getCommon().getPluginName()), Component.text(backtrace), Component.text(callerInfo), message ); - main.getPlugin().getComponentLogger().info(message); + context.getPlugin().getComponentLogger().info(message); for (Player operator : Bukkit.getOnlinePlayers()) { if (!operator.isOp()) continue; @@ -50,15 +56,15 @@ public class Verbose implements Main { } /** - * A dynamic verbose system which uses the format from the {@link Text} system. + * Uses the format from the {@link Text} system. * @param verbose A message with 0 indexed curly brace placeholders. {0}, {1}, {2}... * @param args Qualified placeholder values. */ - public static void send(String verbose, Object... args) { + public void send(String verbose, Object... args) { send(1,verbose,args); } - private static Object[] processArgs(Object... args) { + private Object[] processArgs(Object... args) { Object[] processed = new Object[args.length]; for (int i = 0; i < args.length; i++) { diff --git a/src/main/java/me/trouper/alias/server/systems/display/DisplayManager.java b/src/main/java/me/trouper/alias/server/systems/display/DisplayManager.java new file mode 100644 index 0000000..9273a91 --- /dev/null +++ b/src/main/java/me/trouper/alias/server/systems/display/DisplayManager.java @@ -0,0 +1,26 @@ +package me.trouper.alias.server.systems.display; + +import me.trouper.alias.AliasContext; +import me.trouper.alias.server.systems.display.tracing.BlockDisplayRaytracer; +import me.trouper.alias.server.systems.display.tracing.CustomRaytracer; +import me.trouper.alias.server.systems.display.visual.Outliner; +import me.trouper.alias.server.systems.display.visual.Patterns; + +public class DisplayManager { + private final Patterns patterns; + private final Outliner outliner; + private final BlockDisplayRaytracer blockDisplayRaytracer; + private final CustomRaytracer customRaytracer; + + public DisplayManager(AliasContext context) { + this.patterns = new Patterns(context); + this.blockDisplayRaytracer = new BlockDisplayRaytracer(context); + this.outliner = new Outliner(blockDisplayRaytracer); + this.customRaytracer = new CustomRaytracer(context); + } + + public Patterns getPatterns() { return patterns; } + public Outliner getOutliner() { return outliner; } + public BlockDisplayRaytracer getBlockDisplayRaytracer() { return blockDisplayRaytracer; } + public CustomRaytracer getCustomRaytracer() { return customRaytracer; } +} diff --git a/src/main/java/me/trouper/alias/server/systems/display/tracing/BlockDisplayRaytracer.java b/src/main/java/me/trouper/alias/server/systems/display/tracing/BlockDisplayRaytracer.java new file mode 100755 index 0000000..530ecb3 --- /dev/null +++ b/src/main/java/me/trouper/alias/server/systems/display/tracing/BlockDisplayRaytracer.java @@ -0,0 +1,213 @@ +package me.trouper.alias.server.systems.display.tracing; + +import me.trouper.alias.AliasContext; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Display; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Transformation; +import org.bukkit.util.Vector; +import org.bukkit.util.VoxelShape; +import org.joml.AxisAngle4f; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class BlockDisplayRaytracer { + + private final AliasContext context; + + public BlockDisplayRaytracer(AliasContext context) { + this.context = context; + } + + public void cleanup() { + JavaPlugin plugin = context.getPlugin(); + List worlds = plugin.getServer().getWorlds(); + List entities = new ArrayList<>(); + for (World world : worlds) { + entities.addAll(world.getEntities().stream().filter(entity -> entity.getScoreboardTags().contains(context.getCommon().getTempTag())).toList()); + entities.forEach(entity -> { + if (entity != null) entity.remove(); + }); + } + } + + public void trace(Material display, Location start, Location end, long stayTime, List viewers) { + trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime, viewers); + } + + public void trace(Material display, Location start, Location end, double thickness, long stayTime, List viewers) { + trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime, viewers); + } + + public void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, List viewers) { + World world = start.getWorld(); + + BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> { + AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); + Vector3f transition = new Vector3f(-(float)(thickness / 2F)); + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + Transformation trans = new Transformation(transition, angle, scale, angle); + Location vector = entity.getLocation(); + + vector.setDirection(direction); + entity.teleport(vector); + entity.setBlock(display.createBlockData()); + entity.setBrightness(new Display.Brightness(15, 15)); + entity.setInterpolationDelay(0); + entity.setTransformation(trans); + entity.addScoreboardTag(context.getCommon().getTempTag()); + + for (Player player : Bukkit.getOnlinePlayers()) { + if (!viewers.contains(player)) { + player.hideEntity(context.getPlugin(), entity); + } + } + + Bukkit.getScheduler().runTaskLater(context.getPlugin(), entity::remove, stayTime); + }); + } + + public void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, Consumer onEntitySpawn, List viewers) { + World world = start.getWorld(); + + BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> { + AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); + Vector3f transition = new Vector3f(-(float)(thickness / 2F)); + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + Transformation trans = new Transformation(transition, angle, scale, angle); + Location vector = entity.getLocation(); + + vector.setDirection(direction); + entity.teleport(vector); + entity.setBlock(display.createBlockData()); + entity.setBrightness(new Display.Brightness(15, 15)); + entity.setInterpolationDelay(0); + entity.setTransformation(trans); + entity.addScoreboardTag(context.getCommon().getTempTag()); + + + for (Player player : Bukkit.getOnlinePlayers()) { + if (!viewers.contains(player)) { + player.hideEntity(context.getPlugin(), entity); + } + } + + Bukkit.getScheduler().runTaskLater(context.getPlugin(), entity::remove, stayTime); + Bukkit.getScheduler().runTaskLater(context.getPlugin(), () -> onEntitySpawn.accept(entity), 5); + }); + } + + public void traceGlowing(World world, double x1, double y1, double z1, double x2, double y2, double z2, Color color, long stayTime) { + Location loc1 = new Location(world, x1, y1, z1); + Location loc2 = new Location(world, x2, y2, z2); + BlockDisplay ent = trace(Material.WHITE_CONCRETE, loc1, loc2, 0.01, stayTime); + ent.setGlowColorOverride(color); + ent.setGlowing(true); + } + + public BlockDisplay trace(Material display, Location start, Location end, long stayTime) { + return trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime); + } + + public BlockDisplay trace(Material display, Location start, Location end, double thickness, long stayTime) { + return trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime); + } + + public BlockDisplay trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime) { + World world = start.getWorld(); + + BlockDisplay entity = world.spawn(start, BlockDisplay.class); + AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); + Vector3f transition = new Vector3f(-(float)(thickness / 2F)); + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + Transformation trans = new Transformation(transition, angle, scale, angle); + Location vector = entity.getLocation(); + + vector.setDirection(direction); + entity.teleport(vector); + entity.setBlock(display.createBlockData()); + entity.setBrightness(new Display.Brightness(15, 15)); + entity.setInterpolationDelay(0); + entity.setTransformation(trans); + entity.addScoreboardTag(context.getCommon().getTempTag()); + + Bukkit.getScheduler().runTaskLater(context.getPlugin(), entity::remove, stayTime); + return entity; + } + + public void transform(BlockDisplay display, Location start, Location end, double thickness) { + Vector direction = end.toVector().subtract(start.toVector()); + double distance = direction.length(); + + Location loc = start.clone(); + loc.setDirection(direction); + display.teleport(loc); + + Vector3f translation = new Vector3f(-(float)(thickness / 2F), 0, 0); // Centered + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 1); + Transformation transformation = new Transformation(translation, rotation, scale, rotation); + + display.setTransformation(transformation); + } + + public void transform(BlockDisplay display, Location start, Vector direction, double distance, double thickness) { + Location loc = start.clone(); + loc.setDirection(direction); + display.teleport(loc); + + Vector3f translation = new Vector3f(-(float)(thickness / 2F), 0, 0); + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 1); + Transformation transformation = new Transformation(translation, rotation, scale, rotation); + + display.setTransformation(transformation); + } + + + public void translate(BlockDisplay display, Vector3f offset) { + Transformation current = display.getTransformation(); + Vector3f translation = new Vector3f(current.getTranslation()).add(offset); + display.setTransformation(new Transformation( + translation, + current.getLeftRotation(), + current.getScale(), + current.getRightRotation() + )); + } + + public void scale(BlockDisplay display, Vector3f scale) { + Transformation current = display.getTransformation(); + display.setTransformation(new Transformation( + current.getTranslation(), + current.getLeftRotation(), + scale, + current.getRightRotation() + )); + } + + public void rotate(BlockDisplay display, AxisAngle4f rotation) { + Transformation current = display.getTransformation(); + display.setTransformation(new Transformation( + current.getTranslation(), + rotation, + current.getScale(), + rotation + )); + } + + public void alignToDirection(BlockDisplay display, Vector direction) { + Location loc = display.getLocation().clone(); + loc.setDirection(direction); + display.teleport(loc); + } + +} diff --git a/src/main/java/me/trouper/alias/server/systems/tracing/CustomDisplayRaytracer.java b/src/main/java/me/trouper/alias/server/systems/display/tracing/CustomRaytracer.java similarity index 83% rename from src/main/java/me/trouper/alias/server/systems/tracing/CustomDisplayRaytracer.java rename to src/main/java/me/trouper/alias/server/systems/display/tracing/CustomRaytracer.java index 78c8739..2a3c8c5 100755 --- a/src/main/java/me/trouper/alias/server/systems/tracing/CustomDisplayRaytracer.java +++ b/src/main/java/me/trouper/alias/server/systems/display/tracing/CustomRaytracer.java @@ -1,5 +1,6 @@ -package me.trouper.alias.server.systems.tracing; +package me.trouper.alias.server.systems.display.tracing; +import me.trouper.alias.AliasContext; import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.block.Block; @@ -19,7 +20,13 @@ import java.util.Random; import java.util.function.BiPredicate; import java.util.function.Predicate; -public class CustomDisplayRaytracer { +public class CustomRaytracer { + + private final AliasContext context; + + public CustomRaytracer(AliasContext context) { + this.context = context; + } public static final Predicate HIT_BLOCK = point -> { Block b = point.getBlock(); @@ -77,22 +84,22 @@ public class CustomDisplayRaytracer { return point -> HIT_BLOCK.test(point) && !point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead() && condition.test(e)).isEmpty(); } - public static Point trace(Location start, Location end, Predicate hitCondition) { + public Point trace(Location start, Location end, Predicate hitCondition) { return trace(start, end, 0.5, hitCondition); } - public static Point trace(Location start, Location end, double interval, Predicate hitCondition) { + public Point trace(Location start, Location end, double interval, Predicate hitCondition) { Vector direction = end.toVector().subtract(start.toVector()).normalize(); double distance = end.distance(start); return trace(start, direction, distance, interval, hitCondition); } - public static Point trace(Location start, Vector direction, double distance, Predicate hitCondition) { + public Point trace(Location start, Vector direction, double distance, Predicate hitCondition) { Vector normal = direction.clone().normalize(); return trace(start, normal, distance, 0.5, hitCondition); } - public static Point trace(Location start, Vector direction, double distance, double interval, Predicate hitCondition) { + public Point trace(Location start, Vector direction, double distance, double interval, Predicate hitCondition) { if (interval < 0) throw new IllegalArgumentException("interval cannot be zero!"); if (distance < 0) throw new IllegalArgumentException("distance cannot be zero!"); @@ -106,15 +113,14 @@ public class CustomDisplayRaytracer { } - public static BukkitTask traceDelayed(Plugin plugin, Location start, Vector direction, double distance, double interval, long tickDelay, int pointsPerTick, Predicate hitCondition) { + public BukkitTask traceDelayed(Location start, Vector direction, double distance, double interval, long tickDelay, int pointsPerTick, Predicate hitCondition) { if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!"); if (distance <= 0) throw new IllegalArgumentException("distance cannot be zero or negative!"); if (tickDelay < 0) throw new IllegalArgumentException("tickDelay cannot be negative!"); Vector normalizedDir = direction.clone().normalize(); - - + return new BukkitRunnable() { private double currentDistance = 0.0; private boolean hit = false; @@ -139,33 +145,33 @@ public class CustomDisplayRaytracer { currentDistance += interval; } } - }.runTaskTimer(plugin, 0, tickDelay); + }.runTaskTimer(context.getPlugin(), 0, tickDelay); } - public static BukkitTask traceDelayed(Plugin plugin, Location start, Location end, double interval, long tickDelay, int pointsPerTick, Predicate hitCondition) { + public BukkitTask traceDelayed(Location start, Location end, double interval, long tickDelay, int pointsPerTick, Predicate hitCondition) { Vector direction = end.toVector().subtract(start.toVector()).normalize(); double distance = start.distance(end); - return traceDelayed(plugin, start, direction, distance, interval, tickDelay,pointsPerTick, hitCondition); + return traceDelayed(start, direction, distance, interval, tickDelay,pointsPerTick, hitCondition); } - public static BukkitTask traceDelayed(Plugin plugin, + public BukkitTask traceDelayed(Plugin plugin, Location start, Location end, long tickDelay, Predicate hitCondition) { - return traceDelayed(plugin, start, end,0.5, tickDelay, 1, hitCondition); + return traceDelayed(start, end,0.5, tickDelay, 1, hitCondition); } - public static BukkitTask traceDelayed(Plugin plugin, + public BukkitTask traceDelayed(Plugin plugin, Location start, Vector direction, double distance, long tickDelay, Predicate hitCondition) { - return traceDelayed(plugin, start, direction, distance, 0.5, tickDelay,1, hitCondition); + return traceDelayed(start, direction, distance, 0.5, tickDelay,1, hitCondition); } - public static Point traceWithReflection(Location start, Vector direction, double distance, double interval, + public Point traceWithReflection(Location start, Vector direction, double distance, double interval, int maxReflections, Predicate hitCondition, BiPredicate blockReflectCondition, BiPredicate entityReflectCondition) { @@ -236,25 +242,23 @@ public class CustomDisplayRaytracer { } if (i + interval >= remainingDistance) { - Point finalPoint = blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true); - return finalPoint; + return blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true); } } if (reflections > maxReflections) { - Point finalPoint = blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true); - return finalPoint; + return blocksInFrontOf(currentLocation, currentDirection, remainingDistance, true); } } return blocksInFrontOf(start, normalizedDir, distance, true); } - private static Vector glanceReflect(Vector incident) { + private Vector glanceReflect(Vector incident) { return offsetVector(incident,4).multiply(-1); } - private static BlockFace traceBlockFace(Location startLocation, Vector direction, double maxDistance) { + private BlockFace traceBlockFace(Location startLocation, Vector direction, double maxDistance) { Predicate blockPredicate = block -> true; Predicate entityPredicate = entity -> false; @@ -267,7 +271,7 @@ public class CustomDisplayRaytracer { return null; } - private static Vector calculateReflection(Vector incident, Vector normal) { + private Vector calculateReflection(Vector incident, Vector normal) { // r = i - 2(i dot n)n double dot = incident.dot(normal); Vector reflection = incident.clone().subtract(normal.clone().multiply(2 * dot)); @@ -275,7 +279,7 @@ public class CustomDisplayRaytracer { return reflection.normalize(); } - private static Vector getFaceNormal(BlockFace face) { + private Vector getFaceNormal(BlockFace face) { return switch (face) { case DOWN -> new Vector(0, -1, 0); case NORTH -> new Vector(0, 0, -1); @@ -286,12 +290,12 @@ public class CustomDisplayRaytracer { }; } - public static Point blocksInFrontOf(Location loc, Vector dir, double blocks, boolean missed) { + public Point blocksInFrontOf(Location loc, Vector dir, double blocks, boolean missed) { Vector normal = dir.clone().normalize(); return new Point(loc.clone().add(normal.getX() * blocks, normal.getY() * blocks, normal.getZ() * blocks), blocks, missed); } - public static Vector offsetVector(Vector original, double angleDegrees) { + private Vector offsetVector(Vector original, double angleDegrees) { Random random = new Random(); original = original.clone().normalize(); diff --git a/src/main/java/me/trouper/alias/server/systems/tracing/Point.java b/src/main/java/me/trouper/alias/server/systems/display/tracing/Point.java similarity index 97% rename from src/main/java/me/trouper/alias/server/systems/tracing/Point.java rename to src/main/java/me/trouper/alias/server/systems/display/tracing/Point.java index 8042e0c..12c79bb 100755 --- a/src/main/java/me/trouper/alias/server/systems/tracing/Point.java +++ b/src/main/java/me/trouper/alias/server/systems/display/tracing/Point.java @@ -1,4 +1,4 @@ -package me.trouper.alias.server.systems.tracing; +package me.trouper.alias.server.systems.display.tracing; import org.bukkit.Location; import org.bukkit.World; diff --git a/src/main/java/me/trouper/alias/server/systems/display/visual/Outliner.java b/src/main/java/me/trouper/alias/server/systems/display/visual/Outliner.java new file mode 100644 index 0000000..1344a8a --- /dev/null +++ b/src/main/java/me/trouper/alias/server/systems/display/visual/Outliner.java @@ -0,0 +1,169 @@ +package me.trouper.alias.server.systems.display.visual; + +import me.trouper.alias.server.systems.display.tracing.BlockDisplayRaytracer; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Player; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Vector; +import org.bukkit.util.VoxelShape; + +import java.util.ArrayList; +import java.util.List; + +public class Outliner { + + private final BlockDisplayRaytracer rt; + + public Outliner(BlockDisplayRaytracer raytracer) { + this.rt = raytracer; + } + + public void highlightCollisions(Block block, Color color, long stayTime) { + if (block == null || block.isEmpty() || !block.isCollidable()) + return; + + VoxelShape shape = block.getCollisionShape(); + World world = block.getWorld(); + Vector offset = block.getLocation().toVector(); + + for (BoundingBox box : shape.getBoundingBoxes()) { + highlight(box, offset, world, color, stayTime); + } + } + + public List outline(Material display, Location location, long stayTime) { + return outline(display, location, 0.05, stayTime); + } + + public List outline(Material display, Location location, double thickness, long stayTime) { + Location og = location.getBlock().getLocation(); + + Location a1 = og.clone().add(0, 0, 0); + Location a2 = og.clone().add(1, 0, 0); + Location a3 = og.clone().add(1, 0, 1); + Location a4 = og.clone().add(0, 0, 1); + + Location b1 = og.clone().add(0, 1, 0); + Location b2 = og.clone().add(1, 1, 0); + Location b3 = og.clone().add(1, 1, 1); + Location b4 = og.clone().add(0, 1, 1); + + List a = new ArrayList<>(); + + a.add(rt.trace(display, a1, a2, thickness, stayTime)); + a.add(rt.trace(display, a2, a3, thickness, stayTime)); + a.add(rt.trace(display, a3, a4, thickness, stayTime)); + a.add(rt.trace(display, a4, a1, thickness, stayTime)); + + a.add(rt.trace(display, b1, b2, thickness, stayTime)); + a.add(rt.trace(display, b2, b3, thickness, stayTime)); + a.add(rt.trace(display, b3, b4, thickness, stayTime)); + a.add(rt.trace(display, b4, b1, thickness, stayTime)); + + a.add(rt.trace(display, a1, b1, thickness, stayTime)); + a.add(rt.trace(display, a2, b2, thickness, stayTime)); + a.add(rt.trace(display, a3, b3, thickness, stayTime)); + a.add(rt.trace(display, a4, b4, thickness, stayTime)); + + return a; + } + + public void highlight(BoundingBox box, Vector offset, World world, Color color, long stayTime) { + double x1 = box.getMinX() + offset.getX(); + double y1 = box.getMinY() + offset.getY(); + double z1 = box.getMinZ() + offset.getZ(); + double x2 = box.getMaxX() + offset.getX(); + double y2 = box.getMaxY() + offset.getY(); + double z2 = box.getMaxZ() + offset.getZ(); + + rt.traceGlowing(world, x1, y1, z1, x2, y1, z1, color, stayTime); + rt.traceGlowing(world, x2, y1, z1, x2, y1, z2, color, stayTime); + rt.traceGlowing(world, x2, y1, z2, x1, y1, z2, color, stayTime); + rt.traceGlowing(world, x1, y1, z2, x1, y1, z1, color, stayTime); + + rt.traceGlowing(world, x1, y2, z1, x2, y2, z1, color, stayTime); + rt.traceGlowing(world, x2, y2, z1, x2, y2, z2, color, stayTime); + rt.traceGlowing(world, x2, y2, z2, x1, y2, z2, color, stayTime); + rt.traceGlowing(world, x1, y2, z2, x1, y2, z1, color, stayTime); + + rt.traceGlowing(world, x1, y1, z1, x1, y2, z1, color, stayTime); + rt.traceGlowing(world, x2, y1, z1, x2, y2, z1, color, stayTime); + rt.traceGlowing(world, x2, y1, z2, x2, y2, z2, color, stayTime); + rt.traceGlowing(world, x1, y1, z2, x1, y2, z2, color, stayTime); + } + + public void outline(Material display, Location location, long stayTime, List viewers) { + outline(display, location, 0.05, stayTime, viewers); + } + + public void outline(Material display, Location corner1, Location corner2, double thickness, long stayTime, List viewers) { + World world = corner1.getWorld(); + + int minX = Math.min(corner1.getBlockX(), corner2.getBlockX()); + int minY = Math.min(corner1.getBlockY(), corner2.getBlockY()); + int minZ = Math.min(corner1.getBlockZ(), corner2.getBlockZ()); + int maxX = Math.max(corner1.getBlockX(), corner2.getBlockX()); + int maxY = Math.max(corner1.getBlockY(), corner2.getBlockY()); + int maxZ = Math.max(corner1.getBlockZ(), corner2.getBlockZ()); + + Location a1 = new Location(world, minX, minY, minZ); + Location a2 = new Location(world, maxX + 1, minY, minZ); + Location a3 = new Location(world, maxX + 1, minY, maxZ + 1); + Location a4 = new Location(world, minX, minY, maxZ + 1); + + Location b1 = new Location(world, minX, maxY + 1, minZ); + Location b2 = new Location(world, maxX + 1, maxY + 1, minZ); + Location b3 = new Location(world, maxX + 1, maxY + 1, maxZ + 1); + Location b4 = new Location(world, minX, maxY + 1, maxZ + 1); + + rt.trace(display, a1, a2, thickness, stayTime, viewers); + rt.trace(display, a2, a3, thickness, stayTime, viewers); + rt.trace(display, a3, a4, thickness, stayTime, viewers); + rt.trace(display, a4, a1, thickness, stayTime, viewers); + + rt.trace(display, b1, b2, thickness, stayTime, viewers); + rt.trace(display, b2, b3, thickness, stayTime, viewers); + rt.trace(display, b3, b4, thickness, stayTime, viewers); + rt.trace(display, b4, b1, thickness, stayTime, viewers); + + rt.trace(display, a1, b1, thickness, stayTime, viewers); + rt.trace(display, a2, b2, thickness, stayTime, viewers); + rt.trace(display, a3, b3, thickness, stayTime, viewers); + rt.trace(display, a4, b4, thickness, stayTime, viewers); + } + + + public void outline(Material display, Location location, double thickness, long stayTime, List viewers) { + Location og = location.getBlock().getLocation(); + + Location a1 = og.clone().add(0, 0, 0); + Location a2 = og.clone().add(1, 0, 0); + Location a3 = og.clone().add(1, 0, 1); + Location a4 = og.clone().add(0, 0, 1); + + Location b1 = og.clone().add(0, 1, 0); + Location b2 = og.clone().add(1, 1, 0); + Location b3 = og.clone().add(1, 1, 1); + Location b4 = og.clone().add(0, 1, 1); + + rt.trace(display, a1, a2, thickness, stayTime, viewers); + rt.trace(display, a2, a3, thickness, stayTime, viewers); + rt.trace(display, a3, a4, thickness, stayTime, viewers); + rt.trace(display, a4, a1, thickness, stayTime, viewers); + + rt.trace(display, b1, b2, thickness, stayTime, viewers); + rt.trace(display, b2, b3, thickness, stayTime, viewers); + rt.trace(display, b3, b4, thickness, stayTime, viewers); + rt.trace(display, b4, b1, thickness, stayTime, viewers); + + rt.trace(display, a1, b1, thickness, stayTime, viewers); + rt.trace(display, a2, b2, thickness, stayTime, viewers); + rt.trace(display, a3, b3, thickness, stayTime, viewers); + rt.trace(display, a4, b4, thickness, stayTime, viewers); + } +} diff --git a/src/main/java/me/trouper/alias/server/systems/visual/DisplayUtils.java b/src/main/java/me/trouper/alias/server/systems/display/visual/Patterns.java similarity index 67% rename from src/main/java/me/trouper/alias/server/systems/visual/DisplayUtils.java rename to src/main/java/me/trouper/alias/server/systems/display/visual/Patterns.java index faa4d06..2b4bc6f 100755 --- a/src/main/java/me/trouper/alias/server/systems/visual/DisplayUtils.java +++ b/src/main/java/me/trouper/alias/server/systems/display/visual/Patterns.java @@ -1,6 +1,6 @@ -package me.trouper.alias.server.systems.visual; +package me.trouper.alias.server.systems.display.visual; -import me.trouper.alias.server.Main; +import me.trouper.alias.AliasContext; import me.trouper.alias.utils.misc.Randomizer; import org.bukkit.Bukkit; import org.bukkit.Color; @@ -16,22 +16,28 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; -public class DisplayUtils implements Main { +public class Patterns { // This will 100% get Javadoc in the future when I try to use it. Everything in here is so convoluted. - public static final Function> PARTICLE_FACTORY = particle -> l -> l.getWorld().spawnParticle(particle, l, 1, 0, 0, 0, 0); + private final AliasContext context; - public static final BiFunction> DUST_PARTICLE_FACTORY = (color, thickness) -> { + public final Function> PARTICLE_FACTORY = particle -> l -> l.getWorld().spawnParticle(particle, l, 1, 0, 0, 0, 0); + + public final BiFunction> DUST_PARTICLE_FACTORY = (color, thickness) -> { Particle.DustOptions dust = new Particle.DustOptions(color, thickness); return l -> l.getWorld().spawnParticle(Particle.DUST, l, 1, 0, 0, 0, 0, dust); }; - public static void ring(Location loc, double radius, Color color, float thickness) { + public Patterns(AliasContext context) { + this.context = context; + } + + public void ring(Location loc, double radius, Color color, float thickness) { ring(loc, radius, DUST_PARTICLE_FACTORY.apply(color, thickness)); } - public static void sphere(Location center, double radius, double pointDistance, Consumer action) { + public void sphere(Location center, double radius, double pointDistance, Consumer action) { double dPhi = pointDistance / radius; for (double phi = 0.0; phi <= Math.PI; phi += dPhi) { @@ -55,10 +61,10 @@ public class DisplayUtils implements Main { } } - public static void sphereWave(Location center, double maxRadius, double radialStep, double maxDistanceBetweenPoints, Consumer action) { + public void sphereWave(Location center, double maxRadius, double radialStep, double maxDistanceBetweenPoints, Consumer action) { AtomicReference currentRadius = new AtomicReference<>(radialStep); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), (task) -> { double r = currentRadius.get(); if (r > maxRadius) { task.cancel(); @@ -70,7 +76,7 @@ public class DisplayUtils implements Main { }, 0L, 1L); } - public static void ring(Location loc, double radius, Consumer action) { + public void ring(Location loc, double radius, Consumer action) { for (int theta = 0; theta < 360; theta += 10) { double x = Math.cos(Math.toRadians(theta)) * radius; double z = Math.sin(Math.toRadians(theta)) * radius; @@ -79,17 +85,17 @@ public class DisplayUtils implements Main { } } - public static void ring(Location loc, double radius, double maxDistanceBetweenPoints, Consumer action) { + public void ring(Location loc, double radius, double maxDistanceBetweenPoints, Consumer action) { arc(loc, radius, 0, 360, maxDistanceBetweenPoints, action); } - public static void wave(Location loc, double radius, Color color, float thickness, double gap) { + public void wave(Location loc, double radius, Color color, float thickness, double gap) { wave(loc, radius, DUST_PARTICLE_FACTORY.apply(color, thickness), gap); } - public static void wave(Location loc, double radius, Consumer action, double gap) { + public void wave(Location loc, double radius, Consumer action, double gap) { AtomicReference i = new AtomicReference<>(gap); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), (task) -> { if (i.get() >= radius) { task.cancel(); return; @@ -99,9 +105,9 @@ public class DisplayUtils implements Main { }, 0, 1); } - public static void wave(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer action) { + public void wave(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer action) { AtomicReference r = new AtomicReference<>(radialGap); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), (task) -> { if (r.get() > radius) { task.cancel(); return; @@ -111,19 +117,19 @@ public class DisplayUtils implements Main { }, 0, 1); } - public static void disc(Location loc, double radius, Consumer action, double gap) { + public void disc(Location loc, double radius, Consumer action, double gap) { for (double i = gap; i < radius; i += gap) { ring(loc, i, action); } } - public static void disc(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer action) { + public void disc(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer action) { for (double r = radialGap; r <= radius; r += radialGap) { ring(loc, r, maxDistanceBetweenPoints, action); } } - public static void helix(Location loc, double radius, Consumer action, double gap, int height) { + public void helix(Location loc, double radius, Consumer action, double gap, int height) { int theta = 0; for (double y = 0; y <= height; y += gap) { double x = Math.cos(Math.toRadians(theta)) * radius; @@ -135,7 +141,7 @@ public class DisplayUtils implements Main { } } - public static void vortex(Location loc, double radius, Consumer action, double gapH, double gapV, int height) { + public void vortex(Location loc, double radius, Consumer action, double gapH, double gapV, int height) { double r = radius; int theta = 0; for (double y = 0; y <= height; y += gapV) { @@ -149,14 +155,14 @@ public class DisplayUtils implements Main { } } - public static void beam(Location loc, Consumer action, double gap, int height) { + public void beam(Location loc, Consumer action, double gap, int height) { for (double y = 0; y <= height; y += gap) { Location newLoc = loc.clone().add(0, y, 0); action.accept(newLoc); } } - public static void arc(Location loc, double radius, int angleFrom, int angleTo, Consumer action) { + public void arc(Location loc, double radius, int angleFrom, int angleTo, Consumer action) { for (int theta = angleFrom; theta < angleTo; theta += 10) { double x = Math.cos(Math.toRadians(theta)) * radius; double z = Math.sin(Math.toRadians(theta)) * radius; @@ -165,7 +171,7 @@ public class DisplayUtils implements Main { } } - public static void arc(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action) { + public void arc(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action) { int angleSpan = angleTo - angleFrom; if (angleSpan <= 0) return; @@ -182,23 +188,23 @@ public class DisplayUtils implements Main { } - public static void fan(Location loc, double radius, int angleFrom, int angleTo, Consumer action, double gap) { + public void fan(Location loc, double radius, int angleFrom, int angleTo, Consumer action, double gap) { for (double i = gap; i < radius; i += gap) { arc(loc, i, angleFrom, angleTo, action); } } - public static void fan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action, double radialGap) { + public void fan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action, double radialGap) { for (double r = radialGap; r < radius; r += radialGap) { arc(loc, r, angleFrom, angleTo, maxDistanceBetweenPoints, action); } } - public static void fanWave(Location loc, double radius, int sections, Consumer action, double gap) { + public void fanWave(Location loc, double radius, int sections, Consumer action, double gap) { double arcLength = 360.0 / sections; AtomicReference i = new AtomicReference<>(0.0); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), (task) -> { if (i.get() >= 360) { task.cancel(); return; @@ -209,7 +215,7 @@ public class DisplayUtils implements Main { }, 0, 5); } - public static void fanWaveRandom(Location loc, double radius, int sections, Consumer action, double gap) { + public void fanWaveRandom(Location loc, double radius, int sections, Consumer action, double gap) { double arcLength = 360.0 / sections; List ints = new ArrayList<>(); for (double start = 0; start < 360; start += arcLength) { @@ -218,7 +224,7 @@ public class DisplayUtils implements Main { AtomicInteger i = new AtomicInteger(0); Randomizer random = new Randomizer(); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), (task) -> { if (i.get() >= sections) { task.cancel(); return; @@ -230,9 +236,9 @@ public class DisplayUtils implements Main { }, 0, 5); } - public static void waveFan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action, double radialGap) { + public void waveFan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action, double radialGap) { AtomicReference r = new AtomicReference<>(radialGap); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + Bukkit.getScheduler().runTaskTimer(context.getPlugin(), (task) -> { if (r.get() >= radius) { task.cancel(); return; @@ -242,7 +248,7 @@ public class DisplayUtils implements Main { }, 0, 1); } - public static void waveFan(Location loc, double radius, Vector direction, int angle, double maxDistanceBetweenPoints, Consumer action, double radialGap) { + public void waveFan(Location loc, double radius, Vector direction, int angle, double maxDistanceBetweenPoints, Consumer action, double radialGap) { double baseAngle = Math.toDegrees(Math.atan2(direction.getZ(), direction.getX())); int angleFrom = (int) (baseAngle - angle / 2.0); int angleTo = (int) (baseAngle + angle / 2.0); diff --git a/src/main/java/me/trouper/alias/server/systems/gui/QuickGui.java b/src/main/java/me/trouper/alias/server/systems/gui/QuickGui.java index 94d9846..691fd75 100644 --- a/src/main/java/me/trouper/alias/server/systems/gui/QuickGui.java +++ b/src/main/java/me/trouper/alias/server/systems/gui/QuickGui.java @@ -1,6 +1,5 @@ package me.trouper.alias.server.systems.gui; -import me.trouper.alias.server.Main; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; @@ -17,21 +16,18 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -public class QuickGui implements InventoryHolder, Main { +public class QuickGui implements InventoryHolder { private static final Map registry = new ConcurrentHashMap<>(); private static final MiniMessage miniMessage = MiniMessage.miniMessage(); private final Map slotActions; private final Map slotItems; - private final Map animations; private final GuiAction globalAction; private final GuiCreateAction createAction; private final GuiCloseAction closeAction; @@ -62,7 +58,6 @@ public class QuickGui implements InventoryHolder, Main { this.clickSound = clickSound; this.soundVolume = soundVolume; this.soundPitch = soundPitch; - this.animations = new HashMap<>(); this.viewers = ConcurrentHashMap.newKeySet(); } @@ -141,46 +136,6 @@ public class QuickGui implements InventoryHolder, Main { } } - public void startAnimation(int slot, List frames, long interval) { - stopAnimation(slot); - - if (frames.isEmpty()) return; - - BukkitTask task = new BukkitRunnable() { - private int frameIndex = 0; - - @Override - public void run() { - if (getInventory().getViewers().isEmpty()) { - cancel(); - return; - } - - ItemStack frame = frames.get(frameIndex); - getInventory().setItem(slot, frame); - frameIndex = (frameIndex + 1) % frames.size(); - } - }.runTaskTimer(main.getPlugin(), 0L, interval); - - animations.put(slot, task); - } - - public void stopAnimation(int slot) { - BukkitTask task = animations.remove(slot); - if (task != null && !task.isCancelled()) { - task.cancel(); - } - } - - public void stopAllAnimations() { - animations.values().forEach(task -> { - if (!task.isCancelled()) { - task.cancel(); - } - }); - animations.clear(); - } - private int calculateSize() { if (size > 0 && size % 9 == 0) { return Math.min(size, 54); @@ -233,10 +188,6 @@ public class QuickGui implements InventoryHolder, Main { viewers.remove(player); } - if (viewers.isEmpty()) { - stopAllAnimations(); - } - closeAction.onClose(this, event); } diff --git a/src/main/java/me/trouper/alias/server/systems/tracing/BlockDisplayRaytracer.java b/src/main/java/me/trouper/alias/server/systems/tracing/BlockDisplayRaytracer.java deleted file mode 100755 index 953bfe2..0000000 --- a/src/main/java/me/trouper/alias/server/systems/tracing/BlockDisplayRaytracer.java +++ /dev/null @@ -1,351 +0,0 @@ -package me.trouper.alias.server.systems.tracing; - -import me.trouper.alias.server.Main; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.entity.BlockDisplay; -import org.bukkit.entity.Display; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.BoundingBox; -import org.bukkit.util.Transformation; -import org.bukkit.util.Vector; -import org.bukkit.util.VoxelShape; -import org.joml.AxisAngle4f; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -public class BlockDisplayRaytracer implements Main { - - public static void cleanup() { - JavaPlugin plugin = main.getPlugin(); - List worlds = plugin.getServer().getWorlds(); - List entities = new ArrayList<>(); - for (World world : worlds) { - entities.addAll(world.getEntities().stream().filter(entity -> entity.getScoreboardTags().contains(main.getCommon().getTempTag())).toList()); - entities.forEach(entity -> { - if (entity != null) entity.remove(); - }); - } - } - - public static void outline(Material display, Location location, long stayTime, List viewers) { - outline(display, location, 0.05, stayTime, viewers); - } - - public static void outline(Material display, Location corner1, Location corner2, double thickness, long stayTime, List viewers) { - World world = corner1.getWorld(); - - int minX = Math.min(corner1.getBlockX(), corner2.getBlockX()); - int minY = Math.min(corner1.getBlockY(), corner2.getBlockY()); - int minZ = Math.min(corner1.getBlockZ(), corner2.getBlockZ()); - int maxX = Math.max(corner1.getBlockX(), corner2.getBlockX()); - int maxY = Math.max(corner1.getBlockY(), corner2.getBlockY()); - int maxZ = Math.max(corner1.getBlockZ(), corner2.getBlockZ()); - - Location a1 = new Location(world, minX, minY, minZ); - Location a2 = new Location(world, maxX + 1, minY, minZ); - Location a3 = new Location(world, maxX + 1, minY, maxZ + 1); - Location a4 = new Location(world, minX, minY, maxZ + 1); - - Location b1 = new Location(world, minX, maxY + 1, minZ); - Location b2 = new Location(world, maxX + 1, maxY + 1, minZ); - Location b3 = new Location(world, maxX + 1, maxY + 1, maxZ + 1); - Location b4 = new Location(world, minX, maxY + 1, maxZ + 1); - - trace(display, a1, a2, thickness, stayTime, viewers); - trace(display, a2, a3, thickness, stayTime, viewers); - trace(display, a3, a4, thickness, stayTime, viewers); - trace(display, a4, a1, thickness, stayTime, viewers); - - trace(display, b1, b2, thickness, stayTime, viewers); - trace(display, b2, b3, thickness, stayTime, viewers); - trace(display, b3, b4, thickness, stayTime, viewers); - trace(display, b4, b1, thickness, stayTime, viewers); - - trace(display, a1, b1, thickness, stayTime, viewers); - trace(display, a2, b2, thickness, stayTime, viewers); - trace(display, a3, b3, thickness, stayTime, viewers); - trace(display, a4, b4, thickness, stayTime, viewers); - } - - - public static void outline(Material display, Location location, double thickness, long stayTime, List viewers) { - Location og = location.getBlock().getLocation(); - - Location a1 = og.clone().add(0, 0, 0); - Location a2 = og.clone().add(1, 0, 0); - Location a3 = og.clone().add(1, 0, 1); - Location a4 = og.clone().add(0, 0, 1); - - Location b1 = og.clone().add(0, 1, 0); - Location b2 = og.clone().add(1, 1, 0); - Location b3 = og.clone().add(1, 1, 1); - Location b4 = og.clone().add(0, 1, 1); - - trace(display, a1, a2, thickness, stayTime, viewers); - trace(display, a2, a3, thickness, stayTime, viewers); - trace(display, a3, a4, thickness, stayTime, viewers); - trace(display, a4, a1, thickness, stayTime, viewers); - - trace(display, b1, b2, thickness, stayTime, viewers); - trace(display, b2, b3, thickness, stayTime, viewers); - trace(display, b3, b4, thickness, stayTime, viewers); - trace(display, b4, b1, thickness, stayTime, viewers); - - trace(display, a1, b1, thickness, stayTime, viewers); - trace(display, a2, b2, thickness, stayTime, viewers); - trace(display, a3, b3, thickness, stayTime, viewers); - trace(display, a4, b4, thickness, stayTime, viewers); - } - - public static void trace(Material display, Location start, Location end, long stayTime, List viewers) { - trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime, viewers); - } - - public static void trace(Material display, Location start, Location end, double thickness, long stayTime, List viewers) { - trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime, viewers); - } - - public static void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, List viewers) { - World world = start.getWorld(); - - BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> { - AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); - Vector3f transition = new Vector3f(-(float)(thickness / 2F)); - Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); - Transformation trans = new Transformation(transition, angle, scale, angle); - Location vector = entity.getLocation(); - - vector.setDirection(direction); - entity.teleport(vector); - entity.setBlock(display.createBlockData()); - entity.setBrightness(new Display.Brightness(15, 15)); - entity.setInterpolationDelay(0); - entity.setTransformation(trans); - entity.addScoreboardTag(main.getCommon().getTempTag()); - - for (Player player : Bukkit.getOnlinePlayers()) { - if (!viewers.contains(player)) { - player.hideEntity(main.getPlugin(), entity); - } - } - - Bukkit.getScheduler().runTaskLater(main.getPlugin(), entity::remove, stayTime); - }); - } - - public static void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, Consumer onEntitySpawn, List viewers) { - World world = start.getWorld(); - - BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> { - AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); - Vector3f transition = new Vector3f(-(float)(thickness / 2F)); - Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); - Transformation trans = new Transformation(transition, angle, scale, angle); - Location vector = entity.getLocation(); - - vector.setDirection(direction); - entity.teleport(vector); - entity.setBlock(display.createBlockData()); - entity.setBrightness(new Display.Brightness(15, 15)); - entity.setInterpolationDelay(0); - entity.setTransformation(trans); - entity.addScoreboardTag(main.getCommon().getTempTag()); - - - for (Player player : Bukkit.getOnlinePlayers()) { - if (!viewers.contains(player)) { - player.hideEntity(main.getPlugin(), entity); - } - } - - Bukkit.getScheduler().runTaskLater(main.getPlugin(), entity::remove, stayTime); - Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> onEntitySpawn.accept(entity), 5); - }); - } - - public static List outline(Material display, Location location, long stayTime) { - return outline(display, location, 0.05, stayTime); - } - - public static List outline(Material display, Location location, double thickness, long stayTime) { - Location og = location.getBlock().getLocation(); - - Location a1 = og.clone().add(0, 0, 0); - Location a2 = og.clone().add(1, 0, 0); - Location a3 = og.clone().add(1, 0, 1); - Location a4 = og.clone().add(0, 0, 1); - - Location b1 = og.clone().add(0, 1, 0); - Location b2 = og.clone().add(1, 1, 0); - Location b3 = og.clone().add(1, 1, 1); - Location b4 = og.clone().add(0, 1, 1); - - List a = new ArrayList<>(); - - a.add(trace(display, a1, a2, thickness, stayTime)); - a.add(trace(display, a2, a3, thickness, stayTime)); - a.add(trace(display, a3, a4, thickness, stayTime)); - a.add(trace(display, a4, a1, thickness, stayTime)); - - a.add(trace(display, b1, b2, thickness, stayTime)); - a.add(trace(display, b2, b3, thickness, stayTime)); - a.add(trace(display, b3, b4, thickness, stayTime)); - a.add(trace(display, b4, b1, thickness, stayTime)); - - a.add(trace(display, a1, b1, thickness, stayTime)); - a.add(trace(display, a2, b2, thickness, stayTime)); - a.add(trace(display, a3, b3, thickness, stayTime)); - a.add(trace(display, a4, b4, thickness, stayTime)); - - return a; - } - - public static void highlightCollisions(Block block, Color color, long stayTime) { - if (block == null || block.isEmpty() || !block.isCollidable()) - return; - - VoxelShape shape = block.getCollisionShape(); - World world = block.getWorld(); - Vector offset = block.getLocation().toVector(); - - for (BoundingBox box : shape.getBoundingBoxes()) { - highlight(box, offset, world, color, stayTime); - } - } - - public static void highlight(BoundingBox box, Vector offset, World world, Color color, long stayTime) { - double x1 = box.getMinX() + offset.getX(); - double y1 = box.getMinY() + offset.getY(); - double z1 = box.getMinZ() + offset.getZ(); - double x2 = box.getMaxX() + offset.getX(); - double y2 = box.getMaxY() + offset.getY(); - double z2 = box.getMaxZ() + offset.getZ(); - - traceGlowing(world, x1, y1, z1, x2, y1, z1, color, stayTime); - traceGlowing(world, x2, y1, z1, x2, y1, z2, color, stayTime); - traceGlowing(world, x2, y1, z2, x1, y1, z2, color, stayTime); - traceGlowing(world, x1, y1, z2, x1, y1, z1, color, stayTime); - - traceGlowing(world, x1, y2, z1, x2, y2, z1, color, stayTime); - traceGlowing(world, x2, y2, z1, x2, y2, z2, color, stayTime); - traceGlowing(world, x2, y2, z2, x1, y2, z2, color, stayTime); - traceGlowing(world, x1, y2, z2, x1, y2, z1, color, stayTime); - - traceGlowing(world, x1, y1, z1, x1, y2, z1, color, stayTime); - traceGlowing(world, x2, y1, z1, x2, y2, z1, color, stayTime); - traceGlowing(world, x2, y1, z2, x2, y2, z2, color, stayTime); - traceGlowing(world, x1, y1, z2, x1, y2, z2, color, stayTime); - } - - public static void traceGlowing(World world, double x1, double y1, double z1, double x2, double y2, double z2, Color color, long stayTime) { - Location loc1 = new Location(world, x1, y1, z1); - Location loc2 = new Location(world, x2, y2, z2); - BlockDisplay ent = trace(Material.WHITE_CONCRETE, loc1, loc2, 0.01, stayTime); - ent.setGlowColorOverride(color); - ent.setGlowing(true); - } - - public static BlockDisplay trace(Material display, Location start, Location end, long stayTime) { - return trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime); - } - - public static BlockDisplay trace(Material display, Location start, Location end, double thickness, long stayTime) { - return trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime); - } - - public static BlockDisplay trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime) { - World world = start.getWorld(); - - BlockDisplay entity = world.spawn(start, BlockDisplay.class); - AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); - Vector3f transition = new Vector3f(-(float)(thickness / 2F)); - Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); - Transformation trans = new Transformation(transition, angle, scale, angle); - Location vector = entity.getLocation(); - - vector.setDirection(direction); - entity.teleport(vector); - entity.setBlock(display.createBlockData()); - entity.setBrightness(new Display.Brightness(15, 15)); - entity.setInterpolationDelay(0); - entity.setTransformation(trans); - entity.addScoreboardTag(main.getCommon().getTempTag()); - - Bukkit.getScheduler().runTaskLater(main.getPlugin(), entity::remove, stayTime); - return entity; - } - - public static void transform(BlockDisplay display, Location start, Location end, double thickness) { - Vector direction = end.toVector().subtract(start.toVector()); - double distance = direction.length(); - - Location loc = start.clone(); - loc.setDirection(direction); - display.teleport(loc); - - Vector3f translation = new Vector3f(-(float)(thickness / 2F), 0, 0); // Centered - Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); - AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 1); - Transformation transformation = new Transformation(translation, rotation, scale, rotation); - - display.setTransformation(transformation); - } - - public static void transform(BlockDisplay display, Location start, Vector direction, double distance, double thickness) { - Location loc = start.clone(); - loc.setDirection(direction); - display.teleport(loc); - - Vector3f translation = new Vector3f(-(float)(thickness / 2F), 0, 0); - Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); - AxisAngle4f rotation = new AxisAngle4f(0, 0, 0, 1); - Transformation transformation = new Transformation(translation, rotation, scale, rotation); - - display.setTransformation(transformation); - } - - - public static void translate(BlockDisplay display, Vector3f offset) { - Transformation current = display.getTransformation(); - Vector3f translation = new Vector3f(current.getTranslation()).add(offset); - display.setTransformation(new Transformation( - translation, - current.getLeftRotation(), - current.getScale(), - current.getRightRotation() - )); - } - - public static void scale(BlockDisplay display, Vector3f scale) { - Transformation current = display.getTransformation(); - display.setTransformation(new Transformation( - current.getTranslation(), - current.getLeftRotation(), - scale, - current.getRightRotation() - )); - } - - public static void rotate(BlockDisplay display, AxisAngle4f rotation) { - Transformation current = display.getTransformation(); - display.setTransformation(new Transformation( - current.getTranslation(), - rotation, - current.getScale(), - rotation - )); - } - - public static void alignToDirection(BlockDisplay display, Vector direction) { - Location loc = display.getLocation().clone(); - loc.setDirection(direction); - display.teleport(loc); - } - -} diff --git a/src/main/java/me/trouper/alias/server/systems/tracing/ReflectionResult.java b/src/main/java/me/trouper/alias/server/systems/tracing/ReflectionResult.java deleted file mode 100755 index 78a1ebf..0000000 --- a/src/main/java/me/trouper/alias/server/systems/tracing/ReflectionResult.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.trouper.alias.server.systems.tracing; - -import org.bukkit.util.Vector; - -public class ReflectionResult { - private final Point hitPoint; - private final boolean shouldReflect; - private final Vector reflectedDirection; - - public ReflectionResult(Point hitPoint, boolean shouldReflect, Vector reflectedDirection) { - this.hitPoint = hitPoint; - this.shouldReflect = shouldReflect; - this.reflectedDirection = reflectedDirection; - } - - public Point getHitPoint() { - return hitPoint; - } - - public boolean shouldReflect() { - return shouldReflect; - } - - public Vector getReflectedDirection() { - return reflectedDirection; - } -} diff --git a/src/main/java/me/trouper/alias/server/systems/world/ExplosionUtils.java b/src/main/java/me/trouper/alias/server/systems/world/Explosion.java similarity index 86% rename from src/main/java/me/trouper/alias/server/systems/world/ExplosionUtils.java rename to src/main/java/me/trouper/alias/server/systems/world/Explosion.java index 2da0392..83316de 100644 --- a/src/main/java/me/trouper/alias/server/systems/world/ExplosionUtils.java +++ b/src/main/java/me/trouper/alias/server/systems/world/Explosion.java @@ -1,20 +1,26 @@ package me.trouper.alias.server.systems.world; -import me.trouper.alias.server.Main; +import me.trouper.alias.AliasContext; import me.trouper.alias.server.systems.TaskManager; -import me.trouper.alias.server.systems.burning.BlockBurner; +import me.trouper.alias.server.systems.world.burning.BlockBurner; +import me.trouper.alias.utils.ParticleUtils; import me.trouper.alias.utils.SoundPlayer; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.entity.LivingEntity; -import org.bukkit.util.Vector; import java.util.*; import java.util.concurrent.ThreadLocalRandom; -public class ExplosionUtils implements Main { +public class Explosion { - public static ExplosionResult createExplosion(Location center, ExplosionOptions options) { + private final AliasContext context; + + public Explosion(AliasContext context) { + this.context = context; + } + + public ExplosionResult createExplosion(Location center, ExplosionOptions options) { World world = center.getWorld(); if (world == null) throw new IllegalArgumentException("Center location must have a valid world"); @@ -24,8 +30,8 @@ public class ExplosionUtils implements Main { Map affectedBlocks = getBlocksInRadius(center, maxBurnRadius); Map affectedEntities = getEntitiesInRadius(center, maxBurnRadius); - BlockBurner burner = new BlockBurner(options.getBurnOptions()); - TaskManager sharedTaskManager = burner.getTaskManager(); + TaskManager sharedTaskManager = context.createTaskManager(); + BlockBurner burner = new BlockBurner(sharedTaskManager,options.getBurnOptions()); ExplosionResult result = new ExplosionResult(affectedBlocks.keySet(), sharedTaskManager); List blocksToDestroy = new ArrayList<>(); @@ -39,7 +45,7 @@ public class ExplosionUtils implements Main { scheduleDamage(affectedEntities, options, sharedTaskManager); if (options.isCreateParticles() || options.isPlaySound()) { - createExplosionEffects(center, options); + createExplosionEffects(center, options, sharedTaskManager); } return result; @@ -50,7 +56,7 @@ public class ExplosionUtils implements Main { } } - private static Map getBlocksInRadius(Location center, double radius) { + private Map getBlocksInRadius(Location center, double radius) { World world = center.getWorld(); if (world == null) return Collections.emptyMap(); @@ -82,7 +88,7 @@ public class ExplosionUtils implements Main { return blocks; } - private static Map getEntitiesInRadius(Location center, double radius) { + private Map getEntitiesInRadius(Location center, double radius) { List rawList = center.getNearbyEntities(radius, radius, radius).stream() .filter(entity -> entity instanceof LivingEntity) .map(entity -> (LivingEntity) entity) @@ -101,7 +107,7 @@ public class ExplosionUtils implements Main { return entities; } - private static void categorizeBlocks(Map affectedBlocks, ExplosionOptions options, + private void categorizeBlocks(Map affectedBlocks, ExplosionOptions options, List blocksToDestroy, List blocksToBurn, Map blocksHeatMap) { ThreadLocalRandom random = ThreadLocalRandom.current(); @@ -154,14 +160,14 @@ public class ExplosionUtils implements Main { } } - private static float calculateHeat(double distance, ExplosionOptions options) { + private float calculateHeat(double distance, ExplosionOptions options) { double normalizedDistance = distance / options.getMaxBurnRadius(); double heatRange = options.getMaxHeat() - options.getMinHeat(); double heatFactor = Math.pow(1.0 - normalizedDistance, 2.0); return (float) (options.getMinHeat() + heatRange * heatFactor); } - private static void scheduleDestruction(List blocksToDestroy, ExplosionOptions options, TaskManager taskManager) { + private void scheduleDestruction(List blocksToDestroy, ExplosionOptions options, TaskManager taskManager) { if (blocksToDestroy.isEmpty()) return; long destructionDelayTicks = (long) (options.getDestructionDelay() * 20); @@ -193,7 +199,7 @@ public class ExplosionUtils implements Main { }, destructionDelayTicks); } - private static void scheduleBurning(List blocksToMaybeBurn, Map blocksHeatMap, + private void scheduleBurning(List blocksToMaybeBurn, Map blocksHeatMap, BlockBurner burner, Location center, ExplosionOptions options, TaskManager taskManager) { if (blocksToMaybeBurn.isEmpty()) return; @@ -237,7 +243,7 @@ public class ExplosionUtils implements Main { }, burnDelayTicks); } - private static void scheduleDamage(Map affected, ExplosionOptions options, TaskManager taskManager) { + private void scheduleDamage(Map affected, ExplosionOptions options, TaskManager taskManager) { if (affected.isEmpty()) return; double baseDamage = options.getBaseDamage(); @@ -279,7 +285,7 @@ public class ExplosionUtils implements Main { } } - private static void createExplosionEffects(Location center, ExplosionOptions options) { + private void createExplosionEffects(Location center, ExplosionOptions options, TaskManager taskManager) { World world = center.getWorld(); if (world == null) return; @@ -296,16 +302,22 @@ public class ExplosionUtils implements Main { world.spawnParticle(Particle.EXPLOSION, center, 20, 2, 2, 2, 0.1); world.spawnParticle(Particle.LARGE_SMOKE, center, 15, 1, 1, 1, 0.05); world.spawnParticle(Particle.FLAME, center, 30, 3, 3, 3, 0.1); - - Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> { + + taskManager.scheduleTask(()->{ if (center.getWorld() != null) { center.getWorld().spawnParticle(Particle.SMOKE, center, 50, 4, 4, 4, 0.02); + ParticleUtils.builder() + .type(Particle.SMOKE) + .count((int) options.getCoreRadius()*10) + .offset(options.getCoreRadius()/2,options.getCoreRadius()/2,options.getCoreRadius()/2) + .speed(0.05F) + .spawn(center); } - }, 20L); + },20L); } } - private static boolean isOccluded(Block block, Set doesNotOcclude) { + private boolean isOccluded(Block block, Set doesNotOcclude) { if (block == null) return true; return isOccluding(block.getRelative(0, 1, 0), doesNotOcclude) && @@ -316,7 +328,7 @@ public class ExplosionUtils implements Main { isOccluding(block.getRelative(0, 0, -1), doesNotOcclude); } - private static boolean isOccluding(Block block, Set doesNotOcclude) { + private boolean isOccluding(Block block, Set doesNotOcclude) { return block != null && block.getType().isOccluding() && !doesNotOcclude.contains(block); } } \ No newline at end of file diff --git a/src/main/java/me/trouper/alias/server/systems/world/ExplosionOptions.java b/src/main/java/me/trouper/alias/server/systems/world/ExplosionOptions.java index bc763a5..cdbf9ad 100644 --- a/src/main/java/me/trouper/alias/server/systems/world/ExplosionOptions.java +++ b/src/main/java/me/trouper/alias/server/systems/world/ExplosionOptions.java @@ -1,6 +1,6 @@ package me.trouper.alias.server.systems.world; -import me.trouper.alias.server.systems.burning.BurnOptions; +import me.trouper.alias.server.systems.world.burning.BurnOptions; public class ExplosionOptions { private double coreRadius = 3.0; diff --git a/src/main/java/me/trouper/alias/server/systems/world/ExplosionResult.java b/src/main/java/me/trouper/alias/server/systems/world/ExplosionResult.java index a30a007..cbbfe8f 100644 --- a/src/main/java/me/trouper/alias/server/systems/world/ExplosionResult.java +++ b/src/main/java/me/trouper/alias/server/systems/world/ExplosionResult.java @@ -1,13 +1,12 @@ package me.trouper.alias.server.systems.world; -import me.trouper.alias.server.Main; import me.trouper.alias.server.systems.TaskManager; import org.bukkit.block.Block; import java.io.Closeable; import java.util.Set; -public class ExplosionResult implements Closeable, Main { +public class ExplosionResult implements Closeable { private State previousState; private TaskManager taskManager; private volatile boolean closed = false; diff --git a/src/main/java/me/trouper/alias/server/systems/world/Snapshot.java b/src/main/java/me/trouper/alias/server/systems/world/Snapshot.java index 8d9d064..3027f01 100644 --- a/src/main/java/me/trouper/alias/server/systems/world/Snapshot.java +++ b/src/main/java/me/trouper/alias/server/systems/world/Snapshot.java @@ -1,7 +1,5 @@ package me.trouper.alias.server.systems.world; -import me.trouper.alias.server.Main; -import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.inventory.Inventory; @@ -50,29 +48,27 @@ public class Snapshot { public void restore(Block block) { if (block == null || state == null) return; - Bukkit.getScheduler().runTask(Main.main.getPlugin(), () -> { - try { - block.setBlockData(state.getBlockData()); + try { + block.setBlockData(state.getBlockData()); - if (inventory != null && block.getState() instanceof InventoryHolder) { - InventoryHolder holder = (InventoryHolder) block.getState(); - Inventory inv = holder.getInventory(); + if (inventory != null && block.getState() instanceof InventoryHolder) { + InventoryHolder holder = (InventoryHolder) block.getState(); + Inventory inv = holder.getInventory(); - inv.clear(); + inv.clear(); - for (Map.Entry entry : inventory.entrySet()) { - int slot = entry.getKey(); - ItemStack item = entry.getValue(); + for (Map.Entry entry : inventory.entrySet()) { + int slot = entry.getKey(); + ItemStack item = entry.getValue(); - if (slot >= 0 && slot < inv.getSize() && item != null) { - inv.setItem(slot, item.clone()); - } + if (slot >= 0 && slot < inv.getSize() && item != null) { + inv.setItem(slot, item.clone()); } } - } catch (Exception e) { - System.err.println("Failed to restore block at " + block.getLocation() + ": " + e.getMessage()); } - }); + } catch (Exception e) { + System.err.println("Failed to restore block at " + block.getLocation() + ": " + e.getMessage()); + } } public BlockState getState() { diff --git a/src/main/java/me/trouper/alias/server/systems/world/State.java b/src/main/java/me/trouper/alias/server/systems/world/State.java index 542cc19..9304d7e 100644 --- a/src/main/java/me/trouper/alias/server/systems/world/State.java +++ b/src/main/java/me/trouper/alias/server/systems/world/State.java @@ -4,11 +4,7 @@ import org.bukkit.Location; import org.bukkit.block.Block; import java.io.Closeable; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class State implements Closeable { private Map snapshots; diff --git a/src/main/java/me/trouper/alias/server/systems/burning/BlockBurner.java b/src/main/java/me/trouper/alias/server/systems/world/burning/BlockBurner.java similarity index 94% rename from src/main/java/me/trouper/alias/server/systems/burning/BlockBurner.java rename to src/main/java/me/trouper/alias/server/systems/world/burning/BlockBurner.java index ba28de7..a966cdf 100644 --- a/src/main/java/me/trouper/alias/server/systems/burning/BlockBurner.java +++ b/src/main/java/me/trouper/alias/server/systems/world/burning/BlockBurner.java @@ -1,6 +1,5 @@ -package me.trouper.alias.server.systems.burning; +package me.trouper.alias.server.systems.world.burning; -import me.trouper.alias.server.Main; import me.trouper.alias.server.systems.TaskManager; import org.bukkit.Material; import org.bukkit.block.Block; @@ -13,17 +12,17 @@ import java.io.Closeable; import java.util.*; import java.util.concurrent.ThreadLocalRandom; -public class BlockBurner implements Closeable, Main { +public class BlockBurner implements Closeable { private final BurnOptions options; private final BurnPalette palette; private final TaskManager taskManager; private final Set visited = new HashSet<>(); private final Map burning = new HashMap<>(); - public BlockBurner(BurnOptions options) { + public BlockBurner(TaskManager taskManager, BurnOptions options) { + this.taskManager = taskManager; this.options = options; this.palette = new BurnPalette(); - this.taskManager = new TaskManager(); } @Override diff --git a/src/main/java/me/trouper/alias/server/systems/burning/BurnOptions.java b/src/main/java/me/trouper/alias/server/systems/world/burning/BurnOptions.java similarity index 87% rename from src/main/java/me/trouper/alias/server/systems/burning/BurnOptions.java rename to src/main/java/me/trouper/alias/server/systems/world/burning/BurnOptions.java index 8291729..5457ee6 100644 --- a/src/main/java/me/trouper/alias/server/systems/burning/BurnOptions.java +++ b/src/main/java/me/trouper/alias/server/systems/world/burning/BurnOptions.java @@ -1,4 +1,4 @@ -package me.trouper.alias.server.systems.burning; +package me.trouper.alias.server.systems.world.burning; public class BurnOptions { private boolean disabled = false; diff --git a/src/main/java/me/trouper/alias/server/systems/burning/BurnPalette.java b/src/main/java/me/trouper/alias/server/systems/world/burning/BurnPalette.java similarity index 98% rename from src/main/java/me/trouper/alias/server/systems/burning/BurnPalette.java rename to src/main/java/me/trouper/alias/server/systems/world/burning/BurnPalette.java index 15b846b..3c85bb2 100644 --- a/src/main/java/me/trouper/alias/server/systems/burning/BurnPalette.java +++ b/src/main/java/me/trouper/alias/server/systems/world/burning/BurnPalette.java @@ -1,4 +1,4 @@ -package me.trouper.alias.server.systems.burning; +package me.trouper.alias.server.systems.world.burning; import org.bukkit.Material; import org.bukkit.Tag; @@ -30,7 +30,6 @@ public class BurnPalette { private final List burnWaveTrail; public BurnPalette() { - // Initialize burn wave trail this.burnWaveTrail = Arrays.asList( Material.ORANGE_STAINED_GLASS.createBlockData(), Material.BLACK_STAINED_GLASS.createBlockData(), @@ -38,7 +37,6 @@ public class BurnPalette { Material.LIGHT_GRAY_STAINED_GLASS.createBlockData() ); - // Initialize main burn wave List baseBurnWave = new ArrayList<>(); addRepeated(baseBurnWave, Material.ORANGE_STAINED_GLASS, 3); addRepeated(baseBurnWave, Material.SHROOMLIGHT, 2); diff --git a/src/main/java/me/trouper/alias/server/systems/burning/BurnStage.java b/src/main/java/me/trouper/alias/server/systems/world/burning/BurnStage.java similarity index 87% rename from src/main/java/me/trouper/alias/server/systems/burning/BurnStage.java rename to src/main/java/me/trouper/alias/server/systems/world/burning/BurnStage.java index 32fbeba..9551e30 100644 --- a/src/main/java/me/trouper/alias/server/systems/burning/BurnStage.java +++ b/src/main/java/me/trouper/alias/server/systems/world/burning/BurnStage.java @@ -1,4 +1,4 @@ -package me.trouper.alias.server.systems.burning; +package me.trouper.alias.server.systems.world.burning; import org.bukkit.block.data.BlockData; diff --git a/src/main/java/me/trouper/alias/update/AutoUpdater.java b/src/main/java/me/trouper/alias/server/update/AutoUpdater.java similarity index 59% rename from src/main/java/me/trouper/alias/update/AutoUpdater.java rename to src/main/java/me/trouper/alias/server/update/AutoUpdater.java index 7e539dc..d09022b 100644 --- a/src/main/java/me/trouper/alias/update/AutoUpdater.java +++ b/src/main/java/me/trouper/alias/server/update/AutoUpdater.java @@ -1,41 +1,46 @@ -package me.trouper.alias.update; +package me.trouper.alias.server.update; -import me.trouper.alias.Alias; +import me.trouper.alias.AliasContext; import me.trouper.alias.data.Common; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; +import me.trouper.alias.utils.UpdateUtils; import org.bukkit.plugin.java.JavaPlugin; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; -import java.security.MessageDigest; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.logging.Level; public class AutoUpdater { - public static boolean checkUpdate(JavaPlugin plugin, Common common) { + private final AliasContext context; + + public AutoUpdater(AliasContext context) { + this.context = context; + } + + public boolean checkUpdate() { try { - if (UpdateUtils.isDevelopmentEnvironment(plugin)) { - plugin.getLogger().info("Development environment detected, bypassing update check."); + if (UpdateUtils.isDevelopmentEnvironment(context.getPlugin())) { + context.getPlugin().getLogger().info("Development environment detected, bypassing update check."); return false; } - String updateURL = common.getUpdateURL(); + String updateURL = context.getCommon().getUpdateURL(); if (updateURL == null || updateURL.isEmpty()) { - plugin.getLogger().warning("Update URL is not set."); + context.getPlugin().getLogger().warning("Update URL is not set."); return false; } File updateDir = new File("plugins/update"); if (!updateDir.exists()) updateDir.mkdirs(); - File currentFile = UpdateUtils.findPluginJar(plugin); + File currentFile = UpdateUtils.findPluginJar(context.getPlugin()); if (currentFile == null) { - plugin.getLogger().severe("Could not locate plugin file in plugins folder."); + context.getPlugin().getLogger().severe("Could not locate plugin file in plugins folder."); return false; } @@ -44,13 +49,13 @@ public class AutoUpdater { String remoteHashHex = UpdateUtils.fetchRemoteHash(remoteHashURL); if (remoteHashHex == null) { - plugin.getLogger().warning("Failed to fetch remote hash from: " + remoteHashURL); + context.getPlugin().getLogger().warning("Failed to fetch remote hash from: " + remoteHashURL); return false; } String currentHashHex = UpdateUtils.bytesToHex(currentHash); if (remoteHashHex.equalsIgnoreCase(currentHashHex)) { - plugin.getLogger().info("Plugin is up to date."); + context.getPlugin().getLogger().info("Plugin is up to date."); return false; } @@ -60,35 +65,35 @@ public class AutoUpdater { String updateHashHex = UpdateUtils.bytesToHex(updateHash); if (remoteHashHex.equalsIgnoreCase(updateHashHex)) { - plugin.getLogger().info("An update is already downloaded and ready."); + context.getPlugin().getLogger().info("An update is already downloaded and ready."); return false; } else { - plugin.getLogger().info("Found outdated update file in plugins/update/. It will be replaced."); + context.getPlugin().getLogger().info("Found outdated update file in plugins/update/. It will be replaced."); updateFile.delete(); } } - plugin.getLogger().info("Update available. Downloading new version..."); + context.getPlugin().getLogger().info("Update available. Downloading new version..."); File downloaded = downloadFile(updateURL); if (downloaded == null) { - plugin.getLogger().warning("Failed to download update file."); + context.getPlugin().getLogger().warning("Failed to download update file."); return false; } File destination = new File(updateDir, currentFile.getName()); Files.copy(downloaded.toPath(), destination.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); - plugin.getLogger().info("Saved updated plugin to: " + destination.getAbsolutePath()); + context.getPlugin().getLogger().info("Saved updated plugin to: " + destination.getAbsolutePath()); return true; } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Error during update check", e); + context.getPlugin().getLogger().log(Level.SEVERE, "Error during update check", e); return false; } } - private static File downloadFile(String urlStr) throws IOException { + private File downloadFile(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("User-Agent", "AliasUpdater"); diff --git a/src/main/java/me/trouper/alias/utils/InventoryUtils.java b/src/main/java/me/trouper/alias/utils/InventoryUtils.java index 82d9696..565a485 100644 --- a/src/main/java/me/trouper/alias/utils/InventoryUtils.java +++ b/src/main/java/me/trouper/alias/utils/InventoryUtils.java @@ -4,13 +4,14 @@ import org.bukkit.block.BlockState; import org.bukkit.block.Container; import org.bukkit.entity.Entity; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BlockStateMeta; -public final class InventoryUtils { +public class InventoryUtils { public static Inventory getInventory(Entity entity) { - if (entity instanceof org.bukkit.inventory.InventoryHolder inventoryHolder) { + if (entity instanceof InventoryHolder inventoryHolder) { return inventoryHolder.getInventory(); } return null; diff --git a/src/main/java/me/trouper/alias/utils/ItemBuilder.java b/src/main/java/me/trouper/alias/utils/ItemBuilder.java index 4ed1922..d773477 100644 --- a/src/main/java/me/trouper/alias/utils/ItemBuilder.java +++ b/src/main/java/me/trouper/alias/utils/ItemBuilder.java @@ -1,19 +1,27 @@ package me.trouper.alias.utils; +import com.destroystokyo.paper.profile.PlayerProfile; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.OfflinePlayer; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerTextures; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.UUID; import java.util.function.Function; public class ItemBuilder { @@ -202,6 +210,78 @@ public class ItemBuilder { return this; } + public ItemBuilder playerHead(String playerName) { + ensurePlayerHead(); + if (this.meta instanceof SkullMeta skullMeta) { + OfflinePlayer player = Bukkit.getOfflinePlayer(playerName); + skullMeta.setOwningPlayer(player); + } + return this; + } + + public ItemBuilder playerHead(OfflinePlayer player) { + ensurePlayerHead(); + if (this.meta instanceof SkullMeta skullMeta) { + skullMeta.setOwningPlayer(player); + } + return this; + } + + public ItemBuilder playerHead(UUID uuid) { + ensurePlayerHead(); + if (this.meta instanceof SkullMeta skullMeta) { + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + skullMeta.setOwningPlayer(player); + } + return this; + } + + public ItemBuilder skullTexture(String textureUrl) { + ensurePlayerHead(); + if (this.meta instanceof SkullMeta skullMeta) { + try { + PlayerProfile profile = Bukkit.createProfile(UUID.randomUUID()); + PlayerTextures textures = profile.getTextures(); + textures.setSkin(new URL(textureUrl)); + profile.setTextures(textures); + skullMeta.setPlayerProfile(profile); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid texture URL: " + textureUrl, e); + } + } + return this; + } + + public ItemBuilder skullProfile(PlayerProfile profile) { + ensurePlayerHead(); + if (this.meta instanceof SkullMeta skullMeta) { + skullMeta.setPlayerProfile(profile); + } + return this; + } + + public ItemBuilder createPlayerHead(String playerName, String displayName) { + return material(Material.PLAYER_HEAD) + .playerHead(playerName) + .displayName(displayName); + } + + public ItemBuilder createCustomHead(String textureUrl, String displayName) { + return material(Material.PLAYER_HEAD) + .skullTexture(textureUrl) + .displayName(displayName); + } + + private void ensurePlayerHead() { + if (this.stack.getType() != Material.PLAYER_HEAD) { + this.stack = this.stack.withType(Material.PLAYER_HEAD); + this.meta = this.stack.getItemMeta(); + if (this.meta == null) { + throw new IllegalStateException("Failed to get SkullMeta after converting to player head"); + } + } + } + public ItemStack build() { this.stack.setItemMeta(this.meta); return this.stack.clone(); @@ -246,4 +326,14 @@ public class ItemBuilder { public static ItemBuilder of(ItemStack stack) { return create(stack); } + + public static ItemBuilder headOf(String playerName) { + return create(Material.PLAYER_HEAD) + .playerHead(playerName); + } + + public static ItemBuilder headOfTexture(String url) { + return create(Material.PLAYER_HEAD) + .skullTexture(url); + } } diff --git a/src/main/java/me/trouper/alias/server/systems/visual/ParticleUtils.java b/src/main/java/me/trouper/alias/utils/ParticleUtils.java similarity index 95% rename from src/main/java/me/trouper/alias/server/systems/visual/ParticleUtils.java rename to src/main/java/me/trouper/alias/utils/ParticleUtils.java index 2236419..db76486 100644 --- a/src/main/java/me/trouper/alias/server/systems/visual/ParticleUtils.java +++ b/src/main/java/me/trouper/alias/utils/ParticleUtils.java @@ -1,6 +1,8 @@ -package me.trouper.alias.server.systems.visual; +package me.trouper.alias.utils; -import org.bukkit.*; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; diff --git a/src/main/java/me/trouper/alias/update/UpdateUtils.java b/src/main/java/me/trouper/alias/utils/UpdateUtils.java similarity index 95% rename from src/main/java/me/trouper/alias/update/UpdateUtils.java rename to src/main/java/me/trouper/alias/utils/UpdateUtils.java index 2fab263..48029f2 100644 --- a/src/main/java/me/trouper/alias/update/UpdateUtils.java +++ b/src/main/java/me/trouper/alias/utils/UpdateUtils.java @@ -1,4 +1,4 @@ -package me.trouper.alias.update; +package me.trouper.alias.utils; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; @@ -86,7 +86,6 @@ public class UpdateUtils { try { return "TRUE".equalsIgnoreCase(System.getenv("ALIAS_DEVELOPMENT")); } catch (Exception e) { - plugin.getLogger().warning("Could not determine runtime environment, assuming production."); return false; } } diff --git a/src/main/java/me/trouper/alias/utils/misc/ReflectionUtils.java b/src/main/java/me/trouper/alias/utils/misc/ReflectionUtils.java index 1b11dbf..1a65ecc 100644 --- a/src/main/java/me/trouper/alias/utils/misc/ReflectionUtils.java +++ b/src/main/java/me/trouper/alias/utils/misc/ReflectionUtils.java @@ -3,7 +3,6 @@ package me.trouper.alias.utils.misc; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; -import java.net.URL; import java.util.HashSet; import java.util.Set; import java.util.jar.JarEntry;