diff --git a/src/main/java/dev/drawethree/xprison/XPrison.java b/src/main/java/dev/drawethree/xprison/XPrison.java new file mode 100644 index 0000000..4692ba1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/XPrison.java @@ -0,0 +1,466 @@ +package dev.drawethree.xprison; + +import com.github.lalyos.jfiglet.FigletFont; +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.impl.MySQLDatabase; +import dev.drawethree.xprison.database.impl.SQLiteDatabase; +import dev.drawethree.xprison.database.model.ConnectionProperties; +import dev.drawethree.xprison.database.model.DatabaseCredentials; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.history.XPrisonHistory; +import dev.drawethree.xprison.mainmenu.MainMenu; +import dev.drawethree.xprison.mainmenu.help.HelpGui; +import dev.drawethree.xprison.migrator.ItemMigrator; +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.multipliers.XPrisonMultipliers; +import dev.drawethree.xprison.nicknames.repo.NicknameRepository; +import dev.drawethree.xprison.nicknames.repo.impl.NicknameRepositoryImpl; +import dev.drawethree.xprison.nicknames.service.NicknameService; +import dev.drawethree.xprison.nicknames.service.impl.NicknameServiceImpl; +import dev.drawethree.xprison.pickaxelevels.XPrisonPickaxeLevels; +import dev.drawethree.xprison.placeholders.XPrisonMVdWPlaceholder; +import dev.drawethree.xprison.placeholders.XPrisonPAPIPlaceholder; +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.ranks.XPrisonRanks; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import me.lucko.helper.Events; +import me.lucko.helper.plugin.ExtendedJavaPlugin; +import net.milkbowl.vault.economy.Economy; +import dev.drawethree.xprison.utils.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.codemc.worldguardwrapper.WorldGuardWrapper; +import org.codemc.worldguardwrapper.flag.WrappedState; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +@Getter +public final class XPrison extends ExtendedJavaPlugin { + + private static XPrison instance; + + private boolean debugMode; + private Map modules; + private SQLDatabase pluginDatabase; + private Economy economy; + private FileManager fileManager; + private XPrisonTokens tokens; + private XPrisonGems gems; + private XPrisonRanks ranks; + private XPrisonPrestiges prestiges; + private XPrisonMultipliers multipliers; + private XPrisonEnchants enchants; + private XPrisonAutoSell autoSell; + private XPrisonAutoMiner autoMiner; + private XPrisonPickaxeLevels pickaxeLevels; + private XPrisonGangs gangs; + private XPrisonMines mines; + private XPrisonHistory history; + + private ItemMigrator itemMigrator; + + private List supportedPickaxes; + + private NicknameService nicknameService; + + + @Override + protected void load() { + instance = this; + registerWGFlag(); + } + + + @Override + protected void enable() { + + this.printOnEnableMessage(); + this.modules = new LinkedHashMap<>(); + this.fileManager = new FileManager(this); + this.fileManager.getConfig("config.yml").copyDefaults(true).save(); + this.debugMode = this.getConfig().getBoolean("debug-mode", false); + + // All you have to do is adding the following two lines in your onEnable method. + // You can find the plugin ids of your plugins on the page https://bstats.org/what-is-my-plugin-id + int pluginId = 10520; // <-- Replace with the id of your plugin! + Metrics metrics = new Metrics(this, pluginId); + + if (!this.initDatabase()) { + this.getServer().getPluginManager().disablePlugin(this); + return; + } + + if (!this.setupEconomy()) { + this.getLogger().warning("Economy provider for Vault not found! Economy provider is strictly required. Disabling plugin..."); + this.getServer().getPluginManager().disablePlugin(this); + return; + } else { + this.getLogger().info("Economy provider for Vault found - " + this.getEconomy().getName()); + } + + this.initVariables(); + this.initModules(); + this.loadModules(); + + this.itemMigrator = new ItemMigrator(this); + this.itemMigrator.reload(); + + this.initNicknameService(); + + this.registerPlaceholders(); + + this.registerMainEvents(); + this.registerMainCommand(); + this.startMetrics(); + + SkullUtils.init(); + } + + private void printOnEnableMessage() { + try { + this.getLogger().info(FigletFont.convertOneLine("X-PRISON")); + this.getLogger().info(this.getDescription().getVersion()); + this.getLogger().info("By: " + this.getDescription().getAuthors()); + this.getLogger().info("Website: " + this.getDescription().getWebsite()); + } catch (IOException ignored) { + } + } + + private void initNicknameService() { + NicknameRepository nicknameRepository = new NicknameRepositoryImpl(this.getPluginDatabase()); + nicknameRepository.createTables(); + this.nicknameService = new NicknameServiceImpl(nicknameRepository); + } + + private void initVariables() { + this.supportedPickaxes = this.getConfig().getStringList("supported-pickaxes").stream().map(CompMaterial::fromString).map(CompMaterial::getMaterial).collect(Collectors.toList()); + + for (Material m : this.supportedPickaxes) { + this.getLogger().info("Added support for pickaxe: " + m); + } + } + + private void loadModules() { + if (this.getConfig().getBoolean("modules.tokens")) { + this.loadModule(tokens); + } + + if (this.getConfig().getBoolean("modules.gems")) { + this.loadModule(gems); + } + + if (this.getConfig().getBoolean("modules.ranks")) { + this.loadModule(ranks); + } + + if (this.getConfig().getBoolean("modules.prestiges")) { + this.loadModule(prestiges); + } + + if (this.getConfig().getBoolean("modules.multipliers")) { + this.loadModule(multipliers); + } + + if (this.getConfig().getBoolean("modules.autosell")) { + if (isUltraBackpacksEnabled()) { + this.getLogger().info("Module AutoSell will not be loaded because selling system is handled by UltraBackpacks."); + } else { + this.loadModule(autoSell); + } + } + + if (this.getConfig().getBoolean("modules.mines")) { + this.loadModule(mines); + } + + if (this.getConfig().getBoolean("modules.enchants")) { + this.loadModule(enchants); + } + if (this.getConfig().getBoolean("modules.autominer")) { + this.loadModule(autoMiner); + } + if (this.getConfig().getBoolean("modules.gangs")) { + this.loadModule(gangs); + } + if (this.getConfig().getBoolean("modules.pickaxe_levels")) { + if (!this.isModuleEnabled("Enchants")) { + this.getLogger().warning(TextUtils.applyColor("&cX-Prison - Module 'Pickaxe Levels' requires to have enchants module enabled.")); + } else { + this.loadModule(pickaxeLevels); + } + } + if (this.getConfig().getBoolean("modules.history")) { + this.loadModule(history); + } + } + + private boolean initDatabase() { + try { + String databaseType = this.getConfig().getString("database_type"); + ConnectionProperties connectionProperties = ConnectionProperties.fromConfig(this.getConfig()); + + if ("sqlite".equalsIgnoreCase(databaseType)) { + this.pluginDatabase = new SQLiteDatabase(this, connectionProperties); + this.getLogger().info("Using SQLite (local) database."); + } else if ("mysql".equalsIgnoreCase(databaseType)) { + DatabaseCredentials credentials = DatabaseCredentials.fromConfig(this.getConfig()); + this.pluginDatabase = new MySQLDatabase(this, credentials, connectionProperties); + this.getLogger().info("Using MySQL (remote) database."); + } else { + this.getLogger().warning(String.format("Error! Unknown database type: %s. Disabling plugin.", databaseType)); + this.getServer().getPluginManager().disablePlugin(this); + return false; + } + + this.pluginDatabase.connect(); + } catch (Exception e) { + this.getLogger().warning("Could not maintain Database Connection. Disabling plugin."); + e.printStackTrace(); + return false; + } + return true; + } + + private void initModules() { + this.tokens = new XPrisonTokens(this); + this.gems = new XPrisonGems(this); + this.ranks = new XPrisonRanks(this); + this.prestiges = new XPrisonPrestiges(this); + this.multipliers = new XPrisonMultipliers(this); + this.enchants = new XPrisonEnchants(this); + this.autoSell = new XPrisonAutoSell(this); + this.autoMiner = new XPrisonAutoMiner(this); + this.pickaxeLevels = new XPrisonPickaxeLevels(this); + this.gangs = new XPrisonGangs(this); + this.mines = new XPrisonMines(this); + this.history = new XPrisonHistory(this); + + this.modules.put(this.tokens.getName().toLowerCase(), this.tokens); + this.modules.put(this.gems.getName().toLowerCase(), this.gems); + this.modules.put(this.ranks.getName().toLowerCase(), this.ranks); + this.modules.put(this.prestiges.getName().toLowerCase(), this.prestiges); + this.modules.put(this.multipliers.getName().toLowerCase(), this.multipliers); + this.modules.put(this.enchants.getName().toLowerCase(), this.enchants); + this.modules.put(this.autoSell.getName().toLowerCase(), this.autoSell); + this.modules.put(this.autoMiner.getName().toLowerCase(), this.autoMiner); + this.modules.put(this.pickaxeLevels.getName().toLowerCase(), this.pickaxeLevels); + this.modules.put(this.gangs.getName().toLowerCase(), this.gangs); + this.modules.put(this.mines.getName().toLowerCase(), this.mines); + this.modules.put(this.history.getName().toLowerCase(), this.history); + } + + private void registerMainEvents() { + //Updating of mapping table + Events.subscribe(PlayerJoinEvent.class, EventPriority.LOW) + .handler(e -> { + this.nicknameService.updatePlayerNickname(e.getPlayer()); + }).bindWith(this); + } + + private void startMetrics() { + new Metrics(this, Constants.METRICS_SERVICE_ID); + } + + private void loadModule(XPrisonModule module) { + if (module.isEnabled()) { + return; + } + module.enable(); + this.getLogger().info(TextUtils.applyColor(String.format("&aX-Prison - Module %s loaded.", module.getName()))); + } + + //Always unload via iterator! + private void unloadModule(XPrisonModule module) { + if (!module.isEnabled()) { + return; + } + module.disable(); + this.getLogger().info(TextUtils.applyColor(String.format("&aX-Prison - Module %s unloaded.", module.getName()))); + } + + public void debug(String msg, XPrisonModule module) { + if (!this.debugMode) { + return; + } + if (module != null) { + this.getLogger().info(String.format("[%s] %s", module.getName(), TextUtils.applyColor(msg))); + } else { + this.getLogger().info(TextUtils.applyColor(msg)); + } + } + + public void reloadModule(XPrisonModule module) { + if (!module.isEnabled()) { + return; + } + module.reload(); + this.getLogger().info(TextUtils.applyColor(String.format("X-Prison - Module %s reloaded.", module.getName()))); + } + + private void registerMainCommand() { + + List commandAliases = this.getConfig().getStringList("main-command-aliases"); + String[] commandAliasesArray = commandAliases.toArray(new String[commandAliases.size()]); + + Commands.create() + .assertPermission("xprison.admin") + .handler(c -> { + if (c.args().size() == 0 && c.sender() instanceof Player) { + new MainMenu(this, (Player) c.sender()).open(); + } else if (c.args().size() >= 1) { + if ("reload".equalsIgnoreCase(c.rawArg(0))) { + final String name = c.args().size() >= 2 ? c.rawArg(1).trim().toLowerCase().replace("-", "") : "all"; + switch (name) { + case "all": + case "*": + getModules().forEach(this::reloadModule); + getItemMigrator().reload(); + c.sender().sendMessage(TextUtils.applyColor("&aSuccessfully reloaded all the plugin")); + break; + case "migrator": + case "itemmigrator": + getItemMigrator().reload(); + c.sender().sendMessage(TextUtils.applyColor("&aSuccessfully reloaded item migrator")); + break; + default: + final XPrisonModule module = modules.get(name); + if (module != null) { + reloadModule(module); + c.sender().sendMessage(TextUtils.applyColor("&aSuccessfully reloaded &f" + name + " &amodule")); + } else { + c.sender().sendMessage(TextUtils.applyColor("&cThe module &6" + c.rawArg(1) + " &cdoesn't exist")); + } + break; + } + } else if (c.sender() instanceof Player && "help".equalsIgnoreCase(c.rawArg(0)) || "?".equalsIgnoreCase(c.rawArg(0))) { + new HelpGui((Player) c.sender()).open(); + } + } + }).registerAndBind(this, commandAliasesArray); + } + + @Override + protected void disable() { + + Iterator it = this.modules.values().iterator(); + + while (it.hasNext()) { + this.unloadModule(it.next()); + it.remove(); + } + + if (this.pluginDatabase != null) { + if (this.pluginDatabase instanceof SQLDatabase) { + SQLDatabase sqlDatabase = (SQLDatabase) this.pluginDatabase; + sqlDatabase.close(); + } + } + } + + + public boolean isModuleEnabled(String moduleName) { + XPrisonModule module = this.modules.get(moduleName.toLowerCase()); + return module != null && module.isEnabled(); + } + + private void registerPlaceholders() { + + if (isMVdWPlaceholderAPIEnabled()) { + new XPrisonMVdWPlaceholder(this).register(); + } + + if (isPlaceholderAPIEnabled()) { + new XPrisonPAPIPlaceholder(this).register(); + } + } + + private boolean setupEconomy() { + + if (getServer().getPluginManager().getPlugin("Vault") == null) { + return false; + } + + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + + if (rsp == null) { + return false; + } + + economy = rsp.getProvider(); + return economy != null; + } + + public boolean isPickaxeSupported(Material m) { + return this.supportedPickaxes.contains(m); + } + + public boolean isPickaxeSupported(ItemStack item) { + return item != null && isPickaxeSupported(item.getType()); + } + + public Collection getModules() { + return this.modules.values(); + } + + public boolean isDebugMode() { + return debugMode; + } + + public void setDebugMode(boolean enabled) { + this.debugMode = enabled; + this.getConfig().set("debug-mode", debugMode); + this.saveConfig(); + } + + public boolean isUltraBackpacksEnabled() { + return this.getServer().getPluginManager().isPluginEnabled("UltraBackpacks"); + } + + public boolean isPlaceholderAPIEnabled() { + return this.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"); + } + + public boolean isMVdWPlaceholderAPIEnabled() { + return this.getServer().getPluginManager().isPluginEnabled("MVdWPlaceholderAPI"); + } + + private void registerWGFlag() { + + if (Bukkit.getPluginManager().getPlugin("WorldGuard") == null) { + return; + } + + try { + getWorldGuardWrapper().registerFlag(Constants.ENCHANTS_WG_FLAG_NAME, WrappedState.class, WrappedState.DENY); + } catch (IllegalStateException e) { + // This happens during plugin reloads. Flag cannot be registered as WG was already loaded, + // so we can safely ignore this exception. + } + } + + public static XPrison getInstance() { + return instance; + } + + public WorldGuardWrapper getWorldGuardWrapper() { + return WorldGuardWrapper.getInstance(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/XPrisonModule.java b/src/main/java/dev/drawethree/xprison/XPrisonModule.java new file mode 100644 index 0000000..0ee454e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/XPrisonModule.java @@ -0,0 +1,18 @@ +package dev.drawethree.xprison; + +public interface XPrisonModule { + + void enable(); + + void disable(); + + void reload(); + + boolean isEnabled(); + + String getName(); + + boolean isHistoryEnabled(); + + void resetPlayerData(); +} diff --git a/src/main/java/dev/drawethree/xprison/api/enums/LostCause.java b/src/main/java/dev/drawethree/xprison/api/enums/LostCause.java new file mode 100644 index 0000000..85a3113 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/api/enums/LostCause.java @@ -0,0 +1,30 @@ +package dev.drawethree.xprison.api.enums; + +public enum LostCause { + + /** + * Player lost currency by pay command + */ + PAY, + /** + * Player lost currency by enchanting + */ + ENCHANT, + /** + * Player lost currency by prestiging + */ + PRESTIGE, + /** + * Player lost currency by rank up + */ + RANKUP, + /** + * Player lost currency by admin running command + */ + ADMIN, + /** + * Player lost currency by withdrawing to physical form + */ + WITHDRAW, + +} diff --git a/src/main/java/dev/drawethree/xprison/api/enums/ReceiveCause.java b/src/main/java/dev/drawethree/xprison/api/enums/ReceiveCause.java new file mode 100644 index 0000000..3604da6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/api/enums/ReceiveCause.java @@ -0,0 +1,35 @@ +package dev.drawethree.xprison.api.enums; + +/** + * ReceiveCause enum for Gems and Tokens events. + */ +public enum ReceiveCause { + /** + * Player received currency during mining (himself) + */ + MINING, + /** + * Player received currency by pay command + */ + PAY, + /** + * Player received currency by give command + */ + GIVE, + /** + * Player received currency by redeeming items + */ + REDEEM, + /** + * Player received currency by breaking lucky blocks + */ + LUCKY_BLOCK, + /** + * Player received currency by disenchanting + */ + REFUND, + /** + * Player received currency by mining (from other players) + */ + MINING_OTHERS +} diff --git a/src/main/java/dev/drawethree/xprison/api/events/XPrisonEvent.java b/src/main/java/dev/drawethree/xprison/api/events/XPrisonEvent.java new file mode 100644 index 0000000..6cb77ef --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/api/events/XPrisonEvent.java @@ -0,0 +1,6 @@ +package dev.drawethree.xprison.api.events; + +import org.bukkit.event.Event; + +public abstract class XPrisonEvent extends Event { +} diff --git a/src/main/java/dev/drawethree/xprison/api/events/player/XPrisonPlayerEvent.java b/src/main/java/dev/drawethree/xprison/api/events/player/XPrisonPlayerEvent.java new file mode 100644 index 0000000..01c5f0a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/api/events/player/XPrisonPlayerEvent.java @@ -0,0 +1,20 @@ +package dev.drawethree.xprison.api.events.player; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import lombok.Getter; +import org.bukkit.OfflinePlayer; + +public abstract class XPrisonPlayerEvent extends XPrisonEvent { + + @Getter + protected OfflinePlayer player; + + /** + * Abstract XPrisonPlayerEvent + * + * @param player Player + */ + public XPrisonPlayerEvent(OfflinePlayer player) { + this.player = player; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/XPrisonAutoMiner.java b/src/main/java/dev/drawethree/xprison/autominer/XPrisonAutoMiner.java new file mode 100644 index 0000000..ebee30a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/XPrisonAutoMiner.java @@ -0,0 +1,114 @@ +package dev.drawethree.xprison.autominer; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.autominer.api.XPrisonAutoMinerAPI; +import dev.drawethree.xprison.autominer.api.XPrisonAutoMinerAPIImpl; +import dev.drawethree.xprison.autominer.command.AdminAutoMinerCommand; +import dev.drawethree.xprison.autominer.command.AutoMinerCommand; +import dev.drawethree.xprison.autominer.config.AutoMinerConfig; +import dev.drawethree.xprison.autominer.listener.AutoMinerListener; +import dev.drawethree.xprison.autominer.manager.AutoMinerManager; +import dev.drawethree.xprison.autominer.repo.AutominerRepository; +import dev.drawethree.xprison.autominer.repo.impl.AutominerRepositoryImpl; +import dev.drawethree.xprison.autominer.service.AutominerService; +import dev.drawethree.xprison.autominer.service.impl.AutominerServiceImpl; +import lombok.Getter; + +public final class XPrisonAutoMiner implements XPrisonModule { + + public static final String MODULE_NAME = "Auto Miner"; + + @Getter + private static XPrisonAutoMiner instance; + + @Getter + private final XPrison core; + + @Getter + private AutoMinerManager manager; + + @Getter + private AutoMinerConfig autoMinerConfig; + + @Getter + private XPrisonAutoMinerAPI api; + + @Getter + private AutominerService autominerService; + + @Getter + private AutominerRepository autominerRepository; + + private boolean enabled; + + public XPrisonAutoMiner(XPrison core) { + this.core = core; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void enable() { + instance = this; + + this.autoMinerConfig = new AutoMinerConfig(this); + this.autoMinerConfig.load(); + + this.autominerRepository = new AutominerRepositoryImpl(this.core.getPluginDatabase()); + this.autominerRepository.createTables(); + this.autominerRepository.removeExpiredAutoMiners(); + + this.autominerService = new AutominerServiceImpl(this.autominerRepository); + + this.manager = new AutoMinerManager(this); + this.manager.load(); + + AutoMinerListener listener = new AutoMinerListener(this); + listener.subscribeToEvents(); + + this.registerCommands(); + + this.api = new XPrisonAutoMinerAPIImpl(this); + + this.enabled = true; + } + + @Override + public void disable() { + this.manager.disable(); + this.enabled = false; + } + + @Override + public void reload() { + this.autoMinerConfig.reload(); + this.manager.reload(); + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.autominerRepository.clearTableData(); + } + + private void registerCommands() { + AutoMinerCommand autoMinerCommand = new AutoMinerCommand(this); + autoMinerCommand.register(); + + AdminAutoMinerCommand adminAutoMinerCommand = new AdminAutoMinerCommand(this); + adminAutoMinerCommand.register(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/api/XPrisonAutoMinerAPI.java b/src/main/java/dev/drawethree/xprison/autominer/api/XPrisonAutoMinerAPI.java new file mode 100644 index 0000000..dab03ef --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/api/XPrisonAutoMinerAPI.java @@ -0,0 +1,22 @@ +package dev.drawethree.xprison.autominer.api; + +import org.bukkit.entity.Player; + +public interface XPrisonAutoMinerAPI { + + /** + * Returns true if player is in autominer region, otherwise return false + * + * @param player Player + * @return returns true if player is in autominer region, otherwise return false + */ + boolean isInAutoMinerRegion(Player player); + + /** + * Returns remaining autominer time in seconds for specific player + * + * @param player Player + * @return time in seconds left for specific player autominer + */ + int getAutoMinerTime(Player player); +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/api/XPrisonAutoMinerAPIImpl.java b/src/main/java/dev/drawethree/xprison/autominer/api/XPrisonAutoMinerAPIImpl.java new file mode 100644 index 0000000..6d0ec99 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/api/XPrisonAutoMinerAPIImpl.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.autominer.api; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import org.bukkit.entity.Player; + +public final class XPrisonAutoMinerAPIImpl implements XPrisonAutoMinerAPI { + + private final XPrisonAutoMiner plugin; + + public XPrisonAutoMinerAPIImpl(XPrisonAutoMiner plugin) { + this.plugin = plugin; + } + + @Override + public boolean isInAutoMinerRegion(Player player) { + return this.plugin.getManager().isInAutoMinerRegion(player); + } + + @Override + public int getAutoMinerTime(Player player) { + return this.plugin.getManager().getAutoMinerTime(player); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/api/events/PlayerAutoMinerTimeModifyEvent.java b/src/main/java/dev/drawethree/xprison/autominer/api/events/PlayerAutoMinerTimeModifyEvent.java new file mode 100644 index 0000000..f874e91 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/api/events/PlayerAutoMinerTimeModifyEvent.java @@ -0,0 +1,46 @@ +package dev.drawethree.xprison.autominer.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.TimeUnit; + +public final class PlayerAutoMinerTimeModifyEvent extends XPrisonPlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + + @Getter + @Setter + private final TimeUnit timeUnit; + + @Getter + @Setter + private final long duration; + + /** + * Called when player received autominer time + * + * @param player Player + * @param unit TimeUnit + * @param duration duration, can be negative + */ + public PlayerAutoMinerTimeModifyEvent(Player player, TimeUnit unit, long duration) { + super(player); + this.timeUnit = unit; + this.duration = duration; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/api/events/PlayerAutomineEvent.java b/src/main/java/dev/drawethree/xprison/autominer/api/events/PlayerAutomineEvent.java new file mode 100644 index 0000000..f760fea --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/api/events/PlayerAutomineEvent.java @@ -0,0 +1,42 @@ +package dev.drawethree.xprison.autominer.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class PlayerAutomineEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + @Getter + @Setter + private boolean cancelled; + + @Getter + private final int timeLeft; + + /** + * Called when player auto mines in region + * + * @param player Player + * @param timeLeft Timeleft in seconds of player's autominer time + */ + public PlayerAutomineEvent(Player player, int timeLeft) { + super(player); + this.timeLeft = timeLeft; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/command/AdminAutoMinerCommand.java b/src/main/java/dev/drawethree/xprison/autominer/command/AdminAutoMinerCommand.java new file mode 100644 index 0000000..abba6e3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/command/AdminAutoMinerCommand.java @@ -0,0 +1,78 @@ +package dev.drawethree.xprison.autominer.command; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import dev.drawethree.xprison.autominer.utils.AutoMinerConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import me.lucko.helper.command.context.CommandContext; +import me.lucko.helper.utils.Players; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class AdminAutoMinerCommand { + + private static final String[] COMMAND_ALIASES = {"adminautominer", "aam"}; + + private final XPrisonAutoMiner plugin; + + public AdminAutoMinerCommand(XPrisonAutoMiner plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPermission(AutoMinerConstants.ADMIN_PERMISSION) + .tabHandler(this::createTabHandler) + .handler(c -> { + + if (!validateArguments(c)) { + return; + } + + String action = c.rawArg(0); + + Player target = c.arg(1).parseOrFail(Player.class); + long time = c.arg(2).parseOrFail(Long.class); + + TimeUnit timeUnit; + try { + timeUnit = TimeUnit.valueOf(Objects.requireNonNull(c.rawArg(3)).toUpperCase()); + } catch (IllegalArgumentException e) { + PlayerUtils.sendMessage(c.sender(), "&cInvalid time unit! Please use one from: " + StringUtils.join(TimeUnit.values(), ",")); + return; + } + + if ("remove".equalsIgnoreCase(action)) { + time = -time; + } + + this.plugin.getManager().modifyPlayerAutoMinerTime(c.sender(), target, time, timeUnit); + }).registerAndBind(this.plugin.getCore(), COMMAND_ALIASES); + } + + private List createTabHandler(CommandContext context) { + List list = new ArrayList<>(); + + if (context.args().size() == 1) { + list = Arrays.asList("give", "remove"); + } else if (context.args().size() == 2) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } else if (context.args().size() == 4) { + list = Arrays.stream(TimeUnit.values()).map(TimeUnit::name).collect(Collectors.toList()); + } + + return list; + } + + private boolean validateArguments(CommandContext c) { + return c.args().size() == 4 && ("give".equalsIgnoreCase(c.rawArg(0)) || "remove".equalsIgnoreCase(c.rawArg(0))); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/command/AutoMinerCommand.java b/src/main/java/dev/drawethree/xprison/autominer/command/AutoMinerCommand.java new file mode 100644 index 0000000..bf2cb6e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/command/AutoMinerCommand.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.autominer.command; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import dev.drawethree.xprison.autominer.utils.AutoMinerUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import me.lucko.helper.command.context.CommandContext; +import org.bukkit.entity.Player; + +public class AutoMinerCommand { + + private static final String[] COMMAND_ALIASES = {"autominer"}; + + private final XPrisonAutoMiner plugin; + + public AutoMinerCommand(XPrisonAutoMiner plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .handler(c -> { + + if (!validateArguments(c)) { + return; + } + + int timeLeft = this.plugin.getManager().getAutoMinerTime(c.sender()); + PlayerUtils.sendMessage(c.sender(), this.plugin.getAutoMinerConfig().getMessage("auto_miner_time").replace("%time%", AutoMinerUtils.getAutoMinerTimeLeftFormatted(timeLeft))); + + }).registerAndBind(this.plugin.getCore(), COMMAND_ALIASES); + } + + private boolean validateArguments(CommandContext c) { + return c.args().size() == 0; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/config/AutoMinerConfig.java b/src/main/java/dev/drawethree/xprison/autominer/config/AutoMinerConfig.java new file mode 100644 index 0000000..a916125 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/config/AutoMinerConfig.java @@ -0,0 +1,57 @@ +package dev.drawethree.xprison.autominer.config; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.HashMap; +import java.util.Map; + +public class AutoMinerConfig { + + private final XPrisonAutoMiner plugin; + private final FileManager.Config config; + + private Map messages; + + public AutoMinerConfig(XPrisonAutoMiner plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("autominer.yml").copyDefaults(true).save(); + } + + private void loadMessages() { + messages = new HashMap<>(); + + YamlConfiguration configuration = getYamlConfig(); + + for (String key : configuration.getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(configuration.getString("messages." + key))); + } + } + + public String getMessage(String key) { + return messages.getOrDefault(key.toLowerCase(), "No message with key '" + key + "' found"); + } + + private void loadVariables() { + this.loadMessages(); + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public void load() { + this.getConfig().reload(); + this.loadVariables(); + } + + public void reload() { + this.load(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/listener/AutoMinerListener.java b/src/main/java/dev/drawethree/xprison/autominer/listener/AutoMinerListener.java new file mode 100644 index 0000000..9826640 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/listener/AutoMinerListener.java @@ -0,0 +1,31 @@ +package dev.drawethree.xprison.autominer.listener; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import me.lucko.helper.Events; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class AutoMinerListener { + + private final XPrisonAutoMiner plugin; + + public AutoMinerListener(XPrisonAutoMiner plugin) { + this.plugin = plugin; + } + + public void subscribeToEvents() { + this.subscribeToPlayerJoinEvent(); + this.subscribeToPlayerQuitEvent(); + } + + private void subscribeToPlayerQuitEvent() { + Events.subscribe(PlayerQuitEvent.class) + .handler(e -> this.plugin.getManager().savePlayerAutoMinerData(e.getPlayer(), true)).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerJoinEvent() { + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> this.plugin.getManager().loadPlayerAutoMinerData(e.getPlayer())).bindWith(this.plugin.getCore()); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/manager/AutoMinerManager.java b/src/main/java/dev/drawethree/xprison/autominer/manager/AutoMinerManager.java new file mode 100644 index 0000000..a9ebedb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/manager/AutoMinerManager.java @@ -0,0 +1,216 @@ +package dev.drawethree.xprison.autominer.manager; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import dev.drawethree.xprison.autominer.api.events.PlayerAutoMinerTimeModifyEvent; +import dev.drawethree.xprison.autominer.api.events.PlayerAutomineEvent; +import dev.drawethree.xprison.autominer.model.AutoMinerRegion; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.utils.Players; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.codemc.worldguardwrapper.WorldGuardWrapper; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class AutoMinerManager { + + private final XPrisonAutoMiner plugin; + + private final Map autoMinerTimes; + + private List autoMinerRegions; + + public AutoMinerManager(XPrisonAutoMiner plugin) { + this.plugin = plugin; + this.autoMinerTimes = new ConcurrentHashMap<>(); + } + + private void loadAllPlayersAutoMinerData() { + Schedulers.async().run(() -> { + for (Player p : Players.all()) { + int timeLeft = this.plugin.getAutominerService().getPlayerAutoMinerTime(p); + this.autoMinerTimes.put(p.getUniqueId(), timeLeft); + this.plugin.getCore().debug(String.format("Loaded %s's AutoMiner Time.", p.getName()), this.plugin); + } + }); + } + + + public void loadPlayerAutoMinerData(Player p) { + Schedulers.async().run(() -> { + int timeLeft = this.plugin.getAutominerService().getPlayerAutoMinerTime(p); + this.autoMinerTimes.put(p.getUniqueId(), timeLeft); + this.plugin.getCore().debug(String.format("Loaded %s's AutoMiner Time.", p.getName()), this.plugin); + }); + } + + public void savePlayerAutoMinerData(Player p, boolean async) { + + int timeLeft = getAutoMinerTime(p); + + if (async) { + Schedulers.async().run(() -> savePlayerAutominerData(p, timeLeft)); + } else { + savePlayerAutominerData(p, timeLeft); + } + } + + private void savePlayerAutominerData(Player p, int timeLeft) { + this.plugin.getAutominerService().setAutoMiner(p, timeLeft); + this.autoMinerTimes.remove(p.getUniqueId()); + this.plugin.getCore().debug(String.format("Saved %s's AutoMiner time.", p.getName()), this.plugin); + } + + public void modifyPlayerAutoMinerTime(CommandSender sender, Player p, long time, TimeUnit unit) { + + if (p == null || !p.isOnline()) { + PlayerUtils.sendMessage(sender, "&cPlayer is not online!"); + return; + } + + PlayerAutoMinerTimeModifyEvent event = this.callAutoMinerTimeModifyEvent(p, time, unit); + + time = event.getDuration(); + unit = event.getTimeUnit(); + + int currentTime = getAutoMinerTime(p); + currentTime += unit.toSeconds(time); + + if (currentTime < 0) { + currentTime = 0; + } + + this.autoMinerTimes.put(p.getUniqueId(), currentTime); + + String messageKey = time < 0 ? "auto_miner_time_remove" : "auto_miner_time_add"; + PlayerUtils.sendMessage(sender, this.plugin.getAutoMinerConfig().getMessage(messageKey).replace("%time%", String.valueOf(Math.abs(time))).replace("%timeunit%", unit.name()).replace("%player%", p.getName())); + } + + private PlayerAutoMinerTimeModifyEvent callAutoMinerTimeModifyEvent(Player p, long time, TimeUnit unit) { + PlayerAutoMinerTimeModifyEvent event = new PlayerAutoMinerTimeModifyEvent(p, unit, time); + Events.callSync(event); + return event; + } + + public boolean hasAutoMinerTime(Player p) { + return getAutoMinerTime(p) > 0; + } + + public void decrementPlayerAutominerTime(Player p) { + int newAmount = autoMinerTimes.get(p.getUniqueId()) - 1; + autoMinerTimes.put(p.getUniqueId(), newAmount); + } + + public int getAutoMinerTime(Player player) { + return this.autoMinerTimes.getOrDefault(player.getUniqueId(), 0); + } + + public boolean isInAutoMinerRegion(Player player) { + for (AutoMinerRegion region : this.autoMinerRegions) { + if (region.getRegion().contains(player.getLocation())) { + return true; + } + } + return false; + } + + public PlayerAutomineEvent callAutoMineEvent(Player p) { + PlayerAutomineEvent event = new PlayerAutomineEvent(p, this.getAutoMinerTime(p)); + Events.callSync(event); + return event; + } + + public void saveAllPlayerAutoMinerData(boolean async) { + Players.all().forEach(p -> savePlayerAutoMinerData(p, async)); + this.plugin.getCore().getLogger().info("Saved online players auto miner data."); + } + + private void loadAutoMinerRegions() { + this.autoMinerRegions = new ArrayList<>(); + + YamlConfiguration configuration = this.plugin.getAutoMinerConfig().getYamlConfig(); + + Set regionNames = configuration.getConfigurationSection("auto-miner-regions").getKeys(false); + + for (String regionName : regionNames) { + String worldName = configuration.getString("auto-miner-regions." + regionName + ".world"); + World world = Bukkit.getWorld(worldName); + + if (world == null) { + plugin.getCore().getLogger().warning(String.format("Unable to get world with name %s! Disabling AutoMiner region.", worldName)); + return; + } + + int rewardPeriod = configuration.getInt("auto-miner-regions." + regionName + ".reward-period"); + + if (rewardPeriod <= 0) { + plugin.getCore().getLogger().warning("reward-period in autominer.yml for region " + regionName + " needs to be greater than 0!"); + return; + } + + Optional optRegion = WorldGuardWrapper.getInstance().getRegion(world, regionName); + + if (!optRegion.isPresent()) { + plugin.getCore().getLogger().warning(String.format("There is no such region named %s in world %s!", regionName, world.getName())); + return; + } + + List rewards = configuration.getStringList("auto-miner-regions." + regionName + ".rewards"); + + if (rewards.isEmpty()) { + plugin.getCore().getLogger().warning("rewards in autominer.yml for region " + regionName + " are empty!"); + return; + } + + int blocksBroken = configuration.getInt("auto-miner-regions." + regionName + ".blocks-broken"); + + if (blocksBroken <= 0) { + this.plugin.getCore().getLogger().warning("blocks-broken in autominer.yml for region " + regionName + " needs to be greater than 0!"); + return; + } + + AutoMinerRegion region = new AutoMinerRegion(this.plugin, world, optRegion.get(), rewards, rewardPeriod, blocksBroken); + region.startAutoMinerTask(); + + this.plugin.getCore().getLogger().info("AutoMiner region '" + regionName + "' loaded successfully!"); + this.autoMinerRegions.add(region); + } + } + + public void load() { + this.removeExpiredAutoMiners(); + this.loadAllPlayersAutoMinerData(); + this.loadAutoMinerRegions(); + } + + private void removeExpiredAutoMiners() { + Schedulers.async().run(() -> { + this.plugin.getAutominerService().removeExpiredAutoMiners(); + this.plugin.getCore().debug("Removed expired AutoMiners from database", this.plugin); + }); + } + + public void reload() { + this.stopAutoMinerRegions(); + this.loadAutoMinerRegions(); + } + + public void disable() { + this.stopAutoMinerRegions(); + this.saveAllPlayerAutoMinerData(false); + } + + private void stopAutoMinerRegions() { + for (AutoMinerRegion region : this.autoMinerRegions) { + region.stopAutoMinerTask(); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/model/AutoMinerRegion.java b/src/main/java/dev/drawethree/xprison/autominer/model/AutoMinerRegion.java new file mode 100644 index 0000000..968dd7c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/model/AutoMinerRegion.java @@ -0,0 +1,75 @@ +package dev.drawethree.xprison.autominer.model; + +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import lombok.Getter; +import me.lucko.helper.utils.Players; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class AutoMinerRegion { + + private final XPrisonAutoMiner plugin; + private World world; + private IWrappedRegion region; + private List commands; + private int rewardPeriod; + private int blocksBroken; + private final AutoMinerTask autoMinerTask; + + public AutoMinerRegion(XPrisonAutoMiner plugin, World world, IWrappedRegion region, List rewards, int rewardPeriod, int blocksBroken) { + this.plugin = plugin; + this.world = world; + this.region = region; + this.commands = rewards; + this.rewardPeriod = rewardPeriod; + this.blocksBroken = blocksBroken; + this.autoMinerTask = new AutoMinerTask(this); + } + + public void startAutoMinerTask() { + this.autoMinerTask.start(); + } + + public List getPlayersInRegion() { + List list = new ArrayList<>(); + for (Player p : Players.all()) { + if (isInAutoMinerRegion(p)) { + list.add(p); + } + } + return list; + } + + private boolean isInAutoMinerRegion(Player p) { + return p.getWorld().getName().equals(this.world.getName()) && region.contains(p.getLocation()); + } + + public void stopAutoMinerTask() { + this.autoMinerTask.stop(); + } + + public void setWorld(World world) { + this.world = world; + } + + public void setRegion(IWrappedRegion region) { + this.region = region; + } + + public void setCommands(List commands) { + this.commands = commands; + } + + public void setRewardPeriod(int rewardPeriod) { + this.rewardPeriod = rewardPeriod; + } + + public void setBlocksBroken(int blocksBroken) { + this.blocksBroken = blocksBroken; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/model/AutoMinerTask.java b/src/main/java/dev/drawethree/xprison/autominer/model/AutoMinerTask.java new file mode 100644 index 0000000..f6b8008 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/model/AutoMinerTask.java @@ -0,0 +1,115 @@ +package dev.drawethree.xprison.autominer.model; + +import com.cryptomorin.xseries.messages.ActionBar; +import dev.drawethree.xprison.autominer.api.events.PlayerAutomineEvent; +import me.lucko.helper.Schedulers; +import me.lucko.helper.scheduler.Task; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public final class AutoMinerTask implements Runnable { + + private final AtomicInteger counter; + private final AutoMinerRegion region; + private Task task; + + public AutoMinerTask(AutoMinerRegion region) { + this.region = region; + this.counter = new AtomicInteger(0); + } + + @Override + public void run() { + + int current = counter.getAndIncrement(); + boolean resetCounterAfterEnd = current >= region.getRewardPeriod(); + + List players = this.region.getPlayersInRegion(); + + for (Player p : players) { + + if (!this.executeTaskValidationAndNotifyPlayerOnFail(p)) { + continue; + } + + this.decrementPlayerAutoMinerTimeAndNotify(p); + + if (resetCounterAfterEnd) { + this.executeTaskLogic(p); + } + } + + if (resetCounterAfterEnd) { + counter.set(0); + } + } + + public void start() { + this.stop(); + this.task = Schedulers.async().runRepeating(this, 1, TimeUnit.SECONDS, 1, TimeUnit.SECONDS); + } + + public void stop() { + if (this.task != null) { + this.task.stop(); + } + } + + private void decrementPlayerAutoMinerTimeAndNotify(Player p) { + this.region.getPlugin().getManager().decrementPlayerAutominerTime(p); + ActionBar.sendActionBar(p, this.region.getPlugin().getAutoMinerConfig().getMessage("auto_miner_enabled")); + } + + private boolean executeTaskValidationAndNotifyPlayerOnFail(Player p) { + + if (!this.checkPlayerAutoMinerTime(p)) { + ActionBar.sendActionBar(p, this.region.getPlugin().getAutoMinerConfig().getMessage("auto_miner_disabled")); + return false; + } + + if (!this.checkPlayerItemInHand(p)) { + ActionBar.sendActionBar(p, this.region.getPlugin().getAutoMinerConfig().getMessage("auto_miner_no_pickaxe")); + return false; + } + + return true; + } + + private void executeTaskLogic(Player p) { + + if (!this.callAutoMineEvent(p)) { + return; + } + + this.executeCommands(p); + this.region.getPlugin().getCore().getEnchants().getEnchantsManager().addBlocksBrokenToItem(p, p.getItemInHand(), this.region.getBlocksBroken()); + } + + private boolean callAutoMineEvent(Player p) { + PlayerAutomineEvent event = this.region.getPlugin().getManager().callAutoMineEvent(p); + return !event.isCancelled(); + } + + private boolean checkPlayerItemInHand(Player p) { + ItemStack item = p.getItemInHand(); + return this.region.getPlugin().getCore().isPickaxeSupported(item); + } + + private boolean checkPlayerAutoMinerTime(Player p) { + return this.region.getPlugin().getManager().hasAutoMinerTime(p); + } + + private void executeCommands(Player p) { + + if (this.region.getCommands().isEmpty()) { + return; + } + + this.region.getCommands().forEach(c -> Schedulers.sync().run(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), c.replace("%player%", p.getName())))); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/repo/AutominerRepository.java b/src/main/java/dev/drawethree/xprison/autominer/repo/AutominerRepository.java new file mode 100644 index 0000000..d442f7e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/repo/AutominerRepository.java @@ -0,0 +1,17 @@ +package dev.drawethree.xprison.autominer.repo; + +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public interface AutominerRepository { + + int getPlayerAutoMinerTime(OfflinePlayer player); + + void removeExpiredAutoMiners(); + + void saveAutoMiner(Player p, int timeLeft); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/repo/impl/AutominerRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/autominer/repo/impl/AutominerRepositoryImpl.java new file mode 100644 index 0000000..aee17db --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/repo/impl/AutominerRepositoryImpl.java @@ -0,0 +1,81 @@ +package dev.drawethree.xprison.autominer.repo.impl; + +import dev.drawethree.xprison.autominer.repo.AutominerRepository; +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class AutominerRepositoryImpl implements AutominerRepository { + + private static final String TABLE_NAME = "UltraPrison_AutoMiner"; + private static final String AUTOMINER_UUID_COLNAME = "UUID"; + private static final String AUTOMINER_TIME_COLNAME = "time"; + private final SQLDatabase database; + + public AutominerRepositoryImpl(SQLDatabase database) { + + this.database = database; + } + + @Override + public int getPlayerAutoMinerTime(OfflinePlayer p) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT * FROM " + TABLE_NAME + " WHERE " + AUTOMINER_UUID_COLNAME + "=?")) { + statement.setString(1, p.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getInt(AUTOMINER_TIME_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public void removeExpiredAutoMiners() { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "DELETE FROM " + TABLE_NAME + " WHERE " + AUTOMINER_TIME_COLNAME + " <= 0")) { + statement.execute(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Override + public void saveAutoMiner(Player p, int timeLeft) { + if (database.getDatabaseType() == SQLDatabaseType.MYSQL) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "INSERT INTO " + TABLE_NAME + " VALUES (?,?) ON DUPLICATE KEY UPDATE " + AUTOMINER_TIME_COLNAME + "=?")) { + statement.setString(1, p.getUniqueId().toString()); + statement.setInt(2, timeLeft); + statement.setInt(3, timeLeft); + statement.execute(); + } catch (SQLException e) { + e.printStackTrace(); + } + } else { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "INSERT OR REPLACE INTO " + TABLE_NAME + " VALUES(?,?)")) { + statement.setString(1, p.getUniqueId().toString()); + statement.setDouble(2, timeLeft); + statement.execute(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(UUID varchar(36) NOT NULL UNIQUE, time int, primary key (UUID))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/service/AutominerService.java b/src/main/java/dev/drawethree/xprison/autominer/service/AutominerService.java new file mode 100644 index 0000000..1970258 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/service/AutominerService.java @@ -0,0 +1,13 @@ +package dev.drawethree.xprison.autominer.service; + +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public interface AutominerService { + + int getPlayerAutoMinerTime(OfflinePlayer player); + + void removeExpiredAutoMiners(); + + void setAutoMiner(Player p, int timeLeft); +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/service/impl/AutominerServiceImpl.java b/src/main/java/dev/drawethree/xprison/autominer/service/impl/AutominerServiceImpl.java new file mode 100644 index 0000000..b7c801e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/service/impl/AutominerServiceImpl.java @@ -0,0 +1,32 @@ +package dev.drawethree.xprison.autominer.service.impl; + +import dev.drawethree.xprison.autominer.repo.AutominerRepository; +import dev.drawethree.xprison.autominer.service.AutominerService; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public class AutominerServiceImpl implements AutominerService { + + private final AutominerRepository repository; + + public AutominerServiceImpl(AutominerRepository repository) { + + this.repository = repository; + } + + @Override + public int getPlayerAutoMinerTime(OfflinePlayer player) { + return repository.getPlayerAutoMinerTime(player); + } + + @Override + public void removeExpiredAutoMiners() { + repository.removeExpiredAutoMiners(); + } + + @Override + public void setAutoMiner(Player p, int timeLeft) { + repository.saveAutoMiner(p, timeLeft); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/utils/AutoMinerConstants.java b/src/main/java/dev/drawethree/xprison/autominer/utils/AutoMinerConstants.java new file mode 100644 index 0000000..7ffbfab --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/utils/AutoMinerConstants.java @@ -0,0 +1,6 @@ +package dev.drawethree.xprison.autominer.utils; + +public class AutoMinerConstants { + + public static final String ADMIN_PERMISSION = "xprison.autominer.admin"; +} diff --git a/src/main/java/dev/drawethree/xprison/autominer/utils/AutoMinerUtils.java b/src/main/java/dev/drawethree/xprison/autominer/utils/AutoMinerUtils.java new file mode 100644 index 0000000..ac8f0be --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autominer/utils/AutoMinerUtils.java @@ -0,0 +1,24 @@ +package dev.drawethree.xprison.autominer.utils; + +public class AutoMinerUtils { + + public static String getAutoMinerTimeLeftFormatted(int timeLeft) { + + if (timeLeft == 0) { + return "0s"; + } + + long days = timeLeft / (24 * 60 * 60); + timeLeft -= days * (24 * 60 * 60); + + long hours = timeLeft / (60 * 60); + timeLeft -= hours * (60 * 60); + + long minutes = timeLeft / (60); + timeLeft -= minutes * (60); + + long seconds = timeLeft; + + return days + "d " + hours + "h " + minutes + "m " + seconds + "s"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/XPrisonAutoSell.java b/src/main/java/dev/drawethree/xprison/autosell/XPrisonAutoSell.java new file mode 100644 index 0000000..ada8500 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/XPrisonAutoSell.java @@ -0,0 +1,108 @@ +package dev.drawethree.xprison.autosell; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.autosell.api.XPrisonAutoSellAPI; +import dev.drawethree.xprison.autosell.api.XPrisonAutoSellAPIImpl; +import dev.drawethree.xprison.autosell.command.AutoSellCommand; +import dev.drawethree.xprison.autosell.command.SellAllCommand; +import dev.drawethree.xprison.autosell.command.SellPriceCommand; +import dev.drawethree.xprison.autosell.config.AutoSellConfig; +import dev.drawethree.xprison.autosell.listener.AutoSellListener; +import dev.drawethree.xprison.autosell.manager.AutoSellManager; +import dev.drawethree.xprison.autosell.model.AutoSellBroadcastTask; +import dev.drawethree.xprison.multipliers.XPrisonMultipliers; +import lombok.Getter; + +public final class XPrisonAutoSell implements XPrisonModule { + + public static final String MODULE_NAME = "Auto Sell"; + + @Getter + private static XPrisonAutoSell instance; + @Getter + private final XPrison core; + @Getter + private XPrisonAutoSellAPI api; + @Getter + private AutoSellConfig autoSellConfig; + @Getter + private AutoSellManager manager; + @Getter + private AutoSellBroadcastTask broadcastTask; + + private boolean enabled; + + public XPrisonAutoSell(XPrison core) { + instance = this; + this.core = core; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.autoSellConfig.reload(); + this.manager.reload(); + } + + + @Override + public void enable() { + this.autoSellConfig = new AutoSellConfig(this); + this.autoSellConfig.load(); + + this.manager = new AutoSellManager(this); + this.manager.load(); + + this.broadcastTask = new AutoSellBroadcastTask(this); + this.broadcastTask.start(); + + AutoSellListener listener = new AutoSellListener(this); + listener.subscribeToEvents(); + + this.registerCommands(); + + this.api = new XPrisonAutoSellAPIImpl(this); + this.enabled = true; + } + + private void registerCommands() { + SellAllCommand sellAllCommand = new SellAllCommand(this); + sellAllCommand.register(); + + AutoSellCommand autoSellCommand = new AutoSellCommand(this); + autoSellCommand.register(); + + SellPriceCommand sellPriceCommand = new SellPriceCommand(this); + sellPriceCommand.register(); + } + + public boolean isMultipliersModuleEnabled() { + return this.core.isModuleEnabled(XPrisonMultipliers.MODULE_NAME); + } + + @Override + public void disable() { + this.broadcastTask.stop(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return false; + } + + @Override + public void resetPlayerData() { + } + +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/api/XPrisonAutoSellAPI.java b/src/main/java/dev/drawethree/xprison/autosell/api/XPrisonAutoSellAPI.java new file mode 100644 index 0000000..3ce0219 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/api/XPrisonAutoSellAPI.java @@ -0,0 +1,68 @@ +package dev.drawethree.xprison.autosell.api; + +import dev.drawethree.xprison.autosell.model.SellRegion; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; +import java.util.List; + +public interface XPrisonAutoSellAPI { + + /** + * Method to get current earnings of player + * + * @param player Player + * @return Current earnings + */ + double getCurrentEarnings(Player player); + + /** + * Method to get price for ItemStack in specified mine region + * + * @param regionName Name of region + * @param item ItemStack + * @return Price for item + */ + double getPriceForItem(String regionName, ItemStack item); + + /** + * Method to get price for Block + * + * @param block Block + * @return Price for block + */ + double getPriceForBlock(Block block); + + /** + * Sells the given blocks + * + * @param player Player + * @param blocks List of blocks + */ + void sellBlocks(Player player, List blocks); + + /** + * Method to get if player has autosell enabled + * + * @param p Player + * @return true if player has autosell enabled, otherwise false + */ + boolean hasAutoSellEnabled(Player p); + + /** + * Method to get all sell regions + * + * @return Collection of all loaded and active Sell Regions + */ + Collection getSellRegions(); + + /** + * Method to get SellRegion at specified location + * + * @return Sell Region at given location or null if not present. + */ + SellRegion getSellRegionAtLocation(Location location); +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/api/XPrisonAutoSellAPIImpl.java b/src/main/java/dev/drawethree/xprison/autosell/api/XPrisonAutoSellAPIImpl.java new file mode 100644 index 0000000..9736720 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/api/XPrisonAutoSellAPIImpl.java @@ -0,0 +1,55 @@ +package dev.drawethree.xprison.autosell.api; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.model.SellRegion; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; +import java.util.List; + +public final class XPrisonAutoSellAPIImpl implements XPrisonAutoSellAPI { + + private final XPrisonAutoSell plugin; + + public XPrisonAutoSellAPIImpl(XPrisonAutoSell plugin) { + this.plugin = plugin; + } + + @Override + public double getCurrentEarnings(Player player) { + return plugin.getManager().getCurrentEarnings(player); + } + + @Override + public double getPriceForItem(String regionName, ItemStack item) { + return plugin.getManager().getPriceForItem(regionName, item); + } + + @Override + public double getPriceForBlock(Block block) { + return plugin.getManager().getPriceForBlock(block); + } + + @Override + public void sellBlocks(Player player, List blocks) { + plugin.getManager().sellBlocks(player, blocks); + } + + @Override + public boolean hasAutoSellEnabled(Player p) { + return plugin.getManager().hasAutoSellEnabled(p); + } + + @Override + public Collection getSellRegions() { + return plugin.getManager().getAutoSellRegions(); + } + + @Override + public SellRegion getSellRegionAtLocation(Location location) { + return plugin.getManager().getAutoSellRegion(location); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/api/events/XPrisonAutoSellEvent.java b/src/main/java/dev/drawethree/xprison/autosell/api/events/XPrisonAutoSellEvent.java new file mode 100644 index 0000000..89d6682 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/api/events/XPrisonAutoSellEvent.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.autosell.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import dev.drawethree.xprison.autosell.model.AutoSellItemStack; +import dev.drawethree.xprison.autosell.model.SellRegion; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import java.util.Map; + + +@Getter +public final class XPrisonAutoSellEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private final Player player; + private final SellRegion region; + @Setter + private Map itemsToSell; + @Setter + private boolean cancelled; + + /** + * Called when mined blocks are automatically sold + * + * @param player Player + * @param reg IWrappedRegion where block was mined + * @param itemsToSell ItemStacks to sell with prices + */ + public XPrisonAutoSellEvent(Player player, SellRegion reg, Map itemsToSell) { + super(player); + this.player = player; + this.region = reg; + this.itemsToSell = itemsToSell; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/api/events/XPrisonSellAllEvent.java b/src/main/java/dev/drawethree/xprison/autosell/api/events/XPrisonSellAllEvent.java new file mode 100644 index 0000000..74eaedb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/api/events/XPrisonSellAllEvent.java @@ -0,0 +1,53 @@ +package dev.drawethree.xprison.autosell.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import dev.drawethree.xprison.autosell.model.AutoSellItemStack; +import dev.drawethree.xprison.autosell.model.SellRegion; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import java.util.Map; + + +@Getter +public final class XPrisonSellAllEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private final Player player; + private final SellRegion region; + + @Getter + @Setter + private Map itemsToSell; + + @Getter + @Setter + private boolean cancelled; + + /** + * Called when mined blocks are automatically sold + * + * @param player Player + * @param reg SellRegion where block was mined + * @param itemsToSell Map of items to sell with prices as values + */ + public XPrisonSellAllEvent(Player player, SellRegion reg, Map itemsToSell) { + super(player); + this.player = player; + this.region = reg; + this.itemsToSell = itemsToSell; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/command/AutoSellCommand.java b/src/main/java/dev/drawethree/xprison/autosell/command/AutoSellCommand.java new file mode 100644 index 0000000..6b23fbd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/command/AutoSellCommand.java @@ -0,0 +1,26 @@ +package dev.drawethree.xprison.autosell.command; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.utils.AutoSellContants; +import me.lucko.helper.Commands; + +public class AutoSellCommand { + + private static final String COMMAND_NAME = "autosell"; + private final XPrisonAutoSell plugin; + + public AutoSellCommand(XPrisonAutoSell plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .assertPermission(AutoSellContants.AUTOSELL_PERMISSION, this.plugin.getAutoSellConfig().getMessage("no_permission_autosell_toggle")) + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getManager().toggleAutoSell(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), COMMAND_NAME); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/command/SellAllCommand.java b/src/main/java/dev/drawethree/xprison/autosell/command/SellAllCommand.java new file mode 100644 index 0000000..3c65d05 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/command/SellAllCommand.java @@ -0,0 +1,47 @@ +package dev.drawethree.xprison.autosell.command; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import me.lucko.helper.command.context.CommandContext; +import org.bukkit.entity.Player; +import org.codemc.worldguardwrapper.WorldGuardWrapper; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +public class SellAllCommand { + + private static final String COMMAND_NAME = "sellall"; + private final XPrisonAutoSell plugin; + + public SellAllCommand(XPrisonAutoSell plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .handler(c -> { + IWrappedRegion region = this.parseRegionFromCommandContext(c); + + if (region == null) { + PlayerUtils.sendMessage(c.sender(), this.plugin.getAutoSellConfig().getMessage("invalid_region")); + return; + } + + this.plugin.getManager().sellAll(c.sender(), region); + + }).registerAndBind(this.plugin.getCore(), COMMAND_NAME); + } + + private IWrappedRegion parseRegionFromCommandContext(CommandContext c) { + IWrappedRegion region = null; + if (c.args().size() == 0) { + region = RegionUtils.getRegionWithHighestPriority(c.sender().getLocation()); + } else if (c.args().size() == 1) { + String regionName = c.rawArg(0); + region = WorldGuardWrapper.getInstance().getRegion(c.sender().getLocation().getWorld(), regionName).orElse(null); + } + return region; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/command/SellPriceCommand.java b/src/main/java/dev/drawethree/xprison/autosell/command/SellPriceCommand.java new file mode 100644 index 0000000..fa670d8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/command/SellPriceCommand.java @@ -0,0 +1,139 @@ +package dev.drawethree.xprison.autosell.command; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.gui.AllSellRegionsGui; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.autosell.utils.AutoSellContants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import me.lucko.helper.command.CommandInterruptException; +import me.lucko.helper.command.context.CommandContext; +import org.bukkit.entity.Player; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +public class SellPriceCommand { + + private static final String COMMAND_NAME = "sellprice"; + private final XPrisonAutoSell plugin; + + public SellPriceCommand(XPrisonAutoSell plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .assertPermission(AutoSellContants.ADMIN_PERMISSION) + .handler(c -> { + + if (!this.validateContext(c)) { + this.sendInvalidUsage(c.sender()); + return; + } + + if (isEditorCommand(c)) { + this.openEditorGui(c.sender()); + return; + } + + CompMaterial type = this.parseMaterialFromCommandContext(c); + double price = this.parsePriceFromCommandContext(c); + + if (!validateMaterial(type)) { + PlayerUtils.sendMessage(c.sender(), "&cInvalid item in hand / specified item!"); + return; + } + + if (!validatePrice(price)) { + PlayerUtils.sendMessage(c.sender(), "&cSell price needs to be higher than 0!"); + return; + } + + IWrappedRegion wrappedRegion = RegionUtils.getFirstRegionAtLocation(c.sender().getLocation()); + + if (!validateRegion(wrappedRegion)) { + PlayerUtils.sendMessage(c.sender(), "&cYou must be standing in a region / specify a valid region!"); + return; + } + + SellRegion sellRegion = this.getSellRegionFromWrappedRegion(wrappedRegion); + + if (sellRegion == null) { + sellRegion = new SellRegion(wrappedRegion, c.sender().getWorld()); + } + + sellRegion.addSellPrice(type, price); + + this.plugin.getManager().updateSellRegion(sellRegion); + this.plugin.getAutoSellConfig().saveSellRegion(sellRegion); + + PlayerUtils.sendMessage(c.sender(), String.format("&aSuccessfuly set sell price of &e%s &ato &e$%.2f &ain region &e%s", type.name(), price, wrappedRegion.getId())); + + }).registerAndBind(this.plugin.getCore(), COMMAND_NAME); + } + + private void openEditorGui(Player sender) { + AllSellRegionsGui.createAndOpenTo(sender); + } + + private boolean isEditorCommand(CommandContext c) { + return "editor".equalsIgnoreCase(c.rawArg(0)); + } + + private SellRegion getSellRegionByName(String name) { + return this.plugin.getManager().getSellRegionByName(name); + } + + private SellRegion getSellRegionFromWrappedRegion(IWrappedRegion region) { + return this.plugin.getManager().getSellRegionFromWrappedRegion(region); + } + + private boolean validateRegion(IWrappedRegion region) { + return region != null; + } + + private boolean validatePrice(double price) { + return price > 0.0; + } + + private boolean validateMaterial(CompMaterial type) { + return type != null; + } + + private void sendInvalidUsage(Player player) { + PlayerUtils.sendMessage(player, "&cInvalid usage!"); + PlayerUtils.sendMessage(player, "&c/sellprice editor - Opens Editor GUI for sell prices"); + PlayerUtils.sendMessage(player, "&c/sellprice - Sets the sell price of specified material."); + PlayerUtils.sendMessage(player, "&c/sellprice - Sets the sell price of item material you have in your hand."); + } + + private boolean validateContext(CommandContext context) { + return context.args().size() == 1 || context.args().size() == 2 || context.args().size() == 3; + } + + private CompMaterial parseMaterialFromCommandContext(CommandContext c) { + CompMaterial material = null; + if (c.args().size() == 1) { + if (c.sender().getItemInHand() == null) { + PlayerUtils.sendMessage(c.sender(), "&cPlease hold some item!"); + } else { + material = CompMaterial.fromItem(c.sender().getItemInHand()); + } + } else if (c.args().size() == 2) { + material = CompMaterial.fromString(c.rawArg(0)); + } + return material; + } + + private double parsePriceFromCommandContext(CommandContext c) throws CommandInterruptException { + double price = 0.0; + if (c.args().size() == 1) { + price = c.arg(0).parseOrFail(Double.class); + } else if (c.args().size() == 2) { + price = c.arg(1).parseOrFail(Double.class); + } + return price; + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/config/AutoSellConfig.java b/src/main/java/dev/drawethree/xprison/autosell/config/AutoSellConfig.java new file mode 100644 index 0000000..5dd52fa --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/config/AutoSellConfig.java @@ -0,0 +1,112 @@ +package dev.drawethree.xprison.autosell.config; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AutoSellConfig { + + private final XPrisonAutoSell plugin; + private final FileManager.Config config; + + private Map messages; + private boolean enableAutosellAutomatically; + private boolean autoSmelt; + private int autoSellBroadcastTime; + private List autoSellBroadcastMessage; + private boolean inventoryFullNotificationEnabled; + private List inventoryFullNotificationTitle; + private String inventoryFullNotificationMessage; + + + public AutoSellConfig(XPrisonAutoSell plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("autosell.yml").copyDefaults(true).save(); + } + + public void reload() { + this.getConfig().reload(); + this.load(); + } + + public void load() { + this.loadVariables(); + } + + private void loadVariables() { + this.loadMessages(); + this.autoSellBroadcastTime = this.getYamlConfig().getInt("auto_sell_broadcast.time"); + this.autoSellBroadcastMessage = this.getYamlConfig().getStringList("auto_sell_broadcast.message"); + this.enableAutosellAutomatically = this.getYamlConfig().getBoolean("enable-autosell-automatically"); + this.autoSmelt = this.getYamlConfig().getBoolean("auto-smelt"); + this.inventoryFullNotificationEnabled = this.getYamlConfig().getBoolean("inventory_full_notification.enabled"); + this.inventoryFullNotificationTitle = this.getYamlConfig().getStringList("inventory_full_notification.title"); + this.inventoryFullNotificationMessage = this.getYamlConfig().getString("inventory_full_notification.chat"); + } + + private void loadMessages() { + this.messages = new HashMap<>(); + for (String key : this.getYamlConfig().getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(this.getYamlConfig().getString("messages." + key))); + } + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public String getMessage(String key) { + return messages.getOrDefault(key.toLowerCase(), "Message not found with key: " + key); + } + + public boolean isEnableAutosellAutomatically() { + return enableAutosellAutomatically; + } + + public boolean isAutoSmelt() { + return autoSmelt; + } + + public int getAutoSellBroadcastTime() { + return autoSellBroadcastTime; + } + + public List getAutoSellBroadcastMessage() { + return autoSellBroadcastMessage; + } + + public boolean isInventoryFullNotificationEnabled() { + return inventoryFullNotificationEnabled; + } + + public List getInventoryFullNotificationTitle() { + return inventoryFullNotificationTitle; + } + + public String getInventoryFullNotificationMessage() { + return inventoryFullNotificationMessage; + } + + public void saveSellRegion(SellRegion region) { + this.getConfig().set("regions." + region.getRegion().getId() + ".world", region.getWorld().getName()); + for (CompMaterial material : region.getSellingMaterials()) { + double sellPrice = region.getSellPriceForMaterial(material); + if (sellPrice <= 0.0) { + continue; + } + this.getConfig().set("regions." + region.getRegion().getId() + ".items." + material.name(), sellPrice); + } + this.getConfig().save(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/gui/AllSellRegionsGui.java b/src/main/java/dev/drawethree/xprison/autosell/gui/AllSellRegionsGui.java new file mode 100644 index 0000000..003c024 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/gui/AllSellRegionsGui.java @@ -0,0 +1,47 @@ +package dev.drawethree.xprison.autosell.gui; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.paginated.PaginatedGui; +import me.lucko.helper.menu.paginated.PaginatedGuiBuilder; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Collection; +import java.util.stream.Collectors; + +public final class AllSellRegionsGui { + + private AllSellRegionsGui() { + throw new UnsupportedOperationException("Cannot instantiate"); + } + + public static void createAndOpenTo(Player player) { + + PaginatedGuiBuilder builder = PaginatedGuiBuilder.create(); + + builder.title("Sell Regions"); + builder.lines(6); + builder.previousPageSlot(45); + builder.nextPageSlot(53); + builder.nextPageItem((pageInfo) -> ItemStackBuilder.of(Material.ARROW).name("&aNext Page").lore("&7Click to see next page.").build()); + builder.previousPageItem((pageInfo) -> ItemStackBuilder.of(Material.ARROW).name("&aPrevious Page").lore("&7Click to see previous page.").build()); + + Collection regions = getSellRegions(); + PaginatedGui gui = builder.build(player, paginatedGui -> regions.stream().map(sellRegion -> buildItemForSellRegion(sellRegion, player)).collect(Collectors.toList())); + gui.open(); + } + + private static Item buildItemForSellRegion(SellRegion sellRegion, Player player) { + return ItemStackBuilder.of(Material.DIAMOND_PICKAXE) + .name(sellRegion.getRegion().getId()) + .lore(" ", "&7Click to edit sell prices.", " ") + .build(() -> new SellRegionGui(sellRegion, player).open()); + } + + private static Collection getSellRegions() { + return XPrisonAutoSell.getInstance().getManager().getAutoSellRegions(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/gui/SellRegionGui.java b/src/main/java/dev/drawethree/xprison/autosell/gui/SellRegionGui.java new file mode 100644 index 0000000..fad389d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/gui/SellRegionGui.java @@ -0,0 +1,59 @@ +package dev.drawethree.xprison.autosell.gui; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.autosell.utils.SellPriceComparator; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public final class SellRegionGui extends Gui { + + private final SellRegion sellRegion; + + public SellRegionGui(SellRegion sellRegion, Player player) { + super(player, 6, sellRegion.getRegion().getId() + " Prices"); + this.sellRegion = sellRegion; + } + + @Override + public void redraw() { + this.clearItems(); + + this.setActionItems(); + + this.setBackItem(); + } + + private void setBackItem() { + this.setItem(45, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to all regions").build(() -> { + this.close(); + AllSellRegionsGui.createAndOpenTo(this.getPlayer()); + })); + } + + private void setActionItems() { + for (CompMaterial material : this.sellRegion.getSellingMaterialsSorted(new SellPriceComparator(sellRegion))) { + this.addItemForMaterial(material); + } + } + + + private void addItemForMaterial(CompMaterial material) { + double price = this.sellRegion.getSellPriceForMaterial(material); + + this.addItem(ItemStackBuilder.of(material.toItem()).name(material.name()).lore(" ", String.format("&7Sell Price: &2$&a%,.2f", price), " ", "&aLeft-Click &7to edit the price", "&aRight-Click &7to remove.").build(() -> { + this.deleteSellPrice(material); + this.redraw(); + }, () -> { + new UpdateSellPriceGui(this.getPlayer(), sellRegion, material).open(); + })); + } + + private void deleteSellPrice(CompMaterial material) { + this.sellRegion.removeSellPrice(material); + XPrisonAutoSell.getInstance().getAutoSellConfig().saveSellRegion(sellRegion); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/gui/UpdateSellPriceGui.java b/src/main/java/dev/drawethree/xprison/autosell/gui/UpdateSellPriceGui.java new file mode 100644 index 0000000..6aca82e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/gui/UpdateSellPriceGui.java @@ -0,0 +1,86 @@ +package dev.drawethree.xprison.autosell.gui; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public final class UpdateSellPriceGui extends Gui { + + private final SellRegion sellRegion; + private final CompMaterial material; + private double price; + + public UpdateSellPriceGui(Player player, SellRegion sellRegion, CompMaterial material) { + super(player, 5, "Editing Sell Price"); + this.sellRegion = sellRegion; + this.material = material; + this.price = sellRegion.getSellPriceForMaterial(material); + } + + @Override + public void redraw() { + this.setPreviewItem(); + this.setActionItems(); + this.setBackItem(); + this.setSaveItem(); + } + + private void setPreviewItem() { + this.setItem(4, ItemStackBuilder.of(this.material.toItem()).name("&eSell Price").lore(" ", "&7Selling price for this block", String.format("&7in region &b%s &7is &2$&a%,.2f", this.sellRegion.getRegion().getId(), this.price)).buildItem().build()); + } + + private void setSaveItem() { + this.setItem(40, ItemStackBuilder.of(CompMaterial.GREEN_WOOL.toItem()).name("&aSave").lore("&7Click to save the current price.").build(() -> { + this.saveChanges(); + this.close(); + new SellRegionGui(this.sellRegion, this.getPlayer()).open(); + })); + } + + private void setBackItem() { + this.setItem(36, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to all blocks.").build(() -> { + this.close(); + new SellRegionGui(this.sellRegion, this.getPlayer()).open(); + })); + } + + private void setActionItems() { + this.setItem(10, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$1.0").build(() -> handleAddition(1.0))); + this.setItem(11, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$5.0").build(() -> handleAddition(5.0))); + this.setItem(12, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$10.0").build(() -> handleAddition(10.0))); + this.setItem(19, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$25.0").build(() -> handleAddition(25.0))); + this.setItem(20, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$50.0").build(() -> handleAddition(50.0))); + this.setItem(21, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$100.0").build(() -> handleAddition(100.0))); + this.setItem(28, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$250.0").build(() -> handleAddition(250.0))); + this.setItem(29, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$500.0").build(() -> handleAddition(500.0))); + this.setItem(30, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+$1000.0").build(() -> handleAddition(1000.0))); + + this.setItem(14, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$1.0").build(() -> handleAddition(-1.0))); + this.setItem(15, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$5.0").build(() -> handleAddition(-5.0))); + this.setItem(16, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$10.0").build(() -> handleAddition(-10.0))); + this.setItem(23, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$25.0").build(() -> handleAddition(-25.0))); + this.setItem(24, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$50.0").build(() -> handleAddition(-50.0))); + this.setItem(25, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$100.0").build(() -> handleAddition(-100.0))); + this.setItem(32, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$250.0").build(() -> handleAddition(-250.0))); + this.setItem(33, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$500.0").build(() -> handleAddition(-500.0))); + this.setItem(34, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-$1000.0").build(() -> handleAddition(-1000.0))); + } + + private void saveChanges() { + this.sellRegion.addSellPrice(this.material, this.price); + XPrisonAutoSell.getInstance().getAutoSellConfig().saveSellRegion(sellRegion); + } + + private void handleAddition(double addition) { + if (this.price + addition < 0.0) { + this.price = 0.0; + } else { + this.price += addition; + } + this.redraw(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/listener/AutoSellListener.java b/src/main/java/dev/drawethree/xprison/autosell/listener/AutoSellListener.java new file mode 100644 index 0000000..5f0fb73 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/listener/AutoSellListener.java @@ -0,0 +1,79 @@ +package dev.drawethree.xprison.autosell.listener; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.world.WorldLoadEvent; + +public class AutoSellListener { + + private final XPrisonAutoSell plugin; + + public AutoSellListener(XPrisonAutoSell plugin) { + this.plugin = plugin; + } + + public void subscribeToEvents() { + this.subscribeToPlayerJoinEvent(); + this.subscribeToBlockBreakEvent(); + this.subscribeToWorldLoadEvent(); + } + + private void subscribeToWorldLoadEvent() { + Events.subscribe(WorldLoadEvent.class) + .handler(e -> this.plugin.getManager().loadPostponedAutoSellRegions(e.getWorld())).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerJoinEvent() { + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> Schedulers.sync().runLater(() -> { + + if (this.plugin.getManager().hasAutoSellEnabled(e.getPlayer())) { + PlayerUtils.sendMessage(e.getPlayer(), this.plugin.getAutoSellConfig().getMessage("autosell_enable")); + return; + } + + if (this.plugin.getManager().canPlayerEnableAutosellOnJoin(e.getPlayer())) { + this.plugin.getManager().toggleAutoSell(e.getPlayer()); + } + }, 20)).bindWith(this.plugin.getCore()); + } + + private void subscribeToBlockBreakEvent() { + + Events.subscribe(BlockBreakEvent.class, EventPriority.HIGHEST) + .filter(e -> !e.isCancelled() && e.getPlayer().getItemInHand() != null && this.plugin.getCore().isPickaxeSupported(e.getPlayer().getItemInHand().getType())) + .handler(e -> { + + SellRegion sellRegion = this.plugin.getManager().getAutoSellRegion(e.getBlock().getLocation()); + + if (sellRegion == null) { + return; + } + + boolean success = false; + + if (this.plugin.getManager().hasAutoSellEnabled(e.getPlayer())) { + success = this.plugin.getManager().autoSellBlock(e.getPlayer(), e.getBlock()); + } + + if (!success) { + success = this.plugin.getManager().givePlayerItem(e.getPlayer(), e.getBlock()); + } + + if (success) { + // Do not set block to air due compatibility issues + e.setDropItems(false); + } else { + e.setCancelled(true); + } + }).bindWith(this.plugin.getCore()); + } +} + diff --git a/src/main/java/dev/drawethree/xprison/autosell/manager/AutoSellManager.java b/src/main/java/dev/drawethree/xprison/autosell/manager/AutoSellManager.java new file mode 100644 index 0000000..cea119c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/manager/AutoSellManager.java @@ -0,0 +1,408 @@ +package dev.drawethree.xprison.autosell.manager; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.autosell.api.events.XPrisonAutoSellEvent; +import dev.drawethree.xprison.autosell.api.events.XPrisonSellAllEvent; +import dev.drawethree.xprison.autosell.model.AutoSellItemStack; +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.autosell.utils.AutoSellContants; +import dev.drawethree.xprison.enchants.utils.EnchantUtils; +import dev.drawethree.xprison.multipliers.enums.MultiplierType; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.economy.EconomyUtils; +import dev.drawethree.xprison.utils.inventory.InventoryUtils; +import dev.drawethree.xprison.utils.misc.MaterialUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.cooldown.Cooldown; +import me.lucko.helper.cooldown.CooldownMap; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.codemc.worldguardwrapper.WorldGuardWrapper; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class AutoSellManager { + + private static final CooldownMap INVENTORY_FULL_COOLDOWN_MAP = CooldownMap.create(Cooldown.of(2, TimeUnit.SECONDS)); + + private final XPrisonAutoSell plugin; + private final Map lastEarnings; + private final Map lastItems; + private final Map regionsAutoSell; + private final List enabledAutoSellPlayers; + private final Map> notLoadedSellRegions; + + public AutoSellManager(XPrisonAutoSell plugin) { + this.plugin = plugin; + this.enabledAutoSellPlayers = new ArrayList<>(); + this.lastEarnings = new HashMap<>(); + this.lastItems = new HashMap<>(); + this.regionsAutoSell = new HashMap<>(); + this.notLoadedSellRegions = new HashMap<>(); + } + + + private void loadAutoSellRegions() { + this.regionsAutoSell.clear(); + + YamlConfiguration configuration = this.plugin.getAutoSellConfig().getYamlConfig(); + + ConfigurationSection section = configuration.getConfigurationSection("regions"); + + if (section == null) { + return; + } + + for (String regName : section.getKeys(false)) { + this.loadSellRegionFromConfig(configuration, regName); + } + } + + private boolean loadSellRegionFromConfig(YamlConfiguration config, String regionName) { + + String worldName = config.getString("regions." + regionName + ".world"); + + World world = Bukkit.getWorld(worldName); + + if (world == null) { + this.plugin.getCore().getLogger().warning("There is no such World named " + worldName + "! Perhaps its no loaded yet?"); + this.plugin.getCore().getLogger().warning("Postponing loading of region " + regionName + "."); + this.postponeLoadingOfSellRegion(worldName, regionName); + return false; + } + + IWrappedRegion region = this.validateWrappedRegion(regionName, world); + + if (region == null) { + this.plugin.getCore().getLogger().warning("There is no such WorldGuard region named " + regionName + " in world " + world.getName()); + return false; + } + + String permRequired = config.getString("regions." + regionName + ".permission"); + + Map sellPrices = this.loadSellPricesForRegion(config, regionName); + + SellRegion sellRegion = new SellRegion(region, world, permRequired, sellPrices); + this.regionsAutoSell.put(regionName, sellRegion); + + this.plugin.getCore().getLogger().info("Loaded auto-sell region named '" + regionName + "'"); + return true; + } + + private void postponeLoadingOfSellRegion(String worldName, String regionName) { + Set currentlyPostponed = this.notLoadedSellRegions.getOrDefault(worldName, new HashSet<>()); + currentlyPostponed.add(regionName); + this.notLoadedSellRegions.put(worldName, currentlyPostponed); + } + + private IWrappedRegion validateWrappedRegion(String regionName, World world) { + Optional optRegion = WorldGuardWrapper.getInstance().getRegion(world, regionName); + return optRegion.orElse(null); + } + + private Map loadSellPricesForRegion(YamlConfiguration config, String regionName) { + Map sellPrices = new HashMap<>(); + + for (String item : config.getConfigurationSection("regions." + regionName + ".items").getKeys(false)) { + CompMaterial type = CompMaterial.valueOf(item); + double sellPrice = config.getDouble("regions." + regionName + ".items." + item); + sellPrices.put(type, sellPrice); + } + return sellPrices; + } + + public void reload() { + this.load(); + } + + public void load() { + this.loadAutoSellRegions(); + } + + + public void sellAll(Player sender, IWrappedRegion region) { + + if (!this.validateRegionBeforeSellAll(sender, region)) { + return; + } + + this.plugin.getCore().debug("User " + sender.getName() + " ran /sellall in region " + region.getId(), this.plugin); + + SellRegion sellRegion = regionsAutoSell.get(region.getId()); + + if (!checkIfPlayerCanSellInSellRegion(sender, sellRegion)) { + return; + } + + Map itemsToSell = sellRegion.previewInventorySell(sender); + + XPrisonSellAllEvent event = this.callSellAllEvent(sender, sellRegion, itemsToSell); + + if (event.isCancelled()) { + return; + } + + itemsToSell = event.getItemsToSell(); + + double totalAmount = this.sellItems(sender, itemsToSell); + + itemsToSell.keySet().forEach(item -> sender.getInventory().remove(item.getItemStack())); + + if (totalAmount > 0.0) { + PlayerUtils.sendMessage(sender, this.plugin.getAutoSellConfig().getMessage("sell_all_complete").replace("%price%", String.format("%,.0f", totalAmount))); + } + } + + private double sellItems(Player player, Map itemsToSell) { + + double totalAmount = itemsToSell.values().stream().mapToDouble(Double::doubleValue).sum(); + + if (this.plugin.isMultipliersModuleEnabled()) { + totalAmount = (long) this.plugin.getCore().getMultipliers().getApi().getTotalToDeposit(player, totalAmount, MultiplierType.SELL); + } + + EconomyUtils.deposit(player, totalAmount); + return totalAmount; + } + + private XPrisonSellAllEvent callSellAllEvent(Player sender, SellRegion sellRegion, Map sellItems) { + XPrisonSellAllEvent event = new XPrisonSellAllEvent(sender, sellRegion, sellItems); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("XPrisonSellAllEvent was cancelled.", this.plugin); + } + + return event; + } + + private XPrisonAutoSellEvent callAutoSellEvent(Player player, SellRegion sellRegion, Map itemsToSell) { + XPrisonAutoSellEvent event = new XPrisonAutoSellEvent(player, sellRegion, itemsToSell); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("XPrisonAutoSellEvent was cancelled.", this.plugin); + } + + return event; + } + + private boolean checkIfPlayerCanSellInSellRegion(Player sender, SellRegion sellRegion) { + if (!sellRegion.canPlayerSellInRegion(sender)) { + PlayerUtils.sendMessage(sender, this.plugin.getAutoSellConfig().getMessage("no_permission_sell").replace("%perm%", sellRegion.getPermissionRequired())); + return false; + } + return true; + } + + private boolean validateRegionBeforeSellAll(Player sender, IWrappedRegion region) { + + if (region == null) { + //PlayerUtils.sendMessage(sender, this.plugin.getAutoSellConfig().getMessage("not_in_region")); + return false; + } + + return isAutoSellRegion(region); + } + + private boolean isAutoSellRegion(IWrappedRegion region) { + return regionsAutoSell.containsKey(region.getId()); + } + + public void resetLastEarnings() { + this.lastEarnings.clear(); + } + + public void resetLastItems() { + this.lastItems.clear(); + } + + public double getPlayerLastEarnings(Player p) { + return this.lastEarnings.getOrDefault(p.getUniqueId(), 0.0D); + } + + public long getPlayerLastItemsAmount(Player p) { + return this.lastItems.getOrDefault(p.getUniqueId(), 0L); + } + + public double getCurrentEarnings(Player player) { + return lastEarnings.getOrDefault(player.getUniqueId(), 0.0); + } + + public double getPriceForItem(String regionName, ItemStack item) { + SellRegion region = regionsAutoSell.get(regionName); + if (region != null) { + return region.getPriceForItem(item); + } + return 0.0; + } + + public boolean hasAutoSellEnabled(Player p) { + return enabledAutoSellPlayers.contains(p.getUniqueId()); + } + + public Collection getAutoSellRegions() { + return this.regionsAutoSell.values(); + } + + public SellRegion getAutoSellRegion(Location location) { + for (SellRegion region : this.regionsAutoSell.values()) { + if (region.contains(location)) { + return region; + } + } + return null; + } + + public void toggleAutoSell(Player player) { + boolean removed = enabledAutoSellPlayers.remove(player.getUniqueId()); + + if (removed) { + PlayerUtils.sendMessage(player, this.plugin.getAutoSellConfig().getMessage("autosell_disable")); + } else { + PlayerUtils.sendMessage(player, this.plugin.getAutoSellConfig().getMessage("autosell_enable")); + enabledAutoSellPlayers.add(player.getUniqueId()); + } + } + + public boolean canPlayerEnableAutosellOnJoin(Player player) { + if (!this.plugin.getAutoSellConfig().isEnableAutosellAutomatically()) { + return false; + } + return player.hasPermission(AutoSellContants.AUTOSELL_PERMISSION) && !hasAutoSellEnabled(player); + } + + public SellRegion getSellRegionFromWrappedRegion(IWrappedRegion region) { + return regionsAutoSell.getOrDefault(region.getId(), null); + } + + public void updateSellRegion(SellRegion sellRegion) { + this.regionsAutoSell.put(sellRegion.getRegion().getId(), sellRegion); + } + + public boolean givePlayerItem(Player player, Block block) { + + if (!InventoryUtils.hasSpace(player.getInventory())) { + this.notifyInventoryFull(player); + return true; + } + + player.getInventory().addItem(createItemStackToGive(player, block)); + return true; + } + + private ItemStack createItemStackToGive(Player player, Block block) { + int amount = EnchantUtils.getFortuneBlockCount(player.getItemInHand(), block); + + ItemStack toGive; + + if (this.plugin.getAutoSellConfig().isAutoSmelt()) { + toGive = MaterialUtils.getSmeltedFormAsItemStack(block); + } else { + toGive = CompMaterial.fromBlock(block).toItem(); + } + toGive.setAmount(amount); + return toGive; + } + + private void notifyInventoryFull(Player player) { + + if (!this.plugin.getAutoSellConfig().isInventoryFullNotificationEnabled() || !INVENTORY_FULL_COOLDOWN_MAP.test(player)) { + return; + } + + List inventoryFullTitle = this.plugin.getAutoSellConfig().getInventoryFullNotificationTitle(); + String inventoryFullNotificationMessage = this.plugin.getAutoSellConfig().getInventoryFullNotificationMessage(); + + if (!inventoryFullTitle.isEmpty()) { + PlayerUtils.sendTitle(player, inventoryFullTitle.get(0), inventoryFullTitle.get(1)); + } else { + PlayerUtils.sendMessage(player, inventoryFullNotificationMessage); + } + } + + public boolean autoSellBlock(Player player, Block block) { + + SellRegion sellRegion = this.getAutoSellRegion(block.getLocation()); + + if (sellRegion == null) { + return false; + } + + Map itemsToSell = sellRegion.previewItemsSell(Arrays.asList(createItemStackToGive(player, block))); + + XPrisonAutoSellEvent event = this.callAutoSellEvent(player, sellRegion, itemsToSell); + + if (event.isCancelled()) { + return false; + } + + itemsToSell = event.getItemsToSell(); + + int amountOfItems = itemsToSell.keySet().stream().mapToInt(item -> item.getItemStack().getAmount()).sum(); + double moneyEarned = this.sellItems(player, itemsToSell); + + this.updateCurrentEarningsAndLastItems(player, moneyEarned, amountOfItems); + + return true; + } + + private void updateCurrentEarningsAndLastItems(Player player, double moneyEarned, int amountOfItems) { + this.addToCurrentEarnings(player, moneyEarned); + this.addToLastItems(player, amountOfItems); + } + + public void addToCurrentEarnings(Player player, double amount) { + double current = this.lastEarnings.getOrDefault(player.getUniqueId(), 0.0); + this.lastEarnings.put(player.getUniqueId(), current + amount); + } + + public void addToLastItems(Player player, int amountOfItems) { + long current = this.lastItems.getOrDefault(player.getUniqueId(), 0L); + this.lastItems.put(player.getUniqueId(), current + amountOfItems); + } + + public double getPriceForBlock(String regionName, Block block) { + CompMaterial material = CompMaterial.fromBlock(block); + SellRegion region = regionsAutoSell.get(regionName); + if (region != null) { + return region.getSellPriceForMaterial(material); + } + return 0.0; + } + + public double getPriceForBlock(Block block) { + CompMaterial material = CompMaterial.fromBlock(block); + SellRegion region = getAutoSellRegion(block.getLocation()); + if (region != null) { + return region.getSellPriceForMaterial(material); + } + return 0.0; + } + + public void loadPostponedAutoSellRegions(World world) { + YamlConfiguration configuration = this.plugin.getAutoSellConfig().getYamlConfig(); + Set regionNames = this.notLoadedSellRegions.getOrDefault(world.getName(), new HashSet<>()); + regionNames.removeIf(regionName -> this.loadSellRegionFromConfig(configuration, regionName)); + this.notLoadedSellRegions.put(world.getName(), regionNames); + } + + public SellRegion getSellRegionByName(String name) { + return regionsAutoSell.get(name); + } + + public void sellBlocks(Player player, List blocks) { + blocks.forEach(block -> autoSellBlock(player, block)); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/model/AutoSellBroadcastTask.java b/src/main/java/dev/drawethree/xprison/autosell/model/AutoSellBroadcastTask.java new file mode 100644 index 0000000..af3adc6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/model/AutoSellBroadcastTask.java @@ -0,0 +1,57 @@ +package dev.drawethree.xprison.autosell.model; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Schedulers; +import me.lucko.helper.scheduler.Task; +import me.lucko.helper.utils.Players; +import org.bukkit.entity.Player; + +import java.util.concurrent.TimeUnit; + +public final class AutoSellBroadcastTask implements Runnable { + + private final XPrisonAutoSell plugin; + private Task task; + + public AutoSellBroadcastTask(XPrisonAutoSell plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + + for (Player p : Players.all()) { + + double lastEarnings = this.plugin.getManager().getPlayerLastEarnings(p); + + if (lastEarnings <= 0.0) { + continue; + } + + long lastItems = this.plugin.getManager().getPlayerLastItemsAmount(p); + this.sendAutoSellBroadcastMessage(p, lastEarnings, lastItems); + } + + this.plugin.getManager().resetLastEarnings(); + this.plugin.getManager().resetLastItems(); + + } + + private void sendAutoSellBroadcastMessage(Player p, double lastEarnings, long lastItems) { + for (String s : this.plugin.getAutoSellConfig().getAutoSellBroadcastMessage()) { + PlayerUtils.sendMessage(p, s.replace("%money%", String.format("%,.0f", lastEarnings)).replace("%items%", String.format("%,d", lastItems))); + } + } + + + public void start() { + this.task = Schedulers.async().runRepeating(this, this.plugin.getAutoSellConfig().getAutoSellBroadcastTime(), TimeUnit.SECONDS, this.plugin.getAutoSellConfig().getAutoSellBroadcastTime(), TimeUnit.SECONDS); + } + + public void stop() { + if (this.task != null) { + this.task.stop(); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/model/AutoSellItemStack.java b/src/main/java/dev/drawethree/xprison/autosell/model/AutoSellItemStack.java new file mode 100644 index 0000000..925d850 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/model/AutoSellItemStack.java @@ -0,0 +1,20 @@ +package dev.drawethree.xprison.autosell.model; + +import org.bukkit.inventory.ItemStack; + +public class AutoSellItemStack { + + private final ItemStack itemStack; + + public AutoSellItemStack(ItemStack stack) { + this.itemStack = stack; + } + + public ItemStack getItemStack() { + return itemStack; + } + + public static AutoSellItemStack of(ItemStack item) { + return new AutoSellItemStack(item); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/model/SellRegion.java b/src/main/java/dev/drawethree/xprison/autosell/model/SellRegion.java new file mode 100644 index 0000000..9ffafc1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/model/SellRegion.java @@ -0,0 +1,102 @@ +package dev.drawethree.xprison.autosell.model; + +import dev.drawethree.xprison.utils.compat.CompMaterial; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.*; +import java.util.stream.Collectors; + +@AllArgsConstructor +@ToString +public class SellRegion { + + @Getter + private final IWrappedRegion region; + @Getter + private final World world; + + @Getter + private final String permissionRequired; + private final Map sellPrices; + + + public SellRegion(IWrappedRegion region, World world) { + this.region = region; + this.world = world; + this.permissionRequired = null; + this.sellPrices = new HashMap<>(); + } + + public double getSellPriceForMaterial(CompMaterial material) { + return sellPrices.getOrDefault(material, 0.0); + } + + public Set getSellingMaterials() { + return this.sellPrices.keySet(); + } + + public Set getSellingMaterialsSorted(Comparator comparator) { + return this.sellPrices.keySet().stream().sorted(comparator).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + public void addSellPrice(CompMaterial material, double price) { + this.sellPrices.put(material, price); + } + + public boolean contains(Location loc) { + return Objects.equals(loc.getWorld(), this.world) && this.region.contains(loc); + } + + public boolean canPlayerSellInRegion(Player player) { + if (this.permissionRequired == null || this.permissionRequired.isEmpty()) { + return true; + } + return player.hasPermission(this.permissionRequired); + } + + public double getPriceForItem(ItemStack item) { + CompMaterial material = CompMaterial.fromItem(item); + return item.getAmount() * this.sellPrices.getOrDefault(material, 0.0); + } + + public Map previewInventorySell(Player player) { + return previewItemsSell(Arrays.asList(player.getInventory().getContents())); + } + + public Map previewItemsSell(Collection items) { + + Map itemsToSell = new HashMap<>(); + + for (ItemStack item : items) { + + if (item == null) { + continue; + } + + double priceForItem = this.getPriceForItem(item); + + if (priceForItem <= 0.0) { + continue; + } + + itemsToSell.put(new AutoSellItemStack(item), priceForItem); + } + + return itemsToSell; + } + + public boolean sellsMaterial(CompMaterial material) { + return this.sellPrices.containsKey(material); + } + + public void removeSellPrice(CompMaterial material) { + this.sellPrices.remove(material); + } +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/utils/AutoSellContants.java b/src/main/java/dev/drawethree/xprison/autosell/utils/AutoSellContants.java new file mode 100644 index 0000000..5458736 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/utils/AutoSellContants.java @@ -0,0 +1,7 @@ +package dev.drawethree.xprison.autosell.utils; + +public class AutoSellContants { + + public static final String AUTOSELL_PERMISSION = "xprison.autosell.toggle"; + public static final String ADMIN_PERMISSION = "xprison.autosell.admin"; +} diff --git a/src/main/java/dev/drawethree/xprison/autosell/utils/SellPriceComparator.java b/src/main/java/dev/drawethree/xprison/autosell/utils/SellPriceComparator.java new file mode 100644 index 0000000..9c52d1c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/autosell/utils/SellPriceComparator.java @@ -0,0 +1,22 @@ +package dev.drawethree.xprison.autosell.utils; + +import dev.drawethree.xprison.autosell.model.SellRegion; +import dev.drawethree.xprison.utils.compat.CompMaterial; + +import java.util.Comparator; + +public class SellPriceComparator implements Comparator { + + private final SellRegion region; + + public SellPriceComparator(SellRegion region) { + this.region = region; + } + + @Override + public int compare(CompMaterial o1, CompMaterial o2) { + double sellPrice1 = region.getSellPriceForMaterial(o1); + double sellPrice2 = region.getSellPriceForMaterial(o2); + return Double.compare(sellPrice1, sellPrice2); + } +} diff --git a/src/main/java/dev/drawethree/xprison/config/FileManager.java b/src/main/java/dev/drawethree/xprison/config/FileManager.java new file mode 100644 index 0000000..24377be --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/config/FileManager.java @@ -0,0 +1,169 @@ +package dev.drawethree.xprison.config; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.*; +import java.util.HashMap; + + +public class FileManager { + + private final JavaPlugin plugin; + private HashMap configs = new HashMap(); + + public FileManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + /** + * Get the config by the name(Don't forget the .yml) + * + * @param name + * @return + */ + public Config getConfig(String name) { + if (!configs.containsKey(name)) + configs.put(name, new Config(name)); + + return configs.get(name); + } + + /** + * Save the config by the name(Don't forget the .yml) + * + * @param name + * @return + */ + public Config saveConfig(String name) { + return getConfig(name).save(); + } + + /** + * Reload the config by the name(Don't forget the .yml) + * + * @param name + * @return + */ + public Config reloadConfig(String name) { + return getConfig(name).reload(); + } + + public class Config { + + private String name; + private File file; + private YamlConfiguration config; + + public Config(String name) { + this.name = name; + } + + /** + * Saves the config as long as the config isn't empty + * + * @return + */ + public Config save() { + if ((this.config == null) || (this.file == null)) + return this; + try { + if (config.getConfigurationSection("").getKeys(true).size() != 0) + config.save(this.file); + } catch (IOException ex) { + ex.printStackTrace(); + } + return this; + } + + /** + * Gets the config as a YamlConfiguration + * + * @return + */ + public YamlConfiguration get() { + if (this.config == null) + reload(); + + return this.config; + } + + /** + * Saves the default config(Will overwrite anything in the current config's file) + *

+ * Don't forget to reload after! + * + * @return + */ + public Config saveDefaultConfig() { + file = new File(plugin.getDataFolder(), this.name); + + plugin.saveResource(this.name, false); + + return this; + } + + /** + * Reloads the config + * + * @return + */ + public Config reload() { + if (file == null) + this.file = new File(plugin.getDataFolder(), this.name); + + this.config = YamlConfiguration.loadConfiguration(file); + + Reader defConfigStream; + try { + defConfigStream = new InputStreamReader(plugin.getResource(this.name), "UTF8"); + + if (defConfigStream != null) { + YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream); + this.config.setDefaults(defConfig); + } + } catch (UnsupportedEncodingException | NullPointerException e) { + + } + return this; + } + + /** + * Copies the config from the resources to the config's default settings. + *

+ * Force = true ----> Will add any new values from the default file + *

+ * Force = false ---> Will NOT add new values from the default file + * + * @param force + * @return + */ + public Config copyDefaults(boolean force) { + get().options().copyDefaults(force); + return this; + } + + /** + * An easy way to set a value into the config + * + * @param key + * @param value + * @return + */ + public Config set(String key, Object value) { + get().set(key, value); + return this; + } + + /** + * An easy way to get a value from the config + * + * @param key + * @return + */ + public Object get(String key) { + return get().get(key); + } + } + +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/database/PooledSQLDatabase.java b/src/main/java/dev/drawethree/xprison/database/PooledSQLDatabase.java new file mode 100644 index 0000000..165d18f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/PooledSQLDatabase.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.database; + +import com.zaxxer.hikari.HikariDataSource; +import dev.drawethree.xprison.XPrison; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class PooledSQLDatabase extends SQLDatabase { + + protected static final AtomicInteger POOL_COUNTER = new AtomicInteger(0); + protected HikariDataSource hikari; + + public PooledSQLDatabase(XPrison plugin) { + super(plugin); + } + + @Override + public void close() { + if (this.hikari != null) { + this.hikari.close(); + this.plugin.getLogger().info("Database Connection closed"); + } + } + + @Override + public Connection getConnection() { + try { + return this.hikari.getConnection(); + } catch (SQLException e) { + this.plugin.getLogger().warning("Unable to get database connection!"); + e.printStackTrace(); + } + return null; + } + + +} diff --git a/src/main/java/dev/drawethree/xprison/database/SQLDatabase.java b/src/main/java/dev/drawethree/xprison/database/SQLDatabase.java new file mode 100644 index 0000000..f092768 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/SQLDatabase.java @@ -0,0 +1,87 @@ +package dev.drawethree.xprison.database; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import me.lucko.helper.Schedulers; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; + +public abstract class SQLDatabase { + + protected final XPrison plugin; + + SQLDatabase(XPrison plugin) { + this.plugin = plugin; + } + + public abstract SQLDatabaseType getDatabaseType(); + + public abstract void connect(); + + public abstract void close(); + + public abstract Connection getConnection(); + + public PreparedStatement prepareStatement(Connection connection, String sql, Object... replacements) { + + PreparedStatement statement; + try { + statement = connection.prepareStatement(sql); + this.replaceQueryParameters(statement,replacements); + + if (this.plugin.isDebugMode()) { + this.plugin.getLogger().info("Statement prepared: " + sql + " (Replacement values: " + Arrays.toString(replacements) + ")"); + } + + return statement; + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void executeSql(String sql, Object... replacements) { + + if (sql == null || sql.isEmpty()) { + return; + } + + long startTime = System.currentTimeMillis(); + + try (Connection c = getConnection(); PreparedStatement statement = prepareStatement(c,sql,replacements)) { + + statement.execute(); + + long endTime = System.currentTimeMillis(); + + if (this.plugin.isDebugMode()) { + this.plugin.getLogger().info("Statement executed: " + sql + " (Replacement values: " + Arrays.toString(replacements) + "). Took " + (endTime - startTime) + "ms."); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void replaceQueryParameters(PreparedStatement statement, Object[] replacements) { + if (replacements != null) { + for (int i = 0; i < replacements.length; i++) { + int position = i + 1; + Object value = replacements[i]; + try { + statement.setObject(position, value); + } catch (SQLException e) { + this.plugin.getLogger().warning("Unable to set query parameter at position " + position + " to " + value + " for query: " + statement); + e.printStackTrace(); + } + } + } + } + + public void executeSqlAsync(String sql, Object... replacements) { + Schedulers.async().run(() -> this.executeSql(sql, replacements)); + } +} diff --git a/src/main/java/dev/drawethree/xprison/database/impl/MySQLDatabase.java b/src/main/java/dev/drawethree/xprison/database/impl/MySQLDatabase.java new file mode 100644 index 0000000..eab2c94 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/impl/MySQLDatabase.java @@ -0,0 +1,68 @@ +package dev.drawethree.xprison.database.impl; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.database.PooledSQLDatabase; +import dev.drawethree.xprison.database.model.ConnectionProperties; +import dev.drawethree.xprison.database.model.DatabaseCredentials; +import dev.drawethree.xprison.database.model.SQLDatabaseType; + +public final class MySQLDatabase extends PooledSQLDatabase { + + private final DatabaseCredentials credentials; + private final ConnectionProperties connectionProperties; + + public MySQLDatabase(XPrison parent, DatabaseCredentials credentials, ConnectionProperties connectionProperties) { + super(parent); + this.connectionProperties = connectionProperties; + this.credentials = credentials; + } + + @Override + public void connect() { + final HikariConfig hikari = new HikariConfig(); + + hikari.setPoolName("xprison-" + POOL_COUNTER.getAndIncrement()); + + this.applyCredentials(hikari, credentials, connectionProperties); + this.applyConnectionProperties(hikari, connectionProperties); + this.addDefaultDataSourceProperties(hikari); + this.hikari = new HikariDataSource(hikari); + } + + private void applyCredentials(HikariConfig hikari, DatabaseCredentials credentials, ConnectionProperties connectionProperties) { + hikari.setJdbcUrl("jdbc:mysql://" + credentials.getHost() + ":" + credentials.getPort() + "/" + credentials.getDatabaseName() + "?characterEncoding=" + connectionProperties.getCharacterEncoding()); + hikari.setUsername(credentials.getUserName()); + hikari.setPassword(credentials.getPassword()); + } + + private void applyConnectionProperties(HikariConfig hikari, ConnectionProperties connectionProperties) { + hikari.setConnectionTimeout(connectionProperties.getConnectionTimeout()); + hikari.setIdleTimeout(connectionProperties.getIdleTimeout()); + hikari.setKeepaliveTime(connectionProperties.getKeepAliveTime()); + hikari.setMaxLifetime(connectionProperties.getMaxLifetime()); + hikari.setMinimumIdle(connectionProperties.getMinimumIdle()); + hikari.setMaximumPoolSize(connectionProperties.getMaximumPoolSize()); + hikari.setLeakDetectionThreshold(connectionProperties.getLeakDetectionThreshold()); + hikari.setConnectionTestQuery(connectionProperties.getTestQuery()); + } + + private void addDefaultDataSourceProperties(HikariConfig hikari) { + hikari.addDataSourceProperty("cachePrepStmts", true); + hikari.addDataSourceProperty("prepStmtCacheSize", 250); + hikari.addDataSourceProperty("prepStmtCacheSqlLimit", 2048); + hikari.addDataSourceProperty("useServerPrepStmts", true); + hikari.addDataSourceProperty("useLocalSessionState", true); + hikari.addDataSourceProperty("rewriteBatchedStatements", true); + hikari.addDataSourceProperty("cacheResultSetMetadata", true); + hikari.addDataSourceProperty("cacheServerConfiguration", true); + hikari.addDataSourceProperty("elideSetAutoCommits", true); + hikari.addDataSourceProperty("maintainTimeStats", false); + } + + @Override + public SQLDatabaseType getDatabaseType() { + return SQLDatabaseType.MYSQL; + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/database/impl/SQLiteDatabase.java b/src/main/java/dev/drawethree/xprison/database/impl/SQLiteDatabase.java new file mode 100644 index 0000000..1ca360f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/impl/SQLiteDatabase.java @@ -0,0 +1,65 @@ +package dev.drawethree.xprison.database.impl; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.database.PooledSQLDatabase; +import dev.drawethree.xprison.database.model.ConnectionProperties; +import dev.drawethree.xprison.database.model.SQLDatabaseType; + +import java.io.File; +import java.io.IOException; + +public final class SQLiteDatabase extends PooledSQLDatabase { + + private static final String FILE_NAME = "playerdata.db"; + + private final String filePath; + private final ConnectionProperties connectionProperties; + + public SQLiteDatabase(XPrison plugin, ConnectionProperties connectionProperties) { + super(plugin); + this.connectionProperties = connectionProperties; + this.filePath = this.plugin.getDataFolder().getPath() + File.separator + FILE_NAME; + } + + @Override + public SQLDatabaseType getDatabaseType() { + return SQLDatabaseType.SQLITE; + } + + @Override + public void connect() { + + this.createDBFile(); + + final HikariConfig hikari = new HikariConfig(); + + hikari.setPoolName("xprison-" + POOL_COUNTER.getAndIncrement()); + + hikari.setDriverClassName("org.sqlite.JDBC"); + hikari.setJdbcUrl("jdbc:sqlite:" + this.filePath); + + hikari.setConnectionTimeout(connectionProperties.getConnectionTimeout()); + hikari.setIdleTimeout(connectionProperties.getIdleTimeout()); + hikari.setKeepaliveTime(connectionProperties.getKeepAliveTime()); + hikari.setMaxLifetime(connectionProperties.getMaxLifetime()); + hikari.setMinimumIdle(connectionProperties.getMinimumIdle()); + hikari.setMaximumPoolSize(1); + hikari.setLeakDetectionThreshold(connectionProperties.getLeakDetectionThreshold()); + hikari.setConnectionTestQuery(connectionProperties.getTestQuery()); + + this.hikari = new HikariDataSource(hikari); + } + + private void createDBFile() { + File dbFile = new File(this.filePath); + try { + dbFile.createNewFile(); + } catch (IOException e) { + this.plugin.getLogger().warning(String.format("Unable to create %s", FILE_NAME)); + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/dev/drawethree/xprison/database/model/ConnectionProperties.java b/src/main/java/dev/drawethree/xprison/database/model/ConnectionProperties.java new file mode 100644 index 0000000..edf131f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/model/ConnectionProperties.java @@ -0,0 +1,31 @@ +package dev.drawethree.xprison.database.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.bukkit.configuration.file.FileConfiguration; + +@Getter +@AllArgsConstructor +public class ConnectionProperties { + + private final long idleTimeout, maxLifetime, connectionTimeout, leakDetectionThreshold, keepAliveTime; + private final int minimumIdle, maximumPoolSize; + private final String testQuery, characterEncoding; + + public static ConnectionProperties fromConfig(FileConfiguration config) { + + String rootPath = "connection_properties."; + + long connectionTimeout = config.getLong(rootPath + "connection_timeout"); + long idleTimeout = config.getLong(rootPath + "idle_timeout"); + long keepAliveTime = config.getLong(rootPath + "keep_alive_time"); + long maxLifeTime = config.getLong(rootPath + "max_life_time"); + int minimumIdle = config.getInt(rootPath + "minimum_idle"); + int maximumPoolSize = config.getInt(rootPath + "maximum_pool_size"); + long leakDetectionThreshold = config.getLong(rootPath + "leak_detection_threshold"); + String characterEncoding = config.getString(rootPath + "character_encoding", "utf8"); + String testQuery = config.getString(rootPath + "connection_test_query"); + return new ConnectionProperties(idleTimeout, maxLifeTime, connectionTimeout, leakDetectionThreshold, keepAliveTime, minimumIdle, maximumPoolSize, testQuery,characterEncoding); + } +} + diff --git a/src/main/java/dev/drawethree/xprison/database/model/DatabaseCredentials.java b/src/main/java/dev/drawethree/xprison/database/model/DatabaseCredentials.java new file mode 100644 index 0000000..c6c44e5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/model/DatabaseCredentials.java @@ -0,0 +1,33 @@ +package dev.drawethree.xprison.database.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.file.FileConfiguration; + +@Getter +@AllArgsConstructor +public class DatabaseCredentials { + + private final String host, databaseName, userName, password; + private final int port; + + public static DatabaseCredentials fromConfig(FileConfiguration config) { + + String rootPath = "mysql."; + + String host = config.getString(rootPath + "host"); + String dbName = config.getString(rootPath + "database"); + String userName = config.getString(rootPath + "username"); + String password = config.getString(rootPath + "password"); + int port = config.getInt(rootPath + "port"); + + Validate.notNull(host); + Validate.notNull(dbName); + Validate.notNull(userName); + Validate.notNull(password); + + return new DatabaseCredentials(host, dbName, userName, password, port); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/database/model/SQLDatabaseType.java b/src/main/java/dev/drawethree/xprison/database/model/SQLDatabaseType.java new file mode 100644 index 0000000..998af45 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/database/model/SQLDatabaseType.java @@ -0,0 +1,6 @@ +package dev.drawethree.xprison.database.model; + +public enum SQLDatabaseType { + SQLITE, + MYSQL +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/XPrisonEnchants.java b/src/main/java/dev/drawethree/xprison/enchants/XPrisonEnchants.java new file mode 100644 index 0000000..7d22d92 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/XPrisonEnchants.java @@ -0,0 +1,161 @@ +package dev.drawethree.xprison.enchants; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.enchants.api.XPrisonEnchantsAPI; +import dev.drawethree.xprison.enchants.api.XPrisonEnchantsAPIImpl; +import dev.drawethree.xprison.enchants.command.*; +import dev.drawethree.xprison.enchants.config.EnchantsConfig; +import dev.drawethree.xprison.enchants.gui.DisenchantGUI; +import dev.drawethree.xprison.enchants.gui.EnchantGUI; +import dev.drawethree.xprison.enchants.listener.EnchantsListener; +import dev.drawethree.xprison.enchants.managers.CooldownManager; +import dev.drawethree.xprison.enchants.managers.EnchantsManager; +import dev.drawethree.xprison.enchants.managers.RespawnManager; +import dev.drawethree.xprison.enchants.repo.EnchantsRepository; +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.multipliers.XPrisonMultipliers; +import lombok.Getter; +import me.lucko.helper.utils.Players; +import org.bukkit.entity.Player; + +public final class XPrisonEnchants implements XPrisonModule { + + + public static final String MODULE_NAME = "Enchants"; + + @Getter + private static XPrisonEnchants instance; + + @Getter + private XPrisonEnchantsAPI api; + + @Getter + private EnchantsManager enchantsManager; + + @Getter + private CooldownManager cooldownManager; + + @Getter + private RespawnManager respawnManager; + + @Getter + private EnchantsConfig enchantsConfig; + + @Getter + private EnchantsListener enchantsListener; + + @Getter + private EnchantsRepository enchantsRepository; + + @Getter + private final XPrison core; + + private boolean enabled; + + public XPrisonEnchants(XPrison core) { + instance = this; + this.core = core; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + + this.enchantsConfig.reload(); + this.enchantsRepository.reload(); + + EnchantGUI.init(); + DisenchantGUI.init(); + + } + + @Override + public void enable() { + + this.enchantsConfig = new EnchantsConfig(this); + this.enchantsConfig.load(); + + this.cooldownManager = new CooldownManager(this); + this.respawnManager = new RespawnManager(this); + + this.enchantsManager = new EnchantsManager(this); + this.enchantsManager.enable(); + + this.enchantsListener = new EnchantsListener(this); + this.enchantsListener.register(); + + this.registerCommands(); + + this.enchantsRepository = new EnchantsRepository(this); + this.enchantsRepository.loadDefaultEnchantments(); + + EnchantGUI.init(); + DisenchantGUI.init(); + + this.api = new XPrisonEnchantsAPIImpl(this.enchantsManager, this.enchantsRepository); + + + this.enabled = true; + } + + + private void registerCommands() { + DisenchantCommand disenchantCommand = new DisenchantCommand(this); + disenchantCommand.register(); + + EnchantMenuCommand enchantMenuCommand = new EnchantMenuCommand(this); + enchantMenuCommand.register(); + + GiveFirstJoinPickaxeCommand giveFirstJoinPickaxeCommand = new GiveFirstJoinPickaxeCommand(this); + giveFirstJoinPickaxeCommand.register(); + + GivePickaxeCommand givePickaxeCommand = new GivePickaxeCommand(this); + givePickaxeCommand.register(); + + ValueCommand valueCommand = new ValueCommand(this); + valueCommand.register(); + } + + + @Override + public void disable() { + for (Player p : Players.all()) { + p.closeInventory(); + } + this.enchantsManager.disable(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return false; + } + + @Override + public void resetPlayerData() { + } + + public boolean isAutoSellModuleEnabled() { + return this.core.isModuleEnabled(XPrisonAutoSell.MODULE_NAME); + } + + public boolean isMultipliersModuleEnabled() { + return this.core.isModuleEnabled(XPrisonMultipliers.MODULE_NAME); + } + + public boolean isMinesModuleEnabled() { + return this.core.isModuleEnabled(XPrisonMines.MODULE_NAME); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/XPrisonEnchantsAPI.java b/src/main/java/dev/drawethree/xprison/enchants/api/XPrisonEnchantsAPI.java new file mode 100644 index 0000000..6898e59 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/XPrisonEnchantsAPI.java @@ -0,0 +1,89 @@ +package dev.drawethree.xprison.enchants.api; + +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; + +public interface XPrisonEnchantsAPI { + + + /** + * Method to get all custom enchants applied on specific ItemStack + * + * @param itemStack ItemStack + * @return + */ + Map getEnchants(ItemStack itemStack); + + /** + * Method to check if item has specific enchant + * + * @param item {@link ItemStack} + * @param enchantment {@link XPrisonEnchantment} + * @return true if item has enchant + */ + boolean hasEnchant(ItemStack item, XPrisonEnchantment enchantment); + + /** + * Method to get enchant level of specific ItemStack + * + * @param item ItemStack + * @param enchantment {@link XPrisonEnchantment} + * @return 0 if enchant was not found, otherwise level of enchant + */ + int getEnchantLevel(ItemStack item, XPrisonEnchantment enchantment); + + /** + * Method to set enchant with specific level to pickaxe + * + * @param item pickaxe + * @param enchantment {@link XPrisonEnchantment} + * @param level Enchant Level + * @return modified ItemStack + */ + ItemStack setEnchantLevel(Player player, ItemStack item, XPrisonEnchantment enchantment, int level); + + /** + * Method to remove enchant from pickaxe + * + * @param item ItemStack pickaxe + * @param enchantment {@link XPrisonEnchantment} + * @return modified ItemStack + */ + ItemStack removeEnchant(Player player, ItemStack item, XPrisonEnchantment enchantment); + + /** + * Method to get Enchant by ID + * + * @param id enchant id + * @return XPrisonEnchantment + */ + XPrisonEnchantment getById(int id); + + /** + * Method to get Enchant by ID + * + * @param rawName enchant rawname + * @return XPrisonEnchantment + */ + XPrisonEnchantment getByName(String rawName); + + /** + * Registers a specific {@link XPrisonEnchantment} + * + * @param enchantment + * @return + */ + boolean registerEnchant(XPrisonEnchantment enchantment); + + /** + * Unregisters a specific {@link XPrisonEnchantment} + * + * @param enchantment + * @return + */ + boolean unregisterEnchant(XPrisonEnchantment enchantment); + +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/XPrisonEnchantsAPIImpl.java b/src/main/java/dev/drawethree/xprison/enchants/api/XPrisonEnchantsAPIImpl.java new file mode 100644 index 0000000..124550a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/XPrisonEnchantsAPIImpl.java @@ -0,0 +1,65 @@ +package dev.drawethree.xprison.enchants.api; + +import dev.drawethree.xprison.enchants.managers.EnchantsManager; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.repo.EnchantsRepository; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; + +public final class XPrisonEnchantsAPIImpl implements XPrisonEnchantsAPI { + + private final EnchantsManager enchantsManager; + private final EnchantsRepository enchantsRepository; + + public XPrisonEnchantsAPIImpl(EnchantsManager enchantsManager, EnchantsRepository enchantsRepository) { + this.enchantsManager = enchantsManager; + this.enchantsRepository = enchantsRepository; + } + + @Override + public Map getEnchants(ItemStack pickAxe) { + return this.enchantsManager.getItemEnchants(pickAxe); + } + + @Override + public boolean hasEnchant(ItemStack item, XPrisonEnchantment enchant) { + return getEnchantLevel(item, enchant) != 0; + } + + @Override + public int getEnchantLevel(ItemStack item, XPrisonEnchantment enchantment) { + return this.enchantsManager.getEnchantLevel(item, enchantment); + } + + @Override + public ItemStack setEnchantLevel(Player player, ItemStack item, XPrisonEnchantment enchantment, int level) { + return this.enchantsManager.setEnchantLevel(player, item, enchantment, level); + } + + @Override + public ItemStack removeEnchant(Player player, ItemStack item, XPrisonEnchantment enchantment) { + return this.enchantsManager.removeEnchant(player, item, enchantment); + } + + @Override + public XPrisonEnchantment getById(int id) { + return this.enchantsRepository.getEnchantById(id); + } + + @Override + public XPrisonEnchantment getByName(String rawName) { + return this.enchantsRepository.getEnchantByName(rawName); + } + + @Override + public boolean registerEnchant(XPrisonEnchantment enchantment) { + return this.enchantsRepository.register(enchantment); + } + + @Override + public boolean unregisterEnchant(XPrisonEnchantment enchantment) { + return this.enchantsRepository.unregister(enchantment); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/events/ExplosionTriggerEvent.java b/src/main/java/dev/drawethree/xprison/enchants/api/events/ExplosionTriggerEvent.java new file mode 100644 index 0000000..eca6a66 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/events/ExplosionTriggerEvent.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.enchants.api.events; + + +import lombok.Getter; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.List; + +@Getter +public final class ExplosionTriggerEvent extends XPrisonPlayerEnchantTriggerEvent { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + private boolean cancelled; + + /** + * Called when explosive enchant procs + * + * @param p Player + * @param mineRegion WorldGuard region where it was triggered + * @param originBlock Original block broken that triggered it + * @param blocksAffected List of affected blocks (marked for removal) + */ + public ExplosionTriggerEvent(Player p, IWrappedRegion mineRegion, Block originBlock, List blocksAffected) { + super(p, mineRegion, originBlock, blocksAffected); + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/events/LayerTriggerEvent.java b/src/main/java/dev/drawethree/xprison/enchants/api/events/LayerTriggerEvent.java new file mode 100644 index 0000000..55906dd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/events/LayerTriggerEvent.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.enchants.api.events; + + +import lombok.Getter; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.List; + +@Getter +public final class LayerTriggerEvent extends XPrisonPlayerEnchantTriggerEvent { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + private boolean cancelled; + + /** + * Called when layer enchant procs + * + * @param p Player + * @param mineRegion WorldGuard region where it was triggered + * @param originBlock Original block broken that triggered it + * @param blocks List of affected blocks (marked for removal) + */ + public LayerTriggerEvent(Player p, IWrappedRegion mineRegion, Block originBlock, List blocks) { + super(p, mineRegion, originBlock, blocks); + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/events/NukeTriggerEvent.java b/src/main/java/dev/drawethree/xprison/enchants/api/events/NukeTriggerEvent.java new file mode 100644 index 0000000..bb24ba8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/events/NukeTriggerEvent.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.enchants.api.events; + + +import lombok.Getter; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.List; + +@Getter +public final class NukeTriggerEvent extends XPrisonPlayerEnchantTriggerEvent { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + private boolean cancelled; + + /** + * Called when nuke enchant procs + * + * @param p Player + * @param mineRegion WorldGuard region where it was triggered + * @param originBlock Original block broken that triggered it + * @param blocks List of affected blocks (marked for removal) + */ + public NukeTriggerEvent(Player p, IWrappedRegion mineRegion, Block originBlock, List blocks) { + super(p, mineRegion, originBlock, blocks); + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/events/XPrisonPlayerEnchantEvent.java b/src/main/java/dev/drawethree/xprison/enchants/api/events/XPrisonPlayerEnchantEvent.java new file mode 100644 index 0000000..be6f0a5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/events/XPrisonPlayerEnchantEvent.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.enchants.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class XPrisonPlayerEnchantEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + @Getter + @Setter + private long tokenCost; + @Getter + private final int level; + @Getter + @Setter + private boolean cancelled; + + + /** + * Called when player enchants a tool + * + * @param player Player + * @param tokenCost cost of enchant in tokens + * @param level level of enchant + */ + public XPrisonPlayerEnchantEvent(Player player, long tokenCost, int level) { + super(player); + this.tokenCost = tokenCost; + this.level = level; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/api/events/XPrisonPlayerEnchantTriggerEvent.java b/src/main/java/dev/drawethree/xprison/enchants/api/events/XPrisonPlayerEnchantTriggerEvent.java new file mode 100644 index 0000000..6c43436 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/api/events/XPrisonPlayerEnchantTriggerEvent.java @@ -0,0 +1,27 @@ +package dev.drawethree.xprison.enchants.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.List; + +@Getter +public abstract class XPrisonPlayerEnchantTriggerEvent extends XPrisonPlayerEvent implements Cancellable { + + protected final Player player; + protected final IWrappedRegion mineRegion; + protected final Block originBlock; + protected final List blocksAffected; + + public XPrisonPlayerEnchantTriggerEvent(Player p, IWrappedRegion mineRegion, Block originBlock, List blocksAffected) { + super(p); + this.player = p; + this.mineRegion = mineRegion; + this.originBlock = originBlock; + this.blocksAffected = blocksAffected; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/command/DisenchantCommand.java b/src/main/java/dev/drawethree/xprison/enchants/command/DisenchantCommand.java new file mode 100644 index 0000000..26ce36b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/command/DisenchantCommand.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.enchants.command; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.gui.DisenchantGUI; +import dev.drawethree.xprison.utils.inventory.InventoryUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class DisenchantCommand { + + private final XPrisonEnchants plugin; + + public DisenchantCommand(XPrisonEnchants plugin) { + + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .handler(c -> { + ItemStack pickAxe = c.sender().getItemInHand(); + + if (!validatePickaxe(pickAxe)) { + PlayerUtils.sendMessage(c.sender(), this.plugin.getEnchantsConfig().getMessage("no_pickaxe_found")); + return; + } + + openDisenchantGui(pickAxe, c.sender()); + + }).registerAndBind(this.plugin.getCore(), "disenchant", "dise", "de", "disenchantmenu", "dismenu"); + } + + private void openDisenchantGui(ItemStack pickAxe, Player player) { + int pickaxeSlot = InventoryUtils.getInventorySlot(player, pickAxe); + this.plugin.getCore().debug("Pickaxe slot is: " + pickaxeSlot, this.plugin); + new DisenchantGUI(this.plugin, player, pickAxe, pickaxeSlot).open(); + } + + private boolean validatePickaxe(ItemStack pickAxe) { + return pickAxe != null && this.plugin.getCore().isPickaxeSupported(pickAxe.getType()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/command/EnchantMenuCommand.java b/src/main/java/dev/drawethree/xprison/enchants/command/EnchantMenuCommand.java new file mode 100644 index 0000000..b5239b2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/command/EnchantMenuCommand.java @@ -0,0 +1,44 @@ +package dev.drawethree.xprison.enchants.command; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.gui.EnchantGUI; +import dev.drawethree.xprison.utils.inventory.InventoryUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class EnchantMenuCommand { + + private final XPrisonEnchants plugin; + + public EnchantMenuCommand(XPrisonEnchants plugin) { + + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .handler(c -> { + ItemStack pickAxe = c.sender().getItemInHand(); + + if (!validatePickaxe(pickAxe)) { + PlayerUtils.sendMessage(c.sender(), this.plugin.getEnchantsConfig().getMessage("no_pickaxe_found")); + return; + } + + openEnchantMenu(pickAxe, c.sender()); + }).registerAndBind(this.plugin.getCore(), "enchantmenu", "enchmenu"); + } + + private void openEnchantMenu(ItemStack pickAxe, Player player) { + int pickaxeSlot = InventoryUtils.getInventorySlot(player, pickAxe); + this.plugin.getCore().debug("Pickaxe slot is: " + pickaxeSlot, this.plugin); + new EnchantGUI(this.plugin, player, pickAxe, pickaxeSlot).open(); + } + + private boolean validatePickaxe(ItemStack pickAxe) { + return pickAxe != null && this.plugin.getCore().isPickaxeSupported(pickAxe.getType()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/command/GiveFirstJoinPickaxeCommand.java b/src/main/java/dev/drawethree/xprison/enchants/command/GiveFirstJoinPickaxeCommand.java new file mode 100644 index 0000000..cc72dd4 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/command/GiveFirstJoinPickaxeCommand.java @@ -0,0 +1,33 @@ +package dev.drawethree.xprison.enchants.command; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; + +public class GiveFirstJoinPickaxeCommand { + + private final XPrisonEnchants plugin; + + public GiveFirstJoinPickaxeCommand(XPrisonEnchants plugin) { + + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertOp() + .handler(c -> { + + if (c.args().size() == 0) { + PlayerUtils.sendMessage(c.sender(), "&c/givefirstjoinpickaxe "); + return; + } + + Player target = c.arg(0).parseOrFail(Player.class); + + this.plugin.getEnchantsManager().giveFirstJoinPickaxe(target); + PlayerUtils.sendMessage(c.sender(), "&aYou have given first join pickaxe to &e" + target.getName()); + }).registerAndBind(this.plugin.getCore(), "givefirstjoinpickaxe"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/command/GivePickaxeCommand.java b/src/main/java/dev/drawethree/xprison/enchants/command/GivePickaxeCommand.java new file mode 100644 index 0000000..f5e20cb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/command/GivePickaxeCommand.java @@ -0,0 +1,82 @@ +package dev.drawethree.xprison.enchants.command; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.repo.EnchantsRepository; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.apache.commons.lang.StringUtils; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class GivePickaxeCommand { + + private final XPrisonEnchants plugin; + + public GivePickaxeCommand(XPrisonEnchants plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertOp() + .handler(c -> { + + if (c.args().size() == 0) { + PlayerUtils.sendMessage(c.sender(), "&c/givepickaxe <[enchant1]=[level1],[enchant2]=[level2],...[enchantX]=[levelX]> "); + return; + } + + String input = null, name = null; + Player target = null; + + if (c.args().size() == 1) { + input = c.rawArg(0); + } else if (c.args().size() == 2) { + target = c.arg(0).parseOrFail(Player.class); + input = c.rawArg(1); + } else if (c.args().size() >= 3) { + target = c.arg(0).parseOrFail(Player.class); + input = c.rawArg(1); + name = StringUtils.join(c.args().subList(2, c.args().size()), " "); + } + + Map enchants = parseEnchantsFromInput(input); + + this.plugin.getEnchantsManager().givePickaxe(target, enchants, name, c.sender()); + }).registerAndBind(this.plugin.getCore(), "givepickaxe"); + } + + + private Map parseEnchantsFromInput(String input) { + Map enchants = new HashMap<>(); + + String[] split = input.split(","); + for (String s : split) { + String[] enchantData = s.split("="); + + try { + XPrisonEnchantment enchantment = getEnchantsRepository().getEnchantByName(enchantData[0]); + if (enchantment == null) { + enchantment = getEnchantsRepository().getEnchantById(Integer.parseInt(enchantData[0])); + } + + if (enchantment == null) { + continue; + } + + int enchantLevel = Integer.parseInt(enchantData[1]); + enchants.put(enchantment, enchantLevel); + } catch (Exception ignored) { + + } + } + return enchants; + } + + private EnchantsRepository getEnchantsRepository() { + return this.plugin.getEnchantsRepository(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/command/ValueCommand.java b/src/main/java/dev/drawethree/xprison/enchants/command/ValueCommand.java new file mode 100644 index 0000000..cf3cf22 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/command/ValueCommand.java @@ -0,0 +1,54 @@ +package dev.drawethree.xprison.enchants.command; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.managers.CooldownManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class ValueCommand { + + private static final String COMMAND_NAME = "value"; + + private final XPrisonEnchants plugin; + + public ValueCommand(XPrisonEnchants plugin) { + this.plugin = plugin; + } + + + public void register() { + Commands.create() + .assertPlayer() + .assertPermission("xprison.value", this.plugin.getEnchantsConfig().getMessage("value_no_permission")) + .handler(c -> { + + if (!checkCooldown(c.sender())) { + PlayerUtils.sendMessage(c.sender(), this.plugin.getEnchantsConfig().getMessage("value_cooldown").replace("%time%", String.valueOf(this.getCooldownManager().getRemainingTime(c.sender())))); + return; + } + + ItemStack pickAxe = c.sender().getItemInHand(); + + if (!validatePickaxe(pickAxe)) { + PlayerUtils.sendMessage(c.sender(), this.plugin.getEnchantsConfig().getMessage("value_no_pickaxe")); + return; + } + + PlayerUtils.sendMessage(c.sender(), this.plugin.getEnchantsConfig().getMessage("value_value").replace("%player%", c.sender().getName()).replace("%tokens%", String.format("%,d", this.plugin.getEnchantsManager().getPickaxeValue(pickAxe)))); + }).registerAndBind(plugin.getCore(), COMMAND_NAME); + } + + private boolean validatePickaxe(ItemStack pickAxe) { + return pickAxe != null && this.plugin.getCore().isPickaxeSupported(pickAxe.getType()); + } + + private boolean checkCooldown(Player sender) { + return (sender.isOp() || !getCooldownManager().hasValueCooldown(sender)); + } + + private CooldownManager getCooldownManager() { + return this.plugin.getCooldownManager(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/config/EnchantsConfig.java b/src/main/java/dev/drawethree/xprison/enchants/config/EnchantsConfig.java new file mode 100644 index 0000000..9f9fd23 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/config/EnchantsConfig.java @@ -0,0 +1,124 @@ +package dev.drawethree.xprison.enchants.config; + +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.LevelFormat; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.block.Action; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class EnchantsConfig { + + private final XPrisonEnchants plugin; + private final FileManager.Config config; + + private Map messages; + + private LevelFormat levelFormat; + private String excludedFormat; + private List pickaxeLore; + private boolean allowEnchantsOutside; + private boolean firstJoinPickaxeEnabled; + private CompMaterial firstJoinPickaxeMaterial; + private List firstJoinPickaxeEnchants; + private String firstJoinPickaxeName; + private boolean keepPickaxesOnDeath; + private boolean useUnbreakablePermission; + private List openEnchantMenuActions; + + public EnchantsConfig(XPrisonEnchants plugin) { + this.plugin = plugin; + this.config = plugin.getCore().getFileManager().getConfig("enchants.yml").copyDefaults(true).save(); + } + + public void reload() { + this.getConfig().reload(); + this.load(); + } + + public void load() { + this.loadVariables(); + this.loadMessages(); + } + + private void loadVariables() { + this.levelFormat = LevelFormat.of(getYamlConfig().getString("Pickaxe.level-format", "NUMBER")); + this.excludedFormat = getYamlConfig().getString("Pickaxe.excluded-format", "&7[&c-&7] &8%Enchant% %Level%"); + this.pickaxeLore = getYamlConfig().getStringList("Pickaxe.lore"); + this.openEnchantMenuActions = Arrays.stream(getYamlConfig().getString("open-enchant-menu-action", "RIGHT_CLICK_AIR,RIGHT_CLICK_BLOCK").split(",")).map(s-> Action.valueOf(s.toUpperCase())).collect(Collectors.toList()); + this.allowEnchantsOutside = getYamlConfig().getBoolean("allow-enchants-outside-mine-regions"); + this.firstJoinPickaxeEnabled = getYamlConfig().getBoolean("first-join-pickaxe.enabled"); + this.firstJoinPickaxeMaterial = CompMaterial.fromString(getYamlConfig().getString("first-join-pickaxe.material")); + this.firstJoinPickaxeEnchants = getYamlConfig().getStringList("first-join-pickaxe.enchants"); + this.firstJoinPickaxeName = getYamlConfig().getString("first-join-pickaxe.name"); + this.keepPickaxesOnDeath = getYamlConfig().getBoolean("keep-pickaxes-on-death"); + this.useUnbreakablePermission = getYamlConfig().getBoolean("use-unbreakable-permission"); + } + + private void loadMessages() { + this.messages = new HashMap<>(); + for (String key : getYamlConfig().getConfigurationSection("messages").getKeys(false)) { + messages.put(key, TextUtils.applyColor(getConfig().get().getString("messages." + key))); + } + } + + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public String getMessage(String key) { + return messages.getOrDefault(key.toLowerCase(), "Message not found with key: " + key); + } + + public LevelFormat getLevelFormat() { + return levelFormat; + } + + public String getExcludedFormat() { + return excludedFormat; + } + + public List getPickaxeLore() { + return pickaxeLore; + } + + public boolean isFirstJoinPickaxeEnabled() { + return firstJoinPickaxeEnabled; + } + + public CompMaterial getFirstJoinPickaxeMaterial() { + return firstJoinPickaxeMaterial; + } + + public List getFirstJoinPickaxeEnchants() { + return firstJoinPickaxeEnchants; + } + + public String getFirstJoinPickaxeName() { + return firstJoinPickaxeName; + } + + public boolean isKeepPickaxesOnDeath() { + return keepPickaxesOnDeath; + } + + public boolean isUseUnbreakablePermission() { + return useUnbreakablePermission; + } + + public List getOpenEnchantMenuActions() { + return openEnchantMenuActions; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/gui/DisenchantGUI.java b/src/main/java/dev/drawethree/xprison/enchants/gui/DisenchantGUI.java new file mode 100644 index 0000000..66b1901 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/gui/DisenchantGUI.java @@ -0,0 +1,159 @@ +package dev.drawethree.xprison.enchants.gui; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.utils.GuiUtils; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import lombok.Setter; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public final class DisenchantGUI extends Gui { + + private static List GUI_ITEM_LORE; + private static String GUI_TITLE; + private static Item EMPTY_SLOT_ITEM; + private static Item HELP_ITEM; + private static int HELP_ITEM_SLOT; + private static int PICKAXE_ITEM_SLOT; + private static int GUI_LINES; + private static boolean PICKAXE_ITEM_ENABLED; + private static boolean HELP_ITEM_ENABLED; + + @Getter + @Setter + private ItemStack pickAxe; + + @Getter + private final int pickaxePlayerInventorySlot; + + private final XPrisonEnchants plugin; + + public DisenchantGUI(XPrisonEnchants plugin, Player player, ItemStack pickAxe, int pickaxePlayerInventorySlot) { + super(player, GUI_LINES, GUI_TITLE); + this.plugin = plugin; + this.pickAxe = pickAxe; + this.pickaxePlayerInventorySlot = pickaxePlayerInventorySlot; + + Events.subscribe(InventoryCloseEvent.class, EventPriority.LOWEST) + .filter(e -> e.getInventory().equals(this.getHandle())) + .handler(e -> { + XPrison.getInstance().getEnchants().getEnchantsManager().handlePickaxeUnequip(this.getPlayer(), this.pickAxe); + XPrison.getInstance().getEnchants().getEnchantsManager().handlePickaxeEquip(this.getPlayer(), this.pickAxe); + }).bindWith(this); + + Schedulers.sync().runLater(() -> { + if (!pickAxe.equals(this.getPlayer().getInventory().getItem(this.pickaxePlayerInventorySlot))) { + this.close(); + } + }, 10); + setFallbackGui(player1 -> new EnchantGUI(plugin, player, pickAxe, pickaxePlayerInventorySlot)); + + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + for (int i = 0; i < this.getHandle().getSize(); i++) { + this.setItem(i, EMPTY_SLOT_ITEM); + } + } + + + if (HELP_ITEM_ENABLED) { + this.setItem(HELP_ITEM_SLOT, HELP_ITEM); + } + + if (PICKAXE_ITEM_ENABLED) { + this.setItem(PICKAXE_ITEM_SLOT, Item.builder(pickAxe).build()); + } + + Collection allEnchants = this.plugin.getEnchantsRepository().getAll(); + + for (XPrisonEnchantment enchantment : allEnchants) { + + if (!enchantment.isRefundEnabled() || !enchantment.isEnabled()) { + continue; + } + + int level = XPrisonEnchants.getInstance().getEnchantsManager().getEnchantLevel(this.pickAxe, enchantment); + this.setItem(enchantment.getRefundGuiSlot(), getRefundGuiItem(enchantment, this, level)); + } + } + + + private Item getRefundGuiItem(XPrisonEnchantment enchantment, DisenchantGUI gui, int level) { + Material m = enchantment.isRefundEnabled() ? enchantment.getMaterial() : CompMaterial.BARRIER.toMaterial(); + ItemStackBuilder builder = ItemStackBuilder.of(m); + + if (enchantment.getBase64() != null && !enchantment.getBase64().isEmpty()) { + builder = ItemStackBuilder.of(SkullUtils.getCustomTextureHead(enchantment.getBase64())); + } + + builder.name(enchantment.isRefundEnabled() ? enchantment.getGuiName() : this.plugin.getEnchantsConfig().getMessage("enchant_cant_disenchant")); + builder.lore(enchantment.isRefundEnabled() ? GuiUtils.translateGuiLore(enchantment, GUI_ITEM_LORE, level) : new ArrayList<>()); + + return enchantment.isRefundEnabled() ? builder.buildItem().bind(handler -> { + if (handler.getClick() == ClickType.MIDDLE || handler.getClick() == ClickType.SHIFT_RIGHT) { + this.plugin.getEnchantsManager().disenchant(enchantment, gui, level, 100); + gui.redraw(); + } else if (handler.getClick() == ClickType.LEFT) { + this.plugin.getEnchantsManager().disenchant(enchantment, gui, level, 1); + gui.redraw(); + } else if (handler.getClick() == ClickType.RIGHT) { + this.plugin.getEnchantsManager().disenchant(enchantment, gui, level, 10); + gui.redraw(); + } else if (handler.getClick() == ClickType.DROP) { + this.plugin.getEnchantsManager().disenchantMax(enchantment, gui, level); + } + }, ClickType.MIDDLE, ClickType.SHIFT_RIGHT, ClickType.LEFT, ClickType.RIGHT, ClickType.DROP).build() : builder.buildConsumer(handler -> handler.getWhoClicked().sendMessage(this.plugin.getEnchantsConfig().getMessage("enchant_cant_disenchant"))); + } + + public static void init() { + + GUI_ITEM_LORE = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("disenchant_menu.item.lore"); + GUI_TITLE = TextUtils.applyColor(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("disenchant_menu.title")); + GUI_LINES = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("disenchant_menu.lines"); + + EMPTY_SLOT_ITEM = ItemStackBuilder. + of(CompMaterial.fromString(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("disenchant_menu.empty_slots")).toItem()).buildItem().build(); + + HELP_ITEM_ENABLED = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getBoolean("disenchant_menu.help_item.enabled", true); + PICKAXE_ITEM_ENABLED = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getBoolean("disenchant_menu.pickaxe_enabled", true); + + if (HELP_ITEM_ENABLED) { + String base64 = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("disenchant_menu.help_item.Base64", null); + + if (base64 != null) { + HELP_ITEM = ItemStackBuilder.of(SkullUtils.getCustomTextureHead(base64)) + .name(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("disenchant_menu.help_item.name")).lore(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("disenchant_menu.help_item.lore")).buildItem().build(); + } else { + HELP_ITEM = ItemStackBuilder.of(CompMaterial.fromString(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("disenchant_menu.help_item.material")).toMaterial()) + .name(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("disenchant_menu.help_item.name")).lore(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("disenchant_menu.help_item.lore")).buildItem().build(); + } + HELP_ITEM_SLOT = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("disenchant_menu.help_item.slot"); + } + + if (PICKAXE_ITEM_ENABLED) { + PICKAXE_ITEM_SLOT = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("disenchant_menu.pickaxe_slot"); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/gui/EnchantGUI.java b/src/main/java/dev/drawethree/xprison/enchants/gui/EnchantGUI.java new file mode 100644 index 0000000..ba1ae4e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/gui/EnchantGUI.java @@ -0,0 +1,183 @@ +package dev.drawethree.xprison.enchants.gui; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.utils.GuiUtils; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import lombok.Setter; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; +import java.util.List; + +public final class EnchantGUI extends Gui { + + private static List GUI_ITEM_LORE; + private static String GUI_TITLE; + private static Item EMPTY_SLOT_ITEM; + private static int PICKAXE_ITEM_SLOT; + private static int HELP_ITEM_SLOT; + private static int DISENCHANT_ITEM_SLOT; + private static int GUI_LINES; + private static Item HELP_ITEM; + private static ItemStack DISENCHANT_ITEM; + private static boolean PICKAXE_ITEM_ENABLED; + private static boolean HELP_ITEM_ENABLED; + private static boolean DISENCHANT_ITEM_ENABLED; + + @Getter + @Setter + private ItemStack pickAxe; + + @Getter + private final int pickaxePlayerInventorySlot; + + private final XPrisonEnchants plugin; + + public EnchantGUI(XPrisonEnchants plugin, Player player, ItemStack pickAxe, int pickaxePlayerInventorySlot) { + super(player, GUI_LINES, GUI_TITLE); + this.plugin = plugin; + this.pickAxe = pickAxe; + this.pickaxePlayerInventorySlot = pickaxePlayerInventorySlot; + + Events.subscribe(InventoryCloseEvent.class, EventPriority.LOWEST) + .filter(e -> e.getInventory().equals(this.getHandle())) + .handler(e -> { + XPrison.getInstance().getEnchants().getEnchantsManager().handlePickaxeUnequip(this.getPlayer(), this.pickAxe); + XPrison.getInstance().getEnchants().getEnchantsManager().handlePickaxeEquip(this.getPlayer(), this.pickAxe); + }).bindWith(this); + + // Checking for duping + Schedulers.sync().runLater(() -> { + if (!pickAxe.equals(this.getPlayer().getInventory().getItem(this.pickaxePlayerInventorySlot))) { + this.close(); + } + },10); + } + + @Override + public void redraw() { + + // perform initial setup. + if (isFirstDraw()) { + for (int i = 0; i < this.getHandle().getSize(); i++) { + this.setItem(i, EMPTY_SLOT_ITEM); + } + } + + if (HELP_ITEM_ENABLED) { + this.setItem(HELP_ITEM_SLOT, HELP_ITEM); + } + + if (DISENCHANT_ITEM_ENABLED) { + this.setItem(DISENCHANT_ITEM_SLOT, ItemStackBuilder.of(DISENCHANT_ITEM).build(() -> { + this.close(); + new DisenchantGUI(this.plugin, this.getPlayer(), this.pickAxe, this.pickaxePlayerInventorySlot).open(); + })); + } + + if (PICKAXE_ITEM_ENABLED) { + this.setItem(PICKAXE_ITEM_SLOT, Item.builder(this.pickAxe).build()); + } + + Collection allEnchants = this.plugin.getEnchantsRepository().getAll(); + for (XPrisonEnchantment enchantment : allEnchants) { + if (!enchantment.isEnabled()) { + continue; + } + int level = XPrisonEnchants.getInstance().getEnchantsManager().getEnchantLevel(this.pickAxe, enchantment); + this.setItem(enchantment.getGuiSlot(), getGuiItem(enchantment, this, level)); + } + } + + private Item getGuiItem(XPrisonEnchantment enchantment, EnchantGUI gui, int currentLevel) { + + ItemStackBuilder builder = ItemStackBuilder.of(enchantment.getMaterial()); + + if (enchantment.getBase64() != null && !enchantment.getBase64().isEmpty()) { + builder = ItemStackBuilder.of(SkullUtils.getCustomTextureHead(enchantment.getBase64())); + } + + builder.name(enchantment.getGuiName()); + builder.lore(GuiUtils.translateGuiLore(enchantment, GUI_ITEM_LORE, currentLevel)); + + return builder.buildItem().bind(handler -> { + if (!enchantment.canBeBought(gui.getPickAxe())) { + PlayerUtils.sendMessage(this.getPlayer(), this.plugin.getEnchantsConfig().getMessage("pickaxe_level_required").replace("%pickaxe_level%", String.format("%,d", enchantment.getRequiredPickaxeLevel()))); + return; + } + if (handler.getClick() == ClickType.MIDDLE || handler.getClick() == ClickType.SHIFT_RIGHT) { + this.plugin.getEnchantsManager().buyEnchnant(enchantment, gui, currentLevel, 100); + gui.redraw(); + } else if (handler.getClick() == ClickType.LEFT) { + this.plugin.getEnchantsManager().buyEnchnant(enchantment, gui, currentLevel, 1); + gui.redraw(); + } else if (handler.getClick() == ClickType.RIGHT) { + this.plugin.getEnchantsManager().buyEnchnant(enchantment, gui, currentLevel, 10); + gui.redraw(); + } else if (handler.getClick() == ClickType.DROP) { + this.plugin.getEnchantsManager().buyMaxEnchant(enchantment, gui, currentLevel); + } + }, ClickType.MIDDLE, ClickType.SHIFT_RIGHT, ClickType.RIGHT, ClickType.LEFT, ClickType.DROP).build(); + } + + public static void init() { + + GUI_ITEM_LORE = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("enchant_menu.item.lore"); + GUI_TITLE = TextUtils.applyColor(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.title")); + EMPTY_SLOT_ITEM = ItemStackBuilder. + of(CompMaterial.fromString(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.empty_slots")).toItem()).buildItem().build(); + GUI_LINES = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("enchant_menu.lines"); + + HELP_ITEM_ENABLED = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getBoolean("enchant_menu.help_item.enabled", true); + PICKAXE_ITEM_ENABLED = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getBoolean("enchant_menu.pickaxe_enabled", true); + DISENCHANT_ITEM_ENABLED = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getBoolean("enchant_menu.disenchant_item.enabled", true); + + if (DISENCHANT_ITEM_ENABLED) { + String base64 = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.disenchant_item.Base64", null); + + if (base64 != null) { + DISENCHANT_ITEM = ItemStackBuilder.of(SkullUtils.getCustomTextureHead(base64)) + .name(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.disenchant_item.name")).lore(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("enchant_menu.disenchant_item.lore")).build(); + } else { + DISENCHANT_ITEM = ItemStackBuilder.of(CompMaterial.fromString(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.disenchant_item.material")).toMaterial()) + .name(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.disenchant_item.name")).lore(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("enchant_menu.disenchant_item.lore")).build(); + } + DISENCHANT_ITEM_SLOT = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("enchant_menu.disenchant_item.slot"); + + } + + if (HELP_ITEM_ENABLED) { + String base64 = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.help_item.Base64", null); + + if (base64 != null) { + HELP_ITEM = ItemStackBuilder.of(SkullUtils.getCustomTextureHead(base64)) + .name(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.help_item.name")).lore(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("enchant_menu.help_item.lore")).buildItem().build(); + } else { + HELP_ITEM = ItemStackBuilder.of(CompMaterial.fromString(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.help_item.material")).toMaterial()) + .name(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getString("enchant_menu.help_item.name")).lore(XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getStringList("enchant_menu.help_item.lore")).buildItem().build(); + } + + HELP_ITEM_SLOT = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("enchant_menu.help_item.slot"); + + } + + if (PICKAXE_ITEM_ENABLED) { + PICKAXE_ITEM_SLOT = XPrisonEnchants.getInstance().getEnchantsConfig().getYamlConfig().getInt("enchant_menu.pickaxe_slot"); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/listener/EnchantsListener.java b/src/main/java/dev/drawethree/xprison/enchants/listener/EnchantsListener.java new file mode 100644 index 0000000..4066d17 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/listener/EnchantsListener.java @@ -0,0 +1,172 @@ +package dev.drawethree.xprison.enchants.listener; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.gui.EnchantGUI; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.MinecraftVersion; +import dev.drawethree.xprison.utils.inventory.InventoryUtils; +import me.lucko.helper.Events; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.*; +import org.bukkit.inventory.GrindstoneInventory; +import org.bukkit.inventory.ItemStack; +import org.codemc.worldguardwrapper.flag.IWrappedFlag; +import org.codemc.worldguardwrapper.flag.WrappedState; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class EnchantsListener { + + private final XPrisonEnchants plugin; + private final List ignoredEvents = new ArrayList<>(); + + public EnchantsListener(XPrisonEnchants plugin) { + this.plugin = plugin; + } + + public void register() { + + this.subscribeToPlayerDeathEvent(); + this.subscribeToPlayerRespawnEvent(); + this.subscribeToInventoryClickEvent(); + this.subscribeToPlayerJoinEvent(); + this.subscribeToPlayerDropItemEvent(); + this.subscribeToPlayerInteractEvent(); + this.subscribeToPlayerItemHeldEvent(); + this.subscribeToBlockBreakEvent(); + } + + public List getIgnoredEvents() { + return ignoredEvents; + } + + private void subscribeToBlockBreakEvent() { + Events.subscribe(BlockBreakEvent.class, EventPriority.HIGHEST) + .filter(e -> !e.isCancelled() && !ignoredEvents.contains(e)) + .filter(e -> e.getPlayer().getItemInHand() != null && this.plugin.getCore().isPickaxeSupported(e.getPlayer().getItemInHand().getType())) + .handler(e -> this.plugin.getEnchantsManager().handleBlockBreak(e, e.getPlayer().getItemInHand())).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerItemHeldEvent() { + // Switching pickaxes + Events.subscribe(PlayerItemHeldEvent.class, EventPriority.HIGHEST) + .handler(e -> { + + ItemStack newItem = e.getPlayer().getInventory().getItem(e.getNewSlot()); + ItemStack previousItem = e.getPlayer().getInventory().getItem(e.getPreviousSlot()); + + // Old item + if (previousItem != null && this.plugin.getCore().isPickaxeSupported(previousItem.getType())) { + this.plugin.getEnchantsManager().handlePickaxeUnequip(e.getPlayer(), previousItem); + } + + // New item + if (newItem != null && this.plugin.getCore().isPickaxeSupported(newItem.getType())) { + this.plugin.getEnchantsManager().handlePickaxeEquip(e.getPlayer(), newItem); + } + + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerInteractEvent() { + Events.subscribe(PlayerInteractEvent.class) + .filter(e -> e.getItem() != null && this.plugin.getCore().isPickaxeSupported(e.getItem().getType())) + .filter(e -> (this.plugin.getEnchantsConfig().getOpenEnchantMenuActions().contains(e.getAction()))) + .handler(e -> { + + e.setCancelled(true); + + ItemStack pickAxe = e.getItem(); + int pickaxeSlot = InventoryUtils.getInventorySlot(e.getPlayer(), pickAxe); + this.plugin.getCore().debug("Pickaxe slot is: " + pickaxeSlot, this.plugin); + + new EnchantGUI(this.plugin, e.getPlayer(), pickAxe, pickaxeSlot).open(); + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerDropItemEvent() { + // Dropping pickaxe + Events.subscribe(PlayerDropItemEvent.class, EventPriority.HIGHEST) + .handler(e -> { + if (this.plugin.getCore().isPickaxeSupported(e.getItemDrop().getItemStack())) { + this.plugin.getEnchantsManager().handlePickaxeUnequip(e.getPlayer(), e.getItemDrop().getItemStack()); + } + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerJoinEvent() { + //First join pickaxe + Events.subscribe(PlayerJoinEvent.class) + .filter(e -> !e.getPlayer().hasPlayedBefore() && this.plugin.getEnchantsConfig().isFirstJoinPickaxeEnabled()) + .handler(e -> { + ItemStack firstJoinPickaxe = this.plugin.getEnchantsManager().createFirstJoinPickaxe(e.getPlayer()); + e.getPlayer().getInventory().addItem(firstJoinPickaxe); + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerRespawnEvent() { + Events.subscribe(PlayerRespawnEvent.class, EventPriority.LOWEST) + .handler(e -> this.plugin.getRespawnManager().handleRespawn(e.getPlayer())).bindWith(this.plugin.getCore()); + } + + private void subscribeToInventoryClickEvent() { + //Grindstone disenchanting - disable + if (MinecraftVersion.atLeast(MinecraftVersion.V.v1_14)) { + Events.subscribe(InventoryClickEvent.class) + .filter(e -> e.getInventory() instanceof GrindstoneInventory) + .handler(e -> { + ItemStack item1 = e.getInventory().getItem(0); + ItemStack item2 = e.getInventory().getItem(1); + if (e.getSlot() == 2 && (this.plugin.getEnchantsManager().hasEnchants(item1) || this.plugin.getEnchantsManager().hasEnchants(item2))) { + e.setCancelled(true); + } + }).bindWith(this.plugin.getCore()); + } + + Events.subscribe(InventoryClickEvent.class, EventPriority.MONITOR) + .filter(e -> e.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) + .filter(e -> e.getWhoClicked() instanceof Player) + .filter(e -> !e.isCancelled()) + .handler(e -> { + ItemStack item = e.getCurrentItem(); + if (this.plugin.getCore().isPickaxeSupported(item)) { + this.plugin.getEnchantsManager().handlePickaxeUnequip((Player) e.getWhoClicked(), item); + } + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerDeathEvent() { + Events.subscribe(PlayerDeathEvent.class, EventPriority.LOWEST) + .handler(e -> { + + if (!this.plugin.getEnchantsConfig().isKeepPickaxesOnDeath()) { + return; + } + + List pickaxes = e.getDrops().stream().filter(itemStack -> this.plugin.getCore().isPickaxeSupported(itemStack) && + this.plugin.getEnchantsManager().hasEnchants(itemStack)).collect(Collectors.toList()); + e.getDrops().removeAll(pickaxes); + + this.plugin.getRespawnManager().addRespawnItems(e.getEntity(), pickaxes); + + if (pickaxes.size() > 0) { + this.plugin.getCore().debug("Removed " + e.getEntity().getName() + "'s pickaxes from drops (" + pickaxes.size() + "). Will be given back on respawn.", this.plugin); + } else { + this.plugin.getCore().debug("No Pickaxes found for player " + e.getEntity().getName() + " (PlayerDeathEvent)", this.plugin); + } + + }).bindWith(this.plugin.getCore()); + } + + private Optional> getWGFlag() { + return this.plugin.getCore().getWorldGuardWrapper().getFlag(Constants.ENCHANTS_WG_FLAG_NAME, WrappedState.class); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/managers/CooldownManager.java b/src/main/java/dev/drawethree/xprison/enchants/managers/CooldownManager.java new file mode 100644 index 0000000..f765fc5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/managers/CooldownManager.java @@ -0,0 +1,27 @@ +package dev.drawethree.xprison.enchants.managers; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import me.lucko.helper.cooldown.Cooldown; +import me.lucko.helper.cooldown.CooldownMap; +import org.bukkit.entity.Player; + +import java.util.concurrent.TimeUnit; + +public class CooldownManager { + + private final XPrisonEnchants plugin; + private final CooldownMap valueCooldown; + + public CooldownManager(XPrisonEnchants plugin) { + this.plugin = plugin; + this.valueCooldown = CooldownMap.create(Cooldown.of(30, TimeUnit.SECONDS)); + } + + public boolean hasValueCooldown(Player sender) { + return !valueCooldown.test(sender); + } + + public long getRemainingTime(Player sender) { + return valueCooldown.get(sender).remainingTime(TimeUnit.SECONDS); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/managers/EnchantsManager.java b/src/main/java/dev/drawethree/xprison/enchants/managers/EnchantsManager.java new file mode 100644 index 0000000..d92671c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/managers/EnchantsManager.java @@ -0,0 +1,560 @@ +package dev.drawethree.xprison.enchants.managers; + +import com.saicone.rtag.util.ServerInstance; +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.api.events.XPrisonPlayerEnchantEvent; +import dev.drawethree.xprison.enchants.gui.DisenchantGUI; +import dev.drawethree.xprison.enchants.gui.EnchantGUI; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.repo.EnchantsRepository; +import dev.drawethree.xprison.enchants.utils.EnchantUtils; +import dev.drawethree.xprison.pickaxelevels.XPrisonPickaxeLevels; +import dev.drawethree.xprison.pickaxelevels.model.PickaxeLevel; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.item.PrisonItem; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import me.clip.placeholderapi.PlaceholderAPI; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.time.Time; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.codemc.worldguardwrapper.flag.WrappedState; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EnchantsManager { + + private static final String EXCLUDE_PERMISSION = "xprison.enchant.exclude."; + private static final String UNBREAK_PERMISSION = "xprison.pickaxe.unbreakable"; + private static final boolean USE_META_UNBREAK = ServerInstance.verNumber >= 11; + private static final Pattern PICKAXE_LORE_ENCHANT_PATTER = Pattern.compile("(?i)%Enchant-\\d+%"); + + private final XPrisonEnchants plugin; + private final List lockedPlayers; + + public EnchantsManager(XPrisonEnchants plugin) { + this.plugin = plugin; + this.lockedPlayers = Collections.synchronizedList(new ArrayList<>()); + } + + public Map getItemEnchants(ItemStack itemStack) { + + if (itemStack == null || itemStack.getType() == Material.AIR) { + return new HashMap<>(); + } + + return new PrisonItem(itemStack).getEnchants(getEnchantsRepository()); + } + + public ItemStack updatePickaxe(Player player, ItemStack item) { + + if (item == null || !this.plugin.getCore().isPickaxeSupported(item.getType())) { + return item; + } + + return this.applyLoreToPickaxe(player, item); + } + + private ItemStack applyLoreToPickaxe(Player player, ItemStack item) { + + ItemMeta meta = item.getItemMeta(); + List lore = new ArrayList<>(); + + boolean pickaxeLevels = this.plugin.getCore().isModuleEnabled(XPrisonPickaxeLevels.MODULE_NAME); + + PickaxeLevel currentLevel = null; + PickaxeLevel nextLevel = null; + String pickaxeProgressBar = ""; + + if (pickaxeLevels) { + currentLevel = this.plugin.getCore().getPickaxeLevels().getPickaxeLevelsManager().getPickaxeLevel(item).orElse(null); + nextLevel = this.plugin.getCore().getPickaxeLevels().getPickaxeLevelsManager().getNextPickaxeLevel(currentLevel).orElse(null); + pickaxeProgressBar = this.plugin.getCore().getPickaxeLevels().getPickaxeLevelsManager().getProgressBar(item); + } + + long blocksBroken = getBlocksBroken(item); + final PrisonItem prisonItem = new PrisonItem(item); + Map enchants = prisonItem.getEnchants(getEnchantsRepository()); + + List pickaxeLore = this.plugin.getEnchantsConfig().getPickaxeLore(); + + final boolean isUnbreakable; + Boolean unbreakResult = null; + if (USE_META_UNBREAK ? meta.isUnbreakable() : prisonItem.isUnbreakable()) { + if (!this.plugin.getEnchantsConfig().isUseUnbreakablePermission() || player.hasPermission(UNBREAK_PERMISSION)) { + isUnbreakable = true; + } else { + isUnbreakable = false; + unbreakResult = false; + } + } else { + if (this.plugin.getEnchantsConfig().isUseUnbreakablePermission() && player.hasPermission(UNBREAK_PERMISSION)) { + isUnbreakable = true; + unbreakResult = true; + } else { + isUnbreakable = false; + } + } + final String durability = isUnbreakable ? "∞" : String.valueOf(item.getType().getMaxDurability() - EnchantUtils.getDurability(item, meta)); + if (unbreakResult != null) { + if (USE_META_UNBREAK) { + meta.setUnbreakable(unbreakResult); + } else { + if (unbreakResult) { + prisonItem.setUnbreakable(true); + } else { + prisonItem.remove("Unbreakable"); + } + prisonItem.load(); + meta = item.getItemMeta(); + } + } + + for (String s : pickaxeLore) { + s = s.replace("%Blocks%", String.valueOf(blocksBroken)); + s = s.replace("%Durability%", durability); + + if (pickaxeLevels) { + s = s.replace("%Blocks_Required%", nextLevel == null ? "∞" : String.valueOf(nextLevel.getBlocksRequired())); + s = s.replace("%PickaxeLevel%", currentLevel == null ? "0" : String.valueOf(currentLevel.getLevel())); + s = s.replace("%PickaxeProgress%", pickaxeProgressBar); + } + + Matcher matcher = PICKAXE_LORE_ENCHANT_PATTER.matcher(s); + + if (matcher.find()) { + int enchId = Integer.parseInt(matcher.group().replaceAll("\\D", "")); + XPrisonEnchantment enchantment = getEnchantsRepository().getEnchantById(enchId); + + if (enchantment != null) { + int enchLvl = enchants.getOrDefault(enchantment, 0); + if (enchLvl > 0) { + final String line; + if (player.hasPermission(EXCLUDE_PERMISSION + enchantment.getRawName())) { + line = this.plugin.getEnchantsConfig().getExcludedFormat() + .replace("%Enchant%", enchantment.getNameUncolor()) + .replace("%Level%", this.plugin.getEnchantsConfig().getLevelFormat().format(enchLvl)); + } else { + line = enchantment.getName() + " " + this.plugin.getEnchantsConfig().getLevelFormat().format(enchLvl); + } + s = s.replace(matcher.group(), line); + } else { + continue; + } + } else { + continue; + } + } + + if (this.plugin.getCore().isPlaceholderAPIEnabled()) { + s = PlaceholderAPI.setPlaceholders(player, s); + } + + lore.add(TextUtils.applyColor(s)); + } + + meta.setLore(lore); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + item.setItemMeta(meta); + return item; + } + + private EnchantsRepository getEnchantsRepository() { + return this.plugin.getEnchantsRepository(); + } + + public long getBlocksBroken(ItemStack item) { + + if (item == null || item.getType() == Material.AIR) { + return 0; + } + + return new PrisonItem(item).getBrokenBlocks(); + } + + public synchronized void addBlocksBrokenToItem(Player p, int amount) { + + if (amount == 0) { + return; + } + + final PrisonItem prisonItem = new PrisonItem(p.getItemInHand()); + prisonItem.addBrokenBlocks(amount); + ItemStack item = prisonItem.loadCopy(); + applyLoreToPickaxe(p, item); + p.setItemInHand(item); + } + + public synchronized void addBlocksBrokenToItem(Player player, ItemStack item, int amount) { + + if (amount == 0) { + return; + } + + final PrisonItem prisonItem = new PrisonItem(item); + prisonItem.addBrokenBlocks(amount); + player.setItemInHand(prisonItem.loadCopy()); + applyLoreToPickaxe(player, player.getItemInHand()); + } + + public synchronized int getEnchantLevel(ItemStack itemStack, XPrisonEnchantment enchantment) { + + if (enchantment == null || itemStack == null || itemStack.getType() == Material.AIR) { + return 0; + } + + return Math.min(new PrisonItem(itemStack).getEnchantLevel(enchantment), enchantment.getMaxLevel()); + } + + public void forEachEffectiveEnchant(Player player, ItemStack item, BiConsumer consumer) { + for (var entry : this.getItemEnchants(item).entrySet()) { + final XPrisonEnchantment enchant = entry.getKey(); + if (enchant.isEnabled() && !player.hasPermission(EXCLUDE_PERMISSION + enchant.getRawName())) { + consumer.accept(enchant, entry.getValue()); + } + } + } + + public void handleBlockBreak(BlockBreakEvent e, ItemStack pickAxe) { + + this.addBlocksBrokenToItem(e.getPlayer(), 1); + + if (RegionUtils.getRegionWithHighestPriorityAndFlag(e.getBlock().getLocation(), Constants.ENCHANTS_WG_FLAG_NAME, WrappedState.ALLOW) == null) { + this.plugin.getCore().debug("EnchantsManager::handleBlockBreak >> No region with flag upc-enchants found. Enchants will not be triggered.", this.plugin); + return; + } + + forEachEffectiveEnchant(e.getPlayer(), pickAxe, (enchant, level) -> enchant.onBlockBreak(e, level)); + } + + public void handlePickaxeEquip(Player p, ItemStack newItem) { + forEachEffectiveEnchant(p, newItem, (enchant, level) -> enchant.onEquip(p, newItem, level)); + } + + public void handlePickaxeUnequip(Player p, ItemStack newItem) { + forEachEffectiveEnchant(p, newItem, (enchant, level) -> enchant.onUnequip(p, newItem, level)); + } + + public ItemStack setEnchantLevel(Player player, ItemStack item, XPrisonEnchantment enchantment, int level) { + + if (enchantment == null || item == null) { + return item; + } + + final PrisonItem prisonItem = new PrisonItem(item); + prisonItem.setEnchant(enchantment, level); + prisonItem.load(); + return this.applyLoreToPickaxe(player, item); + } + + public ItemStack removeEnchant(Player player, ItemStack item, XPrisonEnchantment enchantment) { + return setEnchantLevel(player, item, enchantment, 0); + } + + public void buyEnchnant(XPrisonEnchantment enchantment, EnchantGUI gui, int currentLevel, int addition) { + + if (currentLevel >= enchantment.getMaxLevel()) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_max_level")); + return; + } + + if (currentLevel + addition > enchantment.getMaxLevel()) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_max_level_exceed")); + return; + } + + long totalCost = 0; + + long startTime = Time.nowMillis(); + + for (int j = 0; j < addition; j++) { + totalCost += enchantment.getCostOfLevel(currentLevel + j + 1); + } + + this.plugin.getCore().debug(String.format("Calculation of levels %,d - %,d of %s enchant took %dms", currentLevel + 1, currentLevel + addition + 1, enchantment.getRawName(), Time.nowMillis() - startTime), this.plugin); + + if (!plugin.getCore().getTokens().getApi().hasEnough(gui.getPlayer(), totalCost)) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("not_enough_tokens")); + return; + } + + XPrisonPlayerEnchantEvent event = new XPrisonPlayerEnchantEvent(gui.getPlayer(), totalCost, currentLevel + addition); + + Events.callSync(event); + + if (event.isCancelled()) { + return; + } + + plugin.getCore().getTokens().getApi().removeTokens(gui.getPlayer(), totalCost, LostCause.ENCHANT); + + this.setEnchantLevel(gui.getPlayer(), gui.getPickAxe(), enchantment, currentLevel + addition); + + enchantment.onUnequip(gui.getPlayer(), gui.getPickAxe(), currentLevel); + enchantment.onEquip(gui.getPlayer(), gui.getPickAxe(), currentLevel + addition); + + gui.getPlayer().getInventory().setItem(gui.getPickaxePlayerInventorySlot(), gui.getPickAxe()); + + if (addition == 1) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_bought").replace("%tokens%", String.format("%,d", totalCost))); + } else { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_bought_multiple") + .replace("%amount%", String.valueOf(addition)) + .replace("%enchant%", enchantment.getName()) + .replace("%tokens%", String.format("%,d", totalCost))); + } + } + + public void disenchant(XPrisonEnchantment enchantment, DisenchantGUI gui, int currentLevel, int substraction) { + + if (currentLevel <= 0) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_no_level")); + return; + } + + if (currentLevel - substraction < 0) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_min_level_exceed")); + return; + } + + long totalRefunded = 0; + + for (int j = 0; j < substraction; j++) { + totalRefunded += enchantment.getRefundForLevel(currentLevel - j); + } + + plugin.getCore().getTokens().getTokensManager().giveTokens(gui.getPlayer(), totalRefunded, null, ReceiveCause.REFUND); + + this.setEnchantLevel(gui.getPlayer(), gui.getPickAxe(), enchantment, currentLevel - substraction); + + enchantment.onUnequip(gui.getPlayer(), gui.getPickAxe(), currentLevel); + enchantment.onEquip(gui.getPlayer(), gui.getPickAxe(), currentLevel - substraction); + + gui.getPlayer().getInventory().setItem(gui.getPickaxePlayerInventorySlot(), gui.getPickAxe()); + + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_refunded").replace("%amount%", String.format("%,d", substraction)).replace("%enchant%", enchantment.getName())); + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_tokens_back").replace("%tokens%", String.format("%,d", totalRefunded))); + } + + public void disenchantMax(XPrisonEnchantment enchantment, DisenchantGUI gui, int currentLevel) { + + if (currentLevel <= 0) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_no_level")); + return; + } + + if (this.lockedPlayers.contains(gui.getPlayer().getUniqueId())) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("transaction_in_progress")); + return; + } + + this.lockedPlayers.add(gui.getPlayer().getUniqueId()); + + + Schedulers.async().run(() -> { + int current = currentLevel; + int levelsToRefund = current; + + long totalRefunded = 0; + + while (gui.getPlayer().isOnline() && current > 0) { + totalRefunded += enchantment.getRefundForLevel(current); + current--; + } + + if (!gui.getPlayer().isOnline()) { + this.lockedPlayers.remove(gui.getPlayer().getUniqueId()); + return; + } + + int finalCurrent = current; + + this.lockedPlayers.remove(gui.getPlayer().getUniqueId()); + + Schedulers.sync().run(() -> { + enchantment.onUnequip(gui.getPlayer(), gui.getPickAxe(), currentLevel); + this.setEnchantLevel(gui.getPlayer(), gui.getPickAxe(), enchantment, finalCurrent); + gui.getPlayer().getInventory().setItem(gui.getPickaxePlayerInventorySlot(), gui.getPickAxe()); + enchantment.onEquip(gui.getPlayer(), gui.getPickAxe(), finalCurrent); + gui.redraw(); + }); + + plugin.getCore().getTokens().getTokensManager().giveTokens(gui.getPlayer(), totalRefunded, null, ReceiveCause.REFUND); + + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_refunded").replace("%amount%", String.format("%,d", levelsToRefund)).replace("%enchant%", enchantment.getName())); + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_tokens_back").replace("%tokens%", String.format("%,d", totalRefunded))); + }); + } + + public void buyMaxEnchant(XPrisonEnchantment enchantment, EnchantGUI gui, int currentLevel) { + + if (currentLevel >= enchantment.getMaxLevel()) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_max_level")); + return; + } + + if (this.lockedPlayers.contains(gui.getPlayer().getUniqueId())) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("transaction_in_progress")); + return; + } + + this.lockedPlayers.add(gui.getPlayer().getUniqueId()); + + Schedulers.async().run(() -> { + int levelsToBuy = 0; + long totalCost = 0; + + while (gui.getPlayer().isOnline() && (currentLevel + levelsToBuy + 1) <= enchantment.getMaxLevel() && this.plugin.getCore().getTokens().getApi().hasEnough(gui.getPlayer(), totalCost + enchantment.getCostOfLevel(currentLevel + levelsToBuy + 1))) { + levelsToBuy += 1; + totalCost += enchantment.getCostOfLevel(currentLevel + levelsToBuy + 1); + } + + if (!gui.getPlayer().isOnline()) { + this.lockedPlayers.remove(gui.getPlayer().getUniqueId()); + return; + } + + if (levelsToBuy == 0) { + this.lockedPlayers.remove(gui.getPlayer().getUniqueId()); + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("not_enough_tokens")); + return; + } + + XPrisonPlayerEnchantEvent event = new XPrisonPlayerEnchantEvent(gui.getPlayer(), totalCost, currentLevel + levelsToBuy); + + Events.callSync(event); + + if (event.isCancelled()) { + this.lockedPlayers.remove(gui.getPlayer().getUniqueId()); + return; + } + + plugin.getCore().getTokens().getApi().removeTokens(gui.getPlayer(), totalCost, LostCause.ENCHANT); + + int finalLevelsToBuy = levelsToBuy; + + this.lockedPlayers.remove(gui.getPlayer().getUniqueId()); + Schedulers.sync().run(() -> { + enchantment.onUnequip(gui.getPlayer(), gui.getPickAxe(), currentLevel); + this.setEnchantLevel(gui.getPlayer(), gui.getPickAxe(), enchantment, currentLevel + finalLevelsToBuy); + enchantment.onEquip(gui.getPlayer(), gui.getPickAxe(), currentLevel + finalLevelsToBuy); + gui.getPlayer().getInventory().setItem(gui.getPickaxePlayerInventorySlot(), gui.getPickAxe()); + gui.redraw(); + }); + + if (levelsToBuy == 1) { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_bought").replace("%tokens%", String.format("%,d", totalCost))); + } else { + PlayerUtils.sendMessage(gui.getPlayer(), plugin.getEnchantsConfig().getMessage("enchant_bought_multiple") + .replace("%amount%", String.valueOf(levelsToBuy)) + .replace("%enchant%", enchantment.getName()) + .replace("%tokens%", String.format("%,d", totalCost))); + } + }); + } + + public long getPickaxeValue(ItemStack pickAxe) { + + long sum = 0; + + Map playerEnchants = this.getItemEnchants(pickAxe); + + for (XPrisonEnchantment enchantment : playerEnchants.keySet()) { + for (int i = 1; i <= playerEnchants.get(enchantment); i++) { + sum += enchantment.getCostOfLevel(i); + } + } + return sum; + } + + // /givepickaxe + public void givePickaxe(Player target, Map enchants, String pickaxeName, CommandSender sender) { + ItemStackBuilder pickaxeBuilder = ItemStackBuilder.of(Material.DIAMOND_PICKAXE); + + if (pickaxeName != null) { + pickaxeBuilder.name(pickaxeName); + } + + ItemStack pickaxe = pickaxeBuilder.build(); + + for (Map.Entry entry : enchants.entrySet()) { + this.setEnchantLevel(target, pickaxe, entry.getKey(), entry.getValue()); + } + + pickaxe = this.applyLoreToPickaxe(target, pickaxe); + + if (target == null && sender instanceof Player) { + target = (Player) sender; + } + + if (target != null) { + if (target.getInventory().firstEmpty() == -1) { + PlayerUtils.sendMessage(sender, this.plugin.getEnchantsConfig().getMessage("pickaxe_inventory_full").replace("%player%", target.getName())); + return; + } + + target.getInventory().addItem(pickaxe); + PlayerUtils.sendMessage(sender, this.plugin.getEnchantsConfig().getMessage("pickaxe_given").replace("%player%", target.getName())); + PlayerUtils.sendMessage(target, this.plugin.getEnchantsConfig().getMessage("pickaxe_received").replace("%sender%", sender.getName())); + } + } + + public ItemStack createFirstJoinPickaxe(Player player) { + + String pickaxeName = this.plugin.getEnchantsConfig().getFirstJoinPickaxeName(); + pickaxeName = pickaxeName.replace("%player%", player.getName()); + + if (this.plugin.getCore().isPlaceholderAPIEnabled()) { + pickaxeName = PlaceholderAPI.setPlaceholders(player, pickaxeName); + } + + CompMaterial material = this.plugin.getEnchantsConfig().getFirstJoinPickaxeMaterial(); + ItemStack item = ItemStackBuilder.of(material.toItem()).name(pickaxeName).build(); + + List firstJoinPickaxeEnchants = this.plugin.getEnchantsConfig().getFirstJoinPickaxeEnchants(); + + for (String s : firstJoinPickaxeEnchants) { + try { + String[] data = s.split(" "); + XPrisonEnchantment enchantment = getEnchantsRepository().getEnchantByName(data[0]); + int level = Integer.parseInt(data[1]); + this.setEnchantLevel(player, item, enchantment, level); + } catch (Exception e) { + + } + } + + return this.applyLoreToPickaxe(player, item); + } + + public boolean hasEnchants(ItemStack item) { + return item != null && !this.getItemEnchants(item).isEmpty(); + } + + public void enable() { + + } + + public void disable() { + + } + + public void giveFirstJoinPickaxe(Player target) { + target.getInventory().addItem(this.createFirstJoinPickaxe(target)); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/managers/RespawnManager.java b/src/main/java/dev/drawethree/xprison/enchants/managers/RespawnManager.java new file mode 100644 index 0000000..012c8fc --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/managers/RespawnManager.java @@ -0,0 +1,34 @@ +package dev.drawethree.xprison.enchants.managers; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class RespawnManager { + + private final XPrisonEnchants plugin; + private final Map> respawnItems; + + + public RespawnManager(XPrisonEnchants plugin) { + this.plugin = plugin; + this.respawnItems = new HashMap<>(); + } + + public void addRespawnItems(Player player, List items) { + this.respawnItems.put(player.getUniqueId(), items); + } + + public void handleRespawn(Player player) { + if (this.respawnItems.containsKey(player.getUniqueId())) { + this.respawnItems.remove(player.getUniqueId()).forEach(itemStack -> { + player.getInventory().addItem(itemStack); + }); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/LevelFormat.java b/src/main/java/dev/drawethree/xprison/enchants/model/LevelFormat.java new file mode 100644 index 0000000..ab13468 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/LevelFormat.java @@ -0,0 +1,76 @@ +package dev.drawethree.xprison.enchants.model; + +import java.util.Map; +import java.util.TreeMap; + +public enum LevelFormat { + + NUMBER, ROMAN, FIXED; + + private static final Map FIXED_NUMBERS = Map.of( + 1, "I", + 2, "II", + 3, "III", + 4, "IV", + 5, "V", + 6, "VI", + 7, "VII", + 8, "VIII", + 9, "IX", + 10, "X" + ); + + private static final TreeMap ROMAN_NUMBERS = new TreeMap<>(); + + static { + ROMAN_NUMBERS.put(1000, "M"); + ROMAN_NUMBERS.put(900, "CM"); + ROMAN_NUMBERS.put(500, "D"); + ROMAN_NUMBERS.put(400, "CD"); + ROMAN_NUMBERS.put(100, "C"); + ROMAN_NUMBERS.put(90, "XC"); + ROMAN_NUMBERS.put(50, "L"); + ROMAN_NUMBERS.put(40, "XL"); + ROMAN_NUMBERS.put(10, "X"); + ROMAN_NUMBERS.put(9, "IX"); + ROMAN_NUMBERS.put(5, "V"); + ROMAN_NUMBERS.put(4, "IV"); + ROMAN_NUMBERS.put(1, "I"); + } + + public static LevelFormat of(String name) { + if (name == null) { + return LevelFormat.NUMBER; + } + switch (name.trim().toUpperCase()) { + case "ROMAN": + return LevelFormat.ROMAN; + case "FIXED": + return LevelFormat.FIXED; + case "NUMBER": + default: + return LevelFormat.NUMBER; + } + } + + public String format(int level) { + if (this == NUMBER || level < 1) { + return String.valueOf(level); + } + if (level <= 10) { + return FIXED_NUMBERS.get(level); + } + if (this == ROMAN) { + return toRoman(level); + } + return String.valueOf(level); + } + + private String toRoman(int level) { + final int mapLevel = ROMAN_NUMBERS.floorKey(level); + if (mapLevel == level) { + return ROMAN_NUMBERS.get(level); + } + return ROMAN_NUMBERS.get(mapLevel) + toRoman(level - mapLevel); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/Refundable.java b/src/main/java/dev/drawethree/xprison/enchants/model/Refundable.java new file mode 100644 index 0000000..8c18fda --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/Refundable.java @@ -0,0 +1,10 @@ +package dev.drawethree.xprison.enchants.model; + +public interface Refundable { + + boolean isRefundEnabled(); + + int getRefundGuiSlot(); + + double getRefundPercentage(); +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/XPrisonEnchantment.java b/src/main/java/dev/drawethree/xprison/enchants/model/XPrisonEnchantment.java new file mode 100644 index 0000000..ca03781 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/XPrisonEnchantment.java @@ -0,0 +1,116 @@ +package dev.drawethree.xprison.enchants.model; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.pickaxelevels.XPrisonPickaxeLevels; +import dev.drawethree.xprison.pickaxelevels.model.PickaxeLevel; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +@Getter +public abstract class XPrisonEnchantment implements Refundable { + + protected final XPrisonEnchants plugin; + + protected final int id; + private String rawName; + private String name; + private String nameUncolor; + private String guiName; + private String base64; + private Material material; + private List description; + private boolean enabled; + private int guiSlot; + private int maxLevel; + private long cost; + private long increaseCost; + private int requiredPickaxeLevel; + private boolean messagesEnabled; + private boolean refundEnabled; + private int refundGuiSlot; + private double refundPercentage; + + public XPrisonEnchantment(XPrisonEnchants plugin, int id) { + this.plugin = plugin; + this.id = id; + this.reloadDefaultAttributes(); + this.reload(); + } + + private void reloadDefaultAttributes() { + this.rawName = this.plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".RawName"); + this.name = TextUtils.applyColor(this.plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Name")); + this.nameUncolor = this.name.replaceAll("§.", ""); + this.guiName = TextUtils.applyColor(this.plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".GuiName")); + this.material = CompMaterial.fromString(this.plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Material")).toMaterial(); + this.description = TextUtils.applyColor(this.plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Description")); + this.enabled = this.plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Enabled"); + this.guiSlot = this.plugin.getEnchantsConfig().getYamlConfig().getInt("enchants." + id + ".InGuiSlot"); + this.maxLevel = this.plugin.getEnchantsConfig().getYamlConfig().getInt("enchants." + id + ".Max"); + this.cost = this.plugin.getEnchantsConfig().getYamlConfig().getLong("enchants." + id + ".Cost"); + this.increaseCost = this.plugin.getEnchantsConfig().getYamlConfig().getLong("enchants." + id + ".Increase-Cost-by"); + this.requiredPickaxeLevel = this.plugin.getEnchantsConfig().getYamlConfig().getInt("enchants." + id + ".Pickaxe-Level-Required"); + this.messagesEnabled = this.plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Messages-Enabled", true); + this.base64 = this.plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Base64", null); + this.refundEnabled = this.plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + this.id + ".Refund.Enabled", true); + this.refundGuiSlot = this.plugin.getEnchantsConfig().getYamlConfig().getInt("enchants." + this.id + ".Refund.InGuiSlot"); + this.refundPercentage = this.plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + this.id + ".Refund.Percentage", 100.0d); + } + + public abstract String getAuthor(); + + public abstract void onEquip(Player p, ItemStack pickAxe, int level); + + public abstract void onUnequip(Player p, ItemStack pickAxe, int level); + + public abstract void onBlockBreak(BlockBreakEvent e, int enchantLevel); + + public abstract double getChanceToTrigger(int enchantLevel); + + public void reload() { + this.reloadDefaultAttributes(); + } + + public long getCostOfLevel(int level) { + return (this.cost + (this.increaseCost * (level - 1))); + } + + public long getRefundForLevel(int level) { + return (long) (this.getCostOfLevel(level) * (this.getRefundPercentage() / 100.0)); + } + + @Override + public boolean isRefundEnabled() { + return refundEnabled; + } + + @Override + public double getRefundPercentage() { + return refundPercentage; + } + + @Override + public int getRefundGuiSlot() { + return refundGuiSlot; + } + + public int getMaxLevel() { + return this.maxLevel == -1 ? Integer.MAX_VALUE : this.maxLevel; + } + + public boolean canBeBought(ItemStack pickAxe) { + if (!this.plugin.getCore().isModuleEnabled(XPrisonPickaxeLevels.MODULE_NAME)) { + return true; + } + Optional pickaxeLevelOptional = this.plugin.getCore().getPickaxeLevels().getApi().getPickaxeLevel(pickAxe); + return pickaxeLevelOptional.map(level -> level.getLevel() >= this.requiredPickaxeLevel).orElse(true); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/AutoSellEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/AutoSellEnchant.java new file mode 100644 index 0000000..3400c58 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/AutoSellEnchant.java @@ -0,0 +1,63 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.autosell.XPrisonAutoSell; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class AutoSellEnchant extends XPrisonEnchantment { + + private double chance; + + public AutoSellEnchant(XPrisonEnchants instance) { + super(instance, 19); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + if (!this.plugin.getCore().isModuleEnabled(XPrisonAutoSell.MODULE_NAME)) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + this.plugin.getCore().getAutoSell().getManager().sellAll(e.getPlayer(), RegionUtils.getRegionWithHighestPriority(e.getPlayer().getLocation())); + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/BackpackAutoSellEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/BackpackAutoSellEnchant.java new file mode 100644 index 0000000..2d30fb1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/BackpackAutoSellEnchant.java @@ -0,0 +1,69 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.ultrabackpacks.api.UltraBackpacksAPI; +import dev.drawethree.ultrabackpacks.api.exception.BackpackNotFoundException; +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class BackpackAutoSellEnchant extends XPrisonEnchantment { + + private double chance; + + public BackpackAutoSellEnchant(XPrisonEnchants instance) { + super(instance, 19); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + if (!XPrison.getInstance().isUltraBackpacksEnabled()) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + try { + UltraBackpacksAPI.sellBackpack(e.getPlayer(), true); + } catch (BackpackNotFoundException ignored) { + this.plugin.getCore().debug("BackpackAutoSellEnchant::onBlockBreak > Player " + e.getPlayer().getName() + " does not have backpack.", this.plugin); + } + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return this.chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/BlessingEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/BlessingEnchant.java new file mode 100644 index 0000000..08bba76 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/BlessingEnchant.java @@ -0,0 +1,92 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class BlessingEnchant extends XPrisonEnchantment { + + private double chance; + private String amountToGiveExpression; + + public BlessingEnchant(XPrisonEnchants instance) { + super(instance, 13); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + if (!this.plugin.getCore().isModuleEnabled(XPrisonTokens.MODULE_NAME)) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long amount = (long) createExpression(enchantLevel).evaluate(); + + for (Player p : Players.all()) { + plugin.getCore().getTokens().getTokensManager().giveTokens(p, amount, null, ReceiveCause.MINING_OTHERS); + + if (!this.isMessagesEnabled()) { + continue; + } + + if (p.equals(e.getPlayer())) { + PlayerUtils.sendMessage(p, plugin.getEnchantsConfig().getMessage("blessing_your").replace("%amount%", String.format("%,d", amount))); + } else { + PlayerUtils.sendMessage(p, plugin.getEnchantsConfig().getMessage("blessing_other").replace("%amount%", String.format("%,d", amount)).replace("%player%", e.getPlayer().getName())); + } + } + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/BlockBoosterEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/BlockBoosterEnchant.java new file mode 100644 index 0000000..01ed5e2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/BlockBoosterEnchant.java @@ -0,0 +1,127 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.tokens.api.events.XPrisonBlockBreakEvent; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.time.Time; +import org.bukkit.ChatColor; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +public final class BlockBoosterEnchant extends XPrisonEnchantment { + + private static final Map BOOSTED_PLAYERS = new HashMap<>(); + private double chance; + + public BlockBoosterEnchant(XPrisonEnchants instance) { + super(instance, 17); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + + Events.subscribe(XPrisonBlockBreakEvent.class) + .handler(e -> { + if (BOOSTED_PLAYERS.containsKey(e.getPlayer().getUniqueId())) { + List blocks = new ArrayList<>(); + for (Block b : e.getBlocks()) { + blocks.add(b); + blocks.add(b); + } + e.setBlocks(blocks); + } + }).bindWith(instance.getCore()); + } + + public static boolean hasBlockBoosterRunning(Player p) { + return BOOSTED_PLAYERS.containsKey(p.getUniqueId()); + } + + public static String getTimeLeft(Player p) { + + if (!BOOSTED_PLAYERS.containsKey(p.getUniqueId())) { + return ""; + } + + long endTime = BOOSTED_PLAYERS.get(p.getUniqueId()); + + if (System.currentTimeMillis() > endTime) { + return ""; + } + + + long timeLeft = endTime - System.currentTimeMillis(); + + long days = timeLeft / (24 * 60 * 60 * 1000); + timeLeft -= days * (24 * 60 * 60 * 1000); + + long hours = timeLeft / (60 * 60 * 1000); + timeLeft -= hours * (60 * 60 * 1000); + + long minutes = timeLeft / (60 * 1000); + timeLeft -= minutes * (60 * 1000); + + long seconds = timeLeft / (1000); + + timeLeft -= seconds * 1000; + + return ChatColor.GRAY + "(" + ChatColor.WHITE + days + "d " + hours + "h " + minutes + "m " + seconds + "s" + ChatColor.GRAY + ")"; + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + if (hasBlockBoosterRunning(e.getPlayer())) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + PlayerUtils.sendMessage(e.getPlayer(), this.plugin.getEnchantsConfig().getMessage("block_booster_on")); + + BOOSTED_PLAYERS.put(e.getPlayer().getUniqueId(), Time.nowMillis() + TimeUnit.MINUTES.toMillis(1)); + + Schedulers.sync().runLater(() -> { + if (e.getPlayer().isOnline()) { + PlayerUtils.sendMessage(e.getPlayer(), this.plugin.getEnchantsConfig().getMessage("block_booster_off")); + } + BOOSTED_PLAYERS.remove(e.getPlayer().getUniqueId()); + }, 5, TimeUnit.MINUTES); + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/CharityEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/CharityEnchant.java new file mode 100644 index 0000000..422c09e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/CharityEnchant.java @@ -0,0 +1,91 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class CharityEnchant extends XPrisonEnchantment { + + private double chance; + private String amountToGiveExpression; + + public CharityEnchant(XPrisonEnchants instance) { + super(instance, 11); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + if (!this.plugin.getCore().isModuleEnabled(XPrisonTokens.MODULE_NAME)) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long amount = (long) createExpression(enchantLevel).evaluate(); + + for (Player p : Players.all()) { + plugin.getCore().getEconomy().depositPlayer(p, amount); + + if (!this.isMessagesEnabled()) { + continue; + } + + if (p.equals(e.getPlayer())) { + PlayerUtils.sendMessage(p, plugin.getEnchantsConfig().getMessage("charity_your").replace("%amount%", String.format("%,d", amount))); + } else { + PlayerUtils.sendMessage(p, plugin.getEnchantsConfig().getMessage("charity_other").replace("%amount%", String.format("%,d", amount)).replace("%player%", e.getPlayer().getName())); + } + } + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/EfficiencyEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/EfficiencyEnchant.java new file mode 100644 index 0000000..c59e647 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/EfficiencyEnchant.java @@ -0,0 +1,47 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public final class EfficiencyEnchant extends XPrisonEnchantment { + public EfficiencyEnchant(XPrisonEnchants instance) { + super(instance, 1); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + ItemMeta meta = pickAxe.getItemMeta(); + meta.addEnchant(Enchantment.DIG_SPEED, level, true); + pickAxe.setItemMeta(meta); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/ExplosiveEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/ExplosiveEnchant.java new file mode 100644 index 0000000..e0ef4ff --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/ExplosiveEnchant.java @@ -0,0 +1,209 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.ultrabackpacks.api.UltraBackpacksAPI; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.api.events.ExplosionTriggerEvent; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.utils.EnchantUtils; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.multipliers.enums.MultiplierType; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.block.CuboidExplosionBlockProvider; +import dev.drawethree.xprison.utils.block.ExplosionBlockProvider; +import dev.drawethree.xprison.utils.block.SpheroidExplosionBlockProvider; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import me.lucko.helper.Events; +import me.lucko.helper.time.Time; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.codemc.worldguardwrapper.flag.WrappedState; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +public final class ExplosiveEnchant extends XPrisonEnchantment { + + private double chance; + private boolean countBlocksBroken; + private boolean soundsEnabled; + private boolean useEvents; + private ExplosionBlockProvider blockProvider; + + public ExplosiveEnchant(XPrisonEnchants instance) { + super(instance, 9); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.countBlocksBroken = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Count-Blocks-Broken"); + this.soundsEnabled = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Sounds"); + this.useEvents = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Use-Events"); + this.blockProvider = this.loadBlockProvider(); + } + + private ExplosionBlockProvider loadBlockProvider() { + String explosionType = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Explosion-Type", "CUBE"); + + if ("CUBE".equalsIgnoreCase(explosionType)) { + return CuboidExplosionBlockProvider.instance(); + } else if ("SPHERE".equalsIgnoreCase(explosionType)) { + return SpheroidExplosionBlockProvider.instance(); + } else { + return CuboidExplosionBlockProvider.instance(); + } + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long timeStart = Time.nowMillis(); + final Player p = e.getPlayer(); + final Block b = e.getBlock(); + + IWrappedRegion region = RegionUtils.getRegionWithHighestPriorityAndFlag(b.getLocation(), Constants.ENCHANTS_WG_FLAG_NAME, WrappedState.ALLOW); + + if (region == null) { + return; + } + + this.plugin.getCore().debug("ExplosiveEnchant::onBlockBreak >> WG Region used: " + region.getId(), this.plugin); + int radius = this.calculateRadius(enchantLevel); + + List blocksAffected = this.blockProvider.provide(b, radius).stream().filter(block -> region.contains(block.getLocation()) && block.getType() != Material.AIR).collect(Collectors.toList()); + + ExplosionTriggerEvent event = this.callExplosionTriggerEvent(e.getPlayer(), region, e.getBlock(), blocksAffected); + + if (event.isCancelled() || event.getBlocksAffected().isEmpty()) { + this.plugin.getCore().debug("ExplosiveEnchant::onBlockBreak >> ExplosiveTriggerEvent was cancelled. (Blocks affected size: " + event.getBlocksAffected().size(), this.plugin); + return; + } + + if (this.soundsEnabled) { + b.getWorld().createExplosion(b.getLocation().getX(), b.getLocation().getY(), b.getLocation().getZ(), 0F, false, false); + } + + if (this.useEvents) { + final List ignored = this.plugin.getEnchantsListener().getIgnoredEvents(); + blocksAffected = event.getBlocksAffected().stream().filter(block -> { + final BlockBreakEvent blockEvent = new BlockBreakEvent(block, p); + ignored.add(blockEvent); + Bukkit.getPluginManager().callEvent(blockEvent); + ignored.remove(blockEvent); + return !e.isCancelled(); + }).collect(Collectors.toList()); + } else { + blocksAffected = event.getBlocksAffected(); + } + + if (!this.plugin.getCore().isUltraBackpacksEnabled()) { + handleAffectedBlocks(p, region, blocksAffected); + } else { + UltraBackpacksAPI.handleBlocksBroken(p, blocksAffected); + } + + if (!this.useEvents && this.plugin.isMinesModuleEnabled()) { + Mine mine = plugin.getCore().getMines().getApi().getMineAtLocation(e.getBlock().getLocation()); + if (mine != null) { + mine.handleBlockBreak(blocksAffected); + } + } + + if (this.countBlocksBroken) { + plugin.getEnchantsManager().addBlocksBrokenToItem(p, blocksAffected.size()); + } + + if (!this.useEvents) { + plugin.getCore().getTokens().getTokensManager().handleBlockBreak(p, blocksAffected, countBlocksBroken); + } + + long timeEnd = Time.nowMillis(); + this.plugin.getCore().debug("ExplosiveEnchant::onBlockBreak >> Took " + (timeEnd - timeStart) + " ms.", this.plugin); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + private void handleAffectedBlocks(Player p, IWrappedRegion region, List blocksAffected) { + double totalDeposit = 0.0; + int fortuneLevel = EnchantUtils.getItemFortuneLevel(p.getItemInHand()); + boolean autoSellPlayerEnabled = this.plugin.isAutoSellModuleEnabled() && plugin.getCore().getAutoSell().getManager().hasAutoSellEnabled(p); + + for (Block block : blocksAffected) { + + int amplifier = fortuneLevel; + + if (FortuneEnchant.isBlockBlacklisted(block)) { + amplifier = 1; + } + + if (autoSellPlayerEnabled) { + totalDeposit += ((plugin.getCore().getAutoSell().getManager().getPriceForBlock(region.getId(), block) + 0.0) * amplifier); + } else { + ItemStack itemToGive = CompMaterial.fromBlock(block).toItem(amplifier); + p.getInventory().addItem(itemToGive); + } + block.setType(Material.AIR, true); + } + this.giveEconomyRewardToPlayer(p, totalDeposit); + } + + private void giveEconomyRewardToPlayer(Player p, double totalDeposit) { + + double total = this.plugin.isMultipliersModuleEnabled() ? plugin.getCore().getMultipliers().getApi().getTotalToDeposit(p, totalDeposit, MultiplierType.SELL) : totalDeposit; + + plugin.getCore().getEconomy().depositPlayer(p, total); + + if (this.plugin.isAutoSellModuleEnabled()) { + plugin.getCore().getAutoSell().getManager().addToCurrentEarnings(p, total); + } + } + + private int calculateRadius(int enchantLevel) { + int threshold = this.getMaxLevel() / 3; + return enchantLevel <= threshold ? 3 : enchantLevel <= threshold * 2 ? 4 : 5; + } + + private ExplosionTriggerEvent callExplosionTriggerEvent(Player p, IWrappedRegion mineRegion, Block originBlock, List blocks) { + ExplosionTriggerEvent event = new ExplosionTriggerEvent(p, mineRegion, originBlock, blocks); + Events.callSync(event); + this.plugin.getCore().debug("ExplosiveEnchant::callExplosiveTriggerEvent >> ExplosiveTriggerEvent called.", this.plugin); + return event; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.countBlocksBroken = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Count-Blocks-Broken"); + this.soundsEnabled = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Sounds"); + this.useEvents = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Use-Events"); + this.blockProvider = this.loadBlockProvider(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/FlyEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/FlyEnchant.java new file mode 100644 index 0000000..314922e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/FlyEnchant.java @@ -0,0 +1,46 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +public final class FlyEnchant extends XPrisonEnchantment { + + + + public FlyEnchant(XPrisonEnchants instance) { + super(instance, 8); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + p.setAllowFlight(true); + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + p.setAllowFlight(false); + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/FortuneEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/FortuneEnchant.java new file mode 100644 index 0000000..0d36f86 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/FortuneEnchant.java @@ -0,0 +1,63 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import org.bukkit.block.Block; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class FortuneEnchant extends XPrisonEnchantment { + + private static List blackListedBlocks; + + public FortuneEnchant(XPrisonEnchants instance) { + super(instance, 3); + blackListedBlocks = plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Blacklist").stream().map(CompMaterial::fromString).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + ItemMeta meta = pickAxe.getItemMeta(); + meta.removeEnchant(Enchantment.LOOT_BONUS_BLOCKS); + pickAxe.setItemMeta(meta); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + blackListedBlocks = plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Blacklist").stream().map(CompMaterial::fromString).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + public static boolean isBlockBlacklisted(Block block) { + CompMaterial blockMaterial = CompMaterial.fromBlock(block); + return blackListedBlocks.contains(blockMaterial); + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/GangValueFinderEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/GangValueFinderEnchant.java new file mode 100644 index 0000000..0f52c8e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/GangValueFinderEnchant.java @@ -0,0 +1,77 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.gangs.XPrisonGangs; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class GangValueFinderEnchant extends XPrisonEnchantment { + + private double chance; + private String amountToGiveExpression; + + + public GangValueFinderEnchant(XPrisonEnchants instance) { + super(instance, 23); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + if (!this.plugin.getCore().isModuleEnabled(XPrisonGangs.MODULE_NAME)) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + int amount = (int) createExpression(enchantLevel).evaluate(); + plugin.getCore().getGangs().getGangsManager().getPlayerGang(e.getPlayer()).ifPresent(gang -> gang.setValue(gang.getValue() + amount)); + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/GemFinderEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/GemFinderEnchant.java new file mode 100644 index 0000000..4388fd6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/GemFinderEnchant.java @@ -0,0 +1,76 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.gems.XPrisonGems; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class GemFinderEnchant extends XPrisonEnchantment { + + private double chance; + private String amountToGiveExpression; + + public GemFinderEnchant(XPrisonEnchants instance) { + super(instance, 22); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + if (!this.plugin.getCore().isModuleEnabled(XPrisonGems.MODULE_NAME)) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long amount = (long) createExpression(enchantLevel).evaluate(); + plugin.getCore().getGems().getGemsManager().giveGems(e.getPlayer(), amount, null, ReceiveCause.MINING); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/HasteEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/HasteEnchant.java new file mode 100644 index 0000000..798f282 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/HasteEnchant.java @@ -0,0 +1,50 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public final class HasteEnchant extends XPrisonEnchantment { + + public HasteEnchant(XPrisonEnchants instance) { + super(instance, 4); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + if (level == 0) { + this.onUnequip(p, pickAxe, level); + return; + } + p.addPotionEffect(new PotionEffect(PotionEffectType.FAST_DIGGING, Integer.MAX_VALUE, level - 1, true, true), true); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + p.removePotionEffect(PotionEffectType.FAST_DIGGING); + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/JumpBoostEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/JumpBoostEnchant.java new file mode 100644 index 0000000..40ea439 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/JumpBoostEnchant.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public final class JumpBoostEnchant extends XPrisonEnchantment { + public JumpBoostEnchant(XPrisonEnchants instance) { + super(instance, 6); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + if (level == 0) { + this.onUnequip(p, pickAxe, level); + return; + } + p.addPotionEffect(new PotionEffect(PotionEffectType.JUMP, Integer.MAX_VALUE, level - 1, true, true), true); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + p.removePotionEffect(PotionEffectType.JUMP); + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/KeyFinderEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/KeyFinderEnchant.java new file mode 100644 index 0000000..32965cb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/KeyFinderEnchant.java @@ -0,0 +1,67 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public final class KeyFinderEnchant extends XPrisonEnchantment { + + private double chance; + private List commandsToExecute; + + public KeyFinderEnchant(XPrisonEnchants instance) { + super(instance, 15); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandsToExecute = plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Commands"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + String randomCmd = this.getRandomCommandToExecute(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), randomCmd.replace("%player%", e.getPlayer().getName())); + } + + + private String getRandomCommandToExecute() { + return this.commandsToExecute.get(ThreadLocalRandom.current().nextInt(commandsToExecute.size())); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return this.chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandsToExecute = plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Commands"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/KeyallsEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/KeyallsEnchant.java new file mode 100644 index 0000000..7806a9b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/KeyallsEnchant.java @@ -0,0 +1,66 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public final class KeyallsEnchant extends XPrisonEnchantment { + + private double chance; + private List commandsToExecute; + + public KeyallsEnchant(XPrisonEnchants instance) { + super(instance, 18); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandsToExecute = plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Commands"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + String randomCmd = getRandomCommandToExecute(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), randomCmd.replace("%player%", e.getPlayer().getName())); + } + + private String getRandomCommandToExecute() { + return this.commandsToExecute.get(ThreadLocalRandom.current().nextInt(commandsToExecute.size())); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandsToExecute = plugin.getEnchantsConfig().getYamlConfig().getStringList("enchants." + id + ".Commands"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/LayerEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/LayerEnchant.java new file mode 100644 index 0000000..bc07da3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/LayerEnchant.java @@ -0,0 +1,193 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.ultrabackpacks.api.UltraBackpacksAPI; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.api.events.LayerTriggerEvent; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.utils.EnchantUtils; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.multipliers.enums.MultiplierType; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import me.lucko.helper.Events; +import me.lucko.helper.time.Time; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.codemc.worldguardwrapper.flag.WrappedState; +import org.codemc.worldguardwrapper.region.IWrappedRegion; +import org.codemc.worldguardwrapper.selection.ICuboidSelection; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +public final class LayerEnchant extends XPrisonEnchantment { + + private double chance; + private boolean countBlocksBroken; + private boolean useEvents; + + public LayerEnchant(XPrisonEnchants instance) { + super(instance, 10); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.countBlocksBroken = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Count-Blocks-Broken"); + this.useEvents = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Use-Events"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long startTime = Time.nowMillis(); + final Player p = e.getPlayer(); + final Block b = e.getBlock(); + + IWrappedRegion region = RegionUtils.getRegionWithHighestPriorityAndFlag(b.getLocation(), Constants.ENCHANTS_WG_FLAG_NAME, WrappedState.ALLOW); + + if (region == null) { + return; + } + + this.plugin.getCore().debug("LayerEnchant::onBlockBreak >> WG Region used: " + region.getId(), this.plugin); + List blocksAffected = this.getAffectedBlocks(b, region); + + LayerTriggerEvent event = this.callLayerTriggerEvent(e.getPlayer(), region, e.getBlock(), blocksAffected); + + if (event.isCancelled() || event.getBlocksAffected().isEmpty()) { + this.plugin.getCore().debug("LayerEnchant::onBlockBreak >> LayerTriggerEvent was cancelled. (Blocks affected size: " + event.getBlocksAffected().size(), this.plugin); + return; + } + + if (this.useEvents) { + final List ignored = this.plugin.getEnchantsListener().getIgnoredEvents(); + blocksAffected = event.getBlocksAffected().stream().filter(block -> { + final BlockBreakEvent blockEvent = new BlockBreakEvent(block, p); + ignored.add(blockEvent); + Bukkit.getPluginManager().callEvent(blockEvent); + ignored.remove(blockEvent); + return !e.isCancelled(); + }).collect(Collectors.toList()); + } else { + blocksAffected = event.getBlocksAffected(); + } + + if (!this.plugin.getCore().isUltraBackpacksEnabled()) { + handleAffectedBlocks(p, region, blocksAffected); + } else { + UltraBackpacksAPI.handleBlocksBroken(p, blocksAffected); + } + + if (!this.useEvents && this.plugin.isMinesModuleEnabled()) { + Mine mine = plugin.getCore().getMines().getApi().getMineAtLocation(e.getBlock().getLocation()); + if (mine != null) { + mine.handleBlockBreak(blocksAffected); + } + } + + if (this.countBlocksBroken) { + plugin.getEnchantsManager().addBlocksBrokenToItem(p, blocksAffected.size()); + } + + if (!this.useEvents) { + plugin.getCore().getTokens().getTokensManager().handleBlockBreak(p, blocksAffected, countBlocksBroken); + } + + long timeEnd = Time.nowMillis(); + this.plugin.getCore().debug("LayerEnchant::onBlockBreak >> Took " + (timeEnd - startTime) + " ms.", this.plugin); + } + + private List getAffectedBlocks(Block startBlock, IWrappedRegion region) { + List blocksAffected = new ArrayList<>(); + ICuboidSelection selection = (ICuboidSelection) region.getSelection(); + for (int x = selection.getMinimumPoint().getBlockX(); x <= selection.getMaximumPoint().getBlockX(); x++) { + for (int z = selection.getMinimumPoint().getBlockZ(); z <= selection.getMaximumPoint().getBlockZ(); z++) { + Block b1 = startBlock.getWorld().getBlockAt(x, startBlock.getY(), z); + if (b1.getType() == Material.AIR) { + continue; + } + blocksAffected.add(b1); + } + } + return blocksAffected; + } + + private void handleAffectedBlocks(Player p, IWrappedRegion region, List blocksAffected) { + double totalDeposit = 0.0; + int fortuneLevel = EnchantUtils.getItemFortuneLevel(p.getItemInHand()); + boolean autoSellPlayerEnabled = this.plugin.isAutoSellModuleEnabled() && plugin.getCore().getAutoSell().getManager().hasAutoSellEnabled(p); + + for (Block block : blocksAffected) { + + int amplifier = fortuneLevel; + if (FortuneEnchant.isBlockBlacklisted(block)) { + amplifier = 1; + } + + if (autoSellPlayerEnabled) { + totalDeposit += ((plugin.getCore().getAutoSell().getManager().getPriceForBlock(region.getId(), block) + 0.0) * amplifier); + } else { + ItemStack itemToGive = CompMaterial.fromBlock(block).toItem(amplifier); + p.getInventory().addItem(itemToGive); + } + block.setType(Material.AIR, true); + } + this.giveEconomyRewardToPlayer(p, totalDeposit); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + private void giveEconomyRewardToPlayer(Player p, double totalDeposit) { + double total = this.plugin.isMultipliersModuleEnabled() ? plugin.getCore().getMultipliers().getApi().getTotalToDeposit(p, totalDeposit, MultiplierType.SELL) : totalDeposit; + + plugin.getCore().getEconomy().depositPlayer(p, total); + + if (plugin.isAutoSellModuleEnabled()) { + plugin.getCore().getAutoSell().getManager().addToCurrentEarnings(p, total); + } + } + + private LayerTriggerEvent callLayerTriggerEvent(Player player, IWrappedRegion region, Block originBlock, List blocksAffected) { + LayerTriggerEvent event = new LayerTriggerEvent(player, region, originBlock, blocksAffected); + Events.callSync(event); + this.plugin.getCore().debug("LayerEnchant::callLayerTriggerEvent >> LayerTriggerEvent called.", this.plugin); + return event; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.countBlocksBroken = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Count-Blocks-Broken"); + this.useEvents = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Use-Events"); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/NightVisionEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/NightVisionEnchant.java new file mode 100644 index 0000000..3a98515 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/NightVisionEnchant.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public final class NightVisionEnchant extends XPrisonEnchantment { + public NightVisionEnchant(XPrisonEnchants instance) { + super(instance, 7); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + if (level == 0) { + this.onUnequip(p, pickAxe, level); + return; + } + p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, Integer.MAX_VALUE, level - 1, true, true), true); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + p.removePotionEffect(PotionEffectType.NIGHT_VISION); + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/NukeEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/NukeEnchant.java new file mode 100644 index 0000000..4184421 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/NukeEnchant.java @@ -0,0 +1,213 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.ultrabackpacks.api.UltraBackpacksAPI; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.api.events.NukeTriggerEvent; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.utils.EnchantUtils; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.multipliers.enums.MultiplierType; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.misc.MathUtils; +import dev.drawethree.xprison.utils.misc.RegionUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import me.lucko.helper.Events; +import me.lucko.helper.time.Time; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.codemc.worldguardwrapper.flag.WrappedState; +import org.codemc.worldguardwrapper.region.IWrappedRegion; +import org.codemc.worldguardwrapper.selection.ICuboidSelection; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +public final class NukeEnchant extends XPrisonEnchantment { + + private double chance; + private boolean countBlocksBroken; + private boolean removeBlocks; + private boolean useEvents; + private String message; + + public NukeEnchant(XPrisonEnchants instance) { + super(instance, 21); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.countBlocksBroken = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Count-Blocks-Broken"); + this.removeBlocks = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Remove-Blocks"); + this.useEvents = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Use-Events"); + this.message = TextUtils.applyColor(plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Message")); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long startTime = Time.nowMillis(); + final Player p = e.getPlayer(); + final Block b = e.getBlock(); + + IWrappedRegion region = RegionUtils.getRegionWithHighestPriorityAndFlag(b.getLocation(), Constants.ENCHANTS_WG_FLAG_NAME, WrappedState.ALLOW); + + if (region == null) { + return; + } + + this.plugin.getCore().debug("NukeEnchant::onBlockBreak >> WG Region used: " + region.getId(), this.plugin); + List blocksAffected = this.getAffectedBlocks(b, region); + + NukeTriggerEvent event = this.callNukeTriggerEvent(e.getPlayer(), region, e.getBlock(), blocksAffected); + + if (event.isCancelled() || event.getBlocksAffected().isEmpty()) { + this.plugin.getCore().debug("NukeEnchant::onBlockBreak >> NukeTriggerEvent was cancelled. (Blocks affected size: " + event.getBlocksAffected().size(), this.plugin); + return; + } + + if (this.useEvents) { + final List ignored = this.plugin.getEnchantsListener().getIgnoredEvents(); + blocksAffected = event.getBlocksAffected().stream().filter(block -> { + final BlockBreakEvent blockEvent = new BlockBreakEvent(block, p); + ignored.add(blockEvent); + Bukkit.getPluginManager().callEvent(blockEvent); + ignored.remove(blockEvent); + return !e.isCancelled(); + }).collect(Collectors.toList()); + } else { + blocksAffected = event.getBlocksAffected(); + } + + if (!this.plugin.getCore().isUltraBackpacksEnabled()) { + handleAffectedBlocks(p, region, blocksAffected); + } else { + UltraBackpacksAPI.handleBlocksBroken(p, blocksAffected); + } + + if (!this.useEvents && this.plugin.isMinesModuleEnabled() && removeBlocks) { + Mine mine = plugin.getCore().getMines().getApi().getMineAtLocation(e.getBlock().getLocation()); + + if (mine != null) { + mine.handleBlockBreak(blocksAffected); + } + } + + if (this.countBlocksBroken) { + plugin.getEnchantsManager().addBlocksBrokenToItem(p, blocksAffected.size()); + } + + if (!this.useEvents) { + plugin.getCore().getTokens().getTokensManager().handleBlockBreak(p, blocksAffected, countBlocksBroken); + } + + long timeEnd = Time.nowMillis(); + this.plugin.getCore().debug("NukeEnchant::onBlockBreak >> Took " + (timeEnd - startTime) + " ms.", this.plugin); + } + + private void handleAffectedBlocks(Player p, IWrappedRegion region, List blocksAffected) { + double totalDeposit = 0.0; + int fortuneLevel = EnchantUtils.getItemFortuneLevel(p.getItemInHand()); + boolean autoSellPlayerEnabled = this.plugin.isAutoSellModuleEnabled() && plugin.getCore().getAutoSell().getManager().hasAutoSellEnabled(p); + + for (Block block : blocksAffected) { + + int amplifier = fortuneLevel; + + if (FortuneEnchant.isBlockBlacklisted(block)) { + amplifier = 1; + } + + if (autoSellPlayerEnabled) { + totalDeposit += ((plugin.getCore().getAutoSell().getManager().getPriceForBlock(region.getId(), block) + 0.0) * amplifier); + } else { + ItemStack itemToGive = CompMaterial.fromBlock(block).toItem(amplifier); + p.getInventory().addItem(itemToGive); + } + + if (this.removeBlocks) { + block.setType(Material.AIR, true); + } + + } + this.giveEconomyRewardsToPlayer(p, totalDeposit); + } + + private List getAffectedBlocks(Block b, IWrappedRegion region) { + List blocksAffected = new ArrayList<>(); + ICuboidSelection selection = (ICuboidSelection) region.getSelection(); + for (int x = selection.getMinimumPoint().getBlockX(); x <= selection.getMaximumPoint().getBlockX(); x++) { + for (int z = selection.getMinimumPoint().getBlockZ(); z <= selection.getMaximumPoint().getBlockZ(); z++) { + for (int y = selection.getMinimumPoint().getBlockY(); y <= selection.getMaximumPoint().getBlockY(); y++) { + Block b1 = b.getWorld().getBlockAt(x, y, z); + if (b1.getType() == Material.AIR) { + continue; + } + blocksAffected.add(b1); + } + } + } + return blocksAffected; + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + private void giveEconomyRewardsToPlayer(Player p, double totalDeposit) { + double total = this.plugin.isMultipliersModuleEnabled() ? plugin.getCore().getMultipliers().getApi().getTotalToDeposit(p, totalDeposit, MultiplierType.SELL) : totalDeposit; + + plugin.getCore().getEconomy().depositPlayer(p, total); + + if (plugin.isAutoSellModuleEnabled()) { + plugin.getCore().getAutoSell().getManager().addToCurrentEarnings(p, total); + } + + if (message != null || !message.isEmpty()) { + PlayerUtils.sendMessage(p,message.replace("%money%", MathUtils.formatNumber(total))); + } + } + + private NukeTriggerEvent callNukeTriggerEvent(Player p, IWrappedRegion region, Block startBlock, List affectedBlocks) { + NukeTriggerEvent event = new NukeTriggerEvent(p, region, startBlock, affectedBlocks); + Events.callSync(event); + this.plugin.getCore().debug("NukeEnchant::callNukeTriggerEvent >> NukeTriggerEvent called.", this.plugin); + return event; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.countBlocksBroken = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Count-Blocks-Broken"); + this.removeBlocks = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Remove-Blocks"); + this.useEvents = plugin.getEnchantsConfig().getYamlConfig().getBoolean("enchants." + id + ".Use-Events"); + this.message = TextUtils.applyColor(plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Message")); + + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/PrestigeFinderEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/PrestigeFinderEnchant.java new file mode 100644 index 0000000..c7e6f74 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/PrestigeFinderEnchant.java @@ -0,0 +1,71 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class PrestigeFinderEnchant extends XPrisonEnchantment { + + private double chance; + private String commandToExecute; + private String amountToGiveExpression; + + public PrestigeFinderEnchant(XPrisonEnchants instance) { + super(instance, 16); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandToExecute = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Command"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + int levels = (int) createExpression(enchantLevel).evaluate(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), commandToExecute.replace("%player%", e.getPlayer().getName()).replace("%amount%", String.valueOf(levels))); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandToExecute = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Command"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/SalaryEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/SalaryEnchant.java new file mode 100644 index 0000000..b119533 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/SalaryEnchant.java @@ -0,0 +1,75 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class SalaryEnchant extends XPrisonEnchantment { + + private double chance; + private String amountToGiveExpression; + + public SalaryEnchant(XPrisonEnchants instance) { + super(instance, 12); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + double randAmount = createExpression(enchantLevel).evaluate(); + + plugin.getCore().getEconomy().depositPlayer(e.getPlayer(), randAmount); + + if (this.plugin.isAutoSellModuleEnabled()) { + plugin.getCore().getAutoSell().getManager().addToCurrentEarnings(e.getPlayer(), randAmount); + } + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return this.chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/SpeedEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/SpeedEnchant.java new file mode 100644 index 0000000..a860626 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/SpeedEnchant.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public final class SpeedEnchant extends XPrisonEnchantment { + public SpeedEnchant(XPrisonEnchants instance) { + super(instance, 5); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + if (level == 0) { + this.onUnequip(p, pickAxe, level); + return; + } + p.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, Integer.MAX_VALUE, level - 1, true, true), true); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + p.removePotionEffect(PotionEffectType.SPEED); + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/TokenatorEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/TokenatorEnchant.java new file mode 100644 index 0000000..f4417b2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/TokenatorEnchant.java @@ -0,0 +1,76 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public final class TokenatorEnchant extends XPrisonEnchantment { + + private double chance; + private String amountToGiveExpression; + + + public TokenatorEnchant(XPrisonEnchants instance) { + super(instance, 14); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + if (!this.plugin.getCore().isModuleEnabled(XPrisonTokens.MODULE_NAME)) { + return; + } + + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + long randAmount = (long) createExpression(enchantLevel).evaluate(); + plugin.getCore().getTokens().getTokensManager().giveTokens(e.getPlayer(), randAmount, null, ReceiveCause.MINING); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.amountToGiveExpression = plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Amount-To-Give"); + } + + private Expression createExpression(int level) { + return new ExpressionBuilder(this.amountToGiveExpression) + .variables("level") + .build() + .setVariable("level", level); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/UnbreakingEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/UnbreakingEnchant.java new file mode 100644 index 0000000..f68b827 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/UnbreakingEnchant.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public final class UnbreakingEnchant extends XPrisonEnchantment { + + public UnbreakingEnchant(XPrisonEnchants instance) { + super(instance, 2); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + ItemMeta meta = pickAxe.getItemMeta(); + meta.addEnchant(Enchantment.DURABILITY, level, true); + pickAxe.setItemMeta(meta); + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return 100.0; + } + + @Override + public void reload() { + super.reload(); + } + + @Override + public String getAuthor() { + return "Drawethree"; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/model/impl/VoucherFinderEnchant.java b/src/main/java/dev/drawethree/xprison/enchants/model/impl/VoucherFinderEnchant.java new file mode 100644 index 0000000..abc2cca --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/model/impl/VoucherFinderEnchant.java @@ -0,0 +1,87 @@ +package dev.drawethree.xprison.enchants.model.impl; + +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import lombok.AllArgsConstructor; +import lombok.Getter; +import me.lucko.helper.random.RandomSelector; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public final class VoucherFinderEnchant extends XPrisonEnchantment { + + private double chance; + private List commandsToExecute; + + public VoucherFinderEnchant(XPrisonEnchants instance) { + super(instance, 20); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandsToExecute = this.loadCommands(); + } + + @Override + public void onEquip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onUnequip(Player p, ItemStack pickAxe, int level) { + + } + + @Override + public void onBlockBreak(BlockBreakEvent e, int enchantLevel) { + double chance = getChanceToTrigger(enchantLevel); + + if (chance < ThreadLocalRandom.current().nextDouble(100)) { + return; + } + + CommandWithChance randomCmd = getRandomCommandToExecute(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), randomCmd.getCommand().replace("%player%", e.getPlayer().getName())); + } + + private CommandWithChance getRandomCommandToExecute() { + return RandomSelector.weighted(this.commandsToExecute, CommandWithChance::getChance).pick(); + } + + @Override + public double getChanceToTrigger(int enchantLevel) { + return this.chance * enchantLevel; + } + + @Override + public void reload() { + super.reload(); + this.chance = plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Chance"); + this.commandsToExecute = this.loadCommands(); + } + + private List loadCommands() { + List returnList = new ArrayList<>(); + for (String key : this.plugin.getEnchantsConfig().getYamlConfig().getConfigurationSection("enchants." + id + ".Commands").getKeys(false)) { + String cmd = this.plugin.getEnchantsConfig().getYamlConfig().getString("enchants." + id + ".Commands." + key + ".command"); + double chance = this.plugin.getEnchantsConfig().getYamlConfig().getDouble("enchants." + id + ".Commands." + key + ".chance"); + returnList.add(new CommandWithChance(cmd, chance)); + } + return returnList; + } + + @Override + public String getAuthor() { + return "Drawethree"; + } + + @AllArgsConstructor + @Getter + private static class CommandWithChance { + private final String command; + private final double chance; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/repo/EnchantsRepository.java b/src/main/java/dev/drawethree/xprison/enchants/repo/EnchantsRepository.java new file mode 100644 index 0000000..85dd54d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/repo/EnchantsRepository.java @@ -0,0 +1,118 @@ +package dev.drawethree.xprison.enchants.repo; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.model.impl.*; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.apache.commons.lang.Validate; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class EnchantsRepository { + + private final XPrisonEnchants plugin; + + private final Map enchantsById; + private final Map enchantsByName; + + public EnchantsRepository(XPrisonEnchants plugin) { + this.plugin = plugin; + this.enchantsById = new HashMap<>(); + this.enchantsByName = new HashMap<>(); + } + + public Collection getAll() { + return enchantsById.values(); + } + + public XPrisonEnchantment getEnchantBy(Object object) { + if (object instanceof Integer) { + return getEnchantById((int) object); + } else { + final String s = String.valueOf(object); + try { + return getEnchantById(Integer.parseInt(s)); + } catch (NumberFormatException e) { + return getEnchantByName(s); + } + } + } + + public XPrisonEnchantment getEnchantById(int id) { + return enchantsById.get(id); + } + + public XPrisonEnchantment getEnchantByName(String name) { + return enchantsByName.get(name.toLowerCase()); + } + + public void reload() { + + enchantsById.values().forEach(XPrisonEnchantment::reload); + + XPrison.getInstance().getLogger().info(TextUtils.applyColor("&aSuccessfully reloaded all enchants.")); + } + + public void loadDefaultEnchantments() { + register(new EfficiencyEnchant(this.plugin)); + register(new UnbreakingEnchant(this.plugin)); + register(new FortuneEnchant(this.plugin)); + register(new HasteEnchant(this.plugin)); + register(new SpeedEnchant(this.plugin)); + register(new JumpBoostEnchant(this.plugin)); + register(new NightVisionEnchant(this.plugin)); + register(new FlyEnchant(this.plugin)); + register(new ExplosiveEnchant(this.plugin)); + register(new LayerEnchant(this.plugin)); + register(new CharityEnchant(this.plugin)); + register(new SalaryEnchant(this.plugin)); + register(new BlessingEnchant(this.plugin)); + register(new TokenatorEnchant(this.plugin)); + register(new KeyFinderEnchant(this.plugin)); + register(new PrestigeFinderEnchant(this.plugin)); + register(new BlockBoosterEnchant(this.plugin)); + register(new KeyallsEnchant(this.plugin)); + if (XPrison.getInstance().isUltraBackpacksEnabled()) { + register(new BackpackAutoSellEnchant(this.plugin)); + } else { + register(new AutoSellEnchant(this.plugin)); + } + register(new VoucherFinderEnchant(this.plugin)); + register(new NukeEnchant(this.plugin)); + register(new GemFinderEnchant(this.plugin)); + register(new GangValueFinderEnchant(this.plugin)); + } + + public boolean register(XPrisonEnchantment enchantment) { + + if (enchantsById.containsKey(enchantment.getId()) || enchantsByName.containsKey(enchantment.getRawName())) { + XPrison.getInstance().getLogger().warning(TextUtils.applyColor("&cUnable to register enchant " + enchantment.getName() + "&c created by " + enchantment.getAuthor() + ". That enchant is already registered.")); + return false; + } + + Validate.notNull(enchantment.getRawName()); + + enchantsById.put(enchantment.getId(), enchantment); + enchantsByName.put(enchantment.getRawName().toLowerCase(), enchantment); + + XPrison.getInstance().getLogger().info(TextUtils.applyColor("&aSuccessfully registered enchant " + enchantment.getName() + "&a created by " + enchantment.getAuthor())); + return true; + } + + public boolean unregister(XPrisonEnchantment enchantment) { + + if (!enchantsById.containsKey(enchantment.getId()) && !enchantsByName.containsKey(enchantment.getRawName())) { + XPrison.getInstance().getLogger().warning(TextUtils.applyColor("&cUnable to unregister enchant " + enchantment.getName() + "&c created by " + enchantment.getAuthor() + ". That enchant is not registered.")); + return false; + } + + enchantsById.remove(enchantment.getId()); + enchantsByName.remove(enchantment.getRawName()); + + XPrison.getInstance().getLogger().info(TextUtils.applyColor("&aSuccessfully unregistered enchant " + enchantment.getName() + "&a created by " + enchantment.getAuthor())); + return true; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/utils/EnchantUtils.java b/src/main/java/dev/drawethree/xprison/enchants/utils/EnchantUtils.java new file mode 100644 index 0000000..ec7ba82 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/utils/EnchantUtils.java @@ -0,0 +1,50 @@ +package dev.drawethree.xprison.enchants.utils; + +import com.saicone.rtag.util.ServerInstance; +import dev.drawethree.xprison.enchants.XPrisonEnchants; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.model.impl.FortuneEnchant; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; + +public final class EnchantUtils { + + private EnchantUtils() { + throw new UnsupportedOperationException("Cannot instantiate."); + } + + public static int getFortuneBlockCount(ItemStack pickaxe, Block block) { + if (FortuneEnchant.isBlockBlacklisted(block)) { + return 1; + } + return getItemFortuneLevel(pickaxe) + 1; + } + + public static int getItemFortuneLevel(ItemStack item) { + if (item == null) { + return 0; + } + XPrisonEnchantment fortuneEnchant = XPrisonEnchants.getInstance().getEnchantsRepository().getEnchantById(3); + + if (fortuneEnchant == null || !fortuneEnchant.isEnabled()) { + return 0; + } + + return XPrisonEnchants.getInstance().getEnchantsManager().getEnchantLevel(item, fortuneEnchant); + } + + public static int getDurability(ItemStack item) { + return getDurability(item, item.getItemMeta()); + } + + public static int getDurability(ItemStack item, ItemMeta meta) { + if (ServerInstance.isLegacy) { + return item.getDurability(); + } else if (meta instanceof Damageable) { + return ((Damageable) meta).getDamage(); + } + return 0; + } +} diff --git a/src/main/java/dev/drawethree/xprison/enchants/utils/GuiUtils.java b/src/main/java/dev/drawethree/xprison/enchants/utils/GuiUtils.java new file mode 100644 index 0000000..0a98f19 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/enchants/utils/GuiUtils.java @@ -0,0 +1,32 @@ +package dev.drawethree.xprison.enchants.utils; + +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; + +import java.util.ArrayList; +import java.util.List; + +public class GuiUtils { + + private GuiUtils() { + throw new UnsupportedOperationException("Cannot instantiate."); + } + + public static List translateGuiLore(XPrisonEnchantment enchantment, List guiItemLore, + int currentLevel) { + List newList = new ArrayList<>(); + for (String s : guiItemLore) { + if (s.contains("%description%")) { + newList.addAll(enchantment.getDescription()); + continue; + } + newList.add(s + .replace("%refund%", String.format("%,d", enchantment.getRefundForLevel(currentLevel))) + .replace("%cost%", String.format("%,d", enchantment.getCost() + (enchantment.getIncreaseCost() * currentLevel))) + .replace("%max_level%", enchantment.getMaxLevel() == Integer.MAX_VALUE ? "Unlimited" : String.format("%,d", enchantment.getMaxLevel())) + .replace("%chance%", String.format("%,.2f", enchantment.getChanceToTrigger(currentLevel))) + .replace("%current_level%", String.format("%,d", currentLevel)) + .replace("%pickaxe_level%", String.format("%,d", enchantment.getRequiredPickaxeLevel()))); + } + return newList; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/XPrisonGangs.java b/src/main/java/dev/drawethree/xprison/gangs/XPrisonGangs.java new file mode 100644 index 0000000..6b350a2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/XPrisonGangs.java @@ -0,0 +1,120 @@ +package dev.drawethree.xprison.gangs; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.gangs.api.XPrisonGangsAPI; +import dev.drawethree.xprison.gangs.api.XPrisonGangsAPIImpl; +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.config.GangsConfig; +import dev.drawethree.xprison.gangs.listener.GangsListener; +import dev.drawethree.xprison.gangs.managers.GangsManager; +import dev.drawethree.xprison.gangs.model.GangTopByValueProvider; +import dev.drawethree.xprison.gangs.model.GangTopProvider; +import dev.drawethree.xprison.gangs.model.GangUpdateTopTask; +import dev.drawethree.xprison.gangs.repo.GangsRepository; +import dev.drawethree.xprison.gangs.repo.impl.GangsRepositoryImpl; +import dev.drawethree.xprison.gangs.service.GangsService; +import dev.drawethree.xprison.gangs.service.impl.GangsServiceImpl; +import lombok.Getter; + +public final class XPrisonGangs implements XPrisonModule { + + public static final String MODULE_NAME = "Gangs"; + + @Getter + private static XPrisonGangs instance; + + @Getter + private XPrisonGangsAPI api; + + @Getter + private GangsConfig config; + + @Getter + private GangsManager gangsManager; + + @Getter + private GangTopProvider gangTopProvider; + + @Getter + private GangUpdateTopTask gangUpdateTopTask; + + @Getter + private final XPrison core; + + @Getter + private GangsRepository gangsRepository; + + @Getter + private GangsService gangsService; + + private boolean enabled; + + public XPrisonGangs(XPrison prisonCore) { + instance = this; + this.core = prisonCore; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.config.reload(); + } + + @Override + public void enable() { + this.config = new GangsConfig(this); + this.config.load(); + + GangCommand gangCommand = new GangCommand(this); + gangCommand.register(); + + this.gangsRepository = new GangsRepositoryImpl(this.core.getPluginDatabase()); + this.gangsRepository.createTables(); + + this.gangsService = new GangsServiceImpl(this.gangsRepository); + + this.gangsManager = new GangsManager(this); + this.gangsManager.enable(); + + this.gangTopProvider = new GangTopByValueProvider(this.gangsManager); + + GangsListener gangsListener = new GangsListener(this); + gangsListener.register(); + + this.gangUpdateTopTask = new GangUpdateTopTask(this, this.gangTopProvider); + this.gangUpdateTopTask.start(); + + + this.api = new XPrisonGangsAPIImpl(this.gangsManager); + + this.enabled = true; + } + + + @Override + public void disable() { + this.gangsManager.disable(); + this.gangUpdateTopTask.stop(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.gangsRepository.clearTableData(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/api/XPrisonGangsAPI.java b/src/main/java/dev/drawethree/xprison/gangs/api/XPrisonGangsAPI.java new file mode 100644 index 0000000..b6795dc --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/api/XPrisonGangsAPI.java @@ -0,0 +1,35 @@ +package dev.drawethree.xprison.gangs.api; + +import dev.drawethree.xprison.gangs.model.Gang; +import org.bukkit.OfflinePlayer; + +import java.util.Collection; +import java.util.Optional; + +public interface XPrisonGangsAPI { + + /** + * Method to get Gang from player + * + * @param player OfflinePlayer + * @return Optional gang + */ + Optional getPlayerGang(OfflinePlayer player); + + /** + * Method to get Gang from name + * + * @param name name of gang + * @return Optional gang + */ + Optional getByName(String name); + + /** + * Method to get all gangs + * + * @return List of gangs + */ + Collection getAllGangs(); + + +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/api/XPrisonGangsAPIImpl.java b/src/main/java/dev/drawethree/xprison/gangs/api/XPrisonGangsAPIImpl.java new file mode 100644 index 0000000..0bd0312 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/api/XPrisonGangsAPIImpl.java @@ -0,0 +1,32 @@ +package dev.drawethree.xprison.gangs.api; + +import dev.drawethree.xprison.gangs.managers.GangsManager; +import dev.drawethree.xprison.gangs.model.Gang; +import org.bukkit.OfflinePlayer; + +import java.util.Collection; +import java.util.Optional; + +public final class XPrisonGangsAPIImpl implements XPrisonGangsAPI { + + private final GangsManager gangsManager; + + public XPrisonGangsAPIImpl(GangsManager gangsManager) { + this.gangsManager = gangsManager; + } + + @Override + public Optional getPlayerGang(OfflinePlayer player) { + return this.gangsManager.getPlayerGang(player); + } + + @Override + public Optional getByName(String name) { + return this.gangsManager.getGangWithName(name); + } + + @Override + public Collection getAllGangs() { + return this.gangsManager.getAllGangs(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/api/events/GangCreateEvent.java b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangCreateEvent.java new file mode 100644 index 0000000..e2d7b2d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangCreateEvent.java @@ -0,0 +1,51 @@ +package dev.drawethree.xprison.gangs.api.events; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import dev.drawethree.xprison.gangs.model.Gang; +import lombok.Getter; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class GangCreateEvent extends XPrisonEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + private boolean cancelled; + + @Getter + private final CommandSender creator; + + @Getter + private final Gang gang; + + /** + * Fired when gang is created + * + * @param creator CommandSender who created the gang + * @param gang Gang + */ + public GangCreateEvent(CommandSender creator, Gang gang) { + this.creator = creator; + this.gang = gang; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/api/events/GangDisbandEvent.java b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangDisbandEvent.java new file mode 100644 index 0000000..164a12b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangDisbandEvent.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.gangs.api.events; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import dev.drawethree.xprison.gangs.model.Gang; +import lombok.Getter; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class GangDisbandEvent extends XPrisonEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + @Getter + private final Gang gang; + private boolean cancelled; + + /** + * Called when gang is disbanded + * + * @param gang Gang + */ + public GangDisbandEvent(Gang gang) { + this.gang = gang; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/api/events/GangJoinEvent.java b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangJoinEvent.java new file mode 100644 index 0000000..cfe2ff7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangJoinEvent.java @@ -0,0 +1,52 @@ +package dev.drawethree.xprison.gangs.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import dev.drawethree.xprison.gangs.model.Gang; +import lombok.Getter; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class GangJoinEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + private boolean cancelled; + + @Getter + private final OfflinePlayer player; + + @Getter + private final Gang gang; + + /** + * Called when player joins a gang + * + * @param player Player + * @param gang Gang + */ + public GangJoinEvent(OfflinePlayer player, Gang gang) { + super(player); + this.player = player; + this.gang = gang; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/api/events/GangLeaveEvent.java b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangLeaveEvent.java new file mode 100644 index 0000000..362ae98 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/api/events/GangLeaveEvent.java @@ -0,0 +1,58 @@ +package dev.drawethree.xprison.gangs.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import dev.drawethree.xprison.gangs.enums.GangLeaveReason; +import dev.drawethree.xprison.gangs.model.Gang; +import lombok.Getter; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class GangLeaveEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + private boolean cancelled; + + @Getter + private final OfflinePlayer player; + + @Getter + private final Gang gang; + + @Getter + private final GangLeaveReason leaveReason; + + /** + * Called when player leaves a gang + * + * @param player Player + * @param gang Gang + * @param leaveReason GangLeaveReason + */ + public GangLeaveEvent(OfflinePlayer player, Gang gang, GangLeaveReason leaveReason) { + super(player); + this.player = player; + this.gang = gang; + this.leaveReason = leaveReason; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/GangCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/GangCommand.java new file mode 100644 index 0000000..e83a95d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/GangCommand.java @@ -0,0 +1,117 @@ +package dev.drawethree.xprison.gangs.commands; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.commands.impl.*; +import dev.drawethree.xprison.gangs.commands.impl.admin.GangAdminSubCommand; +import dev.drawethree.xprison.gangs.commands.impl.value.GangValueSubCommand; +import dev.drawethree.xprison.gangs.gui.panel.GangPanelGUI; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import me.lucko.helper.command.context.CommandContext; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.util.*; + +public class GangCommand { + + @Getter + private final XPrisonGangs plugin; + private final Map subCommands; + + public GangCommand(XPrisonGangs plugin) { + this.plugin = plugin; + this.subCommands = new HashMap<>(); + } + + private void registerSubCommands() { + registerSubCommand(new GangHelpSubCommand(this)); + registerSubCommand(new GangInfoSubCommand(this)); + registerSubCommand(new GangCreateSubCommand(this)); + registerSubCommand(new GangInviteSubCommand(this)); + registerSubCommand(new GangAcceptSubCommand(this)); + registerSubCommand(new GangLeaveSubCommand(this)); + registerSubCommand(new GangDisbandSubCommand(this)); + registerSubCommand(new GangKickSubCommand(this)); + registerSubCommand(new GangTopSubCommand(this)); + registerSubCommand(new GangAdminSubCommand(this)); + registerSubCommand(new GangValueSubCommand(this)); + registerSubCommand(new GangRenameSubCommand(this)); + registerSubCommand(new GangChatSubCommand(this)); + } + + public void register() { + this.registerSubCommands(); + this.registerMainCommand(); + } + + private void registerMainCommand() { + Commands.create() + .tabHandler(this::createTabHandler) + .handler(c -> { + + if (c.args().size() == 0) { + + if (c.sender() instanceof Player) { + Optional optionalGang = this.getPlugin().getGangsManager().getPlayerGang((Player) c.sender()); + optionalGang.ifPresent(gang -> openGangPanelGui(gang, (Player) c.sender())); + return; + } else if (c.sender() instanceof ConsoleCommandSender) { + this.getHelpSubCommand().execute(c.sender(), c.args()); + return; + } + + } + + GangSubCommand subCommand = this.getSubCommand(Objects.requireNonNull(c.rawArg(0))); + + if (subCommand != null) { + + if (!subCommand.canExecute(c.sender())) { + PlayerUtils.sendMessage(c.sender(), this.plugin.getConfig().getMessage("no-permission")); + return; + } + + subCommand.execute(c.sender(), c.args().subList(1, c.args().size())); + } else { + this.getHelpSubCommand().execute(c.sender(), c.args()); + } + }).registerAndBind(this.plugin.getCore(), this.plugin.getConfig().getGangsCommandAliases()); + } + + private GangSubCommand getHelpSubCommand() { + return getSubCommand("help"); + } + + private List createTabHandler(CommandContext context) { + + if (context.args().size() == 0) { + return new ArrayList<>(this.subCommands.keySet()); + } + + GangSubCommand subCommand = getSubCommand(context.rawArg(0)); + + if (subCommand != null) { + return subCommand.getTabComplete(); + } + + return new ArrayList<>(this.subCommands.keySet()); + } + + private void registerSubCommand(GangSubCommand command) { + for (String alias : command.getAliases()) { + this.subCommands.put(alias.toLowerCase(), command); + } + } + + private GangSubCommand getSubCommand(String arg) { + return subCommands.get(arg.toLowerCase()); + } + + private void openGangPanelGui(Gang gang, Player player) { + new GangPanelGUI(this.plugin, gang, player).open(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/GangSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/GangSubCommand.java new file mode 100644 index 0000000..4d282f5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/GangSubCommand.java @@ -0,0 +1,44 @@ +package dev.drawethree.xprison.gangs.commands; + +import lombok.Getter; +import org.bukkit.command.CommandSender; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class GangSubCommand { + + @Getter + protected final GangCommand command; + @Getter + private final String[] aliases; + + protected final Map subCommands; + + public GangSubCommand(GangCommand command, String... aliases) { + this.command = command; + this.aliases = aliases; + this.subCommands = new HashMap<>(); + } + + public abstract boolean execute(CommandSender sender, List args); + + public abstract String getUsage(); + + public abstract boolean canExecute(CommandSender sender); + + public abstract List getTabComplete(); + + protected void registerSubCommand(GangSubCommand subCommand) { + for (String alias : subCommand.getAliases()) { + this.subCommands.put(alias.toLowerCase(), subCommand); + } + } + + protected GangSubCommand getSubCommand(String name) { + return subCommands.get(name.toLowerCase()); + } + + +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangAcceptSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangAcceptSubCommand.java new file mode 100644 index 0000000..db22010 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangAcceptSubCommand.java @@ -0,0 +1,52 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class GangAcceptSubCommand extends GangSubCommand { + + public GangAcceptSubCommand(GangCommand command) { + super(command, "accept", "join"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang accept "; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player && args.size() == 1) { + + String gangName = args.get(0); + Optional gangOptional = this.command.getPlugin().getGangsManager().getGangWithName(gangName); + + if (!gangOptional.isPresent()) { + PlayerUtils.sendMessage(sender, this.command.getPlugin().getConfig().getMessage("gang-not-exists")); + return false; + } + + return this.command.getPlugin().getGangsManager().acceptInvite((Player) sender, gangOptional.get()); + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangChatSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangChatSubCommand.java new file mode 100644 index 0000000..6232065 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangChatSubCommand.java @@ -0,0 +1,42 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + + +public final class GangChatSubCommand extends GangSubCommand { + + public GangChatSubCommand(GangCommand command) { + super(command, "chat", "c"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang chat"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 0 && sender instanceof Player) { + Player p = (Player) sender; + return this.command.getPlugin().getGangsManager().toggleGangChat(p); + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangCreateSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangCreateSubCommand.java new file mode 100644 index 0000000..76dcc71 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangCreateSubCommand.java @@ -0,0 +1,42 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.enums.GangCreateResult; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public final class GangCreateSubCommand extends GangSubCommand { + + public GangCreateSubCommand(GangCommand command) { + super(command, "create", "new"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang create "; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player && args.size() == 1) { + return this.command.getPlugin().getGangsManager().createGang(args.get(0), (Player) sender) == GangCreateResult.SUCCESS; + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_CREATE_PERM); + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangDisbandSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangDisbandSubCommand.java new file mode 100644 index 0000000..469330d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangDisbandSubCommand.java @@ -0,0 +1,60 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.gui.panel.DisbandGangGUI; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class GangDisbandSubCommand extends GangSubCommand { + + public GangDisbandSubCommand(GangCommand command) { + super(command, "disband", "dis"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang disband"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player && args.size() == 0) { + + Player player = (Player) sender; + Optional gangOptional = this.command.getPlugin().getGangsManager().getPlayerGang(player); + + if (!gangOptional.isPresent()) { + PlayerUtils.sendMessage(player, this.command.getPlugin().getConfig().getMessage("not-in-gang")); + return false; + } + + Gang gang = gangOptional.get(); + + if (!gang.isOwner(player)) { + PlayerUtils.sendMessage(player, this.command.getPlugin().getConfig().getMessage("gang-not-owner")); + return false; + } + + new DisbandGangGUI(this.command.getPlugin(), player, gang).open(); + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangHelpSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangHelpSubCommand.java new file mode 100644 index 0000000..29039c8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangHelpSubCommand.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public final class GangHelpSubCommand extends GangSubCommand { + + public GangHelpSubCommand(GangCommand command) { + super(command, "help", "?"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang help"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.isEmpty()) { + this.command.getPlugin().getGangsManager().sendHelpMenu(sender); + if (sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM)) { + this.command.getPlugin().getGangsManager().sendAdminHelpMenu(sender); + } + return true; + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangInfoSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangInfoSubCommand.java new file mode 100644 index 0000000..10ca865 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangInfoSubCommand.java @@ -0,0 +1,59 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class GangInfoSubCommand extends GangSubCommand { + + public GangInfoSubCommand(GangCommand command) { + super(command, "info", "inspect"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang info "; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player) { + Player p = (Player) sender; + if (args.size() == 0) { + return this.command.getPlugin().getGangsManager().sendGangInfo(p, p); + } else if (args.size() == 1) { + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + + if (this.command.getPlugin().getGangsManager().getPlayerGang(target).isPresent()) { + return this.command.getPlugin().getGangsManager().sendGangInfo(p, target); + } else { + return this.command.getPlugin().getGangsManager().sendGangInfo(p, args.get(0)); + } + } + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + List tabComplete = new ArrayList<>(); + tabComplete.addAll(Players.all().stream().map(Player::getName).collect(Collectors.toList())); + tabComplete.addAll(this.command.getPlugin().getGangsManager().getAllGangs().stream().map(Gang::getName).collect(Collectors.toList())); + return tabComplete; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangInviteSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangInviteSubCommand.java new file mode 100644 index 0000000..bf5fe32 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangInviteSubCommand.java @@ -0,0 +1,44 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.stream.Collectors; + +public final class GangInviteSubCommand extends GangSubCommand { + + public GangInviteSubCommand(GangCommand command) { + super(command, "invite", "inv"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang invite [player]"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player && args.size() == 1) { + Player p = (Player) sender; + Player target = Players.getNullable(args.get(0)); + return this.command.getPlugin().getGangsManager().invitePlayer(p, target); + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangKickSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangKickSubCommand.java new file mode 100644 index 0000000..7463062 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangKickSubCommand.java @@ -0,0 +1,57 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class GangKickSubCommand extends GangSubCommand { + + public GangKickSubCommand(GangCommand command) { + super(command, "kick", "remove"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang kick "; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 1 && sender instanceof Player) { + Player p = (Player) sender; + Optional gang = this.command.getPlugin().getGangsManager().getPlayerGang(p); + + if (!gang.isPresent()) { + PlayerUtils.sendMessage(p, this.command.getPlugin().getConfig().getMessage("not-in-gang")); + return false; + } + + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + + return this.command.getPlugin().getGangsManager().removeFromGang(p, gang.get(), target); + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangLeaveSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangLeaveSubCommand.java new file mode 100644 index 0000000..5546333 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangLeaveSubCommand.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.enums.GangLeaveReason; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public final class GangLeaveSubCommand extends GangSubCommand { + + public GangLeaveSubCommand(GangCommand command) { + super(command, "leave", "quit"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang leave"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 0 && sender instanceof Player) { + Player p = (Player) sender; + return this.command.getPlugin().getGangsManager().leaveGang(p, GangLeaveReason.LEAVE); + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangRenameSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangRenameSubCommand.java new file mode 100644 index 0000000..66e9c93 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangRenameSubCommand.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.enums.GangRenameResult; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class GangRenameSubCommand extends GangSubCommand { + + public GangRenameSubCommand(GangCommand command) { + super(command, "rename"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang rename [new_name]"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 1 && sender instanceof Player) { + Player p = (Player) sender; + String newName = args.get(0); + + Optional gangOptional = this.command.getPlugin().getGangsManager().getPlayerGang(p); + + if (!gangOptional.isPresent()) { + PlayerUtils.sendMessage(p, this.command.getPlugin().getConfig().getMessage("not-in-gang")); + return false; + } + + Gang gang = gangOptional.get(); + + if (!gang.isOwner(p)) { + PlayerUtils.sendMessage(p, this.command.getPlugin().getConfig().getMessage("gang-not-owner")); + return false; + } + + return this.command.getPlugin().getGangsManager().renameGang(gang, newName, p) == GangRenameResult.SUCCESS; + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangTopSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangTopSubCommand.java new file mode 100644 index 0000000..a69a8ef --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/GangTopSubCommand.java @@ -0,0 +1,40 @@ +package dev.drawethree.xprison.gangs.commands.impl; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public final class GangTopSubCommand extends GangSubCommand { + + public GangTopSubCommand(GangCommand command) { + super(command, "top", "leaderboard"); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang top"; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 0) { + return this.command.getPlugin().getGangsManager().sendGangTop(sender); + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public List getTabComplete() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminDisbandSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminDisbandSubCommand.java new file mode 100644 index 0000000..8c3cdc8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminDisbandSubCommand.java @@ -0,0 +1,52 @@ +package dev.drawethree.xprison.gangs.commands.impl.admin; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class GangAdminDisbandSubCommand extends GangSubCommand { + + public GangAdminDisbandSubCommand(GangCommand command) { + super(command, "disband"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 1) { + Optional gangOptional = this.command.getPlugin().getGangsManager().getGangWithName(args.get(0)); + + if (!gangOptional.isPresent()) { + PlayerUtils.sendMessage(sender, this.command.getPlugin().getConfig().getMessage("gang-not-exists")); + return false; + } + + return this.command.getPlugin().getGangsManager().forceDisband(sender, gangOptional.get()); + } + return false; + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang admin disband "; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminJoinSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminJoinSubCommand.java new file mode 100644 index 0000000..582ab79 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminJoinSubCommand.java @@ -0,0 +1,53 @@ +package dev.drawethree.xprison.gangs.commands.impl.admin; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class GangAdminJoinSubCommand extends GangSubCommand { + + public GangAdminJoinSubCommand(GangCommand command) { + super(command, "join", "add"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 2) { + Player target = Players.getNullable(args.get(0)); + Optional gangOptional = this.command.getPlugin().getGangsManager().getGangWithName(args.get(1)); + + if (!gangOptional.isPresent()) { + PlayerUtils.sendMessage(sender, this.command.getPlugin().getConfig().getMessage("gang-not-exists")); + return false; + } + + return this.command.getPlugin().getGangsManager().forceAdd(sender, target, gangOptional.get()); + } + return false; + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang admin join "; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminKickSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminKickSubCommand.java new file mode 100644 index 0000000..0ca4fac --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminKickSubCommand.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.gangs.commands.impl.admin; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.stream.Collectors; + +public final class GangAdminKickSubCommand extends GangSubCommand { + + public GangAdminKickSubCommand(GangCommand command) { + super(command, "kick", "remove"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 1) { + Player target = Players.getNullable(args.get(0)); + return this.command.getPlugin().getGangsManager().forceRemove(sender, target); + } + return false; + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang admin kick "; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminRenameSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminRenameSubCommand.java new file mode 100644 index 0000000..366d428 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminRenameSubCommand.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.gangs.commands.impl.admin; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.stream.Collectors; + +public final class GangAdminRenameSubCommand extends GangSubCommand { + + public GangAdminRenameSubCommand(GangCommand command) { + super(command, "rename"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 2) { + String oldGangName = args.get(0); + String newGangName = args.get(1); + return this.command.getPlugin().getGangsManager().forceRename(sender, oldGangName, newGangName); + } + return false; + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang admin rename "; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return this.command.getPlugin().getGangsManager().getAllGangs().stream().map(Gang::getName).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminSubCommand.java new file mode 100644 index 0000000..a309245 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/admin/GangAdminSubCommand.java @@ -0,0 +1,47 @@ +package dev.drawethree.xprison.gangs.commands.impl.admin; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public final class GangAdminSubCommand extends GangSubCommand { + + public GangAdminSubCommand(GangCommand command) { + super(command, "admin"); + registerSubCommand(new GangAdminJoinSubCommand(command)); + registerSubCommand(new GangAdminKickSubCommand(command)); + registerSubCommand(new GangAdminDisbandSubCommand(command)); + registerSubCommand(new GangAdminRenameSubCommand(command)); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang admin "; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() > 0) { + GangSubCommand subCommand = getSubCommand(args.get(0)); + if (subCommand != null) { + return subCommand.execute(sender, args.subList(1, args.size())); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return new ArrayList<>(this.subCommands.keySet()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueAddSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueAddSubCommand.java new file mode 100644 index 0000000..be52184 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueAddSubCommand.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.gangs.commands.impl.value; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class GangValueAddSubCommand extends GangSubCommand { + public GangValueAddSubCommand(GangCommand command) { + super(command, "add"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 2) { + try { + Optional gang = this.command.getPlugin().getGangsManager().getGangWithName(args.get(0)); + + if (!gang.isPresent()) { + gang = this.command.getPlugin().getGangsManager().getPlayerGang(Players.getOfflineNullable(args.get(0))); + } + + if (!gang.isPresent()) { + PlayerUtils.sendMessage(sender, this.command.getPlugin().getConfig().getMessage("gang-not-exists")); + return false; + } + + long amount = Long.parseLong(args.get(1)); + String operation = "add"; + + return this.command.getPlugin().getGangsManager().modifyValue(sender, gang.get(), amount, operation); + } catch (Exception e) { + sender.sendMessage("§cInternal error."); + return false; + } + } + return false; + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang value add "; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return this.command.getPlugin().getGangsManager().getAllGangs().stream().map(Gang::getName).collect(Collectors.toList()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueRemoveSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueRemoveSubCommand.java new file mode 100644 index 0000000..3681f59 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueRemoveSubCommand.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.gangs.commands.impl.value; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public final class GangValueRemoveSubCommand extends GangSubCommand { + public GangValueRemoveSubCommand(GangCommand command) { + super(command, "remove"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() == 2) { + try { + Optional gang = this.command.getPlugin().getGangsManager().getGangWithName(args.get(0)); + + if (!gang.isPresent()) { + gang = this.command.getPlugin().getGangsManager().getPlayerGang(Players.getOfflineNullable(args.get(0))); + } + + if (!gang.isPresent()) { + PlayerUtils.sendMessage(sender, this.command.getPlugin().getConfig().getMessage("gang-not-exists")); + return false; + } + + long amount = Long.parseLong(args.get(1)); + String operation = "remove"; + + return this.command.getPlugin().getGangsManager().modifyValue(sender, gang.get(), amount, operation); + } catch (Exception e) { + sender.sendMessage("§cInternal error."); + return false; + } + } + return false; + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang value remove "; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return this.command.getPlugin().getGangsManager().getAllGangs().stream().map(Gang::getName).collect(Collectors.toList()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueSubCommand.java b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueSubCommand.java new file mode 100644 index 0000000..67fbf2b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/commands/impl/value/GangValueSubCommand.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.gangs.commands.impl.value; + +import dev.drawethree.xprison.gangs.commands.GangCommand; +import dev.drawethree.xprison.gangs.commands.GangSubCommand; +import dev.drawethree.xprison.gangs.utils.GangsConstants; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public final class GangValueSubCommand extends GangSubCommand { + + public GangValueSubCommand(GangCommand command) { + super(command, "value"); + registerSubCommand(new GangValueAddSubCommand(command)); + registerSubCommand(new GangValueRemoveSubCommand(command)); + } + + @Override + public String getUsage() { + return ChatColor.RED + "/gang value "; + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() > 0) { + GangSubCommand subCommand = getSubCommand(args.get(0)); + if (subCommand != null) { + return subCommand.execute(sender, args.subList(1, args.size())); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(GangsConstants.GANGS_ADMIN_PERM); + } + + @Override + public List getTabComplete() { + return new ArrayList<>(this.subCommands.keySet()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/config/GangsConfig.java b/src/main/java/dev/drawethree/xprison/gangs/config/GangsConfig.java new file mode 100644 index 0000000..a2a200f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/config/GangsConfig.java @@ -0,0 +1,115 @@ +package dev.drawethree.xprison.gangs.config; + +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.EventPriority; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GangsConfig { + + private final XPrisonGangs plugin; + private final FileManager.Config config; + + private Map messages; + private Map placeholders; + @Getter + private String gangDisbandGUITitle; + @Getter + private List gangInfoFormat; + @Getter + private List gangTopFormat; + @Getter + private List gangAdminHelpMenu; + @Getter + private List gangHelpMenu; + @Getter + private List restrictedNames; + @Getter + private EventPriority gangChatPriority; + @Getter + private int maxGangMembers; + @Getter + private int gangUpdateDelay; + @Getter + private int maxGangNameLength; + @Getter + private boolean enableColorCodes; + @Getter + private boolean gangFriendlyFire; + @Getter + private String[] gangsCommandAliases; + + + public GangsConfig(XPrisonGangs plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("gangs.yml").copyDefaults(true).save(); + } + + private void loadMessages() { + messages = new HashMap<>(); + + YamlConfiguration configuration = getYamlConfig(); + + for (String key : configuration.getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(configuration.getString("messages." + key))); + } + } + + public String getMessage(String key) { + return messages.getOrDefault(key.toLowerCase(), "No message with key '" + key + "' found"); + } + + private void loadVariables() { + + this.loadMessages(); + this.loadPlaceholders(); + + this.gangInfoFormat = this.getYamlConfig().getStringList("gang-info-format"); + this.gangHelpMenu = this.getYamlConfig().getStringList("gang-help-menu"); + this.gangDisbandGUITitle = this.getYamlConfig().getString("gang-disband-gui-title"); + this.gangAdminHelpMenu = this.getYamlConfig().getStringList("gang-admin-help-menu"); + this.gangTopFormat = this.getYamlConfig().getStringList("gang-top-format"); + this.gangUpdateDelay = this.getYamlConfig().getInt("gang-top-update", 1); + this.maxGangMembers = this.getYamlConfig().getInt("max-gang-members", 10); + this.maxGangNameLength = this.getYamlConfig().getInt("max-gang-name-length", 10); + this.enableColorCodes = this.getYamlConfig().getBoolean("color-codes-in-gang-name"); + this.gangFriendlyFire = this.getYamlConfig().getBoolean("gang-friendly-fire"); + this.gangChatPriority = EventPriority.valueOf(this.getYamlConfig().getString("gang-chat-priority")); + this.gangsCommandAliases = this.getYamlConfig().getStringList("gangs-command-aliases").toArray(new String[0]); + this.restrictedNames = this.getYamlConfig().getStringList("restricted-names"); + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public void load() { + this.getConfig().reload(); + this.loadVariables(); + } + + private void loadPlaceholders() { + this.placeholders = new HashMap<>(); + for (String key : this.config.get().getConfigurationSection("placeholders").getKeys(false)) { + this.placeholders.put(key.toLowerCase(), TextUtils.applyColor(this.config.get().getString("placeholders." + key))); + } + } + + public String getPlaceholder(String name) { + return this.placeholders.get(name.toLowerCase()); + } + + public void reload() { + this.load(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/enums/GangCreateResult.java b/src/main/java/dev/drawethree/xprison/gangs/enums/GangCreateResult.java new file mode 100644 index 0000000..c408f73 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/enums/GangCreateResult.java @@ -0,0 +1,14 @@ +package dev.drawethree.xprison.gangs.enums; + +public enum GangCreateResult { + SUCCESS, + NAME_RESTRICTED, + NAME_VALID, + NAME_TOO_LONG, + NAME_CONTAINS_COLORS, + NAME_TAKEN, + NAME_EMPTY, + PLAYER_HAS_GANG, + EVENT_CANCELLED, + +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/enums/GangLeaveReason.java b/src/main/java/dev/drawethree/xprison/gangs/enums/GangLeaveReason.java new file mode 100644 index 0000000..7cbfe26 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/enums/GangLeaveReason.java @@ -0,0 +1,7 @@ +package dev.drawethree.xprison.gangs.enums; + +public enum GangLeaveReason { + ADMIN, + KICK, + LEAVE +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/enums/GangNameCheckResult.java b/src/main/java/dev/drawethree/xprison/gangs/enums/GangNameCheckResult.java new file mode 100644 index 0000000..1bd431c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/enums/GangNameCheckResult.java @@ -0,0 +1,10 @@ +package dev.drawethree.xprison.gangs.enums; + +public enum GangNameCheckResult { + SUCCESS, + NAME_RESTRICTED, + NAME_TOO_LONG, + NAME_CONTAINS_COLORS, + NAME_TAKEN, + NAME_EMPTY +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/enums/GangRenameResult.java b/src/main/java/dev/drawethree/xprison/gangs/enums/GangRenameResult.java new file mode 100644 index 0000000..b8cef0e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/enums/GangRenameResult.java @@ -0,0 +1,9 @@ +package dev.drawethree.xprison.gangs.enums; + +public enum GangRenameResult { + SUCCESS, + NAME_TOO_LONG, + NAME_CONTAINS_COLORS, + NAME_TAKEN, + NAME_EMPTY +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/gui/admin/DisbandGangAdminGUI.java b/src/main/java/dev/drawethree/xprison/gangs/gui/admin/DisbandGangAdminGUI.java new file mode 100644 index 0000000..fdb02e9 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/gui/admin/DisbandGangAdminGUI.java @@ -0,0 +1,26 @@ +package dev.drawethree.xprison.gangs.gui.admin; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.gui.ConfirmationGui; +import org.bukkit.entity.Player; + +public final class DisbandGangAdminGUI extends ConfirmationGui { + + private final XPrisonGangs plugin; + private final Gang gang; + + public DisbandGangAdminGUI(XPrisonGangs plugin, Player player, Gang gang) { + super(player, "Disband " + gang.getName() + " gang ?"); + this.plugin = plugin; + this.gang = gang; + } + + @Override + public void confirm(boolean confirm) { + if (confirm) { + this.plugin.getGangsManager().disbandGang(this.getPlayer(), this.gang, true); + } + this.close(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/gui/panel/DisbandGangGUI.java b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/DisbandGangGUI.java new file mode 100644 index 0000000..04e70b7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/DisbandGangGUI.java @@ -0,0 +1,26 @@ +package dev.drawethree.xprison.gangs.gui.panel; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.gui.ConfirmationGui; +import org.bukkit.entity.Player; + +public final class DisbandGangGUI extends ConfirmationGui { + + private final XPrisonGangs plugin; + private final Gang gang; + + public DisbandGangGUI(XPrisonGangs plugin, Player player, Gang gang) { + super(player, plugin.getConfig().getGangDisbandGUITitle()); + this.plugin = plugin; + this.gang = gang; + } + + @Override + public void confirm(boolean confirm) { + if (confirm) { + this.plugin.getGangsManager().disbandGang(getPlayer(), this.gang, false); + } + this.close(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/gui/panel/GangPanelGUI.java b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/GangPanelGUI.java new file mode 100644 index 0000000..4a6222e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/GangPanelGUI.java @@ -0,0 +1,131 @@ +package dev.drawethree.xprison.gangs.gui.panel; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.Services; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import me.lucko.helper.signprompt.SignPromptFactory; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public final class GangPanelGUI extends Gui { + + private static final MenuScheme LAYOUT = new MenuScheme() + .mask("111111111") + .mask("100000001") + .mask("111111111"); + + private static final MenuScheme BUTTONS = new MenuScheme() + .mask("000000000") + .mask("011111110") + .mask("000000000"); + + + private final XPrisonGangs plugin; + private final Gang gang; + + public GangPanelGUI(XPrisonGangs plugin, Gang gang, Player player) { + super(player, 3, "Gang Panel"); + this.plugin = plugin; + this.gang = gang; + } + + @Override + public void redraw() { + if (isFirstDraw()) { + populateLayout(); + populateButtons(); + } + } + + private void populateLayout() { + MenuPopulator populator = LAYOUT.newPopulator(this); + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.BLACK_STAINED_GLASS_PANE.toItem()).name("&a").buildItem().build()); + } + } + + private void populateButtons() { + MenuPopulator populator = BUTTONS.newPopulator(this); + + populator.acceptIfSpace(createGangInfoItem()); + + if (gang.canRenameGang(getPlayer())) { + populator.acceptIfSpace(createGangRenameItem()); + } + if (gang.canManageMembers(getPlayer())) { + populator.acceptIfSpace(createManageMembersItem()); + } + if (gang.canManageInvites(getPlayer())) { + populator.acceptIfSpace(createManageInvitesItem()); + } + if (gang.canDisband(getPlayer())) { + populator.acceptIfSpace(createDisbandGangItem()); + } + + } + + private Item createManageInvitesItem() { + return ItemStackBuilder.of(CompMaterial.PLAYER_HEAD.toItem()).name("&eManage Invites").lore("&7Click to manage pending invites.").build(this::openManageInvitesGui); + } + + private Item createDisbandGangItem() { + return ItemStackBuilder.of(CompMaterial.BARRIER.toItem()).name("&cDisband Gang").lore("&7Click to disband your gang.").build(this::openDisbandGangGui); + } + + private void openDisbandGangGui() { + close(); + new DisbandGangGUI(this.plugin, this.getPlayer(), this.gang).open(); + } + + private Item createManageMembersItem() { + return ItemStackBuilder.of(CompMaterial.PLAYER_HEAD.toItem()).name("&eManage Members").lore("&7Click to manage your gang members.").build(this::openManageMembersGui); + } + + private void openManageMembersGui() { + close(); + new ManageGangMembersGui(this.plugin, this.gang, this.getPlayer()).open(); + } + + private void openManageInvitesGui() { + close(); + new ManageGangInvitesGui(this.plugin, this.gang, this.getPlayer()).open(); + } + + private Item createGangRenameItem() { + return ItemStackBuilder.of(CompMaterial.OAK_SIGN.toItem()).name("&eRename Gang").lore("&7Click to rename your gang.").build(() -> { + SignPromptFactory factory = Services.load(SignPromptFactory.class); + factory.openPrompt(this.getPlayer(), Arrays.asList("", "§e^ ^ ^", "§7Enter gang name", ""), responseHandler -> { + if (responseHandler.get(0).isEmpty()) { + return SignPromptFactory.Response.ACCEPTED; + } + this.plugin.getGangsManager().renameGang(this.gang, responseHandler.get(0), this.getPlayer()); + return SignPromptFactory.Response.ACCEPTED; + }); + }); + } + + private Item createGangInfoItem() { + int gangTopPosition = getGangTopPosition(); + + return ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name("&eGang Info").lore( + " ", + String.format("&8» &e%s &7Gang", this.gang.getName()), + String.format("&8» &7Owner: &e%s", this.gang.getOwnerOffline().getName()), + String.format("&8» &7Members: &e%,d", this.gang.getMembersOffline().size()), + String.format("&8» &7Value: &e%,d", this.gang.getValue()), + String.format("&8» &7Top Placement: &e%s", gangTopPosition == -1 ? "Please Wait" : String.format("#%,d", gangTopPosition)), + " " + ).buildItem().build(); + } + + private int getGangTopPosition() { + return this.plugin.getGangsManager().getGangTopPosition(this.gang); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/gui/panel/ManageGangInvitesGui.java b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/ManageGangInvitesGui.java new file mode 100644 index 0000000..884f8c3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/ManageGangInvitesGui.java @@ -0,0 +1,76 @@ +package dev.drawethree.xprison.gangs.gui.panel; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.model.GangInvitation; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +import java.text.SimpleDateFormat; + +public final class ManageGangInvitesGui extends Gui { + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"); + + private static final MenuScheme LAYOUT = new MenuScheme() + .mask("111111111") + .mask("100000001") + .mask("100000001") + .mask("100000001") + .mask("100000001") + .mask("111111111"); + + private final XPrisonGangs plugin; + private final Gang gang; + + public ManageGangInvitesGui(XPrisonGangs plugin, Gang gang, Player player) { + super(player, 6, "Pending Invites"); + this.plugin = plugin; + this.gang = gang; + } + + @Override + public void redraw() { + clearItems(); + populateLayout(); + populateButtons(); + } + + private void populateLayout() { + MenuPopulator populator = LAYOUT.newPopulator(this); + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.BLACK_STAINED_GLASS_PANE.toItem()).name("&a").buildItem().build()); + } + } + + private void populateButtons() { + this.gang.getPendingInvites().forEach(gangInvitation -> this.addItem(createInviteItem(gangInvitation))); + } + + private Item createInviteItem(GangInvitation invitation) { + + String statusColor = invitation.getInvitedPlayer().isOnline() ? "&a" : "&c"; + String status = invitation.getInvitedPlayer().isOnline() ? "Online" : "Offline"; + + return ItemStackBuilder.of(CompMaterial.PLAYER_HEAD.toItem()) + .name(statusColor + invitation.getInvitedPlayer().getName()) + .lore( + " ", + "&8» &7Online Status: " + statusColor + status, + "&8» &7Invited By: &e" + invitation.getInvitedBy().getName(), + "&8» &7Invited At: &e" + DATE_FORMAT.format(invitation.getInviteDate()), + " ", + "&7Right-click to &cCancel" + ) + .build(ClickType.RIGHT, () -> { + this.gang.removeInvitation(invitation); + redraw(); + }); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/gui/panel/ManageGangMembersGui.java b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/ManageGangMembersGui.java new file mode 100644 index 0000000..030fc81 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/gui/panel/ManageGangMembersGui.java @@ -0,0 +1,73 @@ +package dev.drawethree.xprison.gangs.gui.panel; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +public final class ManageGangMembersGui extends Gui { + + + private static final MenuScheme LAYOUT = new MenuScheme() + .mask("111111111") + .mask("100000001") + .mask("100000001") + .mask("100000001") + .mask("100000001") + .mask("111111111"); + + private final XPrisonGangs plugin; + private final Gang gang; + + public ManageGangMembersGui(XPrisonGangs plugin, Gang gang, Player player) { + super(player, 6, "Gang Members"); + this.plugin = plugin; + this.gang = gang; + } + + @Override + public void redraw() { + clearItems(); + populateLayout(); + populateButtons(); + } + + private void populateLayout() { + MenuPopulator populator = LAYOUT.newPopulator(this); + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.BLACK_STAINED_GLASS_PANE.toItem()).name("&a").buildItem().build()); + } + } + + private void populateButtons() { + this.gang.getMembersOffline().forEach(player -> this.addItem(createGangMemberItem(player))); + } + + private Item createGangMemberItem(OfflinePlayer player) { + + String statusColor = player.isOnline() ? "&a" : "&c"; + String status = player.isOnline() ? "Online" : "Offline"; + + return ItemStackBuilder.of(CompMaterial.PLAYER_HEAD.toItem()) + .name(statusColor + player.getName()) + .lore( + " ", + "&8» &7Online Status: " + statusColor + status, + "&8» &7Role: &e" + (gang.isOwner(player) ? "Owner" : "Member"), + " ", + "&7Right-Click to &cKICK" + ) + .build(ClickType.RIGHT, () -> { + this.plugin.getGangsManager().kickPlayerFromGang(this.gang, player); + redraw(); + }); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/listener/GangsListener.java b/src/main/java/dev/drawethree/xprison/gangs/listener/GangsListener.java new file mode 100644 index 0000000..5bab5d8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/listener/GangsListener.java @@ -0,0 +1,82 @@ +package dev.drawethree.xprison.gangs.listener; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.Optional; + +public class GangsListener { + + private final XPrisonGangs plugin; + + public GangsListener(XPrisonGangs plugin) { + this.plugin = plugin; + } + + public void register() { + this.subscribeToEntityDamageByEntityEvent(); + this.subscribeToAsyncPlayerChatEvent(); + } + + private void subscribeToEntityDamageByEntityEvent() { + Events.subscribe(EntityDamageByEntityEvent.class, EventPriority.HIGHEST) + .filter(e -> e.getDamager() instanceof Player && (e.getEntity() instanceof Player || e.getEntity() instanceof Projectile)) + .handler(e -> { + + if (this.plugin.getConfig().isGangFriendlyFire()) { + return; + } + + Player player = (Player) e.getEntity(); + Player damager = null; + if (e.getDamager() instanceof Player) { + damager = (Player) e.getDamager(); + } else if (e.getDamager() instanceof Projectile) { + Projectile projectile = (Projectile) e.getDamager(); + if (projectile.getShooter() instanceof Player) { + damager = (Player) projectile.getShooter(); + } + } + + if (damager == null) { + return; + } + + if (this.plugin.getGangsManager().arePlayersInSameGang(player, damager)) { + e.setCancelled(true); + } + + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToAsyncPlayerChatEvent() { + Events.subscribe(AsyncPlayerChatEvent.class, this.plugin.getConfig().getGangChatPriority()) + .filter(e -> this.plugin.getGangsManager().hasGangChatEnabled(e.getPlayer())) + .handler(e -> { + + Optional gangOptional = this.plugin.getGangsManager().getPlayerGang(e.getPlayer()); + + if (!gangOptional.isPresent()) { + this.plugin.getGangsManager().disableGangChat(e.getPlayer()); + return; + } + + e.setCancelled(true); + e.getRecipients().clear(); + + Gang gang = gangOptional.get(); + + for (Player p : gang.getOnlinePlayers()) { + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("gang-chat-format").replace("%player%", e.getPlayer().getName()).replace("%message%", e.getMessage()).replace("%gang%", gang.getName())); + } + }).bindWith(this.plugin.getCore()); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/managers/GangsManager.java b/src/main/java/dev/drawethree/xprison/gangs/managers/GangsManager.java new file mode 100644 index 0000000..a181f54 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/managers/GangsManager.java @@ -0,0 +1,534 @@ +package dev.drawethree.xprison.gangs.managers; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gangs.api.events.GangCreateEvent; +import dev.drawethree.xprison.gangs.api.events.GangDisbandEvent; +import dev.drawethree.xprison.gangs.enums.GangCreateResult; +import dev.drawethree.xprison.gangs.enums.GangLeaveReason; +import dev.drawethree.xprison.gangs.enums.GangNameCheckResult; +import dev.drawethree.xprison.gangs.enums.GangRenameResult; +import dev.drawethree.xprison.gangs.gui.admin.DisbandGangAdminGUI; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.model.GangInvitation; +import dev.drawethree.xprison.gangs.model.GangTopProvider; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.utils.Players; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class GangsManager { + + private final XPrisonGangs plugin; + private final Map gangs; + private final List gangChatEnabledPlayers; + private List topGangs; + + public GangsManager(XPrisonGangs plugin) { + this.plugin = plugin; + this.gangChatEnabledPlayers = new ArrayList<>(); + this.gangs = new ConcurrentHashMap<>(); + this.topGangs = new ArrayList<>(); + } + + public void enable() { + this.loadGangs(); + } + + public boolean arePlayersInSameGang(Player player1, Player player2) { + Optional player1Gang = this.getPlayerGang(player1); + Optional player2Gang = this.getPlayerGang(player2); + + if (!player1Gang.isPresent() || !player2Gang.isPresent()) { + return false; + } + + return player1Gang.get().equals(player2Gang.get()); + } + + private void loadGangs() { + this.gangs.clear(); + Schedulers.async().run(() -> { + for (Gang g : this.plugin.getGangsService().getAllGangs()) { + this.gangs.put(g.getUuid(), g); + } + }); + } + + private void saveDataOnDisable() { + for (Gang g : this.gangs.values()) { + this.plugin.getGangsService().updateGang(g); + } + this.plugin.getCore().getLogger().info("Saved all gangs."); + } + + public Optional getPlayerGang(OfflinePlayer p) { + return this.gangs.values().stream().filter(gang -> gang.containsPlayer(p)).findFirst(); + } + + public Optional getGangWithName(String name) { + return this.gangs.values().stream().filter(gang -> ChatColor.stripColor(TextUtils.applyColor(gang.getName())).equalsIgnoreCase(name)).findFirst(); + } + + public GangRenameResult renameGang(Gang gang, String newName, CommandSender whoRenamed) { + + GangNameCheckResult nameCheckResult = this.performNameCheck(newName, whoRenamed); + if (nameCheckResult != GangNameCheckResult.SUCCESS) { + return GangRenameResult.valueOf(nameCheckResult.name()); + } + + gang.setName(newName); + + PlayerUtils.sendMessage(whoRenamed, this.plugin.getConfig().getMessage("gang-rename").replace("%gang%", TextUtils.applyColor(gang.getName()))); + return GangRenameResult.SUCCESS; + } + + public GangCreateResult createGang(String name, Player creator) { + + if (this.getPlayerGang(creator).isPresent()) { + PlayerUtils.sendMessage(creator, this.plugin.getConfig().getMessage("gang-cant-create")); + return GangCreateResult.PLAYER_HAS_GANG; + } + + GangNameCheckResult nameCheckResult = this.performNameCheck(name, creator); + + if (nameCheckResult != GangNameCheckResult.SUCCESS) { + return GangCreateResult.valueOf(nameCheckResult.name()); + } + + Gang g = new Gang(name, creator.getUniqueId()); + + GangCreateEvent gangCreateEvent = new GangCreateEvent(creator, g); + + this.plugin.getCore().debug("Calling GangCreateEvent for gang " + g.getName() + ".", this.plugin); + + Events.call(gangCreateEvent); + + if (gangCreateEvent.isCancelled()) { + this.plugin.getCore().debug("GangCreateEvent for gang " + g.getName() + " was cancelled.", this.plugin); + return GangCreateResult.EVENT_CANCELLED; + } + + this.gangs.put(g.getUuid(), g); + + PlayerUtils.sendMessage(creator, this.plugin.getConfig().getMessage("gang-created").replace("%name%", TextUtils.applyColor(name))); + + this.plugin.getGangsService().createGang(g); + Players.all().forEach(player1 -> PlayerUtils.sendMessage(player1, this.plugin.getConfig().getMessage("gang-create-broadcast").replace("%gang%", TextUtils.applyColor(g.getName())).replace("%player%", creator.getName()))); + return GangCreateResult.SUCCESS; + } + + private GangNameCheckResult performNameCheck(String name, CommandSender sender) { + GangNameCheckResult nameCheck = checkGangName(name); + + if (nameCheck == GangNameCheckResult.NAME_TOO_LONG) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-name-long")); + } else if (nameCheck == GangNameCheckResult.NAME_CONTAINS_COLORS) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-name-colors")); + } else if (nameCheck == GangNameCheckResult.NAME_TAKEN) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-already-exists").replace("%name%", TextUtils.applyColor(name))); + } else if (nameCheck == GangNameCheckResult.NAME_EMPTY) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-invalid-name")); + } else if (nameCheck == GangNameCheckResult.NAME_RESTRICTED) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-name-restricted")); + } + + return nameCheck; + } + + private GangNameCheckResult checkGangName(String name) { + + if (name.isEmpty()) { + return GangNameCheckResult.NAME_EMPTY; + } + + for (String s : this.plugin.getConfig().getRestrictedNames()) { + if (name.contains(s)) { + return GangNameCheckResult.NAME_RESTRICTED; + } + } + + if (this.plugin.getConfig().isEnableColorCodes()) { + if (ChatColor.stripColor(TextUtils.applyColor(name)).length() > this.plugin.getConfig().getMaxGangNameLength()) { + return GangNameCheckResult.NAME_TOO_LONG; + } + } else { + + if (!ChatColor.translateAlternateColorCodes('&', name).equals(name)) { + return GangNameCheckResult.NAME_CONTAINS_COLORS; + } + + if (name.length() > this.plugin.getConfig().getMaxGangNameLength()) { + return GangNameCheckResult.NAME_TOO_LONG; + } + + } + if (!this.getGangWithName(name).isPresent()) { + return GangNameCheckResult.SUCCESS; + } else { + return GangNameCheckResult.NAME_TAKEN; + } + } + + public boolean invitePlayer(Player invitedBy, Player invited) { + + if (invited == null || !invited.isOnline()) { + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("player-not-online")); + return false; + } + + Optional gangOptional = this.getPlayerGang(invitedBy); + + if (!gangOptional.isPresent()) { + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("not-in-gang")); + return false; + } + + Gang gang = gangOptional.get(); + + if (!gang.isOwner(invitedBy)) { + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("gang-not-owner")); + return false; + } + + if (gang.getMembersOffline().size() >= this.plugin.getConfig().getMaxGangMembers()) { + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("gang-full")); + return false; + } + + Optional gang1 = this.getPlayerGang(invited); + + if (gang1.isPresent()) { + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("gang-cant-invite")); + return false; + } + + if (gang.hasPendingInvite(invited)) { + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("gang-invite-pending")); + return false; + } + + GangInvitation invitation = gang.invitePlayer(invitedBy, invited); + + PlayerUtils.sendMessage(invitedBy, this.plugin.getConfig().getMessage("gang-invite-success").replace("%player%", invited.getName())); + PlayerUtils.sendMessage(invited, this.plugin.getConfig().getMessage("gang-invite-received").replace("%gang%", gang.getName())); + + Schedulers.sync().runLater(() -> gang.removeInvitation(invitation), 5, TimeUnit.MINUTES); + return true; + } + + public boolean leaveGang(Player player, GangLeaveReason reason) { + + Optional optGang = this.getPlayerGang(player); + + if (!optGang.isPresent()) { + PlayerUtils.sendMessage(player, this.plugin.getConfig().getMessage("not-in-gang")); + return false; + } + + Gang gang = optGang.get(); + + if (gang.isOwner(player)) { + PlayerUtils.sendMessage(player, this.plugin.getConfig().getMessage("gang-please-disband")); + return false; + } + + if (gang.leavePlayer(player, reason)) { + gang.getOnlinePlayers().forEach(player1 -> PlayerUtils.sendMessage(player1, this.plugin.getConfig().getMessage("gang-player-left").replace("%player%", player.getName()))); + PlayerUtils.sendMessage(player, this.plugin.getConfig().getMessage("gang-left").replace("%gang%", gang.getName())); + return true; + } + + return false; + } + + public boolean joinGang(OfflinePlayer player, Gang gang) { + + Optional optGang = this.getPlayerGang(player); + + if (optGang.isPresent()) { + if (player.isOnline()) { + PlayerUtils.sendMessage(player.getPlayer(), this.plugin.getConfig().getMessage("gang-cant-join")); + } + return false; + } + + if (gang.joinPlayer(player)) { + if (player.isOnline()) { + PlayerUtils.sendMessage(player.getPlayer(), this.plugin.getConfig().getMessage("gang-joined").replace("%gang%", gang.getName())); + } + gang.getOnlinePlayers().stream().filter(player1 -> player1 != player).forEach(player1 -> PlayerUtils.sendMessage(player1, this.plugin.getConfig().getMessage("gang-player-joined").replace("%player%", player.getName()))); + return true; + } else { + return false; + } + } + + private List getGangInfoFormat(Gang g) { + List originalFormat = this.plugin.getConfig().getGangInfoFormat(); + List returnList = new ArrayList<>(); + + for (String s : originalFormat) { + returnList.add(s + .replace("%gang_top%", String.format("%,d", this.getGangTopPosition(g))) + .replace("%gang_value%", String.format("%,d", g.getValue())) + .replace("%gang%", TextUtils.applyColor(g.getName())) + .replace("%gang_owner%", g.getOwnerOffline().getName()) + .replace("%gang_members%", StringUtils.join(g.getMembersOffline().stream().map(OfflinePlayer::getName).toArray(), ", "))); + } + return returnList; + } + + public boolean sendGangInfo(Player p, OfflinePlayer target) { + Optional targetGang = this.getPlayerGang(target); + + if (!targetGang.isPresent()) { + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("gang-player-not-in-gang")); + return true; + } + + for (String s : this.getGangInfoFormat(targetGang.get())) { + PlayerUtils.sendMessage(p, s); + } + return true; + } + + public boolean sendGangInfo(Player p, String gangName) { + Optional targetGang = this.getGangWithName(gangName); + + if (!targetGang.isPresent()) { + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("gang-not-exists")); + return true; + } + + for (String s : this.getGangInfoFormat(targetGang.get())) { + PlayerUtils.sendMessage(p, s); + } + return true; + } + + public boolean hasGangChatEnabled(Player p) { + return this.gangChatEnabledPlayers.contains(p.getUniqueId()); + } + + public void disbandGang(Player player, Gang gang, boolean force) { + + if (!gang.isOwner(player) && !force) { + PlayerUtils.sendMessage(player, this.plugin.getConfig().getMessage("gang-not-owner")); + return; + } + + GangDisbandEvent gangDisbandEvent = new GangDisbandEvent(gang); + + this.plugin.getCore().debug("Calling GangDisbandEvent for gang " + gang.getName() + ".", this.plugin); + + Events.call(gangDisbandEvent); + + if (gangDisbandEvent.isCancelled()) { + this.plugin.getCore().debug("GangDisbandEvent for gang " + gang.getName() + " was cancelled.", this.plugin); + return; + } + + gang.disband(); + + this.gangs.remove(gang.getUuid()); + this.plugin.getGangsService().deleteGang(gang); + + Players.all().forEach(player1 -> PlayerUtils.sendMessage(player1, this.plugin.getConfig().getMessage("gang-disband-broadcast").replace("%gang%", gang.getName()).replace("%player%", player.getName()))); + } + + public boolean acceptInvite(Player player, Gang gang) { + + if (!gang.hasPendingInvite(player)) { + PlayerUtils.sendMessage(player, this.plugin.getConfig().getMessage("gang-no-invite-pending")); + return false; + } + + return joinGang(player, gang); + } + + public void sendHelpMenu(CommandSender sender) { + List gangHelpMenu = this.plugin.getConfig().getGangHelpMenu(); + gangHelpMenu.forEach(s -> PlayerUtils.sendMessage(sender, s)); + } + + public void sendAdminHelpMenu(CommandSender sender) { + List gangAdminHelpMenu = this.plugin.getConfig().getGangHelpMenu(); + gangAdminHelpMenu.forEach(s -> PlayerUtils.sendMessage(sender, s)); + } + + public int getGangTopPosition(Gang gang) { + if (!this.topGangs.contains(gang)) { + return -1; + } + return this.topGangs.indexOf(gang) + 1; + } + + public boolean removeFromGang(Player p, Gang gang, OfflinePlayer target) { + + if (!gang.isOwner(p)) { + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("gang-not-owner")); + return false; + } + + if (target == null) { + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("player-not-online")); + return false; + } + + this.kickPlayerFromGang(gang, target); + return true; + } + + public void kickPlayerFromGang(Gang gang, OfflinePlayer target) { + if (gang.kickPlayer(target)) { + gang.getOnlinePlayers().forEach(player -> PlayerUtils.sendMessage(player, this.plugin.getConfig().getMessage("gang-player-kicked").replace("%player%", target.getName()))); + if (target.isOnline()) { + PlayerUtils.sendMessage(target.getPlayer(), this.plugin.getConfig().getMessage("gang-kicked").replace("%gang%", gang.getName())); + } + } + } + + public boolean sendGangTop(CommandSender sender) { + List gangTopFormat = this.plugin.getConfig().getGangTopFormat(); + for (String s : gangTopFormat) { + if (s.startsWith("{FOR_EACH_GANG}")) { + String rawContent = s.replace("{FOR_EACH_GANG} ", ""); + for (int i = 0; i < 10; i++) { + try { + Gang gang = this.topGangs.get(i); + PlayerUtils.sendMessage(sender, rawContent.replace("%position%", String.valueOf(i + 1)).replace("%gang%", gang.getName()).replace("%value%", String.format("%,d", gang.getValue()))); + } catch (Exception e) { + break; + } + } + } else { + PlayerUtils.sendMessage(sender, s); + } + } + return true; + } + + public boolean forceAdd(CommandSender sender, Player target, Gang gang) { + + if (target == null) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("player-not-online")); + return false; + } + + Optional currentGang = this.getPlayerGang(target); + + if (currentGang.isPresent()) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-cant-invite")); + return false; + } + + return joinGang(target, gang); + } + + public boolean forceRemove(CommandSender sender, Player target) { + + if (target == null) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("player-not-online")); + return false; + } + + Optional currentGang = this.getPlayerGang(target); + + if (!currentGang.isPresent()) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-player-not-in-gang")); + return false; + } + + return leaveGang(target, GangLeaveReason.ADMIN); + } + + public boolean forceDisband(CommandSender sender, Gang gang) { + + if (sender instanceof Player) { + new DisbandGangAdminGUI(this.plugin, (Player) sender, gang).open(); + } else { + PlayerUtils.sendMessage(sender, "§cOnly for players."); + } + return true; + } + + public boolean forceRename(CommandSender sender, String oldName, String newName) { + + Optional targetGang = this.getGangWithName(oldName); + + if (!targetGang.isPresent()) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-not-exists")); + return true; + } + + Gang gang = targetGang.get(); + + gang.setName(newName); + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-force-rename").replace("%old_gang%", oldName).replace("%gang%", gang.getName())); + return true; + } + + public boolean toggleGangChat(Player p) { + + if (!getPlayerGang(p).isPresent()) { + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("not-in-gang")); + return false; + } + + if (this.gangChatEnabledPlayers.contains(p.getUniqueId())) { + this.gangChatEnabledPlayers.remove(p.getUniqueId()); + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("gang-chat-off")); + } else { + this.gangChatEnabledPlayers.add(p.getUniqueId()); + PlayerUtils.sendMessage(p, this.plugin.getConfig().getMessage("gang-chat-on")); + } + return true; + } + + public boolean modifyValue(CommandSender sender, Gang gang, long amount, String operation) { + + if (amount <= 0) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("invalid-value")); + return false; + } + + if (operation.equalsIgnoreCase("add")) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-value-add").replace("%value%", String.valueOf(amount)).replace("%gang%", gang.getName())); + gang.setValue(gang.getValue() + amount); + return true; + } else if (operation.equalsIgnoreCase("remove")) { + PlayerUtils.sendMessage(sender, this.plugin.getConfig().getMessage("gang-value-remove").replace("%value%", String.valueOf(amount)).replace("%gang%", gang.getName())); + gang.setValue(gang.getValue() - amount); + return true; + } else { + PlayerUtils.sendMessage(sender, "§cInvalid operation given."); + return false; + } + } + + public Collection getAllGangs() { + return this.gangs.values(); + } + + public void disable() { + this.saveDataOnDisable(); + } + + public void disableGangChat(Player player) { + this.gangChatEnabledPlayers.remove(player.getUniqueId()); + } + + public void updateGangTop(GangTopProvider provider) { + this.topGangs = provider.provide(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/model/Gang.java b/src/main/java/dev/drawethree/xprison/gangs/model/Gang.java new file mode 100644 index 0000000..3546cd5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/model/Gang.java @@ -0,0 +1,170 @@ +package dev.drawethree.xprison.gangs.model; + +import dev.drawethree.xprison.gangs.api.events.GangJoinEvent; +import dev.drawethree.xprison.gangs.api.events.GangLeaveEvent; +import dev.drawethree.xprison.gangs.enums.GangLeaveReason; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.lucko.helper.Events; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Gang { + + private UUID uuid; + private UUID gangOwner; + private List gangMembers; + private List pendingInvites; + private String name; + private long value; + + public Gang(String name, UUID gangOwner) { + this.uuid = UUID.randomUUID(); + this.name = name; + this.gangOwner = gangOwner; + this.gangMembers = new ArrayList<>(); + this.pendingInvites = new ArrayList<>(); + } + + public boolean containsPlayer(OfflinePlayer p) { + return this.gangOwner.equals(p.getUniqueId()) || this.gangMembers.contains(p.getUniqueId()); + } + + public boolean isOwner(OfflinePlayer p) { + return this.gangOwner.equals(p.getUniqueId()); + } + + public boolean leavePlayer(OfflinePlayer p, GangLeaveReason reason) { + + if (!this.gangMembers.contains(p.getUniqueId())) { + return false; + } + + if (this.callGangLeaveEvent(p, reason)) { + return false; + } + + this.gangMembers.remove(p.getUniqueId()); + return true; + } + + public GangInvitation invitePlayer(Player invitedBy, Player player) { + GangInvitation invitation = getGangInvite(player); + + if (invitation != null) { + return invitation; + } + + invitation = new GangInvitation(this, player, invitedBy); + this.pendingInvites.add(invitation); + return invitation; + } + + private GangInvitation getGangInvite(OfflinePlayer player) { + for (GangInvitation gangInvitation : this.pendingInvites) { + if (gangInvitation.getInvitedPlayer().getUniqueId().equals(player.getUniqueId())) { + return gangInvitation; + } + } + return null; + } + + public boolean hasPendingInvite(Player player) { + return getGangInvite(player) != null; + } + + private boolean callGangLeaveEvent(OfflinePlayer p, GangLeaveReason reason) { + GangLeaveEvent event = new GangLeaveEvent(p, this, reason); + + Events.call(event); + + return event.isCancelled(); + } + + public boolean joinPlayer(OfflinePlayer p) { + + if (this.gangMembers.contains(p.getUniqueId())) { + return false; + } + + if (this.callGangJoinEvent(p)) { + return false; + } + + GangInvitation invitation = getGangInvite(p); + this.removeInvitation(invitation); + + this.gangMembers.add(p.getUniqueId()); + return true; + } + + private boolean callGangJoinEvent(OfflinePlayer p) { + GangJoinEvent event = new GangJoinEvent(p, this); + + Events.call(event); + + return event.isCancelled(); + } + + + public List getOnlinePlayers() { + return Players.all().stream().filter(this::containsPlayer).collect(Collectors.toList()); + } + + public List getMembersOffline() { + List returnList = new ArrayList<>(); + for (UUID uuid : this.gangMembers) { + returnList.add(Players.getOfflineNullable(uuid)); + } + return returnList; + } + + public OfflinePlayer getOwnerOffline() { + return Players.getOfflineNullable(this.gangOwner); + } + + public void disband() { + this.gangMembers.clear(); + this.gangOwner = null; + } + + public boolean kickPlayer(OfflinePlayer target) { + leavePlayer(target, GangLeaveReason.KICK); + return true; + } + + public List getPendingInvites() { + return pendingInvites; + } + + public void removeInvitation(GangInvitation invitation) { + this.pendingInvites.remove(invitation); + } + + public boolean canRenameGang(Player player) { + return isOwner(player); + } + + public boolean canManageMembers(Player player) { + return isOwner(player); + } + + public boolean canDisband(Player player) { + return isOwner(player); + } + + public boolean canManageInvites(Player player) { + return isOwner(player); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/model/GangInvitation.java b/src/main/java/dev/drawethree/xprison/gangs/model/GangInvitation.java new file mode 100644 index 0000000..ed53823 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/model/GangInvitation.java @@ -0,0 +1,34 @@ +package dev.drawethree.xprison.gangs.model; + +import lombok.Data; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.Date; +import java.util.UUID; + +@Data +public class GangInvitation { + + private UUID uuid; + private Gang gang; + private OfflinePlayer invitedPlayer; + private OfflinePlayer invitedBy; + private Date inviteDate; + + public GangInvitation(Gang gang, OfflinePlayer invitedPlayer, Player invitedBy) { + this.uuid = UUID.randomUUID(); + this.gang = gang; + this.invitedPlayer = invitedPlayer; + this.invitedBy = invitedBy; + this.inviteDate = new Date(); + } + + public GangInvitation(UUID uuid, Gang gang, OfflinePlayer invitedPlayer, OfflinePlayer invitedBy, Date inviteDate) { + this.uuid = uuid; + this.gang = gang; + this.invitedPlayer = invitedPlayer; + this.invitedBy = invitedBy; + this.inviteDate = inviteDate; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/model/GangTopByValueProvider.java b/src/main/java/dev/drawethree/xprison/gangs/model/GangTopByValueProvider.java new file mode 100644 index 0000000..51e61f6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/model/GangTopByValueProvider.java @@ -0,0 +1,26 @@ +package dev.drawethree.xprison.gangs.model; + +import dev.drawethree.xprison.gangs.managers.GangsManager; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +public final class GangTopByValueProvider implements GangTopProvider { + + private final GangsManager manager; + + public GangTopByValueProvider(GangsManager manager) { + this.manager = manager; + } + + @Override + public List provide() { + return getAllGangs().stream().sorted(Comparator.comparingLong(Gang::getValue).reversed()).collect(Collectors.toList()); + } + + private Collection getAllGangs() { + return manager.getAllGangs(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/model/GangTopProvider.java b/src/main/java/dev/drawethree/xprison/gangs/model/GangTopProvider.java new file mode 100644 index 0000000..4bf4abc --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/model/GangTopProvider.java @@ -0,0 +1,7 @@ +package dev.drawethree.xprison.gangs.model; + +import java.util.List; + +public interface GangTopProvider { + List provide(); +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/model/GangUpdateTopTask.java b/src/main/java/dev/drawethree/xprison/gangs/model/GangUpdateTopTask.java new file mode 100644 index 0000000..07c8452 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/model/GangUpdateTopTask.java @@ -0,0 +1,37 @@ +package dev.drawethree.xprison.gangs.model; + +import dev.drawethree.xprison.gangs.XPrisonGangs; +import me.lucko.helper.Schedulers; +import me.lucko.helper.scheduler.Task; + +import java.util.concurrent.TimeUnit; + +public final class GangUpdateTopTask implements Runnable { + + private final XPrisonGangs plugin; + private final GangTopProvider gangTopProvider; + + private Task task; + + public GangUpdateTopTask(XPrisonGangs plugin, GangTopProvider gangTopProvider) { + this.plugin = plugin; + this.gangTopProvider = gangTopProvider; + } + + @Override + public void run() { + this.plugin.getGangsManager().updateGangTop(gangTopProvider); + } + + public void start() { + this.stop(); + int delay = this.plugin.getConfig().getGangUpdateDelay(); + this.task = Schedulers.async().runRepeating(this, delay, TimeUnit.MINUTES, delay, TimeUnit.MINUTES); + } + + public void stop() { + if (this.task != null) { + this.task.stop(); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/repo/GangsRepository.java b/src/main/java/dev/drawethree/xprison/gangs/repo/GangsRepository.java new file mode 100644 index 0000000..5e55b9c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/repo/GangsRepository.java @@ -0,0 +1,27 @@ +package dev.drawethree.xprison.gangs.repo; + +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.model.GangInvitation; + +import java.util.List; + +public interface GangsRepository { + + void updateGang(Gang g); + + void deleteGang(Gang g); + + void createGang(Gang g); + + List getAllGangs(); + + List getGangInvitations(Gang gang); + + void createGangInvitation(GangInvitation gangInvitation); + + void deleteGangInvitation(GangInvitation gangInvitation); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/repo/impl/GangsRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/gangs/repo/impl/GangsRepositoryImpl.java new file mode 100644 index 0000000..d9709b7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/repo/impl/GangsRepositoryImpl.java @@ -0,0 +1,178 @@ +package dev.drawethree.xprison.gangs.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.model.GangInvitation; +import dev.drawethree.xprison.gangs.repo.GangsRepository; +import me.lucko.helper.utils.Log; +import me.lucko.helper.utils.Players; +import org.apache.commons.lang.StringUtils; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +public class GangsRepositoryImpl implements GangsRepository { + + private static final String TABLE_NAME = "UltraPrison_Gangs"; + private static final String INVITES_TABLE_NAME = "UltraPrison_Gang_Invites"; + private static final String GANGS_UUID_COLNAME = "UUID"; + private static final String GANGS_NAME_COLNAME = "name"; + private static final String GANGS_OWNER_COLNAME = "owner"; + private static final String GANGS_MEMBERS_COLNAME = "members"; + private static final String GANGS_VALUE_COLNAME = "value"; + + private static final String GANG_INVITATION_UUID = "uuid"; + private static final String GANG_INVITATION_GANG_ID = "gang_id"; + private static final String GANG_INVITATION_INVITED_BY = "invited_by"; + private static final String GANG_INVITATION_INVITED_PLAYER = "invited_player"; + private static final String GANG_INVITATION_INVITE_DATE = "invite_date"; + + private final SQLDatabase database; + + public GangsRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public List getAllGangs() { + List returnList = new ArrayList<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con,"SELECT * FROM " + TABLE_NAME); ResultSet set = statement.executeQuery()) { + while (set.next()) { + Gang gang = new Gang(); + + UUID gangUUID = UUID.fromString(set.getString(GANGS_UUID_COLNAME)); + + if (gangUUID == null) { + gangUUID = UUID.randomUUID(); + } + + gang.setUuid(gangUUID); + + String gangName = set.getString(GANGS_NAME_COLNAME); + gang.setName(gangName); + + UUID owner = UUID.fromString(set.getString(GANGS_OWNER_COLNAME)); + gang.setGangOwner(owner); + + List members = new ArrayList<>(); + + for (String s : set.getString(GANGS_MEMBERS_COLNAME).split(",")) { + if (s.isEmpty()) { + continue; + } + try { + UUID uuid = UUID.fromString(s); + members.add(uuid); + } catch (Exception e) { + Log.warn("Unable to fetch UUID: " + s); + e.printStackTrace(); + } + } + gang.setGangMembers(members); + + long value = set.getLong(GANGS_VALUE_COLNAME); + gang.setValue(value); + List gangInvitations = getGangInvitations(gang); + gang.setPendingInvites(gangInvitations); + + returnList.add(gang); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return returnList; + } + + + @Override + public void createGang(Gang g) { + String sql = database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME + "(UUID,name,owner,members) VALUES(?,?,?,?)" : "INSERT IGNORE INTO " + TABLE_NAME + "(UUID,name,owner,members) VALUES(?,?,?,?)"; + this.database.executeSqlAsync(sql, g.getUuid().toString(), g.getName(), g.getGangOwner().toString(), ""); + } + + @Override + public void createGangInvitation(GangInvitation gangInvitation) { + this.database.executeSql("INSERT IGNORE INTO " + INVITES_TABLE_NAME + "(uuid,gang_id,invited_by,invited_player,invite_date) VALUES(?,?,?,?,?)", + gangInvitation.getUuid().toString(), + gangInvitation.getGang().getUuid().toString(), + gangInvitation.getInvitedBy().getUniqueId().toString(), + gangInvitation.getInvitedPlayer().getUniqueId().toString(), + gangInvitation.getInviteDate()); + } + + @Override + public void deleteGang(Gang g) { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME + " WHERE ?=?", GANGS_UUID_COLNAME, g.getUuid().toString()); + for (GangInvitation gangInvitation : g.getPendingInvites()) { + this.deleteGangInvitation(gangInvitation); + } + } + + + @Override + public List getGangInvitations(Gang gang) { + List returnList = new ArrayList<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con,"SELECT * FROM " + INVITES_TABLE_NAME + " WHERE ?=?")) { + statement.setString(1, GANG_INVITATION_GANG_ID); + statement.setString(2, gang.getUuid().toString()); + try (ResultSet set = statement.executeQuery()) { + while (set.next()) { + UUID uuid = UUID.fromString(set.getString(GANG_INVITATION_UUID)); + OfflinePlayer invitedPlayer = Players.getOfflineNullable(UUID.fromString(set.getString(GANG_INVITATION_INVITED_PLAYER))); + OfflinePlayer invitedBy = Players.getOfflineNullable(UUID.fromString(set.getString(GANG_INVITATION_INVITED_BY))); + Date inviteDate = set.getDate(GANG_INVITATION_INVITE_DATE); + GangInvitation invitation = new GangInvitation(uuid, gang, invitedPlayer, invitedBy, inviteDate); + returnList.add(invitation); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return returnList; + } + + @Override + public void deleteGangInvitation(GangInvitation gangInvitation) { + this.database.executeSqlAsync("DELETE FROM " + INVITES_TABLE_NAME + " WHERE ?=?", GANG_INVITATION_UUID, gangInvitation.getUuid().toString()); + } + + @Override + public void updateGang(Gang g) { + this.database.executeSql("UPDATE " + + TABLE_NAME + " SET " + + GANGS_MEMBERS_COLNAME + "=?," + + GANGS_NAME_COLNAME + "=?," + + GANGS_VALUE_COLNAME + "=? WHERE " + + GANGS_UUID_COLNAME + "=?", + StringUtils.join(g.getMembersOffline().stream().map(OfflinePlayer::getUniqueId).map(UUID::toString).toArray(), ","), + g.getName(), + g.getValue(), + g.getUuid().toString()); + + this.database.executeSql("DELETE FROM " + INVITES_TABLE_NAME + " WHERE ?=?", GANG_INVITATION_GANG_ID, g.getUuid().toString()); + + for (GangInvitation gangInvitation : g.getPendingInvites()) { + createGangInvitation(gangInvitation); + } + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(UUID varchar(36) NOT NULL UNIQUE, name varchar(36) NOT NULL UNIQUE, owner varchar(36) NOT NULL, value bigint default 0, members text, primary key (UUID,name))"); + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + INVITES_TABLE_NAME + "(uuid varchar(36) NOT NULL, gang_id varchar(36) NOT NULL, invited_by varchar(36), invited_player varchar(36) not null, invite_date datetime not null, primary key(uuid))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME); + this.database.executeSqlAsync("DELETE FROM " + INVITES_TABLE_NAME); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/service/GangsService.java b/src/main/java/dev/drawethree/xprison/gangs/service/GangsService.java new file mode 100644 index 0000000..16d24f9 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/service/GangsService.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.gangs.service; + +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.model.GangInvitation; + +import java.util.List; + +public interface GangsService { + + void updateGang(Gang g); + + void deleteGang(Gang g); + + void createGang(Gang g); + + List getAllGangs(); + + List getGangInvitations(Gang gang); + + void createGangInvitation(GangInvitation gangInvitation); + + void deleteGangInvitation(GangInvitation gangInvitation); +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/service/impl/GangsServiceImpl.java b/src/main/java/dev/drawethree/xprison/gangs/service/impl/GangsServiceImpl.java new file mode 100644 index 0000000..66bcedd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/service/impl/GangsServiceImpl.java @@ -0,0 +1,52 @@ +package dev.drawethree.xprison.gangs.service.impl; + +import dev.drawethree.xprison.gangs.model.Gang; +import dev.drawethree.xprison.gangs.model.GangInvitation; +import dev.drawethree.xprison.gangs.repo.GangsRepository; +import dev.drawethree.xprison.gangs.service.GangsService; + +import java.util.List; + +public class GangsServiceImpl implements GangsService { + + private final GangsRepository repository; + + public GangsServiceImpl(GangsRepository repository) { + this.repository = repository; + } + + @Override + public void updateGang(Gang g) { + repository.updateGang(g); + } + + @Override + public void deleteGang(Gang g) { + repository.deleteGang(g); + } + + @Override + public void createGang(Gang g) { + repository.createGang(g); + } + + @Override + public List getAllGangs() { + return repository.getAllGangs(); + } + + @Override + public List getGangInvitations(Gang gang) { + return repository.getGangInvitations(gang); + } + + @Override + public void createGangInvitation(GangInvitation gangInvitation) { + repository.createGangInvitation(gangInvitation); + } + + @Override + public void deleteGangInvitation(GangInvitation gangInvitation) { + repository.deleteGangInvitation(gangInvitation); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gangs/utils/GangsConstants.java b/src/main/java/dev/drawethree/xprison/gangs/utils/GangsConstants.java new file mode 100644 index 0000000..e59d70f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gangs/utils/GangsConstants.java @@ -0,0 +1,11 @@ +package dev.drawethree.xprison.gangs.utils; + +public class GangsConstants { + + public static final String GANGS_ADMIN_PERM = "xprison.gangs.admin"; + public static final String GANGS_CREATE_PERM = "xprison.gangs.create"; + + private GangsConstants() { + throw new UnsupportedOperationException("Cannot instantiate."); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/XPrisonGems.java b/src/main/java/dev/drawethree/xprison/gems/XPrisonGems.java new file mode 100644 index 0000000..98f7e20 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/XPrisonGems.java @@ -0,0 +1,154 @@ +package dev.drawethree.xprison.gems; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.gems.api.XPrisonGemsAPI; +import dev.drawethree.xprison.gems.api.XPrisonGemsAPIImpl; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.gems.managers.GemsManager; +import dev.drawethree.xprison.gems.repo.GemsRepository; +import dev.drawethree.xprison.gems.repo.impl.GemsRepositoryImpl; +import dev.drawethree.xprison.gems.service.GemsService; +import dev.drawethree.xprison.gems.service.impl.GemsServiceImpl; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import me.lucko.helper.Events; +import me.lucko.helper.reflect.MinecraftVersion; +import org.bukkit.event.Event; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.HashMap; + +public final class XPrisonGems implements XPrisonModule { + + public static final String MODULE_NAME = "Gems"; + public static final String GEMS_ADMIN_PERM = "xprison.gems.admin"; + + @Getter + private static XPrisonGems instance; + + @Getter + private FileManager.Config config; + + @Getter + private XPrisonGemsAPI api; + + @Getter + private GemsManager gemsManager; + @Getter + private final XPrison core; + + @Getter + private GemsRepository gemsRepository; + + @Getter + private GemsService gemsService; + + private HashMap messages; + + private boolean enabled; + private CommandManager commandManager; + + @Getter + private long commandCooldown; + + public XPrisonGems(XPrison XPrison) { + instance = this; + this.core = XPrison; + } + + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.config.reload(); + + this.loadMessages(); + this.loadVariables(); + + this.gemsManager.reload(); + this.commandManager.reload(); + } + + @Override + public void enable() { + this.enabled = true; + this.config = this.core.getFileManager().getConfig("gems.yml").copyDefaults(true).save(); + + this.loadVariables(); + this.loadMessages(); + + this.gemsRepository = new GemsRepositoryImpl(this.core.getPluginDatabase()); + this.gemsRepository.createTables(); + this.gemsService = new GemsServiceImpl(this.gemsRepository); + this.gemsManager = new GemsManager(this); + this.commandManager = new CommandManager(this); + this.commandManager.enable(); + this.api = new XPrisonGemsAPIImpl(this.gemsManager); + + this.registerEvents(); + } + + private void loadVariables() { + this.commandCooldown = getConfig().get().getLong("gems-command-cooldown"); + } + + + @Override + public void disable() { + this.gemsManager.stopUpdating(); + this.gemsManager.savePlayerDataOnDisable(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.gemsRepository.clearTableData(); + } + + private void registerEvents() { + Events.subscribe(PlayerInteractEvent.class, EventPriority.LOWEST) + .filter(e -> e.getItem() != null && e.getItem().getType() == this.gemsManager.getGemsItemMaterial() && (e.getAction() == Action.RIGHT_CLICK_BLOCK || e.getAction() == Action.RIGHT_CLICK_AIR)) + .handler(e -> { + if (e.getItem().hasItemMeta()) { + e.setCancelled(true); + e.setUseInteractedBlock(Event.Result.DENY); + boolean offHandClick = false; + if (MinecraftVersion.getRuntimeVersion().isAfter(MinecraftVersion.of(1, 8, 9))) { + offHandClick = e.getHand() == EquipmentSlot.OFF_HAND; + } + this.gemsManager.redeemGems(e.getPlayer(), e.getItem(), e.getPlayer().isSneaking(), offHandClick); + } + }).bindWith(core); + } + + + private void loadMessages() { + this.messages = new HashMap<>(); + for (String key : this.getConfig().get().getConfigurationSection("messages").getKeys(false)) { + this.messages.put(key, TextUtils.applyColor(this.getConfig().get().getString("messages." + key))); + } + } + + public String getMessage(String key) { + return this.messages.get(key); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/api/XPrisonGemsAPI.java b/src/main/java/dev/drawethree/xprison/gems/api/XPrisonGemsAPI.java new file mode 100644 index 0000000..932706d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/api/XPrisonGemsAPI.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.gems.api; + +import dev.drawethree.xprison.api.enums.ReceiveCause; +import org.bukkit.OfflinePlayer; + +public interface XPrisonGemsAPI { + + /** + * Method to get player gems + * + * @param p Player + * @return Player gems amount + */ + long getPlayerGems(OfflinePlayer p); + + /** + * Method to check if player has more or equal gems than specified amount + * + * @param p Player + * @param amount amount + * @return true if player has more or equal gems + */ + boolean hasEnough(OfflinePlayer p, long amount); + + /** + * Method to remove gems from player + * + * @param p Player + * @param amount amount + */ + void removeGems(OfflinePlayer p, long amount); + + /** + * Method to add gems to player + * + * @param p Player + * @param amount amount + * @param cause - Represents why player get these gemes + */ + void addGems(OfflinePlayer p, long amount, ReceiveCause cause); + + +} diff --git a/src/main/java/dev/drawethree/xprison/gems/api/XPrisonGemsAPIImpl.java b/src/main/java/dev/drawethree/xprison/gems/api/XPrisonGemsAPIImpl.java new file mode 100644 index 0000000..606d8fa --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/api/XPrisonGemsAPIImpl.java @@ -0,0 +1,36 @@ +package dev.drawethree.xprison.gems.api; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.gems.managers.GemsManager; +import org.bukkit.OfflinePlayer; + +public final class XPrisonGemsAPIImpl implements XPrisonGemsAPI { + + + private final GemsManager manager; + + public XPrisonGemsAPIImpl(GemsManager manager) { + this.manager = manager; + } + + @Override + public long getPlayerGems(OfflinePlayer p) { + return this.manager.getPlayerGems(p); + } + + @Override + public boolean hasEnough(OfflinePlayer p, long amount) { + return this.getPlayerGems(p) >= amount; + } + + @Override + public void removeGems(OfflinePlayer p, long amount) { + this.manager.removeGems(p, amount, null, LostCause.ADMIN); + } + + @Override + public void addGems(OfflinePlayer p, long amount, ReceiveCause cause) { + this.manager.giveGems(p, amount, null, cause); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/api/events/PlayerGemsLostEvent.java b/src/main/java/dev/drawethree/xprison/gems/api/events/PlayerGemsLostEvent.java new file mode 100644 index 0000000..6a49165 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/api/events/PlayerGemsLostEvent.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.gems.api.events; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class PlayerGemsLostEvent extends XPrisonPlayerEvent { + + + private static final HandlerList handlers = new HandlerList(); + + @Getter + private final LostCause cause; + + @Getter + @Setter + private long amount; + + /** + * Called when player loses tokens + * + * @param cause LostCause + * @param player Player + * @param amount Amount of tokens lost + */ + public PlayerGemsLostEvent(LostCause cause, OfflinePlayer player, long amount) { + super(player); + this.cause = cause; + this.amount = amount; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/gems/api/events/PlayerGemsReceiveEvent.java b/src/main/java/dev/drawethree/xprison/gems/api/events/PlayerGemsReceiveEvent.java new file mode 100644 index 0000000..35f252c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/api/events/PlayerGemsReceiveEvent.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.gems.api.events; + +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class PlayerGemsReceiveEvent extends XPrisonPlayerEvent implements Cancellable { + + + private static final HandlerList handlers = new HandlerList(); + + @Getter + private final ReceiveCause cause; + @Getter + @Setter + private long amount; + + @Getter + @Setter + private boolean cancelled; + + /** + * Called when player receive gems + * + * @param cause ReceiveCause + * @param player Player + * @param amount Amount of gems received + */ + public PlayerGemsReceiveEvent(ReceiveCause cause, OfflinePlayer player, long amount) { + super(player); + this.cause = cause; + this.amount = amount; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsCommand.java new file mode 100644 index 0000000..aa5ec73 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsCommand.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.interfaces.Permissionable; +import lombok.Getter; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public abstract class GemsCommand implements Permissionable { + + protected static final String PERMISSION_ROOT = "xprison.gems.command."; + + @Getter + private final String name; + protected final CommandManager commandManager; + @Getter + private final String[] aliases; + + GemsCommand(CommandManager commandManager, String name, String... aliases) { + this.commandManager = commandManager; + this.name = name; + this.aliases = aliases; + } + + public abstract boolean execute(CommandSender sender, ImmutableList args); + + public abstract boolean canExecute(CommandSender sender); + + public abstract String getUsage(); + + @Override + public String getRequiredPermission() { + return PERMISSION_ROOT + this.name; + } + + public abstract List getTabComplete(List args); +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsGiveCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsGiveCommand.java new file mode 100644 index 0000000..9d3d95a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsGiveCommand.java @@ -0,0 +1,61 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class GemsGiveCommand extends GemsCommand { + + private static final String COMMAND_NAME = "give"; + + public GemsGiveCommand(CommandManager manager) { + super(manager, COMMAND_NAME); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + + if (args.size() == 2) { + try { + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + long amount = Long.parseLong(args.get(1)); + this.commandManager.getPlugin().getGemsManager().giveGems(target, amount, sender, ReceiveCause.GIVE); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(1)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonGems.GEMS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/gems give [player] [gems] - Gives gems to player."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsHelpCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsHelpCommand.java new file mode 100644 index 0000000..a99ddf7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsHelpCommand.java @@ -0,0 +1,51 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public final class GemsHelpCommand extends GemsCommand { + + private static final String COMMAND_NAME = "help"; + private static final String[] COMMAND_ALIASES = {"?"}; + + public GemsHelpCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME, COMMAND_ALIASES); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.isEmpty()) { + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + PlayerUtils.sendMessage(sender, "&e&lGEMS HELP MENU "); + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + for (GemsCommand command : this.commandManager.getAll()) { + if (command.canExecute(sender)) { + PlayerUtils.sendMessage(sender, "&e" + command.getUsage()); + } + } + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + return true; + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public String getUsage() { + return "/gems help - Displays all available commands."; + } + + @Override + public List getTabComplete(List args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsPayCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsPayCommand.java new file mode 100644 index 0000000..3d73390 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsPayCommand.java @@ -0,0 +1,77 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class GemsPayCommand extends GemsCommand { + + private static final String COMMAND_NAME = "pay"; + private static final String[] COMMAND_ALIASES = {"send"}; + + public GemsPayCommand(CommandManager manager) { + super(manager, COMMAND_NAME, COMMAND_ALIASES); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.size() == 2 && sender instanceof Player) { + Player p = (Player) sender; + try { + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + + if (!target.isOnline()) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("player_not_online").replace("%player%", target.getName())); + return true; + } + + long amount = Long.parseLong(args.get(1).replace(",", "")); + + if (0 >= amount) { + return false; + } + + if (target.getUniqueId().equals(p.getUniqueId())) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("gems_cant_send_to_yourself")); + return true; + } + + this.commandManager.getPlugin().getGemsManager().payGems(p, amount, target); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(1)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonGems.GEMS_ADMIN_PERM) || sender.hasPermission(this.getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/gems pay [player] [amount] - Send gems to a player."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsRemoveCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsRemoveCommand.java new file mode 100644 index 0000000..5c3316d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsRemoveCommand.java @@ -0,0 +1,63 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class GemsRemoveCommand extends GemsCommand { + + private static final String COMMAND_NAME = "remove"; + private static final String[] COMMAND_ALIASES = {"subtract", "delete"}; + + public GemsRemoveCommand(CommandManager manager) { + super(manager, COMMAND_NAME, COMMAND_ALIASES); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + + if (args.size() == 2) { + try { + long amount = Long.parseLong(args.get(1)); + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + this.commandManager.getPlugin().getGemsManager().removeGems(target, amount, sender, LostCause.ADMIN); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(0)))); + } + } + return false; + } + + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonGems.GEMS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/gems remove [player] [amount] - Remove gems from player."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsSetCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsSetCommand.java new file mode 100644 index 0000000..e1beed7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsSetCommand.java @@ -0,0 +1,60 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class GemsSetCommand extends GemsCommand { + + private static final String COMMAND_NAME = "set"; + + public GemsSetCommand(CommandManager manager) { + super(manager, COMMAND_NAME); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + + if (args.size() == 2) { + try { + long amount = Long.parseLong(args.get(1)); + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + this.commandManager.getPlugin().getGemsManager().setGems(target, amount, sender); + return true; + } catch (Exception e) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(0)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonGems.GEMS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/gems set [player] [amount] - Sets player gems."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/commands/GemsWithdrawCommand.java b/src/main/java/dev/drawethree/xprison/gems/commands/GemsWithdrawCommand.java new file mode 100644 index 0000000..c820354 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/commands/GemsWithdrawCommand.java @@ -0,0 +1,67 @@ +package dev.drawethree.xprison.gems.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public final class GemsWithdrawCommand extends GemsCommand { + + private static final String COMMAND_NAME = "withdraw"; + + public GemsWithdrawCommand(CommandManager manager) { + super(manager, COMMAND_NAME); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.size() == 2 && sender instanceof Player) { + Player p = (Player) sender; + try { + long amount = Long.parseLong(args.get(0)); + int value = Integer.parseInt(args.get(1)); + if (0 >= amount || 0 >= value) { + return false; + } + this.commandManager.getPlugin().getGemsManager().withdrawGems(p, amount, value); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("not_a_number").replace("%input%", args.get(0) + " or " + args.get(1))); + } + } else if (args.size() == 1 && sender instanceof Player) { + Player p = (Player) sender; + try { + long amount = Long.parseLong(args.get(0)); + int value = 1; + if (0 >= amount) { + return false; + } + this.commandManager.getPlugin().getGemsManager().withdrawGems(p, amount, value); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, this.commandManager.getPlugin().getMessage("not_a_number").replace("%input%", args.get(0) + " or " + args.get(1))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonGems.GEMS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/gems withdraw [amount] [value] - Withdraw gems to physical item."; + } + + @Override + public List getTabComplete(List args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/managers/CommandManager.java b/src/main/java/dev/drawethree/xprison/gems/managers/CommandManager.java new file mode 100644 index 0000000..b65ae20 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/managers/CommandManager.java @@ -0,0 +1,159 @@ +package dev.drawethree.xprison.gems.managers; + +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.commands.*; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import me.lucko.helper.command.context.CommandContext; +import me.lucko.helper.cooldown.Cooldown; +import me.lucko.helper.cooldown.CooldownMap; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static dev.drawethree.xprison.gems.XPrisonGems.GEMS_ADMIN_PERM; + + +public class CommandManager { + + @Getter + private final XPrisonGems plugin; + private final Set commands; + private CooldownMap gemsCommandCooldownMap; + private String[] gemsCommandAliases; + private String[] gemsTopCommandAliases; + private String[] gemsMessageCommandAliases; + + public CommandManager(XPrisonGems plugin) { + this.plugin = plugin; + this.commands = new HashSet<>(); + this.gemsCommandCooldownMap = CooldownMap.create(Cooldown.of(plugin.getCommandCooldown(), TimeUnit.SECONDS)); + } + + private boolean checkCommandCooldown(CommandSender sender) { + if (sender.hasPermission(GEMS_ADMIN_PERM)) { + return true; + } + if (!gemsCommandCooldownMap.test(sender)) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("cooldown").replace("%time%", String.format("%,d", this.gemsCommandCooldownMap.remainingTime(sender, TimeUnit.SECONDS)))); + return false; + } + return true; + } + + private void registerCommands() { + + this.commands.clear(); + + this.registerCommand(new GemsGiveCommand(this)); + this.registerCommand(new GemsPayCommand(this)); + this.registerCommand(new GemsRemoveCommand(this)); + this.registerCommand(new GemsSetCommand(this)); + this.registerCommand(new GemsWithdrawCommand(this)); + this.registerCommand(new GemsHelpCommand(this)); + + Commands.create() + .tabHandler(this::createTabHandler) + .handler(c -> { + if (c.args().size() == 0 && c.sender() instanceof Player) { + this.plugin.getGemsManager().sendInfoMessage(c.sender(), (OfflinePlayer) c.sender()); + return; + } + + GemsCommand subCommand = this.getCommand(c.rawArg(0)); + if (subCommand != null) { + if (subCommand.canExecute(c.sender())) { + subCommand.execute(c.sender(), c.args().subList(1, c.args().size())); + } else { + PlayerUtils.sendMessage(c.sender(), this.plugin.getMessage("no_permission")); + } + } else { + if (!checkCommandCooldown(c.sender())) { + return; + } + OfflinePlayer target = Players.getOfflineNullable(c.rawArg(0)); + this.plugin.getGemsManager().sendInfoMessage(c.sender(), target); + } + }) + .registerAndBind(this.plugin.getCore(), this.gemsCommandAliases); + Commands.create() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getGemsManager().sendGemsTop(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), this.gemsTopCommandAliases); + + // /gemsmessage + Commands.create() + .assertPlayer() + .handler(c -> this.plugin.getGemsManager().toggleGemsMessage(c.sender())).registerAndBind(this.plugin.getCore(), this.gemsMessageCommandAliases); + + } + + private List createTabHandler(CommandContext context) { + List returnList = this.commands.stream().map(GemsCommand::getName).collect(Collectors.toList()); + + GemsCommand subCommand = this.getCommand(context.rawArg(0)); + + if (subCommand != null) { + return subCommand.getTabComplete(context.args().subList(1, context.args().size())); + } + + return returnList; + } + + private void registerCommand(GemsCommand command) { + this.commands.add(command); + } + + private GemsCommand getCommand(String arg) { + for (GemsCommand command : this.commands) { + + if (command.getName().equalsIgnoreCase(arg)) { + return command; + } + + if (command.getAliases() == null) { + continue; + } + + for (String alias : command.getAliases()) { + if (alias.equalsIgnoreCase(arg)) { + return command; + } + } + } + return null; + } + + private void loadVariables() { + this.gemsCommandAliases = this.plugin.getConfig().get().getStringList("gems-command-aliases").toArray(new String[0]); + this.gemsTopCommandAliases = this.plugin.getConfig().get().getStringList("gems-top-command-aliases").toArray(new String[0]); + this.gemsMessageCommandAliases = this.plugin.getConfig().get().getStringList("gems-message-command-aliases").toArray(new String[0]); + } + + public Set getAll() { + return new HashSet<>(this.commands); + } + + public void reload() { + Map cooldownMap = this.gemsCommandCooldownMap.getAll(); + this.gemsCommandCooldownMap = CooldownMap.create(Cooldown.of(plugin.getCommandCooldown(), TimeUnit.SECONDS)); + cooldownMap.forEach((commandSender, cooldown) -> this.gemsCommandCooldownMap.put(commandSender, cooldown)); + this.loadVariables(); + } + + public void enable() { + this.loadVariables(); + this.registerCommands(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/managers/GemsManager.java b/src/main/java/dev/drawethree/xprison/gems/managers/GemsManager.java new file mode 100644 index 0000000..8a82a97 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/managers/GemsManager.java @@ -0,0 +1,389 @@ +package dev.drawethree.xprison.gems.managers; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.gems.api.events.PlayerGemsLostEvent; +import dev.drawethree.xprison.gems.api.events.PlayerGemsReceiveEvent; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.item.PrisonItem; +import dev.drawethree.xprison.utils.misc.NumberUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.scheduler.Task; +import me.lucko.helper.utils.Players; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class GemsManager { + + + private final XPrisonGems plugin; + private String SPACER_LINE; + private String SPACER_LINE_BOTTOM; + private String TOP_FORMAT_GEMS; + private final Map gemsCache = new ConcurrentHashMap<>(); + private Map top10Gems = new LinkedHashMap<>(); + private Task task; + private boolean updating; + private boolean displayGemsMessages; + + private String gemsItemDisplayName; + private ItemStack gemsItem; + private List gemsItemLore; + private final List gemsMessageOnPlayers; + + private long startingGems; + + public GemsManager(XPrisonGems plugin) { + this.plugin = plugin; + this.gemsMessageOnPlayers = new ArrayList<>(); + this.reload(); + + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> { + this.addIntoTable(e.getPlayer()); + this.loadPlayerData(e.getPlayer()); + if (this.displayGemsMessages && hasOffGemsMessages(e.getPlayer())) { + this.gemsMessageOnPlayers.add(e.getPlayer().getUniqueId()); + } + }).bindWith(plugin.getCore()); + Events.subscribe(PlayerQuitEvent.class) + .handler(e -> { + this.savePlayerData(e.getPlayer(), true, true); + e.getPlayer().getActivePotionEffects().forEach(effect -> e.getPlayer().removePotionEffect(effect.getType())); + }).bindWith(plugin.getCore()); + + this.loadPlayerDataOnEnable(); + this.updateTop10(); + } + + public void reload() { + this.SPACER_LINE = plugin.getMessage("top_spacer_line"); + this.SPACER_LINE_BOTTOM = plugin.getMessage("top_spacer_line_bottom"); + this.TOP_FORMAT_GEMS = plugin.getMessage("top_format_gems"); + this.displayGemsMessages = plugin.getConfig().get().getBoolean("display-gems-messages"); + this.gemsItemDisplayName = plugin.getConfig().get().getString("gems.item.name"); + this.gemsItemLore = plugin.getConfig().get().getStringList("gems.item.lore"); + this.gemsItem = CompMaterial.fromString(plugin.getConfig().get().getString("gems.item.material")).toItem(); + this.startingGems = plugin.getConfig().get().getLong("starting-gems"); + } + + public void stopUpdating() { + this.plugin.getCore().debug("Stopping updating Top 10 - Gems", this.plugin); + task.close(); + } + + private void updateTop10() { + this.updating = true; + task = Schedulers.async().runRepeating(() -> { + this.updating = true; + Players.all().forEach(p -> savePlayerData(p, false, false)); + this.updateGemsTop(); + this.updating = false; + }, 1, TimeUnit.MINUTES, 1, TimeUnit.HOURS); + } + + private void savePlayerData(Player player, boolean removeFromCache, boolean async) { + if (async) { + Schedulers.async().run(() -> { + this.plugin.getGemsService().setGems(player, gemsCache.getOrDefault(player.getUniqueId(), 0L)); + if (removeFromCache) { + gemsCache.remove(player.getUniqueId()); + } + this.plugin.getCore().debug(String.format("Saved player %s gems to database.", player.getName()), this.plugin); + }); + } else { + this.plugin.getGemsService().setGems(player, gemsCache.getOrDefault(player.getUniqueId(), 0L)); + if (removeFromCache) { + gemsCache.remove(player.getUniqueId()); + } + this.plugin.getCore().debug(String.format("Saved player %s gems to database.", player.getName()), this.plugin); + } + } + + public void savePlayerDataOnDisable() { + for (UUID uuid : gemsCache.keySet()) { + this.plugin.getGemsService().setGems(Players.getOfflineNullable(uuid), gemsCache.getOrDefault(uuid, 0L)); + } + gemsCache.clear(); + this.plugin.getCore().getLogger().info("Saved online players gems."); + } + + private void addIntoTable(Player player) { + Schedulers.async().run(() -> { + this.plugin.getGemsService().createGems(player, startingGems); + }); + } + + private void loadPlayerDataOnEnable() { + Players.all().forEach(p -> loadPlayerData(p)); + } + + private void loadPlayerData(Player player) { + Schedulers.async().run(() -> { + long playerGems = this.plugin.getGemsService().getPlayerGems(player); + this.gemsCache.put(player.getUniqueId(), playerGems); + this.plugin.getCore().debug(String.format("Loaded gems of player %s from database", player.getName()), this.plugin); + }); + } + + public void setGems(OfflinePlayer p, long newAmount, CommandSender executor) { + Schedulers.async().run(() -> { + if (!p.isOnline()) { + this.plugin.getGemsService().setGems(p, newAmount); + } else { + gemsCache.put(p.getUniqueId(), newAmount); + } + PlayerUtils.sendMessage(executor, plugin.getMessage("admin_set_gems").replace("%player%", p.getName()).replace("%gems%", String.format("%,d", newAmount))); + }); + } + + public void giveGems(OfflinePlayer p, long amount, CommandSender executor, ReceiveCause cause) { + if (Bukkit.isPrimaryThread()) { + giveGemsSync(p, amount, executor, cause); + } else { + Schedulers.sync().run(() -> giveGemsSync(p, amount, executor, cause)); + } + } + + private void giveGemsSync(OfflinePlayer p, long amount, CommandSender executor, ReceiveCause cause) { + long currentGems = getPlayerGems(p); + + this.plugin.getCore().debug("XPrisonPlayerGemsReceiveEvent :: Player Gems :: " + currentGems, this.plugin); + + long finalAmount = this.callGemsReceiveEvent(cause, p, amount); + + this.plugin.getCore().debug("XPrisonPlayerGemsReceiveEvent :: Final amount :: " + finalAmount, this.plugin); + + long newAmount; + + if (NumberUtils.wouldAdditionBeOverMaxLong(currentGems, finalAmount)) { + newAmount = Long.MAX_VALUE; + } else { + newAmount = currentGems + finalAmount; + } + + if (!p.isOnline()) { + Schedulers.async().run(() -> this.plugin.getGemsService().setGems(p, newAmount)); + } else { + gemsCache.put(p.getUniqueId(), newAmount); + if (executor instanceof ConsoleCommandSender && !this.hasOffGemsMessages(p.getPlayer())) { + PlayerUtils.sendMessage(p.getPlayer(), plugin.getMessage("gems_received_console").replace("%gems%", String.format("%,d", finalAmount)).replace("%player%", executor == null ? "Console" : executor.getName())); + } else if (cause == ReceiveCause.MINING && !this.hasOffGemsMessages(p.getPlayer())) { + PlayerUtils.sendMessage(p.getPlayer(), this.plugin.getMessage("gems_received_mining").replace("%amount%", String.format("%,d", finalAmount))); + } + } + + this.plugin.getCore().debug("XPrisonPlayerGemsReceiveEvent :: Player gems final :: " + this.gemsCache.getOrDefault(p.getUniqueId(), 0L), this.plugin); + + if (executor != null && !(executor instanceof ConsoleCommandSender)) { + PlayerUtils.sendMessage(executor, plugin.getMessage("admin_give_gems").replace("%player%", p.getName()).replace("%gems%", String.format("%,d", finalAmount))); + } + + } + + private long callGemsReceiveEvent(ReceiveCause cause, OfflinePlayer p, long amount) { + PlayerGemsReceiveEvent event = new PlayerGemsReceiveEvent(cause, p, amount); + + Events.call(event); + + if (event.isCancelled()) { + return amount; + } + + return event.getAmount(); + } + + public void redeemGems(Player p, ItemStack item, boolean shiftClick, boolean offhand) { + final Long gemsAmount = new PrisonItem(item).getGems(); + if (gemsAmount == null) { + PlayerUtils.sendMessage(p, plugin.getMessage("not_gems_item")); + return; + } + int itemAmount = item.getAmount(); + if (shiftClick) { + if (offhand) { + p.getInventory().setItemInOffHand(null); + } else { + p.setItemInHand(null); + } + this.giveGems(p, gemsAmount * itemAmount, null, ReceiveCause.REDEEM); + PlayerUtils.sendMessage(p, plugin.getMessage("gems_redeem").replace("%gems%", String.format("%,d", gemsAmount * itemAmount))); + } else { + this.giveGems(p, gemsAmount, null, ReceiveCause.REDEEM); + if (item.getAmount() == 1) { + if (offhand) { + p.getInventory().setItemInOffHand(null); + } else { + p.setItemInHand(null); + } + } else { + item.setAmount(item.getAmount() - 1); + } + PlayerUtils.sendMessage(p, plugin.getMessage("gems_redeem").replace("%gems%", String.format("%,d", gemsAmount))); + } + } + + public void payGems(Player executor, long amount, OfflinePlayer target) { + Schedulers.async().run(() -> { + if (getPlayerGems(executor) >= amount) { + this.removeGems(executor, amount, null, LostCause.PAY); + this.giveGems(target, amount, null, ReceiveCause.PAY); + PlayerUtils.sendMessage(executor, plugin.getMessage("gems_send").replace("%player%", target.getName()).replace("%gems%", String.format("%,d", amount))); + if (target.isOnline()) { + PlayerUtils.sendMessage((CommandSender) target, plugin.getMessage("gems_received").replace("%player%", executor.getName()).replace("%gems%", String.format("%,d", amount))); + } + } else { + PlayerUtils.sendMessage(executor, plugin.getMessage("not_enough_gems")); + } + }); + } + + public void withdrawGems(Player executor, long amount, int value) { + Schedulers.async().run(() -> { + long totalAmount = amount * value; + + if (this.getPlayerGems(executor) < totalAmount) { + PlayerUtils.sendMessage(executor, plugin.getMessage("not_enough_gems")); + return; + } + + removeGems(executor, totalAmount, null, LostCause.WITHDRAW); + + ItemStack item = createGemsItem(amount, value); + Collection notFit = executor.getInventory().addItem(item).values(); + + if (!notFit.isEmpty()) { + notFit.forEach(itemStack -> { + this.giveGems(executor, amount * item.getAmount(), null, ReceiveCause.REDEEM); + }); + } + + PlayerUtils.sendMessage(executor, plugin.getMessage("withdraw_successful").replace("%amount%", String.format("%,d", amount)).replace("%value%", String.format("%,d", value))); + }); + } + + public synchronized long getPlayerGems(OfflinePlayer p) { + if (!p.isOnline()) { + return this.plugin.getGemsService().getPlayerGems(p); + } else { + return gemsCache.getOrDefault(p.getUniqueId(), (long) 0); + } + } + + public void removeGems(OfflinePlayer p, long amount, CommandSender executor, LostCause cause) { + Schedulers.async().run(() -> { + long currentgems = getPlayerGems(p); + long finalgems = currentgems - amount; + + if (finalgems < 0) { + finalgems = 0; + } + + this.callGemsLostEvent(cause, p, amount); + + if (!p.isOnline()) { + this.plugin.getGemsService().setGems(p, finalgems); + } else { + gemsCache.put(p.getUniqueId(), finalgems); + } + if (executor != null) { + PlayerUtils.sendMessage(executor, plugin.getMessage("admin_remove_gems").replace("%player%", p.getName()).replace("%gems%", String.format("%,d", amount))); + } + }); + } + + private void callGemsLostEvent(LostCause cause, OfflinePlayer p, long amount) { + PlayerGemsLostEvent event = new PlayerGemsLostEvent(cause, p, amount); + Events.callSync(event); + } + + private ItemStack createGemsItem(long amount, int value) { + ItemStack item = ItemStackBuilder.of(this.gemsItem.clone()).amount(value).name(this.gemsItemDisplayName.replace("%amount%", String.format("%,d", amount)).replace("%tokens%", String.format("%,d", amount))).lore(this.gemsItemLore).enchant(Enchantment.PROTECTION_ENVIRONMENTAL).flag(ItemFlag.HIDE_ENCHANTS).build(); + final PrisonItem prisonItem = new PrisonItem(item); + prisonItem.setGems(amount); + prisonItem.load(); + return item; + } + + public void sendInfoMessage(CommandSender sender, OfflinePlayer target) { + Schedulers.async().run(() -> { + if (sender == target) { + PlayerUtils.sendMessage(sender, plugin.getMessage("your_gems").replace("%gems%", String.format("%,d", this.getPlayerGems(target)))); + } else { + PlayerUtils.sendMessage(sender, plugin.getMessage("other_gems").replace("%gems%", String.format("%,d", this.getPlayerGems(target))).replace("%player%", target.getName())); + } + }); + } + + + private void updateGemsTop() { + top10Gems = new LinkedHashMap<>(); + this.plugin.getCore().debug("Starting updating Top 10 - Gems", this.plugin); + this.top10Gems = this.plugin.getGemsService().getTopGems(10); + this.plugin.getCore().debug("GemsTop updated!", this.plugin); + } + + public void sendGemsTop(CommandSender sender) { + Schedulers.async().run(() -> { + PlayerUtils.sendMessage(sender, SPACER_LINE); + if (this.updating) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("top_updating")); + PlayerUtils.sendMessage(sender, SPACER_LINE_BOTTOM); + return; + } + for (int i = 0; i < 10; i++) { + try { + UUID uuid = (UUID) top10Gems.keySet().toArray()[i]; + OfflinePlayer player = Players.getOfflineNullable(uuid); + String name; + if (player.getName() == null) { + name = "Unknown Player"; + } else { + name = player.getName(); + } + long gems = top10Gems.get(uuid); + PlayerUtils.sendMessage(sender, TOP_FORMAT_GEMS.replace("%position%", String.valueOf(i + 1)).replace("%player%", name).replace("%amount%", String.format("%,d", gems))); + } catch (ArrayIndexOutOfBoundsException e) { + break; + } + } + PlayerUtils.sendMessage(sender, SPACER_LINE_BOTTOM); + }); + } + + + public void toggleGemsMessage(Player p) { + if (this.gemsMessageOnPlayers.contains(p.getUniqueId())) { + PlayerUtils.sendMessage(p, plugin.getMessage("gems_message_disabled")); + this.gemsMessageOnPlayers.remove(p.getUniqueId()); + } else { + PlayerUtils.sendMessage(p, plugin.getMessage("gems_message_enabled")); + this.gemsMessageOnPlayers.add(p.getUniqueId()); + } + } + + public boolean hasOffGemsMessages(Player p) { + return !this.gemsMessageOnPlayers.contains(p.getUniqueId()); + } + + public Material getGemsItemMaterial() { + return this.gemsItem.getType(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/repo/GemsRepository.java b/src/main/java/dev/drawethree/xprison/gems/repo/GemsRepository.java new file mode 100644 index 0000000..87e2968 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/repo/GemsRepository.java @@ -0,0 +1,21 @@ +package dev.drawethree.xprison.gems.repo; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface GemsRepository { + + long getPlayerGems(OfflinePlayer player); + + void updateGems(OfflinePlayer player, long newAmount); + + Map getTopGems(int amountOfRecords); + + void addIntoGems(OfflinePlayer player, long startingGems); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/gems/repo/impl/GemsRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/gems/repo/impl/GemsRepositoryImpl.java new file mode 100644 index 0000000..c839a50 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/repo/impl/GemsRepositoryImpl.java @@ -0,0 +1,76 @@ +package dev.drawethree.xprison.gems.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import dev.drawethree.xprison.gems.repo.GemsRepository; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class GemsRepositoryImpl implements GemsRepository { + + private static final String TABLE_NAME = "UltraPrison_Gems"; + private static final String GEMS_UUID_COLNAME = "UUID"; + private static final String GEMS_GEMS_COLNAME = "Gems"; + + private final SQLDatabase database; + + public GemsRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public long getPlayerGems(OfflinePlayer p) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con,"SELECT * FROM " + TABLE_NAME + " WHERE " + GEMS_UUID_COLNAME + "=?")) { + statement.setString(1, p.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getLong(GEMS_GEMS_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public void updateGems(OfflinePlayer p, long newAmount) { + this.database.executeSql("UPDATE " + TABLE_NAME + " SET " + GEMS_GEMS_COLNAME + "=? WHERE " + GEMS_UUID_COLNAME + "=?", newAmount, p.getUniqueId().toString()); + } + + @Override + public Map getTopGems(int amountOfRecords) { + Map topGems = new LinkedHashMap<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con,"SELECT " + GEMS_UUID_COLNAME + "," + GEMS_GEMS_COLNAME + " FROM " + TABLE_NAME + " ORDER BY " + GEMS_GEMS_COLNAME + " DESC LIMIT " + amountOfRecords); ResultSet set = statement.executeQuery()) { + while (set.next()) { + topGems.put(UUID.fromString(set.getString(GEMS_UUID_COLNAME)), set.getLong(GEMS_GEMS_COLNAME)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return topGems; + } + + @Override + public void addIntoGems(OfflinePlayer player, long startingGems) { + String sql = this.database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME + " VALUES(?,?)" : "INSERT IGNORE INTO " + TABLE_NAME + " VALUES(?,?)"; + this.database.executeSql(sql, player.getUniqueId().toString(), startingGems); + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(UUID varchar(36) NOT NULL UNIQUE, Gems bigint, primary key (UUID))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME); + } +} diff --git a/src/main/java/dev/drawethree/xprison/gems/service/GemsService.java b/src/main/java/dev/drawethree/xprison/gems/service/GemsService.java new file mode 100644 index 0000000..31291e8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/service/GemsService.java @@ -0,0 +1,17 @@ +package dev.drawethree.xprison.gems.service; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface GemsService { + + long getPlayerGems(OfflinePlayer player); + + void setGems(OfflinePlayer player, long newAmount); + + Map getTopGems(int amountOfRecords); + + void createGems(OfflinePlayer player, long startingGems); +} diff --git a/src/main/java/dev/drawethree/xprison/gems/service/impl/GemsServiceImpl.java b/src/main/java/dev/drawethree/xprison/gems/service/impl/GemsServiceImpl.java new file mode 100644 index 0000000..2a4774f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/gems/service/impl/GemsServiceImpl.java @@ -0,0 +1,37 @@ +package dev.drawethree.xprison.gems.service.impl; + +import dev.drawethree.xprison.gems.repo.GemsRepository; +import dev.drawethree.xprison.gems.service.GemsService; +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public class GemsServiceImpl implements GemsService { + + private final GemsRepository repository; + + public GemsServiceImpl(GemsRepository repository) { + this.repository = repository; + } + + @Override + public long getPlayerGems(OfflinePlayer player) { + return repository.getPlayerGems(player); + } + + @Override + public void setGems(OfflinePlayer player, long newAmount) { + repository.updateGems(player, newAmount); + } + + @Override + public Map getTopGems(int amountOfRecords) { + return repository.getTopGems(amountOfRecords); + } + + @Override + public void createGems(OfflinePlayer player, long startingGems) { + repository.addIntoGems(player, startingGems); + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/XPrisonHistory.java b/src/main/java/dev/drawethree/xprison/history/XPrisonHistory.java new file mode 100644 index 0000000..037a778 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/XPrisonHistory.java @@ -0,0 +1,181 @@ +package dev.drawethree.xprison.history; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.autominer.api.events.PlayerAutoMinerTimeModifyEvent; +import dev.drawethree.xprison.autominer.api.events.PlayerAutomineEvent; +import dev.drawethree.xprison.gangs.api.events.GangCreateEvent; +import dev.drawethree.xprison.gangs.api.events.GangDisbandEvent; +import dev.drawethree.xprison.gangs.api.events.GangJoinEvent; +import dev.drawethree.xprison.gangs.api.events.GangLeaveEvent; +import dev.drawethree.xprison.gems.api.events.PlayerGemsLostEvent; +import dev.drawethree.xprison.gems.api.events.PlayerGemsReceiveEvent; +import dev.drawethree.xprison.history.api.XPrisonHistoryAPI; +import dev.drawethree.xprison.history.api.XPrisonHistoryAPIImpl; +import dev.drawethree.xprison.history.gui.PlayerHistoryGUI; +import dev.drawethree.xprison.history.manager.HistoryManager; +import dev.drawethree.xprison.history.repo.HistoryRepository; +import dev.drawethree.xprison.history.repo.impl.HistoryRepositoryImpl; +import dev.drawethree.xprison.history.service.HistoryService; +import dev.drawethree.xprison.history.service.impl.HistoryServiceImpl; +import dev.drawethree.xprison.multipliers.api.events.PlayerMultiplierReceiveEvent; +import dev.drawethree.xprison.prestiges.api.events.PlayerPrestigeEvent; +import dev.drawethree.xprison.ranks.api.events.PlayerRankUpEvent; +import dev.drawethree.xprison.tokens.api.events.PlayerTokensLostEvent; +import dev.drawethree.xprison.tokens.api.events.PlayerTokensReceiveEvent; +import dev.drawethree.xprison.utils.misc.TimeUtil; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import me.lucko.helper.Events; +import me.lucko.helper.event.filter.EventFilters; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; + +public final class XPrisonHistory implements XPrisonModule { + + private static final String MODULE_NAME = "History"; + @Getter + private final XPrison core; + + @Getter + private HistoryManager historyManager; + + @Getter + private HistoryRepository historyRepository; + + @Getter + private HistoryService historyService; + + @Getter + private XPrisonHistoryAPI api; + + private boolean enabled; + + public XPrisonHistory(XPrison core) { + this.core = core; + this.enabled = false; + } + + @Override + public void enable() { + this.enabled = true; + this.historyRepository = new HistoryRepositoryImpl(this.core.getPluginDatabase()); + this.historyRepository.createTables(); + this.historyService = new HistoryServiceImpl(this.historyRepository); + this.historyManager = new HistoryManager(this); + this.api = new XPrisonHistoryAPIImpl(this); + this.registerCommands(); + this.registerEvents(); + } + + private void registerEvents() { + Events.subscribe(PlayerGemsReceiveEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getGems(), String.format("&a&l+%,d GEMS &f(%s). &7Current Gems: &e%,d", e.getAmount(), e.getCause().name(), this.core.getGems().getApi().getPlayerGems(e.getPlayer()))); + }).bindWith(this.core); + Events.subscribe(PlayerGemsLostEvent.class, EventPriority.MONITOR) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getGems(), String.format("&c&l-%,d GEMS &f(%s). &7Current Gems: &e%,d", e.getAmount(), e.getCause().name(), this.core.getTokens().getApi().getPlayerTokens(e.getPlayer()))); + }).bindWith(this.core); + Events.subscribe(PlayerTokensReceiveEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getTokens(), String.format("&a&l+%,d TOKENS &f(%s).&7Current Tokens: &e%,d", e.getAmount(), e.getCause().name(), this.core.getTokens().getApi().getPlayerTokens(e.getPlayer()))); + }).bindWith(this.core); + Events.subscribe(PlayerTokensLostEvent.class, EventPriority.MONITOR) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getTokens(), String.format("&c&l-%,d TOKENS &f(%s).&7Current Tokens: &e%,d", e.getAmount(), e.getCause().name(), this.core.getTokens().getApi().getPlayerTokens(e.getPlayer()))); + }).bindWith(this.core); + Events.subscribe(PlayerRankUpEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getRanks(), String.format("Rank Up: %s&r -> %s", e.getOldRank().getPrefix(), e.getNewRank().getPrefix())); + }).bindWith(this.core); + Events.subscribe(PlayerPrestigeEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getPrestiges(), String.format("Prestige Up: %s&r -> %s", e.getOldPrestige().getPrefix(), e.getNewPrestige().getPrefix())); + }).bindWith(this.core); + Events.subscribe(GangLeaveEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getGangs(), String.format("Left Gang: &e%s", e.getGang().getName())); + }).bindWith(this.core); + Events.subscribe(GangJoinEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getGangs(), String.format("Joined Gang: &e%s", e.getGang().getName())); + }).bindWith(this.core); + Events.subscribe(GangCreateEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + if (e.getCreator() instanceof Player) { + this.historyManager.createPlayerHistoryLine((OfflinePlayer) e.getCreator(), this.core.getGangs(), String.format("Created Gang: &e%s", e.getGang().getName())); + } + }).bindWith(this.core); + Events.subscribe(GangDisbandEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getGang().getOwnerOffline(), this.core.getGangs(), String.format("Disbanded Gang: &e%s", e.getGang().getName())); + }).bindWith(this.core); + Events.subscribe(PlayerMultiplierReceiveEvent.class, EventPriority.MONITOR) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getMultipliers(), String.format("Received x%,.2f %s Multiplier for %,d %s", e.getMultiplier(), e.getType(), e.getDuration(), e.getTimeUnit().name())); + }).bindWith(this.core); + Events.subscribe(PlayerAutomineEvent.class, EventPriority.MONITOR) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getAutoMiner(), String.format("Player is Auto-Mining. Time left: %s", TimeUtil.getTime(e.getTimeLeft() - 1))); + }).bindWith(this.core); + Events.subscribe(PlayerAutoMinerTimeModifyEvent.class, EventPriority.MONITOR) + .handler(e -> { + this.historyManager.createPlayerHistoryLine(e.getPlayer(), this.core.getAutoMiner(), String.format("Received %,d %s of Auto-Miner time.", e.getDuration(), e.getTimeUnit().name())); + }).bindWith(this.core); + } + + private void registerCommands() { + Commands.create() + .assertPermission("xprison.history") + .assertPlayer() + .handler(c -> { + if (c.args().size() != 1) { + PlayerUtils.sendMessage(c.sender(),"&c/history "); + return; + } + OfflinePlayer target = c.arg(0).parseOrFail(OfflinePlayer.class); + new PlayerHistoryGUI(c.sender(), target, this).open(); + }).registerAndBind(this.core, "history"); + } + + @Override + public void disable() { + this.enabled = false; + } + + @Override + public void reload() { + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return false; + } + + @Override + public void resetPlayerData() { + this.historyRepository.clearTableData(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/api/XPrisonHistoryAPI.java b/src/main/java/dev/drawethree/xprison/history/api/XPrisonHistoryAPI.java new file mode 100644 index 0000000..a2dcbe1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/api/XPrisonHistoryAPI.java @@ -0,0 +1,27 @@ +package dev.drawethree.xprison.history.api; + +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.history.model.HistoryLine; +import org.bukkit.OfflinePlayer; + +import java.util.List; + +public interface XPrisonHistoryAPI { + + /** + * Gets players history + * + * @param player Player + * @return List containing all HistoryLine.class of Player + */ + List getPlayerHistory(OfflinePlayer player); + + /** + * Creates a new history line for player + * + * @param player Player + * @param context Context of the history + * @param module XPrisonModule associated with the history + */ + void createHistoryLine(OfflinePlayer player, XPrisonModule module, String context); +} diff --git a/src/main/java/dev/drawethree/xprison/history/api/XPrisonHistoryAPIImpl.java b/src/main/java/dev/drawethree/xprison/history/api/XPrisonHistoryAPIImpl.java new file mode 100644 index 0000000..02a6772 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/api/XPrisonHistoryAPIImpl.java @@ -0,0 +1,27 @@ +package dev.drawethree.xprison.history.api; + +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.history.XPrisonHistory; +import dev.drawethree.xprison.history.model.HistoryLine; +import org.bukkit.OfflinePlayer; + +import java.util.List; + +public final class XPrisonHistoryAPIImpl implements XPrisonHistoryAPI { + + private final XPrisonHistory plugin; + + public XPrisonHistoryAPIImpl(XPrisonHistory plugin) { + this.plugin = plugin; + } + + @Override + public List getPlayerHistory(OfflinePlayer player) { + return this.plugin.getHistoryManager().getPlayerHistory(player); + } + + @Override + public void createHistoryLine(OfflinePlayer player, XPrisonModule module, String context) { + this.plugin.getHistoryManager().createPlayerHistoryLine(player, module, context); + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/gui/PlayerHistoryFilterGUI.java b/src/main/java/dev/drawethree/xprison/history/gui/PlayerHistoryFilterGUI.java new file mode 100644 index 0000000..dbc8b42 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/gui/PlayerHistoryFilterGUI.java @@ -0,0 +1,69 @@ +package dev.drawethree.xprison.history.gui; + +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.history.XPrisonHistory; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public class PlayerHistoryFilterGUI extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("100000001") + .mask("100000001") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + private final OfflinePlayer target; + private final XPrisonHistory plugin; + + public PlayerHistoryFilterGUI(Player player, OfflinePlayer target, XPrisonHistory plugin) { + super(player, 4, "History Filter"); + this.target = target; + this.plugin = plugin; + } + + @Override + public void redraw() { + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + for (XPrisonModule module : this.plugin.getCore().getModules()) { + if (!module.isHistoryEnabled()) { + continue; + } + this.addItem(ItemStackBuilder.of(CompMaterial.HOPPER.toItem()).name("&e" + module.getName()).lore("&7Show only history related to this module.").build(() -> { + this.close(); + this.plugin.getHistoryManager().openPlayerHistoryGui(this.getPlayer(), this.target, historyLine -> historyLine.getModule().equalsIgnoreCase(module.getName())); + })); + } + this.setItem(27, ItemStackBuilder.of(Material.ARROW).name("&c&lBack").lore("&7Click to go back.").build(() -> { + this.close(); + new PlayerHistoryGUI(this.getPlayer(), this.target, this.plugin).open(); + })); + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/gui/PlayerHistoryGUI.java b/src/main/java/dev/drawethree/xprison/history/gui/PlayerHistoryGUI.java new file mode 100644 index 0000000..0791564 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/gui/PlayerHistoryGUI.java @@ -0,0 +1,82 @@ +package dev.drawethree.xprison.history.gui; + +import dev.drawethree.xprison.history.XPrisonHistory; +import dev.drawethree.xprison.history.gui.confirmation.PlayerClearHistoryConfirmationGUI; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public class PlayerHistoryGUI extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + private final OfflinePlayer target; + private final XPrisonHistory plugin; + + public PlayerHistoryGUI(Player viewer, OfflinePlayer target, XPrisonHistory plugin) { + super(viewer, 5, "History Management"); + this.target = target; + this.plugin = plugin; + } + + @Override + public void redraw() { + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + } + + //Player + this.setItem(13, ItemStackBuilder.of(SkullUtils.createPlayerHead(target, "&e&l" + target.getName(), Arrays.asList("&7Currently viewing history profile", "&7of this player."))).buildItem().build()); + + //Filter + this.setItem(20, ItemStackBuilder.of(CompMaterial.HOPPER.toItem()).name("&eFilter History").lore("&7Click to select a filter").build(() -> { + this.close(); + new PlayerHistoryFilterGUI(this.getPlayer(), this.target, this.plugin).open(); + })); + + //Full History + this.setItem(22, ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name("&eFull History").lore("&7Click to show full history").build(() -> { + this.close(); + this.plugin.getHistoryManager().openPlayerHistoryGui(this.getPlayer(), this.target, null); + })); + + //Clear History + this.setItem(24, ItemStackBuilder.of(CompMaterial.BARRIER.toItem()).name("&eClear History").lore("&7Click to clear history").build(() -> { + this.close(); + new PlayerClearHistoryConfirmationGUI(this.getPlayer(), this.target, this.plugin).open(); + })); + + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lClose").lore("&7Click to close the gui.").build(this::close)); + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/gui/confirmation/PlayerClearHistoryConfirmationGUI.java b/src/main/java/dev/drawethree/xprison/history/gui/confirmation/PlayerClearHistoryConfirmationGUI.java new file mode 100644 index 0000000..da1bfdf --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/gui/confirmation/PlayerClearHistoryConfirmationGUI.java @@ -0,0 +1,28 @@ +package dev.drawethree.xprison.history.gui.confirmation; + +import dev.drawethree.xprison.history.XPrisonHistory; +import dev.drawethree.xprison.utils.gui.ConfirmationGui; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public class PlayerClearHistoryConfirmationGUI extends ConfirmationGui { + + private final OfflinePlayer target; + private XPrisonHistory plugin; + + public PlayerClearHistoryConfirmationGUI(Player player, OfflinePlayer target, XPrisonHistory plugin) { + super(player, "Clear " + target.getName() + "?"); + this.target = target; + this.plugin = plugin; + } + + @Override + public void confirm(boolean confirm) { + if (confirm) { + this.plugin.getHistoryManager().clearPlayerHistory(this.target); + PlayerUtils.sendMessage(this.getPlayer(),"&aYou have cleared history data of player &e" + target.getName()); + } + this.close(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/manager/HistoryManager.java b/src/main/java/dev/drawethree/xprison/history/manager/HistoryManager.java new file mode 100644 index 0000000..dffd95e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/manager/HistoryManager.java @@ -0,0 +1,126 @@ +package dev.drawethree.xprison.history.manager; + +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.autominer.XPrisonAutoMiner; +import dev.drawethree.xprison.gangs.XPrisonGangs; +import dev.drawethree.xprison.gems.XPrisonGems; +import dev.drawethree.xprison.history.XPrisonHistory; +import dev.drawethree.xprison.history.model.HistoryLine; +import dev.drawethree.xprison.multipliers.XPrisonMultipliers; +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.ranks.XPrisonRanks; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.paginated.PaginatedGuiBuilder; +import org.apache.commons.lang.Validate; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class HistoryManager { + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); + + private final XPrisonHistory plugin; + + public HistoryManager(XPrisonHistory plugin) { + this.plugin = plugin; + } + + public List getPlayerHistory(OfflinePlayer player) { + return this.plugin.getHistoryService().getPlayerHistory(player); + } + + public void createPlayerHistoryLine(OfflinePlayer player, XPrisonModule module, String context) { + HistoryLine history = createHistoryLineObject(player, module, context); + this.plugin.getHistoryService().createHistoryLine(player, history); + } + + public void clearPlayerHistory(OfflinePlayer target) { + this.plugin.getHistoryService().deleteHistory(target); + } + + public void openPlayerHistoryGui(Player sender, OfflinePlayer target, Predicate filter) { + PaginatedGuiBuilder builder = PaginatedGuiBuilder.create(); + builder.lines(6); + builder.title("History: " + target.getName()); + builder.nextPageSlot(53); + builder.previousPageSlot(45); + builder.build(sender, gui -> { + Stream historyLinesStream = getPlayerHistory(target).stream(); + if (filter != null) { + historyLinesStream = historyLinesStream.filter(filter); + } + List historyLines = historyLinesStream.sorted(Comparator.comparing(HistoryLine::getCreatedAt).reversed()).collect(Collectors.toList()); + if (historyLines.isEmpty()) { + return Collections.singletonList(getEmptyHistoryItem()); + } else { + return historyLines.stream().map(this::createHistoryLineGuiItem).collect(Collectors.toList()); + } + }).open(); + } + + private Item getEmptyHistoryItem() { + return ItemStackBuilder.of(CompMaterial.BARRIER.toItem()).name("&4&lNo History").lore("&cNo history is present for this player.").buildItem().build(); + } + + private HistoryLine createHistoryLineObject(OfflinePlayer player, XPrisonModule module, String context) { + Validate.notNull(player, "Player cannot be null!"); + Validate.notNull(module, "Module cannot be null!"); + Validate.notNull(context, "Context cannot be null!"); + + HistoryLine history = new HistoryLine(); + history.setCreatedAt(new Date()); + history.setContext(context); + history.setPlayerUuid(player.getUniqueId()); + history.setModule(module.getName()); + history.setUuid(UUID.randomUUID()); + return history; + } + + private Item createHistoryLineGuiItem(HistoryLine line) { + return ItemStackBuilder + .of(getIconForModule(line.getModule())) + .name("&e" + line.getModule()) + .lore( + " ", + "&7Module: &e" + line.getModule(), + "&7Date: &e" + DATE_FORMAT.format(line.getCreatedAt()), + "&7Context:", + "&f" + line.getContext(), + " ").buildItem().build(); + } + + private ItemStack getIconForModule(String moduleName) { + switch (moduleName) { + case XPrisonTokens.MODULE_NAME: + case XPrisonMultipliers.MODULE_NAME: + return SkullUtils.COIN_SKULL.clone(); + case XPrisonGems.MODULE_NAME: + return ItemStackBuilder.of(CompMaterial.EMERALD.toItem()).build(); + case XPrisonGangs.MODULE_NAME: + return SkullUtils.GANG_SKULL.clone(); + case XPrisonPrestiges + .MODULE_NAME: + return SkullUtils.DIAMOND_P_SKULL.clone(); + case XPrisonRanks + .MODULE_NAME: + return SkullUtils.DIAMOND_R_SKULL.clone(); + case XPrisonAutoMiner + .MODULE_NAME: + return ItemStackBuilder.of(CompMaterial.DIAMOND_PICKAXE.toItem()).build(); + default: + return ItemStackBuilder.of(CompMaterial.BOOK.toItem()).build(); + } + + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/model/HistoryLine.java b/src/main/java/dev/drawethree/xprison/history/model/HistoryLine.java new file mode 100644 index 0000000..c79d34a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/model/HistoryLine.java @@ -0,0 +1,22 @@ +package dev.drawethree.xprison.history.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class HistoryLine { + + private UUID uuid; + private UUID playerUuid; + private String module; + private String context; + private Date createdAt; +} diff --git a/src/main/java/dev/drawethree/xprison/history/repo/HistoryRepository.java b/src/main/java/dev/drawethree/xprison/history/repo/HistoryRepository.java new file mode 100644 index 0000000..611ab62 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/repo/HistoryRepository.java @@ -0,0 +1,19 @@ +package dev.drawethree.xprison.history.repo; + +import dev.drawethree.xprison.history.model.HistoryLine; +import org.bukkit.OfflinePlayer; + +import java.util.List; + +public interface HistoryRepository { + + List getPlayerHistory(OfflinePlayer player); + + void addHistoryLine(OfflinePlayer player, HistoryLine history); + + void deleteHistory(OfflinePlayer target); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/history/repo/impl/HistoryRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/history/repo/impl/HistoryRepositoryImpl.java new file mode 100644 index 0000000..d9c3bb0 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/repo/impl/HistoryRepositoryImpl.java @@ -0,0 +1,83 @@ +package dev.drawethree.xprison.history.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.history.model.HistoryLine; +import dev.drawethree.xprison.history.repo.HistoryRepository; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +public final class HistoryRepositoryImpl implements HistoryRepository { + + private static final String TABLE_NAME = "UltraPrison_History"; + + private static final String HISTORY_UUID_COLNAME = "uuid"; + private static final String HISTORY_PLAYER_UUID_COLNAME = "player_uuid"; + private static final String HISTORY_MODULE_COLNAME = "module"; + private static final String HISTORY_CONTEXT_COLNAME = "context"; + private static final String HISTORY_CREATED_AT_COLNAME = "created_at"; + + private final SQLDatabase database; + + public HistoryRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public List getPlayerHistory(OfflinePlayer player) { + List returnList = new ArrayList<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con,"SELECT * FROM " + TABLE_NAME + " where ?=?")) { + statement.setString(1, HISTORY_PLAYER_UUID_COLNAME); + statement.setString(2, player.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + while (set.next()) { + + UUID recordId = UUID.fromString(set.getString(HISTORY_UUID_COLNAME)); + UUID playerUuid = UUID.fromString(set.getString(HISTORY_PLAYER_UUID_COLNAME)); + String moduleName = set.getString(HISTORY_MODULE_COLNAME); + String context = set.getString(HISTORY_CONTEXT_COLNAME); + Date createdAt = set.getDate(HISTORY_CREATED_AT_COLNAME); + + HistoryLine line = HistoryLine.builder() + .uuid(recordId) + .playerUuid(playerUuid) + .module(moduleName) + .context(context) + .createdAt(createdAt) + .build(); + returnList.add(line); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return returnList; + } + + @Override + public void addHistoryLine(OfflinePlayer player, HistoryLine history) { + this.database.executeSqlAsync("INSERT INTO " + TABLE_NAME + " values(?,?,?,?,?)", history.getUuid().toString(), history.getPlayerUuid().toString(), history.getModule(), history.getContext(), history.getCreatedAt()); + } + + @Override + public void deleteHistory(OfflinePlayer target) { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME + " where ?=?", HISTORY_PLAYER_UUID_COLNAME, target.getUniqueId().toString()); + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(uuid varchar(36) NOT NULL UNIQUE, player_uuid varchar(36) NOT NULL, module varchar(36) NOT NULL, context TEXT ,created_at DATETIME)"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME); + } +} diff --git a/src/main/java/dev/drawethree/xprison/history/service/HistoryService.java b/src/main/java/dev/drawethree/xprison/history/service/HistoryService.java new file mode 100644 index 0000000..a0a69b0 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/service/HistoryService.java @@ -0,0 +1,15 @@ +package dev.drawethree.xprison.history.service; + +import dev.drawethree.xprison.history.model.HistoryLine; +import org.bukkit.OfflinePlayer; + +import java.util.List; + +public interface HistoryService { + + List getPlayerHistory(OfflinePlayer player); + + void createHistoryLine(OfflinePlayer player, HistoryLine history); + + void deleteHistory(OfflinePlayer target); +} diff --git a/src/main/java/dev/drawethree/xprison/history/service/impl/HistoryServiceImpl.java b/src/main/java/dev/drawethree/xprison/history/service/impl/HistoryServiceImpl.java new file mode 100644 index 0000000..a71820b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/history/service/impl/HistoryServiceImpl.java @@ -0,0 +1,32 @@ +package dev.drawethree.xprison.history.service.impl; + +import dev.drawethree.xprison.history.model.HistoryLine; +import dev.drawethree.xprison.history.repo.HistoryRepository; +import dev.drawethree.xprison.history.service.HistoryService; +import org.bukkit.OfflinePlayer; + +import java.util.List; + +public class HistoryServiceImpl implements HistoryService { + + private final HistoryRepository repository; + + public HistoryServiceImpl(HistoryRepository repository) { + this.repository = repository; + } + + @Override + public List getPlayerHistory(OfflinePlayer player) { + return repository.getPlayerHistory(player); + } + + @Override + public void createHistoryLine(OfflinePlayer player, HistoryLine history) { + repository.addHistoryLine(player, history); + } + + @Override + public void deleteHistory(OfflinePlayer target) { + repository.deleteHistory(target); + } +} diff --git a/src/main/java/dev/drawethree/xprison/interfaces/Permissionable.java b/src/main/java/dev/drawethree/xprison/interfaces/Permissionable.java new file mode 100644 index 0000000..042ee1a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/interfaces/Permissionable.java @@ -0,0 +1,6 @@ +package dev.drawethree.xprison.interfaces; + +public interface Permissionable { + + String getRequiredPermission(); +} diff --git a/src/main/java/dev/drawethree/xprison/interfaces/UPCRepository.java b/src/main/java/dev/drawethree/xprison/interfaces/UPCRepository.java new file mode 100644 index 0000000..b873a73 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/interfaces/UPCRepository.java @@ -0,0 +1,8 @@ +package dev.drawethree.xprison.interfaces; + +public interface UPCRepository { + + void createTables(); + + void resetData(); +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/MainMenu.java b/src/main/java/dev/drawethree/xprison/mainmenu/MainMenu.java new file mode 100644 index 0000000..43b73b1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/MainMenu.java @@ -0,0 +1,139 @@ +package dev.drawethree.xprison.mainmenu; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.history.gui.PlayerHistoryGUI; +import dev.drawethree.xprison.mainmenu.reload.ReloadSelectionGui; +import dev.drawethree.xprison.mainmenu.reset.ResetSelectionGui; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.paginated.PaginatedGuiBuilder; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import me.lucko.helper.utils.Players; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Collections; +import java.util.stream.Collectors; + +public class MainMenu extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + private static final MenuScheme CONTENT = new MenuScheme() + .mask("000000000") + .mask("000111000") + .mask("001111100") + .mask("000111000") + .mask("000000000"); + + + private final XPrison core; + + public MainMenu(XPrison core, Player player) { + super(player, 5, "X-Prison - Main Menu"); + this.core = core; + } + + @Override + public void redraw() { + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + } + + //Information + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&e&lInformation").lore("&7Author: &f" + StringUtils.join(this.core.getDescription().getAuthors(), ", "), "&7Version: &f" + this.core.getDescription().getVersion()).build(() -> { + + })); + + //Reload + this.setItem(21, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&e&lReload Modules").lore("&7Click to reload specific module").build(() -> { + if (!this.getPlayer().hasPermission("xprison.mainmenu.reload")) { + return; + } + this.close(); + new ReloadSelectionGui(this.core, this.getPlayer()).open(); + })); + + //Debug + this.setItem(22, ItemStackBuilder.of(this.core.isDebugMode() ? SkullUtils.CHECK_SKULL.clone() : SkullUtils.CROSS_SKULL.clone()).name("&e&lDebug Mode: " + (this.core.isDebugMode() ? "&2&lON" : "&c&lOFF")).lore("&7Click to toggle debug mode.").build(() -> { + if (!this.getPlayer().hasPermission("xprison.mainmenu.debug")) { + return; + } + this.core.setDebugMode(!this.core.isDebugMode()); + this.redraw(); + })); + + //Reset Data + this.setItem(23, ItemStackBuilder.of(SkullUtils.DANGER_SKULL.clone()).name("&e&lReset Player Data").lore("&7Click to select which module data", "&7would you like to wipe.").build(() -> { + if (!this.getPlayer().hasPermission("xprison.mainmenu.reset")) { + return; + } + this.close(); + new ResetSelectionGui(this.core, this.getPlayer()).open(); + })); + + //Players History + this.setItem(31, ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name("&e&lPlayers History").lore("&7Click to see players history.").build(() -> { + if (!this.getPlayer().hasPermission("xprison.mainmenu.history")) { + return; + } + this.close(); + this.openHistorySelectorGui(); + })); + + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lClose").lore("&7Click to close the gui.").build(this::close)); + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + } + + private void openHistorySelectorGui() { + PaginatedGuiBuilder builder = PaginatedGuiBuilder.create(); + builder.lines(6); + builder.title("Select a player"); + builder.build(this.getPlayer(), gui -> Players.all().stream().map(p -> ItemStackBuilder.of(SkullUtils.createPlayerHead(p, p.getName(), Collections.singletonList("&7Click to view history of this player."))).build(() -> { + new PlayerHistoryGUI(this.getPlayer(), p, this.core.getHistory()).open(); + })).collect(Collectors.toList())).open(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/confirmation/ReloadModuleConfirmationGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/confirmation/ReloadModuleConfirmationGui.java new file mode 100644 index 0000000..8d8891f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/confirmation/ReloadModuleConfirmationGui.java @@ -0,0 +1,32 @@ +package dev.drawethree.xprison.mainmenu.confirmation; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.utils.gui.ConfirmationGui; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.entity.Player; + +public class ReloadModuleConfirmationGui extends ConfirmationGui { + + private final XPrisonModule module; + + public ReloadModuleConfirmationGui(Player player, XPrisonModule module) { + super(player, module == null ? "Reload all modules ?" : "Reload module " + module.getName() + "?"); + this.module = module; + } + + @Override + public void confirm(boolean confirm) { + if (confirm) { + if (module == null) { + XPrison.getInstance().getModules().forEach(module1 -> XPrison.getInstance().reloadModule(module1)); + XPrison.getInstance().getItemMigrator().reload(); + PlayerUtils.sendMessage(this.getPlayer(), "&aSuccessfully reloaded all modules."); + } else { + XPrison.getInstance().reloadModule(module); + PlayerUtils.sendMessage(this.getPlayer(), "&aSuccessfully reloaded &e&l" + this.module.getName() + " &amodule."); + } + } + this.close(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/confirmation/ResetModulePlayerDataConfirmationGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/confirmation/ResetModulePlayerDataConfirmationGui.java new file mode 100644 index 0000000..b40c1bb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/confirmation/ResetModulePlayerDataConfirmationGui.java @@ -0,0 +1,31 @@ +package dev.drawethree.xprison.mainmenu.confirmation; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.utils.gui.ConfirmationGui; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.entity.Player; + +public class ResetModulePlayerDataConfirmationGui extends ConfirmationGui { + private final XPrisonModule module; + + public ResetModulePlayerDataConfirmationGui(Player player, XPrisonModule module) { + super(player, module == null ? "Reset all player data ?" : "Reset " + module.getName() + " player data?"); + this.module = module; + } + + @Override + public void confirm(boolean confirm) { + if (confirm) { + if (module == null) { + XPrison.getInstance().getModules().forEach(XPrisonModule::resetPlayerData); + PlayerUtils.sendMessage(this.getPlayer(), "&aSuccessfully reset player data of all modules."); + } else { + module.resetPlayerData(); + PlayerUtils.sendMessage(this.getPlayer(), "&aSuccessfully reset player data of &e&l" + this.module.getName() + " &amodule."); + } + + } + this.close(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/AutoMinerHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/AutoMinerHelpGui.java new file mode 100644 index 0000000..72d3c79 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/AutoMinerHelpGui.java @@ -0,0 +1,94 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class AutoMinerHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public AutoMinerHelpGui(Player player) { + super(player, 5, "AutoMiner Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7An autominer lets your", + "&7players have an extra", + "&7source of income without", + "&7having to mine." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&f/autominer", + "&7Shows the remaining time", + "&7on a player's autominer.", + " ", + "&f/adminautominer give [player] [time] [DAYS/HOURS/MINUTES/SECONDS]", + "&7Give a player autominer time." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/AutoSellHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/AutoSellHelpGui.java new file mode 100644 index 0000000..cd4036d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/AutoSellHelpGui.java @@ -0,0 +1,105 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class AutoSellHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public AutoSellHelpGui(Player player) { + super(player, 5, "AutoSell Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7AutoSell lets your players", + "&7automatically sell any block", + "&7they mine in your set", + "&7mine regions." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&f/sellprice [price]", + "&7Set the price for any item", + "&7you are holding. Make sure to", + "&7stand in the correct region that", + "&7you want autosell and sellall to work in.", + " ", + "&f/autosell", + "&7Toggle your autosell mode.", + "&7If enabled, blocks mined will", + "&7be sold automatically.", + "&7If disabled, blocks mined will", + "&7go to your inventory.", + " ", + "&f/sellall", + "&7Instantly sell every item", + "&7that is in your inventory.", + "&7You need to be standing in", + "&7mine region." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/EnchantsHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/EnchantsHelpGui.java new file mode 100644 index 0000000..bc3336e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/EnchantsHelpGui.java @@ -0,0 +1,111 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class EnchantsHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public EnchantsHelpGui(Player player) { + super(player, 5, "Enchants Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Enchants are made", + "&7to make your prison server", + "&7more unique!", + "&7You can customize all", + "&7enchants in &fenchants.yml", + "&7You can also create your own", + "&7custom enchants using our API.", + "&7Tutorial can be found on Wiki." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&f/givepickaxe [player] [enchant:rawname=level]", + "&7Example: /givepickaxe Drawethree fortune=10,layer=50", + "&7Give your players a custom pickaxe to mine with.", + " ", + "&f/enchant", + "&7Open up the enchanting GUI.", + " ", + "&f/disenchant", + "&7Opens up the disenchanting GUI.", + " ", + "&f/value", + "&7Display the total value of tokens", + "&7of your held pickaxe.", + " ", + "&f/explosive", + "&fToggle the explosive enchant.", + " ", + "&f/layer", + "&7Toggle the layer enchant." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/GangsHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/GangsHelpGui.java new file mode 100644 index 0000000..df44ad8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/GangsHelpGui.java @@ -0,0 +1,116 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class GangsHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public GangsHelpGui(Player player) { + super(player, 5, "Gangs Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore("&7Gangs are a way to let", "&7your players play together in", "&7competitive groups!").buildItem().build()); + + //Commands + this.setItem(21, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&ePlayer Commands").lore( + "&f/gang create [name]", + "&7Create a gang.", + " ", + "&f/gang invite [player]", + "&7Invite a player to your gang.", + " ", + "&f/gang kick [player]", + "&7Kick a player for your gang.", + " ", + "&f/gang info [player/gang]", + "&7Shows information about gang.", + " ", + "&f/gang chat", + "&7Toggle gang chat.", + " ", + "&f/gang top", + "&7Display top gangs based on their value.", + " ", + "&f/gang accept", + "&7Accept a gang invite.", + " ", + "&f/gang disband", + "&7Disband your gang." + ).buildItem().build()); + + //Commands + this.setItem(23, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eAdmin Commands").lore( + "&f/gang value [add/remove] [gang] [amount]", + "&7Add/remove value from/to a gang.", + " ", + "&f/gang admin [add/remove] [player] [gang]", + "&7Forcibly add or remove player from a gang.", + " ", + "&f/gang admin disband [gang]", + "&7Forcibly disband a gang." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/GemsHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/GemsHelpGui.java new file mode 100644 index 0000000..ff929c9 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/GemsHelpGui.java @@ -0,0 +1,100 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class GemsHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public GemsHelpGui(Player player) { + super(player, 5, "Gems Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Gems allows you to create any", + "&7shop you want. Gems is a currency", + "&7with tons of uses!" + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&ePlayer Commands").lore( + "&f/gems [player]", + "&7View yours or player's balance.", + " ", + "&f/gemstop", + "&7View a list of players with most gems.", + " ", + "&f/gems [add/remove/set] [player] [amount]", + "&7Add, remove or set player's gems balance.", + " ", + "&f/gems pay [player] [amount]", + "&7Give an other player gems from your balance.", + " ", + "&f/gems help", + "&7Displays the help usage for gems." + ).buildItem().build()); + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/HelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/HelpGui.java new file mode 100644 index 0000000..770655c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/HelpGui.java @@ -0,0 +1,138 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class HelpGui extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + private static final MenuScheme CONTENT = new MenuScheme() + .mask("000000000") + .mask("000111000") + .mask("001111100") + .mask("000111000") + .mask("000000000"); + + + public HelpGui(Player player) { + super(player, 5, "X-Prison - Help Menu"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //AutoMiner + this.setItem(11, ItemStackBuilder.of(Material.DIAMOND_PICKAXE).name("&e&lAutoMiner").lore("&7Click to see detailed info", "&7about AutoMiner feature.").build(() -> { + this.close(); + new AutoMinerHelpGui(this.getPlayer()).open(); + })); + //Autosell + this.setItem(12, ItemStackBuilder.of(SkullUtils.MONEY_SKULL.clone()).name("&e&lAutoSell").lore("&7Click to see detailed info", "&7about AutoSell feature.").build(() -> { + this.close(); + new AutoSellHelpGui(this.getPlayer()).open(); + })); + //Enchants + this.setItem(13, ItemStackBuilder.of(CompMaterial.ENCHANTED_BOOK.toItem()).name("&e&lEnchants").lore("&7Click to see detailed info", "&7about Enchants feature.").build(() -> { + this.close(); + new EnchantsHelpGui(this.getPlayer()).open(); + })); + //Gangs + this.setItem(14, ItemStackBuilder.of(SkullUtils.GANG_SKULL.clone()).name("&e&lGangs").lore("&7Click to see detailed info", "&7about Gangs feature.").build(() -> { + this.close(); + new GangsHelpGui(this.getPlayer()).open(); + })); + //Gems + this.setItem(15, ItemStackBuilder.of(Material.EMERALD).name("&e&lGems").lore("&7Click to see detailed info", "&7about Gems feature.").build(() -> { + this.close(); + new GemsHelpGui(this.getPlayer()).open(); + })); + //Ranks + this.setItem(19, ItemStackBuilder.of(SkullUtils.DIAMOND_R_SKULL.clone()).name("&e&lRanks").lore("&7Click to see detailed info", "&7about Ranks feature.").build(() -> { + this.close(); + new RanksHelpGui(this.getPlayer()).open(); + })); + //Prestiges + this.setItem(20, ItemStackBuilder.of(SkullUtils.DIAMOND_P_SKULL.clone()).name("&e&lPrestiges").lore("&7Click to see detailed info", "&7about Prestiges feature.").build(() -> { + this.close(); + new PrestigesHelpGui(this.getPlayer()).open(); + })); + //PickaxeLevels + this.setItem(21, ItemStackBuilder.of(CompMaterial.EXPERIENCE_BOTTLE.toItem()).name("&e&lPickaxe Levels").lore("&7Click to see detailed info", "&7about Pickaxe leveling feature.").build(() -> { + this.close(); + new PickaxeLevelsHelpGui(this.getPlayer()).open(); + })); + //Tokens + this.setItem(22, ItemStackBuilder.of(SkullUtils.COIN_SKULL.clone()).name("&e&lTokens").lore("&7Click to see detailed info", "&7about Tokens feature.").build(() -> { + this.close(); + new TokensHelpGui(this.getPlayer()).open(); + })); + //Multipliers + this.setItem(23, ItemStackBuilder.of(Material.GOLD_INGOT).name("&e&lMultipliers").lore("&7Click to see detailed info", "&7about Multipliers feature.").build(() -> { + this.close(); + new MultipliersHelpGui(this.getPlayer()).open(); + })); + //Mines + this.setItem(24, ItemStackBuilder.of(Material.DIAMOND_ORE).name("&e&lMines").lore("&7Click to see detailed info", "&7about Mines feature.").build(() -> { + this.close(); + new MinesHelpGui(this.getPlayer()).open(); + })); + //History + this.setItem(25, ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name("&e&lHistory").lore("&7Click to see detailed info", "&7about History feature.").build(() -> { + this.close(); + new HistoryHelpGui(this.getPlayer()).open(); + })); + + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lClose").lore("&7Click to close the gui.").build(this::close)); + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/HistoryHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/HistoryHelpGui.java new file mode 100644 index 0000000..08cc353 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/HistoryHelpGui.java @@ -0,0 +1,95 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class HistoryHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public HistoryHelpGui(Player player) { + super(player, 5, "Tokens Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7History module allows you to", + "&7keep a track up how your players", + "&7how they are interacting with our system.", + " ", + "&7History system allows you lookup", + "&7overall player data, or filter it by module.", + " ", + "&7System is GUI-based, you can open the History GUI", + "&7'/upc' command and clicking on book item, or via", + "&7'/history ' command." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&f/history [player]", + "&7Views player's history." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/MinesHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/MinesHelpGui.java new file mode 100644 index 0000000..1cc787b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/MinesHelpGui.java @@ -0,0 +1,131 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MinesHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public MinesHelpGui(Player player) { + super(player, 5, "Mines Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7With Mines module", + "&7you can create unlimited", + "&7amount of prison mines", + "&7where your players can", + "&7mine blocks and progress." + ).buildItem().build()); + + //Commands + this.setItem(21, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eAdmin Commands").lore( + "&f/mines create [name]", + "&7Create a mine.", + " ", + "&f/mines delete [name]", + "&7Deletes a mine.", + " ", + "&f/mines redefine [name]", + "&7Redefine mine region.", + " ", + "&f/mines rename [name] [new_name]", + "&7Renames a mine.", + " ", + "&f/mines reset [name]", + "&7Resets a mine contents.", + " ", + "&f/mines panel [name]", + "&7Opens admin panel for a mine.", + " ", + "&f/mines save [name]", + "&7Saves a mine." + ).buildItem().build()); + + //Commands + this.setItem(23, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eAdmin Commands").lore( + "&f/mines addblock [name]", + "&7Adds a block to a mine you hold in hand.", + " ", + "&f/mines settp [name]", + "&7Sets teleport location for a mine.", + " ", + "&f/mines tp [name]", + "&7Teleports to a mine.", + " ", + "&f/mines list", + "&7Shows all mines.", + " ", + "&f/mines tool", + "&7Gives you a mine selection tool.", + " ", + "&f/mines migrate ", + "&7Migrates mines from other plugins.", + "&7Currently supported: &fJetsPrisonMines, MineResetLite&7." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/MultipliersHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/MultipliersHelpGui.java new file mode 100644 index 0000000..1b5a7be --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/MultipliersHelpGui.java @@ -0,0 +1,102 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MultipliersHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public MultipliersHelpGui(Player player) { + super(player, 5, "Multipliers Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Multipliers allows your players", + "&7to earn more money when they", + "&7are mining." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&f/gmulti [sell/token] [multiplier] [duration] [time_unit] ", + "&7Example: /gmulti sell 1.0 10 MINUTES", + "&7Set the global sell / token multiplier for all players on server.", + " ", + "&f/gmulti [sell/token] reset", + "&7Resets the global sell / token multiplier.", + " ", + "&f/sellmulti [player] [multiplier] [duration] [time_unit] ", + "&7Example: /sellmulti Drawethree 1.0 10 MINUTES", + "&7Set the personal sell multiplier for player.", + " ", + "&f/tokenmulti [player] [multiplier] [duration] [time_unit] ", + "&7Example: /tokenmulti Drawethree 1.0 10 MINUTES", + "&7Set the personal token multiplier for player.", + " ", + "&f/multi", + "&7Check your active multipliers." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/PickaxeLevelsHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/PickaxeLevelsHelpGui.java new file mode 100644 index 0000000..c8c9027 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/PickaxeLevelsHelpGui.java @@ -0,0 +1,88 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class PickaxeLevelsHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public PickaxeLevelsHelpGui(Player player) { + super(player, 5, "Pickaxe Levels Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Pickaxe levels allows your", + "&7players to level up their pickaxes", + "&7and unlock various rewards by", + "&7breaking blocks." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&cThere are no commands associated with this module.", + "&cEverything is customizable in &fpickaxe-levels.yml" + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/PrestigesHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/PrestigesHelpGui.java new file mode 100644 index 0000000..10488b7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/PrestigesHelpGui.java @@ -0,0 +1,97 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class PrestigesHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public PrestigesHelpGui(Player player) { + super(player, 5, "Prestiges Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Prestiges allow your players", + "&7to earn more rewards after they", + "&7achieve highest rank!" + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&ePlayer Commands").lore( + "&f/prestige", + "&7Attempt to buy next prestige.", + " ", + "&f/maxprestige", + "&7Attempt to buy highest prestige possible", + "&7based on your balance.", + " ", + "&f/prestigetop", + "&7Display top prestige users.", + " ", + "&f/prestigeadmin [add/remove/set] [player] [amount]", + "&7Forcibly add, remove or set player's prestige." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/RanksHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/RanksHelpGui.java new file mode 100644 index 0000000..e403c3c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/RanksHelpGui.java @@ -0,0 +1,94 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class RanksHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public RanksHelpGui(Player player) { + super(player, 5, "Ranks Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Ranks allow your players", + "&7to advance to different mines", + "&7and earn custom rewards!" + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&ePlayer Commands").lore( + "&f/rankup", + "&7Attempt to buy next rank.", + " ", + "&f/maxrankup", + "&7Attempt to buy highest rank possible", + "&7based on your balance.", + " ", + "&f/setrank [player] [rank_id]", + "&7Forcibly set player's rank." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/help/TokensHelpGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/help/TokensHelpGui.java new file mode 100644 index 0000000..d95ea91 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/help/TokensHelpGui.java @@ -0,0 +1,103 @@ +package dev.drawethree.xprison.mainmenu.help; + +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class TokensHelpGui extends Gui { + + protected static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + protected static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + + public TokensHelpGui(Player player) { + super(player, 5, "Tokens Help"); + } + + @Override + public void redraw() { + + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //Info + this.setItem(13, ItemStackBuilder.of(SkullUtils.INFO_SKULL.clone()).name("&eWhat it is ?").lore( + "&7Tokens is a currency that allows", + "&7your players to upgrade their", + "&7pickaxe enchants." + ).buildItem().build()); + + //Commands + this.setItem(22, ItemStackBuilder.of(SkullUtils.COMMAND_BLOCK_SKULL.clone()).name("&eCommands").lore( + "&f/tokens [player]", + "&7View yours or player's tokens balance.", + " ", + "&f/tokenstop", + "&7View a list of players with most tokens.", + " ", + "&f/tokens [add/remove/set] [player] [amount]", + "&7Add, remove or set player's tokens balance.", + " ", + "&f/tokens pay [player] [amount]", + "&7Give an other player tokens from your balance.", + " ", + "&f/tokens withdraw [amount] [value]", + "&7Withdraw your tokens to a physical form.", + " ", + "&f/tokens help", + "&7Displays the help usage for tokens." + ).buildItem().build()); + + //Back + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lBack").lore("&7Back to main gui.").build(() -> { + this.close(); + new HelpGui(this.getPlayer()).open(); + })); + + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + } + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/reload/ReloadSelectionGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/reload/ReloadSelectionGui.java new file mode 100644 index 0000000..30aa943 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/reload/ReloadSelectionGui.java @@ -0,0 +1,145 @@ +package dev.drawethree.xprison.mainmenu.reload; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.mainmenu.confirmation.ReloadModuleConfirmationGui; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class ReloadSelectionGui extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + private static final MenuScheme CONTENT = new MenuScheme() + .mask("000000000") + .mask("000111000") + .mask("001111100") + .mask("000111000") + .mask("000000000"); + + private final XPrison core; + + public ReloadSelectionGui(XPrison core, Player player) { + super(player, 5, "UPC - Reload Module"); + this.core = core; + } + + @Override + public void redraw() { + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //AutoMiner + this.setItem(11, ItemStackBuilder.of(Material.DIAMOND_PICKAXE).name("&e&lAutoMiner").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getAutoMiner()).open(); + })); + + //Autosell + this.setItem(12, ItemStackBuilder.of(SkullUtils.MONEY_SKULL.clone()).name("&e&lAutoSell").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getAutoSell()).open(); + })); + + //Enchants + this.setItem(13, ItemStackBuilder.of(CompMaterial.ENCHANTED_BOOK.toItem()).name("&e&lEnchants").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getEnchants()).open(); + })); + + //Gangs + this.setItem(14, ItemStackBuilder.of(SkullUtils.GANG_SKULL.clone()).name("&e&lGangs").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getGangs()).open(); + })); + + //Gems + this.setItem(15, ItemStackBuilder.of(Material.EMERALD).name("&e&lGems").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getGems()).open(); + })); + + //Ranks + this.setItem(19, ItemStackBuilder.of(SkullUtils.DIAMOND_R_SKULL.clone()).name("&e&lRanks").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getRanks()).open(); + })); + + //Prestiges + this.setItem(20, ItemStackBuilder.of(SkullUtils.DIAMOND_P_SKULL.clone()).name("&e&lPrestiges").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getPrestiges()).open(); + })); + + //PickaxeLevels + this.setItem(21, ItemStackBuilder.of(CompMaterial.EXPERIENCE_BOTTLE.toItem()).name("&e&lPickaxe Levels").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getPickaxeLevels()).open(); + })); + + //Tokens + this.setItem(22, ItemStackBuilder.of(SkullUtils.COIN_SKULL.clone()).name("&e&lTokens").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getTokens()).open(); + })); + + //Multipliers + this.setItem(23, ItemStackBuilder.of(Material.GOLD_INGOT).name("&e&lMultipliers").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getMultipliers()).open(); + })); + + //Mines + this.setItem(24, ItemStackBuilder.of(Material.DIAMOND_ORE).name("&e&lMines").lore("&7Click to reload this module.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getMines()).open(); + })); + + //History + this.setItem(25, ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name("&e&lHistory").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), this.core.getHistory()).open(); + })); + + //All Modules + this.setItem(31, ItemStackBuilder.of(CompMaterial.REDSTONE_BLOCK.toItem()).name("&e&lReload All Modules").lore("&7Click to reload all modules.").build(() -> { + new ReloadModuleConfirmationGui(this.getPlayer(), null).open(); + })); + + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lClose").lore("&7Click to close the gui.").build(this::close)); + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mainmenu/reset/ResetSelectionGui.java b/src/main/java/dev/drawethree/xprison/mainmenu/reset/ResetSelectionGui.java new file mode 100644 index 0000000..38d9284 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mainmenu/reset/ResetSelectionGui.java @@ -0,0 +1,146 @@ +package dev.drawethree.xprison.mainmenu.reset; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.mainmenu.confirmation.ResetModulePlayerDataConfirmationGui; +import dev.drawethree.xprison.utils.Constants; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.misc.SkullUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class ResetSelectionGui extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + private static final MenuScheme CONTENT = new MenuScheme() + .mask("000000000") + .mask("000111000") + .mask("001111100") + .mask("000111000") + .mask("000000000"); + + private final XPrison core; + + public ResetSelectionGui(XPrison core, Player player) { + super(player, 5, "UPC - Reset Player Data"); + this.core = core; + + } + + @Override + public void redraw() { + if (isFirstDraw()) { + + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + //AutoMiner + this.setItem(11, ItemStackBuilder.of(Material.DIAMOND_PICKAXE).name("&e&lAutoMiner").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getAutoMiner()).open(); + })); + + //Autosell + this.setItem(12, ItemStackBuilder.of(SkullUtils.MONEY_SKULL.clone()).name("&e&lAutoSell").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getAutoSell()).open(); + })); + + //Enchants + this.setItem(13, ItemStackBuilder.of(CompMaterial.ENCHANTED_BOOK.toItem()).name("&e&lEnchants").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getEnchants()).open(); + })); + + //Gangs + this.setItem(14, ItemStackBuilder.of(SkullUtils.GANG_SKULL.clone()).name("&e&lGangs").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getGangs()).open(); + })); + + //Gems + this.setItem(15, ItemStackBuilder.of(Material.EMERALD).name("&e&lGems").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getGems()).open(); + })); + + //Ranks + this.setItem(19, ItemStackBuilder.of(SkullUtils.DIAMOND_R_SKULL.clone()).name("&e&lRanks").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getRanks()).open(); + })); + + //Prestiges + this.setItem(20, ItemStackBuilder.of(SkullUtils.DIAMOND_P_SKULL.clone()).name("&e&lPrestiges").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getPrestiges()).open(); + })); + + //PickaxeLevels + this.setItem(21, ItemStackBuilder.of(CompMaterial.EXPERIENCE_BOTTLE.toItem()).name("&e&lPickaxe Levels").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getPickaxeLevels()).open(); + })); + + //Tokens + this.setItem(22, ItemStackBuilder.of(SkullUtils.COIN_SKULL.clone()).name("&e&lTokens").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getTokens()).open(); + })); + + //Multipliers + this.setItem(23, ItemStackBuilder.of(Material.GOLD_INGOT).name("&e&lMultipliers").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getMultipliers()).open(); + })); + + //Mines + this.setItem(24, ItemStackBuilder.of(Material.DIAMOND_ORE).name("&e&lMines").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getMines()).open(); + })); + + //History + this.setItem(25, ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name("&e&lHistory").lore("&7Click to reset this module", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), this.core.getHistory()).open(); + })); + + //All Modules + this.setItem(31, ItemStackBuilder.of(CompMaterial.REDSTONE_BLOCK.toItem()).name("&e&lReset All Modules").lore("&7Click to reset all modules", "&7player data.").build(() -> { + new ResetModulePlayerDataConfirmationGui(this.getPlayer(), null).open(); + })); + + this.setItem(36, ItemStackBuilder.of(Material.BARRIER).name("&c&lClose").lore("&7Click to close the gui.").build(this::close)); + this.setItem(44, ItemStackBuilder.of(SkullUtils.HELP_SKULL.clone()).name("&e&lNeed more help?").lore("&7Right-Click to see plugin's Wiki", "&7Left-Click to join Discord Support.") + .build(() -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Wiki"); + PlayerUtils.sendMessage(this.getPlayer(), "&7https://github.com/Drawethree/X-Prison/wiki"); + PlayerUtils.sendMessage(this.getPlayer(), " "); + }, () -> { + this.close(); + PlayerUtils.sendMessage(this.getPlayer(), " "); + PlayerUtils.sendMessage(this.getPlayer(), "&eX-Prison - Discord"); + PlayerUtils.sendMessage(this.getPlayer(), "&7" + Constants.DISCORD_LINK); + PlayerUtils.sendMessage(this.getPlayer(), " "); + })); + + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/migrator/ItemMigrator.java b/src/main/java/dev/drawethree/xprison/migrator/ItemMigrator.java new file mode 100644 index 0000000..131d529 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/migrator/ItemMigrator.java @@ -0,0 +1,126 @@ +package dev.drawethree.xprison.migrator; + +import com.saicone.rtag.RtagItem; +import com.saicone.rtag.tag.TagCompound; +import dev.drawethree.xprison.XPrison; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.HashSet; +import java.util.Map; + +public class ItemMigrator implements Listener { + + private static final String MAIN = "upc"; + private static final String ENCHANT_PATH = "ultra-prison-ench-"; + private static final Map PATHS = Map.of( + "blocks-broken", "blocks", + "gems-amount", "gems", + "ultra-prison-pickaxe-level", "level", + "token-amount", "tokens" + ); + + private final XPrison plugin; + + private boolean enabled; + private boolean bindJoin; + private boolean bindInventory; + + public ItemMigrator(XPrison plugin) { + this.plugin = plugin; + } + + public void reload() { + final boolean enabled = plugin.getConfig().getBoolean("item-migrator.enabled", true); + if (enabled) { + if (!this.enabled) { + this.enabled = true; + Bukkit.getPluginManager().registerEvents(this, plugin); + } + } else if (this.enabled) { + this.enabled = false; + HandlerList.unregisterAll(this); + return; + } + this.bindJoin = plugin.getConfig().getBoolean("item-migrator.bind.join", true); + this.bindInventory = plugin.getConfig().getBoolean("item-migrator.bind.inventory", false); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onJoin(PlayerJoinEvent e) { + if (!bindJoin) { + return; + } + Bukkit.getScheduler().runTaskLater(plugin, () -> { + final Player player = e.getPlayer(); + if (!player.isOnline()) { + return; + } + final Inventory inventory = player.getInventory(); + try { + for (int i = 0; i < inventory.getContents().length; i++) { + final ItemStack item = migrate(inventory.getItem(i)); + if (item != null) { + inventory.setItem(i, item); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + }, 80L); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onClickInventory(InventoryClickEvent e) { + if (!bindInventory) { + return; + } + try { + final ItemStack item = migrate(e.getCurrentItem()); + if (item != null) { + e.setCancelled(true); + e.setCurrentItem(item); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public ItemStack migrate(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return null; + } + final RtagItem tag = new RtagItem(item); + if (tag.notHasTag()) { + return null; + } + final Map map = TagCompound.getValue(tag.getTag()); + boolean edited = false; + for (String key : new HashSet<>(map.keySet())) { + if (PATHS.containsKey(key)) { + final Object value = map.get(key); + map.remove(key); + tag.set(value, MAIN, PATHS.get(key)); + edited = true; + } else if (key.startsWith(ENCHANT_PATH)) { + final Object value = map.get(key); + map.remove(key); + tag.set(value, MAIN, "enchants", key.substring(18)); + edited = true; + } + } + if (edited) { + return tag.loadCopy(); + } + return null; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/XPrisonMines.java b/src/main/java/dev/drawethree/xprison/mines/XPrisonMines.java new file mode 100644 index 0000000..b310051 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/XPrisonMines.java @@ -0,0 +1,169 @@ +package dev.drawethree.xprison.mines; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.mines.api.XPrisonMinesAPI; +import dev.drawethree.xprison.mines.api.XPrisonMinesAPIImpl; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.commands.impl.*; +import dev.drawethree.xprison.mines.listener.MinesListener; +import dev.drawethree.xprison.mines.managers.MineManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class XPrisonMines implements XPrisonModule { + + public static final String MODULE_NAME = "Mines"; + + public static final String MINES_ADMIN_PERM = "xprison.mines.admin"; + + @Getter + private static XPrisonMines instance; + + private boolean enabled; + + private Map messages; + @Getter + private FileManager.Config config; + @Getter + private MineManager manager; + @Getter + private XPrisonMinesAPI api; + @Getter + private final XPrison core; + + private Map commands; + + + public XPrisonMines(XPrison core) { + instance = this; + this.core = core; + this.enabled = false; + } + + @Override + public void enable() { + this.enabled = true; + this.config = this.core.getFileManager().getConfig("mines.yml").copyDefaults(true).save(); + this.loadMessages(); + this.manager = new MineManager(this); + this.manager.enable(); + new MinesListener(this).register(); + this.registerCommands(); + this.api = new XPrisonMinesAPIImpl(this); + } + + @Override + public void disable() { + this.enabled = false; + this.manager.disable(); + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + @Override + public void reload() { + this.config.reload(); + + this.loadMessages(); + + this.manager.reload(); + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return false; + } + + @Override + public void resetPlayerData() { + } + + private void loadMessages() { + this.messages = new HashMap<>(); + for (String key : this.config.get().getConfigurationSection("messages").getKeys(false)) { + this.messages.put(key.toLowerCase(), TextUtils.applyColor(this.config.get().getString("messages." + key))); + } + } + + public String getMessage(String key) { + return this.messages.getOrDefault(key.toLowerCase(), TextUtils.applyColor("&cInvalid message key: " + key)); + } + + private void registerCommands() { + this.commands = new HashMap<>(); + + registerCommand(new MineCreateCommand(this)); + registerCommand(new MineDeleteCommand(this)); + registerCommand(new MineRedefineCommand(this)); + registerCommand(new MinePanelCommand(this)); + registerCommand(new MineTeleportCommand(this)); + registerCommand(new MineToolCommand(this)); + registerCommand(new MineHelpCommand(this)); + registerCommand(new MineResetCommand(this)); + registerCommand(new MineListCommand(this)); + registerCommand(new MineAddBlockCommand(this)); + registerCommand(new MineSetTpCommand(this)); + registerCommand(new MineSaveCommand(this)); + registerCommand(new MineMigrateCommand(this)); + registerCommand(new MineRenameCommand(this)); + + Commands.create() + .handler(c -> { + + if (c.args().size() == 0 && c.sender() instanceof Player) { + this.getCommand("help").execute(c.sender(), c.args()); + return; + } + + MineCommand subCommand = this.getCommand(Objects.requireNonNull(c.rawArg(0))); + + if (subCommand != null) { + if (!subCommand.canExecute(c.sender())) { + PlayerUtils.sendMessage(c.sender(), this.getMessage("no_permission")); + return; + } + + if (!subCommand.execute(c.sender(), c.args().subList(1, c.args().size()))) { + PlayerUtils.sendMessage(c.sender(), subCommand.getUsage()); + } + + } else { + this.getCommand("help").execute(c.sender(), c.args()); + } + }).registerAndBind(core, "mines", "mine"); + } + + private MineCommand getCommand(String name) { + return this.commands.get(name.toLowerCase()); + } + + + private void registerCommand(MineCommand command) { + this.commands.put(command.getName(), command); + + if (command.getAliases() == null || command.getAliases().length == 0) { + return; + } + + for (String alias : command.getAliases()) { + this.commands.put(alias, command); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/api/XPrisonMinesAPI.java b/src/main/java/dev/drawethree/xprison/mines/api/XPrisonMinesAPI.java new file mode 100644 index 0000000..ba6c781 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/api/XPrisonMinesAPI.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.mines.api; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import org.bukkit.Location; + +public interface XPrisonMinesAPI { + + /** + * Gets a mine by name + * + * @param name String + * @return Mine.class + */ + Mine getMineByName(String name); + + /** + * Gets a mine by location + * + * @param loc Location + * @return Mine.class + */ + Mine getMineAtLocation(Location loc); +} diff --git a/src/main/java/dev/drawethree/xprison/mines/api/XPrisonMinesAPIImpl.java b/src/main/java/dev/drawethree/xprison/mines/api/XPrisonMinesAPIImpl.java new file mode 100644 index 0000000..ceff1ee --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/api/XPrisonMinesAPIImpl.java @@ -0,0 +1,24 @@ +package dev.drawethree.xprison.mines.api; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.model.mine.Mine; +import org.bukkit.Location; + +public final class XPrisonMinesAPIImpl implements XPrisonMinesAPI { + + private final XPrisonMines plugin; + + public XPrisonMinesAPIImpl(XPrisonMines plugin) { + this.plugin = plugin; + } + + @Override + public Mine getMineByName(String name) { + return this.plugin.getManager().getMineByName(name); + } + + @Override + public Mine getMineAtLocation(Location loc) { + return this.plugin.getManager().getMineAtLocation(loc); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/api/events/MineCreateEvent.java b/src/main/java/dev/drawethree/xprison/mines/api/events/MineCreateEvent.java new file mode 100644 index 0000000..c9c62ec --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/api/events/MineCreateEvent.java @@ -0,0 +1,47 @@ +package dev.drawethree.xprison.mines.api.events; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import dev.drawethree.xprison.mines.model.mine.Mine; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class MineCreateEvent extends XPrisonEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + @Getter + @Setter + private boolean cancelled; + + @Getter + private CommandSender creator; + + @Getter + private Mine mine; + + /** + * Fired when mine is created + * + * @param creator CommandSender who created the mine + * @param mine Mine + */ + public MineCreateEvent(Player creator, Mine mine) { + this.creator = creator; + this.mine = mine; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/api/events/MineDeleteEvent.java b/src/main/java/dev/drawethree/xprison/mines/api/events/MineDeleteEvent.java new file mode 100644 index 0000000..c7a4c37 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/api/events/MineDeleteEvent.java @@ -0,0 +1,40 @@ +package dev.drawethree.xprison.mines.api.events; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import dev.drawethree.xprison.mines.model.mine.Mine; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class MineDeleteEvent extends XPrisonEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + @Getter + @Setter + private boolean cancelled; + + @Getter + private Mine mine; + + /** + * Called when mine is deleted + * + * @param mine MIne + */ + public MineDeleteEvent(Mine mine) { + this.mine = mine; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/api/events/MinePostResetEvent.java b/src/main/java/dev/drawethree/xprison/mines/api/events/MinePostResetEvent.java new file mode 100644 index 0000000..9a18575 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/api/events/MinePostResetEvent.java @@ -0,0 +1,34 @@ +package dev.drawethree.xprison.mines.api.events; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import dev.drawethree.xprison.mines.model.mine.Mine; +import lombok.Getter; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class MinePostResetEvent extends XPrisonEvent { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + @Getter + private Mine mine; + + /** + * Fired when mine reset was completed + * + * @param mine Mine + */ + public MinePostResetEvent(Mine mine) { + this.mine = mine; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/api/events/MinePreResetEvent.java b/src/main/java/dev/drawethree/xprison/mines/api/events/MinePreResetEvent.java new file mode 100644 index 0000000..6bd4bfa --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/api/events/MinePreResetEvent.java @@ -0,0 +1,40 @@ +package dev.drawethree.xprison.mines.api.events; + +import dev.drawethree.xprison.api.events.XPrisonEvent; +import dev.drawethree.xprison.mines.model.mine.Mine; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class MinePreResetEvent extends XPrisonEvent implements Cancellable { + + private static final HandlerList HANDLERS_LIST = new HandlerList(); + + @Getter + private Mine mine; + + @Getter + @Setter + private boolean cancelled; + + /** + * Fired when mine is going to be reset + * + * @param mine Mine + */ + public MinePreResetEvent(Mine mine) { + this.mine = mine; + } + + public static HandlerList getHandlerList() { + return HANDLERS_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS_LIST; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/MineCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/MineCommand.java new file mode 100644 index 0000000..268ad20 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/MineCommand.java @@ -0,0 +1,28 @@ +package dev.drawethree.xprison.mines.commands; + +import dev.drawethree.xprison.mines.XPrisonMines; +import lombok.Getter; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public abstract class MineCommand { + + protected XPrisonMines plugin; + @Getter + private final String name; + @Getter + private final String[] aliases; + + public MineCommand(XPrisonMines plugin, String name, String... aliases) { + this.plugin = plugin; + this.name = name; + this.aliases = aliases; + } + + public abstract boolean execute(CommandSender sender, List args); + + public abstract String getUsage(); + + public abstract boolean canExecute(CommandSender sender); +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineAddBlockCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineAddBlockCommand.java new file mode 100644 index 0000000..d2b055d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineAddBlockCommand.java @@ -0,0 +1,60 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class MineAddBlockCommand extends MineCommand { + + public MineAddBlockCommand(XPrisonMines plugin) { + super(plugin, "addblock", "blockadd"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (!(sender instanceof Player)) { + return false; + } + + if (args.size() != 1) { + return false; + } + + Mine mine = this.plugin.getManager().getMineByName(args.get(0)); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", args.get(0))); + return true; + } + + ItemStack inHand = ((Player) sender).getItemInHand(); + if (inHand == null || inHand.getType() == Material.AIR) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_no_item_in_hand")); + return true; + } + + CompMaterial material = CompMaterial.fromItem(inHand); + mine.getBlockPalette().addToPalette(material, 0.0); + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_block_added").replace("%block%", material.name()).replace("%mine%", mine.getName())); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines addblock - Adds a block in your hand to the specified mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineCreateCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineCreateCommand.java new file mode 100644 index 0000000..70cdd35 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineCreateCommand.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineCreateCommand extends MineCommand { + + public MineCreateCommand(XPrisonMines plugin) { + super(plugin, "create", "new"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() != 1) { + return false; + } + if (!(sender instanceof Player)) { + return false; + } + + this.plugin.getManager().createMine((Player) sender, args.get(0)); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines create - Creates a new mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineDeleteCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineDeleteCommand.java new file mode 100644 index 0000000..a6ae2c3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineDeleteCommand.java @@ -0,0 +1,35 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public class MineDeleteCommand extends MineCommand { + + public MineDeleteCommand(XPrisonMines plugin) { + super(plugin, "delete", "remove"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (args.size() != 1) { + return false; + } + + this.plugin.getManager().deleteMine(sender, args.get(0)); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines delete - Delete a mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineHelpCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineHelpCommand.java new file mode 100644 index 0000000..93b7a8d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineHelpCommand.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public class MineHelpCommand extends MineCommand { + + public MineHelpCommand(XPrisonMines plugin) { + super(plugin, "help", "?"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM)) { + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + PlayerUtils.sendMessage(sender, "&e&lMINES ADMIN HELP MENU "); + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + PlayerUtils.sendMessage(sender, "&e/mines create [mine] - Creates a new mine"); + PlayerUtils.sendMessage(sender, "&e/mines delete [mine] - Deletes a mine"); + PlayerUtils.sendMessage(sender, "&e/mines redefine [mine] - Redefine mine region for a mine"); + PlayerUtils.sendMessage(sender, "&e/mines rename [mine] [new_name] - Renames a mine"); + PlayerUtils.sendMessage(sender, "&e/mines reset [mine/all] - Reset a mine"); + PlayerUtils.sendMessage(sender, "&e/mines panel [mine] - Opens a Mine Panel"); + PlayerUtils.sendMessage(sender, "&e/mines save [mine] - Force-save a mine"); + PlayerUtils.sendMessage(sender, "&e/mines addblock [mine] - Adds a block you hold in hand to a mine"); + PlayerUtils.sendMessage(sender, "&e/mines settp [mine] - Sets teleport location of a mine"); + PlayerUtils.sendMessage(sender, "&e/mines tp [mine] - Teleports to a mine"); + PlayerUtils.sendMessage(sender, "&e/mines list - Shows all Mines"); + PlayerUtils.sendMessage(sender, "&e/mines tool - Gives you a selection tool"); + PlayerUtils.sendMessage(sender, "&e/mines migrate - Migrate mines from other plugins. [JetsPrisonMines,MineResetLite]"); + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + } + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines help - Shows usage"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineListCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineListCommand.java new file mode 100644 index 0000000..52ed1fd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineListCommand.java @@ -0,0 +1,40 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineListCommand extends MineCommand { + + public MineListCommand(XPrisonMines plugin) { + super(plugin, "list"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player) { + this.plugin.getManager().openMinesListGUI((Player) sender); + } else { + PlayerUtils.sendMessage(sender, "All mines:"); + for (Mine mine : this.plugin.getManager().getMines()) { + PlayerUtils.sendMessage(sender, mine.getName()); + } + } + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines list - Display all mines"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineMigrateCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineMigrateCommand.java new file mode 100644 index 0000000..aedd2a7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineMigrateCommand.java @@ -0,0 +1,36 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.migration.utils.MinesMigrationUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public final class MineMigrateCommand extends MineCommand { + + public MineMigrateCommand(XPrisonMines plugin) { + super(plugin, "migrate"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (sender instanceof Player) { + MinesMigrationUtils.openAllMinesMigrationGui((Player) sender); + } else { + sender.sendMessage("This command can only be executed from game."); + } + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines migrate - Opens Migration GUI"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MinePanelCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MinePanelCommand.java new file mode 100644 index 0000000..ce42cef --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MinePanelCommand.java @@ -0,0 +1,50 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.gui.MinePanelGUI; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MinePanelCommand extends MineCommand { + + public MinePanelCommand(XPrisonMines plugin) { + super(plugin, "panel", "editor"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (!(sender instanceof Player)) { + return false; + } + + if (args.size() != 1) { + return false; + } + + Mine mine = this.plugin.getManager().getMineByName(args.get(0)); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", args.get(0))); + return true; + } + + new MinePanelGUI(mine, (Player) sender).open(); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines panel - Opens a editor for a specified mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineRedefineCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineRedefineCommand.java new file mode 100644 index 0000000..cd36c29 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineRedefineCommand.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineRedefineCommand extends MineCommand { + + public MineRedefineCommand(XPrisonMines plugin) { + super(plugin, "redefine"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() != 1) { + return false; + } + if (!(sender instanceof Player)) { + return false; + } + + this.plugin.getManager().redefineMine((Player) sender, args.get(0)); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines redefine - Redefines region for a mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineRenameCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineRenameCommand.java new file mode 100644 index 0000000..f85d5d4 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineRenameCommand.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineRenameCommand extends MineCommand { + + public MineRenameCommand(XPrisonMines plugin) { + super(plugin, "rename"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (args.size() != 2) { + return false; + } + + if (!(sender instanceof Player)) { + return false; + } + + this.plugin.getManager().renameMine((Player) sender, args.get(0), args.get(1)); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines rename - Renames a mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineResetCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineResetCommand.java new file mode 100644 index 0000000..894f59c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineResetCommand.java @@ -0,0 +1,56 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public class MineResetCommand extends MineCommand { + + public MineResetCommand(XPrisonMines plugin) { + super(plugin, "reset"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (args.size() != 1) { + return false; + } + + if ("all".equalsIgnoreCase(args.get(0))) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_all_reset_started")); + this.plugin.getManager().resetAllMines(); + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_all_reset_success")); + return true; + } + + Mine mine = this.plugin.getManager().getMineByName(args.get(0)); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", args.get(0))); + return true; + } + + if (mine.isResetting()) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_already_reset").replace("%mine%", args.get(0))); + return true; + } + + this.plugin.getManager().resetMine(mine); + return true; + } + + @Override + public String getUsage() { + return "&cUsage:/mines reset - Resets mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineSaveCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineSaveCommand.java new file mode 100644 index 0000000..722c5b3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineSaveCommand.java @@ -0,0 +1,47 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public class MineSaveCommand extends MineCommand { + + + public MineSaveCommand(XPrisonMines plugin) { + super(plugin, "save"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (args.size() != 1) { + return false; + } + + Mine mine = this.plugin.getManager().getMineByName(args.get(0)); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", args.get(0))); + return true; + } + + this.plugin.getManager().getMineSaver().save(mine); + + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_saved").replace("%mine%", mine.getName())); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines save - Saves a mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineSetTpCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineSetTpCommand.java new file mode 100644 index 0000000..6750236 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineSetTpCommand.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineSetTpCommand extends MineCommand { + + public MineSetTpCommand(XPrisonMines plugin) { + super(plugin, "settp", "tpset"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (!(sender instanceof Player)) { + return false; + } + + if (args.size() != 1) { + return false; + } + + Mine mine = this.plugin.getManager().getMineByName(args.get(0)); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", args.get(0))); + return true; + } + + return this.plugin.getManager().setTeleportLocation((Player) sender, mine); + } + + @Override + public String getUsage() { + return "&cUsage: /mines settp - Sets the teleport location of specified mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineTeleportCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineTeleportCommand.java new file mode 100644 index 0000000..dfed16e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineTeleportCommand.java @@ -0,0 +1,54 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineTeleportCommand extends MineCommand { + + public MineTeleportCommand(XPrisonMines plugin) { + super(plugin, "teleport", "tp"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + + if (!(sender instanceof Player)) { + return false; + } + + if (args.size() != 1) { + return false; + } + + Mine mine = this.plugin.getManager().getMineByName(args.get(0)); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", args.get(0))); + return true; + } + + if (!mine.canTeleport((Player) sender)) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("no_permission")); + return true; + } + + this.plugin.getManager().teleportToMine((Player) sender, mine); + return true; + } + + @Override + public String getUsage() { + return "&cUsage: /mines teleport - Teleports you to a specified mine"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineToolCommand.java b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineToolCommand.java new file mode 100644 index 0000000..9ceeece --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/commands/impl/MineToolCommand.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.mines.commands.impl; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.commands.MineCommand; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class MineToolCommand extends MineCommand { + + public MineToolCommand(XPrisonMines plugin) { + super(plugin, "tool"); + } + + @Override + public boolean execute(CommandSender sender, List args) { + if (!(sender instanceof Player)) { + return false; + } + + if (args.size() != 0) { + return false; + } + + return this.plugin.getManager().giveTool((Player) sender); + } + + @Override + public String getUsage() { + return "/mines tool - Gives you a selection tool"; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(XPrisonMines.MINES_ADMIN_PERM); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineBlocksGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineBlocksGUI.java new file mode 100644 index 0000000..8d51d30 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineBlocksGUI.java @@ -0,0 +1,37 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MineBlocksGUI extends Gui { + + private final Mine mine; + + public MineBlocksGUI(Mine mine, Player player) { + super(player, 5, mine.getName() + " - Blocks"); + this.mine = mine; + } + + @Override + public void redraw() { + this.clearItems(); + for (CompMaterial material : this.mine.getBlockPalette().getMaterials()) { + double chance = this.mine.getBlockPalette().getPercentage(material); + this.addItem(ItemStackBuilder.of(material.toItem()).name(material.name()).lore(" ", "&7Chance of spawning this blocks", String.format("&7is &b%,.2f%%", chance), " ", "&aLeft-Click &7to edit the chance", "&aRight-Click &7to remove.").build(() -> { + this.mine.getBlockPalette().removeFromPalette(material); + this.redraw(); + }, () -> { + new MineEditBlockChanceGUI(this.getPlayer(), mine, material).open(); + })); + } + + this.setItem(36, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to panel").build(() -> { + this.close(); + new MinePanelGUI(this.mine, this.getPlayer()).open(); + })); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineEditBlockChanceGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineEditBlockChanceGUI.java new file mode 100644 index 0000000..58ce7b3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineEditBlockChanceGUI.java @@ -0,0 +1,114 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MineEditBlockChanceGUI extends Gui { + + private final Mine mine; + private final CompMaterial material; + + private double currentChance; + + public MineEditBlockChanceGUI(Player player, Mine mine, CompMaterial material) { + super(player, 5, "Editing Block Chance"); + this.mine = mine; + this.material = material; + this.currentChance = mine.getBlockPalette().getPercentage(this.material); + } + + @Override + public void redraw() { + this.setItem(4, ItemStackBuilder.of(this.material.toItem()).name("&eBlock Chance").lore(" ", "&7The chance of spawning this", String.format("&7block is &b%,.2f%%", this.currentChance)).buildItem().build()); + + // + + this.setItem(10, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+0.1").build(() -> { + handleChanceAddition(0.1); + })); + this.setItem(11, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+0.2").build(() -> { + handleChanceAddition(0.2); + })); + this.setItem(12, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+0.5").build(() -> { + handleChanceAddition(0.5); + })); + + this.setItem(19, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+1.0").build(() -> { + handleChanceAddition(1.0); + })); + this.setItem(20, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+2.0").build(() -> { + handleChanceAddition(2.0); + })); + this.setItem(21, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+5.0").build(() -> { + handleChanceAddition(5.0); + })); + + this.setItem(28, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+10.0").build(() -> { + handleChanceAddition(10.0); + })); + this.setItem(29, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+20.0").build(() -> { + handleChanceAddition(20.0); + })); + this.setItem(30, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+50.0").build(() -> { + handleChanceAddition(50.0); + })); + + // - + this.setItem(14, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-0.1").build(() -> { + handleChanceAddition(-0.1); + })); + this.setItem(15, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-0.2").build(() -> { + handleChanceAddition(-0.2); + })); + this.setItem(16, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-0.5").build(() -> { + handleChanceAddition(-0.5); + })); + + this.setItem(23, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-1.0").build(() -> { + handleChanceAddition(-1.0); + })); + this.setItem(24, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-2.0").build(() -> { + handleChanceAddition(-2.0); + })); + this.setItem(25, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-5.0").build(() -> { + handleChanceAddition(-5.0); + })); + + this.setItem(32, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-10.0").build(() -> { + handleChanceAddition(-10.0); + })); + this.setItem(33, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-20.0").build(() -> { + handleChanceAddition(-20.0); + })); + this.setItem(34, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-50.0").build(() -> { + handleChanceAddition(-50.0); + })); + + + this.setItem(36, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to all blocks.").build(() -> { + this.close(); + new MineBlocksGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(40, ItemStackBuilder.of(CompMaterial.GREEN_WOOL.toItem()).name("&aSave").lore("&7Click to save the current chance.").build(() -> { + this.close(); + if (this.mine.getBlockPalette().getTotalPercentage() - this.mine.getBlockPalette().getPercentage(this.material) + this.currentChance > 100.0) { + this.currentChance = 100 - (this.mine.getBlockPalette().getTotalPercentage() - this.mine.getBlockPalette().getPercentage(this.material)); + } + this.mine.getBlockPalette().setPercentage(this.material, this.currentChance); + new MineBlocksGUI(this.mine, this.getPlayer()).open(); + })); + + } + + private void handleChanceAddition(double addition) { + if (this.currentChance + addition > 100.0 || this.currentChance + addition < 0.0) { + return; + } + this.currentChance += addition; + this.redraw(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineEditResetPercentageGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineEditResetPercentageGUI.java new file mode 100644 index 0000000..92d8e15 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineEditResetPercentageGUI.java @@ -0,0 +1,110 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MineEditResetPercentageGUI extends Gui { + + + private final Mine mine; + + private double currentChance; + + public MineEditResetPercentageGUI(Mine mine, Player player) { + super(player, 5, "Editing Reset Percentage"); + this.mine = mine; + this.currentChance = this.mine.getResetPercentage(); + } + + @Override + public void redraw() { + this.setItem(4, ItemStackBuilder.of(CompMaterial.CLOCK.toItem()).name("&eReset Percentage").lore(" ", "&7Current reset percentage of", String.format("&7this mine is &b%,.2f%%", this.currentChance)).buildItem().build()); + + // + + this.setItem(10, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+0.1").build(() -> { + handleChanceAddition(0.1); + })); + this.setItem(11, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+0.2").build(() -> { + handleChanceAddition(0.2); + })); + this.setItem(12, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+0.5").build(() -> { + handleChanceAddition(0.5); + })); + + this.setItem(19, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+1.0").build(() -> { + handleChanceAddition(1.0); + })); + this.setItem(20, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+2.0").build(() -> { + handleChanceAddition(2.0); + })); + this.setItem(21, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+5.0").build(() -> { + handleChanceAddition(5.0); + })); + + this.setItem(28, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+10.0").build(() -> { + handleChanceAddition(10.0); + })); + this.setItem(29, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+20.0").build(() -> { + handleChanceAddition(20.0); + })); + this.setItem(30, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+50.0").build(() -> { + handleChanceAddition(50.0); + })); + + // - + this.setItem(14, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-0.1").build(() -> { + handleChanceAddition(-0.1); + })); + this.setItem(15, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-0.2").build(() -> { + handleChanceAddition(-0.2); + })); + this.setItem(16, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-0.5").build(() -> { + handleChanceAddition(-0.5); + })); + + this.setItem(23, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-1.0").build(() -> { + handleChanceAddition(-1.0); + })); + this.setItem(24, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-2.0").build(() -> { + handleChanceAddition(-2.0); + })); + this.setItem(25, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-5.0").build(() -> { + handleChanceAddition(-5.0); + })); + + this.setItem(32, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-10.0").build(() -> { + handleChanceAddition(-10.0); + })); + this.setItem(33, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-20.0").build(() -> { + handleChanceAddition(-20.0); + })); + this.setItem(34, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-50.0").build(() -> { + handleChanceAddition(-50.0); + })); + + + this.setItem(36, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to reset settings.").build(() -> { + this.close(); + new MineResetOptionsGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(40, ItemStackBuilder.of(CompMaterial.GREEN_WOOL.toItem()).name("&aSave").lore("&7Click to save the current reset percentage.").build(() -> { + this.close(); + this.mine.setResetPercentage(this.currentChance); + new MineResetOptionsGUI(this.mine, this.getPlayer()).open(); + })); + + } + + private void handleChanceAddition(double addition) { + if (this.currentChance + addition > 95.0 || this.currentChance + addition < 5.0) { + return; + } + this.currentChance += addition; + this.redraw(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineEditTimedResetGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineEditTimedResetGUI.java new file mode 100644 index 0000000..b907aa7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineEditTimedResetGUI.java @@ -0,0 +1,71 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MineEditTimedResetGUI extends Gui { + + private final Mine mine; + + private int currentTime; + + public MineEditTimedResetGUI(Mine mine, Player player) { + super(player, 5, "Editing Timed Reset"); + this.mine = mine; + this.currentTime = this.mine.getResetTime(); + } + + @Override + public void redraw() { + this.setItem(4, ItemStackBuilder.of(CompMaterial.CLOCK.toItem()).name("&eReset Time (minutes)").lore(" ", "&7Current reset time of", String.format("&7this mine is &b%,d minutes.", this.currentTime)).buildItem().build()); + + // + + + this.setItem(19, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+1.0").build(() -> { + handleTimeAddition(1); + })); + this.setItem(20, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+2.0").build(() -> { + handleTimeAddition(2); + })); + this.setItem(21, ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&a+5.0").build(() -> { + handleTimeAddition(5); + })); + + // - + + this.setItem(23, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-1.0").build(() -> { + handleTimeAddition(-1); + })); + this.setItem(24, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-2.0").build(() -> { + handleTimeAddition(-2); + })); + this.setItem(25, ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&c-5.0").build(() -> { + handleTimeAddition(-5); + })); + + + this.setItem(36, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to reset settings.").build(() -> { + this.close(); + new MineResetOptionsGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(40, ItemStackBuilder.of(CompMaterial.GREEN_WOOL.toItem()).name("&aSave").lore("&7Click to save the current reset time.").build(() -> { + this.close(); + this.mine.setResetTime(this.currentTime); + new MineResetOptionsGUI(this.mine, this.getPlayer()).open(); + })); + + } + + private void handleTimeAddition(int addition) { + if (this.currentTime + addition < 1.0) { + return; + } + this.currentTime += addition; + this.redraw(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineEffectsGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineEffectsGUI.java new file mode 100644 index 0000000..20e3c87 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineEffectsGUI.java @@ -0,0 +1,65 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.potion.PotionEffectType; + +public class MineEffectsGUI extends Gui { + + private Mine mine; + + public MineEffectsGUI(Mine mine, Player player) { + super(player, 3, "Player effects"); + this.mine = mine; + } + + @Override + public void redraw() { + + this.clearItems(); + + for (PotionEffectType type : PotionEffectType.values()) { + if (type == null) { + continue; + } + this.addItem(this.getItemForEffect(type)); + } + + this.setItem(26, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to panel").build(() -> { + this.close(); + new MinePanelGUI(this.mine, this.getPlayer()).open(); + })); + } + + private Item getItemForEffect(PotionEffectType type) { + boolean enabled = this.mine.isEffectEnabled(type); + + if (enabled) { + return ItemStackBuilder.of(CompMaterial.GLOWSTONE_DUST.toItem()).name("&7" + type.getName() + " &aENABLED &b(" + this.mine.getEffectLevel(type) + ")").lore("&aShift-Left-Click &7to &aincrease.", "&aShift-Right-Click &7to &cdecrease.", "&aClick &7to disable.").buildItem().bind(event -> { + switch (event.getClick()) { + case LEFT: + this.mine.disableEffect(type); + break; + case SHIFT_LEFT: + this.mine.increaseEffect(type); + break; + case SHIFT_RIGHT: + this.mine.decreaseEffect(type); + break; + } + this.redraw(); + }, ClickType.LEFT, ClickType.SHIFT_RIGHT, ClickType.SHIFT_LEFT).build(); + } else { + return ItemStackBuilder.of(CompMaterial.GUNPOWDER.toItem()).name("&7" + type.getName() + " &cDISABLED").lore("&aClick &7to enable.").build(() -> { + this.mine.enableEffect(type); + this.redraw(); + }); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineHologramsGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineHologramsGUI.java new file mode 100644 index 0000000..bf98e94 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineHologramsGUI.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.HologramType; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MineHologramsGUI extends Gui { + + private final Mine mine; + + public MineHologramsGUI(Mine mine, Player player) { + super(player, 1, "Mine Holograms"); + this.mine = mine; + } + + @Override + public void redraw() { + this.setItem(0, ItemStackBuilder.of(CompMaterial.NAME_TAG.toItem()).name("&eBlocks Mined Hologram").lore(" ", "&aLeft-Click &7to spawn on your location.", "&aRight-Click &7to remove.").build(() -> { + this.close(); + this.mine.getManager().deleteHologram(this.mine, HologramType.BLOCKS_MINED, this.getPlayer()); + }, () -> { + this.close(); + this.mine.getManager().createHologram(this.mine, HologramType.BLOCKS_MINED, this.getPlayer()); + })); + this.setItem(1, ItemStackBuilder.of(CompMaterial.NAME_TAG.toItem()).name("&eBlocks Left Hologram").lore(" ", "&aLeft-Click &7to spawn on your location.", "&aRight-Click &7to remove.").build(() -> { + this.close(); + this.mine.getManager().deleteHologram(this.mine, HologramType.BLOCKS_LEFT, this.getPlayer()); + }, () -> { + this.close(); + this.mine.getManager().createHologram(this.mine, HologramType.BLOCKS_LEFT, this.getPlayer()); + })); + this.setItem(2, ItemStackBuilder.of(CompMaterial.NAME_TAG.toItem()).name("&eTimed Reset Hologram").lore(" ", "&aLeft-Click &7to spawn on your location.", "&aRight-Click &7to remove.").build(() -> { + this.close(); + this.mine.getManager().deleteHologram(this.mine, HologramType.TIMED_RESET, this.getPlayer()); + }, () -> { + this.close(); + this.mine.getManager().createHologram(this.mine, HologramType.TIMED_RESET, this.getPlayer()); + })); + + this.setItem(8, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to panel").build(() -> { + this.close(); + new MinePanelGUI(this.mine, this.getPlayer()).open(); + })); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MinePanelGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MinePanelGUI.java new file mode 100644 index 0000000..06530c1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MinePanelGUI.java @@ -0,0 +1,68 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +public class MinePanelGUI extends Gui { + + private Mine mine; + + public MinePanelGUI(Mine mine, Player player) { + super(player, 5, mine.getName() + " Panel"); + this.mine = mine; + } + + @Override + public void redraw() { + for (int i = 0; i < 5 * 9; i++) { + this.setItem(i, ItemStackBuilder.of(CompMaterial.BLACK_STAINED_GLASS_PANE.toItem()).name("&a").buildItem().build()); + } + + this.setItem(11, ItemStackBuilder.of(CompMaterial.DIAMOND_ORE.toItem()).name("&eBlock Percentages").lore(" ", "&7Click to modify blocks in this mine.").build(() -> { + this.close(); + new MineBlocksGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(13, ItemStackBuilder.of(CompMaterial.BEACON.toItem()).name("&eSpawn Location").lore(" ", "&aLeft-Click &7to teleport to mine", "&aRight-Click &7to set the location").build(() -> { + if (XPrisonMines.getInstance().getManager().setTeleportLocation(this.getPlayer(), this.mine)) { + this.close(); + } + }, () -> { + this.close(); + XPrisonMines.getInstance().getManager().teleportToMine(this.getPlayer(), this.mine); + })); + + this.setItem(15, ItemStackBuilder.of(CompMaterial.COMPARATOR.toItem()).name("&eMine Reset Options").lore(" ", "&7Click to modify the reset options.").build(() -> { + this.close(); + new MineResetOptionsGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(29, ItemStackBuilder.of(CompMaterial.NAME_TAG.toItem()).name("&eMine Holograms").lore(" ", "&7Click to modify mine's holograms").build(() -> { + this.close(); + new MineHologramsGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(31, ItemStackBuilder.of(CompMaterial.NETHER_STAR.toItem()).name("&eMine Player Effects").lore(" ", "&7Click to modify mine player effects").build(() -> { + this.close(); + new MineEffectsGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(36, ItemStackBuilder.of(CompMaterial.ARROW.toItem()).name("&cBack").lore("&7Click to show all mines").build(() -> { + this.close(); + XPrisonMines.getInstance().getManager().openMinesListGUI(this.getPlayer()); + })); + + this.setItem(44, ItemStackBuilder.of(CompMaterial.BARRIER.toItem()).name("&c&lDELETE MINE").lore("&7This action cannot be undone!", " ", "&aShift-Left-Click &7to delete this mine.").build(ClickType.SHIFT_LEFT, () -> { + if (XPrisonMines.getInstance().getManager().deleteMine(this.getPlayer(), mine)) { + this.close(); + XPrisonMines.getInstance().getManager().openMinesListGUI(this.getPlayer()); + } + })); + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/gui/MineResetOptionsGUI.java b/src/main/java/dev/drawethree/xprison/mines/gui/MineResetOptionsGUI.java new file mode 100644 index 0000000..b8e6fb6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/gui/MineResetOptionsGUI.java @@ -0,0 +1,63 @@ +package dev.drawethree.xprison.mines.gui; + +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.mines.model.mine.reset.ResetType; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class MineResetOptionsGUI extends Gui { + + private final Mine mine; + + public MineResetOptionsGUI(Mine mine, Player player) { + super(player, 5, "Reset Options"); + this.mine = mine; + } + + @Override + public void redraw() { + + for (int i = 0; i < 5 * 9; i++) { + this.setItem(i, ItemStackBuilder.of(CompMaterial.BLACK_STAINED_GLASS_PANE.toItem()).name("&a").buildItem().build()); + } + + this.setItem(11, ItemStackBuilder.of(CompMaterial.STONE_BUTTON.toItem()).name("&eReset NOW").lore(" ", "&7Resets the mine now.").build(() -> { + this.close(); + this.mine.getManager().resetMine(this.mine); + })); + + this.setItem(13, ItemStackBuilder.of(CompMaterial.COMPARATOR.toItem()).name("&eReset Type: " + this.mine.getResetType().getName()).lore(" ", "&7Instant: Will use more CPU power", "&7but the mine will reset instantly.", " ", "&7Gradual: Will use less CPU power", "&7but mine reset may take more time.", " ", "&aClick &7to change.").build(() -> { + if (this.mine.getResetType() == ResetType.GRADUAL) { + this.mine.setResetType(ResetType.INSTANT); + } else { + this.mine.setResetType(ResetType.GRADUAL); + } + this.redraw(); + })); + + this.setItem(15, ItemStackBuilder.of(CompMaterial.CLOCK.toItem()).name("&eEdit Reset Percentage").lore(" ", "&7Click to edit mine's reset percentage").build(() -> { + this.close(); + new MineEditResetPercentageGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(29, ItemStackBuilder.of(CompMaterial.CLOCK.toItem()).name("&eEdit Timed Reset").lore(" ", "&7Click to edit timed reset").build(() -> { + this.close(); + new MineEditTimedResetGUI(this.mine, this.getPlayer()).open(); + })); + + this.setItem(31, ItemStackBuilder.of(CompMaterial.PAPER.toItem()).name("&eBroadcast Reset: " + this.mine.isBroadcastReset()).lore(" ", "&aTrue &7- All players will get message", "&7on mine's reset.", "&cFalse &7- No broadcast message.").build(() -> { + this.mine.setBroadcastReset(!this.mine.isBroadcastReset()); + this.redraw(); + })); + + this.setItem(36, ItemStackBuilder.of(Material.ARROW).name("&cBack").lore("&7Click to go back to panel").build(() -> { + this.close(); + new MinePanelGUI(this.mine, this.getPlayer()).open(); + })); + + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/listener/MinesListener.java b/src/main/java/dev/drawethree/xprison/mines/listener/MinesListener.java new file mode 100644 index 0000000..ccafe27 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/listener/MinesListener.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.mines.listener; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.managers.MineManager; +import dev.drawethree.xprison.mines.model.mine.Mine; +import me.lucko.helper.Events; +import me.lucko.helper.serialize.Position; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.Arrays; + +public class MinesListener { + + private final XPrisonMines plugin; + + public MinesListener(XPrisonMines plugin) { + this.plugin = plugin; + } + + public void register() { + this.subscribeToBlockBreakEvent(); + this.subscribeToPlayerInteractEvent(); + } + + private void subscribeToBlockBreakEvent() { + Events.subscribe(BlockBreakEvent.class, EventPriority.HIGH) + .filter(e -> !e.isCancelled()) + .handler(e -> { + Mine mine = this.plugin.getManager().getMineAtLocation(e.getBlock().getLocation()); + + if (mine == null) { + return; + } + + if (mine.isResetting()) { + e.setCancelled(true); + return; + } + + mine.handleBlockBreak(Arrays.asList(e.getBlock())); + }).bindWith(this.plugin.getCore()); + } + + private void subscribeToPlayerInteractEvent() { + Events.subscribe(PlayerInteractEvent.class) + .filter(e -> e.getItem() != null && e.getItem().isSimilar(MineManager.SELECTION_TOOL) && e.getClickedBlock() != null) + .handler(e -> { + int pos = e.getAction() == Action.LEFT_CLICK_BLOCK ? 1 : e.getAction() == Action.RIGHT_CLICK_BLOCK ? 2 : -1; + + if (pos == -1) { + return; + } + + e.setCancelled(true); + + this.plugin.getManager().selectPosition(e.getPlayer(), pos, Position.of(e.getClickedBlock())); + }).bindWith(this.plugin.getCore()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/managers/MineManager.java b/src/main/java/dev/drawethree/xprison/mines/managers/MineManager.java new file mode 100644 index 0000000..0933279 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/managers/MineManager.java @@ -0,0 +1,526 @@ +package dev.drawethree.xprison.mines.managers; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.api.events.MineCreateEvent; +import dev.drawethree.xprison.mines.api.events.MineDeleteEvent; +import dev.drawethree.xprison.mines.api.events.MinePostResetEvent; +import dev.drawethree.xprison.mines.api.events.MinePreResetEvent; +import dev.drawethree.xprison.mines.gui.MinePanelGUI; +import dev.drawethree.xprison.mines.model.mine.HologramType; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.mines.model.mine.MineSelection; +import dev.drawethree.xprison.mines.model.mine.loader.MineFileLoader; +import dev.drawethree.xprison.mines.model.mine.loader.MineLoader; +import dev.drawethree.xprison.mines.model.mine.saver.MineFileSaver; +import dev.drawethree.xprison.mines.model.mine.saver.MineSaver; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.location.LocationUtils; +import dev.drawethree.xprison.utils.misc.TimeUtil; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.Getter; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.hologram.Hologram; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.paginated.PaginatedGuiBuilder; +import me.lucko.helper.serialize.Point; +import me.lucko.helper.serialize.Position; +import me.lucko.helper.serialize.Region; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; + +import java.io.File; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class MineManager { + + public static final ItemStack SELECTION_TOOL = ItemStackBuilder.of(Material.STICK).enchant(Enchantment.DURABILITY).name("&eMine Selection Tool").lore("&aRight-Click &fto set &aPosition 1 &7(MIN)", "&aLeft-Click &fto set &aPosition 2 &7(MAX)").build(); + + private final MineLoader mineLoader; + private final MineSaver mineSaver; + + @Getter + private final XPrisonMines plugin; + + private final Map mineSelections; + private Map mines; + + private List hologramBlocksLeftLines; + private List hologramBlocksMinedLines; + private List hologramTimedResetLines; + + private File minesDirectory; + + public MineManager(XPrisonMines plugin) { + this.plugin = plugin; + this.mineSelections = new HashMap<>(); + this.hologramBlocksLeftLines = this.plugin.getConfig().get().getStringList("holograms.blocks_left"); + this.hologramBlocksMinedLines = this.plugin.getConfig().get().getStringList("holograms.blocks_mined"); + this.hologramTimedResetLines = this.plugin.getConfig().get().getStringList("holograms.timed_reset"); + this.mineLoader = new MineFileLoader(this); + this.mineSaver = new MineFileSaver(this); + } + + public void enable() { + this.setupMinesDirectory(); + this.loadMines(); + } + + public List getHologramBlocksLeftLines(Mine mine) { + List copy = new ArrayList<>(); + for (String s : this.hologramBlocksLeftLines) { + copy.add(s.replace("%mine%", mine.getName()).replace("%blocks%", String.format("%,.2f", (double) mine.getCurrentBlocks() / mine.getTotalBlocks() * 100.0D))); + } + return copy; + } + + + public List getHologramBlocksMinedLines(Mine mine) { + List copy = new ArrayList<>(); + for (String s : this.hologramBlocksMinedLines) { + copy.add(s.replace("%mine%", mine.getName()).replace("%blocks%", String.format("%,d", mine.getTotalBlocks() - mine.getCurrentBlocks()))); + } + return copy; + } + + private void setupMinesDirectory() { + File directory = new File(this.plugin.getCore().getDataFolder().getPath() + "/mines/"); + + if (!directory.exists()) { + directory.mkdir(); + this.plugin.getCore().getLogger().info("Created /mines directory"); + } + this.minesDirectory = directory; + } + + private void loadMines() { + this.mines = new HashMap<>(); + File[] files = this.minesDirectory.listFiles(); + + if (files == null) { + return; + } + + for (File file : files) { + if (!file.getName().endsWith(".json")) { + continue; + } + + Mine mine = this.mineLoader.load(file); + + if (mine == null) { + continue; + } + + this.mines.put(mine.getName(), mine); + this.plugin.getCore().getLogger().info("Loaded Mine " + mine.getName()); + + double ratio = (double) mine.getCurrentBlocks() / mine.getTotalBlocks() * 100.0; + + if (ratio <= mine.getResetPercentage() && !mine.isResetting()) { + this.resetMine(mine); + } + } + } + + private void saveMines() { + this.getMines().forEach(this.mineSaver::save); + } + + public void selectPosition(Player player, int position, Position pos) { + + MineSelection selection; + + if (!mineSelections.containsKey(player.getUniqueId())) { + this.mineSelections.put(player.getUniqueId(), new MineSelection()); + } + + selection = this.mineSelections.get(player.getUniqueId()); + + switch (position) { + case 1: + selection.setPos1(pos); + break; + case 2: + selection.setPos2(pos); + break; + } + + if (selection.isValid()) { + PlayerUtils.sendMessage(player, this.plugin.getMessage("selection_valid")); + } + + PlayerUtils.sendMessage(player, this.plugin.getMessage("selection_point_set").replace("%position%", String.valueOf(position)).replace("%location%", LocationUtils.toXYZW(pos.toLocation()))); + } + + public MineSelection getMineSelection(Player player) { + return this.mineSelections.get(player.getUniqueId()); + } + + public boolean createMine(Player creator, String name) { + MineSelection selection = this.getMineSelection(creator); + + if (selection == null || !selection.isValid()) { + PlayerUtils.sendMessage(creator, this.plugin.getMessage("selection_invalid")); + return false; + } + + if (this.getMineByName(name) != null) { + PlayerUtils.sendMessage(creator, this.plugin.getMessage("mine_exists")); + return false; + } + + Mine mine = new Mine(this, name, selection.toRegion()); + + MineCreateEvent event = new MineCreateEvent(creator, mine); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("MineCreateEvent was cancelled.", this.plugin); + return true; + } + + this.mines.put(mine.getName(), mine); + + this.mineSaver.save(mine); + + PlayerUtils.sendMessage(creator, this.plugin.getMessage("mine_created").replace("%mine%", name)); + return true; + } + + public boolean deleteMine(CommandSender sender, String name) { + Mine mine = this.getMineByName(name); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists").replace("%mine%", name)); + return false; + } + + return deleteMine(sender, mine); + } + + public boolean deleteMine(CommandSender sender, Mine mine) { + MineDeleteEvent event = new MineDeleteEvent(mine); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("MineDeleteEvent was cancelled.", this.plugin); + return true; + } + + if (mine.getFile() != null) { + mine.getFile().delete(); + } + + mine.stopTicking(); + + this.despawnHolograms(mine); + + this.mines.remove(mine.getName()); + + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_deleted").replace("%mine%", mine.getName())); + return true; + } + + public Mine getMineByName(String name) { + return this.mines.get(name); + } + + public Mine getMineAtLocation(Location loc) { + for (Mine mine : this.mines.values()) { + if (mine.isInMine(loc)) { + return mine; + } + } + return null; + } + + public void disable() { + this.saveMines(); + this.getMines().forEach(this::despawnHolograms); + } + + public Collection getMines() { + return this.mines.values(); + } + + public boolean teleportToMine(Player player, Mine mine) { + + if (mine.getTeleportLocation() == null) { + PlayerUtils.sendMessage(player, this.plugin.getMessage("mine_no_teleport_location").replace("%mine%", mine.getName())); + return false; + } + + player.teleport(mine.getTeleportLocation().toLocation()); + PlayerUtils.sendMessage(player, this.plugin.getMessage("mine_teleport").replace("%mine%", mine.getName())); + return true; + } + + public void openMinesListGUI(Player player) { + PaginatedGuiBuilder builder = PaginatedGuiBuilder.create(); + + builder.lines(6); + builder.title("Mine List"); + builder.nextPageSlot(53); + builder.previousPageSlot(44); + builder.nextPageItem((pageInfo) -> ItemStackBuilder.of(Material.ARROW).name("&aNext Page").lore("&7Click to see next page.").build()); + builder.previousPageItem((pageInfo) -> ItemStackBuilder.of(Material.ARROW).name("&aPrevious Page").lore("&7Click to see previous page.").build()); + + builder.build(player, paginatedGui -> { + List items = new ArrayList<>(); + List mines = this.mines.values().stream().sorted(Comparator.comparing(Mine::getName)).collect(Collectors.toList()); + for (Mine mine : mines) { + items.add(ItemStackBuilder.of(Material.STONE).name(mine.getName()).lore("&aLeft-Click &7to open Mine Panel for this mine.", "&aRight-Click &7to teleport to this mine.").build(() -> { + this.teleportToMine(player, mine); + }, () -> { + new MinePanelGUI(mine, player).open(); + })); + } + return items; + }).open(); + + } + + public boolean setTeleportLocation(Player player, Mine mine) { + mine.setTeleportLocation(Point.of(player.getLocation())); + PlayerUtils.sendMessage(player, this.plugin.getMessage("mine_teleport_set").replace("%mine%", mine.getName())); + return true; + } + + public boolean giveTool(Player sender) { + sender.getInventory().addItem(SELECTION_TOOL); + PlayerUtils.sendMessage(sender, this.plugin.getMessage("selection_tool_given")); + return true; + } + + public void reload() { + this.hologramBlocksLeftLines = this.plugin.getConfig().get().getStringList("holograms.blocks_left"); + this.hologramBlocksMinedLines = this.plugin.getConfig().get().getStringList("holograms.blocks_mined"); + this.hologramTimedResetLines = this.plugin.getConfig().get().getStringList("holograms.timed_reset"); + } + + public boolean addMineFromMigration(Mine migrated) { + + if (migrated == null) { + return false; + } + + if (this.mines.containsKey(migrated.getName())) { + return false; + } + + this.mines.put(migrated.getName(), migrated); + + return true; + } + + public void resetAllMines() { + this.getMines().forEach(this::resetMine); + } + + public List getHologramTimedResetLines(Mine mine) { + List copy = new ArrayList<>(); + for (String s : this.hologramTimedResetLines) { + copy.add(s.replace("%mine%", mine.getName()).replace("%time%", TimeUtil.getTime(mine.getSecondsToNextReset()))); + } + return copy; + } + + public void resetMine(Mine mine) { + + if (mine == null) { + return; + } + + if (mine.isResetting()) { + return; + } + + MinePreResetEvent preResetEvent = new MinePreResetEvent(mine); + + Events.call(preResetEvent); + + if (preResetEvent.isCancelled()) { + this.getPlugin().getCore().debug("MinePreResetEvent was cancelled.", this.getPlugin()); + return; + } + + mine.setResetting(true); + + if (mine.isBroadcastReset()) { + mine.getPlayersInMine().forEach(player -> PlayerUtils.sendMessage(player, this.getPlugin().getMessage("mine_resetting").replace("%mine%", mine.getName()))); + } + + Schedulers.sync().runLater(() -> { + + if (mine.getTeleportLocation() != null) { + mine.getPlayersInMine().forEach(player -> player.teleport(mine.getTeleportLocation().toLocation())); + } + + mine.getResetType().reset(mine, mine.getBlockPalette()); + + if (mine.isBroadcastReset()) { + mine.getPlayersInMine().forEach(player -> PlayerUtils.sendMessage(player, this.getPlugin().getMessage("mine_reset").replace("%mine%", mine.getName()))); + } + + mine.setNextResetDate(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(mine.getResetTime()))); + + mine.setResetting(false); + + MinePostResetEvent postResetEvent = new MinePostResetEvent(mine); + + Events.call(postResetEvent); + }, 5, TimeUnit.SECONDS); + + } + + public void giveMineEffects(Mine mine, Player player) { + for (PotionEffectType type : mine.getMineEffects().keySet()) { + player.removePotionEffect(type); + player.addPotionEffect(mine.getEffect(type)); + } + } + + + public void createHologram(Mine mine, HologramType type, Player player) { + switch (type) { + case BLOCKS_LEFT: { + if (mine.getBlocksLeftHologram() == null) { + mine.setBlocksLeftHologram(Hologram.create(Position.of(player.getLocation()), this.getHologramBlocksLeftLines(mine))); + mine.getBlocksLeftHologram().spawn(); + } else { + mine.getBlocksLeftHologram().despawn(); + mine.getBlocksLeftHologram().updatePosition(Position.of(player.getLocation())); + mine.getBlocksLeftHologram().spawn(); + } + break; + } + case BLOCKS_MINED: { + if (mine.getBlocksMinedHologram() == null) { + mine.setBlocksMinedHologram(Hologram.create(Position.of(player.getLocation()), this.getHologramBlocksMinedLines(mine))); + mine.getBlocksMinedHologram().spawn(); + } else { + mine.getBlocksMinedHologram().despawn(); + mine.getBlocksMinedHologram().updatePosition(Position.of(player.getLocation())); + mine.getBlocksMinedHologram().spawn(); + } + break; + } + case TIMED_RESET: { + if (mine.getTimedResetHologram() == null) { + mine.setTimedResetHologram(Hologram.create(Position.of(player.getLocation()), this.getHologramTimedResetLines(mine))); + mine.getTimedResetHologram().spawn(); + } else { + mine.getTimedResetHologram().despawn(); + mine.getTimedResetHologram().updatePosition(Position.of(player.getLocation())); + mine.getTimedResetHologram().spawn(); + } + break; + } + } + PlayerUtils.sendMessage(player, this.getPlugin().getMessage("mine_hologram_create").replace("%type%", type.name()).replace("%mine%", mine.getName())); + } + + public void deleteHologram(Mine mine, HologramType type, Player player) { + switch (type) { + case BLOCKS_LEFT: { + if (mine.getBlocksLeftHologram() != null) { + mine.getBlocksLeftHologram().despawn(); + mine.setBlocksLeftHologram(null); + } + break; + } + case BLOCKS_MINED: { + if (mine.getBlocksMinedHologram() != null) { + mine.getBlocksMinedHologram().despawn(); + mine.setBlocksMinedHologram(null); + } + break; + } + case TIMED_RESET: { + if (mine.getTimedResetHologram() != null) { + mine.getTimedResetHologram().despawn(); + mine.setTimedResetHologram(null); + } + break; + } + } + PlayerUtils.sendMessage(player, this.getPlugin().getMessage("mine_hologram_delete").replace("%type%", type.name()).replace("%mine%", mine.getName())); + } + + private void despawnHolograms(Mine mine) { + + if (mine.getBlocksMinedHologram() != null) { + mine.getBlocksMinedHologram().despawn(); + } + + if (mine.getBlocksLeftHologram() != null) { + mine.getBlocksLeftHologram().despawn(); + } + + if (mine.getTimedResetHologram() != null) { + mine.getTimedResetHologram().despawn(); + } + } + + public boolean renameMine(Player sender, String oldMineName, String newName) { + + Mine mine = this.getMineByName(oldMineName); + + if (mine == null) { + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_not_exists")); + return false; + } + + if (mine.getFile() != null) { + mine.getFile().delete(); + } + + this.mines.remove(oldMineName); + mine.setName(newName); + this.mines.put(newName, mine); + + this.mineSaver.save(mine); + + PlayerUtils.sendMessage(sender, this.plugin.getMessage("mine_renamed").replace("%mine%", oldMineName).replace("%new_name%", newName)); + return true; + } + + public boolean redefineMine(Player creator, String name) { + MineSelection selection = this.getMineSelection(creator); + + if (selection == null || !selection.isValid()) { + PlayerUtils.sendMessage(creator, this.plugin.getMessage("selection_invalid")); + return false; + } + + Mine mine = this.getMineByName(name); + if (mine == null) { + PlayerUtils.sendMessage(creator, this.plugin.getMessage("mine_not_exists")); + return false; + } + + Region region = selection.toRegion(); + mine.setMineRegion(region); + + PlayerUtils.sendMessage(creator, this.plugin.getMessage("mine_redefined").replace("%mine%", name)); + return true; + } + + public MineLoader getMineLoader() { + return mineLoader; + } + + public MineSaver getMineSaver() { + return mineSaver; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/exception/MinesMigrationNotSupportedException.java b/src/main/java/dev/drawethree/xprison/mines/migration/exception/MinesMigrationNotSupportedException.java new file mode 100644 index 0000000..408b5ff --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/exception/MinesMigrationNotSupportedException.java @@ -0,0 +1,8 @@ +package dev.drawethree.xprison.mines.migration.exception; + +public class MinesMigrationNotSupportedException extends Throwable { + + public MinesMigrationNotSupportedException(String pluginName) { + super("Mines migration from plugin " + pluginName + " is not supported!"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/gui/AllMinesMigrationGui.java b/src/main/java/dev/drawethree/xprison/mines/migration/gui/AllMinesMigrationGui.java new file mode 100644 index 0000000..ad8839d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/gui/AllMinesMigrationGui.java @@ -0,0 +1,69 @@ +package dev.drawethree.xprison.mines.migration.gui; + +import dev.drawethree.xprison.mines.migration.model.MinesMigration; +import dev.drawethree.xprison.mines.migration.model.impl.MineResetLiteMigration; +import dev.drawethree.xprison.mines.migration.utils.MinesMigrationUtils; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import me.lucko.helper.menu.scheme.MenuPopulator; +import me.lucko.helper.menu.scheme.MenuScheme; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public final class AllMinesMigrationGui extends Gui { + + private static final MenuScheme LAYOUT_WHITE = new MenuScheme() + .mask("011111110") + .mask("110000011") + .mask("100000001") + .mask("110000011") + .mask("011111110"); + + private static final MenuScheme LAYOUT_RED = new MenuScheme() + .mask("100000001") + .mask("000000000") + .mask("000000000") + .mask("000000000") + .mask("100000001"); + + public AllMinesMigrationGui(Player player) { + super(player, 5, "Mines Migration"); + } + + @Override + public void redraw() { + if (isFirstDraw()) { + populateLayout(); + } + this.populateAvailableMigrations(); + } + + private void populateAvailableMigrations() { + try { + MinesMigration migration = new MineResetLiteMigration(); + this.addItem(createItemForMigration(migration)); + } catch (NoClassDefFoundError e) { + + } + } + + private Item createItemForMigration(MinesMigration migration) { + return ItemStackBuilder.of(Material.DIAMOND_PICKAXE).name("&a" + migration.getFromPlugin()).lore(" ", "&7Click to migrate mines from this plugin.", " ").build(() -> MinesMigrationUtils.openMinesMigrationGui(getPlayer(), migration)); + } + + private void populateLayout() { + MenuPopulator populator = LAYOUT_WHITE.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.WHITE_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + + populator = LAYOUT_RED.newPopulator(this); + + while (populator.hasSpace()) { + populator.accept(ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name(" ").buildItem().build()); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/gui/MinesMigrationGui.java b/src/main/java/dev/drawethree/xprison/mines/migration/gui/MinesMigrationGui.java new file mode 100644 index 0000000..1d23cc4 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/gui/MinesMigrationGui.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.mines.migration.gui; + +import dev.drawethree.xprison.mines.migration.model.MinesMigration; +import dev.drawethree.xprison.utils.gui.ConfirmationGui; +import org.bukkit.entity.Player; + +public final class MinesMigrationGui extends ConfirmationGui { + + private final MinesMigration migration; + + public MinesMigrationGui(Player player, MinesMigration migration) { + super(player, "Migrate from " + migration.getFromPlugin() + "?"); + this.migration = migration; + } + + @Override + public void confirm(boolean confirm) { + this.close(); + if (confirm) { + migration.migrate(getPlayer()); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/model/MinesMigration.java b/src/main/java/dev/drawethree/xprison/mines/migration/model/MinesMigration.java new file mode 100644 index 0000000..6b5f47c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/model/MinesMigration.java @@ -0,0 +1,74 @@ +package dev.drawethree.xprison.mines.migration.model; + +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.Getter; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public abstract class MinesMigration { + + @Getter + protected String fromPlugin; + + public MinesMigration(String fromPlugin) { + this.fromPlugin = fromPlugin; + } + + public void migrate(CommandSender sender) { + + List migrated = migrateAllMines(sender); + + int completed = 0, skipped = 0; + + for (Mine mine : migrated) { + if (this.getMinesPlugin().getManager().addMineFromMigration(mine)) { + PlayerUtils.sendMessage(sender, this.getMinesPlugin().getMessage("mine_migration_mine_completed").replace("%plugin%", this.fromPlugin).replace("%mine%", mine.getName())); + completed++; + } else { + PlayerUtils.sendMessage(sender, this.getMinesPlugin().getMessage("mine_migration_mine_skipped").replace("%plugin%", this.fromPlugin).replace("%mine%", mine.getName())); + skipped++; + } + } + + PlayerUtils.sendMessage(sender, this.getMinesPlugin().getMessage("mine_migration_completed").replace("%plugin%", this.fromPlugin)); + PlayerUtils.sendMessage(sender, this.getMinesPlugin().getMessage("mine_migration_result").replace("%plugin%", this.fromPlugin).replace("%completed%", String.format("%,d", completed)).replace("%skipped%", String.format("%,d", skipped))); + } + + private List migrateAllMines(CommandSender sender) { + + List migratedMines = new ArrayList<>(); + + if (!this.preValidateMigration()) { + PlayerUtils.sendMessage(sender, this.getMinesPlugin().getMessage("mine_migration_plugin_not_present").replace("%plugin%", this.fromPlugin)); + return migratedMines; + } + + PlayerUtils.sendMessage(sender, this.getMinesPlugin().getMessage("mine_migration_started").replace("%plugin%", this.fromPlugin)); + + for (T type : this.getMinesToMigrate()) { + + Mine mine = this.migrate(type); + + if (mine != null) { + migratedMines.add(mine); + } + } + + return migratedMines; + } + + private XPrisonMines getMinesPlugin() { + return XPrisonMines.getInstance(); + } + + protected abstract boolean preValidateMigration(); + + protected abstract Mine migrate(T mineClass); + + protected abstract List getMinesToMigrate(); + +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/model/impl/MineResetLiteMigration.java b/src/main/java/dev/drawethree/xprison/mines/migration/model/impl/MineResetLiteMigration.java new file mode 100644 index 0000000..1248d0e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/model/impl/MineResetLiteMigration.java @@ -0,0 +1,68 @@ +package dev.drawethree.xprison.mines.migration.model.impl; + +import com.koletar.jj.mineresetlite.Mine; +import com.koletar.jj.mineresetlite.MineResetLite; +import com.koletar.jj.mineresetlite.SerializableBlock; +import dev.drawethree.xprison.mines.migration.model.MinesMigration; +import dev.drawethree.xprison.mines.model.mine.BlockPalette; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import me.lucko.helper.serialize.Point; +import me.lucko.helper.serialize.Position; +import me.lucko.helper.serialize.Region; +import org.bukkit.Bukkit; + +import java.util.List; +import java.util.Map; + +public final class MineResetLiteMigration extends MinesMigration { + + private final MineResetLite plugin; + + public MineResetLiteMigration() { + super("MineResetLite"); + this.plugin = ((MineResetLite) Bukkit.getPluginManager().getPlugin(this.fromPlugin)); + } + + @Override + protected List getMinesToMigrate() { + return this.plugin.mines; + } + + @Override + protected boolean preValidateMigration() { + return this.plugin != null; + } + + @Override + protected dev.drawethree.xprison.mines.model.mine.Mine migrate(Mine mine) { + dev.drawethree.xprison.mines.model.mine.Mine.Builder builder = new dev.drawethree.xprison.mines.model.mine.Mine.Builder(); + + //Name + String name = mine.getName(); + builder.name(name); + + //Region + Position minPoint = Position.of(mine.getMinX(), mine.getMinY(), mine.getMinZ(), mine.getWorld()); + Position maxPoint = Position.of(mine.getMaxX(), mine.getMaxY(), mine.getMaxZ(), mine.getWorld()); + Region region = minPoint.regionWith(maxPoint); + builder.region(region); + + //Teleport location + if (mine.getTpPos() != null) { + Point teleportLocation = Point.of(mine.getTpPos()); + builder.teleportLocation(teleportLocation); + } + + //Block palette + BlockPalette blockPalette = new BlockPalette(); + + for (Map.Entry entry : mine.getComposition().entrySet()) { + double chance = entry.getValue(); + CompMaterial material = CompMaterial.fromId(entry.getKey().getBlockId(), entry.getKey().getData()); + blockPalette.addToPalette(material, chance); + } + + builder.blockPalette(blockPalette); + return builder.build(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/utils/MinesMigrationFactory.java b/src/main/java/dev/drawethree/xprison/mines/migration/utils/MinesMigrationFactory.java new file mode 100644 index 0000000..1669ffa --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/utils/MinesMigrationFactory.java @@ -0,0 +1,17 @@ +package dev.drawethree.xprison.mines.migration.utils; + +import dev.drawethree.xprison.mines.migration.exception.MinesMigrationNotSupportedException; +import dev.drawethree.xprison.mines.migration.model.MinesMigration; +import dev.drawethree.xprison.mines.migration.model.impl.MineResetLiteMigration; + +public class MinesMigrationFactory { + + public static MinesMigration fromPlugin(String pluginName) throws MinesMigrationNotSupportedException { + + if ("mineresetlite".equalsIgnoreCase(pluginName)) { + return new MineResetLiteMigration(); + } + throw new MinesMigrationNotSupportedException(pluginName); + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/migration/utils/MinesMigrationUtils.java b/src/main/java/dev/drawethree/xprison/mines/migration/utils/MinesMigrationUtils.java new file mode 100644 index 0000000..9ac996f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/migration/utils/MinesMigrationUtils.java @@ -0,0 +1,24 @@ +package dev.drawethree.xprison.mines.migration.utils; + +import dev.drawethree.xprison.mines.migration.gui.AllMinesMigrationGui; +import dev.drawethree.xprison.mines.migration.gui.MinesMigrationGui; +import dev.drawethree.xprison.mines.migration.model.MinesMigration; +import org.bukkit.entity.Player; + +public class MinesMigrationUtils { + + public static void openMinesMigrationGui(Player player, MinesMigration migration) { + MinesMigrationGui gui = new MinesMigrationGui(player, migration); + gui.open(); + } + + public static void openAllMinesMigrationGui(Player player) { + AllMinesMigrationGui gui = new AllMinesMigrationGui(player); + gui.open(); + } + + + private MinesMigrationUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/BlockPalette.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/BlockPalette.java new file mode 100644 index 0000000..8ffe09d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/BlockPalette.java @@ -0,0 +1,96 @@ +package dev.drawethree.xprison.mines.model.mine; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import me.lucko.helper.gson.GsonSerializable; +import me.lucko.helper.gson.JsonBuilder; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class BlockPalette implements GsonSerializable { + + private Map blockPercentages; + + public BlockPalette() { + this.blockPercentages = new HashMap<>(); + } + + private BlockPalette(Map blockPercentages) { + this.blockPercentages = blockPercentages; + } + + public static BlockPalette deserialize(JsonElement element) { + Preconditions.checkArgument(element.isJsonObject()); + JsonObject object = element.getAsJsonObject(); + + Map blocks = new HashMap<>(); + + for (Map.Entry entry : object.entrySet()) { + CompMaterial material = CompMaterial.valueOf(entry.getKey()); + double percentage = entry.getValue().getAsDouble(); + if (percentage <= 0.0) { + continue; + } + blocks.put(material, percentage); + } + + return new BlockPalette(blocks); + } + + public boolean contains(CompMaterial material) { + return blockPercentages.containsKey(material); + } + + public double getPercentage(CompMaterial material) { + return blockPercentages.getOrDefault(material, 0.0); + } + + public void setPercentage(CompMaterial material, double newPercentage) { + if (newPercentage <= 0.0) { + this.blockPercentages.remove(material); + } else { + this.blockPercentages.put(material, newPercentage); + } + } + + public void addToPalette(CompMaterial material, double percentage) { + this.blockPercentages.put(material, percentage); + } + + public void removeFromPalette(CompMaterial material) { + this.blockPercentages.remove(material); + } + + public Set getMaterials() { + return this.blockPercentages.keySet(); + } + + public Set getValidMaterials() { + return this.blockPercentages.keySet().stream().filter(material -> getPercentage(material) > 0.0).collect(Collectors.toSet()); + } + + public double getTotalPercentage() { + return this.blockPercentages.values().stream().mapToDouble(Double::valueOf).sum(); + } + + @Nonnull + @Override + public JsonElement serialize() { + JsonBuilder.JsonObjectBuilder builder = JsonBuilder.object(); + + for (Map.Entry entry : blockPercentages.entrySet()) { + builder.addIfAbsent(entry.getKey().name(), entry.getValue()); + } + return builder.build(); + } + + public boolean isEmpty() { + return this.blockPercentages.isEmpty() || this.getTotalPercentage() <= 0.0; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/HologramType.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/HologramType.java new file mode 100644 index 0000000..518db2c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/HologramType.java @@ -0,0 +1,7 @@ +package dev.drawethree.xprison.mines.model.mine; + +public enum HologramType { + BLOCKS_LEFT, + TIMED_RESET, + BLOCKS_MINED +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/Mine.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/Mine.java new file mode 100644 index 0000000..865d639 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/Mine.java @@ -0,0 +1,451 @@ +package dev.drawethree.xprison.mines.model.mine; + +import com.google.gson.JsonElement; +import dev.drawethree.xprison.mines.XPrisonMines; +import dev.drawethree.xprison.mines.managers.MineManager; +import dev.drawethree.xprison.mines.model.mine.reset.ResetType; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import lombok.Getter; +import lombok.Setter; +import me.lucko.helper.Schedulers; +import me.lucko.helper.gson.GsonSerializable; +import me.lucko.helper.gson.JsonBuilder; +import me.lucko.helper.hologram.Hologram; +import me.lucko.helper.scheduler.Task; +import me.lucko.helper.serialize.Point; +import me.lucko.helper.serialize.Position; +import me.lucko.helper.serialize.Region; +import me.lucko.helper.utils.Players; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import javax.annotation.Nonnull; +import java.io.File; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class Mine implements GsonSerializable { + + @Getter + private final MineManager manager; + + @Getter + @Setter + private String name; + + @Getter + @Setter + private Region mineRegion; + + @Getter + @Setter + private Point teleportLocation; + + @Getter + private final BlockPalette blockPalette; + + @Getter + @Setter + private double resetPercentage; + + @Getter + private final int totalBlocks; + + @Getter + @Setter + private int currentBlocks; + + @Getter + @Setter + private ResetType resetType; + + @Getter + @Setter + private boolean resetting; + + @Getter + @Setter + private boolean broadcastReset; + + @Getter + @Setter + private Hologram blocksLeftHologram; + @Getter + @Setter + private Hologram blocksMinedHologram; + @Getter + @Setter + private Hologram timedResetHologram; + + @Getter + @Setter + private Date nextResetDate; + + @Getter + @Setter + private int resetTime; + + @Getter + private final Map mineEffects; + private Task mineTask; + + public Mine(MineManager manager, String name, Region region) { + this.manager = manager; + this.name = name; + this.mineRegion = region; + this.teleportLocation = null; + this.blockPalette = new BlockPalette(); + this.blockPalette.addToPalette(CompMaterial.STONE, 100.0); + this.resetType = ResetType.INSTANT; + this.resetPercentage = 50.0; + this.resetTime = 10; + this.broadcastReset = true; + this.totalBlocks = this.calculateTotalBlocks(); + this.currentBlocks = this.calculateCurrentBlocks(); + this.mineEffects = new HashMap<>(); + this.startTicking(); + } + + public Mine(MineManager manager, String name, Region region, Point teleportLocation, BlockPalette palette, ResetType resetType, double resetPercentage, boolean broadcastReset, Hologram blocksLeftHologram, Hologram blocksMinedHologram, Hologram timedResetHologram, Map mineEffect, int resetTime) { + this.manager = manager; + this.name = name; + this.mineRegion = region; + this.teleportLocation = teleportLocation; + this.blockPalette = palette; + this.resetType = resetType; + this.resetPercentage = resetPercentage; + this.broadcastReset = broadcastReset; + this.totalBlocks = this.calculateTotalBlocks(); + this.currentBlocks = this.calculateCurrentBlocks(); + this.blocksLeftHologram = blocksLeftHologram; + this.blocksMinedHologram = blocksMinedHologram; + this.timedResetHologram = timedResetHologram; + this.mineEffects = mineEffect; + this.resetTime = resetTime; + this.nextResetDate = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(this.resetTime)); + this.updateHolograms(); + this.startTicking(); + } + + private void startTicking() { + this.mineTask = Schedulers.sync().runRepeating(this::tick, 1, TimeUnit.SECONDS, 1, TimeUnit.SECONDS); + } + + public void updateHolograms() { + + if (this.blocksLeftHologram != null) { + this.blocksLeftHologram.updateLines(this.manager.getHologramBlocksLeftLines(this)); + this.blocksLeftHologram.spawn(); + } + + if (this.blocksMinedHologram != null) { + this.blocksMinedHologram.updateLines(this.manager.getHologramBlocksMinedLines(this)); + this.blocksMinedHologram.spawn(); + } + + if (this.timedResetHologram != null) { + this.timedResetHologram.updateLines(this.manager.getHologramTimedResetLines(this)); + this.timedResetHologram.spawn(); + } + } + + public void handleBlockBreak(List blocks) { + + //Remove blocks that are not in region just for safety + blocks.removeIf(block -> !isInMine(block.getLocation())); + + this.currentBlocks -= blocks.size(); + + double ratio = (double) this.currentBlocks / this.totalBlocks * 100.0; + + if (ratio <= this.resetPercentage && !this.resetting) { + this.manager.resetMine(this); + } + + this.updateHolograms(); + } + + public void stopTicking() { + if (this.mineTask != null) { + this.mineTask.stop(); + } + } + + public boolean isInMine(Location loc) { + return this.mineRegion.inRegion(loc); + } + + public Iterator getBlocksIterator() { + Position min = this.mineRegion.getMin(); + Position max = this.mineRegion.getMax(); + + int minX = (int) Math.min(min.getX(), max.getX()); + int minY = (int) Math.min(min.getY(), max.getY()); + int minZ = (int) Math.min(min.getZ(), max.getZ()); + + int maxX = (int) Math.max(min.getX(), max.getX()); + int maxY = (int) Math.max(min.getY(), max.getY()); + int maxZ = (int) Math.max(min.getZ(), max.getZ()); + + List blocks = new ArrayList<>(1000); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Block b = min.toLocation().getWorld().getBlockAt(x, y, z); + blocks.add(b); + } + } + } + return blocks.iterator(); + } + + private int calculateCurrentBlocks() { + Position min = this.mineRegion.getMin(); + Position max = this.mineRegion.getMax(); + + int minX = (int) Math.min(min.getX(), max.getX()); + int minY = (int) Math.min(min.getY(), max.getY()); + int minZ = (int) Math.min(min.getZ(), max.getZ()); + + int maxX = (int) Math.max(min.getX(), max.getX()); + int maxY = (int) Math.max(min.getY(), max.getY()); + int maxZ = (int) Math.max(min.getZ(), max.getZ()); + + int amount = 0; + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Block b = min.toLocation().getWorld().getBlockAt(x, y, z); + if (b.getType() != Material.AIR) { + amount++; + } + } + } + } + return amount; + } + + + private int calculateTotalBlocks() { + Position min = this.mineRegion.getMin(); + Position max = this.mineRegion.getMax(); + + int minX = (int) Math.min(min.getX(), max.getX()); + int minY = (int) Math.min(min.getY(), max.getY()); + int minZ = (int) Math.min(min.getZ(), max.getZ()); + + int maxX = (int) Math.max(min.getX(), max.getX()); + int maxY = (int) Math.max(min.getY(), max.getY()); + int maxZ = (int) Math.max(min.getZ(), max.getZ()); + + int amount = 0; + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + amount++; + } + } + } + return amount; + } + + private void tick() { + this.getPlayersInMine().forEach(player -> this.manager.giveMineEffects(this, player)); + + if (!this.resetting && this.nextResetDate != null && System.currentTimeMillis() >= this.nextResetDate.getTime()) { + this.manager.resetMine(this); + } + + if (!this.resetting && this.timedResetHologram != null) { + this.timedResetHologram.updateLines(this.manager.getHologramTimedResetLines(this)); + this.timedResetHologram.spawn(); + } + } + + + @Nonnull + @Override + public JsonElement serialize() { + JsonBuilder.JsonObjectBuilder builder = JsonBuilder.object(); + builder.addIfAbsent("name", this.name); + builder.addIfAbsent("teleport-location", this.teleportLocation); + builder.addIfAbsent("region", this.mineRegion); + builder.addIfAbsent("blocks", this.blockPalette); + builder.addIfAbsent("reset-type", this.resetType.getName()); + builder.addIfAbsent("reset-percentage", this.resetPercentage); + builder.addIfAbsent("reset-time", this.resetTime); + builder.addIfAbsent("broadcast-reset", this.broadcastReset); + builder.addIfAbsent("hologram-blocks-mined", this.blocksMinedHologram); + builder.addIfAbsent("hologram-blocks-left", this.blocksLeftHologram); + builder.addIfAbsent("hologram-timed-reset", this.timedResetHologram); + + JsonBuilder.JsonObjectBuilder effectsBuilder = JsonBuilder.object(); + + for (PotionEffectType type : this.mineEffects.keySet()) { + effectsBuilder.addIfAbsent(type.getName(), this.mineEffects.get(type)); + } + + builder.addIfAbsent("effects", effectsBuilder.build()); + + return builder.build(); + } + + public File getFile() { + return new File(this.manager.getPlugin().getCore().getDataFolder().getPath() + "/mines/" + this.getName() + ".json"); + } + + public List getPlayersInMine() { + return Players.all().stream().filter(player -> this.isInMine(player.getLocation())).collect(Collectors.toList()); + } + + public void updateCurrentBlocks() { + this.currentBlocks = this.calculateCurrentBlocks(); + } + + public void increaseEffect(PotionEffectType type) { + this.mineEffects.put(type, this.mineEffects.getOrDefault(type, 0) + 1); + } + + public PotionEffect getEffect(PotionEffectType type) { + if (this.mineEffects.containsKey(type)) { + return new PotionEffect(type, 200, this.mineEffects.get(type)); + } + return null; + } + + public void decreaseEffect(PotionEffectType type) { + int newAmplifier = this.mineEffects.getOrDefault(type, 0) - 1; + if (newAmplifier < 0) { + newAmplifier = 0; + } + this.mineEffects.put(type, newAmplifier); + } + + public void disableEffect(PotionEffectType type) { + this.mineEffects.remove(type); + } + + public boolean isEffectEnabled(PotionEffectType type) { + return this.mineEffects.containsKey(type); + } + + public void enableEffect(PotionEffectType type) { + this.mineEffects.put(type, 0); + } + + public int getEffectLevel(PotionEffectType type) { + return this.mineEffects.getOrDefault(type, 0); + } + + public boolean canTeleport(Player player) { + return player.hasPermission("xprison.mines.tp." + this.name); + } + + public long getSecondsToNextReset() { + if (this.nextResetDate == null) { + return 0; + } + return (int) ((this.nextResetDate.getTime() - System.currentTimeMillis()) / 1000); + } + + public static final class Builder { + + private String name; + private Region mineRegion; + private Point teleportLocation; + private BlockPalette blockPalette; + private double resetPercentage; + private ResetType resetType; + private boolean broadcastReset; + private Hologram blocksMinedHologram; + private Hologram blocksLeftHologram; + private Hologram timedResetHologram; + private Map mineEffects; + private int timedReset; + + public Builder() { + this.name = ""; + this.mineRegion = null; + this.teleportLocation = null; + this.blockPalette = new BlockPalette(); + this.resetPercentage = 50; + this.resetType = ResetType.GRADUAL; + this.broadcastReset = true; + this.blocksMinedHologram = null; + this.blocksLeftHologram = null; + this.timedResetHologram = null; + this.mineEffects = new HashMap<>(); + this.timedReset = 10; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder region(Region region) { + this.mineRegion = region; + return this; + } + + public Builder teleportLocation(Point point) { + this.teleportLocation = point; + return this; + } + + public Builder blockPalette(BlockPalette palette) { + this.blockPalette = palette; + return this; + } + + public Builder resetPercentage(double resetPercentage) { + this.resetPercentage = resetPercentage; + return this; + } + + public Builder resetType(ResetType resetType) { + this.resetType = resetType; + return this; + } + + public Builder broadcastReset(boolean broadcastReset) { + this.broadcastReset = broadcastReset; + return this; + } + + public Builder blocksLeftHologram(Hologram blocksLeftHologram) { + this.blocksLeftHologram = blocksLeftHologram; + return this; + } + + public Builder blocksMinedHologram(Hologram blocksMinedHologram) { + this.blocksMinedHologram = blocksMinedHologram; + return this; + } + + public Builder timedResetHologram(Hologram timedResetHologram) { + this.timedResetHologram = blocksMinedHologram; + return this; + } + + public Builder mineEffects(Map mineEffects) { + this.mineEffects = mineEffects; + return this; + } + + public Builder timedReset(int minutes) { + this.timedReset = minutes; + return this; + } + + public Mine build() { + return new Mine(XPrisonMines.getInstance().getManager(), this.name, this.mineRegion, this.teleportLocation, this.blockPalette, this.resetType, this.resetPercentage, this.broadcastReset, this.blocksLeftHologram, this.blocksMinedHologram, this.timedResetHologram, this.mineEffects, this.timedReset); + } + + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/MineSelection.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/MineSelection.java new file mode 100644 index 0000000..fbb6188 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/MineSelection.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.mines.model.mine; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.lucko.helper.serialize.Position; +import me.lucko.helper.serialize.Region; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class MineSelection { + private Position pos1; + private Position pos2; + + public boolean isValid() { + return pos1 != null && pos2 != null; + } + + public Region toRegion() { + return pos1.regionWith(pos2); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/loader/MineFileLoader.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/loader/MineFileLoader.java new file mode 100644 index 0000000..17d8454 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/loader/MineFileLoader.java @@ -0,0 +1,66 @@ +package dev.drawethree.xprison.mines.model.mine.loader; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.drawethree.xprison.mines.managers.MineManager; +import dev.drawethree.xprison.mines.model.mine.BlockPalette; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.mines.model.mine.reset.ResetType; +import me.lucko.helper.gson.GsonProvider; +import me.lucko.helper.hologram.Hologram; +import me.lucko.helper.serialize.Point; +import me.lucko.helper.serialize.Region; +import org.bukkit.potion.PotionEffectType; + +import java.io.File; +import java.io.FileReader; +import java.util.HashMap; +import java.util.Map; + +public class MineFileLoader implements MineLoader { + + private final MineManager manager; + + public MineFileLoader(MineManager manager) { + this.manager = manager; + } + + @Override + public Mine load(File file) { + try (FileReader reader = new FileReader(file)) { + JsonObject obj = GsonProvider.readObject(reader); + + String name = obj.get("name").getAsString(); + Point teleportLocation = obj.get("teleport-location").isJsonNull() ? null : Point.deserialize(obj.get("teleport-location")); + Region region = obj.get("region").isJsonNull() ? null : Region.deserialize(obj.get("region")); + BlockPalette palette = obj.get("blocks").isJsonNull() ? new BlockPalette() : BlockPalette.deserialize(obj.get("blocks")); + + ResetType resetType = obj.get("reset-type").isJsonNull() ? ResetType.INSTANT : ResetType.of(obj.get("reset-type").getAsString()); + double resetPercentage = obj.get("reset-percentage").getAsDouble(); + int resetTime = obj.has("reset-time") ? obj.get("reset-time").getAsInt() : 10; + boolean broadcastReset = obj.get("broadcast-reset").isJsonNull() || obj.get("broadcast-reset").getAsBoolean(); + + Hologram blocksLeftHologram = obj.has("hologram-blocks-left") ? obj.get("hologram-blocks-left").isJsonNull() ? null : Hologram.deserialize(obj.get("hologram-blocks-left")) : null; + Hologram blocksMinedHologram = obj.has("hologram-blocks-mined") ? obj.get("hologram-blocks-mined").isJsonNull() ? null : Hologram.deserialize(obj.get("hologram-blocks-mined")) : null; + Hologram timedResetHologram = obj.has("hologram-timed-reset") ? obj.get("hologram-timed-reset").isJsonNull() ? null : Hologram.deserialize(obj.get("hologram-timed-reset")) : null; + + Map mineEffects = new HashMap<>(); + + JsonElement mineEffectsObj = obj.get("effects"); + + if (mineEffectsObj != null) { + for (Map.Entry entry : mineEffectsObj.getAsJsonObject().entrySet()) { + PotionEffectType type = PotionEffectType.getByName(entry.getKey()); + int amplifier = entry.getValue().getAsInt(); + mineEffects.put(type, amplifier); + } + } + + return new Mine(this.manager, name, region, teleportLocation, palette, resetType, resetPercentage, broadcastReset, blocksLeftHologram, blocksMinedHologram, timedResetHologram, mineEffects, resetTime); + } catch (Exception e) { + this.manager.getPlugin().getCore().getLogger().warning("Unable to load mine " + file.getName() + "!"); + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/loader/MineLoader.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/loader/MineLoader.java new file mode 100644 index 0000000..df8afce --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/loader/MineLoader.java @@ -0,0 +1,8 @@ +package dev.drawethree.xprison.mines.model.mine.loader; + +import dev.drawethree.xprison.mines.model.mine.Mine; + +public interface MineLoader { + + Mine load(T type); +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/GradualReset.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/GradualReset.java new file mode 100644 index 0000000..4227b59 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/GradualReset.java @@ -0,0 +1,61 @@ +package dev.drawethree.xprison.mines.model.mine.reset; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.mines.model.mine.BlockPalette; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.compat.MinecraftVersion; +import me.lucko.helper.Schedulers; +import me.lucko.helper.random.RandomSelector; +import org.bukkit.block.Block; + +import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; + +public class GradualReset extends ResetType { + + private final int CHANGES_PER_TICK = 350; + + GradualReset() { + super("Gradual"); + } + + @Override + public void reset(Mine paramMine, BlockPalette blockPalette) { + + if (blockPalette.isEmpty()) { + XPrison.getInstance().getLogger().warning("Reset for Mine " + paramMine.getName() + " aborted. Block palette is empty."); + return; + } + + this.schedule(paramMine, blockPalette, paramMine.getBlocksIterator()); + } + + + private void schedule(final Mine mine, BlockPalette blockPalette, Iterator blocksIterator) { + Schedulers.sync().runLater(() -> { + int changes = 0; + RandomSelector selector = RandomSelector.weighted(blockPalette.getValidMaterials(), blockPalette::getPercentage); + while (blocksIterator.hasNext() && changes <= CHANGES_PER_TICK) { + CompMaterial pick = selector.pick(); + Block b = blocksIterator.next(); + b.setType(pick.toMaterial()); + if (MinecraftVersion.olderThan(MinecraftVersion.V.v1_13)) { + try { + Block.class.getMethod("setData", byte.class).invoke(b, pick.getData()); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + changes++; + } + if (blocksIterator.hasNext()) { + schedule(mine, blockPalette, blocksIterator); + } else { + mine.setResetting(false); + mine.updateCurrentBlocks(); + mine.updateHolograms(); + } + }, 1L); + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/InstantReset.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/InstantReset.java new file mode 100644 index 0000000..bb2cb09 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/InstantReset.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.mines.model.mine.reset; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.mines.model.mine.BlockPalette; +import dev.drawethree.xprison.mines.model.mine.Mine; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.compat.MinecraftVersion; +import me.lucko.helper.random.RandomSelector; +import me.lucko.helper.serialize.Position; +import org.bukkit.block.Block; + +import java.lang.reflect.InvocationTargetException; + + +public class InstantReset extends ResetType { + + InstantReset() { + super("Instant"); + } + + @Override + public void reset(Mine mine, BlockPalette blockPalette) { + + if (blockPalette.isEmpty()) { + XPrison.getInstance().getLogger().warning("Reset for Mine " + mine.getName() + " aborted. Block palette is empty."); + return; + } + + RandomSelector selector = RandomSelector.weighted(blockPalette.getValidMaterials(), blockPalette::getPercentage); + + Position min = mine.getMineRegion().getMin(); + Position max = mine.getMineRegion().getMax(); + + int minX = (int) Math.min(min.getX(), max.getX()); + int minY = (int) Math.min(min.getY(), max.getY()); + int minZ = (int) Math.min(min.getZ(), max.getZ()); + + int maxX = (int) Math.max(min.getX(), max.getX()); + int maxY = (int) Math.max(min.getY(), max.getY()); + int maxZ = (int) Math.max(min.getZ(), max.getZ()); + + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Block b = min.toLocation().getWorld().getBlockAt(x, y, z); + CompMaterial pick = selector.pick(); + b.setType(pick.toMaterial()); + if (MinecraftVersion.olderThan(MinecraftVersion.V.v1_13)) { + try { + Block.class.getMethod("setData", byte.class).invoke(b, pick.getData()); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + } + } + } + mine.setResetting(false); + mine.updateCurrentBlocks(); + mine.updateHolograms(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/ResetType.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/ResetType.java new file mode 100644 index 0000000..7d5ece0 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/reset/ResetType.java @@ -0,0 +1,30 @@ +package dev.drawethree.xprison.mines.model.mine.reset; + +import dev.drawethree.xprison.mines.model.mine.BlockPalette; +import dev.drawethree.xprison.mines.model.mine.Mine; +import lombok.Getter; + +public abstract class ResetType { + + public static final GradualReset GRADUAL = new GradualReset(); + public static final InstantReset INSTANT = new InstantReset(); + + @Getter + private final String name; + + ResetType(String paramString) { + this.name = paramString; + } + + public static ResetType of(String name) { + switch (name.toLowerCase()) { + case "gradual": + return GRADUAL; + case "instant": + return INSTANT; + } + return null; + } + + public abstract void reset(Mine paramMine, BlockPalette blockPalette); +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/saver/MineFileSaver.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/saver/MineFileSaver.java new file mode 100644 index 0000000..a9be0b6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/saver/MineFileSaver.java @@ -0,0 +1,26 @@ +package dev.drawethree.xprison.mines.model.mine.saver; + +import dev.drawethree.xprison.mines.managers.MineManager; +import dev.drawethree.xprison.mines.model.mine.Mine; +import me.lucko.helper.gson.GsonProvider; + +import java.io.FileWriter; +import java.io.IOException; + +public class MineFileSaver implements MineSaver { + + private final MineManager manager; + + public MineFileSaver(MineManager manager) { + this.manager = manager; + } + + @Override + public void save(Mine mine) { + try (FileWriter writer = new FileWriter(mine.getFile())) { + GsonProvider.writeObjectPretty(writer, mine.serialize().getAsJsonObject()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/mines/model/mine/saver/MineSaver.java b/src/main/java/dev/drawethree/xprison/mines/model/mine/saver/MineSaver.java new file mode 100644 index 0000000..289f812 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/mines/model/mine/saver/MineSaver.java @@ -0,0 +1,7 @@ +package dev.drawethree.xprison.mines.model.mine.saver; + +import dev.drawethree.xprison.mines.model.mine.Mine; + +public interface MineSaver { + void save(Mine mine); +} diff --git a/src/main/java/dev/drawethree/xprison/multipliers/XPrisonMultipliers.java b/src/main/java/dev/drawethree/xprison/multipliers/XPrisonMultipliers.java new file mode 100644 index 0000000..d3edc76 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/multipliers/XPrisonMultipliers.java @@ -0,0 +1,615 @@ +package dev.drawethree.xprison.multipliers; + +import dev.drawethree.ultrabackpacks.api.event.BackpackSellEvent; +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.multipliers.api.XPrisonMultipliersAPI; +import dev.drawethree.xprison.multipliers.api.XPrisonMultipliersAPIImpl; +import dev.drawethree.xprison.multipliers.api.events.PlayerMultiplierReceiveEvent; +import dev.drawethree.xprison.multipliers.enums.MultiplierType; +import dev.drawethree.xprison.multipliers.multiplier.GlobalMultiplier; +import dev.drawethree.xprison.multipliers.multiplier.Multiplier; +import dev.drawethree.xprison.multipliers.multiplier.PlayerMultiplier; +import dev.drawethree.xprison.multipliers.repo.MultipliersRepository; +import dev.drawethree.xprison.multipliers.repo.impl.MultipliersRepositoryImpl; +import dev.drawethree.xprison.multipliers.service.MultipliersService; +import dev.drawethree.xprison.multipliers.service.impl.MultipliersServiceImpl; +import dev.drawethree.xprison.tokens.api.events.PlayerTokensReceiveEvent; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import dev.drawethree.xprison.utils.text.TextUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.event.filter.EventFilters; +import me.lucko.helper.scheduler.Task; +import me.lucko.helper.time.Time; +import me.lucko.helper.utils.Players; +import org.apache.commons.lang.StringUtils; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public final class XPrisonMultipliers implements XPrisonModule { + + public static final String MODULE_NAME = "Multipliers"; + + @Getter + private static XPrisonMultipliers instance; + @Getter + private final XPrison core; + @Getter + private FileManager.Config config; + @Getter + private XPrisonMultipliersAPI api; + private GlobalMultiplier globalSellMultiplier; + private GlobalMultiplier globalTokenMultiplier; + + private Map rankMultipliers; + private Map sellMultipliers; + private Map tokenMultipliers; + + private Map messages; + private Map permissionToMultiplier; + + private boolean enabled; + + private Task rankUpdateTask; + private int rankMultiplierUpdateTime; + + @Getter + private double globalSellMultiMax; + @Getter + private double globalTokenMultiMax; + @Getter + private double playerSellMultiMax; + @Getter + private double playerTokenMultiMax; + + @Getter + private MultipliersRepository multipliersRepository; + + @Getter + private MultipliersService multipliersService; + + public XPrisonMultipliers(XPrison plugin) { + instance = this; + this.core = plugin; + } + + + private void loadRankMultipliers() { + this.permissionToMultiplier = new LinkedHashMap<>(); + + ConfigurationSection section = getConfig().get().getConfigurationSection("ranks"); + + if (section == null) { + return; + } + + boolean useLuckPerms = getConfig().get().getBoolean("use-luckperms-groups", false); + + String permPrefix = useLuckPerms ? "group." : "xprison.multiplier."; + + for (String rank : section.getKeys(false)) { + String perm = permPrefix + rank; + double multiplier = getConfig().get().getDouble("ranks." + rank); + this.permissionToMultiplier.put(perm, multiplier); + this.core.debug("Loaded Rank Multiplier '" + rank + "' with multiplier x" + multiplier + " (" + perm + ")", this); + } + } + + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.config.reload(); + + this.loadMessages(); + this.loadRankMultipliers(); + + this.rankMultiplierUpdateTime = this.getConfig().get().getInt("rank-multiplier-update-time", 5); + this.globalSellMultiMax = this.getConfig().get().getDouble("global-multiplier.sell.max", 10.0); + this.globalTokenMultiMax = this.getConfig().get().getDouble("global-multiplier.tokens.max", 10.0); + this.playerSellMultiMax = this.getConfig().get().getDouble("sell-multiplier.max", 10.0); + this.playerTokenMultiMax = this.getConfig().get().getDouble("token-multiplier.max", 10.0); + } + + @Override + public void enable() { + + this.enabled = true; + this.config = this.core.getFileManager().getConfig("multipliers.yml").copyDefaults(true).save(); + + this.rankMultipliers = new ConcurrentHashMap<>(); + this.sellMultipliers = new ConcurrentHashMap<>(); + this.tokenMultipliers = new ConcurrentHashMap<>(); + + this.multipliersRepository = new MultipliersRepositoryImpl(this.core.getPluginDatabase()); + this.multipliersRepository.createTables(); + this.multipliersRepository.removeExpiredMultipliers(); + + this.multipliersService = new MultipliersServiceImpl(this.multipliersRepository); + + this.rankMultiplierUpdateTime = this.getConfig().get().getInt("rank-multiplier-update-time", 5); + this.globalSellMultiMax = this.getConfig().get().getDouble("global-multiplier.sell.max", 10.0); + this.globalTokenMultiMax = this.getConfig().get().getDouble("global-multiplier.tokens.max", 10.0); + this.playerSellMultiMax = this.getConfig().get().getDouble("sell-multiplier.max", 10.0); + this.playerTokenMultiMax = this.getConfig().get().getDouble("token-multiplier.max", 10.0); + + this.loadMessages(); + this.loadRankMultipliers(); + this.registerCommands(); + this.registerEvents(); + this.removeExpiredMultipliers(); + this.loadGlobalMultipliers(); + this.loadOnlineMultipliers(); + this.api = new XPrisonMultipliersAPIImpl(this); + + this.rankUpdateTask = Schedulers.async().runRepeating(() -> Players.all().forEach(p -> this.rankMultipliers.put(p.getUniqueId(), this.calculateRankMultiplier(p))), this.rankMultiplierUpdateTime, TimeUnit.MINUTES, this.rankMultiplierUpdateTime, TimeUnit.MINUTES); + } + + private void loadOnlineMultipliers() { + Players.all().forEach(p -> { + this.rankMultipliers.put(p.getUniqueId(), this.calculateRankMultiplier(p)); + this.loadSellMultiplier(p); + this.loadTokenMultiplier(p); + }); + } + + private void registerEvents() { + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> { + this.rankMultipliers.put(e.getPlayer().getUniqueId(), this.calculateRankMultiplier(e.getPlayer())); + this.loadSellMultiplier(e.getPlayer()); + this.loadTokenMultiplier(e.getPlayer()); + }).bindWith(core); + Events.subscribe(PlayerQuitEvent.class) + .handler(e -> { + this.rankMultipliers.remove(e.getPlayer().getUniqueId()); + this.saveSellMultiplier(e.getPlayer(), true); + this.saveTokenMultiplier(e.getPlayer(), true); + }).bindWith(core); + + Events.subscribe(PlayerTokensReceiveEvent.class, EventPriority.HIGHEST) + .filter(EventFilters.ignoreCancelled()) + .handler(e -> { + OfflinePlayer p = e.getPlayer(); + if (p.isOnline() && e.getCause() == ReceiveCause.MINING) { + e.setAmount((long) this.getApi().getTotalToDeposit((Player) p, e.getAmount(), MultiplierType.TOKENS)); + } + }).bindWith(core); + + if (this.core.isUltraBackpacksEnabled()) { + Events.subscribe(BackpackSellEvent.class, EventPriority.NORMAL) + .handler(e -> { + double currentAmount = e.getMoneyToDeposit(); + this.core.debug("BackpacksSellEvent >> Original Amount: " + currentAmount, this); + double newAmount = this.getApi().getTotalToDeposit(e.getPlayer(), currentAmount, MultiplierType.SELL); + this.core.debug("BackpacksSellEvent >> New Amount: " + newAmount, this); + e.setMoneyToDeposit(newAmount); + }).bindWith(core); + } + + } + + private void saveSellMultiplier(Player player, boolean async) { + + PlayerMultiplier multiplier = this.sellMultipliers.remove(player.getUniqueId()); + + if (async) { + Schedulers.async().run(() -> { + this.multipliersService.setSellMultiplier(player, multiplier); + this.core.debug(String.format("Saved Sell Multiplier of player %s", player.getName()), this); + }); + } else { + this.multipliersService.setSellMultiplier(player, multiplier); + this.core.debug(String.format("Saved Sell Multiplier of player %s", player.getName()), this); + } + } + + private void saveTokenMultiplier(Player player, boolean async) { + + PlayerMultiplier multiplier = this.tokenMultipliers.remove(player.getUniqueId()); + + if (async) { + Schedulers.async().run(() -> { + this.multipliersService.setTokenMultiplier(player, multiplier); + this.core.debug(String.format("Saved Token Multiplier of player %s", player.getName()), this); + }); + } else { + this.multipliersService.setTokenMultiplier(player, multiplier); + this.core.debug(String.format("Saved Token Multiplier of player %s", player.getName()), this); + } + } + + private void loadGlobalMultipliers() { + double multiSell = this.config.get().getDouble("global-multiplier.sell.multiplier"); + long timeLeftSell = this.config.get().getLong("global-multiplier.sell.timeLeft"); + + double multiTokens = this.config.get().getDouble("global-multiplier.tokens.multiplier"); + long timeLeftTokens = this.config.get().getLong("global-multiplier.tokens.timeLeft"); + + this.globalSellMultiplier = new GlobalMultiplier(0.0, 0); + this.globalTokenMultiplier = new GlobalMultiplier(0.0, 0); + + if (timeLeftSell > Time.nowMillis()) { + this.globalSellMultiplier.setEndTime(timeLeftSell); + this.globalSellMultiplier.setMultiplier(multiSell); + } + + if (timeLeftTokens > Time.nowMillis()) { + this.globalTokenMultiplier.setEndTime(timeLeftSell); + this.globalTokenMultiplier.setMultiplier(multiTokens); + } + + this.core.getLogger().info(String.format("Loaded Global Sell Multiplier %.2fx", multiSell)); + this.core.getLogger().info(String.format("Loaded Global Token Multiplier %.2fx", multiTokens)); + + } + + private void saveGlobalMultipliers() { + this.config.set("global-multiplier.sell.multiplier", this.globalSellMultiplier.getMultiplier()); + this.config.set("global-multiplier.sell.timeLeft", this.globalSellMultiplier.getEndTime()); + this.config.set("global-multiplier.tokens.multiplier", this.globalTokenMultiplier.getMultiplier()); + this.config.set("global-multiplier.tokens.timeLeft", this.globalTokenMultiplier.getEndTime()); + this.config.save(); + this.core.getLogger().info("Saved Global Multipliers into multipliers.yml"); + } + + private void loadSellMultiplier(Player player) { + Schedulers.async().run(() -> { + + PlayerMultiplier multiplier = this.multipliersService.getSellMultiplier(player); + + if (multiplier == null) { + return; + } + + this.sellMultipliers.put(player.getUniqueId(), multiplier); + + this.core.debug(String.format("Loaded Sell Multiplier %.2fx for player %s", multiplier.getMultiplier(), player.getName()), this); + }); + } + + private void loadTokenMultiplier(Player player) { + Schedulers.async().run(() -> { + + PlayerMultiplier multiplier = this.multipliersService.getTokenMultiplier(player); + + if (multiplier == null) { + return; + } + + this.tokenMultipliers.put(player.getUniqueId(), multiplier); + + this.core.debug(String.format("Loaded Token Multiplier %.2fx for player %s", multiplier.getMultiplier(), player.getName()), this); + }); + } + + private void removeExpiredMultipliers() { + Schedulers.async().run(() -> { + this.multipliersService.removeExpiredMultipliers(); + this.core.debug("Removed expired multipliers from DB.", this); + }); + } + + + @Override + public void disable() { + this.saveAllMultipliers(); + this.rankUpdateTask.stop(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.multipliersRepository.clearTableData(); + } + + private void saveAllMultipliers() { + Players.all().forEach(p -> { + saveSellMultiplier(p, false); + saveTokenMultiplier(p, false); + }); + this.saveGlobalMultipliers(); + this.core.getLogger().info("Saved online players multipliers."); + } + + + private void loadMessages() { + messages = new HashMap<>(); + for (String key : getConfig().get().getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(getConfig().get().getString("messages." + key))); + } + } + + private void registerCommands() { + Commands.create() + .assertPermission("xprison.multipliers.admin") + .handler(c -> { + if (c.args().size() == 4) { + String type = c.rawArg(0); + double amount = c.arg(1).parseOrFail(Double.class); + int duration = c.arg(2).parseOrFail(Integer.class); + TimeUnit timeUnit; + try { + timeUnit = TimeUnit.valueOf(c.rawArg(3).toUpperCase()); + } catch (IllegalArgumentException e) { + PlayerUtils.sendMessage(c.sender(), "&cInvalid time unit! Please use one from: " + StringUtils.join(TimeUnit.values(), ",")); + return; + } + + setupGlobalMultiplier(c.sender(), type, duration, timeUnit, amount); + } else if (c.args().size() == 2 && c.rawArg(1).equalsIgnoreCase("reset")) { + String type = c.rawArg(0); + resetGlobalMultiplier(c.sender(), type); + } else { + PlayerUtils.sendMessage(c.sender(), "&cInvalid usage!"); + PlayerUtils.sendMessage(c.sender(), "&c/gmulti

+ * For convienience do we return the version from the plugin.yml + * + * @return The version as a String. + */ + @Override + public String getVersion() { + return plugin.getDescription().getVersion(); + } + + /** + * This is the method called when a placeholder with our identifier + * is found and needs a value. + *
We specify the value identifier in this method. + *
Since version 2.9.1 can you use OfflinePlayers in your requests. + * + * @param player + * @param identifier A String containing the identifier/value. + * @return possibly-null String of the requested identifier. + */ + @Override + public String onPlaceholderRequest(Player player, String identifier) { + + if (player == null) { + return null; + } + + if (identifier.startsWith("mine_")) { + + String mineName = identifier.replace("mine_", "").split("_")[0]; + Mine mine = plugin.getMines().getManager().getMineByName(mineName); + + if (mine == null) { + return null; + } + + String placeholder = identifier.replace("mine_" + mineName + "_", ""); + switch (placeholder.toLowerCase()) { + case "blocks_left": { + return String.format("%,d", mine.getCurrentBlocks()); + } + case "blocks_left_percentage": { + return String.format("%,.2f", (double) mine.getCurrentBlocks() / mine.getTotalBlocks() * 100.0D); + } + case "reset_time": { + return TimeUtil.getTime(mine.getSecondsToNextReset()); + } + } + } else if (identifier.startsWith("token_has_")) { + long amount = Long.parseLong(identifier.replace("token_has_", "")); + return String.valueOf(plugin.getTokens().getTokensManager().getPlayerTokens(player) >= amount); + } + + switch (identifier.toLowerCase()) { + case "tokens": + case "tokens_2": + return String.format("%,d", plugin.getTokens().getTokensManager().getPlayerTokens(player)); + case "gems": + case "gems_2": + return String.format("%,d", plugin.getGems().getGemsManager().getPlayerGems(player)); + case "blocks": + case "blocks_2": + return String.format("%,d", plugin.getTokens().getTokensManager().getPlayerBrokenBlocks(player)); + case "multiplier_sell": { + PlayerMultiplier sellMulti = plugin.getMultipliers().getApi().getSellMultiplier(player); + if (sellMulti == null || sellMulti.isExpired()) { + return String.format("%.2f", 0.0); + } else { + return String.format("%.2f", (1.0 + sellMulti.getMultiplier())); + } + } + case "multiplier_token": { + PlayerMultiplier tokenMulti = plugin.getMultipliers().getApi().getTokenMultiplier(player); + if (tokenMulti == null || tokenMulti.isExpired()) { + return String.format("%.2f", 0.0); + } else { + return String.format("%.2f", (1.0 + tokenMulti.getMultiplier())); + } + } + case "multiplier_rank": { + Multiplier rankMulti = plugin.getMultipliers().getApi().getRankMultiplier(player); + if (rankMulti == null) { + return String.format("%.2f", 0.0); + } else { + return String.format("%.2f", rankMulti.getMultiplier()); + } + } + case "multiplier_global_sell": { + GlobalMultiplier sellMulti = plugin.getMultipliers().getApi().getGlobalSellMultiplier(); + return String.format("%.2f", sellMulti.isExpired() ? 0.0 : sellMulti.getMultiplier()); + } + case "multiplier_global_token": { + GlobalMultiplier tokenMulti = plugin.getMultipliers().getApi().getGlobalTokenMultiplier(); + return String.format("%.2f", tokenMulti.isExpired() ? 0.0 : tokenMulti.getMultiplier()); + } + case "rank": + return plugin.getRanks().getApi().getPlayerRank(player).getPrefix(); + case "next_rank": { + Optional nextRank = plugin.getRanks().getApi().getNextPlayerRank(player); + return !nextRank.isPresent() ? "" : nextRank.get().getPrefix(); + } + case "next_rank_cost_raw": + return String.valueOf(plugin.getRanks().getRanksManager().getNextRankCost(player)); + case "next_rank_cost": + return String.format("%,.2f", plugin.getRanks().getRanksManager().getNextRankCost(player)); + case "next_rank_cost_formatted": + return MathUtils.formatNumber(plugin.getRanks().getRanksManager().getNextRankCost(player)); + case "prestige": + return plugin.getPrestiges().getApi().getPlayerPrestige(player).getPrefix(); + case "prestige_id": + return String.valueOf(plugin.getPrestiges().getApi().getPlayerPrestige(player).getId()); + case "autominer_time": { + int autominerTime = plugin.getAutoMiner().getManager().getAutoMinerTime(player); + return AutoMinerUtils.getAutoMinerTimeLeftFormatted(autominerTime); + } + case "tokens_formatted": + case "tokens_3": + return MathUtils.formatNumber(plugin.getTokens().getTokensManager().getPlayerTokens(player)); + case "gems_formatted": + case "gems_3": + return MathUtils.formatNumber(plugin.getGems().getGemsManager().getPlayerGems(player)); + case "rankup_progress": + return String.format("%d%%", plugin.getRanks().getRanksManager().getRankupProgress(player)); + case "rankup_progress_bar": + return plugin.getRanks().getRanksManager().getRankupProgressBar(player); + case "tokens_1": + return String.valueOf(plugin.getTokens().getTokensManager().getPlayerTokens(player)); + case "blocks_1": + return String.valueOf(plugin.getTokens().getTokensManager().getPlayerBrokenBlocks(player)); + case "blocks_3": + return MathUtils.formatNumber(plugin.getTokens().getTokensManager().getPlayerBrokenBlocks(player)); + case "gems_1": + return String.valueOf(plugin.getGems().getGemsManager().getPlayerGems(player)); + case "pickaxe_level": { + Optional levelOptional = plugin.getPickaxeLevels().getApi().getPickaxeLevel(player); + return levelOptional.map(level -> String.valueOf(level.getLevel())).orElse("0"); + } + case "pickaxe_progress": + return this.plugin.getPickaxeLevels().getApi().getProgressBar(player); + case "gang_name": + case "gang": { + Optional optionalGang = this.plugin.getGangs().getGangsManager().getPlayerGang(player); + return optionalGang.map(gang -> this.plugin.getGangs().getConfig().getPlaceholder("gang-in-gang").replace("%gang%", gang.getName())).orElseGet(() -> this.plugin.getGangs().getConfig().getPlaceholder("gang-without")); + } + case "gang_value": { + Optional optionalGang = this.plugin.getGangs().getGangsManager().getPlayerGang(player); + if (optionalGang.isPresent()) { + return String.format("%,d", optionalGang.get().getValue()); + } else { + return ""; + } + } + case "gang_has_gang": + return this.plugin.getGangs().getGangsManager().getPlayerGang(player).isPresent() ? "Yes" : "No"; + case "gang_is_leader": { + Optional optionalGang = this.plugin.getGangs().getGangsManager().getPlayerGang(player); + return optionalGang.map(gang -> gang.isOwner(player) ? "Yes" : "No").orElse(""); + } + case "gang_leader_name": { + Optional optionalGang = this.plugin.getGangs().getGangsManager().getPlayerGang(player); + if (optionalGang.isPresent()) { + return optionalGang.get().getOwnerOffline().getName(); + } + return ""; + } + case "gang_members_amount": { + Optional optionalGang = this.plugin.getGangs().getGangsManager().getPlayerGang(player); + // +1 because of leader + return optionalGang.map(gang -> String.valueOf(gang.getMembersOffline().size() + 1)).orElse(""); + } + case "gang_members_online": { + Optional optionalGang = this.plugin.getGangs().getGangsManager().getPlayerGang(player); + return optionalGang.map(gang -> String.valueOf(gang.getOnlinePlayers().size())).orElse(""); + } + default: + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/prestiges/XPrisonPrestiges.java b/src/main/java/dev/drawethree/xprison/prestiges/XPrisonPrestiges.java new file mode 100644 index 0000000..c2359d8 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/XPrisonPrestiges.java @@ -0,0 +1,118 @@ +package dev.drawethree.xprison.prestiges; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.prestiges.api.XPrisonPrestigesAPI; +import dev.drawethree.xprison.prestiges.api.XPrisonPrestigesAPIImpl; +import dev.drawethree.xprison.prestiges.commands.MaxPrestigeCommand; +import dev.drawethree.xprison.prestiges.commands.PrestigeAdminCommand; +import dev.drawethree.xprison.prestiges.commands.PrestigeCommand; +import dev.drawethree.xprison.prestiges.commands.PrestigeTopCommand; +import dev.drawethree.xprison.prestiges.config.PrestigeConfig; +import dev.drawethree.xprison.prestiges.listener.PrestigeListener; +import dev.drawethree.xprison.prestiges.manager.PrestigeManager; +import dev.drawethree.xprison.prestiges.repo.PrestigeRepository; +import dev.drawethree.xprison.prestiges.repo.impl.PrestigeRepositoryImpl; +import dev.drawethree.xprison.prestiges.service.PrestigeService; +import dev.drawethree.xprison.prestiges.service.impl.PrestigeServiceImpl; +import dev.drawethree.xprison.prestiges.task.SavePlayerDataTask; +import lombok.Getter; + +@Getter +public final class XPrisonPrestiges implements XPrisonModule { + + public static final String MODULE_NAME = "Prestiges"; + + @Getter + private PrestigeConfig prestigeConfig; + + private PrestigeManager prestigeManager; + + @Getter + private XPrisonPrestigesAPI api; + + private SavePlayerDataTask savePlayerDataTask; + + @Getter + private final XPrison core; + + @Getter + private PrestigeRepository prestigeRepository; + + @Getter + private PrestigeService prestigeService; + + private boolean enabled; + + public XPrisonPrestiges(XPrison core) { + this.core = core; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.prestigeConfig.reload(); + } + + @Override + public void enable() { + this.enabled = true; + + this.prestigeConfig = new PrestigeConfig(this); + this.prestigeConfig.load(); + + this.prestigeRepository = new PrestigeRepositoryImpl(this.core.getPluginDatabase()); + this.prestigeRepository.createTables(); + + this.prestigeService = new PrestigeServiceImpl(this.prestigeRepository); + + this.prestigeManager = new PrestigeManager(this); + this.prestigeManager.enable(); + + this.api = new XPrisonPrestigesAPIImpl(this); + + this.savePlayerDataTask = new SavePlayerDataTask(this); + this.savePlayerDataTask.start(); + + this.registerCommands(); + this.registerListeners(); + } + + + @Override + public void disable() { + this.savePlayerDataTask.stop(); + this.prestigeManager.disable(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.prestigeRepository.clearTableData(); + } + + private void registerCommands() { + new PrestigeCommand(this).register(); + new MaxPrestigeCommand(this).register(); + new PrestigeTopCommand(this).register(); + new PrestigeAdminCommand(this).register(); + } + + private void registerListeners() { + new PrestigeListener(this).register(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/api/XPrisonPrestigesAPI.java b/src/main/java/dev/drawethree/xprison/prestiges/api/XPrisonPrestigesAPI.java new file mode 100644 index 0000000..19d8061 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/api/XPrisonPrestigesAPI.java @@ -0,0 +1,33 @@ +package dev.drawethree.xprison.prestiges.api; + + +import dev.drawethree.xprison.prestiges.model.Prestige; +import org.bukkit.entity.Player; + +public interface XPrisonPrestigesAPI { + + /** + * Method to get player Prestige + * + * @param p Player + * @return Prestige + */ + Prestige getPlayerPrestige(Player p); + + /** + * Sets a prestige to online player + * + * @param player Player + * @param prestige Prestige + */ + void setPlayerPrestige(Player player, Prestige prestige); + + /** + * Sets a prestige to online player + * + * @param player Player + * @param prestige Prestige + */ + void setPlayerPrestige(Player player, long prestige); + +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/api/XPrisonPrestigesAPIImpl.java b/src/main/java/dev/drawethree/xprison/prestiges/api/XPrisonPrestigesAPIImpl.java new file mode 100644 index 0000000..e4dfd6f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/api/XPrisonPrestigesAPIImpl.java @@ -0,0 +1,30 @@ +package dev.drawethree.xprison.prestiges.api; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.prestiges.model.Prestige; +import org.bukkit.entity.Player; + +public final class XPrisonPrestigesAPIImpl implements XPrisonPrestigesAPI { + + private final XPrisonPrestiges plugin; + + public XPrisonPrestigesAPIImpl(XPrisonPrestiges plugin) { + this.plugin = plugin; + } + + @Override + public Prestige getPlayerPrestige(Player p) { + return plugin.getPrestigeManager().getPlayerPrestige(p); + } + + @Override + public void setPlayerPrestige(Player player, Prestige prestige) { + plugin.getPrestigeManager().setPlayerPrestige(null, player, prestige.getId()); + } + + @Override + public void setPlayerPrestige(Player player, long prestige) { + plugin.getPrestigeManager().setPlayerPrestige(null, player, prestige); + + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/api/events/PlayerPrestigeEvent.java b/src/main/java/dev/drawethree/xprison/prestiges/api/events/PlayerPrestigeEvent.java new file mode 100644 index 0000000..3627ee5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/api/events/PlayerPrestigeEvent.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.prestiges.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import dev.drawethree.xprison.prestiges.model.Prestige; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class PlayerPrestigeEvent extends XPrisonPlayerEvent implements Cancellable { + + + private static final HandlerList handlers = new HandlerList(); + + @Getter + private final Prestige oldPrestige; + + @Getter + @Setter + private Prestige newPrestige; + + @Getter + @Setter + private boolean cancelled; + + /** + * Called when player receive gems + * + * @param player Player + * @param oldPrestige old prestige + * @param newPrestige new prestige + */ + public PlayerPrestigeEvent(Player player, Prestige oldPrestige, Prestige newPrestige) { + super(player); + this.oldPrestige = oldPrestige; + this.newPrestige = newPrestige; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/commands/MaxPrestigeCommand.java b/src/main/java/dev/drawethree/xprison/prestiges/commands/MaxPrestigeCommand.java new file mode 100644 index 0000000..65880a1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/commands/MaxPrestigeCommand.java @@ -0,0 +1,30 @@ +package dev.drawethree.xprison.prestiges.commands; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import me.lucko.helper.Commands; + +public class MaxPrestigeCommand { + + private final XPrisonPrestiges plugin; + + public MaxPrestigeCommand(XPrisonPrestiges plugin) { + + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPermission("xprison.prestiges.maxprestige", this.plugin.getPrestigeConfig().getMessage("no_permission")) + .assertPlayer() + .handler(c -> { + if (c.args().size() == 0) { + + if (this.plugin.getPrestigeManager().isPrestiging(c.sender())) { + return; + } + + this.plugin.getPrestigeManager().buyMaxPrestige(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), "maxprestige", "maxp", "mp"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeAdminCommand.java b/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeAdminCommand.java new file mode 100644 index 0000000..ab74f38 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeAdminCommand.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.prestiges.commands; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; + +public class PrestigeAdminCommand { + private final XPrisonPrestiges plugin; + + public PrestigeAdminCommand(XPrisonPrestiges plugin) { + + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPermission("xprison.prestiges.admin") + .handler(c -> { + if (c.args().size() == 3) { + + Player target = c.arg(1).parseOrFail(Player.class); + int amount = c.arg(2).parseOrFail(Integer.class); + + switch (c.rawArg(0).toLowerCase()) { + case "set": + this.plugin.getPrestigeManager().setPlayerPrestige(c.sender(), target, amount); + break; + case "add": + this.plugin.getPrestigeManager().addPlayerPrestige(c.sender(), target, amount); + break; + case "remove": + this.plugin.getPrestigeManager().removePlayerPrestige(c.sender(), target, amount); + break; + default: + PlayerUtils.sendMessage(c.sender(), "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + PlayerUtils.sendMessage(c.sender(), "&e&lPRESTIGE ADMIN HELP MENU "); + PlayerUtils.sendMessage(c.sender(), "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + PlayerUtils.sendMessage(c.sender(), "&e/prestigeadmin add [player] [amount]"); + PlayerUtils.sendMessage(c.sender(), "&e/prestigeadmin remove [player] [amount]"); + PlayerUtils.sendMessage(c.sender(), "&e/prestigeadmin set [player] [amount]"); + PlayerUtils.sendMessage(c.sender(), "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + break; + } + } + }).registerAndBind(this.plugin.getCore(), "prestigeadmin", "prestigea"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeCommand.java b/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeCommand.java new file mode 100644 index 0000000..891805e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeCommand.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.prestiges.commands; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import me.lucko.helper.Commands; + +public class PrestigeCommand { + + private final XPrisonPrestiges plugin; + + public PrestigeCommand(XPrisonPrestiges plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getPrestigeManager().buyNextPrestige(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), "prestige"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeTopCommand.java b/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeTopCommand.java new file mode 100644 index 0000000..0255eb3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/commands/PrestigeTopCommand.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.prestiges.commands; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import me.lucko.helper.Commands; + +public class PrestigeTopCommand { + + private final XPrisonPrestiges plugin; + + public PrestigeTopCommand(XPrisonPrestiges plugin) { + + this.plugin = plugin; + } + + public void register() { + Commands.create() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getPrestigeManager().sendPrestigeTop(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), "prestigetop"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/config/PrestigeConfig.java b/src/main/java/dev/drawethree/xprison/prestiges/config/PrestigeConfig.java new file mode 100644 index 0000000..8611318 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/config/PrestigeConfig.java @@ -0,0 +1,203 @@ +package dev.drawethree.xprison.prestiges.config; + +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.prestiges.model.Prestige; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class PrestigeConfig { + + private final XPrisonPrestiges plugin; + private final FileManager.Config config; + private Prestige maxPrestige; + private String unlimitedPrestigePrefix; + private List prestigeTopFormat; + private List unlimitedPrestigesRewardPerPrestige; + private Map prestigeById; + private Map messages; + private Map> unlimitedPrestigesRewards; + private int topPlayersAmount; + private long unlimitedPrestigeMax; + private double unlimitedPrestigeCost; + private double increaseCostBy; + private boolean useTokensCurrency; + private boolean unlimitedPrestiges; + private boolean increaseCostEnabled; + private boolean resetRankAfterPrestige; + private int savePlayerDataInterval; + + public PrestigeConfig(XPrisonPrestiges plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("prestiges.yml").copyDefaults(true).save(); + this.prestigeById = new HashMap<>(); + } + + + private void loadMessages(YamlConfiguration configuration) { + this.messages = new HashMap<>(); + + for (String key : configuration.getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(getConfig().get().getString("messages." + key))); + } + } + + public void reload() { + YamlConfiguration configuration = getYamlConfig(); + this.loadVariables(configuration); + this.loadPrestiges(configuration); + this.loadUnlimitedPrestigesRewards(configuration); + this.loadMessages(configuration); + } + + private void loadPrestiges(YamlConfiguration configuration) { + this.prestigeById.clear(); + + if (this.unlimitedPrestiges) { + this.plugin.getCore().getLogger().info(String.format("Loaded %,d prestiges.", this.unlimitedPrestigeMax)); + } else { + for (String key : configuration.getConfigurationSection("Prestige").getKeys(false)) { + long id = Long.parseLong(key); + String prefix = TextUtils.applyColor(configuration.getString("Prestige." + key + ".Prefix")); + long cost = configuration.getLong("Prestige." + key + ".Cost"); + List commands = configuration.getStringList("Prestige." + key + ".CMD"); + Prestige p = new Prestige(id, cost, prefix, commands); + this.prestigeById.put(id, p); + this.maxPrestige = p; + } + this.plugin.getCore().getLogger().info(String.format("Loaded %,d prestiges!", this.prestigeById.keySet().size())); + } + } + + public void load() { + this.reload(); + } + + + public String getMessage(String messageKey) { + return this.messages.getOrDefault(messageKey.toLowerCase(), "Missing message with key: " + messageKey); + } + + private void loadVariables(YamlConfiguration configuration) { + this.prestigeTopFormat = configuration.getStringList("prestige-top-format"); + this.unlimitedPrestiges = configuration.getBoolean("unlimited_prestiges.enabled"); + this.unlimitedPrestigeCost = configuration.getDouble("unlimited_prestiges.prestige_cost"); + this.unlimitedPrestigePrefix = TextUtils.applyColor(configuration.getString("unlimited_prestiges.prefix")); + this.unlimitedPrestigeMax = configuration.getLong("unlimited_prestiges.max_prestige"); + this.increaseCostEnabled = configuration.getBoolean("unlimited_prestiges.increase_cost.enabled"); + this.increaseCostBy = configuration.getDouble("unlimited_prestiges.increase_cost.increase_cost_by"); + boolean unlimitedPrestigesRewardPerPrestigeEnabled = configuration.getBoolean("unlimited_prestiges.rewards-per-prestige.enabled"); + if (unlimitedPrestigesRewardPerPrestigeEnabled) { + this.unlimitedPrestigesRewardPerPrestige = configuration.getStringList("unlimited_prestiges.rewards-per-prestige.rewards"); + } + this.topPlayersAmount = configuration.getInt("top_players_amount"); + this.savePlayerDataInterval = configuration.getInt("player_data_save_interval"); + this.resetRankAfterPrestige = configuration.getBoolean("reset_rank_after_prestige"); + this.useTokensCurrency = configuration.getBoolean("use_tokens_currency"); + this.plugin.getCore().getLogger().info("Using " + (useTokensCurrency ? "Tokens" : "Money") + " currency for Prestiges."); + + } + + private void loadUnlimitedPrestigesRewards(YamlConfiguration configuration) { + this.unlimitedPrestigesRewards = new LinkedHashMap<>(); + + ConfigurationSection section = configuration.getConfigurationSection("unlimited_prestiges.rewards"); + + if (section == null) { + return; + } + + for (String key : section.getKeys(false)) { + try { + long id = Long.parseLong(key); + + List rewards = section.getStringList(key); + + if (!rewards.isEmpty()) { + this.unlimitedPrestigesRewards.put(id, rewards); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public Prestige getMaxPrestige() { + return maxPrestige; + } + + public String getUnlimitedPrestigePrefix() { + return unlimitedPrestigePrefix; + } + + public List getPrestigeTopFormat() { + return prestigeTopFormat; + } + + public List getUnlimitedPrestigesRewardPerPrestige() { + return unlimitedPrestigesRewardPerPrestige; + } + + public Map getPrestigeById() { + return prestigeById; + } + + public Map getMessages() { + return messages; + } + + public Map> getUnlimitedPrestigesRewards() { + return unlimitedPrestigesRewards; + } + + public long getUnlimitedPrestigeMax() { + return unlimitedPrestigeMax; + } + + public double getUnlimitedPrestigeCost() { + return unlimitedPrestigeCost; + } + + public double getIncreaseCostBy() { + return increaseCostBy; + } + + public boolean isUseTokensCurrency() { + return useTokensCurrency; + } + + public boolean isUnlimitedPrestiges() { + return unlimitedPrestiges; + } + + public boolean isIncreaseCostEnabled() { + return increaseCostEnabled; + } + + public boolean isResetRankAfterPrestige() { + return resetRankAfterPrestige; + } + + public int getTopPlayersAmount() { + return topPlayersAmount; + } + + public long getSavePlayerDataInterval() { + return savePlayerDataInterval; + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/listener/PrestigeListener.java b/src/main/java/dev/drawethree/xprison/prestiges/listener/PrestigeListener.java new file mode 100644 index 0000000..feae162 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/listener/PrestigeListener.java @@ -0,0 +1,31 @@ +package dev.drawethree.xprison.prestiges.listener; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import me.lucko.helper.Events; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PrestigeListener { + private final XPrisonPrestiges plugin; + + public PrestigeListener(XPrisonPrestiges plugin) { + this.plugin = plugin; + } + + public void register() { + this.subscribePlayerJoinEvent(); + this.subscribePlayerQuitEvent(); + } + + private void subscribePlayerQuitEvent() { + Events.subscribe(PlayerQuitEvent.class) + .handler(e -> this.plugin.getPrestigeManager().savePlayerData(e.getPlayer(), true, true)).bindWith(plugin.getCore()); + } + + private void subscribePlayerJoinEvent() { + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> this.plugin.getPrestigeManager().loadPlayerPrestige(e.getPlayer())).bindWith(plugin.getCore()); + } + + +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/manager/PrestigeManager.java b/src/main/java/dev/drawethree/xprison/prestiges/manager/PrestigeManager.java new file mode 100644 index 0000000..c41a3ee --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/manager/PrestigeManager.java @@ -0,0 +1,445 @@ +package dev.drawethree.xprison.prestiges.manager; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.prestiges.api.events.PlayerPrestigeEvent; +import dev.drawethree.xprison.prestiges.config.PrestigeConfig; +import dev.drawethree.xprison.prestiges.model.Prestige; +import dev.drawethree.xprison.ranks.XPrisonRanks; +import dev.drawethree.xprison.ranks.manager.RanksManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.utils.Players; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class PrestigeManager { + + private final XPrisonPrestiges plugin; + + private final Map onlinePlayersPrestige; + private final List prestigingPlayers; + + public PrestigeManager(XPrisonPrestiges plugin) { + this.plugin = plugin; + this.onlinePlayersPrestige = new ConcurrentHashMap<>(); + this.prestigingPlayers = new ArrayList<>(10); + } + + + private void saveAllDataSync() { + for (UUID uuid : this.onlinePlayersPrestige.keySet()) { + this.plugin.getPrestigeService().setPrestige(Players.getOfflineNullable(uuid), onlinePlayersPrestige.get(uuid)); + } + this.plugin.getCore().getLogger().info("Saved online players prestiges."); + } + + private void loadAllData() { + for (Player p : Players.all()) { + loadPlayerPrestige(p); + } + } + + public void savePlayerData(Player player, boolean removeFromCache, boolean async) { + if (async) { + Schedulers.async().run(() -> savePlayerDataLogic(player, removeFromCache)); + } else { + savePlayerDataLogic(player, removeFromCache); + } + } + + private void savePlayerDataLogic(Player player, boolean removeFromCache) { + this.plugin.getPrestigeService().setPrestige(player, this.getPlayerPrestige(player).getId()); + if (removeFromCache) { + this.onlinePlayersPrestige.remove(player.getUniqueId()); + } + this.plugin.getCore().debug("Saved " + player.getName() + "'s prestige to database.", this.plugin); + } + + + public void loadPlayerPrestige(Player player) { + Schedulers.async().run(() -> { + this.plugin.getPrestigeService().createPrestige(player); + long prestige = this.plugin.getPrestigeService().getPlayerPrestige(player); + this.onlinePlayersPrestige.put(player.getUniqueId(), prestige); + this.plugin.getCore().debug("Loaded " + player.getName() + "'s prestige.", this.plugin); + }); + } + + private double calculateNextPrestigeCost(Prestige origin) { + + if (!this.getConfig().isIncreaseCostEnabled()) { + return origin.getCost(); + } + + return origin.getId() == 0 ? (this.getConfig().getUnlimitedPrestigeCost()) : (origin.getCost() * this.getConfig().getIncreaseCostBy()); + } + + private PrestigeConfig getConfig() { + return this.plugin.getPrestigeConfig(); + } + + + public Prestige getNextPrestige(Prestige prestige) { + if (this.getConfig().isUnlimitedPrestiges()) { + return new Prestige(prestige.getId() + 1, this.calculateNextPrestigeCost(prestige), this.getConfig().getUnlimitedPrestigePrefix().replace("%prestige%", String.format("%,d", prestige.getId() + 1)), this.getConfig().getUnlimitedPrestigesRewards().getOrDefault(prestige.getId() + 1, null)); + } + return this.getConfig().getPrestigeById().getOrDefault(prestige.getId() + 1, null); + } + + public Prestige getPrestigeById(long id) { + if (this.getConfig().isUnlimitedPrestiges()) { + return new Prestige(id, id * this.getConfig().getIncreaseCostBy(), this.getConfig().getUnlimitedPrestigePrefix().replace("%prestige%", String.format("%,d", id)), this.getConfig().getUnlimitedPrestigesRewards().getOrDefault(id, null)); + } + return this.getConfig().getPrestigeById().getOrDefault(id, null); + } + + public synchronized Prestige getPlayerPrestige(Player p) { + if (this.getConfig().isUnlimitedPrestiges()) { + long prestige = this.onlinePlayersPrestige.getOrDefault(p.getUniqueId(), 0L); + return new Prestige(prestige, this.calculatePrestigeCost(prestige), this.getConfig().getUnlimitedPrestigePrefix().replace("%prestige%", String.format("%,d", prestige)), null); + } else { + return this.getConfig().getPrestigeById().getOrDefault(this.onlinePlayersPrestige.get(p.getUniqueId()), this.getConfig().getPrestigeById().get(0L)); + } + } + + private double calculatePrestigeCost(long prestige) { + + if (!this.getConfig().isIncreaseCostEnabled()) { + return this.getConfig().getUnlimitedPrestigeCost(); + } + + double origin = this.getConfig().getUnlimitedPrestigeCost(); + + for (long i = 0; i < prestige; i++) { + if (i == 0) { + continue; + } + origin = origin * this.getConfig().getIncreaseCostBy(); + } + + return origin; + } + + public boolean isMaxPrestige(Player p) { + if (this.getConfig().isUnlimitedPrestiges()) { + return this.getPlayerPrestige(p).getId() >= this.getConfig().getUnlimitedPrestigeMax(); + } + return this.getPlayerPrestige(p).getId() == this.getConfig().getMaxPrestige().getId(); + } + + private boolean completeTransaction(Player p, double cost) { + if (this.getConfig().isUseTokensCurrency()) { + this.plugin.getCore().getTokens().getApi().removeTokens(p, (long) cost, LostCause.RANKUP); + return true; + } else { + return this.plugin.getCore().getEconomy().withdrawPlayer(p, cost).transactionSuccess(); + } + } + + private boolean isTransactionAllowed(Player p, double cost) { + if (this.getConfig().isUseTokensCurrency()) { + return this.plugin.getCore().getTokens().getApi().hasEnough(p, (long) cost); + } else { + return this.plugin.getCore().getEconomy().has(p, cost); + } + + } + + public boolean buyNextPrestige(Player p) { + + if (areRanksEnabled() && !getRankManager().isMaxRank(p)) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("not_last_rank")); + return false; + } + + if (isMaxPrestige(p)) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("last_prestige")); + return false; + } + + Prestige currentPrestige = this.getPlayerPrestige(p); + Prestige toBuy = getNextPrestige(currentPrestige); + + if (!this.isTransactionAllowed(p, toBuy.getCost())) { + if (this.getConfig().isUseTokensCurrency()) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("not_enough_tokens_prestige").replace("%cost%", String.format("%,.0f", toBuy.getCost()))); + } else { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("not_enough_money_prestige").replace("%cost%", String.format("%,.0f", toBuy.getCost()))); + } + return false; + } + + PlayerPrestigeEvent event = new PlayerPrestigeEvent(p, currentPrestige, toBuy); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("PlayerPrestigeEvent was cancelled.", this.plugin); + return false; + } + + doPrestige(p, toBuy); + + PlayerUtils.sendMessage(p, this.getConfig().getMessage("prestige_up").replace("%Prestige%", toBuy.getPrefix())); + + return true; + } + + public void sendPrestigeTop(CommandSender sender) { + + List prestigeTopFormat = this.getConfig().getPrestigeTopFormat(); + Map topPrestige = this.plugin.getPrestigeService().getTopPrestiges(this.getConfig().getTopPlayersAmount()); + + for (String s : prestigeTopFormat) { + if (s.startsWith("{FOR_EACH_PLAYER}")) { + String rawContent = s.replace("{FOR_EACH_PLAYER} ", ""); + for (int i = 0; i < 10; i++) { + try { + UUID uuid = (UUID) topPrestige.keySet().toArray()[i]; + OfflinePlayer player = Players.getOfflineNullable(uuid); + String name; + if (player.getName() == null) { + name = "Unknown Player"; + } else { + name = player.getName(); + } + long prestige = topPrestige.get(uuid); + PlayerUtils.sendMessage(sender, rawContent.replace("%position%", String.valueOf(i + 1)).replace("%player%", name).replace("%prestige%", String.format("%,d", prestige))); + } catch (Exception e) { + break; + } + } + } else { + PlayerUtils.sendMessage(sender, s); + } + } + } + + public void buyMaxPrestige(Player p) { + + Schedulers.async().run(() -> { + if (areRanksEnabled() && !getRankManager().isMaxRank(p)) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("not_last_rank")); + return; + } + + if (isMaxPrestige(p)) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("last_prestige")); + return; + } + + Prestige startPrestige = this.getPlayerPrestige(p); + + Prestige currentPrestige = startPrestige; + + Prestige nextPrestige = this.getNextPrestige(startPrestige); + + if (!this.isTransactionAllowed(p, nextPrestige.getCost())) { + if (this.getConfig().isUseTokensCurrency()) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("not_enough_tokens_prestige").replace("%cost%", String.format("%,.0f", nextPrestige.getCost()))); + } else { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("not_enough_money_prestige").replace("%cost%", String.format("%,.0f", nextPrestige.getCost()))); + } + return; + } + + PlayerUtils.sendMessage(p, this.getConfig().getMessage("max_prestige_started")); + + this.prestigingPlayers.add(p.getUniqueId()); + while (p.isOnline() && !isMaxPrestige(p) && this.isTransactionAllowed(p, nextPrestige.getCost())) { + + if (areRanksEnabled() && !getRankManager().isMaxRank(p)) { + break; + } + + doPrestige(p, nextPrestige); + currentPrestige = nextPrestige; + nextPrestige = this.getNextPrestige(nextPrestige); + } + + PlayerPrestigeEvent event = new PlayerPrestigeEvent(p, startPrestige, currentPrestige); + + Events.callSync(event); + + this.prestigingPlayers.remove(p.getUniqueId()); + + if (startPrestige.getId() < this.onlinePlayersPrestige.get(p.getUniqueId())) { + PlayerUtils.sendMessage(p, this.getConfig().getMessage("max_prestige_done").replace("%start_prestige%", String.format("%,d", startPrestige.getId())).replace("%prestige%", String.format("%,d", this.onlinePlayersPrestige.get(p.getUniqueId())))); + } + }); + } + + private void doPrestige(Player p, Prestige nextPrestige) { + + if (!this.completeTransaction(p, nextPrestige.getCost())) { + return; + } + + this.onlinePlayersPrestige.put(p.getUniqueId(), nextPrestige.getId()); + + givePrestigeRewards(nextPrestige,p); + + List rewardsPerPrestige = this.getConfig().getUnlimitedPrestigesRewardPerPrestige(); + if (rewardsPerPrestige != null) { + if (!Bukkit.isPrimaryThread()) { + Schedulers.sync().run(() -> rewardsPerPrestige.forEach(s -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", p.getName())))); + } else { + rewardsPerPrestige.forEach(s -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", p.getName()))); + } + } + + if (this.getConfig().isResetRankAfterPrestige() && areRanksEnabled()) { + getRankManager().resetPlayerRank(p); + } + } + + public void addPlayerPrestige(CommandSender sender, Player target, int amount) { + + if (0 > amount) { + return; + } + + if (isMaxPrestige(target)) { + return; + } + + Prestige startPrestige = this.getPlayerPrestige(target); + + long maxPrestige = this.getConfig().isUnlimitedPrestiges() ? this.getConfig().getUnlimitedPrestigeMax() : this.getConfig().getMaxPrestige().getId(); + if (startPrestige.getId() + amount > maxPrestige) { + this.onlinePlayersPrestige.put(target.getUniqueId(), maxPrestige); + } else { + this.onlinePlayersPrestige.put(target.getUniqueId(), this.onlinePlayersPrestige.get(target.getUniqueId()) + amount); + } + + Prestige currentPrestige = this.getPlayerPrestige(target); + + long prestigeGained = currentPrestige.getId() - startPrestige.getId(); + + for (int i = 0; i < prestigeGained; i++) { + Prestige toGive = this.getPrestigeById(currentPrestige.getId() + 1 + i); + + if (toGive == null) { + break; + } + + givePrestigeRewards(toGive,target); + + List rewardsPerPrestige = this.getConfig().getUnlimitedPrestigesRewardPerPrestige(); + if (rewardsPerPrestige != null) { + if (!Bukkit.isPrimaryThread()) { + Schedulers.sync().run(() -> rewardsPerPrestige.forEach(s -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", target.getName())))); + } else { + rewardsPerPrestige.forEach(s -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", target.getName()))); + } + } + } + + PlayerPrestigeEvent event = new PlayerPrestigeEvent(target, startPrestige, currentPrestige); + + Events.callSync(event); + + PlayerUtils.sendMessage(sender, this.getConfig().getMessage("prestige_add").replace("%player%", target.getName()).replace("%amount%", String.format("%,d", amount))); + } + + public void setPlayerPrestige(CommandSender sender, Player target, long amount) { + + if (0 > amount) { + return; + } + + long maxPretige = this.getConfig().isUnlimitedPrestiges() ? this.getConfig().getUnlimitedPrestigeMax() : this.getConfig().getMaxPrestige().getId(); + + this.onlinePlayersPrestige.put(target.getUniqueId(), Math.min(amount, maxPretige)); + + if (sender != null) { + PlayerUtils.sendMessage(sender, this.getConfig().getMessage("prestige_set").replace("%player%", target.getName()).replace("%amount%", String.format("%,d", amount))); + } + } + + public void removePlayerPrestige(CommandSender sender, Player target, long amount) { + + if (0 > amount) { + return; + } + + Prestige currentPrestige = this.getPlayerPrestige(target); + + if (currentPrestige.getId() - amount < 0) { + this.onlinePlayersPrestige.put(target.getUniqueId(), 0L); + } else { + this.onlinePlayersPrestige.put(target.getUniqueId(), this.onlinePlayersPrestige.get(target.getUniqueId()) - amount); + } + + PlayerUtils.sendMessage(sender, this.getConfig().getMessage("prestige_remove").replace("%player%", target.getName()).replace("%amount%", String.format("%,d", amount))); + } + + public int getPrestigeProgress(Player player) { + + if (this.isMaxPrestige(player)) { + return 100; + } + + Prestige current = this.getPlayerPrestige(player); + Prestige next = this.getNextPrestige(current); + + double currentBalance = this.getConfig().isUseTokensCurrency() ? this.plugin.getCore().getTokens().getApi().getPlayerTokens(player) : this.plugin.getCore().getEconomy().getBalance(player); + + int progress = (int) ((currentBalance / next.getCost()) * 100); + + if (progress > 100) { + progress = 100; + } + + return progress; + } + + private boolean areRanksEnabled() { + return this.plugin.getCore().isModuleEnabled(XPrisonRanks.MODULE_NAME); + } + + private RanksManager getRankManager() { + if (!areRanksEnabled()) { + throw new IllegalStateException("Ranks module is not enabled"); + } + return this.plugin.getCore().getRanks().getRanksManager(); + } + + public boolean isPrestiging(Player sender) { + return this.prestigingPlayers.contains(sender.getUniqueId()); + } + + public void givePrestigeRewards(Prestige prestige, Player p) { + if (prestige.getCommandsToExecute() != null) { + if (!Bukkit.isPrimaryThread()) { + Schedulers.sync().run(() -> executeCommands(prestige, p)); + } else { + executeCommands(prestige, p); + } + } + } + + private void executeCommands(Prestige prestige, Player p) { + for (String cmd : prestige.getCommandsToExecute()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("%player%", p.getName()).replace("%Prestige%", prestige.getPrefix())); + } + } + + public void enable() { + this.loadAllData(); + } + + public void disable() { + this.saveAllDataSync(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/model/Prestige.java b/src/main/java/dev/drawethree/xprison/prestiges/model/Prestige.java new file mode 100644 index 0000000..35a7c9a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/model/Prestige.java @@ -0,0 +1,16 @@ +package dev.drawethree.xprison.prestiges.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@AllArgsConstructor +@Getter +public class Prestige { + + private final long id; + private final double cost; + private final String prefix; + private final List commandsToExecute; +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/repo/PrestigeRepository.java b/src/main/java/dev/drawethree/xprison/prestiges/repo/PrestigeRepository.java new file mode 100644 index 0000000..7670a17 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/repo/PrestigeRepository.java @@ -0,0 +1,21 @@ +package dev.drawethree.xprison.prestiges.repo; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface PrestigeRepository { + + void updatePrestige(OfflinePlayer player, long prestige); + + void addIntoPrestiges(OfflinePlayer player); + + long getPlayerPrestige(OfflinePlayer player); + + Map getTopPrestiges(int amountOfRecords); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/repo/impl/PrestigeRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/prestiges/repo/impl/PrestigeRepositoryImpl.java new file mode 100644 index 0000000..5aa7df0 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/repo/impl/PrestigeRepositoryImpl.java @@ -0,0 +1,76 @@ +package dev.drawethree.xprison.prestiges.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import dev.drawethree.xprison.prestiges.repo.PrestigeRepository; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class PrestigeRepositoryImpl implements PrestigeRepository { + + private static final String TABLE_NAME = "UltraPrison_Prestiges"; + private static final String PRESTIGES_UUID_COLNAME = "UUID"; + private static final String PRESTIGES_PRESTIGE_COLNAME = "id_prestige"; + + private final SQLDatabase database; + + public PrestigeRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public void updatePrestige(OfflinePlayer player, long newPrestige) { + this.database.executeSql("UPDATE " + TABLE_NAME + " SET " + PRESTIGES_PRESTIGE_COLNAME + "=? WHERE " + PRESTIGES_UUID_COLNAME + "=?", newPrestige, player.getUniqueId().toString()); + } + + @Override + public void addIntoPrestiges(OfflinePlayer player) { + String sql = this.database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME + "(UUID,id_prestige) VALUES(?,?)" : "INSERT IGNORE INTO " + TABLE_NAME + "(UUID,id_prestige) VALUES(?,?)"; + this.database.executeSql(sql, player.getUniqueId().toString(), 0); + } + + @Override + public long getPlayerPrestige(OfflinePlayer player) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT * FROM " + TABLE_NAME + " WHERE " + PRESTIGES_UUID_COLNAME + "=?")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getLong(PRESTIGES_PRESTIGE_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public Map getTopPrestiges(int amountOfRecords) { + Map top10Prestige = new LinkedHashMap<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT " + PRESTIGES_UUID_COLNAME + "," + PRESTIGES_PRESTIGE_COLNAME + " FROM " + TABLE_NAME + " ORDER BY " + PRESTIGES_PRESTIGE_COLNAME + " DESC LIMIT " + amountOfRecords); ResultSet set = statement.executeQuery()) { + while (set.next()) { + top10Prestige.put(UUID.fromString(set.getString(PRESTIGES_UUID_COLNAME)), set.getLong(PRESTIGES_PRESTIGE_COLNAME)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return top10Prestige; + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(UUID varchar(36) NOT NULL UNIQUE, id_prestige bigint, primary key (UUID))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/service/PrestigeService.java b/src/main/java/dev/drawethree/xprison/prestiges/service/PrestigeService.java new file mode 100644 index 0000000..41dce00 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/service/PrestigeService.java @@ -0,0 +1,17 @@ +package dev.drawethree.xprison.prestiges.service; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface PrestigeService { + + void setPrestige(OfflinePlayer player, long prestige); + + void createPrestige(OfflinePlayer player); + + long getPlayerPrestige(OfflinePlayer player); + + Map getTopPrestiges(int amountOfRecords); +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/service/impl/PrestigeServiceImpl.java b/src/main/java/dev/drawethree/xprison/prestiges/service/impl/PrestigeServiceImpl.java new file mode 100644 index 0000000..632584c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/service/impl/PrestigeServiceImpl.java @@ -0,0 +1,37 @@ +package dev.drawethree.xprison.prestiges.service.impl; + +import dev.drawethree.xprison.prestiges.repo.PrestigeRepository; +import dev.drawethree.xprison.prestiges.service.PrestigeService; +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public class PrestigeServiceImpl implements PrestigeService { + + private final PrestigeRepository repository; + + public PrestigeServiceImpl(PrestigeRepository repository) { + this.repository = repository; + } + + @Override + public void setPrestige(OfflinePlayer player, long prestige) { + repository.updatePrestige(player, prestige); + } + + @Override + public void createPrestige(OfflinePlayer player) { + repository.addIntoPrestiges(player); + } + + @Override + public long getPlayerPrestige(OfflinePlayer player) { + return repository.getPlayerPrestige(player); + } + + @Override + public Map getTopPrestiges(int amountOfRecords) { + return repository.getTopPrestiges(amountOfRecords); + } +} diff --git a/src/main/java/dev/drawethree/xprison/prestiges/task/SavePlayerDataTask.java b/src/main/java/dev/drawethree/xprison/prestiges/task/SavePlayerDataTask.java new file mode 100644 index 0000000..a77c9bd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/prestiges/task/SavePlayerDataTask.java @@ -0,0 +1,35 @@ +package dev.drawethree.xprison.prestiges.task; + +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import me.lucko.helper.Schedulers; +import me.lucko.helper.scheduler.Task; +import me.lucko.helper.utils.Players; + +import java.util.concurrent.TimeUnit; + + +public final class SavePlayerDataTask implements Runnable { + + private final XPrisonPrestiges plugin; + private Task task; + + public SavePlayerDataTask(XPrisonPrestiges plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + Players.all().forEach(p -> this.plugin.getPrestigeManager().savePlayerData(p, false, true)); + } + + public void start() { + this.stop(); + this.task = Schedulers.async().runRepeating(this, 30, TimeUnit.SECONDS, this.plugin.getPrestigeConfig().getSavePlayerDataInterval(), TimeUnit.MINUTES); + } + + public void stop() { + if (task != null) { + task.stop(); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/XPrisonRanks.java b/src/main/java/dev/drawethree/xprison/ranks/XPrisonRanks.java new file mode 100644 index 0000000..5a20cff --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/XPrisonRanks.java @@ -0,0 +1,100 @@ +package dev.drawethree.xprison.ranks; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.ranks.api.XPrisonRanksAPI; +import dev.drawethree.xprison.ranks.api.XPrisonRanksAPIImpl; +import dev.drawethree.xprison.ranks.commands.MaxRankupCommand; +import dev.drawethree.xprison.ranks.commands.RankupCommand; +import dev.drawethree.xprison.ranks.commands.SetRankCommand; +import dev.drawethree.xprison.ranks.config.RanksConfig; +import dev.drawethree.xprison.ranks.listener.RanksListener; +import dev.drawethree.xprison.ranks.manager.RanksManager; +import dev.drawethree.xprison.ranks.repo.RanksRepository; +import dev.drawethree.xprison.ranks.repo.impl.RanksRepositoryImpl; +import dev.drawethree.xprison.ranks.service.RanksService; +import dev.drawethree.xprison.ranks.service.impl.RanksServiceImpl; +import lombok.Getter; + +@Getter +public final class XPrisonRanks implements XPrisonModule { + + public static final String MODULE_NAME = "Ranks"; + + @Getter + private RanksConfig ranksConfig; + @Getter + private RanksManager ranksManager; + @Getter + private XPrisonRanksAPI api; + @Getter + private final XPrison core; + + @Getter + private RanksRepository ranksRepository; + + @Getter + private RanksService ranksService; + + private boolean enabled; + + public XPrisonRanks(XPrison core) { + this.core = core; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.ranksConfig.reload(); + } + + @Override + public void enable() { + this.enabled = true; + this.ranksConfig = new RanksConfig(this); + this.ranksConfig.load(); + this.ranksRepository = new RanksRepositoryImpl(this.core.getPluginDatabase()); + this.ranksRepository.createTables(); + this.ranksService = new RanksServiceImpl(this.ranksRepository); + this.ranksManager = new RanksManager(this); + this.ranksManager.enable(); + this.api = new XPrisonRanksAPIImpl(this.ranksManager); + this.registerCommands(); + this.registerListeners(); + } + + private void registerListeners() { + new RanksListener(this).register(); + } + + @Override + public void disable() { + this.ranksManager.disable(); + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.ranksRepository.clearTableData(); + } + + private void registerCommands() { + new RankupCommand(this).register(); + new MaxRankupCommand(this).register(); + new SetRankCommand(this).register(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/api/XPrisonRanksAPI.java b/src/main/java/dev/drawethree/xprison/ranks/api/XPrisonRanksAPI.java new file mode 100644 index 0000000..a6e14a2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/api/XPrisonRanksAPI.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.ranks.api; + + +import dev.drawethree.xprison.ranks.model.Rank; +import org.bukkit.entity.Player; + +import java.util.Optional; + +public interface XPrisonRanksAPI { + + /** + * Method to get player Rank + * + * @param p Player + * @return Rank + */ + Rank getPlayerRank(Player p); + + /** + * Method to get next player rank + * + * @param player Player + * @return null if he has max rank, otherwise next Rank + */ + Optional getNextPlayerRank(Player player); + + /** + * Method to get player's rankup progress + * + * @param player Player + * @return int 0-100 percentage + */ + int getRankupProgress(Player player); + + /** + * Sets a rank to online player + * + * @param player Player + * @param rank Rank + */ + void setPlayerRank(Player player, Rank rank); + +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/api/XPrisonRanksAPIImpl.java b/src/main/java/dev/drawethree/xprison/ranks/api/XPrisonRanksAPIImpl.java new file mode 100644 index 0000000..3cf2cb0 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/api/XPrisonRanksAPIImpl.java @@ -0,0 +1,36 @@ +package dev.drawethree.xprison.ranks.api; + +import dev.drawethree.xprison.ranks.manager.RanksManager; +import dev.drawethree.xprison.ranks.model.Rank; +import org.bukkit.entity.Player; + +import java.util.Optional; + +public final class XPrisonRanksAPIImpl implements XPrisonRanksAPI { + + private final RanksManager manager; + + public XPrisonRanksAPIImpl(RanksManager manager) { + this.manager = manager; + } + + @Override + public Rank getPlayerRank(Player p) { + return manager.getPlayerRank(p); + } + + @Override + public Optional getNextPlayerRank(Player player) { + return manager.getNextRank(this.getPlayerRank(player).getId()); + } + + @Override + public int getRankupProgress(Player player) { + return manager.getRankupProgress(player); + } + + @Override + public void setPlayerRank(Player player, Rank rank) { + manager.setRank(player, rank, null); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/api/events/PlayerRankUpEvent.java b/src/main/java/dev/drawethree/xprison/ranks/api/events/PlayerRankUpEvent.java new file mode 100644 index 0000000..96bdbe3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/api/events/PlayerRankUpEvent.java @@ -0,0 +1,48 @@ +package dev.drawethree.xprison.ranks.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import dev.drawethree.xprison.ranks.model.Rank; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class PlayerRankUpEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + @Getter + private final Rank oldRank; + + @Getter + @Setter + private Rank newRank; + + @Getter + @Setter + private boolean cancelled; + + /** + * Called when player receive gems + * + * @param player Player + * @param oldR old rank + * @param newR new rank + */ + public PlayerRankUpEvent(Player player, Rank oldR, Rank newR) { + super(player); + this.oldRank = oldR; + this.newRank = newR; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/commands/MaxRankupCommand.java b/src/main/java/dev/drawethree/xprison/ranks/commands/MaxRankupCommand.java new file mode 100644 index 0000000..5183985 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/commands/MaxRankupCommand.java @@ -0,0 +1,26 @@ +package dev.drawethree.xprison.ranks.commands; + +import dev.drawethree.xprison.ranks.XPrisonRanks; +import me.lucko.helper.Commands; + +public class MaxRankupCommand { + + private static final String[] COMMAND_ALIASES = {"maxrankup", "mru"}; + private static final String PERMISSION_REQUIRED = "xprison.ranks.maxrankup"; + private final XPrisonRanks plugin; + + public MaxRankupCommand(XPrisonRanks plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPermission(PERMISSION_REQUIRED, this.plugin.getRanksConfig().getMessage("no_permission")) + .assertPlayer() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getRanksManager().buyMaxRank(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), COMMAND_ALIASES); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/commands/RankupCommand.java b/src/main/java/dev/drawethree/xprison/ranks/commands/RankupCommand.java new file mode 100644 index 0000000..cee7c71 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/commands/RankupCommand.java @@ -0,0 +1,24 @@ +package dev.drawethree.xprison.ranks.commands; + +import dev.drawethree.xprison.ranks.XPrisonRanks; +import me.lucko.helper.Commands; + +public class RankupCommand { + + private static final String[] COMMAND_ALIASES = {"rankup"}; + private final XPrisonRanks plugin; + + public RankupCommand(XPrisonRanks plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPlayer() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getRanksManager().buyNextRank(c.sender()); + } + }).registerAndBind(this.plugin.getCore(), COMMAND_ALIASES); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/commands/SetRankCommand.java b/src/main/java/dev/drawethree/xprison/ranks/commands/SetRankCommand.java new file mode 100644 index 0000000..d87f591 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/commands/SetRankCommand.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.ranks.commands; + +import dev.drawethree.xprison.ranks.XPrisonRanks; +import dev.drawethree.xprison.ranks.model.Rank; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Commands; +import org.bukkit.entity.Player; + +import java.util.Optional; + +public class SetRankCommand { + + private static final String PERMISSION_REQUIRED = "xprison.ranks.admin"; + private static final String[] COMMAND_ALIASES = {"setrank"}; + + private final XPrisonRanks plugin; + + public SetRankCommand(XPrisonRanks plugin) { + this.plugin = plugin; + } + + public void register() { + Commands.create() + .assertPermission(PERMISSION_REQUIRED) + .handler(c -> { + if (c.args().size() == 2) { + Player target = c.arg(0).parseOrFail(Player.class); + Optional rankOptional = this.plugin.getRanksManager().getRankById(c.arg(1).parseOrFail(Integer.class)); + + if (!rankOptional.isPresent()) { + PlayerUtils.sendMessage(c.sender(), "&cInvalid rank id provided."); + return; + } + + this.plugin.getRanksManager().setRank(target, rankOptional.get(), c.sender()); + } + }).registerAndBind(this.plugin.getCore(), COMMAND_ALIASES); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/config/RanksConfig.java b/src/main/java/dev/drawethree/xprison/ranks/config/RanksConfig.java new file mode 100644 index 0000000..4dd6896 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/config/RanksConfig.java @@ -0,0 +1,121 @@ +package dev.drawethree.xprison.ranks.config; + +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.ranks.XPrisonRanks; +import dev.drawethree.xprison.ranks.model.Rank; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RanksConfig { + + private final XPrisonRanks plugin; + private final FileManager.Config config; + private final Map ranksById; + private Map messages; + private Rank defaultRank; + private Rank maxRank; + private boolean useTokensCurrency; + private String progressBarDelimiter; + private int progressBarLength; + + public RanksConfig(XPrisonRanks plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("ranks.yml").copyDefaults(true).save(); + this.ranksById = new HashMap<>(); + } + + + private void loadMessages(YamlConfiguration configuration) { + this.messages = new HashMap<>(); + + for (String key : configuration.getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(getConfig().get().getString("messages." + key))); + } + } + + public void reload() { + YamlConfiguration configuration = getYamlConfig(); + this.loadVariables(configuration); + this.loadRanks(configuration); + this.loadMessages(configuration); + } + + private void loadRanks(YamlConfiguration configuration) { + this.ranksById.clear(); + ConfigurationSection section = configuration.getConfigurationSection("Ranks"); + + boolean defaultSet = false; + if (section != null) { + for (String key : section.getKeys(false)) { + String rootPath = "Ranks." + key + "."; + int id = Integer.parseInt(key); + String prefix = TextUtils.applyColor(configuration.getString(rootPath + "Prefix")); + long cost = configuration.getLong(rootPath + "Cost"); + List commands = configuration.getStringList(rootPath + "CMD"); + Rank rank = new Rank(id, cost, prefix, commands); + this.ranksById.put(id, rank); + + if (!defaultSet) { + this.defaultRank = rank; + defaultSet = true; + } + + this.maxRank = rank; + } + } + this.plugin.getCore().getLogger().info(String.format("Loaded %d ranks!", ranksById.keySet().size())); + } + + public void load() { + this.reload(); + } + + + public String getMessage(String messageKey) { + return this.messages.getOrDefault(messageKey.toLowerCase(), "Missing message with key: " + messageKey); + } + + private void loadVariables(YamlConfiguration configuration) { + this.useTokensCurrency = configuration.getBoolean("use_tokens_currency"); + this.progressBarDelimiter = configuration.getString("progress-bar-delimiter"); + this.progressBarLength = configuration.getInt("progress-bar-length"); + this.plugin.getCore().getLogger().info("Using " + (useTokensCurrency ? "Tokens" : "Money") + " currency for Ranks."); + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public Rank getMaxRank() { + return maxRank; + } + + public Rank getDefaultRank() { + return defaultRank; + } + + public String getProgressBarDelimiter() { + return progressBarDelimiter; + } + + public int getProgressBarLength() { + return progressBarLength; + } + + public Rank getRankById(int id) { + return this.ranksById.get(id); + } + + public boolean isUseTokensCurrency() { + return this.useTokensCurrency; + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/listener/RanksListener.java b/src/main/java/dev/drawethree/xprison/ranks/listener/RanksListener.java new file mode 100644 index 0000000..03baa51 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/listener/RanksListener.java @@ -0,0 +1,32 @@ +package dev.drawethree.xprison.ranks.listener; + +import dev.drawethree.xprison.ranks.XPrisonRanks; +import me.lucko.helper.Events; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Collections; + +public class RanksListener { + + private final XPrisonRanks plugin; + + public RanksListener(XPrisonRanks plugin) { + this.plugin = plugin; + } + + public void register() { + this.subscribePlayerJoinEvent(); + this.subscribePlayerQuitEvent(); + } + + private void subscribePlayerQuitEvent() { + Events.subscribe(PlayerQuitEvent.class) + .handler(e -> this.plugin.getRanksManager().savePlayerRank(e.getPlayer())).bindWith(plugin.getCore()); + } + + private void subscribePlayerJoinEvent() { + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> this.plugin.getRanksManager().loadPlayerRank(Collections.singleton(e.getPlayer()))).bindWith(plugin.getCore()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/manager/RanksManager.java b/src/main/java/dev/drawethree/xprison/ranks/manager/RanksManager.java new file mode 100644 index 0000000..7fcdb08 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/manager/RanksManager.java @@ -0,0 +1,358 @@ +package dev.drawethree.xprison.ranks.manager; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.prestiges.XPrisonPrestiges; +import dev.drawethree.xprison.prestiges.manager.PrestigeManager; +import dev.drawethree.xprison.prestiges.model.Prestige; +import dev.drawethree.xprison.ranks.XPrisonRanks; +import dev.drawethree.xprison.ranks.api.events.PlayerRankUpEvent; +import dev.drawethree.xprison.ranks.model.Rank; +import dev.drawethree.xprison.utils.misc.ProgressBar; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.utils.Players; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class RanksManager { + + private final XPrisonRanks plugin; + private final Map onlinePlayersRanks; + + public RanksManager(XPrisonRanks plugin) { + this.plugin = plugin; + this.onlinePlayersRanks = new ConcurrentHashMap<>(); + } + + private void saveAllDataSync() { + for (UUID uuid : this.onlinePlayersRanks.keySet()) { + this.plugin.getRanksService().setRank(Players.getOfflineNullable(uuid), onlinePlayersRanks.get(uuid)); + } + this.plugin.getCore().getLogger().info("Saved online players ranks."); + } + + private void loadAllData() { + loadPlayerRank(Players.all()); + } + + public void savePlayerRank(Player player) { + Schedulers.async().run(() -> { + this.plugin.getRanksService().setRank(player, this.getPlayerRank(player).getId()); + this.onlinePlayersRanks.remove(player.getUniqueId()); + this.plugin.getCore().debug("Saved " + player.getName() + "'s rank.", this.plugin); + }); + } + + public void loadPlayerRank(Collection players) { + Schedulers.async().run(() -> { + for (Player player : players) { + this.plugin.getRanksService().createRank(player); + int rank = this.plugin.getRanksService().getPlayerRank(player); + this.onlinePlayersRanks.put(player.getUniqueId(), rank); + this.plugin.getCore().debug("Loaded " + player.getName() + "'s rank.", this.plugin); + } + }); + } + + public Optional getNextRank(int id) { + return this.getRankById(id + 1); + } + + public Rank getPlayerRank(Player p) { + int rankId = this.onlinePlayersRanks.getOrDefault(p.getUniqueId(), getDefaultRank().getId()); + return this.getRankById(rankId).orElse(this.getDefaultRank()); + } + + private Rank getDefaultRank() { + return this.plugin.getRanksConfig().getDefaultRank(); + } + + public boolean isMaxRank(Player p) { + return this.getPlayerRank(p).getId() == getMaxRank().getId(); + } + + public boolean buyMaxRank(Player p) { + + if (isMaxRank(p)) { + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("prestige_needed")); + return false; + } + + Rank maxRank = getMaxRank(); + Rank currentRank = this.getPlayerRank(p); + + int finalRankId = currentRank.getId(); + + for (int i = currentRank.getId(); i < maxRank.getId(); i++) { + Optional rank = this.getRankById(i + 1); + if (!rank.isPresent()) { + break; + } + double cost = rank.get().getCost(); + if (!this.isTransactionAllowed(p, cost)) { + break; + } + if (!this.completeTransaction(p, cost)) { + break; + } + finalRankId = i + 1; + } + + Optional nextRankOptional = this.getNextRank(currentRank.getId()); + + if (finalRankId == currentRank.getId() && nextRankOptional.isPresent()) { + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("not_enough_money").replace("%cost%", String.format("%,.0f", nextRankOptional.get().getCost()))); + return false; + } + + Optional finalRankOptional = this.getRankById(finalRankId); + + if (!finalRankOptional.isPresent()) { + return false; + } + + Rank finalRank = finalRankOptional.get(); + + PlayerRankUpEvent event = new PlayerRankUpEvent(p, currentRank, finalRank); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("PlayerRankUpEvent was cancelled.", this.plugin); + return false; + } + + for (int i = currentRank.getId() + 1; i <= finalRank.getId(); i++) { + this.getRankById(i).ifPresent(r -> runCommands(r, p)); + } + + this.onlinePlayersRanks.put(p.getUniqueId(), finalRank.getId()); + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("rank_up").replace("%Rank-1%", currentRank.getPrefix()).replace("%Rank-2%", finalRank.getPrefix())); + return true; + } + + private Rank getMaxRank() { + return this.plugin.getRanksConfig().getMaxRank(); + } + + public boolean buyNextRank(Player p) { + + if (isMaxRank(p)) { + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("prestige_needed")); + return false; + } + + Rank currentRank = this.getPlayerRank(p); + Optional toBuyOptional = getNextRank(currentRank.getId()); + + if (!toBuyOptional.isPresent()) { + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("prestige_needed")); + return false; + } + + Rank toBuy = toBuyOptional.get(); + + if (!this.isTransactionAllowed(p, toBuy.getCost())) { + if (this.plugin.getRanksConfig().isUseTokensCurrency()) { + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("not_enough_tokens").replace("%cost%", String.format("%,.0f", toBuy.getCost()))); + } else { + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("not_enough_money").replace("%cost%", String.format("%,.0f", toBuy.getCost()))); + } + return false; + } + + PlayerRankUpEvent event = new PlayerRankUpEvent(p, currentRank, toBuy); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("PlayerRankUpEvent was cancelled.", this.plugin); + return false; + } + + if (!this.completeTransaction(p, toBuy.getCost())) { + return false; + } + + runCommands(toBuy, p); + + this.onlinePlayersRanks.put(p.getUniqueId(), toBuy.getId()); + + PlayerUtils.sendMessage(p, this.plugin.getRanksConfig().getMessage("rank_up").replace("%Rank-1%", currentRank.getPrefix()).replace("%Rank-2%", toBuy.getPrefix())); + return true; + } + + private boolean completeTransaction(Player p, double cost) { + if (this.plugin.getRanksConfig().isUseTokensCurrency()) { + this.plugin.getCore().getTokens().getApi().removeTokens(p, (long) cost, LostCause.RANKUP); + return true; + } else { + return this.plugin.getCore().getEconomy().withdrawPlayer(p, cost).transactionSuccess(); + } + } + + private boolean isTransactionAllowed(Player p, double cost) { + if (this.plugin.getRanksConfig().isUseTokensCurrency()) { + return this.plugin.getCore().getTokens().getApi().hasEnough(p, (long) cost); + } else { + return this.plugin.getCore().getEconomy().has(p, cost); + } + } + + + public void setRank(Player target, Rank rank, CommandSender sender) { + + Rank currentRank = this.getPlayerRank(target); + + PlayerRankUpEvent event = new PlayerRankUpEvent(target, currentRank, rank); + + Events.call(event); + + if (event.isCancelled()) { + this.plugin.getCore().debug("PlayerRankUpEvent was cancelled.", this.plugin); + return; + } + + this.runCommands(rank, target); + + this.onlinePlayersRanks.put(target.getUniqueId(), rank.getId()); + + if (sender != null) { + PlayerUtils.sendMessage(sender, this.plugin.getRanksConfig().getMessage("rank_set").replace("%rank%", rank.getPrefix()).replace("%player%", target.getName())); + PlayerUtils.sendMessage(target, this.plugin.getRanksConfig().getMessage("rank_up").replace("%Rank-1%", currentRank.getPrefix()).replace("%Rank-2%", rank.getPrefix())); + } + + } + + public int getRankupProgress(Player player) { + + if (this.isMaxRank(player)) { + if (arePrestigesEnabled()) { + return getPrestigeManager().getPrestigeProgress(player); + } + return 100; + } + + Rank current = this.getPlayerRank(player); + Optional nextRankOptional = this.getNextRank(current.getId()); + + if (!nextRankOptional.isPresent()) { + return 100; + } + + Rank next = nextRankOptional.get(); + + double currentBalance = this.plugin.getRanksConfig().isUseTokensCurrency() ? + this.plugin.getCore().getTokens().getApi().getPlayerTokens(player) : this.plugin.getCore().getEconomy().getBalance(player); + + int progress = (int) ((currentBalance / next.getCost()) * 100); + + if (progress > 100) { + progress = 100; + } + + return progress; + } + + public double getNextRankCost(Player player) { + if (this.isMaxRank(player)) { + if (arePrestigesEnabled()) { + if (getPrestigeManager().isMaxPrestige(player)) { + return 0.0; + } else { + Prestige prestige = getPrestigeManager().getPlayerPrestige(player); + Prestige next = getPrestigeManager().getNextPrestige(prestige); + if (next != null) { + return next.getCost(); + } else { + return 0.0; + } + } + } else { + return 0.0; + } + } + + Rank current = this.getPlayerRank(player); + Optional nextRankOptional = this.getNextRank(current.getId()); + return nextRankOptional.map(Rank::getCost).orElse(0.0); + } + + public void resetPlayerRank(Player p) { + setRank(p, getDefaultRank(), null); + } + + private boolean arePrestigesEnabled() { + return this.plugin.getCore().isModuleEnabled(XPrisonPrestiges.MODULE_NAME); + } + + private PrestigeManager getPrestigeManager() { + if (!arePrestigesEnabled()) { + throw new IllegalStateException("Prestiges module is not enabled"); + } + return this.plugin.getCore().getPrestiges().getPrestigeManager(); + } + + public String getRankupProgressBar(Player player) { + + double currentProgress = 0, required = 100; + if (this.isMaxRank(player)) { + currentProgress = 100; + if (arePrestigesEnabled()) { + currentProgress = getPrestigeManager().getPrestigeProgress(player); + } + } else { + Rank current = this.getPlayerRank(player); + Optional next = this.getNextRank(current.getId()); + if (next.isPresent()) { + double currentBalance = this.plugin.getRanksConfig().isUseTokensCurrency() ? this.plugin.getCore().getTokens().getApi().getPlayerTokens(player) : this.plugin.getCore().getEconomy().getBalance(player); + currentProgress = (currentBalance / next.get().getCost()) * 100; + } + } + + if (currentProgress > 100) { + currentProgress = 100; + } + + return ProgressBar.getProgressBar(this.plugin.getRanksConfig().getProgressBarLength(), this.plugin.getRanksConfig().getProgressBarDelimiter(), currentProgress, required); + } + + public void runCommands(Rank rank, Player p) { + if (rank.getCommandsToExecute() != null) { + + if (!Bukkit.isPrimaryThread()) { + Schedulers.async().run(() -> { + executeCommands(rank, p); + }); + } else { + executeCommands(rank, p); + } + } + } + + public Optional getRankById(int id) { + return Optional.ofNullable(this.plugin.getRanksConfig().getRankById(id)); + } + + private void executeCommands(Rank rank, Player p) { + for (String cmd : rank.getCommandsToExecute()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("%player%", p.getName()).replace("%Rank%", rank.getPrefix())); + } + } + + public void disable() { + this.saveAllDataSync(); + } + + public void enable() { + this.loadAllData(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/model/Rank.java b/src/main/java/dev/drawethree/xprison/ranks/model/Rank.java new file mode 100644 index 0000000..59935d2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/model/Rank.java @@ -0,0 +1,17 @@ +package dev.drawethree.xprison.ranks.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@AllArgsConstructor +@Getter +public class Rank { + + private int id; + private double cost; + private String prefix; + private List commandsToExecute; + +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/repo/RanksRepository.java b/src/main/java/dev/drawethree/xprison/ranks/repo/RanksRepository.java new file mode 100644 index 0000000..c12ff78 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/repo/RanksRepository.java @@ -0,0 +1,16 @@ +package dev.drawethree.xprison.ranks.repo; + +import org.bukkit.OfflinePlayer; + +public interface RanksRepository { + + int getPlayerRank(OfflinePlayer player); + + void updateRank(OfflinePlayer player, int rank); + + void addIntoRanks(OfflinePlayer player); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/repo/impl/RanksRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/ranks/repo/impl/RanksRepositoryImpl.java new file mode 100644 index 0000000..de9d5f5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/repo/impl/RanksRepositoryImpl.java @@ -0,0 +1,61 @@ +package dev.drawethree.xprison.ranks.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import dev.drawethree.xprison.ranks.repo.RanksRepository; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class RanksRepositoryImpl implements RanksRepository { + + private static final String TABLE_NAME = "UltraPrison_Ranks"; + private static final String RANKS_UUID_COLNAME = "UUID"; + private static final String RANKS_RANK_COLNAME = "id_rank"; + + private final SQLDatabase database; + + public RanksRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public int getPlayerRank(OfflinePlayer player) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT * FROM " + TABLE_NAME + " WHERE " + RANKS_UUID_COLNAME + "=?")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getInt(RANKS_RANK_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public void updateRank(OfflinePlayer player, int newRank) { + this.database.executeSql("UPDATE " + TABLE_NAME + " SET " + RANKS_RANK_COLNAME + "=? WHERE " + RANKS_UUID_COLNAME + "=?", newRank, player.getUniqueId().toString()); + } + + @Override + public void addIntoRanks(OfflinePlayer player) { + String sql = database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME + "(UUID,id_rank) VALUES(?,?)" : "INSERT IGNORE INTO " + TABLE_NAME + "(UUID,id_rank) VALUES(?,?)"; + this.database.executeSql(sql, player.getUniqueId().toString(), 0); + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(UUID varchar(36) NOT NULL UNIQUE, id_rank int, primary key (UUID))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/service/RanksService.java b/src/main/java/dev/drawethree/xprison/ranks/service/RanksService.java new file mode 100644 index 0000000..e89f893 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/service/RanksService.java @@ -0,0 +1,12 @@ +package dev.drawethree.xprison.ranks.service; + +import org.bukkit.OfflinePlayer; + +public interface RanksService { + + int getPlayerRank(OfflinePlayer player); + + void setRank(OfflinePlayer player, int rank); + + void createRank(OfflinePlayer player); +} diff --git a/src/main/java/dev/drawethree/xprison/ranks/service/impl/RanksServiceImpl.java b/src/main/java/dev/drawethree/xprison/ranks/service/impl/RanksServiceImpl.java new file mode 100644 index 0000000..78e08eb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/ranks/service/impl/RanksServiceImpl.java @@ -0,0 +1,29 @@ +package dev.drawethree.xprison.ranks.service.impl; + +import dev.drawethree.xprison.ranks.repo.RanksRepository; +import dev.drawethree.xprison.ranks.service.RanksService; +import org.bukkit.OfflinePlayer; + +public class RanksServiceImpl implements RanksService { + + private final RanksRepository repository; + + public RanksServiceImpl(RanksRepository repository) { + this.repository = repository; + } + + @Override + public int getPlayerRank(OfflinePlayer player) { + return repository.getPlayerRank(player); + } + + @Override + public void setRank(OfflinePlayer player, int rank) { + repository.updateRank(player, rank); + } + + @Override + public void createRank(OfflinePlayer player) { + repository.addIntoRanks(player); + } +} diff --git a/src/main/java/dev/drawethree/xprison/support/exception/ModuleNotEnabledException.java b/src/main/java/dev/drawethree/xprison/support/exception/ModuleNotEnabledException.java new file mode 100644 index 0000000..fc0bf85 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/support/exception/ModuleNotEnabledException.java @@ -0,0 +1,10 @@ +package dev.drawethree.xprison.support.exception; + +import dev.drawethree.xprison.XPrisonModule; + +public class ModuleNotEnabledException extends Exception { + + public ModuleNotEnabledException(XPrisonModule module) { + super("Module " + module.getName() + " is not enabled"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/XPrisonTokens.java b/src/main/java/dev/drawethree/xprison/tokens/XPrisonTokens.java new file mode 100644 index 0000000..9c41eeb --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/XPrisonTokens.java @@ -0,0 +1,152 @@ +package dev.drawethree.xprison.tokens; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.tokens.api.XPrisonTokensAPI; +import dev.drawethree.xprison.tokens.api.XPrisonTokensAPIImpl; +import dev.drawethree.xprison.tokens.config.BlockRewardsConfig; +import dev.drawethree.xprison.tokens.config.TokensConfig; +import dev.drawethree.xprison.tokens.listener.TokensListener; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.tokens.managers.TokensManager; +import dev.drawethree.xprison.tokens.repo.BlocksRepository; +import dev.drawethree.xprison.tokens.repo.TokensRepository; +import dev.drawethree.xprison.tokens.repo.impl.BlocksRepositoryImpl; +import dev.drawethree.xprison.tokens.repo.impl.TokensRepositoryImpl; +import dev.drawethree.xprison.tokens.service.BlocksService; +import dev.drawethree.xprison.tokens.service.TokensService; +import dev.drawethree.xprison.tokens.service.impl.BlocksServiceImpl; +import dev.drawethree.xprison.tokens.service.impl.TokensServiceImpl; +import dev.drawethree.xprison.tokens.task.SavePlayerDataTask; +import lombok.Getter; + +public final class XPrisonTokens implements XPrisonModule { + + public static final String MODULE_NAME = "Tokens"; + + @Getter + private static XPrisonTokens instance; + + @Getter + private BlockRewardsConfig blockRewardsConfig; + + @Getter + private TokensConfig tokensConfig; + + @Getter + private XPrisonTokensAPI api; + + @Getter + private TokensManager tokensManager; + + @Getter + private CommandManager commandManager; + + @Getter + private TokensRepository tokensRepository; + + @Getter + private TokensService tokensService; + + @Getter + private BlocksRepository blocksRepository; + + @Getter + private BlocksService blocksService; + + @Getter + private final XPrison core; + + private SavePlayerDataTask savePlayerDataTask; + + private boolean enabled; + + + public XPrisonTokens(XPrison prisonCore) { + instance = this; + this.core = prisonCore; + } + + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void reload() { + this.tokensConfig.reload(); + this.blockRewardsConfig.reload(); + this.tokensManager.reload(); + this.commandManager.reload(); + } + + + @Override + public void enable() { + + this.tokensConfig = new TokensConfig(this); + this.blockRewardsConfig = new BlockRewardsConfig(this); + + this.tokensConfig.load(); + this.blockRewardsConfig.load(); + + this.tokensRepository = new TokensRepositoryImpl(this.core.getPluginDatabase()); + this.tokensRepository.createTables(); + + this.tokensService = new TokensServiceImpl(this.tokensRepository); + + this.blocksRepository = new BlocksRepositoryImpl(this.core.getPluginDatabase()); + this.blocksRepository.createTables(); + + this.blocksService = new BlocksServiceImpl(this.blocksRepository); + + this.tokensManager = new TokensManager(this); + this.tokensManager.enable(); + + this.commandManager = new CommandManager(this); + this.commandManager.enable(); + + this.savePlayerDataTask = new SavePlayerDataTask(this); + this.savePlayerDataTask.start(); + + this.registerListeners(); + + this.commandManager.enable(); + + this.api = new XPrisonTokensAPIImpl(this.tokensManager); + + this.enabled = true; + } + + private void registerListeners() { + new TokensListener(this).subscribeToEvents(); + } + + + @Override + public void disable() { + this.tokensManager.disable(); + + this.savePlayerDataTask.stop(); + + this.enabled = false; + } + + @Override + public String getName() { + return MODULE_NAME; + } + + @Override + public boolean isHistoryEnabled() { + return true; + } + + @Override + public void resetPlayerData() { + this.tokensRepository.clearTableData(); + this.blocksRepository.clearTableData(); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/api/XPrisonTokensAPI.java b/src/main/java/dev/drawethree/xprison/tokens/api/XPrisonTokensAPI.java new file mode 100644 index 0000000..533680d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/api/XPrisonTokensAPI.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.tokens.api; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import org.bukkit.OfflinePlayer; + +public interface XPrisonTokensAPI { + + + /** + * Method to get player's tokens + * + * @param p Player + * @return amount of player's tokens + */ + long getPlayerTokens(OfflinePlayer p); + + /** + * Method to check if player has more or equal tokens than specified amount + * + * @param p Player + * @param amount amount + * @return true if player has more or equal tokens than amount + */ + boolean hasEnough(OfflinePlayer p, long amount); + + /** + * Method to remove tokens from player + * + * @param p Player + * @param amount amount + */ + void removeTokens(OfflinePlayer p, long amount, LostCause cause); + + /** + * Method to add tokens to player + * + * @param p Player + * @param amount amount + * @param cause - Represents why player get these tokens + */ + void addTokens(OfflinePlayer p, long amount, ReceiveCause cause); + + +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/api/XPrisonTokensAPIImpl.java b/src/main/java/dev/drawethree/xprison/tokens/api/XPrisonTokensAPIImpl.java new file mode 100644 index 0000000..df4527f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/api/XPrisonTokensAPIImpl.java @@ -0,0 +1,35 @@ +package dev.drawethree.xprison.tokens.api; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.tokens.managers.TokensManager; +import org.bukkit.OfflinePlayer; + +public final class XPrisonTokensAPIImpl implements XPrisonTokensAPI { + + private final TokensManager manager; + + public XPrisonTokensAPIImpl(TokensManager manager) { + this.manager = manager; + } + + @Override + public long getPlayerTokens(OfflinePlayer p) { + return this.manager.getPlayerTokens(p); + } + + @Override + public boolean hasEnough(OfflinePlayer p, long amount) { + return this.getPlayerTokens(p) >= amount; + } + + @Override + public void removeTokens(OfflinePlayer p, long amount, LostCause cause) { + this.manager.removeTokens(p, amount, null, cause); + } + + @Override + public void addTokens(OfflinePlayer p, long amount, ReceiveCause cause) { + this.manager.giveTokens(p, amount, null, cause); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/api/events/PlayerTokensLostEvent.java b/src/main/java/dev/drawethree/xprison/tokens/api/events/PlayerTokensLostEvent.java new file mode 100644 index 0000000..ebe40cd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/api/events/PlayerTokensLostEvent.java @@ -0,0 +1,45 @@ +package dev.drawethree.xprison.tokens.api.events; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class PlayerTokensLostEvent extends XPrisonPlayerEvent { + + + private static final HandlerList handlers = new HandlerList(); + + @Getter + private final LostCause cause; + + @Getter + @Setter + private long amount; + + /** + * Called when player loses tokens + * + * @param cause LostCause + * @param player Player + * @param amount Amount of tokens lost + */ + public PlayerTokensLostEvent(LostCause cause, OfflinePlayer player, long amount) { + super(player); + this.cause = cause; + this.amount = amount; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/api/events/PlayerTokensReceiveEvent.java b/src/main/java/dev/drawethree/xprison/tokens/api/events/PlayerTokensReceiveEvent.java new file mode 100644 index 0000000..ca5d855 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/api/events/PlayerTokensReceiveEvent.java @@ -0,0 +1,49 @@ +package dev.drawethree.xprison.tokens.api.events; + +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public final class PlayerTokensReceiveEvent extends XPrisonPlayerEvent implements Cancellable { + + + private static final HandlerList handlers = new HandlerList(); + + @Getter + private final ReceiveCause cause; + + @Getter + @Setter + private long amount; + + @Getter + @Setter + private boolean cancelled; + + /** + * Called when player receive tokens + * + * @param cause ReceiveCause + * @param player Player + * @param amount Amount of tokens received + */ + public PlayerTokensReceiveEvent(ReceiveCause cause, OfflinePlayer player, long amount) { + super(player); + this.cause = cause; + this.amount = amount; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/api/events/XPrisonBlockBreakEvent.java b/src/main/java/dev/drawethree/xprison/tokens/api/events/XPrisonBlockBreakEvent.java new file mode 100644 index 0000000..d50bf1b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/api/events/XPrisonBlockBreakEvent.java @@ -0,0 +1,43 @@ +package dev.drawethree.xprison.tokens.api.events; + +import dev.drawethree.xprison.api.events.player.XPrisonPlayerEvent; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import java.util.List; + +@Getter +@Setter +public final class XPrisonBlockBreakEvent extends XPrisonPlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private Player player; + private List blocks; + private boolean cancelled; + + /** + * Called when player mines blocks within mine with or without custom enchants. + * + * @param player Player + * @param blocks List of blocks that were affected + */ + public XPrisonBlockBreakEvent(Player player, List blocks) { + super(player); + this.player = player; + this.blocks = blocks; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensCommand.java new file mode 100644 index 0000000..ec40f31 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensCommand.java @@ -0,0 +1,41 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.interfaces.Permissionable; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import lombok.Getter; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public abstract class TokensCommand implements Permissionable { + + protected static final String PERMISSION_ROOT = "xprison.tokens.command."; + + @Getter + private final String name; + protected final CommandManager commandManager; + @Getter + private final String[] aliases; + + TokensCommand(CommandManager commandManager, String name, String... aliases) { + this.commandManager = commandManager; + this.name = name; + this.aliases = aliases; + } + + public abstract boolean execute(CommandSender sender, ImmutableList args); + + public abstract boolean canExecute(CommandSender sender); + + public abstract String getUsage(); + + @Override + public String getRequiredPermission() { + return PERMISSION_ROOT + this.name; + } + + public abstract List getTabComplete(List args); + + +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensGiveCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensGiveCommand.java new file mode 100644 index 0000000..3cff894 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensGiveCommand.java @@ -0,0 +1,60 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.tokens.utils.TokensConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class TokensGiveCommand extends TokensCommand { + + private static final String COMMAND_NAME = "give"; + + public TokensGiveCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.size() == 2) { + try { + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + long amount = Long.parseLong(args.get(1)); + commandManager.getPlugin().getTokensManager().giveTokens(target, amount, sender, ReceiveCause.GIVE); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(1)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(TokensConstants.TOKENS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/tokens give [player] [tokens] - Gives tokens to player."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensHelpCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensHelpCommand.java new file mode 100644 index 0000000..639b518 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensHelpCommand.java @@ -0,0 +1,51 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public final class TokensHelpCommand extends TokensCommand { + + private static final String COMMAND_NAME = "help"; + private static final String[] COMMAND_ALIASES = {"?"}; + + public TokensHelpCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME, COMMAND_ALIASES); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.isEmpty()) { + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + PlayerUtils.sendMessage(sender, "&e&lTOKEN HELP MENU "); + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + for (TokensCommand command : this.commandManager.getAll()) { + if (command.canExecute(sender)) { + PlayerUtils.sendMessage(sender, "&e" + command.getUsage()); + } + } + PlayerUtils.sendMessage(sender, "&e&m-------&f&m-------&e&m--------&f&m--------&e&m--------&f&m-------&e&m-------"); + return true; + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return true; + } + + @Override + public String getUsage() { + return "/tokens help - Displays all available commands."; + } + + @Override + public List getTabComplete(List args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensPayCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensPayCommand.java new file mode 100644 index 0000000..e9f753d --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensPayCommand.java @@ -0,0 +1,78 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.tokens.utils.TokensConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class TokensPayCommand extends TokensCommand { + + private static final String COMMAND_NAME = "pay"; + private static final String[] COMMAND_ALIASES = {"send"}; + + public TokensPayCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME, COMMAND_ALIASES); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.size() == 2 && sender instanceof Player) { + Player p = (Player) sender; + try { + + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + + if (!target.isOnline()) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("player_not_online").replace("%player%", target.getName())); + return true; + } + + long amount = Long.parseLong(args.get(1).replace(",", "")); + + if (0 >= amount) { + return false; + } + + if (target.getUniqueId().equals(p.getUniqueId())) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("tokens_cant_send_to_yourself")); + return true; + } + + commandManager.getPlugin().getTokensManager().payTokens(p, amount, target); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(1)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(TokensConstants.TOKENS_ADMIN_PERM) || sender.hasPermission(this.getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/tokens pay [player] [amount] - Send tokens to a player."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensRemoveCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensRemoveCommand.java new file mode 100644 index 0000000..86aedde --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensRemoveCommand.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.tokens.utils.TokensConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class TokensRemoveCommand extends TokensCommand { + + private static final String COMMAND_NAME = "remove"; + private static final String[] COMMAND_ALIASES = {"subtract", "delete"}; + + public TokensRemoveCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME, COMMAND_ALIASES); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + + if (args.size() == 2) { + try { + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + long amount = Long.parseLong(args.get(1)); + commandManager.getPlugin().getTokensManager().removeTokens(target, amount, sender, LostCause.ADMIN); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(1)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(TokensConstants.TOKENS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/tokens remove [player] [amount] - Remove tokens from player."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensSetCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensSetCommand.java new file mode 100644 index 0000000..336a56a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensSetCommand.java @@ -0,0 +1,60 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.tokens.utils.TokensConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class TokensSetCommand extends TokensCommand { + + private static final String COMMAND_NAME = "set"; + + public TokensSetCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + + if (args.size() == 2) { + try { + long amount = Long.parseLong(args.get(1)); + OfflinePlayer target = Players.getOfflineNullable(args.get(0)); + commandManager.getPlugin().getTokensManager().setTokens(target, amount, sender); + return true; + } catch (Exception e) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("not_a_number").replace("%input%", String.valueOf(args.get(0)))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(TokensConstants.TOKENS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/tokens set [player] [amount] - Sets player tokens."; + } + + @Override + public List getTabComplete(List args) { + List list = new ArrayList<>(); + + if (args.size() == 1) { + list = Players.all().stream().map(Player::getName).collect(Collectors.toList()); + } + + return list; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/commands/TokensWithdrawCommand.java b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensWithdrawCommand.java new file mode 100644 index 0000000..a329b49 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/commands/TokensWithdrawCommand.java @@ -0,0 +1,69 @@ +package dev.drawethree.xprison.tokens.commands; + +import com.google.common.collect.ImmutableList; +import dev.drawethree.xprison.tokens.managers.CommandManager; +import dev.drawethree.xprison.tokens.utils.TokensConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public final class TokensWithdrawCommand extends TokensCommand { + + private static final String COMMAND_NAME = "withdraw"; + + public TokensWithdrawCommand(CommandManager commandManager) { + super(commandManager, COMMAND_NAME); + } + + @Override + public boolean execute(CommandSender sender, ImmutableList args) { + if (args.size() == 2 && sender instanceof Player) { + Player p = (Player) sender; + try { + long amount = Long.parseLong(args.get(0)); + int value = Integer.parseInt(args.get(1)); + + if (0 >= amount || 0 >= value) { + return false; + } + + commandManager.getPlugin().getTokensManager().withdrawTokens(p, amount, value); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("not_a_number").replace("%input%", args.get(0) + " or " + args.get(1))); + } + } else if (args.size() == 1 && sender instanceof Player) { + Player p = (Player) sender; + try { + long amount = Long.parseLong(args.get(0)); + int value = 1; + if (0 >= amount) { + return false; + } + commandManager.getPlugin().getTokensManager().withdrawTokens(p, amount, value); + return true; + } catch (NumberFormatException e) { + PlayerUtils.sendMessage(sender, commandManager.getPlugin().getTokensConfig().getMessage("not_a_number").replace("%input%", args.get(0) + " or " + args.get(1))); + } + } + return false; + } + + @Override + public boolean canExecute(CommandSender sender) { + return sender.hasPermission(TokensConstants.TOKENS_ADMIN_PERM) || sender.hasPermission(getRequiredPermission()); + } + + @Override + public String getUsage() { + return "/tokens withdraw [amount] [value] - Withdraw tokens to physical item."; + } + + @Override + public List getTabComplete(List args) { + return new ArrayList<>(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/config/BlockRewardsConfig.java b/src/main/java/dev/drawethree/xprison/tokens/config/BlockRewardsConfig.java new file mode 100644 index 0000000..47f8f7f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/config/BlockRewardsConfig.java @@ -0,0 +1,63 @@ +package dev.drawethree.xprison.tokens.config; + +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.tokens.model.BlockReward; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class BlockRewardsConfig { + + private final XPrisonTokens plugin; + private final FileManager.Config config; + private final Map blockRewards; + + public BlockRewardsConfig(XPrisonTokens plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("block-rewards.yml").copyDefaults(true).save(); + this.blockRewards = new LinkedHashMap<>(); + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + public void load() { + YamlConfiguration configuration = getYamlConfig(); + this.loadVariables(configuration); + } + + public void reload() { + this.getConfig().reload(); + this.load(); + } + + private void loadVariables(YamlConfiguration configuration) { + this.blockRewards.clear(); + ConfigurationSection section = configuration.getConfigurationSection("block-rewards"); + + if (section != null) { + for (String key : section.getKeys(false)) { + long blocksNeeded = Long.parseLong(key); + String message = TextUtils.applyColor(configuration.getString("block-rewards." + key + ".message")); + List commands = configuration.getStringList("block-rewards." + key + ".commands"); + BlockReward reward = new BlockReward(blocksNeeded, commands, message); + this.blockRewards.put(blocksNeeded, reward); + } + } + this.plugin.getCore().getLogger().info("Loaded " + this.blockRewards.keySet().size() + " Block Rewards!"); + } + + public Map getBlockRewards() { + return blockRewards; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/config/TokensConfig.java b/src/main/java/dev/drawethree/xprison/tokens/config/TokensConfig.java new file mode 100644 index 0000000..45cb61a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/config/TokensConfig.java @@ -0,0 +1,210 @@ +package dev.drawethree.xprison.tokens.config; + +import dev.drawethree.xprison.config.FileManager; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TokensConfig { + + private final FileManager.Config config; + private final XPrisonTokens plugin; + + private double chance; + private long minAmount; + private long maxAmount; + private long commandCooldown; + private long nextResetWeekly; + private long startingTokens; + private int savePlayerDataInterval; + private boolean displayTokenMessages; + private Map messages; + private Map> luckyBlockRewards; + private List worldWhitelist; + private List tokensTopFormat; + private List blocksTopFormat; + private List blocksTopFormatWeekly; + private List tokenItemLore; + private String tokenItemDisplayName; + private ItemStack tokenItem; + private int topPlayersAmount; + + private String[] tokensCommandAliases; + private String[] tokensTopCommandAliases; + private String[] blocksTopCommandAliases; + + + public TokensConfig(XPrisonTokens plugin) { + this.plugin = plugin; + this.config = this.plugin.getCore().getFileManager().getConfig("tokens.yml").copyDefaults(true).save(); + } + + private FileManager.Config getConfig() { + return this.config; + } + + public YamlConfiguration getYamlConfig() { + return this.config.get(); + } + + + private void loadVariables(YamlConfiguration configuration) { + this.chance = configuration.getDouble("tokens.breaking.chance"); + this.minAmount = configuration.getLong("tokens.breaking.min"); + this.maxAmount = configuration.getLong("tokens.breaking.max"); + + this.commandCooldown = configuration.getLong("tokens-command-cooldown"); + + this.luckyBlockRewards = new HashMap<>(); + + for (String key : configuration.getConfigurationSection("lucky-blocks").getKeys(false)) { + CompMaterial material = CompMaterial.fromString(key); + List rewards = configuration.getStringList("lucky-blocks." + key); + if (rewards.isEmpty()) { + continue; + } + this.luckyBlockRewards.put(material.toMaterial(), rewards); + } + + this.topPlayersAmount = configuration.getInt("top_players_amount"); + this.worldWhitelist = configuration.getStringList("world-whitelist"); + this.tokensTopFormat = configuration.getStringList("tokens-top-format"); + this.blocksTopFormat = configuration.getStringList("blocks-top-format"); + this.blocksTopFormatWeekly = configuration.getStringList("blocks-top-weekly-format"); + this.nextResetWeekly = configuration.getLong("next-reset-weekly"); + this.displayTokenMessages = configuration.getBoolean("display-token-messages"); + this.savePlayerDataInterval = configuration.getInt("player_data_save_interval"); + this.tokenItemDisplayName = configuration.getString("tokens.item.name"); + this.tokenItemLore = configuration.getStringList("tokens.item.lore"); + this.tokenItem = CompMaterial.fromString(configuration.getString("tokens.item.material")).toItem(); + this.startingTokens = configuration.getLong("starting-tokens"); + this.tokensCommandAliases = configuration.getStringList("tokens-command-aliases").toArray(new String[0]); + this.tokensTopCommandAliases = configuration.getStringList("tokens-top-command-aliases").toArray(new String[0]); + this.blocksTopCommandAliases = configuration.getStringList("blocks-top-command-aliases").toArray(new String[0]); + + } + + private void loadMessages(YamlConfiguration configuration) { + this.messages = new HashMap<>(); + + for (String key : configuration.getConfigurationSection("messages").getKeys(false)) { + messages.put(key.toLowerCase(), TextUtils.applyColor(getConfig().get().getString("messages." + key))); + } + } + + public void reload() { + this.config.reload(); + YamlConfiguration configuration = getYamlConfig(); + this.loadVariables(configuration); + this.loadMessages(configuration); + } + + public void load() { + this.reload(); + } + + public String getMessage(String messageKey) { + return this.messages.getOrDefault(messageKey.toLowerCase(), "Missing message with key: " + messageKey); + } + + public boolean isDisplayTokenMessages() { + return this.displayTokenMessages; + } + + public Material getTokenItemMaterial() { + return this.tokenItem.getType(); + } + + public long getStartingTokens() { + return this.startingTokens; + } + + public List getWorldWhitelist() { + return worldWhitelist; + } + + public List getTokensTopFormat() { + return tokensTopFormat; + } + + public List getBlocksTopFormat() { + return blocksTopFormat; + } + + public List getBlocksTopFormatWeekly() { + return blocksTopFormatWeekly; + } + + public List getTokenItemLore() { + return tokenItemLore; + } + + public String getTokenItemDisplayName() { + return tokenItemDisplayName; + } + + public List getLuckyBlockReward(Material m) { + return this.luckyBlockRewards.getOrDefault(m, new ArrayList<>()); + } + + public double getChance() { + return chance; + } + + public long getMinAmount() { + return minAmount; + } + + public long getMaxAmount() { + return maxAmount; + } + + public long getCommandCooldown() { + return commandCooldown; + } + + public long getNextResetWeekly() { + return nextResetWeekly; + } + + public int getTopPlayersAmount() { + return topPlayersAmount; + } + + public ItemStack getTokenItem() { + return tokenItem; + } + + public String[] getTokensCommandAliases() { + return tokensCommandAliases; + } + + public String[] getTokensTopCommandAliases() { + return tokensTopCommandAliases; + } + + public String[] getBlocksTopCommandAliases() { + return blocksTopCommandAliases; + } + + public void save() { + this.config.save(); + } + + public void setNextResetWeekly(long time) { + this.nextResetWeekly = time; + + } + + public int getSavePlayerDataInterval() { + return savePlayerDataInterval; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/listener/TokensListener.java b/src/main/java/dev/drawethree/xprison/tokens/listener/TokensListener.java new file mode 100644 index 0000000..adf2533 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/listener/TokensListener.java @@ -0,0 +1,90 @@ +package dev.drawethree.xprison.tokens.listener; + +import dev.drawethree.xprison.tokens.XPrisonTokens; +import me.lucko.helper.Events; +import me.lucko.helper.reflect.MinecraftVersion; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class TokensListener { + + private final XPrisonTokens plugin; + + public TokensListener(XPrisonTokens plugin) { + + this.plugin = plugin; + } + + public void subscribeToEvents() { + this.subscribeToPlayerJoinEvent(); + this.subscribeToPlayerQuitEvent(); + this.subscribeToPlayerInteractEvent(); + this.subscribeToBlockBreakEvent(); + } + + private void subscribeToBlockBreakEvent() { + Events.subscribe(BlockBreakEvent.class, EventPriority.HIGHEST) + .filter(e -> !e.isCancelled()) + .filter(e -> e.getPlayer().getItemInHand() != null && this.plugin.getCore().isPickaxeSupported(e.getPlayer().getItemInHand().getType())) + .filter(e -> { + final List whitelist = this.plugin.getTokensConfig().getWorldWhitelist(); + return whitelist.isEmpty() || whitelist.contains(e.getBlock().getWorld().getName()); + }) + .handler(e -> { + List blocks = new ArrayList<>(); + blocks.add(e.getBlock()); + this.plugin.getTokensManager().handleBlockBreak(e.getPlayer(), blocks, true); + }).bindWith(plugin.getCore()); + } + + private void subscribeToPlayerInteractEvent() { + + Events.subscribe(PlayerInteractEvent.class, EventPriority.LOWEST) + .filter(e -> e.getItem() != null && e.getItem().hasItemMeta() && e.getItem().getType() == this.plugin.getTokensConfig().getTokenItemMaterial() && (e.getAction() == Action.RIGHT_CLICK_BLOCK || e.getAction() == Action.RIGHT_CLICK_AIR)) + .handler(e -> { + + e.setCancelled(true); + e.setUseInteractedBlock(Event.Result.DENY); + + boolean offHandClick = false; + + if (MinecraftVersion.getRuntimeVersion().isAfter(MinecraftVersion.of(1, 8, 9))) { + offHandClick = e.getHand() == EquipmentSlot.OFF_HAND; + } + + this.plugin.getTokensManager().redeemTokens(e.getPlayer(), e.getItem(), e.getPlayer().isSneaking(), offHandClick); + + }).bindWith(plugin.getCore()); + } + + private void subscribeToPlayerQuitEvent() { + Events.subscribe(PlayerQuitEvent.class) + .handler(e -> { + this.plugin.getTokensManager().savePlayerData(Collections.singletonList(e.getPlayer()), true, true); + e.getPlayer().getActivePotionEffects().forEach(effect -> e.getPlayer().removePotionEffect(effect.getType())); + }).bindWith(plugin.getCore()); + } + + private void subscribeToPlayerJoinEvent() { + Events.subscribe(PlayerJoinEvent.class) + .handler(e -> { + this.plugin.getTokensManager().loadPlayerData(Collections.singleton(e.getPlayer())); + + if (this.plugin.getTokensConfig().isDisplayTokenMessages() && this.plugin.getTokensManager().hasOffTokenMessages(e.getPlayer())) { + this.plugin.getTokensManager().addPlayerIntoTokenMessageOnPlayers(e.getPlayer()); + } + + }).bindWith(plugin.getCore()); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/managers/CommandManager.java b/src/main/java/dev/drawethree/xprison/tokens/managers/CommandManager.java new file mode 100644 index 0000000..edeebaf --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/managers/CommandManager.java @@ -0,0 +1,224 @@ +package dev.drawethree.xprison.tokens.managers; + +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.tokens.commands.*; +import dev.drawethree.xprison.tokens.utils.TokensConstants; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.Getter; +import me.lucko.helper.Commands; +import me.lucko.helper.command.context.CommandContext; +import me.lucko.helper.cooldown.Cooldown; +import me.lucko.helper.cooldown.CooldownMap; +import me.lucko.helper.utils.Players; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class CommandManager { + + @Getter + private final XPrisonTokens plugin; + private final Set commands; + private CooldownMap tokensCommandCooldownMap; + + public CommandManager(XPrisonTokens plugin) { + this.plugin = plugin; + this.commands = new HashSet<>(); + this.tokensCommandCooldownMap = CooldownMap.create(Cooldown.of(plugin.getTokensConfig().getCommandCooldown(), TimeUnit.SECONDS)); + } + + + private boolean checkCommandCooldown(CommandSender sender) { + if (sender.hasPermission(TokensConstants.TOKENS_ADMIN_PERM)) { + return true; + } + if (!tokensCommandCooldownMap.test(sender)) { + PlayerUtils.sendMessage(sender, this.plugin.getTokensConfig().getMessage("cooldown").replace("%time%", String.format("%,d", this.tokensCommandCooldownMap.remainingTime(sender, TimeUnit.SECONDS)))); + return false; + } + return true; + } + + private void registerCommands() { + this.commands.clear(); + + this.registerCommand(new TokensGiveCommand(this)); + this.registerCommand(new TokensGiveCommand(this)); + this.registerCommand(new TokensPayCommand(this)); + this.registerCommand(new TokensRemoveCommand(this)); + this.registerCommand(new TokensSetCommand(this)); + this.registerCommand(new TokensWithdrawCommand(this)); + this.registerCommand(new TokensHelpCommand(this)); + + // /tokens, /token + Commands.create() + .tabHandler(this::createTabHandler) + .handler(c -> { + + if (!checkCommandCooldown(c.sender())) { + return; + } + + if (c.args().size() == 0 && c.sender() instanceof Player) { + this.plugin.getTokensManager().sendInfoMessage(c.sender(), (OfflinePlayer) c.sender(), true); + return; + } + + TokensCommand subCommand = this.getCommand(c.rawArg(0)); + if (subCommand != null) { + if (subCommand.canExecute(c.sender())) { + subCommand.execute(c.sender(), c.args().subList(1, c.args().size())); + } else { + PlayerUtils.sendMessage(c.sender(), this.plugin.getTokensConfig().getMessage("no_permission")); + } + } else { + OfflinePlayer target = Players.getOfflineNullable(c.rawArg(0)); + this.plugin.getTokensManager().sendInfoMessage(c.sender(), target, true); + } + }).registerAndBind(this.plugin.getCore(), this.plugin.getTokensConfig().getTokensCommandAliases()); + + // /tokenmessage + Commands.create() + .assertPlayer() + .handler(c -> { + this.plugin.getTokensManager().toggleTokenMessage(c.sender()); + }).registerAndBind(this.plugin.getCore(), "tokenmessage"); + + // /blockstop, / blocktop + Commands.create() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getTokensManager().sendBlocksTop(c.sender()); + } + }) + .registerAndBind(this.plugin.getCore(), this.plugin.getTokensConfig().getBlocksTopCommandAliases()); + + // /blockstopweekly, /blockstopw + Commands.create() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getTokensManager().sendBlocksTopWeekly(c.sender()); + } + }) + .registerAndBind(this.plugin.getCore(), "blockstopweekly", "blockstopw"); + + // /blockstopweeklyreset + Commands.create() + .assertPermission(TokensConstants.TOKENS_ADMIN_PERM, this.plugin.getTokensConfig().getMessage("no_permission")) + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getTokensManager().resetBlocksTopWeekly(c.sender()); + } + }) + .registerAndBind(this.plugin.getCore(), "blockstopweeklyreset"); + + // /tokenstop, /tokentop + Commands.create() + .handler(c -> { + if (c.args().size() == 0) { + this.plugin.getTokensManager().sendTokensTop(c.sender()); + } + }) + .registerAndBind(this.plugin.getCore(), this.plugin.getTokensConfig().getTokensTopCommandAliases()); + + // /blocks + Commands.create() + .handler(c -> { + if (!checkCommandCooldown(c.sender())) { + return; + } + + if (c.args().size() == 0) { + this.plugin.getTokensManager().sendInfoMessage(c.sender(), (OfflinePlayer) c.sender(), false); + } else if (c.args().size() == 1) { + OfflinePlayer target = Players.getOfflineNullable(c.rawArg(0)); + this.plugin.getTokensManager().sendInfoMessage(c.sender(), target, false); + } + }) + .registerAndBind(this.plugin.getCore(), "blocks"); + + // /blocksadmin, /blocksa + Commands.create() + .tabHandler(c -> Arrays.asList("add", "remove", "set")) + .assertPermission(TokensConstants.TOKENS_ADMIN_PERM, this.plugin.getTokensConfig().getMessage("no_permission")) + .handler(c -> { + if (c.args().size() == 3) { + + OfflinePlayer target = c.arg(1).parseOrFail(OfflinePlayer.class); + long amount = c.arg(2).parseOrFail(Long.class); + + switch (c.rawArg(0).toLowerCase()) { + case "add": + this.plugin.getTokensManager().addBlocksBroken(c.sender(), target, amount); + break; + case "remove": + this.plugin.getTokensManager().removeBlocksBroken(c.sender(), target, amount); + break; + case "set": + this.plugin.getTokensManager().setBlocksBroken(c.sender(), target, amount); + break; + default: + PlayerUtils.sendMessage(c.sender(), "&c/blocksadmin "); + break; + } + } else { + PlayerUtils.sendMessage(c.sender(), "&c/blocksadmin "); + } + }) + .registerAndBind(this.plugin.getCore(), "blocksadmin", "blocksa"); + } + + private List createTabHandler(CommandContext context) { + List returnList = this.commands.stream().map(TokensCommand::getName).collect(Collectors.toList()); + + TokensCommand subCommand = this.getCommand(context.rawArg(0)); + + if (subCommand != null) { + return subCommand.getTabComplete(context.args().subList(1, context.args().size())); + } + + return returnList; + } + + private void registerCommand(TokensCommand command) { + this.commands.add(command); + } + + private TokensCommand getCommand(String arg) { + for (TokensCommand command : this.commands) { + + if (command.getName().equalsIgnoreCase(arg)) { + return command; + } + + if (command.getAliases() == null) { + continue; + } + + for (String alias : command.getAliases()) { + if (alias.equalsIgnoreCase(arg)) { + return command; + } + } + } + return null; + } + + public Set getAll() { + return new HashSet<>(this.commands); + } + + public void reload() { + Map cooldownMap = this.tokensCommandCooldownMap.getAll(); + this.tokensCommandCooldownMap = CooldownMap.create(Cooldown.of(plugin.getTokensConfig().getCommandCooldown(), TimeUnit.SECONDS)); + cooldownMap.forEach((commandSender, cooldown) -> this.tokensCommandCooldownMap.put(commandSender, cooldown)); + } + + public void enable() { + this.registerCommands(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/managers/TokensManager.java b/src/main/java/dev/drawethree/xprison/tokens/managers/TokensManager.java new file mode 100644 index 0000000..fb1c049 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/managers/TokensManager.java @@ -0,0 +1,642 @@ +package dev.drawethree.xprison.tokens.managers; + +import dev.drawethree.xprison.api.enums.LostCause; +import dev.drawethree.xprison.api.enums.ReceiveCause; +import dev.drawethree.xprison.tokens.XPrisonTokens; +import dev.drawethree.xprison.tokens.api.events.PlayerTokensLostEvent; +import dev.drawethree.xprison.tokens.api.events.PlayerTokensReceiveEvent; +import dev.drawethree.xprison.tokens.api.events.XPrisonBlockBreakEvent; +import dev.drawethree.xprison.tokens.model.BlockReward; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import dev.drawethree.xprison.utils.item.PrisonItem; +import dev.drawethree.xprison.utils.misc.NumberUtils; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import me.lucko.helper.Events; +import me.lucko.helper.Schedulers; +import me.lucko.helper.time.Time; +import me.lucko.helper.utils.Players; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +public class TokensManager { + + private final XPrisonTokens plugin; + private final Map tokensCache; + private final Map blocksCache; + private final Map blocksCacheWeekly; + private final List tokenMessageOnPlayers; + + public TokensManager(XPrisonTokens plugin) { + this.plugin = plugin; + this.tokenMessageOnPlayers = new ArrayList<>(); + this.tokensCache = new ConcurrentHashMap<>(); + this.blocksCache = new ConcurrentHashMap<>(); + this.blocksCacheWeekly = new ConcurrentHashMap<>(); + } + + public void savePlayerData(Collection players, boolean removeFromCache, boolean async) { + if (async) { + Schedulers.async().run(() -> savePlayerDataLogic(players, removeFromCache)); + } else { + savePlayerDataLogic(players, removeFromCache); + } + } + + private void savePlayerDataLogic(Collection players, boolean removeFromCache) { + for (Player player : players) { + this.plugin.getTokensService().setTokens(player, tokensCache.getOrDefault(player.getUniqueId(), 0L)); + this.plugin.getBlocksService().setBlocks(player, blocksCache.getOrDefault(player.getUniqueId(), 0L)); + this.plugin.getBlocksService().setBlocksWeekly(player, blocksCacheWeekly.getOrDefault(player.getUniqueId(), 0L)); + + if (removeFromCache) { + this.tokensCache.remove(player.getUniqueId()); + this.blocksCache.remove(player.getUniqueId()); + this.blocksCacheWeekly.remove(player.getUniqueId()); + } + + this.plugin.getCore().debug(String.format("Saved player %s tokens & blocks broken to database.", player.getName()), this.plugin); + } + } + + public void savePlayerDataOnDisable() { + for (UUID uuid : blocksCache.keySet()) { + this.plugin.getBlocksService().setBlocks(Players.getOfflineNullable(uuid), blocksCache.get(uuid)); + } + for (UUID uuid : tokensCache.keySet()) { + this.plugin.getTokensService().setTokens(Players.getOfflineNullable(uuid), tokensCache.get(uuid)); + } + for (UUID uuid : blocksCache.keySet()) { + this.plugin.getBlocksService().setBlocksWeekly(Players.getOfflineNullable(uuid), blocksCacheWeekly.get(uuid)); + } + tokensCache.clear(); + blocksCache.clear(); + blocksCacheWeekly.clear(); + this.plugin.getCore().getLogger().info("Saved online player tokens, blocks broken and weekly blocks broken."); + + } + + private void loadPlayerDataOnEnable() { + loadPlayerData(Players.all()); + } + + public void loadPlayerData(Collection players) { + Schedulers.async().run(() -> { + for (Player player : players) { + + this.plugin.getTokensService().createTokens(player, this.plugin.getTokensConfig().getStartingTokens()); + this.plugin.getBlocksService().createBlocks(player); + this.plugin.getBlocksService().createBlocksWeekly(player); + + long playerTokens = this.plugin.getTokensService().getTokens(player); + long playerBlocks = this.plugin.getBlocksService().getPlayerBrokenBlocks(player); + long playerBlocksWeekly = this.plugin.getBlocksService().getPlayerBrokenBlocksWeekly(player); + + this.tokensCache.put(player.getUniqueId(), playerTokens); + this.blocksCache.put(player.getUniqueId(), playerBlocks); + this.blocksCacheWeekly.put(player.getUniqueId(), playerBlocksWeekly); + + this.plugin.getCore().debug(String.format("Loaded tokens and blocks broken of player %s from database", player.getName()), this.plugin); + } + }); + } + + public void setTokens(OfflinePlayer p, long newAmount, CommandSender executor) { + Schedulers.async().run(() -> { + if (!p.isOnline()) { + this.plugin.getTokensService().setTokens(p, newAmount); + } else { + tokensCache.put(p.getUniqueId(), newAmount); + } + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("admin_set_tokens").replace("%player%", p.getName()).replace("%tokens%", String.format("%,d", newAmount))); + }); + } + + public void giveTokens(OfflinePlayer p, long amount, CommandSender executor, ReceiveCause cause) { + long currentTokens = getPlayerTokens(p); + + this.plugin.getCore().debug("XPrisonPlayerTokenReceiveEvent :: Player Tokens :: " + currentTokens, this.plugin); + + long finalAmount = this.callTokensReceiveEvent(cause, p, amount); + + this.plugin.getCore().debug("XPrisonPlayerTokenReceiveEvent :: Final amount :: " + finalAmount, this.plugin); + + long newAmount; + + if (NumberUtils.wouldAdditionBeOverMaxLong(currentTokens, finalAmount)) { + newAmount = Long.MAX_VALUE; + } else { + newAmount = currentTokens + finalAmount; + } + + if (!p.isOnline()) { + this.plugin.getTokensService().setTokens(p, newAmount); + } else { + tokensCache.put(p.getUniqueId(), newAmount); + if (executor instanceof ConsoleCommandSender && !this.hasOffTokenMessages(p.getPlayer())) { + PlayerUtils.sendMessage(p.getPlayer(), plugin.getTokensConfig().getMessage("tokens_received_console").replace("%tokens%", String.format("%,d", finalAmount)).replace("%player%", executor == null ? "Console" : executor.getName())); + } else if (cause == ReceiveCause.MINING && !this.hasOffTokenMessages(p.getPlayer())) { + PlayerUtils.sendMessage(p.getPlayer(), this.plugin.getTokensConfig().getMessage("tokens_received_mining").replace("%amount%", String.format("%,d", finalAmount))); + } else if (cause == ReceiveCause.LUCKY_BLOCK && !this.hasOffTokenMessages(p.getPlayer())) { + PlayerUtils.sendMessage(p.getPlayer(), this.plugin.getTokensConfig().getMessage("lucky_block_mined").replace("%amount%", String.format("%,d", finalAmount))); + } + } + + this.plugin.getCore().debug("XPlayerTokenReceiveEvent :: Player tokens final :: " + this.tokensCache.getOrDefault(p.getUniqueId(), 0L), this.plugin); + + if (executor != null && !(executor instanceof ConsoleCommandSender)) { + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("admin_give_tokens").replace("%player%", p.getName()).replace("%tokens%", String.format("%,d", finalAmount))); + } + } + + private long callTokensReceiveEvent(ReceiveCause cause, OfflinePlayer p, long amount) { + PlayerTokensReceiveEvent event = new PlayerTokensReceiveEvent(cause, p, amount); + + Events.callSync(event); + + if (event.isCancelled()) { + return amount; + } + + return event.getAmount(); + } + + public void redeemTokens(Player p, ItemStack item, boolean shiftClick, boolean offhand) { + final Long tokenAmount = new PrisonItem(item).getTokens(); + if (tokenAmount == null) { + PlayerUtils.sendMessage(p, plugin.getTokensConfig().getMessage("not_token_item")); + return; + } + int itemAmount = item.getAmount(); + if (shiftClick) { + if (offhand) { + p.getInventory().setItemInOffHand(null); + } else { + p.setItemInHand(null); + } + this.giveTokens(p, tokenAmount * itemAmount, null, ReceiveCause.REDEEM); + PlayerUtils.sendMessage(p, plugin.getTokensConfig().getMessage("tokens_redeem").replace("%tokens%", String.format("%,d", tokenAmount * itemAmount))); + } else { + this.giveTokens(p, tokenAmount, null, ReceiveCause.REDEEM); + if (item.getAmount() == 1) { + if (offhand) { + p.getInventory().setItemInOffHand(null); + } else { + p.setItemInHand(null); + } + } else { + item.setAmount(item.getAmount() - 1); + } + PlayerUtils.sendMessage(p, plugin.getTokensConfig().getMessage("tokens_redeem").replace("%tokens%", String.format("%,d", tokenAmount))); + } + } + + public void payTokens(Player executor, long amount, OfflinePlayer target) { + Schedulers.async().run(() -> { + if (getPlayerTokens(executor) >= amount) { + this.removeTokens(executor, amount, null, LostCause.PAY); + this.giveTokens(target, amount, null, ReceiveCause.PAY); + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("tokens_send").replace("%player%", target.getName()).replace("%tokens%", String.format("%,d", amount))); + if (target.isOnline()) { + PlayerUtils.sendMessage((CommandSender) target, plugin.getTokensConfig().getMessage("tokens_received").replace("%player%", executor.getName()).replace("%tokens%", String.format("%,d", amount))); + } + } else { + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("not_enough_tokens")); + } + }); + } + + public void withdrawTokens(Player executor, long amount, int value) { + Schedulers.async().run(() -> { + long totalAmount = amount * value; + + if (this.getPlayerTokens(executor) < totalAmount) { + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("not_enough_tokens")); + return; + } + + removeTokens(executor, totalAmount, null, LostCause.WITHDRAW); + + ItemStack item = createTokenItem(amount, value); + Collection notFit = executor.getInventory().addItem(item).values(); + + if (!notFit.isEmpty()) { + notFit.forEach(itemStack -> { + this.giveTokens(executor, amount * item.getAmount(), null, ReceiveCause.REDEEM); + }); + } + + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("withdraw_successful").replace("%amount%", String.format("%,d", amount)).replace("%value%", String.format("%,d", value))); + }); + } + + public synchronized long getPlayerTokens(OfflinePlayer p) { + if (!p.isOnline()) { + return this.plugin.getTokensManager().getPlayerTokens(p); + } else { + return tokensCache.getOrDefault(p.getUniqueId(), (long) 0); + } + } + + public synchronized long getPlayerBrokenBlocks(OfflinePlayer p) { + if (!p.isOnline()) { + return this.plugin.getBlocksService().getPlayerBrokenBlocks(p); + } else { + return blocksCache.getOrDefault(p.getUniqueId(), (long) 0); + } + } + + public synchronized long getPlayerBrokenBlocksWeekly(OfflinePlayer p) { + if (!p.isOnline()) { + return this.plugin.getBlocksService().getPlayerBrokenBlocksWeekly(p); + } else { + return blocksCacheWeekly.getOrDefault(p.getUniqueId(), (long) 0); + } + } + + public void removeTokens(OfflinePlayer p, long amount, CommandSender executor, LostCause cause) { + long currentTokens = getPlayerTokens(p); + long finalTokens = currentTokens - amount; + + if (finalTokens < 0) { + finalTokens = 0; + } + + this.callTokensLostEvent(cause, p, amount); + + if (!p.isOnline()) { + this.plugin.getTokensService().setTokens(p, amount); + } else { + tokensCache.put(p.getUniqueId(), finalTokens); + } + if (executor != null) { + PlayerUtils.sendMessage(executor, plugin.getTokensConfig().getMessage("admin_remove_tokens").replace("%player%", p.getName()).replace("%tokens%", String.format("%,d", amount))); + } + } + + private void callTokensLostEvent(LostCause cause, OfflinePlayer p, long amount) { + PlayerTokensLostEvent event = new PlayerTokensLostEvent(cause, p, amount); + Events.callSync(event); + } + + private ItemStack createTokenItem(long amount, int value) { + ItemStack item = ItemStackBuilder.of( + this.plugin.getTokensConfig().getTokenItem().clone()) + .amount(value) + .name(this.plugin.getTokensConfig().getTokenItemDisplayName().replace("%amount%", String.format("%,d", amount)).replace("%tokens%", String.format("%,d", amount))) + .lore(this.plugin.getTokensConfig().getTokenItemLore()) + .enchant(Enchantment.PROTECTION_ENVIRONMENTAL) + .flag(ItemFlag.HIDE_ENCHANTS) + .build(); + final PrisonItem prisonItem = new PrisonItem(item); + prisonItem.setTokens(amount); + prisonItem.load(); + return item; + } + + public void sendInfoMessage(CommandSender sender, OfflinePlayer target, boolean tokens) { + Schedulers.async().run(() -> { + if (sender == target) { + if (tokens) { + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("your_tokens").replace("%tokens%", String.format("%,d", this.getPlayerTokens(target)))); + } else { + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("your_blocks").replace("%blocks%", String.format("%,d", this.getPlayerBrokenBlocks(target)))); + } + } else { + if (tokens) { + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("other_tokens").replace("%tokens%", String.format("%,d", this.getPlayerTokens(target))).replace("%player%", target.getName())); + } else { + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("other_blocks").replace("%blocks%", String.format("%,d", this.getPlayerBrokenBlocks(target))).replace("%player%", target.getName())); + } + } + }); + } + + + public void addBlocksBroken(CommandSender sender, OfflinePlayer player, long amount) { + + if (amount <= 0) { + if (sender != null) { + PlayerUtils.sendMessage(sender, "&cPlease specify amount greater than 0!"); + } + return; + } + + long finalAmount = amount; + + Schedulers.async().run(() -> { + + long currentBroken = getPlayerBrokenBlocks(player); + long currentBrokenWeekly = getPlayerBrokenBlocksWeekly(player); + + BlockReward nextReward = this.getNextBlockReward(player); + + if (!player.isOnline()) { + this.plugin.getBlocksService().setBlocks(player, currentBroken + finalAmount); + this.plugin.getBlocksService().setBlocksWeekly(player, currentBrokenWeekly + finalAmount); + } else { + blocksCache.put(player.getUniqueId(), currentBroken + finalAmount); + blocksCacheWeekly.put(player.getUniqueId(), currentBrokenWeekly + finalAmount); + + while (nextReward != null && nextReward.getBlocksRequired() <= blocksCache.get(player.getUniqueId())) { + nextReward.giveTo((Player) player); + nextReward = this.getNextBlockReward(nextReward); + } + } + + if (sender != null && !(sender instanceof ConsoleCommandSender)) { + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("admin_give_blocks").replace("%player%", player.getName()).replace("%blocks%", String.format("%,d", finalAmount))); + } + }); + } + + public void addBlocksBroken(OfflinePlayer player, List blocks) { + + if (player.isOnline()) { + XPrisonBlockBreakEvent event = new XPrisonBlockBreakEvent((Player) player, blocks); + + Events.call(event); + + if (event.isCancelled()) { + return; + } + + blocks = event.getBlocks(); + } + + long finalAmount = blocks.size(); + + Schedulers.async().run(() -> { + + long currentBroken = getPlayerBrokenBlocks(player); + long currentBrokenWeekly = getPlayerBrokenBlocksWeekly(player); + + BlockReward nextReward = this.getNextBlockReward(player); + + if (!player.isOnline()) { + this.plugin.getBlocksService().setBlocks(player, currentBroken + finalAmount); + this.plugin.getBlocksService().setBlocksWeekly(player, currentBrokenWeekly + finalAmount); + } else { + blocksCache.put(player.getUniqueId(), currentBroken + finalAmount); + blocksCacheWeekly.put(player.getUniqueId(), currentBrokenWeekly + finalAmount); + + while (nextReward != null && nextReward.getBlocksRequired() <= blocksCache.get(player.getUniqueId())) { + nextReward.giveTo((Player) player); + nextReward = this.getNextBlockReward(nextReward); + } + } + }); + } + + private BlockReward getNextBlockReward(BlockReward oldReward) { + boolean next = false; + for (long l : this.plugin.getBlockRewardsConfig().getBlockRewards().keySet()) { + if (next) { + return this.plugin.getBlockRewardsConfig().getBlockRewards().get(l); + } + if (l == oldReward.getBlocksRequired()) { + next = true; + } + } + + return null; + } + + public void removeBlocksBroken(CommandSender sender, OfflinePlayer player, long amount) { + + if (amount <= 0) { + PlayerUtils.sendMessage(sender, "&cPlease specify amount greater than 0!"); + return; + } + + Schedulers.async().run(() -> { + + long currentBroken = getPlayerBrokenBlocks(player); + long currentBrokenWeekly = getPlayerBrokenBlocksWeekly(player); + + if (!player.isOnline()) { + this.plugin.getBlocksService().setBlocks(player, currentBroken - amount); + this.plugin.getBlocksService().setBlocksWeekly(player, currentBrokenWeekly - amount); + } else { + blocksCache.put(player.getUniqueId(), currentBroken - amount); + blocksCacheWeekly.put(player.getUniqueId(), currentBrokenWeekly - amount); + } + + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("admin_remove_blocks").replace("%player%", player.getName()).replace("%blocks%", String.format("%,d", amount))); + + }); + } + + public void setBlocksBroken(CommandSender sender, OfflinePlayer player, long amount) { + + if (amount < 0) { + PlayerUtils.sendMessage(sender, "&cPlease specify positive amount!"); + return; + } + + Schedulers.async().run(() -> { + BlockReward nextReward = this.getNextBlockReward(player); + + if (!player.isOnline()) { + + this.plugin.getBlocksService().setBlocks(player, amount); + this.plugin.getBlocksService().setBlocksWeekly(player, amount); + } else { + blocksCache.put(player.getUniqueId(), amount); + blocksCacheWeekly.put(player.getUniqueId(), amount); + + while (nextReward != null && nextReward.getBlocksRequired() <= blocksCache.get(player.getUniqueId())) { + nextReward.giveTo((Player) player); + nextReward = this.getNextBlockReward(nextReward); + } + } + + PlayerUtils.sendMessage(sender, plugin.getTokensConfig().getMessage("admin_set_blocks").replace("%player%", player.getName()).replace("%blocks%", String.format("%,d", amount))); + }); + } + + public void sendTokensTop(CommandSender sender) { + + List format = this.plugin.getTokensConfig().getTokensTopFormat(); + + Schedulers.async().run(() -> { + Map topTokens = this.plugin.getTokensService().getTopTokens(this.plugin.getTokensConfig().getTopPlayersAmount()); + for (String s : format) { + if (s.startsWith("{FOR_EACH_PLAYER}")) { + String rawContent = s.replace("{FOR_EACH_PLAYER} ", ""); + for (int i = 0; i < 10; i++) { + try { + UUID uuid = (UUID) topTokens.keySet().toArray()[i]; + OfflinePlayer player = Players.getOfflineNullable(uuid); + String name; + if (player.getName() == null) { + name = "Unknown Player"; + } else { + name = player.getName(); + } + long tokens = topTokens.get(uuid); + PlayerUtils.sendMessage(sender, rawContent.replace("%position%", String.valueOf(i + 1)).replace("%player%", name).replace("%tokens%", String.format("%,d", tokens))); + } catch (Exception e) { + break; + } + } + } else { + PlayerUtils.sendMessage(sender, s); + } + } + }); + } + + public void sendBlocksTop(CommandSender sender) { + List format = this.plugin.getTokensConfig().getBlocksTopFormat(); + Schedulers.async().run(() -> { + Map topBlocks = this.plugin.getBlocksService().getTopBlocks(this.plugin.getTokensConfig().getTopPlayersAmount()); + for (String s : format) { + if (s.startsWith("{FOR_EACH_PLAYER}")) { + sendBlocksTop(sender, s, topBlocks); + } else { + PlayerUtils.sendMessage(sender, s); + } + } + }); + } + + private void sendBlocksTop(CommandSender sender, String s, Map top) { + String rawContent = s.replace("{FOR_EACH_PLAYER} ", ""); + for (int i = 0; i < 10; i++) { + try { + UUID uuid = (UUID) top.keySet().toArray()[i]; + OfflinePlayer player = Players.getOfflineNullable(uuid); + String name; + if (player.getName() == null) { + name = "Unknown Player"; + } else { + name = player.getName(); + } + long blocks = top.get(uuid); + PlayerUtils.sendMessage(sender, rawContent.replace("%position%", String.valueOf(i + 1)).replace("%player%", name).replace("%blocks%", String.format("%,d", blocks))); + } catch (Exception e) { + break; + } + } + } + + public void sendBlocksTopWeekly(CommandSender sender) { + List format = this.plugin.getTokensConfig().getBlocksTopFormatWeekly(); + + Schedulers.async().run(() -> { + Map topBlocksWeekly = this.plugin.getBlocksService().getTopBlocksWeekly(this.plugin.getTokensConfig().getTopPlayersAmount()); + for (String s : format) { + if (s.startsWith("{FOR_EACH_PLAYER}")) { + sendBlocksTop(sender, s, topBlocksWeekly); + } else { + PlayerUtils.sendMessage(sender, s); + } + } + }); + } + + public BlockReward getNextBlockReward(OfflinePlayer p) { + long blocksBroken = this.getPlayerBrokenBlocks(p); + for (long l : this.plugin.getBlockRewardsConfig().getBlockRewards().keySet()) { + if (l > blocksBroken) { + return this.plugin.getBlockRewardsConfig().getBlockRewards().get(l); + } + } + return null; + } + + public void resetBlocksTopWeekly(CommandSender sender) { + PlayerUtils.sendMessage(sender, "&7&oStarting to reset BlocksTop - Weekly. This may take a while..."); + this.plugin.getTokensConfig().setNextResetWeekly(Time.nowMillis() + TimeUnit.DAYS.toMillis(7)); + this.plugin.getBlocksService().resetBlocksWeekly(); + PlayerUtils.sendMessage(sender, "&aBlocksTop - Weekly - Reset!"); + } + + private void saveWeeklyReset() { + this.plugin.getTokensConfig().getYamlConfig().set("next-reset-weekly", this.plugin.getTokensConfig().getNextResetWeekly()); + this.plugin.getTokensConfig().save(); + } + + public void toggleTokenMessage(Player p) { + if (this.tokenMessageOnPlayers.contains(p.getUniqueId())) { + PlayerUtils.sendMessage(p, plugin.getTokensConfig().getMessage("token_message_disabled")); + this.tokenMessageOnPlayers.remove(p.getUniqueId()); + } else { + PlayerUtils.sendMessage(p, plugin.getTokensConfig().getMessage("token_message_enabled")); + this.tokenMessageOnPlayers.add(p.getUniqueId()); + } + } + + public void reload() { + + } + + public void addPlayerIntoTokenMessageOnPlayers(Player player) { + this.tokenMessageOnPlayers.add(player.getUniqueId()); + } + + public boolean hasOffTokenMessages(Player p) { + return !this.tokenMessageOnPlayers.contains(p.getUniqueId()); + } + + public void disable() { + this.saveWeeklyReset(); + this.savePlayerDataOnDisable(); + } + + + public void handleBlockBreak(Player p, List blocks, boolean countBlocksBroken) { + + long startTime = System.currentTimeMillis(); + + if (countBlocksBroken) { + this.addBlocksBroken(p, blocks); + } + + //Lucky block check + blocks.forEach(block -> { + List rewards = this.plugin.getTokensConfig().getLuckyBlockReward(block.getType()); + for (String s : rewards) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", p.getName())); + } + }); + + long totalAmount = 0; + for (int i = 0; i < blocks.size(); i++) { + double random = ThreadLocalRandom.current().nextDouble(100); + + if (this.plugin.getTokensConfig().getChance() >= random) { + long randAmount = this.plugin.getTokensConfig().getMinAmount() == this.plugin.getTokensConfig().getMaxAmount() ? this.plugin.getTokensConfig().getMinAmount() : ThreadLocalRandom.current().nextLong(this.plugin.getTokensConfig().getMinAmount(), this.plugin.getTokensConfig().getMaxAmount()); + totalAmount += randAmount; + } + } + if (totalAmount > 0) { + this.giveTokens(p, totalAmount, null, ReceiveCause.MINING); + } + this.plugin.getCore().debug("XPrisonTokens::handleBlockBreak >> Took " + (System.currentTimeMillis() - startTime) + " ms.", this.plugin); + } + + public void enable() { + if (this.checkBlocksTopWeeklyReset()) { + resetBlocksTopWeekly(Bukkit.getConsoleSender()); + } + this.loadPlayerDataOnEnable(); + } + + private boolean checkBlocksTopWeeklyReset() { + long nextResetWeeklyMillis = this.plugin.getTokensConfig().getNextResetWeekly(); + return Time.nowMillis() >= nextResetWeeklyMillis; + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/model/BlockReward.java b/src/main/java/dev/drawethree/xprison/tokens/model/BlockReward.java new file mode 100644 index 0000000..73ab251 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/model/BlockReward.java @@ -0,0 +1,36 @@ +package dev.drawethree.xprison.tokens.model; + +import dev.drawethree.xprison.utils.player.PlayerUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import me.lucko.helper.Schedulers; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.List; + +@AllArgsConstructor +@Getter +public class BlockReward { + + private final long blocksRequired; + private final List commandsToRun; + private final String message; + + public void giveTo(Player p) { + + if (!Bukkit.isPrimaryThread()) { + Schedulers.sync().run(() -> executeCommands(p)); + } else { + executeCommands(p); + } + + PlayerUtils.sendMessage(p, this.message); + } + + private void executeCommands(Player p) { + for (String s : this.commandsToRun) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", p.getName())); + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/tokens/repo/BlocksRepository.java b/src/main/java/dev/drawethree/xprison/tokens/repo/BlocksRepository.java new file mode 100644 index 0000000..23a1d81 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/repo/BlocksRepository.java @@ -0,0 +1,31 @@ +package dev.drawethree.xprison.tokens.repo; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface BlocksRepository { + + void resetBlocksWeekly(); + + void updateBlocks(OfflinePlayer player, long newAmount); + + void updateBlocksWeekly(OfflinePlayer player, long newAmount); + + long getPlayerBrokenBlocksWeekly(OfflinePlayer player); + + void addIntoBlocks(OfflinePlayer player); + + void addIntoBlocksWeekly(OfflinePlayer player); + + long getPlayerBrokenBlocks(OfflinePlayer player); + + Map getTopBlocksWeekly(int amountOfRecords); + + Map getTopBlocks(int amountOfRecords); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/repo/TokensRepository.java b/src/main/java/dev/drawethree/xprison/tokens/repo/TokensRepository.java new file mode 100644 index 0000000..f845dd1 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/repo/TokensRepository.java @@ -0,0 +1,21 @@ +package dev.drawethree.xprison.tokens.repo; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface TokensRepository { + + long getPlayerTokens(OfflinePlayer player); + + void updateTokens(OfflinePlayer player, long newAmount); + + Map getTopTokens(int amountOfRecords); + + void addIntoTokens(OfflinePlayer player, long startingTokens); + + void createTables(); + + void clearTableData(); +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/repo/impl/BlocksRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/tokens/repo/impl/BlocksRepositoryImpl.java new file mode 100644 index 0000000..aafb366 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/repo/impl/BlocksRepositoryImpl.java @@ -0,0 +1,122 @@ +package dev.drawethree.xprison.tokens.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import dev.drawethree.xprison.tokens.repo.BlocksRepository; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class BlocksRepositoryImpl implements BlocksRepository { + + private static final String BLOCKS_UUID_COLNAME = "UUID"; + private static final String BLOCKS_BLOCKS_COLNAME = "Blocks"; + private static final String TABLE_NAME_BLOCKS = "UltraPrison_BlocksBroken"; + private static final String TABLE_NAME_BLOCKS_WEEKLY = "UltraPrison_BlocksBrokenWeekly"; + + private final SQLDatabase database; + + public BlocksRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public void resetBlocksWeekly() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME_BLOCKS_WEEKLY); + } + + @Override + public long getPlayerBrokenBlocks(OfflinePlayer player) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT * FROM " + TABLE_NAME_BLOCKS + " WHERE " + BLOCKS_UUID_COLNAME + "=?")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getLong(BLOCKS_BLOCKS_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public long getPlayerBrokenBlocksWeekly(OfflinePlayer player) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT * FROM " + TABLE_NAME_BLOCKS_WEEKLY + " WHERE " + BLOCKS_UUID_COLNAME + "=?")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getLong(BLOCKS_BLOCKS_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public void updateBlocks(OfflinePlayer player, long newAmount) { + this.database.executeSql("UPDATE " + TABLE_NAME_BLOCKS + " SET " + BLOCKS_BLOCKS_COLNAME + "=? WHERE " + BLOCKS_UUID_COLNAME + "=?", newAmount, player.getUniqueId().toString()); + } + + @Override + public void updateBlocksWeekly(OfflinePlayer player, long newAmount) { + this.database.executeSql("UPDATE " + TABLE_NAME_BLOCKS_WEEKLY + " SET " + BLOCKS_BLOCKS_COLNAME + "=? WHERE " + BLOCKS_UUID_COLNAME + "=?", newAmount, player.getUniqueId().toString()); + } + + @Override + public void addIntoBlocks(OfflinePlayer player) { + String sql = database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME_BLOCKS + " VALUES(?,?)" : "INSERT IGNORE INTO " + TABLE_NAME_BLOCKS + " VALUES(?,?)"; + this.database.executeSql(sql, player.getUniqueId().toString(), 0); + } + + @Override + public void addIntoBlocksWeekly(OfflinePlayer player) { + String sql = database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME_BLOCKS_WEEKLY + " VALUES(?,?)" : "INSERT IGNORE INTO " + TABLE_NAME_BLOCKS_WEEKLY + " VALUES(?,?)"; + this.database.executeSql(sql, player.getUniqueId().toString(), 0); + } + + @Override + public Map getTopBlocksWeekly(int amountOfRecords) { + Map topBlocksWeekly = new LinkedHashMap<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT " + BLOCKS_UUID_COLNAME + "," + BLOCKS_BLOCKS_COLNAME + " FROM " + TABLE_NAME_BLOCKS_WEEKLY + " ORDER BY " + BLOCKS_BLOCKS_COLNAME + " DESC LIMIT " + amountOfRecords); ResultSet set = statement.executeQuery()) { + while (set.next()) { + topBlocksWeekly.put(UUID.fromString(set.getString(BLOCKS_UUID_COLNAME)), set.getLong(BLOCKS_BLOCKS_COLNAME)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return topBlocksWeekly; + } + + @Override + public Map getTopBlocks(int amountOfRecords) { + Map topBlocks = new LinkedHashMap<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT " + BLOCKS_UUID_COLNAME + "," + BLOCKS_BLOCKS_COLNAME + " FROM " + TABLE_NAME_BLOCKS + " ORDER BY " + BLOCKS_BLOCKS_COLNAME + " DESC LIMIT " + amountOfRecords); ResultSet set = statement.executeQuery()) { + while (set.next()) { + topBlocks.put(UUID.fromString(set.getString(BLOCKS_UUID_COLNAME)), set.getLong(BLOCKS_BLOCKS_COLNAME)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return topBlocks; + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME_BLOCKS + "(UUID varchar(36) NOT NULL UNIQUE, Blocks bigint, primary key (UUID))"); + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME_BLOCKS_WEEKLY + "(UUID varchar(36) NOT NULL UNIQUE, Blocks bigint, primary key (UUID))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME_BLOCKS); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/repo/impl/TokensRepositoryImpl.java b/src/main/java/dev/drawethree/xprison/tokens/repo/impl/TokensRepositoryImpl.java new file mode 100644 index 0000000..115d6ad --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/repo/impl/TokensRepositoryImpl.java @@ -0,0 +1,77 @@ +package dev.drawethree.xprison.tokens.repo.impl; + +import dev.drawethree.xprison.database.SQLDatabase; +import dev.drawethree.xprison.database.model.SQLDatabaseType; +import dev.drawethree.xprison.tokens.repo.TokensRepository; +import org.bukkit.OfflinePlayer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class TokensRepositoryImpl implements TokensRepository { + + private static final String TABLE_NAME_TOKENS = "UltraPrison_Tokens"; + + private static final String TOKENS_UUID_COLNAME = "UUID"; + private static final String TOKENS_TOKENS_COLNAME = "Tokens"; + + private final SQLDatabase database; + + public TokensRepositoryImpl(SQLDatabase database) { + this.database = database; + } + + @Override + public long getPlayerTokens(OfflinePlayer p) { + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con,"SELECT * FROM " + TABLE_NAME_TOKENS + " WHERE " + TOKENS_UUID_COLNAME + "=?")) { + statement.setString(1, p.getUniqueId().toString()); + try (ResultSet set = statement.executeQuery()) { + if (set.next()) { + return set.getLong(TOKENS_TOKENS_COLNAME); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public void updateTokens(OfflinePlayer p, long amount) { + this.database.executeSql("UPDATE " + TABLE_NAME_TOKENS + " SET " + TOKENS_TOKENS_COLNAME + "=? WHERE " + TOKENS_UUID_COLNAME + "=?", amount, p.getUniqueId().toString()); + } + + @Override + public Map getTopTokens(int amountOfRecords) { + Map top10Tokens = new LinkedHashMap<>(); + try (Connection con = this.database.getConnection(); PreparedStatement statement = database.prepareStatement(con, "SELECT " + TOKENS_UUID_COLNAME + "," + TOKENS_TOKENS_COLNAME + " FROM " + TABLE_NAME_TOKENS + " ORDER BY " + TOKENS_TOKENS_COLNAME + " DESC LIMIT " + amountOfRecords); ResultSet set = statement.executeQuery()) { + while (set.next()) { + top10Tokens.put(UUID.fromString(set.getString(TOKENS_UUID_COLNAME)), set.getLong(TOKENS_TOKENS_COLNAME)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return top10Tokens; + } + + @Override + public void addIntoTokens(OfflinePlayer player, long startingTokens) { + String sql = database.getDatabaseType() == SQLDatabaseType.SQLITE ? "INSERT OR IGNORE INTO " + TABLE_NAME_TOKENS + " VALUES(?,?)" : "INSERT IGNORE INTO " + TABLE_NAME_TOKENS + " VALUES(?,?)"; + this.database.executeSql(sql, player.getUniqueId().toString(), startingTokens); + } + + @Override + public void createTables() { + this.database.executeSql("CREATE TABLE IF NOT EXISTS " + TABLE_NAME_TOKENS + "(UUID varchar(36) NOT NULL UNIQUE, Tokens bigint, primary key (UUID))"); + } + + @Override + public void clearTableData() { + this.database.executeSqlAsync("DELETE FROM " + TABLE_NAME_TOKENS); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/service/BlocksService.java b/src/main/java/dev/drawethree/xprison/tokens/service/BlocksService.java new file mode 100644 index 0000000..f8f0de3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/service/BlocksService.java @@ -0,0 +1,27 @@ +package dev.drawethree.xprison.tokens.service; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface BlocksService { + + void resetBlocksWeekly(); + + void setBlocks(OfflinePlayer player, long newAmount); + + void setBlocksWeekly(OfflinePlayer player, long newAmount); + + long getPlayerBrokenBlocksWeekly(OfflinePlayer player); + + void createBlocks(OfflinePlayer player); + + void createBlocksWeekly(OfflinePlayer player); + + long getPlayerBrokenBlocks(OfflinePlayer player); + + Map getTopBlocksWeekly(int amountOfRecords); + + Map getTopBlocks(int amountOfRecords); +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/service/TokensService.java b/src/main/java/dev/drawethree/xprison/tokens/service/TokensService.java new file mode 100644 index 0000000..63a2c4e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/service/TokensService.java @@ -0,0 +1,17 @@ +package dev.drawethree.xprison.tokens.service; + +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public interface TokensService { + + long getTokens(OfflinePlayer player); + + void setTokens(OfflinePlayer player, long newAmount); + + Map getTopTokens(int amountOfRecords); + + void createTokens(OfflinePlayer player, long startingTokens); +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/service/impl/BlocksServiceImpl.java b/src/main/java/dev/drawethree/xprison/tokens/service/impl/BlocksServiceImpl.java new file mode 100644 index 0000000..42b39a2 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/service/impl/BlocksServiceImpl.java @@ -0,0 +1,62 @@ +package dev.drawethree.xprison.tokens.service.impl; + +import dev.drawethree.xprison.tokens.repo.BlocksRepository; +import dev.drawethree.xprison.tokens.service.BlocksService; +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public class BlocksServiceImpl implements BlocksService { + + private final BlocksRepository repository; + + public BlocksServiceImpl(BlocksRepository repository) { + this.repository = repository; + } + + @Override + public void resetBlocksWeekly() { + repository.resetBlocksWeekly(); + } + + @Override + public void setBlocks(OfflinePlayer player, long newAmount) { + repository.updateBlocks(player, newAmount); + } + + @Override + public void setBlocksWeekly(OfflinePlayer player, long newAmount) { + repository.updateBlocksWeekly(player, newAmount); + } + + @Override + public long getPlayerBrokenBlocksWeekly(OfflinePlayer player) { + return repository.getPlayerBrokenBlocksWeekly(player); + } + + @Override + public void createBlocks(OfflinePlayer player) { + repository.addIntoBlocks(player); + } + + @Override + public void createBlocksWeekly(OfflinePlayer player) { + repository.addIntoBlocksWeekly(player); + } + + @Override + public long getPlayerBrokenBlocks(OfflinePlayer player) { + return repository.getPlayerBrokenBlocks(player); + } + + @Override + public Map getTopBlocksWeekly(int amountOfRecords) { + return repository.getTopBlocksWeekly(amountOfRecords); + } + + @Override + public Map getTopBlocks(int amountOfRecords) { + return repository.getTopBlocks(amountOfRecords); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/service/impl/TokensServiceImpl.java b/src/main/java/dev/drawethree/xprison/tokens/service/impl/TokensServiceImpl.java new file mode 100644 index 0000000..90d7569 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/service/impl/TokensServiceImpl.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.tokens.service.impl; + +import dev.drawethree.xprison.tokens.repo.TokensRepository; +import dev.drawethree.xprison.tokens.service.TokensService; +import org.bukkit.OfflinePlayer; + +import java.util.Map; +import java.util.UUID; + +public class TokensServiceImpl implements TokensService { + + private final TokensRepository repository; + private TokensRepository tokensRepository; + + public TokensServiceImpl(TokensRepository repository) { + + this.repository = repository; + } + + @Override + public long getTokens(OfflinePlayer player) { + return repository.getPlayerTokens(player); + } + + @Override + public void setTokens(OfflinePlayer player, long newAmount) { + repository.updateTokens(player, newAmount); + } + + @Override + public Map getTopTokens(int amountOfRecords) { + return repository.getTopTokens(amountOfRecords); + } + + @Override + public void createTokens(OfflinePlayer player, long startingTokens) { + repository.addIntoTokens(player, startingTokens); + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/task/SavePlayerDataTask.java b/src/main/java/dev/drawethree/xprison/tokens/task/SavePlayerDataTask.java new file mode 100644 index 0000000..57dd529 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/task/SavePlayerDataTask.java @@ -0,0 +1,35 @@ +package dev.drawethree.xprison.tokens.task; + +import dev.drawethree.xprison.tokens.XPrisonTokens; +import me.lucko.helper.Schedulers; +import me.lucko.helper.scheduler.Task; +import me.lucko.helper.utils.Players; + +import java.util.concurrent.TimeUnit; + + +public final class SavePlayerDataTask implements Runnable { + + private final XPrisonTokens plugin; + private Task task; + + public SavePlayerDataTask(XPrisonTokens plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + this.plugin.getTokensManager().savePlayerData(Players.all(), false, true); + } + + public void start() { + stop(); + this.task = Schedulers.async().runRepeating(this, 30, TimeUnit.SECONDS, this.plugin.getTokensConfig().getSavePlayerDataInterval(), TimeUnit.MINUTES); + } + + public void stop() { + if (task != null) { + task.stop(); + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/tokens/utils/TokensConstants.java b/src/main/java/dev/drawethree/xprison/tokens/utils/TokensConstants.java new file mode 100644 index 0000000..91603c6 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/tokens/utils/TokensConstants.java @@ -0,0 +1,10 @@ +package dev.drawethree.xprison.tokens.utils; + +public final class TokensConstants { + + public static final String TOKENS_ADMIN_PERM = "xprison.tokens.admin"; + + private TokensConstants() { + throw new UnsupportedOperationException("Cannot instantiate."); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/Constants.java b/src/main/java/dev/drawethree/xprison/utils/Constants.java new file mode 100644 index 0000000..813e11b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/Constants.java @@ -0,0 +1,15 @@ +package dev.drawethree.xprison.utils; + +public class Constants { + + public static final String DISCORD_LINK = "https://discord.com/invite/9bRjBxKC6y"; + public static final int METRICS_SERVICE_ID = 10520; + public static final String ENCHANTS_WG_FLAG_NAME = "upc-enchants"; + + + private Constants() { + throw new UnsupportedOperationException("Cannot instantiate."); + } + + +} diff --git a/src/main/java/dev/drawethree/xprison/utils/Metrics.java b/src/main/java/dev/drawethree/xprison/utils/Metrics.java new file mode 100644 index 0000000..33885bd --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/Metrics.java @@ -0,0 +1,882 @@ + +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ +package dev.drawethree.xprison.utils; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; +import javax.net.ssl.HttpsURLConnection; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class Metrics { + + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + public Metrics(JavaPlugin plugin, int serviceId) { + 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); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + metricsBase = + new MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText); + } + + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); + } + } + + public static class MetricsBase { + + /** The version of the Metrics class. */ + public static final String METRICS_VERSION = "3.0.2"; + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final ScheduledExecutorService scheduler; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. + * + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. + */ + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics")); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + checkRelocation(); + if (enabled) { + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats + startSubmitting(); + } + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + public void shutdown() { + scheduler.shutdown(); + } + + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** Checks that the class was properly relocated. */ + private void checkRelocation() { + // 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'}); + final String examplePackage = + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + /** + * Gzips the given string. + * + * @param str The string to gzip. + * @return The gzipped string. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable 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 callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class MultiLineChart extends CustomChart { + + private final Callable> 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> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedPie extends CustomChart { + + private final Callable> 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> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimpleBarChart extends CustomChart { + + private final Callable> 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> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedBarChart extends CustomChart { + + private final Callable> 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> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class DrilldownPie extends CustomChart { + + private final Callable>> 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>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public abstract static class CustomChart { + + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; + } + + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } + + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } + + public static class SingleLineChart extends CustomChart { + + private final Callable 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 callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); + + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); + } + + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } + + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } + + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } + + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } + + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } + + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. + */ + public static class JsonObject { + + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/block/CuboidExplosionBlockProvider.java b/src/main/java/dev/drawethree/xprison/utils/block/CuboidExplosionBlockProvider.java new file mode 100644 index 0000000..fd1a2b9 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/block/CuboidExplosionBlockProvider.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.utils.block; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; + +import java.util.ArrayList; +import java.util.List; + +public final class CuboidExplosionBlockProvider implements ExplosionBlockProvider { + + private static CuboidExplosionBlockProvider INSTANCE; + + private CuboidExplosionBlockProvider() { + } + + @Override + public List provide(Block center, int radius) { + List blocks = new ArrayList<>(); + final Location startLocation = center.getLocation(); + final World world = center.getWorld(); + for (int x = startLocation.getBlockX() - (radius == 4 ? 0 : (radius / 2)); x <= startLocation.getBlockX() + (radius == 4 ? radius - 1 : (radius / 2)); x++) { + for (int z = startLocation.getBlockZ() - (radius == 4 ? 0 : (radius / 2)); z <= startLocation.getBlockZ() + (radius == 4 ? radius - 1 : (radius / 2)); z++) { + for (int y = startLocation.getBlockY() - (radius == 4 ? 3 : (radius / 2)); y <= startLocation.getBlockY() + (radius == 4 ? 0 : (radius / 2)); y++) { + Block block = world.getBlockAt(x, y, z); + blocks.add(block); + } + } + } + return blocks; + } + + public static CuboidExplosionBlockProvider instance() { + if (INSTANCE == null) { + INSTANCE = new CuboidExplosionBlockProvider(); + } + return INSTANCE; + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/block/ExplosionBlockProvider.java b/src/main/java/dev/drawethree/xprison/utils/block/ExplosionBlockProvider.java new file mode 100644 index 0000000..701f5e7 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/block/ExplosionBlockProvider.java @@ -0,0 +1,10 @@ +package dev.drawethree.xprison.utils.block; + +import org.bukkit.block.Block; + +import java.util.List; + +public interface ExplosionBlockProvider { + + List provide(Block center, int radius); +} diff --git a/src/main/java/dev/drawethree/xprison/utils/block/SpheroidExplosionBlockProvider.java b/src/main/java/dev/drawethree/xprison/utils/block/SpheroidExplosionBlockProvider.java new file mode 100644 index 0000000..08a3771 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/block/SpheroidExplosionBlockProvider.java @@ -0,0 +1,34 @@ +package dev.drawethree.xprison.utils.block; + +import org.bukkit.block.Block; + +import java.util.ArrayList; +import java.util.List; + +public final class SpheroidExplosionBlockProvider implements ExplosionBlockProvider { + + private static SpheroidExplosionBlockProvider INSTANCE; + + private SpheroidExplosionBlockProvider() { + } + + @Override + public List provide(Block center, int radius) { + List blocks = new ArrayList<>(); + for (int Y = -radius; Y < radius; Y++) + for (int X = -radius; X < radius; X++) + for (int Z = -radius; Z < radius; Z++) + if (Math.sqrt((X * X) + (Y * Y) + (Z * Z)) <= radius) { + final Block block = center.getWorld().getBlockAt(X + center.getX(), Y + center.getY(), Z + center.getZ()); + blocks.add(block); + } + return blocks; + } + + public static SpheroidExplosionBlockProvider instance() { + if (INSTANCE == null) { + INSTANCE = new SpheroidExplosionBlockProvider(); + } + return INSTANCE; + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/compat/CompMaterial.java b/src/main/java/dev/drawethree/xprison/utils/compat/CompMaterial.java new file mode 100644 index 0000000..21b2f01 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/compat/CompMaterial.java @@ -0,0 +1,2516 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Hex_27 + * Copyright (c) 2021 Crypto Morin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package dev.drawethree.xprison.utils.compat; + +import lombok.Getter; +import lombok.NonNull; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; +import java.util.*; + +import static dev.drawethree.xprison.utils.compat.MinecraftVersion.V; + +/** + * CompMaterial - Data Values/Pre-flattening
+ * 1.13 and above as priority. + *

+ * This class is mainly designed to support {@link ItemStack}. If you want to use it on blocks, you'll have to use + * XBlock + *

+ * Pre-flattening: https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening + * Materials: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + * Materials (1.12): https://helpch.at/docs/1.12.2/index.html?org/bukkit/Material.html + * Material IDs: https://minecraft-ids.grahamedgecombe.com/ + * Material Source Code: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/Material.java + * XMaterial v1: https://www.spigotmc.org/threads/329630/ + *

+ * This class will throw a "unsupported material" error if someone tries to use an item with an invalid data value which can only happen in 1.12 servers and below or when the + * utility is missing a new material in that specific version. + * To get an invalid item, (aka Missing Texture Block) you can use the command + * /give @p minecraft:dirt 1 10 where 1 is the item amount, and 10 is the data value. The material {@link #DIRT} with a data value of {@code 10} doesn't exist. + * + * @author Crypto Morin, forked by kangarko from MineAcademy.org + */ +public enum CompMaterial { + ACACIA_BOAT("BOAT_ACACIA"), + ACACIA_BUTTON("WOOD_BUTTON"), + ACACIA_CHEST_BOAT, + ACACIA_DOOR("ACACIA_DOOR", "ACACIA_DOOR_ITEM"), + ACACIA_FENCE, + ACACIA_FENCE_GATE, + ACACIA_LEAVES("LEAVES_2"), + ACACIA_LOG("LOG_2"), + ACACIA_PLANKS(4, "WOOD"), + ACACIA_PRESSURE_PLATE("WOOD_PLATE"), + ACACIA_SAPLING(4, "SAPLING"), + ACACIA_SIGN("SIGN_POST", "SIGN"), + ACACIA_SLAB(4, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + ACACIA_STAIRS("SMOOTH_STAIRS"), + ACACIA_TRAPDOOR("TRAP_DOOR"), + ACACIA_WALL_SIGN("WALL_SIGN"), + ACACIA_WOOD("LOG_2"), + ACTIVATOR_RAIL, + /** + * https://minecraft.gamepedia.com/Air + * {@link Material#isAir()} + * + * @see #VOID_AIR + * @see #CAVE_AIR + */ + AIR, + ALLAY_SPAWN_EGG, + ALLIUM(2, "RED_ROSE"), + AMETHYST_BLOCK, + AMETHYST_CLUSTER, + AMETHYST_SHARD, + ANCIENT_DEBRIS, + ANDESITE(5, "STONE"), + ANDESITE_SLAB("STEP"), + ANDESITE_STAIRS("SMOOTH_STAIRS"), + ANDESITE_WALL, + ANVIL, + APPLE, + ARMOR_STAND, + ARROW, + ATTACHED_MELON_STEM(7, "MELON_STEM"), + ATTACHED_PUMPKIN_STEM(7, "PUMPKIN_STEM"), + AXOLOTL_BUCKET, + AXOLOTL_SPAWN_EGG, + AZALEA, + AZALEA_LEAVES, + AZURE_BLUET(3, "RED_ROSE"), + BAKED_POTATO, + BAMBOO, + BAMBOO_SAPLING, + BARREL, + BARRIER, + BASALT, + BAT_SPAWN_EGG(65, "MONSTER_EGG"), + BEACON, + BEDROCK, + BEEF("RAW_BEEF"), + BEEHIVE, + /** + * Beetroot is a known material in pre-1.13 + */ + BEETROOT("BEETROOT_BLOCK"), + BEETROOTS("BEETROOT"), + BEETROOT_SEEDS, + BEETROOT_SOUP, + BEE_NEST, + BEE_SPAWN_EGG, + BELL, + BIG_DRIPLEAF, + BIG_DRIPLEAF_STEM, + BIRCH_BOAT("BOAT_BIRCH"), + BIRCH_BUTTON("WOOD_BUTTON"), + BIRCH_CHEST_BOAT, + BIRCH_DOOR("BIRCH_DOOR", "BIRCH_DOOR_ITEM"), + BIRCH_FENCE, + BIRCH_FENCE_GATE, + BIRCH_LEAVES(2, "LEAVES"), + BIRCH_LOG(2, "LOG"), + BIRCH_PLANKS(2, "WOOD"), + BIRCH_PRESSURE_PLATE("WOOD_PLATE"), + BIRCH_SAPLING(2, "SAPLING"), + BIRCH_SIGN("SIGN_POST", "SIGN"), + BIRCH_SLAB(2, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + BIRCH_STAIRS("BIRCH_WOOD_STAIRS"), + BIRCH_TRAPDOOR("TRAP_DOOR"), + BIRCH_WALL_SIGN("WALL_SIGN"), + BIRCH_WOOD(2, "LOG"), + BLACKSTONE, + BLACKSTONE_SLAB("STEP"), + BLACKSTONE_STAIRS("SMOOTH_STAIRS"), + BLACKSTONE_WALL, + BLACK_BANNER("STANDING_BANNER", "BANNER"), + /** + * Version 1.12+ interprets "BED" as BLACK_BED due to enum alphabetic ordering. + */ + BLACK_BED(supports(12) ? 15 : 0, "BED_BLOCK", "BED"), + BLACK_CANDLE, + BLACK_CANDLE_CAKE, + BLACK_CARPET(15, "CARPET"), + BLACK_CONCRETE(15, "CONCRETE"), + BLACK_CONCRETE_POWDER(15, "CONCRETE_POWDER"), + BLACK_DYE(0, 14, "INK_SACK", "INK_SAC"), + BLACK_GLAZED_TERRACOTTA, + BLACK_SHULKER_BOX, + BLACK_STAINED_GLASS(15, "STAINED_GLASS"), + BLACK_STAINED_GLASS_PANE(15, "STAINED_GLASS_PANE"), + BLACK_TERRACOTTA(15, "STAINED_CLAY"), + BLACK_WALL_BANNER("WALL_BANNER"), + BLACK_WOOL(15, "WOOL"), + BLAST_FURNACE, + BLAZE_POWDER, + BLAZE_ROD, + BLAZE_SPAWN_EGG(61, "MONSTER_EGG"), + BLUE_BANNER(4, "STANDING_BANNER", "BANNER"), + BLUE_BED(supports(12) ? 11 : 0, "BED_BLOCK", "BED"), + BLUE_CANDLE, + BLUE_CANDLE_CAKE, + BLUE_CARPET(11, "CARPET"), + BLUE_CONCRETE(11, "CONCRETE"), + BLUE_CONCRETE_POWDER(11, "CONCRETE_POWDER"), + BLUE_DYE(4, "INK_SACK", "LAPIS_LAZULI"), + BLUE_GLAZED_TERRACOTTA, + BLUE_ICE, + BLUE_ORCHID(1, "RED_ROSE"), + BLUE_SHULKER_BOX, + BLUE_STAINED_GLASS(11, "STAINED_GLASS"), + BLUE_STAINED_GLASS_PANE(11, "THIN_GLASS", "STAINED_GLASS_PANE"), + BLUE_TERRACOTTA(11, "STAINED_CLAY"), + BLUE_WALL_BANNER(4, "WALL_BANNER"), + BLUE_WOOL(11, "WOOL"), + BONE, + BONE_BLOCK, + BONE_MEAL(15, "INK_SACK"), + BOOK, + BOOKSHELF, + BOW, + BOWL, + BRAIN_CORAL, + BRAIN_CORAL_BLOCK, + BRAIN_CORAL_FAN, + BRAIN_CORAL_WALL_FAN, + BREAD, + BREWING_STAND("BREWING_STAND", "BREWING_STAND_ITEM"), + BRICK("CLAY_BRICK"), + BRICKS("BRICKS", "BRICK"), + BRICK_SLAB(4, "STEP"), + BRICK_STAIRS("SMOOTH_STAIRS"), + BRICK_WALL, + BROWN_BANNER(3, "STANDING_BANNER", "BANNER"), + BROWN_BED(supports(12) ? 12 : 0, "BED_BLOCK", "BED"), + BROWN_CANDLE, + BROWN_CANDLE_CAKE, + BROWN_CARPET(12, "CARPET"), + BROWN_CONCRETE(12, "CONCRETE"), + BROWN_CONCRETE_POWDER(12, "CONCRETE_POWDER"), + BROWN_DYE(3, "INK_SACK", "DYE", "COCOA_BEANS"), + BROWN_GLAZED_TERRACOTTA, + BROWN_MUSHROOM, + BROWN_MUSHROOM_BLOCK("BROWN_MUSHROOM", "HUGE_MUSHROOM_1"), + BROWN_SHULKER_BOX, + BROWN_STAINED_GLASS(12, "STAINED_GLASS"), + BROWN_STAINED_GLASS_PANE(12, "THIN_GLASS", "STAINED_GLASS_PANE"), + BROWN_TERRACOTTA(12, "STAINED_CLAY"), + BROWN_WALL_BANNER(3, "WALL_BANNER"), + BROWN_WOOL(12, "WOOL"), + BUBBLE_COLUMN, + BUBBLE_CORAL, + BUBBLE_CORAL_BLOCK, + BUBBLE_CORAL_FAN, + BUBBLE_CORAL_WALL_FAN, + BUCKET, + BUDDING_AMETHYST, + BUNDLE, + CACTUS, + CAKE("CAKE_BLOCK"), + CALCITE, + CAMPFIRE, + CANDLE, + CANDLE_CAKE, + CARROT("CARROT_ITEM"), + CARROTS("CARROT"), + CARROT_ON_A_STICK("CARROT_STICK"), + CARTOGRAPHY_TABLE, + CARVED_PUMPKIN, + CAT_SPAWN_EGG, + CAULDRON("CAULDRON", "CAULDRON_ITEM"), + /** + * 1.13 tag is not added because it's the same thing as {@link #AIR} + * + * @see #VOID_AIR + */ + CAVE_AIR("AIR"), + CAVE_SPIDER_SPAWN_EGG(59, "MONSTER_EGG"), + CAVE_VINES, + CAVE_VINES_PLANT, + CHAIN, + CHAINMAIL_BOOTS, + CHAINMAIL_CHESTPLATE, + CHAINMAIL_HELMET, + CHAINMAIL_LEGGINGS, + CHAIN_COMMAND_BLOCK("COMMAND", "COMMAND_CHAIN"), + CHARCOAL(1, "COAL"), + CHEST("LOCKED_CHEST"), + CHEST_MINECART("STORAGE_MINECART"), + CHICKEN("RAW_CHICKEN"), + CHICKEN_SPAWN_EGG(93, "MONSTER_EGG"), + CHIPPED_ANVIL(1, "ANVIL"), + CHISELED_DEEPSLATE, + CHISELED_NETHER_BRICKS(1, "NETHER_BRICKS"), + CHISELED_POLISHED_BLACKSTONE("POLISHED_BLACKSTONE"), + CHISELED_QUARTZ_BLOCK(1, "QUARTZ_BLOCK"), + CHISELED_RED_SANDSTONE(1, "RED_SANDSTONE"), + CHISELED_SANDSTONE(1, "SANDSTONE"), + CHISELED_STONE_BRICKS(3, "SMOOTH_BRICK"), + CHORUS_FLOWER, + CHORUS_FRUIT, + CHORUS_PLANT, + CLAY, + CLAY_BALL, + CLOCK("WATCH"), + COAL, + COAL_BLOCK, + COAL_ORE, + COARSE_DIRT(1, "DIRT"), + COBBLED_DEEPSLATE, + COBBLED_DEEPSLATE_SLAB("STEP"), + COBBLED_DEEPSLATE_STAIRS("SMOOTH_STAIRS"), + COBBLED_DEEPSLATE_WALL, + COBBLESTONE, + COBBLESTONE_SLAB(3, "STEP"), + COBBLESTONE_STAIRS("SMOOTH_STAIRS"), + COBBLESTONE_WALL("COBBLE_WALL"), + COBWEB("WEB"), + COCOA, + COCOA_BEANS(3, "INK_SACK"), + COD("RAW_FISH"), + COD_BUCKET, + COD_SPAWN_EGG, + COMMAND_BLOCK("COMMAND"), + COMMAND_BLOCK_MINECART("COMMAND_MINECART"), + /** + * Unlike redstone torch and redstone lamp... neither REDTONE_COMPARATOR_OFF nor REDSTONE_COMPARATOR_ON + * are items. REDSTONE_COMPARATOR is. + * + * @see #REDSTONE_TORCH + * @see #REDSTONE_LAMP + */ + COMPARATOR("REDSTONE_COMPARATOR_OFF", "REDSTONE_COMPARATOR_ON", "REDSTONE_COMPARATOR"), + COMPASS, + COMPOSTER, + CONDUIT, + COOKED_BEEF, + COOKED_CHICKEN, + COOKED_COD("COOKED_FISH"), + COOKED_MUTTON, + COOKED_PORKCHOP("GRILLED_PORK"), + COOKED_RABBIT, + COOKED_SALMON(1, "COOKED_FISH"), + COOKIE, + COPPER_BLOCK, + COPPER_INGOT, + COPPER_ORE, + CORNFLOWER, + COW_SPAWN_EGG(92, "MONSTER_EGG"), + CRACKED_DEEPSLATE_BRICKS, + CRACKED_DEEPSLATE_TILES, + CRACKED_NETHER_BRICKS(2, "NETHER_BRICKS"), + CRACKED_POLISHED_BLACKSTONE_BRICKS("POLISHED_BLACKSTONE_BRICKS"), + CRACKED_STONE_BRICKS(2, "SMOOTH_BRICK"), + CRAFTING_TABLE("WORKBENCH"), + CREEPER_BANNER_PATTERN, + CREEPER_HEAD(4, "SKULL", "SKULL_ITEM"), + CREEPER_SPAWN_EGG(50, "MONSTER_EGG"), + CREEPER_WALL_HEAD(4, "SKULL", "SKULL_ITEM"), + CRIMSON_BUTTON, + CRIMSON_DOOR, + CRIMSON_FENCE, + CRIMSON_FENCE_GATE, + CRIMSON_FUNGUS, + CRIMSON_HYPHAE, + CRIMSON_NYLIUM, + CRIMSON_PLANKS, + CRIMSON_PRESSURE_PLATE, + CRIMSON_ROOTS, + CRIMSON_SIGN("SIGN_POST"), + CRIMSON_SLAB("STEP"), + CRIMSON_STAIRS("SMOOTH_STAIRS"), + CRIMSON_STEM, + CRIMSON_TRAPDOOR, + CRIMSON_WALL_SIGN("WALL_SIGN"), + CROSSBOW, + CRYING_OBSIDIAN, + CUT_COPPER, + CUT_COPPER_SLAB("STEP"), + CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + CUT_RED_SANDSTONE, + CUT_RED_SANDSTONE_SLAB("STONE_SLAB2"), + CUT_SANDSTONE, + CUT_SANDSTONE_SLAB(1, "STEP"), + CYAN_BANNER(6, "STANDING_BANNER", "BANNER"), + CYAN_BED(supports(12) ? 9 : 0, "BED_BLOCK", "BED"), + CYAN_CANDLE, + CYAN_CANDLE_CAKE, + CYAN_CARPET(9, "CARPET"), + CYAN_CONCRETE(9, "CONCRETE"), + CYAN_CONCRETE_POWDER(9, "CONCRETE_POWDER"), + CYAN_DYE(6, "INK_SACK"), + CYAN_GLAZED_TERRACOTTA, + CYAN_SHULKER_BOX, + CYAN_STAINED_GLASS(9, "STAINED_GLASS"), + CYAN_STAINED_GLASS_PANE(9, "STAINED_GLASS_PANE"), + CYAN_TERRACOTTA(9, "STAINED_CLAY"), + CYAN_WALL_BANNER(6, "WALL_BANNER"), + CYAN_WOOL(9, "WOOL"), + DAMAGED_ANVIL(2, "ANVIL"), + DANDELION("YELLOW_FLOWER"), + DARK_OAK_BOAT("BOAT_DARK_OAK"), + DARK_OAK_BUTTON("WOOD_BUTTON"), + DARK_OAK_CHEST_BOAT, + DARK_OAK_DOOR("DARK_OAK_DOOR", "DARK_OAK_DOOR_ITEM"), + DARK_OAK_FENCE, + DARK_OAK_FENCE_GATE, + DARK_OAK_LEAVES(1, "LEAVES_2"), + DARK_OAK_LOG(1, "LOG_2"), + DARK_OAK_PLANKS(5, "WOOD"), + DARK_OAK_PRESSURE_PLATE("WOOD_PLATE"), + DARK_OAK_SAPLING(5, "SAPLING"), + DARK_OAK_SIGN("SIGN_POST", "SIGN"), + DARK_OAK_SLAB(5, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + DARK_OAK_STAIRS("SMOOTH_STAIRS"), + DARK_OAK_TRAPDOOR("TRAP_DOOR"), + DARK_OAK_WALL_SIGN("WALL_SIGN"), + DARK_OAK_WOOD(1, "LOG_2"), + DARK_PRISMARINE(2, "PRISMARINE"), + DARK_PRISMARINE_SLAB("STEP"), + DARK_PRISMARINE_STAIRS("SMOOTH_STAIRS"), + DAYLIGHT_DETECTOR("DAYLIGHT_DETECTOR_INVERTED"), + DEAD_BRAIN_CORAL, + DEAD_BRAIN_CORAL_BLOCK, + DEAD_BRAIN_CORAL_FAN, + DEAD_BRAIN_CORAL_WALL_FAN, + DEAD_BUBBLE_CORAL, + DEAD_BUBBLE_CORAL_BLOCK, + DEAD_BUBBLE_CORAL_FAN, + DEAD_BUBBLE_CORAL_WALL_FAN, + DEAD_BUSH("LONG_GRASS"), + DEAD_FIRE_CORAL, + DEAD_FIRE_CORAL_BLOCK, + DEAD_FIRE_CORAL_FAN, + DEAD_FIRE_CORAL_WALL_FAN, + DEAD_HORN_CORAL, + DEAD_HORN_CORAL_BLOCK, + DEAD_HORN_CORAL_FAN, + DEAD_HORN_CORAL_WALL_FAN, + DEAD_TUBE_CORAL, + DEAD_TUBE_CORAL_BLOCK, + DEAD_TUBE_CORAL_FAN, + DEAD_TUBE_CORAL_WALL_FAN, + DEBUG_STICK, + DEEPSLATE, + DEEPSLATE_BRICKS, + DEEPSLATE_BRICK_SLAB("STEP"), + DEEPSLATE_BRICK_STAIRS("SMOOTH_STAIRS"), + DEEPSLATE_BRICK_WALL, + DEEPSLATE_COAL_ORE, + DEEPSLATE_COPPER_ORE, + DEEPSLATE_DIAMOND_ORE, + DEEPSLATE_EMERALD_ORE, + DEEPSLATE_GOLD_ORE, + DEEPSLATE_IRON_ORE, + DEEPSLATE_LAPIS_ORE, + DEEPSLATE_REDSTONE_ORE, + DEEPSLATE_TILES, + DEEPSLATE_TILE_SLAB("STEP"), + DEEPSLATE_TILE_STAIRS("SMOOTH_STAIRS"), + DEEPSLATE_TILE_WALL, + DETECTOR_RAIL, + DIAMOND, + DIAMOND_AXE, + DIAMOND_BLOCK, + DIAMOND_BOOTS, + DIAMOND_CHESTPLATE, + DIAMOND_HELMET, + DIAMOND_HOE, + DIAMOND_HORSE_ARMOR("DIAMOND_BARDING"), + DIAMOND_LEGGINGS, + DIAMOND_ORE, + DIAMOND_PICKAXE, + DIAMOND_SHOVEL("DIAMOND_SPADE"), + DIAMOND_SWORD, + DIORITE(3, "STONE"), + DIORITE_SLAB("STEP"), + DIORITE_STAIRS("SMOOTH_STAIRS"), + DIORITE_WALL, + DIRT, + /** + * Changed in 1.17 + */ + DIRT_PATH("GRASS_PATH"), + DISC_FRAGMENT_5, + DISPENSER, + DOLPHIN_SPAWN_EGG, + DONKEY_SPAWN_EGG(32, "MONSTER_EGG"), + DRAGON_BREATH("DRAGONS_BREATH"), + DRAGON_EGG, + DRAGON_HEAD(5, "SKULL", "SKULL_ITEM"), + DRAGON_WALL_HEAD(5, "SKULL", "SKULL_ITEM"), + DRIED_KELP, + DRIED_KELP_BLOCK, + DRIPSTONE_BLOCK, + DROPPER, + DROWNED_SPAWN_EGG, + ECHO_SHARD, + EGG, + ELDER_GUARDIAN_SPAWN_EGG(4, "MONSTER_EGG"), + ELYTRA, + EMERALD, + EMERALD_BLOCK, + EMERALD_ORE, + ENCHANTED_BOOK, + ENCHANTED_GOLDEN_APPLE(1, "GOLDEN_APPLE"), + ENCHANTING_TABLE("ENCHANTMENT_TABLE"), + ENDERMAN_SPAWN_EGG(58, "MONSTER_EGG"), + ENDERMITE_SPAWN_EGG(67, "MONSTER_EGG"), + ENDER_CHEST, + ENDER_EYE("EYE_OF_ENDER"), + ENDER_PEARL, + END_CRYSTAL, + END_GATEWAY, + END_PORTAL("ENDER_PORTAL"), + END_PORTAL_FRAME("ENDER_PORTAL_FRAME"), + END_ROD, + END_STONE("ENDER_STONE"), + END_STONE_BRICKS("END_BRICKS"), + END_STONE_BRICK_SLAB(6, "STEP"), + END_STONE_BRICK_STAIRS("SMOOTH_STAIRS"), + END_STONE_BRICK_WALL, + EVOKER_SPAWN_EGG(34, "MONSTER_EGG"), + EXPERIENCE_BOTTLE("EXP_BOTTLE"), + EXPOSED_COPPER, + EXPOSED_CUT_COPPER, + EXPOSED_CUT_COPPER_SLAB("STEP"), + EXPOSED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + FARMLAND("SOIL"), + FEATHER, + FERMENTED_SPIDER_EYE, + FERN(2, "LONG_GRASS"), + /** + * For some reasons filled map items are really special. + * Their data value starts from 0 and every time a player + * creates a new map that maps data value increases. + * https://github.com/CryptoMorin/XSeries/issues/91 + */ + FILLED_MAP("MAP"), + FIRE, + FIREWORK_ROCKET("FIREWORK"), + FIREWORK_STAR("FIREWORK_CHARGE"), + FIRE_CHARGE("FIREBALL"), + FIRE_CORAL, + FIRE_CORAL_BLOCK, + FIRE_CORAL_FAN, + FIRE_CORAL_WALL_FAN, + FISHING_ROD, + FLETCHING_TABLE, + FLINT, + FLINT_AND_STEEL, + FLOWERING_AZALEA, + FLOWERING_AZALEA_LEAVES, + FLOWER_BANNER_PATTERN, + FLOWER_POT("FLOWER_POT", "FLOWER_POT_ITEM"), + FOX_SPAWN_EGG, + FROGSPAWN, + FROG_SPAWN_EGG, + /** + * This special material cannot be obtained as an item. + */ + FROSTED_ICE, + FURNACE("BURNING_FURNACE"), + FURNACE_MINECART("POWERED_MINECART"), + GHAST_SPAWN_EGG(56, "MONSTER_EGG"), + GHAST_TEAR, + GILDED_BLACKSTONE, + GLASS, + GLASS_BOTTLE, + GLASS_PANE("THIN_GLASS"), + GLISTERING_MELON_SLICE("SPECKLED_MELON"), + GLOBE_BANNER_PATTERN, + GLOWSTONE, + GLOWSTONE_DUST, + GLOW_BERRIES, + GLOW_INK_SAC, + GLOW_ITEM_FRAME, + GLOW_LICHEN, + GLOW_SQUID_SPAWN_EGG, + GOAT_HORN, + GOAT_SPAWN_EGG, + GOLDEN_APPLE, + GOLDEN_AXE("GOLD_AXE"), + GOLDEN_BOOTS("GOLD_BOOTS"), + GOLDEN_CARROT, + GOLDEN_CHESTPLATE("GOLD_CHESTPLATE"), + GOLDEN_HELMET("GOLD_HELMET"), + GOLDEN_HOE("GOLD_HOE"), + GOLDEN_HORSE_ARMOR("GOLD_BARDING"), + GOLDEN_LEGGINGS("GOLD_LEGGINGS"), + GOLDEN_PICKAXE("GOLD_PICKAXE"), + GOLDEN_SHOVEL("GOLD_SPADE"), + GOLDEN_SWORD("GOLD_SWORD"), + GOLD_BLOCK, + GOLD_INGOT, + GOLD_NUGGET, + GOLD_ORE, + GRANITE(1, "STONE"), + GRANITE_SLAB("STEP"), + GRANITE_STAIRS("SMOOTH_STAIRS"), + GRANITE_WALL, + GRASS(1, "LONG_GRASS"), + GRASS_BLOCK("GRASS"), + GRAVEL, + GRAY_BANNER(8, "STANDING_BANNER", "BANNER"), + GRAY_BED(supports(12) ? 7 : 0, "BED_BLOCK", "BED"), + GRAY_CANDLE, + GRAY_CANDLE_CAKE, + GRAY_CARPET(7, "CARPET"), + GRAY_CONCRETE(7, "CONCRETE"), + GRAY_CONCRETE_POWDER(7, "CONCRETE_POWDER"), + GRAY_DYE(8, "INK_SACK"), + GRAY_GLAZED_TERRACOTTA, + GRAY_SHULKER_BOX, + GRAY_STAINED_GLASS(7, "STAINED_GLASS"), + GRAY_STAINED_GLASS_PANE(7, "THIN_GLASS", "STAINED_GLASS_PANE"), + GRAY_TERRACOTTA(7, "STAINED_CLAY"), + GRAY_WALL_BANNER(8, "WALL_BANNER"), + GRAY_WOOL(7, "WOOL"), + GREEN_BANNER(2, "STANDING_BANNER", "BANNER"), + GREEN_BED(supports(12) ? 13 : 0, "BED_BLOCK", "BED"), + GREEN_CANDLE, + GREEN_CANDLE_CAKE, + GREEN_CARPET(13, "CARPET"), + GREEN_CONCRETE(13, "CONCRETE"), + GREEN_CONCRETE_POWDER(13, "CONCRETE_POWDER"), + /** + * 1.13 renamed to CACTUS_GREEN + * 1.14 renamed to GREEN_DYE + */ + GREEN_DYE(2, "INK_SACK", "CACTUS_GREEN"), + GREEN_GLAZED_TERRACOTTA, + GREEN_SHULKER_BOX, + GREEN_STAINED_GLASS(13, "STAINED_GLASS"), + GREEN_STAINED_GLASS_PANE(13, "THIN_GLASS", "STAINED_GLASS_PANE"), + GREEN_TERRACOTTA(13, "STAINED_CLAY"), + GREEN_WALL_BANNER(2, "WALL_BANNER"), + GREEN_WOOL(13, "WOOL"), + GRINDSTONE, + GUARDIAN_SPAWN_EGG(68, "MONSTER_EGG"), + GUNPOWDER("SULPHUR"), + HANGING_ROOTS, + HAY_BLOCK, + HEART_OF_THE_SEA, + HEAVY_WEIGHTED_PRESSURE_PLATE("IRON_PLATE"), + HOGLIN_SPAWN_EGG("MONSTER_EGG"), + HONEYCOMB, + HONEYCOMB_BLOCK, + HONEY_BLOCK, + HONEY_BOTTLE, + HOPPER, + HOPPER_MINECART, + HORN_CORAL, + HORN_CORAL_BLOCK, + HORN_CORAL_FAN, + HORN_CORAL_WALL_FAN, + HORSE_SPAWN_EGG(100, "MONSTER_EGG"), + HUSK_SPAWN_EGG(23, "MONSTER_EGG"), + ICE, + INFESTED_CHISELED_STONE_BRICKS(5, "MONSTER_EGGS"), + INFESTED_COBBLESTONE(1, "MONSTER_EGGS"), + INFESTED_CRACKED_STONE_BRICKS(4, "MONSTER_EGGS"), + INFESTED_DEEPSLATE, + INFESTED_MOSSY_STONE_BRICKS(3, "MONSTER_EGGS"), + INFESTED_STONE("MONSTER_EGGS"), + INFESTED_STONE_BRICKS(2, "MONSTER_EGGS"), + /** + * We will only add "INK_SAC" for {@link #BLACK_DYE} since it's + * the only material (linked with this material) that is added + * after 1.13, which means it can use both INK_SACK and INK_SAC. + */ + INK_SAC("INK_SACK"), + IRON_AXE, + IRON_BARS("IRON_FENCE"), + IRON_BLOCK, + IRON_BOOTS, + IRON_CHESTPLATE, + IRON_DOOR("IRON_DOOR_BLOCK"), + IRON_HELMET, + IRON_HOE, + IRON_HORSE_ARMOR("IRON_BARDING"), + IRON_INGOT, + IRON_LEGGINGS, + IRON_NUGGET, + IRON_ORE, + IRON_PICKAXE, + IRON_SHOVEL("IRON_SPADE"), + IRON_SWORD, + IRON_TRAPDOOR, + ITEM_FRAME, + JACK_O_LANTERN, + JIGSAW, + JUKEBOX, + JUNGLE_BOAT("BOAT_JUNGLE"), + JUNGLE_BUTTON("WOOD_BUTTON"), + JUNGLE_CHEST_BOAT, + JUNGLE_DOOR("JUNGLE_DOOR", "JUNGLE_DOOR_ITEM"), + JUNGLE_FENCE, + JUNGLE_FENCE_GATE, + JUNGLE_LEAVES(3, "LEAVES"), + JUNGLE_LOG(3, "LOG"), + JUNGLE_PLANKS(3, "WOOD"), + JUNGLE_PRESSURE_PLATE("WOOD_PLATE"), + JUNGLE_SAPLING(3, "SAPLING"), + JUNGLE_SIGN("SIGN_POST", "SIGN"), + JUNGLE_SLAB(3, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + JUNGLE_STAIRS("JUNGLE_WOOD_STAIRS"), + JUNGLE_TRAPDOOR("TRAP_DOOR"), + JUNGLE_WALL_SIGN("WALL_SIGN"), + JUNGLE_WOOD(3, "LOG"), + KELP, + KELP_PLANT, + KNOWLEDGE_BOOK("BOOK"), + LADDER, + LANTERN, + LAPIS_BLOCK, + LAPIS_LAZULI(4, "INK_SACK"), + LAPIS_ORE, + LARGE_AMETHYST_BUD, + LARGE_FERN(3, "DOUBLE_PLANT"), + LAVA("STATIONARY_LAVA"), + LAVA_BUCKET, + LAVA_CAULDRON, + LEAD("LEASH"), + LEATHER, + LEATHER_BOOTS, + LEATHER_CHESTPLATE, + LEATHER_HELMET, + LEATHER_HORSE_ARMOR("IRON_HORSE_ARMOR"), + LEATHER_LEGGINGS, + LECTERN, + LEVER, + LIGHT, + LIGHTNING_ROD, + LIGHT_BLUE_BANNER(12, "STANDING_BANNER", "BANNER"), + LIGHT_BLUE_BED(supports(12) ? 3 : 0, "BED_BLOCK", "BED"), + LIGHT_BLUE_CANDLE, + LIGHT_BLUE_CANDLE_CAKE, + LIGHT_BLUE_CARPET(3, "CARPET"), + LIGHT_BLUE_CONCRETE(3, "CONCRETE"), + LIGHT_BLUE_CONCRETE_POWDER(3, "CONCRETE_POWDER"), + LIGHT_BLUE_DYE(12, "INK_SACK"), + LIGHT_BLUE_GLAZED_TERRACOTTA, + LIGHT_BLUE_SHULKER_BOX, + LIGHT_BLUE_STAINED_GLASS(3, "STAINED_GLASS"), + LIGHT_BLUE_STAINED_GLASS_PANE(3, "THIN_GLASS", "STAINED_GLASS_PANE"), + LIGHT_BLUE_TERRACOTTA(3, "STAINED_CLAY"), + LIGHT_BLUE_WALL_BANNER(12, "WALL_BANNER", "STANDING_BANNER", "BANNER"), + LIGHT_BLUE_WOOL(3, "WOOL"), + LIGHT_GRAY_BANNER(7, "STANDING_BANNER", "BANNER"), + LIGHT_GRAY_BED(supports(12) ? 8 : 0, "BED_BLOCK", "BED"), + LIGHT_GRAY_CANDLE, + LIGHT_GRAY_CANDLE_CAKE, + LIGHT_GRAY_CARPET(8, "CARPET"), + LIGHT_GRAY_CONCRETE(8, "CONCRETE"), + LIGHT_GRAY_CONCRETE_POWDER(8, "CONCRETE_POWDER"), + LIGHT_GRAY_DYE(7, "INK_SACK"), + /** + * Renamed to SILVER_GLAZED_TERRACOTTA in 1.12 + * Renamed to LIGHT_GRAY_GLAZED_TERRACOTTA in 1.14 + */ + LIGHT_GRAY_GLAZED_TERRACOTTA(0, 12, "STAINED_CLAY", "LIGHT_GRAY_TERRACOTTA", "SILVER_GLAZED_TERRACOTTA"), + LIGHT_GRAY_SHULKER_BOX("SILVER_SHULKER_BOX"), + LIGHT_GRAY_STAINED_GLASS(8, "STAINED_GLASS"), + LIGHT_GRAY_STAINED_GLASS_PANE(8, "THIN_GLASS", "STAINED_GLASS_PANE"), + LIGHT_GRAY_TERRACOTTA(8, "STAINED_CLAY"), + LIGHT_GRAY_WALL_BANNER(7, "WALL_BANNER"), + LIGHT_GRAY_WOOL(8, "WOOL"), + LIGHT_WEIGHTED_PRESSURE_PLATE("GOLD_PLATE"), + LILAC(1, "DOUBLE_PLANT"), + LILY_OF_THE_VALLEY, + LILY_PAD("WATER_LILY"), + LIME_BANNER(10, "STANDING_BANNER", "BANNER"), + LIME_BED(supports(12) ? 5 : 0, "BED_BLOCK", "BED"), + LIME_CANDLE, + LIME_CANDLE_CAKE, + LIME_CARPET(5, "CARPET"), + LIME_CONCRETE(5, "CONCRETE"), + LIME_CONCRETE_POWDER(5, "CONCRETE_POWDER"), + LIME_DYE(10, "INK_SACK"), + LIME_GLAZED_TERRACOTTA, + LIME_SHULKER_BOX, + LIME_STAINED_GLASS(5, "STAINED_GLASS"), + LIME_STAINED_GLASS_PANE(5, "STAINED_GLASS_PANE"), + LIME_TERRACOTTA(5, "STAINED_CLAY"), + LIME_WALL_BANNER(10, "WALL_BANNER"), + LIME_WOOL(5, "WOOL"), + LINGERING_POTION, + LLAMA_SPAWN_EGG(103, "MONSTER_EGG"), + LODESTONE, + LOOM, + MAGENTA_BANNER(13, "STANDING_BANNER", "BANNER"), + MAGENTA_BED(supports(12) ? 2 : 0, "BED_BLOCK", "BED"), + MAGENTA_CANDLE, + MAGENTA_CANDLE_CAKE, + MAGENTA_CARPET(2, "CARPET"), + MAGENTA_CONCRETE(2, "CONCRETE"), + MAGENTA_CONCRETE_POWDER(2, "CONCRETE_POWDER"), + MAGENTA_DYE(13, "INK_SACK"), + MAGENTA_GLAZED_TERRACOTTA, + MAGENTA_SHULKER_BOX, + MAGENTA_STAINED_GLASS(2, "STAINED_GLASS"), + MAGENTA_STAINED_GLASS_PANE(2, "THIN_GLASS", "STAINED_GLASS_PANE"), + MAGENTA_TERRACOTTA(2, "STAINED_CLAY"), + MAGENTA_WALL_BANNER(13, "WALL_BANNER"), + MAGENTA_WOOL(2, "WOOL"), + MAGMA_BLOCK("MAGMA"), + MAGMA_CREAM, + MAGMA_CUBE_SPAWN_EGG(62, "MONSTER_EGG"), + MANGROVE_BOAT, + MANGROVE_BUTTON, + MANGROVE_CHEST_BOAT, + MANGROVE_DOOR, + MANGROVE_FENCE, + MANGROVE_FENCE_GATE, + MANGROVE_LEAVES, + MANGROVE_LOG, + MANGROVE_PLANKS, + MANGROVE_PRESSURE_PLATE, + MANGROVE_PROPAGULE, + MANGROVE_ROOTS, + MANGROVE_SIGN, + MANGROVE_SLAB("STEP"), + MANGROVE_STAIRS("SMOOTH_STAIRS"), + MANGROVE_TRAPDOOR, + MANGROVE_WALL_SIGN, + MANGROVE_WOOD, + /** + * Adding this to the duplicated list will give you a filled map + * for 1.13+ versions and removing it from duplicated list will + * still give you a filled map in -1.12 versions. + * Since higher versions are our priority I'll keep 1.13+ support + * until I can come up with something to fix it. + */ + MAP("EMPTY_MAP"), + MEDIUM_AMETHYST_BUD, + MELON("MELON_BLOCK"), + MELON_SEEDS, + MELON_SLICE("MELON"), + MELON_STEM, + MILK_BUCKET, + MINECART, + MOJANG_BANNER_PATTERN, + MOOSHROOM_SPAWN_EGG(96, "MONSTER_EGG"), + MOSSY_COBBLESTONE, + MOSSY_COBBLESTONE_SLAB(3, "STEP"), + MOSSY_COBBLESTONE_STAIRS("SMOOTH_STAIRS"), + MOSSY_COBBLESTONE_WALL(1, "COBBLE_WALL", "COBBLESTONE_WALL"), + MOSSY_STONE_BRICKS(1, "SMOOTH_BRICK"), + MOSSY_STONE_BRICK_SLAB(5, "STEP"), + MOSSY_STONE_BRICK_STAIRS("SMOOTH_STAIRS"), + MOSSY_STONE_BRICK_WALL, + MOSS_BLOCK, + MOSS_CARPET, + MOVING_PISTON("PISTON_MOVING_PIECE"), + MUD, + MUDDY_MANGROVE_ROOTS, + MUD_BRICKS, + MUD_BRICK_SLAB("STEP"), + MUD_BRICK_STAIRS("SMOOTH_STAIRS"), + MUD_BRICK_WALL, + MULE_SPAWN_EGG(32, "MONSTER_EGG"), + MUSHROOM_STEM("BROWN_MUSHROOM"), + MUSHROOM_STEW("MUSHROOM_SOUP"), + MUSIC_DISC_11("GOLD_RECORD"), + MUSIC_DISC_13("GREEN_RECORD"), + MUSIC_DISC_5, + MUSIC_DISC_BLOCKS("RECORD_3"), + MUSIC_DISC_CAT("RECORD_4"), + MUSIC_DISC_CHIRP("RECORD_5"), + MUSIC_DISC_FAR("RECORD_6"), + MUSIC_DISC_MALL("RECORD_7"), + MUSIC_DISC_MELLOHI("RECORD_8"), + MUSIC_DISC_OTHERSIDE, + MUSIC_DISC_PIGSTEP, + MUSIC_DISC_STAL("RECORD_9"), + MUSIC_DISC_STRAD("RECORD_10"), + MUSIC_DISC_WAIT("RECORD_11"), + MUSIC_DISC_WARD("RECORD_12"), + MUTTON, + MYCELIUM("MYCEL"), + NAME_TAG, + NAUTILUS_SHELL, + NETHERITE_AXE(16, "GOLDEN_AXE", "GOLD_AXE"), + NETHERITE_BLOCK, + NETHERITE_BOOTS, + NETHERITE_CHESTPLATE, + NETHERITE_HELMET, + NETHERITE_HOE, + NETHERITE_INGOT, + NETHERITE_LEGGINGS, + NETHERITE_PICKAXE, + NETHERITE_SCRAP, + NETHERITE_SHOVEL, + NETHERITE_SWORD, + NETHERRACK, + NETHER_BRICK("NETHER_BRICK_ITEM"), + NETHER_BRICKS("NETHER_BRICK"), + NETHER_BRICK_FENCE("NETHER_FENCE"), + NETHER_BRICK_SLAB(6, "STEP"), + NETHER_BRICK_STAIRS("SMOOTH_STAIRS"), + NETHER_BRICK_WALL, + NETHER_GOLD_ORE, + NETHER_PORTAL("PORTAL"), + NETHER_QUARTZ_ORE("QUARTZ_ORE"), + NETHER_SPROUTS, + NETHER_STAR, + /** + * Just like mentioned in https://minecraft.gamepedia.com/Nether_Wart + * Nether wart is also known as nether stalk in the code. + * NETHER_STALK is the planted state of nether warts. + */ + NETHER_WART("NETHER_WARTS", "NETHER_STALK"), + NETHER_WART_BLOCK, + NOTE_BLOCK, + OAK_BOAT("BOAT"), + OAK_BUTTON("WOOD_BUTTON"), + OAK_CHEST_BOAT, + OAK_DOOR("WOODEN_DOOR", "WOOD_DOOR"), + OAK_FENCE("FENCE"), + OAK_FENCE_GATE("FENCE_GATE"), + OAK_LEAVES("LEAVES"), + OAK_LOG("LOG"), + OAK_PLANKS("WOOD"), + OAK_PRESSURE_PLATE("WOOD_PLATE"), + OAK_SAPLING("SAPLING"), + OAK_SIGN("SIGN_POST", "SIGN"), + OAK_SLAB("WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + OAK_STAIRS("WOOD_STAIRS"), + OAK_TRAPDOOR("TRAP_DOOR"), + OAK_WALL_SIGN("WALL_SIGN"), + OAK_WOOD("LOG"), + OBSERVER, + OBSIDIAN, + OCELOT_SPAWN_EGG(98, "MONSTER_EGG"), + OCHRE_FROGLIGHT, + ORANGE_BANNER(14, "STANDING_BANNER", "BANNER"), + ORANGE_BED(supports(12) ? 1 : 0, "BED_BLOCK", "BED"), + ORANGE_CANDLE, + ORANGE_CANDLE_CAKE, + ORANGE_CARPET(1, "CARPET"), + ORANGE_CONCRETE(1, "CONCRETE"), + ORANGE_CONCRETE_POWDER(1, "CONCRETE_POWDER"), + ORANGE_DYE(14, "INK_SACK"), + ORANGE_GLAZED_TERRACOTTA, + ORANGE_SHULKER_BOX, + ORANGE_STAINED_GLASS(1, "STAINED_GLASS"), + ORANGE_STAINED_GLASS_PANE(1, "STAINED_GLASS_PANE"), + ORANGE_TERRACOTTA(1, "STAINED_CLAY"), + ORANGE_TULIP(5, "RED_ROSE"), + ORANGE_WALL_BANNER(14, "WALL_BANNER"), + ORANGE_WOOL(1, "WOOL"), + OXEYE_DAISY(8, "RED_ROSE"), + OXIDIZED_COPPER, + OXIDIZED_CUT_COPPER, + OXIDIZED_CUT_COPPER_SLAB("STEP"), + OXIDIZED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + PACKED_ICE, + PACKED_MUD, + PAINTING, + PANDA_SPAWN_EGG, + PAPER, + PARROT_SPAWN_EGG(105, "MONSTER_EGG"), + PEARLESCENT_FROGLIGHT, + PEONY(5, "DOUBLE_PLANT"), + PETRIFIED_OAK_SLAB("WOOD_STEP"), + PHANTOM_MEMBRANE, + PHANTOM_SPAWN_EGG, + PIGLIN_BANNER_PATTERN, + PIGLIN_BRUTE_SPAWN_EGG, + PIGLIN_SPAWN_EGG(57, "MONSTER_EGG"), + PIG_SPAWN_EGG(90, "MONSTER_EGG"), + PILLAGER_SPAWN_EGG, + PINK_BANNER(9, "STANDING_BANNER", "BANNER"), + PINK_BED(supports(12) ? 6 : 0, "BED_BLOCK", "BED"), + PINK_CANDLE, + PINK_CANDLE_CAKE, + PINK_CARPET(6, "CARPET"), + PINK_CONCRETE(6, "CONCRETE"), + PINK_CONCRETE_POWDER(6, "CONCRETE_POWDER"), + PINK_DYE(9, "INK_SACK"), + PINK_GLAZED_TERRACOTTA, + PINK_SHULKER_BOX, + PINK_STAINED_GLASS(6, "STAINED_GLASS"), + PINK_STAINED_GLASS_PANE(6, "THIN_GLASS", "STAINED_GLASS_PANE"), + PINK_TERRACOTTA(6, "STAINED_CLAY"), + PINK_TULIP(7, "RED_ROSE"), + PINK_WALL_BANNER(9, "WALL_BANNER"), + PINK_WOOL(6, "WOOL"), + PISTON("PISTON_BASE"), + PISTON_HEAD("PISTON_EXTENSION"), + PLAYER_HEAD(3, "SKULL", "SKULL_ITEM"), + PLAYER_WALL_HEAD(3, "SKULL", "SKULL_ITEM"), + PODZOL(2, "DIRT"), + POINTED_DRIPSTONE, + POISONOUS_POTATO, + POLAR_BEAR_SPAWN_EGG(102, "MONSTER_EGG"), + POLISHED_ANDESITE(6, "STONE"), + POLISHED_ANDESITE_SLAB("STEP"), + POLISHED_ANDESITE_STAIRS("SMOOTH_STAIRS"), + POLISHED_BASALT, + POLISHED_BLACKSTONE, + POLISHED_BLACKSTONE_BRICKS, + POLISHED_BLACKSTONE_BRICK_SLAB("STEP"), + POLISHED_BLACKSTONE_BRICK_STAIRS("SMOOTH_STAIRS"), + POLISHED_BLACKSTONE_BRICK_WALL, + POLISHED_BLACKSTONE_BUTTON, + POLISHED_BLACKSTONE_PRESSURE_PLATE, + POLISHED_BLACKSTONE_SLAB("STEP"), + POLISHED_BLACKSTONE_STAIRS("SMOOTH_STAIRS"), + POLISHED_BLACKSTONE_WALL, + POLISHED_DEEPSLATE, + POLISHED_DEEPSLATE_SLAB("STEP"), + POLISHED_DEEPSLATE_STAIRS("SMOOTH_STAIRS"), + POLISHED_DEEPSLATE_WALL, + POLISHED_DIORITE(4, "STONE"), + POLISHED_DIORITE_SLAB("STEP"), + POLISHED_DIORITE_STAIRS("SMOOTH_STAIRS"), + POLISHED_GRANITE(2, "STONE"), + POLISHED_GRANITE_SLAB("STEP"), + POLISHED_GRANITE_STAIRS("SMOOTH_STAIRS"), + POPPED_CHORUS_FRUIT("CHORUS_FRUIT_POPPED"), + POPPY("RED_ROSE"), + PORKCHOP("PORK"), + POTATO("POTATO_ITEM"), + POTATOES("POTATO"), + POTION, + POTTED_ACACIA_SAPLING(4, "FLOWER_POT"), + POTTED_ALLIUM(2, "RED_ROSE", "FLOWER_POT"), + POTTED_AZALEA_BUSH, + POTTED_AZURE_BLUET(3, "RED_ROSE", "FLOWER_POT"), + POTTED_BAMBOO, + POTTED_BIRCH_SAPLING(2, "FLOWER_POT"), + POTTED_BLUE_ORCHID(1, "RED_ROSE", "FLOWER_POT"), + POTTED_BROWN_MUSHROOM("FLOWER_POT"), + POTTED_CACTUS("FLOWER_POT"), + POTTED_CORNFLOWER, + POTTED_CRIMSON_FUNGUS, + POTTED_CRIMSON_ROOTS, + POTTED_DANDELION("YELLOW_FLOWER", "FLOWER_POT"), + POTTED_DARK_OAK_SAPLING(5, "FLOWER_POT"), + POTTED_DEAD_BUSH("FLOWER_POT"), + POTTED_FERN(2, "LONG_GRASS", "FLOWER_POT"), + POTTED_FLOWERING_AZALEA_BUSH, + POTTED_JUNGLE_SAPLING(3, "FLOWER_POT"), + POTTED_LILY_OF_THE_VALLEY, + POTTED_MANGROVE_PROPAGULE, + POTTED_OAK_SAPLING("FLOWER_POT"), + POTTED_ORANGE_TULIP(5, "RED_ROSE", "FLOWER_POT"), + POTTED_OXEYE_DAISY(8, "RED_ROSE", "FLOWER_POT"), + POTTED_PINK_TULIP(7, "RED_ROSE", "FLOWER_POT"), + POTTED_POPPY("RED_ROSE", "FLOWER_POT"), + POTTED_RED_MUSHROOM("FLOWER_POT"), + POTTED_RED_TULIP(4, "RED_ROSE", "FLOWER_POT"), + POTTED_SPRUCE_SAPLING(1, "FLOWER_POT"), + POTTED_WARPED_FUNGUS, + POTTED_WARPED_ROOTS, + POTTED_WHITE_TULIP(6, "RED_ROSE", "FLOWER_POT"), + POTTED_WITHER_ROSE, + POWDER_SNOW, + POWDER_SNOW_BUCKET, + POWDER_SNOW_CAULDRON, + POWERED_RAIL, + PRISMARINE, + PRISMARINE_BRICKS(1, "PRISMARINE"), + PRISMARINE_BRICK_SLAB("STEP"), + PRISMARINE_BRICK_STAIRS("SMOOTH_STAIRS"), + PRISMARINE_CRYSTALS, + PRISMARINE_SHARD, + PRISMARINE_SLAB("STEP"), + PRISMARINE_STAIRS("SMOOTH_STAIRS"), + PRISMARINE_WALL, + PUFFERFISH(3, "RAW_FISH"), + PUFFERFISH_BUCKET, + PUFFERFISH_SPAWN_EGG, + PUMPKIN, + PUMPKIN_PIE, + PUMPKIN_SEEDS, + PUMPKIN_STEM, + PURPLE_BANNER(5, "STANDING_BANNER", "BANNER"), + PURPLE_BED(supports(12) ? 10 : 0, "BED_BLOCK", "BED"), + PURPLE_CANDLE, + PURPLE_CANDLE_CAKE, + PURPLE_CARPET(10, "CARPET"), + PURPLE_CONCRETE(10, "CONCRETE"), + PURPLE_CONCRETE_POWDER(10, "CONCRETE_POWDER"), + PURPLE_DYE(5, "INK_SACK"), + PURPLE_GLAZED_TERRACOTTA, + PURPLE_SHULKER_BOX, + PURPLE_STAINED_GLASS(10, "STAINED_GLASS"), + PURPLE_STAINED_GLASS_PANE(10, "THIN_GLASS", "STAINED_GLASS_PANE"), + PURPLE_TERRACOTTA(10, "STAINED_CLAY"), + PURPLE_WALL_BANNER(5, "WALL_BANNER"), + PURPLE_WOOL(10, "WOOL"), + PURPUR_BLOCK, + PURPUR_PILLAR, + PURPUR_SLAB("PURPUR_DOUBLE_SLAB"), + PURPUR_STAIRS("SMOOTH_STAIRS"), + QUARTZ, + QUARTZ_BLOCK, + QUARTZ_BRICKS, + QUARTZ_PILLAR(2, "QUARTZ_BLOCK"), + QUARTZ_SLAB(7, "STEP"), + QUARTZ_STAIRS("SMOOTH_STAIRS"), + RABBIT, + RABBIT_FOOT, + RABBIT_HIDE, + RABBIT_SPAWN_EGG(101, "MONSTER_EGG"), + RABBIT_STEW, + RAIL("RAILS"), + RAVAGER_SPAWN_EGG, + RAW_COPPER, + RAW_COPPER_BLOCK, + RAW_GOLD, + RAW_GOLD_BLOCK, + RAW_IRON, + RAW_IRON_BLOCK, + RECOVERY_COMPASS, + REDSTONE, + REDSTONE_BLOCK, + /** + * Unlike redstone torch, REDSTONE_LAMP_ON isn't an item. + * The name is just here on the list for matching. + * + * @see #REDSTONE_TORCH + */ + REDSTONE_LAMP("REDSTONE_LAMP_ON", "REDSTONE_LAMP_OFF"), + REDSTONE_ORE("GLOWING_REDSTONE_ORE"), + /** + * REDSTONE_TORCH_OFF isn't an item, but a block. + * But REDSTONE_TORCH_ON is the item. + * The name is just here on the list for matching. + */ + REDSTONE_TORCH("REDSTONE_TORCH_OFF", "REDSTONE_TORCH_ON"), + REDSTONE_WALL_TORCH, + REDSTONE_WIRE, + RED_BANNER(1, "STANDING_BANNER", "BANNER"), + /** + * Data value 14 or 0 + */ + RED_BED(supports(12) ? 14 : 0, "BED_BLOCK", "BED"), + RED_CANDLE, + RED_CANDLE_CAKE, + RED_CARPET(14, "CARPET"), + RED_CONCRETE(14, "CONCRETE"), + RED_CONCRETE_POWDER(14, "CONCRETE_POWDER"), + RED_DYE(1, "INK_SACK", "ROSE_RED"), + RED_GLAZED_TERRACOTTA, + RED_MUSHROOM, + RED_MUSHROOM_BLOCK("RED_MUSHROOM", "HUGE_MUSHROOM_2"), + RED_NETHER_BRICKS("RED_NETHER_BRICK"), + RED_NETHER_BRICK_SLAB("STEP"), + RED_NETHER_BRICK_STAIRS("SMOOTH_STAIRS"), + RED_NETHER_BRICK_WALL, + RED_SAND(1, "SAND"), + RED_SANDSTONE, + RED_SANDSTONE_SLAB("DOUBLE_STONE_SLAB2", "STONE_SLAB2"), + RED_SANDSTONE_STAIRS("SMOOTH_STAIRS"), + RED_SANDSTONE_WALL, + RED_SHULKER_BOX, + RED_STAINED_GLASS(14, "STAINED_GLASS"), + RED_STAINED_GLASS_PANE(14, "THIN_GLASS", "STAINED_GLASS_PANE"), + RED_TERRACOTTA(14, "STAINED_CLAY"), + RED_TULIP(4, "RED_ROSE"), + RED_WALL_BANNER(1, "WALL_BANNER"), + RED_WOOL(14, "WOOL"), + REINFORCED_DEEPSLATE, + REPEATER("DIODE_BLOCK_ON", "DIODE_BLOCK_OFF", "DIODE"), + REPEATING_COMMAND_BLOCK("COMMAND", "COMMAND_REPEATING"), + RESPAWN_ANCHOR, + ROOTED_DIRT, + ROSE_BUSH(4, "DOUBLE_PLANT"), + ROTTEN_FLESH, + SADDLE, + SALMON(1, "RAW_FISH"), + SALMON_BUCKET, + SALMON_SPAWN_EGG, + SAND, + SANDSTONE, + SANDSTONE_SLAB(1, "DOUBLE_STEP", "STEP", "STONE_SLAB"), + SANDSTONE_STAIRS("SMOOTH_STAIRS"), + SANDSTONE_WALL, + SCAFFOLDING, + SCULK, + SCULK_CATALYST, + SCULK_SENSOR, + SCULK_SHRIEKER, + SCULK_VEIN, + SCUTE, + SEAGRASS, + SEA_LANTERN, + SEA_PICKLE, + SHEARS, + SHEEP_SPAWN_EGG(91, "MONSTER_EGG"), + SHIELD, + SHROOMLIGHT, + SHULKER_BOX("PURPLE_SHULKER_BOX"), + SHULKER_SHELL, + SHULKER_SPAWN_EGG(69, "MONSTER_EGG"), + SILVERFISH_SPAWN_EGG(60, "MONSTER_EGG"), + SKELETON_HORSE_SPAWN_EGG(28, "MONSTER_EGG"), + SKELETON_SKULL("SKULL", "SKULL_ITEM"), + SKELETON_SPAWN_EGG(51, "MONSTER_EGG"), + SKELETON_WALL_SKULL("SKULL", "SKULL_ITEM"), + SKULL_BANNER_PATTERN, + SLIME_BALL, + SLIME_BLOCK("STONE"), + SLIME_SPAWN_EGG(55, "MONSTER_EGG"), + SMALL_AMETHYST_BUD, + SMALL_DRIPLEAF, + SMITHING_TABLE, + SMOKER, + SMOOTH_BASALT, + SMOOTH_QUARTZ(0, 13), + SMOOTH_QUARTZ_SLAB(7, "STEP"), + SMOOTH_QUARTZ_STAIRS("SMOOTH_STAIRS"), + SMOOTH_RED_SANDSTONE(2, "RED_SANDSTONE"), + SMOOTH_RED_SANDSTONE_SLAB("STONE_SLAB2"), + SMOOTH_RED_SANDSTONE_STAIRS("SMOOTH_STAIRS"), + SMOOTH_SANDSTONE(2, "SANDSTONE"), + SMOOTH_SANDSTONE_SLAB("STEP"), + SMOOTH_SANDSTONE_STAIRS("SMOOTH_STAIRS"), + SMOOTH_STONE, + SMOOTH_STONE_SLAB("STEP"), + SNOW, + SNOWBALL("SNOW_BALL"), + SNOW_BLOCK, + SOUL_CAMPFIRE, + SOUL_FIRE, + SOUL_LANTERN, + SOUL_SAND, + SOUL_SOIL, + SOUL_TORCH, + SOUL_WALL_TORCH, + SPAWNER("MOB_SPAWNER"), + SPECTRAL_ARROW, + SPIDER_EYE, + SPIDER_SPAWN_EGG(52, "MONSTER_EGG"), + SPLASH_POTION, + SPONGE, + SPORE_BLOSSOM, + SPRUCE_BOAT("BOAT_SPRUCE"), + SPRUCE_BUTTON("WOOD_BUTTON"), + SPRUCE_CHEST_BOAT, + SPRUCE_DOOR("SPRUCE_DOOR", "SPRUCE_DOOR_ITEM"), + SPRUCE_FENCE, + SPRUCE_FENCE_GATE, + SPRUCE_LEAVES(1, "LEAVES"), + SPRUCE_LOG(1, "LOG"), + SPRUCE_PLANKS(1, "WOOD"), + SPRUCE_PRESSURE_PLATE("WOOD_PLATE"), + SPRUCE_SAPLING(1, "SAPLING"), + SPRUCE_SIGN("SIGN_POST", "SIGN"), + SPRUCE_SLAB(1, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + SPRUCE_STAIRS("SPRUCE_WOOD_STAIRS"), + SPRUCE_TRAPDOOR("TRAP_DOOR"), + SPRUCE_WALL_SIGN("WALL_SIGN"), + SPRUCE_WOOD(1, "LOG"), + SPYGLASS, + SQUID_SPAWN_EGG(94, "MONSTER_EGG"), + STICK, + STICKY_PISTON("PISTON_BASE", "PISTON_STICKY_BASE"), + STONE, + STONECUTTER, + STONE_AXE, + STONE_BRICKS("SMOOTH_BRICK"), + STONE_BRICK_SLAB(5, "DOUBLE_STEP", "STEP", "STONE_SLAB"), + STONE_BRICK_STAIRS("SMOOTH_STAIRS"), + STONE_BRICK_WALL, + STONE_BUTTON, + STONE_HOE, + STONE_PICKAXE, + STONE_PRESSURE_PLATE("STONE_PLATE"), + STONE_SHOVEL("STONE_SPADE"), + STONE_SLAB("DOUBLE_STEP", "STEP"), + STONE_STAIRS("SMOOTH_STAIRS"), + STONE_SWORD, + STRAY_SPAWN_EGG(6, "MONSTER_EGG"), + STRIDER_SPAWN_EGG, + STRING, + STRIPPED_ACACIA_LOG("LOG_2"), + STRIPPED_ACACIA_WOOD("LOG_2"), + STRIPPED_BIRCH_LOG(2, "LOG"), + STRIPPED_BIRCH_WOOD(2, "LOG"), + STRIPPED_CRIMSON_HYPHAE, + STRIPPED_CRIMSON_STEM, + STRIPPED_DARK_OAK_LOG("LOG"), + STRIPPED_DARK_OAK_WOOD("LOG"), + STRIPPED_JUNGLE_LOG(3, "LOG"), + STRIPPED_JUNGLE_WOOD(3, "LOG"), + STRIPPED_MANGROVE_LOG, + STRIPPED_MANGROVE_WOOD, + STRIPPED_OAK_LOG("LOG"), + STRIPPED_OAK_WOOD("LOG"), + STRIPPED_SPRUCE_LOG(1, "LOG"), + STRIPPED_SPRUCE_WOOD(1, "LOG"), + STRIPPED_WARPED_HYPHAE, + STRIPPED_WARPED_STEM, + STRUCTURE_BLOCK, + /** + * Originally developers used barrier blocks for its purpose. + * So technically this isn't really considered as a suggested material. + */ + STRUCTURE_VOID(10, "BARRIER"), + SUGAR, + /** + * Sugar Cane is a known material in pre-1.13 + */ + SUGAR_CANE("SUGAR_CANE_BLOCK"), + SUNFLOWER("DOUBLE_PLANT"), + SUSPICIOUS_STEW, + SWEET_BERRIES, + SWEET_BERRY_BUSH, + TADPOLE_BUCKET, + TADPOLE_SPAWN_EGG, + TALL_GRASS(2, "DOUBLE_PLANT"), + TALL_SEAGRASS, + TARGET, + TERRACOTTA("HARD_CLAY"), + TINTED_GLASS, + TIPPED_ARROW(0, 9, "ARROW"), + TNT, + TNT_MINECART("EXPLOSIVE_MINECART"), + TORCH, + TOTEM_OF_UNDYING("TOTEM", "ARROW"), + TRADER_LLAMA_SPAWN_EGG(103, 14), + TRAPPED_CHEST, + TRIDENT, + TRIPWIRE, + TRIPWIRE_HOOK, + TROPICAL_FISH(2, "RAW_FISH"), + TROPICAL_FISH_BUCKET("BUCKET", "WATER_BUCKET"), + TROPICAL_FISH_SPAWN_EGG("MONSTER_EGG"), + TUBE_CORAL, + TUBE_CORAL_BLOCK, + TUBE_CORAL_FAN, + TUBE_CORAL_WALL_FAN, + TUFF, + TURTLE_EGG, + TURTLE_HELMET, + TURTLE_SPAWN_EGG, + TWISTING_VINES, + TWISTING_VINES_PLANT, + VERDANT_FROGLIGHT, + VEX_SPAWN_EGG(35, "MONSTER_EGG"), + VILLAGER_SPAWN_EGG(120, "MONSTER_EGG"), + VINDICATOR_SPAWN_EGG(36, "MONSTER_EGG"), + VINE, + /** + * 1.13 tag is not added because it's the same thing as {@link #AIR} + * + * @see #CAVE_AIR + */ + VOID_AIR("AIR"), + WALL_TORCH("TORCH"), + WANDERING_TRADER_SPAWN_EGG, + WARDEN_SPAWN_EGG, + WARPED_BUTTON, + WARPED_DOOR, + WARPED_FENCE, + WARPED_FENCE_GATE, + WARPED_FUNGUS, + WARPED_FUNGUS_ON_A_STICK, + WARPED_HYPHAE, + WARPED_NYLIUM, + WARPED_PLANKS, + WARPED_PRESSURE_PLATE, + WARPED_ROOTS, + WARPED_SIGN("SIGN_POST"), + WARPED_SLAB("STEP"), + WARPED_STAIRS("SMOOTH_STAIRS"), + WARPED_STEM, + WARPED_TRAPDOOR, + WARPED_WALL_SIGN("WALL_SIGN"), + WARPED_WART_BLOCK, + /** + * This is used for blocks only. + * In 1.13- WATER will turn into STATIONARY_WATER after it finished spreading. + * After 1.13+ this uses + * https://hub.spigotmc.org/javadocs/spigot/org/bukkit/block/data/Levelled.html water flowing system. + */ + WATER("STATIONARY_WATER"), + WATER_BUCKET, + WATER_CAULDRON, + WAXED_COPPER_BLOCK, + WAXED_CUT_COPPER, + WAXED_CUT_COPPER_SLAB("STEP"), + WAXED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + WAXED_EXPOSED_COPPER, + WAXED_EXPOSED_CUT_COPPER, + WAXED_EXPOSED_CUT_COPPER_SLAB("STEP"), + WAXED_EXPOSED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + WAXED_OXIDIZED_COPPER, + WAXED_OXIDIZED_CUT_COPPER, + WAXED_OXIDIZED_CUT_COPPER_SLAB("STEP"), + WAXED_OXIDIZED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + WAXED_WEATHERED_COPPER, + WAXED_WEATHERED_CUT_COPPER, + WAXED_WEATHERED_CUT_COPPER_SLAB("STEP"), + WAXED_WEATHERED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + WEATHERED_COPPER, + WEATHERED_CUT_COPPER, + WEATHERED_CUT_COPPER_SLAB("STEP"), + WEATHERED_CUT_COPPER_STAIRS("SMOOTH_STAIRS"), + WEEPING_VINES, + WEEPING_VINES_PLANT, + WET_SPONGE(1, "SPONGE"), + /** + * Wheat is a known material in pre-1.13 + */ + WHEAT("CROPS"), + WHEAT_SEEDS("SEEDS"), + WHITE_BANNER(15, "STANDING_BANNER", "BANNER"), + WHITE_BED("BED_BLOCK", "BED"), + WHITE_CANDLE, + WHITE_CANDLE_CAKE, + WHITE_CARPET("CARPET"), + WHITE_CONCRETE("CONCRETE"), + WHITE_CONCRETE_POWDER("CONCRETE_POWDER"), + WHITE_DYE(15, "INK_SACK", "BONE_MEAL"), + WHITE_GLAZED_TERRACOTTA, + WHITE_SHULKER_BOX, + WHITE_STAINED_GLASS("STAINED_GLASS"), + WHITE_STAINED_GLASS_PANE("THIN_GLASS", "STAINED_GLASS_PANE"), + WHITE_TERRACOTTA("STAINED_CLAY"), + WHITE_TULIP(6, "RED_ROSE"), + WHITE_WALL_BANNER(15, "WALL_BANNER"), + WHITE_WOOL("WOOL"), + WITCH_SPAWN_EGG(66, "MONSTER_EGG"), + WITHER_ROSE, + WITHER_SKELETON_SKULL(1, "SKULL", "SKULL_ITEM"), + WITHER_SKELETON_SPAWN_EGG(5, "MONSTER_EGG"), + WITHER_SKELETON_WALL_SKULL(1, "SKULL", "SKULL_ITEM"), + WOLF_SPAWN_EGG(95, "MONSTER_EGG"), + WOODEN_AXE("WOOD_AXE"), + WOODEN_HOE("WOOD_HOE"), + WOODEN_PICKAXE("WOOD_PICKAXE"), + WOODEN_SHOVEL("WOOD_SPADE"), + WOODEN_SWORD("WOOD_SWORD"), + WRITABLE_BOOK("BOOK_AND_QUILL"), + WRITTEN_BOOK, + YELLOW_BANNER(11, "STANDING_BANNER", "BANNER"), + YELLOW_BED(supports(12) ? 4 : 0, "BED_BLOCK", "BED"), + YELLOW_CANDLE, + YELLOW_CANDLE_CAKE, + YELLOW_CARPET(4, "CARPET"), + YELLOW_CONCRETE(4, "CONCRETE"), + YELLOW_CONCRETE_POWDER(4, "CONCRETE_POWDER"), + YELLOW_DYE(11, "INK_SACK", "DANDELION_YELLOW"), + YELLOW_GLAZED_TERRACOTTA, + YELLOW_SHULKER_BOX, + YELLOW_STAINED_GLASS(4, "STAINED_GLASS"), + YELLOW_STAINED_GLASS_PANE(4, "THIN_GLASS", "STAINED_GLASS_PANE"), + YELLOW_TERRACOTTA(4, "STAINED_CLAY"), + YELLOW_WALL_BANNER(11, "WALL_BANNER"), + YELLOW_WOOL(4, "WOOL"), + ZOGLIN_SPAWN_EGG, + ZOMBIE_HEAD(2, "SKULL", "SKULL_ITEM"), + ZOMBIE_HORSE_SPAWN_EGG(29, "MONSTER_EGG"), + ZOMBIE_SPAWN_EGG(54, "MONSTER_EGG"), + ZOMBIE_VILLAGER_SPAWN_EGG(27, "MONSTER_EGG"), + ZOMBIE_WALL_HEAD(2, "SKULL", "SKULL_ITEM"), + ZOMBIFIED_PIGLIN_SPAWN_EGG(57, "MONSTER_EGG", "ZOMBIE_PIGMAN_SPAWN_EGG"); + + /** + * Cached array of {@link CompMaterial#values()} to avoid allocating memory for + * calling the method every time. + * + * @since 2.0.0 + */ + private static final CompMaterial[] VALUES = values(); + + /** + * We don't want to use Enums#getIfPresent(Class, String) to avoid a few checks. + * + * @since 5.1.0 + */ + private static final Map NAMES = new HashMap<>(); + + /** + * The maximum data value in the pre-flattening update which belongs to {@link #VILLAGER_SPAWN_EGG}
+ * https://minecraftitemids.com/types/spawn-egg + * + * @see #matchWithData(String) + * @since 8.0.0 + */ + private static final byte MAX_DATA_VALUE = 120; + + /** + * Used to tell the system that the passed object's (name or material) data value + * is not provided or is invalid. + * + * @since 8.0.0 + */ + private static final byte UNKNOWN_DATA_VALUE = -1; + + /** + * The maximum material ID before the pre-flattening update which belongs to {@link #MUSIC_DISC_WAIT} + * + * @since 8.1.0 + */ + private static final short MAX_ID = 2267; + + /** + * CompMaterial Paradox (Duplication Check) + *

+ * A set of duplicated material names in 1.13 and 1.12 that will conflict with the legacy names. + * Values are the new material names. This map also contains illegal elements. Check the static initializer for more info. + *

+ * Duplications are not useful at all in versions above the flattening update {@link Data#ISFLAT} + * This set is only used for matching materials, for parsing refer to {@link #isDuplicated()} + * + * @since 3.0.0 + */ + private static final Set DUPLICATED; + + static { + for (final CompMaterial material : VALUES) + NAMES.put(material.name(), material); + } + + static { + if (Data.ISFLAT) + // It's not needed at all if it's the newer version. We can save some memory. + DUPLICATED = null; + else { + // MELON_SLICE, CARROTS, POTATOES, BEETROOTS, GRASS_BLOCK, BRICKS, NETHER_BRICKS, BROWN_MUSHROOM + // Using the constructor to add elements will decide to allocate more size which we don't need. + DUPLICATED = new HashSet<>(4); + DUPLICATED.add(GRASS.name()); + DUPLICATED.add(MELON.name()); + DUPLICATED.add(BRICK.name()); + DUPLICATED.add(NETHER_BRICK.name()); + } + } + + /** + * The data value of this material pre-flattening. + * + * Can be accessed with {@link ItemStack#getData()} then {@code MaterialData#getData()} + * or {@link ItemStack#getDurability()} if not damageable. + */ + @Getter + private final byte data; + + private final byte version; + + private final String[] legacy; + + /** + * The cached Bukkit parsed material. + * + * @see #toMaterial() + * @since 9.0.0 + */ + @Getter + private final Material material; + + CompMaterial(int data, int version, String... legacy) { + this.data = (byte) data; + this.version = (byte) version; + this.legacy = legacy; + + Material mat = null; + + if (!Data.ISFLAT && this.isDuplicated() || (mat = Material.getMaterial(this.name())) == null) + for (int i = legacy.length - 1; i >= 0; i--) { + mat = Material.getMaterial(legacy[i]); + + if (mat != null) + break; + } + + this.material = mat == null ? Material.STONE : mat; + } + + CompMaterial(int data, String... legacy) { + this(data, 0, legacy); + } + + CompMaterial(int version) { + this(0, version); + } + + CompMaterial() { + this(0, 0); + } + + CompMaterial(String... legacy) { + this(0, 0, legacy); + } + + /** + * Convenience method for giving 1 piece of this material into a players inventory + * + * @param player + */ + public final void give(final Player player) { + this.give(player, 1); + } + + /** + * Convenience method for giving this material into a players inventory + * + * @param player + * @param amount + */ + public final void give(final Player player, final int amount) { + player.getInventory().addItem(this.toItem(amount)); + } + + /** + * Parses an item from this CompMaterial. + * Uses data values on older versions. + * + * @return an ItemStack with the same material (and data value if in older versions.) + * @see #setType(ItemStack) + */ + + public ItemStack toItem() { + return this.toItem(1); + } + + /** + * Parses an item from this CompMaterial. + * Uses data values on older versions. + * + * @return an ItemStack with the same material (and data value if in older versions.) + * @see #setType(ItemStack) + * + * @param amount + */ + + public ItemStack toItem(int amount) { + final Material material = this.toMaterial(); + + if (material == null) + return null; + + return Data.ISFLAT ? new ItemStack(material, amount) : new ItemStack(material, amount, this.data); + } + + /** + * Parses the material of this CompMaterial. + * + * @return the material related to this CompMaterial based on the server version. + * @since 1.0.0 + */ + + public Material toMaterial() { + return this.material; + } + + /** + * Return true if the {@link #getMaterial()} and the given Material matches. + *

+ * NOT cross-version compatible. For this, use {@link #is(ItemStack)} + * + * @param mat + * @return + */ + public final boolean is(final Material mat) { + return this.material == mat; + } + + /** + * Evaluates whether a given {@link ItemStack} is equal material-wise. Takes + * data value in account if we are on Minecraft 1.13+ or older. + * + * @param comp the itemstack + * @return -see above- + */ + public final boolean is(final ItemStack comp) { + if (MinecraftVersion.atLeast(V.v1_13)) + return comp.getType() == this.material; + + return this.is(comp.getType(), comp.getData().getData()); + } + + /** + * Evaluates whether the given block equals this material + * + * @param block + * @return + */ + public final boolean is(final Block block) { + if (MinecraftVersion.atLeast(V.v1_13)) + return block.getType() == this.material; + + return block != null && this.is(block.getType(), block.getData()); + } + + /** + * Evaluates whether the given type/data equals this material + * + * @param type + * @param data + * @return + */ + public final boolean is(final Material type, final int data) { + if (MinecraftVersion.atLeast(V.v1_13)) + return type == this.material; + + if (type == this.toMaterial() && data == this.data) + return true; + + final CompMaterial compMat = fromMaterial(type); + + return isDamageable(compMat) && this.toMaterial() == type; + } + + /** + * Returns true for damageable materials. + * + * @return + */ + public final boolean isDamageable() { + return isDamageable(this); + } + + /** + * Return true if this class equals the given block, comparing data and {@link Material} type + * + * @param block + * @return + */ + public final boolean equals(Block block) { + return block.getData() == this.getData() && block.getType() == this.material; + } + + /** + * Returns true if the given material is damageable. + * + * @param type + * @return + */ + public static boolean isDamageable(final CompMaterial type) { + try { + if (MinecraftVersion.atLeast(V.v1_13)) + return type.toItem().getItemMeta() instanceof org.bukkit.inventory.meta.Damageable; + } catch (final Throwable t) { + // MC unsupported + } + + switch (type.toString()) { + case "HELMET": + case "CHESTPLATE": + case "LEGGINGS": + case "BOOTS": + case "SWORD": + case "AXE": + case "PICKAXE": + case "SHOVEL": + case "HOE": + case "ELYTRA": + case "TURTLE_HELMET": + case "TRIDENT": + case "HORSE_ARMOR": + case "SHEARS": + return true; + default: + return false; + } + } + + /** + * Return true if the given block is air + * + * @param block + * @return + */ + public static boolean isAir(final Block block) { + return block == null || isAir(block.getType()); + } + + /** + * Returns if the given item stack is air + * + * @param item + * @return + */ + public static boolean isAir(@Nullable ItemStack item) { + if (item == null) + return true; + + return isAir(item.getType()); + } + + /** + * Returns if the given material is air + * + * @param material + * @return + */ + public static boolean isAir(final Material material) { + return material == null || nameEquals(material, "AIR", "CAVE_AIR", "VOID_AIR", "LEGACY_AIR"); + } + + /** + * Returns true if the given material is a horse armor (prev. named barding). + * + * @param mat + * @return + */ + public static boolean isHorseArmor(final Material mat) { + return nameEquals(mat, "BARDING", "HORSE_ARMOR"); + } + + /** + * Returns true if the given material is a carpet (any variation). + * + * @param mat + * @return + */ + public static boolean isCarpet(final Material mat) { + return nameContains(mat, "CARPET"); + } + + /** + * Return true if the given material is leaves (any variation) + * + * @param mat + * @return + */ + public static boolean isLeaves(final Material mat) { + return mat.toString().endsWith("_LEAVES") || nameEquals(mat, "LEAVES", "LEAVES_2"); + } + + /** + * Returns true if the given material is a hard clay (any variations), later + * named terracotta. + * + * @param mat + * @return + */ + public static boolean isHardClay(final Material mat) { + return nameContains(mat, "STAINED_CLAY", "HARD_CLAY", "TERRACOTTA"); + } + + /** + * Returns true if the given material is a leash for horses. + * + * @param mat + * @return + */ + public static boolean isLeash(final Material mat) { + return nameEquals(mat, "LEASH", "LEAD"); + } + + /** + * Returns true if the given material is either gold or iron pressure plate. + * + * @param mat + * @return + */ + public static boolean isHeavyPressurePlate(final Material mat) { + return nameContains(mat, "IRON_PLATE", "GOLD_PLATE", "WEIGHTED_PRESSURE_PLATE"); + } + + /** + * Return true for all kinds of wood pressure plates + * + * @param mat + * @return + */ + public static boolean isWoodPressurePlate(final Material mat) { + return nameEquals(mat, "WOOD_PLATE", "ACACIA_PRESSURE_PLATE", "BIRCH_PRESSURE_PLATE", "DARK_OAK_PRESSURE_PLATE", + "JUNGLE_PRESSURE_PLATE", "OAK_PRESSURE_PLATE", "SPRUCE_PRESSURE_PLATE"); + } + + /** + * Returns true if the given material is a firework. + * + * @param mat + * @return + */ + public static boolean isFirework(final Material mat) { + return nameContains(mat, "FIREWORK"); + } + + /** + * Returns true if the given material is log (any variation). + * + * @param mat + * @return + */ + public static boolean isLog(final Material mat) { + return nameEquals(mat, "LOG", "LOG_2") || mat.toString().endsWith("_LOG"); + } + + /** + * Returns true if the given material is a bloat (that's right) (any variation). + * + * @param mat + * @return + */ + public static boolean isBoat(final Material mat) { + return nameContains(mat, "BOAT"); + } + + /** + * Returns true if the given material is a wood button (any variation). + * + * @param mat + * @return + */ + public static boolean isWoodButton(final Material mat) { + final String n = mat.toString(); + + return n.endsWith("_BUTTON") && !n.equals("STONE_BUTTON"); + } + + /** + * Returns true if the given material is a redstone lamp (any state). + * + * @param mat + * @return + */ + public static boolean isRedstoneLamp(final Material mat) { + return nameContains(mat, "REDSTONE_LAMP"); + } + + /** + * Returns true if the given material is a monster egg. + * + * @param mat + * @return + */ + public static boolean isMonsterEgg(final Material mat) { + return nameContains(mat, "MONSTER_EGG", "_SPAWN_EGG"); + } + + /** + * Returns true if the given material is a tree sapling (not potted). + * + * @param mat + * @return + */ + public static boolean isSapling(final Material mat) { + return nameContains(mat, "SAPLING") && !mat.toString().startsWith("POTTED"); + } + + /** + * Returns true if the given material is a wall sign (any variation). + * + * @param mat the material + * @return + */ + public static boolean isWallSign(final Material mat) { + return nameContains(mat, "WALL_SIGN"); + } + + /** + * Returns true if the given material is a dead shrub or a grass. + * + * @param mat + * @return + */ + public static boolean isLongGrass(final Material mat) { + return nameEquals(mat, "LONG_GRASS", "TALL_GRASS", "FERN", "DEAD_BUSH") && !mat.toString().startsWith("POTTED"); + } + + /** + * Returns true if the given material is normally 2 blocks tall when placed. + * + * @param mat + * @return + */ + public static boolean isDoublePlant(final Material mat) { + return nameEquals(mat, "DOUBLE_PLANT", "SUNFLOWER", "LILAC", "TALL_GRASS", "LARGE_FERN", "ROSE_BUSH", "PEONY", "TALL_SEAGRASS"); + } + + /** + * Returns true if the given material is a skull. + * + * @param mat + * @return + */ + public static boolean isSkull(final CompMaterial mat) { + return isSkull(mat.getMaterial()); + } + + /** + * Returns true if the given material is a skull. + * + * @param mat + * @return + */ + public static boolean isSkull(final Material mat) { + final String name = mat.toString(); + + return (name.endsWith("_HEAD") || name.endsWith("_SKULL")) && !name.contains("WALL"); + } + + /** + * Returns true if the given material is a trap door (any variation). + * + * @param mat + * @return + */ + public static boolean isTrapDoor(final Material mat) { + final String name = mat.toString(); + + return name.contains("TRAP_DOOR") || name.contains("TRAPDOOR"); + } + + // Utility method for evaluating matches. + private static boolean nameContains(final Material mat, final String... names) { + final String matName = mat.toString(); + + for (final String name : names) + if (matName.contains(name)) + return true; + + return false; + } + + // Utility method for evaluating matches. + private static boolean nameEquals(final Material mat, final String... names) { + final String matName = mat.toString(); + + for (final String name : names) + if (matName.equals(name)) + return true; + + return false; + } + + /* + * Gets the CompMaterial with this name similar to {@link #valueOf(String)} + * without throwing an exception. + * + * @param name the name of the material. + * + * @return an optional that can be empty. + * @since 5.1.0 + */ + + private static CompMaterial getIfPresent(String name) { + return NAMES.get(name); + } + + /* + * When using 1.13+, this helps to find the old material name + * with its data value using a cached search for optimization. + * + * @see #matchDefinedCompMaterial(String, byte) + * @since 1.0.0 + */ + + private static CompMaterial requestOldMaterial(String name, int data) { + + for (final CompMaterial material : VALUES) + // Not using material.name().equals(name) check is intended. + if ((data == UNKNOWN_DATA_VALUE || data == material.data) && material.anyMatchLegacy(name)) + return material; + + return null; + } + + /** + * Parses the given material name as an CompMaterial with a given data + * value in the string if attached. See matchWithData for more info. + * + * @param key + * @return + */ + public static CompMaterial fromString(String key) { + + // try to resolve common pitfalls and emulate the material enum writing style + key = key.replace(" ", "_").toUpperCase(); + + // Translate generic monster egg to sheep instead of the white unattributed egg on MC 1.13+ + if (MinecraftVersion.atLeast(V.v1_13) && key.equals("MONSTER_EGG")) + key = "SHEEP_SPAWN_EGG"; + + final CompMaterial oldMatch = matchWithData(key); + + return oldMatch != null ? oldMatch : fromLegacy(format(key), UNKNOWN_DATA_VALUE); + } + + /* + * Parses material name and data value from the specified string. + * The separator for the material name and its data value is {@code :} + * Spaces are allowed. Mostly used when getting materials from config for old school minecrafters. + *

+ * Examples + *

+	 *     {@code INK_SACK:1 -> RED_DYE}
+	 *     {@code WOOL: 14  -> RED_WOOL}
+	 * 
+ * + * @param name the material string that consists of the material name, data and separator character. + * + * @return the parsed CompMaterial. + * @see #matchCompMaterial(String) + * @since 3.0.0 + */ + private static CompMaterial matchWithData(String name) { + final int index = name.indexOf(':'); + + if (index != -1) { + final String mat = format(name.substring(0, index)); + + try { + // We don't use Byte.parseByte because we have our own range check. + final byte data = (byte) Integer.parseInt(name.substring(index + 1).replace(" ", "")); + return data >= 0 && data < MAX_DATA_VALUE ? fromLegacy(mat, data) : fromLegacy(mat, UNKNOWN_DATA_VALUE); + + } catch (final NumberFormatException ignored) { + return fromLegacy(mat, UNKNOWN_DATA_VALUE); + } + } + + return null; + } + + /** + * Return a {@link CompMaterial} from the given block, also comparing the data value + * + * @param block + * @return + */ + public static CompMaterial fromBlock(final Block block) { + return MinecraftVersion.atLeast(V.v1_13) ? fromMaterial(block.getType()) : fromLegacy(block.getType().toString(), block.getData()); + } + + /** + * Parses the given item as an CompMaterial using its material and data value (durability) + * if not a damageable item {@link ItemStack#getDurability()}. + * + * @param item the ItemStack to match. + * + * @return an CompMaterial if matched any. + * @throws IllegalArgumentException may be thrown as an unexpected exception. + * @see #fromMaterial(Material) + * @since 2.0.0 + */ + public static CompMaterial fromItem(@NonNull ItemStack item) { + final String material = item.getType().name(); + final byte data = (byte) (Data.ISFLAT || item.getType().getMaxDurability() > 0 ? 0 : item.getDurability()); + + final CompMaterial compmaterial = fromLegacy(material, data); + + // Exception for legacy eggs for non existing entities + if (material.equals("MONSTER_EGG") && compmaterial == null) + return CompMaterial.SHEEP_SPAWN_EGG; + + return compmaterial; + } + + /** + * Parses the given material as an CompMaterial. + * + * @param material + * @return + */ + public static CompMaterial fromMaterial(@NonNull Material material) { + final CompMaterial compmaterial = fromLegacy(material.name(), UNKNOWN_DATA_VALUE); + + return compmaterial; + } + + /** + * See {@link #fromString(String)}, with the addition that + * this method throws an error if the string could not be parsed + * + * @param key + * @return + */ + public static CompMaterial fromStringStrict(final String key) { + final CompMaterial material = fromString(key); + + return material; + } + + /** + * The main method that parses the given material name and data value as an CompMaterial. + * All the values passed to this method will not be null or empty and are formatted correctly. + * + * @param name + * @param data + * @return + */ + public static CompMaterial fromLegacy(String name, int data) { + + // try to resolve common pitfalls and emulate the material enum writing style + name = name.replace(" ", "_").toUpperCase(); + + // CompMaterial does not support legacy names + name = name.replace("LEGACY_", ""); + + Boolean duplicated = null; + final boolean isAMap = name.equalsIgnoreCase("MAP"); + + // Do basic number and boolean checks before accessing more complex enum stuff. + if (Data.ISFLAT || !isAMap && data <= 0 && !(duplicated = isDuplicated(name))) { + final CompMaterial compmaterial = getIfPresent(name); + + if (compmaterial != null) + return compmaterial; + } + // Usually flat versions wouldn't pass this point, but some special materials do. + + final CompMaterial oldCompmaterial = requestOldMaterial(name, data); + + if (oldCompmaterial == null) + // Special case. Refer to FILLED_MAP for more info. + return data >= 0 && isAMap ? FILLED_MAP : null; + + if (!Data.ISFLAT && oldCompmaterial.isPlural() && (duplicated == null ? isDuplicated(name) : duplicated)) + return getIfPresent(name); + + return oldCompmaterial; + } + + /* + * CompMaterial Paradox (Duplication Check) + * Checks if the material has any duplicates. + *

+ * Example: + *

{@code MELON, CARROT, POTATO, BEETROOT -> true} + * + * @param name the name of the material to check. + * + * @return true if there's a duplicated material for this material, otherwise false. + * @since 2.0.0 + */ + private static boolean isDuplicated(String name) { + // Don't use matchCompMaterial() since this method is being called from matchCompMaterial() itself and will cause a StackOverflowError. + return DUPLICATED.contains(name); + } + + /** + * Gets the CompMaterial based on the material's ID (Magic Value) and data value.
+ * You should avoid using this for performance issues. + * + * @param id the ID (Magic value) of the material. + * @param data the data value of the material. + * + * @return a parsed CompMaterial with the same ID and data value. + * @see #fromItem(ItemStack) + * @since 2.0.0 + */ + public static CompMaterial fromId(int id, byte data) { + if (id < 0 || id > MAX_ID || data < 0) + return null; + + for (final CompMaterial materials : VALUES) + if (materials.data == data && materials.getId() == id) + return materials; + + return null; + } + + /** + * Attempts to build the string like an enum name. + * Removes all the spaces, and extra non-English characters. Also removes some config/in-game based strings. + * While this method is hard to maintain, it's extremely efficient. It's approximately more than x5 times faster than + * the normal RegEx + String Methods approach for both formatted and unformatted material names. + * + * @param name the material name to modify. + * + * @return an enum name. + * @since 2.0.0 + */ + protected static String format(String name) { + final int len = name.length(); + final char[] chs = new char[len]; + int count = 0; + boolean appendUnderline = false; + + for (int i = 0; i < len; i++) { + final char ch = name.charAt(i); + + if (!appendUnderline && count != 0 && (ch == '-' || ch == ' ' || ch == '_') && chs[count] != '_') + appendUnderline = true; + else { + boolean number = false; + // Old materials have numbers in them. + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || (number = ch >= '0' && ch <= '9')) { + if (appendUnderline) { + chs[count++] = '_'; + appendUnderline = false; + } + + if (number) + chs[count++] = ch; + else + chs[count++] = (char) (ch & 0x5f); + } + } + } + + return new String(chs, 0, count); + } + + /* + * Checks if the specified version is the same version or higher than the current server version. + * + * @param version the major version to be checked. "1." is ignored. E.g. 1.12 = 12 | 1.9 = 9 + * + * @return true of the version is equal or higher than the current version. + * @since 2.0.0 + */ + private static boolean supports(int version) { + return Data.VERSION >= version; + } + + /* + * Gets the exact major version (..., 1.9, 1.10, ..., 1.14) + * In most cases, you shouldn't be using this method. + * + * @param version Supports {@link Bukkit#getVersion()}, {@link Bukkit#getBukkitVersion()} and normal formats such as "1.14" + * + * @return the exact major version. + * @see #supports(int) + * @see #getVersion() + * @see #getMaterialVersion() + * @since 2.0.0 + */ + + private static String getMajorVersion(@NonNull String version) { + + // getVersion() + int index = version.lastIndexOf("MC:"); + if (index != -1) + version = version.substring(index + 4, version.length() - 1); + else if (version.endsWith("SNAPSHOT")) { + // getBukkitVersion() + index = version.indexOf('-'); + version = version.substring(0, index); + } + + // 1.13.2, 1.14.4, etc... + final int lastDot = version.lastIndexOf('.'); + if (version.indexOf('.') != lastDot) + version = version.substring(0, lastDot); + + return version; + } + + /* + * CompMaterial Paradox (Duplication Check) + * I've concluded that this is just an infinite loop that keeps + * going around the Singular Form and the Plural Form materials. A waste of brain cells and a waste of time. + * This solution works just fine anyway. + *

+ * A solution for CompMaterial Paradox. + * Manually parses the duplicated materials to find the exact material based on the server version. + * If the name ends with "S" -> Plural Form Material. + * Plural methods are only plural if they're also {@link #DUPLICATED} + *

+ * The only special exceptions are {@link #BRICKS} and {@link #NETHER_BRICKS} + * + * @return true if this material is a plural form material, otherwise false. + * @since 8.0.0 + */ + private boolean isPlural() { + // this.name().charAt(this.name().length() - 1) == 'S' + return this == CARROTS || this == POTATOES; + } + + /** + * Checks if the list of given material names matches the given base material. + * Mostly used for configs. + *

+ * Supports {@link String#contains} {@code CONTAINS:NAME} formats. + *

+ * Example: + *

+	 *     CompMaterial material = {@link #fromItem(ItemStack)};
+	 *     if (material.isOneOf(plugin.getConfig().getStringList("disabled-items")) return;
+	 * 
+ *
+ * {@code CONTAINS} Examples: + *
+	 *     {@code "CONTAINS:CHEST" -> CHEST, ENDERCHEST, TRAPPED_CHEST -> true}
+	 *     {@code "cOnTaINS:dYe" -> GREEN_DYE, YELLOW_DYE, BLUE_DYE, INK_SACK -> true}
+	 * 
+ *

+ * The reason that there are tags for {@code CONTAINS} is for the performance. + * + *

+ * + * @param materials the material names to check base material on. + * + * @return true if one of the given material names is similar to the base material. + * @since 3.1.1 + */ + public boolean isOneOf(Collection materials) { + if (materials == null || materials.isEmpty()) + return false; + final String name = this.name(); + + for (String comp : materials) { + final String checker = comp.toUpperCase(Locale.ENGLISH); + if (checker.startsWith("CONTAINS:")) { + comp = format(checker.substring(9)); + + if (name.contains(comp)) + return true; + + continue; + } + + // Direct Object Equals + final CompMaterial xMat = fromString(comp); + + if (xMat != null && xMat == this) + return true; + } + return false; + } + + /** + * Sets the {@link Material} (and data value on older versions) of an item. + * Damageable materials will not have their durability changed. + *

+ * Use {@link #toItem()} instead when creating new ItemStacks. + * + * @param item the item to change its type. + * @return + * + * @see #toItem() + * @since 3.0.0 + */ + public ItemStack setType(ItemStack item) { + Objects.requireNonNull(item, "Cannot set material for null ItemStack"); + final Material material = this.toMaterial(); + Objects.requireNonNull(material, () -> "Unsupported material: " + this.name()); + + item.setType(material); + if (!Data.ISFLAT && material.getMaxDurability() <= 0) + item.setDurability(this.data); + + return item; + } + + /* + * Checks if the given material name matches any of this CompMaterial's legacy material names. + * All the values passed to this method will not be null or empty and are formatted correctly. + * + * @param name the material name to check. + * + * @return true if it's one of the legacy names, otherwise false. + * @since 2.0.0 + */ + private boolean anyMatchLegacy(String name) { + for (int i = this.legacy.length - 1; i >= 0; i--) + if (name.equals(this.legacy[i])) + return true; + return false; + } + + /** + * Gets the ID (Magic value) of the material. + * https://www.minecraftinfo.com/idlist.htm + * + * @return the ID of the material or -1 if it's not a legacy material or the server doesn't support the material. + * @see #fromId(int, byte) + * @since 2.2.0 + */ + public int getId() { + if (this.data != 0 || this.version >= 13) + return -1; + + final Material material = this.toMaterial(); + + if (material == null) + return -1; + + try { + if (Data.ISFLAT && !material.isLegacy()) + return -1; + } catch (final Throwable t) { + } + + return material.getId(); + } + + /* + * This method is needed due to Java enum initialization limitations. + * It's really inefficient yes, but it's only used for initialization. + *

+ * Yes there are many other ways like comparing the hardcoded ordinal or using a boolean in the enum constructor, + * but it's not really a big deal. + *

+ * This method should not be called if the version is after the flattening update {@link Data#ISFLAT} + * and is only used for parsing materials, not matching, for matching check {@link #DUPLICATED} + */ + private boolean isDuplicated() { + switch (this.name()) { + case "MELON": + case "CARROT": + case "POTATO": + case "GRASS": + case "BRICK": + case "NETHER_BRICK": + + // Illegal Elements + // Since both 1.12 and 1.13 have _DOOR CompMaterial will use it + // for 1.12 to parse the material, but it needs _DOOR_ITEM. + // We'll trick CompMaterial into thinking this needs to be parsed + // using the old methods. + // Some of these materials have their enum name added to the legacy list as well. + case "DARK_OAK_DOOR": + case "ACACIA_DOOR": + case "BIRCH_DOOR": + case "JUNGLE_DOOR": + case "SPRUCE_DOOR": + case "MAP": + case "CAULDRON": + case "BREWING_STAND": + case "FLOWER_POT": + return true; + default: + return false; + } + } + + /** + * Used for datas that need to be accessed during enum initilization. + * + * @since 9.0.0 + */ + private static final class Data { + /** + * The current version of the server in the a form of a major version. + * If the static initialization for this fails, you know something's wrong with the server software. + * + * @since 1.0.0 + */ + private static final int VERSION = Integer.parseInt(getMajorVersion(Bukkit.getVersion()).substring(2)); + /** + * Cached result if the server version is after the v1.13 flattening update. + * + * @since 3.0.0 + */ + private static final boolean ISFLAT = supports(13); + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/utils/compat/MinecraftVersion.java b/src/main/java/dev/drawethree/xprison/utils/compat/MinecraftVersion.java new file mode 100644 index 0000000..bbfaa23 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/compat/MinecraftVersion.java @@ -0,0 +1,199 @@ +package dev.drawethree.xprison.utils.compat; + +import lombok.Getter; +import org.bukkit.Bukkit; + +/** + * Represents the current Minecraft version the plugin loaded on + */ +public final class MinecraftVersion { + + /** + * The string representation of the version, for example V1_13 + */ + private static String serverVersion; + + /** + * The wrapper representation of the version + */ + @Getter + private static V current; + + /** + * The version wrapper + */ + public enum V { + v1_20(20, false), + v1_19(19), + v1_18(18), + v1_17(17), + v1_16(16), + v1_15(15), + v1_14(14), + v1_13(13), + v1_12(12), + v1_11(11), + v1_10(10), + v1_9(9), + v1_8(8), + v1_7(7), + v1_6(6), + v1_5(5), + v1_4(4), + v1_3_AND_BELOW(3); + + /** + * The numeric version (the second part of the 1.x number) + */ + private final int minorVersionNumber; + + /** + * Is this library tested with this Minecraft version? + */ + @Getter + private final boolean tested; + + /** + * Creates new enum for a MC version that is tested + * + * @param version + */ + V(int version) { + this(version, true); + } + + /** + * Creates new enum for a MC version + * + * @param version + * @param tested + */ + V(int version, boolean tested) { + this.minorVersionNumber = version; + this.tested = tested; + } + + /** + * Attempts to get the version from number + * + * @param number + * @return + * @throws RuntimeException if number not found + */ + private static V parse(int number) { + for (final V v : values()) { + if (v.minorVersionNumber == number) { + return v; + } + } + return null; + } + + /** + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return "1." + this.minorVersionNumber; + } + } + + /** + * Does the current Minecraft version equal the given version? + * + * @param version + * @return + */ + public static boolean equals(V version) { + return compareWith(version) == 0; + } + + /** + * Is the current Minecraft version older than the given version? + * + * @param version + * @return + */ + public static boolean olderThan(V version) { + return compareWith(version) < 0; + } + + /** + * Is the current Minecraft version newer than the given version? + * + * @param version + * @return + */ + public static boolean newerThan(V version) { + return compareWith(version) > 0; + } + + /** + * Is the current Minecraft version at equals or newer than the given version? + * + * @param version + * @return + */ + public static boolean atLeast(V version) { + return equals(version) || newerThan(version); + } + + // Compares two versions by the number + private static int compareWith(V version) { + try { + return getCurrent().minorVersionNumber - version.minorVersionNumber; + + } catch (final Throwable t) { + t.printStackTrace(); + + return 0; + } + } + + /** + * Return the class versioning such as v1_14_R1 + * + * @return + */ + public static String getServerVersion() { + return serverVersion.equals("craftbukkit") ? "" : serverVersion; + } + + // Initialize the version + static { + try { + + final String packageName = Bukkit.getServer() == null ? "" : Bukkit.getServer().getClass().getPackage().getName(); + final String curr = packageName.substring(packageName.lastIndexOf('.') + 1); + final boolean hasGatekeeper = !"craftbukkit".equals(curr) && !"".equals(packageName); + + serverVersion = curr; + + if (hasGatekeeper) { + int pos = 0; + + for (final char ch : curr.toCharArray()) { + pos++; + + if (pos > 2 && ch == 'R') + break; + } + + final String numericVersion = curr.substring(1, pos - 2).replace("_", "."); + + int found = 0; + + for (final char ch : numericVersion.toCharArray()) + if (ch == '.') + found++; + + current = V.parse(Integer.parseInt(numericVersion.split("\\.")[1])); + + } else + current = V.v1_3_AND_BELOW; + + } catch (final Throwable t) { + t.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/utils/economy/EconomyUtils.java b/src/main/java/dev/drawethree/xprison/utils/economy/EconomyUtils.java new file mode 100644 index 0000000..7ef65ff --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/economy/EconomyUtils.java @@ -0,0 +1,19 @@ +package dev.drawethree.xprison.utils.economy; + +import dev.drawethree.xprison.XPrison; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.entity.Player; + +public class EconomyUtils { + + private static final Economy ECONOMY = XPrison.getInstance().getEconomy(); + + public static EconomyResponse deposit(Player player, double amount) { + return ECONOMY.depositPlayer(player, amount); + } + + public static EconomyResponse withdraw(Player player, double amount) { + return ECONOMY.withdrawPlayer(player, amount); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/gui/ClearDBGui.java b/src/main/java/dev/drawethree/xprison/utils/gui/ClearDBGui.java new file mode 100644 index 0000000..498df4f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/gui/ClearDBGui.java @@ -0,0 +1,36 @@ +package dev.drawethree.xprison.utils.gui; + +import dev.drawethree.xprison.XPrison; +import dev.drawethree.xprison.XPrisonModule; +import dev.drawethree.xprison.utils.player.PlayerUtils; +import org.bukkit.entity.Player; + +import java.util.Collection; + +public class ClearDBGui extends ConfirmationGui { + + private final XPrisonModule module; + + public ClearDBGui(Player player, XPrisonModule module) { + super(player, module == null ? "Clear all player data?" : "Clear data for " + module.getName() + "?"); + this.module = module; + } + + @Override + public void confirm(boolean confirm) { + if (confirm) { + if (this.module == null) { + this.getAllModules().forEach(XPrisonModule::resetPlayerData); + PlayerUtils.sendMessage(this.getPlayer(), "&aX-Prison - All Modules Data have been reset."); + } else { + this.module.resetPlayerData(); + PlayerUtils.sendMessage(this.getPlayer(), "&aX-Prison - DB Player data for module " + module.getName() + " has been reset."); + } + } + this.close(); + } + + private Collection getAllModules() { + return XPrison.getInstance().getModules(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/gui/ConfirmationGui.java b/src/main/java/dev/drawethree/xprison/utils/gui/ConfirmationGui.java new file mode 100644 index 0000000..caeac62 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/gui/ConfirmationGui.java @@ -0,0 +1,51 @@ +package dev.drawethree.xprison.utils.gui; + +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import me.lucko.helper.menu.Gui; +import me.lucko.helper.menu.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public abstract class ConfirmationGui extends Gui { + + private static final ItemStack YES_ITEM = ItemStackBuilder.of(CompMaterial.GREEN_STAINED_GLASS_PANE.toItem()).name("&aYES").build(); + private static final ItemStack NO_ITEM = ItemStackBuilder.of(CompMaterial.RED_STAINED_GLASS_PANE.toItem()).name("&cNO").build(); + + public ConfirmationGui(Player player, String title) { + super(player, 6, title); + } + + @Override + public void redraw() { + if (isFirstDraw()) { + + this.setItem(13, this.getInfoItem()); + for (int i = 19; i < 22; i++) { + this.setItem(i, getItem(true)); + this.setItem(i + 9, getItem(true)); + this.setItem(i + 18, getItem(true)); + } + for (int i = 23; i < 26; i++) { + this.setItem(i, getItem(false)); + this.setItem(i + 9, getItem(false)); + this.setItem(i + 18, getItem(false)); + } + } + } + + + public abstract void confirm(boolean confirm); + + + private Item getItem(boolean action) { + return ItemStackBuilder.of(action ? YES_ITEM : NO_ITEM).build(() -> { + confirm(action); + }); + } + + + private Item getInfoItem() { + return ItemStackBuilder.of(CompMaterial.BOOK.toItem()).name(this.getInitialTitle()).lore("&c&lWARNING!", "&7This action cannot be undone.").buildItem().build(); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/inventory/InventoryUtils.java b/src/main/java/dev/drawethree/xprison/utils/inventory/InventoryUtils.java new file mode 100644 index 0000000..12bed9b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/inventory/InventoryUtils.java @@ -0,0 +1,34 @@ +package dev.drawethree.xprison.utils.inventory; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public final class InventoryUtils { + + private InventoryUtils() { + throw new UnsupportedOperationException("Cannot instantiate."); + } + + public static boolean hasSpace(Inventory inventory) { + return inventory.firstEmpty() != -1; + } + + public static int getInventorySlot(Player player, ItemStack item) { + if (item == null) { + return -1; + } + for (int i = 0; i < player.getInventory().getSize(); i++) { + ItemStack item1 = player.getInventory().getItem(i); + + if (item1 == null) { + continue; + } + + if (item1.equals(item)) { + return i; + } + } + return -1; + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/item/ItemStackBuilder.java b/src/main/java/dev/drawethree/xprison/utils/item/ItemStackBuilder.java new file mode 100644 index 0000000..bcc9de9 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/item/ItemStackBuilder.java @@ -0,0 +1,227 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.drawethree.xprison.utils.item; + +import dev.drawethree.xprison.utils.text.TextUtils; +import me.lucko.helper.menu.Item; +import me.lucko.helper.utils.annotation.NonnullByDefault; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * Easily construct {@link ItemStack} instances + */ +@NonnullByDefault +public final class ItemStackBuilder { + private static final ItemFlag[] ALL_FLAGS = new ItemFlag[]{ + ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_ATTRIBUTES, + ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_POTION_EFFECTS, + ItemFlag.HIDE_DESTROYS, ItemFlag.HIDE_PLACED_ON + }; + + private final ItemStack itemStack; + + public static ItemStackBuilder of(Material material) { + return new ItemStackBuilder(new ItemStack(material)).hideAttributes(); + } + + public static ItemStackBuilder of(ItemStack itemStack) { + return new ItemStackBuilder(itemStack).hideAttributes(); + } + + private ItemStackBuilder(ItemStack itemStack) { + this.itemStack = Objects.requireNonNull(itemStack, "itemStack"); + } + + public ItemStackBuilder transform(Consumer is) { + is.accept(this.itemStack); + return this; + } + + public ItemStackBuilder transformMeta(Consumer meta) { + ItemMeta m = this.itemStack.getItemMeta(); + if (m != null) { + meta.accept(m); + this.itemStack.setItemMeta(m); + } + return this; + } + + public ItemStackBuilder name(String name) { + return transformMeta(meta -> meta.setDisplayName(TextUtils.applyColor(name))); + } + + public ItemStackBuilder type(Material material) { + return transform(itemStack -> itemStack.setType(material)); + } + + public ItemStackBuilder lore(String line) { + return transformMeta(meta -> { + List lore = meta.getLore() == null ? new ArrayList<>() : meta.getLore(); + lore.add(TextUtils.applyColor(line)); + meta.setLore(lore); + }); + } + + public ItemStackBuilder lore(String... lines) { + return transformMeta(meta -> { + List lore = meta.getLore() == null ? new ArrayList<>() : meta.getLore(); + for (String line : lines) { + lore.add(TextUtils.applyColor(line)); + } + meta.setLore(lore); + }); + } + + public ItemStackBuilder lore(Iterable lines) { + return transformMeta(meta -> { + List lore = meta.getLore() == null ? new ArrayList<>() : meta.getLore(); + for (String line : lines) { + lore.add(TextUtils.applyColor(line)); + } + meta.setLore(lore); + }); + } + + public ItemStackBuilder clearLore() { + return transformMeta(meta -> meta.setLore(new ArrayList<>())); + } + + public ItemStackBuilder durability(int durability) { + return transform(itemStack -> itemStack.setDurability((short) durability)); + } + + public ItemStackBuilder data(int data) { + return durability(data); + } + + public ItemStackBuilder amount(int amount) { + return transform(itemStack -> itemStack.setAmount(amount)); + } + + public ItemStackBuilder enchant(Enchantment enchantment, int level) { + return transform(itemStack -> itemStack.addUnsafeEnchantment(enchantment, level)); + } + + public ItemStackBuilder enchant(Enchantment enchantment) { + return transform(itemStack -> itemStack.addUnsafeEnchantment(enchantment, 1)); + } + + public ItemStackBuilder clearEnchantments() { + return transform(itemStack -> itemStack.getEnchantments().keySet().forEach(itemStack::removeEnchantment)); + } + + public ItemStackBuilder flag(ItemFlag... flags) { + return transformMeta(meta -> meta.addItemFlags(flags)); + } + + public ItemStackBuilder unflag(ItemFlag... flags) { + return transformMeta(meta -> meta.removeItemFlags(flags)); + } + + public ItemStackBuilder hideAttributes() { + return flag(ALL_FLAGS); + } + + public ItemStackBuilder showAttributes() { + return unflag(ALL_FLAGS); + } + + public ItemStackBuilder color(Color color) { + return transform(itemStack -> { + Material type = itemStack.getType(); + if (type == Material.LEATHER_BOOTS || type == Material.LEATHER_CHESTPLATE || type == Material.LEATHER_HELMET || type == Material.LEATHER_LEGGINGS) { + LeatherArmorMeta meta = (LeatherArmorMeta) itemStack.getItemMeta(); + meta.setColor(color); + itemStack.setItemMeta(meta); + } + }); + } + + public ItemStackBuilder breakable(boolean flag) { + return transformMeta(meta -> meta.setUnbreakable(!flag)); + } + + public ItemStackBuilder apply(Consumer consumer) { + consumer.accept(this); + return this; + } + + public ItemStack build() { + return this.itemStack; + } + + public Item.Builder buildItem() { + return Item.builder(build()); + } + + public Item build(@Nullable Runnable handler) { + return buildItem().bind(handler, ClickType.RIGHT, ClickType.LEFT).build(); + } + + public Item build(ClickType type, @Nullable Runnable handler) { + return buildItem().bind(type, handler).build(); + } + + public Item build(@Nullable Runnable rightClick, @Nullable Runnable leftClick) { + return buildItem().bind(ClickType.RIGHT, rightClick).bind(ClickType.LEFT, leftClick).build(); + } + + public Item buildFromMap(Map handlers) { + return buildItem().bindAllRunnables(handlers.entrySet()).build(); + } + + public Item buildConsumer(@Nullable Consumer handler) { + return buildItem().bind(handler, ClickType.RIGHT, ClickType.LEFT).build(); + } + + public Item buildConsumer(ClickType type, @Nullable Consumer handler) { + return buildItem().bind(type, handler).build(); + } + + public Item buildConsumer(@Nullable Consumer rightClick, @Nullable Consumer leftClick) { + return buildItem().bind(ClickType.RIGHT, rightClick).bind(ClickType.LEFT, leftClick).build(); + } + + public Item buildFromConsumerMap(Map> handlers) { + return buildItem().bindAllConsumers(handlers.entrySet()).build(); + } + +} \ No newline at end of file diff --git a/src/main/java/dev/drawethree/xprison/utils/item/PrisonItem.java b/src/main/java/dev/drawethree/xprison/utils/item/PrisonItem.java new file mode 100644 index 0000000..d78886b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/item/PrisonItem.java @@ -0,0 +1,71 @@ +package dev.drawethree.xprison.utils.item; + +import com.saicone.rtag.RtagItem; +import dev.drawethree.xprison.enchants.model.XPrisonEnchantment; +import dev.drawethree.xprison.enchants.repo.EnchantsRepository; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; + +public class PrisonItem extends RtagItem { + + private static final String MAIN = "upc"; + + public PrisonItem(ItemStack item) { + super(item); + } + + public Map getEnchants(EnchantsRepository repository) { + final Map enchants = new HashMap<>(); + final Map map = get(MAIN, "enchants"); + if (map != null) { + map.forEach((id, level) -> enchants.put(repository.getEnchantBy(id), (int) level)); + } + return enchants; + } + + public int getEnchantLevel(XPrisonEnchantment enchant) { + return getOptional(MAIN, "enchants", String.valueOf(enchant.getId())).or(0); + } + + public long getBrokenBlocks() { + return getOptional(MAIN, "blocks").or(0L); + } + + public Long getGems() { + return get(MAIN, "gems"); + } + + public Integer getLevel() { + return get(MAIN, "level"); + } + + public Long getTokens() { + return get(MAIN, "tokens"); + } + + public void setEnchant(XPrisonEnchantment enchant, int level) { + if (level > 0) { + set(level, MAIN, "enchants", String.valueOf(enchant.getId())); + } else { + remove(MAIN, "enchants", String.valueOf(enchant.getId())); + } + } + + public void setGems(long amount) { + set(amount, MAIN, "gems"); + } + + public void setLevel(int level) { + set(level, MAIN, "level"); + } + + public void setTokens(long amount) { + set(amount, MAIN, "tokens"); + } + + public void addBrokenBlocks(int amount) { + set(getBrokenBlocks() + amount, MAIN, "blocks"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/location/LocationUtils.java b/src/main/java/dev/drawethree/xprison/utils/location/LocationUtils.java new file mode 100644 index 0000000..623307c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/location/LocationUtils.java @@ -0,0 +1,29 @@ +package dev.drawethree.xprison.utils.location; + +import org.bukkit.Location; + +public class LocationUtils { + + public static final String INVALID_LOCATION = "Invalid Location"; + + public static String toXYZW(Location location) { + if (location == null) { + return INVALID_LOCATION; + } + return "X: " + + location.getBlockX() + + ", " + + "Y: " + + location.getBlockY() + + ", " + + "Z: " + + location.getBlockZ() + + ", " + + "World: " + + location.getWorld().getName(); + } + + private LocationUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/MapUtil.java b/src/main/java/dev/drawethree/xprison/utils/misc/MapUtil.java new file mode 100644 index 0000000..9b7363c --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/MapUtil.java @@ -0,0 +1,25 @@ +package dev.drawethree.xprison.utils.misc; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class MapUtil { + public static > Map sortByValue + (Map map) { + + return map.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, + LinkedHashMap::new + )); + } + + private MapUtil() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/MaterialUtils.java b/src/main/java/dev/drawethree/xprison/utils/misc/MaterialUtils.java new file mode 100644 index 0000000..4fdd646 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/MaterialUtils.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.utils.misc; + +import dev.drawethree.xprison.utils.compat.CompMaterial; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; + +public class MaterialUtils { + + public static ItemStack getSmeltedFormAsItemStack(Block block) { + CompMaterial material = CompMaterial.fromBlock(block); + switch (material) { + case STONE: + return CompMaterial.COBBLESTONE.toItem(); + case COAL_ORE: + return CompMaterial.COAL.toItem(); + case DIAMOND_ORE: + return CompMaterial.DIAMOND.toItem(); + case EMERALD_ORE: + return CompMaterial.EMERALD.toItem(); + case REDSTONE_ORE: + return CompMaterial.REDSTONE.toItem(); + case GOLD_ORE: + return CompMaterial.GOLD_INGOT.toItem(); + case IRON_ORE: + return CompMaterial.IRON_INGOT.toItem(); + case NETHER_QUARTZ_ORE: + return CompMaterial.QUARTZ.toItem(); + case LAPIS_ORE: + return CompMaterial.LAPIS_LAZULI.toItem(); + default: + return material.toItem(); + } + } + + private MaterialUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/MathUtils.java b/src/main/java/dev/drawethree/xprison/utils/misc/MathUtils.java new file mode 100644 index 0000000..c9723d3 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/MathUtils.java @@ -0,0 +1,44 @@ +package dev.drawethree.xprison.utils.misc; + +import java.util.ArrayList; +import java.util.Arrays; + +public class MathUtils { + + /** + * Formats number to format ('k','M','B','T','q','Q','QT','S','SP','O','N','D' + * If number is or equal to 1000.0 will return the original number. + * Supports also negative amounts + * + * @param amount number to format + * @return Formatted number as string + */ + public static String formatNumber(double amount) { + + boolean negative = amount < 0; + String prefix = ""; + + if (negative) { + amount = Math.abs(amount); + prefix = "-"; + } + + if (amount <= 1000.0D) + return prefix + amount; + ArrayList suffixes = new ArrayList<>(Arrays.asList("", "k", "M", "B", "T", "q", "Q", "QT", "S", "SP", "O", + "N", "D")); + double chunks = Math.floor(Math.floor(Math.log10(amount) / 3.0D)); + amount /= Math.pow(10.0D, chunks * 3.0D - 1.0D); + amount /= 10.0D; + String suffix = suffixes.get((int) chunks); + String format = String.valueOf(amount); + if (format.replace(".", "").length() > 5) + format = format.substring(0, 5); + return prefix + format + suffix; + } + + private MathUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } + +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/NumberUtils.java b/src/main/java/dev/drawethree/xprison/utils/misc/NumberUtils.java new file mode 100644 index 0000000..0961b38 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/NumberUtils.java @@ -0,0 +1,23 @@ +package dev.drawethree.xprison.utils.misc; + +import java.math.BigInteger; + +public final class NumberUtils { + + private NumberUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } + + public static boolean isLongGreaterThanMaxLong(String input) { + BigInteger bigIntegerInput = new BigInteger(input); + return bigIntegerInput.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0; + } + + public static boolean wouldAdditionBeOverMaxLong(long number1, long number2) { + BigInteger bigInteger1 = new BigInteger(String.valueOf(number1)); + BigInteger bigInteger2 = new BigInteger(String.valueOf(number2)); + BigInteger result = bigInteger1.add(bigInteger2); + + return result.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0; + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/ProgressBar.java b/src/main/java/dev/drawethree/xprison/utils/misc/ProgressBar.java new file mode 100644 index 0000000..a3d21f5 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/ProgressBar.java @@ -0,0 +1,41 @@ +package dev.drawethree.xprison.utils.misc; + +import dev.drawethree.xprison.utils.text.TextUtils; +import org.bukkit.ChatColor; + +public class ProgressBar { + + static final ChatColor AVAILABLE_COLOR = ChatColor.GREEN; + static final ChatColor NOT_AVAILABLE_COLOR = ChatColor.RED; + static final String DEFAULT_DELIMITER = ":"; + + public static String getProgressBar(int amountOfDelimeters, String delimeter, double current, double required) { + + if (delimeter == null || delimeter.isEmpty()) { + delimeter = DEFAULT_DELIMITER; + } + + if (current > required) { + current = required; + } + + double treshold = required / amountOfDelimeters; + int numberOfGreens = (int) (current / treshold); + + StringBuilder result = new StringBuilder(); + + result.append(AVAILABLE_COLOR); + for (int i = 0; i < numberOfGreens; i++) { + result.append(delimeter); + } + result.append(NOT_AVAILABLE_COLOR); + for (int i = 0; i < amountOfDelimeters - numberOfGreens; i++) { + result.append(delimeter); + } + return TextUtils.applyColor(result.toString()); + } + + private ProgressBar() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/RegionUtils.java b/src/main/java/dev/drawethree/xprison/utils/misc/RegionUtils.java new file mode 100644 index 0000000..af68a6f --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/RegionUtils.java @@ -0,0 +1,50 @@ +package dev.drawethree.xprison.utils.misc; + +import org.bukkit.Location; +import org.codemc.worldguardwrapper.WorldGuardWrapper; +import org.codemc.worldguardwrapper.flag.IWrappedFlag; +import org.codemc.worldguardwrapper.region.IWrappedRegion; + +import java.util.Map; +import java.util.Set; + +public class RegionUtils { + + + public static IWrappedRegion getRegionWithHighestPriority(Location loc) { + Set regions = WorldGuardWrapper.getInstance().getRegions(loc); + IWrappedRegion highestPrioRegion = null; + for (IWrappedRegion region : regions) { + if (highestPrioRegion == null || region.getPriority() > highestPrioRegion.getPriority()) { + highestPrioRegion = region; + } + } + return highestPrioRegion; + } + + public static IWrappedRegion getRegionWithHighestPriorityAndFlag(Location loc, String flagName, Object flagValue) { + Set regions = WorldGuardWrapper.getInstance().getRegions(loc); + + IWrappedRegion highestPrioRegion = null; + + for (IWrappedRegion region : regions) { + for (Map.Entry, Object> flag : region.getFlags().entrySet()) { + if (flag.getKey().getName().equalsIgnoreCase(flagName) && flag.getValue().equals(flagValue)) { + if (highestPrioRegion == null || region.getPriority() > highestPrioRegion.getPriority()) { + highestPrioRegion = region; + } + } + } + } + return highestPrioRegion; + } + + public static IWrappedRegion getFirstRegionAtLocation(Location loc) { + Set regions = WorldGuardWrapper.getInstance().getRegions(loc); + return regions.size() == 0 ? null : regions.iterator().next(); + } + + private RegionUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/SkullUtils.java b/src/main/java/dev/drawethree/xprison/utils/misc/SkullUtils.java new file mode 100644 index 0000000..718208e --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/SkullUtils.java @@ -0,0 +1,78 @@ +package dev.drawethree.xprison.utils.misc; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import dev.drawethree.xprison.utils.compat.CompMaterial; +import dev.drawethree.xprison.utils.compat.MinecraftVersion; +import dev.drawethree.xprison.utils.item.ItemStackBuilder; +import org.bukkit.OfflinePlayer; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.UUID; + +public class SkullUtils { + + public static final ItemStack HELP_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2YzY2E0ZjdjOTJkZGUzYTc3ZWM1MTBhNzRiYThjMmU4ZDBlYzdiODBmMGUzNDhjYzZkZGRkNmI0NThiZCJ9fX0="); + public static final ItemStack DIAMOND_R_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzkzZWQ4MDdkYmYxNDdjNWVmOWI4ZWM0NmQzZmE2ZTJkN2IyZGJkMzQzMWEyMzQxN2MxMzU0YmI4NjNjNCJ9fX0="); + public static final ItemStack DIAMOND_P_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWIxZTQyNzY3MDkwODI4OTcwYTliNDMyNzYyMDYyZmY2ZGY0Y2JjMjMxMWRlMjNhMWJiNDI1M2VjYjE2OTJjIn19fQ=="); + public static final ItemStack MONEY_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTM2ZTk0ZjZjMzRhMzU0NjVmY2U0YTkwZjJlMjU5NzYzODllYjk3MDlhMTIyNzM1NzRmZjcwZmQ0ZGFhNjg1MiJ9fX0="); + public static final ItemStack COIN_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjBhN2I5NGM0ZTU4MWI2OTkxNTlkNDg4NDZlYzA5MTM5MjUwNjIzN2M4OWE5N2M5MzI0OGEwZDhhYmM5MTZkNSJ9fX0="); + public static final ItemStack GANG_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTM3ZjlkYjZlZWNlNDliMmMxZDZkOWVmOTRmNmMxMTQ4OTA0MTIwMjkxMzY1YTE3ZDI3MGY5OGY2MmFlZGUifX19"); + public static final ItemStack INFO_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTY0MzlkMmUzMDZiMjI1NTE2YWE5YTZkMDA3YTdlNzVlZGQyZDUwMTVkMTEzYjQyZjQ0YmU2MmE1MTdlNTc0ZiJ9fX0"); + public static final ItemStack COMMAND_BLOCK_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWNiYTcyNzdmYzg5NWJmM2I2NzM2OTQxNTk4NjRiODMzNTFhNGQxNDcxN2U0NzZlYmRhMWMzYmYzOGZjZjM3In19fQ=="); + public static final ItemStack CHECK_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2UyYTUzMGY0MjcyNmZhN2EzMWVmYWI4ZTQzZGFkZWUxODg5MzdjZjgyNGFmODhlYThlNGM5M2E0OWM1NzI5NCJ9fX0="); + public static final ItemStack CROSS_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTljZGI5YWYzOGNmNDFkYWE1M2JjOGNkYTc2NjVjNTA5NjMyZDE0ZTY3OGYwZjE5ZjI2M2Y0NmU1NDFkOGEzMCJ9fX0="); + public static final ItemStack DANGER_SKULL = getCustomTextureHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTRlMWRhODgyZTQzNDgyOWI5NmVjOGVmMjQyYTM4NGE1M2Q4OTAxOGZhNjVmZWU1YjM3ZGViMDRlY2NiZjEwZSJ9fX0"); + + + public static void init() { + //nothing here, just to make sure the class gets loaded on start. + } + + public static ItemStack getCustomTextureHead(String value) { + ItemStack head = CompMaterial.PLAYER_HEAD.toItem(); + + SkullMeta meta = (SkullMeta) head.getItemMeta(); + + GameProfile profile; + try { + profile = new GameProfile(UUID.randomUUID(), null); + } + catch (Throwable t) { + profile = new GameProfile(UUID.randomUUID(), "X-Prison"); + } + + profile.getProperties().put("textures", new Property("textures", value)); + Field profileField; + try { + profileField = meta.getClass().getDeclaredField("profile"); + profileField.setAccessible(true); + profileField.set(meta, profile); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + e.printStackTrace(); + } + head.setItemMeta(meta); + return head; + } + + + public static ItemStack createPlayerHead(OfflinePlayer player, String displayName, List lore) { + ItemStack baseItem = CompMaterial.PLAYER_HEAD.toItem(); + SkullMeta meta = (SkullMeta) baseItem.getItemMeta(); + + if (MinecraftVersion.atLeast(MinecraftVersion.V.v1_13)) { + meta.setOwningPlayer(player); + } else { + meta.setOwner(player.getName()); + } + baseItem.setItemMeta(meta); + return ItemStackBuilder.of(baseItem).name(displayName).lore(lore).build(); + } + + private SkullUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/misc/TimeUtil.java b/src/main/java/dev/drawethree/xprison/utils/misc/TimeUtil.java new file mode 100644 index 0000000..b496f15 --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/misc/TimeUtil.java @@ -0,0 +1,40 @@ +package dev.drawethree.xprison.utils.misc; + +import java.util.StringJoiner; + +public class TimeUtil { + + public static String getTime(long seconds) { + final StringJoiner joiner = new StringJoiner(" "); + + long minutes = seconds / 60; + long hours = minutes / 60; + final long days = hours / 24; + + seconds %= 60; + minutes %= 60; + hours %= 24; + + if (days > 0) { + joiner.add(days + "d"); + } + + if (hours > 0) { + joiner.add(hours + "h"); + } + + if (minutes > 0) { + joiner.add(minutes + "m"); + } + + if (seconds > 0) { + joiner.add(seconds + "s"); + } + + return joiner.toString(); + } + + private TimeUtil() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/player/PlayerUtils.java b/src/main/java/dev/drawethree/xprison/utils/player/PlayerUtils.java new file mode 100644 index 0000000..9609c7a --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/player/PlayerUtils.java @@ -0,0 +1,68 @@ +package dev.drawethree.xprison.utils.player; + +import dev.drawethree.xprison.utils.text.TextUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public final class PlayerUtils { + + /** + * Sends a message to a player and replaces colors and also hex colors + * + * @param commandSender to who should be message send + * @param message message + */ + public static void sendMessage(CommandSender commandSender, String message) { + if (commandSender instanceof Player && !((Player) commandSender).isOnline()) { + return; + } + if (StringUtils.isBlank(message)) { + return; + } + commandSender.sendMessage(TextUtils.applyColor(message)); + } + + /** + * Sends multiple message to a player and replaces colors and also hex colors + * + * @param commandSender to who should be message send + * @param message message + */ + public static void sendMessage(CommandSender commandSender, List message) { + if (commandSender instanceof Player && !((Player) commandSender).isOnline()) { + return; + } + for (String s : message) { + if (StringUtils.isBlank(s)) { + return; + } + commandSender.sendMessage(TextUtils.applyColor(s)); + } + } + + /** + * Sends a title with subtitle to a player and replaces colors and also hex colors + * + * @param player to who should be message send + * @param title title + * @param subTitle sub title + */ + public static void sendTitle(Player player, String title, String subTitle) { + if (!player.isOnline()) { + return; + } + + if (StringUtils.isBlank(title) || StringUtils.isBlank(subTitle)) { + return; + } + + player.sendTitle(TextUtils.applyColor(title),TextUtils.applyColor(subTitle)); + } + + private PlayerUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/main/java/dev/drawethree/xprison/utils/text/TextUtils.java b/src/main/java/dev/drawethree/xprison/utils/text/TextUtils.java new file mode 100644 index 0000000..d65059b --- /dev/null +++ b/src/main/java/dev/drawethree/xprison/utils/text/TextUtils.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.utils.text; + +import dev.drawethree.xprison.utils.compat.MinecraftVersion; +import net.md_5.bungee.api.ChatColor; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TextUtils { + + private static final Pattern HEX_PATTERN = Pattern.compile("&#([A-Fa-f0-9]{6})"); + + public static String applyColor(String message) { + if (MinecraftVersion.atLeast(MinecraftVersion.V.v1_16)) { + Matcher matcher = HEX_PATTERN.matcher(message); + while (matcher.find()) { + final ChatColor hexColor = ChatColor.of(matcher.group().substring(1)); + final String before = message.substring(0, matcher.start()); + final String after = message.substring(matcher.end()); + message = before + hexColor + after; + matcher = HEX_PATTERN.matcher(message); + } + } + return ChatColor.translateAlternateColorCodes('&', message); + } + + public static List applyColor(List list) { + List returnVal = new ArrayList<>(list.size()); + list.forEach(s -> returnVal.add(applyColor(s))); + return returnVal; + } + + private TextUtils() { + throw new UnsupportedOperationException("Cannot instantiate"); + } +} diff --git a/src/test/java/dev/drawethree/xprison/utils/LocationUtilsTest.java b/src/test/java/dev/drawethree/xprison/utils/LocationUtilsTest.java new file mode 100644 index 0000000..2169f83 --- /dev/null +++ b/src/test/java/dev/drawethree/xprison/utils/LocationUtilsTest.java @@ -0,0 +1,60 @@ +package dev.drawethree.xprison.utils; + +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static dev.drawethree.xprison.utils.location.LocationUtils.INVALID_LOCATION; +import static dev.drawethree.xprison.utils.location.LocationUtils.toXYZW; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class LocationUtilsTest { + + + @Mock + private Location mockLocation; + + @Mock + private World mockWorld; + + @BeforeEach + public void setUp() { + + } + + @Test + public void test_success_toXYZW_0_0_0_world() { + + this.mockWorld = mock(World.class); + this.mockLocation = mock(Location.class); + + String expected = "X: 0, Y: 0, Z: 0, World: world"; + + when(mockWorld.getName()).thenReturn("world"); + when(mockLocation.getWorld()).thenReturn(mockWorld); + when(mockLocation.getBlockX()).thenReturn(0); + when(mockLocation.getBlockY()).thenReturn(0); + when(mockLocation.getBlockZ()).thenReturn(0); + + String actual = toXYZW(mockLocation); + + assertEquals(expected, actual); + + } + + @Test + public void test_sucecss_toXYZW_null() { + + this.mockLocation = null; + + String actual = toXYZW(mockLocation); + + assertEquals(INVALID_LOCATION, actual); + + } +} \ No newline at end of file diff --git a/src/test/java/dev/drawethree/xprison/utils/ProgressBarTest.java b/src/test/java/dev/drawethree/xprison/utils/ProgressBarTest.java new file mode 100644 index 0000000..52fc181 --- /dev/null +++ b/src/test/java/dev/drawethree/xprison/utils/ProgressBarTest.java @@ -0,0 +1,79 @@ +package dev.drawethree.xprison.utils; + + +import org.junit.jupiter.api.Test; + +import static dev.drawethree.xprison.utils.misc.ProgressBar.getProgressBar; +import static dev.drawethree.xprison.utils.text.TextUtils.applyColor; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ProgressBarTest { + + @Test + public void success_no_progress() { + String delimeter = ":"; + int numberOfDelimeters = 20; + double required = 41412199.0; + double current = 0.0; + + String expected = "&a&c::::::::::::::::::::"; + String actual = getProgressBar(numberOfDelimeters, delimeter, current, required); + + assertEquals(applyColor(expected), actual); + } + + @Test + public void success_100_percent() { + String delimeter = ":"; + int numberOfDelimeters = 20; + double required = 1.0; + double current = 1.0; + + + String expected = "&a::::::::::::::::::::&c"; + String actual = getProgressBar(numberOfDelimeters, delimeter, current, required); + + assertEquals(applyColor(expected), actual); + } + + @Test + public void success_no_delimeter() { + + int numberOfDelimeters = 20; + double required = 428919.0; + double current = 4244.0; + + String expected = "&a&c::::::::::::::::::::"; + String actual = getProgressBar(numberOfDelimeters, null, current, required); + assertEquals(applyColor(expected), actual); + } + + + @Test + public void success_50_percent() { + String delimeter = "|"; + int numberOfDelimeters = 50; + double required = 41851429.0; + double current = required / 2.0; + + + String expected = "&a|||||||||||||||||||||||||&c|||||||||||||||||||||||||"; + + String actual = getProgressBar(numberOfDelimeters, delimeter, current, required); + assertEquals(applyColor(expected), actual); + } + + @Test + public void success_almost_50_percent() { + String delimeter = "|"; + int numberOfDelimeters = 50; + double required = 41904129.0; + double current = (required / 2.0) - 1.0; + + + String expected = "&a||||||||||||||||||||||||&c||||||||||||||||||||||||||"; + + String actual = getProgressBar(numberOfDelimeters, delimeter, current, required); + assertEquals(applyColor(expected), actual); + } +} \ No newline at end of file diff --git a/src/test/java/dev/drawethree/xprison/utils/misc/MathUtilsTest.java b/src/test/java/dev/drawethree/xprison/utils/misc/MathUtilsTest.java new file mode 100644 index 0000000..4555f64 --- /dev/null +++ b/src/test/java/dev/drawethree/xprison/utils/misc/MathUtilsTest.java @@ -0,0 +1,38 @@ +package dev.drawethree.xprison.utils.misc; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static dev.drawethree.xprison.utils.misc.MathUtils.formatNumber; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MathUtilsTest { + + private Map numbersToTest; + + @BeforeEach + public void before() { + this.numbersToTest = new HashMap<>(); + + this.numbersToTest.put(500.0, "500.0"); + this.numbersToTest.put(1000.0, "1000.0"); + this.numbersToTest.put(5000.0, "5.0k"); + this.numbersToTest.put(5500.0, "5.5k"); + + this.numbersToTest.put(-500.0, "-500.0"); + this.numbersToTest.put(-1000.0, "-1000.0"); + this.numbersToTest.put(-5000.0, "-5.0k"); + this.numbersToTest.put(-5500.0, "-5.5k"); + } + + @Test + public void formatNumbers() { + for (Map.Entry entry : this.numbersToTest.entrySet()) { + assertEquals(entry.getValue(), formatNumber(entry.getKey())); + } + } +} \ No newline at end of file diff --git a/src/test/java/dev/drawethree/xprison/utils/misc/NumberUtilsTest.java b/src/test/java/dev/drawethree/xprison/utils/misc/NumberUtilsTest.java new file mode 100644 index 0000000..b97f6fa --- /dev/null +++ b/src/test/java/dev/drawethree/xprison/utils/misc/NumberUtilsTest.java @@ -0,0 +1,39 @@ +package dev.drawethree.xprison.utils.misc; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class NumberUtilsTest { + + @Test + public void test_lower1() { + String input = "1000000"; + boolean result = NumberUtils.isLongGreaterThanMaxLong(input); + assertFalse(result); + } + + @Test + public void test_lower2() { + long number1 = Long.MAX_VALUE - 100; + long number2 = 10; + boolean result = NumberUtils.wouldAdditionBeOverMaxLong(number1, number2); + assertFalse(result); + } + + @Test + public void test_greater() { + String input = Long.MAX_VALUE + "1"; + boolean result = NumberUtils.isLongGreaterThanMaxLong(input); + assertTrue(result); + } + + @Test + public void test_greater2() { + long number1 = Long.MAX_VALUE; + long number2 = 10; + boolean result = NumberUtils.wouldAdditionBeOverMaxLong(number1, number2); + assertTrue(result); + } +} \ No newline at end of file