Compare commits

..

No commits in common. "main" and "1.0.6-SNAPSHOT" have entirely different histories.

50 changed files with 517 additions and 944 deletions

View file

@ -1,43 +0,0 @@
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

@ -1,40 +0,0 @@
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: ubuntu-latest runs-on: "arch-linux"
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: ubuntu-latest runs-on: "arch-linux"
env: env:
NEMESIS_VERSION_NAME: "${{ github.ref_name }}" NEMESIS_VERSION_NAME: "${{ github.ref_name }}"
NEMESIS_BUILD_CHANNEL: "production" NEMESIS_BUILD_CHANNEL: "production"
@ -32,10 +32,9 @@ jobs:
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew buildAll run: ./gradlew buildAll
- name: Upload artifacts to release - name: Upload JARs to release
run: | uses: softprops/action-gh-release@v2.5.0
curl -X POST \ with:
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \ files: build/libs/*.jar
-H "Content-Type: application/octet-stream" \ tag_name: ${{ github.ref_name }}
https://inspiran.beetal-castor.ts.net/git/api/v1/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}/assets \ name: ${{ github.ref_name }}
--data-binary @build/libs/your-artifact.jar

View file

@ -1,4 +1,4 @@
<img alt="eye_of_nemesis_social_cover" src="https://github.com/user-attachments/assets/a24bc92f-4dc2-4594-93c9-d056130f9695" /> <img width="1280" height="640" 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)
@ -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. 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: Currently, we support the following Minecraft versions/loaders:
- **PaperMC** `1.21.X, 26.1.X, 26.2` - **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`
- **Project Poseidon/Bukkit** `b1.7.3 (CB1060)` - **Bukkit** `b1.7.3 (CB1060)`
## Performance ## 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. 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.

View file

@ -59,24 +59,9 @@ mcVersions.forEach { ver ->
/* ----------------------------------------- */ /* ----------------------------------------- */
dependencies { dependencies {
add("compileOnly", "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") add("compileOnly", "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("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.10-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

@ -1,5 +1,5 @@
# Nodes # 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 ```yaml
nodes: nodes:
- [HIT]: - [HIT]:
@ -31,13 +31,4 @@ Triggered on `InventoryClickEvent` and `PlayerInteractEvent` (only if item is an
Triggered on `PlayerMoveEvent`, returns true if player is gliding. Triggered on `PlayerMoveEvent`, returns true if player is gliding.
### USE_ENCHANTMENT ### USE_ENCHANTMENT
Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true when the used item has an enchantment matching the provided mapping of enchantment-level. Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true 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.
### TAKE_LECTURN_BOOK
Triggered on `PlayerTakeLecturnBookEvent`, returns true.

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl.v1_7_3.events; package io.github.adrianvic.nemesiseye.impl;
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.v1_7_3; package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.Nemesis;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl.v1_7_3.events; package io.github.adrianvic.nemesiseye.impl;
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.v1_7_3; package io.github.adrianvic.nemesiseye.impl;
import static org.bukkit.Bukkit.getServer; import static org.bukkit.Bukkit.getServer;

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl.v1_7_3.events; package io.github.adrianvic.nemesiseye.impl;
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

@ -1,16 +1,10 @@
package io.github.adrianvic.nemesiseye.impl.v1_7_3; package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.impl.v1_7_3.commands.Eye; import io.github.adrianvic.nemesiseye.impl.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.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;
@ -21,7 +15,6 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -70,7 +63,6 @@ public class b1_7_3 implements Glimmer {
pm = Nemesis.getInstance().getPluginManager(); pm = Nemesis.getInstance().getPluginManager();
pm.registerEvent(Event.Type.ENTITY_DAMAGE, new EntityEventListener(), Event.Priority.Normal, plugin); 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_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); pm.registerEvent(Event.Type.PLAYER_INTERACT, new PlayerEventListener(), Event.Priority.Normal, plugin);
plugin.getCommand("eye").setExecutor(new Eye()); plugin.getCommand("eye").setExecutor(new Eye());
} }
@ -80,43 +72,6 @@ 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
public Map<Action, NodeHandler> getExtraHandlers() {
return new HashMap<>();
}
@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");
@ -144,18 +99,4 @@ 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.v1_7_3.commands; package io.github.adrianvic.nemesiseye.impl.commands;
import io.github.adrianvic.nemesiseye.commands.EyeCore; import io.github.adrianvic.nemesiseye.commands.EyeCore;
import org.bukkit.command.Command; import org.bukkit.command.Command;

View file

@ -7,22 +7,13 @@ 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,12 +1,15 @@
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 io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.Material;
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.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;
@ -15,10 +18,6 @@ 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(
@ -32,12 +31,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 (g().isAir(item)) { if (item == null || item.getType().isAir()) {
return; return;
} }
// Right-click armor equipping // Right-click armor equipping
if (g().isArmor(item) if (isArmor(item)
&& !Validator.can(event.getPlayer(), Action.EQUIP, event)) { && !Validator.can(event.getPlayer(), Action.EQUIP, event)) {
event.setCancelled(true); event.setCancelled(true);
return; return;
@ -45,7 +44,7 @@ public class Events {
// Normal item interaction // Normal item interaction
event.setCancelled( event.setCancelled(
!Validator.can(event.getPlayer(), List.of(Action.INTERACT, Action.INTERACTWITH), event) !Validator.can(event.getPlayer(), Action.INTERACT, event)
); );
} }
@ -68,17 +67,60 @@ public class Events {
} }
public static void onPlayerMoveEvent(PlayerMoveEvent event) { public static void onPlayerMoveEvent(PlayerMoveEvent event) {
if (g().isGliding(event.getPlayer()) if (event.getPlayer().isGliding()
&& !Validator.can( && !Validator.can(
event.getPlayer(), event.getPlayer(),
List.of(Action.GLYDE), List.of(Action.GLYDE),
event event
)) { )) {
g().setGliding(event.getPlayer(), false); event.getPlayer().setGliding(false);
} }
} }
public static void onCreatureSpawnEvent(CreatureSpawnEvent event) { public static void onInventoryClickEvent(InventoryClickEvent event) {
event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event)); if (!isArmorEquipAttempt(event)) {
return;
}
HumanEntity entity = event.getWhoClicked();
if (!Validator.can(entity, Action.EQUIP, event)) {
event.setCancelled(true);
}
}
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;
} }
} }

