diff --git a/build.gradle b/build.gradle index 0f79edc..58636b2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,11 @@ plugins { id 'java' - id "io.freefair.lombok" version "3.8.0" + id "io.freefair.lombok" version "4.1.6" id 'maven' } group 'sh.okx' -version '3.5.8' +version '3.6-beta' repositories { mavenCentral() @@ -29,7 +29,7 @@ repositories { dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' - compile 'org.spigotmc:spigot-api:1.14.4-R0.1-SNAPSHOT' + compile 'org.spigotmc:spigot-api:1.15.1-R0.1-SNAPSHOT' compile('com.github.Realizedd:TokenManager:3.2.4') { transitive = false } @@ -39,7 +39,7 @@ dependencies { } compile 'me.clip:placeholderapi:2.9.2' compile 'com.hm.achievement:advanced-achievements-api:1.1.0' - compile ('com.github.mcMMO-Dev:mcMMO:ac8042f') { + compile ('com.github.mcMMO-Dev:mcMMO:601297') { exclude group: 'com.sk89q.worldguard' } compile 'com.github.Ben12345rocks:VotingPlugin:5.18.2' diff --git a/src/main/java/sh/okx/rankup/JoinUpdateNotifier.java b/src/main/java/sh/okx/rankup/JoinUpdateNotifier.java new file mode 100644 index 0000000..e512801 --- /dev/null +++ b/src/main/java/sh/okx/rankup/JoinUpdateNotifier.java @@ -0,0 +1,29 @@ +package sh.okx.rankup; + +import java.util.function.Supplier; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import sh.okx.rankup.util.UpdateNotifier; + +public class JoinUpdateNotifier implements Listener { + private final UpdateNotifier notifier; + private final Supplier enabledSupplier; + + public JoinUpdateNotifier(UpdateNotifier notifier, + Supplier enabledSupplier) { + this.notifier = notifier; + this.enabledSupplier = enabledSupplier; + } + + @EventHandler + public void on(PlayerJoinEvent e) { + if (enabledSupplier.get()) { + Player player = e.getPlayer(); + if (player.hasPermission("rankup.notify")) { + notifier.notify(player, true); + } + } + } +} diff --git a/src/main/java/sh/okx/rankup/Rankup.java b/src/main/java/sh/okx/rankup/Rankup.java index b60917f..c2cdb23 100644 --- a/src/main/java/sh/okx/rankup/Rankup.java +++ b/src/main/java/sh/okx/rankup/Rankup.java @@ -1,5 +1,11 @@ package sh.okx.rankup; +import java.io.File; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; import lombok.Getter; import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.permission.Permission; @@ -22,38 +28,52 @@ import sh.okx.rankup.commands.RanksCommand; import sh.okx.rankup.commands.RankupCommand; import sh.okx.rankup.gui.Gui; import sh.okx.rankup.gui.GuiListener; -import sh.okx.rankup.messages.NullMessageBuilder; import sh.okx.rankup.messages.Message; import sh.okx.rankup.messages.MessageBuilder; +import sh.okx.rankup.messages.NullMessageBuilder; import sh.okx.rankup.messages.Variable; import sh.okx.rankup.placeholders.Placeholders; import sh.okx.rankup.prestige.Prestige; import sh.okx.rankup.prestige.Prestiges; import sh.okx.rankup.ranks.Rank; import sh.okx.rankup.ranks.Rankups; -import sh.okx.rankup.requirements.DeductibleRequirement; -import sh.okx.rankup.requirements.NonDeductibleRequirement; -import sh.okx.rankup.requirements.ProgressiveRequirement; import sh.okx.rankup.requirements.Requirement; import sh.okx.rankup.requirements.RequirementRegistry; -import sh.okx.rankup.requirements.requirement.*; +import sh.okx.rankup.requirements.XpLevelDeductibleRequirement; +import sh.okx.rankup.requirements.requirement.BlockBreakRequirement; +import sh.okx.rankup.requirements.requirement.CraftItemRequirement; +import sh.okx.rankup.requirements.requirement.GroupRequirement; +import sh.okx.rankup.requirements.requirement.ItemDeductibleRequirement; +import sh.okx.rankup.requirements.requirement.ItemRequirement; +import sh.okx.rankup.requirements.requirement.MobKillsRequirement; +import sh.okx.rankup.requirements.requirement.MoneyDeductibleRequirement; +import sh.okx.rankup.requirements.requirement.MoneyRequirement; +import sh.okx.rankup.requirements.requirement.PermissionRequirement; +import sh.okx.rankup.requirements.requirement.PlaceholderRequirement; +import sh.okx.rankup.requirements.requirement.PlayerKillsRequirement; +import sh.okx.rankup.requirements.requirement.PlaytimeMinutesRequirement; +import sh.okx.rankup.requirements.requirement.TokensDeductibleRequirement; +import sh.okx.rankup.requirements.requirement.TotalMobKillsRequirement; +import sh.okx.rankup.requirements.requirement.UseItemRequirement; +import sh.okx.rankup.requirements.requirement.WorldRequirement; import sh.okx.rankup.requirements.requirement.XpLevelRequirement; import sh.okx.rankup.requirements.requirement.advancedachievements.AdvancedAchievementsAchievementRequirement; import sh.okx.rankup.requirements.requirement.advancedachievements.AdvancedAchievementsTotalRequirement; import sh.okx.rankup.requirements.requirement.mcmmo.McMMOPowerLevelRequirement; import sh.okx.rankup.requirements.requirement.mcmmo.McMMOSkillRequirement; import sh.okx.rankup.requirements.requirement.tokenmanager.TokensRequirement; -import sh.okx.rankup.requirements.requirement.towny.*; +import sh.okx.rankup.requirements.requirement.towny.TownyKingNumberResidentsRequirement; +import sh.okx.rankup.requirements.requirement.towny.TownyKingNumberTownsRequirement; +import sh.okx.rankup.requirements.requirement.towny.TownyKingRequirement; +import sh.okx.rankup.requirements.requirement.towny.TownyMayorNumberResidentsRequirement; +import sh.okx.rankup.requirements.requirement.towny.TownyMayorRequirement; +import sh.okx.rankup.requirements.requirement.towny.TownyResidentRequirement; import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginVotesRequirement; - -import java.io.File; -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; +import sh.okx.rankup.util.UpdateNotifier; +import sh.okx.rankup.util.VersionChecker; public class Rankup extends JavaPlugin { + @Getter private Permission permissions; @Getter @@ -77,9 +97,12 @@ public class Rankup extends JavaPlugin { private RankupHelper helper; private AutoRankup autoRankup; private String errorMessage; + private UpdateNotifier notifier; @Override public void onEnable() { + notifier = new UpdateNotifier(new VersionChecker(this)); + reload(true); Metrics metrics = new Metrics(this); @@ -105,8 +128,10 @@ public class Rankup extends JavaPlugin { } getCommand("rankup").setExecutor(new RankupCommand(this)); - getCommand("rankup3").setExecutor(new InfoCommand(this)); + getCommand("rankup3").setExecutor(new InfoCommand(this, notifier)); getServer().getPluginManager().registerEvents(new GuiListener(this), this); + getServer().getPluginManager().registerEvents( + new JoinUpdateNotifier(notifier, () -> getConfig().getBoolean("notify-update")), this); placeholders = new Placeholders(this); placeholders.register(); @@ -123,7 +148,7 @@ public class Rankup extends JavaPlugin { public void reload(boolean init) { errorMessage = null; - if(!setupPermissions()) { + if (!setupPermissions()) { errorMessage = "No permission plugin found"; } setupEconomy(); @@ -140,7 +165,7 @@ public class Rankup extends JavaPlugin { autoRankup.runTaskTimer(this, time, time); } - if (config.getInt("version") < 4) { + if (config.getInt("version") < 5) { getLogger().severe("You are using an outdated config!"); getLogger().severe("This means that some things might not work!"); getLogger().severe("To update, please rename ALL your config files (or the folder they are in),"); @@ -159,6 +184,7 @@ public class Rankup extends JavaPlugin { /** * Notify the player of an error if there is one + * * @return true if there was an error and action was taken */ public boolean error(CommandSender sender) { @@ -187,9 +213,8 @@ public class Rankup extends JavaPlugin { } /** - * Closes all rankup inventories on disable - * so players cannot grab items from the inventory - * on a plugin reload. + * Closes all rankup inventories on disable so players cannot grab items from the inventory on a + * plugin reload. */ private void closeInventories() { for (Player player : Bukkit.getOnlinePlayers()) { @@ -267,58 +292,62 @@ public class Rankup extends JavaPlugin { private void registerRequirements() { requirements = new RequirementRegistry(); - registerDeductible(new XpLevelRequirement(this)); - requirements.addRequirement(new PlaytimeMinutesRequirement(this)); - requirements.addRequirement(new GroupRequirement(this)); - requirements.addRequirement(new PermissionRequirement(this)); - requirements.addRequirement(new PlaceholderRequirement(this)); - requirements.addRequirement(new WorldRequirement(this)); - requirements.addRequirement(new BlockBreakRequirement(this)); - requirements.addRequirement(new PlayerKillsRequirement(this)); - requirements.addRequirement(new MobKillsRequirement(this)); - registerDeductible(new ItemRequirement(this)); - requirements.addRequirement(new UseItemRequirement(this)); - requirements.addRequirement(new TotalMobKillsRequirement(this)); - requirements.addRequirement(new CraftItemRequirement(this)); + requirements.addRequirements( + new XpLevelRequirement(this, "xp-levelh"), + new XpLevelDeductibleRequirement(this, "xp-level"), + new PlaytimeMinutesRequirement(this), + new GroupRequirement(this), + new PermissionRequirement(this), + new PlaceholderRequirement(this), + new WorldRequirement(this), + new BlockBreakRequirement(this), + new PlayerKillsRequirement(this), + new MobKillsRequirement(this), + new ItemRequirement(this, "itemh"), + new ItemDeductibleRequirement(this, "item"), + new UseItemRequirement(this), + new TotalMobKillsRequirement(this), + new CraftItemRequirement(this)); if (economy != null) { - registerDeductible(new MoneyRequirement(this)); + requirements.addRequirements( + new MoneyRequirement(this, "moneyh"), + new MoneyDeductibleRequirement(this, "money")); } PluginManager pluginManager = Bukkit.getPluginManager(); if (pluginManager.isPluginEnabled("mcMMO")) { - requirements.addRequirement(new McMMOSkillRequirement(this)); - requirements.addRequirement(new McMMOPowerLevelRequirement(this)); + requirements.addRequirements( + new McMMOSkillRequirement(this), + new McMMOPowerLevelRequirement(this)); } if (pluginManager.isPluginEnabled("AdvancedAchievements")) { - requirements.addRequirement(new AdvancedAchievementsAchievementRequirement(this)); - requirements.addRequirement(new AdvancedAchievementsTotalRequirement(this)); + requirements.addRequirements( + new AdvancedAchievementsAchievementRequirement(this), + new AdvancedAchievementsTotalRequirement(this)); } if (pluginManager.isPluginEnabled("VotingPlugin")) { - requirements.addRequirement(new VotingPluginVotesRequirement(this)); + requirements.addRequirements( + new VotingPluginVotesRequirement(this)); } if (Bukkit.getPluginManager().isPluginEnabled("Towny")) { - requirements.addRequirement(new TownyResidentRequirement(this)); - requirements.addRequirement(new TownyMayorRequirement(this)); - requirements.addRequirement(new TownyMayorNumberResidentsRequirement(this)); - requirements.addRequirement(new TownyKingRequirement(this)); - requirements.addRequirement(new TownyKingNumberResidentsRequirement(this)); - requirements.addRequirement(new TownyKingNumberTownsRequirement(this)); + requirements.addRequirements( + new TownyResidentRequirement(this), + new TownyMayorRequirement(this), + new TownyMayorNumberResidentsRequirement(this), + new TownyKingRequirement(this), + new TownyKingNumberResidentsRequirement(this), + new TownyKingNumberTownsRequirement(this)); } if (Bukkit.getPluginManager().isPluginEnabled("TokenManager")) { - registerDeductible(new TokensRequirement(this)); + requirements.addRequirements( + new TokensRequirement(this, "tokenmanager-tokensh"), + new TokensDeductibleRequirement(this, "tokenmanager-tokens")); } } - private void registerDeductible(ProgressiveRequirement requirement) { - if (!(requirement instanceof DeductibleRequirement)) { - throw new IllegalArgumentException("Requirement is not DeductibleRequirement"); - } - requirements.addRequirement(requirement); - requirements.addRequirement(new NonDeductibleRequirement(requirement, requirement.getName() + "h")); - } - private boolean setupPermissions() { - RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Permission.class); + RegisteredServiceProvider rsp = getServer().getServicesManager() + .getRegistration(Permission.class); if (rsp == null) { return false; } @@ -327,7 +356,8 @@ public class Rankup extends JavaPlugin { } private void setupEconomy() { - RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + RegisteredServiceProvider rsp = getServer().getServicesManager() + .getRegistration(Economy.class); if (rsp != null) { economy = rsp.getProvider(); } else { @@ -363,7 +393,8 @@ public class Rankup extends JavaPlugin { return MessageBuilder.of(messages, message); } - public MessageBuilder replaceMoneyRequirements(MessageBuilder builder, CommandSender sender, Rank rank) { + public MessageBuilder replaceMoneyRequirements(MessageBuilder builder, CommandSender sender, + Rank rank) { if (builder instanceof NullMessageBuilder) { return builder; } @@ -394,15 +425,20 @@ public class Rankup extends JavaPlugin { DecimalFormat percentFormat = placeholders.getPercentFormat(); for (Requirement requirement : rank.getRequirements()) { try { - replaceRequirements(builder, Variable.AMOUNT, requirement, () -> simpleFormat.format(requirement.getTotal(player))); + replaceRequirements(builder, Variable.AMOUNT, requirement, + () -> simpleFormat.format(requirement.getTotal(player))); if (rank.isIn(player)) { - replaceRequirements(builder, Variable.AMOUNT_NEEDED, requirement, () -> simpleFormat.format(requirement.getRemaining(player))); + replaceRequirements(builder, Variable.AMOUNT_NEEDED, requirement, + () -> simpleFormat.format(requirement.getRemaining(player))); replaceRequirements(builder, Variable.PERCENT_LEFT, requirement, - () -> percentFormat.format(Math.max(0, (requirement.getRemaining(player) / requirement.getTotal(player)) * 100))); + () -> percentFormat.format(Math.max(0, + (requirement.getRemaining(player) / requirement.getTotal(player)) * 100))); replaceRequirements(builder, Variable.PERCENT_DONE, requirement, - () -> percentFormat.format(Math.min(100, (1 - (requirement.getRemaining(player) / requirement.getTotal(player))) * 100))); + () -> percentFormat.format(Math.min(100, + (1 - (requirement.getRemaining(player) / requirement.getTotal(player))) * 100))); replaceRequirements(builder, Variable.AMOUNT_DONE, requirement, - () -> simpleFormat.format(requirement.getTotal(player) - requirement.getRemaining(player))); + () -> simpleFormat + .format(requirement.getTotal(player) - requirement.getRemaining(player))); } } catch (NumberFormatException ignored) { } @@ -410,7 +446,8 @@ public class Rankup extends JavaPlugin { return builder; } - private void replaceRequirements(MessageBuilder builder, Variable variable, Requirement requirement, Supplier value) { + private void replaceRequirements(MessageBuilder builder, Variable variable, + Requirement requirement, Supplier value) { Object get; try { get = value.get(); @@ -420,7 +457,8 @@ public class Rankup extends JavaPlugin { } } - public MessageBuilder getMessage(CommandSender player, Message message, Rank oldRank, String rankName) { + public MessageBuilder getMessage(CommandSender player, Message message, Rank oldRank, + String rankName) { String oldRankName; if (oldRank instanceof Prestige && oldRank.getRank() == null) { oldRankName = ((Prestige) oldRank).getFrom(); @@ -448,9 +486,4 @@ public class Rankup extends JavaPlugin { } builder.send(sender); } - - public boolean isLegacy() { - String version = Bukkit.getVersion(); - return !(version.contains("1.13") || version.contains("1.14") || version.contains("1.15")); - } } diff --git a/src/main/java/sh/okx/rankup/RankupHelper.java b/src/main/java/sh/okx/rankup/RankupHelper.java index 9f81ce8..dd5f9dd 100644 --- a/src/main/java/sh/okx/rankup/RankupHelper.java +++ b/src/main/java/sh/okx/rankup/RankupHelper.java @@ -13,7 +13,12 @@ import sh.okx.rankup.ranks.Rankups; import java.util.HashMap; import java.util.Map; +/** + * Actually performs the ranking up and prestiging for the plugin and also manages the cooldowns + * between ranking up. + */ public class RankupHelper { + private final Rankup plugin; private final ConfigurationSection config; private final Permission permissions; @@ -28,9 +33,8 @@ public class RankupHelper { this.permissions = plugin.getPermissions(); } - private void doRankup(Player player, Rank rank) { + public void doRankup(Player player, Rank rank) { rank.runCommands(player); - applyCooldown(player); if (rank.getRank() != null) { permissions.playerRemoveGroup(null, player, rank.getRank()); @@ -38,6 +42,42 @@ public class RankupHelper { permissions.playerAddGroup(null, player, rank.getNext()); } + public void sendRankupMessages(Player player, Rank rank) { + plugin.getMessage(rank, Message.SUCCESS_PUBLIC) + .failIfEmpty() + .replaceRanks(player, rank, rank.getNext()) + .broadcast(); + plugin.getMessage(rank, Message.SUCCESS_PRIVATE) + .failIfEmpty() + .replaceRanks(player, rank, rank.getNext()) + .send(player); + } + + public void doPrestige(Player player, Prestige prestige) { + prestige.runCommands(player); + + permissions.playerRemoveGroup(null, player, prestige.getFrom()); + permissions.playerAddGroup(null, player, prestige.getTo()); + + if (prestige.getRank() != null) { + permissions.playerRemoveGroup(null, player, prestige.getRank()); + } + permissions.playerAddGroup(null, player, prestige.getNext()); + } + + public void sendPrestigeMessages(Player player, Prestige prestige) { + plugin.getMessage(prestige, Message.PRESTIGE_SUCCESS_PUBLIC) + .failIfEmpty() + .replaceRanks(player, prestige, prestige.getNext()) + .replaceFromTo(prestige) + .broadcast(); + plugin.getMessage(prestige, Message.PRESTIGE_SUCCESS_PRIVATE) + .failIfEmpty() + .replaceRanks(player, prestige, prestige.getNext()) + .replaceFromTo(prestige) + .send(player); + } + private boolean checkCooldown(Player player, Rank rank) { if (cooldowns.containsKey(player)) { long time = System.currentTimeMillis() - cooldowns.get(player); @@ -46,7 +86,8 @@ public class RankupHelper { long timeLeft = (cooldownSeconds * 1000) - time; if (timeLeft > 0) { long secondsLeft = (long) Math.ceil(timeLeft / 1000f); - plugin.getMessage(rank, secondsLeft > 1 ? Message.COOLDOWN_PLURAL : Message.COOLDOWN_SINGULAR) + plugin + .getMessage(rank, secondsLeft > 1 ? Message.COOLDOWN_PLURAL : Message.COOLDOWN_SINGULAR) .failIfEmpty() .replaceRanks(player, rank.getRank()) .replaceFromTo(rank) @@ -74,16 +115,10 @@ public class RankupHelper { Rank rank = plugin.getRankups().getByPlayer(player); rank.applyRequirements(player); - doRankup(player, rank); + applyCooldown(player); - plugin.getMessage(rank, Message.SUCCESS_PUBLIC) - .failIfEmpty() - .replaceRanks(player, rank, rank.getNext()) - .broadcast(); - plugin.getMessage(rank, Message.SUCCESS_PRIVATE) - .failIfEmpty() - .replaceRanks(player, rank, rank.getNext()) - .send(player); + doRankup(player, rank); + sendRankupMessages(player, rank); } public boolean checkRankup(Player player) { @@ -91,8 +126,7 @@ public class RankupHelper { } /** - * Checks if a player can rankup, - * and if they can't, sends the player a message and returns false + * Checks if a player can rankup, and if they can't, sends the player a message and returns false * * @param player the player to check if they can rankup * @return true if the player can rankup, false otherwise @@ -102,7 +136,8 @@ public class RankupHelper { Rank rank = rankups.getByPlayer(player); if (rankups.isLast(permissions, player)) { Prestiges prestiges = plugin.getPrestiges(); - plugin.getMessage(prestiges == null ? Message.NO_RANKUP : prestiges.isLast(permissions, player) ? Message.NO_RANKUP : Message.MUST_PRESTIGE) + plugin.getMessage(prestiges == null ? Message.NO_RANKUP + : prestiges.isLast(permissions, player) ? Message.NO_RANKUP : Message.MUST_PRESTIGE) .failIf(!message) .replaceRanks(player, rankups.getLast()) .send(player); @@ -135,27 +170,9 @@ public class RankupHelper { Prestige prestige = plugin.getPrestiges().getByPlayer(player); prestige.applyRequirements(player); - prestige.runCommands(player); applyCooldown(player); - - permissions.playerRemoveGroup(null, player, prestige.getFrom()); - permissions.playerAddGroup(null, player, prestige.getTo()); - - if (prestige.getRank() != null) { - permissions.playerRemoveGroup(null, player, prestige.getRank()); - } - permissions.playerAddGroup(null, player, prestige.getNext()); - - plugin.getMessage(prestige, Message.PRESTIGE_SUCCESS_PUBLIC) - .failIfEmpty() - .replaceRanks(player, prestige, prestige.getNext()) - .replaceFromTo(prestige) - .broadcast(); - plugin.getMessage(prestige, Message.PRESTIGE_SUCCESS_PRIVATE) - .failIfEmpty() - .replaceRanks(player, prestige, prestige.getNext()) - .replaceFromTo(prestige) - .send(player); + doPrestige(player, prestige); + sendPrestigeMessages(player, prestige); } public boolean checkPrestige(Player player) { @@ -171,7 +188,8 @@ public class RankupHelper { .replace(Variable.PLAYER, player.getName()) .send(player); return false; - } else if (prestiges.isLast(plugin.getPermissions(), player)) { // check if they are at the highest rank + } else if (prestiges + .isLast(plugin.getPermissions(), player)) { // check if they are at the highest rank plugin.getMessage(prestige, Message.PRESTIGE_NO_PRESTIGE) .failIf(!message) .replaceRanks(player, prestige.getRank()) @@ -179,9 +197,10 @@ public class RankupHelper { .send(player); return false; } else if (!prestige.hasRequirements(player)) { // check if they can afford it - plugin.replaceMoneyRequirements(plugin.getMessage(prestige, Message.PRESTIGE_REQUIREMENTS_NOT_MET) - .failIf(!message) - .replaceRanks(player, prestige, prestiges.next(prestige).getRank()), player, prestige) + plugin.replaceMoneyRequirements( + plugin.getMessage(prestige, Message.PRESTIGE_REQUIREMENTS_NOT_MET) + .failIf(!message) + .replaceRanks(player, prestige, prestiges.next(prestige).getRank()), player, prestige) .replaceFromTo(prestige) .send(player); return false; diff --git a/src/main/java/sh/okx/rankup/commands/InfoCommand.java b/src/main/java/sh/okx/rankup/commands/InfoCommand.java index d0cee56..accc358 100644 --- a/src/main/java/sh/okx/rankup/commands/InfoCommand.java +++ b/src/main/java/sh/okx/rankup/commands/InfoCommand.java @@ -1,24 +1,29 @@ package sh.okx.rankup.commands; -import com.google.common.base.Charsets; -import com.google.common.io.CharStreams; -import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.bukkit.plugin.PluginDescriptionFile; import sh.okx.rankup.Rankup; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; +import sh.okx.rankup.prestige.Prestige; +import sh.okx.rankup.prestige.Prestiges; +import sh.okx.rankup.ranks.Rank; +import sh.okx.rankup.ranks.Rankups; +import sh.okx.rankup.util.UpdateNotifier; -@RequiredArgsConstructor public class InfoCommand implements CommandExecutor { private final Rankup plugin; - private String versionMessage; + + private final UpdateNotifier notifier; + + public InfoCommand(Rankup plugin, UpdateNotifier notifier) { + this.plugin = plugin; + this.notifier = notifier; + } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { @@ -29,6 +34,75 @@ public class InfoCommand implements CommandExecutor { sender.sendMessage(ChatColor.GREEN + "" + ChatColor.BOLD + "Rankup " + ChatColor.YELLOW + "Reloaded configuration files."); } return true; + } else if (args[0].equalsIgnoreCase("forcerankup") && sender.hasPermission("rankup.force")) { + if (args.length < 2) { + sender.sendMessage(ChatColor.YELLOW + "Usage: /" + label + " forcerankup "); + return true; + } + + Player player = Bukkit.getPlayer(args[1]); + if (player == null) { + sender.sendMessage(ChatColor.YELLOW + "Player not found."); + return true; + } + + Rankups rankups = plugin.getRankups(); + if (rankups.isLast(plugin.getPermissions(), player)) { + sender.sendMessage(ChatColor.YELLOW + "That player is at the last rank."); + return true; + } + + Rank rank = rankups.getByPlayer(player); + if (rank == null) { + sender.sendMessage(ChatColor.YELLOW + "That player is not in any rankup groups."); + return true; + } + + plugin.getHelper().doRankup(player, rank); + plugin.getHelper().sendRankupMessages(player, rank); + sender.sendMessage(ChatColor.GREEN + "Successfully forced " + + ChatColor.GOLD + player.getName() + + ChatColor.GREEN + " to rankup from " + ChatColor.GOLD + rank.getRank() + + ChatColor.GREEN + " to " + ChatColor.GOLD + rank.getNext()); + return true; + } else if (args[0].equalsIgnoreCase("forceprestige") && sender.hasPermission("rankup.force")) { + if (plugin.getPrestiges() == null) { + sender.sendMessage(ChatColor.RED + "Prestige is disabled."); + return true; + } + + if (args.length < 2) { + sender.sendMessage(ChatColor.YELLOW + "Usage: /" + label + " forceprestige "); + return true; + } + + Player player = Bukkit.getPlayer(args[1]); + if (player == null) { + sender.sendMessage(ChatColor.YELLOW + "Player not found."); + return true; + } + + Prestiges prestiges = plugin.getPrestiges(); + if (prestiges.isLast(plugin.getPermissions(), player)) { + sender.sendMessage(ChatColor.YELLOW + "That player is at the last prestige."); + return true; + } + + Prestige prestige = prestiges.getByPlayer(player); + if (prestige == null) { + sender.sendMessage(ChatColor.YELLOW + "That player is not in any prestige groups."); + return true; + } + + plugin.getHelper().doPrestige(player, prestige); + plugin.getHelper().sendPrestigeMessages(player, prestige); + sender.sendMessage(ChatColor.GREEN + "Successfully forced " + + ChatColor.GOLD + player.getName() + + ChatColor.GREEN + " to prestige " + + ChatColor.GOLD + prestige.getRank() + + ChatColor.GREEN + " from " + ChatColor.GOLD + prestige.getFrom() + + ChatColor.GREEN + " to " + ChatColor.GOLD + prestige.getTo()); + return true; } } @@ -39,41 +113,18 @@ public class InfoCommand implements CommandExecutor { ChatColor.YELLOW + " by " + ChatColor.BLUE + ChatColor.BOLD + String.join(", ", description.getAuthors())); if (sender.hasPermission("rankup.reload")) { sender.sendMessage(ChatColor.GREEN + "/" + label + " reload " + ChatColor.YELLOW + "Reloads configuration files."); - } - if (sender.hasPermission("rankup.checkversion")) { - if (versionMessage == null) { - if (version.contains("alpha") || version.contains("beta") || version.contains("rc")) { - versionMessage = ChatColor.YELLOW + "You are on a pre-release version."; - sender.sendMessage(versionMessage); - return true; - } - sender.sendMessage(ChatColor.YELLOW + "Checking version..."); - Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { - String message; - try { - String latest = getLatestVersion(); - if (version.equals(latest)) { - message = ChatColor.GREEN + "You are on the latest version."; - } else { - message = ChatColor.YELLOW + "A new version is available: " + ChatColor.GOLD + latest - + "\nhttps://www.spigotmc.org/resources/rankup.17933/"; - } - } catch (IOException e) { - message = ChatColor.RED + "Error while checking version."; - } - versionMessage = message; - Bukkit.getScheduler().runTask(plugin, () -> sender.sendMessage(versionMessage)); - }); - } else { - sender.sendMessage(versionMessage); + sender.sendMessage(ChatColor.GREEN + "/" + label + " forcerankup " + ChatColor.YELLOW + "Force a player to rankup, bypassing requirements."); + if (plugin.getPrestiges() != null) { + sender.sendMessage( + ChatColor.GREEN + "/" + label + " forceprestige " + ChatColor.YELLOW + + "Force a player to prestige, bypassing requirements."); } } + if (sender.hasPermission("rankup.checkversion")) { + notifier.notify(sender, false); + } + return true; } - - private String getLatestVersion() throws IOException { - URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=17933"); - return CharStreams.toString(new InputStreamReader(url.openStream(), Charsets.UTF_8)); - } } diff --git a/src/main/java/sh/okx/rankup/commands/RankupCommand.java b/src/main/java/sh/okx/rankup/commands/RankupCommand.java index 16483c2..32d0715 100644 --- a/src/main/java/sh/okx/rankup/commands/RankupCommand.java +++ b/src/main/java/sh/okx/rankup/commands/RankupCommand.java @@ -18,7 +18,7 @@ import java.util.WeakHashMap; @RequiredArgsConstructor public class RankupCommand implements CommandExecutor { // weak hash maps so players going offline are automatically removed. - // otherwise there is a potential (but small) memory leak. + // otherwise there is a potential (albeit small) memory leak. private final Map confirming = new WeakHashMap<>(); private final Rankup plugin; diff --git a/src/main/java/sh/okx/rankup/gui/Gui.java b/src/main/java/sh/okx/rankup/gui/Gui.java index 55ca1da..1e35994 100644 --- a/src/main/java/sh/okx/rankup/gui/Gui.java +++ b/src/main/java/sh/okx/rankup/gui/Gui.java @@ -21,6 +21,7 @@ import sh.okx.rankup.ranks.Rank; import java.util.Arrays; import java.util.Objects; import java.util.stream.Collectors; +import sh.okx.rankup.util.ItemUtil; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class Gui implements InventoryHolder { @@ -66,20 +67,20 @@ public class Gui implements InventoryHolder { String materialName = section.getString("material").toUpperCase(); // handle default material correctly on older versions - if (plugin.isLegacy() && materialName.equals("BLACK_STAINED_GLASS_PANE")) { + if (!ItemUtil.isServerFlattened() && materialName.equals("BLACK_STAINED_GLASS_PANE")) { materialName = "STAINED_GLASS_PANE:15"; } ItemStack item; - if (plugin.isLegacy()) { + if (ItemUtil.isServerFlattened()) { + Material material = Material.valueOf(materialName); + item = new ItemStack(material); + } else { String[] parts = materialName.split(":"); Material material = Material.valueOf(parts[0]); short type = parts.length > 1 ? Short.parseShort(parts[1]) : 0; item = new ItemStack(material, 1, type); - } else { - Material material = Material.valueOf(materialName); - item = new ItemStack(material); } if (item.getType() == Material.AIR && section.getName().equalsIgnoreCase("fill")) { diff --git a/src/main/java/sh/okx/rankup/requirements/NonDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/NonDeductibleRequirement.java deleted file mode 100644 index c78de55..0000000 --- a/src/main/java/sh/okx/rankup/requirements/NonDeductibleRequirement.java +++ /dev/null @@ -1,30 +0,0 @@ -package sh.okx.rankup.requirements; - -import org.bukkit.entity.Player; - -/** - * Proxy requirement for a deductible requirement that is exactly the same but is not deductible - */ -public class NonDeductibleRequirement extends ProgressiveRequirement { - private final ProgressiveRequirement requirement; - - public NonDeductibleRequirement(ProgressiveRequirement requirement, String name) { - super(requirement.plugin, name, requirement.hasSubRequirement()); - this.requirement = requirement; - } - - protected NonDeductibleRequirement(NonDeductibleRequirement clone) { - super(clone); - this.requirement = clone.requirement; - } - - @Override - public double getProgress(Player player) { - return requirement.getProgress(player); - } - - @Override - public Requirement clone() { - return new NonDeductibleRequirement(this); - } -} diff --git a/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java b/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java index 5279289..faa991f 100644 --- a/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java +++ b/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java @@ -1,5 +1,6 @@ package sh.okx.rankup.requirements; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -12,6 +13,10 @@ public class RequirementRegistry { requirements.add(requirement); } + public void addRequirements(Requirement... requirements) { + Collections.addAll(this.requirements, requirements); + } + public Requirement newRequirement(String name, String value) { for (Requirement requirement : requirements) { if (requirement.getName().equalsIgnoreCase(name)) { diff --git a/src/main/java/sh/okx/rankup/requirements/XpLevelDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/XpLevelDeductibleRequirement.java new file mode 100644 index 0000000..5b207c8 --- /dev/null +++ b/src/main/java/sh/okx/rankup/requirements/XpLevelDeductibleRequirement.java @@ -0,0 +1,26 @@ +package sh.okx.rankup.requirements; + +import org.bukkit.entity.Player; +import sh.okx.rankup.Rankup; +import sh.okx.rankup.requirements.requirement.XpLevelRequirement; + +public class XpLevelDeductibleRequirement extends XpLevelRequirement implements DeductibleRequirement { + + public XpLevelDeductibleRequirement(Rankup plugin, String name) { + super(plugin, name); + } + + private XpLevelDeductibleRequirement(XpLevelDeductibleRequirement clone) { + super(clone); + } + + @Override + public void apply(Player player, double multiplier) { + player.setLevel(player.getLevel() - (int) Math.round(getValueInt() * multiplier)); + } + + @Override + public Requirement clone() { + return new XpLevelDeductibleRequirement(this); + } +} diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java new file mode 100644 index 0000000..e49cb71 --- /dev/null +++ b/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java @@ -0,0 +1,33 @@ +package sh.okx.rankup.requirements.requirement; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import sh.okx.rankup.Rankup; +import sh.okx.rankup.requirements.DeductibleRequirement; +import sh.okx.rankup.requirements.Requirement; + +public class ItemDeductibleRequirement extends ItemRequirement implements DeductibleRequirement { + + public ItemDeductibleRequirement(Rankup plugin, String name) { + super(plugin, name); + } + + public ItemDeductibleRequirement(ItemDeductibleRequirement clone) { + super(clone); + } + + @Override + public void apply(Player player, double multiplier) { + Material type = Material.matchMaterial(getSub()); + if (type == null) { + throw new IllegalArgumentException("Invalid item " + getSub()); + } + player.getInventory().removeItem(new ItemStack(type, (int) (getValueInt() * multiplier))); + } + + @Override + public Requirement clone() { + return new ItemDeductibleRequirement(this); + } +} diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java index 503729a..d7d1d93 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java @@ -1,7 +1,6 @@ package sh.okx.rankup.requirements.requirement; import org.bukkit.Material; -import org.bukkit.Statistic; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import sh.okx.rankup.Rankup; @@ -11,24 +10,15 @@ import sh.okx.rankup.requirements.Requirement; import java.util.Arrays; -public class ItemRequirement extends ProgressiveRequirement implements DeductibleRequirement { - public ItemRequirement(Rankup plugin) { - super(plugin, "item", true); +public class ItemRequirement extends ProgressiveRequirement { + public ItemRequirement(Rankup plugin, String name) { + super(plugin, name, true); } protected ItemRequirement(ItemRequirement clone) { super(clone); } - @Override - public void apply(Player player, double multiplier) { - Material type = Material.matchMaterial(getSub()); - if (type == null) { - throw new IllegalArgumentException("Invalid item " + getSub()); - } - player.getInventory().removeItem(new ItemStack(type, (int) (getValueInt() * multiplier))); - } - @Override public Requirement clone() { return new ItemRequirement(this); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/MoneyDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/MoneyDeductibleRequirement.java new file mode 100644 index 0000000..b42942f --- /dev/null +++ b/src/main/java/sh/okx/rankup/requirements/requirement/MoneyDeductibleRequirement.java @@ -0,0 +1,29 @@ +package sh.okx.rankup.requirements.requirement; + +import net.milkbowl.vault.economy.Economy; +import org.bukkit.entity.Player; +import sh.okx.rankup.Rankup; +import sh.okx.rankup.requirements.DeductibleRequirement; +import sh.okx.rankup.requirements.Requirement; + +public class MoneyDeductibleRequirement extends MoneyRequirement implements DeductibleRequirement { + + public MoneyDeductibleRequirement(Rankup plugin, String name) { + super(plugin, name); + } + + protected MoneyDeductibleRequirement(MoneyDeductibleRequirement clone) { + super(clone); + } + + @Override + public void apply(Player player, double multiplier) { + Economy economy = plugin.getEconomy(); + economy.withdrawPlayer(player, getValueDouble() * multiplier); + } + + @Override + public Requirement clone() { + return new MoneyDeductibleRequirement(this); + } +} diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/MoneyRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/MoneyRequirement.java index 5b3c3e6..713cac9 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/MoneyRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/MoneyRequirement.java @@ -1,27 +1,19 @@ package sh.okx.rankup.requirements.requirement; -import net.milkbowl.vault.economy.Economy; import org.bukkit.entity.Player; import sh.okx.rankup.Rankup; -import sh.okx.rankup.requirements.DeductibleRequirement; import sh.okx.rankup.requirements.ProgressiveRequirement; import sh.okx.rankup.requirements.Requirement; -public class MoneyRequirement extends ProgressiveRequirement implements DeductibleRequirement { - public MoneyRequirement(Rankup plugin) { - super(plugin, "money"); +public class MoneyRequirement extends ProgressiveRequirement { + public MoneyRequirement(Rankup plugin, String name) { + super(plugin, name); } - protected MoneyRequirement(Requirement clone) { + protected MoneyRequirement(MoneyRequirement clone) { super(clone); } - @Override - public void apply(Player player, double multiplier) { - Economy economy = plugin.getEconomy(); - economy.withdrawPlayer(player, getValueDouble() * multiplier); - } - @Override public double getProgress(Player player) { return plugin.getEconomy().getBalance(player); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/TokensDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/TokensDeductibleRequirement.java new file mode 100644 index 0000000..93084ef --- /dev/null +++ b/src/main/java/sh/okx/rankup/requirements/requirement/TokensDeductibleRequirement.java @@ -0,0 +1,26 @@ +package sh.okx.rankup.requirements.requirement; + +import org.bukkit.entity.Player; +import sh.okx.rankup.Rankup; +import sh.okx.rankup.requirements.DeductibleRequirement; +import sh.okx.rankup.requirements.requirement.tokenmanager.TokensRequirement; + +public class TokensDeductibleRequirement extends TokensRequirement implements DeductibleRequirement { + public TokensDeductibleRequirement(Rankup plugin, String name) { + super(plugin, name); + } + + protected TokensDeductibleRequirement(TokensDeductibleRequirement clone) { + super(clone); + } + + @Override + public void apply(Player player, double multiplier) { + manager.removeTokens(player, (long) (getValueInt() * multiplier)); + } + + @Override + public TokensRequirement clone() { + return new TokensDeductibleRequirement(this); + } +} diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/XpLevelRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/XpLevelRequirement.java index 2f409f9..0b56c1d 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/XpLevelRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/XpLevelRequirement.java @@ -2,24 +2,18 @@ package sh.okx.rankup.requirements.requirement; import org.bukkit.entity.Player; import sh.okx.rankup.Rankup; -import sh.okx.rankup.requirements.DeductibleRequirement; import sh.okx.rankup.requirements.ProgressiveRequirement; import sh.okx.rankup.requirements.Requirement; -public class XpLevelRequirement extends ProgressiveRequirement implements DeductibleRequirement { - public XpLevelRequirement(Rankup plugin) { - super(plugin, "xp-level"); +public class XpLevelRequirement extends ProgressiveRequirement { + public XpLevelRequirement(Rankup plugin, String name) { + super(plugin, name); } - protected XpLevelRequirement(Requirement clone) { + protected XpLevelRequirement(XpLevelRequirement clone) { super(clone); } - @Override - public void apply(Player player, double multiplier) { - player.setLevel(player.getLevel() - (int) Math.round(getValueInt() * multiplier)); - } - @Override public double getProgress(Player player) { return player.getLevel(); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/tokenmanager/TokensRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/tokenmanager/TokensRequirement.java index e1fef2c..0f105c7 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/tokenmanager/TokensRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/tokenmanager/TokensRequirement.java @@ -1,31 +1,23 @@ package sh.okx.rankup.requirements.requirement.tokenmanager; +import java.util.Objects; import me.realized.tokenmanager.api.TokenManager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import sh.okx.rankup.Rankup; -import sh.okx.rankup.requirements.DeductibleRequirement; import sh.okx.rankup.requirements.ProgressiveRequirement; -import sh.okx.rankup.requirements.Requirement; -import java.util.Objects; +public class TokensRequirement extends ProgressiveRequirement { + protected final TokenManager manager = (TokenManager) Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("TokenManager")); -public class TokensRequirement extends ProgressiveRequirement implements DeductibleRequirement { - private final TokenManager manager = (TokenManager) Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("TokenManager")); - - public TokensRequirement(Rankup plugin) { - super(plugin, "tokenmanager-tokens"); + public TokensRequirement(Rankup plugin, String name) { + super(plugin, name); } - private TokensRequirement(Requirement clone) { + protected TokensRequirement(TokensRequirement clone) { super(clone); } - @Override - public void apply(Player player, double multiplier) { - manager.removeTokens(player, (long) (getValueInt() * multiplier)); - } - @Override public double getProgress(Player player) { return manager.getTokens(player).orElse(0); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java index 6f294ef..4fddc9d 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java @@ -10,7 +10,7 @@ public class TownyKingNumberTownsRequirement extends ProgressiveRequirement { super(plugin, "towny-king-towns"); } - protected TownyKingNumberTownsRequirement(Requirement clone) { + protected TownyKingNumberTownsRequirement(TownyKingNumberTownsRequirement clone) { super(clone); } diff --git a/src/main/java/sh/okx/rankup/util/ItemUtil.java b/src/main/java/sh/okx/rankup/util/ItemUtil.java new file mode 100644 index 0000000..ceea15d --- /dev/null +++ b/src/main/java/sh/okx/rankup/util/ItemUtil.java @@ -0,0 +1,31 @@ +package sh.okx.rankup.util; + +import com.google.common.base.Suppliers; +import java.util.function.Supplier; +import org.bukkit.Material; + +public class ItemUtil { + private static Supplier flattenedSupplier = Suppliers.memoize(ItemUtil::isServerFlattenedPrivate); + + private static boolean isServerFlattenedPrivate() { + try { + Material.valueOf("BLACK_STAINED_GLASS_PANE"); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + /** + * Determines if a server is post-flattening or pre-flattening. + * The flattening is the name for a process where, instead of using durability to represent + * similar items, Mojang decided to use distinct item types for each item. + * This caused many {@link Material} names to change, making some things incompatible. + * The flattening happened in 1.13. + * + * @return true if the server is post-flattening (server versions 1.13, 1.14, 1.15) or false if it is pre-flattening (1.12, 1.11, 1.10 etc) + */ + public static boolean isServerFlattened() { + return flattenedSupplier.get(); + } +} diff --git a/src/main/java/sh/okx/rankup/util/UpdateNotifier.java b/src/main/java/sh/okx/rankup/util/UpdateNotifier.java new file mode 100644 index 0000000..aaaa170 --- /dev/null +++ b/src/main/java/sh/okx/rankup/util/UpdateNotifier.java @@ -0,0 +1,56 @@ +package sh.okx.rankup.util; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import sh.okx.rankup.util.VersionChecker.VersionCheckerCallback; + +public class UpdateNotifier { + private static final String PREFIX = ChatColor.GREEN + "" + ChatColor.BOLD + "Rankup " + ChatColor.RESET; + + private final VersionChecker checker; + + public UpdateNotifier(VersionChecker checker) { + this.checker = checker; + } + + public void notify(CommandSender sender, boolean join) { + if (!checker.hasChecked() && !join) { + send(sender, false, ChatColor.YELLOW + "Checking version..."); + } + checker.checkVersion(new VersionCheckerCallback() { + @Override + public void onLatestVersion(String version) { + if (!join) { + send(sender, false, ChatColor.GREEN + "You are on the latest version."); + } + } + + @Override + public void onOutdatedVersion(String currentVersion, String latestVersion) { + send(sender, join, ChatColor.YELLOW + "A new version is available: " + ChatColor.GOLD + latestVersion + + ChatColor.YELLOW + ". You are on: " + ChatColor.GOLD + currentVersion + + ChatColor.GOLD + "\nhttps://www.spigotmc.org/resources/rankup.17933/"); + } + + @Override + public void onPreReleaseVersion(String version) { + send(sender, join, ChatColor.RED + "You are on a pre-release version."); + } + + @Override + public void onFailure() { + if (!join) { + send(sender, join, ChatColor.RED + "Error while checking version."); + } + } + }); + } + + private void send(CommandSender sender, boolean prefix, String message) { + if (prefix) { + sender.sendMessage(PREFIX + message); + } else { + sender.sendMessage(message); + } + } +} diff --git a/src/main/java/sh/okx/rankup/util/VersionChecker.java b/src/main/java/sh/okx/rankup/util/VersionChecker.java new file mode 100644 index 0000000..b0c34d2 --- /dev/null +++ b/src/main/java/sh/okx/rankup/util/VersionChecker.java @@ -0,0 +1,144 @@ +package sh.okx.rankup.util; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +public class VersionChecker { + + private static final int RESOURCE_ID = 17933; + + private final Plugin plugin; // used exclusively for scheduling + private final String currentVersion; + private String latestVersion; + private boolean checked = false; + + public VersionChecker(Plugin plugin) { + this.currentVersion = plugin.getDescription().getVersion(); + this.plugin = plugin; + } + + /** + * Checks if the version checker has already made an asynchronous call to the web server to check + * the version, so future checks will run instantly. + * @return true if the version checker already knows the latest version, false otherwise + */ + public boolean hasChecked() { + return checked; + } + + public void checkVersion(VersionCheckerCallback callback) { + if (latestVersion != null) { + if (currentVersion.equals(latestVersion)) { + callback.onLatestVersion(currentVersion); + } else { + callback.onOutdatedVersion(currentVersion, latestVersion); + } + } else { + Bukkit.getScheduler().runTaskAsynchronously(plugin, + () -> checkVersionSync(new SyncVersionCheckerCallback(plugin, callback))); + } + } + + private void checkVersionSync(VersionCheckerCallback callback) { + if (currentVersion.contains("alpha") + || currentVersion.contains("beta") + || currentVersion.contains("rc")) { + checked = true; + callback.onPreReleaseVersion(currentVersion); + return; + } + + try { + latestVersion = getLatestVersion(); + checked = true; + if (currentVersion.equals(latestVersion)) { + callback.onLatestVersion(currentVersion); + } else { + callback.onOutdatedVersion(currentVersion, latestVersion); + } + + } catch (IOException e) { + callback.onFailure(); + } + } + + private String getLatestVersion() throws IOException { + URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID); + return CharStreams.toString(new InputStreamReader(url.openStream(), Charsets.UTF_8)); + } + + /** + * A callback used when a version check runs + */ + public interface VersionCheckerCallback { + + /** + * Called when the plugin is already on the latest version + * + * @param version the current, and latest, version of the plugin + */ + void onLatestVersion(String version); + + /** + * Called when the plugin is on a version other than the latest on the SpigotMC plugin page. + * + * @param currentVersion the current version of the plugin specified in plugin.yml + * @param latestVersion the latest version of the plugin specified on SpigotMC. + */ + void onOutdatedVersion(String currentVersion, String latestVersion); + + /** + * Called when the plugin is on a pre-release version and is exempt to the usual update system. + * @param version the current version of the plugin + */ + void onPreReleaseVersion(String version); + + /** + * Called when the version checker was unable to retrieve the latest version + */ + void onFailure(); + } + + /** + * An implementation of {@link VersionCheckerCallback} that is called asynchronously, and then + * forwards the calls an underlying VersionCheckerCallback synchronously on the main Bukkit thread. + */ + class SyncVersionCheckerCallback implements VersionCheckerCallback { + private final Plugin plugin; + private final VersionCheckerCallback callback; + + SyncVersionCheckerCallback(Plugin plugin, VersionCheckerCallback callback) { + this.plugin = plugin; + this.callback = callback; + } + + @Override + public void onLatestVersion(String version) { + doSync(() -> callback.onLatestVersion(version)); + } + + @Override + public void onOutdatedVersion(String currentVersion, String latestVersion) { + doSync(() -> callback.onOutdatedVersion(currentVersion, latestVersion)); + } + + @Override + public void onPreReleaseVersion(String version) { + doSync(() -> callback.onPreReleaseVersion(version)); + } + + @Override + public void onFailure() { + doSync(callback::onFailure); + } + + private void doSync(Runnable r) { + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, r); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index aea7970..bcb8541 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ # this is used for letting you know that you need to update/change your config file -version: 4 +version: 5 # the locale to use for messages # all messages can be customised but this allows you to @@ -21,16 +21,23 @@ ranks: true prestiges: true # whether or not /prestige and /prestiges should be enabled. -# you can alternatively negate the permission rankup.prestige -# this will also make the command not autocomplete in 1.13 +# when a player reaches the top rank, they can do /prestige to return to the first rank, +# but you will be able to grant them an additional "prestige" group or additional items. +# +# if you do not want this command to autocomplete, make sure +# you negate the permission rankup.prestige with your permissions plugin. # if enabled, a prestiges.yml file will be generated with some example prestiges prestige: false +# if true, players with the permission rankup.notify will receive notifications when they join +# to update if they are on an older version of Rankup. +notify-update: true + # how people should confirm ranking up # options are: gui, text or none confirmation-type: 'gui' -# how long, in seconds, people have to wait between a successful /rankup or a /prestige +# how long, in seconds, people have to wait between a successful /rankup or /prestige # set to 0 to disable. cooldown: 1 @@ -81,6 +88,8 @@ placeholders: # what to shorten money by. # ie 1000 -> 1k # set to an empty list to disable +# for each entry here, it counts as increasing by a factor of 1,000 +# the first represents thousands (1000, 1e3) then millions (1000000, 1e6) then billions (1000000000, 1e9) etc. shorten: - 'k' - 'M' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 369ec5f..32919dd 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -29,20 +29,26 @@ permissions: children: rankup.info: true rankup.rankup: true - rankup.checkversion: true + rankup.admin: true rankup.ranks: true - rankup.reload: true rankup.prestige: true rankup.prestiges: true rankup.auto: true + rankup.admin: + children: + # if a player can see if the plugin needs updating when they run /pru + rankup.checkversion: true + # if a player can run /pru reload + rankup.reload: true + # if a player can force rankup or prestige someone + rankup.force: true + # if a player receives notifications to update rankup when the log in. + rankup.notify: true + default: op rankup.info: default: true rankup.rankup: default: true - rankup.checkversion: - default: op - rankup.reload: - default: op rankup.ranks: default: true rankup.prestige: @@ -50,4 +56,4 @@ permissions: rankup.prestiges: default: true rankup.auto: - default: true + default: true \ No newline at end of file