├── src └── main │ ├── resources │ ├── config.yml │ └── plugin.yml │ └── java │ └── me │ └── messageofdeath │ └── commandnpc │ ├── Utilities │ ├── IDHolder.java │ ├── CooldownManager │ │ ├── Cooldown.java │ │ └── CooldownManager.java │ ├── BungeeCord │ │ └── BungeeCordUtil.java │ ├── CitizenBackend │ │ └── CitizenCommandRegister.java │ ├── queue │ │ └── QueueSystem.java │ ├── Utilities.java │ └── Metrics │ │ └── Metrics.java │ ├── Database │ ├── ClickType.java │ ├── PluginSettings │ │ ├── PluginConfiguration.java │ │ └── PluginSettings.java │ ├── LanguageSettings │ │ ├── LanguageConfiguration.java │ │ └── LanguageSettings.java │ ├── CommandDatabase.java │ └── YamlDatabase.java │ ├── NPCDataManager │ ├── NPCDataManager.java │ ├── NPCData.java │ └── NPCCommand.java │ ├── commands │ ├── ReloadCommand.java │ └── CitizenCommands.java │ ├── CommandNPC.java │ └── Listeners │ └── NPCListener.java ├── .idea └── libraries │ └── Maven__net_milkbowl_vault_VaultAPI_1_6.xml └── pom.xml /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | ClickType: Both 2 | CoolDown: 20 3 | ExecuteCommandMessage: true 4 | BungeeCord: true 5 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ${project.name} 2 | version: ${project.version} 3 | main: me.messageofdeath.commandnpc.CommandNPC 4 | 5 | author: messageofdeath 6 | 7 | depend: [Citizens] 8 | softdepend: [Vault, PlaceholderAPI] 9 | commands: 10 | commandnpc: 11 | description: Reload this plugin. 12 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__net_milkbowl_vault_VaultAPI_1_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/IDHolder.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities; 2 | 3 | public class IDHolder { 4 | 5 | private int ID; 6 | private Object object; 7 | 8 | public IDHolder(int ID, Object object) { 9 | this.ID = ID; 10 | this.object = object; 11 | } 12 | 13 | public int getID() { 14 | return this.ID; 15 | } 16 | 17 | public Object getObject() { 18 | return this.object; 19 | } 20 | 21 | public void setID(int ID) { 22 | this.ID = ID; 23 | } 24 | 25 | public void setObject(Object object) { 26 | this.object = object; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/CooldownManager/Cooldown.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities.CooldownManager; 2 | 3 | import java.util.UUID; 4 | 5 | public class Cooldown { 6 | 7 | private UUID uuid; 8 | private int npcID; 9 | private int commandID; 10 | 11 | public Cooldown(UUID uuid, int npcID, int commandID) { 12 | this.uuid = uuid; 13 | this.npcID = npcID; 14 | this.commandID = commandID; 15 | } 16 | 17 | public UUID getUuid() { 18 | return uuid; 19 | } 20 | 21 | public int getNpcID() { 22 | return npcID; 23 | } 24 | 25 | public int getCommandID() { 26 | return commandID; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/ClickType.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database; 2 | 3 | public enum ClickType { 4 | 5 | LEFT(new String[] {"punch"}), 6 | 7 | RIGHT(new String[] {"interact"}), 8 | 9 | BOTH(new String[] {"all"}); 10 | 11 | private final String[] alternatives; 12 | 13 | ClickType(String[] alternatives) { 14 | this.alternatives = alternatives; 15 | } 16 | 17 | public static boolean hasClickType(String clickType) { 18 | return ClickType.getClickType(clickType) != null; 19 | } 20 | 21 | public static ClickType getClickType(String clickType) { 22 | for(ClickType type : ClickType.values()) { 23 | if(type.name().equalsIgnoreCase(clickType)) { 24 | return type; 25 | } 26 | for(String alternatives : type.alternatives) { 27 | if(clickType.equalsIgnoreCase(alternatives)) { 28 | return type; 29 | } 30 | } 31 | } 32 | return null; 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/NPCDataManager/NPCDataManager.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.NPCDataManager; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class NPCDataManager { 6 | 7 | private final ArrayList data; 8 | 9 | public NPCDataManager() { 10 | this.data = new ArrayList<>(); 11 | } 12 | 13 | public void addNPCData(NPCData data) { 14 | if(!this.hasNPCData(data.getId())) { 15 | this.data.add(data); 16 | } 17 | } 18 | 19 | public void removeNPCData(int id) { 20 | if(this.hasNPCData(id)) { 21 | this.data.remove(this.getNPCData(id)); 22 | } 23 | } 24 | 25 | public boolean hasNPCData(int id) { 26 | return this.getNPCData(id) != null; 27 | } 28 | 29 | public NPCData getNPCData(int id) { 30 | for(NPCData data : this.data) { 31 | if(data.getId() == id) { 32 | return data; 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | public ArrayList getNPCDatas() { 39 | ArrayList data = new ArrayList<>(); 40 | data.addAll(this.data); 41 | return data; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/BungeeCord/BungeeCordUtil.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities.BungeeCord; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.java.JavaPlugin; 5 | 6 | import com.google.common.io.ByteArrayDataOutput; 7 | import com.google.common.io.ByteStreams; 8 | 9 | import me.messageofdeath.commandnpc.CommandNPC; 10 | 11 | public class BungeeCordUtil { 12 | 13 | public static void setupUtil() { 14 | JavaPlugin plugin = CommandNPC.getInstance(); 15 | plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "BungeeCord"); 16 | } 17 | 18 | public static void disableUtil() { 19 | JavaPlugin plugin = CommandNPC.getInstance(); 20 | plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin, "BungeeCord"); 21 | } 22 | 23 | public static void sendPlayerToServer(Player player, String serverName) { 24 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 25 | out.writeUTF("Connect"); 26 | out.writeUTF(serverName); 27 | player.sendPluginMessage(CommandNPC.getInstance(), "BungeeCord", out.toByteArray()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/CitizenBackend/CitizenCommandRegister.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities.CitizenBackend; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import me.messageofdeath.commandnpc.CommandNPC; 6 | import net.citizensnpcs.api.CitizensAPI; 7 | import net.citizensnpcs.api.command.CommandManager; 8 | 9 | public class CitizenCommandRegister { 10 | 11 | private final CommandNPC instance; 12 | 13 | public CitizenCommandRegister(CommandNPC instance) { 14 | this.instance = instance; 15 | } 16 | 17 | public void registerCitizenCommand(Class classx) { 18 | try { 19 | Field field = CitizensAPI.getPlugin().getClass().getDeclaredField("commands"); 20 | field.setAccessible(true); 21 | Object obj = field.get(CitizensAPI.getPlugin()); 22 | CommandManager manager = (CommandManager) obj; 23 | manager.register(classx); 24 | field.setAccessible(false); 25 | } catch (Exception e) { 26 | this.instance.logError("Citizens", "CitizenCommandRegister", "registerCitizenCommand(Class)", "Couldn't implement commands into citizens! " + "Shutting down!"); 27 | this.instance.getServer().getPluginManager().disablePlugin(this.instance); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/CooldownManager/CooldownManager.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities.CooldownManager; 2 | 3 | import java.util.ArrayList; 4 | import java.util.UUID; 5 | 6 | public class CooldownManager { 7 | 8 | private ArrayList cooldowns; 9 | 10 | public CooldownManager() { 11 | cooldowns = new ArrayList<>(); 12 | } 13 | 14 | public void addCooldown(Cooldown cooldown) { 15 | if(cooldowns.stream().noneMatch(search -> search.getUuid() == cooldown.getUuid())) { 16 | cooldowns.add(cooldown); 17 | } 18 | } 19 | 20 | public void removeCooldown(UUID uuid) { 21 | cooldowns.stream().filter(search -> search.getUuid() == uuid).findFirst().ifPresent(cooldowns::remove); 22 | } 23 | 24 | public boolean hasCooldown(UUID uuid) { 25 | return cooldowns.stream().anyMatch(search -> search.getUuid() == uuid); 26 | } 27 | 28 | public Cooldown getCooldown(UUID uuid) { 29 | return cooldowns.stream().filter(search -> search.getUuid() == uuid).findFirst().orElse(null); 30 | } 31 | 32 | public Cooldown[] getCooldowns(UUID uuid) { 33 | return cooldowns.stream().filter(search -> search.getUuid() == uuid).toArray(Cooldown[]::new); 34 | } 35 | 36 | public ArrayList getCooldowns() { 37 | return cooldowns; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/PluginSettings/PluginConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database.PluginSettings; 2 | 3 | import me.messageofdeath.commandnpc.CommandNPC; 4 | import me.messageofdeath.commandnpc.Database.YamlDatabase; 5 | 6 | public class PluginConfiguration { 7 | 8 | protected final CommandNPC instance; 9 | private YamlDatabase config; 10 | 11 | public PluginConfiguration(CommandNPC instance) { 12 | this.instance = instance; 13 | } 14 | 15 | public void initConfiguration() { 16 | this.config = new YamlDatabase(this.instance, "config", false); 17 | this.config.onStartUp(); 18 | this.config.saveOnSet = false; 19 | boolean changes = false; 20 | for (PluginSettings setting : PluginSettings.values()) { 21 | if (!this.config.contains(setting.getName().replace("_", "."))) { 22 | changes = true; 23 | this.config.set(setting.getName().replace("_", "."), setting.getDefaultSetting()); 24 | } 25 | } 26 | if (changes) { 27 | this.config.save(); 28 | } 29 | this.config.saveOnSet = true; 30 | } 31 | 32 | public YamlDatabase getConfig() { 33 | return this.config; 34 | } 35 | 36 | public void loadConfiguration() { 37 | for (PluginSettings setting : PluginSettings.values()) { 38 | setting.setSetting(this.config.getString(setting.getName().replace("_", "."), setting.getDefaultSetting())); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/LanguageSettings/LanguageConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database.LanguageSettings; 2 | 3 | import me.messageofdeath.commandnpc.CommandNPC; 4 | import me.messageofdeath.commandnpc.Database.YamlDatabase; 5 | 6 | public class LanguageConfiguration { 7 | 8 | protected final CommandNPC instance; 9 | private YamlDatabase config; 10 | 11 | public LanguageConfiguration(CommandNPC instance) { 12 | this.instance = instance; 13 | } 14 | 15 | public void initConfiguration() { 16 | this.config = new YamlDatabase(this.instance, "language", false); 17 | this.config.onStartUp(); 18 | this.config.saveOnSet = false; 19 | boolean changes = false; 20 | for(LanguageSettings setting : LanguageSettings.values()) { 21 | if (!this.config.contains(setting.getName().replace("_", "."))) { 22 | changes = true; 23 | this.config.set(setting.getName().replace("_", "."), setting.getDefaultSetting()); 24 | } 25 | } 26 | if(changes) { 27 | this.config.save(); 28 | } 29 | this.config.saveOnSet = true; 30 | } 31 | 32 | public YamlDatabase getConfig() { 33 | return this.config; 34 | } 35 | 36 | public void loadConfiguration() { 37 | for(LanguageSettings setting : LanguageSettings.values()) { 38 | setting.setSetting(this.config.getString(setting.getName().replace("_", "."), setting.getDefaultSetting())); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/PluginSettings/PluginSettings.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database.PluginSettings; 2 | 3 | import me.messageofdeath.commandnpc.Utilities.Utilities; 4 | 5 | public enum PluginSettings { 6 | 7 | /**-------------------------------------- General --------------------------------------**/ 8 | ClickType("both"), 9 | CoolDown("20"), 10 | ExecuteCommandMessage("false"), 11 | BungeeCord("false"), 12 | CooldownMessage("true"); 13 | 14 | private String setting; 15 | private final String defaultSetting; 16 | 17 | PluginSettings(String defaultSetting) { 18 | this.defaultSetting = defaultSetting; 19 | } 20 | 21 | public String getName() { 22 | return toString(); 23 | } 24 | 25 | public void setSetting(String setting) { 26 | this.setting = setting; 27 | } 28 | 29 | public void setDefaultSetting() { 30 | this.setting = this.defaultSetting; 31 | } 32 | 33 | public String getSetting() { 34 | return this.setting; 35 | } 36 | 37 | public Integer getInteger() { 38 | if(Utilities.isInteger(this.setting)) { 39 | return Integer.parseInt(this.setting); 40 | }else{ 41 | return -1; 42 | } 43 | } 44 | 45 | public Double getDouble() { 46 | if(Utilities.isDouble(this.setting)) { 47 | return Double.parseDouble(this.setting); 48 | }else{ 49 | return -1.0; 50 | } 51 | } 52 | 53 | public boolean getBoolean() { 54 | return Boolean.parseBoolean(this.setting); 55 | } 56 | 57 | public String getDefaultSetting() { 58 | return this.defaultSetting; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/commands/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.commands; 2 | 3 | import net.citizensnpcs.api.util.Messaging; 4 | 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | 9 | import me.messageofdeath.commandnpc.CommandNPC; 10 | import me.messageofdeath.commandnpc.Database.LanguageSettings.LanguageSettings; 11 | import me.messageofdeath.commandnpc.Database.PluginSettings.PluginSettings; 12 | import me.messageofdeath.commandnpc.Utilities.BungeeCord.BungeeCordUtil; 13 | 14 | public class ReloadCommand implements CommandExecutor { 15 | 16 | @Override 17 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 18 | if(args.length == 1) { 19 | if (args[0].equalsIgnoreCase("reload")) { 20 | if(sender.hasPermission("commandnpc.admin") || sender.isOp()) { 21 | if(PluginSettings.BungeeCord.getBoolean()) { 22 | BungeeCordUtil.disableUtil(); 23 | } 24 | CommandNPC.getInstance().reloadConfigX(); 25 | Messaging.send(sender, LanguageSettings.Commands_CmdNPC_Reload.getSetting()); 26 | if(PluginSettings.BungeeCord.getBoolean()) { 27 | BungeeCordUtil.setupUtil(); 28 | } 29 | return true; 30 | }else{ 31 | Messaging.sendError(sender, LanguageSettings.Commands_NoPermission.getSetting()); 32 | } 33 | }else{ 34 | Messaging.sendError(sender, LanguageSettings.Commands_WrongArgs.getSetting()); 35 | } 36 | }else{ 37 | Messaging.sendError(sender, LanguageSettings.Commands_WrongArgs.getSetting()); 38 | } 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/queue/QueueSystem.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities.queue; 2 | 3 | import java.util.LinkedList; 4 | 5 | public class QueueSystem { 6 | 7 | private volatile boolean stop, interrupt; 8 | private final QueueExecutor[] threads; 9 | private final LinkedList queue; 10 | 11 | public QueueSystem(int nThreads) { 12 | queue = new LinkedList<>(); 13 | stop = false; 14 | interrupt = false; 15 | threads = new QueueExecutor[nThreads]; 16 | for(int i = 0; i < nThreads; i++) { 17 | threads[i] = new QueueExecutor(i); 18 | threads[i].start(); 19 | } 20 | } 21 | 22 | public void execute(Runnable r) { 23 | synchronized (queue) { 24 | queue.addLast(r); 25 | queue.notify(); 26 | } 27 | } 28 | 29 | public synchronized void stop() { 30 | stop = true; 31 | if(queue.isEmpty()) { 32 | interrupt = true; 33 | for(Thread thread : threads) { 34 | thread.interrupt(); 35 | } 36 | } 37 | } 38 | 39 | private class QueueExecutor extends Thread { 40 | 41 | final int id; 42 | 43 | private QueueExecutor(int id) { 44 | this.id = id; 45 | } 46 | 47 | public void run() { 48 | synchronized (queue) { 49 | while(!stop || stop && !queue.isEmpty()) { 50 | if(queue.isEmpty()) { 51 | try { 52 | queue.wait(); 53 | } catch (InterruptedException e) { 54 | if (!interrupt) { 55 | e.printStackTrace(); 56 | } 57 | continue; 58 | } 59 | } 60 | try { 61 | queue.removeFirst().run(); 62 | } catch (RuntimeException e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/LanguageSettings/LanguageSettings.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database.LanguageSettings; 2 | 3 | public enum LanguageSettings { 4 | 5 | /**-------------------------------------- General --------------------------------------**/ 6 | General_PluginTag("&8[&2CommandNPC&8] &6"), 7 | General_ErrorTag("&0[&cError&0] &c"), 8 | 9 | /**-------------------------------------- commandnpc NPC --------------------------------------**/ 10 | CmdNPC_Cooldown("You cannot do this yet!"), 11 | CmdNPC_NoMoney("You do not have enough money for this!"), 12 | CmdNPC_Executed("Command Executed."), 13 | 14 | /**-------------------------------------- General Commands --------------------------------------**/ 15 | Commands_NoPermission("You do not have permission for this!"), 16 | Commands_WrongArgs("Wrong Args. Use /commandnpc reload"), 17 | Commands_MustBeNumeric("The argument '%arg' must be numeric!"), 18 | Commands_AlreadyExists("A %type with that name already exists!"), 19 | Commands_DoesNotExist("A %type with that name doesn't exist!"), 20 | Commands_List_Header("Available %type:"), 21 | Commands_List_Line(" &8 -&a %name"), 22 | Commands_List_InfoHeader("Information for NPC '%id':"), 23 | Commands_List_InfoLineHeader(" &7%name: &6%value"), 24 | Commands_List_InfoLinePrefix(" &8- "), 25 | Commands_List_InfoLine("&2%name: &b%value"), 26 | Commands_SetTo_Header("You have set the following variables:"), 27 | Commands_SetTo_Line(" &8- &6%variable to&8: &b%value"), 28 | Commands_SetTo_Nothing(" &8- &4Nothing's changed..."), 29 | 30 | /**-------------------------------------- commandnpc Commands --------------------------------------**/ 31 | Commands_CmdNPC_Reload("You have successfully reloaded the commandnpc configuration file!"), 32 | 33 | /**-------------------------------------- Citizens Commands --------------------------------------**/ 34 | Commands_Citizens_Add("You have successfully added the command to the NPC!"), 35 | Commands_Citizens_Reset("You have successfully reset the commands for this NPC!"), 36 | Commands_Citizens_Removed("You have successfully removed the command!"), 37 | Commands_Citizens_NumBetween("You must enter a number between %num1 and %num2!"), 38 | Commands_Citizens_ValueFlagT("For value flag 't', you must use 'left', 'right', or 'both'!"), 39 | Commands_Citizens_NoCmdInput("You didn't input a command!"), 40 | Commands_Citizens_NoCommands("There are no set commands at the moment for this NPC!"); 41 | 42 | private String setting; 43 | private final String defaultSetting; 44 | 45 | LanguageSettings(String defaultSetting) { 46 | this.defaultSetting = defaultSetting; 47 | } 48 | 49 | public String getName() { 50 | return toString(); 51 | } 52 | 53 | public void setSetting(String setting) { 54 | this.setting = setting; 55 | } 56 | 57 | public void setDefaultSetting() { 58 | this.setting = this.defaultSetting; 59 | } 60 | 61 | public String getSetting() { 62 | return this.setting; 63 | } 64 | 65 | public String getDefaultSetting() { 66 | return this.defaultSetting; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/NPCDataManager/NPCData.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.NPCDataManager; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | 6 | public class NPCData { 7 | 8 | private final int npcID; 9 | private ArrayList commands; 10 | 11 | public NPCData(int npcID) { 12 | this.npcID = npcID; 13 | this.commands = new ArrayList<>(); 14 | } 15 | 16 | public NPCData(int npcID, NPCCommand command) { 17 | this(npcID); 18 | this.addCommand(command); 19 | } 20 | 21 | public NPCData(int npcID, ArrayList commands) { 22 | this(npcID); 23 | for(NPCCommand cmd : commands) { 24 | this.addCommand(cmd); 25 | } 26 | } 27 | 28 | public int getId() { 29 | return this.npcID; 30 | } 31 | 32 | public boolean isRandom() { 33 | return commands.stream().anyMatch(NPCCommand::isRandom); 34 | } 35 | 36 | public void addCommand(NPCCommand command) { 37 | this.commands.add(command); 38 | checkPositions(); 39 | } 40 | 41 | public void removeCommand(int id) { 42 | commands.stream().filter(cmd -> cmd.getID() == id).findFirst().ifPresent(commands::remove); 43 | checkPositions(); 44 | } 45 | 46 | public boolean hasCommand(int id) { 47 | return commands.stream().anyMatch(cmd -> cmd.getID() == id); 48 | } 49 | 50 | public NPCCommand getCommand(int id) { 51 | return commands.stream().filter(cmd -> cmd.getID() == id).findFirst().orElse(null); 52 | } 53 | 54 | public ArrayList getCommands() { 55 | return commands; 56 | } 57 | 58 | private void checkPositions() { 59 | ArrayList commands = this.commands; 60 | commands.sort(comparePosition()); 61 | if (!commands.isEmpty()) { 62 | int lastPosition = 0; 63 | int difference; 64 | NPCCommand commandx; 65 | for (int i = 0; i < commands.size(); i++) { 66 | commandx = commands.get(i); 67 | if (lastPosition != commandx.getID()) { 68 | difference = commandx.getID() - lastPosition; 69 | if (difference > 1) { 70 | for (int x = i; x < commands.size(); x++) { 71 | commands.get(x).setID(commands.get(x).getID() - difference); 72 | } 73 | } 74 | difference = commandx.getID() - lastPosition; 75 | if (difference == 0) { 76 | for (int x = i; x < commands.size(); x++) { 77 | commands.get(x).setID(commands.get(x).getID() + 1); 78 | } 79 | } 80 | lastPosition = commandx.getID(); 81 | }else if(lastPosition == commandx.getID()){ 82 | commandx.setID(commandx.getID() + 1); 83 | lastPosition = commandx.getID(); 84 | } 85 | } 86 | commands.sort(comparePosition()); 87 | this.commands = commands; 88 | } 89 | } 90 | 91 | private static Comparator comparePosition() { 92 | return (cmd1, cmd2) -> { 93 | if (cmd1.getID() > cmd2.getID()) { 94 | return 1; 95 | } 96 | if (cmd1.getID() < cmd2.getID()) { 97 | return -1; 98 | } 99 | return 0; 100 | }; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/NPCDataManager/NPCCommand.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.NPCDataManager; 2 | 3 | import me.messageofdeath.commandnpc.Database.ClickType; 4 | 5 | public class NPCCommand { 6 | 7 | private int id; 8 | private int delay; 9 | private int cooldown; 10 | private String command; 11 | private String permission; 12 | private String cooldownMessage; 13 | private ClickType clickType; 14 | private boolean inConsole; 15 | private boolean asOp; 16 | private boolean isRandom; 17 | private boolean ignorePermMsg; 18 | private boolean ignoreMoneyMsg; 19 | private double cost; 20 | 21 | public NPCCommand(String command, String permission, String cooldownMessage, ClickType clickType, boolean inConsole, boolean asOp, boolean isRandom, boolean ignorePermMsg, 22 | boolean ignoreMoneyMsg, double cost, int delay, int cooldown) { 23 | this(1000, command, permission, cooldownMessage, clickType, inConsole, asOp, isRandom, ignorePermMsg, ignoreMoneyMsg, cost, delay, cooldown); 24 | } 25 | 26 | public NPCCommand(int id, String command, String permission, String cooldownMessage, ClickType clickType, boolean inConsole, boolean asOp, boolean isRandom, boolean ignorePermMsg, 27 | boolean ignoreMoneyMsg, double cost, int delay, int cooldown) { 28 | this.id = id; 29 | this.command = command; 30 | this.permission = permission; 31 | this.cooldownMessage = cooldownMessage; 32 | this.clickType = clickType; 33 | this.inConsole = inConsole; 34 | this.asOp = asOp; 35 | this.isRandom = isRandom; 36 | this.ignorePermMsg = ignorePermMsg; 37 | this.ignoreMoneyMsg = ignoreMoneyMsg; 38 | this.cost = cost; 39 | this.delay = delay; 40 | this.cooldown = cooldown; 41 | } 42 | 43 | public int getID() { 44 | return this.id; 45 | } 46 | 47 | public String getCommand() { 48 | return this.command; 49 | } 50 | 51 | public String getPermission() { 52 | return this.permission; 53 | } 54 | 55 | public String getCooldownMessage() { 56 | return this.cooldownMessage; 57 | } 58 | 59 | public ClickType getClickType() { 60 | return this.clickType; 61 | } 62 | 63 | public boolean inConsole() { 64 | return this.inConsole; 65 | } 66 | 67 | public boolean asOp() { 68 | return this.asOp; 69 | } 70 | 71 | public boolean isRandom() { 72 | return this.isRandom; 73 | } 74 | 75 | public boolean isIgnorePermMsg() { 76 | return this.ignorePermMsg; 77 | } 78 | 79 | public boolean isIgnoreMoneyMsg() { 80 | return this.ignoreMoneyMsg; 81 | } 82 | 83 | public double getCost() { 84 | return this.cost; 85 | } 86 | 87 | public int getDelay() { 88 | return this.delay; 89 | } 90 | 91 | public int getCooldown() { 92 | return this.cooldown; 93 | } 94 | 95 | public void setID(int id) { 96 | this.id = id; 97 | } 98 | 99 | public void setCommand(String command) { 100 | this.command = command; 101 | } 102 | 103 | public void setPermission(String permission) { 104 | this.permission = permission; 105 | } 106 | 107 | public void setCooldownMessage(String cooldownMessage) { 108 | this.cooldownMessage = cooldownMessage; 109 | } 110 | 111 | public void setClickType(ClickType clickType) { 112 | this.clickType = clickType; 113 | } 114 | 115 | public void setInConsole(boolean inConsole) { 116 | this.inConsole = inConsole; 117 | } 118 | 119 | public void setAsOP(boolean asOp) { 120 | this.asOp = asOp; 121 | } 122 | 123 | public void setIsRandom(boolean isRandom) { 124 | this.isRandom = isRandom; 125 | } 126 | 127 | public void setIgnorePermMsg(boolean ignorePermMsg) { 128 | this.ignorePermMsg = ignorePermMsg; 129 | } 130 | 131 | public void setIgnoreMoneyMsg(boolean ignoreMoneyMsg) { 132 | this.ignoreMoneyMsg = ignoreMoneyMsg; 133 | } 134 | 135 | public void setCost(double cost) { 136 | this.cost = cost; 137 | } 138 | 139 | public void setDelay(int delay) { 140 | this.delay = delay; 141 | } 142 | 143 | public void setCooldown(int cooldown) { 144 | this.cooldown = cooldown; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/Utilities.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | 6 | public class Utilities { 7 | 8 | private static final String alpha = "^[a-zA-Z]*$"; 9 | private static final String alphaNumeric = "^[a-zA-Z0-9]*$"; 10 | private static final String intRegex = "\\d+"; 11 | private static final String floatRegex = "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)"; 12 | private static final String Digits = "(\\p{Digit}+)"; 13 | private static final String HexDigits = "(\\p{XDigit}+)"; 14 | private static final String Exp = "[eE][+-]?"+Digits; 15 | private static final String doubleRegex = ("[\\x00-\\x20]*"+"[+-]?("+"NaN|"+"Infinity|"+"((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ 16 | "(\\.("+Digits+")("+Exp+")?)|"+"(("+"(0[xX]"+HexDigits+"(\\.)?)|"+"(0[xX]"+HexDigits+"?(\\.)"+HexDigits+")"+")[pP][+-]?"+Digits+"))"+ 17 | "[fFdD]?))"+"[\\x00-\\x20]*"); 18 | 19 | public static boolean isAlpha(String total) { 20 | return total.matches(alpha); 21 | } 22 | 23 | public static boolean isAlphanumeric(String total) { 24 | return total.matches(alphaNumeric); 25 | } 26 | 27 | public static boolean isNumeric(String total) { 28 | return isInteger(total) || isFloat(total) || isDouble(total); 29 | } 30 | 31 | public static boolean isDouble(String total) { 32 | return total.matches(doubleRegex); 33 | } 34 | 35 | public static boolean isInteger(String total) { 36 | return total.matches(intRegex); 37 | } 38 | 39 | public static boolean isFloat(String total) { 40 | return total.matches(floatRegex); 41 | } 42 | 43 | public static String getOrdinal(int n) { 44 | char[] args = (n + "").toCharArray(); 45 | char last = args[args.length - 1]; 46 | if(last == '1') { 47 | return n + "st"; 48 | }else if(last == '2') { 49 | return n + "nd"; 50 | }else if(last == '3') { 51 | return n + "rd"; 52 | }else{ 53 | return n + "th"; 54 | } 55 | } 56 | 57 | public static String getTime(int time) { 58 | int days = time >= 60*60*24 ? time / (60*60*24) : 0; 59 | time -= days*60*60*24; 60 | int hours = time >= 60*60 ? time / (60*60) : 0; 61 | time -= hours*60*60; 62 | int minutes = time >= 60 ? time / 60 : 0; 63 | time -= minutes*60; 64 | int seconds = time; 65 | return (days != 0 ? days + (days == 1 ? " day" : " days") : "") + (hours != 0 ? hours + (hours == 1 ? " hour" : " hours") : "") + 66 | (minutes != 0 ? minutes + (minutes == 1 ? " minute" : " minutes") : "") + (seconds != 0 ? seconds + (seconds == 1 ? " second" : " seconds") : ""); 67 | } 68 | 69 | public static ArrayList sortIDs(ArrayList holders) { 70 | if (!holders.isEmpty()) { 71 | holders.sort(Utilities.compareIDs()); 72 | int lastPosition = 0; 73 | int difference; 74 | IDHolder holder; 75 | for (int i = 0; i < holders.size(); i++) { 76 | holder = holders.get(i); 77 | if (lastPosition != holder.getID()) { 78 | difference = holder.getID() - lastPosition; 79 | if (difference > 1) { 80 | for (int x = i; x < holders.size(); x++) { 81 | holders.get(x).setID(holders.get(x).getID() - difference); 82 | } 83 | } 84 | difference = holder.getID() - lastPosition; 85 | if (difference == 0) { 86 | for (int x = i; x < holders.size(); x++) { 87 | holders.get(x).setID(holders.get(x).getID() + 1); 88 | } 89 | } 90 | lastPosition = holder.getID(); 91 | }else if(lastPosition == holder.getID()){ 92 | holder.setID(holder.getID() + 1); 93 | lastPosition = holder.getID(); 94 | } 95 | } 96 | holders.sort(Utilities.compareIDs()); 97 | } 98 | return holders; 99 | } 100 | 101 | public static ArrayList setID(int oldID, int newID, ArrayList holders) { 102 | boolean has = false; 103 | for(IDHolder holder : holders) { 104 | if(holder.getID() == oldID) { 105 | has = true; 106 | holder.setID(newID); 107 | continue; 108 | } 109 | if(has) { 110 | holder.setID(holder.getID() + 1); 111 | } 112 | } 113 | return Utilities.sortIDs(holders); 114 | } 115 | 116 | public static int getNextID(ArrayList holders) { 117 | ArrayList ids = new ArrayList<>(); 118 | for (IDHolder holder : holders) { 119 | ids.add(holder.getID()); 120 | } 121 | for(int i = 1; i < ids.size(); i++) { 122 | if (!ids.contains(i)) { 123 | return i; 124 | } 125 | } 126 | return 0; 127 | } 128 | 129 | private static Comparator compareIDs() { 130 | return (holder1, holder2) -> { 131 | if (holder1.getID() > holder2.getID()) { 132 | return 1; 133 | } 134 | if (holder1.getID() < holder2.getID()) { 135 | return -1; 136 | } 137 | return 0; 138 | }; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/CommandDatabase.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database; 2 | 3 | import java.util.ArrayList; 4 | 5 | import me.messageofdeath.commandnpc.CommandNPC; 6 | import me.messageofdeath.commandnpc.Database.PluginSettings.PluginSettings; 7 | import me.messageofdeath.commandnpc.NPCDataManager.NPCCommand; 8 | import me.messageofdeath.commandnpc.NPCDataManager.NPCData; 9 | 10 | public class CommandDatabase { 11 | 12 | private final CommandNPC instance; 13 | private YamlDatabase database; 14 | 15 | public CommandDatabase(CommandNPC instance) { 16 | this.instance = instance; 17 | } 18 | 19 | public void initDatabase() { 20 | database = new YamlDatabase(instance, "commands"); 21 | database.onStartUp(); 22 | } 23 | 24 | public void loadDatabase() { 25 | CommandNPC.getQueueSystem().execute(() -> { 26 | for (String idx : database.getSection("NPCS", new ArrayList<>())) { 27 | NPCData data = new NPCData(Integer.parseInt(idx)); 28 | for (String commands : database.getStringArray("NPCS." + idx + ".Commands", new ArrayList<>())) { 29 | String[] args = commands.split("~~~"); 30 | ClickType clickType; 31 | if (args.length == 13) {//Version 1.9.1 32 | clickType = this.getClickType(args); 33 | data.addCommand(new NPCCommand(Integer.parseInt(args[0]), args[1], args[2], args[4], clickType, Boolean.parseBoolean(args[5]), 34 | Boolean.parseBoolean(args[6]), Boolean.parseBoolean(args[7]), Boolean.parseBoolean(args[11]), Boolean.parseBoolean(args[12]), 35 | Double.parseDouble(args[8]), Integer.parseInt(args[9]), Integer.parseInt(args[10]))); 36 | continue; 37 | }else if (args.length == 11) {//Version 1.9.0 38 | clickType = this.getClickType(args); 39 | data.addCommand(new NPCCommand(Integer.parseInt(args[0]), args[1], args[2], args[4], clickType, Boolean.parseBoolean(args[5]), 40 | Boolean.parseBoolean(args[6]), Boolean.parseBoolean(args[7]), false, false, Double.parseDouble(args[8]), Integer.parseInt(args[9]), Integer.parseInt(args[10]))); 41 | continue; 42 | }else if (args.length == 9) {//Version 1.8.8-1.8.9 43 | clickType = this.getClickType(args); 44 | data.addCommand(new NPCCommand(Integer.parseInt(args[0]), args[1], args[2], "", clickType, Boolean.parseBoolean(args[4]), 45 | Boolean.parseBoolean(args[5]), Boolean.parseBoolean(args[6]), false, false, Double.parseDouble(args[7]), Integer.parseInt(args[8]), 0)); 46 | continue; 47 | }else if (args.length == 8) {//Version 1.8.7 48 | clickType = this.getClickType(args); 49 | data.addCommand(new NPCCommand(Integer.parseInt(args[0]), args[1], args[2], "", clickType, Boolean.parseBoolean(args[4]), 50 | Boolean.parseBoolean(args[5]), false, false, false, Double.parseDouble(args[6]), Integer.parseInt(args[7]), 0)); 51 | continue; 52 | } 53 | args = commands.split("~"); 54 | if (args.length == 7) {// Version 1.8.6 55 | clickType = this.getClickType(args); 56 | data.addCommand(new NPCCommand(Integer.parseInt(args[0]), args[1], args[2], "", clickType, Boolean.parseBoolean(args[4]), 57 | Boolean.parseBoolean(args[5]), false, false, false, Double.parseDouble(args[6]), 0, 0)); 58 | }else if (args.length == 5) {//Version 1.8.5 59 | String cmd = args[2]; 60 | if (cmd.toLowerCase().contains("-c")) { 61 | cmd = cmd.replace("-c", ""); 62 | } 63 | data.addCommand(new NPCCommand(args[0], args[1], "", ClickType.getClickType(PluginSettings.ClickType.getSetting()), Boolean.parseBoolean(cmd), 64 | Boolean.parseBoolean(args[3]), false, false, false, Double.parseDouble(args[4]), 0, 0)); 65 | }else{ 66 | instance.logError("Loading Command", "CommandDatabase", "loadDatabase()", "Incompatible command! ***Not detrimental*** ID: "+idx+" | Line: " + commands); 67 | } 68 | } 69 | CommandNPC.getCommandManager().addNPCData(data); 70 | } 71 | instance.log("Loading commands complete!", true); 72 | }); 73 | } 74 | 75 | public void deleteNPC(int id) { 76 | CommandNPC.getQueueSystem().execute(() -> database.set("NPCS." + id, null)); 77 | } 78 | 79 | public void saveDatabase() { 80 | CommandNPC.getQueueSystem().execute(() -> { 81 | for (NPCData data : CommandNPC.getCommandManager().getNPCDatas()) { 82 | ArrayList commands = new ArrayList<>(); 83 | for (NPCCommand command : data.getCommands()) { 84 | commands.add(command.getID() + "~~~" + command.getCommand() + "~~~" + command.getPermission() + "~~~" + command.getClickType().name() + "~~~" + 85 | command.getCooldownMessage() + "~~~" + command.inConsole() + "~~~" + command.asOp() + "~~~" + command.isRandom() + "~~~" + 86 | command.getCost() + "~~~" + command.getDelay() + "~~~" + command.getCooldown() + "~~~" + command.isIgnorePermMsg() + "~~~" + command.isIgnoreMoneyMsg()); 87 | } 88 | database.set("NPCS." + data.getId() + ".Commands", commands); 89 | } 90 | }); 91 | } 92 | 93 | private ClickType getClickType(String[] args) { 94 | return args.length > 3 && ClickType.hasClickType(args[3]) ? ClickType.getClickType(args[3]) : ClickType.getClickType(PluginSettings.ClickType.getSetting()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.messageofdeath 8 | commandnpc 9 | CommandNPC 10 | 1.9.1 11 | jar 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.12-R0.1-SNAPSHOT 17 | 2.0.22-SNAPSHOT 18 | 1.6 19 | 2.0.8 20 | 21 | 22 | 23 | 24 | spigot-repo 25 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 26 | 27 | 28 | vault-repo 29 | http://nexus.hc.to/content/repositories/pub_releases 30 | 31 | 32 | citizens-repo 33 | http://repo.citizensnpcs.co 34 | 35 | 36 | placeholderapi 37 | http://repo.extendedclip.com/content/repositories/placeholderapi/ 38 | 39 | 40 | 41 | 42 | 43 | org.spigotmc 44 | spigot-api 45 | ${bukkit.version} 46 | provided 47 | 48 | 49 | org.bukkit 50 | bukkit 51 | ${bukkit.version} 52 | provided 53 | 54 | 55 | net.milkbowl.vault 56 | VaultAPI 57 | ${vault.version} 58 | provided 59 | 60 | 61 | net.citizensnpcs 62 | citizensapi 63 | ${citizensapi.version} 64 | provided 65 | 66 | 67 | me.clip 68 | placeholderapi 69 | ${placeholderapi.version} 70 | provided 71 | 72 | 73 | 74 | 75 | clean install 76 | src/main/java 77 | ${project.name}-${project.version} 78 | 79 | 80 | 81 | . 82 | ${basedir}/src/main/resources 83 | true 84 | 85 | **/* 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-compiler-plugin 94 | 3.6.1 95 | 96 | ${jdk.version} 97 | ${jdk.version} 98 | true 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-jar-plugin 104 | 3.0.2 105 | 106 | 107 | false 108 | 109 | ${project.name} 110 | ${project.version} 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-shade-plugin 119 | 3.0.0 120 | 121 | 122 | package 123 | 124 | shade 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/CommandNPC.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc; 2 | 3 | import java.util.ArrayList; 4 | 5 | import me.messageofdeath.commandnpc.Database.CommandDatabase; 6 | import me.messageofdeath.commandnpc.Database.LanguageSettings.LanguageConfiguration; 7 | import me.messageofdeath.commandnpc.Database.LanguageSettings.LanguageSettings; 8 | import me.messageofdeath.commandnpc.Database.PluginSettings.PluginConfiguration; 9 | import me.messageofdeath.commandnpc.Database.PluginSettings.PluginSettings; 10 | import me.messageofdeath.commandnpc.Listeners.NPCListener; 11 | import me.messageofdeath.commandnpc.NPCDataManager.NPCDataManager; 12 | import me.messageofdeath.commandnpc.Utilities.BungeeCord.BungeeCordUtil; 13 | import me.messageofdeath.commandnpc.Utilities.CitizenBackend.CitizenCommandRegister; 14 | import me.messageofdeath.commandnpc.Utilities.Metrics.Metrics; 15 | import me.messageofdeath.commandnpc.Utilities.queue.QueueSystem; 16 | import me.messageofdeath.commandnpc.commands.CitizenCommands; 17 | import me.messageofdeath.commandnpc.commands.ReloadCommand; 18 | import net.milkbowl.vault.economy.Economy; 19 | 20 | import org.bukkit.ChatColor; 21 | import org.bukkit.plugin.RegisteredServiceProvider; 22 | import org.bukkit.plugin.java.JavaPlugin; 23 | 24 | public class CommandNPC extends JavaPlugin { 25 | 26 | public static String prefix; 27 | 28 | private static CommandDatabase database; 29 | 30 | private static JavaPlugin instance; 31 | 32 | private static PluginConfiguration config; 33 | 34 | private static NPCDataManager manager; 35 | 36 | private static Economy econ = null; 37 | 38 | private static QueueSystem queueSystem; 39 | 40 | private static boolean placeHolderAPI = false; 41 | 42 | private static boolean econAvailable = false; 43 | 44 | @Override 45 | public void onEnable() { 46 | CommandNPC.queueSystem = new QueueSystem(1); 47 | new Metrics(this); 48 | LanguageConfiguration langConfig = new LanguageConfiguration(this); 49 | langConfig.initConfiguration(); 50 | langConfig.loadConfiguration(); 51 | this.reloadConfigX(); 52 | CommandNPC.prefix = CommandNPC.getColorized(LanguageSettings.General_PluginTag.getSetting()); 53 | /** --------------Checking for Dependencies-------------- **/ 54 | if (!getServer().getPluginManager().isPluginEnabled("Citizens")) { 55 | this.logError("Required Dependencies", "commandnpc", "onEnable()", "Citizens 2 not found! commandnpc will now shut down."); 56 | super.getServer().getPluginManager().disablePlugin(this); 57 | return; 58 | } 59 | if (getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { 60 | placeHolderAPI = true; 61 | } 62 | this.setupEconomy(); 63 | 64 | /** --------------Initiation of Databases, Managers, and Parsers-------------- **/ 65 | CommandNPC.instance = this; 66 | CommandNPC.manager = new NPCDataManager(); 67 | CitizenCommandRegister commandRegister = new CitizenCommandRegister(this); 68 | /** --------------Initiation of the Listener-------------- **/ 69 | super.getServer().getPluginManager().registerEvents(new NPCListener(), this); 70 | /** --------------Initiation and Loading of Databases-------------- **/ 71 | this.log("Initiating Database", true); 72 | CommandNPC.database = new CommandDatabase(this); 73 | CommandNPC.database.initDatabase(); 74 | CommandNPC.database.loadDatabase(); 75 | /** --------------Registering Commands-------------- **/ 76 | this.log("Injecting command info into Citizens.", true); 77 | commandRegister.registerCitizenCommand(CitizenCommands.class); 78 | super.getCommand("commandnpc").setExecutor(new ReloadCommand()); 79 | if(PluginSettings.BungeeCord.getBoolean()) { 80 | this.log("Setting up BungeeCord", true); 81 | BungeeCordUtil.setupUtil(); 82 | } 83 | this.log("CommandNPC successfully loaded!", true); 84 | } 85 | 86 | @Override 87 | public void onDisable() { 88 | if (database != null) { 89 | database.saveDatabase(); 90 | } 91 | queueSystem.stop(); 92 | if(PluginSettings.BungeeCord.getBoolean()) { 93 | this.log("Disabling BungeeCord Support", true); 94 | BungeeCordUtil.disableUtil(); 95 | } 96 | } 97 | 98 | public static Economy getEcon() { 99 | return econ; 100 | } 101 | 102 | private static String getColorized(String input) { 103 | return ChatColor.translateAlternateColorCodes('&', input); 104 | } 105 | 106 | public void log(String log, boolean prefix) { 107 | getServer().getConsoleSender().sendMessage(CommandNPC.getColorized((prefix ? CommandNPC.prefix : "") + log)); 108 | } 109 | 110 | public static NPCDataManager getCommandManager() { 111 | return manager; 112 | } 113 | 114 | public static QueueSystem getQueueSystem() { 115 | return queueSystem; 116 | } 117 | 118 | public static CommandDatabase getCommandDatabase() { 119 | return database; 120 | } 121 | 122 | public static PluginConfiguration getConfigX() { 123 | return config; 124 | } 125 | 126 | public static boolean hasPlaceHolderAPI() { 127 | return placeHolderAPI; 128 | } 129 | 130 | public void reloadConfigX() { 131 | config = new PluginConfiguration(this); 132 | config.initConfiguration(); 133 | config.loadConfiguration(); 134 | } 135 | 136 | public static CommandNPC getInstance() { 137 | return (CommandNPC)instance; 138 | } 139 | 140 | public static boolean isEconAvailable() { 141 | return econAvailable; 142 | } 143 | 144 | public void logError(String topic, String classx, String method, String error) { 145 | final String space = " "; 146 | String text = "&cTopic"; 147 | topic = "&c" + topic; 148 | log("&4---------------------&b{&2CommandNPC &cError&b}&4---------------------", false); 149 | log(space.substring((space.length() + text.length()) / 2, space.length()) + text, false); 150 | log(space.substring((space.length() + topic.length()) / 2, space.length()) + topic, false); 151 | log("", false); 152 | for (String s : getLines(error, space)) { 153 | log("&b" + space.substring((space.length() + s.length()) / 2, space.length()) + s, false); 154 | } 155 | log("", false); 156 | String cl = "&8Class: &c" + classx + " &8Method: &c" + method; 157 | for (String s : getLines(cl, space)) { 158 | log("&c" + space.substring((space.length() + s.length()) / 2, space.length()) + s, false); 159 | } 160 | log("", false); 161 | log("&4---------------------&b{&2CommandNPC &cError&b}&4---------------------", false); 162 | } 163 | 164 | private ArrayList getLines(String parse, final String space) { 165 | ArrayList lines = new ArrayList<>(); 166 | String s = ""; 167 | String[] split = parse.split(" "); 168 | final int length = split.length; 169 | for (int i = 0; i < length; i++) { 170 | if (s.length() + split[i].length() < space.length()) { 171 | s += split[i] + " "; 172 | } else { 173 | lines.add(s); 174 | s = split[i] + " "; 175 | } 176 | if (i + 1 == length) { 177 | lines.add(s); 178 | } 179 | } 180 | return lines; 181 | } 182 | 183 | private void setupEconomy() { 184 | if (getServer().getPluginManager().getPlugin("Vault") == null) { 185 | log("Vault not found! Economy support for CommandNPC has been disabled.", true); 186 | return; 187 | } 188 | RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); 189 | if (rsp == null) { 190 | log("Vault compatible economy not found! Economy support for CommandNPC has been disabled.", true); 191 | return; 192 | } 193 | econ = rsp.getProvider(); 194 | if (econ != null) { 195 | log("Vault compatible economy found! Economy support for CommandNPC has been enabled.", true); 196 | econAvailable = true; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Listeners/NPCListener.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Listeners; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Random; 6 | import java.util.UUID; 7 | 8 | import me.clip.placeholderapi.PlaceholderAPI; 9 | import me.messageofdeath.commandnpc.CommandNPC; 10 | import me.messageofdeath.commandnpc.Database.ClickType; 11 | import me.messageofdeath.commandnpc.Database.LanguageSettings.LanguageSettings; 12 | import me.messageofdeath.commandnpc.Database.PluginSettings.PluginSettings; 13 | import me.messageofdeath.commandnpc.NPCDataManager.NPCCommand; 14 | import me.messageofdeath.commandnpc.NPCDataManager.NPCData; 15 | import me.messageofdeath.commandnpc.Utilities.BungeeCord.BungeeCordUtil; 16 | import me.messageofdeath.commandnpc.Utilities.CooldownManager.Cooldown; 17 | import me.messageofdeath.commandnpc.Utilities.CooldownManager.CooldownManager; 18 | import net.citizensnpcs.api.event.NPCLeftClickEvent; 19 | import net.citizensnpcs.api.event.NPCRemoveEvent; 20 | import net.citizensnpcs.api.event.NPCRightClickEvent; 21 | import net.citizensnpcs.api.npc.NPC; 22 | import net.citizensnpcs.api.util.Messaging; 23 | 24 | import org.bukkit.Bukkit; 25 | import org.bukkit.entity.Player; 26 | import org.bukkit.event.EventHandler; 27 | import org.bukkit.event.EventPriority; 28 | import org.bukkit.event.Listener; 29 | import org.bukkit.scheduler.BukkitScheduler; 30 | 31 | public class NPCListener implements Listener { 32 | 33 | private final ArrayList coolingDown = new ArrayList<>();//To prevent server overloads 34 | private final long delay = PluginSettings.CoolDown.getInteger(); 35 | private final CooldownManager cooldown; 36 | 37 | public NPCListener() { 38 | cooldown = new CooldownManager(); 39 | } 40 | 41 | @EventHandler(priority = EventPriority.HIGHEST) 42 | public void onNPCDelete(NPCRemoveEvent event) { 43 | if (CommandNPC.getCommandManager().hasNPCData(event.getNPC().getId())) { 44 | CommandNPC.getCommandManager().removeNPCData(event.getNPC().getId()); 45 | } 46 | } 47 | 48 | @EventHandler(priority = EventPriority.HIGHEST) 49 | public void onRight(NPCRightClickEvent event) { 50 | if (CommandNPC.getCommandManager().hasNPCData(event.getNPC().getId()) && coolingDown.contains(event.getClicker().getUniqueId())) { 51 | if(PluginSettings.CooldownMessage.getBoolean()) { 52 | Messaging.send(event.getClicker(), LanguageSettings.CmdNPC_Cooldown.getSetting()); 53 | } 54 | return; 55 | } 56 | this.onClick(event.getClicker(), event.getNPC(), ClickType.RIGHT); 57 | } 58 | 59 | @EventHandler(priority = EventPriority.HIGHEST) 60 | public void onLeft(NPCLeftClickEvent event) { 61 | if (CommandNPC.getCommandManager().hasNPCData(event.getNPC().getId()) && coolingDown.contains(event.getClicker().getUniqueId())) { 62 | if(PluginSettings.CooldownMessage.getBoolean()) { 63 | Messaging.send(event.getClicker(), LanguageSettings.CmdNPC_Cooldown.getSetting()); 64 | } 65 | return; 66 | } 67 | this.onClick(event.getClicker(), event.getNPC(), ClickType.LEFT); 68 | } 69 | 70 | private void onClick(final Player player, NPC npc, ClickType clickType) { 71 | if (CommandNPC.getCommandManager().hasNPCData(npc.getId())) { 72 | BukkitScheduler scheduler = Bukkit.getServer().getScheduler(); 73 | if(delay > 0) { 74 | coolingDown.add(player.getUniqueId()); 75 | scheduler.scheduleSyncDelayedTask(CommandNPC.getInstance(), () -> coolingDown.remove(player.getUniqueId()), this.delay); 76 | } 77 | NPCData data = CommandNPC.getCommandManager().getNPCData(npc.getId()); 78 | boolean isOp = player.isOp(); 79 | ArrayList commands = new ArrayList<>(); 80 | if(data.isRandom()) { 81 | commands.add(data.getCommands().get(new Random().nextInt(data.getCommands().size()))); 82 | }else{ 83 | commands = data.getCommands(); 84 | } 85 | for(NPCCommand command : commands) { 86 | if(command.getClickType() == clickType || command.getClickType() == ClickType.BOTH) { 87 | if(command.getPermission().isEmpty() || player.hasPermission(command.getPermission()) || command.getPermission().equalsIgnoreCase("noPerm")) { 88 | //------------ Cooldown ------------ 89 | if(cooldown.hasCooldown(player.getUniqueId())) { 90 | if(Arrays.stream(cooldown.getCooldowns(player.getUniqueId())).anyMatch(search -> 91 | search.getNpcID() == npc.getId() && search.getCommandID() == command.getID())) { 92 | if (command.getCooldownMessage() != null && !command.getCooldownMessage().isEmpty()) { 93 | Messaging.sendError(player, command.getCooldownMessage()); 94 | } 95 | continue; 96 | } 97 | } 98 | //------------ Economy ------------ 99 | if(command.getCost() > 0 && CommandNPC.isEconAvailable()) { 100 | if(CommandNPC.getEcon().has(player, command.getCost())) { 101 | CommandNPC.getEcon().withdrawPlayer(player, command.getCost()); 102 | }else{ 103 | if(!command.isIgnoreMoneyMsg()) { 104 | Messaging.sendError(player, LanguageSettings.CmdNPC_NoMoney.getSetting()); 105 | } 106 | continue; 107 | } 108 | } 109 | //------------ BungeeCord ------------ 110 | if(command.getCommand().toLowerCase().startsWith("server ")) { 111 | if(PluginSettings.BungeeCord.getBoolean()) { 112 | String[] args = command.getCommand().split(" "); 113 | if(args.length == 2) { 114 | if(command.getDelay() > 0) { 115 | scheduler.scheduleSyncDelayedTask(CommandNPC.getInstance(), () -> { 116 | BungeeCordUtil.sendPlayerToServer(player, args[1]); 117 | CommandNPC.getInstance().log("Sent '"+player.getName()+"' to server '"+args[1]+"'!", true); 118 | }, command.getDelay()); 119 | }else{ 120 | BungeeCordUtil.sendPlayerToServer(player, args[1]); 121 | CommandNPC.getInstance().log("Sent '"+player.getName()+"' to server '"+args[1]+"'!", true); 122 | } 123 | executeCooldown(player.getUniqueId(), npc.getId(), command.getID(), command.getCooldown()); 124 | continue; 125 | }else{ 126 | Messaging.sendError(player, "Inform the system administrator to look in console for error."); 127 | CommandNPC.getInstance().logError("BungeeCord Command", "NPCListener", "onClick(Player, NPC, ClickType)", "/server command for NPC " + 128 | "ID: " + npc.getId() + ", Command ID: " + command.getID() + ", does not follow the format of /server "); 129 | continue; 130 | } 131 | }else{ 132 | Messaging.sendError(player, "Inform the system administrator to look in console for error."); 133 | CommandNPC.getInstance().logError("BungeeCord Command", "NPCListener", "onClick(Player, NPC, ClickType)", "BungeeCord is " + 134 | "disabled in config.yml, yet an NPC has the command /server registered to it."); 135 | continue; 136 | } 137 | } 138 | //------------ Execute Command ------------ 139 | if(!command.inConsole()) { 140 | try{ 141 | if(command.asOp() && !isOp) { 142 | player.setOp(true); 143 | } 144 | if(command.getDelay() > 0) { 145 | scheduler.scheduleSyncDelayedTask(CommandNPC.getInstance(), () -> { 146 | player.chat("/" + process(player, command.getCommand())); 147 | if(PluginSettings.ExecuteCommandMessage.getBoolean()) { 148 | Messaging.send(player, LanguageSettings.CmdNPC_Executed.getSetting()); 149 | } 150 | }, command.getDelay()); 151 | }else{ 152 | player.chat("/" + process(player, command.getCommand())); 153 | if(PluginSettings.ExecuteCommandMessage.getBoolean()) { 154 | Messaging.send(player, LanguageSettings.CmdNPC_Executed.getSetting()); 155 | } 156 | } 157 | }finally{ 158 | player.setOp(isOp); 159 | } 160 | }else{ 161 | if(command.getDelay() > 0) { 162 | scheduler.scheduleSyncDelayedTask(CommandNPC.getInstance(), () -> { 163 | Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), process(player, command.getCommand())); 164 | if(PluginSettings.ExecuteCommandMessage.getBoolean()) { 165 | Messaging.send(player, LanguageSettings.CmdNPC_Executed.getSetting()); 166 | } 167 | }, command.getDelay()); 168 | }else{ 169 | Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), process(player, command.getCommand())); 170 | if(PluginSettings.ExecuteCommandMessage.getBoolean()) { 171 | Messaging.send(player, LanguageSettings.CmdNPC_Executed.getSetting()); 172 | } 173 | } 174 | } 175 | //------------ Cooldown ------------ 176 | executeCooldown(player.getUniqueId(), npc.getId(), command.getID(), command.getCooldown()); 177 | }else{ 178 | if(!command.isIgnorePermMsg()) { 179 | Messaging.sendError(player, LanguageSettings.Commands_NoPermission.getSetting()); 180 | } 181 | } 182 | }//Wrong clickType (Do nothing) 183 | } 184 | } 185 | } 186 | 187 | private String process(Player player, String cmd) { 188 | return CommandNPC.hasPlaceHolderAPI() ? PlaceholderAPI.setPlaceholders(player, cmd.replace("%name", player.getName())) 189 | : cmd.replace("%name", player.getName()); 190 | } 191 | 192 | private void executeCooldown(UUID uuid, int npcID, int commandID, int cd) { 193 | if(cd > 0) { 194 | cooldown.addCooldown(new Cooldown(uuid, npcID, commandID)); 195 | Bukkit.getScheduler().scheduleSyncDelayedTask(CommandNPC.getInstance(), () -> cooldown.removeCooldown(uuid), cd); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Database/YamlDatabase.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Database; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.util.ArrayList; 8 | 9 | import org.bukkit.Material; 10 | import org.bukkit.configuration.ConfigurationSection; 11 | import org.bukkit.configuration.file.FileConfiguration; 12 | import org.bukkit.configuration.file.YamlConfiguration; 13 | import org.bukkit.plugin.java.JavaPlugin; 14 | 15 | /** 16 | * @author messageofDEATH 17 | */ 18 | 19 | public class YamlDatabase { 20 | 21 | public JavaPlugin plugin = null; 22 | public String fileName = null, fileExtension = null, fileLocation = null; 23 | public File file = null; 24 | public FileConfiguration fileConfig = null; 25 | public boolean createdFile = false, saveOnSet = true, copyFileOnStart = true; 26 | 27 | /** 28 | * Creates a new instance of YamlDatabase with the default fileLocation. 29 | * @param plugin 30 | * @return new YamlDatabase 31 | */ 32 | 33 | public YamlDatabase(JavaPlugin plugin) { 34 | this.plugin = plugin; 35 | this.fileExtension = ".yml"; 36 | this.fileConfig = new YamlConfiguration(); 37 | } 38 | 39 | /** 40 | * Creates a new instance of YamlDatabase with the default fileLocation. 41 | * @param plugin 42 | * @param fileName 43 | * @return new YamlDatabase 44 | */ 45 | public YamlDatabase(JavaPlugin plugin, String fileName) { 46 | this.plugin = plugin; 47 | this.fileName = fileName; 48 | this.fileExtension = ".yml"; 49 | this.fileConfig = new YamlConfiguration(); 50 | } 51 | 52 | public YamlDatabase(JavaPlugin plugin, String fileName, boolean copyFileOnStart) { 53 | this.plugin = plugin; 54 | this.fileName = fileName; 55 | this.fileExtension = ".yml"; 56 | this.fileConfig = new YamlConfiguration(); 57 | this.copyFileOnStart = copyFileOnStart; 58 | } 59 | 60 | /** 61 | * Creates a new instance of YamlDatabase with a set fileLocation. 62 | * @param plugin 63 | * @param fileName 64 | * @param fileLocation 65 | * @return new YamlDatabase 66 | */ 67 | 68 | public YamlDatabase(JavaPlugin plugin, String fileName, String fileLocation) { 69 | this.plugin = plugin; 70 | this.fileName = fileName; 71 | this.fileExtension = ".yml"; 72 | this.fileConfig = new YamlConfiguration(); 73 | this.fileLocation = fileLocation; 74 | } 75 | 76 | /** 77 | * Checks if file exists, if not creates one and puts default. 78 | */ 79 | public void changeFile(String fileName) { 80 | this.changeFile(fileName, this.plugin.getDataFolder().getPath()); 81 | } 82 | 83 | public void changeFile(String fileName, String fileLocation) { 84 | this.fileName = fileName; 85 | this.fileLocation = fileLocation; 86 | this.onStartUp(); 87 | } 88 | 89 | public void onStartUp() { 90 | if(this.fileLocation == null) 91 | this.file = new File(this.plugin.getDataFolder(), this.fileName + this.fileExtension); 92 | else 93 | this.file = new File(this.fileLocation, this.fileName + this.fileExtension); 94 | try{ 95 | // *** Config *** 96 | this.fileConfig = YamlConfiguration.loadConfiguration(this.file); 97 | if(!this.file.exists()){ 98 | this.file.getParentFile().mkdirs(); 99 | this.file.createNewFile(); 100 | if(copyFileOnStart && this.plugin.getResource(this.fileName + this.fileExtension) != null) 101 | copy(this.plugin.getResource(this.fileName + this.fileExtension), this.file); 102 | this.createdFile = true; 103 | } 104 | //this.fileConfig.load(this.file); 105 | }catch (Exception e){e.getCause();} 106 | } 107 | 108 | /** 109 | * Saves the file. 110 | */ 111 | 112 | public void onShutDown() { 113 | this.save(); 114 | } 115 | 116 | private void copy(InputStream in, File file) { 117 | try{ 118 | OutputStream out = new FileOutputStream(file); 119 | byte[] buf = new byte[1024]; 120 | int len; 121 | while ((len = in.read(buf)) > 0){ 122 | out.write(buf, 0, len); 123 | } 124 | out.close(); 125 | in.close(); 126 | }catch (Exception e){ 127 | e.printStackTrace(); 128 | } 129 | } 130 | 131 | /** 132 | * Reloads the file 133 | */ 134 | 135 | public void reload() { 136 | try{ 137 | this.fileConfig.load(this.file); 138 | }catch (Exception ignored){ 139 | 140 | } 141 | } 142 | 143 | /** 144 | * Saves the file 145 | */ 146 | 147 | public void save() { 148 | try{ 149 | this.fileConfig.save(this.file); 150 | }catch(Exception ignored) { 151 | 152 | } 153 | } 154 | 155 | /** 156 | * Gets a ConfigurationSection value from the file. 157 | * @param key 158 | * @param fallback 159 | * @return the ConfigurationSection for the key, if exists. 160 | * @return fallback when the key doesn't exist. 161 | */ 162 | 163 | public ConfigurationSection getConfigurationSection(String key, ConfigurationSection fallback) { 164 | if(this.fileConfig.contains(key)) { 165 | return this.fileConfig.getConfigurationSection(key); 166 | }else{ 167 | return fallback; 168 | } 169 | } 170 | 171 | /** 172 | * Gets the ConfigurationSection in a List value from the file. 173 | * @param key 174 | * @return the List for the key, if exists. 175 | * @return fallback when the key doesn't exist. 176 | */ 177 | 178 | public ArrayList getSection(String key, ArrayList fallback) { 179 | if(this.fileConfig.contains(key)) { 180 | ArrayList section = new ArrayList<>(); 181 | for(Object str : getConfigurationSection(key, null).getKeys(false).toArray()) { 182 | section.add(String.valueOf(str)); 183 | } 184 | return section; 185 | }else{ 186 | return fallback; 187 | } 188 | } 189 | 190 | /** 191 | * Gets an integer value from the file. 192 | * @param key 193 | * @param fallback 194 | * @return the integer for the key, if exists. 195 | * @return fallback when the key doesn't exist. 196 | */ 197 | public int getInteger(String key, int fallback){ 198 | if(this.fileConfig.contains(key)) { 199 | return this.fileConfig.getInt(key); 200 | }else{ 201 | return fallback; 202 | } 203 | } 204 | 205 | /** 206 | * Gets an string value from the file. 207 | * @param key 208 | * @param fallback 209 | * @return the string for the key, if exists. 210 | * @return fallback when the key doesn't exist. 211 | */ 212 | public String getString(String key, String fallback){ 213 | if(this.fileConfig.contains(key)) { 214 | return this.fileConfig.getString(key); 215 | }else{ 216 | return fallback; 217 | } 218 | } 219 | 220 | /** 221 | * Gets an float value from the file. 222 | * @param key 223 | * @return whether it exists or not 224 | */ 225 | 226 | public boolean contains(String key) { 227 | return this.fileConfig.contains(key); 228 | } 229 | 230 | /** 231 | * Gets an boolean value from the file. It will accept "true" and "false". 232 | * @param key 233 | * @param fallback 234 | * @return the boolean for the key, if exists. 235 | * @return fallback when the key doesn't exist. 236 | */ 237 | public boolean getBoolean(String key, boolean fallback){ 238 | if(this.fileConfig.contains(key)) { 239 | return this.fileConfig.getBoolean(key); 240 | }else{ 241 | return fallback; 242 | } 243 | } 244 | 245 | /** 246 | * Gets a List value from the file. 247 | * @param key 248 | * @param fallback 249 | * @return the List for the key, if exists. 250 | * @return fallback when the key doesn't exist. 251 | */ 252 | 253 | public ArrayList getStringArray(String key, ArrayList fallback) { 254 | if(this.fileConfig.contains(key)) { 255 | return (ArrayList)this.fileConfig.getStringList(key); 256 | }else{ 257 | return fallback; 258 | } 259 | } 260 | 261 | /** 262 | * Gets an double value from the file. 263 | * @param key 264 | * @param fallback 265 | * @return the double for the key, if exists. 266 | * @return fallback when the key doesn't exist. 267 | */ 268 | public double getDouble(String key, double fallback){ 269 | if(this.fileConfig.contains(key)) { 270 | return this.fileConfig.getDouble(key); 271 | }else{ 272 | return fallback; 273 | } 274 | } 275 | 276 | /** 277 | * Gets an Object value from the file. 278 | * @param key 279 | * @param fallback 280 | * @return the Object for the key, if exists. 281 | * @return fallback when the key doesn't exist. 282 | */ 283 | 284 | public Object getObject(String key, Object fallback) { 285 | if(this.fileConfig.contains(key)) { 286 | return this.fileConfig.get(key); 287 | }else{ 288 | return fallback; 289 | } 290 | } 291 | /** 292 | * Gets an float value from the file. 293 | * @param key 294 | * @param fallback 295 | * @return the float for the key, if exists. 296 | * @return fallback when the key doesn't exist. 297 | */ 298 | public float getFloat(String key, float fallback){ 299 | if(this.fileConfig.contains(key)) { 300 | return (float) this.fileConfig.getDouble(key); 301 | }else{ 302 | return fallback; 303 | } 304 | } 305 | 306 | /** 307 | * Gets an material value from the file. It parses material-ids as well as Bukkit-Material names. 308 | * @param key 309 | * @param fallback 310 | * @return the material for the key, if exists. 311 | * @return fallback when the key doesn't exist. 312 | */ 313 | public Material getMaterial(String key, Material fallback){ 314 | if(this.fileConfig.contains(key)) { 315 | return this.fileConfig.getItemStack(key).getType(); 316 | }else{ 317 | return fallback; 318 | } 319 | } 320 | 321 | /** 322 | * When one key exists multiple times, use this method to get all values as strings in a list. 323 | * @param key the key to search 324 | * @return all values for that key. 325 | */ 326 | 327 | /** 328 | * Writes the keySet to the file 329 | */ 330 | 331 | public void set(String key, Object set) { 332 | this.fileConfig.set(key, set); 333 | if(this.saveOnSet) { 334 | this.save(); 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/commands/CitizenCommands.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.commands; 2 | 3 | import java.util.ArrayList; 4 | 5 | import org.bukkit.command.CommandSender; 6 | 7 | import me.messageofdeath.commandnpc.CommandNPC; 8 | import me.messageofdeath.commandnpc.Database.ClickType; 9 | import me.messageofdeath.commandnpc.Database.LanguageSettings.LanguageSettings; 10 | import me.messageofdeath.commandnpc.Database.PluginSettings.PluginSettings; 11 | import me.messageofdeath.commandnpc.NPCDataManager.NPCCommand; 12 | import me.messageofdeath.commandnpc.NPCDataManager.NPCData; 13 | import me.messageofdeath.commandnpc.Utilities.Utilities; 14 | import net.citizensnpcs.api.command.Command; 15 | import net.citizensnpcs.api.command.CommandContext; 16 | import net.citizensnpcs.api.command.Requirements; 17 | import net.citizensnpcs.api.npc.NPC; 18 | import net.citizensnpcs.api.util.Messaging; 19 | 20 | @Requirements(selected = true, ownership = true) 21 | public class CitizenCommands { 22 | 23 | @Command(aliases = { "npc" }, usage = "cmdadd [-c console] [-o Op] [-r random] [-i ignorePermMsg] [-l ignoreMoneyMsg] [--v price] [--t clickType] " + 24 | "[--d delay] [--cd cooldown] [--p custom.permission.node] ", 25 | desc = "Add a command to a NPC", modifiers = { "cmdadd" }, min = 2, flags = "coril", permission = "commandnpc.admin") 26 | public void addCmd(CommandContext args, CommandSender sender, NPC npc) { 27 | int id = npc.getId(); 28 | String permission = "noPerm"; 29 | ClickType clickType; 30 | if(ClickType.hasClickType(PluginSettings.ClickType.getSetting())) { 31 | clickType = ClickType.getClickType(PluginSettings.ClickType.getSetting()); 32 | }else{ 33 | clickType = ClickType.BOTH; 34 | CommandNPC.getInstance().logError("ClickType", "CitizensCommands", "addCmd(CommandContext, CommandSender, NPC)", "ClickType from config.yml did not resolve! " 35 | + "Resulting to ClickType BOTH!"); 36 | } 37 | boolean inConsole = false; 38 | boolean isRandom = false; 39 | boolean asOp = false; 40 | boolean ignorePerm = false; 41 | boolean ignoreMoney = false; 42 | String cmd; 43 | double cost = 0; 44 | int delay = 0; 45 | int cooldown = 0; 46 | 47 | if (args.hasFlag('c')) { 48 | inConsole = true; 49 | } 50 | if (args.hasFlag('o')) { 51 | asOp = true; 52 | } 53 | if (args.hasFlag('r')) { 54 | isRandom = true; 55 | } 56 | if (args.hasFlag('i')) { 57 | ignorePerm = true; 58 | } 59 | if (args.hasFlag('l')) { 60 | ignoreMoney = true; 61 | } 62 | if(args.hasValueFlag("t")) { 63 | String value = args.getFlag("t"); 64 | if(ClickType.hasClickType(value)) { 65 | clickType = ClickType.getClickType(value); 66 | }else{ 67 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_ValueFlagT.getSetting()); 68 | } 69 | } 70 | if (args.hasValueFlag("p")) { 71 | permission = args.getFlag("p"); 72 | } 73 | if (args.hasValueFlag("v")) { 74 | if(Utilities.isDouble(args.getFlag("v"))) { 75 | cost = args.getFlagDouble("v"); 76 | }else{ 77 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "price")); 78 | } 79 | } 80 | if (args.hasValueFlag("d")) { 81 | if(Utilities.isInteger(args.getFlag("d"))) { 82 | delay = args.getFlagInteger("d"); 83 | }else{ 84 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "delay")); 85 | } 86 | } 87 | if(args.hasValueFlag("cd")) { 88 | if(Utilities.isInteger(args.getFlag("cd"))) { 89 | cooldown = args.getFlagInteger("cd"); 90 | }else{ 91 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "cooldown")); 92 | } 93 | } 94 | cmd = args.getJoinedStrings(1); 95 | if (cmd != null) { 96 | NPCCommand npcCommand = new NPCCommand(cmd, permission, "", clickType, inConsole, asOp, isRandom, ignorePerm, ignoreMoney, cost, delay, cooldown); 97 | if (CommandNPC.getCommandManager().hasNPCData(id)) { 98 | CommandNPC.getCommandManager().getNPCData(id).addCommand(npcCommand); 99 | } else { 100 | CommandNPC.getCommandManager().addNPCData(new NPCData(id, npcCommand)); 101 | } 102 | CommandNPC.getCommandDatabase().saveDatabase(); 103 | Messaging.send(sender, LanguageSettings.Commands_Citizens_Add.getSetting()); 104 | }else{ 105 | Messaging.send(sender, LanguageSettings.Commands_Citizens_NoCmdInput.getSetting()); 106 | } 107 | } 108 | 109 | @Command(aliases = { "npc" }, usage = "cmdremove ", desc = "Remove a command on the NPC.", modifiers = { "cmdremove" }, min = 2, max = 2, 110 | permission = "commandnpc.admin") 111 | public void removeCmd(CommandContext args, CommandSender sender, NPC npc) { 112 | int id = npc.getId(); 113 | if(CommandNPC.getCommandManager().hasNPCData(id)) { 114 | NPCData data = CommandNPC.getCommandManager().getNPCData(id); 115 | if(Utilities.isInteger(args.getString(1))) { 116 | if(data.hasCommand(args.getInteger(1))) { 117 | data.removeCommand(args.getInteger(1)); 118 | Messaging.send(sender, LanguageSettings.Commands_Citizens_Removed.getSetting()); 119 | }else{ 120 | Messaging.sendError(sender, LanguageSettings.Commands_DoesNotExist.getSetting().replace("%type", "ID")); } 121 | }else{ 122 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NumBetween.getSetting().replace("%num1", "1").replace("%num2", data.getCommands().size() + "")); 123 | } 124 | }else{ 125 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NoCommands.getSetting()); 126 | } 127 | } 128 | 129 | @Command(aliases = { "npc" }, usage = "cmdset [-c console] [-o Op] [-r random] [-m cdMsg] [-i ignorePermMsg] [-l ignoreMoneyMsg] [--v price] " + 130 | "[--t clickType] [--d delay] [--cd cooldown] [--p custom.permission.node] [command | cdMsg...]", 131 | desc = "Set various variables for the command.", modifiers = { "cmdset" }, min = 2, flags = "cormil", permission = "commandnpc.admin") 132 | public void setCmd(CommandContext args, CommandSender sender, NPC npc) { 133 | int npcID = npc.getId(); 134 | if(Utilities.isInteger(args.getString(1))) { 135 | int id = args.getInteger(1); 136 | if(CommandNPC.getCommandManager().hasNPCData(npcID)) { 137 | NPCData data = CommandNPC.getCommandManager().getNPCData(npcID); 138 | if(data.hasCommand(id)) { 139 | NPCCommand command = data.getCommand(id); 140 | Messaging.send(sender, CommandNPC.prefix + LanguageSettings.Commands_SetTo_Header.getSetting()); 141 | if(args.hasFlag('c')) { 142 | command.setInConsole(!command.inConsole()); 143 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Console") 144 | .replace("%value", command.inConsole() + "")); 145 | } 146 | if(args.hasFlag('o')) { 147 | command.setAsOP(!command.asOp()); 148 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Op") 149 | .replace("%value", command.asOp() + "")); 150 | } 151 | if(args.hasFlag('r')) { 152 | command.setIsRandom(!command.isRandom()); 153 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Random") 154 | .replace("%value", command.isRandom() + "")); 155 | } 156 | if(args.hasFlag('i')) { 157 | command.setIgnorePermMsg(!command.isIgnorePermMsg()); 158 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Ignore Perm Message") 159 | .replace("%value", command.isIgnorePermMsg() + "")); 160 | } 161 | if(args.hasFlag('l')) { 162 | command.setIgnoreMoneyMsg(!command.isIgnoreMoneyMsg()); 163 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Ignore Money Message") 164 | .replace("%value", command.isIgnoreMoneyMsg() + "")); 165 | } 166 | if(args.hasValueFlag("p")) { 167 | command.setPermission(args.getFlag("p")); 168 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Permission") 169 | .replace("%value", command.getPermission())); 170 | } 171 | if(args.hasValueFlag("v")) { 172 | if(Utilities.isDouble(args.getFlag("v"))) { 173 | command.setCost(args.getFlagDouble("v")); 174 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Cost") 175 | .replace("%value", command.getCost() + "")); 176 | }else{ 177 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "cost")); 178 | } 179 | } 180 | if (args.hasValueFlag("d")) { 181 | if(Utilities.isInteger(args.getFlag("d"))) { 182 | command.setDelay(args.getFlagInteger("d")); 183 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Delay") 184 | .replace("%value", command.getDelay() + "")); 185 | }else{ 186 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "delay")); 187 | } 188 | } 189 | if(args.hasValueFlag("cd")) { 190 | if(Utilities.isInteger(args.getFlag("cd"))) { 191 | command.setCooldown(args.getFlagInteger("cd")); 192 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Cooldown") 193 | .replace("%value", command.getCooldown() + "")); 194 | }else{ 195 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "cooldown")); 196 | } 197 | } 198 | if(args.hasValueFlag("t")) { 199 | String value = args.getFlag("t"); 200 | if(ClickType.hasClickType(value)) { 201 | command.setClickType(ClickType.getClickType(value)); 202 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "ClickType") 203 | .replace("%value", command.getClickType().name())); 204 | }else{ 205 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_ValueFlagT.getSetting()); 206 | } 207 | } 208 | if(args.argsLength() > 2) { 209 | if(args.hasFlag('m')) { 210 | command.setCooldownMessage(args.getJoinedStrings(2)); 211 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Cooldown Message") 212 | .replace("%value", command.getCooldownMessage())); 213 | }else { 214 | command.setCommand(args.getJoinedStrings(2)); 215 | Messaging.send(sender, LanguageSettings.Commands_SetTo_Line.getSetting().replace("%variable", "Command") 216 | .replace("%value", command.getCommand())); 217 | } 218 | } 219 | CommandNPC.getCommandDatabase().saveDatabase(); 220 | }else{ 221 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NumBetween.getSetting().replace("%num1", "1") 222 | .replace("%num2", data.getCommands().size() + "")); 223 | } 224 | }else{ 225 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NoCommands.getSetting()); 226 | } 227 | }else{ 228 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "ID")); 229 | } 230 | } 231 | 232 | @Command(aliases = { "npc" }, usage = "cmdinfo [ID]", desc = "Displays various information about the commands of an NPC.", modifiers = { "cmdinfo" }, min = 1, max = 2, 233 | permission = "commandnpc.admin") 234 | public void infoCmds(CommandContext args, CommandSender sender, NPC npc) { 235 | int id = npc.getId(); 236 | if(CommandNPC.getCommandManager().hasNPCData(id)) { 237 | NPCData data = CommandNPC.getCommandManager().getNPCData(id); 238 | ArrayList commands; 239 | if(args.argsLength() == 2) { 240 | if(Utilities.isInteger(args.getString(1))) { 241 | int cmdID = args.getInteger(1); 242 | if(data.hasCommand(cmdID)) { 243 | commands = new ArrayList<>(); 244 | commands.add(data.getCommand(cmdID)); 245 | }else{ 246 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NumBetween.getSetting().replace("%num1", "1") 247 | .replace("%num2", data.getCommands().size() + "")); 248 | return; 249 | } 250 | }else{ 251 | Messaging.sendError(sender, LanguageSettings.Commands_MustBeNumeric.getSetting().replace("%arg", "ID")); 252 | return; 253 | } 254 | }else{ 255 | commands = data.getCommands(); 256 | } 257 | //npc cmdset 2 -c -o -r -m -i -l --v 25 --t left --d 60 --cd 200 --p custom.permission This is a test cooldown message 258 | Messaging.send(sender, CommandNPC.prefix + LanguageSettings.Commands_List_InfoHeader.getSetting().replace("%id", id + "")); 259 | String prefix = LanguageSettings.Commands_List_InfoLinePrefix.getSetting(); 260 | String infoLine = LanguageSettings.Commands_List_InfoLine.getSetting(); 261 | String spacer = " &8| "; 262 | for(NPCCommand command : commands) { 263 | Messaging.send(sender, LanguageSettings.Commands_List_InfoLineHeader.getSetting().replace("%name", "Command ID") 264 | .replace("%value", "" + command.getID())); 265 | Messaging.send(sender, prefix + infoLine.replace("%name", "Command").replace("%value", command.getCommand())); 266 | Messaging.send(sender, prefix + infoLine.replace("%name", "Permission").replace("%value", command.getPermission())); 267 | Messaging.send(sender, prefix + infoLine.replace("%name", "Cooldown Message").replace("%value", command.getCooldownMessage())); 268 | Messaging.send(sender, prefix + infoLine.replace("%name", "Ignore Perm Message").replace("%value", command.isIgnorePermMsg() + "") 269 | + spacer + infoLine.replace("%name", "Ignore Money Message").replace("%value", command.isIgnoreMoneyMsg() + "")); 270 | Messaging.send(sender, prefix + infoLine.replace("%name", "ClickType").replace("%value", command.getClickType().name().toLowerCase()) 271 | + spacer + infoLine.replace("%name", "Cost").replace("%value", command.getCost() + "") 272 | + spacer + infoLine.replace("%name", "Cooldown").replace("%value", command.getCooldown() + "")); 273 | Messaging.send(sender, prefix + infoLine.replace("%name", "In Console").replace("%value", command.inConsole() + "") 274 | + spacer + infoLine.replace("%name", "As Op").replace("%value", command.asOp() + "")); 275 | } 276 | }else{ 277 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NoCommands.getSetting()); 278 | } 279 | } 280 | 281 | @Command(aliases = { "npc" }, usage = "cmdreset", desc = "Reset the commands on the NPC.", modifiers = { "cmdreset" }, min = 1, max = 1, permission = "commandnpc.admin") 282 | public void resetCmds(CommandContext args, CommandSender sender, NPC npc) { 283 | int id = npc.getId(); 284 | if(CommandNPC.getCommandManager().hasNPCData(id)) { 285 | CommandNPC.getCommandManager().removeNPCData(id); 286 | CommandNPC.getCommandDatabase().deleteNPC(id); 287 | Messaging.send(sender, LanguageSettings.Commands_Citizens_Reset.getSetting()); 288 | }else{ 289 | Messaging.sendError(sender, LanguageSettings.Commands_Citizens_NoCommands.getSetting()); 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/main/java/me/messageofdeath/commandnpc/Utilities/Metrics/Metrics.java: -------------------------------------------------------------------------------- 1 | package me.messageofdeath.commandnpc.Utilities.Metrics; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.configuration.file.YamlConfiguration; 5 | import org.bukkit.plugin.ServicePriority; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | import org.json.simple.JSONArray; 8 | import org.json.simple.JSONObject; 9 | 10 | import javax.net.ssl.HttpsURLConnection; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.DataOutputStream; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.net.URL; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Locale; 21 | import java.util.Map; 22 | import java.util.Timer; 23 | import java.util.TimerTask; 24 | import java.util.UUID; 25 | import java.util.logging.Level; 26 | import java.util.zip.GZIPOutputStream; 27 | 28 | /** 29 | * bStats collects some data for plugin authors. 30 | * 31 | * Check out https://bStats.org/ to learn more about bStats! 32 | */ 33 | public class Metrics { 34 | 35 | static { 36 | // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D 37 | final String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's' }); 38 | final String examplePackage = new String(new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' }); 39 | // We want to make sure nobody just copy & pastes the example and use the wrong package names 40 | if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { 41 | throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); 42 | } 43 | } 44 | 45 | // The version of this bStats class 46 | private static final int B_STATS_VERSION = 1; 47 | 48 | // The url to which the data is sent 49 | private static final String URL = "https://bStats.org/submitData/bukkit"; 50 | 51 | // Should failed requests be logged? 52 | private static boolean logFailedRequests; 53 | 54 | // The uuid of the server 55 | private static String serverUUID; 56 | 57 | // The plugin 58 | private final JavaPlugin plugin; 59 | 60 | // A list with all custom charts 61 | private final List charts = new ArrayList<>(); 62 | 63 | /** 64 | * Class constructor. 65 | * 66 | * @param plugin The plugin which stats should be submitted. 67 | */ 68 | public Metrics(JavaPlugin plugin) { 69 | if (plugin == null) { 70 | throw new IllegalArgumentException("Plugin cannot be null!"); 71 | } 72 | this.plugin = plugin; 73 | 74 | // Get the config file 75 | File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); 76 | File configFile = new File(bStatsFolder, "config.yml"); 77 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 78 | 79 | // Check if the config file exists 80 | if (!config.isSet("serverUuid")) { 81 | 82 | // Add default values 83 | config.addDefault("enabled", true); 84 | // Every server gets it's unique random id. 85 | config.addDefault("serverUuid", UUID.randomUUID().toString()); 86 | // Should failed request be logged? 87 | config.addDefault("logFailedRequests", false); 88 | 89 | // Inform the server owners about bStats 90 | config.options().header( 91 | "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + 92 | "To honor their work, you should not disable it.\n" + 93 | "This has nearly no effect on the server performance!\n" + 94 | "Check out https://bStats.org/ to learn more :)" 95 | ).copyDefaults(true); 96 | try { 97 | config.save(configFile); 98 | } catch (IOException ignored) { } 99 | } 100 | 101 | // Load the data 102 | serverUUID = config.getString("serverUuid"); 103 | logFailedRequests = config.getBoolean("logFailedRequests", false); 104 | if (config.getBoolean("enabled", true)) { 105 | boolean found = false; 106 | // Search for all other bStats Metrics classes to see if we are the first one 107 | for (Class service : Bukkit.getServicesManager().getKnownServices()) { 108 | try { 109 | service.getField("B_STATS_VERSION"); // Our identifier :) 110 | found = true; // We aren't the first 111 | break; 112 | } catch (NoSuchFieldException ignored) { } 113 | } 114 | // Register our service 115 | Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); 116 | if (!found) { 117 | // We are the first! 118 | startSubmitting(); 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Adds a custom chart. 125 | * 126 | * @param chart The chart to add. 127 | */ 128 | public void addCustomChart(CustomChart chart) { 129 | if (chart == null) { 130 | throw new IllegalArgumentException("Chart cannot be null!"); 131 | } 132 | charts.add(chart); 133 | } 134 | 135 | /** 136 | * Starts the Scheduler which submits our data every 30 minutes. 137 | */ 138 | private void startSubmitting() { 139 | final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags 140 | timer.scheduleAtFixedRate(new TimerTask() { 141 | @Override 142 | public void run() { 143 | if (!plugin.isEnabled()) { // Plugin was disabled 144 | timer.cancel(); 145 | return; 146 | } 147 | // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler 148 | // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) 149 | Bukkit.getScheduler().runTask(plugin, new Runnable() { 150 | @Override 151 | public void run() { 152 | submitData(); 153 | } 154 | }); 155 | } 156 | }, 1000*60*5, 1000*60*30); 157 | // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start 158 | // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! 159 | // WARNING: Just don't do it! 160 | } 161 | 162 | /** 163 | * Gets the plugin specific data. 164 | * This method is called using Reflection. 165 | * 166 | * @return The plugin specific data. 167 | */ 168 | public JSONObject getPluginData() { 169 | JSONObject data = new JSONObject(); 170 | 171 | String pluginName = plugin.getDescription().getName(); 172 | String pluginVersion = plugin.getDescription().getVersion(); 173 | 174 | data.put("pluginName", pluginName); // Append the name of the plugin 175 | data.put("pluginVersion", pluginVersion); // Append the version of the plugin 176 | JSONArray customCharts = new JSONArray(); 177 | for (CustomChart customChart : charts) { 178 | // Add the data of the custom charts 179 | JSONObject chart = customChart.getRequestJsonObject(); 180 | if (chart == null) { // If the chart is null, we skip it 181 | continue; 182 | } 183 | customCharts.add(chart); 184 | } 185 | data.put("customCharts", customCharts); 186 | 187 | return data; 188 | } 189 | 190 | /** 191 | * Gets the server specific data. 192 | * 193 | * @return The server specific data. 194 | */ 195 | private JSONObject getServerData() { 196 | // Minecraft specific data 197 | int playerAmount = Bukkit.getOnlinePlayers().size(); 198 | int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; 199 | String bukkitVersion = org.bukkit.Bukkit.getVersion(); 200 | bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); 201 | 202 | // OS/Java specific data 203 | String javaVersion = System.getProperty("java.version"); 204 | String osName = System.getProperty("os.name"); 205 | String osArch = System.getProperty("os.arch"); 206 | String osVersion = System.getProperty("os.version"); 207 | int coreCount = Runtime.getRuntime().availableProcessors(); 208 | 209 | JSONObject data = new JSONObject(); 210 | 211 | data.put("serverUUID", serverUUID); 212 | 213 | data.put("playerAmount", playerAmount); 214 | data.put("onlineMode", onlineMode); 215 | data.put("bukkitVersion", bukkitVersion); 216 | 217 | data.put("javaVersion", javaVersion); 218 | data.put("osName", osName); 219 | data.put("osArch", osArch); 220 | data.put("osVersion", osVersion); 221 | data.put("coreCount", coreCount); 222 | 223 | return data; 224 | } 225 | 226 | /** 227 | * Collects the data and sends it afterwards. 228 | */ 229 | private void submitData() { 230 | final JSONObject data = getServerData(); 231 | 232 | JSONArray pluginData = new JSONArray(); 233 | // Search for all other bStats Metrics classes to get their plugin data 234 | for (Class service : Bukkit.getServicesManager().getKnownServices()) { 235 | try { 236 | service.getField("B_STATS_VERSION"); // Our identifier :) 237 | } catch (NoSuchFieldException ignored) { 238 | continue; // Continue "searching" 239 | } 240 | // Found one! 241 | try { 242 | pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service))); 243 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } 244 | } 245 | 246 | data.put("plugins", pluginData); 247 | 248 | // Create a new thread for the connection to the bStats server 249 | new Thread(new Runnable() { 250 | @Override 251 | public void run() { 252 | try { 253 | // Send the data 254 | sendData(data); 255 | } catch (Exception e) { 256 | // Something went wrong! :( 257 | if (logFailedRequests) { 258 | plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); 259 | } 260 | } 261 | } 262 | }).start(); 263 | } 264 | 265 | /** 266 | * Sends the data to the bStats server. 267 | * 268 | * @param data The data to send. 269 | * @throws Exception If the request failed. 270 | */ 271 | private static void sendData(JSONObject data) throws Exception { 272 | if (data == null) { 273 | throw new IllegalArgumentException("Data cannot be null!"); 274 | } 275 | if (Bukkit.isPrimaryThread()) { 276 | throw new IllegalAccessException("This method must not be called from the main thread!"); 277 | } 278 | HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); 279 | 280 | // Compress the data to save bandwidth 281 | byte[] compressedData = compress(data.toString()); 282 | 283 | // Add headers 284 | connection.setRequestMethod("POST"); 285 | connection.addRequestProperty("Accept", "application/json"); 286 | connection.addRequestProperty("Connection", "close"); 287 | connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request 288 | connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); 289 | connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format 290 | connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); 291 | 292 | // Send data 293 | connection.setDoOutput(true); 294 | DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); 295 | outputStream.write(compressedData); 296 | outputStream.flush(); 297 | outputStream.close(); 298 | 299 | connection.getInputStream().close(); // We don't care about the response - Just send our data :) 300 | } 301 | 302 | /** 303 | * Gzips the given String. 304 | * 305 | * @param str The string to gzip. 306 | * @return The gzipped String. 307 | * @throws IOException If the compression failed. 308 | */ 309 | private static byte[] compress(final String str) throws IOException { 310 | if (str == null) { 311 | return null; 312 | } 313 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 314 | GZIPOutputStream gzip = new GZIPOutputStream(outputStream); 315 | gzip.write(str.getBytes("UTF-8")); 316 | gzip.close(); 317 | return outputStream.toByteArray(); 318 | } 319 | 320 | /** 321 | * Represents a custom chart. 322 | */ 323 | public static abstract class CustomChart { 324 | 325 | // The id of the chart 326 | protected final String chartId; 327 | 328 | /** 329 | * Class constructor. 330 | * 331 | * @param chartId The id of the chart. 332 | */ 333 | public CustomChart(String chartId) { 334 | if (chartId == null || chartId.isEmpty()) { 335 | throw new IllegalArgumentException("ChartId cannot be null or empty!"); 336 | } 337 | this.chartId = chartId; 338 | } 339 | 340 | protected JSONObject getRequestJsonObject() { 341 | JSONObject chart = new JSONObject(); 342 | chart.put("chartId", chartId); 343 | try { 344 | JSONObject data = getChartData(); 345 | if (data == null) { 346 | // If the data is null we don't send the chart. 347 | return null; 348 | } 349 | chart.put("data", data); 350 | } catch (Throwable t) { 351 | if (logFailedRequests) { 352 | Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); 353 | } 354 | return null; 355 | } 356 | return chart; 357 | } 358 | 359 | protected abstract JSONObject getChartData(); 360 | 361 | } 362 | 363 | /** 364 | * Represents a custom simple pie. 365 | */ 366 | public static abstract class SimplePie extends CustomChart { 367 | 368 | /** 369 | * Class constructor. 370 | * 371 | * @param chartId The id of the chart. 372 | */ 373 | public SimplePie(String chartId) { 374 | super(chartId); 375 | } 376 | 377 | /** 378 | * Gets the value of the pie. 379 | * 380 | * @return The value of the pie. 381 | */ 382 | public abstract String getValue(); 383 | 384 | @Override 385 | protected JSONObject getChartData() { 386 | JSONObject data = new JSONObject(); 387 | String value = getValue(); 388 | if (value == null || value.isEmpty()) { 389 | // Null = skip the chart 390 | return null; 391 | } 392 | data.put("value", value); 393 | return data; 394 | } 395 | } 396 | 397 | /** 398 | * Represents a custom advanced pie. 399 | */ 400 | public static abstract class AdvancedPie extends CustomChart { 401 | 402 | /** 403 | * Class constructor. 404 | * 405 | * @param chartId The id of the chart. 406 | */ 407 | public AdvancedPie(String chartId) { 408 | super(chartId); 409 | } 410 | 411 | /** 412 | * Gets the values of the pie. 413 | * 414 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. 415 | * You don't have to create a map yourself! 416 | * @return The values of the pie. 417 | */ 418 | public abstract HashMap getValues(HashMap valueMap); 419 | 420 | @Override 421 | protected JSONObject getChartData() { 422 | JSONObject data = new JSONObject(); 423 | JSONObject values = new JSONObject(); 424 | HashMap map = getValues(new HashMap()); 425 | if (map == null || map.isEmpty()) { 426 | // Null = skip the chart 427 | return null; 428 | } 429 | boolean allSkipped = true; 430 | for (Map.Entry entry : map.entrySet()) { 431 | if (entry.getValue() == 0) { 432 | continue; // Skip this invalid 433 | } 434 | allSkipped = false; 435 | values.put(entry.getKey(), entry.getValue()); 436 | } 437 | if (allSkipped) { 438 | // Null = skip the chart 439 | return null; 440 | } 441 | data.put("values", values); 442 | return data; 443 | } 444 | } 445 | 446 | /** 447 | * Represents a custom single line chart. 448 | */ 449 | public static abstract class SingleLineChart extends CustomChart { 450 | 451 | /** 452 | * Class constructor. 453 | * 454 | * @param chartId The id of the chart. 455 | */ 456 | public SingleLineChart(String chartId) { 457 | super(chartId); 458 | } 459 | 460 | /** 461 | * Gets the value of the chart. 462 | * 463 | * @return The value of the chart. 464 | */ 465 | public abstract int getValue(); 466 | 467 | @Override 468 | protected JSONObject getChartData() { 469 | JSONObject data = new JSONObject(); 470 | int value = getValue(); 471 | if (value == 0) { 472 | // Null = skip the chart 473 | return null; 474 | } 475 | data.put("value", value); 476 | return data; 477 | } 478 | 479 | } 480 | 481 | /** 482 | * Represents a custom multi line chart. 483 | */ 484 | public static abstract class MultiLineChart extends CustomChart { 485 | 486 | /** 487 | * Class constructor. 488 | * 489 | * @param chartId The id of the chart. 490 | */ 491 | public MultiLineChart(String chartId) { 492 | super(chartId); 493 | } 494 | 495 | /** 496 | * Gets the values of the chart. 497 | * 498 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. 499 | * You don't have to create a map yourself! 500 | * @return The values of the chart. 501 | */ 502 | public abstract HashMap getValues(HashMap valueMap); 503 | 504 | @Override 505 | protected JSONObject getChartData() { 506 | JSONObject data = new JSONObject(); 507 | JSONObject values = new JSONObject(); 508 | HashMap map = getValues(new HashMap()); 509 | if (map == null || map.isEmpty()) { 510 | // Null = skip the chart 511 | return null; 512 | } 513 | boolean allSkipped = true; 514 | for (Map.Entry entry : map.entrySet()) { 515 | if (entry.getValue() == 0) { 516 | continue; // Skip this invalid 517 | } 518 | allSkipped = false; 519 | values.put(entry.getKey(), entry.getValue()); 520 | } 521 | if (allSkipped) { 522 | // Null = skip the chart 523 | return null; 524 | } 525 | data.put("values", values); 526 | return data; 527 | } 528 | 529 | } 530 | 531 | /** 532 | * Represents a custom simple bar chart. 533 | */ 534 | public static abstract class SimpleBarChart extends CustomChart { 535 | 536 | /** 537 | * Class constructor. 538 | * 539 | * @param chartId The id of the chart. 540 | */ 541 | public SimpleBarChart(String chartId) { 542 | super(chartId); 543 | } 544 | 545 | /** 546 | * Gets the value of the chart. 547 | * 548 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. 549 | * You don't have to create a map yourself! 550 | * @return The value of the chart. 551 | */ 552 | public abstract HashMap getValues(HashMap valueMap); 553 | 554 | @Override 555 | protected JSONObject getChartData() { 556 | JSONObject data = new JSONObject(); 557 | JSONObject values = new JSONObject(); 558 | HashMap map = getValues(new HashMap()); 559 | if (map == null || map.isEmpty()) { 560 | // Null = skip the chart 561 | return null; 562 | } 563 | for (Map.Entry entry : map.entrySet()) { 564 | JSONArray categoryValues = new JSONArray(); 565 | categoryValues.add(entry.getValue()); 566 | values.put(entry.getKey(), categoryValues); 567 | } 568 | data.put("values", values); 569 | return data; 570 | } 571 | 572 | } 573 | 574 | /** 575 | * Represents a custom advanced bar chart. 576 | */ 577 | public static abstract class AdvancedBarChart extends CustomChart { 578 | 579 | /** 580 | * Class constructor. 581 | * 582 | * @param chartId The id of the chart. 583 | */ 584 | public AdvancedBarChart(String chartId) { 585 | super(chartId); 586 | } 587 | 588 | /** 589 | * Gets the value of the chart. 590 | * 591 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. 592 | * You don't have to create a map yourself! 593 | * @return The value of the chart. 594 | */ 595 | public abstract HashMap getValues(HashMap valueMap); 596 | 597 | @Override 598 | protected JSONObject getChartData() { 599 | JSONObject data = new JSONObject(); 600 | JSONObject values = new JSONObject(); 601 | HashMap map = getValues(new HashMap()); 602 | if (map == null || map.isEmpty()) { 603 | // Null = skip the chart 604 | return null; 605 | } 606 | boolean allSkipped = true; 607 | for (Map.Entry entry : map.entrySet()) { 608 | if (entry.getValue().length == 0) { 609 | continue; // Skip this invalid 610 | } 611 | allSkipped = false; 612 | JSONArray categoryValues = new JSONArray(); 613 | for (int categoryValue : entry.getValue()) { 614 | categoryValues.add(categoryValue); 615 | } 616 | values.put(entry.getKey(), categoryValues); 617 | } 618 | if (allSkipped) { 619 | // Null = skip the chart 620 | return null; 621 | } 622 | data.put("values", values); 623 | return data; 624 | } 625 | 626 | } 627 | 628 | /** 629 | * Represents a custom simple map chart. 630 | */ 631 | public static abstract class SimpleMapChart extends CustomChart { 632 | 633 | /** 634 | * Class constructor. 635 | * 636 | * @param chartId The id of the chart. 637 | */ 638 | public SimpleMapChart(String chartId) { 639 | super(chartId); 640 | } 641 | 642 | /** 643 | * Gets the value of the chart. 644 | * 645 | * @return The value of the chart. 646 | */ 647 | public abstract Country getValue(); 648 | 649 | @Override 650 | protected JSONObject getChartData() { 651 | JSONObject data = new JSONObject(); 652 | Country value = getValue(); 653 | 654 | if (value == null) { 655 | // Null = skip the chart 656 | return null; 657 | } 658 | data.put("value", value.getCountryIsoTag()); 659 | return data; 660 | } 661 | 662 | } 663 | 664 | /** 665 | * Represents a custom advanced map chart. 666 | */ 667 | public static abstract class AdvancedMapChart extends CustomChart { 668 | 669 | /** 670 | * Class constructor. 671 | * 672 | * @param chartId The id of the chart. 673 | */ 674 | public AdvancedMapChart(String chartId) { 675 | super(chartId); 676 | } 677 | 678 | /** 679 | * Gets the value of the chart. 680 | * 681 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. 682 | * You don't have to create a map yourself! 683 | * @return The value of the chart. 684 | */ 685 | public abstract HashMap getValues(HashMap valueMap); 686 | 687 | @Override 688 | protected JSONObject getChartData() { 689 | JSONObject data = new JSONObject(); 690 | JSONObject values = new JSONObject(); 691 | HashMap map = getValues(new HashMap()); 692 | if (map == null || map.isEmpty()) { 693 | // Null = skip the chart 694 | return null; 695 | } 696 | boolean allSkipped = true; 697 | for (Map.Entry entry : map.entrySet()) { 698 | if (entry.getValue() == 0) { 699 | continue; // Skip this invalid 700 | } 701 | allSkipped = false; 702 | values.put(entry.getKey().getCountryIsoTag(), entry.getValue()); 703 | } 704 | if (allSkipped) { 705 | // Null = skip the chart 706 | return null; 707 | } 708 | data.put("values", values); 709 | return data; 710 | } 711 | 712 | } 713 | 714 | /** 715 | * A enum which is used for custom maps. 716 | */ 717 | public enum Country { 718 | 719 | /** 720 | * bStats will use the country of the server. 721 | */ 722 | AUTO_DETECT("AUTO", "Auto Detected"), 723 | 724 | ANDORRA("AD", "Andorra"), 725 | UNITED_ARAB_EMIRATES("AE", "United Arab Emirates"), 726 | AFGHANISTAN("AF", "Afghanistan"), 727 | ANTIGUA_AND_BARBUDA("AG", "Antigua and Barbuda"), 728 | ANGUILLA("AI", "Anguilla"), 729 | ALBANIA("AL", "Albania"), 730 | ARMENIA("AM", "Armenia"), 731 | NETHERLANDS_ANTILLES("AN", "Netherlands Antilles"), 732 | ANGOLA("AO", "Angola"), 733 | ANTARCTICA("AQ", "Antarctica"), 734 | ARGENTINA("AR", "Argentina"), 735 | AMERICAN_SAMOA("AS", "American Samoa"), 736 | AUSTRIA("AT", "Austria"), 737 | AUSTRALIA("AU", "Australia"), 738 | ARUBA("AW", "Aruba"), 739 | ALAND_ISLANDS("AX", "Åland Islands"), 740 | AZERBAIJAN("AZ", "Azerbaijan"), 741 | BOSNIA_AND_HERZEGOVINA("BA", "Bosnia and Herzegovina"), 742 | BARBADOS("BB", "Barbados"), 743 | BANGLADESH("BD", "Bangladesh"), 744 | BELGIUM("BE", "Belgium"), 745 | BURKINA_FASO("BF", "Burkina Faso"), 746 | BULGARIA("BG", "Bulgaria"), 747 | BAHRAIN("BH", "Bahrain"), 748 | BURUNDI("BI", "Burundi"), 749 | BENIN("BJ", "Benin"), 750 | SAINT_BARTHELEMY("BL", "Saint Barthélemy"), 751 | BERMUDA("BM", "Bermuda"), 752 | BRUNEI("BN", "Brunei"), 753 | BOLIVIA("BO", "Bolivia"), 754 | BONAIRE_SINT_EUSTATIUS_AND_SABA("BQ", "Bonaire, Sint Eustatius and Saba"), 755 | BRAZIL("BR", "Brazil"), 756 | BAHAMAS("BS", "Bahamas"), 757 | BHUTAN("BT", "Bhutan"), 758 | BOUVET_ISLAND("BV", "Bouvet Island"), 759 | BOTSWANA("BW", "Botswana"), 760 | BELARUS("BY", "Belarus"), 761 | BELIZE("BZ", "Belize"), 762 | CANADA("CA", "Canada"), 763 | COCOS_ISLANDS("CC", "Cocos Islands"), 764 | THE_DEMOCRATIC_REPUBLIC_OF_CONGO("CD", "The Democratic Republic Of Congo"), 765 | CENTRAL_AFRICAN_REPUBLIC("CF", "Central African Republic"), 766 | CONGO("CG", "Congo"), 767 | SWITZERLAND("CH", "Switzerland"), 768 | COTE_D_IVOIRE("CI", "Côte d'Ivoire"), 769 | COOK_ISLANDS("CK", "Cook Islands"), 770 | CHILE("CL", "Chile"), 771 | CAMEROON("CM", "Cameroon"), 772 | CHINA("CN", "China"), 773 | COLOMBIA("CO", "Colombia"), 774 | COSTA_RICA("CR", "Costa Rica"), 775 | CUBA("CU", "Cuba"), 776 | CAPE_VERDE("CV", "Cape Verde"), 777 | CURACAO("CW", "Curaçao"), 778 | CHRISTMAS_ISLAND("CX", "Christmas Island"), 779 | CYPRUS("CY", "Cyprus"), 780 | CZECH_REPUBLIC("CZ", "Czech Republic"), 781 | GERMANY("DE", "Germany"), 782 | DJIBOUTI("DJ", "Djibouti"), 783 | DENMARK("DK", "Denmark"), 784 | DOMINICA("DM", "Dominica"), 785 | DOMINICAN_REPUBLIC("DO", "Dominican Republic"), 786 | ALGERIA("DZ", "Algeria"), 787 | ECUADOR("EC", "Ecuador"), 788 | ESTONIA("EE", "Estonia"), 789 | EGYPT("EG", "Egypt"), 790 | WESTERN_SAHARA("EH", "Western Sahara"), 791 | ERITREA("ER", "Eritrea"), 792 | SPAIN("ES", "Spain"), 793 | ETHIOPIA("ET", "Ethiopia"), 794 | FINLAND("FI", "Finland"), 795 | FIJI("FJ", "Fiji"), 796 | FALKLAND_ISLANDS("FK", "Falkland Islands"), 797 | MICRONESIA("FM", "Micronesia"), 798 | FAROE_ISLANDS("FO", "Faroe Islands"), 799 | FRANCE("FR", "France"), 800 | GABON("GA", "Gabon"), 801 | UNITED_KINGDOM("GB", "United Kingdom"), 802 | GRENADA("GD", "Grenada"), 803 | GEORGIA("GE", "Georgia"), 804 | FRENCH_GUIANA("GF", "French Guiana"), 805 | GUERNSEY("GG", "Guernsey"), 806 | GHANA("GH", "Ghana"), 807 | GIBRALTAR("GI", "Gibraltar"), 808 | GREENLAND("GL", "Greenland"), 809 | GAMBIA("GM", "Gambia"), 810 | GUINEA("GN", "Guinea"), 811 | GUADELOUPE("GP", "Guadeloupe"), 812 | EQUATORIAL_GUINEA("GQ", "Equatorial Guinea"), 813 | GREECE("GR", "Greece"), 814 | SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS("GS", "South Georgia And The South Sandwich Islands"), 815 | GUATEMALA("GT", "Guatemala"), 816 | GUAM("GU", "Guam"), 817 | GUINEA_BISSAU("GW", "Guinea-Bissau"), 818 | GUYANA("GY", "Guyana"), 819 | HONG_KONG("HK", "Hong Kong"), 820 | HEARD_ISLAND_AND_MCDONALD_ISLANDS("HM", "Heard Island And McDonald Islands"), 821 | HONDURAS("HN", "Honduras"), 822 | CROATIA("HR", "Croatia"), 823 | HAITI("HT", "Haiti"), 824 | HUNGARY("HU", "Hungary"), 825 | INDONESIA("ID", "Indonesia"), 826 | IRELAND("IE", "Ireland"), 827 | ISRAEL("IL", "Israel"), 828 | ISLE_OF_MAN("IM", "Isle Of Man"), 829 | INDIA("IN", "India"), 830 | BRITISH_INDIAN_OCEAN_TERRITORY("IO", "British Indian Ocean Territory"), 831 | IRAQ("IQ", "Iraq"), 832 | IRAN("IR", "Iran"), 833 | ICELAND("IS", "Iceland"), 834 | ITALY("IT", "Italy"), 835 | JERSEY("JE", "Jersey"), 836 | JAMAICA("JM", "Jamaica"), 837 | JORDAN("JO", "Jordan"), 838 | JAPAN("JP", "Japan"), 839 | KENYA("KE", "Kenya"), 840 | KYRGYZSTAN("KG", "Kyrgyzstan"), 841 | CAMBODIA("KH", "Cambodia"), 842 | KIRIBATI("KI", "Kiribati"), 843 | COMOROS("KM", "Comoros"), 844 | SAINT_KITTS_AND_NEVIS("KN", "Saint Kitts And Nevis"), 845 | NORTH_KOREA("KP", "North Korea"), 846 | SOUTH_KOREA("KR", "South Korea"), 847 | KUWAIT("KW", "Kuwait"), 848 | CAYMAN_ISLANDS("KY", "Cayman Islands"), 849 | KAZAKHSTAN("KZ", "Kazakhstan"), 850 | LAOS("LA", "Laos"), 851 | LEBANON("LB", "Lebanon"), 852 | SAINT_LUCIA("LC", "Saint Lucia"), 853 | LIECHTENSTEIN("LI", "Liechtenstein"), 854 | SRI_LANKA("LK", "Sri Lanka"), 855 | LIBERIA("LR", "Liberia"), 856 | LESOTHO("LS", "Lesotho"), 857 | LITHUANIA("LT", "Lithuania"), 858 | LUXEMBOURG("LU", "Luxembourg"), 859 | LATVIA("LV", "Latvia"), 860 | LIBYA("LY", "Libya"), 861 | MOROCCO("MA", "Morocco"), 862 | MONACO("MC", "Monaco"), 863 | MOLDOVA("MD", "Moldova"), 864 | MONTENEGRO("ME", "Montenegro"), 865 | SAINT_MARTIN("MF", "Saint Martin"), 866 | MADAGASCAR("MG", "Madagascar"), 867 | MARSHALL_ISLANDS("MH", "Marshall Islands"), 868 | MACEDONIA("MK", "Macedonia"), 869 | MALI("ML", "Mali"), 870 | MYANMAR("MM", "Myanmar"), 871 | MONGOLIA("MN", "Mongolia"), 872 | MACAO("MO", "Macao"), 873 | NORTHERN_MARIANA_ISLANDS("MP", "Northern Mariana Islands"), 874 | MARTINIQUE("MQ", "Martinique"), 875 | MAURITANIA("MR", "Mauritania"), 876 | MONTSERRAT("MS", "Montserrat"), 877 | MALTA("MT", "Malta"), 878 | MAURITIUS("MU", "Mauritius"), 879 | MALDIVES("MV", "Maldives"), 880 | MALAWI("MW", "Malawi"), 881 | MEXICO("MX", "Mexico"), 882 | MALAYSIA("MY", "Malaysia"), 883 | MOZAMBIQUE("MZ", "Mozambique"), 884 | NAMIBIA("NA", "Namibia"), 885 | NEW_CALEDONIA("NC", "New Caledonia"), 886 | NIGER("NE", "Niger"), 887 | NORFOLK_ISLAND("NF", "Norfolk Island"), 888 | NIGERIA("NG", "Nigeria"), 889 | NICARAGUA("NI", "Nicaragua"), 890 | NETHERLANDS("NL", "Netherlands"), 891 | NORWAY("NO", "Norway"), 892 | NEPAL("NP", "Nepal"), 893 | NAURU("NR", "Nauru"), 894 | NIUE("NU", "Niue"), 895 | NEW_ZEALAND("NZ", "New Zealand"), 896 | OMAN("OM", "Oman"), 897 | PANAMA("PA", "Panama"), 898 | PERU("PE", "Peru"), 899 | FRENCH_POLYNESIA("PF", "French Polynesia"), 900 | PAPUA_NEW_GUINEA("PG", "Papua New Guinea"), 901 | PHILIPPINES("PH", "Philippines"), 902 | PAKISTAN("PK", "Pakistan"), 903 | POLAND("PL", "Poland"), 904 | SAINT_PIERRE_AND_MIQUELON("PM", "Saint Pierre And Miquelon"), 905 | PITCAIRN("PN", "Pitcairn"), 906 | PUERTO_RICO("PR", "Puerto Rico"), 907 | PALESTINE("PS", "Palestine"), 908 | PORTUGAL("PT", "Portugal"), 909 | PALAU("PW", "Palau"), 910 | PARAGUAY("PY", "Paraguay"), 911 | QATAR("QA", "Qatar"), 912 | REUNION("RE", "Reunion"), 913 | ROMANIA("RO", "Romania"), 914 | SERBIA("RS", "Serbia"), 915 | RUSSIA("RU", "Russia"), 916 | RWANDA("RW", "Rwanda"), 917 | SAUDI_ARABIA("SA", "Saudi Arabia"), 918 | SOLOMON_ISLANDS("SB", "Solomon Islands"), 919 | SEYCHELLES("SC", "Seychelles"), 920 | SUDAN("SD", "Sudan"), 921 | SWEDEN("SE", "Sweden"), 922 | SINGAPORE("SG", "Singapore"), 923 | SAINT_HELENA("SH", "Saint Helena"), 924 | SLOVENIA("SI", "Slovenia"), 925 | SVALBARD_AND_JAN_MAYEN("SJ", "Svalbard And Jan Mayen"), 926 | SLOVAKIA("SK", "Slovakia"), 927 | SIERRA_LEONE("SL", "Sierra Leone"), 928 | SAN_MARINO("SM", "San Marino"), 929 | SENEGAL("SN", "Senegal"), 930 | SOMALIA("SO", "Somalia"), 931 | SURINAME("SR", "Suriname"), 932 | SOUTH_SUDAN("SS", "South Sudan"), 933 | SAO_TOME_AND_PRINCIPE("ST", "Sao Tome And Principe"), 934 | EL_SALVADOR("SV", "El Salvador"), 935 | SINT_MAARTEN_DUTCH_PART("SX", "Sint Maarten (Dutch part)"), 936 | SYRIA("SY", "Syria"), 937 | SWAZILAND("SZ", "Swaziland"), 938 | TURKS_AND_CAICOS_ISLANDS("TC", "Turks And Caicos Islands"), 939 | CHAD("TD", "Chad"), 940 | FRENCH_SOUTHERN_TERRITORIES("TF", "French Southern Territories"), 941 | TOGO("TG", "Togo"), 942 | THAILAND("TH", "Thailand"), 943 | TAJIKISTAN("TJ", "Tajikistan"), 944 | TOKELAU("TK", "Tokelau"), 945 | TIMOR_LESTE("TL", "Timor-Leste"), 946 | TURKMENISTAN("TM", "Turkmenistan"), 947 | TUNISIA("TN", "Tunisia"), 948 | TONGA("TO", "Tonga"), 949 | TURKEY("TR", "Turkey"), 950 | TRINIDAD_AND_TOBAGO("TT", "Trinidad and Tobago"), 951 | TUVALU("TV", "Tuvalu"), 952 | TAIWAN("TW", "Taiwan"), 953 | TANZANIA("TZ", "Tanzania"), 954 | UKRAINE("UA", "Ukraine"), 955 | UGANDA("UG", "Uganda"), 956 | UNITED_STATES_MINOR_OUTLYING_ISLANDS("UM", "United States Minor Outlying Islands"), 957 | UNITED_STATES("US", "United States"), 958 | URUGUAY("UY", "Uruguay"), 959 | UZBEKISTAN("UZ", "Uzbekistan"), 960 | VATICAN("VA", "Vatican"), 961 | SAINT_VINCENT_AND_THE_GRENADINES("VC", "Saint Vincent And The Grenadines"), 962 | VENEZUELA("VE", "Venezuela"), 963 | BRITISH_VIRGIN_ISLANDS("VG", "British Virgin Islands"), 964 | U_S__VIRGIN_ISLANDS("VI", "U.S. Virgin Islands"), 965 | VIETNAM("VN", "Vietnam"), 966 | VANUATU("VU", "Vanuatu"), 967 | WALLIS_AND_FUTUNA("WF", "Wallis And Futuna"), 968 | SAMOA("WS", "Samoa"), 969 | YEMEN("YE", "Yemen"), 970 | MAYOTTE("YT", "Mayotte"), 971 | SOUTH_AFRICA("ZA", "South Africa"), 972 | ZAMBIA("ZM", "Zambia"), 973 | ZIMBABWE("ZW", "Zimbabwe"); 974 | 975 | private String isoTag; 976 | private String name; 977 | 978 | Country(String isoTag, String name) { 979 | this.isoTag = isoTag; 980 | this.name = name; 981 | } 982 | 983 | /** 984 | * Gets the name of the country. 985 | * 986 | * @return The name of the country. 987 | */ 988 | public String getCountryName() { 989 | return name; 990 | } 991 | 992 | /** 993 | * Gets the iso tag of the country. 994 | * 995 | * @return The iso tag of the country. 996 | */ 997 | public String getCountryIsoTag() { 998 | return isoTag; 999 | } 1000 | 1001 | /** 1002 | * Gets a country by it's iso tag. 1003 | * 1004 | * @param isoTag The iso tag of the county. 1005 | * @return The country with the given iso tag or null if unknown. 1006 | */ 1007 | public static Country byIsoTag(String isoTag) { 1008 | for (Country country : Country.values()) { 1009 | if (country.getCountryIsoTag().equals(isoTag)) { 1010 | return country; 1011 | } 1012 | } 1013 | return null; 1014 | } 1015 | 1016 | /** 1017 | * Gets a country by a locale. 1018 | * 1019 | * @param locale The locale. 1020 | * @return The country from the giben locale or null if unknown country or 1021 | * if the locale does not contain a country. 1022 | */ 1023 | public static Country byLocale(Locale locale) { 1024 | return byIsoTag(locale.getCountry()); 1025 | } 1026 | 1027 | } 1028 | 1029 | } --------------------------------------------------------------------------------