View file

@ -1,39 +0,0 @@
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;
import org.bukkit.event.player.PlayerTakeLecternBookEvent;
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);
}
public static void onPlayerTakeLecternBookEvent(PlayerTakeLecternBookEvent event) {
if (!Validator.can(event.getPlayer().getWorld(), Action.TAKE_LECTERN_BOOK, event)) {
event.setCancelled(true);
}
}
}

View file

@ -2,10 +2,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 io.github.adrianvic.nemesiseye.policy.policies.GlobalPolicy;
import org.bukkit.World;
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;
@ -22,9 +19,12 @@ public class Validator {
return true; return true;
} }
public static boolean can(LivingEntity entity, Action action, Event event) { public static boolean can(HumanEntity entity, Action action, Event event) {
boolean restricted = false;
boolean allowed = false;
for (Policy policy : getPoliciesForEntity(entity)) { for (Policy policy : getPoliciesForEntity(entity)) {
boolean matches = policy.matches(entity.getWorld(), action, event); boolean matches = policy.matches(entity, action, event);
switch (policy.effect()) { switch (policy.effect()) {
case ALLOW: case ALLOW:
@ -40,38 +40,8 @@ public class Validator {
return true; return true;
} }
public static boolean can(World world, Action action, Event event) {
boolean allowed = true;
for (GlobalPolicy policy : getGlobalPolicies()) { public static List<Policy> getPoliciesForEntity(HumanEntity entity) {
boolean matches = policy.matches(world, action, event);
switch (policy.effect()) {
case ALLOW:
if (matches) return true;
break;
case DENY:
if (matches) return false;
break;
}
}
return allowed;
}
public static List<GlobalPolicy> getGlobalPolicies() {
List<Policy> ps = Config.getInstance().getPolicies();
List<GlobalPolicy> result = new ArrayList<>();
for (Policy policy : ps) {
if (policy instanceof GlobalPolicy p) result.add(p);
}
return result;
}
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,9 +26,10 @@ 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 (glim.hasPermission(commandSender, sub.permission())) { else if (commandSender.hasPermission(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()));
Commands.sendNoPermissionError(commandSender); Commands.sendNoPermissionError(commandSender);
return true; return true;
} }
@ -40,15 +41,15 @@ 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 (glim.hasPermission(commandSender, e.getValue().permission())) { if (e.getValue().hasPermission(commandSender)) {
cmds.put(e.getKey(), e.getValue());
cmds.put(e.getKey(), e.getValue()); cmds.put(e.getKey(), e.getValue());
} }
} }
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 && glim.hasPermission(commandSender, sub.permission())) { if (sub != null && commandSender.hasPermission(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,6 +1,5 @@
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;
@ -14,6 +13,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 Nemesis.getInstance().getGlimmer().hasPermission(sender, permission()); return sender.hasPermission(permission());
} }
} }

View file

@ -2,16 +2,11 @@ package io.github.adrianvic.nemesiseye.policy;
public enum Action { public enum Action {
INTERACT, INTERACT,
INTERACTWITH,
BREAK, BREAK,
HIT, HIT,
// TODO CRAFT, // TODO CRAFT,
EQUIP, EQUIP,
PLACE, PLACE,
USE_ENCHANTMENT, USE_ENCHANTMENT,
GLYDE, GLYDE
SPAWN,
ENDERMAN_ATTACK_PLAYER,
FORMBLOCK,
TAKE_LECTERN_BOOK
} }

View file

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

View file

@ -1,36 +1,24 @@
package io.github.adrianvic.nemesiseye.policy; 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.*;
import io.github.adrianvic.nemesiseye.policy.handlers.generic.Boolean;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class NodeHandlers { 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 Boolean()); handlers.put(Action.GLYDE, new glyde());
handlers.put(Action.EQUIP, new Equip()); 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.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) { public static NodeHandler get(Action type) {
NodeHandler handler = handlers.get(type); return handlers.get(type);
if (handler == null) {
handler = Nemesis.getInstance().getGlimmer().getExtraHandlers().get(type);
}
return handler;
} }
} }

