Compare commits

..

10 commits

Author SHA1 Message Date
aa1a17c8fd Atualizar .forgejo/workflows/release-build.yml 2026-05-20 22:48:23 -03:00
0de8990d91 Atualizar .forgejo/workflows/build.yml 2026-05-20 22:47:42 -03:00
fe35fc0a41 Atualizar README.md 2026-05-20 22:45:45 -03:00
da27a0a40c Merge remote-tracking branch 'origin/main'
Some checks failed
Build / build (push) Has been cancelled
2026-05-20 18:14:59 -03:00
76f13a2412 Move version-specific code to reflection implementation and add basic testing. 2026-05-20 18:13:16 -03:00
b044052b6d Move isArmor to reflection implementations and refactor NodeHandler replace use of safeMatches(String expression, String against) usage with safeMatches(List<String> expressions, String against). 2026-05-20 15:54:14 -03:00
af6ac27960 Refactor codebase to use LivingEntity, add SPAWN action and handler, add DataShifter.parseValueToStringList overload that iterates over a list of strings. 2026-05-20 15:38:45 -03:00
4b5ee42547 Atualizar .github/workflows/release-build.yml
Some checks failed
Build / build (push) Has been cancelled
2026-05-19 15:49:04 -03:00
7aadf220f5 Split github and forgejo workflows.
Some checks are pending
Build / build (push) Waiting to run
2026-05-19 15:43:35 -03:00
a8bbf78a3c Merge pull request '1.0.6-SNAPSHOT' (#1) from 1.0.6-SNAPSHOT into main
Some checks failed
Build / build (push) Has been cancelled
Release Build / build-and-release (release) Failing after 8s
Reviewed-on: #1
2026-05-19 15:36:37 -03:00
39 changed files with 596 additions and 244 deletions

View file

@ -0,0 +1,43 @@
name: Build
on:
push:
paths:
- 'src/**'
- '.github/**'
- 'build.gradle.kts'
- 'gradle.properties'
- 'settings.gradle'
jobs:
build:
runs-on: arch-linux
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

View file

@ -0,0 +1,40 @@
name: Release Build
on:
release:
types: [published]
jobs:
build-and-release:
runs-on: arch-linux
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 }}

View file

@ -12,7 +12,7 @@ on:
jobs: jobs:
build: build:
runs-on: "arch-linux" runs-on: ubuntu-latest
env: env:
NEMESIS_VERSION_NAME: "autobuild-${{ github.sha }}" NEMESIS_VERSION_NAME: "autobuild-${{ github.sha }}"
NEMESIS_BUILD_CHANNEL: "autobuild" NEMESIS_BUILD_CHANNEL: "autobuild"

View file

@ -6,7 +6,7 @@ on:
jobs: jobs:
build-and-release: build-and-release:
runs-on: "arch-linux" runs-on: ubuntu-latest
env: env:
NEMESIS_VERSION_NAME: "${{ github.ref_name }}" NEMESIS_VERSION_NAME: "${{ github.ref_name }}"
NEMESIS_BUILD_CHANNEL: "production" NEMESIS_BUILD_CHANNEL: "production"
@ -32,9 +32,10 @@ jobs:
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew buildAll run: ./gradlew buildAll
- name: Upload JARs to release - name: Upload artifacts to release
uses: softprops/action-gh-release@v2.5.0 run: |
with: curl -X POST \
files: build/libs/*.jar -H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
tag_name: ${{ github.ref_name }} -H "Content-Type: application/octet-stream" \
name: ${{ github.ref_name }} 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

View file

@ -1,4 +1,4 @@
<img width="1280" height="640" alt="eye_of_nemesis_social_cover" src="https://github.com/user-attachments/assets/a24bc92f-4dc2-4594-93c9-d056130f9695" /> <img alt="eye_of_nemesis_social_cover" src="https://github.com/user-attachments/assets/a24bc92f-4dc2-4594-93c9-d056130f9695" />
[![Modrinth Badge](https://img.shields.io/badge/Modrinth-Black?style=social&logo=Modrinth&logoColor=green)](https://modrinth.com/plugin/eye-of-nemesis) [![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) [![English Wiki Badge](https://img.shields.io/badge/English-White?style=flat-square&label=Wiki&color=black)](https://github.com/adrianvic/NemesisEye/wiki)

View file

@ -59,9 +59,24 @@ mcVersions.forEach { ver ->
/* ----------------------------------------- */ /* ----------------------------------------- */
dependencies { dependencies {
add("compileOnly", "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.10-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")) 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()
} }
/* ----------------------------------------- */ /* ----------------------------------------- */

View file

@ -2,9 +2,13 @@ package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.impl.commands.Eye; 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.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers; import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
@ -72,6 +76,38 @@ public class b1_7_3 implements Glimmer {
return entity.getItemInHand(); 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 @Override
public void sendMessage(CommandSender commandSender, String text) { public void sendMessage(CommandSender commandSender, String text) {
String[] lines = text.split("\\r?\\n"); String[] lines = text.split("\\r?\\n");
@ -99,4 +135,18 @@ public class b1_7_3 implements Glimmer {
public boolean hasAnyEnchantment(ItemStack itemStack) { public boolean hasAnyEnchantment(ItemStack itemStack) {
return false; 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");
}
} }

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl; package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events; import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl; package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events; import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl; package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events; import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;

View file

@ -7,13 +7,22 @@ import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class DataShifter { public class DataShifter {
public static boolean safeMatches(String expression, String against) { public static boolean safeMatches(String expression, String against) {
String cleanPattern = expression.trim(); String cleanPattern = expression.trim();
Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE); Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE);
return pattern.matcher(against).matches(); return pattern.matcher(against).matches();
} }
public static boolean safeMatches(List<String> expressions, String against) {
for (String s : expressions) {
if (DataShifter.safeMatches(s, against)) {
return true;
}
}
return false;
}
public static List<String> parseValueToStringList(List<Object> values) { public static List<String> parseValueToStringList(List<Object> values) {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
for (Object o : values) { for (Object o : values) {

View file

@ -1,15 +1,14 @@
package io.github.adrianvic.nemesiseye; package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.policy.Action; 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.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -18,6 +17,10 @@ import java.util.List;
public class Events { public class Events {
private static Glimmer g() {
return Nemesis.getInstance().getGlimmer();
}
public static void onBlockBreak(BlockBreakEvent event) { public static void onBlockBreak(BlockBreakEvent event) {
event.setCancelled( event.setCancelled(
!Validator.can( !Validator.can(
@ -31,12 +34,12 @@ public class Events {
public static void onInteractionEvent(PlayerInteractEvent event) { public static void onInteractionEvent(PlayerInteractEvent event) {
ItemStack item = event.getItem(); ItemStack item = event.getItem();
if (item == null || item.getType().isAir()) { if (g().isAir(item)) {
return; return;
} }
// Right-click armor equipping // Right-click armor equipping
if (isArmor(item) if (g().isArmor(item)
&& !Validator.can(event.getPlayer(), Action.EQUIP, event)) { && !Validator.can(event.getPlayer(), Action.EQUIP, event)) {
event.setCancelled(true); event.setCancelled(true);
return; return;
@ -67,18 +70,18 @@ public class Events {
} }
public static void onPlayerMoveEvent(PlayerMoveEvent event) { public static void onPlayerMoveEvent(PlayerMoveEvent event) {
if (event.getPlayer().isGliding() if (g().isGliding(event.getPlayer())
&& !Validator.can( && !Validator.can(
event.getPlayer(), event.getPlayer(),
List.of(Action.GLYDE), List.of(Action.GLYDE),
event event
)) { )) {
event.getPlayer().setGliding(false); g().setGliding(event.getPlayer(), false);
} }
} }
public static void onInventoryClickEvent(InventoryClickEvent event) { public static void onInventoryClickEvent(InventoryClickEvent event) {
if (!isArmorEquipAttempt(event)) { if (!g().isArmorEquipAttempt(event)) {
return; return;
} }
@ -89,38 +92,7 @@ public class Events {
} }
} }
private static boolean isArmorEquipAttempt(InventoryClickEvent event) { public static void onCreatureSpawnEvent(CreatureSpawnEvent event) {
if (event.getSlotType() == InventoryType.SlotType.ARMOR) { event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event));
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;
} }
} }

View file

@ -3,6 +3,7 @@ package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.Policy;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import java.util.ArrayList; import java.util.ArrayList;
@ -19,7 +20,7 @@ public class Validator {
return true; 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 restricted = false;
boolean allowed = false; boolean allowed = false;
@ -41,7 +42,7 @@ public class Validator {
} }
public static List<Policy> getPoliciesForEntity(HumanEntity entity) { public static List<Policy> getPoliciesForEntity(LivingEntity entity) {
List<Policy> ps = Config.getInstance().getPolicies(); List<Policy> ps = Config.getInstance().getPolicies();
List<Policy> result = new ArrayList<>(); List<Policy> result = new ArrayList<>();

View file

@ -26,7 +26,7 @@ public class EyeCore {
commandSender.sendMessage("Unknown command, try '/eye help' to list available commands."); commandSender.sendMessage("Unknown command, try '/eye help' to list available commands.");
return true; 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)); return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length));
} else { } else {
// Nemesis.getInstance().getLogger().info("does not have %s".formatted(sub.permission())); // Nemesis.getInstance().getLogger().info("does not have %s".formatted(sub.permission()));
@ -41,7 +41,7 @@ public class EyeCore {
if (strings.length == 1) { if (strings.length == 1) {
Map<String, Subcommand> cmds = new HashMap<>(); Map<String, Subcommand> cmds = new HashMap<>();
for (Map.Entry<String, Subcommand> e : Commands.getAll().entrySet()) { for (Map.Entry<String, Subcommand> 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());
cmds.put(e.getKey(), e.getValue()); cmds.put(e.getKey(), e.getValue());
} }
@ -49,7 +49,7 @@ public class EyeCore {
return new ArrayList<>(cmds.keySet()); return new ArrayList<>(cmds.keySet());
} }
Subcommand sub = Commands.get(strings[0].toLowerCase()); 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 sub.onTabComplete(commandSender, Arrays.copyOfRange(strings, 1, strings.length));
} }
return List.of(); return List.of();

View file

@ -1,5 +1,6 @@
package io.github.adrianvic.nemesiseye.commands.sub; package io.github.adrianvic.nemesiseye.commands.sub;
import io.github.adrianvic.nemesiseye.Nemesis;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permission; import org.bukkit.permissions.Permission;
@ -13,6 +14,6 @@ public interface Subcommand {
List<String> onTabComplete(CommandSender sender, String[] strings); List<String> onTabComplete(CommandSender sender, String[] strings);
String permission(); String permission();
default boolean hasPermission(CommandSender sender) { default boolean hasPermission(CommandSender sender) {
return sender.hasPermission(permission()); return Nemesis.getInstance().getGlimmer().hasPermission(sender, permission());
} }
} }

View file

@ -8,5 +8,6 @@ public enum Action {
EQUIP, EQUIP,
PLACE, PLACE,
USE_ENCHANTMENT, USE_ENCHANTMENT,
GLYDE GLYDE,
SPAWN
} }

View file

@ -1,8 +1,9 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
public interface NodeHandler { public interface NodeHandler {
boolean check(HumanEntity entity, PolicyNode node, Action action, Event event); boolean check(LivingEntity entity, PolicyNode node, Action action, Event event);
} }

View file

@ -9,13 +9,14 @@ public class NodeHandlers {
private static final Map<Action, NodeHandler> handlers = new HashMap<>(); private static final Map<Action, NodeHandler> handlers = new HashMap<>();
static { static {
handlers.put(Action.HIT, new useItem()); handlers.put(Action.HIT, new UseItem());
handlers.put(Action.PLACE, new bePlaced()); handlers.put(Action.PLACE, new BePlaced());
handlers.put(Action.INTERACT, new useItem()); handlers.put(Action.INTERACT, new UseItem());
handlers.put(Action.USE_ENCHANTMENT, new useEnchantment()); handlers.put(Action.USE_ENCHANTMENT, new UseEnchantment());
handlers.put(Action.GLYDE, new glyde()); handlers.put(Action.GLYDE, new Glyde());
handlers.put(Action.EQUIP, new equip()); handlers.put(Action.EQUIP, new Equip());
handlers.put(Action.BREAK, new useItem()); // TODO: implement place handler handlers.put(Action.SPAWN, new Spawn());
handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler
} }
public static NodeHandler get(Action type) { public static NodeHandler get(Action type) {

View file

@ -1,6 +1,7 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import java.util.List; import java.util.List;
@ -9,7 +10,7 @@ public interface Policy {
String name(); String name();
List<PolicyNode> nodes(); List<PolicyNode> nodes();
boolean policyAllowList(); boolean policyAllowList();
boolean applies(HumanEntity entity); boolean applies(LivingEntity entity);
Effect effect(); Effect effect();
int weight(); int weight();
List<String> worlds(); List<String> worlds();
@ -18,7 +19,7 @@ public interface Policy {
nodes().add(node); 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())) { if (!worlds().contains(entity.getWorld().getName())) {
return false; return false;
} }

View file

@ -2,6 +2,7 @@ package io.github.adrianvic.nemesiseye.policy;
import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.DataShifter;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import java.util.ArrayList; import java.util.ArrayList;
@ -55,7 +56,7 @@ public record PolicyNode(List<Action> actions, List<Object> values) {
return handlers; return handlers;
} }
public boolean matches(HumanEntity entity, Action action, Event event) { public boolean matches(LivingEntity entity, Action action, Event event) {
if (!actions.contains(action)) { if (!actions.contains(action)) {
return false; return false;
} }

View file

@ -5,21 +5,22 @@ import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
public class bePlaced implements NodeHandler { import java.util.List;
public class BePlaced implements NodeHandler {
@Override @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) { if (event instanceof BlockPlaceEvent bpe) {
String type = bpe.getBlock().getType().toString(); String type = bpe.getBlock().getType().toString();
List<String> parsedValue = DataShifter.parseValueToStringList(node.values());
for (String s : DataShifter.parseValueToStringList(node.values())) { return DataShifter.safeMatches(parsedValue, type);
if (DataShifter.safeMatches(s, type)) {
return true;
}
}
} }
return false; return false;
} }
} }

View file

@ -6,23 +6,26 @@ import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; 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.event.Event;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
public class useEnchantment implements NodeHandler { import java.util.List;
public class Equip implements NodeHandler {
private final Glimmer glim = Nemesis.getInstance().getGlimmer(); private final Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override @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 = glim.getItemInMainHandHumanEntity(entity); ItemStack item = glim.getEquippedItem(event);
if (!glim.hasItemMeta(item)) return false; if (!glim.isArmor(item)) {
if (!glim.hasAnyEnchantment(item)) return false; return false;
}
boolean matches = glim.hasEnchantment(item, String type = item.getType().name();
DataShifter.parseValueToStringMap(node.values())); List<String> parsedValue = DataShifter.parseValueToStringList(node.values());
return matches; return DataShifter.safeMatches(parsedValue, type);
} }
} }

View file

@ -3,12 +3,12 @@ package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; 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.Event;
public class glyde implements NodeHandler { public class Glyde implements NodeHandler {
@Override @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; return true;
} }
} }

View file

@ -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<String> parsedValue = DataShifter.parseValueToStringList(node.values());
return DataShifter.safeMatches(parsedValue, type);
}
}

View file

@ -0,0 +1,34 @@
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.isAir(item)) return false;
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;
}
}

View file

@ -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 java.util.List;
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) {
org.bukkit.inventory.ItemStack item = glim.getItemInMainHandHumanEntity(e);
if (glim.isAir(item)) return false;
String type = item.getType().toString();
List<String> parsedValue = DataShifter.parseValueToStringList(node.values());
return DataShifter.safeMatches(parsedValue, type);
}
return false;
}
}

