diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/README.md b/README.md
index 1a7ac04..f009fb1 100644
--- a/README.md
+++ b/README.md
@@ -4,17 +4,18 @@ SimpleCompass is a small plugin for [Spigot](https://www.spigotmc.org) Minecraft
## How to install
-There is no dependencies, simply drop the jar file into your plugin directory, then restart (or reload) your server. All configuration parameters are explained in this [config.yml](https://github.com/arboriginal/SimpleCompass/blob/master/src/config.yml).
+There is no dependencies, simply drop the jar file into your plugin directory, then restart (or reload) your server. All configuration parameters are explained in this [config.yml](https://github.com/arboriginal/SimpleCompass/blob/master/src/main/resources/config.yml).
You can download the last release here: [SimpleCompass.jar](https://github.com/arboriginal/SimpleCompass/releases).
## Permissions
-All permissions are listed with a short description in this [plugin.yml](https://github.com/arboriginal/SimpleCompass/blob/master/src/plugin.yml#L41).
+All permissions are listed with a short description in this [plugin.yml](https://github.com/arboriginal/SimpleCompass/blob/master/src/main/resources/plugin.yml#L41).
## Commands
* **/scompass** visual interface (clickable book)
+* **/scompass-toggle** to quickly toggle on/off your compass(es)
* **/scompass-option** will show the menu to choose where and when display the compass
* **/sctrack** to tracker a position, coordinates or a player (see below)
* **/scompass-reload** will reload the configuration
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..771ddcd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,60 @@
+
+ 4.0.0
+
+ SimpleCompass
+ Simple compass to help player who don't have sense of direction.
+
+ me.arboriginal
+ SimpleCompass
+ 1.1
+
+
+
+ arboriginal
+ https://github.com/arboriginal
+
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+ 1.13
+ simplecompass.63140
+ plugin.SimpleCompass
+
+
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+
+
+
+ org.spigotmc
+ spigot-api
+ 1.13.2-R0.1-SNAPSHOT
+ provided
+
+
+
+
+ src/main/java
+ clean package
+
+
+ src/main/resources
+ true
+
+ config.yml
+ plugin.yml
+ lang/*.yml
+
+
+
+
+
diff --git a/src/main/java/me/arboriginal/SimpleCompass/commands/AbstractCommand.java b/src/main/java/me/arboriginal/SimpleCompass/commands/AbstractCommand.java
new file mode 100644
index 0000000..e0a9f71
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/commands/AbstractCommand.java
@@ -0,0 +1,228 @@
+package me.arboriginal.SimpleCompass.commands;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.bukkit.entity.Player;
+import com.google.common.collect.ImmutableMap;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TrackingActions;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public abstract class AbstractCommand {
+ protected SimpleCompass sc;
+ protected String mainCommand;
+ protected Map subCommands = new HashMap();
+
+ public enum SubCmds {
+ OPTION, TRACK,
+ }
+
+ public enum CompassOptions {
+ ALWAYS, ELYTRA, ELYTRA_VEHICLE, VEHICLE, DISABLED,
+ }
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public AbstractCommand(SimpleCompass plugin, String command) {
+ sc = plugin;
+ mainCommand = command;
+ }
+
+ // Abstract methods ------------------------------------------------------------------------------------------------
+
+ protected abstract void showOptions(Player player, CompassTypes modified);
+
+ // Public methods > Options ----------------------------------------------------------------------------------------
+
+ public CompassTypes modifyOption(Player player, CompassTypes type, String value) {
+ if (value.equals(CompassModes.MODE180.toString()) || value.equals(CompassModes.MODE360.toString()))
+ sc.datas.compassModeSet(player, type, CompassModes.valueOf(value));
+ else
+ sc.datas.compassOptionSet(player, type, CompassOptions.valueOf(value));
+ return type;
+ }
+
+ public boolean performCommandOption(Player player, String[] args) {
+ if (!hasAccessOption(player, args, true)) return false;
+
+ CompassTypes modified = null;
+
+ switch (args.length) {
+ case 0:
+ break;
+
+ case 1:
+ if (allowedOptions(player).size() != 1) {
+ sc.sendMessage(player, "missing_type");
+ return false;
+ }
+
+ modified = modifyOption(player, CompassTypes.valueOf(args[1]), allowedOptions(player).get(0));
+ break;
+
+ case 2:
+ modified = modifyOption(player, CompassTypes.valueOf(args[1]), args[0]);
+ break;
+
+ default:
+ return false;
+ }
+
+ showOptions(player, modified);
+
+ return true;
+ }
+
+ // Protected methods > Options -------------------------------------------------------------------------------------
+
+ protected List completeCommandOption(Player player, String[] args) {
+ switch (args.length) {
+ case 1:
+ return getFilteredList(allowedOptions(player), args[0]);
+
+ case 2:
+ if (allowedOptions(player).contains(args[0])) return getFilteredList(allowedTypes(player), args[1]);
+ }
+
+ return null;
+ }
+
+ protected List allowedOptions(Player player) {
+ List list = new ArrayList<>();
+
+ for (CompassOptions option : CompassOptions.values())
+ if (player.hasPermission("scompass.option." + option)) list.add(option.toString());
+
+ for (CompassModes mode : CompassModes.values()) list.add(mode.toString());
+
+ return list;
+ }
+
+ protected List allowedTypes(Player player) {
+ List list = new ArrayList();
+
+ for (CompassTypes type : CompassTypes.values())
+ if (player.hasPermission("scompass.use." + type)) list.add(type);
+
+ return list;
+ }
+
+ protected boolean hasAccessOption(Player player, String[] args, boolean showError) {
+ if (args.length > 2 || allowedOptions(player).isEmpty() || allowedTypes(player).isEmpty()) {
+ if (showError) sc.sendMessage(player, "wrong_usage");
+ return false;
+ }
+
+ if (args.length > 0 && !allowedOptions(player).contains(args[0])) {
+ if (showError) sc.sendMessage(player, "invalid_option", ImmutableMap.of("option", args[0]));
+ return false;
+ }
+
+ if (args.length > 1 && !isAllowedTypes(player, args[1])) {
+ if (showError) sc.sendMessage(player, "invalid_type", ImmutableMap.of("type", args[0]));
+ return false;
+ }
+
+ return true;
+ }
+
+ protected boolean isAllowedTypes(Player player, String value) {
+ for (CompassTypes type : allowedTypes(player)) if (type.toString().equalsIgnoreCase(value)) return true;
+
+ return false;
+ }
+
+ // Public methods > Trackers ---------------------------------------------------------------------------------------
+
+ public boolean performCommandTrack(Player player, String[] args) {
+ HashMap cmdArgs = getTrackArguments(player, args);
+ AbstractTracker tracker = (AbstractTracker) cmdArgs.get("tracker");
+ TrackingActions action = (TrackingActions) cmdArgs.get("action");
+ String target = (String) cmdArgs.get("target");
+
+ if (tracker == null) {
+ sc.sendMessage(player, "wrong_usage");
+ return false;
+ }
+
+ if (action == null) {
+ List list = tracker.list(player, null, "");
+
+ if (list == null || list.isEmpty()) tracker.sendMessage(player, "list_empty");
+ else tracker.sendMessage(player, "list", ImmutableMap.of("list", String.join(", ", list)));
+ return true;
+ }
+
+ if (action == TrackingActions.HELP && args.length == 2 && player.hasPermission("scompass.help")) {
+ String help = tracker.help(player,
+ mainCommand + (subCommands.containsKey(SubCmds.TRACK) ? " " + subCommands.get(SubCmds.TRACK) : ""));
+
+ if (help != null && !help.isEmpty()) player.sendMessage(help);
+ return true;
+ }
+
+ return tracker.perform(player, "sctrack", action, target, args);
+ }
+
+ // Protected methods > Trackers ------------------------------------------------------------------------------------
+
+ protected List completeCommandTrack(Player player, String[] args) {
+ if (args.length == 1) return sc.targets.getTrackersList((Player) player, args[0]);
+
+ HashMap parsed = getTrackArguments((Player) player, args);
+ AbstractTracker tracker = (AbstractTracker) parsed.get("tracker");
+ if (tracker != null) return tracker.commandSuggestions((Player) player, args, parsed);
+
+ return null;
+ }
+
+ protected List getActionsAvailable(Player player, String trackerID) {
+ if (!sc.targets.canUseTracker(player, trackerID)) return null;
+ return sc.trackers.get(trackerID).getActionsAvailable(player, false);
+ }
+
+ protected HashMap getTrackArguments(Player player, String[] args) {
+ HashMap parsed = new HashMap();
+ if (args.length == 0) return parsed;
+
+ AbstractTracker tracker = sc.targets.getTrackerByName(args[0]);
+ if (tracker == null || !sc.targets.getAvailableTrackers(player).contains(tracker.trackerID()))
+ return parsed;
+
+ parsed.put("tracker", tracker);
+ if (args.length == 1) return parsed;
+
+ tracker.parseArguments(player, args, parsed);
+ return parsed;
+ }
+
+ // Utils methods ---------------------------------------------------------------------------------------------------
+
+ protected ImmutableMap clickableOption(CompassTypes type, Object option, Object selected) {
+ String optionType = (option instanceof CompassModes) ? "modes" : "options";
+ String optionName = sc.locale.getString(optionType + "." + option);
+
+ return ImmutableMap.of(
+ "text", sc.prepareMessage("commands." + mainCommand + ".options."
+ + (option.toString().equals(selected.toString()) ? "active" : "inactive"),
+ ImmutableMap.of("option", optionName)),
+ "hover", sc.prepareMessage("commands." + mainCommand + ".options.hover",
+ ImmutableMap.of("option", optionName, "type", sc.locale.getString("types." + type))),
+ "click", "/" + mainCommand
+ + (subCommands.containsKey(SubCmds.OPTION) ? " " + subCommands.get(SubCmds.OPTION) : "")
+ + " " + option + " " + type);
+ }
+
+ protected List getFilteredList(List> inputList, String startWith) {
+ List list = new ArrayList();
+
+ for (Object candidate : inputList)
+ if (candidate.toString().toLowerCase().startsWith(startWith.toLowerCase())) list.add(candidate.toString());
+
+ return list;
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/commands/InterfaceCommand.java b/src/main/java/me/arboriginal/SimpleCompass/commands/InterfaceCommand.java
new file mode 100644
index 0000000..6b88dc7
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/commands/InterfaceCommand.java
@@ -0,0 +1,416 @@
+package me.arboriginal.SimpleCompass.commands;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.bukkit.Material;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.conversations.Conversation;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.BookMeta;
+import org.bukkit.scheduler.BukkitRunnable;
+import com.google.common.collect.ImmutableMap;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TargetSelector;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TrackingActions;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+import me.arboriginal.SimpleCompass.utils.NMSUtil;
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+public class InterfaceCommand extends AbstractCommand implements CommandExecutor, TabCompleter {
+ private static final String SELECT_TARGET = "*S313C7:74R637*", SELECT_PAGER = "*S313C7:P463R*";
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public InterfaceCommand(SimpleCompass plugin) {
+ super(plugin, "scompass");
+
+ subCommands.put(SubCmds.OPTION, plugin.locale.getString("subcommands." + SubCmds.OPTION));
+
+ if (!sc.trackers.isEmpty())
+ subCommands.put(SubCmds.TRACK, plugin.locale.getString("subcommands." + SubCmds.TRACK));
+ }
+
+ // CommandExecutor methods -----------------------------------------------------------------------------------------
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (!(sender instanceof Player)) sc.sendMessage(sender, "command_only_for_players");
+ else if (((Player) sender).isSleeping()) {
+ sc.sendMessage(sender, "command_no_sleeping");
+ return true;
+ }
+ else if (args.length == 0) showOptions((Player) sender, null);
+ else if (args[0].equals(SELECT_TARGET)) targetSelector((Player) sender, subArgs(args));
+ else if (subCommand((Player) sender, SubCmds.OPTION, args[0]))
+ return performCommandOption((Player) sender, subArgs(args));
+ else if (subCommand((Player) sender, SubCmds.TRACK, args[0]))
+ return performCommandTrack((Player) sender, subArgs(args));
+ else {
+ sc.sendMessage(sender, "wrong_usage");
+ return false;
+ }
+
+ return true;
+ }
+
+ // TabCompleter methods --------------------------------------------------------------------------------------------
+
+ @Override
+ public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
+ if (args.length == 0 || !(sender instanceof Player)) return null;
+
+ if (args.length == 1) {
+ List list = new ArrayList();
+
+ for (SubCmds subCommand : subCommands.keySet()) {
+ String subCommandName = subCommands.get(subCommand);
+
+ if (subCommandName.toLowerCase().startsWith(args[0].toLowerCase())) list.add(subCommandName);
+ }
+
+ return list;
+ }
+ else if (subCommand((Player) sender, SubCmds.OPTION, args[0]))
+ return completeCommandOption((Player) sender, Arrays.stream(args).skip(1).toArray(String[]::new));
+ else if (subCommand((Player) sender, SubCmds.TRACK, args[0]))
+ return completeCommandTrack((Player) sender, Arrays.stream(args).skip(1).toArray(String[]::new));
+
+ sc.sendMessage(sender, "wrong_usage");
+ return null;
+ }
+
+ // CommandUtil methods ---------------------------------------------------------------------------------------------
+
+ @Override
+ public void showOptions(Player player, CompassTypes modified) {
+ if (modified != null) sc.sendMessage(player, "commands." + mainCommand + ".saved");
+
+ ItemStack book = buildInterface(player, modified);
+
+ if (sc.config.getBoolean("interface.give_book_everytime") || !NMSUtil.openBook(player, book))
+ giveBook(player, book);
+ }
+
+ // Private methods -------------------------------------------------------------------------------------------------
+
+ private ItemStack buildInterface(Player player, CompassTypes modified) {
+ ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
+ BookMeta meta = bookMeta(book, "options");
+
+ List types = allowedTypes(player);
+ List opts = allowedOptions(player);
+
+ if (modified != null) buildInterfaceOptions(player, meta, types, opts, modified);
+ if (player.hasPermission("scompass.track")) buildInterfaceTracking(player, meta);
+ if (modified == null) buildInterfaceOptions(player, meta, types, opts, modified);
+
+ book.setItemMeta(meta);
+ return book;
+ }
+
+ private void buildInterfaceOptions(
+ Player player, BookMeta meta, List types, List opts, CompassTypes modified) {
+ if (!player.hasPermission("scompass.option")) return;
+
+ if (modified != null) {
+ types.remove(modified);
+ meta.spigot().addPage(buildPage(player, modified, opts));
+ }
+
+ for (CompassTypes type : types) meta.spigot().addPage(buildPage(player, type, opts));
+ }
+
+ private void buildInterfaceTracking(Player player, BookMeta meta) {
+ List trackers = new ArrayList();
+ Set ordered = sc.locale.getConfigurationSection(
+ "commands." + mainCommand + ".track.buttons").getKeys(false);
+
+ for (String trackerID : sc.targets.trackersPriority)
+ if (player.hasPermission("scompass.track." + trackerID)) trackers.add(trackerID);
+
+ for (String[] part : chunk(trackers.toArray(new String[trackers.size()]),
+ sc.locale.getInt("commands." + mainCommand + ".track.per_page"))) {
+ ArrayList content = new ArrayList();
+ content.add(new TextComponent(sc.prepareMessage("commands." + mainCommand + ".header") + "\n\n"));
+
+ for (String trackerID : part) {
+ AbstractTracker tracker = sc.trackers.get(trackerID);
+ if (tracker == null) continue;
+
+ Map> commands = new LinkedHashMap>();
+ List actions = tracker.getActionsAvailable(player, false);
+
+ for (String actionName : ordered) {
+ TrackingActions action; // @formatter:off
+ try { action = TrackingActions.valueOf(actionName); } catch (Exception e) { continue; }
+ String text = sc.prepareMessage("commands." + mainCommand + ".track.buttons." + action + ".text");
+ // @formatter:on
+ if (!actions.contains(action)) {
+ commands.put("{" + action + "}", ImmutableMap.of("text", inactiveCommandText(text)));
+ continue;
+ }
+
+ commands.put("{" + action + "}", ImmutableMap.of("text", text, "hover",
+ sc.prepareMessage("commands." + mainCommand + ".track.buttons." + action + ".hover"),
+ "click", "/" + mainCommand
+ + " " + (tracker.requireTarget(action) != TargetSelector.NONE
+ ? SELECT_TARGET
+ : subCommands.get(SubCmds.TRACK))
+ + " " + tracker.trackerName() + " " + tracker.getActionName(action)));
+ }
+
+ content.add(sc.createClickableMessage(sc.prepareMessage("commands." + mainCommand + ".track.content",
+ ImmutableMap.of("tracker", tracker.trackerName(), "buttons",
+ String.join(" ", commands.keySet())))
+ + "\n", commands));
+ }
+
+ meta.spigot().addPage(content.stream().toArray(BaseComponent[]::new));
+ }
+ }
+
+ private String inactiveCommandText(String text) {
+ String[] inactiveText = text.replace("&", "§").split("§");
+
+ String out = "§7";
+ for (int i = 0; i < inactiveText.length; i++) if (inactiveText[i].length() > 1)
+ out += (inactiveText[i].charAt(0) == 'l') ? "§" + inactiveText[i] + "§7" : inactiveText[i].substring(1);
+
+ return out;
+ }
+
+ private BaseComponent[] buildPage(Player player, CompassTypes type, List optionsList) {
+ CompassModes typeMode = sc.datas.compassModeGet(player, type);
+ CompassOptions selected = sc.datas.compassOptionGet(player, type);
+ ArrayList content = new ArrayList();
+ Map> commands = new LinkedHashMap>();
+
+ content.add(new TextComponent(sc.prepareMessage("commands." + mainCommand + ".header")));
+
+ for (CompassModes mode : CompassModes.values())
+ commands.put("{" + mode + "}", clickableOption(type, mode, typeMode));
+
+ content.add(sc.createClickableMessage(sc.prepareMessage("commands." + mainCommand + ".content",
+ ImmutableMap.of("type", sc.locale.getString("types." + type))), commands));
+ content.add(new TextComponent("\n" + sc.prepareMessage("commands." + mainCommand + ".footer") + "\n"));
+
+ commands = new LinkedHashMap>();
+
+ for (String option : optionsList) {
+ if (option.equals(CompassModes.MODE180.toString()) || option.equals(CompassModes.MODE360.toString()))
+ continue;
+
+ commands.put("{" + option + "}", clickableOption(type, option, selected));
+ }
+
+ content.add(sc.createClickableMessage(String.join("\n", commands.keySet()), commands));
+
+ return content.stream().toArray(BaseComponent[]::new);
+ }
+
+ private void giveBook(Player player, ItemStack book) {
+ if (!sc.config.getBoolean("interface.give_book_everytime")
+ && !sc.config.getBoolean("interface.give_book_on_fail")) {
+ sc.sendMessage(player, "interface_failed_auto_open");
+ return;
+ }
+
+ long cooldown = sc.datas.cooldownBookGet(player);
+
+ if (cooldown > 0) {
+ sc.sendMessage(player, "interface_book_give_cooldown",
+ ImmutableMap.of("delay", "" + sc.formatTime(cooldown)));
+ return;
+ }
+
+ int slot = player.getInventory().firstEmpty();
+
+ if (slot == -1) {
+ sc.sendMessage(player, "interface_failed_auto_open_give_failed");
+ return;
+ }
+
+ player.getInventory().setItem(slot, book);
+ sc.datas.cooldownBookSet(player);
+ sc.sendMessage(player, "interface_failed_auto_open_give");
+ }
+
+ private boolean subCommand(Player player, SubCmds command, String argument) {
+ return subCommands.get(command) != null
+ && argument.toLowerCase().equals(subCommands.get(command).toLowerCase())
+ && player.hasPermission("scompass." + command.toString().toLowerCase())
+ && (command == SubCmds.OPTION || !sc.trackers.isEmpty());
+ }
+
+ private void targetSelector(Player player, String[] args) {
+ HashMap cmdArgs = getTrackArguments(player, args);
+ if (cmdArgs.get("tracker") == null || cmdArgs.get("action") == null) return;
+
+ AbstractTracker tracker = (AbstractTracker) cmdArgs.get("tracker");
+ TrackingActions action = (TrackingActions) cmdArgs.get("action");
+
+ switch (tracker.requireTarget(action)) { // @formatter:off
+ case ACTIVE: targetSelectorList(player, tracker.activeTargets(player, ""), args); break;
+ case AVAILABLE: targetSelectorList(player, tracker.availableTargets(player, ""), args); break;
+ case NEW: targetSelectorNew(player, args, false); break;
+ case NEWCOORDS: targetSelectorNew(player, args, true); break;
+ default:
+ } // @formatter:on
+ }
+
+ private void targetSelectorFallback(Player player, List pages, String args[]) {
+ int pager = 0;
+ // @formatter:off
+ if (args.length > 3 && args[2].equals(SELECT_PAGER))
+ try { pager = Integer.parseInt(args[3]); } catch (Exception e) {}
+ // @formatter:on
+ player.spigot().sendMessage(pages.get(pager));
+ if (pages.size() == 1) return;
+
+ Map> commands = new LinkedHashMap>();
+
+ String command = "/" + mainCommand + " " + SELECT_TARGET + " " + args[0] + " " + args[1] + " " + SELECT_PAGER;
+
+ if (pager > 0) commands.put("{prev}", ImmutableMap.of(
+ "text", sc.prepareMessage("commands." + mainCommand + ".targets.prev.title"),
+ "hover", sc.prepareMessage("commands." + mainCommand + ".targets.prev.hover"),
+ "click", command + " " + (pager - 1)));
+
+ if (pager < pages.size() - 1) commands.put("{next}", ImmutableMap.of(
+ "text", sc.prepareMessage("commands." + mainCommand + ".targets.next.title"),
+ "hover", sc.prepareMessage("commands." + mainCommand + ".targets.next.hover"),
+ "click", command + " " + (pager + 1)));
+
+ if (!commands.isEmpty()) player.spigot().sendMessage(
+ sc.createClickableMessage(" " + String.join(" ", commands.keySet()) + "\n", commands));
+ }
+
+ private void targetSelectorList(Player player, List targets, String[] args) {
+ ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
+ BookMeta meta = bookMeta(book, "targets");
+ String head = sc.prepareMessage("commands." + mainCommand + ".targets.header") + "\n\n";
+ String command = "/" + mainCommand + " " + subCommands.get(SubCmds.TRACK) + " " + String.join(" ", args);
+
+ List pages = targetSelectorPages(targets, new TextComponent(head), command);
+ for (BaseComponent[] page : pages) meta.spigot().addPage(page);
+ book.setItemMeta(meta);
+
+ if (!NMSUtil.openBook(player, book)) targetSelectorFallback(player, pages, args);
+ }
+
+ private List targetSelectorPages(List targets, BaseComponent head, String command) {
+ List pages = new ArrayList();
+
+ for (String[] part : chunk(targets.toArray(new String[targets.size()]),
+ sc.locale.getInt("commands." + mainCommand + ".targets.per_page"))) {
+ BaseComponent[] page = new BaseComponent[part.length + 1];
+ page[0] = head;
+
+ for (int i = 0; i < part.length; i++) {
+ Map> commands = new LinkedHashMap>();
+
+ commands.put("{cmd}", ImmutableMap.of(
+ "text", sc.prepareMessage("commands." + mainCommand + ".targets.content",
+ ImmutableMap.of("target", part[i])),
+ "hover", sc.prepareMessage("commands." + mainCommand + ".targets.hover",
+ ImmutableMap.of("target", part[i])),
+ "click", command + " " + part[i]));
+
+ page[i + 1] = sc.createClickableMessage("{cmd}" + "\n", commands);
+ }
+
+ pages.add(page);
+ }
+
+ if (pages.isEmpty()) pages.add(new BaseComponent[] { head,
+ new TextComponent(sc.prepareMessage("commands." + mainCommand + ".targets.no_targets")) });
+
+ return pages;
+ }
+
+ private void targetSelectorNew(Player player, String[] args, boolean coords) {
+ String cancel = sc.locale.getString("commands." + mainCommand + ".targets.new.cancel");
+
+ Conversation conv = new Conversation(sc, player, new StringPrompt() {
+ @Override
+ public String getPromptText(ConversationContext paramConversationContext) {
+ return sc.prepareMessage(
+ "commands." + mainCommand + ".targets.new.name_" + (coords ? "coords" : "only"),
+ ImmutableMap.of("word", cancel));
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext paramConversationContext, String paramString) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (this.isCancelled()) return;
+ if (paramString.equals(cancel))
+ player.sendMessage(sc.prepareMessage("commands." + mainCommand + ".targets.new.cancelled"));
+ else player.performCommand(mainCommand + " " + subCommands.get(SubCmds.TRACK)
+ + " " + String.join(" ", args) + " " + paramString);
+ this.cancel();
+ }
+ }.runTaskLater(sc, sc.config.getInt("delays.target_cancel"));
+
+ return END_OF_CONVERSATION;
+ }
+ });
+
+ conv.setLocalEchoEnabled(false);
+ player.beginConversation(conv);
+ }
+
+ // ----------------------------------------------------------------------------------------------
+ // Utils methods
+ // ----------------------------------------------------------------------------------------------
+
+ private BookMeta bookMeta(ItemStack book, String type) {
+ BookMeta meta = (BookMeta) book.getItemMeta();
+ meta.setTitle(sc.prepareMessage("commands." + mainCommand + ".books." + type + ".title"));
+ meta.setAuthor(sc.prepareMessage("commands." + mainCommand + ".books." + type + ".author"));
+
+ ArrayList lore = new ArrayList();
+ for (String row : sc.locale.getStringList("commands." + mainCommand + ".books." + type + ".lore"))
+ lore.add(sc.formatMessage(row));
+
+ meta.setLore(lore);
+ return meta;
+ }
+
+ private List chunk(String[] input, int number) {
+ List parts = new ArrayList();
+
+ int left = input.length;
+ while (left > 0) {
+ int size = Math.min(left, number);
+ String[] part = new String[size];
+
+ System.arraycopy(input, input.length - left, part, 0, size);
+ parts.add(part);
+
+ left -= size;
+ }
+
+ return parts;
+ }
+
+ private String[] subArgs(String[] args) {
+ return Arrays.stream(args).skip(1).toArray(String[]::new);
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/commands/OptionCommand.java b/src/main/java/me/arboriginal/SimpleCompass/commands/OptionCommand.java
new file mode 100644
index 0000000..093c649
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/commands/OptionCommand.java
@@ -0,0 +1,81 @@
+package me.arboriginal.SimpleCompass.commands;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import com.google.common.collect.ImmutableMap;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class OptionCommand extends AbstractCommand implements CommandExecutor, TabCompleter {
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public OptionCommand(SimpleCompass plugin) {
+ super(plugin, "scoption");
+ }
+
+ // CommandExecutor methods -----------------------------------------------------------------------------------------
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (!(sender instanceof Player)) {
+ sc.sendMessage(sender, "command_only_for_players");
+ return true;
+ }
+ else if (((Player) sender).isSleeping()) {
+ sc.sendMessage(sender, "command_no_sleeping");
+ return true;
+ }
+
+ return performCommandOption((Player) sender, args);
+ }
+
+ // TabCompleter methods --------------------------------------------------------------------------------------------
+
+ @Override
+ public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
+ if (sender instanceof Player) return completeCommandOption((Player) sender, args);
+ return null;
+ }
+
+ // SimpleCompassOptions methods ------------------------------------------------------------------------------------
+
+ @Override
+ public void showOptions(Player player, CompassTypes modified) {
+ sc.sendMessage(player, "commands." + mainCommand + ".header");
+
+ for (CompassTypes type : allowedTypes(player)) {
+ CompassModes typeMode = sc.datas.compassModeGet(player, type);
+ CompassOptions selected = sc.datas.compassOptionGet(player, type);
+ Map> commands = new LinkedHashMap>();
+
+ for (CompassModes mode : CompassModes.values())
+ commands.put("{" + mode + "}", clickableOption(type, mode, typeMode));
+
+ player.spigot().sendMessage(
+ sc.createClickableMessage(sc.prepareMessage("commands." + mainCommand + ".content",
+ ImmutableMap.of("type", sc.locale.getString("types." + type))), commands));
+
+ commands = new LinkedHashMap>();
+
+ for (String option : allowedOptions(player)) {
+ if (option.equals(CompassModes.MODE180.toString()) || option.equals(CompassModes.MODE360.toString()))
+ continue;
+
+ commands.put("{" + option + "}", clickableOption(type, option, selected));
+ }
+
+ player.spigot().sendMessage(sc.createClickableMessage(String.join("", commands.keySet()), commands));
+ }
+
+ sc.sendMessage(player, "commands." + mainCommand + ".footer");
+
+ if (modified != null) sc.sendMessage(player, "commands." + mainCommand + ".saved");
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/commands/TrackCommand.java b/src/main/java/me/arboriginal/SimpleCompass/commands/TrackCommand.java
new file mode 100644
index 0000000..e0ec20a
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/commands/TrackCommand.java
@@ -0,0 +1,48 @@
+package me.arboriginal.SimpleCompass.commands;
+
+import java.util.List;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class TrackCommand extends AbstractCommand implements CommandExecutor, TabCompleter {
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public TrackCommand(SimpleCompass plugin) {
+ super(plugin, "sctrack");
+ }
+
+ // CommandExecutor methods -----------------------------------------------------------------------------------------
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (!(sender instanceof Player)) {
+ sc.sendMessage(sender, "command_only_for_players");
+ return true;
+ }
+
+ if (((Player) sender).isSleeping()) {
+ sc.sendMessage(sender, "command_no_sleeping");
+ return true;
+ }
+
+ return performCommandTrack((Player) sender, args);
+ }
+
+ // TabCompleter methods --------------------------------------------------------------------------------------------
+
+ @Override
+ public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
+ if (sender instanceof Player) return completeCommandTrack((Player) sender, args);
+ return null;
+ }
+
+ // CommandUtil methods ---------------------------------------------------------------------------------------------
+
+ @Override
+ public void showOptions(Player player, CompassTypes modified) {}
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/compasses/AbstractCompass.java b/src/main/java/me/arboriginal/SimpleCompass/compasses/AbstractCompass.java
new file mode 100644
index 0000000..e6451c5
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/compasses/AbstractCompass.java
@@ -0,0 +1,186 @@
+package me.arboriginal.SimpleCompass.compasses;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.util.Vector;
+import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public abstract class AbstractCompass {
+ protected SimpleCompass sc;
+ protected CompassTypes type;
+ protected BukkitRunnable task = null;
+ protected String warning = "";
+
+ public enum CompassTypes {
+ ACTIONBAR, BOSSBAR,
+ }
+
+ public enum CompassModes {
+ MODE180, MODE360,
+ }
+
+ public Player owner;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public AbstractCompass(SimpleCompass plugin, Player player, CompassTypes compassTypes) {
+ sc = plugin;
+ type = compassTypes;
+ owner = player;
+
+ init();
+ refresh();
+ }
+
+ // Abstract methods ------------------------------------------------------------------------------------------------
+
+ public abstract void display(String datas);
+
+ // Default methods -------------------------------------------------------------------------------------------------
+
+ public void delete() {
+ sc.tasks.clear(TasksTypes.REFRESH_STATUS, owner);
+
+ if (task != null) task.cancel();
+ if (sc.compasses.hasRequiredItems(owner, type, false)) return;
+
+ String message = sc.prepareMessage("warnPlayerNoMoreFuel");
+ if (message.isEmpty()) return;
+
+ warning = message;
+ display(warning);
+ }
+
+ public void init() {}
+
+ public void refresh() {
+ display(buildCompassDatas());
+ }
+
+ // Private methods -------------------------------------------------------------------------------------------------
+
+ private String buildCompassDatas() {
+ double rotation = (owner.getEyeLocation().getYaw() - 180) % 360;
+ if (rotation < 0) rotation += 360;
+
+ CompassModes mode = sc.datas.compassModeGet(owner, type);
+
+ String prefix = "compass." + type + "." + mode;
+ String sep = sc.config.getString(prefix + ".separator_value");
+ String cSep = sc.config.getString(prefix + ".separator_color");
+ String cOn = sc.config.getString(prefix + ".active_color");
+ String cOff = sc.config.getString(prefix + ".inactive_color");
+ String nOn = sc.config.getString(prefix + ".active_north_color");
+ String nOff = sc.config.getString(prefix + ".north_color");
+ String west = sc.config.getString(prefix + ".cardinals.west");
+ String north = sc.config.getString(prefix + ".cardinals.north");
+ String east = sc.config.getString(prefix + ".cardinals.east");
+ String south = sc.config.getString(prefix + ".cardinals.south");
+ String datas = sep + "♤" + sep + "♡" + sep + "♢" + sep + "♧";
+ int start = (int) Math.round(rotation * datas.length() / 360);
+ char face = getFacing();
+
+ datas = datas.substring(start) + datas.substring(0, start);
+
+ if (mode == CompassModes.MODE180) {
+ int strip = (int) Math.ceil(datas.length() / 4);
+ datas = datas.substring(strip - 1, datas.length() - strip + 1);
+ }
+
+ return sc.config.getString(prefix + ".before") + cSep + injectActivatedTrackers(datas, cSep) // @formatter:off
+ .replace("♤", ((face == 'W') ? cOn : cOff) + west + cSep)
+ .replace("♡", ((face == 'N') ? nOn : nOff) + north + cSep)
+ .replace("♢", ((face == 'E') ? cOn : cOff) + east + cSep)
+ .replace("♧", ((face == 'S') ? cOn : cOff) + south + cSep)
+ + sc.config.getString(prefix + ".after"); // @formatter:on
+ }
+
+ private char getFacing() {
+ try {
+ if (owner.getClass().getMethod("myMethodToFind", (Class>[]) null) != null)
+ return owner.getFacing().toString().charAt(0);
+ }
+ catch (Exception e) {}
+ // Use this as a fallback for Spigot version (like 1.12) which doesn't support player.getFacing()
+ return Arrays.asList('S', 'W', 'N', 'E').get(Math.round(owner.getLocation().getYaw() / 90f) & 0x3);
+ }
+
+ private HashMap>> getActiveTargets() {
+ String cacheKey = "trackers." + type;
+ // @formatter:off
+ @SuppressWarnings("unchecked")
+ HashMap>> trackers
+ = (HashMap>>) sc.cache.get(owner.getUniqueId(), cacheKey);
+ // @formatter:on
+ if (trackers == null) {
+ trackers = sc.targets.getTargetsCoords(owner);
+ sc.cache.set(owner.getUniqueId(), cacheKey, trackers, sc.config.getInt("delays.trackers_list"));
+ }
+
+ return trackers;
+ }
+
+ private String injectActivatedTrackers(String compass, String sepColor) {
+ HashMap>> targets = getActiveTargets();
+ if (targets.isEmpty()) return compass;
+
+ Location refPos = owner.getEyeLocation();
+
+ HashMap placeholders = new HashMap();
+
+ for (String trackerID : sc.targets.trackersPriority) {
+ AbstractTracker tracker = sc.trackers.get(trackerID);
+ if (tracker == null) continue;
+
+ int hlMaxAngle = tracker.settings.getInt("settings.hl_angle", 0);
+ String hlMarker = null, hlSymbol = null;
+
+ if (hlMaxAngle > 0) {
+ hlMarker = tracker.settings.getString("settings.hl_temp", null);
+ hlSymbol = tracker.settings.getString("settings.hl_symbol", null);
+ if (hlMarker != null && hlSymbol != null) placeholders.put(hlMarker, hlSymbol + sepColor);
+ }
+
+ for (String targetType : targets.keySet()) {
+ ArrayList coords = targets.get(targetType).get(trackerID);
+ if (coords == null || coords.isEmpty()) continue;
+
+ boolean active = targetType.equals("on");
+ String marker = tracker.settings.getString("settings." + (active ? "" : "inactive_") + "temp");
+ String symbol = tracker.settings.getString("settings." + (active ? "" : "inactive_") + "symbol");
+ placeholders.put(marker, symbol + sepColor);
+
+ for (double[] target : coords) {
+ Vector blockDirection = new Location(owner.getWorld(), target[0], refPos.getY(), target[1])
+ .subtract(refPos).toVector().normalize();
+
+ Vector lookAt = refPos.getDirection().setY(0);
+ boolean viewable = (lookAt.dot(blockDirection) > 0);
+ double angle = Math.toDegrees(blockDirection.angle(lookAt.crossProduct(new Vector(0, 1, 0))));
+ String tMarker = marker;
+
+ if (!viewable) angle = (angle > 90) ? 180 : 0;
+ else if (active && hlMarker != null && hlSymbol != null && angle > 90 - hlMaxAngle
+ && angle < 90 + hlMaxAngle)
+ tMarker = hlMarker;
+
+ int start = compass.length() - (int) Math.round(2 * angle * compass.length() / 360);
+
+ compass = (start < 2) ? tMarker + compass.substring(start + 1)
+ : compass.substring(0, start - 1) + tMarker + compass.substring(start);
+ }
+ }
+ }
+
+ for (String placeholder : placeholders.keySet())
+ compass = compass.replaceAll(placeholder, placeholders.get(placeholder));
+
+ return compass;
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/compasses/ActionbarCompass.java b/src/main/java/me/arboriginal/SimpleCompass/compasses/ActionbarCompass.java
new file mode 100644
index 0000000..fae055a
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/compasses/ActionbarCompass.java
@@ -0,0 +1,40 @@
+package me.arboriginal.SimpleCompass.compasses;
+
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+import net.md_5.bungee.api.ChatMessageType;
+import net.md_5.bungee.api.chat.TextComponent;
+
+public class ActionbarCompass extends AbstractCompass {
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public ActionbarCompass(SimpleCompass plugin, Player player) {
+ super(plugin, player, CompassTypes.ACTIONBAR);
+ }
+
+ // SimpleCompass methods -------------------------------------------------------------------------------------------
+
+ @Override
+ public void display(String datas) {
+ owner.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(datas));
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+
+ if (sc.config.getBoolean("compass.ACTIONBAR.maintain_when_not_moving") && task == null) {
+ task = new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (isCancelled()) return;
+ refresh();
+ }
+ };
+
+ Long delay = sc.config.getLong("compass.ACTIONBAR.maintain_delay");
+ task.runTaskTimer(sc, delay, delay);
+ }
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/compasses/BossbarCompass.java b/src/main/java/me/arboriginal/SimpleCompass/compasses/BossbarCompass.java
new file mode 100644
index 0000000..97b141a
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/compasses/BossbarCompass.java
@@ -0,0 +1,129 @@
+package me.arboriginal.SimpleCompass.compasses;
+
+import java.util.Map;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarStyle;
+import org.bukkit.boss.BossBar;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.scheduler.BukkitRunnable;
+import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class BossbarCompass extends AbstractCompass {
+ public BossBar bossbar;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public BossbarCompass(SimpleCompass plugin, Player player) {
+ super(plugin, player, CompassTypes.BOSSBAR);
+ }
+
+ // SimpleCompass methods -------------------------------------------------------------------------------------------
+
+ @Override
+ public void delete() {
+ super.delete();
+ sc.tasks.set(TasksTypes.REMOVEWARNING, owner, this);
+ }
+
+ @Override
+ public void display(String datas) {
+ bossbar.setTitle(datas);
+ bossbar.setProgress(getProgress());
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+
+ if (sc.config.getBoolean("compass.BOSSBAR.disappear_when_not_moving")) {
+ if (task != null) task.cancel();
+
+ bossbar.setVisible(true);
+
+ task = new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (isCancelled()) return;
+ bossbar.setVisible(false);
+ this.cancel();
+ }
+ };
+
+ task.runTaskLaterAsynchronously(sc, sc.config.getInt("compass.BOSSBAR.disappear_delay"));
+ }
+ }
+
+ @Override
+ public void init() {
+ super.init();
+
+ bossbar = Bukkit.createBossBar("",
+ BarColor.valueOf(sc.config.getString("compass.BOSSBAR.attributes.color")),
+ BarStyle.valueOf(sc.config.getString("compass.BOSSBAR.attributes.style")));
+
+ bossbar.addPlayer(owner);
+ bossbar.setProgress(getProgress());
+ bossbar.setVisible(true);
+ }
+
+ // Specific methods ------------------------------------------------------------------------------------------------
+
+ private void alterColor(double durability) {
+ Object levels = sc.config.get("compass.BOSSBAR.attributes.elytra_durability.levels");
+ if (levels == null || !(levels instanceof Map)) return;
+
+ durability *= 100;
+
+ for (Object value : ((Map, ?>) levels).keySet()) if (durability < (int) value) {
+ bossbar.setColor(BarColor.valueOf((String) ((Map, ?>) levels).get(value)));
+ return;
+ }
+
+ bossbar.setColor(BarColor.valueOf(sc.config.getString("compass.BOSSBAR.attributes.color")));
+ }
+
+ @SuppressWarnings("deprecation")
+ private int elytraDurabilityOldMethod(ItemStack chestplate) {
+ try {
+ if (chestplate.getClass().getMethod("getDurability", (Class>[]) null) != null)
+ return (int) chestplate.getDurability();
+ }
+ catch (Exception e) {}
+
+ return -1;
+ }
+
+ private double getProgress() {
+ String cacheKey = "compass." + type;
+ Double progress = (Double) sc.cache.get(owner.getUniqueId(), cacheKey);
+ if (progress != null) return progress;
+
+ if (sc.config.getBoolean("compass.BOSSBAR.attributes.elytra_durability.wearing") // @formatter:off
+ || (sc.config.getBoolean("compass.BOSSBAR.attributes.elytra_durability.gliding") && owner.isGliding())) {
+ ItemStack chestplate = owner.getInventory().getChestplate(); // @formatter:on
+
+ if (chestplate != null && chestplate.getType() == Material.ELYTRA
+ && !chestplate.getItemMeta().isUnbreakable()) {
+ Map metas = chestplate.getItemMeta().serialize();
+
+ int damages = (metas.containsKey("Damage") && metas.get("Damage") instanceof Integer)
+ ? (int) metas.get("Damage")
+ : elytraDurabilityOldMethod(chestplate);
+
+ if (damages > -1) {
+ progress = Math.max(0, 1 - damages / ((double) chestplate.getType().getMaxDurability()));
+ alterColor(progress);
+ }
+ }
+ }
+
+ if (progress == null) progress = sc.config.getDouble("compass.BOSSBAR.attributes.progress");
+
+ sc.cache.set(owner.getUniqueId(), cacheKey, progress, sc.config.getInt("delays.elytra_durability") * 1000);
+ return progress;
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/managers/CompassManager.java b/src/main/java/me/arboriginal/SimpleCompass/managers/CompassManager.java
new file mode 100644
index 0000000..570a315
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/managers/CompassManager.java
@@ -0,0 +1,245 @@
+package me.arboriginal.SimpleCompass.managers;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+import org.bukkit.configuration.MemorySection;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.scheduler.BukkitRunnable;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.compasses.ActionbarCompass;
+import me.arboriginal.SimpleCompass.compasses.BossbarCompass;
+import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class CompassManager {
+ private HashMap> compasses;
+
+ public enum RequirementsSections {
+ HOTBAR, INVENTORY, MAIN_HAND, OFF_HAND,
+ }
+
+ private SimpleCompass sc;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public CompassManager(SimpleCompass plugin) {
+ sc = plugin;
+ compasses = new HashMap>();
+ for (CompassTypes type : CompassTypes.values()) compasses.put(type, new HashMap());
+ }
+
+ // Misc methods ----------------------------------------------------------------------------------------------------
+
+ public void commandTrigger(String command) {
+ if (sc.config.getList("commands_trigger_refresh").contains(command.split(" ")[0]))
+ for (Player player : sc.getServer().getOnlinePlayers()) sc.tasks.set(TasksTypes.REFRESH_STATUS, player);
+ }
+
+ public void unload() {
+ removeCompass();
+ }
+
+ // Compass methods -------------------------------------------------------------------------------------------------
+
+ public void createCompass(CompassTypes type, Player player) {
+ UUID uid = player.getUniqueId();
+ AbstractCompass compass = getCompass(type, uid);
+ if (compass != null) return;
+
+ switch (type) { // @formatter:off
+ case ACTIONBAR: compass = new ActionbarCompass(sc, player); break;
+ case BOSSBAR: compass = new BossbarCompass(sc, player); break;
+ } // @formatter:on
+
+ if (compass != null) {
+ BukkitRunnable task = sc.tasks.get(TasksTypes.REMOVEWARNING, uid);
+ if (task != null) task.run();
+ compasses.get(type).put(uid, compass);
+ }
+ }
+
+ public AbstractCompass getCompass(CompassTypes type, UUID uid) {
+ return compasses.get(type).get(uid);
+ }
+
+ public void refreshCompass(Player player, CompassTypes type) {
+ refreshCompassState(player, type);
+ refreshCompassDatas(player, type);
+ }
+
+ public void removeCompass() {
+ for (CompassTypes type : CompassTypes.values()) removeCompass(type);
+ }
+
+ public void removeCompass(CompassTypes type) {
+ Iterator it = compasses.get(type).keySet().iterator();
+ while (it.hasNext()) {
+ AbstractCompass compass = getCompass(type, it.next());
+ if (compass != null) compass.delete();
+ it.remove();
+ }
+ }
+
+ public void removeCompass(Player player) {
+ for (CompassTypes type : CompassTypes.values()) removeCompass(type, player);
+ }
+
+ public void removeCompass(CompassTypes type, Player player) {
+ removeCompass(type, player.getUniqueId());
+ }
+
+ public void removeCompass(CompassTypes type, UUID uid) {
+ AbstractCompass compass = getCompass(type, uid);
+ if (compass == null) return;
+ compass.delete();
+ compasses.get(type).remove(uid);
+ }
+
+ // Compass state methods -------------------------------------------------------------------------------------------
+
+ public boolean getCompassState(Player player, CompassTypes type) {
+ if (!player.hasPermission("scompass.use") || !player.hasPermission("scompass.use." + type)) return false;
+
+ boolean active = false;
+
+ switch (sc.datas.compassOptionGet(player, type)) { // @formatter:off
+ case ALWAYS: active = true; break;
+ case VEHICLE: active = player.isInsideVehicle(); break;
+ case ELYTRA: active = player.isGliding(); break;
+ case ELYTRA_VEHICLE: active = (player.isInsideVehicle() || player.isGliding()); break;
+ default: active = false;
+ } // @formatter:on
+
+ return active && hasRequiredItems(player, type, true);
+ }
+
+ public void refreshCompassState() {
+ for (Player player : sc.getServer().getOnlinePlayers()) refreshCompassState(player);
+ }
+
+ public void refreshCompassState(CompassTypes type) {
+ for (Player player : sc.getServer().getOnlinePlayers()) refreshCompassState(player, type);
+ }
+
+ public void refreshCompassState(Player player) {
+ for (CompassTypes type : CompassTypes.values()) refreshCompassState(player, type);
+ }
+
+ public void refreshCompassState(Player player, CompassTypes type) {
+ if (getCompassState(player, type)) createCompass(type, player);
+ else removeCompass(type, player);
+ }
+
+ // Compass item requirements methods -------------------------------------------------------------------------------
+
+ ItemStack consumeItem(Player player, CompassTypes type, ItemStack stack) {
+ stack.setAmount(stack.getAmount() - 1);
+ sc.datas.cooldownConsumeSet(player, type);
+ return stack;
+ }
+
+ public boolean hasRequiredItems(Player player, CompassTypes type, boolean consume) {
+ if (((MemorySection) sc.config.get("compass." + type + ".require.items")).getKeys(false).isEmpty()) return true;
+
+ List lores = sc.config.getStringList("ignored_lores");
+ PlayerInventory inv = player.getInventory();
+ ItemStack stack;
+
+ consume &= shouldConsume(player, type);
+
+ for (RequirementsSections section : RequirementsSections.values()) {
+ List items = sc.config.getStringList("compass." + type + ".require.items." + section);
+ if (items.isEmpty()) continue;
+
+ switch (section) {
+ case OFF_HAND:
+ stack = inv.getItemInOffHand();
+
+ if (stack != null && isValidItem(stack, items, lores)) {
+ if (consume) inv.setItemInOffHand(consumeItem(player, type, stack));
+ return true;
+ }
+ break;
+
+ case MAIN_HAND:
+ stack = inv.getItemInMainHand();
+
+ if (stack != null && isValidItem(stack, items, lores)) {
+ if (consume) inv.setItemInMainHand(consumeItem(player, type, stack));
+ return true;
+ }
+ break;
+
+ case HOTBAR:
+ case INVENTORY: // @formatter:off
+ for (int i = (section == RequirementsSections.HOTBAR ? 0 : 9);
+ i <= (section == RequirementsSections.HOTBAR ? 8 : 35); i++) { // @formatter:on
+ stack = inv.getItem(i);
+ if (stack == null) continue;
+
+ if (isValidItem(stack, items, lores)) {
+ if (consume) inv.setItem(i, consumeItem(player, type, stack));
+ return true;
+ }
+ }
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isValidItem(ItemStack stack, List requiredItems, List ignoredLores) {
+ if (!requiredItems.contains(stack.getType().toString())) return false;
+ if (ignoredLores.isEmpty()) return true;
+
+ List itemLores = stack.getItemMeta().getLore();
+ if (itemLores == null) return true;
+
+ for (Object lore : stack.getItemMeta().getLore()) if (ignoredLores.contains(lore)) return false;
+ return true;
+ }
+
+ public boolean shouldConsume(Player player, CompassTypes type) {
+ return sc.config.getBoolean("compass." + type + ".require.consume")
+ && !player.hasPermission("scompass.use.free")
+ && sc.datas.cooldownConsumeGet(player, type) < 1;
+ }
+
+ // Compass data methods --------------------------------------------------------------------------------------------
+
+ public void refreshCompassDatas() {
+ for (CompassTypes type : CompassTypes.values()) refreshCompassDatas(type);
+ }
+
+ public void refreshCompassDatas(CompassTypes type) {
+ for (UUID uid : compasses.get(type).keySet()) refreshCompassDatas(type, uid);
+ }
+
+ public void refreshCompassDatas(CompassTypes type, UUID uid) {
+ AbstractCompass compass = getCompass(type, uid);
+ if (compass == null) return;
+
+ if (!((MemorySection) sc.config.get("compass." + type + ".require.items")).getKeys(false).isEmpty()
+ && shouldConsume(compass.owner, type) && !hasRequiredItems(compass.owner, type, true))
+ removeCompass(type, compass.owner);
+ else compass.refresh();
+ }
+
+ public void refreshCompassDatas(Player player) {
+ refreshCompassDatas(player.getUniqueId());
+ }
+
+ public void refreshCompassDatas(Player player, CompassTypes type) {
+ refreshCompassDatas(type, player.getUniqueId());
+ }
+
+ public void refreshCompassDatas(UUID uid) {
+ for (CompassTypes type : CompassTypes.values()) refreshCompassDatas(type, uid);
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/managers/DataManager.java b/src/main/java/me/arboriginal/SimpleCompass/managers/DataManager.java
new file mode 100644
index 0000000..42926b7
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/managers/DataManager.java
@@ -0,0 +1,210 @@
+package me.arboriginal.SimpleCompass.managers;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import me.arboriginal.SimpleCompass.commands.AbstractCommand.CompassOptions;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+import me.arboriginal.SimpleCompass.utils.CacheUtil;
+
+public class DataManager {
+ private SimpleCompass sc;
+ private File file;
+
+ public YamlConfiguration users;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public DataManager(SimpleCompass plugin) {
+ sc = plugin;
+ users = new YamlConfiguration();
+ file = new File(sc.getDataFolder(), "usersDatas.yml");
+
+ if (file.exists()) users = YamlConfiguration.loadConfiguration(file);
+ else saveUserDatas();
+ }
+
+ // General methods -------------------------------------------------------------------------------------------------
+
+ public String getKey(Player player, String key) {
+ return player.getUniqueId() + "." + key;
+ }
+
+ public boolean saveUserDatas() {
+ try {
+ if (!file.exists()) file.createNewFile();
+ users.save(file);
+ return true;
+ }
+ catch (IOException e) {
+ sc.getLogger().severe(sc.prepareMessage("file_not_writable"));
+ }
+
+ return false;
+ }
+
+ // Cooldown methods ------------------------------------------------------------------------------------------------
+
+ public Long cooldownGet(Player player, String cooldown) {
+ String dataKey = cooldownKey(player, cooldown);
+
+ if (users.contains(dataKey)) {
+ Long left = users.getLong(dataKey) - CacheUtil.now();
+ if (left > 0) return left;
+ }
+
+ return 0L;
+ }
+
+ public String cooldownKey(Player player, String cooldown) {
+ return getKey(player, "cooldowns." + cooldown);
+ }
+
+ public void cooldownSet(Player player, String cooldown, int delay) {
+ users.set(cooldownKey(player, cooldown), CacheUtil.now() + delay * 1000);
+ saveUserDatas();
+ }
+
+ // Book cooldown
+
+ public String cooldownBook() {
+ return "interface_book";
+ }
+
+ public Long cooldownBookGet(Player player) {
+ return cooldownGet(player, cooldownBook());
+ }
+
+ public void cooldownBookSet(Player player) {
+ cooldownSet(player, cooldownBook(), sc.config.getInt("interface.give_book_cooldown"));
+ }
+
+ // Consume cooldown
+
+ public String cooldownConsume(CompassTypes type) {
+ return "consume_" + type;
+ }
+
+ public Long cooldownConsumeGet(Player player, CompassTypes type) {
+ return cooldownGet(player, cooldownConsume(type));
+ }
+
+ public void cooldownConsumeSet(Player player, CompassTypes type) {
+ cooldownSet(player, cooldownConsume(type), sc.config.getInt("compass." + type + ".require.duration"));
+ }
+
+ // Compass options methods -----------------------------------------------------------------------------------------
+
+ public CompassOptions compassOptionGet(Player player, CompassTypes type) {
+ String key = compassOptionKey(player, type);
+ return CompassOptions.valueOf(users.contains(key) ? users.getString(key)
+ : sc.config.getString("compass." + type + ".default.option"));
+ }
+
+ public String compassOptionKey(Player player, CompassTypes type) {
+ return getKey(player, type + ".option");
+ }
+
+ public void compassOptionSet(Player player, CompassTypes type, CompassOptions option) {
+ if (sc.config.getBoolean("single_compass_mode") && option != CompassOptions.DISABLED)
+ for (CompassTypes otherType : CompassTypes.values()) if (type != otherType) {
+ users.set(compassOptionKey(player, otherType), CompassOptions.DISABLED.toString());
+ sc.compasses.removeCompass(otherType, player);
+ }
+
+ users.set(compassOptionKey(player, type), option.toString());
+ saveUserDatas();
+
+ sc.tasks.set(TasksTypes.valueOf("REFRESH_" + type), player);
+ }
+
+ public void compassOptionToggle(Player player) {
+ String key = getKey(player, "toggle");
+
+ ConfigurationSection previous = users.getConfigurationSection(key);
+ for (CompassTypes type : CompassTypes.values()) {
+ CompassOptions option = compassOptionGet(player, type);
+
+ if (previous == null) {
+ users.set(key + "." + type, option.toString());
+ users.set(compassOptionKey(player, type), CompassOptions.DISABLED.toString());
+ }
+ else users.set(compassOptionKey(player, type), previous.get(type.toString()));
+ }
+
+ if (previous == null) sc.sendMessage(player, "toggle_state_saved");
+ else {
+ users.set(key, null);
+ sc.sendMessage(player, "toggle_state_restored");
+ }
+
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, player);
+ saveUserDatas();
+ }
+
+ // Compass modes methods -------------------------------------------------------------------------------------------
+
+ public CompassModes compassModeGet(Player player, CompassTypes type) {
+ String key = compassModeKey(player, type);
+ return CompassModes.valueOf(users.contains(key) ? users.getString(key)
+ : sc.config.getString("compass." + type + ".default.mode"));
+ }
+
+ public String compassModeKey(Player player, CompassTypes type) {
+ return getKey(player, type + ".mode");
+ }
+
+ public void compassModeSet(Player player, CompassTypes type, CompassModes mode) {
+ users.set(compassModeKey(player, type), mode.toString());
+ saveUserDatas();
+
+ sc.tasks.set(TasksTypes.valueOf("REFRESH_" + type), player);
+ }
+
+ // Tracker targets methods -----------------------------------------------------------------------------------------
+
+ public boolean activeTargetAdd(Player player, String type, String name) {
+ List list = activeTargetsList(player, type);
+ if (list.contains(name)) return false;
+
+ list.add(name);
+ activeTargetsSave(player, type, list);
+ return true;
+ }
+
+ public boolean activeTargetDel(Player player, String type, String name) {
+ List list = activeTargetsList(player, type);
+ if (!list.contains(name)) return false;
+
+ list.remove(name);
+ activeTargetsSave(player, type, list);
+ return true;
+ }
+
+ public String activeTargetsKey(Player player, String type) {
+ return getKey(player, "active_targets." + type);
+ }
+
+ public List activeTargetsList(Player player, String type) {
+ String key = activeTargetsKey(player, type);
+ List list = new ArrayList();
+
+ if (users.getList(key) != null) for (String target : users.getStringList(key)) list.add(target);
+
+ return list;
+ }
+
+ public void activeTargetsSave(Player player, String type, List list) {
+ users.set(activeTargetsKey(player, type), list);
+ saveUserDatas();
+ sc.tasks.set(TasksTypes.REFRESH_ACTIONBAR, player);
+ sc.tasks.set(TasksTypes.REFRESH_BOSSBAR, player);
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/managers/TargetManager.java b/src/main/java/me/arboriginal/SimpleCompass/managers/TargetManager.java
new file mode 100644
index 0000000..244f7df
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/managers/TargetManager.java
@@ -0,0 +1,173 @@
+package me.arboriginal.SimpleCompass.managers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+import org.bukkit.entity.Player;
+import com.google.common.collect.ImmutableMap;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class TargetManager {
+ private SimpleCompass sc;
+ public String[] trackersPriority;
+
+ public HashMap>> activeTargets;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public TargetManager(SimpleCompass plugin) {
+ sc = plugin;
+ activeTargets = new HashMap>>();
+
+ for (String trackerID : sc.trackers.keySet()) activeTargets.put(trackerID, new HashMap>());
+
+ trackersPriority = new String[sc.trackers.size()];
+ if (sc.trackers.size() == 0) return;
+
+ List priority = sc.config.getStringList("trackers_priorities");
+ for (int i = 0; i < priority.size(); i++) trackersPriority[i] = priority.get(i);
+ }
+
+ // Trackers methods ------------------------------------------------------------------------------------------------
+
+ public boolean canUseTracker(Player player, String trackerID) {
+ return player.hasPermission("scompass.track.*") || player.hasPermission("scompass.track." + trackerID);
+ }
+
+ public List getAvailableTrackers(Player player) {
+ List list = new ArrayList();
+ for (String trackerID : sc.trackers.keySet()) if (canUseTracker(player, trackerID)) list.add(trackerID);
+ return list;
+ }
+
+ public AbstractTracker getTrackerByName(String name) {
+ for (String trackerID : sc.trackers.keySet()) {
+ AbstractTracker tracker = sc.trackers.get(trackerID);
+ if (name.toLowerCase().equals(tracker.trackerName().toLowerCase())) return tracker;
+ }
+
+ return null;
+ }
+
+ public List getTrackersList(Player player, String startWith) {
+ List list = new ArrayList();
+
+ for (String trackerID : sc.targets.getAvailableTrackers(player)) {
+ String name = sc.trackers.get(trackerID).trackerName();
+ if (name.toLowerCase().startsWith(startWith.toLowerCase())) list.add(name);
+ }
+
+ return list;
+ }
+
+ // Targets methods -------------------------------------------------------------------------------------------------
+
+ public void activateTarget(Player player, String type, String name) {
+ UUID uid = player.getUniqueId();
+
+ if (!activeTargets.get(type).containsKey(uid)) activeTargets.get(type).put(uid, new ArrayList());
+ if (activeTargets.get(type).get(uid).contains(name)) return;
+
+ activeTargets.get(type).get(uid).add(name);
+ sc.datas.activeTargetAdd(player, type, name);
+ }
+
+ public void disableTarget(Player player, String type, String name) {
+ UUID uid = player.getUniqueId();
+
+ if (!activeTargets.get(type).containsKey(uid)) return;
+ if (!activeTargets.get(type).get(uid).contains(name)) return;
+
+ activeTargets.get(type).get(uid).remove(name);
+ sc.datas.activeTargetDel(player, type, name);
+ }
+
+ public HashMap>> getTargetsCoords(Player player) {
+ HashMap>> list // @formatter:off
+ = new HashMap>>();
+ // @formatter:on
+ list.put("on", new HashMap>());
+ list.put("off", new HashMap>());
+
+ HashMap> stop = new HashMap>();
+
+ UUID uid = player.getUniqueId();
+
+ List invalids = new ArrayList();
+
+ for (String trackerID : sc.trackers.keySet()) {
+ AbstractTracker tracker = sc.trackers.get(trackerID);
+ if (tracker == null) continue;
+
+ HashMap sublistOn = new HashMap();
+
+ if (activeTargets.get(trackerID).containsKey(uid)) {
+ ArrayList closest = new ArrayList();
+
+ Iterator it = activeTargets.get(trackerID).get(uid).iterator();
+ while (it.hasNext()) {
+ String name = it.next();
+ double[] coords = tracker.get(player, name);
+ if (coords == null) invalids.add(trackerID + ":" + name);
+ if (coords == null || tracker.playerIsClose(player, coords)) closest.add(name);
+ else sublistOn.put(name, coords);
+ }
+
+ if (!sublistOn.isEmpty()) list.get("on").put(trackerID, new ArrayList(sublistOn.values()));
+ if (!closest.isEmpty()) stop.put(tracker, closest);
+ }
+
+ if (tracker.settings.getBoolean("settings.inactive_target", false)) {
+ ArrayList sublistOff = new ArrayList();
+
+ for (String name : tracker.availableTargets(player, "")) {
+ if (sublistOn.containsKey(name)) continue;
+ double[] coords = tracker.get(player, name);
+ if (coords != null) sublistOff.add(coords);
+ }
+
+ if (!sublistOff.isEmpty()) list.get("off").put(trackerID, sublistOff);
+ }
+ }
+
+ if (!stop.isEmpty()) {
+ stop.forEach((tracker, stopped) -> {
+ stopped.forEach(name -> {
+ tracker.disable(player, name);
+ if (!invalids.contains(tracker.trackerID() + ":" + name)) tracker.sendMessage(player,
+ "target_auto_disabled", ImmutableMap.of("tracker", tracker.trackerName(), "target", name));
+ });
+ });
+ }
+
+ return list;
+ }
+
+ public boolean loadTargets() {
+ boolean hasLoadedTrackers = false;
+ for (Player player : sc.getServer().getOnlinePlayers()) if (loadTargets(player)) hasLoadedTrackers = true;
+ return hasLoadedTrackers;
+ }
+
+ public boolean loadTargets(Player player) {
+ boolean hasLoadedTrackers = false;
+
+ for (String trackerID : sc.trackers.keySet()) {
+ List targets = sc.datas.activeTargetsList(player, trackerID);
+ if (!targets.isEmpty()) hasLoadedTrackers = true;
+ for (String name : targets) activateTarget(player, trackerID, name);
+ }
+
+ return hasLoadedTrackers;
+ }
+
+ public void unloadTargets(Player player) {
+ UUID uid = player.getUniqueId();
+
+ for (String trackerID : sc.trackers.keySet())
+ if (activeTargets.get(trackerID).containsKey(uid)) activeTargets.get(trackerID).remove(uid);
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/managers/TaskManager.java b/src/main/java/me/arboriginal/SimpleCompass/managers/TaskManager.java
new file mode 100644
index 0000000..f982424
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/managers/TaskManager.java
@@ -0,0 +1,154 @@
+package me.arboriginal.SimpleCompass.managers;
+
+import java.util.HashMap;
+import java.util.UUID;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.compasses.BossbarCompass;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class TaskManager {
+ private SimpleCompass sc;
+
+ private HashMap> tasks;
+
+ public enum TasksTypes {
+ FIX_UUID, REFRESH_ACTIONBAR, REFRESH_BOSSBAR, REFRESH_STATUS, REMOVEWARNING,
+ }
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public TaskManager(SimpleCompass plugin) {
+ sc = plugin;
+ tasks = new HashMap>();
+ for (TasksTypes type : TasksTypes.values()) tasks.put(type, new HashMap());
+ }
+
+ // Tasks methods ---------------------------------------------------------------------------------------------------
+
+ public void clear() {
+ for (TasksTypes type : TasksTypes.values()) clear(type);
+ }
+
+ public void clear(Player player) {
+ clear(player.getUniqueId());
+ }
+
+ public void clear(UUID uid) {
+ for (TasksTypes type : TasksTypes.values()) clear(type, uid);
+ }
+
+ public void clear(TasksTypes type) {
+ for (UUID uid : tasks.get(type).keySet()) clear(type, uid);
+ }
+
+ public void clear(TasksTypes type, Player player) {
+ clear(type, player.getUniqueId());
+ }
+
+ public void clear(TasksTypes type, UUID uid) {
+ clear(type, uid, get(type, uid));
+ }
+
+ public void clear(TasksTypes type, UUID uid, BukkitRunnable task) {
+ if (task == null) task = get(type, uid);
+ if (task != null) {
+ task.cancel();
+ tasks.get(type).remove(uid);
+ }
+ }
+
+ public BukkitRunnable get(TasksTypes type, UUID uid) {
+ return tasks.get(type).get(uid);
+ }
+
+ public void set(TasksTypes type, Player player) {
+ set(type, player, null);
+ }
+
+ public void set(TasksTypes type, Player player, Object data) {
+ UUID uid = player.getUniqueId();
+
+ if (!sc.isReady) {
+ if (data != null && data instanceof BossbarCompass) ((BossbarCompass) data).bossbar.removeAll();
+ clear(type, uid);
+ return;
+ }
+
+ BukkitRunnable task = null;
+
+ switch (type) {
+ case FIX_UUID:
+ task = new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (isCancelled() || sc.compasses.getCompass(CompassTypes.BOSSBAR, uid) == null) return;
+ sc.compasses.removeCompass(CompassTypes.BOSSBAR, player);
+ sc.compasses.refreshCompassState(player);
+ clear(type, uid, this);
+
+ if (sc.trackers.isEmpty()) return;
+ sc.trackers.forEach((trackerID, tracker) -> {
+ for (String name : tracker.autoloadTargets(player, ""))
+ tracker.activate(player, name, false);
+ });
+ }
+ };
+
+ task.runTaskLaterAsynchronously(sc, sc.config.getInt("delays.fix_uuid"));
+ break;
+
+ case REFRESH_ACTIONBAR:
+ case REFRESH_BOSSBAR:
+ CompassTypes compassType = CompassTypes.valueOf(type.toString().substring(8));
+
+ task = new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (isCancelled()) return;
+ sc.compasses.refreshCompass(player, compassType);
+ clear(type, uid, this);
+ }
+ };
+
+ task.runTaskLaterAsynchronously(sc, sc.config.getInt("delays.option_take_effect"));
+ break;
+
+ case REFRESH_STATUS:
+ task = new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (isCancelled()) return;
+ sc.compasses.refreshCompassState(player);
+ clear(type, uid, this);
+ }
+ };
+
+ task.runTaskLaterAsynchronously(sc,
+ (data == null || !(data instanceof Integer)) ? sc.config.getInt("delays.refresh_status")
+ : (int) data);
+ break;
+
+ case REMOVEWARNING:
+ if (data == null || !(data instanceof BossbarCompass)) break;
+
+ task = new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (isCancelled()) return;
+ ((BossbarCompass) data).bossbar.removeAll();
+ this.cancel();
+ }
+ };
+
+ task.runTaskLaterAsynchronously(sc, sc.config.getInt("compass.BOSSBAR.warnPlayerNoMoreFuel") * 20);
+ break;
+ }
+
+ if (task != null) {
+ clear(type, uid);
+ tasks.get(type).put(uid, task);
+ }
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/plugin/AbstractTracker.java b/src/main/java/me/arboriginal/SimpleCompass/plugin/AbstractTracker.java
new file mode 100644
index 0000000..c3ae53b
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/plugin/AbstractTracker.java
@@ -0,0 +1,402 @@
+package me.arboriginal.SimpleCompass.plugin;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.configuration.MemorySection;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import com.google.common.collect.ImmutableMap;
+
+public abstract class AbstractTracker {
+ protected SimpleCompass sc;
+ protected File sf;
+ protected URL res;
+
+ public enum TrackingActions {
+ ADD, ACCEPT, ASK, DEL, DENY, HELP, START, STOP,
+ }
+
+ public enum TargetSelector {
+ ACTIVE, AVAILABLE, NEW, NEWCOORDS, NONE,
+ }
+
+ public FileConfiguration settings;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public AbstractTracker(SimpleCompass plugin) {
+ sc = plugin;
+ sf = new File(sc.getDataFolder(), "trackers/" + getClass().getSimpleName() + ".yml");
+ res = getClass().getResource("/settings.yml");
+ }
+
+ // Tracker methods -------------------------------------------------------------------------------------------------
+
+ public abstract String trackerID();
+
+ public String github() {
+ return null;
+ }
+
+ public String trackerName() {
+ return settings.getString("locales." + sc.config.getString("language") + ".name");
+ }
+
+ public String version() {
+ return "?";
+ }
+
+ // Initialization and update methods -------------------------------------------------------------------------------
+
+ /**
+ * At this state, config has not been read from user file,
+ * so DO NOT USE sc.config here, and DO NOT call methods which use this.
+ */
+ public boolean init() {
+ if (res == null) {
+ sc.getLogger().warning("settings.yml missing in " + sf.getAbsolutePath());
+ return false;
+ }
+
+ settings = YamlConfiguration.loadConfiguration(sf);
+ settings.options().copyDefaults(true);
+
+ InputStream is;
+
+ try {
+ is = res.openStream();
+ }
+ catch (Exception e) {
+ sc.getLogger().warning("Can't write default settings to " + sf.getAbsolutePath());
+ return false;
+ }
+
+ settings.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(is)));
+
+ try {
+ settings.save(sf);
+ }
+ catch (Exception e) {
+ sc.getLogger().severe("Can't write to " + sf.getAbsolutePath());
+ return false;
+ }
+
+ return true;
+ }
+
+ public void checkUpdate(CommandSender sender) {
+ if (!settings.getBoolean("settings.check_update", true)) return;
+
+ String github = github();
+
+ if (github == null) {
+ sendMessage(sender, "tracker_check_update_available",
+ ImmutableMap.of("tracker", trackerName(), "version", "new", "current", version()));
+ return;
+ }
+
+ String version = sc.githubVersion(github);
+
+ if (version == null)
+ sendMessage(sender, "tracker_check_update_failed", ImmutableMap.of("tracker", trackerName()));
+ else {
+ String current = version();
+ if (!version.equals(current))
+ sendMessage(sender, "tracker_check_update_available",
+ ImmutableMap.of("tracker", trackerName(), "version", version, "current", current));
+ }
+ }
+
+ // Utils methods ---------------------------------------------------------------------------------------------------
+
+ public void sendMessage(CommandSender sender, String key) {
+ sendMessage(sender, key, null);
+ }
+
+ public void sendMessage(CommandSender sender, String key, Map placeholders) {
+ if (key.isEmpty()) return;
+ String message = prepareMessage(key, placeholders);
+ if (!message.isEmpty()) sender.sendMessage(message);
+ }
+
+ public String prepareMessage(String key) {
+ return prepareMessage(key, null);
+ }
+
+ public String prepareMessage(String key, Map placeholders) {
+ String message = settings.getString("locales." + sc.config.getString("language") + "." + key);
+ if (message == null) message = sc.locale.getString(key);
+ if (message == null) return "";
+
+ if (placeholders != null) for (Iterator i = placeholders.keySet().iterator(); i.hasNext();) {
+ String placeholder = i.next();
+ message = message.replace("{" + placeholder + "}", placeholders.get(placeholder));
+ }
+
+ return sc.formatMessage(message.replace("{prefix}", sc.locale.getString("prefix")));
+ }
+
+ // Actions methods -------------------------------------------------------------------------------------------------
+
+ public TrackingActions getActionByName(String name) {
+ for (TrackingActions action : TrackingActions.values())
+ if (name.equalsIgnoreCase(getActionName(action))) return action;
+ return null;
+ }
+
+ public String getActionName(TrackingActions action) {
+ return sc.locale.getString("actions." + action);
+ }
+
+ public List getActionsAvailable(Player player, boolean keepUnavailable) {
+ List list = new ArrayList();
+ if (player.hasPermission("scompass.help")) list.add(TrackingActions.HELP);
+ return list;
+ }
+
+ public boolean limitReached(Player player, TrackingActions action, boolean showError, Integer current) {
+ if (!settings.contains("settings.limits." + action)) return false;
+ int limit = settings.getInt("settings.limits." + action), count;
+
+ if (current == null) {
+ UUID uid = player.getUniqueId();
+
+ count = sc.targets.activeTargets.get(trackerID()).containsKey(uid)
+ ? sc.targets.activeTargets.get(trackerID()).get(uid).size()
+ : 0;
+ }
+ else count = current;
+
+ if (count < limit) return false;
+
+ if (showError) sendMessage(player, "commands.sctrack.limits." + action,
+ ImmutableMap.of("limit", "" + limit, "tracker", trackerName()));
+
+ return true;
+ }
+
+ public TargetSelector requireTarget(TrackingActions action) {
+ switch (action) { // @formatter:off
+ case ASK:
+ case DEL:
+ case START: return TargetSelector.AVAILABLE;
+ case STOP: return TargetSelector.ACTIVE;
+ case ADD: return TargetSelector.NEW;
+ default: return TargetSelector.NONE;
+ } // @formatter:on
+ }
+
+ // Targets methods -------------------------------------------------------------------------------------------------
+
+ public boolean activate(Player player, String name, boolean showError) {
+ if (limitReached(player, TrackingActions.START, showError, null)) return false;
+ sc.targets.activateTarget(player, trackerID(), name);
+ return true;
+ }
+
+ public List activeTargets(Player player, String startWith) {
+ List list = new ArrayList();
+
+ for (String candidate : sc.datas.activeTargetsList(player, trackerID()))
+ if (startWith.isEmpty() || candidate.toLowerCase().startsWith(startWith.toLowerCase())) list.add(candidate);
+
+ return list;
+ }
+
+ public List availableTargets(Player player, String startWith) {
+ List list = new ArrayList();
+ String root = key(player);
+
+ if (datas().contains(root)) ((MemorySection) datas().get(root)).getKeys(false).forEach(candidate -> {
+ if (startWith.isEmpty() || candidate.toLowerCase().startsWith(startWith.toLowerCase()))
+ list.add(candidate);
+ });
+
+ return list;
+ }
+
+ public List autoloadTargets(Player player, String startWith) {
+ List list = new ArrayList();
+
+ if (settings.getBoolean("settings.autoload_target", false)) {
+ String perm = "scompass.track.auto." + trackerID() + ".";
+ for (String name : availableTargets(player, startWith))
+ if (player.hasPermission(perm + "*") || player.hasPermission(perm + name)) list.add(name);
+ }
+
+ return list;
+ }
+
+ public boolean del(Player player, String name) {
+ String key = key(player, name);
+
+ if (datas().contains(key)) {
+ disable(player, name);
+ return save(key, null);
+ }
+
+ return false;
+ }
+
+ public void disable(Player player, String name) {
+ sc.targets.disableTarget(player, trackerID(), name);
+ }
+
+ public double[] get(Player player, String name) {
+ String key = key(player, name);
+
+ if (datas().contains(key + ".x") && datas().contains(key + ".z"))
+ return new double[] { datas().getDouble(key + ".x"), datas().getDouble(key + ".z") };
+
+ return null;
+ }
+
+ public double[] getCoords(Player player, String[] args) {
+ double[] coords = null;
+
+ if (args.length == 5) try {
+ coords = new double[] { Double.parseDouble(args[3]), Double.parseDouble(args[4]) };
+ }
+ catch (Exception e) {}
+
+ if (coords == null) coords = new double[] { player.getLocation().getX(), player.getLocation().getZ() };
+
+ return coords;
+ }
+
+ public List list(Player player, TrackingActions action, String startWith) {
+ if (action == null) return availableTargets(player, startWith);
+
+ List list = new ArrayList();
+
+ switch (action) { // @formatter:off
+ case ACCEPT:
+ case ADD:
+ case DENY: break;
+
+ case ASK:
+ case DEL:
+ case START: list.addAll(availableTargets(player, startWith)); break;
+ default: list.addAll(activeTargets(player, startWith)); break;
+ } // @formatter:on
+
+ return list;
+ }
+
+ public List listFiltered(Player player, List list) {
+ if (player.hasPermission("scompass.track." + trackerID() + ".defined.*")) return list;
+
+ List filtered = new ArrayList();
+ for (String name : list)
+ if (player.hasPermission("scompass.track." + trackerID() + ".defined." + name)) filtered.add(name);
+
+ return filtered;
+ }
+
+ public boolean set(Player player, String name, double[] coords) {
+ String key = key(player, name);
+ if (datas().contains(key)) return false;
+
+ String root = key(player);
+ int current = datas().contains(root)
+ ? datas().getConfigurationSection(root).getKeys(false).size()
+ : 0;
+
+ if (limitReached(player, TrackingActions.ADD, true, current)) return false;
+
+ boolean success = (save(key + ".x", coords[0]) && save(key + ".z", coords[1]));
+ if (success) return true;
+
+ save(key, null);
+ return false;
+ }
+
+ public boolean playerIsClose(Player player, double[] coords) {
+ int dist = settings.getInt("settings.auto_disabled", 0);
+ return (dist > 0 && player.getLocation().distance(
+ new Location(player.getWorld(), coords[0], player.getLocation().getY(), coords[1])) < dist);
+ }
+
+ // Command methods -------------------------------------------------------------------------------------------------
+
+ public List commandSuggestions(Player player, String[] args, HashMap parsed) {
+ List list = new ArrayList<>();
+ if (args.length < 1 || args.length > 3) return list;
+
+ switch (args.length) {
+ case 2:
+ for (TrackingActions action : getActionsAvailable(player, false)) {
+ String name = getActionName(action);
+ if (name.toLowerCase().startsWith(args[1].toLowerCase())) list.add(name);
+ }
+ break;
+
+ case 3:
+ list.addAll(list(player, (TrackingActions) parsed.get("action"), args[2]));
+ break;
+ }
+
+ return list;
+ }
+
+ public String help(Player player, String command) {
+ String s = prepareMessage("commands.sctrack.help.separator") + "\n";
+ String h = s + prepareMessage("commands.sctrack.help.header", ImmutableMap.of("tracker", trackerName())) + s;
+
+ List list = new ArrayList();
+ list.add("noargs");
+
+ HashMap placeholders = new HashMap();
+ placeholders.put("command", command);
+ placeholders.put("tracker", trackerName());
+
+ for (TrackingActions action : getActionsAvailable(player, true)) if (action != TrackingActions.HELP) {
+ list.add(action.toString());
+ placeholders.put(action.toString(), sc.locale.getString("actions." + action));
+ }
+
+ for (String key : list) h += prepareMessage("help." + key, placeholders) + "\n";
+ return h + s;
+ }
+
+ public void parseArguments(Player player, String[] args, HashMap parsed) {
+ TrackingActions action = getActionByName(args[1]);
+ if (action == null || !getActionsAvailable(player, false).contains(action)) return;
+ parsed.put("action", action);
+ if (args.length == 2) return;
+ if (get(player, args[2]) != null) parsed.put("target", args[2]);
+ }
+
+ public abstract boolean perform(Player player, String command, TrackingActions action, String target,
+ String[] args);
+
+ // Storage methods -------------------------------------------------------------------------------------------------
+
+ public MemorySection datas() {
+ return sc.datas.users;
+ }
+
+ public String key(Player player) {
+ return key(player, null);
+ }
+
+ public String key(Player player, String name) {
+ return sc.datas.getKey(player, trackerID() + (name == null ? "" : "." + name));
+ }
+
+ public boolean save(String key, Object value) {
+ datas().set(key, value);
+ return sc.datas.saveUserDatas();
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/plugin/Listeners.java b/src/main/java/me/arboriginal/SimpleCompass/plugin/Listeners.java
new file mode 100644
index 0000000..e7b75b3
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/plugin/Listeners.java
@@ -0,0 +1,143 @@
+package me.arboriginal.SimpleCompass.plugin;
+
+import java.util.HashMap;
+import java.util.UUID;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityPickupItemEvent;
+import org.bukkit.event.entity.EntityToggleGlideEvent;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.inventory.InventoryOpenEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerCommandSendEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerSwapHandItemsEvent;
+import org.bukkit.event.server.ServerCommandEvent;
+import org.bukkit.event.vehicle.VehicleEnterEvent;
+import org.bukkit.event.vehicle.VehicleExitEvent;
+import org.bukkit.inventory.InventoryHolder;
+import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
+import me.arboriginal.SimpleCompass.utils.CacheUtil;
+
+public class Listeners implements Listener {
+ public SimpleCompass sc;
+ public HashMap locks;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public Listeners(SimpleCompass plugin) {
+ sc = plugin;
+ locks = new HashMap();
+ }
+
+ // Listener methods ------------------------------------------------------------------------------------------------
+
+ @EventHandler
+ public void onEntityPickupItem(EntityPickupItemEvent event) {
+ if (event.isCancelled() || !isPlayer(event.getEntity())) return;
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, (Player) event.getEntity(), sc.config.getInt("delays.pickup_refresh"));
+ }
+
+ @EventHandler
+ public void onEntityToggleGlide(EntityToggleGlideEvent event) {
+ if (event.isCancelled() || !isPlayer(event.getEntity())) return;
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, (Player) event.getEntity());
+ }
+
+ @EventHandler
+ public void onInventoryClose(InventoryCloseEvent event) {
+ InventoryHolder holder = event.getInventory().getHolder();
+ if (!(holder instanceof Player) || !isPlayer((Player) holder)) return;
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, (Player) holder);
+ }
+
+ @EventHandler
+ public void onInventoryOpen(InventoryOpenEvent event) {
+ if (!event.isCancelled()) sc.tasks.clear(TasksTypes.REFRESH_STATUS, (Player) event.getPlayer());
+ }
+
+ @EventHandler
+ public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
+ if (!event.isCancelled()) sc.compasses.commandTrigger(event.getMessage().substring(1));
+ }
+
+ @EventHandler
+ public void onPlayerCommandSend(PlayerCommandSendEvent event) {
+ if (isPlayer(event.getPlayer())) sc.tasks.set(TasksTypes.REFRESH_STATUS, event.getPlayer());
+ }
+
+ @EventHandler
+ public void onPlayerDeath(PlayerDeathEvent event) {
+ if (isPlayer(event.getEntity())) sc.compasses.removeCompass((Player) event.getEntity());
+ }
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+
+ sc.cache.init(player.getUniqueId());
+ sc.targets.loadTargets(player);
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, player);
+ sc.tasks.set(TasksTypes.FIX_UUID, player);
+ }
+
+ @EventHandler
+ public void onPlayerMove(PlayerMoveEvent event) {
+ if (event.isCancelled()) return;
+
+ Player player = event.getPlayer();
+ UUID uid = player.getUniqueId();
+ Long now = CacheUtil.now();
+
+ if (locks.containsKey(uid) && locks.get(uid) > now) return;
+
+ locks.put(uid, now + sc.config.getInt("delays.update_compass"));
+ sc.compasses.refreshCompassDatas(player);
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ Player player = event.getPlayer();
+ UUID uid = player.getUniqueId();
+
+ locks.remove(uid);
+ sc.tasks.clear(player);
+ sc.targets.unloadTargets(player);
+ sc.cache.clear(uid);
+ }
+
+ @EventHandler
+ public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
+ if (!event.isCancelled()) sc.tasks.set(TasksTypes.REFRESH_STATUS, event.getPlayer());
+ }
+
+ @EventHandler
+ public void onVehicleEnter(VehicleEnterEvent event) {
+ if (event.isCancelled() || !isPlayer(event.getEntered())) return;
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, (Player) event.getEntered());
+ }
+
+ @EventHandler
+ public void onVehicleExit(VehicleExitEvent event) {
+ if (event.isCancelled() || !isPlayer(event.getExited())) return;
+ sc.tasks.set(TasksTypes.REFRESH_STATUS, (Player) event.getExited());
+ }
+
+ @EventHandler
+ public void onServerCommand(ServerCommandEvent event) {
+ if (!event.isCancelled()) sc.compasses.commandTrigger(event.getCommand());
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Private methods
+ // -----------------------------------------------------------------------------------------------
+
+ private boolean isPlayer(Entity entity) {
+ return (entity instanceof Player) && !entity.hasMetadata("NPC");
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/plugin/SimpleCompass.java b/src/main/java/me/arboriginal/SimpleCompass/plugin/SimpleCompass.java
new file mode 100644
index 0000000..ad19ae0
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/plugin/SimpleCompass.java
@@ -0,0 +1,370 @@
+package me.arboriginal.SimpleCompass.plugin;
+
+import java.io.File;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import org.apache.commons.lang.time.DurationFormatUtils;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.java.JavaPlugin;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import me.arboriginal.SimpleCompass.commands.InterfaceCommand;
+import me.arboriginal.SimpleCompass.commands.OptionCommand;
+import me.arboriginal.SimpleCompass.commands.TrackCommand;
+import me.arboriginal.SimpleCompass.managers.CompassManager;
+import me.arboriginal.SimpleCompass.managers.DataManager;
+import me.arboriginal.SimpleCompass.managers.TargetManager;
+import me.arboriginal.SimpleCompass.managers.TaskManager;
+import me.arboriginal.SimpleCompass.utils.CacheUtil;
+import me.arboriginal.SimpleCompass.utils.ConfigUtil;
+import me.arboriginal.SimpleCompass.utils.LangUtil;
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.ClickEvent;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+public class SimpleCompass extends JavaPlugin implements TabCompleter {
+ public FileConfiguration config, locale;
+ public CacheUtil cache;
+ public DataManager datas;
+ public TaskManager tasks;
+ public TargetManager targets;
+ public Listeners listeners;
+ public CompassManager compasses = null;
+ public boolean isReady = false;
+
+ public HashMap trackers;
+
+ // JavaPlugin methods ----------------------------------------------------------------------------------------------
+
+ @Override
+ public void onDisable() {
+ super.onDisable();
+
+ isReady = false;
+ compasses.unload();
+ tasks.clear();
+ }
+
+ @Override
+ public void onEnable() {
+ super.onEnable();
+
+ try {
+ getServer().spigot();
+ }
+ catch (Exception e) {
+ getServer().getPluginManager().disablePlugin(this);
+ getLogger().severe("This plugin only works on Spigot servers!");
+ // No need to go on, it will not work
+ return;
+ }
+
+ loadTrackers();
+ reloadConfig();
+ checkUpdate(getServer().getConsoleSender());
+
+ if (!trackers.isEmpty()) getCommand("scompass-track").setExecutor(new TrackCommand(this));
+
+ getCommand("scompass-option").setExecutor(new OptionCommand(this));
+ getCommand("scompass").setExecutor(new InterfaceCommand(this));
+
+ listeners = new Listeners(this);
+ getServer().getPluginManager().registerEvents(listeners, this);
+ }
+
+ @Override
+ public void onLoad() {
+ super.onLoad();
+ cache = new CacheUtil(this);
+ }
+
+ @Override
+ public void reloadConfig() {
+ super.reloadConfig();
+ cache.reset();
+
+ saveDefaultConfig();
+
+ isReady = false;
+ config = getConfig();
+ config.options().copyDefaults(true);
+ locale = new LangUtil(this).getLocale(config.getString("language"));
+
+ for (ConfigUtil.ConfigError error : new ConfigUtil(this).validate(config))
+ getLogger().warning(prepareMessage(error.errorKey, error.placeholders));
+
+ saveConfig();
+
+ if (compasses != null) compasses.unload();
+
+ datas = new DataManager(this);
+ tasks = new TaskManager(this);
+ targets = new TargetManager(this);
+ compasses = new CompassManager(this);
+
+ targets.loadTargets();
+ compasses.refreshCompassState();
+
+ isReady = true;
+ }
+
+ // JavaPlugin methods: Basic commands ------------------------------------------------------------------------------
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ switch (command.getName().toLowerCase()) {
+ case "scompass-reload":
+ reloadConfig();
+
+ Iterator it = trackers.keySet().iterator();
+ while (it.hasNext()) {
+ String trackerID = it.next();
+
+ if (!trackers.get(trackerID).init()) {
+ it.remove();
+ sendMessage(sender, "tracker_disabled", ImmutableMap.of("tracker", trackerID));
+ }
+ }
+
+ sendMessage(sender, "configuration_reloaded");
+ checkUpdate(sender);
+ return true;
+
+ case "scompass-toggle":
+ if (sender instanceof Player) datas.compassOptionToggle((Player) sender);
+ else sendMessage(sender, "command_only_for_players");
+ return true;
+ }
+
+ return super.onCommand(sender, command, label, args);
+ }
+
+ // Public methods --------------------------------------------------------------------------------------------------
+
+ public TextComponent createClickableMessage(String text, Map> commands) {
+ TextComponent textComponent = new TextComponent();
+
+ for (String command : commands.keySet()) text = text.replace(command, "§k" + command + "§r");
+
+ for (BaseComponent component : TextComponent.fromLegacyText(text)) {
+ if (component instanceof TextComponent) {
+ Map command = commands.get(((TextComponent) component).getText().trim());
+
+ if (command != null) {
+ if (command.containsKey("text")) ((TextComponent) component).setText("§r" + command.get("text"));
+
+ if (command.containsKey("click"))
+ component.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command.get("click")));
+
+ if (command.containsKey("hover"))
+ component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
+ TextComponent.fromLegacyText(command.get("hover"))));
+ }
+ }
+
+ textComponent.addExtra(component);
+ }
+
+ return textComponent;
+ }
+
+ public String formatMessage(String message) {
+ return ChatColor.translateAlternateColorCodes('&', message);
+ }
+
+ public String formatTime(long time) {
+ String[] parts = DurationFormatUtils.formatDuration(time, "HH:mm:ss").split(":");
+ String hour = locale.getString("time_display.hour");
+ String minute = locale.getString("time_display.minute");
+ String second = locale.getString("time_display.second");
+ String human = "";
+
+ if (time < 60000) human = Math.max(Integer.parseInt(parts[2]), 1) + second;
+ else {
+ if (time >= 3600000) human = parts[0] + hour + " ";
+
+ human += (time > config.getInt("min_time_to_display_seconds") * 1000)
+ ? parts[1] + minute + " " + parts[2] + second
+ : (Integer.parseInt(parts[1]) + 1) + minute;
+ }
+
+ return human.replaceFirst("^0+(?!$)", "");
+ }
+
+ public String githubVersion(String repository) {
+ String version = (String) cache.versionGet(repository);
+ if (version != null) return version;
+
+ String url = "https://api.github.com/repos/" + repository + "/releases";
+
+ try {
+ HttpURLConnection connexion = (HttpURLConnection) new URL(url).openConnection();
+ connexion.addRequestProperty("User-Agent", "SimpleCompass");
+ JsonElement element = new JsonParser().parse(new InputStreamReader(connexion.getInputStream()));
+
+ version = element.getAsJsonArray().get(0).getAsJsonObject().get("tag_name").getAsString();
+ }
+ catch (Exception e) {}
+
+ cache.versionSet(repository, version, config.getInt("delays.update_version_cache"));
+ return version;
+ }
+
+ public String prepareMessage(String key) {
+ return prepareMessage(key, null);
+ }
+
+ public String prepareMessage(String key, Map placeholders) {
+ String message = locale.getString(key);
+
+ if (placeholders != null) {
+ for (Iterator i = placeholders.keySet().iterator(); i.hasNext();) {
+ String placeholder = i.next();
+
+ message = message.replace("{" + placeholder + "}", placeholders.get(placeholder));
+ }
+ }
+
+ return formatMessage(message.replace("{prefix}", locale.getString("prefix")));
+ }
+
+ public void sendMessage(CommandSender sender, String key) {
+ sendMessage(sender, key, null);
+ }
+
+ public void sendMessage(CommandSender sender, String key, Map placeholders) {
+ if (key.isEmpty()) return;
+
+ String message = prepareMessage(key, placeholders);
+
+ if (!message.isEmpty()) sender.sendMessage(message);
+ }
+
+ // Private methods -------------------------------------------------------------------------------------------------
+
+ private void checkUpdate(CommandSender sender) {
+ if (!config.getBoolean("check_update")) return;
+ String version = githubVersion("arboriginal/SimpleCompass");
+
+ if (version == null) sendMessage(sender, "plugin_check_update_failed");
+ else {
+ String current = getDescription().getVersion();
+ if (!version.equals(current)) sendMessage(sender, "plugin_check_update_available",
+ ImmutableMap.of("version", version, "current", current));
+ }
+
+ if (trackers.isEmpty()) return;
+ trackers.forEach((trackerID, tracker) -> {
+ tracker.checkUpdate(sender);
+ });
+ }
+
+ private void loadTrackers() {
+ File dir = new File(getDataFolder(), "trackers");
+ if (!dir.exists()) dir.mkdirs();
+
+ if (!dir.exists() || !dir.isDirectory()) {
+ getLogger().severe("Unable to create trackers folder...");
+ return;
+ }
+
+ trackers = new HashMap();
+ for (final File file : dir.listFiles()) {
+ if (file.isDirectory() || !file.getName().endsWith(".jar")) continue;
+
+ Exception error = loadTrackerFile(file);
+
+ if (error == null)
+ getLogger().info("Tracker §6{tracker}§r successfully loaded".replace("{tracker}", file.getName()));
+ else getLogger().severe("Error loading tracker " + file.getName() + ": " + error.getMessage());
+ }
+ }
+
+ private Exception loadTrackerException(URLClassLoader loader, JarFile jar, String message) {
+ if (loader != null) try {
+ loader.close();
+ }
+ catch (Exception e) {}
+
+ if (jar != null) try {
+ jar.close();
+ }
+ catch (Exception e) {}
+
+ return new Exception(message);
+ }
+
+ private Exception loadTrackerFile(File file) {
+ URLClassLoader loader = null;
+ JarFile jar = null;
+
+ try {
+ loader = new URLClassLoader(new URL[] { new URL("jar:" + file.toURI().toURL() + "!/") }, getClassLoader());
+ }
+ catch (Exception e) {
+ return loadTrackerException(loader, jar, "Can't initialize a class loader from " + file.getName());
+ }
+
+ Enumeration entries = null;
+
+ try {
+ jar = new JarFile(file.getAbsolutePath());
+ entries = jar.entries();
+ }
+ catch (Exception e) {}
+
+ if (entries == null) return loadTrackerException(loader, jar, "Can't read content of " + file.getName());
+
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ if (entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
+
+ String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.');
+ Object tracker = null;
+
+ try {
+ tracker = Class.forName(className, true, loader).getConstructor(this.getClass()).newInstance(this);
+ }
+ catch (NoClassDefFoundError e) {
+ return loadTrackerException(loader, jar,
+ "Can't find class " + className + " in " + file.getName() + "...");
+ }
+ catch (Exception e) {
+ return loadTrackerException(loader, jar,
+ "Can't load class " + className + " from " + file.getName() + "...");
+ }
+
+ if (!(tracker instanceof AbstractTracker)) continue;
+ String trackrID = ((AbstractTracker) tracker).trackerID();
+
+ if (trackers.containsKey(trackrID)) return loadTrackerException(loader, jar,
+ "Tracker {tracker} is using the ID {id} which is already used by {other}..."
+ .replace("{tracker}", file.getName()).replace("id", trackrID)
+ .replace("{other}", trackers.get(trackrID).getClass().getSimpleName()));
+
+ if (!((AbstractTracker) tracker).init()) return loadTrackerException(loader, jar,
+ "Tracker {tracker} failed on init...".replace("{tracker}", file.getName()).replace("id", trackrID));
+
+ trackers.put(trackrID, (AbstractTracker) tracker);
+ loadTrackerException(loader, jar, "");
+ return null;
+ }
+
+ return loadTrackerException(loader, jar, "No tracker found in the jar file...");
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/utils/CacheUtil.java b/src/main/java/me/arboriginal/SimpleCompass/utils/CacheUtil.java
new file mode 100644
index 0000000..dc97a50
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/utils/CacheUtil.java
@@ -0,0 +1,103 @@
+package me.arboriginal.SimpleCompass.utils;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.UUID;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class CacheUtil {
+ private SimpleCompass sc;
+ private File vcf;
+ private FileConfiguration vcc;
+ private HashMap> datas;
+
+ public static final int PERMANENT = -1;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public CacheUtil(SimpleCompass plugin) {
+ sc = plugin;
+ vcf = new File(sc.getDataFolder(), "versionCache.yml");
+
+ if (!vcf.exists()) try {
+ if (!sc.getDataFolder().exists()) sc.getDataFolder().mkdirs();
+ vcf.createNewFile();
+ }
+ catch (Exception e) {
+ sc.getLogger().warning("Can't write to version cache file");
+ }
+
+ vcc = YamlConfiguration.loadConfiguration(vcf);
+ reset();
+ }
+
+ // Static methods --------------------------------------------------------------------------------------------------
+
+ public static long now() {
+ return System.currentTimeMillis();
+ }
+
+ // Public methods --------------------------------------------------------------------------------------------------
+
+ public void clear(UUID uid) {
+ datas.remove(uid);
+ }
+
+ public void clear(UUID uid, String key) {
+ datas.get(uid).remove(key);
+ }
+
+ public Object get(UUID uid, String key) {
+ if (!datas.containsKey(uid)) return null;
+ Data data = datas.get(uid).get(key);
+ return (data != null && (data.expire == PERMANENT || data.expire > now())) ? data.value : null;
+ }
+
+ public void init(UUID uid) {
+ datas.put(uid, new HashMap());
+ }
+
+ public void reset() {
+ datas = new HashMap>();
+ for (Player player : sc.getServer().getOnlinePlayers()) init(player.getUniqueId());
+ }
+
+ public void set(UUID uid, String key, Object value, int duration) {
+ if (!datas.containsKey(uid)) init(uid);
+ datas.get(uid).put(key, new Data((duration == PERMANENT) ? PERMANENT : now() + duration, value));
+ }
+
+ // Public methods: Version update check cache ----------------------------------------------------------------------
+
+ public Object versionGet(String key) {
+ long expire = vcc.getLong("version." + key + ".expire", 0);
+ return (expire > now()) ? vcc.get("version." + key + ".value", null) : null;
+ }
+
+ public void versionSet(String key, Object value, int duration) {
+ vcc.set("version." + key + ".expire", now() + duration * 60000);
+ vcc.set("version." + key + ".value", value);
+
+ try {
+ vcc.save(vcf);
+ }
+ catch (Exception e) {
+ sc.getLogger().warning("Can't write to version cache file");
+ }
+ }
+
+ // Private classes -------------------------------------------------------------------------------------------------
+
+ private static class Data {
+ public long expire;
+ public Object value;
+
+ Data(long expiration, Object datas) {
+ expire = expiration;
+ value = datas;
+ }
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/utils/ConfigUtil.java b/src/main/java/me/arboriginal/SimpleCompass/utils/ConfigUtil.java
new file mode 100644
index 0000000..489556c
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/utils/ConfigUtil.java
@@ -0,0 +1,258 @@
+package me.arboriginal.SimpleCompass.utils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.lang.StringUtils;
+import org.bukkit.Material;
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarStyle;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.MemoryConfiguration;
+import org.bukkit.configuration.file.FileConfiguration;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import me.arboriginal.SimpleCompass.commands.AbstractCommand.CompassOptions;
+import me.arboriginal.SimpleCompass.commands.AbstractCommand.SubCmds;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
+import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
+import me.arboriginal.SimpleCompass.managers.CompassManager.RequirementsSections;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
+import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TrackingActions;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class ConfigUtil {
+ private List errors;
+ private SimpleCompass sc;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public ConfigUtil(SimpleCompass plugin) {
+ sc = plugin;
+ }
+
+ // Public methods --------------------------------------------------------------------------------------------------
+
+ public List validate(FileConfiguration configuration) {
+ clearErrors();
+
+ List modified = new ArrayList();
+
+ modified.addAll(validateCustomNames(TrackingActions.values(), "actions"));
+ modified.addAll(validateCustomNames(SubCmds.values(), "subcommands"));
+
+ if (!modified.isEmpty())
+ addError("invalid_names", ImmutableMap.of("modified", String.join(" ,", modified)));
+
+ validateTrackerSettings();
+ validateBossbarAttributes();
+
+ for (CompassTypes type : CompassTypes.values()) {
+ validateDefaultSettings(type);
+
+ for (CompassModes mode : CompassModes.values()) validateCardinals(type, mode);
+
+ boolean hasRequirements = false;
+
+ for (RequirementsSections section : RequirementsSections.values())
+ if (!validateRequiredItems(type, section).isEmpty()) hasRequirements = true;
+
+ if (!hasRequirements) {
+ fixValue("compass." + type + ".require.items", new MemoryConfiguration());
+ fixValue("compass." + type + ".require.consume", false);
+ }
+ }
+
+ return errors;
+ }
+
+ public void clearErrors() {
+ errors = new ArrayList();
+ }
+
+ // Private methods -------------------------------------------------------------------------------------------------
+
+ private void addError(String errorKey) {
+ addError(errorKey, null);
+ }
+
+ private void addError(String errorKey, Map placeholders) {
+ errors.add(new ConfigError(errorKey, placeholders));
+ }
+
+ private void fixValue(String key) {
+ fixValue(key, sc.config.getDefaults().get(key));
+ }
+
+ private void fixValue(String key, Object value) {
+ sc.config.set(key, value);
+ }
+
+ private void validateBossbarAttributes() {
+ try {
+ BarColor.valueOf(sc.config.getString("compass.BOSSBAR.attributes.color"));
+ }
+ catch (Exception e) {
+ addError("invalid_bossbar_color");
+ fixValue("compass.BOSSBAR.attributes.color");
+ }
+
+ try {
+ BarStyle.valueOf(sc.config.getString("compass.BOSSBAR.attributes.style"));
+ }
+ catch (Exception e) {
+ addError("invalid_bossbar_style");
+ fixValue("compass.BOSSBAR.attributes.style");
+ }
+
+ ConfigurationSection levels = sc.config
+ .getConfigurationSection("compass.BOSSBAR.attributes.elytra_durability.levels");
+
+ if (levels == null) return;
+
+ TreeMap sortedLevel = new TreeMap();
+
+ for (String level : levels.getKeys(false)) {
+ try {
+ BarColor.valueOf(levels.get(level).toString());
+ sortedLevel.put(Integer.parseInt(level), levels.get(level).toString());
+ }
+ catch (Exception e) {}
+ }
+
+ if (levels.getKeys(false).size() != sortedLevel.size()) addError("invalid_bossbar_color_level");
+
+ fixValue("compass.BOSSBAR.attributes.elytra_durability.levels", sortedLevel);
+ }
+
+ private void validateCardinals(CompassTypes type, CompassModes mode) {
+ String key = type + "." + mode;
+ String fillChar = sc.config.getString("compass." + key + ".cardinals.filling_char");
+
+ if (fillChar.isEmpty()) return;
+
+ List cardinals = ImmutableList.of("east", "north", "south", "west");
+
+ int maxLength = 0;
+
+ for (String cardinal : cardinals)
+ maxLength = Math.max(maxLength, sc.config.getString("compass." + key + ".cardinals." + cardinal).length());
+
+ for (String cardinal : cardinals) {
+ String word = sc.config.getString("compass." + key + ".cardinals." + cardinal);
+
+ if (word.length() < maxLength) {
+ word = StringUtils.repeat(fillChar, (int) Math.floor((double) (maxLength - word.length()) / 2)) + word
+ + StringUtils.repeat(fillChar, (int) Math.ceil((double) (maxLength - word.length()) / 2));
+
+ fixValue("compass." + key + ".cardinals." + cardinal, word);
+ addError("cardinal_length", ImmutableMap.of("key", key, "cardinal", cardinal));
+ }
+ }
+ }
+
+ private List validateCustomNames(Object[] values, String section) {
+ List modified = new ArrayList();
+
+ for (Object obj : values) {
+ String key = section + "." + obj;
+
+ if (sc.locale.getString(key).contains(" ")) {
+ sc.locale.getString(sc.locale.getString(key).replaceAll(" ", ""));
+ modified.add(key);
+ }
+ }
+
+ return modified;
+ }
+
+ private void validateDefaultSettings(CompassTypes type) {
+ try {
+ CompassOptions.valueOf(sc.config.getString("compass." + type + ".default.option"));
+ }
+ catch (Exception e) {
+ addError("invalid_choice", ImmutableMap.of("type", "" + type, "key", "option"));
+ fixValue("compass." + type + ".default.option");
+ }
+
+ try {
+ CompassModes.valueOf(sc.config.getString("compass." + type + ".default.mode"));
+ }
+ catch (Exception e) {
+ addError("invalid_choice", ImmutableMap.of("type", "" + type, "key", "mode"));
+ fixValue("compass." + type + ".default.mode");
+ }
+ }
+
+ private List validateRequiredItems(CompassTypes type, RequirementsSections section) {
+ List list = sc.config.getStringList("compass." + type + ".require.items." + section);
+ List items = new ArrayList();
+
+ if (list.size() == 0) return items;
+
+ if (list.contains("AIR")) {
+ fixValue("compass." + type + ".require.items." + section, items);
+ return items;
+ }
+
+ for (String item : list) {
+ try {
+ Material.valueOf(item);
+ items.add(item);
+ }
+ catch (Exception e) {}
+ }
+
+ if (list.size() > items.size()) {
+ addError("invalid_items", ImmutableMap.of("section", "" + section, "type", "" + type,
+ "ignored", "" + (list.size() - items.size())));
+ fixValue("compass." + type + ".require.items." + section, items);
+ }
+
+ return items;
+ }
+
+ private void validateTrackerSettings() {
+ Iterator it = sc.trackers.keySet().iterator();
+
+ while (it.hasNext()) {
+ String trackerID = it.next();
+
+ if (!((AbstractTracker) sc.trackers.get(trackerID)).trackerName().toLowerCase().matches("^[a-z0-9]+$")) {
+ it.remove();
+ sc.sendMessage(sc.getServer().getConsoleSender(), "tracker_disabled_invalid_name",
+ ImmutableMap.of("tracker", trackerID));
+ }
+ }
+
+ List userPriorities = sc.config.getStringList("trackers_priorities");
+ List readPriorities = new ArrayList();
+
+ for (String priority : sc.config.getStringList("trackers_priorities")) {
+ if (!sc.trackers.containsKey(priority)) userPriorities.remove(priority);
+ else if (!readPriorities.contains(priority)) readPriorities.add(priority);
+ }
+
+ if (readPriorities.size() != sc.trackers.size()) {
+ fixValue("trackers_priorities");
+ addError("invalid_priorities");
+ }
+
+ for (String tracker : sc.trackers.keySet()) if (!userPriorities.contains(tracker)) userPriorities.add(tracker);
+ fixValue("trackers_priorities", userPriorities);
+ }
+
+ // Private classes -------------------------------------------------------------------------------------------------
+
+ public static class ConfigError {
+ public final String errorKey;
+ public final Map placeholders;
+
+ public ConfigError(String k, Map p) {
+ errorKey = k;
+ placeholders = p;
+ }
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/utils/LangUtil.java b/src/main/java/me/arboriginal/SimpleCompass/utils/LangUtil.java
new file mode 100644
index 0000000..b496501
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/utils/LangUtil.java
@@ -0,0 +1,106 @@
+package me.arboriginal.SimpleCompass.utils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import com.google.common.base.Charsets;
+import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
+
+public class LangUtil {
+ private SimpleCompass sc;
+
+ // Constructor methods ---------------------------------------------------------------------------------------------
+
+ public LangUtil(SimpleCompass plugin) {
+ sc = plugin;
+ }
+
+ // Public methods --------------------------------------------------------------------------------------------------
+
+ public FileConfiguration getLocale(String language) {
+ FileConfiguration locale;
+ boolean newFile;
+
+ String langRes = "lang/" + language + ".yml";
+ String langRdef = "lang/en.yml";
+ File langFile = new File(sc.getDataFolder(), langRes);
+
+ if (newFile = !langFile.exists()) {
+ String logMsg = null;
+
+ sc.getLogger().warning("Lang file for « " + language + " » doesn't exist.");
+
+ if (sc.getResource(langRes) != null) {
+ logMsg = "Lang file for « " + language + " » copied from plugin.";
+ }
+ else {
+ sc.getLogger().warning("Lang « " + language + " » doesn't exist in the plugin either.");
+
+ try {
+ writeResourceToFile(sc.getResource(langRdef), langFile);
+
+ logMsg = "A new lang file for « " + language + " » has been generated, based on english.";
+ langRes = null;
+ }
+ catch (Exception e) {
+ sc.getLogger().severe("Lang file for « " + language + " » cannot be generated.");
+
+ logMsg = "Fallback to default lang file.";
+ langRes = langRdef;
+ }
+ }
+
+ copyResourceToFile(langRes, langFile);
+ sc.getLogger().info(logMsg);
+ }
+
+ locale = YamlConfiguration.loadConfiguration(langFile);
+
+ if (!newFile) saveConfigToFile(locale, langFile, langRdef);
+
+ return locale;
+ }
+
+ // Private methods -------------------------------------------------------------------------------------------------
+
+ private void copyResourceToFile(String resource, File file) {
+ if (resource != null) {
+ sc.saveResource(resource, false);
+
+ file = new File(sc.getDataFolder(), resource);
+ }
+ }
+
+ private void saveConfigToFile(FileConfiguration config, File file, String defaultRes) {
+ config.setDefaults(YamlConfiguration.loadConfiguration(
+ new InputStreamReader(sc.getResource(defaultRes), Charsets.UTF_8)));
+ // This ensure sentences added in next versions are stored in the file with their default values
+ config.options().copyDefaults(true);
+
+ try {
+ config.save(file);
+ }
+ catch (Exception e) {
+ sc.getLogger().warning("The language file cannot be updated in your plugin folder. "
+ + "You need to check by yourself if you didn't missed some sentences you want to translate. "
+ + "Default language will be used for them.");
+ }
+ }
+
+ private void writeResourceToFile(InputStream in, File file) throws Exception {
+ file.getParentFile().mkdirs();
+
+ OutputStream out = new FileOutputStream(file);
+ byte[] buf = new byte[1024];
+ int len;
+
+ while ((len = in.read(buf)) > 0) out.write(buf, 0, len);
+
+ out.close();
+ in.close();
+ }
+}
diff --git a/src/main/java/me/arboriginal/SimpleCompass/utils/NMSUtil.java b/src/main/java/me/arboriginal/SimpleCompass/utils/NMSUtil.java
new file mode 100644
index 0000000..66b6ae6
--- /dev/null
+++ b/src/main/java/me/arboriginal/SimpleCompass/utils/NMSUtil.java
@@ -0,0 +1,71 @@
+package me.arboriginal.SimpleCompass.utils;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+public final class NMSUtil {
+ private static final String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
+
+ // Public methods --------------------------------------------------------------------------------------------------
+
+ public static boolean openBook(Player player, ItemStack book) {
+ try { // 1.14 new method
+ player.getClass().getMethod("openBook", ItemStack.class).invoke(player, book);
+ return true;
+ }
+ catch (Exception e) {}
+ // Fallback for 1.13...
+ int heldSlot = player.getInventory().getHeldItemSlot();
+ ItemStack current = player.getInventory().getItem(heldSlot);
+ boolean success = false;
+
+ player.getInventory().setItem(heldSlot, book);
+
+ try {
+ Object key = getNMSClass("MinecraftKey").getConstructor(String.class).newInstance("minecraft:book_open");
+ Class> bb = getBufferClass("ByteBuf"), ic = int.class, pds = getNMSClass("PacketDataSerializer");
+ // @formatter:off
+ success = sendPacket(player, getNMSClass("PacketPlayOutCustomPayload").getConstructor(key.getClass(), pds)
+ .newInstance(key, pds.getConstructor(getBufferClass("ByteBuf"))
+ .newInstance(bb.getMethod("writerIndex", ic).invoke(bb.getMethod("setByte", ic, ic)
+ .invoke(getBufferClass("Unpooled").getMethod("buffer", ic).invoke(null, 256), 0, 0), 1))));
+ } catch (Exception e) {}
+ // @formatter:on
+ player.getInventory().setItem(heldSlot, current);
+ return success;
+ }
+
+ // Private methods -------------------------------------------------------------------------------------------------
+
+ private static Class> getBufferClass(String name) {
+ return getClass(name, "io.netty.buffer");
+ }
+
+ private static Class> getNMSClass(String name) {
+ return getClass(name, "net.minecraft.server." + version);
+ }
+
+ private static Class> getClass(String name, String namespace) {
+ try {
+ return Class.forName(namespace + "." + name);
+ }
+ catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static boolean sendPacket(Player player, Object packet) {
+ try {
+ Object handle = player.getClass().getMethod("getHandle").invoke(player);
+ Object target = handle.getClass().getField("playerConnection").get(handle);
+
+ target.getClass().getMethod("sendPacket", getNMSClass("Packet")).invoke(target, packet);
+
+ return true;
+ }
+ catch (Exception e) {}
+
+ return false;
+ }
+}
diff --git a/src/config.yml b/src/main/resources/config.yml
similarity index 90%
rename from src/config.yml
rename to src/main/resources/config.yml
index 2bb13df..4047d6b 100644
--- a/src/config.yml
+++ b/src/main/resources/config.yml
@@ -1,5 +1,16 @@
-# You can find the default config with description of parameters here:
-# https://github.com/arboriginal/SimpleCompass/blob/master/src/config.yml
+# ----------------------------------------------------------------------------------------------------------------------
+# ${project.artifactId}, version ${project.version} - Configuration file.
+# .
+# You will find an explanation of all parameters here:
+# https://github.com/arboriginal/${project.artifactId}/blob/master/src/main/resources/config.yml
+# .
+# Personal message: I'd love to have videos showing and explaining the plugin in use, but I'm not good at making vids...
+# If you are able to make one, send me the link (preferred on Youtube) and I will include it (with credit and link)
+# on the plugin page. English language is IMHO the most important, because it will be understandable by the most,
+# but if you want to make a video in another language, I will include it too.
+# .
+# Remember to drink water, eat at least 5 fruits & vegetables a day and to be crazy all days, life is a big joke! ;)
+# ----------------------------------------------------------------------------------------------------------------------
language: en # Choose lang file you want to use, actually, only en and fr exists, but you can set what you want:
# If this lang file doesn't exist, a new one is created (copy of english, so you can edit it).
@@ -91,8 +102,8 @@ compass:
after: " §8] ==--"
cardinals: # Those four cardinal points should have the same length for a better precision. If not, they will be filled with "filling_char".
north: "North"
- east: "East"
- west: "West"
+ east: "East "
+ west: "West "
south: "South"
filling_char: " " # If they don't have the same length, this character will be used to fill the short ones. Put an empty string to disable.
@@ -107,8 +118,8 @@ compass:
after: " §8] ==--"
cardinals:
north: "North"
- east: "East"
- west: "West"
+ east: "East "
+ west: "West "
south: "South"
filling_char: " "
diff --git a/lang/en.yml b/src/main/resources/lang/en.yml
similarity index 90%
rename from lang/en.yml
rename to src/main/resources/lang/en.yml
index 5901f9b..5a1f855 100644
--- a/lang/en.yml
+++ b/src/main/resources/lang/en.yml
@@ -1,12 +1,21 @@
+# ----------------------------------------------------------------------------------------------------------------------
+# ${project.artifactId}, version ${project.version} - Localization file.
+# .
# In all messages, you can use colors with « & » followed by a number / letter:
# from 0 (black) to r (reset). See this link: https://wiki.ess3.net/mc/
# (You can still use the classic notation with « § » symbol instead of « & ».)
+# .
+# BEWARE: When you see « {word} », this is a placeholder!
+# So you have to let it like that (not translated, not customised),
+# Otherwise, the plugin will not be able to substitute the dynamic value it represents.
+# .
+# If you don't remember where a message is used, you can read the online version which contains details here:
+# https://github.com/arboriginal/SimpleCompass/blob/master/src/main/resources/lang/en.yml
+# ----------------------------------------------------------------------------------------------------------------------
+prefix: "&8[&eSimpleCompass&8] "
# Prefix which can be used in all messages, can be an empty string to disable it globally...
-prefix: "&8[&eSimpleCompass&8] " # But you can also decide to remove the {prefix} part in specific messages.
-
-# BEWARE: When you see « {word} », this is a placeholder! So you have to let it like that (not translated, not customised),
-# Otherwise, the plugin will not be able to substitute the dynamic value it represents.
+# But you can also decide to remove the {prefix} part in specific messages.
# Message displayed when using the reload command
configuration_reloaded: "{prefix}&aConfiguration successfully reloaded"
@@ -74,6 +83,10 @@ interface_failed_auto_open_give_failed: "{prefix}&eInterface book cannot be auto
# In case of the book interface cannot be opened automaticaly and the book cannot be given to the player because of the cooldown
interface_book_give_cooldown: "{prefix}&cYou have to wait {delay} before another book interface will be available!"
+# Messages displayed when using the command /scompass-toggle
+toggle_state_saved: "{prefix}&6Compass settings put aside."
+toggle_state_restored: "{prefix}&6Compass settings restored."
+
# You can define your own action names here (choose something short and understandable by your players)
actions:
ACCEPT: accept
diff --git a/lang/fr.yml b/src/main/resources/lang/fr.yml
similarity index 91%
rename from lang/fr.yml
rename to src/main/resources/lang/fr.yml
index 1b58e0f..9f37457 100644
--- a/lang/fr.yml
+++ b/src/main/resources/lang/fr.yml
@@ -1,12 +1,21 @@
+# ----------------------------------------------------------------------------------------------------------------------
+# ${project.artifactId}, version ${project.version} - Fichier de traductions.
+# .
# Dans tous les messages, tu peux utiliser les couleurs avec « & » suivi d'un chiffre / lettre :
# de 0 (noir) à r (reset). Voir ce lien : https://wiki.ess3.net/mc/
# (Tu peux également utiliser la notation classique avec le symbole « § » à la place de « & ».)
+# .
+# ATTENTION : Quand tu vois « {word} », c'est un masque de remplacement !
+# Donc tu dois le laisser tel quel (non traduit, non personnalisé),
+# Sinon, le plugin ne sera pas capable de substituer la value dynamique qu'il représente.
+# .
+# Si tu ne te sais plus où un message est utilisé, tu peux consulter la version en ligne qui contient des details ici :
+# https://github.com/arboriginal/SimpleCompass/blob/master/src/main/resources/lang/fr.yml
+# ----------------------------------------------------------------------------------------------------------------------
+prefix: "&8[&eSimpleCompass&8] "
# Préfixe qui peut être utilisé dans tous les messages, peut être une chaine vide pour le désactiver globalement...
-prefix: "&8[&eSimpleCompass&8] " # Mais tu peux aussi le décider de retirer la partie {prefix} dans des messages spécifiques.
-
-# ATTENTION : Quand tu vois « {word} », c'est un masque de remplacement ! Donc tu dois le laisser tel quel (non traduit, non personnalisé),
-# Sinon, le plugin ne sera pas capable de substituer la value dynamique qu'il représente.
+# Mais tu peux aussi le décider de retirer la partie {prefix} dans des messages spécifiques.
# Message affiché quand la commande reload est utilisée
configuration_reloaded: "{prefix}&aConfiguration rechargée"
@@ -74,6 +83,10 @@ interface_failed_auto_open_give_failed: "{prefix}&eL'interface ne peut être ouv
# Au cas où l'interface sous forme de livre ne peut être ouverte automatiquement et que le livre n'a pu être donné car le joueur doit attendre
interface_book_give_cooldown: "{prefix}&cTu dois attendre {delay} avant qu'un autre livre-interface soit disponible !"
+# Messages affichés lors de l'utilisation de la commande /scompass-toggle
+toggle_state_saved: "{prefix}&6Réglages de boussole mis de côté."
+toggle_state_restored: "{prefix}&6Réglages de boussole restauré."
+
# Tu peux définir tes propres noms d'action ici (choisis quelque chose de court et compréhensible pour les joueurs)
actions:
ACCEPT: accepter
diff --git a/src/plugin.yml b/src/main/resources/plugin.yml
similarity index 85%
rename from src/plugin.yml
rename to src/main/resources/plugin.yml
index 42f538c..4f9379f 100644
--- a/src/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,19 +1,18 @@
-name: SimpleCompass
-description: Simple compass to help player who don't have sense of direction.
-version: 1.0
+name: ${project.name}
+description: ${project.description}
+version: ${project.version}
author: arboriginal
-website: https://www.spigotmc.org/resources/simplecompass.63140/
-dev-url: https://github.com/arboriginal/SimpleCompass
+website: https://www.spigotmc.org/resources/${spigot-id}/
+dev-url: https://github.com/arboriginal/${project.artifactId}
-depend: []
-softdepend: []
+main: ${project.groupId}.${project.artifactId}.${spigot-main}
+api-version: ${spigot-api}
-api-version: 1.13
+depend: [ ]
+softdepend: [ ]
database: false
-main: me.arboriginal.SimpleCompass.plugin.SimpleCompass
-
commands:
scompass:
description: Use the SimpleCompass interface
@@ -24,6 +23,11 @@ commands:
permission: scompass.reload
aliases: screload
+ scompass-toggle:
+ description: Quickly toggle ON/OFF your compass(es)
+ permission: scompass.toggle
+ aliases: sctoggle
+
scompass-option:
description: Choose personal options for SimpleCompass
permission: scompass.option
@@ -57,6 +61,7 @@ permissions:
scompass.use: true
scompass.use.*: true
scompass.use.free: true
+ scompass.toggle: true
scompass.option: true
scompass.option.*: true
scompass.track.*: true
@@ -69,6 +74,10 @@ permissions:
description: Allows to display the command help.
default: true
+ scompass.toggle: # Technically works without scompass.use, but have no interest without...
+ description: Allows to use /scompass-toggle command.
+ default: true
+
scompass.use.*:
description: Allows to use all compass types.
default: false
diff --git a/src/me/arboriginal/SimpleCompass/commands/AbstractCommand.java b/src/me/arboriginal/SimpleCompass/commands/AbstractCommand.java
deleted file mode 100644
index b22828c..0000000
--- a/src/me/arboriginal/SimpleCompass/commands/AbstractCommand.java
+++ /dev/null
@@ -1,251 +0,0 @@
-package me.arboriginal.SimpleCompass.commands;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.bukkit.entity.Player;
-import com.google.common.collect.ImmutableMap;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
-import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TrackingActions;
-
-public abstract class AbstractCommand {
- protected SimpleCompass sc;
- protected String mainCommand;
- protected Map subCommands = new HashMap();
-
- public enum SubCmds {
- OPTION, TRACK,
- }
-
- public enum CompassOptions {
- ALWAYS, ELYTRA, ELYTRA_VEHICLE, VEHICLE, DISABLED,
- }
-
- //-----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public AbstractCommand(SimpleCompass plugin, String command) {
- sc = plugin;
- mainCommand = command;
- }
-
- //-----------------------------------------------------------------------------------------------
- // Abstract methods
- // ----------------------------------------------------------------------------------------------
-
- protected abstract void showOptions(Player player, CompassTypes modified);
-
- // ----------------------------------------------------------------------------------------------
- // Public methods > Options
- // ----------------------------------------------------------------------------------------------
-
- public CompassTypes modifyOption(Player player, CompassTypes type, String value) {
- if (value.equals(CompassModes.MODE180.toString()) || value.equals(CompassModes.MODE360.toString()))
- sc.datas.compassModeSet(player, type, CompassModes.valueOf(value));
- else
- sc.datas.compassOptionSet(player, type, CompassOptions.valueOf(value));
- return type;
- }
-
- public boolean performCommandOption(Player player, String[] args) {
- if (!hasAccessOption(player, args, true)) return false;
-
- CompassTypes modified = null;
-
- switch (args.length) {
- case 0:
- break;
-
- case 1:
- if (allowedOptions(player).size() != 1) {
- sc.sendMessage(player, "missing_type");
- return false;
- }
-
- modified = modifyOption(player, CompassTypes.valueOf(args[1]), allowedOptions(player).get(0));
- break;
-
- case 2:
- modified = modifyOption(player, CompassTypes.valueOf(args[1]), args[0]);
- break;
-
- default:
- return false;
- }
-
- showOptions(player, modified);
-
- return true;
- }
-
- // ----------------------------------------------------------------------------------------------
- // Protected methods > Options
- // ----------------------------------------------------------------------------------------------
-
- protected List completeCommandOption(Player player, String[] args) {
- switch (args.length) {
- case 1:
- return getFilteredList(allowedOptions(player), args[0]);
-
- case 2:
- if (allowedOptions(player).contains(args[0])) return getFilteredList(allowedTypes(player), args[1]);
- }
-
- return null;
- }
-
- protected List allowedOptions(Player player) {
- List list = new ArrayList<>();
-
- for (CompassOptions option : CompassOptions.values())
- if (player.hasPermission("scompass.option." + option)) list.add(option.toString());
-
- for (CompassModes mode : CompassModes.values()) list.add(mode.toString());
-
- return list;
- }
-
- protected List allowedTypes(Player player) {
- List list = new ArrayList();
-
- for (CompassTypes type : CompassTypes.values()) if (player.hasPermission("scompass.use." + type)) list.add(type);
-
- return list;
- }
-
- protected boolean hasAccessOption(Player player, String[] args, boolean showError) {
- if (args.length > 2 || allowedOptions(player).isEmpty() || allowedTypes(player).isEmpty()) {
- if (showError) sc.sendMessage(player, "wrong_usage");
- return false;
- }
-
- if (args.length > 0 && !allowedOptions(player).contains(args[0])) {
- if (showError) sc.sendMessage(player, "invalid_option", ImmutableMap.of("option", args[0]));
- return false;
- }
-
- if (args.length > 1 && !isAllowedTypes(player, args[1])) {
- if (showError) sc.sendMessage(player, "invalid_type", ImmutableMap.of("type", args[0]));
- return false;
- }
-
- return true;
- }
-
- protected boolean isAllowedTypes(Player player, String value) {
- for (CompassTypes type : allowedTypes(player)) if (type.toString().equalsIgnoreCase(value)) return true;
-
- return false;
- }
-
- // ----------------------------------------------------------------------------------------------
- // Public methods > Trackers
- // ----------------------------------------------------------------------------------------------
-
- public boolean performCommandTrack(Player player, String[] args) {
- HashMap cmdArgs = getTrackArguments(player, args);
- AbstractTracker tracker = (AbstractTracker) cmdArgs.get("tracker");
- TrackingActions action = (TrackingActions) cmdArgs.get("action");
- String target = (String) cmdArgs.get("target");
-
- if (tracker == null) {
- sc.sendMessage(player, "wrong_usage");
- return false;
- }
-
- if (action == null) {
- List list = tracker.list(player, null, "");
-
- if (list == null || list.isEmpty())
- tracker.sendMessage(player, "list_empty");
- else
- tracker.sendMessage(player, "list", ImmutableMap.of("list", String.join(", ", list)));
-
- return true;
- }
-
- if (action.equals(TrackingActions.HELP) && args.length == 2 && player.hasPermission("scompass.help")) {
- String help = tracker.help(player,
- mainCommand + (subCommands.containsKey(SubCmds.TRACK) ? " " + subCommands.get(SubCmds.TRACK) : ""));
-
- if (help != null && !help.isEmpty()) player.sendMessage(help);
-
- return true;
- }
-
- return tracker.perform(player, "sctrack", action, target, args);
- }
-
- // ----------------------------------------------------------------------------------------------
- // Protected methods > Trackers
- // ----------------------------------------------------------------------------------------------
-
- protected List completeCommandTrack(Player player, String[] args) {
- HashMap parsed = getTrackArguments((Player) player, args);
-
- if (args.length == 1) return sc.targets.getTrackersList((Player) player, args[0]);
-
- AbstractTracker tracker = (AbstractTracker) parsed.get("tracker");
-
- if (tracker != null) return tracker.commandSuggestions((Player) player, args, parsed);
-
- return null;
- }
-
- protected List getActionsAvailable(Player player, String trackerID) {
- if (!sc.targets.canUseTracker(player, trackerID)) return null;
- return sc.trackers.get(trackerID).getActionsAvailable(player, false);
- }
-
- protected HashMap getTrackArguments(Player player, String[] args) {
- HashMap parsed = new HashMap();
-
- if (args.length == 0) return parsed;
-
- AbstractTracker tracker = sc.targets.getTrackerByName(args[0]);
-
- if (tracker == null || !sc.targets.getAvailableTrackers(player).contains(tracker.trackerID()))
- return parsed;
-
- parsed.put("tracker", tracker);
-
- if (args.length == 1) return parsed;
-
- tracker.parseArguments(player, args, parsed);
-
- return parsed;
- }
-
- // ----------------------------------------------------------------------------------------------
- // Utils methods
- // ----------------------------------------------------------------------------------------------
-
- protected ImmutableMap clickableOption(CompassTypes type, Object option, Object selected) {
- String optionType = (option instanceof CompassModes) ? "modes" : "options";
- String optionName = sc.locale.getString(optionType + "." + option);
-
- return ImmutableMap.of(
- "text", sc.prepareMessage("commands." + mainCommand + ".options."
- + (option.toString().equals(selected.toString()) ? "active" : "inactive"),
- ImmutableMap.of("option", optionName)),
- "hover", sc.prepareMessage("commands." + mainCommand + ".options.hover",
- ImmutableMap.of("option", optionName, "type", sc.locale.getString("types." + type))),
- "click",
- "/" + mainCommand + (subCommands.containsKey(SubCmds.OPTION) ? " " + subCommands.get(SubCmds.OPTION) : "")
- + " " + option + " " + type);
- }
-
- protected List getFilteredList(List> inputList, String startWith) {
- List list = new ArrayList();
-
- for (Object candidate : inputList)
- if (candidate.toString().toLowerCase().startsWith(startWith.toLowerCase())) list.add(candidate.toString());
-
- return list;
- }
-}
diff --git a/src/me/arboriginal/SimpleCompass/commands/InterfaceCommand.java b/src/me/arboriginal/SimpleCompass/commands/InterfaceCommand.java
deleted file mode 100644
index 2819f2d..0000000
--- a/src/me/arboriginal/SimpleCompass/commands/InterfaceCommand.java
+++ /dev/null
@@ -1,437 +0,0 @@
-package me.arboriginal.SimpleCompass.commands;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.bukkit.Material;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.bukkit.command.TabCompleter;
-import org.bukkit.conversations.Conversation;
-import org.bukkit.conversations.ConversationContext;
-import org.bukkit.conversations.Prompt;
-import org.bukkit.conversations.StringPrompt;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.BookMeta;
-import org.bukkit.scheduler.BukkitRunnable;
-import com.google.common.collect.ImmutableMap;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
-import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
-import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TargetSelector;
-import me.arboriginal.SimpleCompass.plugin.AbstractTracker.TrackingActions;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-import me.arboriginal.SimpleCompass.utils.NMSUtil;
-import net.md_5.bungee.api.chat.BaseComponent;
-import net.md_5.bungee.api.chat.TextComponent;
-
-public class InterfaceCommand extends AbstractCommand implements CommandExecutor, TabCompleter {
- private static final String SELECT_TARGET = "*S313C7:74R637*", SELECT_PAGER = "*S313C7:P463R*";
- //-----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public InterfaceCommand(SimpleCompass plugin) {
- super(plugin, "scompass");
-
- subCommands.put(SubCmds.OPTION, plugin.locale.getString("subcommands." + SubCmds.OPTION));
-
- if (!sc.trackers.isEmpty())
- subCommands.put(SubCmds.TRACK, plugin.locale.getString("subcommands." + SubCmds.TRACK));
- }
-
- // ----------------------------------------------------------------------------------------------
- // CommandExecutor methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- if (!(sender instanceof Player))
- sc.sendMessage(sender, "command_only_for_players");
- else if (((Player) sender).isSleeping()) {
- sc.sendMessage(sender, "command_no_sleeping");
- return true;
- }
- else if (args.length == 0)
- showOptions((Player) sender, null);
- else if (args[0].equals(SELECT_TARGET))
- targetSelector((Player) sender, subArgs(args));
- else if (subCommand((Player) sender, SubCmds.OPTION, args[0]))
- return performCommandOption((Player) sender, subArgs(args));
- else if (subCommand((Player) sender, SubCmds.TRACK, args[0]))
- return performCommandTrack((Player) sender, subArgs(args));
- else {
- sc.sendMessage(sender, "wrong_usage");
- return false;
- }
-
- return true;
- }
-
- // ----------------------------------------------------------------------------------------------
- // CommandUtil methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public void showOptions(Player player, CompassTypes modified) {
- if (modified != null) sc.sendMessage(player, "commands." + mainCommand + ".saved");
-
- ItemStack book = buildInterface(player, modified);
-
- if (sc.config.getBoolean("interface.give_book_everytime") || !NMSUtil.openBook(player, book))
- giveBook(player, book);
- }
-
- // ----------------------------------------------------------------------------------------------
- // TabCompleter methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
- if (args.length == 0 || !(sender instanceof Player)) return null;
-
- if (args.length == 1) {
- List list = new ArrayList();
-
- for (SubCmds subCommand : subCommands.keySet()) {
- String subCommandName = subCommands.get(subCommand);
-
- if (subCommandName.toLowerCase().startsWith(args[0].toLowerCase())) list.add(subCommandName);
- }
-
- return list;
- }
- else if (subCommand((Player) sender, SubCmds.OPTION, args[0]))
- return completeCommandOption((Player) sender, Arrays.stream(args).skip(1).toArray(String[]::new));
- else if (subCommand((Player) sender, SubCmds.TRACK, args[0]))
- return completeCommandTrack((Player) sender, Arrays.stream(args).skip(1).toArray(String[]::new));
- else {
- sc.sendMessage(sender, "wrong_usage");
- return null;
- }
- }
-
- // ----------------------------------------------------------------------------------------------
- // Private methods
- // ----------------------------------------------------------------------------------------------
-
- private ItemStack buildInterface(Player player, CompassTypes modified) {
- ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
- BookMeta meta = bookMeta(book, "options");
-
- List types = allowedTypes(player);
- List opts = allowedOptions(player);
-
- if (modified != null) buildInterfaceOptions(player, meta, types, opts, modified);
- if (player.hasPermission("scompass.track")) buildInterfaceTracking(player, meta);
- if (modified == null) buildInterfaceOptions(player, meta, types, opts, modified);
-
- book.setItemMeta(meta);
- return book;
- }
-
- private void buildInterfaceOptions(
- Player player, BookMeta meta, List types, List opts, CompassTypes modified) {
- if (!player.hasPermission("scompass.option")) return;
-
- if (modified != null) {
- types.remove(modified);
- meta.spigot().addPage(buildPage(player, modified, opts));
- }
-
- for (CompassTypes type : types) meta.spigot().addPage(buildPage(player, type, opts));
- }
-
- private void buildInterfaceTracking(Player player, BookMeta meta) {
- List trackers = new ArrayList();
- Set ordered = sc.locale.getConfigurationSection(
- "commands." + mainCommand + ".track.buttons").getKeys(false);
-
- for (String trackerID : sc.targets.trackersPriority)
- if (player.hasPermission("scompass.track." + trackerID)) trackers.add(trackerID);
-
- for (String[] part : chunk(trackers.toArray(new String[trackers.size()]),
- sc.locale.getInt("commands." + mainCommand + ".track.per_page"))) {
- ArrayList content = new ArrayList();
- content.add(new TextComponent(sc.prepareMessage("commands." + mainCommand + ".header") + "\n\n"));
-
- for (String trackerID : part) {
- AbstractTracker tracker = sc.trackers.get(trackerID);
- if (tracker == null) continue;
-
- Map> commands = new LinkedHashMap>();
- List actions = tracker.getActionsAvailable(player, false);
-
- for (String actionName : ordered) { // @formatter:off
- TrackingActions action;
- try { action = TrackingActions.valueOf(actionName); } catch (Exception e) { continue; }
- String text = sc.prepareMessage("commands." + mainCommand + ".track.buttons." + action + ".text");
-
- if (!actions.contains(action)) {
- commands.put("{" + action + "}", ImmutableMap.of("text", inactiveCommandText(text)));
- continue;
- }
-
- commands.put("{" + action + "}", ImmutableMap.of(
- "text", text,
- "hover", sc.prepareMessage("commands." + mainCommand + ".track.buttons." + action + ".hover"),
- "click", "/" + mainCommand
- + " " + (!tracker.requireTarget(action).equals(TargetSelector.NONE)
- ? SELECT_TARGET : subCommands.get(SubCmds.TRACK))
- + " " + tracker.trackerName() + " " + tracker.getActionName(action)));
- } // @formatter:on
-
- content.add(sc.createClickableMessage(sc.prepareMessage("commands." + mainCommand + ".track.content",
- ImmutableMap.of("tracker", tracker.trackerName(), "buttons", String.join(" ", commands.keySet())))
- + "\n", commands));
- }
-
- meta.spigot().addPage(content.stream().toArray(BaseComponent[]::new));
- }
- }
-
- private String inactiveCommandText(String text) {
- String[] inactiveText = text.replace("&", "§").split("§");
- String out = "§7";
-
- for (int i = 0; i < inactiveText.length; i++)
- if (inactiveText[i].length() > 1)
- out += (inactiveText[i].charAt(0) == 'l') ? "§" + inactiveText[i] + "§7" : inactiveText[i].substring(1);
-
- return out;
- }
-
- private BaseComponent[] buildPage(Player player, CompassTypes type, List optionsList) {
- CompassModes typeMode = sc.datas.compassModeGet(player, type);
- CompassOptions selected = sc.datas.compassOptionGet(player, type);
- ArrayList content = new ArrayList();
- Map> commands = new LinkedHashMap>();
-
- content.add(new TextComponent(sc.prepareMessage("commands." + mainCommand + ".header")));
-
- for (CompassModes mode : CompassModes.values())
- commands.put("{" + mode + "}", clickableOption(type, mode, typeMode));
-
- content.add(sc.createClickableMessage(sc.prepareMessage("commands." + mainCommand + ".content",
- ImmutableMap.of("type", sc.locale.getString("types." + type))), commands));
- content.add(new TextComponent("\n" + sc.prepareMessage("commands." + mainCommand + ".footer") + "\n"));
-
- commands = new LinkedHashMap>();
-
- for (String option : optionsList) {
- if (option.equals(CompassModes.MODE180.toString()) || option.equals(CompassModes.MODE360.toString())) continue;
-
- commands.put("{" + option + "}", clickableOption(type, option, selected));
- }
-
- content.add(sc.createClickableMessage(String.join("\n", commands.keySet()), commands));
-
- return content.stream().toArray(BaseComponent[]::new);
- }
-
- private void giveBook(Player player, ItemStack book) {
- if (!sc.config.getBoolean("interface.give_book_everytime")
- && !sc.config.getBoolean("interface.give_book_on_fail")) {
- sc.sendMessage(player, "interface_failed_auto_open");
- return;
- }
-
- long cooldown = sc.datas.cooldownBookGet(player);
-
- if (cooldown > 0) {
- sc.sendMessage(player, "interface_book_give_cooldown", ImmutableMap.of("delay", "" + sc.formatTime(cooldown)));
- return;
- }
-
- int slot = player.getInventory().firstEmpty();
-
- if (slot == -1) {
- sc.sendMessage(player, "interface_failed_auto_open_give_failed");
- return;
- }
-
- player.getInventory().setItem(slot, book);
- sc.datas.cooldownBookSet(player);
- sc.sendMessage(player, "interface_failed_auto_open_give");
- }
-
- private boolean subCommand(Player player, SubCmds command, String argument) {
- return subCommands.get(command) != null
- && argument.toLowerCase().equals(subCommands.get(command).toLowerCase())
- && player.hasPermission("scompass." + command.toString().toLowerCase())
- && (command.equals(SubCmds.OPTION) || !sc.trackers.isEmpty());
- }
-
- private void targetSelector(Player player, String[] args) {
- HashMap cmdArgs = getTrackArguments(player, args); // @formatter:off
- if (cmdArgs.get("tracker") == null || cmdArgs.get("action") == null) return;
- AbstractTracker tracker = (AbstractTracker) cmdArgs.get("tracker");
- TrackingActions action = (TrackingActions) cmdArgs.get("action"); // @formatter:on
-
- switch (tracker.requireTarget(action)) {
- case ACTIVE:
- targetSelectorList(player, tracker.activeTargets(player, ""), args);
- break;
-
- case AVAILABLE:
- targetSelectorList(player, tracker.availableTargets(player, ""), args);
- break;
-
- case NEW:
- targetSelectorNew(player, args, false);
- break;
-
- case NEWCOORDS:
- targetSelectorNew(player, args, true);
- break;
-
- default:
- break;
- }
- }
-
- private void targetSelectorFallback(Player player, List pages, String args[]) {
- int pager = 0; // @formatter:off
- if (args.length > 3 && args[2].equals(SELECT_PAGER))
- try { pager = Integer.parseInt(args[3]); } catch (Exception e) {}
-
- player.spigot().sendMessage(pages.get(pager)); if (pages.size() == 1) return;
-
- String command = "/" + mainCommand + " " + SELECT_TARGET + " " + args[0] + " " + args[1] + " " + SELECT_PAGER;
- Map> commands = new LinkedHashMap>();
-
- if (pager > 0)
- commands.put("{prev}", ImmutableMap.of(
- "text", sc.prepareMessage("commands." + mainCommand + ".targets.prev.title"),
- "hover", sc.prepareMessage("commands." + mainCommand + ".targets.prev.hover"),
- "click", command + " " + (pager - 1)));
- if (pager < pages.size() - 1)
- commands.put("{next}", ImmutableMap.of(
- "text", sc.prepareMessage("commands." + mainCommand + ".targets.next.title"),
- "hover", sc.prepareMessage("commands." + mainCommand + ".targets.next.hover"),
- "click", command + " " + (pager + 1)));
- // @formatter:on
- if (!commands.isEmpty()) player.spigot().sendMessage(
- sc.createClickableMessage(" " + String.join(" ", commands.keySet()) + "\n", commands));
- }
-
- private void targetSelectorList(Player player, List targets, String[] args) {
- ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
- BookMeta meta = bookMeta(book, "targets");
- String head = sc.prepareMessage("commands." + mainCommand + ".targets.header") + "\n\n";
- String command = "/" + mainCommand + " " + subCommands.get(SubCmds.TRACK) + " " + String.join(" ", args);
-
- List pages = targetSelectorPages(targets, new TextComponent(head), command);
- for (BaseComponent[] page : pages) meta.spigot().addPage(page);
- book.setItemMeta(meta);
-
- if (!NMSUtil.openBook(player, book)) targetSelectorFallback(player, pages, args);
- }
-
- private List targetSelectorPages(List targets, BaseComponent head, String command) {
- List pages = new ArrayList();
-
- for (String[] part : chunk(targets.toArray(new String[targets.size()]),
- sc.locale.getInt("commands." + mainCommand + ".targets.per_page"))) {
- BaseComponent[] page = new BaseComponent[part.length + 1];
- page[0] = head;
-
- for (int i = 0; i < part.length; i++) {
- Map> commands = new LinkedHashMap>();
-
- commands.put("{cmd}", ImmutableMap.of(
- "text", sc.prepareMessage("commands." + mainCommand + ".targets.content",
- ImmutableMap.of("target", part[i])),
- "hover", sc.prepareMessage("commands." + mainCommand + ".targets.hover",
- ImmutableMap.of("target", part[i])),
- "click", command + " " + part[i]));
-
- page[i + 1] = sc.createClickableMessage("{cmd}" + "\n", commands);
- }
-
- pages.add(page);
- }
-
- if (pages.isEmpty()) pages.add(new BaseComponent[] { head,
- new TextComponent(sc.prepareMessage("commands." + mainCommand + ".targets.no_targets")) });
-
- return pages;
- }
-
- private void targetSelectorNew(Player player, String[] args, boolean coords) {
- String cancel = sc.locale.getString("commands." + mainCommand + ".targets.new.cancel");
-
- Conversation conv = new Conversation(sc, player, new StringPrompt() {
- @Override
- public String getPromptText(ConversationContext paramConversationContext) {
- return sc.prepareMessage("commands." + mainCommand + ".targets.new.name_" + (coords ? "coords" : "only"),
- ImmutableMap.of("word", cancel));
- }
-
- @Override
- public Prompt acceptInput(ConversationContext paramConversationContext, String paramString) {
- new BukkitRunnable() {
- @Override
- public void run() {
- if (this.isCancelled()) return;
- if (paramString.equals(cancel))
- player.sendMessage(sc.prepareMessage("commands." + mainCommand + ".targets.new.cancelled"));
- else
- player.performCommand(mainCommand + " " + subCommands.get(SubCmds.TRACK)
- + " " + String.join(" ", args) + " " + paramString);
- this.cancel();
- }
- }.runTaskLater(sc, sc.config.getInt("delays.target_cancel"));
-
- return END_OF_CONVERSATION;
- }
- });
-
- conv.setLocalEchoEnabled(false);
- player.beginConversation(conv);
- }
-
- // ----------------------------------------------------------------------------------------------
- // Utils methods
- // ----------------------------------------------------------------------------------------------
-
- private BookMeta bookMeta(ItemStack book, String type) {
- BookMeta meta = (BookMeta) book.getItemMeta();
- meta.setTitle(/* */sc.prepareMessage("commands." + mainCommand + ".books." + type + ".title"));
- meta.setAuthor(/**/sc.prepareMessage("commands." + mainCommand + ".books." + type + ".author"));
-
- ArrayList lore = new ArrayList();
- for (String row : sc.locale.getStringList("commands." + mainCommand + ".books." + type + ".lore"))
- lore.add(sc.formatMessage(row));
-
- meta.setLore(lore);
- return meta;
- }
-
- private List chunk(String[] input, int number) {
- List parts = new ArrayList();
- int left = input.length;
-
- while (left > 0) {
- int size = Math.min(left, number);
- String[] part = new String[size];
-
- System.arraycopy(input, input.length - left, part, 0, size);
- parts.add(part);
-
- left -= size;
- }
-
- return parts;
- }
-
- private String[] subArgs(String[] args) {
- return Arrays.stream(args).skip(1).toArray(String[]::new);
- }
-}
diff --git a/src/me/arboriginal/SimpleCompass/commands/OptionCommand.java b/src/me/arboriginal/SimpleCompass/commands/OptionCommand.java
deleted file mode 100644
index b313d26..0000000
--- a/src/me/arboriginal/SimpleCompass/commands/OptionCommand.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package me.arboriginal.SimpleCompass.commands;
-
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.bukkit.command.TabCompleter;
-import org.bukkit.entity.Player;
-import com.google.common.collect.ImmutableMap;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassModes;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-
-public class OptionCommand extends AbstractCommand implements CommandExecutor, TabCompleter {
- //-----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public OptionCommand(SimpleCompass plugin) {
- super(plugin, "scoption");
- }
-
- // ----------------------------------------------------------------------------------------------
- // CommandExecutor methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- if (!(sender instanceof Player)) {
- sc.sendMessage(sender, "command_only_for_players");
- return true;
- }
- else if (((Player) sender).isSleeping()) {
- sc.sendMessage(sender, "command_no_sleeping");
- return true;
- }
-
- return performCommandOption((Player) sender, args);
- }
-
- // ----------------------------------------------------------------------------------------------
- // TabCompleter methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
- if (sender instanceof Player) return completeCommandOption((Player) sender, args);
-
- return null;
- }
-
- // ----------------------------------------------------------------------------------------------
- // SimpleCompassOptions methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public void showOptions(Player player, CompassTypes modified) {
- sc.sendMessage(player, "commands." + mainCommand + ".header");
-
- for (CompassTypes type : allowedTypes(player)) {
- CompassModes typeMode = sc.datas.compassModeGet(player, type);
- CompassOptions selected = sc.datas.compassOptionGet(player, type);
- Map> commands = new LinkedHashMap>();
-
- for (CompassModes mode : CompassModes.values())
- commands.put("{" + mode + "}", clickableOption(type, mode, typeMode));
-
- player.spigot().sendMessage(
- sc.createClickableMessage(sc.prepareMessage("commands." + mainCommand + ".content",
- ImmutableMap.of("type", sc.locale.getString("types." + type))), commands));
-
- commands = new LinkedHashMap>();
-
- for (String option : allowedOptions(player)) {
- if (option.equals(CompassModes.MODE180.toString()) || option.equals(CompassModes.MODE360.toString())) continue;
-
- commands.put("{" + option + "}", clickableOption(type, option, selected));
- }
-
- player.spigot().sendMessage(sc.createClickableMessage(String.join("", commands.keySet()), commands));
- }
-
- sc.sendMessage(player, "commands." + mainCommand + ".footer");
-
- if (modified != null) sc.sendMessage(player, "commands." + mainCommand + ".saved");
- }
-}
diff --git a/src/me/arboriginal/SimpleCompass/commands/TrackCommand.java b/src/me/arboriginal/SimpleCompass/commands/TrackCommand.java
deleted file mode 100644
index 428f8d6..0000000
--- a/src/me/arboriginal/SimpleCompass/commands/TrackCommand.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package me.arboriginal.SimpleCompass.commands;
-
-import java.util.List;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.bukkit.command.TabCompleter;
-import org.bukkit.entity.Player;
-import me.arboriginal.SimpleCompass.compasses.AbstractCompass.CompassTypes;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-
-public class TrackCommand extends AbstractCommand implements CommandExecutor, TabCompleter {
- // ----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public TrackCommand(SimpleCompass plugin) {
- super(plugin, "sctrack");
- }
-
- // ----------------------------------------------------------------------------------------------
- // CommandExecutor methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- if (!(sender instanceof Player)) {
- sc.sendMessage(sender, "command_only_for_players");
- return true;
- }
- else if (((Player) sender).isSleeping()) {
- sc.sendMessage(sender, "command_no_sleeping");
- return true;
- }
-
- return performCommandTrack((Player) sender, args);
- }
-
- // ----------------------------------------------------------------------------------------------
- // TabCompleter methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
- if (sender instanceof Player) return completeCommandTrack((Player) sender, args);
-
- return null;
- }
-
- //----------------------------------------------------------------------------------------------
- // CommandUtil methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public void showOptions(Player player, CompassTypes modified) {}
-}
diff --git a/src/me/arboriginal/SimpleCompass/compasses/AbstractCompass.java b/src/me/arboriginal/SimpleCompass/compasses/AbstractCompass.java
deleted file mode 100644
index 371868c..0000000
--- a/src/me/arboriginal/SimpleCompass/compasses/AbstractCompass.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package me.arboriginal.SimpleCompass.compasses;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import org.bukkit.Location;
-import org.bukkit.entity.Player;
-import org.bukkit.scheduler.BukkitRunnable;
-import org.bukkit.util.Vector;
-import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
-import me.arboriginal.SimpleCompass.plugin.AbstractTracker;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-
-public abstract class AbstractCompass {
- protected SimpleCompass sc;
- protected CompassTypes type;
- protected BukkitRunnable task = null;
- protected String warning = "";
-
- public enum CompassTypes {
- ACTIONBAR, BOSSBAR,
- }
-
- public enum CompassModes {
- MODE180, MODE360,
- }
-
- public Player owner;
-
- // ----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public AbstractCompass(SimpleCompass plugin, Player player, CompassTypes compassTypes) {
- sc = plugin;
- type = compassTypes;
- owner = player;
-
- init();
- refresh();
- }
-
- // ----------------------------------------------------------------------------------------------
- // Abstract methods
- // ----------------------------------------------------------------------------------------------
-
- public abstract void display(String datas);
-
- // ----------------------------------------------------------------------------------------------
- // Default methods
- // ----------------------------------------------------------------------------------------------
-
- public void delete() {
- sc.tasks.clear(TasksTypes.REFRESH_STATUS, owner);
-
- if (task != null) task.cancel();
- if (sc.compasses.hasRequiredItems(owner, type, false)) return;
-
- String message = sc.prepareMessage("warnPlayerNoMoreFuel");
- if (message.isEmpty()) return;
-
- warning = message;
- display(warning);
- }
-
- public void init() {}
-
- public void refresh() {
- display(buildCompassDatas());
- }
-
- // ----------------------------------------------------------------------------------------------
- // Private methods
- // ----------------------------------------------------------------------------------------------
-
- private String buildCompassDatas() {
- double rotation = (owner.getEyeLocation().getYaw() - 180) % 360;
-
- if (rotation < 0) rotation += 360;
-
- CompassModes mode = sc.datas.compassModeGet(owner, type);
-
- String prefix = "compass." + type + "." + mode;
- String sep = sc.config.getString(prefix + ".separator_value");
- String cSep = sc.config.getString(prefix + ".separator_color");
- String cOn = sc.config.getString(prefix + ".active_color");
- String cOff = sc.config.getString(prefix + ".inactive_color");
- String nOn = sc.config.getString(prefix + ".active_north_color");
- String nOff = sc.config.getString(prefix + ".north_color");
- String west = sc.config.getString(prefix + ".cardinals.west");
- String north = sc.config.getString(prefix + ".cardinals.north");
- String east = sc.config.getString(prefix + ".cardinals.east");
- String south = sc.config.getString(prefix + ".cardinals.south");
- String datas = sep + "♤" + sep + "♡" + sep + "♢" + sep + "♧";
- int start = (int) Math.round(rotation * datas.length() / 360);
- char face = getFacing();
-
- datas = datas.substring(start) + datas.substring(0, start);
-
- if (mode.equals(CompassModes.MODE180)) {
- int strip = (int) Math.ceil(datas.length() / 4);
- datas = datas.substring(strip - 1, datas.length() - strip + 1);
- }
-
- datas = injectActivatedTrackers(datas, cSep);
-
- return sc.config.getString(prefix + ".before") + cSep
- + datas // @formatter:off
- .replace("♤", ((face == 'W') ? cOn : cOff) + west + cSep)
- .replace("♡", ((face == 'N') ? nOn : nOff) + north + cSep)
- .replace("♢", ((face == 'E') ? cOn : cOff) + east + cSep)
- .replace("♧", ((face == 'S') ? cOn : cOff) + south + cSep)
- + sc.config.getString(prefix + ".after"); // @formatter:on
- }
-
- private char getFacing() {
- try {
- if (owner.getClass().getMethod("myMethodToFind", (Class>[]) null) != null)
- return owner.getFacing().toString().charAt(0);
- }
- catch (Exception e) {}
- // Use this as a fallback for Spigot version (like 1.12) which doesn't support player.getFacing()
- return Arrays.asList('S', 'W', 'N', 'E').get(Math.round(owner.getLocation().getYaw() / 90f) & 0x3);
- }
-
- private HashMap>> getActiveTargets() { // @formatter:off
- String cacheKey = "trackers." + type;
- @SuppressWarnings("unchecked")
- HashMap>> trackers
- = (HashMap>>) sc.cache.get(owner.getUniqueId(), cacheKey);
-
- if (trackers == null) {
- trackers = sc.targets.getTargetsCoords(owner);
- sc.cache.set(owner.getUniqueId(), cacheKey, trackers, sc.config.getInt("delays.trackers_list"));
- }
-
- return trackers;
- } // @formatter:on
-
- private String injectActivatedTrackers(String compass, String sepColor) {
- HashMap>> targets = getActiveTargets();
- if (targets.isEmpty()) return compass;
-
- Location refPos = owner.getEyeLocation();
-
- HashMap placeholders = new HashMap();
-
- for (String trackerID : sc.targets.trackersPriority) {
- AbstractTracker tracker = sc.trackers.get(trackerID);
- if (tracker == null) continue;
- int hlMaxAngle = tracker.settings.getInt("settings.hl_angle", 0);
- String hlMarker = null, hlSymbol = null;
-
- if (hlMaxAngle > 0) {
- hlMarker = tracker.settings.getString("settings.hl_temp", null);
- hlSymbol = tracker.settings.getString("settings.hl_symbol", null);
- if (hlMarker != null && hlSymbol != null) placeholders.put(hlMarker, hlSymbol + sepColor);
- }
-
- for (String targetType : targets.keySet()) {
- ArrayList coords = targets.get(targetType).get(trackerID);
- if (coords == null || coords.isEmpty()) continue;
- boolean active = targetType.equals("on");
-
- String marker = tracker.settings.getString("settings." + (active ? "" : "inactive_") + "temp");
- String symbol = tracker.settings.getString("settings." + (active ? "" : "inactive_") + "symbol");
- placeholders.put(marker, symbol + sepColor);
-
- for (double[] target : coords) {
- Vector blockDirection = new Location(owner.getWorld(), target[0], refPos.getY(), target[1])
- .subtract(refPos).toVector().normalize();
-
- Vector lookAt = refPos.getDirection().setY(0);
- boolean viewable = (lookAt.dot(blockDirection) > 0);
- double angle = Math.toDegrees(blockDirection.angle(lookAt.crossProduct(new Vector(0, 1, 0))));
- String tMarker = marker;
-
- if (!viewable)
- angle = (angle > 90) ? 180 : 0;
- else if (active && hlMarker != null && hlSymbol != null && angle > 90 - hlMaxAngle && angle < 90 + hlMaxAngle)
- tMarker = hlMarker;
-
- int start = compass.length() - (int) Math.round(2 * angle * compass.length() / 360);
-
- compass = (start < 2) ? tMarker + compass.substring(start + 1)
- : compass.substring(0, start - 1) + tMarker + compass.substring(start);
- }
- }
- }
-
- for (String placeholder : placeholders.keySet())
- compass = compass.replaceAll(placeholder, placeholders.get(placeholder));
-
- return compass;
- }
-}
diff --git a/src/me/arboriginal/SimpleCompass/compasses/ActionbarCompass.java b/src/me/arboriginal/SimpleCompass/compasses/ActionbarCompass.java
deleted file mode 100644
index 03bfb46..0000000
--- a/src/me/arboriginal/SimpleCompass/compasses/ActionbarCompass.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package me.arboriginal.SimpleCompass.compasses;
-
-import org.bukkit.entity.Player;
-import org.bukkit.scheduler.BukkitRunnable;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-import net.md_5.bungee.api.ChatMessageType;
-import net.md_5.bungee.api.chat.TextComponent;
-
-public class ActionbarCompass extends AbstractCompass {
- // ----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public ActionbarCompass(SimpleCompass plugin, Player player) {
- super(plugin, player, CompassTypes.ACTIONBAR);
- }
-
- // ----------------------------------------------------------------------------------------------
- // SimpleCompass methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public void display(String datas) {
- owner.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(datas));
- }
-
- @Override
- public void refresh() {
- super.refresh();
-
- if (sc.config.getBoolean("compass.ACTIONBAR.maintain_when_not_moving") && task == null) {
- task = new BukkitRunnable() {
- @Override
- public void run() {
- if (isCancelled()) return;
- refresh();
- }
- };
-
- Long delay = sc.config.getLong("compass.ACTIONBAR.maintain_delay");
-
- task.runTaskTimer(sc, delay, delay);
- }
- }
-}
diff --git a/src/me/arboriginal/SimpleCompass/compasses/BossbarCompass.java b/src/me/arboriginal/SimpleCompass/compasses/BossbarCompass.java
deleted file mode 100644
index 9178b30..0000000
--- a/src/me/arboriginal/SimpleCompass/compasses/BossbarCompass.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package me.arboriginal.SimpleCompass.compasses;
-
-import java.util.Map;
-import org.bukkit.Bukkit;
-import org.bukkit.Material;
-import org.bukkit.boss.BarColor;
-import org.bukkit.boss.BarStyle;
-import org.bukkit.boss.BossBar;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.scheduler.BukkitRunnable;
-import me.arboriginal.SimpleCompass.managers.TaskManager.TasksTypes;
-import me.arboriginal.SimpleCompass.plugin.SimpleCompass;
-
-public class BossbarCompass extends AbstractCompass {
- public BossBar bossbar;
-
- // ----------------------------------------------------------------------------------------------
- // Constructor methods
- // ----------------------------------------------------------------------------------------------
-
- public BossbarCompass(SimpleCompass plugin, Player player) {
- super(plugin, player, CompassTypes.BOSSBAR);
- }
-
- // ----------------------------------------------------------------------------------------------
- // SimpleCompass methods
- // ----------------------------------------------------------------------------------------------
-
- @Override
- public void delete() {
- super.delete();
- sc.tasks.set(TasksTypes.REMOVEWARNING, owner, this);
- }
-
- @Override
- public void display(String datas) {
- bossbar.setTitle(datas);
- bossbar.setProgress(getProgress());
- }
-
- @Override
- public void refresh() {
- super.refresh();
-
- if (sc.config.getBoolean("compass.BOSSBAR.disappear_when_not_moving")) {
- if (task != null) task.cancel();
-
- bossbar.setVisible(true);
-
- task = new BukkitRunnable() {
- @Override
- public void run() {
- if (isCancelled()) return;
- bossbar.setVisible(false);
- this.cancel();
- }
- };
-
- task.runTaskLaterAsynchronously(sc, sc.config.getInt("compass.BOSSBAR.disappear_delay"));
- }
- }
-
- @Override
- public void init() {
- super.init();
-
- bossbar = Bukkit.createBossBar("",
- BarColor.valueOf(sc.config.getString("compass.BOSSBAR.attributes.color")),
- BarStyle.valueOf(sc.config.getString("compass.BOSSBAR.attributes.style")));
-
- bossbar.addPlayer(owner);
- bossbar.setProgress(getProgress());
- bossbar.setVisible(true);
- }
-
- // ----------------------------------------------------------------------------------------------
- // Specific methods
- // ----------------------------------------------------------------------------------------------
-
- private void alterColor(double durability) {
- Object levels = sc.config.get("compass.BOSSBAR.attributes.elytra_durability.levels");
-
- if (levels == null || !(levels instanceof Map)) return;
-
- durability *= 100;
-
- for (Object value : ((Map, ?>) levels).keySet()) {
- if (durability < (int) value) {
- bossbar.setColor(BarColor.valueOf((String) ((Map, ?>) levels).get(value)));
- return;
- }
- }
-
- bossbar.setColor(BarColor.valueOf(sc.config.getString("compass.BOSSBAR.attributes.color")));
- }
-
- @SuppressWarnings("deprecation")
- private int elytraDurabilityOldMethod(ItemStack chestplate) {
- try {
- if (chestplate.getClass().getMethod("getDurability", (Class>[]) null) != null)
- return (int) chestplate.getDurability();
- }
- catch (Exception e) {}
-
- return -1;
- }
-
- private double getProgress() {
- String cacheKey = "compass." + type;
- Double progress = (Double) sc.cache.get(owner.getUniqueId(), cacheKey);
-
- if (progress != null) return progress;
-
- if (/**/sc.config.getBoolean("compass.BOSSBAR.attributes.elytra_durability.wearing")
- || (sc.config.getBoolean("compass.BOSSBAR.attributes.elytra_durability.gliding") && owner.isGliding())) {
- ItemStack chestplate = owner.getInventory().getChestplate();
-
- if (chestplate != null && chestplate.getType().equals(Material.ELYTRA)
- && !chestplate.getItemMeta().isUnbreakable()) {
- Map