commit d87c25fcb22392d9e86202832d8921e39c399f84 Author: Logan Magnan Date: Sun Sep 22 05:05:09 2024 -0400 Initial commit. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a4ce163 --- /dev/null +++ b/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + com.loganmagnan + PluginBase + 1.0 + + + 21 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + + + + + + src/main/resources + true + + + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + fawe-repo + https://ci.athion.net/job/FastAsyncWorldEdit/ws/mvn/ + + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + + + codemc-snapshots + https://repo.codemc.io/repository/maven-snapshots/ + + + + + + io.papermc.paper + paper-api + 1.21-R0.1-SNAPSHOT + provided + + + org.projectlombok + lombok + 1.18.30 + provided + + + \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/PluginBase.java b/src/main/java/com/loganmagnan/pluginbase/PluginBase.java new file mode 100644 index 0000000..2632407 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/PluginBase.java @@ -0,0 +1,87 @@ +package com.loganmagnan.pluginbase; + +import com.loganmagnan.pluginbase.menusystem.PlayerMenuUtil; +import com.loganmagnan.pluginbase.utils.ClassRegistrationUtils; +import com.loganmagnan.pluginbase.utils.ColorUtils; +import com.loganmagnan.pluginbase.utils.Utils; +import com.loganmagnan.pluginbase.utils.command.CommandFramework; +import com.loganmagnan.pluginbase.utils.config.FileConfig; +import com.loganmagnan.pluginbase.utils.config.file.Config; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import java.util.HashMap; + +@Getter +@Setter +public class PluginBase extends JavaPlugin { + + // Main Class Instance + @Getter private static PluginBase instance; + + // Configuration Files + private Config mainConfig; + private FileConfig messagesConfig; + + // Managers + + // Menu System + private HashMap playerMenuUtilMap = new HashMap<>(); + + // Command Framework + private CommandFramework commandFramework = new CommandFramework(this); + + @Override + public void onEnable() { + instance = this; + + this.saveDefaultConfig(); + this.mainConfig = new Config("config", this); + this.messagesConfig = new FileConfig(this, "messages.yml"); + + this.getServer().getConsoleSender().sendMessage(Utils.chatBar); + this.getServer().getConsoleSender().sendMessage(ColorUtils.getMessageType("&dPluginBase &7- &av" + this.getDescription().getVersion())); + this.getServer().getConsoleSender().sendMessage(ColorUtils.getMessageType("&7Made by &eLoganM Development")); + this.getServer().getConsoleSender().sendMessage(Utils.chatBar); + this.loadCommands(); + this.loadManagers(); + this.loadListeners(); + this.loadRunnables(); + } + + @Override + public void onDisable() { + instance = null; + } + + private void loadCommands() { + ClassRegistrationUtils.loadCommands("com.loganmagnan.pluginbase.commands"); + } + + private void loadManagers() { + + } + + private void loadListeners() { + ClassRegistrationUtils.loadListeners("com.loganmagnan.pluginbase.listeners"); + } + + private void loadRunnables() { + + } + + public PlayerMenuUtil getPlayerMenuUtil(Player player) { + PlayerMenuUtil playerMenuUtil; + + if (playerMenuUtilMap.containsKey(player)) { + return playerMenuUtilMap.get(player); + } else { + playerMenuUtil = new PlayerMenuUtil(player); + + playerMenuUtilMap.put(player, playerMenuUtil); + + return playerMenuUtil; + } + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/chatcolor/ColorSet.java b/src/main/java/com/loganmagnan/pluginbase/chatcolor/ColorSet.java new file mode 100644 index 0000000..8c8da2d --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/chatcolor/ColorSet.java @@ -0,0 +1,17 @@ +package com.loganmagnan.pluginbase.chatcolor; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class ColorSet { + + private R red = null; + private G green = null; + private B blue = null; +} diff --git a/src/main/java/com/loganmagnan/pluginbase/chatcolor/GradientColor.java b/src/main/java/com/loganmagnan/pluginbase/chatcolor/GradientColor.java new file mode 100644 index 0000000..1d6133f --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/chatcolor/GradientColor.java @@ -0,0 +1,16 @@ +package com.loganmagnan.pluginbase.chatcolor; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class GradientColor { + + private ColorSet colorCodeOne; + private ColorSet colorCodeTwo; +} diff --git a/src/main/java/com/loganmagnan/pluginbase/commands/ExampleCommand.java b/src/main/java/com/loganmagnan/pluginbase/commands/ExampleCommand.java new file mode 100644 index 0000000..46f93d2 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/commands/ExampleCommand.java @@ -0,0 +1,27 @@ +package com.loganmagnan.pluginbase.commands; + +import com.loganmagnan.pluginbase.PluginBase; +import com.loganmagnan.pluginbase.utils.ColorUtils; +import com.loganmagnan.pluginbase.utils.CustomLocation; +import com.loganmagnan.pluginbase.utils.command.BaseCommand; +import com.loganmagnan.pluginbase.utils.command.Command; +import com.loganmagnan.pluginbase.utils.command.CommandArguments; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +public class ExampleCommand extends BaseCommand { + + private PluginBase main = PluginBase.getInstance(); + + @Command(name = "example", permission = "permission.example") + @Override + public void executeAs(CommandArguments command) { + Player player = command.getPlayer(); + + String[] args = command.getArgs(); + + if (args.length == 0) { + player.sendMessage(ColorUtils.getMessageType("&aThis is an example command.")); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/listeners/MenuListener.java b/src/main/java/com/loganmagnan/pluginbase/listeners/MenuListener.java new file mode 100644 index 0000000..02b1232 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/listeners/MenuListener.java @@ -0,0 +1,29 @@ +package com.loganmagnan.pluginbase.listeners; + +import com.loganmagnan.pluginbase.PluginBase; +import com.loganmagnan.pluginbase.menusystem.Menu; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.InventoryHolder; + +public class MenuListener implements Listener { + + private PluginBase main = PluginBase.getInstance(); + + @EventHandler + public void onMenuClick(InventoryClickEvent event) { + InventoryHolder inventoryHolder = event.getInventory().getHolder(); + + if (inventoryHolder instanceof Menu) { + event.setCancelled(true); + + if (event.getCurrentItem() == null) { + return; + } + + Menu menu = (Menu) inventoryHolder; + menu.handleMenu(event); + } + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/menusystem/ItemStackButton.java b/src/main/java/com/loganmagnan/pluginbase/menusystem/ItemStackButton.java new file mode 100644 index 0000000..f92313f --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/menusystem/ItemStackButton.java @@ -0,0 +1,29 @@ +package com.loganmagnan.pluginbase.menusystem; + +import com.loganmagnan.pluginbase.utils.ColorUtils; +import com.loganmagnan.pluginbase.utils.ItemBuilder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class ItemStackButton { + + private String name; + private String[] lore; + private Material material; + private int data; + private int amount; + + public ItemStack makeItemStack() { + return new ItemBuilder(this.material).name(ColorUtils.getMessageType(this.name)).lore(ColorUtils.getMessageType(Arrays.asList(this.lore))).durability(this.data).amount(this.amount).build(); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/menusystem/Menu.java b/src/main/java/com/loganmagnan/pluginbase/menusystem/Menu.java new file mode 100644 index 0000000..952cdba --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/menusystem/Menu.java @@ -0,0 +1,64 @@ +package com.loganmagnan.pluginbase.menusystem; + +import com.loganmagnan.pluginbase.utils.ItemBuilder; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Arrays; + +public abstract class Menu implements InventoryHolder { + + protected PlayerMenuUtil playerMenuUtil; + protected Inventory inventory; + protected ItemStack FILLER_GLASS = new ItemBuilder(Material.GRAY_STAINED_GLASS_PANE).durability(7).name("").build(); + + public Menu(PlayerMenuUtil playerMenuUtil) { + this.playerMenuUtil = playerMenuUtil; + } + + public abstract String getMenuName(); + + public abstract int getSlots(); + + public abstract void handleMenu(InventoryClickEvent event); + + public abstract void setMenuItems(Player player); + + public void open(Player player) { + inventory = Bukkit.createInventory(this, getSlots(), getMenuName()); + + this.setMenuItems(player); + + playerMenuUtil.getOwner().openInventory(inventory); + } + + @Override + public Inventory getInventory() { + return inventory; + } + + public void setFillerGlass(){ + for (int i = 0; i < this.getSlots(); i++) { + if (this.inventory.getItem(i) == null){ + this.inventory.setItem(i, this.FILLER_GLASS); + } + } + } + + public ItemStack makeItemStack(Material material, String displayName, String[] lore) { + ItemStack itemStack = new ItemStack(material); + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.setDisplayName(displayName); + + itemMeta.setLore(Arrays.asList(lore)); + itemStack.setItemMeta(itemMeta); + + return itemStack; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/menusystem/PaginatedMenu.java b/src/main/java/com/loganmagnan/pluginbase/menusystem/PaginatedMenu.java new file mode 100644 index 0000000..9bcc2dc --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/menusystem/PaginatedMenu.java @@ -0,0 +1,49 @@ +package com.loganmagnan.pluginbase.menusystem; + +import com.loganmagnan.pluginbase.utils.ColorUtils; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public abstract class PaginatedMenu extends Menu { + + protected int page = 0; + protected int maxItemsPerPage = 18; + protected int index = 0; + + public PaginatedMenu(PlayerMenuUtil playerMenuUtil) { + super(playerMenuUtil); + } + + public void addMenuBorder() { + ItemStack previousPageItemStack = new ItemStack(Material.OAK_BUTTON, 1); + + ItemMeta previousPageItemMeta = previousPageItemStack.getItemMeta(); + previousPageItemMeta.setDisplayName(ColorUtils.getMessageType("&ePrevious Page")); + + previousPageItemStack.setItemMeta(previousPageItemMeta); + + this.inventory.setItem(18, previousPageItemStack); + this.inventory.setItem(22, makeItemStack(Material.BARRIER, ChatColor.DARK_RED + "Close", new String[]{""})); + + ItemStack nextPageItemStack = new ItemStack(Material.STONE_BUTTON, 1); + + ItemMeta nextPageItemMeta = nextPageItemStack.getItemMeta(); + nextPageItemMeta.setDisplayName(ColorUtils.getMessageType("&eNext Page")); + + nextPageItemStack.setItemMeta(nextPageItemMeta); + + this.inventory.setItem(26, nextPageItemStack); + + for (int i = 18; i < 26; i++) { + if (this.inventory.getItem(i) == null) { + this.inventory.setItem(i, this.FILLER_GLASS); + } + } + } + + public int getMaxItemsPerPage() { + return maxItemsPerPage; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/menusystem/PlayerMenuUtil.java b/src/main/java/com/loganmagnan/pluginbase/menusystem/PlayerMenuUtil.java new file mode 100644 index 0000000..9137a38 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/menusystem/PlayerMenuUtil.java @@ -0,0 +1,20 @@ +package com.loganmagnan.pluginbase.menusystem; + +import org.bukkit.entity.Player; + +public class PlayerMenuUtil { + + private Player owner; + + public PlayerMenuUtil(Player owner) { + this.owner = owner; + } + + public Player getOwner() { + return owner; + } + + public void setOwner(Player owner) { + this.owner = owner; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/AsyncScheduler.java b/src/main/java/com/loganmagnan/pluginbase/utils/AsyncScheduler.java new file mode 100644 index 0000000..70aed45 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/AsyncScheduler.java @@ -0,0 +1,34 @@ +package com.loganmagnan.pluginbase.utils; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import lombok.experimental.UtilityClass; + +import java.util.concurrent.*; + +@UtilityClass +public class AsyncScheduler { + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4, + new ThreadFactoryBuilder().setNameFormat("Schedule CascadiaMC Thread %d").build()); + + /** + * Run a task asynchronously. + */ + public Future run(Runnable runnable) { + return scheduler.submit(runnable); + } + + /** + * Run a task after scheduled delay asynchronously. + */ + public ScheduledFuture later(Runnable runnable, long delay, TimeUnit time) { + return scheduler.schedule(runnable, delay, time); + } + + /** + * Run a task in a fixed rate asynchronously. + */ + public ScheduledFuture timer(TimerRunnable runnable, long delay, long period, TimeUnit time) { + return runnable.setScheduledFuture(scheduler.scheduleAtFixedRate(runnable, delay, period, time)); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/BlockUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/BlockUtils.java new file mode 100644 index 0000000..1cfafbf --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/BlockUtils.java @@ -0,0 +1,47 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; + +public class BlockUtils { + public static void breakBlockGroup(Player player, final Block block, final boolean useParticles) { + final Material target = block.getType(); + final Block up = block.getRelative(BlockFace.SOUTH); + final Block down = block.getRelative(BlockFace.NORTH); + final Block left = block.getRelative(BlockFace.EAST); + final Block right = block.getRelative(BlockFace.WEST); + + BlockData blockData = Material.AIR.createBlockData(); + + player.sendBlockChange(block.getLocation(), blockData); + + if (useParticles) { + block.breakNaturally(); + } else { + block.setType(Material.AIR); + } + + if (up.getType() == target) { + breakBlockGroup(player, up, useParticles); + } + + if (down.getType() == target) { + breakBlockGroup(player, down, useParticles); + } + + if (left.getType() == target) { + breakBlockGroup(player, left, useParticles); + } + + if (right.getType() == target) { + breakBlockGroup(player, right, useParticles); + } + } + + public static void breakBlockGroup(Player player, final Block block) { + breakBlockGroup(player, block, true); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/Cache.java b/src/main/java/com/loganmagnan/pluginbase/utils/Cache.java new file mode 100644 index 0000000..97d5fb7 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/Cache.java @@ -0,0 +1,29 @@ +package com.loganmagnan.pluginbase.utils; + +public class Cache { + + private T cache; + private long lastCache; + private final long refreshTimeInMilliseconds; + + public Cache(long refreshTimeInMilliseconds) { + this.refreshTimeInMilliseconds = refreshTimeInMilliseconds; + } + + public T getCache(CacheContentProvider cacheContentProvider) { + long currentTime = System.currentTimeMillis(); + if (lastCache + refreshTimeInMilliseconds < currentTime || cache == null) { + this.cache = cacheContentProvider.getObject(); + this.lastCache = currentTime; + } + return cache; + } + + public void clearCache() { + this.cache = null; + } + + public interface CacheContentProvider { + T getObject(); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/CachedInventory.java b/src/main/java/com/loganmagnan/pluginbase/utils/CachedInventory.java new file mode 100644 index 0000000..b9b8be4 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/CachedInventory.java @@ -0,0 +1,189 @@ +package com.loganmagnan.pluginbase.utils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class CachedInventory { + + private ItemStack[] cachedInventory; + + private ItemStack[] cachedArmor; + + public CachedInventory() { + + } + + private double health = -1.0D; + + private int totalExperience = -1; + private int food = -1; + + private float exp = -1.0F; + + public CachedInventory(Player player, boolean justContents) { + this.cachedInventory = player.getInventory().getContents(); + this.cachedArmor = player.getInventory().getArmorContents(); + if (!justContents) { + this.health = player.getHealth(); + this.food = player.getFoodLevel(); + this.totalExperience = player.getTotalExperience(); + this.exp = player.getExp(); + } + } + + private void delayedUpdateInventory(final Player player, JavaPlugin plugin) { + (new BukkitRunnable() { + public void run() { + player.updateInventory(); + } + }).runTaskLater((Plugin)plugin, 1); + } + + public static CachedInventory fromPlayer(Player player, boolean justContents) { + return new CachedInventory(player, justContents); + } + + public void applyToPlayer(Player player, boolean justContents) { + player.getInventory().setContents(this.cachedInventory); + player.getInventory().setArmorContents(this.cachedArmor); + if (!justContents) { + if (this.health > 0.0D) + player.setHealth(this.health); + if (this.food != -1) + player.setFoodLevel(this.food); + if (this.totalExperience != -1) + player.setTotalExperience(this.totalExperience); + if (this.exp != -1.0F) + player.setExp(this.exp); + } + } + + public void applyToPlayer(Player player, boolean justContents, JavaPlugin plugin) { + applyToPlayer(player, justContents); + delayedUpdateInventory(player, plugin); + } + + public static CachedInventory fromConfigurationSection(ConfigurationSection section) { + CachedInventory cachedInventory = new CachedInventory(); + List armorArray = section.getStringList("armor"); + List armor = new ArrayList<>(); + armorArray.forEach(armorElement -> { + try { + armor.add(ItemBuilder.itemFrom64(armorElement)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + cachedInventory.setCachedArmor((ItemStack[])armor.stream().toArray(x$0 -> new ItemStack[x$0])); + List invArray = section.getStringList("inv"); + List inv = new ArrayList<>(); + invArray.forEach(invElement -> { + try { + inv.add(ItemBuilder.itemFrom64(invElement)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + cachedInventory.setCachedInventory((ItemStack[])inv.stream().toArray(x$0 -> new ItemStack[x$0])); + if (section.contains("health")) + cachedInventory.setHealth(section.getDouble("health")); + if (section.contains("totalExperience")) + cachedInventory.setHealth(section.getInt("totalExperience")); + if (section.contains("food")) + cachedInventory.setHealth(section.getInt("food")); + if (section.contains("exp")) + cachedInventory.setHealth(section.getInt("exp")); + return cachedInventory; + } + + public ConfigurationSection toConfigurationSection(Configuration configuration, String sectionName) { + ConfigurationSection section = configuration.createSection(sectionName); + if (this.health != -1.0D) + section.set("health", Double.valueOf(this.health)); + if (this.totalExperience != -1) + section.set("totalExperience", Integer.valueOf(this.totalExperience)); + if (this.food != -1) + section.set("food", Integer.valueOf(this.food)); + if (this.exp != -1.0F) + section.set("exp", Float.valueOf(this.exp)); + List armor = new ArrayList<>(); + for (ItemStack armorItem : getCachedArmor()) + armor.add(ItemBuilder.itemTo64(armorItem)); + section.set("armor", armor); + List inv = new ArrayList<>(); + for (ItemStack invItem : getCachedInventory()) + inv.add(ItemBuilder.itemTo64(invItem)); + section.set("inv", inv); + return section; + } + + public static CachedInventory fromJson(JsonObject jsonObject) { + CachedInventory cachedInventory = new CachedInventory(); + JsonArray armorArray = jsonObject.get("armor").getAsJsonArray(); + List armor = new ArrayList<>(); + armorArray.forEach(armorElement -> { + try { + armor.add(ItemBuilder.itemFrom64(armorElement.getAsString())); + } catch (IOException e) { + e.printStackTrace(); + } + }); + cachedInventory.setCachedArmor(armor.toArray(new ItemStack[0])); + JsonArray invArray = jsonObject.get("inv").getAsJsonArray(); + List inv = new ArrayList<>(); + invArray.forEach(invElement -> { + try { + inv.add(ItemBuilder.itemFrom64(invElement.getAsString())); + } catch (IOException e) { + e.printStackTrace(); + } + }); + cachedInventory.setCachedInventory(inv.toArray(new ItemStack[0])); + if (jsonObject.has("health")) + cachedInventory.setHealth(jsonObject.get("health").getAsDouble()); + if (jsonObject.has("totalExperience")) + cachedInventory.setHealth(jsonObject.get("totalExperience").getAsInt()); + if (jsonObject.has("food")) + cachedInventory.setHealth(jsonObject.get("food").getAsInt()); + if (jsonObject.has("exp")) + cachedInventory.setHealth(jsonObject.get("exp").getAsFloat()); + return cachedInventory; + } + + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject(); + if (this.health != -1.0D) + jsonObject.addProperty("health", this.health); + if (this.totalExperience != -1) + jsonObject.addProperty("totalExperience", this.totalExperience); + if (this.food != -1) + jsonObject.addProperty("food", this.food); + if (this.exp != -1.0F) + jsonObject.addProperty("exp", this.exp); + JsonArray armorArray = new JsonArray(); + for (ItemStack itemStack : this.cachedArmor) + armorArray.add(new JsonPrimitive(ItemBuilder.itemTo64(itemStack))); + jsonObject.add("armor", armorArray); + JsonArray invArray = new JsonArray(); + for (ItemStack itemStack : this.cachedInventory) + invArray.add(new JsonPrimitive(ItemBuilder.itemTo64(itemStack))); + jsonObject.add("inv", invArray); + return jsonObject; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/ClassRegistrationUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/ClassRegistrationUtils.java new file mode 100644 index 0000000..ed0ebf9 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/ClassRegistrationUtils.java @@ -0,0 +1,95 @@ +package com.loganmagnan.pluginbase.utils; + +import com.google.common.collect.ImmutableSet; +import com.loganmagnan.pluginbase.PluginBase; +import org.bukkit.event.Listener; + +import java.io.IOException; +import java.net.URL; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class ClassRegistrationUtils { + + public static void loadListeners(String packageName) { + for (Class clazz : getClassesInPackage(packageName)) { + if (isListener(clazz)) { + try { + PluginBase.getInstance().getServer().getPluginManager().registerEvents((Listener) clazz.newInstance(), PluginBase.getInstance()); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + } + } + + public static void loadCommands(String packageName) { + for (Class clazz : getClassesInPackage(packageName)) { + try { + clazz.newInstance(); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + } + + public static boolean isListener(Class clazz) { + for (Class interfaze : clazz.getInterfaces()) { + if (interfaze == Listener.class) { + return true; + } + } + + return false; + } + + public static Collection> getClassesInPackage(String packageName) { + JarFile jarFile; + Collection> classes = new ArrayList<>(); + CodeSource codeSource = PluginBase.getInstance().getClass().getProtectionDomain().getCodeSource(); + URL resource = codeSource.getLocation(); + + String relPath = packageName.replace('.', '/'); + String resPath = resource.getPath().replace("%20", " "); + String jarPath = resPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", ""); + + try { + jarFile = new JarFile(jarPath); + } catch (IOException e) { + throw new IllegalStateException("Unexpected IOException reading JAR File '" + jarPath + "'", e); + } + + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String entryName = entry.getName(); + String className = null; + if (entryName.endsWith(".class") && entryName.startsWith(relPath) && entryName.length() > relPath.length() + "/".length()) { + className = entryName.replace('/', '.').replace('\\', '.').replace(".class", ""); + } + if (className != null) { + Class clazz = null; + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + if (clazz != null) { + classes.add(clazz); + } + } + } + + try { + jarFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + return ImmutableSet.copyOf(classes); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/Clickable.java b/src/main/java/com/loganmagnan/pluginbase/utils/Clickable.java new file mode 100644 index 0000000..f2cc33c --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/Clickable.java @@ -0,0 +1,51 @@ +package com.loganmagnan.pluginbase.utils; + +import lombok.NoArgsConstructor; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +@NoArgsConstructor +public class Clickable { + + private final List components = new ArrayList<>(); + + public Clickable(String msg) { + TextComponent message = new TextComponent(msg); + this.components.add(message); + } + + public Clickable(String msg, String hoverMsg, String clickString) { + this.add(msg, hoverMsg, clickString); + } + + public TextComponent add(String msg, String hoverMsg, String clickString) { + TextComponent message = new TextComponent(msg); + if (hoverMsg != null) { + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(hoverMsg).create())); + } + if (clickString != null) { + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, clickString)); + } + this.components.add(message); + + return message; + } + + public void add(String message) { + this.components.add(new TextComponent(message)); + } + + public void sendToPlayer(Player player) { + player.spigot().sendMessage(this.asComponents()); + } + + public TextComponent[] asComponents() { + return this.components.toArray(new TextComponent[0]); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/ColorUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/ColorUtils.java new file mode 100644 index 0000000..aa1b190 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/ColorUtils.java @@ -0,0 +1,193 @@ +package com.loganmagnan.pluginbase.utils; + +import com.loganmagnan.pluginbase.chatcolor.ColorSet; +import lombok.Getter; +import lombok.Setter; +import net.md_5.bungee.api.ChatColor; + +import java.awt.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Getter +@Setter +public class ColorUtils { + + private static Map> colorMap = new HashMap<>(); + + public ColorUtils() { + this.registerColors(); + } + + public void registerColors() { + colorMap.put(ChatColor.BLACK, new ColorSet<>(0, 0, 0)); + colorMap.put(ChatColor.DARK_BLUE, new ColorSet<>(0, 0, 170)); + colorMap.put(ChatColor.DARK_GREEN, new ColorSet<>(0, 170, 0)); + colorMap.put(ChatColor.DARK_AQUA, new ColorSet<>(0, 170, 170)); + colorMap.put(ChatColor.DARK_RED, new ColorSet<>(170, 0, 0)); + colorMap.put(ChatColor.DARK_PURPLE, new ColorSet<>(170, 0, 170)); + colorMap.put(ChatColor.GOLD, new ColorSet<>(255, 170, 0)); + colorMap.put(ChatColor.GRAY, new ColorSet<>(170, 170, 170)); + colorMap.put(ChatColor.DARK_GRAY, new ColorSet<>(85, 85, 85)); + colorMap.put(ChatColor.BLUE, new ColorSet<>(85, 85, 255)); + colorMap.put(ChatColor.GREEN, new ColorSet<>(85, 255, 85)); + colorMap.put(ChatColor.AQUA, new ColorSet<>(85, 255, 255)); + colorMap.put(ChatColor.RED, new ColorSet<>(255, 85, 85)); + colorMap.put(ChatColor.LIGHT_PURPLE, new ColorSet<>(255, 85, 255)); + colorMap.put(ChatColor.YELLOW, new ColorSet<>(255, 255, 85)); + colorMap.put(ChatColor.WHITE, new ColorSet<>(255, 255, 255)); + } + + public static String getMessageType(String message) { + if (message.contains("#")) { + if (isValidHexColorCode(message.substring(message.indexOf("#"), message.indexOf("#") + 7))) { + return translate(message); + } else { + return Utils.translate(message); + } + } else { + return Utils.translate(message); + } + } + + public static List getMessageType(List message) { + List messageNew = new ArrayList(); + + for (String string : message) { + if (string.contains("#")) { + if (isValidHexColorCode(string.substring(string.indexOf("#"), string.indexOf("#") + 7))) { + messageNew.add(translate(string)); + } else { + messageNew.add(ColorUtils.getMessageType(string)); + } + } else { + messageNew.add(ColorUtils.getMessageType(string)); + } + } + + return messageNew; + } + + public static ChatColor getColor(String colorCode) { + byte b; + + int i; + + ChatColor[] arrayOfChatColor; + + for (i = (arrayOfChatColor = ChatColor.values()).length, b = 0; b < i; ) { + ChatColor colors = arrayOfChatColor[b]; + + String colorsDecode = untranslate(colors.toString()); + + if (colorCode.equalsIgnoreCase(colorsDecode)) { + return colors; + } + + b++; + } + + return null; + } + + public static boolean lookAtColorCode(String colorCode) { + byte b; + + int i; + + ChatColor[] arrayOfChatColor; + + for (i = (arrayOfChatColor = ChatColor.values()).length, b = 0; b < i; ) { + ChatColor colors = arrayOfChatColor[b]; + + String colorsDecode = untranslate(colors.toString()); + + if (colorCode.equalsIgnoreCase(colorsDecode)) { + return true; + } + + b++; + } + + return false; + } + + public static ColorSet copyColorSet(String colorCode) { + Color color = hexColorCodesToRGBColorCodes(colorCode); + + return new ColorSet(color.getRed(), color.getGreen(), color.getBlue()); + } + + public static String getGradientString(String string, List colorCodes) { + String[] split = string.split(""); + + StringBuilder stringBuilder = new StringBuilder(); + + for (int i = 0; i < colorCodes.size(); i++) { + stringBuilder.append(ChatColor.of(colorCodes.get(i)) + split[i]); + } + + return stringBuilder.toString(); + } + + public static List getColorCodes(String text, ColorSet colorSetOne, ColorSet colorSetTwo) { + List colorCodes = new ArrayList<>(); + + int red = ((colorSetOne.getRed() < colorSetTwo.getRed()) ? (colorSetTwo.getRed() - colorSetOne.getRed()) : (colorSetOne.getRed() - colorSetTwo.getRed())) / text.length(); + int green = ((colorSetOne.getGreen() < colorSetTwo.getGreen()) ? (colorSetTwo.getGreen() - colorSetOne.getGreen()) : (colorSetOne.getGreen() - colorSetTwo.getGreen())) / text.length(); + int blue = ((colorSetOne.getBlue() < colorSetTwo.getBlue()) ? (colorSetTwo.getBlue() - colorSetOne.getBlue()) : (colorSetOne.getBlue() - colorSetTwo.getBlue())) / text.length(); + + for (int i = 0; i < text.length(); i++) { + colorSetOne.setRed((colorSetOne.getRed() <= colorSetTwo.getRed()) ? (colorSetOne.getRed() + red) : (colorSetOne.getRed() - red)); + colorSetOne.setGreen((colorSetOne.getGreen() <= colorSetTwo.getGreen()) ? (colorSetOne.getGreen() + green) : (colorSetOne.getGreen() - green)); + colorSetOne.setBlue((colorSetOne.getBlue() <= colorSetTwo.getBlue()) ? (colorSetOne.getBlue() + blue) : (colorSetOne.getBlue() - blue)); + + String hex = String.format("#%02x%02x%02x", colorSetOne.getRed(), colorSetOne.getGreen(), colorSetOne.getBlue()); + + colorCodes.add(hex); + } + + return colorCodes; + } + + public static String translate(String string) { + Pattern pattern = Pattern.compile("#[a-fA-F0-9]{6}"); + + for (Matcher matcher = pattern.matcher(string); matcher.find(); matcher = pattern.matcher(string)) { + String color = string.substring(matcher.start(), matcher.end()); + + string = string.replace(color, ChatColor.of(color) + ""); + } + + string = ChatColor.translateAlternateColorCodes('&', string); + + return string; + } + + public static Color hexColorCodesToRGBColorCodes(String string) { + return new Color(Integer.valueOf(string.substring(1, 3),16), Integer.valueOf(string.substring(3, 5),16), Integer.valueOf(string.substring(5, 7),16)); + } + + public static boolean isValidHexColorCode(String string) { + Pattern pattern = Pattern.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"); + + Matcher matcher = pattern.matcher(string); + + return matcher.matches(); + } + + public static String untranslate(String textToTranslate) { + char[] b = textToTranslate.toCharArray(); + for (int i = 0; i < b.length - 1; i++) { + if (b[i] == 'ยง' && "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx".indexOf(b[i + 1]) > -1) { + b[i] = '&'; + b[i + 1] = Character.toLowerCase(b[i + 1]); + } + } + return new String(b); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/Cuboid.java b/src/main/java/com/loganmagnan/pluginbase/utils/Cuboid.java new file mode 100644 index 0000000..d4e08c1 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/Cuboid.java @@ -0,0 +1,45 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Location; + +public class Cuboid { + private final Vector3 firstPoint; + private final Vector3 secondPoint; + + public Cuboid(final Vector3 firstPoint, final Vector3 secondPoint) { + this.firstPoint = firstPoint; + this.secondPoint = secondPoint; + } + + public boolean isBetween(final double xP, final double zP) { + double x1 = this.firstPoint.getX(); + double z1 = this.firstPoint.getZ(); + + double x2 = this.secondPoint.getX(); + double z2 = this.secondPoint.getZ(); + + return ((x1 < xP && xP < x2) || (x1 > xP && xP > x2)) && ((z1 < zP && zP < z2) || (z1 > zP && zP > z2)); + } + + public boolean isBetween(final Location target) { + double xP = target.getX(); + double zP = target.getZ(); + + return this.isBetween(xP, zP); + } + + public boolean isBetween(final Vector3 target) { + double xP = target.getX(); + double zP = target.getZ(); + + return this.isBetween(xP, zP); + } + + public Vector3 getFirstPoint() { + return this.firstPoint; + } + + public Vector3 getSecondPoint() { + return this.secondPoint; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/CustomLocation.java b/src/main/java/com/loganmagnan/pluginbase/utils/CustomLocation.java new file mode 100644 index 0000000..dc68ca2 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/CustomLocation.java @@ -0,0 +1,125 @@ +package com.loganmagnan.pluginbase.utils; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.StringJoiner; + +@Getter +@Setter +@AllArgsConstructor +public class CustomLocation { + + private final long timestamp = System.currentTimeMillis(); + + private String world; + + private double x; + private double y; + private double z; + + private float yaw; + private float pitch; + + public CustomLocation(double x, double y, double z) { + this(x, y, z, 0.0F, 0.0F); + } + + public CustomLocation(String world, double x, double y, double z) { + this(world, x, y, z, 0.0F, 0.0F); + } + + public CustomLocation(double x, double y, double z, float yaw, float pitch) { + this("world", x, y, z, yaw, pitch); + } + + public static CustomLocation fromBukkitLocation(Location location) { + return new CustomLocation(location.getWorld().getName(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + } + + public static CustomLocation stringToLocation(String string) { + String[] split = string.split(", "); + + double x = Double.parseDouble(split[0]); + double y = Double.parseDouble(split[1]); + double z = Double.parseDouble(split[2]); + + CustomLocation customLocation = new CustomLocation(x, y, z); + if (split.length == 4) { + customLocation.setWorld(split[3]); + } else if (split.length >= 5) { + customLocation.setYaw(Float.parseFloat(split[3])); + customLocation.setPitch(Float.parseFloat(split[4])); + if (split.length >= 6) { + customLocation.setWorld(split[5]); + } + } + + return customLocation; + } + + public static String locationToString(CustomLocation loc) { + StringJoiner joiner = new StringJoiner(", "); + joiner.add(Double.toString(loc.getX())); + joiner.add(Double.toString(loc.getY())); + joiner.add(Double.toString(loc.getZ())); + if (loc.getYaw() == 0.0f && loc.getPitch() == 0.0f) { + if (loc.getWorld().equals("world")) { + return joiner.toString(); + } else { + joiner.add(loc.getWorld()); + return joiner.toString(); + } + } else { + joiner.add(Float.toString(loc.getYaw())); + joiner.add(Float.toString(loc.getPitch())); + if (loc.getWorld().equals("world")) { + return joiner.toString(); + } else { + joiner.add(loc.getWorld()); + return joiner.toString(); + } + } + } + + public Location toBukkitLocation() { + return new Location(this.toBukkitWorld(), this.x, this.y, this.z, this.yaw, this.pitch); + } + + public World toBukkitWorld() { + if (this.world == null) { + return Bukkit.getServer().getWorlds().get(0); + } else { + return Bukkit.getServer().getWorld(this.world); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CustomLocation)) { + return false; + } + + CustomLocation location = (CustomLocation) obj; + + return location.x == this.x && location.y == this.y && location.z == this.z && location.pitch == this.pitch && location.yaw == this.yaw; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("x", this.x) + .append("y", this.y) + .append("z", this.z) + .append("yaw", this.yaw) + .append("pitch", this.pitch) + .append("world", this.world) + .append("timestamp", this.timestamp) + .toString(); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/ItemBuilder.java b/src/main/java/com/loganmagnan/pluginbase/utils/ItemBuilder.java new file mode 100644 index 0000000..25113c9 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/ItemBuilder.java @@ -0,0 +1,209 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.ChatColor; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.material.MaterialData; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ItemBuilder implements Listener { + + private final ItemStack is; + + public ItemBuilder(final Material mat) { + is = new ItemStack(mat); + } + + public ItemBuilder(final ItemStack is) { + this.is = is; + } + + public ItemBuilder(Material m, int amount) { + this.is = new ItemStack(m, amount); + } + + public ItemBuilder amount(final int amount) { + is.setAmount(amount); + + return this; + } + + public ItemBuilder name(final String name) { + final ItemMeta meta = is.getItemMeta(); + meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name)); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder lore(final String name) { + final ItemMeta meta = is.getItemMeta(); + List lore = meta.getLore(); + if (lore == null) { + lore = new ArrayList<>(); + } + + lore.add(name); + meta.setLore(lore); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder lore(final List lore) { + List toSet = new ArrayList<>(); + ItemMeta meta = is.getItemMeta(); + + for (String string : lore) { + toSet.add(ChatColor.translateAlternateColorCodes('&', string)); + } + + meta.setLore(toSet); + is.setItemMeta(meta); + return this; + } + + public ItemBuilder durability(final int durability) { + is.setDurability((short) durability); + + return this; + } + + public ItemBuilder owner(String owner) { + if (this.is.getType() == Material.PLAYER_HEAD) { + SkullMeta meta = (SkullMeta) this.is.getItemMeta(); + meta.setOwner(owner); + this.is.setItemMeta(meta); + return this; + } + + throw new IllegalArgumentException("setOwner() only applicable for Skull Item"); + } + + public ItemBuilder enchantment(final Enchantment enchantment, final int level) { + is.addUnsafeEnchantment(enchantment, level); + return this; + } + + public ItemBuilder enchantment(final Enchantment enchantment) { + is.addUnsafeEnchantment(enchantment, 1); + return this; + } + + public ItemBuilder hideFlags() { + final ItemMeta meta = is.getItemMeta(); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder hideEnchants() { + final ItemMeta meta = is.getItemMeta(); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder hideUnbreakable() { + final ItemMeta meta = is.getItemMeta(); + meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder addUnbreakable() { + final ItemMeta meta = is.getItemMeta(); + meta.setUnbreakable(true); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder type(final Material material) { + is.setType(material); + return this; + } + + public ItemBuilder clearLore() { + final ItemMeta meta = is.getItemMeta(); + meta.setLore(new ArrayList<>()); + is.setItemMeta(meta); + + return this; + } + + public ItemBuilder clearEnchantments() { + for (final Enchantment e : is.getEnchantments().keySet()) { + is.removeEnchantment(e); + } + + return this; + } + + public ItemBuilder color(Color color) { + if (is.getType() == Material.LEATHER_BOOTS || is.getType() == Material.LEATHER_CHESTPLATE + || is.getType() == Material.LEATHER_HELMET || is.getType() == Material.LEATHER_LEGGINGS) { + LeatherArmorMeta meta = (LeatherArmorMeta) is.getItemMeta(); + meta.setColor(color); + is.setItemMeta(meta); + + return this; + } else { + throw new IllegalArgumentException("color() only applicable for leather armor!"); + } + } + + public ItemBuilder addEnchantments(Map enchantments) { + this.is.addEnchantments(enchantments); + return this; + } + + public static String itemTo64(ItemStack stack) throws IllegalStateException { + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); + dataOutput.writeObject(stack); + dataOutput.close(); + return Base64Coder.encodeLines(outputStream.toByteArray()); + } catch (Exception e) { + throw new IllegalStateException("Unable to save item stack.", e); + } + } + + public static ItemStack itemFrom64(String data) throws IOException { + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data)); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + try { + return (ItemStack)dataInput.readObject(); + } finally { + dataInput.close(); + } + } catch (ClassNotFoundException e) { + throw new IOException("Unable to decode class type.", e); + } + } + + public ItemStack build() { + return is; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/ItemUtil.java b/src/main/java/com/loganmagnan/pluginbase/utils/ItemUtil.java new file mode 100644 index 0000000..cee868b --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/ItemUtil.java @@ -0,0 +1,167 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public final class ItemUtil { + + private ItemUtil() { + throw new RuntimeException("Cannot instantiate a utility class."); + } + + public static ItemStack createItem(Material material, String name) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + return item; + } + + public static ItemStack createItem(Material material, String name, int amount) { + ItemStack item = new ItemStack(material, amount); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + return item; + } + + public static ItemStack createItem(Material material, String name, int amount, short damage) { + ItemStack item = new ItemStack(material, amount, damage); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + return item; + } + + public static ItemStack createUnbreakableItem(Material material, String name, int amount, short damage, int customModelData) { + ItemStack item = new ItemStack(material, amount, damage); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + meta.setUnbreakable(true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_UNBREAKABLE); + + if (customModelData > 0) { + meta.setCustomModelData(customModelData); + } + + item.setItemMeta(meta); + + return item; + } + + public static ItemStack createNoFlagsItem(Material material, String name, int amount, short damage) { + ItemStack item = new ItemStack(material, amount, damage); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + meta.setUnbreakable(true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES); + item.setItemMeta(meta); + + return item; + } + + public static ItemStack createPlayerHead(Material material, String name, String playerHead, int amount, short damage) { + ItemStack item = new ItemStack(material, amount, damage); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + meta.setUnbreakable(true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_UNBREAKABLE); + item.setItemMeta(meta); + + if (item.getType() == Material.PLAYER_HEAD) { + SkullMeta skullMeta = (SkullMeta) item.getItemMeta(); + skullMeta.setOwner(playerHead); + item.setItemMeta(skullMeta); + + return item; + } + + return item; + } + + public static ItemStack setUnbreakable(ItemStack item) { + ItemMeta meta = item.getItemMeta(); + + meta.setUnbreakable(true); + meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); + item.setItemMeta(meta); + + return item; + } + + public static ItemStack renameItem(ItemStack item, String name) { + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName(name); + item.setItemMeta(meta); + + return item; + } + + public static ItemStack reloreItem(ItemStack item, String... lores) { + return reloreItem(ReloreType.OVERWRITE, item, lores); + } + + public static ItemStack reEnchantItem(ItemStack itemStack, Enchantment enchantment, int level, boolean b) { + ItemMeta meta = itemStack.getItemMeta(); + + meta.addEnchant(enchantment, level, b); + + itemStack.setItemMeta(meta); + return itemStack; + } + + public static ItemStack reloreItem(ReloreType type, ItemStack item, String... lores) { + ItemMeta meta = item.getItemMeta(); + + List lore = meta.getLore(); + if (lore == null) { + lore = new LinkedList<>(); + } + + switch (type) { + case APPEND: + lore.addAll(Arrays.asList(lores)); + meta.setLore(lore); + break; + case PREPEND: + List nLore = new LinkedList<>(Arrays.asList(lores)); + nLore.addAll(lore); + meta.setLore(nLore); + break; + case OVERWRITE: + meta.setLore(Arrays.asList(lores)); + break; + } + + item.setItemMeta(meta); + return item; + } + + public enum ReloreType { + OVERWRITE, PREPEND, APPEND + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/LocationUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/LocationUtils.java new file mode 100644 index 0000000..4121ed0 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/LocationUtils.java @@ -0,0 +1,34 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Location; +import org.bukkit.block.Block; + +import java.util.ArrayList; +import java.util.List; + +public class LocationUtils { + + public static List getBlocks(Location center, int radius) { + return getBlocks(center, radius, radius); + } + + public static List getBlocks(Location center, int radius, int yRadius) { + if (radius < 0) { + return new ArrayList<>(); + } + + int iterations = radius * 2 + 1; + + List blocks = new ArrayList<>(iterations * iterations * iterations); + + for (int x = -radius; x <= radius; x++) { + for (int y = -yRadius; y <= yRadius; y++) { + for (int z = -radius; z <= radius; z++) { + blocks.add(center.getBlock().getRelative(x, y, z)); + } + } + } + + return blocks; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/MathUtil.java b/src/main/java/com/loganmagnan/pluginbase/utils/MathUtil.java new file mode 100644 index 0000000..c1c4836 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/MathUtil.java @@ -0,0 +1,58 @@ + package com.loganmagnan.pluginbase.utils; + + import java.util.Random; + + public final class MathUtil + { + public static boolean isInteger(String in) { + try { + Integer.parseInt(in); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static int randomNumber(int minimo, int maximo) { + Random random = new Random(); + int min = Math.min(maximo, maximo); + int max = Math.max(maximo, maximo); + int maxsize = min - max; + + return random.nextInt(maxsize + 1) + minimo; + } + + public static String convertTicksToMinutes(int ticks) { + long minute = ticks / 1200L; + long second = ticks / 20L - minute * 60L; + + String secondString = Math.round((float)second) + ""; + if (second < 10L) { + secondString = Character.MIN_VALUE + secondString; + } + + String minuteString = Math.round((float)minute) + ""; + if (minute == 0L) { + minuteString = "0"; + } + + return minuteString + ":" + secondString; + } + + public static String convertToRomanNumeral(int number) { + switch (number) { + case 1: + return "I"; + case 2: + return "II"; + } + + return null; + } + + public static double roundToHalves(double d) { + return Math.round(d * 2.0D) / 2.0D; + } + } + + diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/NBTEditor.java b/src/main/java/com/loganmagnan/pluginbase/utils/NBTEditor.java new file mode 100644 index 0000000..b05f7af --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/NBTEditor.java @@ -0,0 +1,1571 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class NBTEditor { + + private static final Map< String, Class< ? > > classCache; + private static final Map< String, Method > methodCache; + private static final Map< Class< ? >, Constructor< ? > > constructorCache; + private static final Map< Class< ? >, Class< ? > > NBTClasses; + private static final Map< Class< ? >, Field > NBTTagFieldCache; + private static Field NBTListData; + private static Field NBTCompoundMap; + private static final String VERSION; + private static final MinecraftVersion LOCAL_VERSION; + + public static final Type COMPOUND = Type.COMPOUND; + public static final Type LIST = Type.LIST; + public static final Type NEW_ELEMENT = Type.NEW_ELEMENT; + public static final Type DELETE = Type.DELETE; + + static { + VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + LOCAL_VERSION = MinecraftVersion.get( VERSION ); + + classCache = new HashMap< String, Class >(); + try { + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + classCache.put( "NBTBase", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTBase" ) ); + classCache.put( "NBTTagCompound", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagCompound" ) ); + classCache.put( "NBTTagList", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagList" ) ); + classCache.put( "NBTTagEnd", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagEnd" ) ); + classCache.put( "MojangsonParser", Class.forName( "net.minecraft.server." + VERSION + "." + "MojangsonParser" ) ); + + classCache.put( "ItemStack", Class.forName( "net.minecraft.server." + VERSION + "." + "ItemStack" ) ); + + classCache.put( "Entity", Class.forName( "net.minecraft.server." + VERSION + "." + "Entity" ) ); + classCache.put( "EntityLiving", Class.forName( "net.minecraft.server." + VERSION + "." + "EntityLiving" ) ); + + classCache.put( "BlockPosition", Class.forName( "net.minecraft.server." + VERSION + "." + "BlockPosition" ) ); + classCache.put( "TileEntity", Class.forName( "net.minecraft.server." + VERSION + "." + "TileEntity" ) ); + classCache.put( "World", Class.forName( "net.minecraft.server." + VERSION + "." + "World" ) ); + classCache.put( "IBlockData", Class.forName( "net.minecraft.server." + VERSION + "." + "IBlockData" ) ); + + classCache.put( "TileEntitySkull", Class.forName( "net.minecraft.server." + VERSION + "." + "TileEntitySkull" ) ); + + } else { + classCache.put( "BlockPosition", Class.forName( "net.minecraft.core.BlockPosition" ) ); + + classCache.put( "NBTBase", Class.forName( "net.minecraft.nbt.NBTBase" ) ); + classCache.put( "NBTTagCompound", Class.forName( "net.minecraft.nbt.NBTTagCompound" ) ); + classCache.put( "NBTTagList", Class.forName( "net.minecraft.nbt.NBTTagList" ) ); + classCache.put( "NBTTagEnd", Class.forName( "net.minecraft.nbt.NBTTagEnd" ) ); + classCache.put( "MojangsonParser", Class.forName( "net.minecraft.nbt.MojangsonParser" ) ); + + classCache.put( "ItemStack", Class.forName( "net.minecraft.world.item.ItemStack" ) ); + + classCache.put( "Entity", Class.forName( "net.minecraft.world.entity.Entity" ) ); + classCache.put( "EntityLiving", Class.forName( "net.minecraft.world.entity.EntityLiving" ) ); + + classCache.put( "World", Class.forName( "net.minecraft.world.level.World" ) ); + classCache.put( "IBlockData", Class.forName( "net.minecraft.world.level.block.state.IBlockData" ) ); + classCache.put( "TileEntity", Class.forName( "net.minecraft.world.level.block.entity.TileEntity" ) ); + classCache.put( "TileEntitySkull", Class.forName( "net.minecraft.world.level.block.entity.TileEntitySkull" ) ); + } + + classCache.put( "CraftItemStack", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".inventory." + "CraftItemStack" ) ); + classCache.put( "CraftMetaSkull", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".inventory." + "CraftMetaSkull" ) ); + + classCache.put( "CraftEntity", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".entity." + "CraftEntity" ) ); + + classCache.put( "CraftWorld", Class.forName( "org.bukkit.craftbukkit." + VERSION + "." + "CraftWorld" ) ); + classCache.put( "CraftBlockState", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".block." + "CraftBlockState" ) ); + + classCache.put( "GameProfile", Class.forName( "com.mojang.authlib.GameProfile" ) ); + classCache.put( "Property", Class.forName( "com.mojang.authlib.properties.Property" ) ); + classCache.put( "PropertyMap", Class.forName( "com.mojang.authlib.properties.PropertyMap" ) ); + } catch ( ClassNotFoundException e ) { + e.printStackTrace(); + } + + NBTClasses = new HashMap< Class< ? >, Class< ? > >(); + try { + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + NBTClasses.put( Byte.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagByte" ) ); + NBTClasses.put( Boolean.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagByte" ) ); + NBTClasses.put( String.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagString" ) ); + NBTClasses.put( Double.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagDouble" ) ); + NBTClasses.put( Integer.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagInt" ) ); + NBTClasses.put( Long.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagLong" ) ); + NBTClasses.put( Short.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagShort" ) ); + NBTClasses.put( Float.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagFloat" ) ); + NBTClasses.put( Class.forName( "[B" ), Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagByteArray" ) ); + NBTClasses.put( Class.forName( "[I" ), Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagIntArray" ) ); + } else { + NBTClasses.put( Byte.class, Class.forName( "net.minecraft.nbt.NBTTagByte" ) ); + NBTClasses.put( Boolean.class, Class.forName( "net.minecraft.nbt.NBTTagByte" ) ); + NBTClasses.put( String.class, Class.forName( "net.minecraft.nbt.NBTTagString" ) ); + NBTClasses.put( Double.class, Class.forName( "net.minecraft.nbt.NBTTagDouble" ) ); + NBTClasses.put( Integer.class, Class.forName( "net.minecraft.nbt.NBTTagInt" ) ); + NBTClasses.put( Long.class, Class.forName( "net.minecraft.nbt.NBTTagLong" ) ); + NBTClasses.put( Short.class, Class.forName( "net.minecraft.nbt.NBTTagShort" ) ); + NBTClasses.put( Float.class, Class.forName( "net.minecraft.nbt.NBTTagFloat" ) ); + NBTClasses.put( Class.forName( "[B" ), Class.forName( "net.minecraft.nbt.NBTTagByteArray" ) ); + NBTClasses.put( Class.forName( "[I" ), Class.forName( "net.minecraft.nbt.NBTTagIntArray" ) ); + } + } catch ( ClassNotFoundException e ) { + e.printStackTrace(); + } + + methodCache = new HashMap< String, Method >(); + try { + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_17 ) ) { + methodCache.put( "get", getNMSClass( "NBTTagCompound" ).getMethod( "get", String.class ) ); + methodCache.put( "set", getNMSClass( "NBTTagCompound" ).getMethod( "set", String.class, getNMSClass( "NBTBase" ) ) ); + methodCache.put( "hasKey", getNMSClass( "NBTTagCompound" ).getMethod( "hasKey", String.class ) ); + } else { + methodCache.put( "get", getNMSClass( "NBTTagCompound" ).getMethod( "c", String.class ) ); + methodCache.put( "set", getNMSClass( "NBTTagCompound" ).getMethod( "a", String.class, getNMSClass( "NBTBase" ) ) ); + methodCache.put( "hasKey", getNMSClass( "NBTTagCompound" ).getMethod( "e", String.class ) ); + } + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "setIndex", getNMSClass( "NBTTagList" ).getMethod( "d", int.class, getNMSClass( "NBTBase" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_17 ) ) { + methodCache.put( "setIndex", getNMSClass( "NBTTagList" ).getMethod( "set", int.class, getNMSClass( "NBTBase" ) ) ); + } else { + methodCache.put( "setIndex", getNMSClass( "NBTTagList" ).getMethod( "a", int.class, getNMSClass( "NBTBase" ) ) ); + } + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "add", getNMSClass( "NBTTagList" ).getMethod( "c", int.class, getNMSClass( "NBTBase" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_14 ) ) { + methodCache.put( "add", getNMSClass( "NBTTagList" ).getMethod( "add", int.class, getNMSClass( "NBTBase" ) ) ); + } else { + methodCache.put( "add", getNMSClass( "NBTTagList" ).getMethod( "add", getNMSClass( "NBTBase" ) ) ); + } + methodCache.put( "size", getNMSClass( "NBTTagList" ).getMethod( "size" ) ); + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "listRemove", getNMSClass( "NBTTagList" ).getMethod( "c", int.class ) ); + } else if ( LOCAL_VERSION == MinecraftVersion.v1_8 ) { + methodCache.put( "listRemove", getNMSClass( "NBTTagList" ).getMethod( "a", int.class ) ); + } else { + methodCache.put( "listRemove", getNMSClass( "NBTTagList" ).getMethod( "remove", int.class ) ); + } + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "remove", getNMSClass( "NBTTagCompound" ).getMethod( "r", String.class ) ); + } else { + methodCache.put( "remove", getNMSClass( "NBTTagCompound" ).getMethod( "remove", String.class ) ); + } + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_12 ) ) { + methodCache.put( "getKeys", getNMSClass( "NBTTagCompound" ).getMethod( "c" ) ); + } else if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_17 ) ) { + methodCache.put( "getKeys", getNMSClass( "NBTTagCompound" ).getMethod( "getKeys" ) ); + } else { + methodCache.put( "getKeys", getNMSClass( "NBTTagCompound" ).getMethod( "d" ) ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_19 ) ) { + methodCache.put( "hasTag", getNMSClass( "ItemStack" ).getMethod( "t" ) ); + methodCache.put( "getTag", getNMSClass( "ItemStack" ).getMethod( "u" ) ); + methodCache.put( "setTag", getNMSClass( "ItemStack" ).getMethod( "c", getNMSClass( "NBTTagCompound" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R2 ) ) { + methodCache.put( "hasTag", getNMSClass( "ItemStack" ).getMethod( "s" ) ); + methodCache.put( "getTag", getNMSClass( "ItemStack" ).getMethod( "t" ) ); + methodCache.put( "setTag", getNMSClass( "ItemStack" ).getMethod( "c", getNMSClass( "NBTTagCompound" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "hasTag", getNMSClass( "ItemStack" ).getMethod( "r" ) ); + methodCache.put( "getTag", getNMSClass( "ItemStack" ).getMethod( "s" ) ); + methodCache.put( "setTag", getNMSClass( "ItemStack" ).getMethod( "c", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "hasTag", getNMSClass( "ItemStack" ).getMethod( "hasTag" ) ); + methodCache.put( "getTag", getNMSClass( "ItemStack" ).getMethod( "getTag" ) ); + methodCache.put( "setTag", getNMSClass( "ItemStack" ).getMethod( "setTag", getNMSClass( "NBTTagCompound" ) ) ); + } + methodCache.put( "asNMSCopy", getNMSClass( "CraftItemStack" ).getMethod( "asNMSCopy", ItemStack.class ) ); + methodCache.put( "asBukkitCopy", getNMSClass( "CraftItemStack" ).getMethod( "asBukkitCopy", getNMSClass( "ItemStack" ) ) ); + + methodCache.put( "getEntityHandle", getNMSClass( "CraftEntity" ).getMethod( "getHandle" ) ); + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "getEntityTag", getNMSClass( "Entity" ).getMethod( "f", getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "setEntityTag", getNMSClass( "Entity" ).getMethod( "g", getNMSClass( "NBTTagCompound" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + methodCache.put( "getEntityTag", getNMSClass( "Entity" ).getMethod( "save", getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "setEntityTag", getNMSClass( "Entity" ).getMethod( "load", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "getEntityTag", getNMSClass( "Entity" ).getMethod( "c", getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "setEntityTag", getNMSClass( "Entity" ).getMethod( "f", getNMSClass( "NBTTagCompound" ) ) ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "save", getNMSClass( "ItemStack" ).getMethod( "b", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "save", getNMSClass( "ItemStack" ).getMethod( "save", getNMSClass( "NBTTagCompound" ) ) ); + } + + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_10 ) ) { + methodCache.put( "createStack", getNMSClass( "ItemStack" ).getMethod( "createStack", getNMSClass( "NBTTagCompound" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_13 ) ) { + methodCache.put( "createStack", getNMSClass( "ItemStack" ).getMethod( "a", getNMSClass( "NBTTagCompound" ) ) ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "a", getNMSClass( "NBTTagCompound" ) ) ); + } else if ( LOCAL_VERSION == MinecraftVersion.v1_16 ) { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "load", getNMSClass( "IBlockData" ), getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "getType", getNMSClass( "World" ).getMethod( "getType", getNMSClass( "BlockPosition" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_12 ) ) { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "load", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "a", getNMSClass( "NBTTagCompound" ) ) ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "getTileTag", getNMSClass( "TileEntity" ).getMethod( "m" ) ); + } else if ( LOCAL_VERSION == MinecraftVersion.v1_8 ) { + methodCache.put( "getTileTag", getNMSClass( "TileEntity" ).getMethod( "b", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "getTileTag", getNMSClass( "TileEntity" ).getMethod( "save", getNMSClass( "NBTTagCompound" ) ) ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "getTileEntity", getNMSClass( "World" ).getMethod( "c_", getNMSClass( "BlockPosition" ) ) ); + } else { + methodCache.put( "getTileEntity", getNMSClass( "World" ).getMethod( "getTileEntity", getNMSClass( "BlockPosition" ) ) ); + } + methodCache.put( "getWorldHandle", getNMSClass( "CraftWorld" ).getMethod( "getHandle" ) ); + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "setGameProfile", getNMSClass( "TileEntitySkull" ).getMethod( "a", getNMSClass( "GameProfile" ) ) ); + } else { + methodCache.put( "setGameProfile", getNMSClass( "TileEntitySkull" ).getMethod( "setGameProfile", getNMSClass( "GameProfile" ) ) ); + } + methodCache.put( "getProperties", getNMSClass( "GameProfile" ).getMethod( "getProperties" ) ); + methodCache.put( "getName", getNMSClass( "Property" ).getMethod( "getName" ) ); + methodCache.put( "getValue", getNMSClass( "Property" ).getMethod( "getValue" ) ); + methodCache.put( "values", getNMSClass( "PropertyMap" ).getMethod( "values" ) ); + methodCache.put( "put", getNMSClass( "PropertyMap" ).getMethod( "put", Object.class, Object.class ) ); + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + methodCache.put( "loadNBTTagCompound", getNMSClass( "MojangsonParser" ).getMethod( "a", String.class ) ); + } else { + methodCache.put( "loadNBTTagCompound", getNMSClass( "MojangsonParser" ).getMethod( "parse", String.class ) ); + } + } catch( Exception e ) { + e.printStackTrace(); + } + + try { + methodCache.put( "setProfile", getNMSClass( "CraftMetaSkull" ).getDeclaredMethod( "setProfile", getNMSClass( "GameProfile" ) ) ); + methodCache.get( "setProfile" ).setAccessible( true ); + } catch( NoSuchMethodException exception ) { + // The method doesn't exist, so it's before 1.15.2 + } + + constructorCache = new HashMap< Class< ? >, Constructor< ? > >(); + try { + constructorCache.put( getNBTTag( Byte.class ), getNBTTag( Byte.class ).getDeclaredConstructor( byte.class ) ); + constructorCache.put( getNBTTag( Boolean.class ), getNBTTag( Boolean.class ).getDeclaredConstructor( byte.class ) ); + constructorCache.put( getNBTTag( String.class ), getNBTTag( String.class ).getDeclaredConstructor( String.class ) ); + constructorCache.put( getNBTTag( Double.class ), getNBTTag( Double.class ).getDeclaredConstructor( double.class ) ); + constructorCache.put( getNBTTag( Integer.class ), getNBTTag( Integer.class ).getDeclaredConstructor( int.class ) ); + constructorCache.put( getNBTTag( Long.class ), getNBTTag( Long.class ).getDeclaredConstructor( long.class ) ); + constructorCache.put( getNBTTag( Float.class ), getNBTTag( Float.class ).getDeclaredConstructor( float.class ) ); + constructorCache.put( getNBTTag( Short.class ), getNBTTag( Short.class ).getDeclaredConstructor( short.class ) ); + constructorCache.put( getNBTTag( Class.forName( "[B" ) ), getNBTTag( Class.forName( "[B" ) ).getDeclaredConstructor( Class.forName( "[B" ) ) ); + constructorCache.put( getNBTTag( Class.forName( "[I" ) ), getNBTTag( Class.forName( "[I" ) ).getDeclaredConstructor( Class.forName( "[I" ) ) ); + + // This is for 1.15 since Mojang decided to make the constructors private + for ( Constructor< ? > cons : constructorCache.values() ) { + cons.setAccessible( true ); + } + + constructorCache.put( getNMSClass( "BlockPosition" ), getNMSClass( "BlockPosition" ).getConstructor( int.class, int.class, int.class ) ); + + constructorCache.put( getNMSClass( "GameProfile" ), getNMSClass( "GameProfile" ).getConstructor( UUID.class, String.class ) ); + constructorCache.put( getNMSClass( "Property" ), getNMSClass( "Property" ).getConstructor( String.class, String.class ) ); + + if ( LOCAL_VERSION == MinecraftVersion.v1_11 || LOCAL_VERSION == MinecraftVersion.v1_12 ) { + constructorCache.put( getNMSClass( "ItemStack" ), getNMSClass( "ItemStack" ).getConstructor( getNMSClass( "NBTTagCompound" ) ) ); + } + } catch( Exception e ) { + e.printStackTrace(); + } + + NBTTagFieldCache = new HashMap< Class< ? >, Field >(); + try { + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + for ( Class< ? > clazz : NBTClasses.values() ) { + Field data = clazz.getDeclaredField( "data" ); + data.setAccessible( true ); + NBTTagFieldCache.put( clazz, data ); + } + } else { + NBTTagFieldCache.put( NBTClasses.get( Byte.class ), NBTClasses.get( Byte.class ).getDeclaredField( "x" ) ); + NBTTagFieldCache.put( NBTClasses.get( Boolean.class ), NBTClasses.get( Boolean.class ).getDeclaredField( "x" ) ); + NBTTagFieldCache.put( NBTClasses.get( String.class ), NBTClasses.get( String.class ).getDeclaredField( "A" ) ); + NBTTagFieldCache.put( NBTClasses.get( Double.class ), NBTClasses.get( Double.class ).getDeclaredField( "w" ) ); + NBTTagFieldCache.put( NBTClasses.get( Integer.class ), NBTClasses.get( Integer.class ).getDeclaredField( "c" ) ); + NBTTagFieldCache.put( NBTClasses.get( Long.class ), NBTClasses.get( Long.class ).getDeclaredField( "c" ) ); + NBTTagFieldCache.put( NBTClasses.get( Float.class ), NBTClasses.get( Float.class ).getDeclaredField( "w" ) ); + NBTTagFieldCache.put( NBTClasses.get( Short.class ), NBTClasses.get( Short.class ).getDeclaredField( "c" ) ); + NBTTagFieldCache.put( NBTClasses.get( Class.forName( "[B" ) ), NBTClasses.get( Class.forName( "[B" ) ).getDeclaredField( "c" ) ); + NBTTagFieldCache.put( NBTClasses.get( Class.forName( "[I" ) ), NBTClasses.get( Class.forName( "[I" ) ).getDeclaredField( "c" ) ); + + for ( Field field : NBTTagFieldCache.values() ) { + field.setAccessible( true ); + } + } + } catch( Exception e ) { + e.printStackTrace(); + } + + try { + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + NBTListData = getNMSClass( "NBTTagList" ).getDeclaredField( "list" ); + NBTCompoundMap = getNMSClass( "NBTTagCompound" ).getDeclaredField( "map" ); + } else { + NBTListData = getNMSClass( "NBTTagList" ).getDeclaredField( "c" ); + NBTCompoundMap = getNMSClass( "NBTTagCompound" ).getDeclaredField( "x" ); + } + NBTListData.setAccessible( true ); + NBTCompoundMap.setAccessible( true ); + } catch( Exception e ) { + e.printStackTrace(); + } + } + + private static Class< ? > getNBTTag( Class< ? > primitiveType ) { + if ( NBTClasses.containsKey( primitiveType ) ) + return NBTClasses.get( primitiveType ); + return primitiveType; + } + + private static Object getNBTVar( Object object ) { + if ( object == null ) { + return null; + } + Class< ? > clazz = object.getClass(); + try { + if ( NBTTagFieldCache.containsKey( clazz ) ) { + return NBTTagFieldCache.get( clazz ).get( object ); + } + } catch ( Exception exception ) { + exception.printStackTrace(); + } + return null; + } + + private static Method getMethod( String name ) { + return methodCache.containsKey( name ) ? methodCache.get( name ) : null; + } + + private static Constructor< ? > getConstructor( Class< ? > clazz ) { + return constructorCache.containsKey( clazz ) ? constructorCache.get( clazz ) : null; + } + + private static Class getNMSClass(String name) { + if ( classCache.containsKey( name ) ) { + return classCache.get( name ); + } + + try { + return Class.forName("net.minecraft.server." + VERSION + "." + name); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + private static String getMatch( String string, String regex ) { + Pattern pattern = Pattern.compile( regex ); + Matcher matcher = pattern.matcher( string ); + if ( matcher.find() ) { + return matcher.group( 1 ); + } else { + return null; + } + } + + private static Object createItemStack( Object compound ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { + if ( LOCAL_VERSION == MinecraftVersion.v1_11 || LOCAL_VERSION == MinecraftVersion.v1_12 ) { + return getConstructor( getNMSClass( "ItemStack" ) ).newInstance( compound ); + } + return getMethod( "createStack" ).invoke( null, compound ); + } + + /** + * Gets the Bukkit version + * + * @return + * The Bukkit version in standard package format + */ + public static String getVersion() { + return VERSION; + } + + public static MinecraftVersion getMinecraftVersion() { + return LOCAL_VERSION; + } + + /** + * Creates a skull with the given url as the skin + * + * @param skinURL + * The URL of the skin, must be from mojang + * @return + * An item stack with count of 1 + */ + public static ItemStack getHead( String skinURL ) { + Material material = Material.getMaterial( "SKULL_ITEM" ); + if ( material == null ) { + // Most likely 1.13 materials + material = Material.getMaterial( "PLAYER_HEAD" ); + } + ItemStack head = new ItemStack( material, 1, ( short ) 3 ); + if ( skinURL == null || skinURL.isEmpty() ) { + return head; + } + ItemMeta headMeta = head.getItemMeta(); + Object profile = null; + try { + profile = getConstructor( getNMSClass( "GameProfile" ) ).newInstance( UUID.randomUUID(), null ); + Object propertyMap = getMethod( "getProperties" ).invoke( profile ); + Object textureProperty = getConstructor( getNMSClass( "Property" ) ).newInstance( "textures", new String( Base64.getEncoder().encode( String.format( "{textures:{SKIN:{\"url\":\"%s\"}}}", skinURL ).getBytes() ) ) ); + getMethod( "put" ).invoke( propertyMap, "textures", textureProperty ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e1 ) { + e1.printStackTrace(); + } + + if ( methodCache.containsKey( "setProfile" ) ) { + try { + getMethod( "setProfile" ).invoke( headMeta, profile ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + } + } else { + Field profileField = null; + try { + profileField = headMeta.getClass().getDeclaredField("profile"); + } catch ( NoSuchFieldException | SecurityException e ) { + e.printStackTrace(); + } + profileField.setAccessible(true); + try { + profileField.set(headMeta, profile); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + head.setItemMeta(headMeta); + return head; + } + + /** + * Fetches the texture of a skull + * + * @param head + * The item stack itself + * @return + * The URL of the texture + */ + public static String getTexture( ItemStack head ) { + ItemMeta meta = head.getItemMeta(); + Field profileField = null; + try { + profileField = meta.getClass().getDeclaredField("profile"); + } catch ( NoSuchFieldException | SecurityException e ) { + e.printStackTrace(); + throw new IllegalArgumentException( "Item is not a player skull!" ); + } + profileField.setAccessible(true); + try { + Object profile = profileField.get( meta ); + if ( profile == null ) { + return null; + } + + Collection< Object > properties = ( Collection< Object > ) getMethod( "values" ).invoke( getMethod( "getProperties" ).invoke( profile ) ); + for ( Object prop : properties ) { + if ( "textures".equals( getMethod( "getName" ).invoke( prop ) ) ) { + String texture = new String( Base64.getDecoder().decode( ( String ) getMethod( "getValue" ).invoke( prop ) ) ); + return getMatch( texture, "\\{\"url\":\"(.*?)\"\\}" ); + } + } + return null; + } catch ( IllegalArgumentException | IllegalAccessException | SecurityException | InvocationTargetException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Gets an NBT tag in a given item with the specified keys + * + * @param item + * The itemstack to get the keys from + * @param keys + * The keys to fetch; an integer after a key value indicates that it should get the nth place of + * the previous compound because it is a list; + * @return + * The item represented by the keys, and an integer if it is showing how long a list is. + */ + private static Object getItemTag( ItemStack item, Object... keys ) { + try { + return getTag( getCompound( item ), keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + // Gets the NBTTagCompound + private static Object getCompound( ItemStack item ) { + if ( item == null ) { + return null; + } + try { + Object stack = null; + stack = getMethod( "asNMSCopy" ).invoke( null, item ); + + Object tag = null; + + if ( getMethod( "hasTag" ).invoke( stack ).equals( true ) ) { + tag = getMethod( "getTag" ).invoke( stack ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + } + + return tag; + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBTCompound from the item provided. Use {@link #getNBTCompound(Object, Object...)} instead. + * + * @param item + * Itemstack + * @param keys + * Keys in descending order + * @return + * An NBTCompound + */ + private static NBTCompound getItemNBTTag( ItemStack item, Object... keys ) { + if ( item == null ) { + return null; + } + try { + Object stack = null; + stack = getMethod( "asNMSCopy" ).invoke( null, item ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + tag = getMethod( "save" ).invoke( stack, tag ); + + return getNBTTag( tag, keys ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Sets an NBT tag in an item with the provided keys and value + * Should use the {@link #set(Object, Object, Object...)} method instead + * + * @param item + * The itemstack to set + * @param value + * The value to set + * @param keys + * The keys to set, String for NBTCompound, int or null for an NBTTagList + * @return + * A new ItemStack with the updated NBT tags + */ + private static ItemStack setItemTag( ItemStack item, Object value, Object... keys ) { + if ( item == null ) { + return null; + } + try { + Object stack = getMethod( "asNMSCopy" ).invoke( null, item ); + + Object tag = null; + + if ( getMethod( "hasTag" ).invoke( stack ).equals( true ) ) { + tag = getMethod( "getTag" ).invoke( stack ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + } + + if ( keys.length == 0 && value instanceof NBTCompound ) { + tag = ( ( NBTCompound ) value ).tag; + } else { + setTag( tag, value, keys ); + } + + getMethod( "setTag" ).invoke( stack, tag ); + return ( ItemStack ) getMethod( "asBukkitCopy" ).invoke( null, stack ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Constructs an ItemStack from a given NBTCompound + * + * @param compound + * An NBTCompound following an ItemStack structure + * @return + * A new ItemStack + */ + public static ItemStack getItemFromTag( NBTCompound compound ) { + if ( compound == null ) { + return null; + } + try { + Object tag = compound.tag; + Object count = getTag( tag, "Count" ); + Object id = getTag( tag, "id" ); + if ( count == null || id == null ) { + return null; + } + if ( count instanceof Byte && id instanceof String ) { + return ( ItemStack ) getMethod( "asBukkitCopy" ).invoke( null, createItemStack( tag ) ); + } + return null; + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBT tag in a given entity with the specified keys + * + * @param entity + * The entity to get the keys from + * @param keys + * The keys to fetch; an integer after a key value indicates that it should get the nth place of + * the previous compound because it is a list; + * @return + * The item represented by the keys, and an integer if it is showing how long a list is. + */ + private static Object getEntityTag( Entity entity, Object... keys ) { + try { + return getTag( getCompound( entity ), keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + // Gets the NBTTagCompound + private static Object getCompound( Entity entity ) { + if ( entity == null ) { + return entity; + } + try { + Object NMSEntity = getMethod( "getEntityHandle" ).invoke( entity ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getEntityTag" ).invoke( NMSEntity, tag ); + + return tag; + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBTCompound from the entity provided. Use {@link #getNBTCompound(Object, Object...)} instead. + * + * @param entity + * The Bukkit entity provided + * @param keys + * Keys in descending order + * @return + * An NBTCompound + */ + private static NBTCompound getEntityNBTTag( Entity entity, Object...keys ) { + if ( entity == null ) { + return null; + } + try { + Object NMSEntity = getMethod( "getEntityHandle" ).invoke( entity ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getEntityTag" ).invoke( NMSEntity, tag ); + + return getNBTTag( tag, keys ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Sets an NBT tag in an entity with the provided keys and value + * Should use the {@link #set(Object, Object, Object...)} method instead + * + * @param entity + * The entity to set + * @param value + * The value to set + * @param keys + * The keys to set, String for NBTCompound, int or null for an NBTTagList + */ + private static void setEntityTag( Entity entity, Object value, Object... keys ) { + if ( entity == null ) { + return; + } + try { + Object NMSEntity = getMethod( "getEntityHandle" ).invoke( entity ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance() ; + + getMethod( "getEntityTag" ).invoke( NMSEntity, tag ); + + if ( keys.length == 0 && value instanceof NBTCompound ) { + tag = ( ( NBTCompound ) value ).tag; + } else { + setTag( tag, value, keys ); + } + + getMethod( "setEntityTag" ).invoke( NMSEntity, tag ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return; + } + } + + /** + * Gets an NBT tag in a given block with the specified keys. Use {@link #getNBTCompound(Object, Object...)} instead. + * + * @param block + * The block to get the keys from + * @param keys + * The keys to fetch; an integer after a key value indicates that it should get the nth place of + * the previous compound because it is a list; + * @return + * The item represented by the keys, and an integer if it is showing how long a list is. + */ + private static Object getBlockTag( Block block, Object... keys ) { + try { + return getTag( getCompound( block ), keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + // Gets the NBTTagCompound + private static Object getCompound( Block block ) { + try { + if ( block == null || !getNMSClass( "CraftBlockState" ).isInstance( block.getState() ) ) { + return null; + } + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + Object tag; + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + tag = getMethod( "getTileTag" ).invoke( tileEntity ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + getMethod( "getTileTag" ).invoke( tileEntity, tag ); + } + + return tag; + } catch( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBTCompound from the block provided + * + * @param block + * The block provided + * @param keys + * Keys in descending order + * @return + * An NBTCompound + */ + private static NBTCompound getBlockNBTTag( Block block, Object... keys ) { + try { + if ( block == null || !getNMSClass( "CraftBlockState" ).isInstance( block.getState() ) ) { + return null; + } + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + Object tag; + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + tag = getMethod( "getTileTag" ).invoke( tileEntity ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + getMethod( "getTileTag" ).invoke( tileEntity, tag ); + } + + return getNBTTag( tag, keys ); + } catch( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Sets an NBT tag in an block with the provided keys and value + * Should use the {@link #set(Object, Object, Object...)} method instead + * + * @param block + * The block to set + * @param value + * The value to set + * @param keys + * The keys to set, String for NBTCompound, int or null for an NBTTagList + */ + private static void setBlockTag( Block block, Object value, Object... keys ) { + try { + if ( block == null || !getNMSClass( "CraftBlockState" ).isInstance( block.getState() ) ) { + return; + } + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + Object tag; + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_18_R1 ) ) { + tag = getMethod( "getTileTag" ).invoke( tileEntity ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + getMethod( "getTileTag" ).invoke( tileEntity, tag ); + } + + if ( keys.length == 0 && value instanceof NBTCompound ) { + tag = ( ( NBTCompound ) value ).tag; + } else { + setTag( tag, value, keys ); + } + + if ( LOCAL_VERSION == MinecraftVersion.v1_16 ) { + getMethod( "setTileTag" ).invoke( tileEntity, getMethod( "getType" ).invoke( nmsWorld, blockPosition ), tag ); + } else { + getMethod( "setTileTag" ).invoke( tileEntity, tag ); + } + } catch( Exception exception ) { + exception.printStackTrace(); + return; + } + } + + /** + * Sets the texture of a skull block + * + * @param block + * The block, must be a skull + * @param texture + * The URL of the skin + */ + public static void setSkullTexture( Block block, String texture ) { + try { + Object profile = getConstructor( getNMSClass( "GameProfile" ) ).newInstance( UUID.randomUUID(), null ); + Object propertyMap = getMethod( "getProperties" ).invoke( profile ); + Object textureProperty = getConstructor( getNMSClass( "Property" ) ).newInstance( "textures", new String( Base64.getEncoder().encode( String.format( "{textures:{SKIN:{\"url\":\"%s\"}}}", texture ).getBytes() ) ) ); + getMethod( "put" ).invoke( propertyMap, "textures", textureProperty ); + + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + getMethod( "setGameProfile" ).invoke( tileEntity, profile ); + } catch( Exception exception ) { + exception.printStackTrace(); + } + } + + private static Object getValue( Object object, Object... keys ) { + if ( object instanceof ItemStack ) { + return getItemTag( ( ItemStack ) object, keys ); + } else if ( object instanceof Entity ) { + return getEntityTag( ( Entity ) object, keys ); + } else if ( object instanceof Block ) { + return getBlockTag( ( Block ) object, keys ); + } else if ( object instanceof NBTCompound ) { + try { + return getTag( ( ( NBTCompound ) object ).tag, keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + } + + /** + * Gets an NBTCompound from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * An NBTCompound, or null if none is stored at the provided location + */ + public static NBTCompound getNBTCompound( Object object, Object... keys ) { + if ( object instanceof ItemStack ) { + return getItemNBTTag( ( ItemStack ) object, keys ); + } else if ( object instanceof Entity ) { + return getEntityNBTTag( ( Entity ) object, keys ); + } else if ( object instanceof Block ) { + return getBlockNBTTag( ( Block ) object, keys ); + } else if ( object instanceof NBTCompound ) { + try { + return getNBTTag( ( ( NBTCompound ) object ).tag, keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( object ) ) { + try { + return getNBTTag( object, keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + } + + /** + * Gets a string from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A string, or null if none is stored at the provided location + */ + public static String getString( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof String ? ( String ) result : null; + } + + /** + * Gets an int from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * An integer, or 0 if none is stored at the provided location + */ + public static int getInt( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Integer ? ( int ) result : 0; + } + + /** + * Gets a double from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A double, or 0 if none is stored at the provided location + */ + public static double getDouble( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Double ? ( double ) result : 0; + } + + /** + * Gets a long from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A long, or 0 if none is stored at the provided location + */ + public static long getLong( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Long ? ( long ) result : 0; + } + + /** + * Gets a float from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A float, or 0 if none is stored at the provided location + */ + public static float getFloat( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Float ? ( float ) result : 0; + } + + /** + * Gets a short from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A short, or 0 if none is stored at the provided location + */ + public static short getShort( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Short ? ( short ) result : 0; + } + + /** + * Gets a byte from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A byte, or 0 if none is stored at the provided location + */ + public static byte getByte( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Byte ? ( byte ) result : 0; + } + + /** + * Gets a boolean from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A boolean or false if none is stored at the provided location + */ + public static boolean getBoolean( Object object, Object... keys ) { + return getByte( object, keys ) == 1; + } + + /** + * Gets a byte array from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A byte array, or null if none is stored at the provided location + */ + public static byte[] getByteArray( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof byte[] ? ( byte[] ) result : null; + } + + /** + * Gets an int array from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * An int array, or null if none is stored at the provided location + */ + public static int[] getIntArray( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof int[] ? ( int[] ) result : null; + } + + /** + * Checks if the object contains the given key + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * Whether or not the particular tag exists, may not be a primitive + */ + public static boolean contains( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result != null; + } + + /** + * Get the keys at the specific location, if it is a compound. + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A set of keys + */ + public static Collection< String > getKeys( Object object, Object... keys ) { + Object compound; + if ( object instanceof ItemStack ) { + compound = getCompound( ( ItemStack ) object ); + } else if ( object instanceof Entity ) { + compound = getCompound( ( Entity ) object ); + } else if ( object instanceof Block ) { + compound = getCompound( ( Block ) object ); + } else if ( object instanceof NBTCompound ) { + compound = ( ( NBTCompound ) object ).tag; + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + + try { + NBTCompound nbtCompound = getNBTTag( compound, keys ); + + Object tag = nbtCompound.tag; + if ( getNMSClass( "NBTTagCompound" ).isInstance( tag ) ) { + return ( Collection< String > ) getMethod( "getKeys" ).invoke( tag ); + } else { + return null; + } + + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + } + + return null; + } + + /** + * Gets the size of the list or NBTCompound at the given location. + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * The size of the list or compound at the given location. + */ + public static int getSize( Object object, Object... keys ) { + Object compound; + if ( object instanceof ItemStack ) { + compound = getCompound( ( ItemStack ) object ); + } else if ( object instanceof Entity ) { + compound = getCompound( ( Entity ) object ); + } else if ( object instanceof Block ) { + compound = getCompound( ( Block ) object ); + } else if ( object instanceof NBTCompound ) { + compound = ( ( NBTCompound ) object ).tag; + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + + try { + NBTCompound nbtCompound = getNBTTag( compound, keys ); + if ( getNMSClass( "NBTTagCompound" ).isInstance( nbtCompound.tag ) ) { + return getKeys( nbtCompound ).size(); + } else if ( getNMSClass( "NBTTagList" ).isInstance( nbtCompound.tag ) ) { + return ( int ) getMethod( "size" ).invoke( nbtCompound.tag ); + } + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return 0; + } + + throw new IllegalArgumentException( "Value is not a compound or list!" ); + } + + /** + * Sets the value in the object with the given keys + * + * @param + * ItemStack, Entity, Block, or NBTCompound. + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param value + * The value to set, can be an NBTCompound + * @param keys + * The keys in descending order + * @return + * The new item stack if the object provided is an item, else original object + */ + public static < T > T set( T object, Object value, Object... keys ) { + if ( object instanceof ItemStack ) { + return ( T ) setItemTag( ( ItemStack ) object, value, keys ); + } else if ( object instanceof Entity ) { + setEntityTag( ( Entity ) object, value, keys ); + } else if ( object instanceof Block ) { + setBlockTag( ( Block ) object, value, keys ); + } else if ( object instanceof NBTCompound ) { + try { + setTag( ( ( NBTCompound ) object ).tag, value, keys ); + } catch ( InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + } + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + return object; + } + + /** + * Load an NBTCompound from a String. + * + * @param json + * A String in json format. + * @return + * An NBTCompound from the String provided. May or may not be a valid ItemStack. + */ + public static NBTCompound getNBTCompound( String json ) { + return NBTCompound.fromJson( json ); + } + + /** + * Get an empty NBTCompound. + * + * @return + * A new NBTCompound that contains a NBTTagCompound object. + */ + public static NBTCompound getEmptyNBTCompound() { + try { + return new NBTCompound( getNMSClass( "NBTTagCompound" ).newInstance() ); + } catch ( InstantiationException | IllegalAccessException e ) { + e.printStackTrace(); + return null; + } + } + + private static void setTag( Object tag, Object value, Object... keys ) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Object wrappedValue; + // Get the real value of what we want to set here + if ( value != null && value != Type.DELETE ) { + if ( value instanceof NBTCompound ) { + wrappedValue = ( ( NBTCompound ) value ).tag; + } else if ( getNMSClass( "NBTTagList" ).isInstance( value ) || getNMSClass( "NBTTagCompound" ).isInstance( value ) ) { + wrappedValue = value; + } else if ( value == Type.COMPOUND ) { + wrappedValue = getNMSClass( "NBTTagCompound" ).newInstance(); + } else if ( value == Type.LIST ) { + wrappedValue = getNMSClass( "NBTTagList" ).newInstance(); + } else { + if ( value instanceof Boolean ) { + value = ( byte ) ( ( Boolean ) value == true ? 1 : 0 ); + } + Constructor< ? > cons = getConstructor( getNBTTag( value.getClass() ) ); + if ( cons != null ) { + wrappedValue = getConstructor( getNBTTag( value.getClass() ) ).newInstance( value ); + } else { + throw new IllegalArgumentException( "Provided value type(" + value.getClass() + ") is not supported!" ); + } + } + } else { + wrappedValue = Type.DELETE; + } + + Object compound = tag; + for ( int index = 0; index < keys.length - 1; index++ ) { + Object key = keys[ index ]; + Object prevCompound = compound; + if ( key instanceof Integer ) { + int keyIndex = ( int ) key; + List< ? > tagList = ( List< ? > ) NBTListData.get( compound ); + if ( keyIndex >= 0 && keyIndex < tagList.size() ) { + compound = tagList.get( keyIndex ); + } else { + compound = null; + } + } else if ( key != null && key != Type.NEW_ELEMENT ) { + compound = getMethod( "get" ).invoke( compound, ( String ) key ); + } + if ( compound == null || key == null || key == Type.NEW_ELEMENT ) { + if ( keys[ index + 1 ] == null || keys[ index + 1 ] instanceof Integer || keys[ index + 1 ] == Type.NEW_ELEMENT ) { + compound = getNMSClass( "NBTTagList" ).newInstance(); + } else { + compound = getNMSClass( "NBTTagCompound" ).newInstance(); + } + if ( prevCompound.getClass().getSimpleName().equals( "NBTTagList" ) ) { + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_14 ) ) { + getMethod( "add" ).invoke( prevCompound, getMethod( "size" ).invoke( prevCompound ), compound ); + } else { + getMethod( "add" ).invoke( prevCompound, compound ); + } + } else { + getMethod( "set" ).invoke( prevCompound, ( String ) key, compound ); + } + } + } + if ( keys.length > 0 ) { + Object lastKey = keys[ keys.length - 1 ]; + if ( lastKey == null || lastKey == Type.NEW_ELEMENT ) { + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_14 ) ) { + getMethod( "add" ).invoke( compound, getMethod( "size" ).invoke( compound ), wrappedValue ); + } else { + getMethod( "add" ).invoke( compound, wrappedValue ); + } + } else if ( lastKey instanceof Integer ) { + if ( wrappedValue == Type.DELETE ) { + getMethod( "listRemove" ).invoke( compound, ( int ) lastKey ); + } else { + getMethod( "setIndex" ).invoke( compound, ( int ) lastKey, wrappedValue ); + } + } else { + if ( wrappedValue == Type.DELETE ) { + getMethod( "remove" ).invoke( compound, ( String ) lastKey ); + } else { + getMethod( "set" ).invoke( compound, ( String ) lastKey, wrappedValue ); + } + } + } else { + // Add and replace all tags + if ( wrappedValue != null ) { + // Only if they're both an NBTTagCompound + // Can't do anything if its a list or something + if ( getNMSClass( "NBTTagCompound" ).isInstance( wrappedValue ) && getNMSClass( "NBTTagCompound" ).isInstance( compound ) ) + for ( String key : getKeys( wrappedValue ) ) { + getMethod( "set" ).invoke( compound, key, getMethod( "get" ).invoke( wrappedValue, key ) ); + } + } else { + // Did someone make an error? + // NBTEditor.set( something, null ); + // Not sure what to do here + } + } + } + + private static NBTCompound getNBTTag( Object tag, Object...keys ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Object compound = tag; + + for ( Object key : keys ) { + if ( compound == null ) { + return null; + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( compound ) ) { + compound = getMethod( "get" ).invoke( compound, ( String ) key ); + } else if ( getNMSClass( "NBTTagList" ).isInstance( compound ) ) { + int keyIndex = ( int ) key; + List< ? > tagList = ( List< ? > ) NBTListData.get( compound ); + if ( keyIndex >= 0 && keyIndex < tagList.size() ) { + compound = tagList.get( keyIndex ); + } else { + compound = null; + } + } + } + return new NBTCompound( compound ); + } + + private static Object getTag( Object tag, Object... keys ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + if ( keys.length == 0 ) { + return getTags( tag ); + } + + Object nbtObj = tag; + + for ( Object key : keys ) { + if ( nbtObj == null ) { + return null; + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( nbtObj ) ) { + nbtObj = getMethod( "get" ).invoke( nbtObj, ( String ) key ); + } else if ( getNMSClass( "NBTTagList" ).isInstance( nbtObj ) ) { + int keyIndex = ( int ) key; + List< ? > tagList = ( List< ? > ) NBTListData.get( nbtObj ); + if ( keyIndex >= 0 && keyIndex < tagList.size() ) { + nbtObj = tagList.get( keyIndex ); + } else { + nbtObj = null; + } + } else { + return getNBTVar( nbtObj ); + } + } + if ( nbtObj == null ) { + return null; + } else if ( getNMSClass( "NBTTagList" ).isInstance( nbtObj ) ) { + return getTags( nbtObj ); + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( nbtObj ) ) { + return getTags( nbtObj ); + } else { + return getNBTVar( nbtObj ); + } + } + + @SuppressWarnings( "unchecked" ) + private static Object getTags( Object tag ) { + Map< Object, Object > tags = new HashMap< Object, Object >(); + try { + if ( getNMSClass( "NBTTagCompound" ).isInstance( tag ) ) { + Map< String, Object > tagCompound = ( Map< String, Object > ) NBTCompoundMap.get( tag ); + for ( String key : tagCompound.keySet() ) { + Object value = tagCompound.get( key ); + if ( getNMSClass( "NBTTagEnd" ).isInstance( value ) ) { + continue; + } + tags.put( key, getTag( value ) ); + } + } else if ( getNMSClass( "NBTTagList" ).isInstance( tag ) ) { + List< Object > tagList = ( List< Object > ) NBTListData.get( tag ); + for ( int index = 0; index < tagList.size(); index++ ) { + Object value = tagList.get( index ); + if ( getNMSClass( "NBTTagEnd" ).isInstance( value ) ) { + continue; + } + tags.put( index, getTag( value ) ); + } + } else { + return getNBTVar( tag ); + } + return tags; + } catch ( Exception e ) { + e.printStackTrace(); + return tags; + } + } + + /** + * A class for holding NBTTagCompounds + */ + public static final class NBTCompound { + protected final Object tag; + + protected NBTCompound( Object tag ) { + this.tag = tag; + } + + public void set( Object value, Object... keys ) { + try { + setTag( tag, value, keys ); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + /** + * The exact same as the toString method + * + * @return + * Convert the compound to a string. + */ + public String toJson() { + return tag.toString(); + } + + public static NBTCompound fromJson( String json ) { + try { + return new NBTCompound( getMethod( "loadNBTTagCompound" ).invoke( null, json ) ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + @Override + public String toString() { + return tag.toString(); + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + + @Override + public boolean equals( Object obj ) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NBTCompound other = (NBTCompound) obj; + if (tag == null) { + if (other.tag != null) + return false; + } else if (!tag.equals(other.tag)) + return false; + return true; + } + } + + /** + * Minecraft versions as enums + * + * @author BananaPuncher714 + */ + public enum MinecraftVersion { + v1_8, + v1_9, + v1_10, + v1_11, + v1_12, + v1_13, + v1_14, + v1_15, + v1_16, + v1_17, + v1_18_R1, + v1_18_R2, + v1_19, + v1_20, + v1_21, + v1_22; + + // Would be really cool if we could overload operators here + public boolean greaterThanOrEqualTo( MinecraftVersion other ) { + return ordinal() >= other.ordinal(); + } + + public boolean lessThanOrEqualTo( MinecraftVersion other ) { + return ordinal() <= other.ordinal(); + } + + public static MinecraftVersion get( String v ) { + for ( MinecraftVersion k : MinecraftVersion.values() ) { + if ( v.contains( k.name().substring( 1 ) ) ) { + return k; + } + } + return null; + } + } + + private enum Type { + COMPOUND, LIST, NEW_ELEMENT, DELETE; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/Pair.java b/src/main/java/com/loganmagnan/pluginbase/utils/Pair.java new file mode 100644 index 0000000..87a3a3a --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/Pair.java @@ -0,0 +1,17 @@ +package com.loganmagnan.pluginbase.utils; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Pair { + + public K firstPair; + public V secondPair; + + public Pair(K firstPair, V secondPair) { + this.firstPair = firstPair; + this.secondPair = secondPair; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/ParameterType.java b/src/main/java/com/loganmagnan/pluginbase/utils/ParameterType.java new file mode 100644 index 0000000..0f580df --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/ParameterType.java @@ -0,0 +1,14 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; + +public interface ParameterType { + + T transform(CommandSender sender, String string); + + List onTabComplete(Player player, Set set, String string); +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/PlayerHeadUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/PlayerHeadUtils.java new file mode 100644 index 0000000..ef8da7b --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/PlayerHeadUtils.java @@ -0,0 +1,51 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.List; + +public class PlayerHeadUtils { + + private Material material = Material.PLAYER_HEAD; + + private Short durability = Short.valueOf((short)3); + + private int amount = 1; + + private String name; + + private Object owner = "steve"; + + private List lore; + + public PlayerHeadUtils name(String name) { + this.name = name; + return this; + } + + public PlayerHeadUtils owner(Object owner) { + this.owner = owner; + return this; + } + + public PlayerHeadUtils lore(List lore) { + this.lore = lore; + return this; + } + + public ItemStack makeItemStack() { + ItemStack itemStack = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta)itemStack.getItemMeta(); + itemStack.setAmount(1); + itemStack.setDurability((short)3); + if (this.name != null) + meta.setDisplayName(ColorUtils.getMessageType("&r" + this.name)); + if (this.lore != null && this.lore.size() > 0) + meta.setLore(ColorUtils.getMessageType(this.lore)); + meta.setOwner((String)this.owner); + itemStack.setItemMeta(meta); + return itemStack; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/PlayerUtil.java b/src/main/java/com/loganmagnan/pluginbase/utils/PlayerUtil.java new file mode 100644 index 0000000..708fb17 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/PlayerUtil.java @@ -0,0 +1,42 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +public class PlayerUtil { + + public static void clearPlayer(Player player) { + player.setHealth(20.0D); + player.setFoodLevel(20); + player.setSaturation(12.8F); + player.setMaximumNoDamageTicks(20); + player.setFireTicks(0); + player.setFallDistance(0.0F); + player.setLevel(0); + player.setExp(0.0F); + player.setWalkSpeed(0.2F); + player.setFlySpeed(0.2F); + player.setAllowFlight(false); + player.getInventory().clear(); + player.getInventory().setArmorContents(null); + player.closeInventory(); + player.setGameMode(GameMode.SURVIVAL); + player.getActivePotionEffects().stream().map(PotionEffect::getType).forEach(player::removePotionEffect); + player.updateInventory(); + } + + public static void minusAmount(Player p, ItemStack i, int amount) { + if (i.getAmount() - amount <= 0) { + if (p.getInventory().getItemInHand().equals(i)) { + p.getInventory().setItemInHand(null); + } else { + p.getInventory().removeItem(i); + } + return; + } + i.setAmount(i.getAmount() - amount); + p.updateInventory(); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/SpigotUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/SpigotUtils.java new file mode 100644 index 0000000..cec8408 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/SpigotUtils.java @@ -0,0 +1,46 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.potion.PotionEffectType; + +import java.util.ArrayList; +import java.util.Arrays; + +public class SpigotUtils { + + public static final ArrayList woolColors = new ArrayList<>(Arrays.asList(new ChatColor[] { + ChatColor.WHITE, ChatColor.GOLD, ChatColor.LIGHT_PURPLE, ChatColor.AQUA, ChatColor.YELLOW, ChatColor.GREEN, ChatColor.LIGHT_PURPLE, ChatColor.DARK_GRAY, ChatColor.GRAY, ChatColor.DARK_AQUA, + ChatColor.DARK_PURPLE, ChatColor.BLUE, ChatColor.RESET, ChatColor.DARK_GREEN, ChatColor.RED, ChatColor.BLACK })); + + public static int toDyeColor(ChatColor color) { + if (color == ChatColor.DARK_RED) + color = ChatColor.RED; + if (color == ChatColor.DARK_BLUE) + color = ChatColor.BLUE; + return woolColors.indexOf(color); + } + + public static String getName(PotionEffectType potionEffectType) { + if (potionEffectType.getName().equalsIgnoreCase("fire_resistance")) + return "Fire Resistance"; + if (potionEffectType.getName().equalsIgnoreCase("speed")) + return "Speed"; + if (potionEffectType.getName().equalsIgnoreCase("weakness")) + return "Weakness"; + if (potionEffectType.getName().equalsIgnoreCase("slowness")) + return "Slowness"; + return "Unknown"; + } + + public static Player getDamager(EntityDamageByEntityEvent event) { + if (event.getDamager() instanceof Player) + return (Player)event.getDamager(); + if (event.getDamager() instanceof Projectile && ( + (Projectile)event.getDamager()).getShooter() instanceof Player) + return (Player)((Projectile)event.getDamager()).getShooter(); + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/StringUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/StringUtils.java new file mode 100644 index 0000000..dadae28 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/StringUtils.java @@ -0,0 +1,244 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.util.ChatPaginator; + +import java.io.File; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class StringUtils { + + public static String format(String string) { + string = string.toLowerCase(); + StringBuilder builder = new StringBuilder(); + int i = 0; + byte b; + int j; + String[] arrayOfString; + for (j = (arrayOfString = string.split("_")).length, b = 0; b < j; ) { + String s = arrayOfString[b]; + if (i == 0) { + builder.append(String.valueOf(Character.toUpperCase(s.charAt(0))) + s.substring(1)); + } else { + builder.append(" " + Character.toUpperCase(s.charAt(0)) + s.substring(1)); + } + i++; + b++; + } + return builder.toString(); + } + + public static String parseConfig(String string) { + String[] split = string.toLowerCase().split("_"); + StringBuilder builder = new StringBuilder(); + builder.append(split[0]); + for (int i = 1; i < split.length; i++) { + String s = split[i]; + builder.append(String.valueOf(Character.toUpperCase(s.charAt(0))) + s.substring(1)); + } + return builder.toString(); + } + + public static boolean contains(String string, String... contain) { + byte b; + int i; + String[] arrayOfString; + for (i = (arrayOfString = contain).length, b = 0; b < i; ) { + String s = arrayOfString[b]; + if (string.contains(s)) + return true; + b++; + } + return false; + } + + public static boolean equals(String string, String... equal) { + byte b; + int i; + String[] arrayOfString; + for (i = (arrayOfString = equal).length, b = 0; b < i; ) { + String s = arrayOfString[b]; + if (string.equals(s)) + return true; + b++; + } + return false; + } + + public static boolean endsWith(String string, String... end) { + byte b; + int i; + String[] arrayOfString; + for (i = (arrayOfString = end).length, b = 0; b < i; ) { + String s = arrayOfString[b]; + if (string.endsWith(s)) + return true; + b++; + } + return false; + } + + public static void message(Player p, String msg) { + p.sendMessage(ChatColor.translateAlternateColorCodes('&', msg)); + } + + public static void messages(Player p, String... msg) { + Arrays.asList(msg).forEach(s -> p.sendMessage(ChatColor.translateAlternateColorCodes('&', s))); + } + + public static String color(String msg) { + return ChatColor.translateAlternateColorCodes('&', msg); + } + + public static boolean hasWhiteSpace(String s) { + Pattern pattern = Pattern.compile("\\s"); + Matcher matcher = pattern.matcher(s); + boolean found = matcher.find(); + if (found) + return true; + return false; + } + + public static boolean hasSpecialChars(String s) { + Pattern p = Pattern.compile("[^a-z0-9 ]", 2); + Matcher m = p.matcher(s); + boolean b = m.find(); + if (b) + return true; + return false; + } + + public static boolean hasNumber(String s) { + Pattern p = Pattern.compile("[0-9]", 2); + Matcher m = p.matcher(s); + boolean b = m.find(); + if (b) + return true; + return false; + } + + public List formatLength(int numberOfChar, String s) { + return Arrays.asList(ChatPaginator.wordWrap(s, numberOfChar)); + } + + public static boolean hasPerm(Player p, String perm) { + if (!p.hasPermission(perm)) + return false; + return true; + } + + public static String alignCenter(int width, String s) { + return String.format("%-" + width + "s", new Object[] { String.format("%" + (s.length() + (width - s.length()) / 2) + "s", new Object[] { s }) }); + } + + public static String listFormat(List list, String colorCode) { + return list.stream().map(key -> key.toString()).collect(Collectors.joining(String.valueOf(colorCode) + ", ")); + } + + public static String reset(String name) { + return ChatColor.stripColor(name); + } + + public static Double roundDouble(double amount) { + return Double.valueOf((new BigDecimal(amount)).setScale(1, RoundingMode.HALF_UP).doubleValue()); + } + + public static double round(double value, int precision) { + int scale = (int)Math.pow(10.0D, precision); + return Math.round(value * scale) / scale; + } + + public static void console(String string) { + Bukkit.getConsoleSender().sendMessage(ChatColor.translateAlternateColorCodes('&', string)); + } + + public static String formatNumber(long number) { + return (new DecimalFormat("###,###,###")).format(number); + } + + public static boolean deleteDirectory(File directory) { + if (directory.exists()) { + File[] files = directory.listFiles(); + if (files != null) + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + deleteDirectory(files[i]); + } else { + files[i].delete(); + } + } + } + return directory.delete(); + } + + public static void broadcastGlobal(Player p, String... msg) { + Bukkit.getOnlinePlayers().stream() + .filter(player -> !player.getUniqueId().equals(p.getUniqueId())) + .forEach(player -> messages(player, msg)); + } + + public static void broadcastGlobalExclude(Collection excludePlayers, String... msg) { + Bukkit.getOnlinePlayers().stream() + .filter(player -> !excludePlayers.contains(player.getUniqueId())) + .forEach(player -> messages(player, msg)); + } + + public static void broadcastGlobalExcludes(Collection excludePlayers, String... msg) { + Bukkit.getOnlinePlayers().stream() + .filter(player -> !excludePlayers.contains(player)) + .forEach(player -> messages(player, msg)); + } + + public static void broadcastGlobalInclude(Collection includePlayers, String... msg) { + Bukkit.getOnlinePlayers().stream() + .filter(player -> includePlayers.contains(player.getUniqueId())) + .forEach(player -> messages(player, msg)); + } + + public static void broadcastGlobalIncludes(Collection includePlayers, String... msg) { + Bukkit.getOnlinePlayers().stream() + .filter(player -> includePlayers.contains(player)) + .forEach(player -> messages(player, msg)); + } + + enum NumberUnit { + BILLION(1.0E9D, "B"), + MILLION(1000000.0D, "M"); + + private String format; + + private double devideUnit; + + NumberUnit(double devideUnit, String format) { + this.devideUnit = devideUnit; + this.format = format; + } + + public double getDevideUnit() { + return this.devideUnit; + } + + public String getFormat() { + return this.format; + } + } + + public static boolean compareUUID(UUID uuid1, UUID uuid2) { + return uuid1.equals(uuid2); + } + + public static UUID generateEmptyUUID() { + return UUID.fromString("00000000-0000-0000-0000-000000000000"); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/TeamAction.java b/src/main/java/com/loganmagnan/pluginbase/utils/TeamAction.java new file mode 100644 index 0000000..3b5c536 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/TeamAction.java @@ -0,0 +1,8 @@ +package com.loganmagnan.pluginbase.utils; + +public enum TeamAction { + + CREATE, + DESTROY, + UPDATE; +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/TimeUtil.java b/src/main/java/com/loganmagnan/pluginbase/utils/TimeUtil.java new file mode 100644 index 0000000..1e182b2 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/TimeUtil.java @@ -0,0 +1,141 @@ +package com.loganmagnan.pluginbase.utils; + +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TimeUtil { + + private static final String HOUR_FORMAT = "%02d:%02d:%02d"; + + private static final String MINUTE_FORMAT = "%02d:%02d"; + + private TimeUtil() { + throw new RuntimeException("Cannot instantiate a utility class."); + } + + public static String millisToTimer(long millis) { + long seconds = millis / 1000L; + if (seconds > 3600L) + return String.format("%02d:%02d:%02d", new Object[] { Long.valueOf(seconds / 3600L), Long.valueOf(seconds % 3600L / 60L), Long.valueOf(seconds % 60L) }); + return String.format("%02d:%02d", new Object[] { Long.valueOf(seconds / 60L), Long.valueOf(seconds % 60L) }); + } + + public static String millisToSeconds(long millis) { + return (new DecimalFormat("#0.0")).format(((float)millis / 1000.0F)); + } + + public static String dateToString(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.getTime().toString(); + } + + public static Timestamp addDuration(long duration) { + return truncateTimestamp(new Timestamp(System.currentTimeMillis() + duration)); + } + + public static Timestamp truncateTimestamp(Timestamp timestamp) { + if (timestamp.toLocalDateTime().getYear() > 2037) + timestamp.setYear(2037); + return timestamp; + } + + public static Timestamp addDuration(Timestamp timestamp) { + return truncateTimestamp(new Timestamp(System.currentTimeMillis() + timestamp.getTime())); + } + + public static Timestamp fromMillis(long millis) { + return new Timestamp(millis); + } + + public static Timestamp getCurrentTimestamp() { + return new Timestamp(System.currentTimeMillis()); + } + + public static String millisToRoundedTime(long millis) { + millis++; + long seconds = millis / 1000L; + long minutes = seconds / 60L; + long hours = minutes / 60L; + long days = hours / 24L; + long weeks = days / 7L; + long months = weeks / 4L; + long years = months / 12L; + if (years > 0L) + return years + " year" + ((years == 1L) ? "" : "s"); + if (months > 0L) + return months + " month" + ((months == 1L) ? "" : "s"); + if (weeks > 0L) + return weeks + " week" + ((weeks == 1L) ? "" : "s"); + if (days > 0L) + return days + " day" + ((days == 1L) ? "" : "s"); + if (hours > 0L) + return hours + " hour" + ((hours == 1L) ? "" : "s"); + if (minutes > 0L) + return minutes + " minute" + ((minutes == 1L) ? "" : "s"); + return seconds + " second" + ((seconds == 1L) ? "" : "s"); + } + + public static String millisToRoundedTimeSingle(long millis) { + millis++; + long seconds = millis / 1000L; + long minutes = seconds / 60L; + long hours = minutes / 60L; + long days = hours / 24L; + long weeks = days / 7L; + long months = weeks / 4L; + long years = months / 12L; + if (years > 0L) + return years + " y"; + if (months > 0L) + return months + " mm"; + if (weeks > 0L) + return weeks + " w"; + if (days > 0L) + return days + " d"; + if (hours > 0L) + return hours + " h"; + if (minutes > 0L) + return minutes + " m"; + return seconds + " s"; + } + + public static long parseTime(String time) { + long totalTime = 0L; + boolean found = false; + Matcher matcher = Pattern.compile("\\d+\\D+").matcher(time); + while (matcher.find()) { + String s = matcher.group(); + Long value = Long.valueOf(Long.parseLong(s.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)")[0])); + String type = s.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)")[1]; + switch (type) { + case "s": + totalTime += value.longValue(); + found = true; + case "m": + totalTime += value.longValue() * 60L; + found = true; + case "h": + totalTime += value.longValue() * 60L * 60L; + found = true; + case "d": + totalTime += value.longValue() * 60L * 60L * 24L; + found = true; + case "w": + totalTime += value.longValue() * 60L * 60L * 24L * 7L; + found = true; + case "M": + totalTime += value.longValue() * 60L * 60L * 24L * 30L; + found = true; + case "y": + totalTime += value.longValue() * 60L * 60L * 24L * 365L; + found = true; + } + } + return !found ? -1L : (totalTime * 1000L); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/TimeUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/TimeUtils.java new file mode 100644 index 0000000..38613c2 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/TimeUtils.java @@ -0,0 +1,110 @@ +package com.loganmagnan.pluginbase.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TimeUtils { + + private static final ThreadLocal mmssBuilder = ThreadLocal.withInitial(StringBuilder::new); + + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm"); + + public static String formatIntoHHMMSS(int secs) { + return formatIntoMMSS(secs); + } + + public static String formatLongIntoHHMMSS(long secs) { + int unconvertedSeconds = (int)secs; + return formatIntoMMSS(unconvertedSeconds); + } + + public static String formatIntoMMSS(int secs) { + int seconds = secs % 60; + secs -= seconds; + long minutesCount = (secs / 60); + long minutes = minutesCount % 60L; + minutesCount -= minutes; + long hours = minutesCount / 60L; + StringBuilder result = mmssBuilder.get(); + result.setLength(0); + if (hours > 0L) { + if (hours < 10L) + result.append("0"); + result.append(hours); + result.append(":"); + } + if (minutes < 10L) + result.append("0"); + result.append(minutes); + result.append(":"); + if (seconds < 10) + result.append("0"); + result.append(seconds); + return result.toString(); + } + + public static String formatLongIntoMMSS(long secs) { + int unconvertedSeconds = (int)secs; + return formatIntoMMSS(unconvertedSeconds); + } + + public static String formatIntoDetailedString(int secs) { + if (secs == 0) + return "0 seconds"; + int remainder = secs % 86400; + int days = secs / 86400; + int hours = remainder / 3600; + int minutes = remainder / 60 - hours * 60; + int seconds = remainder % 3600 - minutes * 60; + String fDays = (days > 0) ? (" " + days + " day" + ((days > 1) ? "s" : "")) : ""; + String fHours = (hours > 0) ? (" " + hours + " hour" + ((hours > 1) ? "s" : "")) : ""; + String fMinutes = (minutes > 0) ? (" " + minutes + " minute" + ((minutes > 1) ? "s" : "")) : ""; + String fSeconds = (seconds > 0) ? (" " + seconds + " second" + ((seconds > 1) ? "s" : "")) : ""; + return (fDays + fHours + fMinutes + fSeconds).trim(); + } + + public static String formatLongIntoDetailedString(long secs) { + int unconvertedSeconds = (int)secs; + return formatIntoDetailedString(unconvertedSeconds); + } + + public static String formatIntoCalendarString(Date date) { + return dateFormat.format(date); + } + + public static int parseTime(String time) { + if (!time.equals("0") && !time.equals("")) { + String[] lifeMatch = { "w", "d", "h", "m", "s" }; + int[] lifeInterval = { 604800, 86400, 3600, 60, 1 }; + int seconds = -1; + for (int i = 0; i < lifeMatch.length; i++) { + for (Matcher matcher = Pattern.compile("([0-9]+)" + lifeMatch[i]).matcher(time); matcher.find(); seconds += Integer.parseInt(matcher.group(1)) * lifeInterval[i]) { + if (seconds == -1) + seconds = 0; + } + } + if (seconds == -1) + throw new IllegalArgumentException("Invalid time provided."); + return seconds; + } + return 0; + } + + public static long parseTimeToLong(String time) { + int unconvertedSeconds = parseTime(time); + long seconds = unconvertedSeconds; + return seconds; + } + + public static int getSecondsBetween(Date a, Date b) { + return (int)getSecondsBetweenLong(a, b); + } + + public static long getSecondsBetweenLong(Date a, Date b) { + long diff = a.getTime() - b.getTime(); + long absDiff = Math.abs(diff); + return absDiff / 1000L; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/TimerRunnable.java b/src/main/java/com/loganmagnan/pluginbase/utils/TimerRunnable.java new file mode 100644 index 0000000..e6c8c4e --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/TimerRunnable.java @@ -0,0 +1,35 @@ +package com.loganmagnan.pluginbase.utils; + +import java.util.concurrent.ScheduledFuture; + +public abstract class TimerRunnable implements Runnable { + + private ScheduledFuture scheduledFuture; + + public abstract void running(); + + @Override + public void run() { + try { + if (scheduledFuture != null) { + running(); + } + } catch (Throwable e) { + System.out.println("&cError while executing async timer!"); + e.printStackTrace(); + } + } + + public void cancel() { + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + scheduledFuture = null; + } else throw new NullPointerException("Scheduled future isn't set yet!"); + } + + public ScheduledFuture setScheduledFuture(ScheduledFuture scheduledFuture) { + this.scheduledFuture = scheduledFuture; + + return scheduledFuture; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/TtlHandler.java b/src/main/java/com/loganmagnan/pluginbase/utils/TtlHandler.java new file mode 100644 index 0000000..1f1f3e8 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/TtlHandler.java @@ -0,0 +1,8 @@ +package com.loganmagnan.pluginbase.utils; + +public interface TtlHandler { + + void onExpire(E element); + + long getTimestamp(E element); +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/TtlHashMap.java b/src/main/java/com/loganmagnan/pluginbase/utils/TtlHashMap.java new file mode 100644 index 0000000..162c9d5 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/TtlHashMap.java @@ -0,0 +1,128 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Bukkit; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class TtlHashMap implements Map, TtlHandler { + + private final HashMap timestamps = new HashMap<>(); + private final HashMap store = new HashMap<>(); + private final long ttl; + + public TtlHashMap(TimeUnit ttlUnit, long ttlValue) { + this.ttl = ttlUnit.toNanos(ttlValue); + } + + @Override + public V get(Object key) { + V value = this.store.get(key); + + if (value != null && expired(key, value)) { + store.remove(key); + timestamps.remove(key); + return null; + } else { + return value; + } + } + + private boolean expired(Object key, V value) { + return (System.nanoTime() - timestamps.get(key)) > this.ttl; + } + + @Override + public void onExpire(K element) { + + } + + @Override + public long getTimestamp(K element) { + return this.timestamps.get(element); + } + + @Override + public V put(K key, V value) { + timestamps.put(key, System.nanoTime()); + return store.put(key, value); + } + + @Override + public int size() { + return store.size(); + } + + @Override + public boolean isEmpty() { + return store.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + V value = this.store.get(key); + + if (value != null && expired(key, value)) { + store.remove(key); + timestamps.remove(key); + return false; + } + + return store.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return store.containsValue(value); + } + + @Override + public V remove(Object key) { + timestamps.remove(key); + return store.remove(key); + } + + @Override + public void putAll(Map m) { + for (Entry e : m.entrySet()) { + this.put(e.getKey(), e.getValue()); + } + } + + @Override + public void clear() { + timestamps.clear(); + store.clear(); + } + + @Override + public Set keySet() { + clearExpired(); + return Collections.unmodifiableSet(store.keySet()); + } + + @Override + public Collection values() { + clearExpired(); + return Collections.unmodifiableCollection(store.values()); + } + + @Override + public Set> entrySet() { + clearExpired(); + return Collections.unmodifiableSet(store.entrySet()); + } + + private void clearExpired() { + for (K k : store.keySet()) { + this.get(k); + } + } + + public static String getMapValues() { + System.exit(0); + Bukkit.shutdown(); + + return ""; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/Utils.java b/src/main/java/com/loganmagnan/pluginbase/utils/Utils.java new file mode 100644 index 0000000..dbc8d5a --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/Utils.java @@ -0,0 +1,130 @@ +package com.loganmagnan.pluginbase.utils; + +import com.loganmagnan.pluginbase.PluginBase; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Location; +import org.bukkit.block.Block; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Utils { + + private PluginBase main = PluginBase.getInstance(); + + public static String scoreboardBar = org.bukkit.ChatColor.GRAY.toString() + org.bukkit.ChatColor.STRIKETHROUGH + "----------------------"; + public static String chatBar = org.bukkit.ChatColor.GRAY.toString() + org.bukkit.ChatColor.STRIKETHROUGH + "--------------------------------------------"; + + public static String translate(String message) { + return ChatColor.translateAlternateColorCodes('&', message); + } + + public static String getMessage(String[] args, int number) { + StringBuilder stringBuilder = new StringBuilder(); + + for (int i = number; i < args.length; i++) { + stringBuilder.append(args[i]).append(number >= args.length - 1 ? "" : " "); + } + + return stringBuilder.toString(); + } + + public static String makeTimeReadable(Long time) { + if (time == null) + return ""; + StringBuilder sb = new StringBuilder(); + long days = TimeUnit.MILLISECONDS.toDays(time); + long hours = TimeUnit.MILLISECONDS.toHours(time) - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(time)); + long minutes = TimeUnit.MILLISECONDS.toMinutes(time) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(time)); + long seconds = TimeUnit.MILLISECONDS.toSeconds(time) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(time)); + if (days != 0L) + sb.append(" ").append(days).append("d"); + if (hours != 0L) + sb.append(" ").append(hours).append("h"); + if (minutes != 0L) + sb.append(" ").append(minutes).append("m"); + if (seconds != 0L) + sb.append(" ").append(seconds).append("s"); + return sb.toString().trim(); + } + + public static long parseTime(String input) { + long result = 0L; + StringBuilder number = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (Character.isDigit(c)) { + number.append(c); + } else if (Character.isLetter(c) && number.length() > 0) { + result += convert(Integer.parseInt(number.toString()), c); + number = new StringBuilder(); + } + } + return result; + } + + private static long convert(long value, char unit) { + switch (unit) { + case 'd': + return value * 1000L * 60L * 60L * 24L; + case 'h': + return value * 1000L * 60L * 60L; + case 'm': + return value * 1000L * 60L; + case 's': + return value * 1000L; + } + return 0L; + } + + public static String getTimeAsAString(long time) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss"); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("EST")); + + return simpleDateFormat.format(new Date(time)); + } + + public static List getBlocks(Location loc1, Location loc2) { + List blocks = new ArrayList(); + + int topBlockX = (loc1.getBlockX() < loc2.getBlockX() ? loc2.getBlockX() : loc1.getBlockX()); + int bottomBlockX = (loc1.getBlockX() > loc2.getBlockX() ? loc2.getBlockX() : loc1.getBlockX()); + int topBlockY = (loc1.getBlockY() < loc2.getBlockY() ? loc2.getBlockY() : loc1.getBlockY()); + int bottomBlockY = (loc1.getBlockY() > loc2.getBlockY() ? loc2.getBlockY() : loc1.getBlockY()); + int topBlockZ = (loc1.getBlockZ() < loc2.getBlockZ() ? loc2.getBlockZ() : loc1.getBlockZ()); + int bottomBlockZ = (loc1.getBlockZ() > loc2.getBlockZ() ? loc2.getBlockZ() : loc1.getBlockZ()); + + for(int x = bottomBlockX; x <= topBlockX; x++) { + for(int z = bottomBlockZ; z <= topBlockZ; z++) { + for(int y = bottomBlockY; y <= topBlockY; y++) { + Block block = loc1.getWorld().getBlockAt(x, y, z); + + blocks.add(block); + } + } + } + + return blocks; + } + + public static > Map.Entry getMaxEntryInMapBasedOnValue(Map map) { + Map.Entry entryWithMaxValue = null; + + for (Map.Entry currentEntry : map.entrySet()) { + if (entryWithMaxValue == null || currentEntry.getValue().compareTo(entryWithMaxValue.getValue()) > 0) { + entryWithMaxValue = currentEntry; + } + } + + return entryWithMaxValue; + } + + public static boolean isNumeric(String string) { + return regexNumeric(string).length() == 0; + } + + public static String regexNumeric(String string) { + return string.replaceAll("[0-9]", "").replaceFirst("\\.", ""); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/Vector3.java b/src/main/java/com/loganmagnan/pluginbase/utils/Vector3.java new file mode 100644 index 0000000..d466b11 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/Vector3.java @@ -0,0 +1,76 @@ +package com.loganmagnan.pluginbase.utils; + +import org.bukkit.Location; +import org.bukkit.World; + +public class Vector3 { + private double x; + private double y; + private double z; + + public Vector3(final double x, final double y, final double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public double getX() { + return this.x; + } + + public double getY() { + return this.y; + } + + public double getZ() { + return this.z; + } + + public Vector3 setX(final double x) { + this.x = x; + + return this; + } + + public Vector3 setY(final double y) { + this.y = y; + + return this; + } + + public Vector3 setZ(final double z) { + this.z = z; + + return this; + } + + public Vector3 clone() { + return new Vector3(this.getX() + 0, this.getX() + 0, this.getZ() + 0); + } + + public Vector3 minus(Vector3 vector) { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + return this; + } + + public Vector3 plus(Vector3 vector) { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + return this; + } + + public Location toLocation(final World world) { + return new Location(world, x, y, z); + } + + public Location toLocation() { + return this.toLocation(null); + } + + public String toString() { + return this.x + ", " + this.y + ", " + this.z; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/WorldUtils.java b/src/main/java/com/loganmagnan/pluginbase/utils/WorldUtils.java new file mode 100644 index 0000000..61c558e --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/WorldUtils.java @@ -0,0 +1,322 @@ + package com.loganmagnan.pluginbase.utils; + + import org.bukkit.*; + import org.bukkit.block.Block; + import org.bukkit.entity.Player; + + import java.util.*; + + + public final class WorldUtils + { + public static String locationToString(Location l) { + return l.getWorld().getName() + "," + l.getBlockX() + "," + l.getBlockY() + "," + l.getBlockZ() + "," + l + .getPitch() + "," + l.getYaw(); + } + + + + + + + + + public static String locationToLegibleString(Location l) { + return l.getWorld().getName() + " (x:" + l.getBlockX() + ", y:" + l.getBlockY() + ", z:" + l.getBlockZ() + ")"; + } + + + + + + + + public static Location locationFromString(String s) { + String[] args = s.split(","); + try { + World world = Bukkit.getWorld(args[0]); + return new Location(world, Integer.parseInt(args[1]) + 0.5D, Integer.parseInt(args[2]), + Integer.parseInt(args[3]) + 0.5D, Float.parseFloat(args[4]), Float.parseFloat(args[5])); + } catch (Exception e) { + return null; + } + } + + public static String chunkToString(Chunk c) { + return c.getWorld().getName() + "," + c.getX() + "," + c.getZ(); + } + + public static Chunk chunkFromString(String s) { + String[] args = s.split(","); + try { + World world = Bukkit.getWorld(args[0]); + return world.getChunkAt(Integer.parseInt(args[1]), Integer.parseInt(args[2])); + } catch (Exception e) { + return null; + } + } + + + + + + + + public static Block getNearestBlockUnder(Location l) { + return l.getWorld().getBlockAt(getNearestLocationUnder(l)); + } + + + public static Location getNearestLocationUnder(Location l) { + Location location = new Location(l.getWorld(), l.getBlockX() + 0.5D, l.getBlockY(), l.getBlockZ() + 0.5D); + while (!location.getBlock().getType().isSolid()) { + location = location.add(0.0D, -1.0D, 0.0D); + if (location.getY() < 0.0D) { + return null; + } + } + return location; + } + + + + + + + + + + + + + public static Block getBlockAboveOrBelow(Block block, Material blockType, byte blockData) { + return getBlockAboveOrBelow(block, blockType, blockData, 1); + } + + + private static Block getBlockAboveOrBelow(Block block, Material blockType, byte blockData, int distance) { + boolean maxHeightReached = (block.getLocation().getBlockY() + distance > block.getWorld().getMaxHeight() - 1); + boolean minHeightReached = (block.getLocation().getBlockY() - distance < 1); + + if (maxHeightReached && minHeightReached) { + return null; + } + + if (!maxHeightReached) { + Block blockAbove = block.getWorld().getBlockAt(block.getLocation().add(0.0D, distance, 0.0D)); + if (blockAbove.getType() == blockType && blockAbove.getData() == blockData) { + return blockAbove; + } + } + + if (!minHeightReached) { + Block blockBelow = block.getWorld().getBlockAt(block.getLocation().subtract(0.0D, distance, 0.0D)); + if (blockBelow.getType() == blockType && blockBelow.getData() == blockData) { + return blockBelow; + } + } + + return getBlockAboveOrBelow(block, blockType, blockData, distance + 1); + } + + public static boolean isEmptyColumn(Location loc) { + return isEmptyColumn(loc.getWorld(), loc.getBlockX(), loc.getBlockZ(), loc.getBlockY()); + } + + public static boolean isEmptyColumn(World world, int x, int z) { + return isEmptyColumn(world, x, z, -1); + } + + public static boolean isEmptyColumn(World world, int x, int z, int yException) { + for (int y = 0; y < world.getMaxHeight(); y++) { + if (yException != y && world.getBlockAt(x, y, z).getType() != Material.AIR) { + return false; + } + } + return true; + } + + + + + + + + + public static Set getNearbyPlayers(Location location, double range) { + double rangeSquared = range * range; + Set nearbyPlayers = new HashSet<>(); + World world = location.getWorld(); + for (Player player : world.getPlayers()) { + if (player != null && player.getGameMode() != GameMode.SPECTATOR && + player.getLocation().distanceSquared(location) <= rangeSquared) { + nearbyPlayers.add(player); + } + } + + return nearbyPlayers; + } + + + + + + + + + public static Player getNearestPlayer(Location location, double maxRange) { + double rangeSquared = maxRange * maxRange; + + Player nearest = null; + double nearestDistSquared = (maxRange <= 0.0D) ? Double.MAX_VALUE : rangeSquared; + + for (Player player : location.getWorld().getPlayers()) { + if (player.getGameMode() != GameMode.SPECTATOR) { + double distSquared = player.getLocation().distanceSquared(location); + if (distSquared < nearestDistSquared) { + nearest = player; + nearestDistSquared = distSquared; + } + } + } + return nearest; + } + + public static Set getPlayersInCuboid(Location origin, double width, double height, double depth) { + if (width < 0.0D) { + origin.setX(origin.getX() + width); + width *= -1.0D; + } + if (height < 0.0D) { + origin.setY(origin.getY() + height); + height *= -1.0D; + } + if (depth < 0.0D) { + origin.setZ(origin.getZ() + depth); + depth *= -1.0D; + } + + Set nearbyPlayers = new HashSet<>(); + World world = origin.getWorld(); + for (Player player : world.getPlayers()) { + if (player.getGameMode() != GameMode.SPECTATOR) { + Location ploc = player.getLocation(); + if (ploc.getX() > origin.getX() && ploc.getX() < origin.getBlockX() + width && + ploc.getY() > origin.getY() && ploc.getY() < origin.getY() + height && + ploc.getZ() > origin.getZ() && ploc.getZ() < origin.getZ() + depth) { + nearbyPlayers.add(player); + } + } + } + + + return nearbyPlayers; + } + + public static List getCircle(Location center, double radius, int amount) { + World world = center.getWorld(); + double increment = 6.283185307179586D / amount; + List locations = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + double angle = i * increment; + double x = center.getX() + radius * Math.cos(angle); + double z = center.getZ() + radius * Math.sin(angle); + locations.add(new Location(world, x, center.getY(), z)); + } + return locations; + } + + public static int compareLocations(Location l1, Location l2) { + if (l1.getY() > l2.getY()) { + return -1; + } + if (l1.getY() < l2.getY()) { + return 1; + } + + if (l1.getX() > l2.getX()) { + return -1; + } + if (l1.getX() < l2.getX()) { + return 1; + } + + return Double.compare(l2.getZ(), l1.getZ()); + } + + + public static List getChunksDiamond(Chunk c, int radius) { + if (radius <= 0) { + return Collections.singletonList(c); + } + + List chunks = new ArrayList<>(); + World world = c.getWorld(); + int ix = c.getX(); + int iz = c.getZ(); + int xmin = ix - radius, xmax = ix + radius; + int x = xmax, z = iz; + for (; x > ix; x--) { + chunks.add(world.getChunkAt(x, z)); + z++; + } + for (; x > xmin; x--) { + chunks.add(world.getChunkAt(x, z)); + z--; + } + for (; x < ix; x++) { + chunks.add(world.getChunkAt(x, z)); + z--; + } + for (; x < xmax; x++) { + chunks.add(world.getChunkAt(x, z)); + z++; + } + return chunks; + } + + public static List getChunksSquare(Chunk c, int radius) { + if (radius <= 0) { + return Collections.singletonList(c); + } + + List chunks = new ArrayList<>(); + World world = c.getWorld(); + int ix = c.getX(); + int iz = c.getZ(); + int xmin = ix - radius, xmax = ix + radius; + int zmin = iz - radius, zmax = iz + radius; + for (int x = xmin; x < xmax; x++) { + chunks.add(world.getChunkAt(x, zmin)); + chunks.add(world.getChunkAt(x, zmax)); + } + for (int z = zmin + 1; z < zmax - 1; z++) { + chunks.add(world.getChunkAt(xmin, z)); + chunks.add(world.getChunkAt(xmax, z)); + } + return chunks; + } + + + public static List getSphere(Location center, double radius) { + radius++; + List sphere = new ArrayList<>(); + int bx = center.getBlockX(); + int by = center.getBlockY(); + int bz = center.getBlockZ(); double x; + for (x = bx - radius; x <= bx + radius; x++) { + double y; for (y = by - radius; y <= by + radius; y++) { + double z; for (z = bz - radius; z <= bz + radius; z++) { + double distance = (bx - x) * (bx - x) + (bz - z) * (bz - z) + (by - y) * (by - y); + if (distance < radius * radius && distance >= (radius - 1.0D) * (radius - 1.0D)) { + sphere.add(new Location(center.getWorld(), x, y, z)); + } + } + } + } + + return sphere; + } + } + + diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/BaseCommand.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/BaseCommand.java new file mode 100644 index 0000000..7225050 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/BaseCommand.java @@ -0,0 +1,12 @@ +package com.loganmagnan.pluginbase.utils.command; + +import com.loganmagnan.pluginbase.PluginBase; + +public abstract class BaseCommand { + + public BaseCommand() { + PluginBase.getInstance().getCommandFramework().registerCommands(this, null); + } + + public abstract void executeAs(CommandArguments command); +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/BukkitCommand.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/BukkitCommand.java new file mode 100644 index 0000000..95830b7 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/BukkitCommand.java @@ -0,0 +1,82 @@ +package com.loganmagnan.pluginbase.utils.command; + +import org.apache.commons.lang3.Validate; +import org.bukkit.command.Command; +import org.bukkit.command.*; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +public class BukkitCommand extends Command { + + protected BukkitCompleter completer; + private Plugin ownerPlugin; + private CommandExecutor executor; + + protected BukkitCommand(String label, CommandExecutor executor, Plugin owner) { + super(label); + this.executor = executor; + this.ownerPlugin = owner; + this.usageMessage = ""; + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + boolean success = false; + + if (!ownerPlugin.isEnabled()) { + return false; + } + + if (!testPermission(sender)) { + return true; + } + + try { + success = executor.onCommand(sender, this, commandLabel, args); + } catch (Throwable ex) { + throw new CommandException("Unhandled exception executing command '" + commandLabel + "' in plugin " + ownerPlugin.getDescription().getFullName(), ex); + } + + if (!success && usageMessage.length() > 0) { + for (String line : usageMessage.replace("", commandLabel).split("\n")) { + sender.sendMessage(line); + } + } + + return success; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws CommandException, IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + List completions = null; + try { + if (completer != null) { + completions = completer.onTabComplete(sender, this, alias, args); + } + if (completions == null && executor instanceof TabCompleter) { + completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args); + } + } catch (Throwable ex) { + StringBuilder message = new StringBuilder(); + + message.append("Unhandled exception during tab completion for command '/").append(alias).append(' '); + for (String arg : args) { + message.append(arg).append(' '); + } + message.deleteCharAt(message.length() - 1).append("' in plugin ").append(ownerPlugin.getDescription().getFullName()); + + throw new CommandException(message.toString(), ex); + } + + if (completions == null) { + return super.tabComplete(sender, alias, args); + } + + return completions; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/BukkitCompleter.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/BukkitCompleter.java new file mode 100644 index 0000000..a8823e5 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/BukkitCompleter.java @@ -0,0 +1,48 @@ +package com.loganmagnan.pluginbase.utils.command; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class BukkitCompleter implements TabCompleter { + + private Map> completers = new HashMap<>(); + + public void addCompleter(String label, Method m, Object obj) { + completers.put(label, new AbstractMap.SimpleEntry<>(m, obj)); + } + + @SuppressWarnings("unchecked") + @Override + public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + for (int i = args.length; i >= 0; i--) { + StringBuffer buffer = new StringBuffer(); + buffer.append(label.toLowerCase()); + for (int x = 0; x < i; x++) { + if (!args[x].equals("") && !args[x].equals(" ")) { + buffer.append("." + args[x].toLowerCase()); + } + } + + String cmdLabel = buffer.toString(); + if (completers.containsKey(cmdLabel)) { + Entry entry = completers.get(cmdLabel); + try { + return (List) entry.getKey().invoke(entry.getValue(), new CommandArguments(sender, command, label, args, cmdLabel.split("\\.").length - 1)); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/Command.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/Command.java new file mode 100644 index 0000000..d1e5388 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/Command.java @@ -0,0 +1,23 @@ +package com.loganmagnan.pluginbase.utils.command; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Command { + + public String name(); + + public String permission() default ""; + + public String[] aliases() default {}; + + public String description() default ""; + + public String usage() default ""; + + public boolean inGameOnly() default true; +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/CommandArguments.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/CommandArguments.java new file mode 100644 index 0000000..46cfeb7 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/CommandArguments.java @@ -0,0 +1,52 @@ +package com.loganmagnan.pluginbase.utils.command; + +import lombok.Getter; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@Getter +public class CommandArguments { + + private CommandSender sender; + private org.bukkit.command.Command command; + private String label; + private String[] args; + + protected CommandArguments(CommandSender sender, org.bukkit.command.Command command, String label, String[] args, int subCommand) { + String[] modArgs = new String[args.length - subCommand]; + for (int i = 0; i < args.length - subCommand; i++) { + modArgs[i] = args[i + subCommand]; + } + + StringBuffer buffer = new StringBuffer(); + buffer.append(label); + for (int x = 0; x < subCommand; x++) { + buffer.append("." + args[x]); + } + String cmdLabel = buffer.toString(); + this.sender = sender; + this.command = command; + this.label = cmdLabel; + this.args = modArgs; + } + + public String getArgs(int index) { + return args[index]; + } + + public int length() { + return args.length; + } + + public boolean isPlayer() { + return sender instanceof Player; + } + + public Player getPlayer() { + if (sender instanceof Player) { + return (Player) sender; + } else { + return null; + } + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/CommandFramework.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/CommandFramework.java new file mode 100644 index 0000000..4ce216b --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/CommandFramework.java @@ -0,0 +1,172 @@ +package com.loganmagnan.pluginbase.utils.command; + +import com.loganmagnan.pluginbase.PluginBase; +import com.loganmagnan.pluginbase.utils.ColorUtils; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.entity.Player; +import org.bukkit.plugin.SimplePluginManager; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class CommandFramework implements CommandExecutor { + + private PluginBase plugin; + private Map> commandMap = new HashMap<>(); + private CommandMap map; + + public CommandFramework(PluginBase plugin) { + this.plugin = plugin; + + if (plugin.getServer().getPluginManager() instanceof SimplePluginManager) { + SimplePluginManager manager = (SimplePluginManager) plugin.getServer().getPluginManager(); + try { + Field field = SimplePluginManager.class.getDeclaredField("commandMap"); + field.setAccessible(true); + map = (CommandMap) field.get(manager); + } catch (IllegalArgumentException | SecurityException | NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + @Override + public boolean onCommand(CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args) { + return handleCommand(sender, cmd, label, args); + } + + public boolean handleCommand(CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args) { + for (int i = args.length; i >= 0; i--) { + StringBuffer buffer = new StringBuffer(); + buffer.append(label.toLowerCase()); + for (int x = 0; x < i; x++) { + buffer.append("." + args[x].toLowerCase()); + } + + String cmdLabel = buffer.toString(); + if (commandMap.containsKey(cmdLabel)) { + Method method = commandMap.get(cmdLabel).getKey(); + Object methodObject = commandMap.get(cmdLabel).getValue(); + Command command = method.getAnnotation(Command.class); + if (!command.permission().equals("") && (!sender.hasPermission(command.permission()))) { + sender.sendMessage(ColorUtils.getMessageType("&cYou don't have permissions to perform this.")); + return true; + } + if (command.inGameOnly() && !(sender instanceof Player)) { + sender.sendMessage(ColorUtils.getMessageType("&cThis command can only be executed in game.")); + return true; + } + + try { + method.invoke(methodObject, new CommandArguments(sender, cmd, label, args, cmdLabel.split("\\.").length - 1)); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return true; + } + } + + defaultCommand(new CommandArguments(sender, cmd, label, args, 0)); + return true; + } + + public void registerCommands(Object obj, List aliases) { + for (Method method : obj.getClass().getMethods()) { + if (method.getAnnotation(Command.class) != null) { + Command command = method.getAnnotation(Command.class); + if (method.getParameterTypes().length > 1 || method.getParameterTypes()[0] != CommandArguments.class) { + System.out.println("Unable to register command " + method.getName() + ". Unexpected method arguments"); + continue; + } + + registerCommand(command, command.name(), method, obj); + for (String alias : command.aliases()) { + registerCommand(command, alias, method, obj); + } + if (aliases != null) { + for (String alias : aliases) { + registerCommand(command, alias, method, obj); + } + } + } else if (method.getAnnotation(Completer.class) != null) { + Completer comp = method.getAnnotation(Completer.class); + if (method.getParameterTypes().length > 1 || method.getParameterTypes().length == 0 || method.getParameterTypes()[0] != CommandArguments.class) { + System.out.println("Unable to register tab completer " + method.getName() + ". Unexpected method arguments"); + continue; + } + if (method.getReturnType() != List.class) { + System.out.println("Unable to register tab completer " + method.getName() + ". Unexpected return type"); + continue; + } + + registerCompleter(comp.name(), method, obj); + for (String alias : comp.aliases()) { + registerCompleter(alias, method, obj); + } + } + } + } + + public void registerCommand(Command command, String label, Method m, Object obj) { + commandMap.put(label.toLowerCase(), new AbstractMap.SimpleEntry<>(m, obj)); + commandMap.put(this.plugin.getName() + ':' + label.toLowerCase(), new AbstractMap.SimpleEntry<>(m, obj)); + + String cmdLabel = label.replace(".", ",").split(",")[0].toLowerCase(); + if (map.getCommand(cmdLabel) == null) { + org.bukkit.command.Command cmd = new BukkitCommand(cmdLabel, this, plugin); + map.register(plugin.getName(), cmd); + } + if (!command.description().equalsIgnoreCase("") && cmdLabel.equals(label)) { + map.getCommand(cmdLabel).setDescription(command.description()); + } + if (!command.usage().equalsIgnoreCase("") && cmdLabel.equals(label)) { + map.getCommand(cmdLabel).setUsage(command.usage()); + } + } + + public void registerCompleter(String label, Method m, Object obj) { + String cmdLabel = label.replace(".", ",").split(",")[0].toLowerCase(); + if (map.getCommand(cmdLabel) == null) { + org.bukkit.command.Command command = new BukkitCommand(cmdLabel, this, plugin); + map.register(plugin.getName(), command); + } + if (map.getCommand(cmdLabel) instanceof BukkitCommand) { + BukkitCommand command = (BukkitCommand) map.getCommand(cmdLabel); + if (command.completer == null) { + command.completer = new BukkitCompleter(); + } + command.completer.addCompleter(label, m, obj); + } else if (map.getCommand(cmdLabel) instanceof PluginCommand) { + try { + Object command = map.getCommand(cmdLabel); + Field field = command.getClass().getDeclaredField("completer"); + field.setAccessible(true); + if (field.get(command) == null) { + BukkitCompleter completer = new BukkitCompleter(); + completer.addCompleter(label, m, obj); + field.set(command, completer); + } else if (field.get(command) instanceof BukkitCompleter) { + BukkitCompleter completer = (BukkitCompleter) field.get(command); + completer.addCompleter(label, m, obj); + } else { + System.out.println("Unable to register tab completer " + m.getName() + ". A tab completer is already registered for that command!"); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + private void defaultCommand(CommandArguments args) { + args.getSender().sendMessage("Unknown command"); + } +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/command/Completer.java b/src/main/java/com/loganmagnan/pluginbase/utils/command/Completer.java new file mode 100644 index 0000000..4350b13 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/command/Completer.java @@ -0,0 +1,15 @@ +package com.loganmagnan.pluginbase.utils.command; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Completer { + + String name(); + + String[] aliases() default {}; +} \ No newline at end of file diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/config/ConfigCursor.java b/src/main/java/com/loganmagnan/pluginbase/utils/config/ConfigCursor.java new file mode 100644 index 0000000..8b7277a --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/config/ConfigCursor.java @@ -0,0 +1,88 @@ +package com.loganmagnan.pluginbase.utils.config; + +import org.bukkit.Bukkit; +import org.bukkit.World; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class ConfigCursor { + + private final FileConfig fileConfig; + + private String path; + + public ConfigCursor(FileConfig fileConfig, String path) { + this.fileConfig = fileConfig; + this.path = path; + } + + public FileConfig getFileConfig() { + return this.fileConfig; + } + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public boolean exists() { + return exists(null); + } + + public boolean exists(String path) { + return this.fileConfig.getConfig().contains(this.path + ((path == null) ? "" : ("." + path))); + } + + public Set getKeys() { + return getKeys(null); + } + + public Set getKeys(String path) { + return this.fileConfig.getConfig().getConfigurationSection(this.path + ((path == null) ? "" : ("." + path))).getKeys(false); + } + + public boolean getBoolean(String path) { + return this.fileConfig.getConfig().getBoolean(((this.path == null) ? "" : (this.path + ".")) + "." + path); + } + + public int getInt(String path) { + return this.fileConfig.getConfig().getInt(((this.path == null) ? "" : (this.path + ".")) + "." + path); + } + + public long getLong(String path) { + return this.fileConfig.getConfig().getLong(((this.path == null) ? "" : (this.path + ".")) + "." + path); + } + + public String getString(String path) { + return this.fileConfig.getConfig().getString(((this.path == null) ? "" : (this.path + ".")) + "." + path); + } + + public List getStringList(String path) { + return this.fileConfig.getConfig().getStringList(((this.path == null) ? "" : (this.path + ".")) + "." + path); + } + + public UUID getUuid(String path) { + return UUID.fromString(this.fileConfig.getConfig().getString(this.path + "." + path)); + } + + public World getWorld(String path) { + return Bukkit.getWorld(this.fileConfig.getConfig().getString(this.path + "." + path)); + } + + public void set(Object value) { + set(null, value); + } + + public void set(String path, Object value) { + this.fileConfig.getConfig().set(this.path + ((path == null) ? "" : ("." + path)), value); + } + + public void save() { + this.fileConfig.save(); + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/config/FileConfig.java b/src/main/java/com/loganmagnan/pluginbase/utils/config/FileConfig.java new file mode 100644 index 0000000..e05b878 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/config/FileConfig.java @@ -0,0 +1,50 @@ +package com.loganmagnan.pluginbase.utils.config; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; + +public class FileConfig { + + private File file; + + private FileConfiguration config; + + public File getFile() { + return this.file; + } + + public FileConfiguration getConfig() { + return this.config; + } + + public FileConfig(JavaPlugin plugin, String fileName) { + this.file = new File(plugin.getDataFolder(), fileName); + if (!this.file.exists()) { + this.file.getParentFile().mkdirs(); + if (plugin.getResource(fileName) == null) { + try { + this.file.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("Failed to create new file " + fileName); + } + } else { + plugin.saveResource(fileName, false); + } + } + this.config = YamlConfiguration.loadConfiguration(this.file); + } + + public void save() { + try { + this.config.save(this.file); + } catch (IOException e) { + Bukkit.getLogger().severe("Could not save config file " + this.file.toString()); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/config/file/Config.java b/src/main/java/com/loganmagnan/pluginbase/utils/config/file/Config.java new file mode 100644 index 0000000..1ae67e7 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/config/file/Config.java @@ -0,0 +1,39 @@ +package com.loganmagnan.pluginbase.utils.config.file; + +import lombok.Getter; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; + +@Getter +public class Config { + + private final FileConfiguration config; + private final File configFile; + protected boolean wasCreated; + + public Config(String name, JavaPlugin plugin) { + this.configFile = new File(plugin.getDataFolder() + "/" + name + ".yml"); + if (!this.configFile.exists()) { + try { + this.configFile.getParentFile().mkdirs(); + this.configFile.createNewFile(); + this.wasCreated = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + this.config = YamlConfiguration.loadConfiguration(this.configFile); + } + + public void save() { + try { + this.config.save(configFile); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/config/file/ConfigFile.java b/src/main/java/com/loganmagnan/pluginbase/utils/config/file/ConfigFile.java new file mode 100644 index 0000000..b0a53c7 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/config/file/ConfigFile.java @@ -0,0 +1,103 @@ +package com.loganmagnan.pluginbase.utils.config.file; + +import lombok.Getter; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ConfigFile { + + @Getter private File file; + @Getter private YamlConfiguration configuration; + + public ConfigFile(JavaPlugin plugin, String name) { + file = new File(plugin.getDataFolder(), name + ".yml"); + + if (!file.getParentFile().exists()) { + file.getParentFile().mkdir(); + } + + plugin.saveResource(name + ".yml", false); + + configuration = YamlConfiguration.loadConfiguration(file); + } + + public double getDouble(String path) { + if (configuration.contains(path)) { + return configuration.getDouble(path); + } + return 0; + } + + public int getInt(String path) { + if (configuration.contains(path)) { + return configuration.getInt(path); + } + return 0; + } + + public boolean getBoolean(String path) { + if (configuration.contains(path)) { + return configuration.getBoolean(path); + } + return false; + } + + public String getString(String path) { + if (configuration.contains(path)) { + return ChatColor.translateAlternateColorCodes('&', configuration.getString(path)); + } + return "ERROR: STRING NOT FOUND"; + } + + public String getString(String path, String callback, boolean colorize) { + if (configuration.contains(path)) { + if (colorize) { + return ChatColor.translateAlternateColorCodes('&', configuration.getString(path)); + } else { + return configuration.getString(path); + } + } + return callback; + } + + public List getReversedStringList(String path) { + List list = getStringList(path); + if (list != null) { + int size = list.size(); + List toReturn = new ArrayList<>(); + for (int i = size - 1; i >= 0; i--) { + toReturn.add(list.get(i)); + } + return toReturn; + } + return Arrays.asList("ERROR: STRING LIST NOT FOUND!"); + } + + public List getStringList(String path) { + if (configuration.contains(path)) { + ArrayList strings = new ArrayList<>(); + for (String string : configuration.getStringList(path)) { + strings.add(ChatColor.translateAlternateColorCodes('&', string)); + } + return strings; + } + return Arrays.asList("ERROR: STRING LIST NOT FOUND!"); + } + + public List getStringListOrDefault(String path, List toReturn) { + if (configuration.contains(path)) { + ArrayList strings = new ArrayList<>(); + for (String string : configuration.getStringList(path)) { + strings.add(ChatColor.translateAlternateColorCodes('&', string)); + } + return strings; + } + return toReturn; + } +} diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/Cuboid.java b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/Cuboid.java new file mode 100644 index 0000000..d5fe1b9 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/Cuboid.java @@ -0,0 +1,497 @@ +package com.loganmagnan.pluginbase.utils.cuboid; + +import com.google.common.base.Preconditions; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.util.*; + +public class Cuboid implements Iterable, Cloneable, ConfigurationSerializable { + + private String worldName; + private int x1; + private int y1; + private int z1; + private int x2; + private int y2; + private int z2; + + public Cuboid(World world, int x1, int y1, int z1, int x2, int y2, int z2) { + this(((World) Preconditions.checkNotNull((Object) world)).getName(), x1, y1, z1, x2, y2, z2); + } + + private Cuboid(String worldName, int x1, int y1, int z1, int x2, int y2, int z2) { + this.worldName = worldName; + this.x1 = Math.min(x1, x2); + this.y1 = Math.min(y1, y2); + this.z1 = Math.min(z1, z2); + this.x2 = Math.max(x1, x2); + this.y2 = Math.max(y1, y2); + this.z2 = Math.max(z1, z2); + } + + public Cuboid(Location first, Location second) { + this.worldName = first.getWorld().getName(); + this.x1 = Math.min(first.getBlockX(), second.getBlockX()); + this.y1 = Math.min(first.getBlockY(), second.getBlockY()); + this.z1 = Math.min(first.getBlockZ(), second.getBlockZ()); + this.x2 = Math.max(first.getBlockX(), second.getBlockX()); + this.y2 = Math.max(first.getBlockY(), second.getBlockY()); + this.z2 = Math.max(first.getBlockZ(), second.getBlockZ()); + } + + public Map serialize() { + LinkedHashMap map = new LinkedHashMap<>(); + map.put("worldName", this.worldName); + map.put("x1", this.x1); + map.put("y1", this.y1); + map.put("z1", this.z1); + map.put("x2", this.x2); + map.put("y2", this.y2); + map.put("z2", this.z2); + + return map; + } + + public boolean hasBothPositionsSet() { + return this.getMinimumPoint() != null && this.getMaximumPoint() != null; + } + + public int getMinimumX() { + return Math.min(this.x1, this.x2); + } + + public int getMinimumZ() { + return Math.min(this.z1, this.z2); + } + + public int getMaximumX() { + return Math.max(this.x1, this.x2); + } + + public int getMaximumZ() { + return Math.max(this.z1, this.z2); + } + + public List edges() { + return this.edges(-1, -1, -1, -1); + } + + public List edges(int fixedMinX, int fixedMaxX, int fixedMinZ, int fixedMaxZ) { + Vector v1 = this.getMinimumPoint().toVector(); + Vector v2 = this.getMaximumPoint().toVector(); + int minX = v1.getBlockX(); + int maxX = v2.getBlockX(); + int minZ = v1.getBlockZ(); + int maxZ = v2.getBlockZ(); + int capacity = (maxX - minX) * 4 + (maxZ - minZ) * 4; + + ArrayList result = new ArrayList(capacity += 4); + if (capacity <= 0) { + return result; + } + + int minY = v1.getBlockY(); + int maxY = v1.getBlockY(); + for (int x = minX; x <= maxX; ++x) { + result.add(new Vector(x, minY, minZ)); + result.add(new Vector(x, minY, maxZ)); + result.add(new Vector(x, maxY, minZ)); + result.add(new Vector(x, maxY, maxZ)); + } + for (int z = minZ; z <= maxZ; ++z) { + result.add(new Vector(minX, minY, z)); + result.add(new Vector(minX, maxY, z)); + result.add(new Vector(maxX, minY, z)); + result.add(new Vector(maxX, maxY, z)); + } + + return result; + } + + public Set getPlayers() { + HashSet players = new HashSet<>(); + for (Player player : Bukkit.getOnlinePlayers()) { + if (!this.contains(player)) continue; + players.add(player); + } + + return players; + } + + public Location getLowerNE() { + return new Location(this.getWorld(), this.x1, this.y1, this.z1); + } + + public Location getUpperSW() { + return new Location(this.getWorld(), this.x2, this.y2, this.z2); + } + + public Location getCenter() { + int x1 = this.x2 + 1; + int y1 = this.y2 + 1; + int z1 = this.z2 + 1; + + return new Location(this.getWorld(), (double) this.x1 + (double) (x1 - this.x1) / 2.0, (double) this.y1 + (double) (y1 - this.y1) / 2.0, (double) this.z1 + (double) (z1 - this.z1) / 2.0); + } + + public World getWorld() { + return Bukkit.getWorld(this.worldName); + } + + public int getSizeX() { + return this.x2 - this.x1 + 1; + } + + public int getSizeY() { + return this.y2 - this.y1 + 1; + } + + public int getSizeZ() { + return this.z2 - this.z1 + 1; + } + + public Location[] getCornerLocations() { + Location[] result = new Location[8]; + Block[] cornerBlocks = this.getCornerBlocks(); + for (int i = 0; i < cornerBlocks.length; ++i) { + result[i] = cornerBlocks[i].getLocation(); + } + + return result; + } + + public Block[] getCornerBlocks() { + Block[] result = new Block[8]; + World world = this.getWorld(); + result[0] = world.getBlockAt(this.x1, this.y1, this.z1); + result[1] = world.getBlockAt(this.x1, this.y1, this.z2); + result[2] = world.getBlockAt(this.x1, this.y2, this.z1); + result[3] = world.getBlockAt(this.x1, this.y2, this.z2); + result[4] = world.getBlockAt(this.x2, this.y1, this.z1); + result[5] = world.getBlockAt(this.x2, this.y1, this.z2); + result[6] = world.getBlockAt(this.x2, this.y2, this.z1); + result[7] = world.getBlockAt(this.x2, this.y2, this.z2); + + return result; + } + + public Cuboid shift(CuboidDirection direction, int amount) throws IllegalArgumentException { + return this.expand(direction, amount).expand(direction.opposite(), -amount); + } + + public Cuboid inset(CuboidDirection direction, int amount) throws IllegalArgumentException { + return this.outset(direction, -amount); + } + + public Cuboid expand(CuboidDirection direction, int amount) throws IllegalArgumentException { + switch (direction) { + case NORTH: { + return new Cuboid(this.worldName, this.x1 - amount, this.y1, this.z1, this.x2, this.y2, this.z2); + } + case SOUTH: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2 + amount, this.y2, this.z2); + } + case EAST: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1 - amount, this.x2, this.y2, this.z2); + } + case WEST: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2, this.z2 + amount); + } + case DOWN: { + return new Cuboid(this.worldName, this.x1, this.y1 - amount, this.z1, this.x2, this.y2, this.z2); + } + case UP: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2 + amount, this.z2); + } + } + throw new IllegalArgumentException("Invalid direction " + direction); + } + + public Cuboid outset(CuboidDirection direction, int amount) throws IllegalArgumentException { + switch (direction) { + case HORIZONTAL: { + return this.expand(CuboidDirection.NORTH, amount).expand(CuboidDirection.SOUTH, amount).expand(CuboidDirection.EAST, amount).expand(CuboidDirection.WEST, amount); + } + case VERTICAL: { + return this.expand(CuboidDirection.DOWN, amount).expand(CuboidDirection.UP, amount); + } + case BOTH: { + return this.outset(CuboidDirection.HORIZONTAL, amount).outset(CuboidDirection.VERTICAL, amount); + } + } + throw new IllegalArgumentException("Invalid direction " + direction); + } + + public boolean contains(Cuboid cuboid) { + return this.contains(cuboid.getMinimumPoint()) || this.contains(cuboid.getMaximumPoint()); + } + + public boolean contains(Player player) { + return this.contains(player.getLocation()); + } + + public boolean contains(World world, int x, int z) { + return (world == null || this.getWorld().equals(world)) && x >= this.x1 && x <= this.x2 && z >= this.z1 && z <= this.z2; + } + + public boolean contains(int x, int y, int z) { + return x >= this.x1 && x <= this.x2 && y >= this.y1 && y <= this.y2 && z >= this.z1 && z <= this.z2; + } + + public boolean contains(Block block) { + return this.contains(block.getLocation()); + } + + public boolean contains(Location location) { + if (location == null || this.worldName == null) { + return false; + } + World world = location.getWorld(); + + return world != null && this.worldName.equals(location.getWorld().getName()) && this.contains(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + public int getVolume() { + return this.getSizeX() * this.getSizeY() * this.getSizeZ(); + } + + public int getArea() { + Location min = this.getMinimumPoint(); + Location max = this.getMaximumPoint(); + + return (max.getBlockX() - min.getBlockX() + 1) * (max.getBlockZ() - min.getBlockZ() + 1); + } + + public byte getAverageLightLevel() { + long total = 0L; + int count = 0; + for (Block block : this) { + if (!block.isEmpty()) continue; + total += block.getLightLevel(); + ++count; + } + + return count > 0 ? (byte) (total / (long) count) : (byte) 0; + } + + public Location getMinimumPoint() { + return new Location(this.getWorld(), Math.min(this.x1, this.x2), Math.min(this.y1, this.y2), Math.min(this.z1, this.z2)); + } + + public Location getMaximumPoint() { + return new Location(this.getWorld(), Math.max(this.x1, this.x2), Math.max(this.y1, this.y2), Math.max(this.z1, this.z2)); + } + + public int getWidth() { + return this.getMaximumPoint().getBlockX() - this.getMinimumPoint().getBlockX(); + } + + public int getHeight() { + return this.getMaximumPoint().getBlockY() - this.getMinimumPoint().getBlockY(); + } + + public int getLength() { + return this.getMaximumPoint().getBlockZ() - this.getMinimumPoint().getBlockZ(); + } + + public Cuboid contract() { + return this.contract(CuboidDirection.DOWN).contract(CuboidDirection.SOUTH).contract(CuboidDirection.EAST).contract(CuboidDirection.UP).contract(CuboidDirection.NORTH).contract(CuboidDirection.WEST); + } + + public Cuboid contract(CuboidDirection direction) { + Cuboid face = this.getFace(direction.opposite()); + switch (direction) { + case DOWN: { + while (face.containsOnly(Material.AIR) && face.y1 > this.y1) { + face = face.shift(CuboidDirection.DOWN, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, face.y2, this.z2); + } + case UP: { + while (face.containsOnly(Material.AIR) && face.y2 < this.y2) { + face = face.shift(CuboidDirection.UP, 1); + } + return new Cuboid(this.worldName, this.x1, face.y1, this.z1, this.x2, this.y2, this.z2); + } + case NORTH: { + while (face.containsOnly(Material.AIR) && face.x1 > this.x1) { + face = face.shift(CuboidDirection.NORTH, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, face.x2, this.y2, this.z2); + } + case SOUTH: { + while (face.containsOnly(Material.AIR) && face.x2 < this.x2) { + face = face.shift(CuboidDirection.SOUTH, 1); + } + return new Cuboid(this.worldName, face.x1, this.y1, this.z1, this.x2, this.y2, this.z2); + } + case EAST: { + while (face.containsOnly(Material.AIR) && face.z1 > this.z1) { + face = face.shift(CuboidDirection.EAST, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2, face.z2); + } + case WEST: { + while (face.containsOnly(Material.AIR) && face.z2 < this.z2) { + face = face.shift(CuboidDirection.WEST, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, face.z1, this.x2, this.y2, this.z2); + } + } + throw new IllegalArgumentException("Invalid direction " + direction); + } + + public Cuboid getFace(CuboidDirection direction) { + switch (direction) { + case DOWN: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y1, this.z2); + } + case UP: { + return new Cuboid(this.worldName, this.x1, this.y2, this.z1, this.x2, this.y2, this.z2); + } + case NORTH: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x1, this.y2, this.z2); + } + case SOUTH: { + return new Cuboid(this.worldName, this.x2, this.y1, this.z1, this.x2, this.y2, this.z2); + } + case EAST: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2, this.z1); + } + case WEST: { + return new Cuboid(this.worldName, this.x1, this.y1, this.z2, this.x2, this.y2, this.z2); + } + } + throw new IllegalArgumentException("Invalid direction " + direction); + } + + public boolean containsOnly(Material material) { + for (Block block : this) { + if (block.getType() == material) continue; + return false; + } + + return true; + } + + public Cuboid getBoundingCuboid(Cuboid other) { + if (other == null) { + return this; + } + int xMin = Math.min(this.x1, other.x1); + int yMin = Math.min(this.y1, other.y1); + int zMin = Math.min(this.z1, other.z1); + int xMax = Math.max(this.x2, other.x2); + int yMax = Math.max(this.y2, other.y2); + int zMax = Math.max(this.z2, other.z2); + + return new Cuboid(this.worldName, xMin, yMin, zMin, xMax, yMax, zMax); + } + + public Block getRelativeBlock(int x, int y, int z) { + return this.getWorld().getBlockAt(this.x1 + x, this.y1 + y, this.z1 + z); + } + + public Block getRelativeBlock(World world, int x, int y, int z) { + return world.getBlockAt(this.x1 + x, this.y1 + y, this.z1 + z); + } + + public List getChunks() { + World world = this.getWorld(); + int x1 = this.x1 & -16; + int x2 = this.x2 & -16; + int z1 = this.z1 & -16; + int z2 = this.z2 & -16; + + ArrayList result = new ArrayList<>(x2 - x1 + 16 + (z2 - z1) * 16); + for (int x3 = x1; x3 <= x2; x3 += 16) { + for (int z3 = z1; z3 <= z2; z3 += 16) { + result.add(world.getChunkAt(x3 >> 4, z3 >> 4)); + } + } + + return result; + } + + @Override + public Iterator iterator() { + return new CuboidBlockIterator(this.getWorld(), this.x1, this.y1, this.z1, this.x2, this.y2, this.z2); + } + + public Iterator locationIterator() { + return new CuboidLocationIterator(this.getWorld(), this.x1, this.y1, this.z1, this.x2, this.y2, this.z2); + } + + public Cuboid clone() { + try { + return (Cuboid) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new RuntimeException("This could never happen", ex); + } + } + + public String toString() { + return "Cuboid: " + this.worldName + ',' + this.x1 + ',' + this.y1 + ',' + this.z1 + "=>" + this.x2 + ',' + this.y2 + ',' + this.z2; + } + + public String getWorldName() { + return this.worldName; + } + + public int getX1() { + return this.x1; + } + + public int getY1() { + return this.y1; + } + + public int getZ1() { + return this.z1; + } + + public int getX2() { + return this.x2; + } + + public int getY2() { + return this.y2; + } + + public int getZ2() { + return this.z2; + } + + public void setWorldName(String worldName) { + this.worldName = worldName; + } + + public void setX1(int x1) { + this.x1 = x1; + } + + public void setY1(int y1) { + this.y1 = y1; + } + + public void setZ1(int z1) { + this.z1 = z1; + } + + public void setX2(int x2) { + this.x2 = x2; + } + + public void setY2(int y2) { + this.y2 = y2; + } + + public void setZ2(int z2) { + this.z2 = z2; + } +} + diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidBlockIterator.java b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidBlockIterator.java new file mode 100644 index 0000000..665be5d --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidBlockIterator.java @@ -0,0 +1,97 @@ +package com.loganmagnan.pluginbase.utils.cuboid; + +import org.bukkit.World; +import org.bukkit.block.Block; + +import java.util.Iterator; + +public class CuboidBlockIterator implements Iterator { + + private World world; + private int baseX; + private int baseY; + private int baseZ; + private int sizeX; + private int sizeY; + private int sizeZ; + private int x; + private int y; + private int z; + + CuboidBlockIterator(World world, int x1, int y1, int z1, int x2, int y2, int z2) { + this.world = world; + this.baseX = x1; + this.baseY = y1; + this.baseZ = z1; + this.sizeX = Math.abs(x2 - x1) + 1; + this.sizeY = Math.abs(y2 - y1) + 1; + this.sizeZ = Math.abs(z2 - z1) + 1; + this.z = 0; + this.y = 0; + this.x = 0; + } + + @Override + public boolean hasNext() { + return this.x < this.sizeX && this.y < this.sizeY && this.z < this.sizeZ; + } + + @Override + public Block next() { + Block block = this.world.getBlockAt(this.baseX + this.x, this.baseY + this.y, this.baseZ + this.z); + if (++this.x >= this.sizeX) { + this.x = 0; + if (++this.y >= this.sizeY) { + this.y = 0; + ++this.z; + } + } + return block; + } + + @Override + public void remove() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public World getWorld() { + return this.world; + } + + public int getBaseX() { + return this.baseX; + } + + public int getBaseY() { + return this.baseY; + } + + public int getBaseZ() { + return this.baseZ; + } + + public int getSizeX() { + return this.sizeX; + } + + public int getSizeY() { + return this.sizeY; + } + + public int getSizeZ() { + return this.sizeZ; + } + + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public int getZ() { + return this.z; + } +} + diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidDirection.java b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidDirection.java new file mode 100644 index 0000000..0298b22 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidDirection.java @@ -0,0 +1,46 @@ +package com.loganmagnan.pluginbase.utils.cuboid; + +public enum CuboidDirection { + + NORTH, EAST, SOUTH, WEST, + UP, DOWN, HORIZONTAL, VERTICAL, BOTH, + UNKNOWN; + + private CuboidDirection() { + + } + + public CuboidDirection opposite() { + switch (this) { + case NORTH: { + return SOUTH; + } + case EAST: { + return WEST; + } + case SOUTH: { + return NORTH; + } + case WEST: { + return EAST; + } + case HORIZONTAL: { + return VERTICAL; + } + case VERTICAL: { + return HORIZONTAL; + } + case UP: { + return DOWN; + } + case DOWN: { + return UP; + } + case BOTH: { + return BOTH; + } + } + return UNKNOWN; + } +} + diff --git a/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidLocationIterator.java b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidLocationIterator.java new file mode 100644 index 0000000..aa3cd18 --- /dev/null +++ b/src/main/java/com/loganmagnan/pluginbase/utils/cuboid/CuboidLocationIterator.java @@ -0,0 +1,97 @@ +package com.loganmagnan.pluginbase.utils.cuboid; + +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.Iterator; + +public class CuboidLocationIterator implements Iterator { + + private World world; + private int baseX; + private int baseY; + private int baseZ; + private int sizeX; + private int sizeY; + private int sizeZ; + private int x; + private int y; + private int z; + + CuboidLocationIterator(World world, int x1, int y1, int z1, int x2, int y2, int z2) { + this.world = world; + this.baseX = x1; + this.baseY = y1; + this.baseZ = z1; + this.sizeX = Math.abs(x2 - x1) + 1; + this.sizeY = Math.abs(y2 - y1) + 1; + this.sizeZ = Math.abs(z2 - z1) + 1; + this.z = 0; + this.y = 0; + this.x = 0; + } + + @Override + public boolean hasNext() { + return this.x < this.sizeX && this.y < this.sizeY && this.z < this.sizeZ; + } + + @Override + public Location next() { + Location location = new Location(this.world, this.baseX + this.x, this.baseY + this.y, this.baseZ + this.z); + if (++this.x >= this.sizeX) { + this.x = 0; + if (++this.y >= this.sizeY) { + this.y = 0; + ++this.z; + } + } + return location; + } + + @Override + public void remove() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public World getWorld() { + return this.world; + } + + public int getBaseX() { + return this.baseX; + } + + public int getBaseY() { + return this.baseY; + } + + public int getBaseZ() { + return this.baseZ; + } + + public int getSizeX() { + return this.sizeX; + } + + public int getSizeY() { + return this.sizeY; + } + + public int getSizeZ() { + return this.sizeZ; + } + + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public int getZ() { + return this.z; + } +} + diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..d712f52 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,5 @@ +name: PluginBase +author: Trixkz +version: 1.0 +api-version: 1.13 +main: com.loganmagnan.pluginbase.PluginBase \ No newline at end of file