View file

@ -1,84 +0,0 @@
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.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.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;
public class equip implements NodeHandler {
@Override
public boolean check(HumanEntity 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
&& 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)) {
item = current;
}
}
// Cursor click onto armor slot
else if (slotType == InventoryType.SlotType.ARMOR) {
ItemStack cursor = e.getCursor();
if (isArmor(cursor)) {
item = cursor;
}
}
}
if (!isArmor(item)) {
return false;
}
String type = item.getType().name();
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;
}
}

View file

@ -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;
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();
for (String s : DataShifter.parseValueToStringList(node.values())) {
if (DataShifter.safeMatches(s, type)) {
return true;
}
}
return false;
}
}

View file

@ -4,12 +4,13 @@ import io.github.adrianvic.nemesiseye.policy.Effect;
import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import java.util.List; import java.util.List;
public record Core(String name, List<String> worlds, List<PolicyNode> nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy { public record Core(String name, List<String> worlds, List<PolicyNode> nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy {
@Override @Override
public boolean applies(HumanEntity entity) { public boolean applies(LivingEntity entity) {
return false; return false;
} }
} }

View file

@ -4,11 +4,12 @@ import io.github.adrianvic.nemesiseye.policy.Effect;
import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import java.util.List; import java.util.List;
public record GlobalPolicy(String name, List<String> worlds, List<PolicyNode> nodes, boolean policyAllowList, Effect effect, int weight) implements Policy { public record GlobalPolicy(String name, List<String> worlds, List<PolicyNode> nodes, boolean policyAllowList, Effect effect, int weight) implements Policy {
public boolean applies(HumanEntity entity) { public boolean applies(LivingEntity entity) {
return true; return true;
} }
} }

View file

@ -5,12 +5,13 @@ import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import java.util.List; import java.util.List;
public record LocationPolicy(String name, List<String> worlds, List<Glimmer.Box> locations, List<PolicyNode> nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy { public record LocationPolicy(String name, List<String> worlds, List<Glimmer.Box> locations, List<PolicyNode> nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy {
@Override @Override
public boolean applies(HumanEntity entity) { public boolean applies(LivingEntity entity) {
for (Glimmer.Box box : locations) { for (Glimmer.Box box : locations) {
if (box.contains(entity.getLocation().toVector(), entity.getWorld())) { if (box.contains(entity.getLocation().toVector(), entity.getWorld())) {
return !policyAllowList; return !policyAllowList;

View file

@ -1,18 +1,20 @@
package io.github.adrianvic.nemesiseye.policy.policies; 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.Effect;
import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import java.util.List; import java.util.List;
public record PermissionPolicy(String name, List<String> worlds, List<String> permissions, List<PolicyNode> nodes, boolean policyAllowList, Effect effect, int weight) implements Policy { public record PermissionPolicy(String name, List<String> worlds, List<String> permissions, List<PolicyNode> nodes, boolean policyAllowList, Effect effect, int weight) implements Policy {
@Override @Override
public boolean applies(HumanEntity entity) { public boolean applies(LivingEntity entity) {
for (String perm : permissions) { for (String perm : permissions) {
if (entity.hasPermission(perm)) { if (Nemesis.getInstance().getGlimmer().hasPermission(entity, perm)) {
return true; return true;
} }
} }

View file

@ -4,13 +4,14 @@ import io.github.adrianvic.nemesiseye.policy.Effect;
import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import java.util.List; import java.util.List;
public record PlayerNamePolicy(String name, List<String> worlds, List<String> playerName, List<PolicyNode> nodes, Effect effect, boolean policyAllowList, int weight) implements Policy { public record PlayerNamePolicy(String name, List<String> worlds, List<String> playerName, List<PolicyNode> nodes, Effect effect, boolean policyAllowList, int weight) implements Policy {
@Override @Override
public boolean applies(HumanEntity entity) { public boolean applies(LivingEntity entity) {
if (playerName.contains(entity.getName())) { if (playerName.contains(entity.getName())) {
return !policyAllowList(); return !policyAllowList();
} else { } else {

View file

@ -26,8 +26,19 @@ public interface Glimmer {
boolean hasItemMeta(ItemStack item); boolean hasItemMeta(ItemStack item);
boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap); boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap);
boolean hasAnyEnchantment(ItemStack itemStack); boolean hasAnyEnchantment(ItemStack itemStack);
boolean isArmor(ItemStack item);
boolean isAir(ItemStack item);
ItemStack getItemInMainHandHumanEntity(HumanEntity entity); 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 // Commands
void sendMessage(CommandSender commandSender, String text); void sendMessage(CommandSender commandSender, String text);

View file

@ -94,54 +94,67 @@ public class VersionMatcher {
} }
public Glimmer loadGlim() { public Glimmer loadGlim() {
String rawVersion; String rawVersion = null;
try { try {
rawVersion = Bukkit.getMinecraftVersion(); // returns something like "1.21.10" rawVersion = Bukkit.getMinecraftVersion();
} catch (NoSuchMethodError e) { } catch (NoSuchMethodError ignored) {}
return betaLoadGlim();
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); String matchInfo = getVersion("release", rawVersion);
if (matchInfo.isEmpty()) { if (matchInfo.isEmpty()) {
// TODO: Should change to something more robust, it's not beta since we have Bukkit.getMinecraftVersion() matchInfo = getVersion("beta", rawVersion);
return betaLoadGlim();
} }
// split the returned string: "pattern|classSuffix" if (matchInfo.isEmpty()) {
String[] partsInfo = matchInfo.split("\\|"); // Fallback to b1.7.3 if everything fails, or throw error?
String classSuffix = partsInfo[1]; // e.g. "r1_21" // 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("\\."); String[] versionParts = rawVersion.split("\\.");
int major = parseInt(versionParts[0]); 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; int patch = versionParts.length > 2 ? parseInt(versionParts[2]) : 0;
while (true) { while (major >= 0) {
String className = "io.github.adrianvic.nemesiseye.impl." + classSuffix; while (minor >= 0) {
Glimmer glimmer = tryInstantiate(className); while (patch >= 0) {
if (glimmer != null) return glimmer; 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--;
patch--; }
continue;
}
if (minor > 0) {
minor--; minor--;
patch = 20; patch = 20; // Search up to .20 patch of previous minor
continue;
} }
className = "io.github.adrianvic.nemesiseye.impl.r" + major + "_" + minor; major--;
glimmer = tryInstantiate(className); minor = 30; // Search up to .30 minor of previous major
if (glimmer != null) return glimmer;
throw new IllegalStateException(
"No suitable implementation found for version " + rawVersion);
} }
}
private Glimmer betaLoadGlim() { throw new IllegalStateException("No suitable implementation found for version " + rawVersion);
// 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
} }
private Glimmer tryInstantiate(String className) { private Glimmer tryInstantiate(String className) {

View file

@ -6,7 +6,9 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
@ -36,5 +38,8 @@ public class EventListener implements Listener {
public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); } public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); }
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @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); }
} }

View file

@ -67,6 +67,84 @@ public class r1_21 implements Glimmer {
return entity.getInventory().getItemInMainHand(); 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 @Override
public void sendMessage(CommandSender commandSender, String text) { public void sendMessage(CommandSender commandSender, String text) {
commandSender.sendMessage(text); commandSender.sendMessage(text);
@ -105,4 +183,19 @@ public class r1_21 implements Glimmer {
public boolean hasAnyEnchantment(ItemStack item) { public boolean hasAnyEnchantment(ItemStack item) {
return !(item.getItemMeta().getEnchants().isEmpty()); 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;
}
} }

View file

@ -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
}
}

View file

@ -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<Glimmer> 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)));
}
}