mirror of
https://github.com/songoda/EpicHeads.git
synced 2024-11-29 14:06:29 +01:00
The rest of the features.
This commit is contained in:
parent
3067532b6f
commit
996e17eb96
6
pom.xml
6
pom.xml
@ -68,5 +68,11 @@
|
||||
<artifactId>songodaupdater</artifactId>
|
||||
<version>1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.milkbowl</groupId>
|
||||
<artifactId>vault</artifactId>
|
||||
<version>1.7.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -5,11 +5,16 @@ import com.songoda.epicheads.head.Head;
|
||||
import com.songoda.epicheads.head.HeadManager;
|
||||
import com.songoda.epicheads.head.Tag;
|
||||
import com.songoda.epicheads.listeners.ItemListeners;
|
||||
import com.songoda.epicheads.players.EPlayer;
|
||||
import com.songoda.epicheads.players.PlayerManager;
|
||||
import com.songoda.epicheads.utils.Methods;
|
||||
import com.songoda.epicheads.utils.Metrics;
|
||||
import com.songoda.epicheads.utils.ServerVersion;
|
||||
import com.songoda.epicheads.utils.SettingsManager;
|
||||
import com.songoda.epicheads.utils.gui.AbstractGUI;
|
||||
import com.songoda.epicheads.utils.storage.Storage;
|
||||
import com.songoda.epicheads.utils.storage.StorageRow;
|
||||
import com.songoda.epicheads.utils.storage.types.StorageYaml;
|
||||
import com.songoda.epicheads.utils.updateModules.LocaleModule;
|
||||
import com.songoda.update.Plugin;
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
@ -26,6 +31,7 @@ import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EpicHeads extends JavaPlugin {
|
||||
private static CommandSender console = Bukkit.getConsoleSender();
|
||||
@ -40,6 +46,7 @@ public class EpicHeads extends JavaPlugin {
|
||||
private CommandManager commandManager;
|
||||
|
||||
private Locale locale;
|
||||
private Storage storage;
|
||||
|
||||
public static EpicHeads getInstance() {
|
||||
return INSTANCE;
|
||||
@ -68,6 +75,7 @@ public class EpicHeads extends JavaPlugin {
|
||||
SongodaUpdate.load(plugin);
|
||||
|
||||
this.references = new References();
|
||||
this.storage = new StorageYaml(this);
|
||||
|
||||
// Setup Managers
|
||||
this.headManager = new HeadManager();
|
||||
@ -84,17 +92,51 @@ public class EpicHeads extends JavaPlugin {
|
||||
// Load Heads
|
||||
loadHeads();
|
||||
|
||||
// Load Favorites
|
||||
Bukkit.getScheduler().runTaskLater(this, this::loadData, 10);
|
||||
|
||||
int timeout = SettingsManager.Setting.AUTOSAVE.getInt() * 60 * 20;
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(this, this::saveToFile, timeout, timeout);
|
||||
|
||||
// Start Metrics
|
||||
new Metrics(this);
|
||||
|
||||
console.sendMessage(Methods.formatText("&a============================="));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.storage.closeConnection();
|
||||
this.saveToFile();
|
||||
console.sendMessage(Methods.formatText("&a============================="));
|
||||
console.sendMessage(Methods.formatText("&7EpicHeads " + this.getDescription().getVersion() + " by &5Songoda <3!"));
|
||||
console.sendMessage(Methods.formatText("&7Action: &cDisabling&7..."));
|
||||
console.sendMessage(Methods.formatText("&a============================="));
|
||||
}
|
||||
|
||||
private void saveToFile() {
|
||||
storage.doSave();
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
// Adding in favorites.
|
||||
if (storage.containsGroup("")) {
|
||||
for (StorageRow row : storage.getRowsByGroup("players")) {
|
||||
if (row.get("uuid").asObject() == null)
|
||||
continue;
|
||||
|
||||
EPlayer player = new EPlayer(
|
||||
UUID.fromString(row.get("uuid").asString()),
|
||||
row.get("favorites").asIntList());
|
||||
|
||||
this.playerManager.addPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
// Save data initially so that if the person reloads again fast they don't lose all their data.
|
||||
this.saveToFile();
|
||||
}
|
||||
|
||||
|
||||
private void downloadHeads() {
|
||||
try {
|
||||
@ -213,4 +255,7 @@ public class EpicHeads extends JavaPlugin {
|
||||
return commandManager;
|
||||
}
|
||||
|
||||
public SettingsManager getSettingsManager() {
|
||||
return settingsManager;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ package com.songoda.epicheads.command;
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.command.commands.CommandEpicHeads;
|
||||
import com.songoda.epicheads.command.commands.CommandHelp;
|
||||
import com.songoda.epicheads.command.commands.CommandReload;
|
||||
import com.songoda.epicheads.command.commands.CommandSettings;
|
||||
import com.songoda.epicheads.utils.Methods;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -26,10 +29,10 @@ public class CommandManager implements CommandExecutor {
|
||||
instance.getCommand("EpicHeads").setExecutor(this);
|
||||
|
||||
AbstractCommand commandEpicHeads = addCommand(new CommandEpicHeads());
|
||||
/*
|
||||
|
||||
addCommand(new CommandSettings(commandEpicHeads));
|
||||
addCommand(new CommandHelp(commandEpicHeads));
|
||||
addCommand(new CommandReload(commandEpicHeads)); */
|
||||
addCommand(new CommandReload(commandEpicHeads));
|
||||
|
||||
for (AbstractCommand abstractCommand : commands) {
|
||||
if (abstractCommand.getParent() != null) continue;
|
||||
|
@ -27,16 +27,16 @@ public class CommandEpicHeads extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "epicheads.menu";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/epicheads";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Displays this page.";
|
||||
return "Displays heads overview.";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package com.songoda.epicheads.command.commands;
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.command.AbstractCommand;
|
||||
import com.songoda.epicheads.utils.Methods;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandHelp extends AbstractCommand {
|
||||
|
||||
public CommandHelp(AbstractCommand parent) {
|
||||
super(parent, false, "help");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(EpicHeads instance, CommandSender sender, String... args) {
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage(Methods.formatText(instance.getReferences().getPrefix() + "&7Version " + instance.getDescription().getVersion() + " Created with <3 by &5&l&oSongoda"));
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage(Methods.formatText("&7Welcome to EpicHeads! To get started try using the /epicheads command to access the heads panel."));
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage(Methods.formatText("&6Commands:"));
|
||||
for (AbstractCommand command : instance.getCommandManager().getCommands()) {
|
||||
if (command.getPermissionNode() == null || sender.hasPermission(command.getPermissionNode())) {
|
||||
sender.sendMessage(Methods.formatText("&8 - &a" + command.getSyntax() + "&7 - " + command.getDescription()));
|
||||
}
|
||||
}
|
||||
sender.sendMessage("");
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(EpicHeads instance, CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/epicheads help";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Displays this page.";
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.songoda.epicheads.command.commands;
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.command.AbstractCommand;
|
||||
import com.songoda.epicheads.utils.Methods;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandReload extends AbstractCommand {
|
||||
|
||||
public CommandReload(AbstractCommand parent) {
|
||||
super(parent, false, "reload");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(EpicHeads instance, CommandSender sender, String... args) {
|
||||
instance.reload();
|
||||
sender.sendMessage(Methods.formatText(instance.getReferences().getPrefix() + "&7Configuration and Language files reloaded."));
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(EpicHeads instance, CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "epicheads.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/epicheads reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Reload the Configuration and Language files.";
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.songoda.epicheads.command.commands;
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.command.AbstractCommand;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandSettings extends AbstractCommand {
|
||||
|
||||
public CommandSettings(AbstractCommand parent) {
|
||||
super(parent, true, "settings");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(EpicHeads instance, CommandSender sender, String... args) {
|
||||
Player player = (Player) sender;
|
||||
instance.getSettingsManager().openSettingsManager(player);
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(EpicHeads instance, CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "epicheads.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/ehe settings";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Edit EpicHeads Settings.";
|
||||
}
|
||||
}
|
@ -5,13 +5,17 @@ import com.songoda.epicheads.head.Head;
|
||||
import com.songoda.epicheads.head.Tag;
|
||||
import com.songoda.epicheads.players.EPlayer;
|
||||
import com.songoda.epicheads.utils.AbstractChatConfirm;
|
||||
import com.songoda.epicheads.utils.SettingsManager;
|
||||
import com.songoda.epicheads.utils.gui.AbstractGUI;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@ -45,17 +49,16 @@ public class GUIHeads extends AbstractGUI {
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
|
||||
|
||||
int numHeads = this.heads.size();
|
||||
if (numHeads == 0) {
|
||||
player.sendMessage("No heads found.");
|
||||
player.sendMessage(plugin.getReferences().getPrefix() + plugin.getLocale().getMessage("general.search.nonefound"));
|
||||
return;
|
||||
}
|
||||
Tag tag = heads.get(0).getTag();
|
||||
|
||||
this.maxPage = (int) Math.floor(numHeads / 45.0);
|
||||
init((query != null ? "Query: " + query : tag.getName()) + " (" + numHeads + ") Page " + (page + 1) + "/" + (maxPage + 1), 54);
|
||||
init((query != null ? plugin.getLocale().getMessage("general.word.query") + ": " + query : tag.getName())
|
||||
+ " (" + numHeads + ") " + plugin.getLocale().getMessage("general.word.page") + " " + (page + 1) + "/" + (maxPage + 1), 54);
|
||||
constructGUI();
|
||||
}
|
||||
|
||||
@ -67,7 +70,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (page - 2 > 0) {
|
||||
createButton(0, Material.ARROW, "Back");
|
||||
createButton(0, Material.ARROW, "&c" + plugin.getLocale().getMessage("general.word.page") + " " + (page - 2));
|
||||
registerClickable(0, ((player1, inventory1, cursor, slot, type) -> {
|
||||
page -= 3;
|
||||
updateTitle();
|
||||
@ -76,7 +79,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
}
|
||||
|
||||
if (page - 1 > 0) {
|
||||
createButton(1, Material.ARROW, "Back");
|
||||
createButton(1, Material.ARROW, "&c" + plugin.getLocale().getMessage("general.word.page") + " " + (page - 1));
|
||||
registerClickable(1, ((player1, inventory1, cursor, slot, type) -> {
|
||||
page -= 2;
|
||||
updateTitle();
|
||||
@ -85,7 +88,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
}
|
||||
|
||||
if (page != 0) {
|
||||
createButton(2, Material.ARROW, "Back");
|
||||
createButton(2, Material.ARROW, "&c" + plugin.getLocale().getMessage("general.word.page") + " " + page);
|
||||
registerClickable(2, ((player1, inventory1, cursor, slot, type) -> {
|
||||
page--;
|
||||
updateTitle();
|
||||
@ -93,16 +96,16 @@ public class GUIHeads extends AbstractGUI {
|
||||
inventory.getItem(2).setAmount(page);
|
||||
}
|
||||
|
||||
createButton(3, Material.COMPASS, "Create Search");
|
||||
createButton(3, Material.COMPASS, plugin.getLocale().getMessage("gui.heads.search"));
|
||||
|
||||
createButton(4, Material.MAP, "Back To Categories");
|
||||
createButton(4, Material.MAP, plugin.getLocale().getMessage("gui.heads.categories"));
|
||||
inventory.getItem(4).setAmount(page + 1);
|
||||
|
||||
if (heads.size() > 1)
|
||||
createButton(5, Material.COMPASS, "Refine Search");
|
||||
createButton(5, Material.COMPASS, plugin.getLocale().getMessage("gui.heads.refine"));
|
||||
|
||||
if (page != maxPage) {
|
||||
createButton(6, Material.ARROW, "Next");
|
||||
createButton(6, Material.ARROW, "&c" + plugin.getLocale().getMessage("general.word.page") + " " + (page + 2));
|
||||
registerClickable(6, ((player1, inventory1, cursor, slot, type) -> {
|
||||
page++;
|
||||
updateTitle();
|
||||
@ -111,7 +114,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
}
|
||||
|
||||
if (page + 1 < maxPage) {
|
||||
createButton(7, Material.ARROW, "Next");
|
||||
createButton(7, Material.ARROW, "&c" + plugin.getLocale().getMessage("general.word.page") + " " + (page + 3));
|
||||
registerClickable(7, ((player1, inventory1, cursor, slot, type) -> {
|
||||
page += 2;
|
||||
updateTitle();
|
||||
@ -120,7 +123,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
}
|
||||
|
||||
if (page + 2 < maxPage) {
|
||||
createButton(8, Material.ARROW, "Next");
|
||||
createButton(8, Material.ARROW, "&c" + plugin.getLocale().getMessage("general.word.page") + " " + (page + 4));
|
||||
registerClickable(8, ((player1, inventory1, cursor, slot, type) -> {
|
||||
page += 3;
|
||||
updateTitle();
|
||||
@ -135,10 +138,15 @@ public class GUIHeads extends AbstractGUI {
|
||||
|
||||
if (head.getName() == null) continue;
|
||||
|
||||
ItemStack item = head.asItemStack(favorites.contains(head.getId()));
|
||||
boolean free = (!player.hasPermission("epicheads.freeheads") &&
|
||||
(SettingsManager.Setting.FREE_IN_CREATIVE.getBoolean() && player.getGameMode() == GameMode.CREATIVE));
|
||||
|
||||
ItemStack item = head.asItemStack(favorites.contains(head.getId()), free);
|
||||
|
||||
inventory.setItem(i + 9, item);
|
||||
|
||||
double cost = SettingsManager.Setting.PRICE.getDouble();
|
||||
|
||||
registerClickable(i + 9, ((player1, inventory1, cursor, slot, type) -> {
|
||||
if (type == ClickType.SHIFT_LEFT || type == ClickType.SHIFT_RIGHT) {
|
||||
EPlayer ePlayer = plugin.getPlayerManager().getPlayer(player);
|
||||
@ -154,6 +162,23 @@ public class GUIHeads extends AbstractGUI {
|
||||
meta.setLore(new ArrayList<>());
|
||||
item.setItemMeta(meta);
|
||||
|
||||
|
||||
if (!player.hasPermission("epicheads.bypasscost") && free) {
|
||||
if (plugin.getServer().getPluginManager().getPlugin("Vault") != null) {
|
||||
RegisteredServiceProvider<Economy> rsp = plugin.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
|
||||
net.milkbowl.vault.economy.Economy econ = rsp.getProvider();
|
||||
if (econ.has(player, cost)) {
|
||||
econ.withdrawPlayer(player, cost);
|
||||
} else {
|
||||
player.sendMessage(plugin.getLocale().getMessage("event.buyhead.cannotafford"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
player.sendMessage("Vault is not installed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
player.getInventory().addItem(item);
|
||||
}));
|
||||
}
|
||||
@ -170,7 +195,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
if (heads.size() > 1) {
|
||||
registerClickable(5, ((player1, inventory1, cursor, slot, type) -> {
|
||||
|
||||
player.sendMessage("Refine your search...");
|
||||
player.sendMessage(plugin.getReferences().getPrefix() + plugin.getLocale().getMessage("general.search.refine"));
|
||||
AbstractChatConfirm abstractChatConfirm = new AbstractChatConfirm(player, event -> {
|
||||
this.page = 0;
|
||||
this.heads = this.heads.stream().filter(head -> head.getName().toLowerCase()
|
||||
@ -191,7 +216,7 @@ public class GUIHeads extends AbstractGUI {
|
||||
}
|
||||
|
||||
public static void doSearch(Player player) {
|
||||
player.sendMessage("Enter your query...");
|
||||
player.sendMessage(EpicHeads.getInstance().getReferences().getPrefix() + EpicHeads.getInstance().getLocale().getMessage("general.search.global"));
|
||||
new AbstractChatConfirm(player, event -> {
|
||||
List<Head> heads = EpicHeads.getInstance().getHeadManager().getHeads().stream()
|
||||
.filter(head -> head.getName().toLowerCase().contains(event.getMessage().toLowerCase()))
|
||||
|
@ -3,10 +3,13 @@ package com.songoda.epicheads.gui;
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.head.Tag;
|
||||
import com.songoda.epicheads.utils.Methods;
|
||||
import com.songoda.epicheads.utils.SettingsManager;
|
||||
import com.songoda.epicheads.utils.gui.AbstractGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GUIOverview extends AbstractGUI {
|
||||
@ -17,15 +20,19 @@ public class GUIOverview extends AbstractGUI {
|
||||
super(player);
|
||||
this.plugin = plugin;
|
||||
|
||||
init("EpicHeads (" + plugin.getHeadManager().getHeads().size() + " heads)", 45);
|
||||
init(plugin.getLocale().getMessage("gui.overview.title", plugin.getHeadManager().getHeads().size()), 45);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void constructGUI() {
|
||||
|
||||
createButton(5, Material.STONE, "&6&lView Favorites",
|
||||
"Shift click any head",
|
||||
"to save as a favorite.");
|
||||
ArrayList<String> lore = new ArrayList<>();
|
||||
String[] parts = plugin.getLocale().getMessage("gui.overview.favoriteslore").split("\\|");
|
||||
for (String line : parts)
|
||||
lore.add(Methods.formatText(line));
|
||||
|
||||
createButton(4, Material.GOLDEN_APPLE, plugin.getLocale().getMessage("gui.overview.viewfavorites"),
|
||||
lore);
|
||||
|
||||
inventory.setItem(0, Methods.getBackgroundGlass(true));
|
||||
inventory.setItem(1, Methods.getBackgroundGlass(true));
|
||||
@ -54,30 +61,84 @@ public class GUIOverview extends AbstractGUI {
|
||||
if (i + add == 7 || i + add == 16) add = add + 2;
|
||||
|
||||
Tag tag = plugin.getHeadManager().getTags().get(i);
|
||||
createButton(i + 10 + add, Material.STONE, "&c&l" + tag.getName(), "&e" + tag.getCount() + " heads");
|
||||
|
||||
registerClickable(i + 10 + add, ((player1, inventory1, cursor, slot, type) -> new GUIHeads(plugin, player, null, plugin.getHeadManager().getHeadsByTag(tag))));
|
||||
if (!player.hasPermission("epicheads.category." + tag.getName())) return;
|
||||
|
||||
TagInfo tagInfo = TagInfo.valueOf(tag.getName().toUpperCase());
|
||||
|
||||
createButton(i + 10 + add, Methods.addTexture(new ItemStack(Material.PLAYER_HEAD, 1, (byte) 3),
|
||||
tagInfo.getUrl()),
|
||||
plugin.getLocale().getMessage("gui.overview.headname", tagInfo.getName()),
|
||||
plugin.getLocale().getMessage("gui.overview.headlore", tag.getCount()));
|
||||
|
||||
registerClickable(i + 10 + add, ((player1, inventory1, cursor, slot, type) ->
|
||||
new GUIHeads(plugin, player, null, plugin.getHeadManager().getHeadsByTag(tag))));
|
||||
}
|
||||
|
||||
createButton(39, Material.COMPASS, "Search");
|
||||
createButton(39, Material.COMPASS, plugin.getLocale().getMessage("gui.overview.search"));
|
||||
|
||||
createButton(41, Material.STONE,
|
||||
"Add or request new heads",
|
||||
"in our discord server.");
|
||||
|
||||
if (SettingsManager.Setting.DISCORD.getBoolean()) {
|
||||
|
||||
ArrayList<String> lore2 = new ArrayList<>();
|
||||
String[] parts2 = plugin.getLocale().getMessage("gui.overview.discordlore").split("\\|");
|
||||
for (String line : parts2)
|
||||
lore2.add(Methods.formatText(line));
|
||||
|
||||
createButton(41, Methods.addTexture(new ItemStack(Material.PLAYER_HEAD, 1, (byte) 3),
|
||||
"a3b183b148b9b4e2b158334aff3b5bb6c2c2dbbc4d67f76a7be856687a2b623"),
|
||||
plugin.getLocale().getMessage("gui.overview.discord"),
|
||||
lore2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerClickables() {
|
||||
registerClickable(5, ((player1, inventory1, cursor, slot, type) ->
|
||||
new GUIHeads(plugin, player, null, plugin.getPlayerManager().getPlayer(player).getFavoritesAsHeads())));
|
||||
registerClickable(4, ((player1, inventory1, cursor, slot, type) ->
|
||||
new GUIHeads(plugin, player, plugin.getLocale().getMessage("general.word.favorites"),
|
||||
plugin.getPlayerManager().getPlayer(player).getFavoritesAsHeads())));
|
||||
|
||||
registerClickable(39, ((player1, inventory1, cursor, slot, type) ->
|
||||
GUIHeads.doSearch(player1)));
|
||||
|
||||
registerClickable(41, ((player1, inventory1, cursor, slot, type) -> {
|
||||
player.sendMessage(Methods.formatText(plugin.getReferences().getPrefix() + "&9https://discord.gg/A9TRJQb"));
|
||||
player.closeInventory();
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerOnCloses() {
|
||||
|
||||
}
|
||||
|
||||
public enum TagInfo {
|
||||
ALPHABET("&9&lAlphabet", "9c60da2944a177dd08268fbec04e40812d1d929650be66529b1ee5e1e7eca"),
|
||||
HUMANS("&a&lHumans", "b7c224f0453e745c85966025e7767d592feea3ad4c69d2366d94d5e8c9a8c2ce"),
|
||||
FOOD("&b&lFood", "f7b9f08ada4e8ba586a04ed2e9e25fe8b9d568a665243f9c603799a7c896736"),
|
||||
MISC("&8&lMisc", "f5612dc7b86d71afc1197301c15fd979e9f39e7b1f41d8f1ebdf8115576e2e"),
|
||||
ANIMALS("&d&lAnimals", "d8cdd4f285632c25d762ece25f4193b966c2641b15d9bdbc0a113023de76ab"),
|
||||
GAMES("&b&lGames", "dba8d8e53d8a5a75770b62cce73db6bab701cc3de4a9b654d213d54af9615"),
|
||||
MONSTERS("&c&lMonsters", "68d2183640218ab330ac56d2aab7e29a9790a545f691619e38578ea4a69ae0b6"),
|
||||
INTERIOR("&6&lInterior", "ec6d9024fc5412e8e2664123732d2291dfc6bb175f72cf894096f7f313641fd4"),
|
||||
BLOCKS("&9&lBlocks", "7788f5ddaf52c5842287b9427a74dac8f0919eb2fdb1b51365ab25eb392c47");
|
||||
|
||||
String name;
|
||||
String url;
|
||||
|
||||
TagInfo(String name, String url) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package com.songoda.epicheads.head;
|
||||
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.utils.Methods;
|
||||
import com.songoda.epicheads.utils.SettingsManager;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
@ -47,19 +49,24 @@ public class Head {
|
||||
}
|
||||
|
||||
public ItemStack asItemStack() {
|
||||
return asItemStack(false);
|
||||
return asItemStack(false, false);
|
||||
}
|
||||
|
||||
public ItemStack asItemStack(boolean favorite) {
|
||||
public ItemStack asItemStack(boolean favorite) { return asItemStack(favorite, false); }
|
||||
|
||||
public ItemStack asItemStack(boolean favorite, boolean includeCost) {
|
||||
ItemStack item = Methods.addTexture(new ItemStack(Material.PLAYER_HEAD, 1, (byte) 3),
|
||||
this.URL);
|
||||
|
||||
double cost = SettingsManager.Setting.PRICE.getDouble();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName(Methods.formatText((favorite ? "&6⭐ " : "") + "&9" + name));
|
||||
List<String> lore = new ArrayList<>();
|
||||
if (this.staffPicked == 1)
|
||||
lore.add(Methods.formatText("&8Staff Favorite"));
|
||||
lore.add(Methods.formatText("&8ID: &7" + this.id));
|
||||
lore.add(Methods.formatText(EpicHeads.getInstance().getLocale().getMessage("general.head.staffpicked")));
|
||||
lore.add(Methods.formatText(EpicHeads.getInstance().getLocale().getMessage("general.head.id", this.id)));
|
||||
if (includeCost)
|
||||
lore.add(EpicHeads.getInstance().getLocale().getMessage("general.head.cost", cost));
|
||||
|
||||
meta.setLore(lore);
|
||||
item.setItemMeta(meta);
|
||||
|
@ -18,6 +18,11 @@ public class EPlayer {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public EPlayer(UUID uuid, List<Integer> favorites) {
|
||||
this.uuid = uuid;
|
||||
this.favorites = favorites;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package com.songoda.epicheads.players;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
public class PlayerManager {
|
||||
|
||||
@ -18,4 +16,13 @@ public class PlayerManager {
|
||||
return getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
public EPlayer addPlayer(EPlayer player) {
|
||||
registeredHeads.put(player.getUuid(), player);
|
||||
return player;
|
||||
}
|
||||
|
||||
public List<EPlayer> getPlayers() {
|
||||
return new ArrayList<>(registeredHeads.values());
|
||||
}
|
||||
|
||||
}
|
||||
|
67
src/main/java/com/songoda/epicheads/utils/ConfigWrapper.java
Normal file
67
src/main/java/com/songoda/epicheads/utils/ConfigWrapper.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.songoda.epicheads.utils;
|
||||
|
||||
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;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* ConfigWrapper made by @clip
|
||||
*/
|
||||
public class ConfigWrapper {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final String folderName, fileName;
|
||||
private FileConfiguration config;
|
||||
private File configFile;
|
||||
|
||||
public ConfigWrapper(final JavaPlugin instance, final String folderName, final String fileName) {
|
||||
this.plugin = instance;
|
||||
this.folderName = folderName;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public void createNewFile(final String message, final String header) {
|
||||
reloadConfig();
|
||||
saveConfig();
|
||||
loadConfig(header);
|
||||
|
||||
if (message != null) {
|
||||
plugin.getLogger().info(message);
|
||||
}
|
||||
}
|
||||
|
||||
public FileConfiguration getConfig() {
|
||||
if (config == null) {
|
||||
reloadConfig();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void loadConfig(final String header) {
|
||||
config.options().header(header);
|
||||
config.options().copyDefaults(true);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
public void reloadConfig() {
|
||||
if (configFile == null) {
|
||||
configFile = new File(plugin.getDataFolder() + folderName, fileName);
|
||||
}
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
}
|
||||
|
||||
public void saveConfig() {
|
||||
if (config == null || configFile == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getConfig().save(configFile);
|
||||
} catch (final IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Could not save config to " + configFile, ex);
|
||||
}
|
||||
}
|
||||
}
|
695
src/main/java/com/songoda/epicheads/utils/Metrics.java
Normal file
695
src/main/java/com/songoda/epicheads/utils/Metrics.java
Normal file
@ -0,0 +1,695 @@
|
||||
package com.songoda.epicheads.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class Metrics {
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
|
||||
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
*/
|
||||
public Metrics(Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly no effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
|
||||
// Load the data
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
// Register our service
|
||||
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JSONObject getPluginData() {
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
String pluginName = plugin.getDescription().getName();
|
||||
String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.put("pluginName", pluginName); // Append the name of the plugin
|
||||
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
JSONArray customCharts = new JSONArray();
|
||||
for (CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
JSONObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.put("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JSONObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String bukkitVersion = Bukkit.getVersion();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("serverUUID", serverUUID);
|
||||
|
||||
data.put("playerAmount", playerAmount);
|
||||
data.put("onlineMode", onlineMode);
|
||||
data.put("bukkitVersion", bukkitVersion);
|
||||
|
||||
data.put("javaVersion", javaVersion);
|
||||
data.put("osName", osName);
|
||||
data.put("osArch", osArch);
|
||||
data.put("osVersion", osVersion);
|
||||
data.put("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JSONObject data = getServerData();
|
||||
|
||||
JSONArray pluginData = new JSONArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
|
||||
data.put("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JSONObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data.toString());
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JSONObject getRequestJsonObject() {
|
||||
JSONObject chart = new JSONObject();
|
||||
chart.put("chartId", chartId);
|
||||
try {
|
||||
JSONObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.put("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JSONObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JSONObject value = new JSONObject();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
value.put(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.put(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
categoryValues.add(entry.getValue());
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
for (int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(categoryValue);
|
||||
}
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -170,6 +170,11 @@ public class SettingsManager implements Listener {
|
||||
|
||||
public enum Setting {
|
||||
|
||||
AUTOSAVE("Main.Auto Save Interval In Seconds", 15),
|
||||
DISCORD("Main.Show Discord Button", true),
|
||||
PRICE("Main.Head Cost", 24.99),
|
||||
FREE_IN_CREATIVE("Main.Heads Free In Creative Mode", false),
|
||||
|
||||
GLASS_TYPE_1("Interfaces.Glass Type 1", 7),
|
||||
GLASS_TYPE_2("Interfaces.Glass Type 2", 11),
|
||||
GLASS_TYPE_3("Interfaces.Glass Type 3", 3),
|
||||
@ -202,5 +207,8 @@ public class SettingsManager implements Listener {
|
||||
|
||||
public char getChar() { return EpicHeads.getInstance().getConfig().getString(setting).charAt(0); }
|
||||
|
||||
public double getDouble() {
|
||||
return EpicHeads.getInstance().getConfig().getDouble(setting);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.songoda.epicheads.utils.storage;
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.players.EPlayer;
|
||||
import com.songoda.epicheads.utils.ConfigWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Storage {
|
||||
|
||||
protected final EpicHeads instance;
|
||||
protected final ConfigWrapper dataFile;
|
||||
|
||||
public Storage(EpicHeads instance) {
|
||||
this.instance = instance;
|
||||
this.dataFile = new ConfigWrapper(instance, "", "data.yml");
|
||||
this.dataFile.createNewFile(null, "EpicSpawners Data File");
|
||||
this.dataFile.getConfig().options().copyDefaults(true);
|
||||
this.dataFile.saveConfig();
|
||||
}
|
||||
|
||||
public abstract boolean containsGroup(String group);
|
||||
|
||||
public abstract List<StorageRow> getRowsByGroup(String group);
|
||||
|
||||
public abstract void prepareSaveItem(String group, StorageItem... items);
|
||||
|
||||
public void updateData(EpicHeads plugin) {
|
||||
// Save game data
|
||||
for (EPlayer player : instance.getPlayerManager().getPlayers()) {
|
||||
prepareSaveItem("players", new StorageItem("uuid", player.getUuid().toString()),
|
||||
new StorageItem("favorites", player.getFavorites()));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void doSave();
|
||||
|
||||
public abstract void save();
|
||||
|
||||
public abstract void makeBackup();
|
||||
|
||||
public abstract void closeConnection();
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.songoda.epicheads.utils.storage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StorageItem {
|
||||
|
||||
private final Object object;
|
||||
private String key = null;
|
||||
|
||||
public StorageItem(Object object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public StorageItem(String key, Object object) {
|
||||
this.key = key;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public StorageItem(String key, List<Integer> integers) {
|
||||
String object = "";
|
||||
for (Integer i : integers) {
|
||||
object += i + ";";
|
||||
}
|
||||
this.key = key;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
if (object == null) return null;
|
||||
return (String) object;
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
if (object == null) return false;
|
||||
return (boolean) object;
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
if (object == null) return 0;
|
||||
return (int) object;
|
||||
}
|
||||
|
||||
public Object asObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public List<Integer> asIntList() {
|
||||
List<Integer> list = new ArrayList<>();
|
||||
if (object == null) return list;
|
||||
String[] stack = ((String) object).split(";");
|
||||
for (String item : stack) {
|
||||
if (item.equals("")) continue;
|
||||
list.add(Integer.valueOf(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.songoda.epicheads.utils.storage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class StorageRow {
|
||||
|
||||
private final String key;
|
||||
|
||||
private final Map<String, StorageItem> items;
|
||||
|
||||
public StorageRow(String key, Map<String, StorageItem> items) {
|
||||
this.key = key;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public StorageItem get(String key) {
|
||||
if (!items.containsKey(key) || items.get(key).asObject().toString().equals("")) return new StorageItem(null);
|
||||
return items.get(key);
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package com.songoda.epicheads.utils.storage.types;
|
||||
|
||||
import com.songoda.epicheads.EpicHeads;
|
||||
import com.songoda.epicheads.utils.storage.Storage;
|
||||
import com.songoda.epicheads.utils.storage.StorageItem;
|
||||
import com.songoda.epicheads.utils.storage.StorageRow;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.MemorySection;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class StorageYaml extends Storage {
|
||||
|
||||
private static final Map<String, Object> toSave = new HashMap<>();
|
||||
private static Map<String, Object> lastSave = null;
|
||||
|
||||
public StorageYaml(EpicHeads instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsGroup(String group) {
|
||||
return dataFile.getConfig().contains("data." + group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageRow> getRowsByGroup(String group) {
|
||||
List<StorageRow> rows = new ArrayList<>();
|
||||
ConfigurationSection currentSection = dataFile.getConfig().getConfigurationSection("data." + group);
|
||||
for (String key : currentSection.getKeys(false)) {
|
||||
|
||||
Map<String, StorageItem> items = new HashMap<>();
|
||||
ConfigurationSection currentSection2 = dataFile.getConfig().getConfigurationSection("data." + group + "." + key);
|
||||
for (String key2 : currentSection2.getKeys(false)) {
|
||||
String path = "data." + group + "." + key + "." + key2;
|
||||
items.put(key2, new StorageItem(dataFile.getConfig().get(path) instanceof MemorySection
|
||||
? convertToInLineList(path) : dataFile.getConfig().get(path)));
|
||||
}
|
||||
if (items.isEmpty()) continue;
|
||||
StorageRow row = new StorageRow(key, items);
|
||||
rows.add(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private String convertToInLineList(String path) {
|
||||
StringBuilder converted = new StringBuilder();
|
||||
for (String key : dataFile.getConfig().getConfigurationSection(path).getKeys(false)) {
|
||||
converted.append(key).append(":").append(dataFile.getConfig().getInt(path + "." + key)).append(";");
|
||||
}
|
||||
return converted.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareSaveItem(String group, StorageItem... items) {
|
||||
for (StorageItem item : items) {
|
||||
if (item == null || item.asObject() == null) continue;
|
||||
toSave.put("data." + group + "." + items[0].asString() + "." + item.getKey(), item.asObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSave() {
|
||||
this.updateData(instance);
|
||||
|
||||
if (lastSave == null)
|
||||
lastSave = new HashMap<>(toSave);
|
||||
|
||||
if (toSave.isEmpty()) return;
|
||||
Map<String, Object> nextSave = new HashMap<>(toSave);
|
||||
|
||||
this.makeBackup();
|
||||
this.save();
|
||||
|
||||
toSave.clear();
|
||||
lastSave.clear();
|
||||
lastSave.putAll(nextSave);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
for (Map.Entry<String, Object> entry : lastSave.entrySet()) {
|
||||
if (toSave.containsKey(entry.getKey())) {
|
||||
Object newValue = toSave.get(entry.getKey());
|
||||
if (!entry.getValue().equals(newValue)) {
|
||||
dataFile.getConfig().set(entry.getKey(), newValue);
|
||||
}
|
||||
toSave.remove(entry.getKey());
|
||||
} else {
|
||||
dataFile.getConfig().set(entry.getKey(), null);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : toSave.entrySet()) {
|
||||
dataFile.getConfig().set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
dataFile.saveConfig();
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeBackup() {
|
||||
File data = new File(instance.getDataFolder(), "data.yml");
|
||||
File dataClone = new File(instance.getDataFolder(), "data-backup-" + System.currentTimeMillis() + ".yml");
|
||||
try {
|
||||
copyFile(data, dataClone);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Deque<File> backups = new ArrayDeque<>();
|
||||
for (File file : Objects.requireNonNull(instance.getDataFolder().listFiles())) {
|
||||
if (file.getName().toLowerCase().contains("data-backup")) {
|
||||
backups.add(file);
|
||||
}
|
||||
}
|
||||
if (backups.size() > 3) {
|
||||
backups.getFirst().delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection() {
|
||||
dataFile.saveConfig();
|
||||
}
|
||||
|
||||
private static void copyFile(File source, File dest) throws IOException {
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
is = new FileInputStream(source);
|
||||
os = new FileOutputStream(dest);
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = is.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
} finally {
|
||||
is.close();
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,30 @@
|
||||
#General Messages
|
||||
|
||||
general.nametag.prefix = "&7[&6EpicHeads&7]"
|
||||
general.nametag.prefix = "&7[&6EpicHeads&7]"
|
||||
general.word.page = "Page"
|
||||
general.word.query = "Query"
|
||||
general.word.favorites = "Favorites"
|
||||
|
||||
general.search.global = "&6Enter your search query."
|
||||
general.search.refine = "&6Enter a search term to refine your search.")
|
||||
general.search.nonefound = "&cNo heads founds.."
|
||||
|
||||
general.head.staffpicked = "&8Staff Favorite";
|
||||
general.head.id = "&8ID: &7%id%"
|
||||
general.head.cost = "&8Cost: &7$%cost%";
|
||||
|
||||
gui.heads.refine = "&9Refine Search"
|
||||
gui.heads.search = "&9Create Search"
|
||||
gui.heads.categories = "&cBack To Categories"
|
||||
|
||||
gui.overview.title = "EpicHeads (%count% heads)"
|
||||
gui.overview.search = "&a&lSearch"
|
||||
gui.overview.headname = "&c&l%name%"
|
||||
gui.overview.headlore = "&e%count% heads"
|
||||
gui.overview.viewfavorites = "&6&lView Favorites"
|
||||
gui.overview.favoriteslore = "&8Shift click any head|&8to save as a favorite."
|
||||
gui.overview.discord = "&9&lDiscord"
|
||||
gui.overview.discordlore = "&8Add or request new heads|&8in our discord server."
|
||||
|
||||
event.general.nopermission = "&cYou do not have permission to use that command."
|
||||
event.buyhead.cannotafford = "&cYou cannot afford this head."
|
@ -3,10 +3,11 @@ description: EpicHeads
|
||||
main: com.songoda.epicheads.EpicHeads
|
||||
version: maven-version-number
|
||||
author: Songoda
|
||||
softdepend: [Vault]
|
||||
api-version: 1.13
|
||||
commands:
|
||||
EpicHeads:
|
||||
description: View information on this plugin.
|
||||
default: true
|
||||
aliases: [ehe, heads, head]
|
||||
aliases: [ehe, heads]
|
||||
usage: /epicheads
|
Loading…
Reference in New Issue
Block a user