3
0
Mirror von https://github.com/TheSilentPro/HeadDB.git synchronisiert 2024-12-27 11:20:05 +01:00

Merge pull request #27 from TheSilentPro/v4

4.0.0
Dieser Commit ist enthalten in:
Silent 2022-02-27 00:08:06 +01:00 committet von GitHub
Commit 956d6a082b
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
30 geänderte Dateien mit 667 neuen und 318 gelöschten Zeilen

16
pom.xml
Datei anzeigen

@ -6,7 +6,7 @@
<groupId>tsp.headdb</groupId>
<artifactId>HeadDB</artifactId>
<version>3.3.0</version>
<version>4.0.0</version>
<packaging>jar</packaging>
<name>HeadDB</name>
@ -63,6 +63,13 @@
<version>1.7</version>
<scope>provided</scope>
</dependency>
<!-- Treasury -->
<dependency>
<groupId>me.lokka30</groupId>
<artifactId>treasury-api</artifactId>
<version>1.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@ -106,6 +113,13 @@
</execution>
</executions>
</plugin>
<!-- Javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>

Datei anzeigen

@ -3,9 +3,10 @@ package tsp.headdb;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.command.CommandHeadDB;
import tsp.headdb.database.DatabaseUpdateTask;
import tsp.headdb.economy.HEconomyProvider;
import tsp.headdb.command.HeadDBCommand;
import tsp.headdb.economy.TreasuryProvider;
import tsp.headdb.implementation.DatabaseUpdateTask;
import tsp.headdb.economy.BasicEconomyProvider;
import tsp.headdb.economy.VaultProvider;
import tsp.headdb.listener.JoinListener;
import tsp.headdb.listener.MenuListener;
@ -19,10 +20,13 @@ import tsp.headdb.util.Utils;
import javax.annotation.Nullable;
import java.io.File;
/**
* Main class of HeadDB
*/
public class HeadDB extends JavaPlugin {
private static HeadDB instance;
private HEconomyProvider economyProvider;
private BasicEconomyProvider economyProvider;
private PlayerDataFile playerData;
private Localization localization;
@ -42,11 +46,14 @@ public class HeadDB extends JavaPlugin {
if (rawProvider.equalsIgnoreCase("vault")) {
economyProvider = new VaultProvider();
economyProvider.initProvider();
} else if (rawProvider.equalsIgnoreCase("treasury")) {
economyProvider = new TreasuryProvider();
economyProvider.initProvider();
}
}
long refresh = getConfig().getLong("refresh") * 20;
HeadAPI.getDatabase().updateAsync(heads -> Log.info("Fetched " + HeadAPI.getHeads().size() + " heads!")); // Update database on startup
HeadAPI.getDatabase().update(heads -> Log.info("Fetched " + HeadAPI.getHeads().size() + " heads!")); // Update database on startup
Bukkit.getScheduler().runTaskTimerAsynchronously(this, new DatabaseUpdateTask(), refresh, refresh); // Update database on set interval (also saves data)
HeadAPI.getDatabase().setRefresh(refresh);
@ -54,7 +61,7 @@ public class HeadDB extends JavaPlugin {
new MenuListener(this);
new PagedPaneListener(this);
getCommand("headdb").setExecutor(new CommandHeadDB());
getCommand("headdb").setExecutor(new HeadDBCommand());
Log.debug("Starting metrics...");
new Metrics(this, 9152);
@ -83,7 +90,7 @@ public class HeadDB extends JavaPlugin {
}
@Nullable
public HEconomyProvider getEconomyProvider() {
public BasicEconomyProvider getEconomyProvider() {
return economyProvider;
}

Datei anzeigen

@ -1,11 +1,14 @@
package tsp.headdb.api;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import tsp.headdb.HeadDB;
import tsp.headdb.database.Category;
import tsp.headdb.database.HeadDatabase;
import tsp.headdb.implementation.Category;
import tsp.headdb.implementation.Head;
import tsp.headdb.implementation.HeadDatabase;
import tsp.headdb.implementation.LocalHead;
import tsp.headdb.inventory.InventoryUtils;
import tsp.headdb.storage.PlayerDataFile;
@ -14,6 +17,7 @@ import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* This class provides simple methods
@ -21,7 +25,6 @@ import java.util.UUID;
*
* @author TheSilentPro
*/
// TODO: Possibly change to singleton class
public final class HeadAPI {
private HeadAPI() {}
@ -45,7 +48,9 @@ public final class HeadAPI {
*
* @param player Target player
*/
public static void openDatabase(Player player) {
public static void openDatabase(@Nonnull Player player) {
Validate.notNull(player, "Player can not be null!");
InventoryUtils.openDatabase(player);
}
@ -55,7 +60,10 @@ public final class HeadAPI {
* @param player Target player
* @param category Category to open
*/
public static void openCategoryDatabase(Player player, Category category) {
public static void openCategoryDatabase(@Nonnull Player player, @Nonnull Category category) {
Validate.notNull(player, "Player can not be null!");
Validate.notNull(category, "Category can not be null!");
InventoryUtils.openCategoryDatabase(player, category);
}
@ -65,7 +73,10 @@ public final class HeadAPI {
* @param player Target player
* @param search Search term
*/
public static void openSearchDatabase(Player player, String search) {
public static void openSearchDatabase(@Nonnull Player player, @Nonnull String search) {
Validate.notNull(player, "Player can not be null!");
Validate.notNull(search, "Search can not be null!");
InventoryUtils.openSearchDatabase(player, search);
}
@ -75,7 +86,10 @@ public final class HeadAPI {
* @param player Target player
* @param tag Tag search term
*/
public static void openTagSearchDatabase(Player player, String tag) {
public static void openTagSearchDatabase(@Nonnull Player player, @Nonnull String tag) {
Validate.notNull(player, "Player can not be null!");
Validate.notNull(tag, "Tag can not be null!");
InventoryUtils.openTagSearchDatabase(player, tag);
}
@ -97,7 +111,9 @@ public final class HeadAPI {
* @return The head
*/
@Nullable
public static Head getHeadByUniqueId(UUID uuid) {
public static Head getHeadByUniqueId(@Nonnull UUID uuid) {
Validate.notNull(uuid, "UUID can not be null!");
return database.getHeadByUniqueId(uuid);
}
@ -108,7 +124,9 @@ public final class HeadAPI {
* @return List of heads
*/
@Nonnull
public static List<Head> getHeadsByTag(String tag) {
public static List<Head> getHeadsByTag(@Nonnull String tag) {
Validate.notNull(tag, "Tag can not be null!");
return database.getHeadsByTag(tag);
}
@ -119,7 +137,9 @@ public final class HeadAPI {
* @return List of heads
*/
@Nonnull
public static List<Head> getHeadsByName(String name) {
public static List<Head> getHeadsByName(@Nonnull String name) {
Validate.notNull(name, "Name can not be null!");
return database.getHeadsByName(name);
}
@ -131,7 +151,10 @@ public final class HeadAPI {
* @return List of heads
*/
@Nonnull
public static List<Head> getHeadsByName(Category category, String name) {
public static List<Head> getHeadsByName(@Nonnull Category category, @Nonnull String name) {
Validate.notNull(category, "Category can not be null!");
Validate.notNull(name, "Name can not be null!");
return database.getHeadsByName(category, name);
}
@ -142,7 +165,9 @@ public final class HeadAPI {
* @return The head
*/
@Nullable
public static Head getHeadByValue(String value) {
public static Head getHeadByValue(@Nonnull String value) {
Validate.notNull(value, "Value can not be null!");
return database.getHeadByValue(value);
}
@ -153,7 +178,9 @@ public final class HeadAPI {
* @return List of heads
*/
@Nonnull
public static List<Head> getHeads(Category category) {
public static List<Head> getHeads(@Nonnull Category category) {
Validate.notNull(category, "Category can not be null!");
return database.getHeads(category);
}
@ -173,7 +200,10 @@ public final class HeadAPI {
* @param uuid The player's unique id
* @param textureValue The head's texture value
*/
public static void addFavoriteHead(UUID uuid, String textureValue) {
public static void addFavoriteHead(@Nonnull UUID uuid, @Nonnull String textureValue) {
Validate.notNull(uuid, "UUID can not be null!");
Validate.notNull(textureValue, "Value can not be null!");
HeadDB.getInstance().getPlayerData().modifyFavorite(uuid, textureValue, PlayerDataFile.ModificationType.SET);
}
@ -183,7 +213,10 @@ public final class HeadAPI {
* @param uuid The player's unique id
* @param textureValue The head's texture value
*/
public static void removeFavoriteHead(UUID uuid, String textureValue) {
public static void removeFavoriteHead(@Nonnull UUID uuid, @Nonnull String textureValue) {
Validate.notNull(uuid, "UUID can not be null!");
Validate.notNull(textureValue, "Value can not be null!");
HeadDB.getInstance().getPlayerData().modifyFavorite(uuid, textureValue, PlayerDataFile.ModificationType.REMOVE);
}
@ -194,15 +227,13 @@ public final class HeadAPI {
* @return List of favorite {@link Head}'s for the player
*/
@Nonnull
public static List<Head> getFavoriteHeads(UUID uuid) {
List<Head> result = new ArrayList<>();
public static List<Head> getFavoriteHeads(@Nonnull UUID uuid) {
Validate.notNull(uuid, "UUID can not be null!");
List<String> textures = HeadDB.getInstance().getPlayerData().getFavoriteHeadsByTexture(uuid);
for (String texture : textures) {
result.add(HeadAPI.getHeadByValue(texture));
}
return result;
return HeadDB.getInstance().getPlayerData().getFavoriteHeadsByTexture(uuid).stream()
.map(HeadAPI::getHeadByValue)
.filter(head -> head != null)
.collect(Collectors.toList());
}
/**
@ -214,13 +245,10 @@ public final class HeadAPI {
*/
@Nonnull
public static List<LocalHead> getLocalHeads() {
List<LocalHead> result = new ArrayList<>();
for (String entry : HeadDB.getInstance().getPlayerData().getEntries()) {
OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(entry));
result.add(new LocalHead(player.getUniqueId()).name(player.getName()));
}
return result;
return HeadDB.getInstance().getPlayerData().getEntries().stream()
.map(entry -> Bukkit.getOfflinePlayer(UUID.fromString(entry)))
.map(player -> new LocalHead(player.getUniqueId()).name(player.getName()))
.collect(Collectors.toList());
}
}

Datei anzeigen

@ -1,16 +1,16 @@
package tsp.headdb.event;
package tsp.headdb.api.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import tsp.headdb.api.Head;
import tsp.headdb.database.Category;
import tsp.headdb.database.HeadDatabase;
import tsp.headdb.implementation.Head;
import tsp.headdb.implementation.Category;
import tsp.headdb.implementation.HeadDatabase;
import java.util.List;
import java.util.Map;
/**
* This event is called AFTER a {@link tsp.headdb.database.HeadDatabase} updates.
* This event is called AFTER a {@link HeadDatabase} updates.
* The event is called asynchronously and can not be cancelled.
*
* @author TheSilentPro

Datei anzeigen

@ -1,10 +1,10 @@
package tsp.headdb.event;
package tsp.headdb.api.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import tsp.headdb.api.Head;
import tsp.headdb.implementation.Head;
/**
* This event is called when a player purchases a {@link Head}
@ -21,6 +21,7 @@ public class PlayerHeadPurchaseEvent extends Event implements Cancellable {
private double cost;
public PlayerHeadPurchaseEvent(Player player, Head head, double cost) {
super(true);
this.player = player;
this.head = head;
this.cost = cost;

Datei anzeigen

@ -1,175 +0,0 @@
package tsp.headdb.command;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import tsp.headdb.HeadDB;
import tsp.headdb.api.Head;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Localization;
import tsp.headdb.util.Utils;
import java.util.concurrent.TimeUnit;
// TODO: Cleaner way to handle this command
public class CommandHeadDB implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
Localization localization = HeadDB.getInstance().getLocalization();
if (args.length == 0) {
if (!sender.hasPermission("headdb.open")) {
Utils.sendMessage(sender, localization.getMessage("noPermission"));
return true;
}
if (!(sender instanceof Player)) {
Utils.sendMessage(sender, localization.getMessage("onlyPlayers"));
return true;
}
Player player = (Player) sender;
Utils.sendMessage(player, localization.getMessage("databaseOpen"));
HeadAPI.openDatabase(player);
return true;
}
String sub = args[0];
if (sub.equalsIgnoreCase("info") || sub.equalsIgnoreCase("i")) {
// These should not be customizable
Utils.sendMessage(sender, "&7Running &cHeadDB - " + HeadDB.getInstance().getDescription().getVersion());
Utils.sendMessage(sender, "&7Created by &c" + HeadDB.getInstance().getDescription().getAuthors());
Utils.sendMessage(sender, "&7There are currently &c" + HeadAPI.getHeads().size() + " &7heads in the database.");
return true;
}
if (sub.equalsIgnoreCase("reload") || sub.equalsIgnoreCase("r")) {
if (!sender.hasPermission("headdb.reload")) {
Utils.sendMessage(sender, localization.getMessage("noPermission"));
return true;
}
HeadDB.getInstance().getLocalization().load();
Utils.sendMessage(sender, localization.getMessage("reloadMessages"));
return true;
}
if (sub.equalsIgnoreCase("search") || sub.equalsIgnoreCase("s")) {
if (!sender.hasPermission("headdb.search")) {
Utils.sendMessage(sender, localization.getMessage("noPermission"));
return true;
}
if (!(sender instanceof Player)) {
Utils.sendMessage(sender, localization.getMessage("onlyPlayers"));
return true;
}
Player player = (Player) sender;
if (args.length < 2) {
Utils.sendMessage(player, "&c/hdb search <name>");
return true;
}
StringBuilder builder = new StringBuilder();
for (int i = 1; i < args.length; i++) {
builder.append(args[i]);
if (i != args.length - 1) {
builder.append(" ");
}
}
String name = builder.toString();
Utils.sendMessage(sender, "&7Searching for &e" + name);
HeadAPI.openSearchDatabase(player, name);
return true;
}
if (sub.equalsIgnoreCase("tagsearch") || sub.equalsIgnoreCase("ts")) {
if (!sender.hasPermission("headdb.tagsearch")) {
Utils.sendMessage(sender, localization.getMessage("noPermission"));
return true;
}
if (args.length < 2) {
Utils.sendMessage(sender, "&c/hdb tagsearch <tag>");
return true;
}
if (!(sender instanceof Player)) {
Utils.sendMessage(sender, localization.getMessage("onlyPlayers"));
return true;
}
Player player = (Player) sender;
String tag = args[1];
Utils.sendMessage(sender, "&7Searching for heads with tag &e" + tag);
HeadAPI.openTagSearchDatabase(player, tag);
return true;
}
if (sub.equalsIgnoreCase("give") || sub.equalsIgnoreCase("g")) {
if (!sender.hasPermission("headdb.give")) {
Utils.sendMessage(sender, localization.getMessage("noPermission"));
return true;
}
if (args.length < 3) {
Utils.sendMessage(sender, "&c/hdb give <id> <player> &6[amount]");
return true;
}
try {
int id = Integer.parseInt(args[1]);
Player target = Bukkit.getPlayer(args[2]);
if (target == null) {
Utils.sendMessage(sender, localization.getMessage("invalidPlayer"));
return true;
}
int amount = 1;
if (args.length > 3) {
amount = Integer.parseInt(args[3]);
}
Head head = HeadAPI.getHeadByID(id);
if (head == null) {
Utils.sendMessage(sender, "&cCould not find head with id &e" + id);
return true;
}
ItemStack item = head.getMenuItem();
item.setAmount(amount);
target.getInventory().addItem(item);
Utils.sendMessage(sender, "&7Gave &6" + target.getName() + " &ex" + amount + " " + head.getName());
return true;
} catch (NumberFormatException nfe) {
Utils.sendMessage(sender, "&cID/Amount must be a number!");
return true;
}
}
if (sub.equalsIgnoreCase("update") || sub.equalsIgnoreCase("u")) {
if (!sender.hasPermission("headdb.update")) {
Utils.sendMessage(sender, "&cNo permission!");
return true;
}
Utils.sendMessage(sender, "&7Updating...");
long start = System.currentTimeMillis();
HeadAPI.getDatabase().updateAsync(heads -> {
Utils.sendMessage(sender, "&7Done! Took: &a" + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - start) + " &7seconds");
Utils.sendMessage(sender, "&7There are &a" + HeadAPI.getHeads().size() + " &7heads in the database!");
});
return true;
}
Utils.sendMessage(sender, " ");
Utils.sendMessage(sender, "&c&lHeadDB &c- &5Commands");
Utils.sendMessage(sender, "&7&oParameters:&c command &9(aliases)&c arguments... &7- Description");
Utils.sendMessage(sender, "&7 > &c/hdb &7- Opens the database");
Utils.sendMessage(sender, "&7 > &c/hdb info &9(i) &7- Plugin Information");
Utils.sendMessage(sender, "&7 > &c/hdb reload &9(r) &7- Reloads the messages file");
Utils.sendMessage(sender, "&7 > &c/hdb search &9(s) &c<name> &7- Search for heads matching a name");
Utils.sendMessage(sender, "&7 > &c/hdb tagsearch &9(ts) &c<tag> &7- Search for heads matching a tag");
Utils.sendMessage(sender, "&7 > &c/hdb update &9(u) &7- Forcefully update the database");
Utils.sendMessage(sender, "&7 > &c/hdb give &9(g) &c<id> <player> &6[amount] &7- Give player a head");
Utils.sendMessage(sender, " ");
return true;
}
}

Datei anzeigen

@ -0,0 +1,53 @@
package tsp.headdb.command;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.implementation.Head;
import tsp.headdb.util.Utils;
public class GiveCommand implements HeadSubCommand {
@Override
public void handle(CommandSender sender, String[] args) {
if (!sender.hasPermission("headdb.give")) {
Utils.sendMessage(sender, getLocalization().getMessage("noPermission"));
return;
}
if (args.length < 3) {
Utils.sendMessage(sender, "&c/hdb give <id> <player> &6[amount]");
return;
}
try {
int id = Integer.parseInt(args[1]);
Player target = Bukkit.getPlayer(args[2]);
if (target == null) {
Utils.sendMessage(sender, getLocalization().getMessage("invalidPlayer"));
return;
}
int amount = 1;
if (args.length > 3) {
amount = Integer.parseInt(args[3]);
}
Head head = HeadAPI.getHeadByID(id);
if (head == null) {
Utils.sendMessage(sender, "&cCould not find head with id &e" + id);
return;
}
ItemStack item = head.getMenuItem();
item.setAmount(amount);
target.getInventory().addItem(item);
Utils.sendMessage(sender, "&7Gave &6" + target.getName() + " &ex" + amount + " " + head.getName());
} catch (NumberFormatException nfe) {
Utils.sendMessage(sender, "&cID/Amount must be a number!");
}
}
}

Datei anzeigen

@ -0,0 +1,68 @@
package tsp.headdb.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tsp.headdb.HeadDB;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Localization;
import tsp.headdb.util.Utils;
public class HeadDBCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
Localization localization = HeadDB.getInstance().getLocalization();
if (args.length == 0) {
if (!sender.hasPermission("headdb.open")) {
Utils.sendMessage(sender, localization.getMessage("noPermission"));
return true;
}
if (!(sender instanceof Player)) {
Utils.sendMessage(sender, localization.getMessage("onlyPlayers"));
return true;
}
Player player = (Player) sender;
Utils.sendMessage(player, localization.getMessage("databaseOpen"));
HeadAPI.openDatabase(player);
return true;
}
String sub = args[0];
HeadSubCommand subCommand = null;
if (sub.equalsIgnoreCase("info") || sub.equalsIgnoreCase("i")) {
subCommand = new InfoCommand();
} else if (sub.equalsIgnoreCase("reload") || sub.equalsIgnoreCase("r")) {
subCommand = new ReloadCommand();
} else if (sub.equalsIgnoreCase("search") || sub.equalsIgnoreCase("s")) {
subCommand = new SearchCommand();
} else if (sub.equalsIgnoreCase("tagsearch") || sub.equalsIgnoreCase("ts")) {
subCommand = new TagSearchCommand();
} else if (sub.equalsIgnoreCase("give") || sub.equalsIgnoreCase("g")) {
subCommand = new GiveCommand();
} else if (sub.equalsIgnoreCase("update") || sub.equalsIgnoreCase("u")) {
subCommand = new UpdateCommand();
}
if (subCommand != null) {
subCommand.handle(sender, args);
return true;
}
Utils.sendMessage(sender, " ");
Utils.sendMessage(sender, "&c&lHeadDB &c- &5Commands");
Utils.sendMessage(sender, "&7&oParameters:&c command &9(aliases)&c arguments... &7- Description");
Utils.sendMessage(sender, "&7 > &c/hdb &7- Opens the database");
Utils.sendMessage(sender, "&7 > &c/hdb info &9(i) &7- Plugin Information");
Utils.sendMessage(sender, "&7 > &c/hdb reload &9(r) &7- Reloads the messages file");
Utils.sendMessage(sender, "&7 > &c/hdb search &9(s) &c<name> &7- Search for heads matching a name");
Utils.sendMessage(sender, "&7 > &c/hdb tagsearch &9(ts) &c<tag> &7- Search for heads matching a tag");
Utils.sendMessage(sender, "&7 > &c/hdb update &9(u) &7- Forcefully update the database");
Utils.sendMessage(sender, "&7 > &c/hdb give &9(g) &c<id> <player> &6[amount] &7- Give player a head");
Utils.sendMessage(sender, " ");
return true;
}
}

Datei anzeigen

@ -0,0 +1,21 @@
package tsp.headdb.command;
import org.bukkit.command.CommandSender;
import tsp.headdb.HeadDB;
import tsp.headdb.util.Localization;
/**
* An interface for a HeadDB sub-command
*
* @author TheSilentPro
* @since 4.0.0
*/
public interface HeadSubCommand {
void handle(CommandSender sender, String[] args);
default Localization getLocalization() {
return HeadDB.getInstance().getLocalization();
}
}

Datei anzeigen

@ -0,0 +1,18 @@
package tsp.headdb.command;
import org.bukkit.command.CommandSender;
import tsp.headdb.HeadDB;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Utils;
public class InfoCommand implements HeadSubCommand {
@Override
public void handle(CommandSender sender, String[] args) {
// These should not be customizable
Utils.sendMessage(sender, "&7Running &cHeadDB - " + HeadDB.getInstance().getDescription().getVersion());
Utils.sendMessage(sender, "&7Created by &c" + HeadDB.getInstance().getDescription().getAuthors());
Utils.sendMessage(sender, "&7There are currently &c" + HeadAPI.getHeads().size() + " &7heads in the database.");
}
}

Datei anzeigen

@ -0,0 +1,20 @@
package tsp.headdb.command;
import org.bukkit.command.CommandSender;
import tsp.headdb.HeadDB;
import tsp.headdb.util.Utils;
public class ReloadCommand implements HeadSubCommand {
@Override
public void handle(CommandSender sender, String[] args) {
if (!sender.hasPermission("headdb.reload")) {
Utils.sendMessage(sender, getLocalization().getMessage("noPermission"));
return;
}
HeadDB.getInstance().getLocalization().load();
Utils.sendMessage(sender, getLocalization().getMessage("reloadMessages"));
}
}

Datei anzeigen

@ -0,0 +1,40 @@
package tsp.headdb.command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Utils;
public class SearchCommand implements HeadSubCommand {
@Override
public void handle(CommandSender sender, String[] args) {
if (!sender.hasPermission("headdb.search")) {
Utils.sendMessage(sender, getLocalization().getMessage("noPermission"));
return;
}
if (!(sender instanceof Player)) {
Utils.sendMessage(sender, getLocalization().getMessage("onlyPlayers"));
return;
}
Player player = (Player) sender;
if (args.length < 2) {
Utils.sendMessage(player, "&c/hdb search <name>");
return;
}
StringBuilder builder = new StringBuilder();
for (int i = 1; i < args.length; i++) {
builder.append(args[i]);
if (i != args.length - 1) {
builder.append(" ");
}
}
String name = builder.toString();
Utils.sendMessage(sender, "&7Searching for &e" + name);
HeadAPI.openSearchDatabase(player, name);
return;
}
}

Datei anzeigen

@ -0,0 +1,34 @@
package tsp.headdb.command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Utils;
public class TagSearchCommand implements HeadSubCommand {
@Override
public void handle(CommandSender sender, String[] args) {
if (!sender.hasPermission("headdb.tagsearch")) {
Utils.sendMessage(sender, getLocalization().getMessage("noPermission"));
return;
}
if (args.length < 2) {
Utils.sendMessage(sender, "&c/hdb tagsearch <tag>");
return;
}
if (!(sender instanceof Player)) {
Utils.sendMessage(sender, getLocalization().getMessage("onlyPlayers"));
return;
}
Player player = (Player) sender;
String tag = args[1];
Utils.sendMessage(sender, "&7Searching for heads with tag &e" + tag);
HeadAPI.openTagSearchDatabase(player, tag);
return;
}
}

Datei anzeigen

@ -0,0 +1,26 @@
package tsp.headdb.command;
import org.bukkit.command.CommandSender;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Utils;
import java.util.concurrent.TimeUnit;
public class UpdateCommand implements HeadSubCommand {
@Override
public void handle(CommandSender sender, String[] args) {
if (!sender.hasPermission("headdb.update")) {
Utils.sendMessage(sender, getLocalization().getMessage("noPermission"));
return;
}
Utils.sendMessage(sender, "&7Updating...");
long start = System.currentTimeMillis();
HeadAPI.getDatabase().update(heads -> {
Utils.sendMessage(sender, "&7Done! Took: &a" + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - start) + " &7seconds");
Utils.sendMessage(sender, "&7There are &a" + HeadAPI.getHeads().size() + " &7heads in the database!");
});
}
}

Datei anzeigen

@ -0,0 +1,48 @@
package tsp.headdb.economy;
import org.bukkit.entity.Player;
import java.math.BigDecimal;
import java.util.function.Consumer;
/**
* An interface for generalizing Economy Provider's
*
* @author TheSilentPro
* @since 4.0.0
* @see VaultProvider
* @see TreasuryProvider
*/
public interface BasicEconomyProvider {
/**
* Retrieve if the player can purchase a head using this economy provider
*
* @param player The player
* @param cost The cost
* @param result If the player has enough to purchase
*/
default void canPurchase(Player player, BigDecimal cost, Consumer<Boolean> result) {
result.accept(true);
}
/**
* Charge the player a specific amount using this economy provider
*
* @param player The player
* @param amount The amount
* @param result If the transaction was successful
*/
default void charge(Player player, BigDecimal amount, Consumer<Boolean> result) {
result.accept(true);
}
/**
* Convenience method for initializing economy
*
* @see VaultProvider#initProvider()
* @see TreasuryProvider#initProvider()
*/
void initProvider();
}

Datei anzeigen

@ -1,21 +0,0 @@
package tsp.headdb.economy;
import org.bukkit.entity.Player;
import java.math.BigDecimal;
public interface HEconomyProvider {
default boolean canPurchase(Player player, BigDecimal cost) {
return true;
}
default void charge(Player player, BigDecimal amount) {
}
default void initProvider() {
}
}

Datei anzeigen

@ -0,0 +1,101 @@
package tsp.headdb.economy;
import me.lokka30.treasury.api.common.service.Service;
import me.lokka30.treasury.api.common.service.ServiceRegistry;
import me.lokka30.treasury.api.economy.EconomyProvider;
import me.lokka30.treasury.api.economy.account.PlayerAccount;
import me.lokka30.treasury.api.economy.currency.Currency;
import me.lokka30.treasury.api.economy.response.EconomyException;
import me.lokka30.treasury.api.economy.response.EconomySubscriber;
import me.lokka30.treasury.api.economy.transaction.EconomyTransactionInitiator;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import tsp.headdb.HeadDB;
import tsp.headdb.util.Log;
import java.math.BigDecimal;
import java.util.Optional;
import java.util.function.Consumer;
/**
* A {@link BasicEconomyProvider} for Treasury
*
* @author TheSilentPro
* @since 4.0.0
*/
public class TreasuryProvider implements BasicEconomyProvider {
private EconomyProvider provider;
private EconomyTransactionInitiator<?> transactionInitiator;
private Currency currency;
@Override
public void canPurchase(Player player, BigDecimal cost, Consumer<Boolean> result) {
EconomySubscriber
.<Boolean>asFuture(s -> provider.hasPlayerAccount(player.getUniqueId(), s))
.thenCompose(val -> {
if (val) {
return EconomySubscriber.<PlayerAccount>asFuture(s -> provider.retrievePlayerAccount(player.getUniqueId(), s));
} else {
return EconomySubscriber.<PlayerAccount>asFuture(s -> provider.createPlayerAccount(player.getUniqueId(), s));
}
})
.thenCompose(account -> EconomySubscriber.<BigDecimal>asFuture(s -> account.retrieveBalance(currency, s)))
.whenComplete((bal, ex) -> {
result.accept(bal.compareTo(cost) >= 0);
});
}
@Override
public void charge(Player player, BigDecimal amount, Consumer<Boolean> result) {
EconomySubscriber
.<Boolean>asFuture(s -> provider.hasPlayerAccount(player.getUniqueId(), s))
.thenCompose(val -> {
if (val) {
return EconomySubscriber.<PlayerAccount>asFuture(s -> provider.retrievePlayerAccount(player.getUniqueId(), s));
} else {
return EconomySubscriber.<PlayerAccount>asFuture(s -> provider.createPlayerAccount(player.getUniqueId(), s));
}
}).whenComplete((account, ex) -> {
account.withdrawBalance(
amount,
transactionInitiator,
currency,
new EconomySubscriber<BigDecimal>() {
@Override
public void succeed(@NotNull BigDecimal bigDecimal) {
result.accept(true);
}
@Override
public void fail(@NotNull EconomyException exception) {
result.accept(false);
exception.printStackTrace();
}
});
});
}
@Override
public void initProvider() {
Optional<Service<EconomyProvider>> service = ServiceRegistry.INSTANCE.serviceFor(EconomyProvider.class);
if(!service.isPresent()) {
Log.error("Unable to find a supported economy plugin for Treasury!");
return;
}
provider = service.get().get();
transactionInitiator = EconomyTransactionInitiator.createInitiator(EconomyTransactionInitiator.Type.PLUGIN, "HeadDB");
String rawCurrency = HeadDB.getInstance().getConfig().getString("economy.currency");
if (rawCurrency == null || rawCurrency.isEmpty()) {
currency = provider.getPrimaryCurrency();
} else {
currency = provider.getCurrencies().stream()
.filter(currency -> currency.getIdentifier().equalsIgnoreCase(rawCurrency))
.findFirst().get();
}
}
}

Datei anzeigen

@ -5,21 +5,29 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import tsp.headdb.util.Log;
import tsp.headdb.util.Utils;
import java.math.BigDecimal;
import java.util.function.Consumer;
public class VaultProvider implements HEconomyProvider {
/**
* A {@link BasicEconomyProvider} for Vault
*
* @author TheSilentPro
* @since 4.0.0
*/
public class VaultProvider implements BasicEconomyProvider {
private Economy economy;
@Override
public boolean canPurchase(Player player, BigDecimal cost) {
return economy.has(player, cost.doubleValue());
public void canPurchase(Player player, BigDecimal cost, Consumer<Boolean> result) {
Utils.async(t -> result.accept(economy.has(player, cost.doubleValue())));
}
@Override
public void charge(Player player, BigDecimal amount) {
economy.withdrawPlayer(player, amount.doubleValue());
public void charge(Player player, BigDecimal amount, Consumer<Boolean> result) {
Utils.async(t -> result.accept(economy.withdrawPlayer(player, amount.doubleValue()).transactionSuccess()));
}
public void initProvider() {

Datei anzeigen

@ -1,13 +1,18 @@
package tsp.headdb.database;
package tsp.headdb.implementation;
import org.bukkit.ChatColor;
import org.bukkit.inventory.ItemStack;
import tsp.headdb.api.Head;
import tsp.headdb.api.HeadAPI;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a category for heads
*
* @author TheSilentPro
*/
public enum Category {
ALPHABET("alphabet", ChatColor.YELLOW, 20),
@ -45,15 +50,24 @@ public enum Category {
return location;
}
/**
* Retrieve the first valid head from a category
*
* @return First valid head
*/
public ItemStack getItem() {
if (item.containsKey(this)) {
return item.get(this).getMenuItem();
}
item.put(this, HeadAPI.getHeads(this).get(0));
return getItem();
return HeadAPI.getHeads(this).stream()
.filter(head -> head != null)
.findFirst().get().getMenuItem();
}
/**
* Retrieve a {@link Category} by name
*
* @param name The name
* @return The category if it exists. Else it returns null
*/
@Nullable
public static Category getByName(String name) {
for (Category category : cache) {
if (category.getName().equalsIgnoreCase(name)) {

Datei anzeigen

@ -1,15 +1,18 @@
package tsp.headdb.database;
package tsp.headdb.implementation;
import tsp.headdb.HeadDB;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.util.Log;
/**
* Task that updates the database on an interval
*/
public class DatabaseUpdateTask implements Runnable {
@Override
public void run() {
HeadDB.getInstance().getPlayerData().save();
HeadAPI.getDatabase().updateAsync(heads -> Log.info("Fetched " + HeadAPI.getHeads().size() + " heads!"));
HeadAPI.getDatabase().update(heads -> Log.info("Fetched " + HeadAPI.getHeads().size() + " heads!"));
}
}

Datei anzeigen

@ -1,14 +1,11 @@
package tsp.headdb.api;
package tsp.headdb.implementation;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import tsp.headdb.HeadDB;
import tsp.headdb.database.Category;
import tsp.headdb.util.Log;
import tsp.headdb.util.Utils;
@ -17,6 +14,11 @@ import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* Represents a Head that a player can obtain via the database
*
* @author TheSilentPro
*/
public class Head {
private String name;

Datei anzeigen

@ -1,4 +1,4 @@
package tsp.headdb.database;
package tsp.headdb.implementation;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@ -7,8 +7,7 @@ import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import tsp.headdb.api.Head;
import tsp.headdb.event.DatabaseUpdateEvent;
import tsp.headdb.api.event.DatabaseUpdateEvent;
import tsp.headdb.util.Log;
import tsp.headdb.util.Utils;
@ -18,6 +17,7 @@ import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -117,7 +117,7 @@ public class HeadDatabase {
}
public List<Head> getHeads(Category category) {
return HEADS.get(category);
return Collections.unmodifiableList(HEADS.get(category));
}
/**
@ -129,7 +129,7 @@ public class HeadDatabase {
public List<Head> getHeads() {
if (HEADS.isEmpty() || isLastUpdateOld()) {
// Technically this should never be reached due to the update task in the main class.
updateAsync(result -> {
update(result -> {
if (result != null) {
for (Category category : result.keySet()) {
HEADS.put(category, result.get(category));
@ -145,7 +145,7 @@ public class HeadDatabase {
return heads;
}
public void getHeadsNoCacheAsync(Consumer<Map<Category, List<Head>>> resultSet) {
public void getHeadsNoCache(Consumer<Map<Category, List<Head>>> resultSet) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, task -> {
Log.debug("[" + plugin.getName() + "] Updating database... ");
Map<Category, List<Head>> result = new HashMap<>();
@ -207,8 +207,8 @@ public class HeadDatabase {
});
}
public void updateAsync(Consumer<Map<Category, List<Head>>> result) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, task -> getHeadsNoCacheAsync(heads -> {
public void update(Consumer<Map<Category, List<Head>>> result) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, task -> getHeadsNoCache(heads -> {
if (heads == null) {
Log.error("[" + plugin.getName() + "] Failed to update database! Check above for any errors.");
result.accept(null);

Datei anzeigen

@ -1,17 +1,21 @@
package tsp.headdb.api;
package tsp.headdb.implementation;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import tsp.headdb.database.Category;
import tsp.headdb.util.Utils;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Represents a local player head that can be obtained via the LocalHeads option
*
* @author TheSilentPro
*/
public class LocalHead extends Head {
private UUID uuid;

Datei anzeigen

@ -9,22 +9,21 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import tsp.headdb.HeadDB;
import tsp.headdb.api.Head;
import tsp.headdb.implementation.Head;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.api.LocalHead;
import tsp.headdb.database.Category;
import tsp.headdb.economy.HEconomyProvider;
import tsp.headdb.event.PlayerHeadPurchaseEvent;
import tsp.headdb.implementation.LocalHead;
import tsp.headdb.implementation.Category;
import tsp.headdb.economy.BasicEconomyProvider;
import tsp.headdb.api.event.PlayerHeadPurchaseEvent;
import tsp.headdb.util.Localization;
import tsp.headdb.util.Utils;
import net.milkbowl.vault.economy.Economy;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* Class for handling the "dirty" work
@ -308,42 +307,53 @@ public class InventoryUtils {
return HeadDB.getInstance().getConfig().getDouble("economy.cost." + category);
}
public static boolean processPayment(Player player, int amount, String category, String description) {
HEconomyProvider economyProvider = HeadDB.getInstance().getEconomyProvider();
public static void processPayment(Player player, int amount, String category, String description, Consumer<Boolean> result) {
Utils.async(task -> {
BasicEconomyProvider economyProvider = HeadDB.getInstance().getEconomyProvider();
// If economy is disabled or no plugin is present, the item is free.
// Don't mention receiving it for free in this case, since it is always free.
if (economyProvider == null) {
Utils.sendMessage(player, String.format(localization.getMessage("noEconomy"), amount, description));
Utils.playSound(player, "noEconomy");
return true;
result.accept(true);
return;
}
BigDecimal cost = BigDecimal.valueOf(getCategoryCost(player, category) * amount);
// If the cost is higher than zero, attempt to charge for it.
if (cost.compareTo(BigDecimal.ZERO) > 0) {
if (economyProvider.canPurchase(player, cost)) {
economyProvider.charge(player, cost);
economyProvider.canPurchase(player, cost, paymentResult -> {
if (paymentResult) {
economyProvider.charge(player, cost, chargeResult -> {
if (chargeResult) {
Utils.sendMessage(player, String.format(localization.getMessage("purchasedHead"), amount, description, cost));
Utils.playSound(player, "paid");
return true;
result.accept(true);
return;
}
});
}
});
Utils.sendMessage(player, String.format(localization.getMessage("notEnoughMoney"), amount, description));
Utils.playSound(player, "unavailable");
return false;
result.accept(false);
return;
}
// Otherwise, the item is free.
Utils.sendMessage(player, String.format(localization.getMessage("free"), amount, description));
Utils.playSound(player, "free");
return true;
result.accept(true);
});
}
public static void purchaseHead(Player player, Head head, int amount, String category, String description) {
if (!processPayment(player, amount, category, description)) return;
Utils.sendMessage(player, String.format(localization.getMessage("processPayment"), amount, head.getName()));
processPayment(player, amount, category, description, result -> {
if (result) {
PlayerHeadPurchaseEvent event = new PlayerHeadPurchaseEvent(player, head, getCategoryCost(player, category));
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
@ -352,6 +362,8 @@ public class InventoryUtils {
player.getInventory().addItem(item);
}
}
});
}
private static String replace(String message, int size, String category, String search, Player player) {
return message

Datei anzeigen

@ -6,6 +6,10 @@ import org.bukkit.event.player.PlayerJoinEvent;
import tsp.headdb.HeadDB;
import tsp.headdb.storage.PlayerDataFile;
/**
* This saves heads from players that join
* Used for local heads option
*/
public class JoinListener implements Listener {
public JoinListener(HeadDB plugin) {

Datei anzeigen

@ -12,10 +12,13 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import tsp.headdb.HeadDB;
import tsp.headdb.api.HeadAPI;
import tsp.headdb.database.Category;
import tsp.headdb.implementation.Category;
import tsp.headdb.inventory.InventoryUtils;
import tsp.headdb.util.Utils;
/**
* This handles all clicks on the main inventory
*/
public class MenuListener implements Listener {
public MenuListener(JavaPlugin plugin) {

Datei anzeigen

@ -19,6 +19,9 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* Manages the data file that stores information
*/
public class PlayerDataFile {
private final File file;

Datei anzeigen

@ -7,6 +7,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
import tsp.headdb.HeadDB;
import java.io.BufferedReader;
@ -17,11 +18,18 @@ import java.net.URLConnection;
import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* Several utilities used by the plugin
*/
public class Utils {
private static final FileConfiguration config = HeadDB.getInstance().getConfig();
public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}");
public static void async(Consumer<BukkitTask> task) {
Bukkit.getScheduler().runTaskAsynchronously(HeadDB.getInstance(), task);
}
/**
* Retrieve the latest release from spigot
*

Datei anzeigen

@ -11,8 +11,12 @@ requireCategoryPermission: false
# Economy options
economy:
enable: false
# Supported: VAULT
# Supported: VAULT, TREASURY
provider: "VAULT"
# Providers like treasury support multiple currencies
# Set the ID of the one used for head purchasing below.
# Leave empty to use the primary currency or if the provider does not support multiple currencies
currency: ''
cost:
alphabet: 100
animals: 100

Datei anzeigen

@ -4,9 +4,10 @@ databaseOpen: "&7Opening &cHead Database"
invalidPlayer: "&cPlayer is not online!"
localFavorites: "&cLocal heads can not be added to favorites!"
noEconomy: "&7You received &e%d &7x &e%s&7!"
purchasedHead: "&7You purchased &e%d &7x &e%s &7for &e%.2f&7!"
notEnoughMoney: "&cYou do not have enough to purchase &e%d &cx &e%s&7."
free: "&7You received &e%d &7x &e%s &7for &efree&7!"
purchasedHead: "&7You purchased &e%dx %s &7for &e%.2f&7!"
proccessPayment: "&7Purchasing &e%dx %s&7. Please wait..."
notEnoughMoney: "&cYou do not have enough to purchase &e%dx %s&c."
free: "&7You received &e%dx %s &7for &efree&7!"
reloadMessages: "&aReloaded messages file!"
menu: