diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/misc/PlayerManager.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/misc/PlayerManager.java index 3b75b01e3f..e822ea25ed 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/misc/PlayerManager.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/misc/PlayerManager.java @@ -211,7 +211,7 @@ public boolean hasServerPermission(UUID playerUUID, String playername, String pe return player.hasPermission(perm); } else { player = Bukkit.getPlayer(playername); - if (player != null) { + if (player != null && playerUUID.equals(player.getUniqueId())) { return player.hasPermission(perm); } } diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/QueuedGeneratedReward.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/QueuedGeneratedReward.java new file mode 100644 index 0000000000..580a8a344e --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/QueuedGeneratedReward.java @@ -0,0 +1,52 @@ +package com.bencodez.advancedcore.api.rewards; + +import java.io.File; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import com.bencodez.advancedcore.api.user.AdvancedCoreUser; + +/** + * A generated directly-defined reward that is still referenced by a persisted + * offline or timed queue. These rewards are intentionally restricted to the + * users whose persisted queue references the generated reward name. + */ +final class QueuedGeneratedReward extends Reward { + + private final Set allowedUserUuids; + + QueuedGeneratedReward(File folder, String reward, Set allowedUserUuids) { + super(folder, reward); + this.allowedUserUuids = Collections.unmodifiableSet(new HashSet<>(allowedUserUuids)); + } + + @Override + public void giveReward(AdvancedCoreUser user, RewardOptions rewardOptions) { + if (user == null || user.getUUID() == null || !allowedUserUuids.contains(user.getUUID())) { + plugin.getLogger().warning("Blocked generated queued reward " + getRewardName() + + " for a user without a matching persisted queue entry"); + return; + } + super.giveReward(user, rewardOptions); + } + + @Override + public boolean isGeneratedSnapshotCreated() { + // A loaded generated snapshot must retain snapshot provenance if execution is + // deferred again (paused rewards, vanish-as-offline, or another offline retry). + return true; + } + + @Override + public void validate() { + // This file is a persisted snapshot created from a reward that was already + // validated before it was queued. Revalidating requires the live injected + // registries and adds no security boundary; execution is instead restricted + // to UUIDs with a matching persisted queue reference. + } + + Set getAllowedUserUuids() { + return allowedUserUuids; + } +} diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/Reward.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/Reward.java index 6f7857eadd..d86d8d2110 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/Reward.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/Reward.java @@ -1,505 +1,508 @@ -package com.bencodez.advancedcore.api.rewards; - -import java.io.File; -import java.text.SimpleDateFormat; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import com.bencodez.advancedcore.AdvancedCorePlugin; -import com.bencodez.advancedcore.api.item.ItemBuilder; -import com.bencodez.advancedcore.api.rewards.injected.RewardInject; -import com.bencodez.advancedcore.api.rewards.injectedrequirement.RequirementInject; -import com.bencodez.advancedcore.api.user.AdvancedCoreUser; -import com.bencodez.advancedcore.listeners.PlayerRewardEvent; -import com.bencodez.simpleapi.file.annotation.AnnotationHandler; - -import lombok.Getter; -import lombok.Setter; - -/** - * The Class Reward. - */ -public class Reward { - - @Getter - @Setter - private RewardFileData config; - - @Getter - @Setter - private boolean delayEnabled; - - @Getter - @Setter - private int delayHours; - - @Getter - @Setter - private int delayMinutes; - - @Getter - @Setter - private int delaySeconds; - - @Getter - @Setter - private int delayMilliSeconds; - - @Getter - @Setter - private File file; - - @Getter - @Setter - private boolean forceOffline; - - @Getter - @Setter - private String name; - - @Getter - private boolean needsRewardFile = true; - - /** The plugin. */ - AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance(); - - @Getter - @Setter - private boolean timedEnabled; - - @Getter - @Setter - private int timedHour; - - @Getter - @Setter - private int timedMinute; - - /** - * Instantiates a new reward. - * - * @param file the file - * @param reward the reward - */ - public Reward(File file, String reward) { - load(file, reward); - } - - /** - * Instantiates a new reward. - * - * @param reward the reward - */ - public Reward(String reward) { - load(plugin.getRewardHandler().getDefaultFolder(), reward); - } - - public Reward(String name, ConfigurationSection section) { - load(name, section); - } - - public boolean canGiveReward(AdvancedCoreUser user, RewardOptions options) { - for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { - try { - plugin.extraDebug(getRewardName() + ": Checking " + inject.getPath() + ":" + inject.getPriority()); - if (!inject.onRequirementRequest(this, user, getConfig().getConfigData(), options)) { - return false; - } - } catch (Exception e) { - plugin.debug("Failed to check requirement"); - e.printStackTrace(); - return false; - } - } - return true; - } - - public boolean checkDelayed(AdvancedCoreUser user, HashMap placeholders) { - if (!isDelayEnabled()) { - return false; - } - - LocalDateTime time = LocalDateTime.now(); - time = time.plus(getDelayHours(), ChronoUnit.HOURS); - time = time.plus(getDelayMinutes(), ChronoUnit.MINUTES); - time = time.plus(getDelaySeconds(), ChronoUnit.SECONDS); - time = time.plus(getDelayMilliSeconds(), ChronoUnit.MILLIS); - checkRewardFile(); - user.addTimedReward(this, placeholders, time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); - - plugin.debug("Giving reward " + name + " in " + getDelayHours() + " hours, " + getDelayMinutes() + " minutes, " - + getDelaySeconds() + " seconds (" + time.toString() + ")"); - return true; - } - - public void checkRewardFile() { - if (!getConfig().hasRewardFile() && needsRewardFile) { - setRewardFile(); - } - } - - public boolean checkTimed(AdvancedCoreUser user, HashMap placeholders) { - if (!isTimedEnabled()) { - return false; - } - - LocalDateTime time = LocalDateTime.now(); - time = time.withHour(getTimedHour()); - time = time.withMinute(getTimedMinute()); - - if (LocalDateTime.now().isAfter(time)) { - time = time.plusDays(1); - } - checkRewardFile(); - user.addTimedReward(this, placeholders, time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); - - plugin.debug("Giving reward " + name + " at " + time.toString()); - return true; - } - - public ItemStack getItem() { - return new ItemStack(Material.STONE); - } - - public ItemStack getItemStack(AdvancedCoreUser user, String item) { - return new ItemBuilder(getConfig().getItemSection(item)).setSkullOwner(user.getOfflinePlayer()) - .toItemStack(user.getPlayer()); - } - - /** - * Gets the reward name. - * - * @return the reward name - */ - public String getRewardName() { - return name; - } - - public void giveInjectedRewards(AdvancedCoreUser user, HashMap placeholders) { - - ArrayList postReward = new ArrayList<>(); - - for (final RewardInject inject : plugin.getRewardHandler().getInjectedRewards()) { - boolean Addplaceholder = inject.isAddAsPlaceholder(); - try { - Object obj = null; - plugin.extraDebug( - getRewardName() + ": Attempting to give " + inject.getPath() + ":" + inject.getPriority()); - if (!inject.isPostReward()) { - if (inject.isSynchronize()) { - synchronized (inject.getObject()) { - obj = inject.onRewardRequest(this, user, getConfig().getConfigData(), placeholders); - } - } else { - obj = inject.onRewardRequest(this, user, getConfig().getConfigData(), placeholders); - } - if (Addplaceholder && obj != null) { - String placeholderName = inject.getPlaceholderName(); - String value = ""; - if (obj instanceof Boolean) { - Boolean b = (Boolean) obj; - value = b.toString(); - } else if (obj instanceof String) { - String b = (String) obj; - value = b; - } else if (obj instanceof Double) { - Double b = (Double) obj; - value = b.toString(); - } else if (obj instanceof Integer) { - Integer b = (Integer) obj; - value = b.toString(); - } - plugin.extraDebug("Adding placeholder " + placeholderName + ":" + value); - placeholders.put(placeholderName, value); - } - } else { - postReward.add(inject); - - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - for (RewardInject inject : postReward) { - try { - inject.onRewardRequest(this, user, getConfig().getConfigData(), placeholders); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public void giveReward(AdvancedCoreUser user, RewardOptions rewardOptions) { - if (!AdvancedCorePlugin.getInstance().getOptions().isProcessRewards()) { - AdvancedCorePlugin.getInstance().debug("Processing rewards is disabled"); - return; - } - - if (rewardOptions == null) { - rewardOptions = new RewardOptions(); - } - - if (!rewardOptions.getPlaceholders().containsKey("ExecDate")) { - rewardOptions.addPlaceholder("ExecDate", "" + System.currentTimeMillis()); - } - - if (!rewardOptions.getPlaceholders().containsKey("date")) { - try { - LocalDateTime ldt = LocalDateTime.now(); - Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - rewardOptions.addPlaceholder("Date", - "" + new SimpleDateFormat(plugin.getOptions().getFormatRewardTimeFormat()).format(date)); - } catch (Exception e) { - e.printStackTrace(); - } - } - - PlayerRewardEvent event = new PlayerRewardEvent(this, user, rewardOptions); - Bukkit.getPluginManager().callEvent(event); - - if (event.isCancelled()) { - plugin.debug("Reward " + name + " was cancelled for " + user.getPlayerName()); - return; - } - - if (rewardOptions.isCheckTimed()) { - if (checkDelayed(user, rewardOptions.getPlaceholders()) - || checkTimed(user, rewardOptions.getPlaceholders())) { - return; - } - } - - if (!rewardOptions.isOnlineSet()) { - rewardOptions.setOnline(user.isOnline()); - } - - for (RewardPlaceholderHandle handle : plugin.getRewardHandler().getPlaceholders()) { - if (handle.isPreProcess()) { - rewardOptions.addPlaceholder(handle.getKey(), handle.getValue(this, user)); - } - } - - // Check requirements - boolean allowOffline = false; - boolean canGive = true; - if (!rewardOptions.isIgnoreRequirements()) { - for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { - try { - plugin.extraDebug(getRewardName() + ": Checking requirement " + inject.getPath() + ":" - + inject.getPriority()); - if (!inject.onRequirementRequest(this, user, getConfig().getConfigData(), rewardOptions)) { - plugin.debug(getRewardName() + ": Requirement failed " + inject.getPath() + ":" - + inject.isAllowReattempt()); - canGive = false; - if (!inject.isAllowReattempt()) { - return; - } - allowOffline = true; - } - } catch (Exception e) { - plugin.debug("Failed to check requirement " + inject.getPath()); - e.printStackTrace(); - canGive = false; - } - } - } - - if (plugin.getOptions().isPauseRewards()) { - checkRewardFile(); - user.addOfflineRewards(this, rewardOptions.getPlaceholders()); - plugin.getLogger() - .info("Rewards are paused, saving offline reward " + getRewardName() + ": " + user.getPlayerName()); - return; - } - - if ((plugin.getOptions().isTreatVanishAsOffline() && user.isVanished())) { - checkRewardFile(); - user.addOfflineRewards(this, rewardOptions.getPlaceholders()); - plugin.getLogger() - .info(getRewardName() + ": " + user.getPlayerName() + " is vanished, saving reward offline"); - return; - } - - // save reward for offline - if (((((!rewardOptions.isOnline() || rewardOptions.getServer() != null) && !user.isOnline()) || allowOffline) - && (!isForceOffline() && !rewardOptions.isForceOffline()))) { - if (rewardOptions.isGiveOffline()) { - checkRewardFile(); - user.addOfflineRewards(this, rewardOptions.getPlaceholders()); - plugin.debug("Saving offline reward " + getRewardName() + " for " + user.getPlayerName()); - } - return; - } - - // give reward - if (canGive || isForceOffline() || rewardOptions.isForceOffline()) { - plugin.debug(name + ": Passed requirements, attempting to give to " + user.getPlayerName() + "/" - + user.getUUID()); - giveRewardUser(user, rewardOptions.getPlaceholders(), rewardOptions); - } - } - - /** - * Give reward user. - * - * @param user the user - * @param phs placeholders - * @param rewardOptions rewardOptions - */ - public void giveRewardUser(AdvancedCoreUser user, HashMap phs, RewardOptions rewardOptions) { - - Player player = user.getPlayer(); - if (player == null) { - player = Bukkit.getPlayer(user.getPlayerName()); - } - if (player != null || isForceOffline()) { - - // placeholders - if (phs == null) { - phs = new HashMap<>(); - } - final String playerName = user.getPlayerName(); - phs.put("player", playerName); - if (player != null) { - phs.put("displayname", player.getDisplayName()); - } - phs.put("@p", playerName); - LocalDateTime ldt = LocalDateTime.now(); - Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - phs.put("CurrentDate", "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); - phs.put("uuid", user.getUUID()); - - for (RewardPlaceholderHandle handle : plugin.getRewardHandler().getPlaceholders()) { - if (!handle.isPreProcess()) { - phs.put(handle.getKey(), handle.getValue(this, user)); - } - } - - final HashMap placeholders = new HashMap<>(phs); - - giveInjectedRewards(user, placeholders); - - plugin.debug("Gave " + user.getPlayerName() + " reward " + name); - } else { - plugin.debug(getRewardName() + ": Player == null & forceoffline false, player: " + user.getPlayerName() - + "/" + user.getUUID()); - } - } - - /** - * Load. - * - * @param folder the folder - * @param reward the reward - */ - public void load(File folder, String reward) { - name = reward; - if (folder.isDirectory()) { - file = new File(folder, reward + ".yml"); - } else { - file = folder; - } - config = new RewardFileData(this, folder); - loadValues(); - } - - public void load(String name, ConfigurationSection section) { - config = new RewardFileData(this, section); - this.name = name; - loadValues(); - } - - public void loadValues() { - forceOffline = getConfig().getForceOffline(); - - setDelayEnabled(getConfig().getDelayedEnabled()); - if (delayEnabled) { - setDelayHours(getConfig().getDelayedHours()); - setDelayMinutes(getConfig().getDelayedMinutes()); - setDelaySeconds(getConfig().getDelayedSeconds()); - setDelayMilliSeconds(getConfig().getDelayedMilliSeconds()); - } - - setTimedEnabled(getConfig().getTimedEnabled()); - if (timedEnabled) { - setTimedHour(getConfig().getTimedHour()); - setTimedMinute(getConfig().getTimedMinute()); - } - - new AnnotationHandler().load(getConfig().getConfigData(), this); - } - - public Reward needsRewardFile(boolean value) { - needsRewardFile = value; - return this; - } - - @SuppressWarnings("deprecation") - private void setRewardFile() { - Reward reward = plugin.getRewardHandler().getRewardDirectlyDefined(name); - ConfigurationSection section = getConfig().getConfigData(); - reward.getConfig().setData(section); - reward.getConfig().getFileData().options() - .header("Directly defined reward file. WRONG PLACE TO EDIT THIS! DO NOT EDIT"); - reward.getConfig().setDirectlyDefinedReward(true); - reward.getConfig().save(reward.getConfig().getFileData()); - plugin.getRewardHandler().updateReward(reward); - } - - public void validate() { - if (getName().equalsIgnoreCase("examplebasic") || getName().equalsIgnoreCase("exampleadvanced")) { - return; - } - for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { - inject.validate(this, getConfig().getConfigData()); - } - for (RewardInject inject : plugin.getRewardHandler().getInjectedRewards()) { - inject.validate(this, getConfig().getConfigData()); - } - for (String str : getConfig().getConfigData().getKeys(false)) { - boolean valid = false; - for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { - if (inject.hasValidator()) { - if (inject.getValidate().isValid(inject, str)) { - valid = true; - } - } else if (inject.getPath().startsWith(str)) { - valid = true; - } - } - for (RewardInject inject : plugin.getRewardHandler().getInjectedRewards()) { - if (inject.isAlwaysValid()) { - valid = true; - } else { - if (inject.hasValidator()) { - if (inject.getValidate().isValid(inject, str)) { - valid = true; - } - } else if (inject.getPath().startsWith(str)) { - valid = true; - } - } - } - if (plugin.getRewardHandler().getValidPaths().contains(str)) { - valid = true; - } - if (!valid) { - plugin.getLogger().warning(str + " possibly not valid in reward " + getRewardName()); - } - } - } - -} +package com.bencodez.advancedcore.api.rewards; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.item.ItemBuilder; +import com.bencodez.advancedcore.api.rewards.injected.RewardInject; +import com.bencodez.advancedcore.api.rewards.injectedrequirement.RequirementInject; +import com.bencodez.advancedcore.api.user.AdvancedCoreUser; +import com.bencodez.advancedcore.listeners.PlayerRewardEvent; +import com.bencodez.simpleapi.file.annotation.AnnotationHandler; + +import lombok.Getter; +import lombok.Setter; + +/** + * The Class Reward. + */ +public class Reward { + + @Getter + @Setter + private RewardFileData config; + + @Getter + @Setter + private boolean delayEnabled; + + @Getter + @Setter + private int delayHours; + + @Getter + @Setter + private int delayMinutes; + + @Getter + @Setter + private int delaySeconds; + + @Getter + @Setter + private int delayMilliSeconds; + + @Getter + @Setter + private File file; + + @Getter + @Setter + private boolean forceOffline; + + @Getter + @Setter + private String name; + + @Getter + private boolean needsRewardFile = true; + + @Getter + private boolean generatedSnapshotCreated = false; + + /** The plugin. */ + AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance(); + + @Getter + @Setter + private boolean timedEnabled; + + @Getter + @Setter + private int timedHour; + + @Getter + @Setter + private int timedMinute; + + /** + * Instantiates a new reward. + * + * @param file the file + * @param reward the reward + */ + public Reward(File file, String reward) { + load(file, reward); + } + + /** + * Instantiates a new reward. + * + * @param reward the reward + */ + public Reward(String reward) { + load(plugin.getRewardHandler().getDefaultFolder(), reward); + } + + public Reward(String name, ConfigurationSection section) { + load(name, section); + } + + public boolean canGiveReward(AdvancedCoreUser user, RewardOptions options) { + for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { + try { + plugin.extraDebug(getRewardName() + ": Checking " + inject.getPath() + ":" + inject.getPriority()); + if (!inject.onRequirementRequest(this, user, getConfig().getConfigData(), options)) { + return false; + } + } catch (Exception e) { + plugin.debug("Failed to check requirement"); + e.printStackTrace(); + return false; + } + } + return true; + } + + public boolean checkDelayed(AdvancedCoreUser user, HashMap placeholders) { + if (!isDelayEnabled()) { + return false; + } + + LocalDateTime time = LocalDateTime.now(); + time = time.plus(getDelayHours(), ChronoUnit.HOURS); + time = time.plus(getDelayMinutes(), ChronoUnit.MINUTES); + time = time.plus(getDelaySeconds(), ChronoUnit.SECONDS); + time = time.plus(getDelayMilliSeconds(), ChronoUnit.MILLIS); + checkRewardFile(); + user.addTimedReward(this, placeholders, time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + + plugin.debug("Giving reward " + name + " in " + getDelayHours() + " hours, " + getDelayMinutes() + " minutes, " + + getDelaySeconds() + " seconds (" + time.toString() + ")"); + return true; + } + + public void checkRewardFile() { + if (!getConfig().hasRewardFile() && needsRewardFile) { + setRewardFile(); + } + } + + public boolean checkTimed(AdvancedCoreUser user, HashMap placeholders) { + if (!isTimedEnabled()) { + return false; + } + + LocalDateTime time = LocalDateTime.now(); + time = time.withHour(getTimedHour()); + time = time.withMinute(getTimedMinute()); + + if (LocalDateTime.now().isAfter(time)) { + time = time.plusDays(1); + } + checkRewardFile(); + user.addTimedReward(this, placeholders, time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + + plugin.debug("Giving reward " + name + " at " + time.toString()); + return true; + } + + public ItemStack getItem() { + return new ItemStack(Material.STONE); + } + + public ItemStack getItemStack(AdvancedCoreUser user, String item) { + return new ItemBuilder(getConfig().getItemSection(item)).setSkullOwner(user.getOfflinePlayer()) + .toItemStack(user.getPlayer()); + } + + /** + * Gets the reward name. + * + * @return the reward name + */ + public String getRewardName() { + return name; + } + + public void giveInjectedRewards(AdvancedCoreUser user, HashMap placeholders) { + + ArrayList postReward = new ArrayList<>(); + + for (final RewardInject inject : plugin.getRewardHandler().getInjectedRewards()) { + boolean Addplaceholder = inject.isAddAsPlaceholder(); + try { + Object obj = null; + plugin.extraDebug( + getRewardName() + ": Attempting to give " + inject.getPath() + ":" + inject.getPriority()); + if (!inject.isPostReward()) { + if (inject.isSynchronize()) { + synchronized (inject.getObject()) { + obj = inject.onRewardRequest(this, user, getConfig().getConfigData(), placeholders); + } + } else { + obj = inject.onRewardRequest(this, user, getConfig().getConfigData(), placeholders); + } + if (Addplaceholder && obj != null) { + String placeholderName = inject.getPlaceholderName(); + String value = ""; + if (obj instanceof Boolean) { + Boolean b = (Boolean) obj; + value = b.toString(); + } else if (obj instanceof String) { + String b = (String) obj; + value = b; + } else if (obj instanceof Double) { + Double b = (Double) obj; + value = b.toString(); + } else if (obj instanceof Integer) { + Integer b = (Integer) obj; + value = b.toString(); + } + plugin.extraDebug("Adding placeholder " + placeholderName + ":" + value); + placeholders.put(placeholderName, value); + } + } else { + postReward.add(inject); + + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + for (RewardInject inject : postReward) { + try { + inject.onRewardRequest(this, user, getConfig().getConfigData(), placeholders); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public void giveReward(AdvancedCoreUser user, RewardOptions rewardOptions) { + if (!AdvancedCorePlugin.getInstance().getOptions().isProcessRewards()) { + AdvancedCorePlugin.getInstance().debug("Processing rewards is disabled"); + return; + } + + if (rewardOptions == null) { + rewardOptions = new RewardOptions(); + } + + if (!rewardOptions.getPlaceholders().containsKey("ExecDate")) { + rewardOptions.addPlaceholder("ExecDate", "" + System.currentTimeMillis()); + } + + if (!rewardOptions.getPlaceholders().containsKey("date")) { + try { + LocalDateTime ldt = LocalDateTime.now(); + Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + rewardOptions.addPlaceholder("Date", + "" + new SimpleDateFormat(plugin.getOptions().getFormatRewardTimeFormat()).format(date)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + PlayerRewardEvent event = new PlayerRewardEvent(this, user, rewardOptions); + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + plugin.debug("Reward " + name + " was cancelled for " + user.getPlayerName()); + return; + } + + if (rewardOptions.isCheckTimed()) { + if (checkDelayed(user, rewardOptions.getPlaceholders()) + || checkTimed(user, rewardOptions.getPlaceholders())) { + return; + } + } + + if (!rewardOptions.isOnlineSet()) { + rewardOptions.setOnline(user.isOnline()); + } + + for (RewardPlaceholderHandle handle : plugin.getRewardHandler().getPlaceholders()) { + if (handle.isPreProcess()) { + rewardOptions.addPlaceholder(handle.getKey(), handle.getValue(this, user)); + } + } + + // Check requirements + boolean allowOffline = false; + boolean canGive = true; + if (!rewardOptions.isIgnoreRequirements()) { + for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { + try { + plugin.extraDebug(getRewardName() + ": Checking requirement " + inject.getPath() + ":" + + inject.getPriority()); + if (!inject.onRequirementRequest(this, user, getConfig().getConfigData(), rewardOptions)) { + plugin.debug(getRewardName() + ": Requirement failed " + inject.getPath() + ":" + + inject.isAllowReattempt()); + canGive = false; + if (!inject.isAllowReattempt()) { + return; + } + allowOffline = true; + } + } catch (Exception e) { + plugin.debug("Failed to check requirement " + inject.getPath()); + e.printStackTrace(); + canGive = false; + } + } + } + + if (plugin.getOptions().isPauseRewards()) { + checkRewardFile(); + user.addOfflineRewards(this, rewardOptions.getPlaceholders()); + plugin.getLogger() + .info("Rewards are paused, saving offline reward " + getRewardName() + ": " + user.getPlayerName()); + return; + } + + if ((plugin.getOptions().isTreatVanishAsOffline() && user.isVanished())) { + checkRewardFile(); + user.addOfflineRewards(this, rewardOptions.getPlaceholders()); + plugin.getLogger() + .info(getRewardName() + ": " + user.getPlayerName() + " is vanished, saving reward offline"); + return; + } + + // save reward for offline + if (((((!rewardOptions.isOnline() || rewardOptions.getServer() != null) && !user.isOnline()) || allowOffline) + && (!isForceOffline() && !rewardOptions.isForceOffline()))) { + if (rewardOptions.isGiveOffline()) { + checkRewardFile(); + user.addOfflineRewards(this, rewardOptions.getPlaceholders()); + plugin.debug("Saving offline reward " + getRewardName() + " for " + user.getPlayerName()); + } + return; + } + + // give reward + if (canGive || isForceOffline() || rewardOptions.isForceOffline()) { + plugin.debug(name + ": Passed requirements, attempting to give to " + user.getPlayerName() + "/" + + user.getUUID()); + giveRewardUser(user, rewardOptions.getPlaceholders(), rewardOptions); + } + } + + /** + * Give reward user. + * + * @param user the user + * @param phs placeholders + * @param rewardOptions rewardOptions + */ + public void giveRewardUser(AdvancedCoreUser user, HashMap phs, RewardOptions rewardOptions) { + + Player player = user.getPlayer(); + if (player == null) { + player = Bukkit.getPlayer(user.getPlayerName()); + } + if (player != null || isForceOffline()) { + + // placeholders + if (phs == null) { + phs = new HashMap<>(); + } + final String playerName = user.getPlayerName(); + phs.put("player", playerName); + if (player != null) { + phs.put("displayname", player.getDisplayName()); + } + phs.put("@p", playerName); + LocalDateTime ldt = LocalDateTime.now(); + Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + phs.put("CurrentDate", "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); + phs.put("uuid", user.getUUID()); + + for (RewardPlaceholderHandle handle : plugin.getRewardHandler().getPlaceholders()) { + if (!handle.isPreProcess()) { + phs.put(handle.getKey(), handle.getValue(this, user)); + } + } + + final HashMap placeholders = new HashMap<>(phs); + + giveInjectedRewards(user, placeholders); + + plugin.debug("Gave " + user.getPlayerName() + " reward " + name); + } else { + plugin.debug(getRewardName() + ": Player == null & forceoffline false, player: " + user.getPlayerName() + + "/" + user.getUUID()); + } + } + + /** + * Load. + * + * @param folder the folder + * @param reward the reward + */ + public void load(File folder, String reward) { + name = reward; + if (folder.isDirectory()) { + file = new File(folder, reward + ".yml"); + } else { + file = folder; + } + config = new RewardFileData(this, folder); + loadValues(); + } + + public void load(String name, ConfigurationSection section) { + config = new RewardFileData(this, section); + this.name = name; + loadValues(); + } + + public void loadValues() { + forceOffline = getConfig().getForceOffline(); + + setDelayEnabled(getConfig().getDelayedEnabled()); + if (delayEnabled) { + setDelayHours(getConfig().getDelayedHours()); + setDelayMinutes(getConfig().getDelayedMinutes()); + setDelaySeconds(getConfig().getDelayedSeconds()); + setDelayMilliSeconds(getConfig().getDelayedMilliSeconds()); + } + + setTimedEnabled(getConfig().getTimedEnabled()); + if (timedEnabled) { + setTimedHour(getConfig().getTimedHour()); + setTimedMinute(getConfig().getTimedMinute()); + } + + new AnnotationHandler().load(getConfig().getConfigData(), this); + } + + public Reward needsRewardFile(boolean value) { + needsRewardFile = value; + return this; + } + + @SuppressWarnings("deprecation") + private void setRewardFile() { + Reward reward = plugin.getRewardHandler().getRewardDirectlyDefined(name); + ConfigurationSection section = getConfig().getConfigData(); + reward.getConfig().setData(section); + reward.getConfig().getFileData().options() + .header("Directly defined reward file. WRONG PLACE TO EDIT THIS! DO NOT EDIT"); + reward.getConfig().setDirectlyDefinedReward(true); + reward.getConfig().save(reward.getConfig().getFileData()); + generatedSnapshotCreated = true; + } + + public void validate() { + if (getName().equalsIgnoreCase("examplebasic") || getName().equalsIgnoreCase("exampleadvanced")) { + return; + } + for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { + inject.validate(this, getConfig().getConfigData()); + } + for (RewardInject inject : plugin.getRewardHandler().getInjectedRewards()) { + inject.validate(this, getConfig().getConfigData()); + } + for (String str : getConfig().getConfigData().getKeys(false)) { + boolean valid = false; + for (RequirementInject inject : plugin.getRewardHandler().getInjectedRequirements()) { + if (inject.hasValidator()) { + if (inject.getValidate().isValid(inject, str)) { + valid = true; + } + } else if (inject.getPath().startsWith(str)) { + valid = true; + } + } + for (RewardInject inject : plugin.getRewardHandler().getInjectedRewards()) { + if (inject.isAlwaysValid()) { + valid = true; + } else { + if (inject.hasValidator()) { + if (inject.getValidate().isValid(inject, str)) { + valid = true; + } + } else if (inject.getPath().startsWith(str)) { + valid = true; + } + } + } + if (plugin.getRewardHandler().getValidPaths().contains(str)) { + valid = true; + } + if (!valid) { + plugin.getLogger().warning(str + " possibly not valid in reward " + getRewardName()); + } + } + } + +} diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardBuilder.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardBuilder.java index 8d9cc1787d..0c16f10e0a 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardBuilder.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardBuilder.java @@ -1,175 +1,176 @@ -package com.bencodez.advancedcore.api.rewards; - -import java.text.SimpleDateFormat; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; - -import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; - -import com.bencodez.advancedcore.AdvancedCorePlugin; -import com.bencodez.advancedcore.api.user.AdvancedCoreUser; - -public class RewardBuilder { - private ConfigurationSection data; - private String path; - private Reward reward; - private RewardOptions rewardOptions; - - public RewardBuilder(ConfigurationSection data, String path) { - this.data = data; - this.path = path; - this.rewardOptions = new RewardOptions(); - LocalDateTime ldt = LocalDateTime.now(); - Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - this.rewardOptions.getPlaceholders().put("Date", - "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); - } - - public RewardBuilder(ConfigurationSection data, String path, RewardOptions rewardOptions) { - this.data = data; - this.path = path; - this.rewardOptions = rewardOptions; - if (rewardOptions == null) { - this.rewardOptions = new RewardOptions(); - } - LocalDateTime ldt = LocalDateTime.now(); - Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - this.rewardOptions.getPlaceholders().put("Date", - "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); - } - - public RewardBuilder(Reward reward) { - this.reward = reward; - this.rewardOptions = new RewardOptions(); - LocalDateTime ldt = LocalDateTime.now(); - Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - this.rewardOptions.getPlaceholders().put("Date", - "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); - } - - public RewardBuilder(Reward reward, RewardOptions rewardOptions) { - this.reward = reward; - this.rewardOptions = rewardOptions; - if (rewardOptions == null) { - this.rewardOptions = new RewardOptions(); - } - LocalDateTime ldt = LocalDateTime.now(); - Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - this.rewardOptions.getPlaceholders().put("Date", - "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); - } - - public RewardBuilder disableDefaultWorlds() { - this.rewardOptions.disableDefaultWorlds(); - return this; - } - - public RewardOptions getRewardOptions() { - return rewardOptions; - } - - public RewardBuilder orginalTrigger(long trigger) { - this.rewardOptions.orginalTrigger(trigger); - return this; - } - - public void send(AdvancedCoreUser user) { - if (reward == null) { - if (data != null) { - user.getPlugin().getRewardHandler().giveReward(user, data, path, rewardOptions); - } - } else { - user.getPlugin().getRewardHandler().giveReward(user, reward, rewardOptions); - } - } - - public void send(AdvancedCoreUser... users) { - for (AdvancedCoreUser user : users) { - send(user); - } - } - - public void send(ArrayList users) { - for (AdvancedCoreUser user : users) { - send(user); - } - } - - public void send(OfflinePlayer p) { - send(AdvancedCorePlugin.getInstance().getUserManager().getUser(p)); - } - - public void send(Player p) { - send(AdvancedCorePlugin.getInstance().getUserManager().getUser(p)); - } - - public RewardBuilder setCheckTimed(boolean checkTimed) { - getRewardOptions().setCheckTimed(checkTimed); - return this; - } - - public RewardBuilder setGiveOffline(boolean giveOffline) { - getRewardOptions().setGiveOffline(giveOffline); - return this; - } - - public RewardBuilder setIgnoreChance(boolean ignoreChance) { - getRewardOptions().setIgnoreChance(ignoreChance); - return this; - } - - public RewardBuilder setIgnoreRequirements(boolean ignoreRequirements) { - getRewardOptions().setIgnoreRequirements(ignoreRequirements); - return this; - } - - public RewardBuilder setOnline(boolean online) { - getRewardOptions().setOnline(online); - return this; - } - - /** - * @param reward the reward to set - */ - public void setReward(Reward reward) { - this.reward = reward; - } - - public RewardBuilder setRewardOptions(RewardOptions rewardOptions) { - this.rewardOptions = rewardOptions; - return this; - } - - public RewardBuilder setServer(boolean b) { - if (b) { - getRewardOptions().setServer(b); - } - return this; - } - - public RewardBuilder withPlaceHolder(HashMap placeholders) { - this.rewardOptions.getPlaceholders().putAll(placeholders); - return this; - } - - public RewardBuilder withPlaceHolder(String toReplace, String replaceWith) { - this.rewardOptions.getPlaceholders().put(toReplace, replaceWith); - return this; - } - - public RewardBuilder withPrefix(String prefix) { - this.rewardOptions.setPrefix(prefix); - return this; - } - - public RewardBuilder withSuffix(String suffix) { - this.rewardOptions.setSuffix(suffix); - return this; - } - -} +package com.bencodez.advancedcore.api.rewards; + +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.user.AdvancedCoreUser; + +public class RewardBuilder { + private ConfigurationSection data; + private String path; + private Reward reward; + private RewardOptions rewardOptions; + + public RewardBuilder(ConfigurationSection data, String path) { + this.data = data; + this.path = path; + this.rewardOptions = new RewardOptions(); + LocalDateTime ldt = LocalDateTime.now(); + Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + this.rewardOptions.getPlaceholders().put("Date", + "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); + } + + public RewardBuilder(ConfigurationSection data, String path, RewardOptions rewardOptions) { + this.data = data; + this.path = path; + this.rewardOptions = rewardOptions; + if (rewardOptions == null) { + this.rewardOptions = new RewardOptions(); + } + LocalDateTime ldt = LocalDateTime.now(); + Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + this.rewardOptions.getPlaceholders().put("Date", + "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); + } + + public RewardBuilder(Reward reward) { + this.reward = reward; + this.rewardOptions = new RewardOptions(); + LocalDateTime ldt = LocalDateTime.now(); + Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + this.rewardOptions.getPlaceholders().put("Date", + "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); + } + + public RewardBuilder(Reward reward, RewardOptions rewardOptions) { + this.reward = reward; + this.rewardOptions = rewardOptions; + if (rewardOptions == null) { + this.rewardOptions = new RewardOptions(); + } + LocalDateTime ldt = LocalDateTime.now(); + Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + this.rewardOptions.getPlaceholders().put("Date", + "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(date)); + } + + public RewardBuilder disableDefaultWorlds() { + this.rewardOptions.disableDefaultWorlds(); + return this; + } + + public RewardOptions getRewardOptions() { + return rewardOptions; + } + + public RewardBuilder orginalTrigger(long trigger) { + this.rewardOptions.orginalTrigger(trigger); + return this; + } + + public void send(AdvancedCoreUser user) { + RewardHandler handler = user.getPlugin().getRewardHandler(); + if (reward == null) { + if (data != null) { + handler.giveReward(user, data, path, rewardOptions); + } + } else { + handler.giveReward(user, reward, rewardOptions); + } + } + + public void send(AdvancedCoreUser... users) { + for (AdvancedCoreUser user : users) { + send(user); + } + } + + public void send(ArrayList users) { + for (AdvancedCoreUser user : users) { + send(user); + } + } + + public void send(OfflinePlayer p) { + send(AdvancedCorePlugin.getInstance().getUserManager().getUser(p)); + } + + public void send(Player p) { + send(AdvancedCorePlugin.getInstance().getUserManager().getUser(p)); + } + + public RewardBuilder setCheckTimed(boolean checkTimed) { + getRewardOptions().setCheckTimed(checkTimed); + return this; + } + + public RewardBuilder setGiveOffline(boolean giveOffline) { + getRewardOptions().setGiveOffline(giveOffline); + return this; + } + + public RewardBuilder setIgnoreChance(boolean ignoreChance) { + getRewardOptions().setIgnoreChance(ignoreChance); + return this; + } + + public RewardBuilder setIgnoreRequirements(boolean ignoreRequirements) { + getRewardOptions().setIgnoreRequirements(ignoreRequirements); + return this; + } + + public RewardBuilder setOnline(boolean online) { + getRewardOptions().setOnline(online); + return this; + } + + /** + * @param reward the reward to set + */ + public void setReward(Reward reward) { + this.reward = reward; + } + + public RewardBuilder setRewardOptions(RewardOptions rewardOptions) { + this.rewardOptions = rewardOptions; + return this; + } + + public RewardBuilder setServer(boolean b) { + if (b) { + getRewardOptions().setServer(b); + } + return this; + } + + public RewardBuilder withPlaceHolder(HashMap placeholders) { + this.rewardOptions.getPlaceholders().putAll(placeholders); + return this; + } + + public RewardBuilder withPlaceHolder(String toReplace, String replaceWith) { + this.rewardOptions.getPlaceholders().put(toReplace, replaceWith); + return this; + } + + public RewardBuilder withPrefix(String prefix) { + this.rewardOptions.setPrefix(prefix); + return this; + } + + public RewardBuilder withSuffix(String suffix) { + this.rewardOptions.setSuffix(suffix); + return this; + } + +} diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardExecutor.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardExecutor.java index 5f225d8aee..dc85523835 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardExecutor.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardExecutor.java @@ -1,6 +1,8 @@ package com.bencodez.advancedcore.api.rewards; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Base64; import org.bukkit.Bukkit; import org.bukkit.configuration.Configuration; @@ -17,6 +19,8 @@ */ public class RewardExecutor { + private static final String QUEUED_REFERENCE_PREFIX = "\\AdvancedCoreQueue/1/"; + private final RewardHandler handler; private final AdvancedCorePlugin plugin; @@ -122,9 +126,57 @@ public void giveReward(AdvancedCoreUser user, String reward, RewardOptions rewar MiscUtils.getInstance().executeConsoleCommands(user.getPlayerName(), reward, context.getPlaceholders()); return; } + giveReward(user, handler.getReward(reward), context.getOptions()); } + void givePersistedQueueReward(AdvancedCoreUser user, String reward, RewardOptions rewardOptions) { + RewardExecutionContext context = new RewardExecutionContext(rewardOptions).initializeOnlineState(user); + if (reward == null || reward.isEmpty()) { + return; + } + + String rewardName = reward; + Boolean generatedSnapshot = null; + if (reward.startsWith(QUEUED_REFERENCE_PREFIX)) { + String encoded = reward.substring(QUEUED_REFERENCE_PREFIX.length()); + int modeEnd = encoded.indexOf('/'); + if (modeEnd > 0) { + String mode = encoded.substring(0, modeEnd); + String encodedName = encoded.substring(modeEnd + 1); + if ((mode.equals("snapshot") || mode.equals("normal")) && !encodedName.isEmpty()) { + try { + rewardName = new String(Base64.getUrlDecoder().decode(encodedName), StandardCharsets.UTF_8); + generatedSnapshot = Boolean.valueOf(mode.equals("snapshot")); + } catch (IllegalArgumentException ignored) { + rewardName = reward; + } + } + } + } + + Reward resolved; + if (generatedSnapshot != null) { + if (generatedSnapshot.booleanValue()) { + resolved = handler.getQueuedGeneratedReward(rewardName, user.getUUID()); + } else { + resolved = handler.getReward(rewardName); + } + } else if (handler.rewardExist(rewardName) || handler.hasDirectRewardHandle(rewardName)) { + // Legacy persisted queue entry: a real registered reward wins over any + // stale generated file with the same name. + resolved = handler.getReward(rewardName); + } else { + // Legacy generated queue entries predate explicit provenance. This branch is + // reachable only through the PersistedQueueReference capability path. + resolved = handler.getQueuedGeneratedReward(rewardName, user.getUUID()); + if (resolved == null) { + resolved = handler.getReward(rewardName); + } + } + giveReward(user, resolved, context.getOptions()); + } + public void updateReward(Configuration data, String path, RewardOptions rewardOptions) { if (data == null || path == null || !data.isConfigurationSection(path)) { return; @@ -141,8 +193,10 @@ private void giveSectionReward(AdvancedCoreUser user, ConfigurationSection data, String rewardName = context.buildRewardName(path); DirectlyDefinedReward direct = handler.getDirectlyDefined(path); SubDirectlyDefinedReward sub = handler.getSubDirectlyDefined(rewardName); + SubRewardResolver resolver = handler.getSubRewardResolver(); + SubDirectlyDefinedReward fileSub = resolver == null ? null : resolver.getFileBackedSubReward(rewardName); - if (context.supportsDirectDispatch() && (direct != null || sub != null)) { + if (context.supportsDirectDispatch() && (direct != null || sub != null || fileSub != null)) { if (direct != null) { Reward reward = direct.getReward(); if (reward != null) { @@ -156,14 +210,14 @@ private void giveSectionReward(AdvancedCoreUser user, ConfigurationSection data, return; } - Reward reward = sub.getReward(); + SubDirectlyDefinedReward selectedSub = sub != null ? sub : fileSub; + Reward reward = selectedSub.getReward(); if (reward != null) { - plugin.debug("Giving subdirectlydefined reward " + rewardName + ", Options: " + options + " to " + plugin.debug("Giving sub reward " + rewardName + ", Options: " + options + " to " + user.getPlayerName() + "/" + user.getUUID()); giveReward(user, reward, options); } else { - plugin.debug("Failed to give subdirectlydefined reward " + path + ", Options: " + options - + ", Reward == null"); + plugin.debug("Failed to give sub reward " + path + ", Options: " + options + ", Reward == null"); } return; } diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardFileData.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardFileData.java index 0a9703f2cc..6844b1ea2d 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardFileData.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardFileData.java @@ -1,683 +1,695 @@ -package com.bencodez.advancedcore.api.rewards; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; - -import com.bencodez.advancedcore.AdvancedCorePlugin; -import com.bencodez.advancedcore.api.misc.files.FilesManager; -import com.bencodez.simpleapi.file.CaseInsensitiveSection; - -import lombok.Getter; -import net.md_5.bungee.api.ChatColor; - -/** - * The Class RewardFileData. - */ -public class RewardFileData { - - private ConfigurationSection configData; - - /** The data file. */ - private File dataFile; - - /** The data. */ - private FileConfiguration fileData; - - /** The plugin. */ - AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance(); - - /** The reward. */ - private Reward reward; - - @Getter - private File rewardFolder; - - public RewardFileData(Reward reward, ConfigurationSection section) { - this.reward = reward; - configData = new CaseInsensitiveSection(section); - } - - /** - * Instantiates a new reward file data. - * - * @param reward the reward - * @param rewardFolder the reward folder - */ - public RewardFileData(Reward reward, File rewardFolder) { - this.reward = reward; - dataFile = reward.getFile(); - - if (!rewardFolder.isDirectory()) { - rewardFolder = rewardFolder.getParentFile(); - } - - this.rewardFolder = rewardFolder; - - setup(); - } - - public void createSection(String path) { - if (fileData != null) { - fileData.createSection(path); - save(fileData); - reload(); - } - } - - /** - * Gets the chance. - * - * @return the chance - */ - public double getChance() { - return getConfigData().getDouble("Chance"); - } - - public Set getChoices() { - if (getConfigData().isConfigurationSection("Choices")) { - return getConfigData().getConfigurationSection("Choices").getKeys(false); - } - return new HashSet<>(); - } - - public ConfigurationSection getChoicesItem(String choice) { - return getConfigData().getConfigurationSection("Choices." + choice + ".DisplayItem"); - } - - public String getChoicesRewardsPath(String choice) { - return "Choices." + choice + ".Rewards"; - } - - /** - * Gets the commands console. - * - * @return the commands console - */ - @SuppressWarnings("unchecked") - public ArrayList getCommandsConsole() { - if (getConfigData().isList("Commands")) { - return (ArrayList) getConfigData().getList("Commands", new ArrayList<>()); - } - return (ArrayList) getConfigData().getList("Commands.Console", new ArrayList<>()); - - } - - /** - * Gets the commands player. - * - * @return the commands player - */ - @SuppressWarnings("unchecked") - public ArrayList getCommandsPlayer() { - return (ArrayList) getConfigData().getList("Commands.Player", new ArrayList<>()); - } - - public ConfigurationSection getConfigData() { - return configData; - } - - /** - * @return the dFile - */ - public File getDataFile() { - return dataFile; - } - - /** - * Gets the delayed enabled. - * - * @return the delayed enabled - */ - public boolean getDelayedEnabled() { - return getConfigData().getBoolean("Delayed.Enabled"); - } - - /** - * Gets the delayed hours. - * - * @return the delayed hours - */ - public int getDelayedHours() { - return getConfigData().getInt("Delayed.Hours"); - } - - public int getDelayedMilliSeconds() { - return getConfigData().getInt("Delayed.MilliSeconds"); - } - - /** - * Gets the delayed minutes. - * - * @return the delayed minutes - */ - public int getDelayedMinutes() { - return getConfigData().getInt("Delayed.Minutes"); - } - - public int getDelayedSeconds() { - return getConfigData().getInt("Delayed.Seconds"); - } - - /** - * Gets the data. - * - * @return the data - */ - /* - * private FileConfiguration getFileData() { return fileData; } - */ - - public ConfigurationSection getDisplayItem() { - return getConfigData().getConfigurationSection("DisplayItem"); - } - - /** - * Gets the choice rewards enabled. - * - * @return the choice rewards enabled - */ - public boolean getEnableChoices() { - return getConfigData().getBoolean("EnableChoices"); - } - - public FileConfiguration getFileData() { - return fileData; - } - - public boolean getForceOffline() { - return getConfigData().getBoolean("ForceOffline"); - } - - /** - * Gets the item amount. - * - * @param item the item - * @return the item amount - */ - @Deprecated - public int getItemAmount(String item) { - return getConfigData().getInt("Items." + item + ".Amount"); - } - - /** - * Gets the item data. - * - * @param item the item - * @return the item data - */ - @Deprecated - public int getItemData(String item) { - return getConfigData().getInt("Items." + item + ".Data"); - } - - /** - * Gets the item durability. - * - * @param item the item - * @return the item durability - */ - @Deprecated - public int getItemDurability(String item) { - return getConfigData().getInt("Items." + item + ".Durability"); - } - - /** - * Gets the item enchants. - * - * @param item the item - * @return the item enchants - */ - @Deprecated - public Set getItemEnchants(String item) { - try { - return getConfigData().getConfigurationSection("Items." + item + ".Enchants").getKeys(false); - } catch (Exception ex) { - return new HashSet<>(); - } - } - - /** - * Gets the item enchants level. - * - * @param item the item - * @param enchant the enchant - * @return the item enchants level - */ - @Deprecated - public int getItemEnchantsLevel(String item, String enchant) { - return getConfigData().getInt("Items." + item + ".Enchants." + enchant); - } - - /** - * Gets the item lore. - * - * @param item the item - * @return the item lore - */ - @SuppressWarnings("unchecked") - @Deprecated - public ArrayList getItemLore(String item) { - return (ArrayList) getConfigData().getList("Items." + item + ".Lore"); - } - - /** - * Gets the item material. - * - * @param item the item - * @return the item material - */ - @Deprecated - public String getItemMaterial(String item) { - return getConfigData().getString("Items." + item + ".Material"); - } - - /** - * Gets the item max amount. - * - * @param item the item - * @return the item max amount - */ - @Deprecated - public int getItemMaxAmount(String item) { - return getConfigData().getInt("Items." + item + ".MaxAmount"); - } - - /** - * Gets the item min amount. - * - * @param item the item - * @return the item min amount - */ - @Deprecated - public int getItemMinAmount(String item) { - return getConfigData().getInt("Items." + item + ".MinAmount"); - } - - /** - * Gets the item name. - * - * @param item the item - * @return the item name - */ - @Deprecated - public String getItemName(String item) { - return getConfigData().getString("Items." + item + ".Name"); - } - - /** - * Gets the items. - * - * @return the items - */ - public Set getItems() { - try { - return getConfigData().getConfigurationSection("Items").getKeys(false); - } catch (Exception ex) { - return new HashSet<>(); - } - } - - public ConfigurationSection getItemSection(String item) { - return getConfigData().getConfigurationSection("Items." + item); - } - - /** - * Gets the item skull. - * - * @param item the item - * @return the item skull - */ - @Deprecated - public String getItemSkull(String item) { - return getConfigData().getString("Items." + item + ".Skull"); - } - - /** - * Gets the permission. - * - * @return the permission - */ - public String getPermission() { - return getConfigData().getString("Permission", "AdvancedCore.Reward." + reward.getRewardName()); - } - - @SuppressWarnings("unchecked") - public ArrayList getPriority() { - return (ArrayList) getConfigData().getList("Priority", new ArrayList<>()); - } - - /** - * Gets the require permission. - * - * @return the require permission - */ - public boolean getRequirePermission() { - return getConfigData().getBoolean("RequirePermission"); - } - - /** - * Gets the reward type. - * - * @return the reward type - */ - public String getRewardType() { - String str = getConfigData().getString("RewardType", "BOTH"); - if (str != null) { - if (str.equalsIgnoreCase("online")) { - return "ONLINE"; - } - if (str.equalsIgnoreCase("offline")) { - return "OFFLINE"; - } else { - return "BOTH"; - } - } - return "BOTH"; - } - - public String getServer() { - return getConfigData().getString("Server", ""); - } - - /** - * Gets the timed enabled. - * - * @return the timed enabled - */ - public boolean getTimedEnabled() { - return getConfigData().getBoolean("Timed.Enabled"); - } - - /** - * Gets the timed hour. - * - * @return the timed hour - */ - public int getTimedHour() { - return getConfigData().getInt("Timed.Hour"); - } - - /** - * Gets the timed minute. - * - * @return the timed minute - */ - public int getTimedMinute() { - return getConfigData().getInt("Timed.Minute"); - } - - /** - * Gets the worlds. - * - * @return the worlds - */ - @SuppressWarnings("unchecked") - public ArrayList getWorlds() { - - return (ArrayList) getConfigData().getList("Worlds", new ArrayList<>()); - - } - - public boolean hasRewardFile() { - return dataFile != null; - } - - public boolean isDirectlyDefinedReward() { - return getConfigData().getBoolean("DirectlyDefinedReward"); - } - - public boolean isRewardFile() { - return dataFile != null && !isDirectlyDefinedReward(); - } - - /** - * Reload. - */ - public void reload() { - fileData = YamlConfiguration.loadConfiguration(dataFile); - configData = fileData.getConfigurationSection(""); - } - - public void save() { - FilesManager.getInstance().editFile(dataFile, fileData); - } - - public void save(FileConfiguration fileData) { - FilesManager.getInstance().editFile(dataFile, fileData); - } - - /** - * Sets the. - * - * @param path the path - * @param value the value - */ - public void set(String path, Object value) { - if (fileData != null) { - fileData.set(path, value); - save(fileData); - reload(); - } else { - plugin.debug("Editing invalid reward: " + reward.getName()); - } - } - - /** - * Sets the chance. - * - * @param d the new chance - */ - public void setChance(double d) { - set("Chance", d); - } - - /** - * Sets the commands console. - * - * @param value the new commands console - */ - public void setCommandsConsole(ArrayList value) { - set("Commands.Console", value); - } - - /** - * Sets the commands player. - * - * @param value the new commands player - */ - public void setCommandsPlayer(ArrayList value) { - set("Commands.Player", value); - } - - public void setConfigData(ConfigurationSection configData) { - this.configData = configData; - } - - /* - * public void setData(ConfigurationSection value) { getFileData().set("", - * value); reward.loadValues(); } - */ - - public void setData(ConfigurationSection value) { - // getFileData().set("", value); - Map map = value.getConfigurationSection("").getValues(true); - for (Entry entry : map.entrySet()) { - set(entry.getKey(), entry.getValue()); - } - reward.loadValues(); - } - - /** - * @param dFile the dFile to set - */ - public void setDataFile(File dFile) { - this.dataFile = dFile; - } - - public void setDirectlyDefinedReward(boolean b) { - set("DirectlyDefinedReward", b); - } - - /** - * Sets the give in each world. - * - * @param value the new give in each world - */ - public void setGiveInEachWorld(boolean value) { - set("GiveInEachWorld", value); - } - - /** - * Sets the item amount. - * - * @param item the item - * @param value the value - */ - public void setItemAmount(String item, int value) { - set("Items." + item + ".Amount", value); - } - - /** - * Sets the item data. - * - * @param item the item - * @param value the value - */ - public void setItemData(String item, int value) { - set("Items." + item + ".Data", value); - } - - /** - * Sets the item durability. - * - * @param item the item - * @param value the value - */ - public void setItemDurability(String item, int value) { - set("Items." + item + ".Durability", value); - } - - /** - * Sets the item enchant. - * - * @param item the item - * @param enchant the enchant - * @param value the value - */ - public void setItemEnchant(String item, String enchant, int value) { - set("Items." + item + ".Enchants." + enchant, value); - } - - /** - * Sets the item lore. - * - * @param item the item - * @param value the value - */ - public void setItemLore(String item, ArrayList value) { - set("Items." + item + ".Lore", value); - } - - /** - * Sets the item material. - * - * @param item the item - * @param value the value - */ - public void setItemMaterial(String item, String value) { - set("Items." + item + ".Material", value); - } - - /** - * Sets the item max amount. - * - * @param item the item - * @param value the value - */ - public void setItemMaxAmount(String item, int value) { - set("Items." + item + ".MaxAmount", value); - } - - /** - * Sets the item min amount. - * - * @param item the item - * @param value the value - */ - public void setItemMinAmount(String item, int value) { - set("Items." + item + ".MinAmount", value); - } - - /** - * Sets the item name. - * - * @param item the item - * @param value the value - */ - public void setItemName(String item, String value) { - set("Items." + item + ".Name", value); - } - - /** - * Sets the permission. - * - * @param perm the new permission - */ - public void setPermission(String perm) { - set("Permission", perm); - } - - /** - * Sets the require permission. - * - * @param value the new require permission - */ - public void setRequirePermission(boolean value) { - set("RequirePermission", value); - } - - /** - * Sets the reward type. - * - * @param value the new reward type - */ - public void setRewardType(String value) { - set("RewardType", value); - } - - /** - * Setup. - */ - public void setup() { - fileData = YamlConfiguration.loadConfiguration(dataFile); - - if (!dataFile.exists()) { - try { - fileData.save(dataFile); - } catch (IOException e) { - plugin.getLogger().severe(ChatColor.RED + "Could not create " + dataFile.getAbsolutePath()); - - } - } - configData = new CaseInsensitiveSection(fileData.getConfigurationSection("")); - } - - /** - * Sets the worlds. - * - * @param value the new worlds - */ - public void setWorlds(ArrayList value) { - set("Worlds", value); - } - -} +package com.bencodez.advancedcore.api.rewards; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.misc.files.FilesManager; +import com.bencodez.simpleapi.file.CaseInsensitiveSection; + +import lombok.Getter; +import net.md_5.bungee.api.ChatColor; + +/** + * The Class RewardFileData. + */ +public class RewardFileData { + + private ConfigurationSection configData; + + /** The data file. */ + private File dataFile; + + /** The data. */ + private FileConfiguration fileData; + + /** The plugin. */ + AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance(); + + /** The reward. */ + private Reward reward; + + @Getter + private File rewardFolder; + + public RewardFileData(Reward reward, ConfigurationSection section) { + this.reward = reward; + configData = new CaseInsensitiveSection(section); + } + + /** + * Instantiates a new reward file data. + * + * @param reward the reward + * @param rewardFolder the reward folder + */ + public RewardFileData(Reward reward, File rewardFolder) { + this.reward = reward; + dataFile = reward.getFile(); + + if (!rewardFolder.isDirectory()) { + rewardFolder = rewardFolder.getParentFile(); + } + + this.rewardFolder = rewardFolder; + + setup(); + } + + public void createSection(String path) { + if (fileData != null) { + fileData.createSection(path); + save(fileData); + reload(); + } + } + + /** + * Gets the chance. + * + * @return the chance + */ + public double getChance() { + return getConfigData().getDouble("Chance"); + } + + public Set getChoices() { + if (getConfigData().isConfigurationSection("Choices")) { + return getConfigData().getConfigurationSection("Choices").getKeys(false); + } + return new HashSet<>(); + } + + public ConfigurationSection getChoicesItem(String choice) { + return getConfigData().getConfigurationSection("Choices." + choice + ".DisplayItem"); + } + + public String getChoicesRewardsPath(String choice) { + return "Choices." + choice + ".Rewards"; + } + + /** + * Gets the commands console. + * + * @return the commands console + */ + @SuppressWarnings("unchecked") + public ArrayList getCommandsConsole() { + if (getConfigData().isList("Commands")) { + return (ArrayList) getConfigData().getList("Commands", new ArrayList<>()); + } + return (ArrayList) getConfigData().getList("Commands.Console", new ArrayList<>()); + + } + + /** + * Gets the commands player. + * + * @return the commands player + */ + @SuppressWarnings("unchecked") + public ArrayList getCommandsPlayer() { + return (ArrayList) getConfigData().getList("Commands.Player", new ArrayList<>()); + } + + public ConfigurationSection getConfigData() { + return configData; + } + + /** + * @return the dFile + */ + public File getDataFile() { + return dataFile; + } + + /** + * Gets the delayed enabled. + * + * @return the delayed enabled + */ + public boolean getDelayedEnabled() { + return getConfigData().getBoolean("Delayed.Enabled"); + } + + /** + * Gets the delayed hours. + * + * @return the delayed hours + */ + public int getDelayedHours() { + return getConfigData().getInt("Delayed.Hours"); + } + + public int getDelayedMilliSeconds() { + return getConfigData().getInt("Delayed.MilliSeconds"); + } + + /** + * Gets the delayed minutes. + * + * @return the delayed minutes + */ + public int getDelayedMinutes() { + return getConfigData().getInt("Delayed.Minutes"); + } + + public int getDelayedSeconds() { + return getConfigData().getInt("Delayed.Seconds"); + } + + /** + * Gets the data. + * + * @return the data + */ + /* + * private FileConfiguration getFileData() { return fileData; } + */ + + public ConfigurationSection getDisplayItem() { + return getConfigData().getConfigurationSection("DisplayItem"); + } + + /** + * Gets the choice rewards enabled. + * + * @return the choice rewards enabled + */ + public boolean getEnableChoices() { + return getConfigData().getBoolean("EnableChoices"); + } + + public FileConfiguration getFileData() { + return fileData; + } + + public boolean getForceOffline() { + return getConfigData().getBoolean("ForceOffline"); + } + + /** + * Gets the item amount. + * + * @param item the item + * @return the item amount + */ + @Deprecated + public int getItemAmount(String item) { + return getConfigData().getInt("Items." + item + ".Amount"); + } + + /** + * Gets the item data. + * + * @param item the item + * @return the item data + */ + @Deprecated + public int getItemData(String item) { + return getConfigData().getInt("Items." + item + ".Data"); + } + + /** + * Gets the item durability. + * + * @param item the item + * @return the item durability + */ + @Deprecated + public int getItemDurability(String item) { + return getConfigData().getInt("Items." + item + ".Durability"); + } + + /** + * Gets the item enchants. + * + * @param item the item + * @return the item enchants + */ + @Deprecated + public Set getItemEnchants(String item) { + try { + return getConfigData().getConfigurationSection("Items." + item + ".Enchants").getKeys(false); + } catch (Exception ex) { + return new HashSet<>(); + } + } + + /** + * Gets the item enchants level. + * + * @param item the item + * @param enchant the enchant + * @return the item enchants level + */ + @Deprecated + public int getItemEnchantsLevel(String item, String enchant) { + return getConfigData().getInt("Items." + item + ".Enchants." + enchant); + } + + /** + * Gets the item lore. + * + * @param item the item + * @return the item lore + */ + @SuppressWarnings("unchecked") + @Deprecated + public ArrayList getItemLore(String item) { + return (ArrayList) getConfigData().getList("Items." + item + ".Lore"); + } + + /** + * Gets the item material. + * + * @param item the item + * @return the item material + */ + @Deprecated + public String getItemMaterial(String item) { + return getConfigData().getString("Items." + item + ".Material"); + } + + /** + * Gets the item max amount. + * + * @param item the item + * @return the item max amount + */ + @Deprecated + public int getItemMaxAmount(String item) { + return getConfigData().getInt("Items." + item + ".MaxAmount"); + } + + /** + * Gets the item min amount. + * + * @param item the item + * @return the item min amount + */ + @Deprecated + public int getItemMinAmount(String item) { + return getConfigData().getInt("Items." + item + ".MinAmount"); + } + + /** + * Gets the item name. + * + * @param item the item + * @return the item name + */ + @Deprecated + public String getItemName(String item) { + return getConfigData().getString("Items." + item + ".Name"); + } + + /** + * Gets the items. + * + * @return the items + */ + public Set getItems() { + try { + return getConfigData().getConfigurationSection("Items").getKeys(false); + } catch (Exception ex) { + return new HashSet<>(); + } + } + + public ConfigurationSection getItemSection(String item) { + return getConfigData().getConfigurationSection("Items." + item); + } + + /** + * Gets the item skull. + * + * @param item the item + * @return the item skull + */ + @Deprecated + public String getItemSkull(String item) { + return getConfigData().getString("Items." + item + ".Skull"); + } + + /** + * Gets the permission. + * + * @return the permission + */ + public String getPermission() { + return getConfigData().getString("Permission", "AdvancedCore.Reward." + reward.getRewardName()); + } + + @SuppressWarnings("unchecked") + public ArrayList getPriority() { + return (ArrayList) getConfigData().getList("Priority", new ArrayList<>()); + } + + /** + * Gets the require permission. + * + * @return the require permission + */ + public boolean getRequirePermission() { + return getConfigData().getBoolean("RequirePermission"); + } + + /** + * Gets the reward type. + * + * @return the reward type + */ + public String getRewardType() { + String str = getConfigData().getString("RewardType", "BOTH"); + if (str != null) { + if (str.equalsIgnoreCase("online")) { + return "ONLINE"; + } + if (str.equalsIgnoreCase("offline")) { + return "OFFLINE"; + } else { + return "BOTH"; + } + } + return "BOTH"; + } + + public String getServer() { + return getConfigData().getString("Server", ""); + } + + /** + * Gets the timed enabled. + * + * @return the timed enabled + */ + public boolean getTimedEnabled() { + return getConfigData().getBoolean("Timed.Enabled"); + } + + /** + * Gets the timed hour. + * + * @return the timed hour + */ + public int getTimedHour() { + return getConfigData().getInt("Timed.Hour"); + } + + /** + * Gets the timed minute. + * + * @return the timed minute + */ + public int getTimedMinute() { + return getConfigData().getInt("Timed.Minute"); + } + + /** + * Gets the worlds. + * + * @return the worlds + */ + @SuppressWarnings("unchecked") + public ArrayList getWorlds() { + + return (ArrayList) getConfigData().getList("Worlds", new ArrayList<>()); + + } + + public boolean hasRewardFile() { + return dataFile != null; + } + + public boolean isDirectlyDefinedReward() { + return getConfigData().getBoolean("DirectlyDefinedReward"); + } + + public boolean isRewardFile() { + return dataFile != null && !isDirectlyDefinedReward(); + } + + /** + * Reload. + */ + public void reload() { + fileData = YamlConfiguration.loadConfiguration(dataFile); + configData = fileData.getConfigurationSection(""); + } + + public void save() { + FilesManager.getInstance().editFile(dataFile, fileData); + } + + public void save(FileConfiguration fileData) { + FilesManager.getInstance().editFile(dataFile, fileData); + } + + /** + * Sets the. + * + * @param path the path + * @param value the value + */ + public void set(String path, Object value) { + if (fileData != null) { + fileData.set(path, value); + save(fileData); + reload(); + } else { + plugin.debug("Editing invalid reward: " + reward.getName()); + } + } + + /** + * Sets the chance. + * + * @param d the new chance + */ + public void setChance(double d) { + set("Chance", d); + } + + /** + * Sets the commands console. + * + * @param value the new commands console + */ + public void setCommandsConsole(ArrayList value) { + set("Commands.Console", value); + } + + /** + * Sets the commands player. + * + * @param value the new commands player + */ + public void setCommandsPlayer(ArrayList value) { + set("Commands.Player", value); + } + + public void setConfigData(ConfigurationSection configData) { + this.configData = configData; + } + + /* + * public void setData(ConfigurationSection value) { getFileData().set("", + * value); reward.loadValues(); } + */ + + public void setData(ConfigurationSection value) { + if (fileData == null || value == null) { + plugin.debug("Editing invalid reward: " + reward.getName()); + return; + } + + // Snapshot writes replace the previous contents completely. Reusing a generated + // queue file must not retain reward actions that were removed from the source + // section after an earlier snapshot was created. + for (String key : new HashSet<>(fileData.getKeys(false))) { + fileData.set(key, null); + } + Map map = value.getConfigurationSection("").getValues(true); + for (Entry entry : map.entrySet()) { + fileData.set(entry.getKey(), entry.getValue()); + } + save(fileData); + reload(); + reward.loadValues(); + } + + /** + * @param dFile the dFile to set + */ + public void setDataFile(File dFile) { + this.dataFile = dFile; + } + + public void setDirectlyDefinedReward(boolean b) { + set("DirectlyDefinedReward", b); + } + + /** + * Sets the give in each world. + * + * @param value the new give in each world + */ + public void setGiveInEachWorld(boolean value) { + set("GiveInEachWorld", value); + } + + /** + * Sets the item amount. + * + * @param item the item + * @param value the value + */ + public void setItemAmount(String item, int value) { + set("Items." + item + ".Amount", value); + } + + /** + * Sets the item data. + * + * @param item the item + * @param value the value + */ + public void setItemData(String item, int value) { + set("Items." + item + ".Data", value); + } + + /** + * Sets the item durability. + * + * @param item the item + * @param value the value + */ + public void setItemDurability(String item, int value) { + set("Items." + item + ".Durability", value); + } + + /** + * Sets the item enchant. + * + * @param item the item + * @param enchant the enchant + * @param value the value + */ + public void setItemEnchant(String item, String enchant, int value) { + set("Items." + item + ".Enchants." + enchant, value); + } + + /** + * Sets the item lore. + * + * @param item the item + * @param value the value + */ + public void setItemLore(String item, ArrayList value) { + set("Items." + item + ".Lore", value); + } + + /** + * Sets the item material. + * + * @param item the item + * @param value the value + */ + public void setItemMaterial(String item, String value) { + set("Items." + item + ".Material", value); + } + + /** + * Sets the item max amount. + * + * @param item the item + * @param value the value + */ + public void setItemMaxAmount(String item, int value) { + set("Items." + item + ".MaxAmount", value); + } + + /** + * Sets the item min amount. + * + * @param item the item + * @param value the value + */ + public void setItemMinAmount(String item, int value) { + set("Items." + item + ".MinAmount", value); + } + + /** + * Sets the item name. + * + * @param item the item + * @param value the value + */ + public void setItemName(String item, String value) { + set("Items." + item + ".Name", value); + } + + /** + * Sets the permission. + * + * @param perm the new permission + */ + public void setPermission(String perm) { + set("Permission", perm); + } + + /** + * Sets the require permission. + * + * @param value the new require permission + */ + public void setRequirePermission(boolean value) { + set("RequirePermission", value); + } + + /** + * Sets the reward type. + * + * @param value the new reward type + */ + public void setRewardType(String value) { + set("RewardType", value); + } + + /** + * Setup. + */ + public void setup() { + fileData = YamlConfiguration.loadConfiguration(dataFile); + + if (!dataFile.exists()) { + try { + fileData.save(dataFile); + } catch (IOException e) { + plugin.getLogger().severe(ChatColor.RED + "Could not create " + dataFile.getAbsolutePath()); + + } + } + configData = new CaseInsensitiveSection(fileData.getConfigurationSection("")); + } + + /** + * Sets the worlds. + * + * @param value the new worlds + */ + public void setWorlds(ArrayList value) { + set("Worlds", value); + } + +} diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardHandler.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardHandler.java index 137c224a26..885e062615 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardHandler.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardHandler.java @@ -20,6 +20,7 @@ import com.bencodez.advancedcore.api.rewards.injected.RewardInject; import com.bencodez.advancedcore.api.rewards.injectedrequirement.RequirementInject; import com.bencodez.advancedcore.api.user.AdvancedCoreUser; +import com.bencodez.advancedcore.api.user.PersistedQueueReference; import com.bencodez.advancedcore.api.user.UserStartup; import com.bencodez.advancedcore.command.gui.RewardEditGUI; @@ -143,6 +144,10 @@ public Reward getRewardDirectlyDefined(String reward) { return rewardLoader.getRewardDirectlyDefined(reward); } + public Reward getQueuedGeneratedReward(String reward, String userUuid) { + return rewardLoader.getQueuedGeneratedReward(reward, userUuid); + } + public ArrayList getRewardFiles(File folder) { return rewardLoader.getRewardFiles(folder); } @@ -187,6 +192,14 @@ public void giveReward(AdvancedCoreUser user, String reward, RewardOptions rewar rewardExecutor.giveReward(user, reward, rewardOptions); } + public void givePersistedQueueReward(AdvancedCoreUser user, PersistedQueueReference rewardReference, + RewardOptions rewardOptions) { + if (rewardReference == null) { + return; + } + rewardExecutor.givePersistedQueueReward(user, rewardReference.getReference(), rewardOptions); + } + public boolean hasDirectRewardHandle(String reward) { return rewardRegistry.hasDirectRewardHandle(reward); } diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardLoader.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardLoader.java index 14ac22011e..0912da1b3c 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardLoader.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardLoader.java @@ -3,6 +3,11 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +import org.bukkit.configuration.file.YamlConfiguration; import com.bencodez.advancedcore.AdvancedCorePlugin; import com.bencodez.advancedcore.api.exceptions.FileDirectoryException; @@ -16,6 +21,7 @@ public class RewardLoader { private final RewardHandler handler; private final AdvancedCorePlugin plugin; private final ArrayList rewardFolders = new ArrayList<>(); + private final Set suppressedDirectlyDefinedRewards = new HashSet<>(); public RewardLoader(RewardHandler handler, AdvancedCorePlugin plugin) { this.handler = handler; @@ -63,6 +69,28 @@ public void checkDirectlyDefined() { loadRewards(); } + /** + * Generated DirectlyDefinedReward files are persisted execution snapshots for + * delayed/offline queues. They must remain on disk, but they are never loaded + * into the public reward registry. Queue replay resolves them explicitly via + * getQueuedGeneratedReward(). + */ + private void suppressGeneratedDirectlyDefinedFiles() { + suppressedDirectlyDefinedRewards.clear(); + for (File folder : rewardFolders) { + if (folder == null || !folder.getName().equalsIgnoreCase("DirectlyDefined")) { + continue; + } + for (String fileName : getRewardFiles(folder)) { + File generatedFile = new File(folder, fileName); + YamlConfiguration data = YamlConfiguration.loadConfiguration(generatedFile); + if (data.getBoolean("DirectlyDefinedReward", false)) { + suppressedDirectlyDefinedRewards.add(fileName.toLowerCase(Locale.ROOT)); + } + } + } + } + private void copyFile(String fileName) { File file = new File(plugin.getDataFolder(), "Rewards" + File.separator + fileName); if (!file.exists()) { @@ -109,9 +137,37 @@ public Reward getRewardDirectlyDefined(String reward) { File directFolder = new File(getDefaultFolder().getAbsolutePath() + File.separator + "DirectlyDefined"); directFolder.mkdirs(); + File existing = new File(directFolder, reward + ".yml"); + if (isGeneratedSnapshot(existing)) { + plugin.getLogger().warning("Blocked standalone lookup of generated queued reward " + reward); + return new QueuedGeneratedReward(directFolder, reward, Collections.emptySet()); + } return new Reward(directFolder, reward); } + public Reward getQueuedGeneratedReward(String reward, String userUuid) { + if (reward == null || reward.isEmpty() || userUuid == null || userUuid.isEmpty()) { + return null; + } + reward = RewardRegistry.normalizeLookupName(reward); + if (!RewardRegistry.isSafeRewardFileName(reward)) { + return null; + } + File directFolder = new File(getDefaultFolder().getAbsolutePath() + File.separator + "DirectlyDefined"); + File file = new File(directFolder, reward + ".yml"); + if (!isGeneratedSnapshot(file)) { + return null; + } + return new QueuedGeneratedReward(directFolder, reward, Set.of(userUuid)); + } + + private boolean isGeneratedSnapshot(File file) { + if (file == null || !file.isFile()) { + return false; + } + return YamlConfiguration.loadConfiguration(file).getBoolean("DirectlyDefinedReward", false); + } + public ArrayList getRewardFiles(File folder) { ArrayList fileNames = new ArrayList<>(); if (folder != null && folder.exists()) { @@ -142,6 +198,7 @@ public ArrayList getRewardNames(File file) { } public void loadRewards() { + suppressGeneratedDirectlyDefinedFiles(); handler.getRewardRegistry().resetRewards(); setupExample(); handler.addValidPath("DirectlyDefinedReward"); @@ -160,6 +217,12 @@ public void loadRewards() { private void loadRewards(File file) { for (String reward : getRewardNames(file)) { if (!reward.equals("")) { + String exactFileName = (reward + ".yml").toLowerCase(Locale.ROOT); + if (file.getName().equalsIgnoreCase("DirectlyDefined") + && suppressedDirectlyDefinedRewards.contains(exactFileName)) { + plugin.extraDebug("Suppressing generated queued reward from standalone lookup: " + reward); + continue; + } if (!handler.rewardExist(reward)) { try { Reward reward1 = new Reward(file, reward); diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardRegistry.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardRegistry.java index fa360ea950..242c6d4e6f 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardRegistry.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardRegistry.java @@ -1,5 +1,6 @@ package com.bencodez.advancedcore.api.rewards; +import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -93,6 +94,11 @@ public Reward getReward(String reward) { } } + if (!isSafeRewardFileName(reward)) { + plugin.getLogger().warning("Rejected unsafe reward file name: " + reward); + throw new IllegalArgumentException("Reward name must not contain path separators or be an absolute path"); + } + return new Reward(reward); } @@ -138,6 +144,13 @@ public static String normalizeDirectPath(String path) { return normalizeLookupName(path).replace('.', '_').toLowerCase(Locale.ROOT); } + static boolean isSafeRewardFileName(String reward) { + if (reward == null || reward.indexOf('\0') >= 0 || new File(reward).isAbsolute()) { + return false; + } + return reward.indexOf('/') < 0 && reward.indexOf('\\') < 0; + } + public boolean rewardExist(String reward) { reward = normalizeLookupName(reward); if (reward.isEmpty()) { @@ -160,6 +173,14 @@ public void resetSubDirectlyDefinedRewards() { } public void updateReward(Reward reward) { + RewardFileData config = reward == null ? null : reward.getConfig(); + if (config != null && config.isDirectlyDefinedReward()) { + File folder = config.getRewardFolder(); + if (folder != null && folder.getName().equalsIgnoreCase("DirectlyDefined")) { + plugin.extraDebug("Keeping generated queued reward snapshot out of public registry: " + reward.getName()); + return; + } + } reward.validate(); for (int i = getRewards().size() - 1; i >= 0; i--) { if (getRewards().get(i).getFile().getPath().equals(reward.getFile().getPath())) { diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubDirectlyDefinedReward.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubDirectlyDefinedReward.java index 2b5641cb36..b7d7c88f98 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubDirectlyDefinedReward.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubDirectlyDefinedReward.java @@ -37,12 +37,25 @@ public String getFullPath() { public Reward getReward() { if (isDirectlyDefined()) { - return new Reward((master.getFullPath() + master.needsDot() + getPath()).replace(".", "_"), - getFileData().getConfigurationSection(getPath())).needsRewardFile(false); + Reward reward = new Reward((master.getFullPath() + master.needsDot() + getPath()).replace(".", "_"), + getFileData().getConfigurationSection(getPath())); + // File-backed sub rewards are not exposed through standalone lookup, but they + // still need a generated queue snapshot if delayed/timed/offline execution + // defers them. Directly-defined sub rewards already have a stable internal + // definition and do not need snapshot materialization. + return isFileBacked() ? reward : reward.needsRewardFile(false); } return null; } + private boolean isFileBacked() { + DefinedReward current = master; + while (current instanceof SubDirectlyDefinedReward) { + current = ((SubDirectlyDefinedReward) current).getMaster(); + } + return current instanceof RewardFileDefinedReward; + } + public Object getValue(String path) { return getFileData().get(getPath() + "." + path); } diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubRewardResolver.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubRewardResolver.java index 65b2caa399..994a7954b1 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubRewardResolver.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubRewardResolver.java @@ -2,17 +2,19 @@ import java.util.HashSet; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import com.bencodez.advancedcore.AdvancedCorePlugin; import com.bencodez.advancedcore.api.rewards.injected.RewardInject; /** - * Discovers directly-defined sub rewards and prevents recursive discovery loops. + * Discovers sub rewards and prevents recursive discovery loops. */ public class SubRewardResolver { private final RewardHandler handler; private final AdvancedCorePlugin plugin; + private final CopyOnWriteArrayList fileBackedSubRewards = new CopyOnWriteArrayList<>(); public SubRewardResolver(RewardHandler handler, AdvancedCorePlugin plugin) { this.handler = handler; @@ -22,21 +24,32 @@ public SubRewardResolver(RewardHandler handler, AdvancedCorePlugin plugin) { public void checkSubRewards() { plugin.extraDebug("Checking directlydefined rewards for sub rewards"); handler.getRewardRegistry().resetSubDirectlyDefinedRewards(); + fileBackedSubRewards.clear(); for (DirectlyDefinedReward direct : handler.getDirectlyDefinedRewards()) { - checkSubRewards(direct, new HashSet<>()); + checkSubRewards(direct, new HashSet<>(), true); } - plugin.extraDebug("Checking reward file for sub rewards"); + plugin.extraDebug("Checking reward files for internal sub rewards"); for (Reward reward : handler.getRewards()) { - checkSubRewards(new RewardFileDefinedReward(reward), new HashSet<>()); + checkSubRewards(new RewardFileDefinedReward(reward), new HashSet<>(), false); } } public void checkSubRewards(DefinedReward direct) { - checkSubRewards(direct, new HashSet<>()); + checkSubRewards(direct, new HashSet<>(), !(direct instanceof RewardFileDefinedReward)); } - private void checkSubRewards(DefinedReward direct, Set activePaths) { + public SubDirectlyDefinedReward getFileBackedSubReward(String path) { + String normalized = RewardRegistry.normalizeDirectPath(path); + for (SubDirectlyDefinedReward sub : fileBackedSubRewards) { + if (RewardRegistry.normalizeDirectPath(sub.getFullPath()).equals(normalized)) { + return sub; + } + } + return null; + } + + private void checkSubRewards(DefinedReward direct, Set activePaths, boolean exposeStandalone) { String path = direct.getFullPath(); if (!activePaths.add(path)) { plugin.getLogger().warning("Detected recursive sub reward path, skipping: " + path); @@ -50,12 +63,22 @@ private void checkSubRewards(DefinedReward direct, Set activePaths) { plugin.getLogger().warning("Detected recursive sub reward path, skipping: " + subPath); continue; } - handler.addSubDirectlyDefined(sub); - checkSubRewards(sub, activePaths); + if (exposeStandalone) { + handler.addSubDirectlyDefined(sub); + } else { + addFileBackedSubReward(sub); + } + checkSubRewards(sub, activePaths, exposeStandalone); } } } finally { activePaths.remove(path); } } + + private void addFileBackedSubReward(SubDirectlyDefinedReward sub) { + if (getFileBackedSubReward(sub.getFullPath()) == null) { + fileBackedSubRewards.add(sub); + } + } } diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedPriority.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedPriority.java index 92edd4b28c..fc1bb37eb7 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedPriority.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedPriority.java @@ -32,7 +32,9 @@ public static void register(RewardHandler handler, AdvancedCorePlugin plugin) { public String onRewardRequested(Reward sourceReward, AdvancedCoreUser user, ConfigurationSection section, HashMap placeholders) { for (String key : section.getKeys(false)) { - Reward reward = handler.getReward(section, key, new RewardOptions()); + RewardOptions namingOptions = new RewardOptions() + .setPrefix(sourceReward.getName() + "_AdvancedPriority"); + Reward reward = handler.getReward(section, key, namingOptions); if (reward != null && reward.canGiveReward(user, new RewardOptions().withPlaceHolder(placeholders))) { plugin.extraDebug("AdvancedPriority: Giving reward " + reward.getName()); reward.giveReward(user, new RewardOptions().setIgnoreChance(true).setIgnoreRequirements(true) diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedRewards.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedRewards.java index 56199e4232..f3701c64ff 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedRewards.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/builtin/RewardAdvancedRewards.java @@ -37,7 +37,7 @@ public String onRewardRequested(Reward reward, AdvancedCoreUser user, Configurat ArrayList rewards = ArrayUtils.convert(keys); for (String rewardName : rewards) { handler.giveReward(user, section, rewardName, new RewardOptions().setPlaceholders(placeholders) - .setPrefix(reward.getRewardName() + "_AdvancedRewards_" + rewardName)); + .setPrefix(reward.getRewardName() + "_AdvancedRewards")); } return null; } diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/AdvancedCoreUser.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/AdvancedCoreUser.java index 4cb9445372..666e041500 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/AdvancedCoreUser.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/AdvancedCoreUser.java @@ -1,1661 +1,1672 @@ -package com.bencodez.advancedcore.api.user; - -import java.text.SimpleDateFormat; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import org.bukkit.Bukkit; -import org.bukkit.Effect; -import org.bukkit.NamespacedKey; -import org.bukkit.OfflinePlayer; -import org.bukkit.Particle; -import org.bukkit.Registry; -import org.bukkit.Sound; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.metadata.MetadataValue; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; - -import com.bencodez.advancedcore.AdvancedCorePlugin; -import com.bencodez.advancedcore.api.item.ItemBuilder; -import com.bencodez.advancedcore.api.messages.PlaceholderUtils; -import com.bencodez.advancedcore.api.misc.PlayerManager; -import com.bencodez.advancedcore.api.misc.effects.BossBar; -import com.bencodez.advancedcore.api.misc.effects.Title; -import com.bencodez.advancedcore.api.rewards.Reward; -import com.bencodez.advancedcore.api.rewards.RewardBuilder; -import com.bencodez.advancedcore.api.rewards.RewardHandler; -import com.bencodez.advancedcore.api.rewards.RewardOptions; -import com.bencodez.advancedcore.api.user.usercache.UserDataCache; -import com.bencodez.simpleapi.array.ArrayUtils; -import com.bencodez.simpleapi.messages.actionbar.ActionBar; -import com.bencodez.simpleapi.player.PlayerUtils; -import com.bencodez.simpleapi.sql.Column; - -import lombok.Getter; -import lombok.Setter; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.chat.ComponentSerializer; - -/** - * The Class User. - */ -public class AdvancedCoreUser { - - /** - * User data fetch mode for this user. - * - * @return the user data fetch mode - */ - @Getter - private UserDataFetchMode userDataFetchMode = UserDataFetchMode.DEFAULT; - - /** - * User data for this user. - * - * @param data the user data to set - */ - @Setter - private UserData data; - - private boolean loadName = true; - - /** The player name. */ - private String playerName; - - /** - * Plugin instance. - * - * @return the plugin instance - */ - @Getter - private AdvancedCorePlugin plugin = null; - - /** The uuid. */ - private String uuid; - - /** - * Instantiates a new user from an existing user. - * - * @param plugin the plugin - * @param user the user to copy from - */ - public AdvancedCoreUser(AdvancedCorePlugin plugin, AdvancedCoreUser user) { - this.userDataFetchMode = user.userDataFetchMode; - this.data = user.getUserData(); - this.uuid = user.getUUID(); - this.playerName = user.getPlayerName(); - this.loadName = user.loadName; - this.plugin = plugin; - loadData(); - getUserData().setTempCache(user.getUserData().getTempCache()); - } - - /** - * Instantiates a new user. - * - * @param plugin the plugin - * @param player the player - */ - @Deprecated - public AdvancedCoreUser(AdvancedCorePlugin plugin, Player player) { - this.plugin = plugin; - loadData(); - uuid = player.getUniqueId().toString(); - setPlayerName(player.getName()); - } - - /** - * Instantiates a new user. - * - * @param plugin the plugin - * @param playerName the player name - */ - @Deprecated - public AdvancedCoreUser(AdvancedCorePlugin plugin, String playerName) { - this.plugin = plugin; - loadData(); - uuid = PlayerManager.getInstance().getUUID(playerName); - setPlayerName(playerName); - } - - /** - * Instantiates a new user. - * - * @param plugin the plugin - * @param uuid the uuid - */ - @Deprecated - public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid) { - this.plugin = plugin; - this.uuid = uuid.toString(); - loadData(); - setPlayerName(PlayerManager.getInstance().getPlayerName(this, this.uuid, false)); - } - - /** - * Instantiates a new user. - * - * @param plugin the plugin - * @param uuid the uuid - * @param loadName the load name - */ - @Deprecated - public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid, boolean loadName) { - this.plugin = plugin; - this.uuid = uuid.toString(); - this.loadName = loadName; - loadData(); - if (this.loadName) { - setPlayerName(PlayerManager.getInstance().getPlayerName(this, this.uuid)); - } - - } - - /** - * Instantiates a new user. - * - * @param plugin the plugin - * @param uuid the uuid - * @param loadName the load name - * @param loadData the load data - */ - @Deprecated - public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid, boolean loadName, boolean loadData) { - this.plugin = plugin; - this.uuid = uuid.toString(); - this.loadName = loadName; - if (loadData) { - loadData(); - } - if (this.loadName) { - setPlayerName(PlayerManager.getInstance().getPlayerName(this, this.uuid)); - } - - } - - /** - * Instantiates a new user. - * - * @param plugin the plugin - * @param uuid the uuid - * @param playerName the player name - */ - @Deprecated - public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid, String playerName) { - this.plugin = plugin; - this.uuid = uuid.toString(); - if (!plugin.getOptions().isOnlineMode()) { - this.uuid = PlayerManager.getInstance().getUUID(playerName); - } - loadData(); - setPlayerName(playerName); - } - - /** - * Adds offline rewards to be given when the player next logs in. - * - * @param reward the reward - * @param placeholders the placeholders - */ - public void addOfflineRewards(Reward reward, HashMap placeholders) { - synchronized (plugin) { - ArrayList offlineRewards = getOfflineRewards(); - offlineRewards.add(reward.getRewardName() + "%placeholders%" + ArrayUtils.makeString(placeholders)); - setOfflineRewards(offlineRewards); - } - } - - /** - * Adds a permission to the player. - * - * @param permission the permission - */ - public void addPermission(String permission) { - plugin.getPermissionHandler().addPermission(getPlayer(), permission); - } - - /** - * Adds a permission to the player with a delay. - * - * @param permission the permission - * @param delay the delay in milliseconds - */ - public void addPermission(String permission, long delay) { - plugin.getPermissionHandler().addPermission(getPlayer(), permission, delay); - } - - /** - * Adds a timed reward to be given at a specific time. - * - * @param reward the reward - * @param placeholders the placeholders - * @param epochMilli the epoch time in milliseconds when the reward should be - * given - */ - public synchronized void addTimedReward(Reward reward, HashMap placeholders, long epochMilli) { - HashMap timed = getTimedRewards(); - String rewardName = reward.getRewardName(); - rewardName += "%extime%" + System.currentTimeMillis(); - - timed.put(rewardName + "%placeholders%" + ArrayUtils.makeString(placeholders), epochMilli); - setTimedRewards(timed); - loadTimedDelayedTimer(epochMilli); - } - - /** - * Adds an unclaimed choice reward. - * - * @param name the reward name - */ - public void addUnClaimedChoiceReward(String name) { - ArrayList choices = getUnClaimedChoices(); - choices.add(name); - setUnClaimedChoice(choices); - } - - /** - * Caches the user data. - */ - public void cache() { - plugin.getUserManager().getDataManager().cacheUser(UUID.fromString(uuid), getPlayerName()); - } - - /** - * Caches the user data asynchronously. - */ - public void cacheAsync() { - getPlugin().getBukkitScheduler().runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - cache(); - } - }); - } - - /** - * Caches the user data if needed. - */ - public void cacheIfNeeded() { - plugin.getUserManager().getDataManager().cacheUserIfNeeded(UUID.fromString(uuid)); - } - - /** - * Checks and processes delayed/timed rewards. - */ - public void checkDelayedTimedRewards() { - plugin.debug("Checking timed/delayed for " + getPlayerName()); - HashMap timed = getTimedRewards(); - HashMap newTimed = new HashMap<>(); - for (Entry entry : timed.entrySet()) { - long time = entry.getValue(); - - if (time != 0) { - Date timeDate = new Date(time); - if (new Date().after(timeDate)) { - String[] data = entry.getKey().split("%placeholders%"); - String rewardName = data[0]; - rewardName = rewardName.split("%extime%")[0]; - String placeholders = ""; - if (data.length > 1) { - placeholders = data[1]; - } - new RewardBuilder(plugin.getRewardHandler().getReward(rewardName)).setCheckTimed(false) - .withPlaceHolder(ArrayUtils.fromString(placeholders)) - .withPlaceHolder("date", - "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(new Date(time))) - .send(this); - plugin.debug("Giving timed/delayed reward " + rewardName + " for " + getPlayerName() - + " with placeholders " + ArrayUtils.fromString(placeholders)); - } else { - newTimed.put(entry.getKey(), time); - } - } - - } - setTimedRewards(newTimed); - } - - /** - * Check offline rewards. - */ - public void checkOfflineRewards() { - if (!plugin.getOptions().isProcessRewards()) { - plugin.debug("Processing rewards is disabled"); - return; - } - if (isCheckWorld()) { - setCheckWorld(false); - } - ArrayList rewards = getOfflineRewards(); - if (rewards.isEmpty()) { - return; - } - - setOfflineRewards(new ArrayList<>()); - RewardHandler rewardHandler = plugin.getRewardHandler(); - AdvancedCoreUser user = this; - - for (String rewardEntry : rewards) { - if (rewardEntry == null || rewardEntry.equals("null")) { - continue; - } - - String[] parts = rewardEntry.split("%placeholders%", 2); - String rewardName = parts[0]; - String placeholderStr = parts.length > 1 ? parts[1] : ""; - - RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false) - .withPlaceHolder(ArrayUtils.fromString(placeholderStr)); - - rewardHandler.giveReward(user, rewardName, options); - } - - } - - /** - * Clears the user cache. - */ - public void clearCache() { - if (isCached()) { - getCache().clearCache(); - } - } - - /** - * Clears the temporary cache. - */ - public void clearTempCache() { - getUserData().clearTempCache(); - } - - /** - * Closes the player's inventory. - */ - public void closeInv() { - if (plugin.isEnabled()) { - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - Player player = getPlayer(); - if (player != null) { - player.closeInventory(); - } - } - }, getPlayer()); - } - } - - /** - * Sets the user data fetch mode. - * - * @param mode the fetch mode - * @return this user instance - */ - public AdvancedCoreUser userDataFetechMode(UserDataFetchMode mode) { - this.userDataFetchMode = mode; - return this; - } - - /** - * Forces running of offline rewards without processing checks. - */ - public void forceRunOfflineRewards() { - if (!plugin.getOptions().isProcessRewards()) { - plugin.debug("Processing rewards is disabled"); - return; - } - - setCheckWorld(false); - ArrayList rewards = getOfflineRewards(); - if (rewards.isEmpty()) { - return; - } - - setOfflineRewards(new ArrayList<>()); - RewardHandler rewardHandler = plugin.getRewardHandler(); - AdvancedCoreUser user = this; - - for (String rewardEntry : rewards) { - if (rewardEntry == null || rewardEntry.equals("null")) { - continue; - } - - String[] parts = rewardEntry.split("%placeholders%", 2); - String rewardName = parts[0]; - String placeholderStr = parts.length > 1 ? parts[1] : ""; - - RewardOptions options = new RewardOptions().setOnline(false).setGiveOffline(false).forceOffline() - .setCheckTimed(false).withPlaceHolder(ArrayUtils.fromString(placeholderStr)); - - rewardHandler.giveReward(user, rewardName, options); - } - } - - /** - * Gets the user data cache. - * - * @return the cache - */ - public UserDataCache getCache() { - return plugin.getUserManager().getDataManager().getCache(java.util.UUID.fromString(getUUID())); - } - - /** - * Gets the user's choice preference for a reward. - * - * @param rewardName the reward name - * @return the choice preference - */ - public String getChoicePreference(String rewardName) { - ArrayList data = getChoicePreferenceData(); - - for (String str : data) { - String[] data1 = str.split(":"); - if (data1.length > 1) { - if (data1[0].equals(rewardName)) { - return data1[1]; - } - } - } - return ""; - } - - /** - * Gets the choice preference data. - * - * @return the choice preference data list - */ - public ArrayList getChoicePreferenceData() { - return getData().getStringList("ChoicePreference", userDataFetchMode); - } - - /** - * Gets the user data. - * - * @return the user data - */ - public UserData getData() { - if (data == null) { - loadData(); - } - return data; - } - - /** - * Gets the input method. - * - * @return the input method string - */ - public String getInputMethod() { - return getUserData().getString("InputMethod", userDataFetchMode); - } - - /** - * Gets the Java UUID object. - * - * @return the Java UUID - */ - public UUID getJavaUUID() { - return UUID.fromString(uuid); - } - - /** - * Gets the last online time in milliseconds. - * - * @return the last online time - */ - public long getLastOnline() { - String d = getData().getString("LastOnline", userDataFetchMode); - long time = 0; - if (d != null && !d.equals("") && !d.equals("null")) { - time = Long.valueOf(d); - } - if (time == 0 && getPlugin().getOptions().isOnlineMode()) { - OfflinePlayer player = getOfflinePlayer(); - if (player != null) { - time = player.getLastPlayed(); - if (time > 0) { - setLastOnline(time); - } - } - - } - return time; - } - - /** - * Gets the number of days since the player's last login. - * - * @return the number of days since login - */ - public int getNumberOfDaysSinceLogin() { - long time = getLastOnline(); - if (time > 0) { - LocalDateTime online = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()); - LocalDateTime now = LocalDateTime.now(); - Duration dur = Duration.between(online, now); - return (int) dur.toDays(); - } - - return -1; - } - - /** - * Gets the offline player. - * - * @return the offline player - */ - @SuppressWarnings("deprecation") - public OfflinePlayer getOfflinePlayer() { - if (getPlayerName().isBlank()) { - return null; - } - if (!plugin.getOptions().isOnlineMode()) { - return Bukkit.getOfflinePlayer(getPlayerName()); - } - if (uuid != null && !uuid.equals("")) { - return Bukkit.getOfflinePlayer(java.util.UUID.fromString(uuid)); - } - return null; - } - - /** - * Gets the offline rewards list. - * - * @return the offline rewards - */ - public ArrayList getOfflineRewards() { - return getUserData().getStringList(plugin.getUserManager().getOfflineRewardsPath(), userDataFetchMode); - } - - /** - * Gets the player. - * - * @return the player - */ - public Player getPlayer() { - if (!plugin.getOptions().isOnlineMode()) { - return Bukkit.getPlayer(getPlayerName()); - } - if (uuid != null && !uuid.isEmpty()) { - return Bukkit.getPlayer(java.util.UUID.fromString(uuid)); - } - return null; - } - - /** - * Gets the player's head as an ItemStack. - * - * @return the player head - */ - public ItemStack getPlayerHead() { - return PlayerManager.getInstance().getPlayerSkull(getJavaUUID(), getPlayerName(), false); - } - - /** - * Gets the player name. - * - * @return the player name - */ - public String getPlayerName() { - if (playerName != null) { - return playerName; - } - if (userDataFetchMode.allowTempCache()) { - return getUserData().getString("PlayerName", UserDataFetchMode.TEMP_ONLY); - } - return ""; - } - - /** - * Gets the repeat amount for a reward. - * - * @param reward the reward - * @return the repeat amount - */ - public int getRepeatAmount(Reward reward) { - return getData().getInt("Repeat" + reward.getName(), userDataFetchMode); - } - - /** - * Gets the timed rewards map. - * - * @return the timed rewards - */ - public HashMap getTimedRewards() { - ArrayList timedReward = getUserData().getStringList("TimedRewards", userDataFetchMode); - HashMap timedRewards = new HashMap<>(); - for (String str : timedReward) { - if (str != null && !str.equals("null")) { - String[] data = str.split("%ExecutionTime/%"); - plugin.extraDebug("TimedReward: " + str); - if (data.length > 1) { - String name = data[0]; - - String timeStr = data[1]; - timedRewards.put(name, Long.valueOf(timeStr)); - } - } - } - return timedRewards; - } - - /** - * Gets the unclaimed choices list. - * - * @return the unclaimed choices - */ - public ArrayList getUnClaimedChoices() { - return getData().getStringList("UnClaimedChoices", userDataFetchMode); - } - - /** - * Gets the user data instance. - * - * @return the user data - */ - public UserData getUserData() { - if (data == null) { - loadData(); - } - return data; - } - - /** - * Gets the uuid. - * - * @return the uuid - */ - public String getUUID() { - return uuid; - } - - /** - * Give exp. - * - * @param exp the exp - */ - public void giveExp(int exp) { - Player player = getPlayer(); - if (player != null) { - player.giveExp(exp); - } - } - - /** - * Gives experience levels to the player. - * - * @param num the number of levels to give - */ - public void giveExpLevels(int num) { - Player p = getPlayer(); - if (p != null) { - p.setLevel(p.getLevel() + num); - } - } - - /** - * Gives an item to the player from an ItemBuilder. - * - * @param builder the item builder - */ - public void giveItem(ItemBuilder builder) { - giveItem(builder.toItemStack(getPlayer())); - } - - /** - * Give item. - * - * @param item the item - */ - public void giveItem(ItemStack item) { - if ((item == null) || (item.getAmount() == 0)) { - return; - } - - final Player player = getPlayer(); - - if (plugin.isEnabled()) { - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - if (player != null) { - plugin.getFullInventoryHandler().giveItem(player, item); - } - } - }, player); - } - - } - - /** - * Gives an item to the player with placeholders. - * - * @param itemStack the item stack - * @param placeholders the placeholders - */ - public void giveItem(ItemStack itemStack, HashMap placeholders) { - giveItem(new ItemBuilder(itemStack).setPlaceholders(placeholders).toItemStack(getPlayer())); - } - - /** - * Gives multiple items to the player. - * - * @param item the items to give - */ - public void giveItems(ItemStack... item) { - if (item == null) { - return; - } - - final Player player = getPlayer(); - - if (plugin.isEnabled()) { - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - if (player != null) { - plugin.getFullInventoryHandler().giveItem(player, item); - } - } - }, player); - } - - } - - /** - * Give user money, needs vault installed - * - * @param m Amount of money to give - */ - public void giveMoney(double m) { - if (!plugin.isEnabled()) { - return; - } - if (plugin.getVaultHandler() != null && plugin.getVaultHandler().getEcon() != null) { - try { - if (m > 0) { - final double money = m; - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - plugin.getVaultHandler().getEcon().depositPlayer(getOfflinePlayer(), money); - } - }); - - } else if (m < 0) { - m = m * -1; - final double money = m; - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - plugin.getVaultHandler().getEcon().withdrawPlayer(getOfflinePlayer(), money); - } - }); - - } - } catch ( - - IllegalStateException e) { - e.printStackTrace(); - } - } - } - - /** - * Give money. - * - * @param money the money - */ - public void giveMoney(int money) { - giveMoney((double) money); - } - - /** - * Give potion effect. - * - * @param potionName the potion name - * @param duration the duration - * @param amplifier the amplifier - */ - public void givePotionEffect(String potionName, int duration, int amplifier) { - Player player = getPlayer(); - if (player != null && plugin.isEnabled()) { - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @SuppressWarnings("deprecation") - @Override - public void run() { - player.addPotionEffect( - new PotionEffect(PotionEffectType.getByName(potionName), 20 * duration, amplifier)); - } - }, player); - - } - } - - /** - * Gives a reward from a configuration file. - * - * @param data the configuration data - * @param path the path to the reward - * @param rewardOptions the reward options - */ - public void giveReward(FileConfiguration data, String path, RewardOptions rewardOptions) { - plugin.getRewardHandler().giveReward(this, data, path, rewardOptions); - } - - /** - * Gives a reward to the user. - * - * @param reward the reward - * @param rewardOptions the reward options - */ - public void giveReward(Reward reward, RewardOptions rewardOptions) { - reward.giveReward(this, rewardOptions); - } - - /** - * Checks if the user has unclaimed choices. - * - * @return true if the user has choices - */ - public boolean hasChoices() { - return getUnClaimedChoices().size() > 0; - } - - /** - * Check if player joined before - * - * @return true, if successful - */ - public boolean hasLoggedOnBefore() { - OfflinePlayer player = Bukkit.getOfflinePlayer(java.util.UUID.fromString(uuid)); - if (player != null) { - if (player.hasPlayedBefore() || player.isOnline()) { - return true; - } - - } - ArrayList uuids = plugin.getUserManager().getAllUUIDs(); - if (uuids.contains(getUUID())) { - return true; - } - return false; - } - - /** - * Checks if the user has a permission. - * - * @param perm the permission - * @return true if the user has the permission - */ - public boolean hasPermission(String perm) { - return hasPermission(perm, true); - } - - /** - * Checks if the user has a permission. - * - * @param perm the permission - * @param offlineCheck whether to check offline permissions - * @return true if the user has the permission - */ - public boolean hasPermission(String perm, boolean offlineCheck) { - boolean negate = perm != null && perm.startsWith("!"); - if (negate) { - perm = perm.substring(1); - } - - Player player = getPlayer(); - - // Online fast path - if (player != null) { - boolean has = player.hasPermission(perm); - return negate ? !has : has; - } - - if (!offlineCheck) { - plugin.debug("Unable to get player for permission check for " + getPlayerName() + "/" + getUUID() - + " (offlineCheck is false)"); - return false; - } - // Offline path: LuckPerms (if available) - if (plugin.getLuckPermsHandle() != null && plugin.getLuckPermsHandle().luckpermsApiLoaded()) { - boolean has = plugin.getLuckPermsHandle().hasPermission(getJavaUUID(), perm); - return negate ? !has : has; - } - - plugin.debug("Unable to get player for permission check for " + getPlayerName() + "/" + getUUID() - + " (offline and no LuckPerms hook)"); - return false; - } - - /** - * Checks if the player is banned. - * - * @return true if the player is banned - */ - public boolean isBanned() { - if (plugin.getBannedPlayers().contains(getUUID())) { - return true; - } - return false; - } - - /** - * Checks if the player is a Bedrock user. - * - * @return true if the player is a Bedrock user - */ - public boolean isBedrockUser() { - return getData().getBoolean("isBedrock", userDataFetchMode); - } - - /** - * Sets whether the player is a Bedrock user. - * - * @param isBedrock true if the player is a Bedrock user - */ - public void setBedrockUser(boolean isBedrock) { - getData().setBoolean("isBedrock", isBedrock); - } - - /** - * Checks if the user data is cached. - * - * @return true if cached - */ - public boolean isCached() { - return plugin.getUserManager().getDataManager().isCached(UUID.fromString(uuid)); - } - - /** - * Checks if world checking is enabled for the user. - * - * @return true if world checking is enabled - */ - public boolean isCheckWorld() { - if (!plugin.isLoadUserData()) { - return false; - } - return Boolean.valueOf(getData().getString("CheckWorld", userDataFetchMode)); - } - - /** - * Checks if the player is in any of the specified worlds. - * - * @param worlds the list of world names - * @return true if the player is in one of the worlds - */ - public boolean isInWorld(ArrayList worlds) { - Player p = getPlayer(); - if (p != null) { - for (String world : worlds) { - if (p.getWorld().getName().equalsIgnoreCase(world)) { - return true; - } - } - } - - return false; - } - - /** - * Checks if the player is in a specific world. - * - * @param world the world name - * @return true if the player is in the world - */ - public boolean isInWorld(String world) { - Player p = getPlayer(); - if (p != null) { - return p.getWorld().getName().equalsIgnoreCase(world); - } - - return false; - } - - /** - * Checks if is online. - * - * @return true, if is online - */ - public boolean isOnline() { - boolean online = PlayerUtils.isPlayerOnline(getPlayerName()); - if (!online) { - return false; - } - if (plugin.getOptions().isTreatVanishAsOffline()) { - if (isVanished()) { - return false; - } - } - return true; - } - - /** - * Checks if the player is vanished. - * - * @return true if the player is vanished - */ - public boolean isVanished() { - Player player = getPlayer(); - if (player != null) { - for (MetadataValue meta : player.getMetadata("vanished")) { - if (meta.asBoolean()) { - return true; - } - } - - try { - try { - if (plugin.getCmiHandle() != null) { - return plugin.getCmiHandle().isVanished(player); - } - } catch (Exception e) { - } - } catch (Exception e) { - plugin.debug(e); - } - } - return false; - } - - /** - * Loads the user cache. - */ - public void loadCache() { - plugin.getUserManager().getDataManager().cacheUser(UUID.fromString(uuid), getPlayerName()); - } - - /** - * Loads the user data. - */ - public void loadData() { - data = new UserData(this); - } - - /** - * Loads a timer for delayed/timed rewards. - * - * @param time the time in milliseconds - */ - public void loadTimedDelayedTimer(long time) { - long delay = time - System.currentTimeMillis(); - if (delay < 0) { - delay = 0; - } - delay += 500; - plugin.getRewardHandler().getDelayedTimer().schedule(new Runnable() { - - @Override - public void run() { - checkDelayedTimedRewards(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - /** - * Play particle effect. - * - * @param effectName the effect name - * @param data the data - * @param particles the particles - * @param radius the radius - */ - public void playEffect(String effectName, int data, int particles, int radius) { - Player player = getPlayer(); - if ((player != null) && (effectName != null)) { - try { - Effect effect = Effect.valueOf(effectName); - for (int i = 0; i < particles; i++) { - player.getWorld().playEffect(player.getLocation(), effect, data, radius); - } - } catch (Exception e) { - e.printStackTrace(); - } - - } - } - - /** - * Plays a particle effect for the player. - * - * @param effectName the particle effect name - * @param data the data value - * @param particles the number of particles - * @param radius the radius - */ - public void playParticle(String effectName, int data, int particles, int radius) { - Player player = getPlayer(); - if ((player != null) && (effectName != null)) { - try { - Particle effect = Particle.valueOf(effectName); - for (int i = 0; i < particles; i++) { - player.getWorld().spawnParticle(effect, player.getLocation(), particles, radius, radius, radius, - data); - } - - } catch (Exception e) { - plugin.getLogger().warning( - "Failed to create particle: " + effectName + ", " + data + ", " + particles + ", " + radius); - e.printStackTrace(); - } - } - } - - /** - * Plays a particle effect for the player. - * - * @param effectName the particle effect name - * @param data the data value - * @param particles the number of particles - * @param radius the radius - */ - @Deprecated - public void playParticleEffect(String effectName, int data, int particles, int radius) { - playParticle(effectName, data, particles, radius); - } - - /** - * Play sound. - * - * @param soundName the sound name - * @param volume the volume - * @param pitch the pitch - */ - public void playSound(String soundName, float volume, float pitch) { - Player player = getPlayer(); - if (player != null) { - Sound sound = null; - try { - sound = Registry.SOUNDS.get(NamespacedKey.minecraft(soundName)); - } catch (Exception e) { - plugin.debug(e); - } - if (sound != null) { - player.playSound(player.getLocation(), sound, volume, pitch); - } else { - plugin.debug("Invalid sound: " + soundName); - } - } - } - - /** - * Performs commands as the player with placeholders. - * - * @param commands the list of commands - * @param placeholders the placeholders - */ - public void preformCommand(ArrayList commands, HashMap placeholders) { - if (commands != null && !commands.isEmpty()) { - final ArrayList cmds = PlaceholderUtils.replaceJavascript(getPlayer(), - PlaceholderUtils.replacePlaceHolder(commands, placeholders)); - - final Player player = getPlayer(); - if (player != null && plugin.isEnabled()) { - for (final String cmd : cmds) { - plugin.debug("Executing player command for " + getPlayerName() + ": " + cmd); - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - player.chat("/" + cmd); - } - }); - } - } - } - } - - /** - * Performs a command as the player with placeholders. - * - * @param command the command - * @param placeholders the placeholders - */ - public void preformCommand(String command, HashMap placeholders) { - if (command != null && !command.isEmpty()) { - final String cmd = PlaceholderUtils.replaceJavascript(getPlayer(), - PlaceholderUtils.replacePlaceHolder(command, placeholders)); - plugin.debug("Executing player command for " + getPlayerName() + ": " + command); - if (plugin.isEnabled()) { - getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - Player player = getPlayer(); - if (player != null) { - player.chat("/" + cmd); - } - } - }); - } - } - } - - /** - * Removes the user from storage. - */ - public void remove() { - plugin.debug("Removing " + getUUID() + " (" + getPlayerName() + ") from storage..."); - getData().remove(); - } - - /** - * Removes a permission from the player. - * - * @param permission the permission - */ - public void removePermission(String permission) { - plugin.getPermissionHandler().removePermission(UUID.fromString(getUUID()), getPlayerName(), permission); - } - - /** - * Removes an unclaimed choice reward. - * - * @param name the reward name - */ - public void removeUnClaimedChoiceReward(String name) { - ArrayList choices = getUnClaimedChoices(); - choices.remove(name); - setUnClaimedChoice(choices); - } - - /** - * Send action bar. - * - * @param msg the msg - * @param delay the delay - */ - public void sendActionBar(String msg, int delay) { - // plugin.debug("attempting to send action bar"); - if (msg != null && msg != "") { - Player player = getPlayer(); - if (player != null) { - - try { - ActionBar actionBar = new ActionBar(PlaceholderUtils.replaceJavascript(getPlayer(), msg), delay); - actionBar.send(player); - } catch (Exception ex) { - plugin.debug("Failed to send ActionBar, turn debug on to see stack trace"); - plugin.debug(ex); - } - } - } - } - - /** - * Send boss bar. - * - * @param msg the msg - * @param color the color - * @param style the style - * @param progress the progress - * @param delay the delay - */ - public void sendBossBar(String msg, String color, String style, double progress, int delay) { - if (msg != null && msg != "") { - Player player = getPlayer(); - if (player != null) { - try { - BossBar bossBar = new BossBar(PlaceholderUtils.replaceJavascript(getPlayer(), msg), color, style, - progress); - bossBar.send(player, delay); - } catch (Exception ex) { - plugin.debug("Failed to send BossBar"); - plugin.debug(ex); - } - } - } - } - - /** - * Send json. - * - * @param messages the messages - */ - public void sendJson(ArrayList messages) { - sendJson(messages, true); - } - - /** - * Sends JSON messages to the player. - * - * @param messages the text component messages - * @param javascript whether to process javascript placeholders - */ - public void sendJson(ArrayList messages, boolean javascript) { - Player player = getPlayer(); - if ((player != null) && (messages != null)) { - ArrayList texts = new ArrayList<>(); - TextComponent newLine = new TextComponent(ComponentSerializer.parse("{text: \"\n\"}")); - for (int i = 0; i < messages.size(); i++) { - TextComponent txt = messages.get(i); - if (javascript) { - txt.setText(PlaceholderUtils.replaceJavascript(getPlayer(), txt.getText())); - } - texts.add(txt); - if (i + 1 < messages.size()) { - texts.add(newLine); - } - - } - - PlayerUtils.getServerHandle().sendMessage(player, ArrayUtils.convertBaseComponent(texts)); - } - - } - - /** - * Send json. - * - * @param message the message - */ - public void sendJson(TextComponent message) { - Player player = getPlayer(); - if ((player != null) && (message != null)) { - message.setText(PlaceholderUtils.replaceJavascript(getPlayer(), message.getText())); - PlayerUtils.getServerHandle().sendMessage(player, message); - } - } - - /** - * Send message. - * - * @param msg the msg - */ - public void sendMessage(ArrayList msg) { - sendMessage(ArrayUtils.convert(msg)); - } - - /** - * Sends a message with placeholders to the player. - * - * @param msg the message list - * @param placeholders the placeholders - */ - public void sendMessage(ArrayList msg, HashMap placeholders) { - sendMessage(ArrayUtils.convert(PlaceholderUtils.replacePlaceHolder(msg, placeholders))); - } - - /** - * Send message. - * - * @param msg the msg - */ - public void sendMessage(String msg) { - Player player = getPlayer(); - if ((player != null) && (msg != null)) { - if (!msg.equals("")) { - for (String str : msg.split("%NewLine%")) { - PlayerUtils.getServerHandle().sendMessage(player, - PlaceholderUtils.parseJson(PlaceholderUtils.parseText(player, str))); - } - } - } - } - - /** - * Sends a message with placeholders to the player. - * - * @param msg the message - * @param placeholders the placeholders - */ - public void sendMessage(String msg, HashMap placeholders) { - sendMessage(PlaceholderUtils.replacePlaceHolder(msg, placeholders)); - } - - /** - * Sends a message with a single placeholder replacement to the player. - * - * @param msg the message - * @param toReplace the placeholder to replace - * @param replace the replacement value - */ - public void sendMessage(String msg, String toReplace, String replace) { - sendMessage(PlaceholderUtils.replacePlaceHolder(msg, toReplace, replace)); - } - - /** - * Send message. - * - * @param msg the msg - */ - public void sendMessage(String[] msg) { - Player player = getPlayer(); - if ((player != null) && (msg != null)) { - - ArrayList texts = new ArrayList<>(); - for (String str : msg) { - if ((player != null) && (msg != null)) { - if (!str.equals("")) { - for (String str1 : str.split("%NewLine%")) { - TextComponent text = PlaceholderUtils.parseJson(PlaceholderUtils.parseText(player, str1)); - text.setText(PlaceholderUtils.replaceJavascript(getPlayer(), text.getText())); - texts.add(text); - } - } - - } - } - if (texts.size() > 0) { - sendJson(texts, false); - } - } - - } - - /** - * Send title. - * - * @param title the title - * @param subTitle the sub title - * @param fadeIn the fade in - * @param showTime the show time - * @param fadeOut the fade out - */ - public void sendTitle(String title, String subTitle, int fadeIn, int showTime, int fadeOut) { - Player player = getPlayer(); - if (player != null) { - try { - Title titleObject = new Title(PlaceholderUtils.replaceJavascript(getPlayer(), title), - PlaceholderUtils.replaceJavascript(getPlayer(), subTitle), fadeIn, showTime, fadeOut); - titleObject.send(player); - } catch (Exception ex) { - plugin.getLogger().info("Failed to send Title, turn debug on to see stack trace"); - plugin.debug(ex); - } - } - } - - /** - * Sets whether to check the world for the user. - * - * @param b true to enable world checking - */ - public void setCheckWorld(boolean b) { - getData().setString("CheckWorld", "" + b); - } - - /** - * Sets the user's choice preference for a reward. - * - * @param reward the reward name - * @param preference the preference - */ - public void setChoicePreference(String reward, String preference) { - ArrayList data = getChoicePreferenceData(); - ArrayList choices = new ArrayList<>(); - - boolean added = false; - for (String str : data) { - String[] data1 = str.split(":"); - if (data1.length > 1) { - if (data1[0].equals(reward)) { - choices.add(reward + ":" + preference); - added = true; - } else { - choices.add(str); - } - } - } - if (!added) { - choices.add(reward + ":" + preference); - } - getData().setStringList("ChoicePreference", choices); - } - - /** - * Sets the input method for the user. - * - * @param inputMethod the input method - */ - public void setInputMethod(String inputMethod) { - data.setString("InputMethod", inputMethod); - } - - /** - * Sets the last online time for the user. - * - * @param online the last online time in milliseconds - */ - public void setLastOnline(long online) { - getData().setString("LastOnline", "" + online); - } - - /** - * Sets the offline rewards list. - * - * @param offlineRewards the offline rewards - */ - public void setOfflineRewards(ArrayList offlineRewards) { - // MySQL TEXT max length is 65535 bytes - int maxLength = 65535; - String str = String.join("%line%", offlineRewards); - - // Remove oldest rewards until within limit - while (str.getBytes().length > maxLength && !offlineRewards.isEmpty()) { - offlineRewards.remove(0); - str = String.join("%line%", offlineRewards); - } - - data.setStringList(plugin.getUserManager().getOfflineRewardsPath(), offlineRewards); - } - - /** - * Sets the player name. - * - * @param playerName the player name - */ - public void setPlayerName(String playerName) { - this.playerName = playerName; - } - - /** - * Sets the repeat amount for a reward. - * - * @param reward the reward - * @param amount the repeat amount - */ - public void setRepeatAmount(Reward reward, int amount) { - getData().setInt("Repeat" + reward.getName(), amount); - } - - /** - * Sets the timed rewards map. - * - * @param timed the timed rewards - */ - public void setTimedRewards(HashMap timed) { - ArrayList timedRewards = new ArrayList<>(); - for (Entry entry : timed.entrySet()) { - - String str = ""; - str += entry.getKey() + "%ExecutionTime/%"; - str += entry.getValue(); - timedRewards.add(str); - - } - data.setStringList("TimedRewards", timedRewards); - } - - /** - * Sets the unclaimed choice rewards list. - * - * @param rewards the unclaimed rewards - */ - public void setUnClaimedChoice(ArrayList rewards) { - getData().setStringList("UnClaimedChoices", rewards); - } - - /** - * Sets the user to not cache data. - */ - @Deprecated - public void dontCache() { - userDataFetchMode = UserDataFetchMode.NO_CACHE; - } - - /** - * Sets the uuid. - * - * @param uuid the new uuid - */ - public void setUUID(String uuid) { - this.uuid = uuid; - } - - /** - * Enables temporary caching for this user. - * - * @return this user instance - */ - public AdvancedCoreUser tempCache() { - getUserData().tempCache(); - return this; - } - - /** - * Updates the player name in storage. - * - * @param force whether to force the update - */ - public void updateName(boolean force) { - if (getData().hasData() || force) { - String playerName = getData().getString("PlayerName", userDataFetchMode); - if (playerName == null || !playerName.equals(getPlayerName())) { - getData().setString("PlayerName", getPlayerName(), true); - } - } - } - - /** - * Updates the temporary cache with specific columns. - * - * @param cols the columns to update - */ - public void updateTempCacheWithColumns(ArrayList cols) { - getUserData().updateTempCacheWithColumns(cols); - } - +package com.bencodez.advancedcore.api.user; + +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.bukkit.Bukkit; +import org.bukkit.Effect; +import org.bukkit.NamespacedKey; +import org.bukkit.OfflinePlayer; +import org.bukkit.Particle; +import org.bukkit.Registry; +import org.bukkit.Sound; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.item.ItemBuilder; +import com.bencodez.advancedcore.api.messages.PlaceholderUtils; +import com.bencodez.advancedcore.api.misc.PlayerManager; +import com.bencodez.advancedcore.api.misc.effects.BossBar; +import com.bencodez.advancedcore.api.misc.effects.Title; +import com.bencodez.advancedcore.api.rewards.Reward; +import com.bencodez.advancedcore.api.rewards.RewardBuilder; +import com.bencodez.advancedcore.api.rewards.RewardHandler; +import com.bencodez.advancedcore.api.rewards.RewardOptions; +import com.bencodez.advancedcore.api.user.usercache.UserDataCache; +import com.bencodez.simpleapi.array.ArrayUtils; +import com.bencodez.simpleapi.messages.actionbar.ActionBar; +import com.bencodez.simpleapi.player.PlayerUtils; +import com.bencodez.simpleapi.sql.Column; + +import lombok.Getter; +import lombok.Setter; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.chat.ComponentSerializer; + +/** + * The Class User. + */ +public class AdvancedCoreUser { + + private static final String QUEUED_REFERENCE_PREFIX = "\\AdvancedCoreQueue/1/"; + + /** + * User data fetch mode for this user. + * + * @return the user data fetch mode + */ + @Getter + private UserDataFetchMode userDataFetchMode = UserDataFetchMode.DEFAULT; + + /** + * User data for this user. + * + * @param data the user data to set + */ + @Setter + private UserData data; + + private boolean loadName = true; + + /** The player name. */ + private String playerName; + + /** + * Plugin instance. + * + * @return the plugin instance + */ + @Getter + private AdvancedCorePlugin plugin = null; + + /** The uuid. */ + private String uuid; + + /** + * Instantiates a new user from an existing user. + * + * @param plugin the plugin + * @param user the user to copy from + */ + public AdvancedCoreUser(AdvancedCorePlugin plugin, AdvancedCoreUser user) { + this.userDataFetchMode = user.userDataFetchMode; + this.data = user.getUserData(); + this.uuid = user.getUUID(); + this.playerName = user.getPlayerName(); + this.loadName = user.loadName; + this.plugin = plugin; + loadData(); + getUserData().setTempCache(user.getUserData().getTempCache()); + } + + /** + * Instantiates a new user. + * + * @param plugin the plugin + * @param player the player + */ + @Deprecated + public AdvancedCoreUser(AdvancedCorePlugin plugin, Player player) { + this.plugin = plugin; + loadData(); + uuid = player.getUniqueId().toString(); + setPlayerName(player.getName()); + } + + /** + * Instantiates a new user. + * + * @param plugin the plugin + * @param playerName the player name + */ + @Deprecated + public AdvancedCoreUser(AdvancedCorePlugin plugin, String playerName) { + this.plugin = plugin; + loadData(); + uuid = PlayerManager.getInstance().getUUID(playerName); + setPlayerName(playerName); + } + + /** + * Instantiates a new user. + * + * @param plugin the plugin + * @param uuid the uuid + */ + @Deprecated + public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid) { + this.plugin = plugin; + this.uuid = uuid.toString(); + loadData(); + setPlayerName(PlayerManager.getInstance().getPlayerName(this, this.uuid, false)); + } + + /** + * Instantiates a new user. + * + * @param plugin the plugin + * @param uuid the uuid + * @param loadName the load name + */ + @Deprecated + public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid, boolean loadName) { + this.plugin = plugin; + this.uuid = uuid.toString(); + this.loadName = loadName; + loadData(); + if (this.loadName) { + setPlayerName(PlayerManager.getInstance().getPlayerName(this, this.uuid)); + } + + } + + /** + * Instantiates a new user. + * + * @param plugin the plugin + * @param uuid the uuid + * @param loadName the load name + * @param loadData the load data + */ + @Deprecated + public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid, boolean loadName, boolean loadData) { + this.plugin = plugin; + this.uuid = uuid.toString(); + this.loadName = loadName; + if (loadData) { + loadData(); + } + if (this.loadName) { + setPlayerName(PlayerManager.getInstance().getPlayerName(this, this.uuid)); + } + + } + + /** + * Instantiates a new user. + * + * @param plugin the plugin + * @param uuid the uuid + * @param playerName the player name + */ + @Deprecated + public AdvancedCoreUser(AdvancedCorePlugin plugin, UUID uuid, String playerName) { + this.plugin = plugin; + this.uuid = uuid.toString(); + if (!plugin.getOptions().isOnlineMode()) { + this.uuid = PlayerManager.getInstance().getUUID(playerName); + } + loadData(); + setPlayerName(playerName); + } + + /** + * Adds offline rewards to be given when the player next logs in. + * + * @param reward the reward + * @param placeholders the placeholders + */ + public void addOfflineRewards(Reward reward, HashMap placeholders) { + synchronized (plugin) { + ArrayList offlineRewards = getOfflineRewards(); + offlineRewards.add(queuedRewardReference(reward) + "%placeholders%" + ArrayUtils.makeString(placeholders)); + setOfflineRewards(offlineRewards); + } + } + + /** + * Adds a permission to the player. + * + * @param permission the permission + */ + public void addPermission(String permission) { + plugin.getPermissionHandler().addPermission(getPlayer(), permission); + } + + /** + * Adds a permission to the player with a delay. + * + * @param permission the permission + * @param delay the delay in milliseconds + */ + public void addPermission(String permission, long delay) { + plugin.getPermissionHandler().addPermission(getPlayer(), permission, delay); + } + + /** + * Adds a timed reward to be given at a specific time. + * + * @param reward the reward + * @param placeholders the placeholders + * @param epochMilli the epoch time in milliseconds when the reward should be + * given + */ + public synchronized void addTimedReward(Reward reward, HashMap placeholders, long epochMilli) { + HashMap timed = getTimedRewards(); + String rewardName = queuedRewardReference(reward); + rewardName += "%extime%" + System.currentTimeMillis(); + + timed.put(rewardName + "%placeholders%" + ArrayUtils.makeString(placeholders), epochMilli); + setTimedRewards(timed); + loadTimedDelayedTimer(epochMilli); + } + + private String queuedRewardReference(Reward reward) { + String encodedName = Base64.getUrlEncoder().withoutPadding() + .encodeToString(reward.getRewardName().getBytes(StandardCharsets.UTF_8)); + return QUEUED_REFERENCE_PREFIX + (reward.isGeneratedSnapshotCreated() ? "snapshot/" : "normal/") + + encodedName; + } + + /** + * Adds an unclaimed choice reward. + * + * @param name the reward name + */ + public void addUnClaimedChoiceReward(String name) { + ArrayList choices = getUnClaimedChoices(); + choices.add(name); + setUnClaimedChoice(choices); + } + + /** + * Caches the user data. + */ + public void cache() { + plugin.getUserManager().getDataManager().cacheUser(UUID.fromString(uuid), getPlayerName()); + } + + /** + * Caches the user data asynchronously. + */ + public void cacheAsync() { + getPlugin().getBukkitScheduler().runTaskAsynchronously(plugin, new Runnable() { + + @Override + public void run() { + cache(); + } + }); + } + + /** + * Caches the user data if needed. + */ + public void cacheIfNeeded() { + plugin.getUserManager().getDataManager().cacheUserIfNeeded(UUID.fromString(uuid)); + } + + /** + * Checks and processes delayed/timed rewards. + */ + public void checkDelayedTimedRewards() { + plugin.debug("Checking timed/delayed for " + getPlayerName()); + HashMap timed = getTimedRewards(); + HashMap newTimed = new HashMap<>(); + for (Entry entry : timed.entrySet()) { + long time = entry.getValue(); + + if (time != 0) { + Date timeDate = new Date(time); + if (new Date().after(timeDate)) { + String[] data = entry.getKey().split("%placeholders%", 2); + String rewardReference = data[0].split("%extime%", 2)[0]; + String placeholders = ""; + if (data.length > 1) { + placeholders = data[1]; + } + RewardOptions replayOptions = new RewardOptions().setCheckTimed(false) + .withPlaceHolder(ArrayUtils.fromString(placeholders)); + replayOptions.addPlaceholder("date", + "" + new SimpleDateFormat("EEE, d MMM yyyy HH:mm").format(new Date(time))); + plugin.getRewardHandler().givePersistedQueueReward(this, new PersistedQueueReference(rewardReference), replayOptions); + String rewardName = rewardReference; + plugin.debug("Giving timed/delayed reward " + rewardName + " for " + getPlayerName() + + " with placeholders " + ArrayUtils.fromString(placeholders)); + } else { + newTimed.put(entry.getKey(), time); + } + } + + } + setTimedRewards(newTimed); + } + + /** + * Check offline rewards. + */ + public void checkOfflineRewards() { + if (!plugin.getOptions().isProcessRewards()) { + plugin.debug("Processing rewards is disabled"); + return; + } + if (isCheckWorld()) { + setCheckWorld(false); + } + ArrayList rewards = getOfflineRewards(); + if (rewards.isEmpty()) { + return; + } + + setOfflineRewards(new ArrayList<>()); + RewardHandler rewardHandler = plugin.getRewardHandler(); + AdvancedCoreUser user = this; + + for (String rewardEntry : rewards) { + if (rewardEntry == null || rewardEntry.equals("null")) { + continue; + } + + String[] parts = rewardEntry.split("%placeholders%", 2); + String rewardReference = parts[0]; + String placeholderStr = parts.length > 1 ? parts[1] : ""; + + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false) + .withPlaceHolder(ArrayUtils.fromString(placeholderStr)); + + rewardHandler.givePersistedQueueReward(user, new PersistedQueueReference(rewardReference), options); + } + + } + + /** + * Clears the user cache. + */ + public void clearCache() { + if (isCached()) { + getCache().clearCache(); + } + } + + /** + * Clears the temporary cache. + */ + public void clearTempCache() { + getUserData().clearTempCache(); + } + + /** + * Closes the player's inventory. + */ + public void closeInv() { + if (plugin.isEnabled()) { + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + Player player = getPlayer(); + if (player != null) { + player.closeInventory(); + } + } + }, getPlayer()); + } + } + + /** + * Sets the user data fetch mode. + * + * @param mode the fetch mode + * @return this user instance + */ + public AdvancedCoreUser userDataFetechMode(UserDataFetchMode mode) { + this.userDataFetchMode = mode; + return this; + } + + /** + * Forces running of offline rewards without processing checks. + */ + public void forceRunOfflineRewards() { + if (!plugin.getOptions().isProcessRewards()) { + plugin.debug("Processing rewards is disabled"); + return; + } + + setCheckWorld(false); + ArrayList rewards = getOfflineRewards(); + if (rewards.isEmpty()) { + return; + } + + setOfflineRewards(new ArrayList<>()); + RewardHandler rewardHandler = plugin.getRewardHandler(); + AdvancedCoreUser user = this; + + for (String rewardEntry : rewards) { + if (rewardEntry == null || rewardEntry.equals("null")) { + continue; + } + + String[] parts = rewardEntry.split("%placeholders%", 2); + String rewardReference = parts[0]; + String placeholderStr = parts.length > 1 ? parts[1] : ""; + + RewardOptions options = new RewardOptions().setOnline(false).setGiveOffline(false).forceOffline() + .setCheckTimed(false).withPlaceHolder(ArrayUtils.fromString(placeholderStr)); + + rewardHandler.givePersistedQueueReward(user, new PersistedQueueReference(rewardReference), options); + } + } + + /** + * Gets the user data cache. + * + * @return the cache + */ + public UserDataCache getCache() { + return plugin.getUserManager().getDataManager().getCache(java.util.UUID.fromString(getUUID())); + } + + /** + * Gets the user's choice preference for a reward. + * + * @param rewardName the reward name + * @return the choice preference + */ + public String getChoicePreference(String rewardName) { + ArrayList data = getChoicePreferenceData(); + + for (String str : data) { + String[] data1 = str.split(":"); + if (data1.length > 1) { + if (data1[0].equals(rewardName)) { + return data1[1]; + } + } + } + return ""; + } + + /** + * Gets the choice preference data. + * + * @return the choice preference data list + */ + public ArrayList getChoicePreferenceData() { + return getData().getStringList("ChoicePreference", userDataFetchMode); + } + + /** + * Gets the user data. + * + * @return the user data + */ + public UserData getData() { + if (data == null) { + loadData(); + } + return data; + } + + /** + * Gets the input method. + * + * @return the input method string + */ + public String getInputMethod() { + return getUserData().getString("InputMethod", userDataFetchMode); + } + + /** + * Gets the Java UUID object. + * + * @return the Java UUID + */ + public UUID getJavaUUID() { + return UUID.fromString(uuid); + } + + /** + * Gets the last online time in milliseconds. + * + * @return the last online time + */ + public long getLastOnline() { + String d = getData().getString("LastOnline", userDataFetchMode); + long time = 0; + if (d != null && !d.equals("") && !d.equals("null")) { + time = Long.valueOf(d); + } + if (time == 0 && getPlugin().getOptions().isOnlineMode()) { + OfflinePlayer player = getOfflinePlayer(); + if (player != null) { + time = player.getLastPlayed(); + if (time > 0) { + setLastOnline(time); + } + } + + } + return time; + } + + /** + * Gets the number of days since the player's last login. + * + * @return the number of days since login + */ + public int getNumberOfDaysSinceLogin() { + long time = getLastOnline(); + if (time > 0) { + LocalDateTime online = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()); + LocalDateTime now = LocalDateTime.now(); + Duration dur = Duration.between(online, now); + return (int) dur.toDays(); + } + + return -1; + } + + /** + * Gets the offline player. + * + * @return the offline player + */ + @SuppressWarnings("deprecation") + public OfflinePlayer getOfflinePlayer() { + if (getPlayerName().isBlank()) { + return null; + } + if (!plugin.getOptions().isOnlineMode()) { + return Bukkit.getOfflinePlayer(getPlayerName()); + } + if (uuid != null && !uuid.equals("")) { + return Bukkit.getOfflinePlayer(java.util.UUID.fromString(uuid)); + } + return null; + } + + /** + * Gets the offline rewards list. + * + * @return the offline rewards + */ + public ArrayList getOfflineRewards() { + return getUserData().getStringList(plugin.getUserManager().getOfflineRewardsPath(), userDataFetchMode); + } + + /** + * Gets the player. + * + * @return the player + */ + public Player getPlayer() { + if (!plugin.getOptions().isOnlineMode()) { + return Bukkit.getPlayer(getPlayerName()); + } + if (uuid != null && !uuid.isEmpty()) { + return Bukkit.getPlayer(java.util.UUID.fromString(uuid)); + } + return null; + } + + /** + * Gets the player's head as an ItemStack. + * + * @return the player head + */ + public ItemStack getPlayerHead() { + return PlayerManager.getInstance().getPlayerSkull(getJavaUUID(), getPlayerName(), false); + } + + /** + * Gets the player name. + * + * @return the player name + */ + public String getPlayerName() { + if (playerName != null) { + return playerName; + } + if (userDataFetchMode.allowTempCache()) { + return getUserData().getString("PlayerName", UserDataFetchMode.TEMP_ONLY); + } + return ""; + } + + /** + * Gets the repeat amount for a reward. + * + * @param reward the reward + * @return the repeat amount + */ + public int getRepeatAmount(Reward reward) { + return getData().getInt("Repeat" + reward.getName(), userDataFetchMode); + } + + /** + * Gets the timed rewards map. + * + * @return the timed rewards + */ + public HashMap getTimedRewards() { + ArrayList timedReward = getUserData().getStringList("TimedRewards", userDataFetchMode); + HashMap timedRewards = new HashMap<>(); + for (String str : timedReward) { + if (str != null && !str.equals("null")) { + String[] data = str.split("%ExecutionTime/%"); + plugin.extraDebug("TimedReward: " + str); + if (data.length > 1) { + String name = data[0]; + + String timeStr = data[1]; + timedRewards.put(name, Long.valueOf(timeStr)); + } + } + } + return timedRewards; + } + + /** + * Gets the unclaimed choices list. + * + * @return the unclaimed choices + */ + public ArrayList getUnClaimedChoices() { + return getData().getStringList("UnClaimedChoices", userDataFetchMode); + } + + /** + * Gets the user data instance. + * + * @return the user data + */ + public UserData getUserData() { + if (data == null) { + loadData(); + } + return data; + } + + /** + * Gets the uuid. + * + * @return the uuid + */ + public String getUUID() { + return uuid; + } + + /** + * Give exp. + * + * @param exp the exp + */ + public void giveExp(int exp) { + Player player = getPlayer(); + if (player != null) { + player.giveExp(exp); + } + } + + /** + * Gives experience levels to the player. + * + * @param num the number of levels to give + */ + public void giveExpLevels(int num) { + Player p = getPlayer(); + if (p != null) { + p.setLevel(p.getLevel() + num); + } + } + + /** + * Gives an item to the player from an ItemBuilder. + * + * @param builder the item builder + */ + public void giveItem(ItemBuilder builder) { + giveItem(builder.toItemStack(getPlayer())); + } + + /** + * Give item. + * + * @param item the item + */ + public void giveItem(ItemStack item) { + if ((item == null) || (item.getAmount() == 0)) { + return; + } + + final Player player = getPlayer(); + + if (plugin.isEnabled()) { + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + if (player != null) { + plugin.getFullInventoryHandler().giveItem(player, item); + } + } + }, player); + } + + } + + /** + * Gives an item to the player with placeholders. + * + * @param itemStack the item stack + * @param placeholders the placeholders + */ + public void giveItem(ItemStack itemStack, HashMap placeholders) { + giveItem(new ItemBuilder(itemStack).setPlaceholders(placeholders).toItemStack(getPlayer())); + } + + /** + * Gives multiple items to the player. + * + * @param item the items to give + */ + public void giveItems(ItemStack... item) { + if (item == null) { + return; + } + + final Player player = getPlayer(); + + if (plugin.isEnabled()) { + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + if (player != null) { + plugin.getFullInventoryHandler().giveItem(player, item); + } + } + }, player); + } + + } + + /** + * Give user money, needs vault installed + * + * @param m Amount of money to give + */ + public void giveMoney(double m) { + if (!plugin.isEnabled()) { + return; + } + if (plugin.getVaultHandler() != null && plugin.getVaultHandler().getEcon() != null) { + try { + if (m > 0) { + final double money = m; + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + plugin.getVaultHandler().getEcon().depositPlayer(getOfflinePlayer(), money); + } + }); + + } else if (m < 0) { + m = m * -1; + final double money = m; + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + plugin.getVaultHandler().getEcon().withdrawPlayer(getOfflinePlayer(), money); + } + }); + + } + } catch ( + + IllegalStateException e) { + e.printStackTrace(); + } + } + } + + /** + * Give money. + * + * @param money the money + */ + public void giveMoney(int money) { + giveMoney((double) money); + } + + /** + * Give potion effect. + * + * @param potionName the potion name + * @param duration the duration + * @param amplifier the amplifier + */ + public void givePotionEffect(String potionName, int duration, int amplifier) { + Player player = getPlayer(); + if (player != null && plugin.isEnabled()) { + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @SuppressWarnings("deprecation") + @Override + public void run() { + player.addPotionEffect( + new PotionEffect(PotionEffectType.getByName(potionName), 20 * duration, amplifier)); + } + }, player); + + } + } + + /** + * Gives a reward from a configuration file. + * + * @param data the configuration data + * @param path the path to the reward + * @param rewardOptions the reward options + */ + public void giveReward(FileConfiguration data, String path, RewardOptions rewardOptions) { + plugin.getRewardHandler().giveReward(this, data, path, rewardOptions); + } + + /** + * Gives a reward to the user. + * + * @param reward the reward + * @param rewardOptions the reward options + */ + public void giveReward(Reward reward, RewardOptions rewardOptions) { + reward.giveReward(this, rewardOptions); + } + + /** + * Checks if the user has unclaimed choices. + * + * @return true if the user has choices + */ + public boolean hasChoices() { + return getUnClaimedChoices().size() > 0; + } + + /** + * Check if player joined before + * + * @return true, if successful + */ + public boolean hasLoggedOnBefore() { + OfflinePlayer player = Bukkit.getOfflinePlayer(java.util.UUID.fromString(uuid)); + if (player != null) { + if (player.hasPlayedBefore() || player.isOnline()) { + return true; + } + + } + ArrayList uuids = plugin.getUserManager().getAllUUIDs(); + if (uuids.contains(getUUID())) { + return true; + } + return false; + } + + /** + * Checks if the user has a permission. + * + * @param perm the permission + * @return true if the user has the permission + */ + public boolean hasPermission(String perm) { + return hasPermission(perm, true); + } + + /** + * Checks if the user has a permission. + * + * @param perm the permission + * @param offlineCheck whether to check offline permissions + * @return true if the user has the permission + */ + public boolean hasPermission(String perm, boolean offlineCheck) { + boolean negate = perm != null && perm.startsWith("!"); + if (negate) { + perm = perm.substring(1); + } + + Player player = getPlayer(); + + // Online fast path + if (player != null) { + boolean has = player.hasPermission(perm); + return negate ? !has : has; + } + + if (!offlineCheck) { + plugin.debug("Unable to get player for permission check for " + getPlayerName() + "/" + getUUID() + + " (offlineCheck is false)"); + return false; + } + // Offline path: LuckPerms (if available) + if (plugin.getLuckPermsHandle() != null && plugin.getLuckPermsHandle().luckpermsApiLoaded()) { + boolean has = plugin.getLuckPermsHandle().hasPermission(getJavaUUID(), perm); + return negate ? !has : has; + } + + plugin.debug("Unable to get player for permission check for " + getPlayerName() + "/" + getUUID() + + " (offline and no LuckPerms hook)"); + return false; + } + + /** + * Checks if the player is banned. + * + * @return true if the player is banned + */ + public boolean isBanned() { + if (plugin.getBannedPlayers().contains(getUUID())) { + return true; + } + return false; + } + + /** + * Checks if the player is a Bedrock user. + * + * @return true if the player is a Bedrock user + */ + public boolean isBedrockUser() { + return getData().getBoolean("isBedrock", userDataFetchMode); + } + + /** + * Sets whether the player is a Bedrock user. + * + * @param isBedrock true if the player is a Bedrock user + */ + public void setBedrockUser(boolean isBedrock) { + getData().setBoolean("isBedrock", isBedrock); + } + + /** + * Checks if the user data is cached. + * + * @return true if cached + */ + public boolean isCached() { + return plugin.getUserManager().getDataManager().isCached(UUID.fromString(uuid)); + } + + /** + * Checks if world checking is enabled for the user. + * + * @return true if world checking is enabled + */ + public boolean isCheckWorld() { + if (!plugin.isLoadUserData()) { + return false; + } + return Boolean.valueOf(getData().getString("CheckWorld", userDataFetchMode)); + } + + /** + * Checks if the player is in any of the specified worlds. + * + * @param worlds the list of world names + * @return true if the player is in one of the worlds + */ + public boolean isInWorld(ArrayList worlds) { + Player p = getPlayer(); + if (p != null) { + for (String world : worlds) { + if (p.getWorld().getName().equalsIgnoreCase(world)) { + return true; + } + } + } + + return false; + } + + /** + * Checks if the player is in a specific world. + * + * @param world the world name + * @return true if the player is in the world + */ + public boolean isInWorld(String world) { + Player p = getPlayer(); + if (p != null) { + return p.getWorld().getName().equalsIgnoreCase(world); + } + + return false; + } + + /** + * Checks if is online. + * + * @return true, if is online + */ + public boolean isOnline() { + boolean online = PlayerUtils.isPlayerOnline(getPlayerName()); + if (!online) { + return false; + } + if (plugin.getOptions().isTreatVanishAsOffline()) { + if (isVanished()) { + return false; + } + } + return true; + } + + /** + * Checks if the player is vanished. + * + * @return true if the player is vanished + */ + public boolean isVanished() { + Player player = getPlayer(); + if (player != null) { + for (MetadataValue meta : player.getMetadata("vanished")) { + if (meta.asBoolean()) { + return true; + } + } + + try { + try { + if (plugin.getCmiHandle() != null) { + return plugin.getCmiHandle().isVanished(player); + } + } catch (Exception e) { + } + } catch (Exception e) { + plugin.debug(e); + } + } + return false; + } + + /** + * Loads the user cache. + */ + public void loadCache() { + plugin.getUserManager().getDataManager().cacheUser(UUID.fromString(uuid), getPlayerName()); + } + + /** + * Loads the user data. + */ + public void loadData() { + data = new UserData(this); + } + + /** + * Loads a timer for delayed/timed rewards. + * + * @param time the time in milliseconds + */ + public void loadTimedDelayedTimer(long time) { + long delay = time - System.currentTimeMillis(); + if (delay < 0) { + delay = 0; + } + delay += 500; + plugin.getRewardHandler().getDelayedTimer().schedule(new Runnable() { + + @Override + public void run() { + checkDelayedTimedRewards(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + /** + * Play particle effect. + * + * @param effectName the effect name + * @param data the data + * @param particles the particles + * @param radius the radius + */ + public void playEffect(String effectName, int data, int particles, int radius) { + Player player = getPlayer(); + if ((player != null) && (effectName != null)) { + try { + Effect effect = Effect.valueOf(effectName); + for (int i = 0; i < particles; i++) { + player.getWorld().playEffect(player.getLocation(), effect, data, radius); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + } + + /** + * Plays a particle effect for the player. + * + * @param effectName the particle effect name + * @param data the data value + * @param particles the number of particles + * @param radius the radius + */ + public void playParticle(String effectName, int data, int particles, int radius) { + Player player = getPlayer(); + if ((player != null) && (effectName != null)) { + try { + Particle effect = Particle.valueOf(effectName); + for (int i = 0; i < particles; i++) { + player.getWorld().spawnParticle(effect, player.getLocation(), particles, radius, radius, radius, + data); + } + + } catch (Exception e) { + plugin.getLogger().warning( + "Failed to create particle: " + effectName + ", " + data + ", " + particles + ", " + radius); + e.printStackTrace(); + } + } + } + + /** + * Plays a particle effect for the player. + * + * @param effectName the particle effect name + * @param data the data value + * @param particles the number of particles + * @param radius the radius + */ + @Deprecated + public void playParticleEffect(String effectName, int data, int particles, int radius) { + playParticle(effectName, data, particles, radius); + } + + /** + * Play sound. + * + * @param soundName the sound name + * @param volume the volume + * @param pitch the pitch + */ + public void playSound(String soundName, float volume, float pitch) { + Player player = getPlayer(); + if (player != null) { + Sound sound = null; + try { + sound = Registry.SOUNDS.get(NamespacedKey.minecraft(soundName)); + } catch (Exception e) { + plugin.debug(e); + } + if (sound != null) { + player.playSound(player.getLocation(), sound, volume, pitch); + } else { + plugin.debug("Invalid sound: " + soundName); + } + } + } + + /** + * Performs commands as the player with placeholders. + * + * @param commands the list of commands + * @param placeholders the placeholders + */ + public void preformCommand(ArrayList commands, HashMap placeholders) { + if (commands != null && !commands.isEmpty()) { + final ArrayList cmds = PlaceholderUtils.replaceJavascript(getPlayer(), + PlaceholderUtils.replacePlaceHolder(commands, placeholders)); + + final Player player = getPlayer(); + if (player != null && plugin.isEnabled()) { + for (final String cmd : cmds) { + plugin.debug("Executing player command for " + getPlayerName() + ": " + cmd); + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + player.chat("/" + cmd); + } + }); + } + } + } + } + + /** + * Performs a command as the player with placeholders. + * + * @param command the command + * @param placeholders the placeholders + */ + public void preformCommand(String command, HashMap placeholders) { + if (command != null && !command.isEmpty()) { + final String cmd = PlaceholderUtils.replaceJavascript(getPlayer(), + PlaceholderUtils.replacePlaceHolder(command, placeholders)); + plugin.debug("Executing player command for " + getPlayerName() + ": " + command); + if (plugin.isEnabled()) { + getPlugin().getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + Player player = getPlayer(); + if (player != null) { + player.chat("/" + cmd); + } + } + }); + } + } + } + + /** + * Removes the user from storage. + */ + public void remove() { + plugin.debug("Removing " + getUUID() + " (" + getPlayerName() + ") from storage..."); + getData().remove(); + } + + /** + * Removes a permission from the player. + * + * @param permission the permission + */ + public void removePermission(String permission) { + plugin.getPermissionHandler().removePermission(UUID.fromString(getUUID()), getPlayerName(), permission); + } + + /** + * Removes an unclaimed choice reward. + * + * @param name the reward name + */ + public void removeUnClaimedChoiceReward(String name) { + ArrayList choices = getUnClaimedChoices(); + choices.remove(name); + setUnClaimedChoice(choices); + } + + /** + * Send action bar. + * + * @param msg the msg + * @param delay the delay + */ + public void sendActionBar(String msg, int delay) { + // plugin.debug("attempting to send action bar"); + if (msg != null && msg != "") { + Player player = getPlayer(); + if (player != null) { + + try { + ActionBar actionBar = new ActionBar(PlaceholderUtils.replaceJavascript(getPlayer(), msg), delay); + actionBar.send(player); + } catch (Exception ex) { + plugin.debug("Failed to send ActionBar, turn debug on to see stack trace"); + plugin.debug(ex); + } + } + } + } + + /** + * Send boss bar. + * + * @param msg the msg + * @param color the color + * @param style the style + * @param progress the progress + * @param delay the delay + */ + public void sendBossBar(String msg, String color, String style, double progress, int delay) { + if (msg != null && msg != "") { + Player player = getPlayer(); + if (player != null) { + try { + BossBar bossBar = new BossBar(PlaceholderUtils.replaceJavascript(getPlayer(), msg), color, style, + progress); + bossBar.send(player, delay); + } catch (Exception ex) { + plugin.debug("Failed to send BossBar"); + plugin.debug(ex); + } + } + } + } + + /** + * Send json. + * + * @param messages the messages + */ + public void sendJson(ArrayList messages) { + sendJson(messages, true); + } + + /** + * Sends JSON messages to the player. + * + * @param messages the text component messages + * @param javascript whether to process javascript placeholders + */ + public void sendJson(ArrayList messages, boolean javascript) { + Player player = getPlayer(); + if ((player != null) && (messages != null)) { + ArrayList texts = new ArrayList<>(); + TextComponent newLine = new TextComponent(ComponentSerializer.parse("{text: \"\n\"}")); + for (int i = 0; i < messages.size(); i++) { + TextComponent txt = messages.get(i); + if (javascript) { + txt.setText(PlaceholderUtils.replaceJavascript(getPlayer(), txt.getText())); + } + texts.add(txt); + if (i + 1 < messages.size()) { + texts.add(newLine); + } + + } + + PlayerUtils.getServerHandle().sendMessage(player, ArrayUtils.convertBaseComponent(texts)); + } + + } + + /** + * Send json. + * + * @param message the message + */ + public void sendJson(TextComponent message) { + Player player = getPlayer(); + if ((player != null) && (message != null)) { + message.setText(PlaceholderUtils.replaceJavascript(getPlayer(), message.getText())); + PlayerUtils.getServerHandle().sendMessage(player, message); + } + } + + /** + * Send message. + * + * @param msg the msg + */ + public void sendMessage(ArrayList msg) { + sendMessage(ArrayUtils.convert(msg)); + } + + /** + * Sends a message with placeholders to the player. + * + * @param msg the message list + * @param placeholders the placeholders + */ + public void sendMessage(ArrayList msg, HashMap placeholders) { + sendMessage(ArrayUtils.convert(PlaceholderUtils.replacePlaceHolder(msg, placeholders))); + } + + /** + * Send message. + * + * @param msg the msg + */ + public void sendMessage(String msg) { + Player player = getPlayer(); + if ((player != null) && (msg != null)) { + if (!msg.equals("")) { + for (String str : msg.split("%NewLine%")) { + PlayerUtils.getServerHandle().sendMessage(player, + PlaceholderUtils.parseJson(PlaceholderUtils.parseText(player, str))); + } + } + } + } + + /** + * Sends a message with placeholders to the player. + * + * @param msg the message + * @param placeholders the placeholders + */ + public void sendMessage(String msg, HashMap placeholders) { + sendMessage(PlaceholderUtils.replacePlaceHolder(msg, placeholders)); + } + + /** + * Sends a message with a single placeholder replacement to the player. + * + * @param msg the message + * @param toReplace the placeholder to replace + * @param replace the replacement value + */ + public void sendMessage(String msg, String toReplace, String replace) { + sendMessage(PlaceholderUtils.replacePlaceHolder(msg, toReplace, replace)); + } + + /** + * Send message. + * + * @param msg the msg + */ + public void sendMessage(String[] msg) { + Player player = getPlayer(); + if ((player != null) && (msg != null)) { + + ArrayList texts = new ArrayList<>(); + for (String str : msg) { + if ((player != null) && (msg != null)) { + if (!str.equals("")) { + for (String str1 : str.split("%NewLine%")) { + TextComponent text = PlaceholderUtils.parseJson(PlaceholderUtils.parseText(player, str1)); + text.setText(PlaceholderUtils.replaceJavascript(getPlayer(), text.getText())); + texts.add(text); + } + } + + } + } + if (texts.size() > 0) { + sendJson(texts, false); + } + } + + } + + /** + * Send title. + * + * @param title the title + * @param subTitle the sub title + * @param fadeIn the fade in + * @param showTime the show time + * @param fadeOut the fade out + */ + public void sendTitle(String title, String subTitle, int fadeIn, int showTime, int fadeOut) { + Player player = getPlayer(); + if (player != null) { + try { + Title titleObject = new Title(PlaceholderUtils.replaceJavascript(getPlayer(), title), + PlaceholderUtils.replaceJavascript(getPlayer(), subTitle), fadeIn, showTime, fadeOut); + titleObject.send(player); + } catch (Exception ex) { + plugin.getLogger().info("Failed to send Title, turn debug on to see stack trace"); + plugin.debug(ex); + } + } + } + + /** + * Sets whether to check the world for the user. + * + * @param b true to enable world checking + */ + public void setCheckWorld(boolean b) { + getData().setString("CheckWorld", "" + b); + } + + /** + * Sets the user's choice preference for a reward. + * + * @param reward the reward name + * @param preference the preference + */ + public void setChoicePreference(String reward, String preference) { + ArrayList data = getChoicePreferenceData(); + ArrayList choices = new ArrayList<>(); + + boolean added = false; + for (String str : data) { + String[] data1 = str.split(":"); + if (data1.length > 1) { + if (data1[0].equals(reward)) { + choices.add(reward + ":" + preference); + added = true; + } else { + choices.add(str); + } + } + } + if (!added) { + choices.add(reward + ":" + preference); + } + getData().setStringList("ChoicePreference", choices); + } + + /** + * Sets the input method for the user. + * + * @param inputMethod the input method + */ + public void setInputMethod(String inputMethod) { + data.setString("InputMethod", inputMethod); + } + + /** + * Sets the last online time for the user. + * + * @param online the last online time in milliseconds + */ + public void setLastOnline(long online) { + getData().setString("LastOnline", "" + online); + } + + /** + * Sets the offline rewards list. + * + * @param offlineRewards the offline rewards + */ + public void setOfflineRewards(ArrayList offlineRewards) { + // MySQL TEXT max length is 65535 bytes + int maxLength = 65535; + String str = String.join("%line%", offlineRewards); + + // Remove oldest rewards until within limit + while (str.getBytes().length > maxLength && !offlineRewards.isEmpty()) { + offlineRewards.remove(0); + str = String.join("%line%", offlineRewards); + } + + data.setStringList(plugin.getUserManager().getOfflineRewardsPath(), offlineRewards); + } + + /** + * Sets the player name. + * + * @param playerName the player name + */ + public void setPlayerName(String playerName) { + this.playerName = playerName; + } + + /** + * Sets the repeat amount for a reward. + * + * @param reward the reward + * @param amount the repeat amount + */ + public void setRepeatAmount(Reward reward, int amount) { + getData().setInt("Repeat" + reward.getName(), amount); + } + + /** + * Sets the timed rewards map. + * + * @param timed the timed rewards + */ + public void setTimedRewards(HashMap timed) { + ArrayList timedRewards = new ArrayList<>(); + for (Entry entry : timed.entrySet()) { + + String str = ""; + str += entry.getKey() + "%ExecutionTime/%"; + str += entry.getValue(); + timedRewards.add(str); + + } + data.setStringList("TimedRewards", timedRewards); + } + + /** + * Sets the unclaimed choice rewards list. + * + * @param rewards the unclaimed rewards + */ + public void setUnClaimedChoice(ArrayList rewards) { + getData().setStringList("UnClaimedChoices", rewards); + } + + /** + * Sets the user to not cache data. + */ + @Deprecated + public void dontCache() { + userDataFetchMode = UserDataFetchMode.NO_CACHE; + } + + /** + * Sets the uuid. + * + * @param uuid the new uuid + */ + public void setUUID(String uuid) { + this.uuid = uuid; + } + + /** + * Enables temporary caching for this user. + * + * @return this user instance + */ + public AdvancedCoreUser tempCache() { + getUserData().tempCache(); + return this; + } + + /** + * Updates the player name in storage. + * + * @param force whether to force the update + */ + public void updateName(boolean force) { + if (getData().hasData() || force) { + String playerName = getData().getString("PlayerName", userDataFetchMode); + if (playerName == null || !playerName.equals(getPlayerName())) { + getData().setString("PlayerName", getPlayerName(), true); + } + } + } + + /** + * Updates the temporary cache with specific columns. + * + * @param cols the columns to update + */ + public void updateTempCacheWithColumns(ArrayList cols) { + getUserData().updateTempCacheWithColumns(cols); + } + } \ No newline at end of file diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/PersistedQueueReference.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/PersistedQueueReference.java new file mode 100644 index 0000000000..c6f002b5bd --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/PersistedQueueReference.java @@ -0,0 +1,19 @@ +package com.bencodez.advancedcore.api.user; + +/** + * Capability wrapper for a reward reference read from this user's persisted + * offline/timed queue. The constructor is package-private so ordinary reward + * callers cannot manufacture queue provenance through RewardOptions. + */ +public final class PersistedQueueReference { + + private final String reference; + + PersistedQueueReference(String reference) { + this.reference = reference; + } + + public String getReference() { + return reference; + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/misc/PlayerManagerSecurityTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/misc/PlayerManagerSecurityTest.java new file mode 100644 index 0000000000..3cf1b41e94 --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/misc/PlayerManagerSecurityTest.java @@ -0,0 +1,65 @@ +package com.bencodez.advancedcore.api.misc; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import com.bencodez.advancedcore.AdvancedCoreConfigOptions; +import com.bencodez.advancedcore.AdvancedCorePlugin; + +class PlayerManagerSecurityTest { + + @AfterEach + void tearDown() { + AdvancedCorePlugin.setInstance(null); + } + + @Test + void nameFallbackRequiresMatchingUuid() { + UUID requestedUuid = UUID.randomUUID(); + Player differentPlayer = mock(Player.class); + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + + when(differentPlayer.getUniqueId()).thenReturn(UUID.randomUUID()); + when(differentPlayer.hasPermission("reward.permission")).thenReturn(true); + when(plugin.getOptions()).thenReturn(options); + AdvancedCorePlugin.setInstance(plugin); + PlayerManager.getInstance().plugin = plugin; + + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(() -> Bukkit.getPlayer(requestedUuid)).thenReturn(null); + bukkit.when(() -> Bukkit.getPlayer("stored-name")).thenReturn(differentPlayer); + + assertFalse(PlayerManager.getInstance().hasServerPermission(requestedUuid, "stored-name", + "reward.permission")); + } + } + + @Test + void nameFallbackAllowsMatchingUuid() { + UUID requestedUuid = UUID.randomUUID(); + Player requestedPlayer = mock(Player.class); + + when(requestedPlayer.getUniqueId()).thenReturn(requestedUuid); + when(requestedPlayer.hasPermission("reward.permission")).thenReturn(true); + + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(() -> Bukkit.getPlayer(requestedUuid)).thenReturn(null); + bukkit.when(() -> Bukkit.getPlayer("stored-name")).thenReturn(requestedPlayer); + + assertTrue(PlayerManager.getInstance().hasServerPermission(requestedUuid, "stored-name", + "reward.permission")); + } + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/AdvancedRewardsDispatchSecurityTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/AdvancedRewardsDispatchSecurityTest.java new file mode 100644 index 0000000000..2a669daad0 --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/AdvancedRewardsDispatchSecurityTest.java @@ -0,0 +1,55 @@ +package com.bencodez.advancedcore.api.rewards; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import java.util.HashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.Test; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedConstruction; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.item.ItemBuilder; +import com.bencodez.advancedcore.api.rewards.builtin.RewardAdvancedRewards; +import com.bencodez.advancedcore.api.rewards.injected.RewardInject; +import com.bencodez.advancedcore.api.rewards.injected.RewardInjectConfigurationSection; +import com.bencodez.advancedcore.api.user.AdvancedCoreUser; + +class AdvancedRewardsDispatchSecurityTest { + + @Test + void childDispatchPrefixDoesNotDuplicateChildName() { + RewardHandler handler = mock(RewardHandler.class); + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreUser user = mock(AdvancedCoreUser.class); + Reward reward = mock(Reward.class); + CopyOnWriteArrayList injects = new CopyOnWriteArrayList<>(); + when(handler.getInjectedRewards()).thenReturn(injects); + when(reward.getRewardName()).thenReturn("Daily"); + + try (MockedConstruction ignored = mockConstruction(ItemBuilder.class, + withSettings().defaultAnswer(Answers.RETURNS_SELF))) { + RewardAdvancedRewards.register(handler, plugin); + } + + YamlConfiguration data = new YamlConfiguration(); + ConfigurationSection section = data.createSection("AdvancedRewards"); + section.createSection("foo"); + + ((RewardInjectConfigurationSection) injects.get(0)).onRewardRequested(reward, user, section, new HashMap<>()); + + ArgumentCaptor options = ArgumentCaptor.forClass(RewardOptions.class); + verify(handler).giveReward(eq(user), eq(section), eq("foo"), options.capture()); + assertEquals("Daily_AdvancedRewards", options.getValue().getPrefix()); + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardFileDataSnapshotTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardFileDataSnapshotTest.java new file mode 100644 index 0000000000..ffb523cffa --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardFileDataSnapshotTest.java @@ -0,0 +1,65 @@ +package com.bencodez.advancedcore.api.rewards; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.util.List; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.misc.files.FilesManager; + +class RewardFileDataSnapshotTest { + + @TempDir + File tempDir; + + @AfterEach + void tearDown() { + AdvancedCorePlugin.setInstance(null); + } + + @Test + void snapshotReplacementDropsKeysRemovedFromSourceSection() throws Exception { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCorePlugin.setInstance(plugin); + + File target = new File(tempDir, "Queued.yml"); + YamlConfiguration existing = new YamlConfiguration(); + existing.set("Commands", List.of("old command")); + existing.set("Messages", List.of("old message")); + existing.save(target); + + Reward reward = mock(Reward.class); + when(reward.getFile()).thenReturn(target); + when(reward.getName()).thenReturn("Queued"); + RewardFileData rewardFileData = new RewardFileData(reward, tempDir); + + YamlConfiguration replacement = new YamlConfiguration(); + replacement.set("Messages", List.of("new message")); + + FilesManager filesManager = mock(FilesManager.class); + try (MockedStatic files = org.mockito.Mockito.mockStatic(FilesManager.class)) { + files.when(FilesManager::getInstance).thenReturn(filesManager); + rewardFileData.setData(replacement); + } + + ArgumentCaptor saved = ArgumentCaptor.forClass(FileConfiguration.class); + verify(filesManager).editFile(eq(target), saved.capture()); + assertFalse(saved.getValue().contains("Commands"), + "reused generated snapshots must not retain actions removed from the source reward"); + assertEquals(List.of("new message"), saved.getValue().getStringList("Messages")); + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardLoaderSecurityTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardLoaderSecurityTest.java new file mode 100644 index 0000000000..1944e514df --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardLoaderSecurityTest.java @@ -0,0 +1,233 @@ +package com.bencodez.advancedcore.api.rewards; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import com.bencodez.advancedcore.AdvancedCoreConfigOptions; +import com.bencodez.advancedcore.AdvancedCorePlugin; + +class RewardLoaderSecurityTest { + + @TempDir + File tempDir; + + @AfterEach + void tearDown() { + AdvancedCorePlugin.setInstance(null); + } + + @Test + void loadRewardsSuppressesGeneratedSnapshotsWithoutDeletingThem() throws IOException { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + RewardHandler handler = mock(RewardHandler.class); + RewardRegistry registry = mock(RewardRegistry.class); + Logger logger = mock(Logger.class); + ArrayList rewards = new ArrayList<>(); + + File directlyDefined = new File(tempDir, "Rewards/DirectlyDefined"); + assertTrue(directlyDefined.mkdirs()); + File generated = new File(directlyDefined, "Daily_Rewards.yml"); + YamlConfiguration data = new YamlConfiguration(); + data.set("DirectlyDefinedReward", true); + data.set("Messages", List.of("generated reward")); + data.save(generated); + + when(plugin.getDataFolder()).thenReturn(tempDir); + when(plugin.getOptions()).thenReturn(options); + when(plugin.getLogger()).thenReturn(logger); + when(options.isLoadDefaultRewards()).thenReturn(false); + when(handler.getRewardRegistry()).thenReturn(registry); + when(handler.getRewards()).thenReturn(rewards); + + AdvancedCorePlugin.setInstance(plugin); + RewardLoader loader = new RewardLoader(handler, plugin); + loader.addRewardFolder(directlyDefined, false, false); + loader.loadRewards(); + + assertTrue(generated.exists()); + assertFalse(new File(directlyDefined, "Daily_Rewards.yml.disabled").exists()); + assertTrue(rewards.isEmpty()); + } + + @Test + void standaloneDirectLookupOfGeneratedSnapshotIsBlocked() throws IOException { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + RewardHandler handler = mock(RewardHandler.class); + Logger logger = mock(Logger.class); + + File directlyDefined = new File(tempDir, "Rewards/DirectlyDefined"); + assertTrue(directlyDefined.mkdirs()); + YamlConfiguration data = new YamlConfiguration(); + data.set("DirectlyDefinedReward", true); + data.set("Messages", List.of("generated reward")); + data.save(new File(directlyDefined, "QueuedReward.yml")); + + when(plugin.getDataFolder()).thenReturn(tempDir); + when(plugin.getOptions()).thenReturn(options); + when(plugin.getLogger()).thenReturn(logger); + when(handler.getRewards()).thenReturn(new ArrayList<>()); + + AdvancedCorePlugin.setInstance(plugin); + RewardLoader loader = new RewardLoader(handler, plugin); + Reward reward = loader.getRewardDirectlyDefined("QueuedReward"); + + assertTrue(reward instanceof QueuedGeneratedReward); + assertTrue(((QueuedGeneratedReward) reward).getAllowedUserUuids().isEmpty()); + } + + @Test + void queueOnlyResolverBindsGeneratedSnapshotToRequestedUser() throws IOException { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + RewardHandler handler = mock(RewardHandler.class); + Logger logger = mock(Logger.class); + UUID queuedUuid = UUID.randomUUID(); + + File directlyDefined = new File(tempDir, "Rewards/DirectlyDefined"); + assertTrue(directlyDefined.mkdirs()); + YamlConfiguration data = new YamlConfiguration(); + data.set("DirectlyDefinedReward", true); + data.set("Messages", List.of("queued reward")); + data.save(new File(directlyDefined, "QueuedReward.yml")); + + when(plugin.getDataFolder()).thenReturn(tempDir); + when(plugin.getOptions()).thenReturn(options); + when(plugin.getLogger()).thenReturn(logger); + when(handler.getRewards()).thenReturn(new ArrayList<>()); + + AdvancedCorePlugin.setInstance(plugin); + RewardLoader loader = new RewardLoader(handler, plugin); + Reward reward = loader.getQueuedGeneratedReward("QueuedReward", queuedUuid.toString()); + + assertTrue(reward instanceof QueuedGeneratedReward); + assertTrue(reward.isGeneratedSnapshotCreated()); + assertEquals(java.util.Set.of(queuedUuid.toString()), + ((QueuedGeneratedReward) reward).getAllowedUserUuids()); + } + + @Test + @SuppressWarnings("unchecked") + void generatedDotNameDoesNotSuppressManualUnderscoreName() throws Exception { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + RewardHandler handler = mock(RewardHandler.class); + + File directlyDefined = new File(tempDir, "Rewards/DirectlyDefined"); + assertTrue(directlyDefined.mkdirs()); + YamlConfiguration generatedData = new YamlConfiguration(); + generatedData.set("DirectlyDefinedReward", true); + generatedData.set("Messages", List.of("generated")); + generatedData.save(new File(directlyDefined, "Foo.Bar.yml")); + YamlConfiguration manualData = new YamlConfiguration(); + manualData.set("Messages", List.of("manual")); + manualData.save(new File(directlyDefined, "Foo_Bar.yml")); + + when(plugin.getDataFolder()).thenReturn(tempDir); + + RewardLoader loader = new RewardLoader(handler, plugin); + loader.addRewardFolder(directlyDefined, false, false); + + java.lang.reflect.Method suppress = RewardLoader.class + .getDeclaredMethod("suppressGeneratedDirectlyDefinedFiles"); + suppress.setAccessible(true); + suppress.invoke(loader); + + java.lang.reflect.Field suppressedField = RewardLoader.class + .getDeclaredField("suppressedDirectlyDefinedRewards"); + suppressedField.setAccessible(true); + Set suppressed = (Set) suppressedField.get(loader); + + assertTrue(suppressed.contains("foo.bar.yml")); + assertFalse(suppressed.contains("foo_bar.yml")); + } + + @Test + void reusedGeneratedSnapshotDropsRemovedRewardKeys() throws IOException { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCorePlugin.setInstance(plugin); + + File directlyDefined = new File(tempDir, "Rewards/DirectlyDefined"); + assertTrue(directlyDefined.mkdirs()); + File snapshot = new File(directlyDefined, "Daily_AdvancedRewards_foo.yml"); + YamlConfiguration oldData = new YamlConfiguration(); + oldData.set("Commands", List.of("old command")); + oldData.set("Messages", List.of("old message")); + oldData.save(snapshot); + + Reward reward = new Reward(directlyDefined, "Daily_AdvancedRewards_foo"); + YamlConfiguration replacement = new YamlConfiguration(); + replacement.set("Messages", List.of("new message")); + + com.bencodez.advancedcore.api.misc.files.FilesManager filesManager = + mock(com.bencodez.advancedcore.api.misc.files.FilesManager.class); + org.mockito.Mockito.doAnswer(invocation -> { + File file = invocation.getArgument(0); + org.bukkit.configuration.file.FileConfiguration config = invocation.getArgument(1); + config.save(file); + return null; + }).when(filesManager).editFile( + org.mockito.ArgumentMatchers.any(File.class), + org.mockito.ArgumentMatchers.any(org.bukkit.configuration.file.FileConfiguration.class)); + + try (org.mockito.MockedStatic files = + org.mockito.Mockito.mockStatic(com.bencodez.advancedcore.api.misc.files.FilesManager.class)) { + files.when(com.bencodez.advancedcore.api.misc.files.FilesManager::getInstance).thenReturn(filesManager); + reward.getConfig().setData(replacement); + } + + YamlConfiguration saved = YamlConfiguration.loadConfiguration(snapshot); + assertFalse(saved.contains("Commands")); + assertFalse(saved.isConfigurationSection("Commands")); + assertEquals(List.of("new message"), saved.getStringList("Messages")); + } + + @Test + void loadRewardsDoesNotSuppressNormalDirectFolderFileWithoutGeneratedMarker() throws IOException { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + RewardHandler handler = mock(RewardHandler.class); + RewardRegistry registry = mock(RewardRegistry.class); + Logger logger = mock(Logger.class); + ArrayList rewards = new ArrayList<>(); + + File directlyDefined = new File(tempDir, "Rewards/DirectlyDefined"); + assertTrue(directlyDefined.mkdirs()); + File manual = new File(directlyDefined, "ExampleBasic.yml"); + YamlConfiguration manualData = new YamlConfiguration(); + manualData.set("Messages", List.of("manual reward")); + manualData.save(manual); + + when(plugin.getDataFolder()).thenReturn(tempDir); + when(plugin.getOptions()).thenReturn(options); + when(plugin.getLogger()).thenReturn(logger); + when(options.isLoadDefaultRewards()).thenReturn(false); + when(handler.getRewardRegistry()).thenReturn(registry); + when(handler.getRewards()).thenReturn(rewards); + when(handler.rewardExist("ExampleBasic")).thenReturn(false); + + AdvancedCorePlugin.setInstance(plugin); + RewardLoader loader = new RewardLoader(handler, plugin); + loader.addRewardFolder(directlyDefined, false, false); + loader.loadRewards(); + + assertTrue(manual.exists()); + assertEquals(1, rewards.size()); + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardRegistrySecurityTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardRegistrySecurityTest.java new file mode 100644 index 0000000000..0590c6f7dc --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/rewards/RewardRegistrySecurityTest.java @@ -0,0 +1,50 @@ +package com.bencodez.advancedcore.api.rewards; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +import com.bencodez.advancedcore.AdvancedCorePlugin; + +class RewardRegistrySecurityTest { + + @Test + void rewardFileNameRejectsPathTraversal() { + assertFalse(RewardRegistry.isSafeRewardFileName("../../OtherPlugin/config")); + assertFalse(RewardRegistry.isSafeRewardFileName("..\\..\\OtherPlugin\\config")); + assertFalse(RewardRegistry.isSafeRewardFileName("folder/reward")); + assertFalse(RewardRegistry.isSafeRewardFileName("reward\0name")); + } + + @Test + void rewardFileNameAllowsNormalRewardNames() { + assertTrue(RewardRegistry.isSafeRewardFileName("Daily")); + assertTrue(RewardRegistry.isSafeRewardFileName("Daily_Reward")); + assertTrue(RewardRegistry.isSafeRewardFileName("Daily.Reward-v2")); + } + + @Test + void generatedDirectlyDefinedSnapshotIsNeverPublishedToRegistry() { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + RewardRegistry registry = new RewardRegistry(plugin); + Reward reward = mock(Reward.class); + RewardFileData config = mock(RewardFileData.class); + + when(reward.getConfig()).thenReturn(config); + when(reward.getName()).thenReturn("PriorityChild"); + when(config.isDirectlyDefinedReward()).thenReturn(true); + when(config.getRewardFolder()).thenReturn(new File("Rewards", "DirectlyDefined")); + + registry.updateReward(reward); + + assertFalse(registry.getRewards().contains(reward)); + verify(reward, never()).validate(); + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/QueuedGeneratedRewardDispatchTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/QueuedGeneratedRewardDispatchTest.java new file mode 100644 index 0000000000..ec2ee9fadc --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/QueuedGeneratedRewardDispatchTest.java @@ -0,0 +1,233 @@ +package com.bencodez.advancedcore.api.rewards; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.user.AdvancedCoreUser; + +class QueuedGeneratedRewardDispatchTest { + + private static final String QUEUE_PREFIX = "\\AdvancedCoreQueue/1/"; + + private AdvancedCorePlugin plugin; + private RewardHandler handler; + private AdvancedCoreUser user; + private RewardExecutor executor; + + @BeforeEach + void setUp() { + plugin = mock(AdvancedCorePlugin.class); + handler = mock(RewardHandler.class); + user = mock(AdvancedCoreUser.class); + executor = new RewardExecutor(handler, plugin); + when(plugin.getLogger()).thenReturn(mock(Logger.class)); + when(plugin.getRewardHandler()).thenReturn(handler); + when(user.getPlugin()).thenReturn(plugin); + when(user.getUUID()).thenReturn("uuid"); + when(user.getPlayerName()).thenReturn("Ben"); + when(user.isOnline()).thenReturn(true); + } + + private String queuedReference(String rewardName, boolean snapshot) { + String encodedName = Base64.getUrlEncoder().withoutPadding() + .encodeToString(rewardName.getBytes(StandardCharsets.UTF_8)); + return QUEUE_PREFIX + (snapshot ? "snapshot/" : "normal/") + encodedName; + } + + @Test + void offlineQueueReplayResolvesGeneratedSnapshotBeforeNormalFallback() { + Reward queued = mock(Reward.class); + Reward fallback = mock(Reward.class); + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false); + when(handler.getQueuedGeneratedReward("QueuedReward", "uuid")).thenReturn(queued); + when(handler.getReward("QueuedReward")).thenReturn(fallback); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.givePersistedQueueReward(user, "QueuedReward", options); + } + + verify(handler).getQueuedGeneratedReward("QueuedReward", "uuid"); + verify(handler, never()).getReward("QueuedReward"); + verify(queued).giveReward(user, options); + verify(fallback, never()).giveReward(user, options); + } + + @Test + void timedQueueReplayCanResolveGeneratedSnapshot() { + Reward queued = mock(Reward.class); + RewardOptions options = new RewardOptions().setCheckTimed(false).addPlaceholder("date", "now"); + when(handler.getQueuedGeneratedReward("QueuedReward", "uuid")).thenReturn(queued); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.givePersistedQueueReward(user, "QueuedReward", options); + } + + verify(handler).getQueuedGeneratedReward("QueuedReward", "uuid"); + verify(queued).giveReward(user, options); + } + + @Test + void rewardBuilderUsesAlreadyResolvedQueueRewardWithoutReresolving() { + Reward resolved = mock(Reward.class); + when(resolved.getRewardName()).thenReturn("QueuedReward"); + + RewardBuilder builder = new RewardBuilder(resolved).setCheckTimed(false).withPlaceHolder("date", "now"); + builder.send(user); + + verify(handler, never()).getQueuedGeneratedReward("QueuedReward", "uuid"); + verify(handler).giveReward(user, resolved, builder.getRewardOptions()); + } + + @Test + void explicitNormalQueueReferenceDoesNotUseStaleSnapshot() { + Reward normal = mock(Reward.class); + Reward stale = mock(Reward.class); + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false); + when(handler.getReward("Daily")).thenReturn(normal); + when(handler.getQueuedGeneratedReward("Daily", "uuid")).thenReturn(stale); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.givePersistedQueueReward(user, queuedReference("Daily", false), options); + } + + verify(normal).giveReward(user, options); + verify(handler, never()).getQueuedGeneratedReward("Daily", "uuid"); + } + + @Test + void explicitSnapshotQueueReferenceUsesGeneratedSnapshot() { + Reward queued = mock(Reward.class); + Reward normal = mock(Reward.class); + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false); + when(handler.getQueuedGeneratedReward("Daily", "uuid")).thenReturn(queued); + when(handler.getReward("Daily")).thenReturn(normal); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.givePersistedQueueReward(user, queuedReference("Daily", true), options); + } + + verify(queued).giveReward(user, options); + verify(handler, never()).getReward("Daily"); + } + + @Test + void legacyQueueReferencePrefersRegisteredRewardOverStaleSnapshot() { + Reward normal = mock(Reward.class); + Reward stale = mock(Reward.class); + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false); + when(handler.rewardExist("Daily")).thenReturn(true); + when(handler.getReward("Daily")).thenReturn(normal); + when(handler.getQueuedGeneratedReward("Daily", "uuid")).thenReturn(stale); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.givePersistedQueueReward(user, "Daily", options); + } + + verify(normal).giveReward(user, options); + verify(handler, never()).getQueuedGeneratedReward("Daily", "uuid"); + } + + @Test + void legacyQueueNameEndingInOldMarkerIsPreservedExactly() { + String rewardName = "Promo%generatedsnapshot%true"; + Reward normal = mock(Reward.class); + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false); + when(handler.rewardExist(rewardName)).thenReturn(true); + when(handler.getReward(rewardName)).thenReturn(normal); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.givePersistedQueueReward(user, rewardName, options); + } + + verify(handler).getReward(rewardName); + verify(normal).giveReward(user, options); + verify(handler, never()).getQueuedGeneratedReward("Promo", "uuid"); + verify(handler, never()).getReward("Promo"); + } + + @Test + void normalDispatchPreservesRewardNameContainingSnapshotMarkerText() { + String rewardName = "Promo%generatedsnapshot%Bonus"; + Reward normal = mock(Reward.class); + RewardOptions options = new RewardOptions(); + when(handler.getReward(rewardName)).thenReturn(normal); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.giveReward(user, rewardName, options); + } + + verify(handler).getReward(rewardName); + verify(normal).giveReward(user, options); + verify(handler, never()).getReward("Promo"); + verify(handler, never()).getQueuedGeneratedReward("Promo", "uuid"); + } + + @Test + void forgedOfflineOptionsCannotGrantQueueSnapshotAccess() { + Reward normal = mock(Reward.class); + Reward queued = mock(Reward.class); + RewardOptions options = new RewardOptions().setOnline(false).setCheckTimed(false); + when(handler.getReward("QueuedReward")).thenReturn(normal); + when(handler.getQueuedGeneratedReward("QueuedReward", "uuid")).thenReturn(queued); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.giveReward(user, "QueuedReward", options); + } + + verify(handler).getReward("QueuedReward"); + verify(handler, never()).getQueuedGeneratedReward("QueuedReward", "uuid"); + verify(normal).giveReward(user, options); + verify(queued, never()).giveReward(user, options); + } + + @Test + void forgedTimedPlaceholderCannotGrantQueueSnapshotAccess() { + Reward normal = mock(Reward.class); + Reward queued = mock(Reward.class); + RewardOptions options = new RewardOptions().setCheckTimed(false).addPlaceholder("date", "now"); + when(handler.getReward("QueuedReward")).thenReturn(normal); + when(handler.getQueuedGeneratedReward("QueuedReward", "uuid")).thenReturn(queued); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.giveReward(user, "QueuedReward", options); + } + + verify(handler).getReward("QueuedReward"); + verify(handler, never()).getQueuedGeneratedReward("QueuedReward", "uuid"); + verify(normal).giveReward(user, options); + verify(queued, never()).giveReward(user, options); + } + + @Test + void normalStandaloneDispatchCannotResolveGeneratedSnapshot() { + RewardOptions options = new RewardOptions(); + + try (MockedStatic bukkit = org.mockito.Mockito.mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::isPrimaryThread).thenReturn(false); + executor.giveReward(user, "QueuedReward", options); + } + + verify(handler, never()).getQueuedGeneratedReward("QueuedReward", "uuid"); + } +} diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardServicesTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardServicesTest.java index b3ad2b593c..a1755a4520 100644 --- a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardServicesTest.java +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardServicesTest.java @@ -1,6 +1,8 @@ package com.bencodez.advancedcore.tests.rewards; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -25,6 +27,8 @@ import com.bencodez.advancedcore.AdvancedCorePlugin; import com.bencodez.advancedcore.api.rewards.DefinedReward; import com.bencodez.advancedcore.api.rewards.DirectlyDefinedReward; +import com.bencodez.advancedcore.api.rewards.Reward; +import com.bencodez.advancedcore.api.rewards.RewardFileDefinedReward; import com.bencodez.advancedcore.api.rewards.RewardHandler; import com.bencodez.advancedcore.api.rewards.SubDirectlyDefinedReward; import com.bencodez.advancedcore.api.rewards.injected.RewardInject; @@ -118,8 +122,7 @@ public void subRewardResolverStopsRecursiveDiscovery() { when(loop.isDirectlyDefined()).thenReturn(true); RewardInject inject = mock(RewardInject.class); - when(inject.subRewards(any(DefinedReward.class))) - .thenReturn(new ArrayList<>(List.of(loop))); + when(inject.subRewards(any(DefinedReward.class))).thenReturn(new ArrayList<>(List.of(loop))); handler.getInjectedRewards().add(inject); handler.checkSubRewards(root); @@ -128,4 +131,52 @@ public void subRewardResolverStopsRecursiveDiscovery() { assertSame(loop, handler.getSubDirectlyDefinedRewards().get(0)); verify(logger).warning("Detected recursive sub reward path, skipping: Loop"); } + + @Test + public void fileBackedSubRewardsRemainInternalToSectionDispatch() { + Reward root = mock(Reward.class); + when(root.getName()).thenReturn("Daily"); + handler.getRewards().add(root); + + SubDirectlyDefinedReward sub = mock(SubDirectlyDefinedReward.class); + when(sub.getFullPath()).thenReturn("Daily.Rewards"); + + RewardInject inject = mock(RewardInject.class); + when(inject.subRewards(any(DefinedReward.class))).thenAnswer(invocation -> { + DefinedReward defined = invocation.getArgument(0); + if (defined instanceof RewardFileDefinedReward) { + return new ArrayList<>(List.of(sub)); + } + return new ArrayList<>(); + }); + handler.getInjectedRewards().add(inject); + + handler.checkSubRewards(); + + assertNull(handler.getSubDirectlyDefined("Daily_Rewards")); + assertSame(sub, handler.getSubRewardResolver().getFileBackedSubReward("Daily_Rewards")); + } + + @Test + public void fileBackedSubRewardsRemainSnapshotCapableForDeferredExecution() { + YamlConfiguration config = new YamlConfiguration(); + config.createSection("Parent.Child").set("Messages", List.of("queued")); + + RewardFileDefinedReward fileMaster = mock(RewardFileDefinedReward.class); + when(fileMaster.getFullPath()).thenReturn("Daily"); + when(fileMaster.needsDot()).thenReturn("."); + when(fileMaster.getPath()).thenReturn("Parent"); + when(fileMaster.getFileData()).thenReturn(config); + Reward fileBacked = new SubDirectlyDefinedReward(fileMaster, "Child").getReward(); + + DirectlyDefinedReward directMaster = mock(DirectlyDefinedReward.class); + when(directMaster.getFullPath()).thenReturn("Direct"); + when(directMaster.needsDot()).thenReturn("."); + when(directMaster.getPath()).thenReturn("Parent"); + when(directMaster.getFileData()).thenReturn(config); + Reward directlyDefined = new SubDirectlyDefinedReward(directMaster, "Child").getReward(); + + assertTrue(fileBacked.isNeedsRewardFile()); + assertFalse(directlyDefined.isNeedsRewardFile()); + } } diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/AdvancedCoreUserTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/AdvancedCoreUserTest.java index 692030ae56..780107790d 100644 --- a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/AdvancedCoreUserTest.java +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/user/AdvancedCoreUserTest.java @@ -20,6 +20,7 @@ import com.bencodez.advancedcore.api.rewards.RewardHandler; import com.bencodez.advancedcore.api.rewards.RewardOptions; import com.bencodez.advancedcore.api.user.AdvancedCoreUser; +import com.bencodez.advancedcore.api.user.PersistedQueueReference; import com.bencodez.advancedcore.api.user.UserData; import com.bencodez.advancedcore.api.user.UserDataFetchMode; import com.bencodez.advancedcore.api.user.UserManager; @@ -67,8 +68,10 @@ void checkOfflineRewards_preservesServerRequirementForNormalReplay() { user.checkOfflineRewards(); + ArgumentCaptor referenceCaptor = ArgumentCaptor.forClass(PersistedQueueReference.class); ArgumentCaptor optionsCaptor = ArgumentCaptor.forClass(RewardOptions.class); - verify(rewardHandler).giveReward(eq(user), eq("VoteReward"), optionsCaptor.capture()); + verify(rewardHandler).givePersistedQueueReward(eq(user), referenceCaptor.capture(), optionsCaptor.capture()); + assertEquals("VoteReward", referenceCaptor.getValue().getReference()); RewardOptions options = optionsCaptor.getValue(); assertFalse(options.isForceOffline()); assertTrue(options.isGiveOffline()); @@ -84,8 +87,10 @@ void forceRunOfflineRewards_stillBypassesNormalReplayChecks() { user.forceRunOfflineRewards(); + ArgumentCaptor referenceCaptor = ArgumentCaptor.forClass(PersistedQueueReference.class); ArgumentCaptor optionsCaptor = ArgumentCaptor.forClass(RewardOptions.class); - verify(rewardHandler).giveReward(eq(user), eq("VoteReward"), optionsCaptor.capture()); + verify(rewardHandler).givePersistedQueueReward(eq(user), referenceCaptor.capture(), optionsCaptor.capture()); + assertEquals("VoteReward", referenceCaptor.getValue().getReference()); RewardOptions options = optionsCaptor.getValue(); assertTrue(options.isForceOffline()); assertFalse(options.isGiveOffline());