View file

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

View file

@ -1,7 +1,7 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.DataShifter;
import org.bukkit.World; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import java.util.ArrayList; import java.util.ArrayList;
@ -18,28 +18,27 @@ public record PolicyNode(List<Action> actions, List<Object> values) {
List<Action> nodeActions = new ArrayList<>(); List<Action> nodeActions = new ArrayList<>();
List<Object> nodeValues = new ArrayList<>(); List<Object> nodeValues = new ArrayList<>();
if (rawNode.getKey() instanceof List<?> rawTypes && (rawNode.getValue() == null || rawNode.getValue() instanceof Map<?, ?>)) { if (rawNode.getKey() instanceof List<?> rawTypes && rawNode.getValue() instanceof Map<?,?> rawNodeValues) {
for (Object rawType : rawTypes) { for (Object rawType : rawTypes) {
if (rawType instanceof String rts && !rts.isEmpty() && DataShifter.enumOrDefault(Action.class, rts, null) != null) { if (rawType instanceof String rts && !rts.isEmpty() && !(rts == null) && DataShifter.enumOrDefault(Action.class, rts, null) != null) {
nodeActions.add(DataShifter.enumOrDefault(Action.class, rts, null)); nodeActions.add(DataShifter.enumOrDefault(Action.class, rts, null));
} }
} }
Map<String, Object> semiParsedNodeValue = new HashMap<>();
if (rawNode.getValue() instanceof Map<?, ?> rawNodeValues) { for (Map.Entry<?,?> rawNodeValueEntries : rawNodeValues.entrySet()) {
Map<String, Object> semiParsedNodeValue = new HashMap<>(); if (rawNodeValueEntries.getKey() instanceof String stringKey) {
for (Map.Entry<?, ?> rawNodeValueEntries : rawNodeValues.entrySet()) { semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue());
if (rawNodeValueEntries.getKey() instanceof String stringKey) {
semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue());
}
} }
}
if (semiParsedNodeValue.get("values") instanceof List<?> l) { if (semiParsedNodeValue.get("values") instanceof List<?> l) {
nodeValues.addAll(l); for (Object v : l) {
nodeValues.add(v);
} }
} }
} }
if (!nodeActions.isEmpty()) { if (!nodeActions.isEmpty() && !nodeValues.isEmpty()) {
PolicyNode newNode = new PolicyNode(nodeActions, nodeValues); PolicyNode newNode = new PolicyNode(nodeActions, nodeValues);
nodes.add(newNode); nodes.add(newNode);
} }
@ -56,7 +55,7 @@ public record PolicyNode(List<Action> actions, List<Object> values) {
return handlers; return handlers;
} }
public boolean matches(World world, Action action, Event event) { public boolean matches(HumanEntity entity, Action action, Event event) {
if (!actions.contains(action)) { if (!actions.contains(action)) {
return false; return false;
} }
@ -67,6 +66,7 @@ public record PolicyNode(List<Action> actions, List<Object> values) {
return false; return false;
} }
return handler.check(world, this, action, event); boolean result = handler.check(entity, this, action, event);
return result;
} }
} }

