From 69afab1aa289660916b9808f749ad2e881deab41 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 13:15:06 -0600 Subject: [PATCH 1/6] Extract reward injection registry --- .../api/rewards/RewardInjectionRegistry.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardInjectionRegistry.java diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardInjectionRegistry.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardInjectionRegistry.java new file mode 100644 index 0000000000..d55d68fff6 --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardInjectionRegistry.java @@ -0,0 +1,90 @@ +package com.bencodez.advancedcore.api.rewards; + +import java.util.Collections; +import java.util.Comparator; +import java.util.concurrent.CopyOnWriteArrayList; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.rewards.builtin.BuiltinRewards; +import com.bencodez.advancedcore.api.rewards.builtin.requirements.BuiltinRequirements; +import com.bencodez.advancedcore.api.rewards.injected.RewardInject; +import com.bencodez.advancedcore.api.rewards.injectedrequirement.RequirementInject; + +/** + * Owns injected reward, requirement, and reward-placeholder registrations. + */ +public class RewardInjectionRegistry { + + private final RewardHandler handler; + private final AdvancedCorePlugin plugin; + private final CopyOnWriteArrayList injectedRequirements = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList injectedRewards = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList placeholders = new CopyOnWriteArrayList<>(); + + public RewardInjectionRegistry(RewardHandler handler, AdvancedCorePlugin plugin) { + this.handler = handler; + this.plugin = plugin; + } + + public void addInjectedRequirement(RequirementInject inject) { + injectedRequirements.add(inject); + sortInjectedRequirements(); + } + + public void addInjectedReward(RewardInject inject) { + injectedRewards.add(inject); + sortInjectedRewards(); + } + + public void addPlaceholder(RewardPlaceholderHandle handle) { + placeholders.add(handle); + } + + public CopyOnWriteArrayList getInjectedRequirements() { + return injectedRequirements; + } + + public CopyOnWriteArrayList getInjectedRewards() { + return injectedRewards; + } + + public CopyOnWriteArrayList getPlaceholders() { + return placeholders; + } + + public void loadInjectedRequirements() { + injectedRequirements.clear(); + BuiltinRequirements.register(handler, plugin); + for (RequirementInject requirement : injectedRequirements) { + requirement.setInternalReward(true); + } + sortInjectedRequirements(); + } + + public void loadInjectedRewards() { + injectedRewards.clear(); + BuiltinRewards.register(handler, plugin); + for (RewardInject reward : injectedRewards) { + reward.setInternalReward(true); + } + sortInjectedRewards(); + } + + public void sortInjectedRequirements() { + Collections.sort(injectedRequirements, new Comparator() { + @Override + public int compare(RequirementInject o1, RequirementInject o2) { + return Integer.compare(o2.getPriority(), o1.getPriority()); + } + }); + } + + public void sortInjectedRewards() { + Collections.sort(injectedRewards, new Comparator() { + @Override + public int compare(RewardInject o1, RewardInject o2) { + return Integer.compare(o2.getPriority(), o1.getPriority()); + } + }); + } +} From a53ade9934ff59fb438702a712f1f94364ab0600 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 13:15:26 -0600 Subject: [PATCH 2/6] Extract reward loader --- .../api/rewards/RewardLoader.java | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardLoader.java 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 new file mode 100644 index 0000000000..14ac22011e --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardLoader.java @@ -0,0 +1,206 @@ +package com.bencodez.advancedcore.api.rewards; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.exceptions.FileDirectoryException; + +/** + * Owns reward folder discovery and reward-file loading. + */ +public class RewardLoader { + + private File defaultFolder; + private final RewardHandler handler; + private final AdvancedCorePlugin plugin; + private final ArrayList rewardFolders = new ArrayList<>(); + + public RewardLoader(RewardHandler handler, AdvancedCorePlugin plugin) { + this.handler = handler; + this.plugin = plugin; + defaultFolder = new File(plugin.getDataFolder(), "Rewards"); + } + + public void addRewardFolder(File file) { + addRewardFolder(file, true, true); + } + + public void addRewardFolder(File file, boolean load, boolean create) { + if (create) { + file.mkdirs(); + } + if (!file.exists()) { + return; + } + if (file.isDirectory()) { + if (!rewardFolders.contains(file)) { + rewardFolders.add(file); + if (load) { + loadRewards(); + } + } + } else { + plugin.debug(file.getAbsolutePath()); + try { + throw new FileDirectoryException("File is not a directory"); + } catch (FileDirectoryException e) { + e.printStackTrace(); + } + } + } + + public void checkDirectlyDefined() { + for (Reward rewardFile : handler.getRewards()) { + File folder = rewardFile.getConfig().getRewardFolder(); + if (folder != null && folder.getName().equalsIgnoreCase("DirectlyDefined")) { + if (handler.hasDirectRewardHandle(rewardFile.getName())) { + rewardFile.getFile().delete(); + } + } + } + loadRewards(); + } + + private void copyFile(String fileName) { + File file = new File(plugin.getDataFolder(), "Rewards" + File.separator + fileName); + if (!file.exists()) { + plugin.saveResource("Rewards" + File.separator + fileName, true); + } + } + + public File getDefaultFolder() { + return defaultFolder; + } + + private String getFileExtension(File file) { + String name = file.getName(); + int lastIndexOf = name.lastIndexOf("."); + if (lastIndexOf == -1) { + return ""; + } + return name.substring(lastIndexOf); + } + + public Reward getRewardDirectlyDefined(String reward) { + if (reward == null) { + reward = ""; + } + reward = reward.replace(" ", "_"); + + for (Reward rewardFile : handler.getRewards()) { + File folder = rewardFile.getConfig().getRewardFolder(); + if (folder != null && folder.getName().equalsIgnoreCase("DirectlyDefined")) { + if (rewardFile.getName().equalsIgnoreCase(reward)) { + return rewardFile; + } + } + } + + if (reward.equals("")) { + plugin.getLogger().warning("Tried to get any empty reward file name, renaming to EmptyName"); + reward = "EmptyName"; + } + + if (reward.equalsIgnoreCase("examplebasic") || reward.equalsIgnoreCase("exampleadvanced")) { + plugin.getLogger().warning("Using example rewards as a reward, becarefull"); + } + + File directFolder = new File(getDefaultFolder().getAbsolutePath() + File.separator + "DirectlyDefined"); + directFolder.mkdirs(); + return new Reward(directFolder, reward); + } + + public ArrayList getRewardFiles(File folder) { + ArrayList fileNames = new ArrayList<>(); + if (folder != null && folder.exists()) { + File[] files = folder.listFiles(); + if (files == null) { + return fileNames; + } + for (File file : files) { + if (getFileExtension(file).equals(".yml")) { + fileNames.add(file.getName()); + } + } + } + return fileNames; + } + + public ArrayList getRewardNames(File file) { + ArrayList rewardFiles = getRewardFiles(file); + for (int i = 0; i < rewardFiles.size(); i++) { + if (rewardFiles.get(i).contains(".yml")) { + rewardFiles.set(i, rewardFiles.get(i).replace(".yml", "")); + } else { + plugin.debug("Not a proper reward file: " + rewardFiles.get(i)); + } + } + Collections.sort(rewardFiles, String.CASE_INSENSITIVE_ORDER); + return rewardFiles; + } + + public void loadRewards() { + handler.getRewardRegistry().resetRewards(); + setupExample(); + handler.addValidPath("DirectlyDefinedReward"); + handler.addValidPath("Delayed"); + handler.addValidPath("Timed"); + handler.addValidPath("DisplayItem"); + handler.addValidPath("ForceOffline"); + for (File file : rewardFolders) { + loadRewards(file); + } + handler.sortInjectedRewards(); + handler.sortInjectedRequirements(); + plugin.debug("Loaded rewards"); + } + + private void loadRewards(File file) { + for (String reward : getRewardNames(file)) { + if (!reward.equals("")) { + if (!handler.rewardExist(reward)) { + try { + Reward reward1 = new Reward(file, reward); + reward1.validate(); + if (!reward1.getConfig().isDirectlyDefinedReward() + || file.getName().equalsIgnoreCase("DirectlyDefined")) { + handler.getRewards().add(reward1); + if (reward1.getConfig().getConfigData().getConfigurationSection("").getKeys(true).size() > 0) { + plugin.extraDebug("Loaded Reward File: " + file.getAbsolutePath() + "/" + reward); + } else { + plugin.debug("Loaded empty reward file" + file.getAbsolutePath() + "/" + reward); + } + } else { + plugin.extraDebug("Ignoring directly defined reward file " + file.getAbsolutePath() + "/" + reward); + } + } catch (Exception e) { + plugin.getLogger().severe("Failed to load reward file " + reward + ".yml: " + e.getMessage()); + e.printStackTrace(); + } + } else { + plugin.debug("Detected that a reward file named " + reward + + " already exists, cannot load reward file " + file.getAbsolutePath() + "/" + reward); + } + } else { + plugin.getLogger().warning( + "Detected getting a reward file with an empty name! That means you either didn't type a name or didn't properly make an empty list"); + } + } + } + + public void setDefaultFolder(File defaultFolder) { + this.defaultFolder = defaultFolder; + } + + public void setupExample() { + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdir(); + } + if (AdvancedCorePlugin.getInstance().getOptions().isLoadDefaultRewards()) { + copyFile("ExampleBasic.yml"); + copyFile("ExampleAdvanced.yml"); + } + } +} From fa08fbdbbe729364d8daed7ed9438f2417910dfd Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 13:15:35 -0600 Subject: [PATCH 3/6] Extract reward validator helpers --- .../api/rewards/RewardValidator.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardValidator.java diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardValidator.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardValidator.java new file mode 100644 index 0000000000..55189592c8 --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardValidator.java @@ -0,0 +1,74 @@ +package com.bencodez.advancedcore.api.rewards; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.configuration.file.FileConfiguration; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.simpleapi.array.ArrayUtils; + +/** + * Owns reward validation helper state and conflict/content checks. + */ +public class RewardValidator { + + private final RewardHandler handler; + private final AdvancedCorePlugin plugin; + private Set validPaths = new HashSet<>(); + + public RewardValidator(RewardHandler handler, AdvancedCorePlugin plugin) { + this.handler = handler; + this.plugin = plugin; + } + + public void addValidPath(String path) { + validPaths.add(path); + } + + public void checkDirectlyDefinedRewardFiles() { + ArrayList directlyDefinedPaths = new ArrayList<>(); + for (DirectlyDefinedReward direct : handler.getDirectlyDefinedRewards()) { + directlyDefinedPaths.add(direct.getPath().replace(".", "_")); + } + + for (SubDirectlyDefinedReward direct : handler.getSubDirectlyDefinedRewards()) { + directlyDefinedPaths.add(direct.getFullPath().replace(".", "_")); + } + + for (Reward rewardFile : handler.getRewards()) { + if (ArrayUtils.containsIgnoreCase(directlyDefinedPaths, rewardFile.getName())) { + plugin.getLogger().warning("Found reward file conflict: " + rewardFile.getName() + + ", recommend deleting or renaming file to prevent issues"); + } + } + } + + public Set getValidPaths() { + return validPaths; + } + + public boolean hasRewards(FileConfiguration data, String path) { + if (data.isList(path)) { + if (data.getList(path, new ArrayList<>()).size() != 0) { + return true; + } + } + if (data.isConfigurationSection(path)) { + if (data.getConfigurationSection(path).getKeys(false).size() != 0) { + return true; + } + } + if (data.isString(path)) { + if (!data.getString(path, "").equals("")) { + return true; + } + } + return false; + } + + public void setValidPaths(Set validPaths) { + this.validPaths = validPaths; + } +} From 8ced485bcbdd95c224d9376f971f25aa13caa4e4 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 13:15:44 -0600 Subject: [PATCH 4/6] Extract sub reward resolver --- .../api/rewards/SubRewardResolver.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubRewardResolver.java 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 new file mode 100644 index 0000000000..65b2caa399 --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/SubRewardResolver.java @@ -0,0 +1,61 @@ +package com.bencodez.advancedcore.api.rewards; + +import java.util.HashSet; +import java.util.Set; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.rewards.injected.RewardInject; + +/** + * Discovers directly-defined sub rewards and prevents recursive discovery loops. + */ +public class SubRewardResolver { + + private final RewardHandler handler; + private final AdvancedCorePlugin plugin; + + public SubRewardResolver(RewardHandler handler, AdvancedCorePlugin plugin) { + this.handler = handler; + this.plugin = plugin; + } + + public void checkSubRewards() { + plugin.extraDebug("Checking directlydefined rewards for sub rewards"); + handler.getRewardRegistry().resetSubDirectlyDefinedRewards(); + for (DirectlyDefinedReward direct : handler.getDirectlyDefinedRewards()) { + checkSubRewards(direct, new HashSet<>()); + } + + plugin.extraDebug("Checking reward file for sub rewards"); + for (Reward reward : handler.getRewards()) { + checkSubRewards(new RewardFileDefinedReward(reward), new HashSet<>()); + } + } + + public void checkSubRewards(DefinedReward direct) { + checkSubRewards(direct, new HashSet<>()); + } + + private void checkSubRewards(DefinedReward direct, Set activePaths) { + String path = direct.getFullPath(); + if (!activePaths.add(path)) { + plugin.getLogger().warning("Detected recursive sub reward path, skipping: " + path); + return; + } + try { + for (RewardInject inject : handler.getInjectedRewards()) { + for (SubDirectlyDefinedReward sub : inject.subRewards(direct)) { + String subPath = sub.getFullPath(); + if (activePaths.contains(subPath)) { + plugin.getLogger().warning("Detected recursive sub reward path, skipping: " + subPath); + continue; + } + handler.addSubDirectlyDefined(sub); + checkSubRewards(sub, activePaths); + } + } + } finally { + activePaths.remove(path); + } + } +} From f198e0ee76965e4fbbf5db29eedf45290f63a9ef Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 13:16:25 -0600 Subject: [PATCH 5/6] Delegate reward services from RewardHandler --- .../api/rewards/RewardHandler.java | 363 +++--------------- 1 file changed, 51 insertions(+), 312 deletions(-) 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 8f80b95856..00dc9ac6a7 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 @@ -2,10 +2,7 @@ import java.io.File; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; @@ -21,7 +18,6 @@ import org.bukkit.entity.Player; import com.bencodez.advancedcore.AdvancedCorePlugin; -import com.bencodez.advancedcore.api.exceptions.FileDirectoryException; import com.bencodez.advancedcore.api.misc.MiscUtils; import com.bencodez.advancedcore.api.rewards.injected.RewardInject; import com.bencodez.advancedcore.api.rewards.injectedrequirement.RequirementInject; @@ -31,45 +27,36 @@ import com.bencodez.simpleapi.array.ArrayUtils; import lombok.Getter; -import lombok.Setter; /** - * The Class RewardHandler. + * Public reward facade. Specialized reward state and service responsibilities are + * delegated to focused classes while this class preserves the existing API. */ public class RewardHandler { - /** The default folder. */ - private File defaultFolder; - @Getter private final RewardRegistry rewardRegistry; - @Getter - private CopyOnWriteArrayList injectedRequirements = new CopyOnWriteArrayList(); - + private final RewardLoader rewardLoader; @Getter - private CopyOnWriteArrayList injectedRewards = new CopyOnWriteArrayList(); - + private final RewardValidator rewardValidator; + @Getter + private final SubRewardResolver subRewardResolver; @Getter - private CopyOnWriteArrayList placeholders = new CopyOnWriteArrayList(); + private final RewardInjectionRegistry rewardInjectionRegistry; AdvancedCorePlugin plugin; - /** The reward folders. */ - private ArrayList rewardFolders; - @Getter private ScheduledExecutorService delayedTimer = Executors.newSingleThreadScheduledExecutor(); - @Getter - @Setter - private Set validPaths = new HashSet<>(); - public RewardHandler(AdvancedCorePlugin plugin) { this.plugin = plugin; rewardRegistry = new RewardRegistry(plugin); - rewardFolders = new ArrayList<>(); - setDefaultFolder(new File(plugin.getDataFolder(), "Rewards")); + rewardInjectionRegistry = new RewardInjectionRegistry(this, plugin); + rewardValidator = new RewardValidator(this, plugin); + subRewardResolver = new SubRewardResolver(this, plugin); + rewardLoader = new RewardLoader(this, plugin); } public void addDirectlyDefined(DirectlyDefinedReward directlyDefinedReward) { @@ -77,45 +64,23 @@ public void addDirectlyDefined(DirectlyDefinedReward directlyDefinedReward) { } public void addInjectedRequirements(RequirementInject inject) { - injectedRequirements.add(inject); - sortInjectedRequirements(); + rewardInjectionRegistry.addInjectedRequirement(inject); } public void addInjectedReward(RewardInject inject) { - injectedRewards.add(inject); - sortInjectedRewards(); + rewardInjectionRegistry.addInjectedReward(inject); } public void addPlaceholder(RewardPlaceholderHandle handle) { - placeholders.add(handle); + rewardInjectionRegistry.addPlaceholder(handle); } public void addRewardFolder(File file) { - addRewardFolder(file, true, true); + rewardLoader.addRewardFolder(file); } public void addRewardFolder(File file, boolean load, boolean create) { - if (create) { - file.mkdirs(); - } - if (!file.exists()) { - return; - } - if (file.isDirectory()) { - if (!rewardFolders.contains(file)) { - rewardFolders.add(file); - if (load) { - loadRewards(); - } - } - } else { - plugin.debug(file.getAbsolutePath()); - try { - throw new FileDirectoryException("File is not a directory"); - } catch (FileDirectoryException e) { - e.printStackTrace(); - } - } + rewardLoader.addRewardFolder(file, load, create); } public void addSubDirectlyDefined(SubDirectlyDefinedReward subDirectlyDefinedReward) { @@ -123,80 +88,27 @@ public void addSubDirectlyDefined(SubDirectlyDefinedReward subDirectlyDefinedRew } public void addValidPath(String path) { - validPaths.add(path); + rewardValidator.addValidPath(path); } public void checkDirectlyDefinedRewardFiles() { - ArrayList directlyDefinedPaths = new ArrayList(); - for (DirectlyDefinedReward direct : getDirectlyDefinedRewards()) { - directlyDefinedPaths.add(direct.getPath().replace(".", "_")); - } - - for (SubDirectlyDefinedReward direct : getSubDirectlyDefinedRewards()) { - directlyDefinedPaths.add(direct.getFullPath().replace(".", "_")); - } - - for (Reward rewardFile : getRewards()) { - if (ArrayUtils.containsIgnoreCase(directlyDefinedPaths, rewardFile.getName())) { - plugin.getLogger().warning("Found reward file conflict: " + rewardFile.getName() - + ", recommend deleting or renaming file to prevent issues"); - } - } + rewardValidator.checkDirectlyDefinedRewardFiles(); } public void checkDirectlyDefined() { - for (Reward rewardFile : getRewards()) { - File folder = rewardFile.getConfig().getRewardFolder(); - if (folder != null && folder.getName().equalsIgnoreCase("DirectlyDefined")) { - if (hasDirectRewardHandle(rewardFile.getName())) { - rewardFile.getFile().delete(); - } - } - } - loadRewards(); + rewardLoader.checkDirectlyDefined(); } public void checkSubRewards() { - plugin.extraDebug("Checking directlydefined rewards for sub rewards"); - rewardRegistry.resetSubDirectlyDefinedRewards(); - for (DirectlyDefinedReward direct : getDirectlyDefinedRewards()) { - checkSubRewards(direct); - } - - plugin.extraDebug("Checking reward file for sub rewards"); - for (Reward reward : getRewards()) { - checkSubRewards(new RewardFileDefinedReward(reward)); - } + subRewardResolver.checkSubRewards(); } public void checkSubRewards(DefinedReward direct) { - for (RewardInject inject : getInjectedRewards()) { - for (SubDirectlyDefinedReward sub : inject.subRewards(direct)) { - addSubDirectlyDefined(sub); - checkSubRewards(sub); - } - } - } - - /** - * Copy file. - * - * @param fileName the file name - */ - private void copyFile(String fileName) { - File file = new File(plugin.getDataFolder(), "Rewards" + File.separator + fileName); - if (!file.exists()) { - plugin.saveResource("Rewards" + File.separator + fileName, true); - } + subRewardResolver.checkSubRewards(direct); } - /** - * Gets the default folder. - * - * @return the default folder - */ public File getDefaultFolder() { - return defaultFolder; + return rewardLoader.getDefaultFolder(); } public DirectlyDefinedReward getDirectlyDefined(String path) { @@ -207,13 +119,16 @@ public CopyOnWriteArrayList getDirectlyDefinedRewards() { return rewardRegistry.getDirectlyDefinedRewards(); } - private String getFileExtension(File file) { - String name = file.getName(); - int lastIndexOf = name.lastIndexOf("."); - if (lastIndexOf == -1) { - return ""; // empty extension - } - return name.substring(lastIndexOf); + public CopyOnWriteArrayList getInjectedRequirements() { + return rewardInjectionRegistry.getInjectedRequirements(); + } + + public CopyOnWriteArrayList getInjectedRewards() { + return rewardInjectionRegistry.getInjectedRewards(); + } + + public CopyOnWriteArrayList getPlaceholders() { + return rewardInjectionRegistry.getPlaceholders(); } public Reward getReward(ConfigurationSection data, String path, RewardOptions rewardOptions) { @@ -243,95 +158,22 @@ public Reward getReward(ConfigurationSection data, String path, RewardOptions re return null; } - /** - * Gets the reward. - * - * @param reward the reward - * @return the reward - */ public Reward getReward(String reward) { return rewardRegistry.getReward(reward); } public Reward getRewardDirectlyDefined(String reward) { - if (reward == null) { - reward = ""; - } - reward = reward.replace(" ", "_"); - - for (Reward rewardFile : getRewards()) { - File folder = rewardFile.getConfig().getRewardFolder(); - if (folder != null && folder.getName().equalsIgnoreCase("DirectlyDefined")) { - if (rewardFile.getName().equalsIgnoreCase(reward)) { - return rewardFile; - } - } - } - - if (reward.equals("")) { - plugin.getLogger().warning("Tried to get any empty reward file name, renaming to EmptyName"); - reward = "EmptyName"; - } - - if (reward.equalsIgnoreCase("examplebasic") || reward.equalsIgnoreCase("exampleadvanced")) { - plugin.getLogger().warning("Using example rewards as a reward, becarefull"); - } - - File directFolder = new File(getDefaultFolder().getAbsolutePath() + File.separator + "DirectlyDefined"); - directFolder.mkdirs(); - return new Reward(directFolder, reward); + return rewardLoader.getRewardDirectlyDefined(reward); } - /** - * Gets the reward files. - * - * @param folder the folder - * @return the reward files - */ public ArrayList getRewardFiles(File folder) { - ArrayList fileNames = new ArrayList(); - if (folder != null && folder.exists()) { - File[] files = folder.listFiles(); - if (files == null) { - return fileNames; - } - for (File file : files) { - if (getFileExtension(file).equals(".yml")) { - fileNames.add(file.getName()); - } - } - } - return fileNames; + return rewardLoader.getRewardFiles(folder); } - /** - * Gets the reward names. - * - * @param file the file - * @return the reward names - */ public ArrayList getRewardNames(File file) { - ArrayList rewardFiles = getRewardFiles(file); - if (rewardFiles == null) { - return new ArrayList<>(); - } - for (int i = 0; i < rewardFiles.size(); i++) { - if (rewardFiles.get(i).contains(".yml")) { - rewardFiles.set(i, rewardFiles.get(i).replace(".yml", "")); - } else { - plugin.debug("Not a proper reward file: " + rewardFiles.get(i)); - } - } - - Collections.sort(rewardFiles, String.CASE_INSENSITIVE_ORDER); - return rewardFiles; + return rewardLoader.getRewardNames(file); } - /** - * Gets the rewards. - * - * @return the rewards - */ public List getRewards() { return rewardRegistry.getRewards(); } @@ -344,6 +186,14 @@ public CopyOnWriteArrayList getSubDirectlyDefinedRewar return rewardRegistry.getSubDirectlyDefinedRewards(); } + public Set getValidPaths() { + return rewardValidator.getValidPaths(); + } + + public void setValidPaths(Set validPaths) { + rewardValidator.setValidPaths(validPaths); + } + public void giveChoicesReward(Reward mainReward, AdvancedCoreUser user, String choice) { RewardBuilder reward = new RewardBuilder(mainReward.getConfig().getConfigData(), mainReward.getConfig().getChoicesRewardsPath(choice)); @@ -441,7 +291,6 @@ public void giveReward(AdvancedCoreUser user, ConfigurationSection data, String public void giveReward(AdvancedCoreUser user, Reward reward, RewardOptions rewardOptions) { if (reward != null) { - // make sure reward is async to avoid issues if (Bukkit.isPrimaryThread()) { plugin.getBukkitScheduler().runTaskAsynchronously(plugin, new Runnable() { @Override @@ -473,92 +322,19 @@ public boolean hasDirectRewardHandle(String reward) { } public boolean hasRewards(FileConfiguration data, String path) { - if (data.isList(path)) { - if (data.getList(path, new ArrayList<>()).size() != 0) { - return true; - } - } - if (data.isConfigurationSection(path)) { - if (data.getConfigurationSection(path).getKeys(false).size() != 0) { - return true; - } - } - if (data.isString(path)) { - if (!data.getString(path, "").equals("")) { - return true; - } - } - return false; + return rewardValidator.hasRewards(data, path); } public void loadInjectedRequirements() { - injectedRequirements.clear(); - com.bencodez.advancedcore.api.rewards.builtin.requirements.BuiltinRequirements.register(this, plugin); - for (RequirementInject requirement : injectedRequirements) { - requirement.setInternalReward(true); - } - sortInjectedRequirements(); + rewardInjectionRegistry.loadInjectedRequirements(); } public void loadInjectedRewards() { - injectedRewards.clear(); - com.bencodez.advancedcore.api.rewards.builtin.BuiltinRewards.register(this, plugin); - for (RewardInject reward : injectedRewards) { - reward.setInternalReward(true); - } - sortInjectedRewards(); + rewardInjectionRegistry.loadInjectedRewards(); } - /** - * Load rewards. - */ public void loadRewards() { - rewardRegistry.resetRewards(); - setupExample(); - addValidPath("DirectlyDefinedReward"); - addValidPath("Delayed"); - addValidPath("Timed"); - addValidPath("DisplayItem"); - addValidPath("ForceOffline"); - for (File file : rewardFolders) { - loadRewards(file); - } - sortInjectedRewards(); - sortInjectedRequirements(); - plugin.debug("Loaded rewards"); - } - - private void loadRewards(File file) { - for (String reward : getRewardNames(file)) { - if (!reward.equals("")) { - if (!rewardExist(reward)) { - try { - Reward reward1 = new Reward(file, reward); - reward1.validate(); - if (!reward1.getConfig().isDirectlyDefinedReward() - || file.getName().equalsIgnoreCase("DirectlyDefined")) { - getRewards().add(reward1); - if (reward1.getConfig().getConfigData().getConfigurationSection("").getKeys(true).size() > 0) { - plugin.extraDebug("Loaded Reward File: " + file.getAbsolutePath() + "/" + reward); - } else { - plugin.debug("Loaded empty reward file" + file.getAbsolutePath() + "/" + reward); - } - } else { - plugin.extraDebug("Ignoring directly defined reward file " + file.getAbsolutePath() + "/" + reward); - } - } catch (Exception e) { - plugin.getLogger().severe("Failed to load reward file " + reward + ".yml: " + e.getMessage()); - e.printStackTrace(); - } - } else { - plugin.debug("Detected that a reward file named " + reward - + " already exists, cannot load reward file " + file.getAbsolutePath() + "/" + reward); - } - } else { - plugin.getLogger().warning( - "Detected getting a reward file with an empty name! That means you either didn't type a name or didn't properly make an empty list"); - } - } + rewardLoader.loadRewards(); } public void openSubReward(Player player, String path, RewardEditData reward) { @@ -588,36 +364,16 @@ public void setData(String path, Object value) { }, reward), reward.getName() + "." + path); } - /** - * Reward exist. - * - * @param reward the reward - * @return true, if successful - */ public boolean rewardExist(String reward) { return rewardRegistry.rewardExist(reward); } - /** - * Sets the default folder. - * - * @param defaultFolder the new default folder - */ public void setDefaultFolder(File defaultFolder) { - this.defaultFolder = defaultFolder; + rewardLoader.setDefaultFolder(defaultFolder); } - /** - * Setup example. - */ public void setupExample() { - if (!plugin.getDataFolder().exists()) { - plugin.getDataFolder().mkdir(); - } - if (AdvancedCorePlugin.getInstance().getOptions().isLoadDefaultRewards()) { - copyFile("ExampleBasic.yml"); - copyFile("ExampleAdvanced.yml"); - } + rewardLoader.setupExample(); } public void shutdown() { @@ -631,30 +387,13 @@ public void shutdown() { } public void sortInjectedRequirements() { - Collections.sort(injectedRequirements, new Comparator() { - @Override - public int compare(RequirementInject o1, RequirementInject o2) { - return Integer.compare(o2.getPriority(), o1.getPriority()); - } - }); + rewardInjectionRegistry.sortInjectedRequirements(); } public void sortInjectedRewards() { - Collections.sort(injectedRewards, new Comparator() { - @Override - public int compare(RewardInject o1, RewardInject o2) { - return Integer.compare(o2.getPriority(), o1.getPriority()); - } - }); + rewardInjectionRegistry.sortInjectedRewards(); } - /* - * private void updateReward(Reward reward) { for (int i = getRewards().size() - - * 1; i >= 0; i--) { if - * (getRewards().get(i).getFile().getName().equals(reward.getFile().getName())) - * { getRewards().set(i, reward); return; } } getRewards().add(reward); } - */ - public void startup() { plugin.addUserStartup(new UserStartup() { @Override From 1566fa69a15b40f98beb9867415f14f5b087f977 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 13:16:48 -0600 Subject: [PATCH 6/6] Test extracted reward services --- .../tests/rewards/RewardServicesTest.java | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardServicesTest.java 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 new file mode 100644 index 0000000000..b3ad2b593c --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardServicesTest.java @@ -0,0 +1,131 @@ +package com.bencodez.advancedcore.tests.rewards; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +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.RewardHandler; +import com.bencodez.advancedcore.api.rewards.SubDirectlyDefinedReward; +import com.bencodez.advancedcore.api.rewards.injected.RewardInject; +import com.bencodez.advancedcore.api.rewards.injectedrequirement.RequirementInject; + +public class RewardServicesTest { + + @TempDir + File tempDir; + + private RewardHandler handler; + private AdvancedCorePlugin plugin; + private Logger logger; + + @BeforeEach + public void setUp() { + plugin = mock(AdvancedCorePlugin.class); + logger = mock(Logger.class); + when(plugin.getDataFolder()).thenReturn(tempDir); + when(plugin.getLogger()).thenReturn(logger); + AdvancedCorePlugin.setInstance(plugin); + handler = new RewardHandler(plugin); + } + + @AfterEach + public void tearDown() { + handler.getDelayedTimer().shutdownNow(); + AdvancedCorePlugin.setInstance(null); + } + + @Test + public void loaderOwnsDefaultFolderAndDiscoversSortedYamlRewards() throws IOException { + File rewards = new File(tempDir, "Rewards"); + assertEquals(rewards.getAbsolutePath(), handler.getDefaultFolder().getAbsolutePath()); + assertTrue(rewards.mkdirs()); + assertTrue(new File(rewards, "Zulu.yml").createNewFile()); + assertTrue(new File(rewards, "alpha.yml").createNewFile()); + assertTrue(new File(rewards, "ignored.txt").createNewFile()); + + assertEquals(List.of("alpha", "Zulu"), handler.getRewardNames(rewards)); + } + + @Test + public void injectionRegistrySortsRewardsAndRequirementsByPriority() { + RewardInject lowReward = mock(RewardInject.class); + RewardInject highReward = mock(RewardInject.class); + when(lowReward.getPriority()).thenReturn(10); + when(highReward.getPriority()).thenReturn(100); + + handler.addInjectedReward(lowReward); + handler.addInjectedReward(highReward); + assertSame(highReward, handler.getInjectedRewards().get(0)); + assertSame(lowReward, handler.getInjectedRewards().get(1)); + + RequirementInject lowRequirement = mock(RequirementInject.class); + RequirementInject highRequirement = mock(RequirementInject.class); + when(lowRequirement.getPriority()).thenReturn(5); + when(highRequirement.getPriority()).thenReturn(50); + + handler.addInjectedRequirements(lowRequirement); + handler.addInjectedRequirements(highRequirement); + assertSame(highRequirement, handler.getInjectedRequirements().get(0)); + assertSame(lowRequirement, handler.getInjectedRequirements().get(1)); + } + + @Test + public void validatorPreservesValidPathFacadeAndRewardContentChecks() { + Set paths = new HashSet<>(List.of("CustomPath")); + handler.setValidPaths(paths); + handler.addValidPath("SecondPath"); + assertSame(paths, handler.getValidPaths()); + assertTrue(handler.getValidPaths().contains("SecondPath")); + + YamlConfiguration config = new YamlConfiguration(); + config.set("StringReward", "Example"); + config.set("ListReward", List.of("One")); + config.createSection("SectionReward").set("Message", "Hello"); + + assertTrue(handler.hasRewards(config, "StringReward")); + assertTrue(handler.hasRewards(config, "ListReward")); + assertTrue(handler.hasRewards(config, "SectionReward")); + } + + @Test + public void subRewardResolverStopsRecursiveDiscovery() { + DirectlyDefinedReward root = mock(DirectlyDefinedReward.class); + when(root.getFullPath()).thenReturn("Root"); + + SubDirectlyDefinedReward loop = mock(SubDirectlyDefinedReward.class); + when(loop.getFullPath()).thenReturn("Loop"); + when(loop.isDirectlyDefined()).thenReturn(true); + + RewardInject inject = mock(RewardInject.class); + when(inject.subRewards(any(DefinedReward.class))) + .thenReturn(new ArrayList<>(List.of(loop))); + handler.getInjectedRewards().add(inject); + + handler.checkSubRewards(root); + + assertEquals(1, handler.getSubDirectlyDefinedRewards().size()); + assertSame(loop, handler.getSubDirectlyDefinedRewards().get(0)); + verify(logger).warning("Detected recursive sub reward path, skipping: Loop"); + } +}