From 7aadf220f5fe64c5e1eacdeeaeb5bbb86942f807 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 19 May 2026 15:43:35 -0300 Subject: [PATCH 01/12] Split github and forgejo workflows. --- .forgejo/workflows/build.yml | 43 ++++++++++++++++++++++++++++ .forgejo/workflows/release-build.yml | 40 ++++++++++++++++++++++++++ .github/workflows/build.yml | 2 +- .github/workflows/release-build.yml | 2 +- 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 .forgejo/workflows/build.yml create mode 100644 .forgejo/workflows/release-build.yml diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml new file mode 100644 index 0000000..c3c036b --- /dev/null +++ b/.forgejo/workflows/build.yml @@ -0,0 +1,43 @@ +name: Build + +on: + push: + paths: + - 'src/**' + - '.github/**' + - 'build.gradle.kts' + - 'gradle.properties' + - 'settings.gradle' + +jobs: + build: + runs-on: ubuntu-latest + env: + NEMESIS_VERSION_NAME: "autobuild-${{ github.sha }}" + NEMESIS_BUILD_CHANNEL: "autobuild" + steps: + - name: Checkout + uses: actions/checkout@v6.0.1 + + - name: Setup Java enviroment + uses: actions/setup-java@v5.1.0 + with: + distribution: temurin + java-version: 21 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + + - name: Download CB1060 + run: | + mkdir -p libs + curl -L -o libs/craftbukkit-1060.jar https://archive.org/download/craftbukkit1060/craftbukkit1-7-3%281060%29.jar + + - name: Build with Gradle + run: ./gradlew buildAll + + + - name: Upload artifacts + uses: actions/upload-artifact@v6.0.0 + with: + path: build/libs/*.jar diff --git a/.forgejo/workflows/release-build.yml b/.forgejo/workflows/release-build.yml new file mode 100644 index 0000000..dcc83d3 --- /dev/null +++ b/.forgejo/workflows/release-build.yml @@ -0,0 +1,40 @@ +name: Release Build + +on: + release: + types: [published] + +jobs: + build-and-release: + runs-on: ubuntu-latest + env: + NEMESIS_VERSION_NAME: "${{ github.ref_name }}" + NEMESIS_BUILD_CHANNEL: "production" + steps: + - name: Checkout + uses: actions/checkout@v6.0.1 + + - name: Set up Java + uses: actions/setup-java@v5.1.0 + with: + distribution: temurin + java-version: 21 + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v5 + + - name: Download CB1060 + run: | + mkdir -p libs + curl -L -o libs/craftbukkit-1060.jar \ + https://archive.org/download/craftbukkit1060/craftbukkit1-7-3%281060%29.jar + + - name: Build with Gradle + run: ./gradlew buildAll + + - name: Upload JARs to release + uses: softprops/action-gh-release@v2.5.0 + with: + files: build/libs/*.jar + tag_name: ${{ github.ref_name }} + name: ${{ github.ref_name }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5d4ddb..0e8199d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ on: jobs: build: - runs-on: "arch-linux" + runs-on: ubuntu-latest env: NEMESIS_VERSION_NAME: "autobuild-${{ github.sha }}" NEMESIS_BUILD_CHANNEL: "autobuild" diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index c038796..b9f7c74 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -6,7 +6,7 @@ on: jobs: build-and-release: - runs-on: "arch-linux" + runs-on: ubuntu-latest env: NEMESIS_VERSION_NAME: "${{ github.ref_name }}" NEMESIS_BUILD_CHANNEL: "production" From 4b5ee425477eecd390b276f0c6b936956ce6ebf6 Mon Sep 17 00:00:00 2001 From: tenkuma Date: Tue, 19 May 2026 15:49:04 -0300 Subject: [PATCH 02/12] Atualizar .github/workflows/release-build.yml --- .github/workflows/release-build.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index b9f7c74..9570712 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -32,9 +32,10 @@ jobs: - name: Build with Gradle run: ./gradlew buildAll - - name: Upload JARs to release - uses: softprops/action-gh-release@v2.5.0 - with: - files: build/libs/*.jar - tag_name: ${{ github.ref_name }} - name: ${{ github.ref_name }} \ No newline at end of file + - name: Upload artifacts to release + run: | + curl -X POST \ + -H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + https://inspiran.beetal-castor.ts.net/git/api/v1/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}/assets \ + --data-binary @build/libs/your-artifact.jar \ No newline at end of file From af6ac27960ea635759220e32bb55eafc0eb079d7 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Wed, 20 May 2026 15:38:45 -0300 Subject: [PATCH 03/12] Refactor codebase to use LivingEntity, add SPAWN action and handler, add DataShifter.parseValueToStringList overload that iterates over a list of strings. --- .../adrianvic/nemesiseye/DataShifter.java | 11 ++++++- .../github/adrianvic/nemesiseye/Events.java | 5 +++ .../adrianvic/nemesiseye/Validator.java | 5 +-- .../adrianvic/nemesiseye/policy/Action.java | 3 +- .../nemesiseye/policy/NodeHandler.java | 3 +- .../nemesiseye/policy/NodeHandlers.java | 15 +++++---- .../adrianvic/nemesiseye/policy/Policy.java | 5 +-- .../nemesiseye/policy/PolicyNode.java | 3 +- .../handlers/{bePlaced.java => BePlaced.java} | 5 +-- .../handlers/{equip.java => Equip.java} | 5 +-- .../handlers/{glyde.java => Glyde.java} | 5 +-- .../nemesiseye/policy/handlers/Spawn.java | 21 ++++++++++++ .../policy/handlers/UseEnchantment.java | 33 +++++++++++++++++++ .../handlers/{useItem.java => UseItem.java} | 15 +++++---- .../policy/handlers/useEnchantment.java | 28 ---------------- .../nemesiseye/policy/policies/Core.java | 3 +- .../policy/policies/GlobalPolicy.java | 3 +- .../policy/policies/LocationPolicy.java | 3 +- .../policy/policies/PermissionPolicy.java | 3 +- .../policy/policies/PlayerNamePolicy.java | 3 +- .../nemesiseye/impl/EventListener.java | 7 +++- 21 files changed, 123 insertions(+), 61 deletions(-) rename src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/{bePlaced.java => BePlaced.java} (81%) rename src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/{equip.java => Equip.java} (93%) rename src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/{glyde.java => Glyde.java} (65%) create mode 100644 src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java create mode 100644 src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java rename src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/{useItem.java => UseItem.java} (54%) delete mode 100644 src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java diff --git a/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java b/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java index ceb0e36..45ec7b5 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java @@ -7,13 +7,22 @@ import java.util.Map; import java.util.regex.Pattern; public class DataShifter { - public static boolean safeMatches(String expression, String against) { String cleanPattern = expression.trim(); Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE); return pattern.matcher(against).matches(); } + public static boolean safeMatches(List expressions, String against) { + for (String s : expressions) { + if (DataShifter.safeMatches(s, against)) { + return true; + } + } + + return false; + } + public static List parseValueToStringList(List values) { List result = new ArrayList<>(); for (Object o : values) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Events.java b/src/main/java/io/github/adrianvic/nemesiseye/Events.java index 442bbdc..58b5140 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Events.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Events.java @@ -6,6 +6,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; @@ -89,6 +90,10 @@ public class Events { } } + public static void onCreatureSpawnEvent(CreatureSpawnEvent event) { + event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event)); + } + private static boolean isArmorEquipAttempt(InventoryClickEvent event) { if (event.getSlotType() == InventoryType.SlotType.ARMOR) { return true; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java index 9222de5..9989ea6 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java @@ -3,6 +3,7 @@ package io.github.adrianvic.nemesiseye; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Policy; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import java.util.ArrayList; @@ -19,7 +20,7 @@ public class Validator { return true; } - public static boolean can(HumanEntity entity, Action action, Event event) { + public static boolean can(LivingEntity entity, Action action, Event event) { boolean restricted = false; boolean allowed = false; @@ -41,7 +42,7 @@ public class Validator { } - public static List getPoliciesForEntity(HumanEntity entity) { + public static List getPoliciesForEntity(LivingEntity entity) { List ps = Config.getInstance().getPolicies(); List result = new ArrayList<>(); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java index b80f6a9..debbb36 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java @@ -8,5 +8,6 @@ public enum Action { EQUIP, PLACE, USE_ENCHANTMENT, - GLYDE + GLYDE, + SPAWN } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java index 31fa568..2202797 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java @@ -1,8 +1,9 @@ package io.github.adrianvic.nemesiseye.policy; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; public interface NodeHandler { - boolean check(HumanEntity entity, PolicyNode node, Action action, Event event); + boolean check(LivingEntity entity, PolicyNode node, Action action, Event event); } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java index 465747e..70f4a49 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java @@ -9,13 +9,14 @@ public class NodeHandlers { private static final Map handlers = new HashMap<>(); static { - handlers.put(Action.HIT, new useItem()); - handlers.put(Action.PLACE, new bePlaced()); - handlers.put(Action.INTERACT, new useItem()); - handlers.put(Action.USE_ENCHANTMENT, new useEnchantment()); - handlers.put(Action.GLYDE, new glyde()); - handlers.put(Action.EQUIP, new equip()); - handlers.put(Action.BREAK, new useItem()); // TODO: implement place handler + handlers.put(Action.HIT, new UseItem()); + handlers.put(Action.PLACE, new BePlaced()); + handlers.put(Action.INTERACT, new UseItem()); + handlers.put(Action.USE_ENCHANTMENT, new UseEnchantment()); + handlers.put(Action.GLYDE, new Glyde()); + handlers.put(Action.EQUIP, new Equip()); + handlers.put(Action.SPAWN, new Spawn()); + handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler } public static NodeHandler get(Action type) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java index 5992433..5cb69cc 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java @@ -1,6 +1,7 @@ package io.github.adrianvic.nemesiseye.policy; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import java.util.List; @@ -9,7 +10,7 @@ public interface Policy { String name(); List nodes(); boolean policyAllowList(); - boolean applies(HumanEntity entity); + boolean applies(LivingEntity entity); Effect effect(); int weight(); List worlds(); @@ -18,7 +19,7 @@ public interface Policy { nodes().add(node); } - default boolean matches(HumanEntity entity, Action action, Event event) { + default boolean matches(LivingEntity entity, Action action, Event event) { if (!worlds().contains(entity.getWorld().getName())) { return false; } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java index c87f916..8dcbc4e 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java @@ -2,6 +2,7 @@ package io.github.adrianvic.nemesiseye.policy; import io.github.adrianvic.nemesiseye.DataShifter; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import java.util.ArrayList; @@ -55,7 +56,7 @@ public record PolicyNode(List actions, List values) { return handlers; } - public boolean matches(HumanEntity entity, Action action, Event event) { + public boolean matches(LivingEntity entity, Action action, Event event) { if (!actions.contains(action)) { return false; } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/bePlaced.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java similarity index 81% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/bePlaced.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java index edf5d7c..8e6f28b 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/bePlaced.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java @@ -5,12 +5,13 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import org.bukkit.event.block.BlockPlaceEvent; -public class bePlaced implements NodeHandler { +public class BePlaced implements NodeHandler { @Override - public boolean check(HumanEntity entity, PolicyNode node, Action action, Event event) { + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { if (event instanceof BlockPlaceEvent bpe) { String type = bpe.getBlock().getType().toString(); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/equip.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java similarity index 93% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/equip.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java index 88bb6b6..73c1c1f 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/equip.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java @@ -6,6 +6,7 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.inventory.ClickType; @@ -13,10 +14,10 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; -public class equip implements NodeHandler { +public class Equip implements NodeHandler { @Override - public boolean check(HumanEntity entity, PolicyNode node, Action action, Event event) { + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { ItemStack item = null; if (event instanceof PlayerArmorChangeEvent e) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/glyde.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java similarity index 65% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/glyde.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java index dcce855..f9b9233 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/glyde.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java @@ -4,11 +4,12 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; -public class glyde implements NodeHandler { +public class Glyde implements NodeHandler { @Override - public boolean check(HumanEntity entity, PolicyNode node, Action action, Event event) { + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { return true; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java new file mode 100644 index 0000000..938f6ee --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java @@ -0,0 +1,21 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; + +import java.util.List; + +public class Spawn implements NodeHandler { + + @Override + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + String type = entity.getType().name(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java new file mode 100644 index 0000000..38b4f93 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java @@ -0,0 +1,33 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.Nemesis; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; + +public class UseEnchantment implements NodeHandler { + private final Glimmer glim = Nemesis.getInstance().getGlimmer(); + + @Override + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + if (entity instanceof HumanEntity e) { + ItemStack item = glim.getItemInMainHandHumanEntity(e); + + if (!glim.hasItemMeta(item)) return false; + if (!glim.hasAnyEnchantment(item)) return false; + + boolean matches = glim.hasEnchantment(item, + DataShifter.parseValueToStringMap(node.values())); + + return matches; + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useItem.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java similarity index 54% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useItem.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java index 6009407..45be0a7 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useItem.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java @@ -7,19 +7,22 @@ import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; -public class useItem implements NodeHandler { +public class UseItem implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(HumanEntity entity, PolicyNode node, Action action, Event event) { - String type = glim.getItemInMainHandHumanEntity(entity).getType().toString(); + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + if (entity instanceof HumanEntity e) { + String type = glim.getItemInMainHandHumanEntity(e).getType().toString(); - for (String s : DataShifter.parseValueToStringList(node.values())) { - if (DataShifter.safeMatches(s, type)) { - return true; + for (String s : DataShifter.parseValueToStringList(node.values())) { + if (DataShifter.safeMatches(s, type)) { + return true; + } } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java deleted file mode 100644 index 5fe3ae0..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy.handlers; - -import io.github.adrianvic.nemesiseye.DataShifter; -import io.github.adrianvic.nemesiseye.Nemesis; -import io.github.adrianvic.nemesiseye.policy.Action; -import io.github.adrianvic.nemesiseye.policy.NodeHandler; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.Event; -import org.bukkit.inventory.ItemStack; - -public class useEnchantment implements NodeHandler { - private final Glimmer glim = Nemesis.getInstance().getGlimmer(); - - @Override - public boolean check(HumanEntity entity, PolicyNode node, Action action, Event event) { - ItemStack item = glim.getItemInMainHandHumanEntity(entity); - - if (!glim.hasItemMeta(item)) return false; - if (!glim.hasAnyEnchantment(item)) return false; - - boolean matches = glim.hasEnchantment(item, - DataShifter.parseValueToStringMap(node.values())); - - return matches; - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java index 8476fda..7e85467 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java @@ -4,12 +4,13 @@ import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import java.util.List; public record Core(String name, List worlds, List nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy { @Override - public boolean applies(HumanEntity entity) { + public boolean applies(LivingEntity entity) { return false; } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java index 7080ea8..127430c 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java @@ -4,11 +4,12 @@ import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import java.util.List; public record GlobalPolicy(String name, List worlds, List nodes, boolean policyAllowList, Effect effect, int weight) implements Policy { - public boolean applies(HumanEntity entity) { + public boolean applies(LivingEntity entity) { return true; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java index 5765535..255e433 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java @@ -5,12 +5,13 @@ import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import java.util.List; public record LocationPolicy(String name, List worlds, List locations, List nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy { @Override - public boolean applies(HumanEntity entity) { + public boolean applies(LivingEntity entity) { for (Glimmer.Box box : locations) { if (box.contains(entity.getLocation().toVector(), entity.getWorld())) { return !policyAllowList; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java index 1c952ac..b7eefc7 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java @@ -4,13 +4,14 @@ import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import java.util.List; public record PermissionPolicy(String name, List worlds, List permissions, List nodes, boolean policyAllowList, Effect effect, int weight) implements Policy { @Override - public boolean applies(HumanEntity entity) { + public boolean applies(LivingEntity entity) { for (String perm : permissions) { if (entity.hasPermission(perm)) { return true; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java index 8eeae4b..ba40e4b 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java @@ -4,13 +4,14 @@ import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import java.util.List; public record PlayerNamePolicy(String name, List worlds, List playerName, List nodes, Effect effect, boolean policyAllowList, int weight) implements Policy { @Override - public boolean applies(HumanEntity entity) { + public boolean applies(LivingEntity entity) { if (playerName.contains(entity.getName())) { return !policyAllowList(); } else { diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java index f7bf288..ea64591 100644 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java @@ -6,7 +6,9 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntitySpawnEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; @@ -36,5 +38,8 @@ public class EventListener implements Listener { public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event);} + public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event); } + + @EventHandler + public void onCreatureSpawnEvent(CreatureSpawnEvent event) { Events.onCreatureSpawnEvent(event); } } \ No newline at end of file From b044052b6dc1e7ec3b3035cde7050d9053944b25 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Wed, 20 May 2026 15:54:14 -0300 Subject: [PATCH 04/12] Move isArmor to reflection implementations and refactor NodeHandler replace use of safeMatches(String expression, String against) usage with safeMatches(List expressions, String against). --- .../adrianvic/nemesiseye/impl/b1_7_3.java | 18 ++++++++ .../impl/{ => events}/BlockEventListener.java | 2 +- .../{ => events}/EntityEventListener.java | 2 +- .../{ => events}/PlayerEventListener.java | 2 +- .../nemesiseye/policy/handlers/BePlaced.java | 10 ++--- .../nemesiseye/policy/handlers/Equip.java | 42 ++++++------------- .../nemesiseye/policy/handlers/Glyde.java | 1 - .../nemesiseye/policy/handlers/UseItem.java | 9 ++-- .../nemesiseye/reflection/Glimmer.java | 1 + .../adrianvic/nemesiseye/impl/r1_21.java | 15 +++++++ 10 files changed, 58 insertions(+), 44 deletions(-) rename src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/{ => events}/BlockEventListener.java (90%) rename src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/{ => events}/EntityEventListener.java (89%) rename src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/{ => events}/PlayerEventListener.java (86%) diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java index 19d08fb..3d38305 100644 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java @@ -2,9 +2,13 @@ package io.github.adrianvic.nemesiseye.impl; import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.impl.commands.Eye; +import io.github.adrianvic.nemesiseye.impl.events.BlockEventListener; +import io.github.adrianvic.nemesiseye.impl.events.EntityEventListener; +import io.github.adrianvic.nemesiseye.impl.events.PlayerEventListener; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyParsers; import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.HumanEntity; @@ -99,4 +103,18 @@ public class b1_7_3 implements Glimmer { public boolean hasAnyEnchantment(ItemStack itemStack) { return false; } + + @Override + public boolean isArmor(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return false; + } + + String name = item.getType().name(); + + return name.endsWith("_HELMET") + || name.endsWith("_CHESTPLATE") + || name.endsWith("_LEGGINGS") + || name.endsWith("_BOOTS"); + } } diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/BlockEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java similarity index 90% rename from src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/BlockEventListener.java rename to src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java index 76fd2d1..6ea38bf 100644 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/BlockEventListener.java +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java @@ -1,4 +1,4 @@ -package io.github.adrianvic.nemesiseye.impl; +package io.github.adrianvic.nemesiseye.impl.events; import io.github.adrianvic.nemesiseye.Events; import org.bukkit.event.block.BlockBreakEvent; diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/EntityEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java similarity index 89% rename from src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/EntityEventListener.java rename to src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java index cf49da1..e6655f8 100644 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/EntityEventListener.java +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java @@ -1,4 +1,4 @@ -package io.github.adrianvic.nemesiseye.impl; +package io.github.adrianvic.nemesiseye.impl.events; import io.github.adrianvic.nemesiseye.Events; import org.bukkit.event.entity.EntityDamageByEntityEvent; diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/PlayerEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java similarity index 86% rename from src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/PlayerEventListener.java rename to src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java index b77a56a..ec81484 100644 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/PlayerEventListener.java +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java @@ -1,4 +1,4 @@ -package io.github.adrianvic.nemesiseye.impl; +package io.github.adrianvic.nemesiseye.impl.events; import io.github.adrianvic.nemesiseye.Events; import org.bukkit.event.player.PlayerInteractEvent; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java index 8e6f28b..ff9d08b 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java @@ -9,18 +9,18 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import org.bukkit.event.block.BlockPlaceEvent; +import java.util.List; + public class BePlaced implements NodeHandler { @Override public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { if (event instanceof BlockPlaceEvent bpe) { String type = bpe.getBlock().getType().toString(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); - for (String s : DataShifter.parseValueToStringList(node.values())) { - if (DataShifter.safeMatches(s, type)) { - return true; - } - } + return DataShifter.safeMatches(parsedValue, type); } + return false; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java index 73c1c1f..16e1ad9 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java @@ -2,10 +2,11 @@ package io.github.adrianvic.nemesiseye.policy.handlers; import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.HumanEntity; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -14,72 +15,53 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; +import java.util.List; + public class Equip implements NodeHandler { + private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { ItemStack item = null; if (event instanceof PlayerArmorChangeEvent e) { - // Right click equip, dispenser equip, etc... item = e.getNewItem(); } else if (event instanceof InventoryClickEvent e) { InventoryType.SlotType slotType = e.getSlotType(); - // Number key swap into armor slot - if (e.getClick() == ClickType.NUMBER_KEY + if (e.getClick() == ClickType.NUMBER_KEY // hotbar key swap && slotType == InventoryType.SlotType.ARMOR && entity instanceof Player player) { item = player.getInventory().getItem(e.getHotbarButton()); } - // Shift click armor from inventory else if (e.isShiftClick()) { ItemStack current = e.getCurrentItem(); - if (isArmor(current)) { + if (glim.isArmor(current)) { item = current; } } - // Cursor click onto armor slot + // regular click else if (slotType == InventoryType.SlotType.ARMOR) { ItemStack cursor = e.getCursor(); - if (isArmor(cursor)) { + if (glim.isArmor(cursor)) { item = cursor; } } } - if (!isArmor(item)) { + if (!glim.isArmor(item)) { return false; } String type = item.getType().name(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); - for (String s : DataShifter.parseValueToStringList(node.values())) { - if (DataShifter.safeMatches(s, type)) { - return true; - } - } - - return false; - } - - private boolean isArmor(ItemStack item) { - if (item == null || item.getType().isAir()) { - return false; - } - - String name = item.getType().name(); - - return name.endsWith("_HELMET") - || name.endsWith("_CHESTPLATE") - || name.endsWith("_LEGGINGS") - || name.endsWith("_BOOTS") - || item.getType() == org.bukkit.Material.ELYTRA; + return DataShifter.safeMatches(parsedValue, type); } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java index f9b9233..46d2b11 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java @@ -3,7 +3,6 @@ package io.github.adrianvic.nemesiseye.policy.handlers; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java index 45be0a7..671c77a 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java @@ -10,6 +10,8 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; +import java.util.List; + public class UseItem implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @@ -18,12 +20,9 @@ public class UseItem implements NodeHandler { public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { if (entity instanceof HumanEntity e) { String type = glim.getItemInMainHandHumanEntity(e).getType().toString(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); - for (String s : DataShifter.parseValueToStringList(node.values())) { - if (DataShifter.safeMatches(s, type)) { - return true; - } - } + return DataShifter.safeMatches(parsedValue, type); } return false; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java index dec3092..1462f72 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java @@ -26,6 +26,7 @@ public interface Glimmer { boolean hasItemMeta(ItemStack item); boolean hasEnchantment(ItemStack item, Map valuesmap); boolean hasAnyEnchantment(ItemStack itemStack); + boolean isArmor(ItemStack item); ItemStack getItemInMainHandHumanEntity(HumanEntity entity); // Commands diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java index b929d0b..069d02b 100644 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java @@ -105,4 +105,19 @@ public class r1_21 implements Glimmer { public boolean hasAnyEnchantment(ItemStack item) { return !(item.getItemMeta().getEnchants().isEmpty()); } + + @Override + public boolean isArmor(ItemStack item) { + if (item == null || item.getType().isAir()) { + return false; + } + + String name = item.getType().name(); + + return name.endsWith("_HELMET") + || name.endsWith("_CHESTPLATE") + || name.endsWith("_LEGGINGS") + || name.endsWith("_BOOTS") + || item.getType() == org.bukkit.Material.ELYTRA; + } } From 76f13a2412ec8c45fb66ce7ea36dda5e60383af3 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Wed, 20 May 2026 18:13:16 -0300 Subject: [PATCH 05/12] Move version-specific code to reflection implementation and add basic testing. --- build.gradle.kts | 19 ++++- .../adrianvic/nemesiseye/impl/b1_7_3.java | 32 ++++++++ .../github/adrianvic/nemesiseye/Events.java | 53 +++---------- .../nemesiseye/commands/EyeCore.java | 6 +- .../nemesiseye/commands/sub/Subcommand.java | 3 +- .../nemesiseye/policy/handlers/Equip.java | 38 +-------- .../policy/handlers/UseEnchantment.java | 1 + .../nemesiseye/policy/handlers/UseItem.java | 5 +- .../policy/policies/PermissionPolicy.java | 3 +- .../nemesiseye/reflection/Glimmer.java | 10 +++ .../nemesiseye/reflection/VersionMatcher.java | 75 ++++++++++-------- .../adrianvic/nemesiseye/impl/r1_21.java | 78 +++++++++++++++++++ .../adrianvic/nemesiseye/ValidatorTest.java | 43 ++++++++++ .../impl/ImplementationContractTest.java | 64 +++++++++++++++ 14 files changed, 311 insertions(+), 119 deletions(-) create mode 100644 src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java create mode 100644 src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java diff --git a/build.gradle.kts b/build.gradle.kts index 488a93d..e2d9a2e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,9 +59,24 @@ mcVersions.forEach { ver -> /* ----------------------------------------- */ dependencies { - add("compileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") - add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") + add("compileOnly", "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") + add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") add("b1_7_3CompileOnly", files("libs/craftbukkit-1060.jar")) + + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + testImplementation("org.mockito:mockito-core:5.5.0") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testImplementation("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") + testImplementation("com.github.seeseemelk:MockBukkit-v1.21:3.102.0") + + // Allow tests to see the versioned implementations + mcVersions.forEach { ver -> + testImplementation(sourceSets[ver].output) + } +} + +tasks.test { + useJUnitPlatform() } /* ----------------------------------------- */ diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java index 3d38305..0a86ce9 100644 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java @@ -76,6 +76,38 @@ public class b1_7_3 implements Glimmer { return entity.getItemInHand(); } + @Override + public boolean isAir(ItemStack item) { + return item == null || item.getType() == Material.AIR; + } + + @Override + public boolean isGliding(org.bukkit.entity.Player player) { + return false; + } + + @Override + public void setGliding(org.bukkit.entity.Player player, boolean gliding) { + } + + @Override + public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) { + if (sender instanceof org.bukkit.entity.Player p) { + return p.isOp(); + } + return true; // Console always has permission + } + + @Override + public boolean isArmorEquipAttempt(org.bukkit.event.Event event) { + return false; + } + + @Override + public ItemStack getEquippedItem(org.bukkit.event.Event event) { + return null; + } + @Override public void sendMessage(CommandSender commandSender, String text) { String[] lines = text.split("\\r?\\n"); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Events.java b/src/main/java/io/github/adrianvic/nemesiseye/Events.java index 58b5140..a611890 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Events.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Events.java @@ -1,16 +1,14 @@ package io.github.adrianvic.nemesiseye; import io.github.adrianvic.nemesiseye.policy.Action; -import org.bukkit.Material; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; @@ -19,6 +17,10 @@ import java.util.List; public class Events { + private static Glimmer g() { + return Nemesis.getInstance().getGlimmer(); + } + public static void onBlockBreak(BlockBreakEvent event) { event.setCancelled( !Validator.can( @@ -32,12 +34,12 @@ public class Events { public static void onInteractionEvent(PlayerInteractEvent event) { ItemStack item = event.getItem(); - if (item == null || item.getType().isAir()) { + if (g().isAir(item)) { return; } // Right-click armor equipping - if (isArmor(item) + if (g().isArmor(item) && !Validator.can(event.getPlayer(), Action.EQUIP, event)) { event.setCancelled(true); return; @@ -68,18 +70,18 @@ public class Events { } public static void onPlayerMoveEvent(PlayerMoveEvent event) { - if (event.getPlayer().isGliding() + if (g().isGliding(event.getPlayer()) && !Validator.can( event.getPlayer(), List.of(Action.GLYDE), event )) { - event.getPlayer().setGliding(false); + g().setGliding(event.getPlayer(), false); } } public static void onInventoryClickEvent(InventoryClickEvent event) { - if (!isArmorEquipAttempt(event)) { + if (!g().isArmorEquipAttempt(event)) { return; } @@ -93,39 +95,4 @@ public class Events { public static void onCreatureSpawnEvent(CreatureSpawnEvent event) { event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event)); } - - private static boolean isArmorEquipAttempt(InventoryClickEvent event) { - if (event.getSlotType() == InventoryType.SlotType.ARMOR) { - return true; - } - - if (event.isShiftClick()) { - return isArmor(event.getCurrentItem()); - } - - if (event.getClick() == ClickType.NUMBER_KEY - && event.getSlotType() == InventoryType.SlotType.ARMOR - && event.getWhoClicked() instanceof Player player) { - return isArmor( - player.getInventory().getItem(event.getHotbarButton()) - ); - } - - return false; - } - - private static boolean isArmor(ItemStack item) { - if (item == null || item.getType().isAir()) { - return false; - } - - Material type = item.getType(); - String name = type.name(); - - return name.endsWith("_HELMET") - || name.endsWith("_CHESTPLATE") - || name.endsWith("_LEGGINGS") - || name.endsWith("_BOOTS") - || type == Material.ELYTRA; - } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java index e6583db..6d2c799 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java @@ -26,7 +26,7 @@ public class EyeCore { commandSender.sendMessage("Unknown command, try '/eye help' to list available commands."); return true; } - else if (commandSender.hasPermission(sub.permission())) { + else if (glim.hasPermission(commandSender, sub.permission())) { return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); } else { // Nemesis.getInstance().getLogger().info("does not have %s".formatted(sub.permission())); @@ -41,7 +41,7 @@ public class EyeCore { if (strings.length == 1) { Map cmds = new HashMap<>(); for (Map.Entry e : Commands.getAll().entrySet()) { - if (e.getValue().hasPermission(commandSender)) { + if (glim.hasPermission(commandSender, e.getValue().permission())) { cmds.put(e.getKey(), e.getValue()); cmds.put(e.getKey(), e.getValue()); } @@ -49,7 +49,7 @@ public class EyeCore { return new ArrayList<>(cmds.keySet()); } Subcommand sub = Commands.get(strings[0].toLowerCase()); - if (sub != null && commandSender.hasPermission(sub.permission())) { + if (sub != null && glim.hasPermission(commandSender, sub.permission())) { return sub.onTabComplete(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); } return List.of(); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java index 3678494..00e0c56 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java @@ -1,5 +1,6 @@ package io.github.adrianvic.nemesiseye.commands.sub; +import io.github.adrianvic.nemesiseye.Nemesis; import org.bukkit.command.CommandSender; import org.bukkit.permissions.Permission; @@ -13,6 +14,6 @@ public interface Subcommand { List onTabComplete(CommandSender sender, String[] strings); String permission(); default boolean hasPermission(CommandSender sender) { - return sender.hasPermission(permission()); + return Nemesis.getInstance().getGlimmer().hasPermission(sender, permission()); } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java index 16e1ad9..8d5cfcf 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java @@ -1,6 +1,5 @@ package io.github.adrianvic.nemesiseye.policy.handlers; -import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.policy.Action; @@ -8,11 +7,7 @@ import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; import org.bukkit.event.Event; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; import java.util.List; @@ -22,38 +17,7 @@ public class Equip implements NodeHandler { @Override public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { - ItemStack item = null; - - if (event instanceof PlayerArmorChangeEvent e) { - item = e.getNewItem(); - } - - else if (event instanceof InventoryClickEvent e) { - InventoryType.SlotType slotType = e.getSlotType(); - - if (e.getClick() == ClickType.NUMBER_KEY // hotbar key swap - && slotType == InventoryType.SlotType.ARMOR - && entity instanceof Player player) { - item = player.getInventory().getItem(e.getHotbarButton()); - } - - else if (e.isShiftClick()) { - ItemStack current = e.getCurrentItem(); - - if (glim.isArmor(current)) { - item = current; - } - } - - // regular click - else if (slotType == InventoryType.SlotType.ARMOR) { - ItemStack cursor = e.getCursor(); - - if (glim.isArmor(cursor)) { - item = cursor; - } - } - } + ItemStack item = glim.getEquippedItem(event); if (!glim.isArmor(item)) { return false; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java index 38b4f93..d67e9f9 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java @@ -19,6 +19,7 @@ public class UseEnchantment implements NodeHandler { if (entity instanceof HumanEntity e) { ItemStack item = glim.getItemInMainHandHumanEntity(e); + if (glim.isAir(item)) return false; if (!glim.hasItemMeta(item)) return false; if (!glim.hasAnyEnchantment(item)) return false; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java index 671c77a..ef91a98 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java @@ -19,7 +19,10 @@ public class UseItem implements NodeHandler { @Override public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { if (entity instanceof HumanEntity e) { - String type = glim.getItemInMainHandHumanEntity(e).getType().toString(); + org.bukkit.inventory.ItemStack item = glim.getItemInMainHandHumanEntity(e); + if (glim.isAir(item)) return false; + + String type = item.getType().toString(); List parsedValue = DataShifter.parseValueToStringList(node.values()); return DataShifter.safeMatches(parsedValue, type); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java index b7eefc7..309ce07 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java @@ -1,5 +1,6 @@ package io.github.adrianvic.nemesiseye.policy.policies; +import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyNode; @@ -13,7 +14,7 @@ public record PermissionPolicy(String name, List worlds, List pe @Override public boolean applies(LivingEntity entity) { for (String perm : permissions) { - if (entity.hasPermission(perm)) { + if (Nemesis.getInstance().getGlimmer().hasPermission(entity, perm)) { return true; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java index 1462f72..ccc69b4 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java @@ -27,8 +27,18 @@ public interface Glimmer { boolean hasEnchantment(ItemStack item, Map valuesmap); boolean hasAnyEnchantment(ItemStack itemStack); boolean isArmor(ItemStack item); + boolean isAir(ItemStack item); ItemStack getItemInMainHandHumanEntity(HumanEntity entity); + // Players + boolean isGliding(org.bukkit.entity.Player player); + void setGliding(org.bukkit.entity.Player player, boolean gliding); + boolean hasPermission(org.bukkit.command.CommandSender sender, String permission); + + // Events + boolean isArmorEquipAttempt(org.bukkit.event.Event event); + ItemStack getEquippedItem(org.bukkit.event.Event event); + // Commands void sendMessage(CommandSender commandSender, String text); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java index 7852cd5..d8323cc 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java @@ -94,54 +94,67 @@ public class VersionMatcher { } public Glimmer loadGlim() { - String rawVersion; + String rawVersion = null; try { - rawVersion = Bukkit.getMinecraftVersion(); // returns something like "1.21.10" - } catch (NoSuchMethodError e) { - return betaLoadGlim(); + rawVersion = Bukkit.getMinecraftVersion(); + } catch (NoSuchMethodError ignored) {} + + if (rawVersion == null || rawVersion.isEmpty()) { + String v = Bukkit.getVersion(); // e.g. "git-Bukkit-0.0.0-1060-... (MC: 1.7.3)" + int start = v.lastIndexOf("MC: "); + if (start != -1) { + rawVersion = v.substring(start + 4, v.length() - 1); + } else { + rawVersion = "unknown"; + } } String matchInfo = getVersion("release", rawVersion); if (matchInfo.isEmpty()) { - // TODO: Should change to something more robust, it's not beta since we have Bukkit.getMinecraftVersion() - return betaLoadGlim(); + matchInfo = getVersion("beta", rawVersion); } - // split the returned string: "pattern|classSuffix" - String[] partsInfo = matchInfo.split("\\|"); - String classSuffix = partsInfo[1]; // e.g. "r1_21" + if (matchInfo.isEmpty()) { + // Fallback to b1.7.3 if everything fails, or throw error? + // User said it supports b1.7.3 and 1.21.x. + Glimmer fallback = tryInstantiate("io.github.adrianvic.nemesiseye.impl.b1_7_3"); + if (fallback != null) return fallback; + throw new IllegalStateException("No suitable implementation found for version " + rawVersion); + } + String[] partsInfo = matchInfo.split("\\|"); + String classSuffix = partsInfo[1]; + + Glimmer glimmer = tryInstantiate("io.github.adrianvic.nemesiseye.impl." + classSuffix); + if (glimmer != null) return glimmer; + + // Backward search for older implementations String[] versionParts = rawVersion.split("\\."); int major = parseInt(versionParts[0]); - int minor = parseInt(versionParts[1]); + int minor = versionParts.length > 1 ? parseInt(versionParts[1]) : 0; int patch = versionParts.length > 2 ? parseInt(versionParts[2]) : 0; - while (true) { - String className = "io.github.adrianvic.nemesiseye.impl." + classSuffix; - Glimmer glimmer = tryInstantiate(className); - if (glimmer != null) return glimmer; + while (major >= 0) { + while (minor >= 0) { + while (patch >= 0) { + String className = String.format("io.github.adrianvic.nemesiseye.impl.r%d_%d_%d", major, minor, patch); + glimmer = tryInstantiate(className); + if (glimmer != null) return glimmer; + + className = String.format("io.github.adrianvic.nemesiseye.impl.r%d_%d", major, minor); + glimmer = tryInstantiate(className); + if (glimmer != null) return glimmer; - if (patch > 0) { - patch--; - continue; - } - if (minor > 0) { + patch--; + } minor--; - patch = 20; - continue; + patch = 20; // Search up to .20 patch of previous minor } - className = "io.github.adrianvic.nemesiseye.impl.r" + major + "_" + minor; - glimmer = tryInstantiate(className); - if (glimmer != null) return glimmer; - - throw new IllegalStateException( - "No suitable implementation found for version " + rawVersion); + major--; + minor = 30; // Search up to .30 minor of previous major } - } - private Glimmer betaLoadGlim() { - // Bukkit.getVersion() // returns something like "1.1.10 (MC: 1.7.3)" WEIRD - return tryInstantiate("io.github.adrianvic.nemesiseye.impl.b1_7_3"); // only supported beta version for now + throw new IllegalStateException("No suitable implementation found for version " + rawVersion); } private Glimmer tryInstantiate(String className) { diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java index 069d02b..ac1b6d7 100644 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java @@ -67,6 +67,84 @@ public class r1_21 implements Glimmer { return entity.getInventory().getItemInMainHand(); } + @Override + public boolean isAir(ItemStack item) { + return item == null || item.getType().isAir(); + } + + @Override + public boolean isGliding(org.bukkit.entity.Player player) { + return player.isGliding(); + } + + @Override + public void setGliding(org.bukkit.entity.Player player, boolean gliding) { + player.setGliding(gliding); + } + + @Override + public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) { + return sender.hasPermission(permission); + } + + @Override + public boolean isArmorEquipAttempt(org.bukkit.event.Event event) { + if (!(event instanceof org.bukkit.event.inventory.InventoryClickEvent e)) { + return false; + } + + if (e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) { + return true; + } + + if (e.isShiftClick()) { + return isArmor(e.getCurrentItem()); + } + + if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY + && e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR + && e.getWhoClicked() instanceof org.bukkit.entity.Player player) { + return isArmor( + player.getInventory().getItem(e.getHotbarButton()) + ); + } + + return false; + } + + @Override + public ItemStack getEquippedItem(org.bukkit.event.Event event) { + if (event instanceof org.bukkit.event.inventory.InventoryClickEvent e) { + org.bukkit.event.inventory.InventoryType.SlotType slotType = e.getSlotType(); + + if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY // hotbar key swap + && slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR + && e.getWhoClicked() instanceof org.bukkit.entity.Player player) { + return player.getInventory().getItem(e.getHotbarButton()); + } + + if (e.isShiftClick()) { + ItemStack current = e.getCurrentItem(); + if (isArmor(current)) return current; + } + + // regular click + if (slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) { + ItemStack cursor = e.getCursor(); + if (isArmor(cursor)) return cursor; + } + } + + // Try Paper's PlayerArmorChangeEvent via reflection or just check if class exists + try { + if (event instanceof com.destroystokyo.paper.event.player.PlayerArmorChangeEvent e) { + return e.getNewItem(); + } + } catch (NoClassDefFoundError | Exception ignored) {} + + return null; + } + @Override public void sendMessage(CommandSender commandSender, String text) { commandSender.sendMessage(text); diff --git a/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java b/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java new file mode 100644 index 0000000..fe4ef52 --- /dev/null +++ b/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java @@ -0,0 +1,43 @@ +package io.github.adrianvic.nemesiseye; + +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.Effect; +import io.github.adrianvic.nemesiseye.policy.Policy; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class ValidatorTest { + + private Glimmer mockGlim; + private Config mockConfig; + + @BeforeEach + void setUp() { + mockGlim = mock(Glimmer.class); + } + + @Test + void testValidatorCanDeny() { + Player player = mock(Player.class); + Event event = mock(Event.class); + Policy policy = mock(Policy.class); + + when(policy.applies(player)).thenReturn(true); + when(policy.matches(player, Action.BREAK, event)).thenReturn(true); + when(policy.effect()).thenReturn(Effect.DENY); + + // We need to handle the static Config.getInstance() + // This is tricky without refactoring or Mockito-inline + // For now, let's just demonstrate the concept if Validator was more testable + } +} diff --git a/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java b/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java new file mode 100644 index 0000000..763bf53 --- /dev/null +++ b/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java @@ -0,0 +1,64 @@ +package io.github.adrianvic.nemesiseye.impl; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +public class ImplementationContractTest { + + private ServerMock server; + + @BeforeEach + void setUp() { + server = MockBukkit.mock(); + } + + @AfterEach + void tearDown() { + MockBukkit.unmock(); + } + + static Stream implementations() { + return Stream.of( + new b1_7_3(), + new r1_21() + ); + } + + @ParameterizedTest + @MethodSource("implementations") + void testIsArmor(Glimmer glim) { + // Helmets + assertTrue(glim.isArmor(new ItemStack(Material.IRON_HELMET))); + assertTrue(glim.isArmor(new ItemStack(Material.DIAMOND_HELMET))); + + // Chestplates + assertTrue(glim.isArmor(new ItemStack(Material.GOLDEN_CHESTPLATE))); + + // Non-armor + assertFalse(glim.isArmor(new ItemStack(Material.STICK))); + assertFalse(glim.isArmor(new ItemStack(Material.DIRT))); + + // Null/Air + assertFalse(glim.isArmor(null)); + assertFalse(glim.isArmor(new ItemStack(Material.AIR))); + } + + @ParameterizedTest + @MethodSource("implementations") + void testIsAir(Glimmer glim) { + assertTrue(glim.isAir(null)); + assertTrue(glim.isAir(new ItemStack(Material.AIR))); + assertFalse(glim.isAir(new ItemStack(Material.STONE))); + } +} From fe35fc0a41288d4bf78a031a1d80ececcc6947bd Mon Sep 17 00:00:00 2001 From: tenkuma Date: Wed, 20 May 2026 22:45:45 -0300 Subject: [PATCH 06/12] Atualizar README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6751543..bf357cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -eye_of_nemesis_social_cover +eye_of_nemesis_social_cover [![Modrinth Badge](https://img.shields.io/badge/Modrinth-Black?style=social&logo=Modrinth&logoColor=green)](https://modrinth.com/plugin/eye-of-nemesis) [![English Wiki Badge](https://img.shields.io/badge/English-White?style=flat-square&label=Wiki&color=black)](https://github.com/adrianvic/NemesisEye/wiki) From 0de8990d91dbe0e3d2a26f218b9407dbf737fbb7 Mon Sep 17 00:00:00 2001 From: tenkuma Date: Wed, 20 May 2026 22:47:42 -0300 Subject: [PATCH 07/12] Atualizar .forgejo/workflows/build.yml --- .forgejo/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index c3c036b..149d2f5 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -11,7 +11,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: arch-linux env: NEMESIS_VERSION_NAME: "autobuild-${{ github.sha }}" NEMESIS_BUILD_CHANNEL: "autobuild" From aa1a17c8fdcff8c3c99fd72cfdff64bc05cb7a98 Mon Sep 17 00:00:00 2001 From: tenkuma Date: Wed, 20 May 2026 22:48:23 -0300 Subject: [PATCH 08/12] Atualizar .forgejo/workflows/release-build.yml --- .forgejo/workflows/release-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/release-build.yml b/.forgejo/workflows/release-build.yml index dcc83d3..392e32e 100644 --- a/.forgejo/workflows/release-build.yml +++ b/.forgejo/workflows/release-build.yml @@ -6,7 +6,7 @@ on: jobs: build-and-release: - runs-on: ubuntu-latest + runs-on: arch-linux env: NEMESIS_VERSION_NAME: "${{ github.ref_name }}" NEMESIS_BUILD_CHANNEL: "production" From 1f053c00ac47ebd8a82bab3abe835e7e8786ab20 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 16 Jun 2026 19:16:06 -0300 Subject: [PATCH 09/12] Bump to 1.0.6-SNAPSHOT; add ENDERMAN_ATTACK_PLAYER and FORMBLOCK actions; refactor Validator, NodeHandler and Policy to be aware of world the action takes place instead of entity (for supporting entity-less actions like FORMBLOCK); change VersionMatcher to load 1.21 implementation on all versions that don't match b1.7.3 to support future versions; properly register autocomplete for /eye command. --- docs/Nodes.md | 11 +- .../nemesiseye/impl/ConfigurationEx.java | 89 -------- .../github/adrianvic/nemesiseye/impl/Log.java | 41 ---- .../adrianvic/nemesiseye/impl/b1_7_3.java | 152 ------------- .../nemesiseye/impl/commands/Eye.java | 19 -- .../impl/events/BlockEventListener.java | 18 -- .../impl/events/EntityEventListener.java | 13 -- .../impl/events/PlayerEventListener.java | 13 -- .../github/adrianvic/nemesiseye/Events.java | 13 +- .../adrianvic/nemesiseye/Validator.java | 34 ++- .../nemesiseye/commands/EyeCore.java | 3 +- .../adrianvic/nemesiseye/policy/Action.java | 5 +- .../nemesiseye/policy/NodeHandler.java | 5 +- .../nemesiseye/policy/NodeHandlers.java | 6 +- .../adrianvic/nemesiseye/policy/Policy.java | 8 +- .../nemesiseye/policy/PolicyNode.java | 33 ++- .../nemesiseye/policy/PolicyParser.java | 5 +- .../nemesiseye/policy/handlers/BePlaced.java | 5 +- .../handlers/{Glyde.java => Boolean.java} | 6 +- .../nemesiseye/policy/handlers/Equip.java | 4 +- .../nemesiseye/policy/handlers/FormBlock.java | 28 +++ .../policy/handlers/InteractWith.java | 30 +++ .../nemesiseye/policy/handlers/Spawn.java | 15 +- .../policy/handlers/UseEnchantment.java | 11 +- .../nemesiseye/policy/handlers/UseItem.java | 7 +- .../nemesiseye/reflection/VersionMatcher.java | 141 +----------- src/main/resources/settings.yml | 42 ++-- .../nemesiseye/impl/EventListener.java | 45 ---- .../nemesiseye/impl/commands/Eye.java | 29 --- .../adrianvic/nemesiseye/impl/r1_21.java | 201 ------------------ .../adrianvic/nemesiseye/ValidatorTest.java | 13 +- .../impl/ImplementationContractTest.java | 2 + 32 files changed, 216 insertions(+), 831 deletions(-) delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java delete mode 100644 src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java rename src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/{Glyde.java => Boolean.java} (63%) create mode 100644 src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java create mode 100644 src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/InteractWith.java delete mode 100644 src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java delete mode 100644 src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java delete mode 100644 src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java diff --git a/docs/Nodes.md b/docs/Nodes.md index 57cff1d..d5a88bf 100644 --- a/docs/Nodes.md +++ b/docs/Nodes.md @@ -1,5 +1,5 @@ # Nodes -Nodes narrow down even further if an action can be done or not. A node needs _properties_ to know what it should check against, for example, a HIT node needs a _list_ of blocks that it will check if the player is holding; while a USE_ENCHANTMENT node needs a mapping of enchantment to level. Some nodes like glyde don't need a value and will ignore if you provide one. +Nodes narrow down even further if an action can be done or not. A node needs _properties_ to know what it should check against, for example, a HIT node needs a _list_ of blocks that it will check if the player is holding; while a USE_ENCHANTMENT node needs a mapping of enchantment to level. Some nodes like GLYDE don't need a value and will ignore if you provide one. ```yaml nodes: - [HIT]: @@ -31,4 +31,11 @@ Triggered on `InventoryClickEvent` and `PlayerInteractEvent` (only if item is an Triggered on `PlayerMoveEvent`, returns true if player is gliding. ### USE_ENCHANTMENT -Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true the used item has an enchantment matching the provided mapping of enchantment-level. \ No newline at end of file +Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true when the used item has an enchantment matching the provided mapping of enchantment-level. + +### FORMBLOCK +Triggered on `BlockFormEvent` (ex. triggers when snow is formed from rain), returns true when the block being formed is in the provided list of materials. + +## ENDERMAN_ATTACK_PLAYER +Triggered on `EndermanAttackPlayerEvent` (from actions other than hitting the Enderman), returns true. + diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java deleted file mode 100644 index a4b3327..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl; - -import io.github.adrianvic.nemesiseye.Nemesis; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.config.Configuration; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; - -public class ConfigurationEx extends Configuration { - private final File configFile; - private final Log logger; - JavaPlugin plugin; - - public ConfigurationEx(String fileName, Log _logger) { - super(new File(Nemesis.getInstance().getDataFolder(), fileName)); - this.plugin = Nemesis.getInstance(); - logger = _logger; - this.configFile = new File(plugin.getDataFolder(), fileName); - } - - @Override - public void load() { - createParentDirectories(); - - if (!configFile.exists()) { - copyDefaultConfig(); - } - - try { - super.load(); - } catch (Exception e) { - logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - private void createParentDirectories() { - try { - Files.createDirectories(configFile.getParentFile().toPath()); - } catch (IOException e) { - logger.severe(String.format("Failed to generate default config directory: %s", e.getMessage())); - } - } - - private void copyDefaultConfig() { - // Load the config from the JAR directly (it is located at the root level) - String resourcePath = "/" + configFile.getName(); // Root path of JAR - - try (InputStream input = plugin.getClass().getResourceAsStream(resourcePath)) { - if (input == null) { - logger.severe(String.format("Default config '%s' wasn't found in the JAR.", configFile.getName())); - return; - } - - Files.copy(input, configFile.toPath()); - if (Files.exists(configFile.toPath())) { - logger.info(String.format("Default config '%s' generated successfully.", configFile.getName())); - } else { - logger.warning("We tried to generate the default config file, but it was not found even after the creation. Maybe your permissions are broken?"); - } - } catch (IOException e) { - logger.severe(String.format("Failed to generate default config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - public void loadConfig() { - try { - this.load(); - logger.info(String.format("Config '%s' loaded successfully.", configFile.getName())); - } catch (Exception e) { - logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - public void saveConfig() { - try { - this.save(); - logger.info(String.format("Config '%s' saved successfully.", configFile.getName())); - } catch (Exception e) { - logger.severe(String.format("Failed to save config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - public File getConfig() { - return configFile; - } -} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java deleted file mode 100644 index ccba8b1..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl; - -import static org.bukkit.Bukkit.getServer; - -import io.github.adrianvic.nemesiseye.Nemesis; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.java.JavaPlugin; - -public class Log { - JavaPlugin plugin; - PluginDescriptionFile pdf; - - public Log() { - plugin = Nemesis.getInstance(); - pdf = plugin.getDescription(); - } - - public void info(String message) { - getServer().getLogger().info("[" + pdf.getName() + "] " + message); - } - - public void infoc(String message) { - getServer().getLogger().info("[" + pdf.getName() + "] " + message); - } - - public void warning(String message) { - getServer().getLogger().warning("[" + pdf.getName() + "] " + message); - } - - public void warningc(String message) { - getServer().getLogger().warning("[" + pdf.getName() + "] " + message); - } - - public void severe(String message) { - getServer().getLogger().severe("[" + pdf.getName() + "] " + message); - } - - public void severec(String message) { - getServer().getLogger().severe("[" + pdf.getName() + "] " + message); - } -} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java deleted file mode 100644 index 0a86ce9..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java +++ /dev/null @@ -1,152 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl; - -import io.github.adrianvic.nemesiseye.Nemesis; -import io.github.adrianvic.nemesiseye.impl.commands.Eye; -import io.github.adrianvic.nemesiseye.impl.events.BlockEventListener; -import io.github.adrianvic.nemesiseye.impl.events.EntityEventListener; -import io.github.adrianvic.nemesiseye.impl.events.PlayerEventListener; -import io.github.adrianvic.nemesiseye.policy.Policy; -import io.github.adrianvic.nemesiseye.policy.PolicyParsers; -import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.Event; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@SuppressWarnings("unused") -public class b1_7_3 implements Glimmer { - JavaPlugin plugin; - PluginManager pm; - ConfigurationEx config; - - @Override - public File loadConfigFile() { - config = new ConfigurationEx("settings.yml", new Log()); - config.load(); - return config.getConfig(); - } - - @Override - public List loadPoliciesFromFile(File file) { - List rawPolicies = config.getList("Policies"); - - if (rawPolicies == null) { - return new ArrayList<>(); - } - - List> result = new ArrayList<>(rawPolicies.size()); - - for (Object entry : rawPolicies) { - if (entry instanceof Map m) { - result.add(m); - } - } - - List allPolicies = new ArrayList<>(); - for (Map policyMap : result) { - if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) { - allPolicies.add(PolicyParsers.get(type).parse(policyMap)); - } - } - - return allPolicies; - } - - @Override - public void onLoad() { - plugin = Nemesis.getInstance(); - pm = Nemesis.getInstance().getPluginManager(); - pm.registerEvent(Event.Type.ENTITY_DAMAGE, new EntityEventListener(), Event.Priority.Normal, plugin); - pm.registerEvent(Event.Type.BLOCK_BREAK, new BlockEventListener(), Event.Priority.Normal, plugin); - pm.registerEvent(Event.Type.PLAYER_INTERACT, new PlayerEventListener(), Event.Priority.Normal, plugin); - plugin.getCommand("eye").setExecutor(new Eye()); - } - - @Override - public ItemStack getItemInMainHandHumanEntity(HumanEntity entity) { - return entity.getItemInHand(); - } - - @Override - public boolean isAir(ItemStack item) { - return item == null || item.getType() == Material.AIR; - } - - @Override - public boolean isGliding(org.bukkit.entity.Player player) { - return false; - } - - @Override - public void setGliding(org.bukkit.entity.Player player, boolean gliding) { - } - - @Override - public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) { - if (sender instanceof org.bukkit.entity.Player p) { - return p.isOp(); - } - return true; // Console always has permission - } - - @Override - public boolean isArmorEquipAttempt(org.bukkit.event.Event event) { - return false; - } - - @Override - public ItemStack getEquippedItem(org.bukkit.event.Event event) { - return null; - } - - @Override - public void sendMessage(CommandSender commandSender, String text) { - String[] lines = text.split("\\r?\\n"); - for (String line : lines) { - commandSender.sendMessage(line); - } - } - - @Override - public boolean hasItemMeta(ItemStack item) { - return false; - } - - @Override - public List getWorlds() { - return plugin.getServer().getWorlds(); - } - - @Override - public boolean hasEnchantment(ItemStack item, Map valuesmap) { - return false; - } - - @Override - public boolean hasAnyEnchantment(ItemStack itemStack) { - return false; - } - - @Override - public boolean isArmor(ItemStack item) { - if (item == null || item.getType() == Material.AIR) { - return false; - } - - String name = item.getType().name(); - - return name.endsWith("_HELMET") - || name.endsWith("_CHESTPLATE") - || name.endsWith("_LEGGINGS") - || name.endsWith("_BOOTS"); - } -} diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java deleted file mode 100644 index b3db3e5..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl.commands; - -import io.github.adrianvic.nemesiseye.commands.EyeCore; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; - -public class Eye implements CommandExecutor { - private final EyeCore core; - - public Eye() { - core = new EyeCore(); - } - - @Override - public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) { - return core.onCommand(commandSender, command, s, strings); - } -} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java deleted file mode 100644 index 6ea38bf..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl.events; - -import io.github.adrianvic.nemesiseye.Events; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockListener; -import org.bukkit.event.block.BlockPlaceEvent; - -public class BlockEventListener extends BlockListener { - @Override - public void onBlockBreak(BlockBreakEvent event) { - Events.onBlockBreak(event); - } - - @Override - public void onBlockPlace(BlockPlaceEvent event) { - Events.onBlockPlaceEvent(event); - } -} diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java deleted file mode 100644 index e6655f8..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl.events; - -import io.github.adrianvic.nemesiseye.Events; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.EntityListener; - -public class EntityEventListener extends EntityListener { - @Override - public void onEntityDamage(EntityDamageEvent event) { - if (event instanceof EntityDamageByEntityEvent e) Events.onEntityDamageByEntityEvent(e); - } -} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java deleted file mode 100644 index ec81484..0000000 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl.events; - -import io.github.adrianvic.nemesiseye.Events; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerListener; - -public class PlayerEventListener extends PlayerListener { - @Override - public void onPlayerInteract(PlayerInteractEvent event) { - Events.onInteractionEvent(event); - } -} - diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Events.java b/src/main/java/io/github/adrianvic/nemesiseye/Events.java index a611890..bb857e2 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Events.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Events.java @@ -1,10 +1,12 @@ package io.github.adrianvic.nemesiseye; +import com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -47,7 +49,7 @@ public class Events { // Normal item interaction event.setCancelled( - !Validator.can(event.getPlayer(), Action.INTERACT, event) + !Validator.can(event.getPlayer(), List.of(Action.INTERACT, Action.INTERACTWITH), event) ); } @@ -95,4 +97,13 @@ public class Events { public static void onCreatureSpawnEvent(CreatureSpawnEvent event) { event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event)); } + + public static void onEndermanAttackPlayerEvent(EndermanAttackPlayerEvent event) { + event.setCancelled(!Validator.can(event.getEntity(), Action.ENDERMAN_ATTACK_PLAYER, event)); + } + + public static void onBlockFormEvent(BlockFormEvent event) { + boolean canceled = !Validator.can(event.getBlock().getWorld(), Action.FORMBLOCK, event); + event.setCancelled(canceled); + } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java index 9989ea6..1e3e020 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java @@ -2,6 +2,8 @@ package io.github.adrianvic.nemesiseye; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Policy; +import io.github.adrianvic.nemesiseye.policy.policies.GlobalPolicy; +import org.bukkit.World; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; @@ -21,11 +23,8 @@ public class Validator { } public static boolean can(LivingEntity entity, Action action, Event event) { - boolean restricted = false; - boolean allowed = false; - for (Policy policy : getPoliciesForEntity(entity)) { - boolean matches = policy.matches(entity, action, event); + boolean matches = policy.matches(entity.getWorld(), action, event); switch (policy.effect()) { case ALLOW: @@ -41,6 +40,33 @@ public class Validator { return true; } + public static boolean can(World world, Action action, Event event) { + for (GlobalPolicy policy : getGlobalPolicies()) { + boolean matches = policy.matches(world, action, event); + + switch (policy.effect()) { + case ALLOW: + if (matches) return true; + break; + + case DENY: + if (matches) return false; + } + } + + return true; + } + + public static List getGlobalPolicies() { + List ps = Config.getInstance().getPolicies(); + List result = new ArrayList<>(); + + for (Policy policy : ps) { + if (policy instanceof GlobalPolicy p) result.add(p); + } + + return result; + } public static List getPoliciesForEntity(LivingEntity entity) { List ps = Config.getInstance().getPolicies(); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java index 6d2c799..fafdb24 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java @@ -29,7 +29,6 @@ public class EyeCore { else if (glim.hasPermission(commandSender, sub.permission())) { return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); } else { - // Nemesis.getInstance().getLogger().info("does not have %s".formatted(sub.permission())); Commands.sendNoPermissionError(commandSender); return true; } @@ -43,11 +42,11 @@ public class EyeCore { for (Map.Entry e : Commands.getAll().entrySet()) { if (glim.hasPermission(commandSender, e.getValue().permission())) { cmds.put(e.getKey(), e.getValue()); - cmds.put(e.getKey(), e.getValue()); } } return new ArrayList<>(cmds.keySet()); } + Subcommand sub = Commands.get(strings[0].toLowerCase()); if (sub != null && glim.hasPermission(commandSender, sub.permission())) { return sub.onTabComplete(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java index debbb36..7b85c69 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java @@ -2,6 +2,7 @@ package io.github.adrianvic.nemesiseye.policy; public enum Action { INTERACT, + INTERACTWITH, BREAK, HIT, // TODO CRAFT, @@ -9,5 +10,7 @@ public enum Action { PLACE, USE_ENCHANTMENT, GLYDE, - SPAWN + SPAWN, + ENDERMAN_ATTACK_PLAYER, + FORMBLOCK } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java index 2202797..e2a6f7a 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java @@ -1,9 +1,8 @@ package io.github.adrianvic.nemesiseye.policy; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.LivingEntity; +import org.bukkit.World; import org.bukkit.event.Event; public interface NodeHandler { - boolean check(LivingEntity entity, PolicyNode node, Action action, Event event); + boolean check(World world, PolicyNode node, Action action, Event event); } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java index 70f4a49..77db729 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java @@ -1,6 +1,7 @@ package io.github.adrianvic.nemesiseye.policy; import io.github.adrianvic.nemesiseye.policy.handlers.*; +import io.github.adrianvic.nemesiseye.policy.handlers.Boolean; import java.util.HashMap; import java.util.Map; @@ -13,10 +14,13 @@ public class NodeHandlers { handlers.put(Action.PLACE, new BePlaced()); handlers.put(Action.INTERACT, new UseItem()); handlers.put(Action.USE_ENCHANTMENT, new UseEnchantment()); - handlers.put(Action.GLYDE, new Glyde()); + handlers.put(Action.GLYDE, new Boolean()); handlers.put(Action.EQUIP, new Equip()); handlers.put(Action.SPAWN, new Spawn()); handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler + handlers.put(Action.INTERACTWITH, new InteractWith()); + handlers.put(Action.ENDERMAN_ATTACK_PLAYER, new Boolean()); + handlers.put(Action.FORMBLOCK, new FormBlock()); } public static NodeHandler get(Action type) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java index 5cb69cc..eba8457 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java @@ -1,6 +1,6 @@ package io.github.adrianvic.nemesiseye.policy; -import org.bukkit.entity.HumanEntity; +import org.bukkit.World; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; @@ -19,13 +19,13 @@ public interface Policy { nodes().add(node); } - default boolean matches(LivingEntity entity, Action action, Event event) { - if (!worlds().contains(entity.getWorld().getName())) { + default boolean matches(World world, Action action, Event event) { + if (!worlds().contains(world.getName())) { return false; } for (PolicyNode node : nodes()) { - if (node.matches(entity, action, event)) { + if (node.matches(world, action, event)) { return true; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java index 8dcbc4e..0b9e77d 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java @@ -1,8 +1,7 @@ package io.github.adrianvic.nemesiseye.policy; import io.github.adrianvic.nemesiseye.DataShifter; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.LivingEntity; +import org.bukkit.World; import org.bukkit.event.Event; import java.util.ArrayList; @@ -19,27 +18,28 @@ public record PolicyNode(List actions, List values) { List nodeActions = new ArrayList<>(); List nodeValues = new ArrayList<>(); - if (rawNode.getKey() instanceof List rawTypes && rawNode.getValue() instanceof Map rawNodeValues) { + if (rawNode.getKey() instanceof List rawTypes && (rawNode.getValue() == null || rawNode.getValue() instanceof Map)) { for (Object rawType : rawTypes) { - if (rawType instanceof String rts && !rts.isEmpty() && !(rts == null) && DataShifter.enumOrDefault(Action.class, rts, null) != null) { + if (rawType instanceof String rts && !rts.isEmpty() && DataShifter.enumOrDefault(Action.class, rts, null) != null) { nodeActions.add(DataShifter.enumOrDefault(Action.class, rts, null)); } } - Map semiParsedNodeValue = new HashMap<>(); - for (Map.Entry rawNodeValueEntries : rawNodeValues.entrySet()) { - if (rawNodeValueEntries.getKey() instanceof String stringKey) { - semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue()); - } - } - if (semiParsedNodeValue.get("values") instanceof List l) { - for (Object v : l) { - nodeValues.add(v); + if (rawNode.getValue() instanceof Map rawNodeValues) { + Map semiParsedNodeValue = new HashMap<>(); + for (Map.Entry rawNodeValueEntries : rawNodeValues.entrySet()) { + if (rawNodeValueEntries.getKey() instanceof String stringKey) { + semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue()); + } + } + + if (semiParsedNodeValue.get("values") instanceof List l) { + nodeValues.addAll(l); } } } - if (!nodeActions.isEmpty() && !nodeValues.isEmpty()) { + if (!nodeActions.isEmpty()) { PolicyNode newNode = new PolicyNode(nodeActions, nodeValues); nodes.add(newNode); } @@ -56,7 +56,7 @@ public record PolicyNode(List actions, List values) { return handlers; } - public boolean matches(LivingEntity entity, Action action, Event event) { + public boolean matches(World world, Action action, Event event) { if (!actions.contains(action)) { return false; } @@ -67,7 +67,6 @@ public record PolicyNode(List actions, List values) { return false; } - boolean result = handler.check(entity, this, action, event); - return result; + return handler.check(world, this, action, event); } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java index 8050873..4e29e18 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java @@ -34,8 +34,11 @@ public interface PolicyParser { if (rawNodes instanceof List list) { for (Object o : list) { - if (o instanceof Map map) + if (o instanceof Map map) { nodeList.add((Map) map); + } else if (o instanceof List actions) { + nodeList.add(Map.of(actions, Map.of())); + } } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java index ff9d08b..f892302 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java @@ -4,8 +4,7 @@ import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.LivingEntity; +import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.event.block.BlockPlaceEvent; @@ -13,7 +12,7 @@ import java.util.List; public class BePlaced implements NodeHandler { @Override - public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + public boolean check(World world, PolicyNode node, Action action, Event event) { if (event instanceof BlockPlaceEvent bpe) { String type = bpe.getBlock().getType().toString(); List parsedValue = DataShifter.parseValueToStringList(node.values()); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java similarity index 63% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java index 46d2b11..8b9f16c 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java @@ -3,12 +3,12 @@ package io.github.adrianvic.nemesiseye.policy.handlers; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.LivingEntity; +import org.bukkit.World; import org.bukkit.event.Event; -public class Glyde implements NodeHandler { +public class Boolean implements NodeHandler { @Override - public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + public boolean check(World world, PolicyNode node, Action action, Event event) { return true; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java index 8d5cfcf..9b95756 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java @@ -6,7 +6,7 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.entity.LivingEntity; +import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.inventory.ItemStack; @@ -16,7 +16,7 @@ public class Equip implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + public boolean check(World world, PolicyNode node, Action action, Event event) { ItemStack item = glim.getEquippedItem(event); if (!glim.isArmor(item)) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java new file mode 100644 index 0000000..56bfc45 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java @@ -0,0 +1,28 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.Event; +import org.bukkit.event.block.BlockFormEvent; + +import java.util.List; + +public class FormBlock implements NodeHandler { + @Override + public boolean check(World world, PolicyNode node, Action action, Event event) { + if (event instanceof BlockFormEvent e) { + Material material = e.getNewState().getType(); + + String type = material.toString(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/InteractWith.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/InteractWith.java new file mode 100644 index 0000000..33d35eb --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/InteractWith.java @@ -0,0 +1,30 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.Nemesis; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.List; + +public class InteractWith implements NodeHandler { + @Override + public boolean check(World world, PolicyNode node, Action action, Event event) { + if (event instanceof PlayerInteractEvent e) { + Material material = e.getMaterial(); + + String type = material.toString(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java index 938f6ee..cde4d9d 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java @@ -4,18 +4,23 @@ import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.LivingEntity; +import org.bukkit.World; import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityEvent; import java.util.List; public class Spawn implements NodeHandler { @Override - public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { - String type = entity.getType().name(); - List parsedValue = DataShifter.parseValueToStringList(node.values()); + public boolean check(World world, PolicyNode node, Action action, Event event) { + if (event instanceof EntityEvent e) { + String type = e.getEntity().getType().name(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); - return DataShifter.safeMatches(parsedValue, type); + return DataShifter.safeMatches(parsedValue, type); + } + + return false; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java index d67e9f9..f329e48 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java @@ -6,27 +6,26 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.World; import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityEvent; import org.bukkit.inventory.ItemStack; public class UseEnchantment implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { - if (entity instanceof HumanEntity e) { + public boolean check(World world, PolicyNode node, Action action, Event event) { + if (event instanceof EntityEvent entityEvent && entityEvent.getEntity() instanceof HumanEntity e) { ItemStack item = glim.getItemInMainHandHumanEntity(e); if (glim.isAir(item)) return false; if (!glim.hasItemMeta(item)) return false; if (!glim.hasAnyEnchantment(item)) return false; - boolean matches = glim.hasEnchantment(item, + return glim.hasEnchantment(item, DataShifter.parseValueToStringMap(node.values())); - - return matches; } return false; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java index ef91a98..545adfe 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java @@ -6,9 +6,10 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.World; import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityEvent; import java.util.List; @@ -17,8 +18,8 @@ public class UseItem implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { - if (entity instanceof HumanEntity e) { + public boolean check(World world, PolicyNode node, Action action, Event event) { + if (event instanceof EntityEvent entityEvent && entityEvent.getEntity() instanceof HumanEntity e) { org.bukkit.inventory.ItemStack item = glim.getItemInMainHandHumanEntity(e); if (glim.isAir(item)) return false; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java index d8323cc..1e83f2c 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java @@ -3,96 +3,10 @@ package io.github.adrianvic.nemesiseye.reflection; import io.github.adrianvic.nemesiseye.DataShifter; import org.bukkit.Bukkit; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class VersionMatcher { public VersionMatcher() {} - private record Entry(String pattern, String classSuffix) {} - - public String getVersion(String type, String serverVersion) { - if (type == null || serverVersion == null) { - return ""; - } - - Map> map = populateMap(); - List entries = map.get(type.toLowerCase()); - if (entries == null || entries.isEmpty()) { - return ""; - } - - for (Entry e : entries) { - if (DataShifter.safeMatches(e.pattern, serverVersion)) { - return e.pattern + "|" + e.classSuffix; - } - } - - List sorted = new ArrayList<>(entries); - Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern)); - - Entry oldest = sorted.get(0); - Entry newest = sorted.get(sorted.size() - 1); - - int cmpOldest = compareVersions(serverVersion, oldest.pattern); - int cmpNewest = compareVersions(serverVersion, newest.pattern); - - if (cmpOldest < 0) { - return oldest.pattern + "|" + oldest.classSuffix; - } else if (cmpNewest > 0) { - return newest.pattern + "|" + newest.classSuffix; - } - - // should not happen because we already tried all patterns - return newest.pattern + "|" + newest.classSuffix; - } - - private Map> populateMap() { - Map> map = new HashMap<>(); - - // RELEASE patterns, newest first (order does not matter for matching) - map.put("release", List.of( - new Entry("^1\\.21\\..*$", "r1_21") - )); - - // BETA patterns - map.put("beta", List.of( - new Entry("^1\\.7\\.3$", "b1_7_3") - )); - - return map; - } - - private int compareVersions(String v1, String v2) { - String clean1 = v1.replaceAll("[^0-9.]", ""); - String clean2 = v2.replaceAll("[^0-9.]", ""); - - String[] a1 = clean1.split("\\."); - String[] a2 = clean2.split("\\."); - - int len = Math.max(a1.length, a2.length); - for (int i = 0; i < len; i++) { - int n1 = i < a1.length ? parseInt(a1[i]) : 0; - int n2 = i < a2.length ? parseInt(a2[i]) : 0; - if (n1 != n2) { - return n1 - n2; - } - } - return 0; - } - - private int parseInt(String s) { - try { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - return 0; - } - } - public Glimmer loadGlim() { String rawVersion = null; try { @@ -109,52 +23,19 @@ public class VersionMatcher { } } - String matchInfo = getVersion("release", rawVersion); - if (matchInfo.isEmpty()) { - matchInfo = getVersion("beta", rawVersion); + String impl = DataShifter.safeMatches("^1\\.7\\.3$", rawVersion) + ? "b1_7_3" + : "r1_21"; + + String subPkg = impl.equals("b1_7_3") ? "v1_7_3" : "v1_21"; + Glimmer glim = tryInstantiate( + "io.github.adrianvic.nemesiseye.impl." + subPkg + "." + impl); + + if (glim == null) { + throw new IllegalStateException("Missing implementation: " + impl); } - if (matchInfo.isEmpty()) { - // Fallback to b1.7.3 if everything fails, or throw error? - // User said it supports b1.7.3 and 1.21.x. - Glimmer fallback = tryInstantiate("io.github.adrianvic.nemesiseye.impl.b1_7_3"); - if (fallback != null) return fallback; - throw new IllegalStateException("No suitable implementation found for version " + rawVersion); - } - - String[] partsInfo = matchInfo.split("\\|"); - String classSuffix = partsInfo[1]; - - Glimmer glimmer = tryInstantiate("io.github.adrianvic.nemesiseye.impl." + classSuffix); - if (glimmer != null) return glimmer; - - // Backward search for older implementations - String[] versionParts = rawVersion.split("\\."); - int major = parseInt(versionParts[0]); - int minor = versionParts.length > 1 ? parseInt(versionParts[1]) : 0; - int patch = versionParts.length > 2 ? parseInt(versionParts[2]) : 0; - - while (major >= 0) { - while (minor >= 0) { - while (patch >= 0) { - String className = String.format("io.github.adrianvic.nemesiseye.impl.r%d_%d_%d", major, minor, patch); - glimmer = tryInstantiate(className); - if (glimmer != null) return glimmer; - - className = String.format("io.github.adrianvic.nemesiseye.impl.r%d_%d", major, minor); - glimmer = tryInstantiate(className); - if (glimmer != null) return glimmer; - - patch--; - } - minor--; - patch = 20; // Search up to .20 patch of previous minor - } - major--; - minor = 30; // Search up to .30 minor of previous major - } - - throw new IllegalStateException("No suitable implementation found for version " + rawVersion); + return glim; } private Glimmer tryInstantiate(String className) { diff --git a/src/main/resources/settings.yml b/src/main/resources/settings.yml index a94dd2f..0611c04 100644 --- a/src/main/resources/settings.yml +++ b/src/main/resources/settings.yml @@ -5,18 +5,19 @@ # \_\ \_____ / /_/ /_______ /\_______ /\____|__ / # \/ \/ \/ \/ # EYE OF NEMESIS - Example config file. -# Documentation in our wiki: https://github.com/adrianvic/NemesisEye +# Documentation in our repository's /docs Policies: - - name: "Bedrock-allow-admins" - type: "permission" + # This example blocks the usage of bedrock without the server.usebedrock permissin + - name: "Bedrock-allow-admins" # No spaces here or else commands which need the name as argument will not work + type: "permission" # global | location | permission | playerName worlds: [world] - effect: ALLOW - weight: 3 + effect: ALLOW # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met) + weight: 3 # Greater weight overrides lower permissions: - "server.usebedrock" nodes: - - [BREAK, PLACE, HIT, INTERACT]: + - [PLACE]: values: - BEDROCK @@ -26,15 +27,16 @@ Policies: effect: DENY weight: 2 nodes: - - [BREAK, PLACE, HIT, INTERACT]: + - [PLACE]: values: - BEDROCK - - name: "Block-non-beta-items" # No spaces here or else commands which need the name as argument will not work - type: "location" # global | location | permission | playerName - effect: DENY # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met) + # This example blocks all non-beta (b1.7.3) items, blocks, mobs, enchantments in a rectangular area + - name: "Block-non-beta-items" + type: "location" + effect: DENY worlds: [world] - weight: 0 # Greater weight overrides lower + weight: 0 policyAllowList: false # Inverts the policy validation logic, for example a location policy will affect all players NOT inside it's location locations: worlds: [world] @@ -42,7 +44,7 @@ Policies: - corner1: { x: 2100, y: 256, z: 1400 } corner2: { x: 1000, y: -64, z: 2200 } nodes: - - [INTERACT, BREAK, HIT, PLACE]: + - [INTERACT, BREAK, HIT, PLACE, GLYDE, EQUIP, SPAWN]: values: - '.*' - [USE_ENCHANTMENT]: @@ -59,7 +61,7 @@ Policies: - corner1: { x: 2100, y: 256, z: 1400 } corner2: { x: 1000, y: -64, z: 2200 } nodes: - - [INTERACT, BREAK, HIT, PLACE]: + - [INTERACT, BREAK, HIT, PLACE, SPAWN]: values: - AIR - STONE @@ -168,4 +170,16 @@ Policies: - MUSIC_DISK_CAT - MUSIC_DISK_13 - DIRT - - BREAD \ No newline at end of file + - BREAD + - ZOMBIE + - SKELETON + - CHICKEN + - PIG + - SHEEP + - COW + - SQUID + - WOLF + - PIGZOMBIE + - SPIDER + - GHAST + - CREEPER \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java deleted file mode 100644 index ea64591..0000000 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl; - -import io.github.adrianvic.nemesiseye.Events; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntitySpawnEvent; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerMoveEvent; - -public class EventListener implements Listener { - @EventHandler - public void onBlockBreak(BlockBreakEvent event) { - Events.onBlockBreak(event); - } - - @EventHandler - public void onInteractionEvent(PlayerInteractEvent event) { - Events.onInteractionEvent(event); - } - - @EventHandler - public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { - Events.onEntityDamageByEntityEvent(event); - } - - @EventHandler - public void onBlockPlaceEvent(BlockPlaceEvent event) { - Events.onBlockPlaceEvent(event); - } - - @EventHandler - public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event); } - - @EventHandler - public void onCreatureSpawnEvent(CreatureSpawnEvent event) { Events.onCreatureSpawnEvent(event); } -} \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java deleted file mode 100644 index e28b412..0000000 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl.commands; - -import io.github.adrianvic.nemesiseye.commands.EyeCore; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -public class Eye implements CommandExecutor, TabCompleter { - private final EyeCore core; - - public Eye() { - core = new EyeCore(); - } - - @Override - public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { - return core.onCommand(commandSender, command, s, strings); - } - - @Override - public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { - return core.onTabComplete(commandSender, command, s, strings); - } -} \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java deleted file mode 100644 index ac1b6d7..0000000 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java +++ /dev/null @@ -1,201 +0,0 @@ -package io.github.adrianvic.nemesiseye.impl; - -import io.github.adrianvic.nemesiseye.DataShifter; -import io.github.adrianvic.nemesiseye.Nemesis; -import io.github.adrianvic.nemesiseye.impl.commands.Eye; -import io.github.adrianvic.nemesiseye.policy.Policy; -import io.github.adrianvic.nemesiseye.policy.PolicyParsers; -import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.PluginManager; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@SuppressWarnings("unused") -public class r1_21 implements Glimmer { - @Override - public File loadConfigFile() { - File file = new File(Nemesis.getInstance().getDataFolder(), "settings.yml"); - - if (!file.exists()) - Nemesis.getInstance().saveResource("settings.yml", false); - - return file; - } - - @Override - public List loadPoliciesFromFile(File file) { - YamlConfiguration config = new YamlConfiguration(); - config.options().parseComments(true); - - try { - config.load(file); - } catch (Exception e) { - e.printStackTrace(); - } - - List> rawPolicies = config.getMapList("Policies"); - List allPolicies = new ArrayList<>(); - - for (Map policyMap : rawPolicies) { - if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) { - allPolicies.add(PolicyParsers.get(type).parse(policyMap)); - } - } - - return allPolicies; - } - - @Override - public void onLoad() { - PluginManager pm = Nemesis.getInstance().getPluginManager(); - Nemesis.getInstance().getCommand("eye").setExecutor(new Eye()); - pm.registerEvents(new EventListener(), Nemesis.getInstance()); - } - - @Override - public ItemStack getItemInMainHandHumanEntity(HumanEntity entity) { - return entity.getInventory().getItemInMainHand(); - } - - @Override - public boolean isAir(ItemStack item) { - return item == null || item.getType().isAir(); - } - - @Override - public boolean isGliding(org.bukkit.entity.Player player) { - return player.isGliding(); - } - - @Override - public void setGliding(org.bukkit.entity.Player player, boolean gliding) { - player.setGliding(gliding); - } - - @Override - public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) { - return sender.hasPermission(permission); - } - - @Override - public boolean isArmorEquipAttempt(org.bukkit.event.Event event) { - if (!(event instanceof org.bukkit.event.inventory.InventoryClickEvent e)) { - return false; - } - - if (e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) { - return true; - } - - if (e.isShiftClick()) { - return isArmor(e.getCurrentItem()); - } - - if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY - && e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR - && e.getWhoClicked() instanceof org.bukkit.entity.Player player) { - return isArmor( - player.getInventory().getItem(e.getHotbarButton()) - ); - } - - return false; - } - - @Override - public ItemStack getEquippedItem(org.bukkit.event.Event event) { - if (event instanceof org.bukkit.event.inventory.InventoryClickEvent e) { - org.bukkit.event.inventory.InventoryType.SlotType slotType = e.getSlotType(); - - if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY // hotbar key swap - && slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR - && e.getWhoClicked() instanceof org.bukkit.entity.Player player) { - return player.getInventory().getItem(e.getHotbarButton()); - } - - if (e.isShiftClick()) { - ItemStack current = e.getCurrentItem(); - if (isArmor(current)) return current; - } - - // regular click - if (slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) { - ItemStack cursor = e.getCursor(); - if (isArmor(cursor)) return cursor; - } - } - - // Try Paper's PlayerArmorChangeEvent via reflection or just check if class exists - try { - if (event instanceof com.destroystokyo.paper.event.player.PlayerArmorChangeEvent e) { - return e.getNewItem(); - } - } catch (NoClassDefFoundError | Exception ignored) {} - - return null; - } - - @Override - public void sendMessage(CommandSender commandSender, String text) { - commandSender.sendMessage(text); - } - - @Override - public boolean hasItemMeta(ItemStack item) { - return item.getItemMeta() != null; - } - - @Override - public List getWorlds() { - return Bukkit.getWorlds(); - } - - @Override - public boolean hasEnchantment(ItemStack item, Map valuesmap) { - Map enchantments = item.getEnchantments(); - - for (Map.Entry ench : enchantments.entrySet()) { - String enchKey = ench.getKey().getKey().getKey(); - String enchLevel = ench.getValue().toString(); - - for (Map.Entry rule : valuesmap.entrySet()) { - if ( - DataShifter.safeMatches(rule.getKey(), enchKey) && - DataShifter.safeMatches(rule.getValue(), enchLevel) - ) { - return true; - } - } - } - return false; - } - - public boolean hasAnyEnchantment(ItemStack item) { - return !(item.getItemMeta().getEnchants().isEmpty()); - } - - @Override - public boolean isArmor(ItemStack item) { - if (item == null || item.getType().isAir()) { - return false; - } - - String name = item.getType().name(); - - return name.endsWith("_HELMET") - || name.endsWith("_CHESTPLATE") - || name.endsWith("_LEGGINGS") - || name.endsWith("_BOOTS") - || item.getType() == org.bukkit.Material.ELYTRA; - } -} diff --git a/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java b/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java index fe4ef52..bd00215 100644 --- a/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java +++ b/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java @@ -4,16 +4,11 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.entity.Player; +import org.bukkit.World; import org.bukkit.event.Event; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class ValidatorTest { @@ -28,12 +23,12 @@ public class ValidatorTest { @Test void testValidatorCanDeny() { - Player player = mock(Player.class); + World world = mock(World.class); Event event = mock(Event.class); Policy policy = mock(Policy.class); - when(policy.applies(player)).thenReturn(true); - when(policy.matches(player, Action.BREAK, event)).thenReturn(true); +// when(policy.applies(world)).thenReturn(true); + when(policy.matches(world, Action.BREAK, event)).thenReturn(true); when(policy.effect()).thenReturn(Effect.DENY); // We need to handle the static Config.getInstance() diff --git a/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java b/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java index 763bf53..74379c8 100644 --- a/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java +++ b/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java @@ -2,6 +2,8 @@ package io.github.adrianvic.nemesiseye.impl; import be.seeseemelk.mockbukkit.MockBukkit; import be.seeseemelk.mockbukkit.ServerMock; +import io.github.adrianvic.nemesiseye.impl.v1_21.r1_21; +import io.github.adrianvic.nemesiseye.impl.v1_7_3.b1_7_3; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; From 21636595fb3d1fff40c858f3e6ebc9c507c04140 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 16 Jun 2026 19:20:25 -0300 Subject: [PATCH 10/12] Update supported versions in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf357cf..0871654 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ I made this plugin as an effort to preserve a village from my private server. Or Since version 1.0.3-SNAPSHOT, Eye of Nemesis has _reflection_, a technique that allows me to target multiple versions of the game while sharing the codebase across versions. Currently, we support the following Minecraft versions/loaders: -- **PaperMC** `1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10` -- **Bukkit** `b1.7.3 (CB1060)` +- **PaperMC** `1.21.X, 26.1.X, 26.2` +- **Project Poseidon/Bukkit** `b1.7.3 (CB1060)` ## Performance This plugin is not scalable as it is and will end up running unoptimized checks when your players do things with policies in effect, I made it for a server with a few friends. I'll look forward into writing more performant code after all my other priorities are implemented. From d6e65cda12f3635c0227e834d38f0cba76a71d4d Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 16 Jun 2026 20:52:24 -0300 Subject: [PATCH 11/12] Divide modern and beta-safe event handling to avoid class not found errors. Fix compatibility with Poseidon. --- .../nemesiseye/impl/v1_7_3/b1_7_3.java | 9 ++++++ .../github/adrianvic/nemesiseye/Events.java | 25 --------------- .../adrianvic/nemesiseye/ModernEvents.java | 32 +++++++++++++++++++ .../adrianvic/nemesiseye/Validator.java | 5 ++- .../nemesiseye/policy/NodeHandlers.java | 14 +++++--- .../nemesiseye/reflection/Glimmer.java | 5 +++ .../nemesiseye/impl/v1_21/EventListener.java | 8 ++--- .../nemesiseye/impl/v1_21/r1_21.java | 12 +++++++ 8 files changed, 76 insertions(+), 34 deletions(-) create mode 100644 src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/v1_7_3/b1_7_3.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/v1_7_3/b1_7_3.java index 0129a81..97ad260 100644 --- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/v1_7_3/b1_7_3.java +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/v1_7_3/b1_7_3.java @@ -5,6 +5,8 @@ import io.github.adrianvic.nemesiseye.impl.v1_7_3.commands.Eye; import io.github.adrianvic.nemesiseye.impl.v1_7_3.events.BlockEventListener; import io.github.adrianvic.nemesiseye.impl.v1_7_3.events.EntityEventListener; import io.github.adrianvic.nemesiseye.impl.v1_7_3.events.PlayerEventListener; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyParsers; import io.github.adrianvic.nemesiseye.reflection.Glimmer; @@ -19,6 +21,7 @@ import org.bukkit.plugin.java.JavaPlugin; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,6 +70,7 @@ public class b1_7_3 implements Glimmer { pm = Nemesis.getInstance().getPluginManager(); pm.registerEvent(Event.Type.ENTITY_DAMAGE, new EntityEventListener(), Event.Priority.Normal, plugin); pm.registerEvent(Event.Type.BLOCK_BREAK, new BlockEventListener(), Event.Priority.Normal, plugin); + pm.registerEvent(Event.Type.BLOCK_PLACE, new BlockEventListener(), Event.Priority.Normal, plugin); pm.registerEvent(Event.Type.PLAYER_INTERACT, new PlayerEventListener(), Event.Priority.Normal, plugin); plugin.getCommand("eye").setExecutor(new Eye()); } @@ -108,6 +112,11 @@ public class b1_7_3 implements Glimmer { return null; } + @Override + public Map getExtraHandlers() { + return new HashMap<>(); + } + @Override public void sendMessage(CommandSender commandSender, String text) { String[] lines = text.split("\\r?\\n"); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Events.java b/src/main/java/io/github/adrianvic/nemesiseye/Events.java index bb857e2..c452cd6 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Events.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Events.java @@ -1,16 +1,12 @@ package io.github.adrianvic.nemesiseye; -import com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; @@ -82,28 +78,7 @@ public class Events { } } - public static void onInventoryClickEvent(InventoryClickEvent event) { - if (!g().isArmorEquipAttempt(event)) { - return; - } - - HumanEntity entity = event.getWhoClicked(); - - if (!Validator.can(entity, Action.EQUIP, event)) { - event.setCancelled(true); - } - } - public static void onCreatureSpawnEvent(CreatureSpawnEvent event) { event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event)); } - - public static void onEndermanAttackPlayerEvent(EndermanAttackPlayerEvent event) { - event.setCancelled(!Validator.can(event.getEntity(), Action.ENDERMAN_ATTACK_PLAYER, event)); - } - - public static void onBlockFormEvent(BlockFormEvent event) { - boolean canceled = !Validator.can(event.getBlock().getWorld(), Action.FORMBLOCK, event); - event.setCancelled(canceled); - } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java b/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java new file mode 100644 index 0000000..7208703 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java @@ -0,0 +1,32 @@ +package io.github.adrianvic.nemesiseye; + +import com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent; +import io.github.adrianvic.nemesiseye.policy.Action; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.inventory.InventoryClickEvent; + +public class ModernEvents { + public static void onInventoryClickEvent(InventoryClickEvent event) { + if (!Nemesis.getInstance().getGlimmer().isArmorEquipAttempt(event)) { + return; + } + + HumanEntity entity = event.getWhoClicked(); + + if (!Validator.can(entity, Action.EQUIP, event)) { + event.setCancelled(true); + } + } + + public static void onEndermanAttackPlayerEvent(EndermanAttackPlayerEvent event) { + if (!Validator.can(event.getEntity(), Action.ENDERMAN_ATTACK_PLAYER, event)) { + event.setCancelled(true); + } + } + + public static void onBlockFormEvent(BlockFormEvent event) { + boolean canceled = !Validator.can(event.getBlock().getWorld(), Action.FORMBLOCK, event); + event.setCancelled(canceled); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java index 1e3e020..41c1531 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java @@ -41,6 +41,8 @@ public class Validator { } public static boolean can(World world, Action action, Event event) { + boolean allowed = true; + for (GlobalPolicy policy : getGlobalPolicies()) { boolean matches = policy.matches(world, action, event); @@ -51,10 +53,11 @@ public class Validator { case DENY: if (matches) return false; + break; } } - return true; + return allowed; } public static List getGlobalPolicies() { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java index 77db729..bef30f1 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java @@ -1,5 +1,6 @@ package io.github.adrianvic.nemesiseye.policy; +import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.policy.handlers.*; import io.github.adrianvic.nemesiseye.policy.handlers.Boolean; @@ -8,7 +9,7 @@ import java.util.Map; public class NodeHandlers { private static final Map handlers = new HashMap<>(); - + static { handlers.put(Action.HIT, new UseItem()); handlers.put(Action.PLACE, new BePlaced()); @@ -20,10 +21,15 @@ public class NodeHandlers { handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler handlers.put(Action.INTERACTWITH, new InteractWith()); handlers.put(Action.ENDERMAN_ATTACK_PLAYER, new Boolean()); - handlers.put(Action.FORMBLOCK, new FormBlock()); } - + public static NodeHandler get(Action type) { - return handlers.get(type); + NodeHandler handler = handlers.get(type); + + if (handler == null) { + handler = Nemesis.getInstance().getGlimmer().getExtraHandlers().get(type); + } + + return handler; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java index ccc69b4..a8ba767 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java @@ -1,5 +1,7 @@ package io.github.adrianvic.nemesiseye.reflection; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.Policy; import org.bukkit.Location; import org.bukkit.World; @@ -39,6 +41,9 @@ public interface Glimmer { boolean isArmorEquipAttempt(org.bukkit.event.Event event); ItemStack getEquippedItem(org.bukkit.event.Event event); + // Handlers + Map getExtraHandlers(); + // Commands void sendMessage(CommandSender commandSender, String text); diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java index 7718061..a096dd1 100644 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java @@ -1,6 +1,7 @@ package io.github.adrianvic.nemesiseye.impl.v1_21; import io.github.adrianvic.nemesiseye.Events; +import io.github.adrianvic.nemesiseye.ModernEvents; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -9,7 +10,6 @@ import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntitySpawnEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; @@ -39,18 +39,18 @@ public class EventListener implements Listener { public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event); } + public void onInventoryClickEvent(InventoryClickEvent event) { ModernEvents.onInventoryClickEvent(event); } @EventHandler public void onCreatureSpawnEvent(CreatureSpawnEvent event) { Events.onCreatureSpawnEvent(event); } @EventHandler public void onEndermanAttackPlayerEvent(com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event) { - Events.onEndermanAttackPlayerEvent(event); + ModernEvents.onEndermanAttackPlayerEvent(event); } @EventHandler public void onBlockFormEvent(BlockFormEvent event) { - Events.onBlockFormEvent(event); + ModernEvents.onBlockFormEvent(event); } } \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/r1_21.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/r1_21.java index f135aa9..edd5a60 100644 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/r1_21.java +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/r1_21.java @@ -3,8 +3,11 @@ package io.github.adrianvic.nemesiseye.impl.v1_21; import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.impl.v1_21.commands.Eye; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.PolicyParsers; +import io.github.adrianvic.nemesiseye.policy.handlers.FormBlock; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.Bukkit; import org.bukkit.World; @@ -17,6 +20,7 @@ import org.bukkit.plugin.PluginManager; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -147,6 +151,13 @@ public class r1_21 implements Glimmer { return null; } + @Override + public Map getExtraHandlers() { + Map handlers = new HashMap<>(); + handlers.put(Action.FORMBLOCK, new FormBlock()); + return handlers; + } + @Override public void sendMessage(CommandSender commandSender, String text) { commandSender.sendMessage(text); @@ -182,6 +193,7 @@ public class r1_21 implements Glimmer { return false; } + @Override public boolean hasAnyEnchantment(ItemStack item) { return !(item.getItemMeta().getEnchants().isEmpty()); } From 1fbc200936b2a66f6b70740ba53caa3f3917c7e7 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Wed, 17 Jun 2026 17:21:41 -0300 Subject: [PATCH 12/12] Add TAKE_LECTERN_BOOK action. --- docs/Nodes.md | 4 +++- .../java/io/github/adrianvic/nemesiseye/ModernEvents.java | 7 +++++++ .../java/io/github/adrianvic/nemesiseye/policy/Action.java | 3 ++- .../github/adrianvic/nemesiseye/policy/NodeHandlers.java | 3 ++- .../nemesiseye/policy/handlers/{ => generic}/Boolean.java | 2 +- .../adrianvic/nemesiseye/impl/v1_21/EventListener.java | 4 ++++ 6 files changed, 19 insertions(+), 4 deletions(-) rename src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/{ => generic}/Boolean.java (86%) diff --git a/docs/Nodes.md b/docs/Nodes.md index d5a88bf..d5b475b 100644 --- a/docs/Nodes.md +++ b/docs/Nodes.md @@ -36,6 +36,8 @@ Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true w ### FORMBLOCK Triggered on `BlockFormEvent` (ex. triggers when snow is formed from rain), returns true when the block being formed is in the provided list of materials. -## ENDERMAN_ATTACK_PLAYER +### ENDERMAN_ATTACK_PLAYER Triggered on `EndermanAttackPlayerEvent` (from actions other than hitting the Enderman), returns true. +### TAKE_LECTURN_BOOK +Triggered on `PlayerTakeLecturnBookEvent`, returns true. \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java b/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java index 7208703..e93f7c8 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/ModernEvents.java @@ -5,6 +5,7 @@ import io.github.adrianvic.nemesiseye.policy.Action; import org.bukkit.entity.HumanEntity; import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerTakeLecternBookEvent; public class ModernEvents { public static void onInventoryClickEvent(InventoryClickEvent event) { @@ -29,4 +30,10 @@ public class ModernEvents { boolean canceled = !Validator.can(event.getBlock().getWorld(), Action.FORMBLOCK, event); event.setCancelled(canceled); } + + public static void onPlayerTakeLecternBookEvent(PlayerTakeLecternBookEvent event) { + if (!Validator.can(event.getPlayer().getWorld(), Action.TAKE_LECTERN_BOOK, event)) { + event.setCancelled(true); + } + } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java index 7b85c69..ca63c96 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java @@ -12,5 +12,6 @@ public enum Action { GLYDE, SPAWN, ENDERMAN_ATTACK_PLAYER, - FORMBLOCK + FORMBLOCK, + TAKE_LECTERN_BOOK } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java index bef30f1..2bc4f0d 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java @@ -2,7 +2,7 @@ package io.github.adrianvic.nemesiseye.policy; import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.policy.handlers.*; -import io.github.adrianvic.nemesiseye.policy.handlers.Boolean; +import io.github.adrianvic.nemesiseye.policy.handlers.generic.Boolean; import java.util.HashMap; import java.util.Map; @@ -21,6 +21,7 @@ public class NodeHandlers { handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler handlers.put(Action.INTERACTWITH, new InteractWith()); handlers.put(Action.ENDERMAN_ATTACK_PLAYER, new Boolean()); + handlers.put(Action.TAKE_LECTERN_BOOK, new Boolean()); } public static NodeHandler get(Action type) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/generic/Boolean.java similarity index 86% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/generic/Boolean.java index 8b9f16c..2b09322 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/generic/Boolean.java @@ -1,4 +1,4 @@ -package io.github.adrianvic.nemesiseye.policy.handlers; +package io.github.adrianvic.nemesiseye.policy.handlers.generic; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.NodeHandler; diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java index a096dd1..e7a7d8e 100644 --- a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/v1_21/EventListener.java @@ -13,6 +13,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTakeLecternBookEvent; public class EventListener implements Listener { @EventHandler @@ -53,4 +54,7 @@ public class EventListener implements Listener { public void onBlockFormEvent(BlockFormEvent event) { ModernEvents.onBlockFormEvent(event); } + + @EventHandler + public void onPlayerTakeLecternBook(PlayerTakeLecternBookEvent event) { ModernEvents.onPlayerTakeLecternBookEvent(event); } } \ No newline at end of file