View file

@ -34,11 +34,8 @@ public interface PolicyParser {
if (rawNodes instanceof List<?> list) { if (rawNodes instanceof List<?> list) {
for (Object o : list) { for (Object o : list) {
if (o instanceof Map<?, ?> map) { if (o instanceof Map<?, ?> map)
nodeList.add((Map<Object, Object>) map); nodeList.add((Map<Object, Object>) map);
} else if (o instanceof List<?> actions) {
nodeList.add(Map.of(actions, Map.of()));
}
} }
} }

View file

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

View file

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

View file

@ -1,26 +0,0 @@
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.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(World world, PolicyNode node, Action action, Event event) {
if (event instanceof EntityEvent e) {
String type = e.getEntity().getType().name();
List<String> parsedValue = DataShifter.parseValueToStringList(node.values());
return DataShifter.safeMatches(parsedValue, type);
}
return false;
}
}

View file

@ -1,33 +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.World;
import org.bukkit.entity.HumanEntity;
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(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;
return glim.hasEnchantment(item,
DataShifter.parseValueToStringMap(node.values()));
}
return false;
}
}

View file

@ -1,34 +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.World;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.Event;
import org.bukkit.event.entity.EntityEvent;
import java.util.List;
public class UseItem implements NodeHandler {
private final Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
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;
String type = item.getType().toString();
List<String> parsedValue = DataShifter.parseValueToStringList(node.values());
return DataShifter.safeMatches(parsedValue, type);
}
return false;
}
}

View file

@ -4,22 +4,22 @@ import io.github.adrianvic.nemesiseye.DataShifter;
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.World; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import java.util.List; public class bePlaced implements NodeHandler {
public class BePlaced implements NodeHandler {
@Override @Override
public boolean check(World world, PolicyNode node, Action action, Event event) { public boolean check(HumanEntity 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());
return DataShifter.safeMatches(parsedValue, type); for (String s : DataShifter.parseValueToStringList(node.values())) {
if (DataShifter.safeMatches(s, type)) {
return true;
}
}
} }
return false; return false;
} }
} }

View file

@ -0,0 +1,84 @@
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,14 +1,14 @@
package io.github.adrianvic.nemesiseye.policy.handlers.generic; 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.World; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.Event; import org.bukkit.event.Event;
public class Boolean implements NodeHandler { public class glyde implements NodeHandler {
@Override @Override
public boolean check(World world, PolicyNode node, Action action, Event event) { public boolean check(HumanEntity entity, PolicyNode node, Action action, Event event) {
return true; return true;
} }
} }

View file

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

View file

@ -0,0 +1,28 @@
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,13 +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 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(LivingEntity entity) { public boolean applies(HumanEntity entity) {
return false; return false;
} }
} }

View file

@ -4,12 +4,11 @@ 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(LivingEntity entity) { public boolean applies(HumanEntity entity) {
return true; return true;
} }
} }

