The rest of the features.

This commit is contained in:
Brianna 2019-05-05 20:21:51 -04:00
parent 3067532b6f
commit 996e17eb96
21 changed files with 1416 additions and 45 deletions

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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.";
}
}

View File

@ -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.";
}
}

View File

@ -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.";
}
}

View File

@ -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.";
}
}

View File

@ -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()))

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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());
}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -1,3 +1,30 @@
#General Messages
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."

View File

@ -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