package me.desht.scrollingmenusign; import me.desht.dhutils.LogUtils; import me.desht.dhutils.UUIDFetcher; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import java.util.*; /** * Represents the usage-limitation on SMS menus and menu items. */ public class SMSRemainingUses { private static final String GLOBAL_MAX = "&GLOBAL"; private static final String GLOBAL_REMAINING = "&GLOBALREMAINING"; private static final String PER_PLAYER_MAX = "&PERPLAYER"; private int globalMax = 0; private int globalRemaining = 0; private int perPlayerMax = 0; private final Map<UUID, Integer> uses = new HashMap<UUID, Integer>(); private final Map<String, Integer> oldUses = new HashMap<String, Integer>(); private final SMSUseLimitable parentObject; SMSRemainingUses(SMSUseLimitable lim) { this.parentObject = lim; } SMSRemainingUses(SMSUseLimitable lim, ConfigurationSection node) { this.parentObject = lim; if (node == null) return; if (!node.getKeys(false).isEmpty() && !node.contains("limits")) { migrateOldFormatData(node); } else { globalMax = node.getInt("limits.globalMax", 0); globalRemaining = node.getInt("limits.globalRemaining", 0); perPlayerMax = node.getInt("limits.perPlayerMax", 0); ConfigurationSection cs = node.getConfigurationSection("players"); if (cs != null) { for (String id : cs.getKeys(false)) { uses.put(UUID.fromString(id), cs.getInt(id)); } } } } /** * Check if this item has usage limits (either global or per-player) in place. * * @return true if there are limitations; false otherwise */ public boolean hasLimitedUses() { return globalMax > 0 || perPlayerMax > 0; } /** * Get the remaining uses for the player. * * @param playerName the player's name * @return the remaining uses * @deprecated use {@link #getRemainingUses(org.bukkit.OfflinePlayer)} */ @Deprecated public int getRemainingUses(String playerName) { @SuppressWarnings("deprecation") Player player = Bukkit.getPlayer(playerName); return player == null ? 0 : getRemainingUses(player); } /** * Get the remaining uses for the given player. * * @param player the player to check for * @return the number of uses remaining, or Integer.MAX_VALUE if there is no limit */ public int getRemainingUses(OfflinePlayer player) { if (globalMax > 0) { return globalRemaining; } else if (perPlayerMax > 0) { Integer uses = this.uses.get(player.getUniqueId()); return uses == null ? perPlayerMax : uses; } else { return Integer.MAX_VALUE; } } /** * Clear all usage limits for this item, for all players. */ public void clearUses() { uses.clear(); perPlayerMax = globalMax = globalRemaining = 0; autosave(); } /** * Clear usage limits for the given player. * * @param playerName The player name to remove usage limits for * @deprecated use {@link #clearUses(org.bukkit.OfflinePlayer)} */ @Deprecated public void clearUses(String playerName) { @SuppressWarnings("deprecation") Player player = Bukkit.getPlayer(playerName); if (player != null) { clearUses(player); } } /** * Reset the usage count for the given player. This does not remove the * usage limitation, so subsequent uses of the item will decrement the usage * count again. * * @param player the player */ public void clearUses(OfflinePlayer player) { uses.remove(player.getUniqueId()); autosave(); } /** * Set the usage limits per player. This is the total number of times an * item or menu can be used by each player. * * @param useCount The usage limit */ public void setUses(int useCount) { uses.clear(); globalMax = globalRemaining = 0; perPlayerMax = useCount; autosave(); } /** * Set the global usage limit. This is the total number of times an item/menu can be * used by any player. * * @param useCount the usage count */ public void setGlobalUses(int useCount) { uses.clear(); globalMax = globalRemaining = useCount; perPlayerMax = 0; autosave(); } /** * Record a usage event against this item. * * @param playerName name of the player who used the menu/item * @throws SMSException if there are not enough uses remaining * @deprecated use {@link #use(org.bukkit.OfflinePlayer)} */ @Deprecated public void use(String playerName) { @SuppressWarnings("deprecation") Player player = Bukkit.getPlayer(playerName); if (player != null) { use(player); } } /** * Record a usage event against this item. * * @param player The player who used the menu/item * @throws SMSException if there are not enough uses remaining */ public void use(OfflinePlayer player) { if (globalMax > 0) { SMSValidate.isTrue(globalRemaining > 0, "Not enough uses remaining"); globalRemaining--; } else { Integer uses = this.uses.get(player.getUniqueId()); SMSValidate.isTrue(uses == null || uses > 0, "Not enough uses remaining"); this.uses.put(player.getUniqueId(), uses == null ? perPlayerMax - 1 : uses - 1); } autosave(); } private void autosave() { parentObject.autosave(); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { if (globalMax > 0) { return String.format("%d/%d (global)", globalRemaining, globalMax); } else if (perPlayerMax > 0) { return String.format("%d (per-player)", perPlayerMax); } else { return ""; } } /** * Return a formatted description of the total and remaining usage for the given player. * * @param playerName the player name * @return a formatted string * @deprecated use {@link #toString(org.bukkit.OfflinePlayer)} */ @Deprecated public String toString(String playerName) { @SuppressWarnings("deprecation") Player player = Bukkit.getPlayer(playerName); return player == null ? "" : toString(player); } /** * Return a formatted description of the total and remaining usage for the given player. * * @param player the player * @return Formatted string */ public String toString(OfflinePlayer player) { if (globalMax > 0) { return String.format("%d/%d (global)", globalRemaining, globalMax); } else if (perPlayerMax > 0) { return String.format("%d/%d (for %s)", getRemainingUses(player), perPlayerMax, player.getName()); } else { return ""; } } Map<String,Map<String,Integer>> freeze() { Map<String,Map<String,Integer>> res = new HashMap<String, Map<String, Integer>>(); if (hasLimitedUses()) { Map<String,Integer> l = new HashMap<String, Integer>(); if (globalMax > 0) { l.put("globalMax", globalMax); l.put("globalRemaining", globalRemaining); } else if (perPlayerMax > 0) { l.put("perPlayerMax", perPlayerMax); } res.put("limits", l); Map<String,Integer> p = new HashMap<String, Integer>(); for (Map.Entry<UUID, Integer> e : uses.entrySet()) { p.put(e.getKey().toString(), e.getValue()); } res.put("players", p); } return res; } String getDescription() { return parentObject.getDescription(); } private void migrateOldFormatData(ConfigurationSection node) { LogUtils.info("migrating [" + parentObject.getLimitableName() + "] usage-limit data to new format..."); globalMax = node.getInt(GLOBAL_MAX); globalRemaining = node.getInt(GLOBAL_REMAINING); perPlayerMax = node.getInt(PER_PLAYER_MAX); for (String key : node.getKeys(false)) { if (!key.startsWith("&")) { oldUses.put(key, node.getInt(key)); } } if (!oldUses.isEmpty()) { Bukkit.getScheduler().runTaskAsynchronously( ScrollingMenuSign.getInstance(), new AsyncMigrationTask(new ArrayList<String>(oldUses.keySet())) ); } } private class AsyncMigrationTask implements Runnable { private final List<String> toMigrate; public AsyncMigrationTask(List<String> toMigrate) { this.toMigrate = toMigrate; } @Override public void run() { UUIDFetcher uf = new UUIDFetcher(toMigrate); try { Map<String,UUID> res = uf.call(); Bukkit.getScheduler().runTask(ScrollingMenuSign.getInstance(), new SyncMigrationTask(res)); } catch (Exception e) { e.printStackTrace(); } } } private class SyncMigrationTask implements Runnable { private final Map<String, UUID> data; public SyncMigrationTask(Map<String, UUID> res) { this.data = res; } @Override public void run() { for (Map.Entry<String, UUID> e : data.entrySet()) { Integer n = oldUses.get(e.getKey()); if (n != null) { uses.put(e.getValue(), n); } else { LogUtils.warning("can't find usage-limit data for " + e.getKey() + " in " + parentObject.getLimitableName()); } } oldUses.clear(); LogUtils.info(data.size() + " usage-limit player names for " + parentObject.getLimitableName() + " migrated to UUIDs"); autosave(); } } }