View file

@ -5,13 +5,12 @@ 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(LivingEntity entity) { public boolean applies(HumanEntity 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,20 +1,18 @@
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(LivingEntity entity) { public boolean applies(HumanEntity entity) {
for (String perm : permissions) { for (String perm : permissions) {
if (Nemesis.getInstance().getGlimmer().hasPermission(entity, perm)) { if (entity.hasPermission(perm)) {
return true; return true;
} }
} }

View file

@ -4,14 +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 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(LivingEntity entity) { public boolean applies(HumanEntity entity) {
if (playerName.contains(entity.getName())) { if (playerName.contains(entity.getName())) {
return !policyAllowList(); return !policyAllowList();
} else { } else {

View file

@ -1,7 +1,5 @@
package io.github.adrianvic.nemesiseye.reflection; 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 io.github.adrianvic.nemesiseye.policy.Policy;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
@ -28,22 +26,8 @@ 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);
// Handlers
Map<Action, NodeHandler> getExtraHandlers();
// Commands // Commands
void sendMessage(CommandSender commandSender, String text); void sendMessage(CommandSender commandSender, String text);

View file

@ -3,39 +3,145 @@ package io.github.adrianvic.nemesiseye.reflection;
import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.DataShifter;
import org.bukkit.Bukkit; 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 class VersionMatcher {
public VersionMatcher() {} public VersionMatcher() {}
public Glimmer loadGlim() { private record Entry(String pattern, String classSuffix) {}
String rawVersion = null;
try {
rawVersion = Bukkit.getMinecraftVersion();
} catch (NoSuchMethodError ignored) {}
if (rawVersion == null || rawVersion.isEmpty()) { public String getVersion(String type, String serverVersion) {
String v = Bukkit.getVersion(); // e.g. "git-Bukkit-0.0.0-1060-... (MC: 1.7.3)" if (type == null || serverVersion == null) {
int start = v.lastIndexOf("MC: "); return "";
if (start != -1) { }
rawVersion = v.substring(start + 4, v.length() - 1);
} else { Map<String, List<Entry>> map = populateMap();
rawVersion = "unknown"; List<Entry> 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;
} }
} }
String impl = DataShifter.safeMatches("^1\\.7\\.3$", rawVersion) List<Entry> sorted = new ArrayList<>(entries);
? "b1_7_3" Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern));
: "r1_21";
String subPkg = impl.equals("b1_7_3") ? "v1_7_3" : "v1_21"; Entry oldest = sorted.get(0);
Glimmer glim = tryInstantiate( Entry newest = sorted.get(sorted.size() - 1);
"io.github.adrianvic.nemesiseye.impl." + subPkg + "." + impl);
if (glim == null) { int cmpOldest = compareVersions(serverVersion, oldest.pattern);
throw new IllegalStateException("Missing implementation: " + impl); int cmpNewest = compareVersions(serverVersion, newest.pattern);
if (cmpOldest < 0) {
return oldest.pattern + "|" + oldest.classSuffix;
} else if (cmpNewest > 0) {
return newest.pattern + "|" + newest.classSuffix;
} }
return glim; // should not happen because we already tried all patterns
return newest.pattern + "|" + newest.classSuffix;
}
private Map<String, List<Entry>> populateMap() {
Map<String, List<Entry>> 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;
try {
rawVersion = Bukkit.getMinecraftVersion(); // returns something like "1.21.10"
} catch (NoSuchMethodError e) {
return betaLoadGlim();
}
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();
}
// split the returned string: "pattern|classSuffix"
String[] partsInfo = matchInfo.split("\\|");
String classSuffix = partsInfo[1]; // e.g. "r1_21"
String[] versionParts = rawVersion.split("\\.");
int major = parseInt(versionParts[0]);
int minor = parseInt(versionParts[1]);
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;
if (patch > 0) {
patch--;
continue;
}
if (minor > 0) {
minor--;
patch = 20;
continue;
}
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);
}
}
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
} }
private Glimmer tryInstantiate(String className) { private Glimmer tryInstantiate(String className) {

View file

@ -5,19 +5,18 @@
# \_\ \_____ / /_/ /_______ /\_______ /\____|__ / # \_\ \_____ / /_/ /_______ /\_______ /\____|__ /
# \/ \/ \/ \/ # \/ \/ \/ \/
# EYE OF NEMESIS - Example config file. # EYE OF NEMESIS - Example config file.
# Documentation in our repository's /docs # Documentation in our wiki: https://github.com/adrianvic/NemesisEye
Policies: Policies:
# This example blocks the usage of bedrock without the server.usebedrock permissin - name: "Bedrock-allow-admins"
- name: "Bedrock-allow-admins" # No spaces here or else commands which need the name as argument will not work type: "permission"
type: "permission" # global | location | permission | playerName
worlds: [world] worlds: [world]
effect: ALLOW # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met) effect: ALLOW
weight: 3 # Greater weight overrides lower weight: 3
permissions: permissions:
- "server.usebedrock" - "server.usebedrock"
nodes: nodes:
- [PLACE]: - [BREAK, PLACE, HIT, INTERACT]:
values: values:
- BEDROCK - BEDROCK
@ -27,16 +26,15 @@ Policies:
effect: DENY effect: DENY
weight: 2 weight: 2
nodes: nodes:
- [PLACE]: - [BREAK, PLACE, HIT, INTERACT]:
values: values:
- BEDROCK - BEDROCK
# This example blocks all non-beta (b1.7.3) items, blocks, mobs, enchantments in a rectangular area - name: "Block-non-beta-items" # No spaces here or else commands which need the name as argument will not work
- name: "Block-non-beta-items" type: "location" # global | location | permission | playerName
type: "location" effect: DENY # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met)
effect: DENY
worlds: [world] worlds: [world]
weight: 0 weight: 0 # Greater weight overrides lower
policyAllowList: false # Inverts the policy validation logic, for example a location policy will affect all players NOT inside it's location policyAllowList: false # Inverts the policy validation logic, for example a location policy will affect all players NOT inside it's location
locations: locations:
worlds: [world] worlds: [world]
@ -44,7 +42,7 @@ Policies:
- corner1: { x: 2100, y: 256, z: 1400 } - corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 } corner2: { x: 1000, y: -64, z: 2200 }
nodes: nodes:
- [INTERACT, BREAK, HIT, PLACE, GLYDE, EQUIP, SPAWN]: - [INTERACT, BREAK, HIT, PLACE]:
values: values:
- '.*' - '.*'
- [USE_ENCHANTMENT]: - [USE_ENCHANTMENT]:
@ -61,7 +59,7 @@ Policies:
- corner1: { x: 2100, y: 256, z: 1400 } - corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 } corner2: { x: 1000, y: -64, z: 2200 }
nodes: nodes:
- [INTERACT, BREAK, HIT, PLACE, SPAWN]: - [INTERACT, BREAK, HIT, PLACE]:
values: values:
- AIR - AIR
- STONE - STONE
@ -170,16 +168,4 @@ Policies:
- MUSIC_DISK_CAT - MUSIC_DISK_CAT
- MUSIC_DISK_13 - MUSIC_DISK_13
- DIRT - DIRT
- BREAD - BREAD
- ZOMBIE
- SKELETON
- CHICKEN
- PIG
- SHEEP
- COW
- SQUID
- WOLF
- PIGZOMBIE
- SPIDER
- GHAST
- CREEPER

