diff --git a/pom.xml b/pom.xml index d4aeddb..c2ff00a 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ UTF-8 2.0.0 2.0.0 + 3.7.0 0.2.1 0.2.0 2.0.0 @@ -248,6 +249,19 @@ + + + com.dre.brewery + BreweryX + ${breweryx.version} + provided + + + * + * + + + org.objenesis @@ -273,6 +287,10 @@ placeholderapi https://repo.extendedclip.com/releases/ + + breweryx + https://repo.jsinco.dev/releases/ + diff --git a/src/main/java/net/tfminecraft/activitytf/ActivityPlugin.java b/src/main/java/net/tfminecraft/activitytf/ActivityPlugin.java index 2f2f486..6c73b41 100644 --- a/src/main/java/net/tfminecraft/activitytf/ActivityPlugin.java +++ b/src/main/java/net/tfminecraft/activitytf/ActivityPlugin.java @@ -8,6 +8,7 @@ import net.tfminecraft.activitytf.listeners.AdvancedCraftListener; import net.tfminecraft.activitytf.listeners.ArchaeologyListener; import net.tfminecraft.activitytf.listeners.BattleListener; +import net.tfminecraft.activitytf.listeners.BreweryListener; import net.tfminecraft.activitytf.listeners.CasinoWinListener; import net.tfminecraft.activitytf.listeners.CharacterChatListener; import net.tfminecraft.activitytf.listeners.CraftListener; @@ -162,6 +163,12 @@ private void registerHooks() { getLogger().info("Hooked into Cooking."); } + if (Bukkit.getPluginManager().isPluginEnabled("BreweryX")) { + getServer().getPluginManager().registerEvents( + new BreweryListener(ActivityManager.getInstance()), this); + getLogger().info("Hooked into BreweryX."); + } + if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { placeholderHook = new PlaceholderHook(ActivityManager.getInstance()); placeholderHook.register(); diff --git a/src/main/java/net/tfminecraft/activitytf/listeners/BreweryListener.java b/src/main/java/net/tfminecraft/activitytf/listeners/BreweryListener.java new file mode 100644 index 0000000..c5df615 --- /dev/null +++ b/src/main/java/net/tfminecraft/activitytf/listeners/BreweryListener.java @@ -0,0 +1,25 @@ +package net.tfminecraft.activitytf.listeners; + +import com.dre.brewery.api.events.IngedientAddEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import net.tfminecraft.activitytf.managers.ActivityManager; + +public class BreweryListener implements Listener { + + private final ActivityManager manager; + + public BreweryListener(ActivityManager manager) { + this.manager = manager; + } + + // BreweryX only fires this for an item a cauldron recipe accepts, once per item added. + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onIngredientAdd(IngedientAddEvent event) { + if (event.getPlayer() == null) { + return; + } + manager.recordAction(event.getPlayer().getUniqueId(), "brew_ingredient", 1); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c21d417..0e74481 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -311,6 +311,20 @@ activities: every: 5 points: 1 daily-cap: 2 + # BreweryX activities (brew_*). The shipped config.yml is only written on a fresh + # install, so an existing server must copy these entries into its own config.yml + # and run /activity reload; until then they are unknown ids and earn nothing. + # BreweryX: ingredients put into a brewing cauldron + brew_ingredient: + display: "#e6ca40&lGather Brewing Ingredients" + description: + - "&7Add &a%every% &7ingredients to a brewing cauldron." + - "&7Earn &a%points% &7activity points per completion." + - "&7" + material: CAULDRON + every: 10 + points: 1 + daily-cap: 1 # RPCharacters: profession upgrades purchased profession_upgrade: display: "#e6ca40&lProfession Upgrade" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1681c90..ada98cc 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,7 +5,7 @@ author: Justin api-version: '1.21.10' softdepend: [VotingPlugin, MusicalInstruments, geiger_counter, MMOCore, MMOItems, MythicLib, PlaceholderAPI, VFBuilders, RPCharacters, InteractibleFurniture, SimpleFactions, AdvancedCrafting, TLibs, Archaeo, - MarketBlock, Games, Cooking, ItemsAdder] + MarketBlock, Games, Cooking, ItemsAdder, BreweryX] commands: activity: description: Open the weekly activity bar, or run admin subcommands diff --git a/src/test/java/net/tfminecraft/activitytf/listeners/BreweryListenerHandlersTest.java b/src/test/java/net/tfminecraft/activitytf/listeners/BreweryListenerHandlersTest.java new file mode 100644 index 0000000..920ff20 --- /dev/null +++ b/src/test/java/net/tfminecraft/activitytf/listeners/BreweryListenerHandlersTest.java @@ -0,0 +1,29 @@ +package net.tfminecraft.activitytf.listeners; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BreweryListenerHandlersTest { + + // BreweryListenerTest calls the handlers directly, which skips Bukkit's ignoreCancelled filter. + @Test + void everyHandlerSkipsCancelledEventsAtMonitor() { + int handlers = 0; + for (Method method : BreweryListener.class.getDeclaredMethods()) { + EventHandler handler = method.getAnnotation(EventHandler.class); + if (handler == null) { + continue; + } + handlers++; + assertTrue(handler.ignoreCancelled(), method.getName() + " must ignore cancelled events"); + assertEquals(EventPriority.MONITOR, handler.priority(), method.getName()); + } + assertTrue(handlers > 0); + } +} diff --git a/src/test/java/net/tfminecraft/activitytf/listeners/BreweryListenerTest.java b/src/test/java/net/tfminecraft/activitytf/listeners/BreweryListenerTest.java new file mode 100644 index 0000000..0bc62e6 --- /dev/null +++ b/src/test/java/net/tfminecraft/activitytf/listeners/BreweryListenerTest.java @@ -0,0 +1,90 @@ +package net.tfminecraft.activitytf.listeners; + +import com.dre.brewery.api.events.IngedientAddEvent; +import net.tfminecraft.activitytf.managers.ActivityManager; +import net.tfminecraft.activitytf.managers.TestManagers; +import net.tfminecraft.activitytf.models.ActivityDef; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerEvent; +import org.junit.jupiter.api.Test; +import org.objenesis.ObjenesisStd; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BreweryListenerTest { + + private static Player stubPlayer(UUID uuid) { + InvocationHandler handler = (proxy, method, args) -> switch (method.getName()) { + case "getUniqueId" -> uuid; + case "toString" -> "stub-player"; + case "hashCode" -> uuid.hashCode(); + case "equals" -> proxy == args[0]; + default -> throw new UnsupportedOperationException( + "unexpected call to Player#" + method.getName() + " - this test double only answers getUniqueId()"); + }; + return (Player) Proxy.newProxyInstance( + BreweryListenerTest.class.getClassLoader(), new Class[]{Player.class}, handler); + } + + static void set(Class owner, Object target, String name, Object value) { + try { + Field field = owner.getDeclaredField(name); + field.setAccessible(true); + field.set(target, value); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private static IngedientAddEvent ingredient(Player player) { + IngedientAddEvent event = new ObjenesisStd().newInstance(IngedientAddEvent.class); + set(PlayerEvent.class, event, "player", player); + return event; + } + + /** A manager with every given activity revealed for the returned player. */ + static UUID revealed(ActivityManager manager) { + TestManagers.bukkit(); + TestManagers.storeLoaded(manager); + UUID uuid = UUID.randomUUID(); + for (int slot = 0; slot < manager.tasks(uuid).tasks().size(); slot++) { + manager.reveal(uuid, slot); + } + return uuid; + } + + static ActivityManager managerWith(String... ids) { + ActivityDef[] defs = new ActivityDef[ids.length]; + for (int i = 0; i < ids.length; i++) { + defs[i] = new ActivityDef(ids[i], ids[i], Material.POTION, null, 1, 1, 0); + } + ActivityManager manager = TestManagers.manager(defs); + TestManagers.guarantee(manager, ids); + return manager; + } + + @Test + void anIngredientWithoutAPlayerIsIgnored() { + BreweryListener listener = new BreweryListener(null); + assertDoesNotThrow(() -> listener.onIngredientAdd(ingredient(null))); + } + + @Test + void eachIngredientAddedCreditsOne() { + ActivityManager manager = managerWith("brew_ingredient"); + UUID uuid = revealed(manager); + BreweryListener listener = new BreweryListener(manager); + + listener.onIngredientAdd(ingredient(stubPlayer(uuid))); + listener.onIngredientAdd(ingredient(stubPlayer(uuid))); + + assertEquals(2, manager.tasks(uuid).count("brew_ingredient")); + } +}