From 7c167699a12e1d578393ea9c558cac07c22c75f0 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 12:29:21 -0600 Subject: [PATCH 1/3] Add RewardRegistry --- .../api/rewards/RewardRegistry.java | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardRegistry.java 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 new file mode 100644 index 0000000000..010d5f74c0 --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/rewards/RewardRegistry.java @@ -0,0 +1,166 @@ +package com.bencodez.advancedcore.api.rewards; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import com.bencodez.advancedcore.AdvancedCorePlugin; + +/** + * Owns registered reward state and reward lookup behavior. + *

+ * {@link RewardHandler} remains the compatibility facade for callers while + * registry-specific state and lookup responsibilities live here. + */ +public class RewardRegistry { + + private final AdvancedCorePlugin plugin; + private final CopyOnWriteArrayList directlyDefinedRewards = new CopyOnWriteArrayList<>(); + private CopyOnWriteArrayList subDirectlyDefinedRewards = new CopyOnWriteArrayList<>(); + private List rewards; + + public RewardRegistry(AdvancedCorePlugin plugin) { + this.plugin = plugin; + resetRewards(); + } + + public void addDirectlyDefined(DirectlyDefinedReward directlyDefinedReward) { + if (getDirectlyDefined(directlyDefinedReward.getPath()) != null) { + plugin.extraDebug( + "DirectlyDefinedReward with path already exists, skipping: " + directlyDefinedReward.getPath()); + return; + } + plugin.extraDebug("Adding directlydefined reward handle: " + directlyDefinedReward.getPath() + + ", isdirectlydefined: " + directlyDefinedReward.isDirectlyDefined()); + directlyDefinedRewards.add(directlyDefinedReward); + } + + public void addSubDirectlyDefined(SubDirectlyDefinedReward subDirectlyDefinedReward) { + plugin.extraDebug("Adding subdirectlydefined reward handle: " + subDirectlyDefinedReward.getFullPath() + + ", isdirectlydefined: " + subDirectlyDefinedReward.isDirectlyDefined()); + subDirectlyDefinedRewards.add(subDirectlyDefinedReward); + } + + public DirectlyDefinedReward getDirectlyDefined(String path) { + for (DirectlyDefinedReward direct : directlyDefinedRewards) { + if (direct.getPath().equalsIgnoreCase(path)) { + return direct; + } + } + return null; + } + + public CopyOnWriteArrayList getDirectlyDefinedRewards() { + return directlyDefinedRewards; + } + + public Reward getReward(String reward) { + if (reward == null) { + reward = ""; + } + reward = reward.replace(" ", "_"); + + 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, be carefull"); + } + + for (DirectlyDefinedReward direct : directlyDefinedRewards) { + if (direct.getPath().replace(".", "_").equals(reward)) { + plugin.debug("Using directlydefined reward for: " + reward); + return direct.getReward(); + } + } + + for (SubDirectlyDefinedReward direct : subDirectlyDefinedRewards) { + if (matchesSubDirectlyDefined(direct, reward)) { + plugin.debug("Using subdirectlydefined reward for: " + reward); + return direct.getReward(); + } + } + + for (Reward rewardFile : getRewards()) { + if (rewardFile.getName().equalsIgnoreCase(reward)) { + return rewardFile; + } + } + + return new Reward(reward); + } + + public List getRewards() { + if (rewards == null) { + resetRewards(); + } + return rewards; + } + + public SubDirectlyDefinedReward getSubDirectlyDefined(String path) { + for (SubDirectlyDefinedReward direct : subDirectlyDefinedRewards) { + if (matchesSubDirectlyDefined(direct, path)) { + return direct; + } + } + return null; + } + + public CopyOnWriteArrayList getSubDirectlyDefinedRewards() { + return subDirectlyDefinedRewards; + } + + public boolean hasDirectRewardHandle(String reward) { + for (DirectlyDefinedReward direct : directlyDefinedRewards) { + if (direct.getPath().replace(".", "_").equals(reward)) { + return true; + } + } + for (SubDirectlyDefinedReward direct : subDirectlyDefinedRewards) { + if (matchesSubDirectlyDefined(direct, reward)) { + return true; + } + } + return false; + } + + public boolean rewardExist(String reward) { + if (reward.equals("")) { + return false; + } + for (Reward rewardName : getRewards()) { + if (rewardName.getName().equalsIgnoreCase(reward)) { + return true; + } + } + return false; + } + + public void resetRewards() { + rewards = Collections.synchronizedList(new ArrayList()); + } + + public void resetSubDirectlyDefinedRewards() { + subDirectlyDefinedRewards = new CopyOnWriteArrayList<>(); + } + + public void updateReward(Reward reward) { + reward.validate(); + for (int i = getRewards().size() - 1; i >= 0; i--) { + if (getRewards().get(i).getFile().getPath().equals(reward.getFile().getPath())) { + getRewards().set(i, reward); + return; + } + } + getRewards().add(reward); + } + + private boolean matchesSubDirectlyDefined(SubDirectlyDefinedReward direct, String reward) { + return direct.getFullPath().equalsIgnoreCase(reward) + || direct.getFullPath().replace(".", "_").equalsIgnoreCase(reward) + || direct.getFullPath().equalsIgnoreCase(reward.replaceAll("_", ".")); + } +} From b3fcbcc92a4a3b89e4f2359c84984d503cc9ef7d Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 12:30:23 -0600 Subject: [PATCH 2/3] Delegate reward registry responsibilities --- .../api/rewards/RewardHandler.java | 128 +++--------------- 1 file changed, 22 insertions(+), 106 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 1a3a01fdeb..8f80b95856 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 @@ -42,10 +42,7 @@ public class RewardHandler { private File defaultFolder; @Getter - private CopyOnWriteArrayList directlyDefinedRewards = new CopyOnWriteArrayList(); - - @Getter - private CopyOnWriteArrayList subDirectlyDefinedRewards = new CopyOnWriteArrayList(); + private final RewardRegistry rewardRegistry; @Getter private CopyOnWriteArrayList injectedRequirements = new CopyOnWriteArrayList(); @@ -61,9 +58,6 @@ public class RewardHandler { /** The reward folders. */ private ArrayList rewardFolders; - /** The rewards. */ - private List rewards; - @Getter private ScheduledExecutorService delayedTimer = Executors.newSingleThreadScheduledExecutor(); @@ -73,19 +67,13 @@ public class RewardHandler { public RewardHandler(AdvancedCorePlugin plugin) { this.plugin = plugin; + rewardRegistry = new RewardRegistry(plugin); rewardFolders = new ArrayList<>(); setDefaultFolder(new File(plugin.getDataFolder(), "Rewards")); } public void addDirectlyDefined(DirectlyDefinedReward directlyDefinedReward) { - if (getDirectlyDefined(directlyDefinedReward.getPath()) != null) { - plugin.extraDebug( - "DirectlyDefinedReward with path already exists, skipping: " + directlyDefinedReward.getPath()); - return; - } - plugin.extraDebug("Adding directlydefined reward handle: " + directlyDefinedReward.getPath() - + ", isdirectlydefined: " + directlyDefinedReward.isDirectlyDefined()); - directlyDefinedRewards.add(directlyDefinedReward); + rewardRegistry.addDirectlyDefined(directlyDefinedReward); } public void addInjectedRequirements(RequirementInject inject) { @@ -131,9 +119,7 @@ public void addRewardFolder(File file, boolean load, boolean create) { } public void addSubDirectlyDefined(SubDirectlyDefinedReward subDirectlyDefinedReward) { - plugin.extraDebug("Adding subdirectlydefined reward handle: " + subDirectlyDefinedReward.getFullPath() - + ", isdirectlydefined: " + subDirectlyDefinedReward.isDirectlyDefined()); - subDirectlyDefinedRewards.add(subDirectlyDefinedReward); + rewardRegistry.addSubDirectlyDefined(subDirectlyDefinedReward); } public void addValidPath(String path) { @@ -172,7 +158,7 @@ public void checkDirectlyDefined() { public void checkSubRewards() { plugin.extraDebug("Checking directlydefined rewards for sub rewards"); - subDirectlyDefinedRewards = new CopyOnWriteArrayList(); + rewardRegistry.resetSubDirectlyDefinedRewards(); for (DirectlyDefinedReward direct : getDirectlyDefinedRewards()) { checkSubRewards(direct); } @@ -214,12 +200,11 @@ public File getDefaultFolder() { } public DirectlyDefinedReward getDirectlyDefined(String path) { - for (DirectlyDefinedReward direct : getDirectlyDefinedRewards()) { - if (direct.getPath().equalsIgnoreCase(path)) { - return direct; - } - } - return null; + return rewardRegistry.getDirectlyDefined(path); + } + + public CopyOnWriteArrayList getDirectlyDefinedRewards() { + return rewardRegistry.getDirectlyDefinedRewards(); } private String getFileExtension(File file) { @@ -265,41 +250,7 @@ public Reward getReward(ConfigurationSection data, String path, RewardOptions re * @return the reward */ public Reward getReward(String reward) { - if (reward == null) { - reward = ""; - } - reward = reward.replace(" ", "_"); - - 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, be carefull"); - } - - for (DirectlyDefinedReward direct : getDirectlyDefinedRewards()) { - if (direct.getPath().replace(".", "_").equals(reward)) { - plugin.debug("Using directlydefined reward for: " + reward); - return direct.getReward(); - } - } - - for (SubDirectlyDefinedReward direct : getSubDirectlyDefinedRewards()) { - if (matchesSubDirectlyDefined(direct, reward)) { - plugin.debug("Using subdirectlydefined reward for: " + reward); - return direct.getReward(); - } - } - - for (Reward rewardFile : getRewards()) { - if (rewardFile.getName().equalsIgnoreCase(reward)) { - return rewardFile; - } - } - - return new Reward(reward); + return rewardRegistry.getReward(reward); } public Reward getRewardDirectlyDefined(String reward) { @@ -382,19 +333,15 @@ public ArrayList getRewardNames(File file) { * @return the rewards */ public List getRewards() { - if (rewards == null) { - rewards = Collections.synchronizedList(new ArrayList()); - } - return rewards; + return rewardRegistry.getRewards(); } public SubDirectlyDefinedReward getSubDirectlyDefined(String path) { - for (SubDirectlyDefinedReward direct : getSubDirectlyDefinedRewards()) { - if (matchesSubDirectlyDefined(direct, path)) { - return direct; - } - } - return null; + return rewardRegistry.getSubDirectlyDefined(path); + } + + public CopyOnWriteArrayList getSubDirectlyDefinedRewards() { + return rewardRegistry.getSubDirectlyDefinedRewards(); } public void giveChoicesReward(Reward mainReward, AdvancedCoreUser user, String choice) { @@ -522,23 +469,7 @@ public void giveReward(AdvancedCoreUser user, String reward, RewardOptions rewar } public boolean hasDirectRewardHandle(String reward) { - for (DirectlyDefinedReward direct : getDirectlyDefinedRewards()) { - if (direct.getPath().replace(".", "_").equals(reward)) { - return true; - } - } - for (SubDirectlyDefinedReward direct : getSubDirectlyDefinedRewards()) { - if (matchesSubDirectlyDefined(direct, reward)) { - return true; - } - } - return false; - } - - private boolean matchesSubDirectlyDefined(SubDirectlyDefinedReward direct, String reward) { - return direct.getFullPath().equalsIgnoreCase(reward) - || direct.getFullPath().replace(".", "_").equalsIgnoreCase(reward) - || direct.getFullPath().equalsIgnoreCase(reward.replaceAll("_", ".")); + return rewardRegistry.hasDirectRewardHandle(reward); } public boolean hasRewards(FileConfiguration data, String path) { @@ -582,7 +513,7 @@ public void loadInjectedRewards() { * Load rewards. */ public void loadRewards() { - rewards = Collections.synchronizedList(new ArrayList()); + rewardRegistry.resetRewards(); setupExample(); addValidPath("DirectlyDefinedReward"); addValidPath("Delayed"); @@ -606,7 +537,7 @@ private void loadRewards(File file) { reward1.validate(); if (!reward1.getConfig().isDirectlyDefinedReward() || file.getName().equalsIgnoreCase("DirectlyDefined")) { - rewards.add(reward1); + getRewards().add(reward1); if (reward1.getConfig().getConfigData().getConfigurationSection("").getKeys(true).size() > 0) { plugin.extraDebug("Loaded Reward File: " + file.getAbsolutePath() + "/" + reward); } else { @@ -664,15 +595,7 @@ public void setData(String path, Object value) { * @return true, if successful */ public boolean rewardExist(String reward) { - if (reward.equals("")) { - return false; - } - for (Reward rewardName : getRewards()) { - if (rewardName.getName().equalsIgnoreCase(reward)) { - return true; - } - } - return false; + return rewardRegistry.rewardExist(reward); } /** @@ -780,13 +703,6 @@ public void updateReward(Configuration data, String path, RewardOptions rewardOp } public void updateReward(Reward reward) { - reward.validate(); - for (int i = getRewards().size() - 1; i >= 0; i--) { - if (getRewards().get(i).getFile().getPath().equals(reward.getFile().getPath())) { - getRewards().set(i, reward); - return; - } - } - getRewards().add(reward); + rewardRegistry.updateReward(reward); } } From 0df8cd848ccb2cbb646cbd461ce42240a57691b9 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 23 Aug 2026 12:31:00 -0600 Subject: [PATCH 3/3] Test RewardRegistry behavior --- .../tests/rewards/RewardRegistryTest.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardRegistryTest.java diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardRegistryTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardRegistryTest.java new file mode 100644 index 0000000000..8449b84293 --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/rewards/RewardRegistryTest.java @@ -0,0 +1,119 @@ +package com.bencodez.advancedcore.tests.rewards; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +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 java.util.logging.Logger; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.rewards.DirectlyDefinedReward; +import com.bencodez.advancedcore.api.rewards.Reward; +import com.bencodez.advancedcore.api.rewards.RewardRegistry; +import com.bencodez.advancedcore.api.rewards.SubDirectlyDefinedReward; + +public class RewardRegistryTest { + + private RewardRegistry registry; + + @BeforeEach + public void setUp() { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + when(plugin.getLogger()).thenReturn(mock(Logger.class)); + registry = new RewardRegistry(plugin); + } + + @Test + public void directlyDefinedLookupIsCaseInsensitiveAndRejectsDuplicatePaths() { + DirectlyDefinedReward first = mock(DirectlyDefinedReward.class); + DirectlyDefinedReward duplicate = mock(DirectlyDefinedReward.class); + when(first.getPath()).thenReturn("Test.Path"); + when(duplicate.getPath()).thenReturn("test.path"); + + registry.addDirectlyDefined(first); + registry.addDirectlyDefined(duplicate); + + assertSame(first, registry.getDirectlyDefined("TEST.PATH")); + assertTrue(registry.getDirectlyDefinedRewards().size() == 1); + } + + @Test + public void subDirectlyDefinedLookupSupportsFileStyleUnderscores() { + SubDirectlyDefinedReward sub = mock(SubDirectlyDefinedReward.class); + when(sub.getFullPath()).thenReturn("Test_Rewards_Name.Rewards"); + + registry.addSubDirectlyDefined(sub); + + assertSame(sub, registry.getSubDirectlyDefined("Test_Rewards_Name_Rewards")); + assertTrue(registry.hasDirectRewardHandle("Test_Rewards_Name_Rewards")); + } + + @Test + public void loadedRewardLookupNormalizesSpacesAndIgnoresCase() { + Reward reward = mock(Reward.class); + when(reward.getName()).thenReturn("My_Reward"); + registry.getRewards().add(reward); + + assertSame(reward, registry.getReward("my reward")); + } + + @Test + public void directlyDefinedRewardLookupPreservesExistingPathBehavior() { + DirectlyDefinedReward direct = mock(DirectlyDefinedReward.class); + Reward reward = mock(Reward.class); + when(direct.getPath()).thenReturn("Direct.Reward"); + when(direct.getReward()).thenReturn(reward); + registry.addDirectlyDefined(direct); + + assertSame(reward, registry.getReward("Direct_Reward")); + } + + @Test + public void rewardExistIsCaseInsensitiveAndRejectsEmptyNames() { + Reward reward = mock(Reward.class); + when(reward.getName()).thenReturn("DailyReward"); + registry.getRewards().add(reward); + + assertTrue(registry.rewardExist("dailyreward")); + assertFalse(registry.rewardExist("")); + } + + @Test + public void resetRewardsReplacesAndClearsTheLoadedRewardList() { + List original = registry.getRewards(); + original.add(mock(Reward.class)); + + registry.resetRewards(); + + assertNotSame(original, registry.getRewards()); + assertTrue(registry.getRewards().isEmpty()); + } + + @Test + public void updateRewardReplacesMatchingFileAndValidatesReplacement() { + Reward oldReward = mock(Reward.class); + Reward replacement = mock(Reward.class); + File oldFile = mock(File.class); + File replacementFile = mock(File.class); + when(oldReward.getFile()).thenReturn(oldFile); + when(replacement.getFile()).thenReturn(replacementFile); + when(oldFile.getPath()).thenReturn("Rewards/Test.yml"); + when(replacementFile.getPath()).thenReturn("Rewards/Test.yml"); + registry.getRewards().add(oldReward); + + registry.updateReward(replacement); + + verify(replacement).validate(); + assertTrue(registry.getRewards().size() == 1); + assertSame(replacement, registry.getRewards().get(0)); + } +}