View file

@ -1,19 +1,15 @@
package io.github.adrianvic.nemesiseye.impl.v1_21; package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Events; import io.github.adrianvic.nemesiseye.Events;
import io.github.adrianvic.nemesiseye.ModernEvents;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; 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.BlockFormEvent;
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.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;
import org.bukkit.event.player.PlayerTakeLecternBookEvent;
public class EventListener implements Listener { public class EventListener implements Listener {
@EventHandler @EventHandler
@ -40,21 +36,5 @@ 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) { ModernEvents.onInventoryClickEvent(event); } public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event);}
@EventHandler
public void onCreatureSpawnEvent(CreatureSpawnEvent event) { Events.onCreatureSpawnEvent(event); }
@EventHandler
public void onEndermanAttackPlayerEvent(com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event) {
ModernEvents.onEndermanAttackPlayerEvent(event);
}
@EventHandler
public void onBlockFormEvent(BlockFormEvent event) {
ModernEvents.onBlockFormEvent(event);
}
@EventHandler
public void onPlayerTakeLecternBook(PlayerTakeLecternBookEvent event) { ModernEvents.onPlayerTakeLecternBookEvent(event); }
} }

View file

@ -1,4 +1,4 @@
package io.github.adrianvic.nemesiseye.impl.v1_21.commands; package io.github.adrianvic.nemesiseye.impl.commands;
import io.github.adrianvic.nemesiseye.commands.EyeCore; import io.github.adrianvic.nemesiseye.commands.EyeCore;
import org.bukkit.command.Command; import org.bukkit.command.Command;

View file

@ -0,0 +1,108 @@
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<Policy> loadPoliciesFromFile(File file) {
YamlConfiguration config = new YamlConfiguration();
config.options().parseComments(true);
try {
config.load(file);
} catch (Exception e) {
e.printStackTrace();
}
List<Map<?, ?>> rawPolicies = config.getMapList("Policies");
List<Policy> 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 void sendMessage(CommandSender commandSender, String text) {
commandSender.sendMessage(text);
}
@Override
public boolean hasItemMeta(ItemStack item) {
return item.getItemMeta() != null;
}
@Override
public List<World> getWorlds() {
return Bukkit.getWorlds();
}
@Override
public boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap) {
Map<Enchantment, Integer> enchantments = item.getEnchantments();
for (Map.Entry<Enchantment, Integer> ench : enchantments.entrySet()) {
String enchKey = ench.getKey().getKey().getKey();
String enchLevel = ench.getValue().toString();
for (Map.Entry<String, String> 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());
}
}

View file

@ -1,215 +0,0 @@
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;
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.HashMap;
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<Policy> loadPoliciesFromFile(File file) {
YamlConfiguration config = new YamlConfiguration();
config.options().parseComments(true);
try {
config.load(file);
} catch (Exception e) {
e.printStackTrace();
}
List<Map<?, ?>> rawPolicies = config.getMapList("Policies");
List<Policy> 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();
Eye eye = new Eye();
Nemesis.getInstance().getCommand("eye").setExecutor(eye);
Nemesis.getInstance().getCommand("eye").setTabCompleter(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 Map<Action, NodeHandler> getExtraHandlers() {
Map<Action, NodeHandler> handlers = new HashMap<>();
handlers.put(Action.FORMBLOCK, new FormBlock());
return handlers;
}
@Override
public void sendMessage(CommandSender commandSender, String text) {
commandSender.sendMessage(text);
}
@Override
public boolean hasItemMeta(ItemStack item) {
return item.getItemMeta() != null;
}
@Override
public List<World> getWorlds() {
return Bukkit.getWorlds();
}
@Override
public boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap) {
Map<Enchantment, Integer> enchantments = item.getEnchantments();
for (Map.Entry<Enchantment, Integer> ench : enchantments.entrySet()) {
String enchKey = ench.getKey().getKey().getKey();
String enchLevel = ench.getValue().toString();
for (Map.Entry<String, String> rule : valuesmap.entrySet()) {
if (
DataShifter.safeMatches(rule.getKey(), enchKey) &&
DataShifter.safeMatches(rule.getValue(), enchLevel)
) {
return true;
}
}
}
return false;
}
@Override
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;
}
}

View file

@ -1,38 +0,0 @@
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.World;
import org.bukkit.event.Event;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
public class ValidatorTest {
private Glimmer mockGlim;
private Config mockConfig;
@BeforeEach
void setUp() {
mockGlim = mock(Glimmer.class);
}
@Test
void testValidatorCanDeny() {
World world = mock(World.class);
Event event = mock(Event.class);
Policy policy = mock(Policy.class);
// 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()
// This is tricky without refactoring or Mockito-inline
// For now, let's just demonstrate the concept if Validator was more testable
}
}

View file

@ -1,66 +0,0 @@
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;
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)));
}
}