Compare commits

..

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

68 changed files with 522 additions and 1942 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 }}

15
.github/FUNDING.yml vendored
View file

@ -1,15 +0,0 @@
# These are supported funding model platforms
github: adrianvic
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View file

@ -1,44 +0,0 @@
name: Build
on:
workflow_dispatch:
push:
paths:
- 'src/**'
- '.github/**'
- 'build.gradle.kts'
- 'gradle.properties'
- 'settings.gradle'
jobs:
build:
runs-on: ubuntu-latest
env:
NEMESIS_VERSION_NAME: "autobuild-${{ github.sha }}"
NEMESIS_BUILD_CHANNEL: "autobuild"
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Setup Java enviroment
uses: actions/setup-java@v5.1.0
with:
distribution: temurin
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
- name: Download CB1060
run: |
mkdir -p libs
curl -L -o libs/craftbukkit-1060.jar https://archive.org/download/craftbukkit1060/craftbukkit1-7-3%281060%29.jar
- name: Build with Gradle
run: ./gradlew buildAll
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
path: build/libs/*.jar

View file

@ -1,41 +0,0 @@
name: Release Build
on:
release:
types: [published]
jobs:
build-and-release:
runs-on: ubuntu-latest
env:
NEMESIS_VERSION_NAME: "${{ github.ref_name }}"
NEMESIS_BUILD_CHANNEL: "production"
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Set up Java
uses: actions/setup-java@v5.1.0
with:
distribution: temurin
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
- name: Download CB1060
run: |
mkdir -p libs
curl -L -o libs/craftbukkit-1060.jar \
https://archive.org/download/craftbukkit1060/craftbukkit1-7-3%281060%29.jar
- name: Build with Gradle
run: ./gradlew buildAll
- name: Upload artifacts to release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
https://inspiran.beetal-castor.ts.net/git/api/v1/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}/assets \
--data-binary @build/libs/your-artifact.jar

3
.gitignore vendored
View file

@ -2,5 +2,4 @@
build/ build/
out/ out/
.idea/ .idea/
libs/ libs/
run/

View file

@ -1,24 +1,12 @@
<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)
[![English Wiki Badge](https://img.shields.io/badge/English-White?style=flat-square&label=Wiki&color=black)](https://github.com/adrianvic/NemesisEye/wiki)
[![Portuguese Wiki Badge](https://img.shields.io/badge/Portuguese-White?style=flat-square&label=Wiki&color=black)](https://mgr.rf.gd/w/Eye_of_Nemesis)
> [!IMPORTANT]
> This project is in an early stage, please report any bug you find.
# Eye of Nemesis # Eye of Nemesis
Eye of Nemesis is a plugin that allows server admins to write policies that will deny or allow (black/whitelist) players to do specific things based on the value of nodes. Eye of Nemesis is a plugin that allows server admins to write policies that will deny or allow (black/whitelist) players to do specific things based on the value of nodes.
## Warnings
- Even though running `/eye` will tell you to run `/eye help` to list all available commands, this is not implemented yet, however all commands are available as tab-complete of `/eye`.
- This plugin is in a very early stage.
## Motivations ## Motivations
I made this plugin as an effort to preserve a village from my private server. Originally from beta 1.7.3 Betalands server, then transferred to RetroMC, and then finally we downloaded the chunks to merge into our server, I was afraid it would not have the same feeling after all the updates, so I had the idea to make a plugin that can block the newer features. I made this plugin as an effort to preserve a village from my private server. Originally from beta 1.7.3 Betalands server, then transferred to RetroMC, and then finally we downloaded the chunks to merge into our server, I was afraid it would not have the same feeling after all the updates, so I had the idea to make a plugin that can block the newer features.
## Game version and loaders
Since version 1.0.3-SNAPSHOT, Eye of Nemesis has _reflection_, a technique that allows me to target multiple versions of the game while sharing the codebase across versions.
Currently, we support the following Minecraft versions/loaders:
- **PaperMC** `1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10`
- **Bukkit** `b1.7.3 (CB1060)`
## 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

@ -4,7 +4,7 @@ plugins {
} }
group = "io.github.adrianvic" group = "io.github.adrianvic"
version = System.getenv("NEMESIS_VERSION_NAME") ?: "unknown" version = "1.0.3-SNAPSHOT"
repositories { repositories {
mavenCentral() mavenCentral()
@ -24,14 +24,6 @@ val mcVersions = listOf(
/* CREATE SOURCE SET PER VERSION */ /* CREATE SOURCE SET PER VERSION */
/* ----------------------------------------- */ /* ----------------------------------------- */
tasks.withType<ProcessResources> {
inputs.property("version", project.version)
filesMatching("plugin.yml") {
expand("version" to project.version)
}
}
mcVersions.forEach { ver -> mcVersions.forEach { ver ->
val ss = sourceSets.create(ver) { val ss = sourceSets.create(ver) {
java.srcDir("src/$ver/java") java.srcDir("src/$ver/java")
@ -59,24 +51,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()
} }
/* ----------------------------------------- */ /* ----------------------------------------- */
@ -92,37 +69,13 @@ mcVersions.forEach { ver ->
manifest { manifest {
attributes( attributes(
"Nemesis-Impl-Version" to ver, "Nemesis-Impl-Version" to ver
"Nemesis-Environment" to (System.getenv("NEMESIS_BUILD_CHANNEL") ?: "dev")
) )
} }
} }
} }
tasks.register("buildAll") {
dependsOn(tasks.withType<Jar>())
}
tasks.register<Jar>("shadowJar") {
from(sourceSets["main"].output)
mcVersions.forEach { ver ->
from(sourceSets[ver].output)
}
// This is kinda gross and, essentially, we shouldn't have it here...
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier.set("all-implementations")
archiveVersion.set(project.version.toString())
manifest {
attributes(
"Implemented-Versions" to mcVersions.joinToString(",")
)
}
}
/* ----------------------------------------- */ /* ----------------------------------------- */
/* JAVA SETTINGS */ /* JAVA SETTINGS */
/* ----------------------------------------- */ /* ----------------------------------------- */
@ -137,18 +90,4 @@ tasks.withType<JavaCompile> {
tasks.runServer { tasks.runServer {
minecraftVersion("1.21") minecraftVersion("1.21")
downloadPlugins {
modrinth("viaversion", "5.7.0")
modrinth("luckperms", "v5.5.17-bukkit")
}
}
tasks.register("runServerClean") {
doFirst {
exec {
commandLine("rm", "run/plugins/Eye-of-Nemesis/settings.yml")
}
}
dependsOn("runServer")
} }

View file

@ -1,34 +0,0 @@
# 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.
```yaml
nodes:
- [HIT]:
values:
- 'GOLD_SWORD'
- [USE_ENCHANTMENT]:
values:
- "UNBREAKING": "1"
- [GLYDE]
```
## Available nodes
### INTERACT
Triggered on `InteractionEvent`, returns true when the provided `list` contains the material used to interact.
### HIT
Triggered on `EntityDamageByEntityEvent`, returns true when the provided `list` contains the material used to hit.
### PLACE
Triggered on `BlockPlaceEvent`, returns true when the provided `list` contains the material being placed.
### BREAK
Triggered on `BlockBreakEvent`, returns true when the provided `list` contains the material used to break.
### EQUIP
Triggered on `InventoryClickEvent` and `PlayerInteractEvent` (only if item is an armor), returns true when the provided `list` contains the material being equipped.
### GLYDE
Triggered on `PlayerMoveEvent`, returns true if player is gliding.
### USE_ENCHANTMENT
Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true the used item has an enchantment matching the provided mapping of enchantment-level.

View file

@ -1,89 +0,0 @@
# Policies
Policies are a list of nodes with a specific condition to be met, they also determine what effect a node is going to have. Below is an example policy:
```yaml
- name: "Allow example"
type: "location"
effect: ALLOW
weight: 1
worlds: [world]
locations:
coordinates:
- corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 }
nodes:
- [INTERACT, BREAK, HIT, PLACE]:
values:
- AIR
```
## Properties
### `name`
Name of the policy, must not contain spaces or else commands that require you to type the policy name will not work.
Example: `name: "disable-axe-damage"`
### `type`
One of the policy [types](#Types).
Example: `type: location`
### `effect`
Can be `deny` to block the action if a node check returns true, `ALLOW` to revert `DENY` effects if the node check is true or `ALLOWONLY` to allow the action **only** if the node check returns true.
Example: `effect: DENY`
### `weight`
A policy with greater weight will override a conflicting policy of lower weight.
Example: `weight: 0`
### `nodes`
Must contain a list of node mappings to evaluate if the policy applies to a player.
Example:
```yaml
nodes:
- [HIT]:
values:
- '.*_AXE$'
```
### `locations`
Only used for `type: "location"`, is a list of mappings where corner1 and corner2 should map x, y and z to two corners of a rectangular area where the policy will take effect; `worlds` may be defined here.
Example:
```yaml
locations:
worlds: [world]
coordinates:
- corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 }
```
### `permissions`
Only used for `type: "permission"`, is a list of permissions.
Example:
```yaml
permissions:
- "server.axehit"
```
### `names`
Only used for `type: "playerName"`, is a list of player names.
## Types
### `global`
Always matches.
### `location`
Matches if the player location is inside any rectangle defined in the `locations` property.
### `playerName`
Matches if the player name is in the `names` property.
### `permission`
Matches if the player has a permission in the `permissions` property.

View file

@ -1,9 +1,8 @@
package io.github.adrianvic.nemesiseye.impl.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;
import org.bukkit.event.block.BlockListener; import org.bukkit.event.block.BlockListener;
import org.bukkit.event.block.BlockPlaceEvent;
public class BlockEventListener extends BlockListener { public class BlockEventListener extends BlockListener {
@Override @Override
@ -11,8 +10,4 @@ public class BlockEventListener extends BlockListener {
Events.onBlockBreak(event); Events.onBlockBreak(event);
} }
@Override
public void onBlockPlace(BlockPlaceEvent event) {
Events.onBlockPlaceEvent(event);
}
} }

View file

@ -0,0 +1,13 @@
package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityListener;
public class EntityEventListener extends EntityListener {
public void onEntityDamage(EntityDamageByEntityEvent event) {
// Events.onEntityDamageByEntityEvent(event);
event.setCancelled(true);
}
}

View file

@ -1,6 +1,8 @@
package io.github.adrianvic.nemesiseye.impl.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.BlockListener;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerListener; import org.bukkit.event.player.PlayerListener;

View file

@ -2,15 +2,11 @@ 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.policy.policies.LocationPolicy;
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.entity.HumanEntity; 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;
@ -22,7 +18,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@SuppressWarnings("unused")
public class b1_7_3 implements Glimmer { public class b1_7_3 implements Glimmer {
JavaPlugin plugin; JavaPlugin plugin;
PluginManager pm; PluginManager pm;
@ -52,15 +47,32 @@ public class b1_7_3 implements Glimmer {
} }
List<Policy> allPolicies = new ArrayList<>(); List<Policy> allPolicies = new ArrayList<>();
for (Map<?, ?> policyMap : result) { for (Map<?, ?> map : result) {
if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) { for (Map.Entry<?, ?> entry : map.entrySet()) {
allPolicies.add(PolicyParsers.get(type).parse(policyMap)); if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
List<Policy> parsed = PolicyParsers.get(k).parse(v);
allPolicies.addAll(parsed);
}
} }
} }
return allPolicies; return allPolicies;
} }
@Override
public List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies) {
List<Policy> result = new ArrayList<>();
for (Policy p : policies) {
if (p instanceof LocationPolicy lp) {
for (List<Box> boxList : lp.locations()) {
for (Box b : boxList) {
if (b.contains(entity.getLocation().toVector())) result.add(p);
}
}
}
}
return result;
}
@Override @Override
public void onLoad() { public void onLoad() {
plugin = Nemesis.getInstance(); plugin = Nemesis.getInstance();
@ -76,46 +88,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 void sendMessage(CommandSender commandSender, String text) {
String[] lines = text.split("\\r?\\n");
for (String line : lines) {
commandSender.sendMessage(line);
}
}
@Override @Override
public boolean hasItemMeta(ItemStack item) { public boolean hasItemMeta(ItemStack item) {
return false; return false;
@ -135,18 +107,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

@ -6,7 +6,7 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
public class Eye implements CommandExecutor { public class Eye implements CommandExecutor {
private final EyeCore core; private EyeCore core;
public Eye() { public Eye() {
core = new EyeCore(); core = new EyeCore();

View file

@ -1,13 +0,0 @@
package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityListener;
public class EntityEventListener extends EntityListener {
@Override
public void onEntityDamage(EntityDamageEvent event) {
if (event instanceof EntityDamageByEntityEvent e) Events.onEntityDamageByEntityEvent(e);
}
}

View file

@ -0,0 +1,8 @@
name: "Eye-of-Nemesis"
version: '1.0.3-SNAPSHOT'
main: io.github.adrianvic.nemesiseye.Nemesis
author: 'Adrian Victor'
description: "Change what players can do based in custom criteria."
commands:
eye:
description: "Run /eye help to see all available commands."

View file

@ -0,0 +1 @@
impl.version=b1_7_3

View file

@ -1,30 +1,26 @@
package io.github.adrianvic.nemesiseye; package io.github.adrianvic.nemesiseye;
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.policies.GlobalPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
public class Config { public class Config {
private static Config instance = new Config(); private static Config instance = new Config();
private final Glimmer glim = Nemesis.getInstance().getGlimmer(); private Glimmer glim = Nemesis.getInstance().getGlimmer();
private File file; private File file;
private List<Policy> policies = new ArrayList<>(); private List<Policy> policies = new ArrayList<>();
private Config() {} private Config() {}
public void load() { public void load() {
policies = glim.loadPoliciesFromFile(glim.loadConfigFile()); List<Policy> newPolicies = glim.loadPoliciesFromFile(glim.loadConfigFile());
policies.sort(Comparator.comparingInt(Policy::weight).reversed()); policies = newPolicies;
} }
// TODO: Implement config saving
//
// public void save() { // public void save() {
// try { // try {
// config.save(file); // config.save(file);

View file

@ -13,16 +13,6 @@ public class DataShifter {
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) {
@ -50,14 +40,14 @@ public class DataShifter {
return out; return out;
} }
public static <T extends Enum<T>> T enumOrDefault(Class<T> type, String string, T def) { public static List<Map<?, ?>> parseValueToListOfMaps(List<?> values) {
try { List<Map<?, ?>> result = new ArrayList<>();
return Enum.valueOf(type, string);
} catch (IllegalArgumentException e) { for (Object o : values) {
return def; if (o instanceof Map<?, ?> raw) {
} catch (Exception e) { result.add(raw);
e.printStackTrace(); }
return def;
} }
return result;
} }
} }

View file

@ -1,98 +1,25 @@
package io.github.adrianvic.nemesiseye; package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.policy.Action;
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.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
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.inventory.ItemStack;
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.canBreak(event.getPlayer()));
!Validator.can(
event.getPlayer(),
List.of(Action.BREAK, Action.USE_ENCHANTMENT),
event
)
);
} }
public static void onInteractionEvent(PlayerInteractEvent event) { public static void onInteractionEvent(PlayerInteractEvent event) {
ItemStack item = event.getItem(); if (event.getItem() != null) {
event.setCancelled(!Validator.canInteract(event.getPlayer()));
if (g().isAir(item)) {
return;
} }
// Right-click armor equipping
if (g().isArmor(item)
&& !Validator.can(event.getPlayer(), Action.EQUIP, event)) {
event.setCancelled(true);
return;
}
// Normal item interaction
event.setCancelled(
!Validator.can(event.getPlayer(), Action.INTERACT, event)
);
}
public static void onBlockPlaceEvent(BlockPlaceEvent event) {
event.setCancelled(
!Validator.can(event.getPlayer(), Action.PLACE, event)
);
} }
public static void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { public static void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Player player) { if (event.getDamager() instanceof Player) {
event.setCancelled( event.setCancelled(!Validator.canHit((HumanEntity) event.getDamager()));
!Validator.can(
player,
List.of(Action.HIT, Action.USE_ENCHANTMENT),
event
)
);
} }
} }
}
public static void onPlayerMoveEvent(PlayerMoveEvent event) {
if (g().isGliding(event.getPlayer())
&& !Validator.can(
event.getPlayer(),
List.of(Action.GLYDE),
event
)) {
g().setGliding(event.getPlayer(), false);
}
}
public static void onInventoryClickEvent(InventoryClickEvent event) {
if (!g().isArmorEquipAttempt(event)) {
return;
}
HumanEntity entity = event.getWhoClicked();
if (!Validator.can(entity, Action.EQUIP, event)) {
event.setCancelled(true);
}
}
public static void onCreatureSpawnEvent(CreatureSpawnEvent event) {
event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event));
}
}

View file

@ -1,10 +1,14 @@
package io.github.adrianvic.nemesiseye; package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.commands.EyeCore;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import io.github.adrianvic.nemesiseye.reflection.VersionMatcher;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public final class Nemesis extends JavaPlugin { public final class Nemesis extends JavaPlugin {
private Glimmer glim; private Glimmer glim;
private static final String VERSION_PROP = "impl.version"; private static final String VERSION_PROP = "impl.version";
@ -13,11 +17,44 @@ public final class Nemesis extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
glim = new VersionMatcher().loadGlim(); glim = loadGlim();
glim.onLoad(); glim.onLoad();
Config.getInstance().load(); Config.getInstance().load();
} }
private String readImplVersion() {
Properties props = new Properties();
try (InputStream is = getClass().getClassLoader()
.getResourceAsStream("version.properties")) {
if (is == null) {
throw new IllegalStateException("version.properties not found on classpath.");
}
props.load(is);
} catch (IOException e) {
throw new IllegalStateException("Failed to load version.properties", e);
}
String version = props.getProperty(VERSION_PROP);
if (version == null || version.isBlank()) {
throw new IllegalStateException(VERSION_PROP + " property missing in version.properties.");
}
return version.trim();
}
private Glimmer loadGlim() {
String implVersion = readImplVersion();
String className = "io.github.adrianvic.nemesiseye.impl." + implVersion;
try {
Class<?> clazz = Class.forName(className, true, getClass().getClassLoader());
if (!Glimmer.class.isAssignableFrom(clazz)) {
throw new IllegalStateException(className + " does not implement Glimmer.");
}
return (Glimmer) clazz.getDeclaredConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Failed to instantiate " + className, e);
}
}
@Override @Override
public void onDisable() { public void onDisable() {
} }
@ -25,4 +62,4 @@ public final class Nemesis extends JavaPlugin {
public static Nemesis getInstance() { return instance; } public static Nemesis getInstance() { return instance; }
public Glimmer getGlimmer() { return glim; } public Glimmer getGlimmer() { return glim; }
public PluginManager getPluginManager() { return this.getServer().getPluginManager(); } public PluginManager getPluginManager() { return this.getServer().getPluginManager(); }
} }

View file

@ -2,54 +2,52 @@ 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.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Validator { public class Validator {
public static boolean can(HumanEntity entity, List<Action> actions, Event event) { private final static Glimmer glim = Nemesis.getInstance().getGlimmer();
for (Action action : actions) {
if (!can(entity, action, event)) {
return false;
}
}
public static boolean canInteract(HumanEntity entity) {
return checkAgainstEntity(entity, Action.INTERACT);
}
public static boolean canBreak(HumanEntity entity) {
return checkAgainstEntity(entity, Action.BREAK);
}
public static boolean canHit(HumanEntity entity) {
return checkAgainstEntity(entity, Action.HIT);
}
public static boolean checkAgainstEntity(HumanEntity entity, Action action) {
return checkAgainstNodes(entity, getNodesForPolicies(getPoliciesForEntity(entity)), action);
}
public static boolean checkAgainstNodes(HumanEntity entity, List<PolicyNode> nodes, Action action) {
for (PolicyNode n : nodes) {
if (!checkAgainstNode(entity, n, action)) return false;
}
return true; return true;
} }
public static boolean can(LivingEntity entity, Action action, Event event) { public static boolean checkAgainstNode(HumanEntity entity, PolicyNode node, Action action) {
boolean restricted = false; return node.getHandler().allows(entity, node, action);
boolean allowed = false;
for (Policy policy : getPoliciesForEntity(entity)) {
boolean matches = policy.matches(entity, action, event);
switch (policy.effect()) {
case ALLOW:
if (matches) return true;
break;
case DENY:
if (matches) return false;
break;
}
}
return true;
} }
public static List<PolicyNode> getNodesForPolicies(List<Policy> policies) {
List<PolicyNode> nodes = new ArrayList<>();
for (Policy p : policies) {
nodes.addAll(p.nodes());
}
return nodes;
}
public static List<Policy> getPoliciesForEntity(LivingEntity entity) { public static List<Policy> getPoliciesForEntity(HumanEntity entity) {
List<Policy> ps = Config.getInstance().getPolicies(); List<Policy> ps = Config.getInstance().getPolicies();
List<Policy> result = new ArrayList<>(); return glim.getApplyingPoliciesForEntity(entity, ps);
for (Policy policy : ps) {
if (policy.applies(entity)) result.add(policy);
}
return result;
} }
} }

View file

@ -1,35 +0,0 @@
package io.github.adrianvic.nemesiseye.commands;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.commands.sub.*;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.command.CommandSender;
import java.util.HashMap;
import java.util.Map;
public class Commands {
private static final Map<String, Subcommand> commands = new HashMap<>();
private static final Glimmer glim = Nemesis.getInstance().getGlimmer();
static {
commands.put("help", new Help());
commands.put("listpolicies", new ListPolicies());
commands.put("currentpolicies", new CurrentPolicies());
commands.put("policyinfo", new PolicyInfo());
commands.put("reload", new Reload());
}
public static Map<String, Subcommand> getAll() {
return commands;
}
public static Subcommand get(String type) {
return commands.get(type);
}
public static void sendNoPermissionError(CommandSender sender) {
glim.sendMessage(sender, "You do not have the necessary permission to use this command.");
}
}

View file

@ -2,56 +2,52 @@ package io.github.adrianvic.nemesiseye.commands;
import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.commands.sub.*; import io.github.adrianvic.nemesiseye.commands.sub.*;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.ChatColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.*; import java.util.*;
public class EyeCore { public class EyeCore {
private final Glimmer glim = Nemesis.getInstance().getGlimmer(); public final Map<String, Subcommand> subs = new HashMap<>();
public EyeCore() {} public EyeCore() {
register(new Reload());
register(new ListPolicies());
register(new PolicyInfo());
register(new CurrentPolicies());
}
private void register(Subcommand sub) {
subs.put(sub.name(), sub);
}
public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) { public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) {
if (strings.length == 0) { if (strings.length == 0) {
glim.sendMessage(commandSender, """ commandSender.sendMessage("""
%sEye of Nemesis%s version %s%s%s Eye of Nemesis version %s
Usage: '/eye <command>' Usage: '/eye <command>'
Use '/eye help' for a list of available commands Use '/eye help' for a list of available commands
""".formatted(ChatColor.AQUA, ChatColor.WHITE, ChatColor.GRAY, Nemesis.getInstance().getDescription().getVersion(), ChatColor.WHITE)); """.formatted(Nemesis.getInstance().getDescription().getVersion()));
} else { } else {
Subcommand sub = Commands.get(strings[0].toLowerCase()); Subcommand sub = subs.get(strings[0].toLowerCase());
if (sub == null) { if (sub == null) {
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())) { return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length));
return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length));
} else {
// Nemesis.getInstance().getLogger().info("does not have %s".formatted(sub.permission()));
Commands.sendNoPermissionError(commandSender);
return true;
}
} }
return false; return false;
} }
public List<String> onTabComplete(CommandSender commandSender, Command command, String s, String [] strings) { public List<String> onTabComplete(CommandSender commandSender, Command command, String s, String [] strings) {
if (strings.length == 1) { if (strings.length == 1) {
Map<String, Subcommand> cmds = new HashMap<>(); return new ArrayList<>(subs.keySet());
for (Map.Entry<String, Subcommand> e : Commands.getAll().entrySet()) {
if (glim.hasPermission(commandSender, e.getValue().permission())) {
cmds.put(e.getKey(), e.getValue());
cmds.put(e.getKey(), e.getValue());
}
}
return new ArrayList<>(cmds.keySet());
} }
Subcommand sub = Commands.get(strings[0].toLowerCase()); Subcommand sub = subs.get(strings[0].toLowerCase());
if (sub != null && glim.hasPermission(commandSender, sub.permission())) { if (sub != null) {
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();
} }
public Map<String, Subcommand> getSubs() { return subs; };
} }

View file

@ -9,6 +9,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class CurrentPolicies implements Subcommand { public class CurrentPolicies implements Subcommand {
@Override
public String name() {
return "currentpolicies";
}
@Override @Override
public boolean execute(CommandSender commandSender, String[] args) { public boolean execute(CommandSender commandSender, String[] args) {
List<Policy> policies = Validator.getPoliciesForEntity((HumanEntity) commandSender); List<Policy> policies = Validator.getPoliciesForEntity((HumanEntity) commandSender);
@ -29,19 +34,4 @@ public class CurrentPolicies implements Subcommand {
public List<String> onTabComplete(CommandSender sender, String[] args) { public List<String> onTabComplete(CommandSender sender, String[] args) {
return List.of(); return List.of();
} }
@Override
public String description() {
return "Lists policies appliying to you.";
}
@Override
public String usage() {
return "";
}
@Override
public String permission() {
return "nemsiseye.policies.list.self";
}
} }

View file

@ -1,54 +0,0 @@
package io.github.adrianvic.nemesiseye.commands.sub;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.commands.Commands;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Help implements Subcommand {
private final Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean execute(CommandSender commandSender, String[] strings) {
List<String> rstr = new ArrayList<>();
Map<String, Subcommand> allSubcommands = Commands.getAll();
if (allSubcommands.isEmpty()) {
rstr.add("No commands found.");
}
for (Map.Entry<String, Subcommand> e : allSubcommands.entrySet()) {
if (e.getValue().hasPermission(commandSender)) {
rstr.add("""
%s - %s
Usage: /eye %s %s
""".formatted(e.getKey(), e.getValue().description(), e.getKey(), e.getValue().usage()));
}
}
glim.sendMessage(commandSender, String.join("\n", rstr));
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, String[] strings) {
return List.of();
}
@Override
public String description() {
return "Lists all commands.";
}
@Override
public String usage() {
return "";
}
@Override
public String permission() {
return "nemsiseye.help";
}
}

View file

@ -8,6 +8,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ListPolicies implements Subcommand { public class ListPolicies implements Subcommand {
@Override
public String name() {
return "listpolicies";
}
@Override @Override
public boolean execute(CommandSender commandSender, String[] args) { public boolean execute(CommandSender commandSender, String[] args) {
List<String> rstr = new ArrayList<>(); List<String> rstr = new ArrayList<>();
@ -22,19 +28,4 @@ public class ListPolicies implements Subcommand {
public List<String> onTabComplete(CommandSender sender, String[] args) { public List<String> onTabComplete(CommandSender sender, String[] args) {
return List.of(); return List.of();
} }
@Override
public String description() {
return "Lists all loaded policies.";
}
@Override
public String usage() {
return "";
}
@Override
public String permission() {
return "nemsiseye.policy.list.all";
}
} }

View file

@ -1,9 +1,7 @@
package io.github.adrianvic.nemesiseye.commands.sub; package io.github.adrianvic.nemesiseye.commands.sub;
import io.github.adrianvic.nemesiseye.Config; import io.github.adrianvic.nemesiseye.Config;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -11,18 +9,22 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class PolicyInfo implements Subcommand { public class PolicyInfo implements Subcommand {
private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override
public String name() {
return "policyinfo";
}
@Override @Override
public boolean execute(CommandSender commandSender, String[] strings) { public boolean execute(CommandSender commandSender, String[] strings) {
List<Policy> policies = Config.getInstance().getPolicies(); List<Policy> policies = Config.getInstance().getPolicies();
for (Policy policy : policies) { for (Policy policy : policies) {
if (policy.name().equals(strings[0])) { if (policy.name().equals(strings[0])) {
glim.sendMessage(commandSender, String.format(""" commandSender.sendMessage(String.format("""
Showing info for policy %s%s%s: Showing info for policy "%s":
Type: %s Type: %s
Nodes: %s Nodes: %s
""", ChatColor.GREEN, policy.name(), ChatColor.WHITE, policy.getClass().getTypeName(), policy.nodes().size())); %s
""", policy.name(), "location", policy.nodes().size(), policy.allowlist() ? "Is allowlist" : "Is blacklist"));
} }
} }
return true; return true;
@ -36,19 +38,4 @@ public class PolicyInfo implements Subcommand {
} }
return rstr; return rstr;
} }
@Override
public String description() {
return "Shows info for a specified policy.";
}
@Override
public String usage() {
return "<policy>";
}
@Override
public String permission() {
return "nemsiseye.policy.info";
}
} }

View file

@ -1,16 +1,16 @@
package io.github.adrianvic.nemesiseye.commands.sub; package io.github.adrianvic.nemesiseye.commands.sub;
import io.github.adrianvic.nemesiseye.Config; import io.github.adrianvic.nemesiseye.Config;
import io.github.adrianvic.nemesiseye.commands.Commands;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List; import java.util.List;
public class Reload implements Subcommand { public class Reload implements Subcommand {
@Override @Override
public boolean execute(CommandSender commandSender, String [] strings) { public boolean execute(CommandSender commandSender, String [] strings) {
Config.getInstance().load(); Config.getInstance().load();
commandSender.sendMessage("Reloading..."); commandSender.sendMessage("Reloading...");
return true; return true;
} }
@ -20,17 +20,7 @@ public class Reload implements Subcommand {
} }
@Override @Override
public String permission() { public String name() {
return "nemsiseye.reload"; return "reload";
}
@Override
public String description() {
return "Reloads the plugin configuration file.";
}
@Override
public String usage() {
return "";
} }
} }

View file

@ -1,19 +1,12 @@
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 java.util.List; import java.util.List;
public interface Subcommand { public interface Subcommand {
String description(); String name();
String usage();
@SuppressWarnings("SameReturnValue") @SuppressWarnings("SameReturnValue")
boolean execute(CommandSender commandSender, String[] strings); boolean execute(CommandSender commandSender, String[] strings);
List<String> onTabComplete(CommandSender sender, String[] strings); List<String> onTabComplete(CommandSender sender, String[] strings);
String permission();
default boolean hasPermission(CommandSender sender) {
return Nemesis.getInstance().getGlimmer().hasPermission(sender, permission());
}
} }

View file

@ -4,10 +4,6 @@ public enum Action {
INTERACT, INTERACT,
BREAK, BREAK,
HIT, HIT,
// TODO CRAFT, CRAFT,
EQUIP, EQUIP
PLACE,
USE_ENCHANTMENT,
GLYDE,
SPAWN
} }

View file

@ -1,6 +0,0 @@
package io.github.adrianvic.nemesiseye.policy;
public enum Effect {
DENY,
ALLOW,
}

View file

@ -1,9 +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;
public interface NodeHandler { public interface NodeHandler {
boolean check(LivingEntity entity, PolicyNode node, Action action, Event event); boolean allows(HumanEntity entity, PolicyNode node, Action action);
} }

View file

@ -1,25 +1,22 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import io.github.adrianvic.nemesiseye.policy.handlers.*; import io.github.adrianvic.nemesiseye.policy.handlers.attackWith;
import io.github.adrianvic.nemesiseye.policy.handlers.useEnchantment;
import io.github.adrianvic.nemesiseye.policy.handlers.useItem;
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<String, NodeHandler> handlers = new HashMap<>();
static { static {
handlers.put(Action.HIT, new UseItem()); handlers.put("attackWithItemInHand", new attackWith());
handlers.put(Action.PLACE, new BePlaced()); handlers.put("useItem", new useItem());
handlers.put(Action.INTERACT, new UseItem()); handlers.put("useEnchantment", new useEnchantment());
handlers.put(Action.USE_ENCHANTMENT, new UseEnchantment());
handlers.put(Action.GLYDE, new Glyde());
handlers.put(Action.EQUIP, new Equip());
handlers.put(Action.SPAWN, new Spawn());
handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler
} }
public static NodeHandler get(Action type) { public static NodeHandler get(String type) {
return handlers.get(type); return handlers.get(type);
} }
} }

View file

@ -1,35 +1,13 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import java.util.List; import java.util.List;
public interface Policy { public interface Policy {
String name(); String name();
List<PolicyNode> nodes(); List<PolicyNode> nodes();
boolean policyAllowList(); boolean allowlist();
boolean applies(LivingEntity entity);
Effect effect();
int weight();
List<String> worlds();
default void addNode(PolicyNode node) { default PolicyParser getParser() {
nodes().add(node); return PolicyParsers.get("");
}
default boolean matches(LivingEntity entity, Action action, Event event) {
if (!worlds().contains(entity.getWorld().getName())) {
return false;
}
for (PolicyNode node : nodes()) {
if (node.matches(entity, action, event)) {
return true;
}
}
return false;
} }
} }

View file

@ -1,73 +1,34 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import io.github.adrianvic.nemesiseye.DataShifter;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public record PolicyNode(List<Action> actions, List<Object> values) { public record PolicyNode(String type, List<Object> values, boolean isWhitelist) {
public static List<PolicyNode> parseNodes(List<Map<Object,Object>> raw, Effect effect) { public static List<PolicyNode> parseNodes(List<Map<String,Object>> raw, boolean isWhitelist) {
List<PolicyNode> nodes = new ArrayList<>(); List<PolicyNode> nodes = new ArrayList<>();
for (Map<Object, Object> m : raw) { for (Map<String, Object> m : raw) {
for (Map.Entry<Object, Object> rawNode : m.entrySet()) { for (Map.Entry<String, Object> entry : m.entrySet()) {
List<Action> nodeActions = new ArrayList<>(); String type = entry.getKey();
List<Object> nodeValues = new ArrayList<>(); List<Object> values = new ArrayList<>();
Object val = entry.getValue();
if (rawNode.getKey() instanceof List<?> rawTypes && rawNode.getValue() instanceof Map<?,?> rawNodeValues) { if (val instanceof String s) {
for (Object rawType : rawTypes) { values.add(s);
if (rawType instanceof String rts && !rts.isEmpty() && !(rts == null) && DataShifter.enumOrDefault(Action.class, rts, null) != null) { } else if (val instanceof List<?> l) {
nodeActions.add(DataShifter.enumOrDefault(Action.class, rts, null)); values.addAll(l);
} } else if (val instanceof Map<?,?> map) {
} values.add(map);
Map<String, Object> semiParsedNodeValue = new HashMap<>();
for (Map.Entry<?,?> rawNodeValueEntries : rawNodeValues.entrySet()) {
if (rawNodeValueEntries.getKey() instanceof String stringKey) {
semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue());
}
}
if (semiParsedNodeValue.get("values") instanceof List<?> l) {
for (Object v : l) {
nodeValues.add(v);
}
}
} }
if (!nodeActions.isEmpty() && !nodeValues.isEmpty()) { nodes.add(new PolicyNode(type, values, isWhitelist));
PolicyNode newNode = new PolicyNode(nodeActions, nodeValues);
nodes.add(newNode);
}
} }
} }
return nodes; return nodes;
} }
public List<NodeHandler> getHandler() { public NodeHandler getHandler() {
List<NodeHandler> handlers = new ArrayList<>(); return NodeHandlers.get(type);
for (Action a : actions) {
handlers.add(NodeHandlers.get(a));
}
return handlers;
}
public boolean matches(LivingEntity entity, Action action, Event event) {
if (!actions.contains(action)) {
return false;
}
NodeHandler handler = NodeHandlers.get(action);
if (handler == null) {
return false;
}
boolean result = handler.check(entity, this, action, event);
return result;
} }
} }

View file

@ -1,47 +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.policy.policies.Core;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
public interface PolicyParser { public interface PolicyParser {
default Policy parse(Map<?, ?> raw) { List<Policy> parse(List<?> raw);
boolean policyAllowList = Boolean.TRUE.equals(raw.get("policyAllowList"));
boolean nodesAllowList = Boolean.TRUE.equals(raw.get("nodesAllowList"));
Effect effect = DataShifter.enumOrDefault(Effect.class, (String) raw.get("effect"), Effect.DENY);
String name = (String) raw.get("name");
Integer weightObj = (Integer) raw.get("weight");
int weight = weightObj != null ? weightObj : 0;
// Worlds
List<String> worlds = new ArrayList<>();
if (raw.get("worlds") instanceof List<?> list) {
for (Object object : list) {
if (object instanceof String result) {
worlds.add(result);
}
}
} else {
worlds.add("world");
}
// Nodes
Object rawNodes = raw.get("nodes");
List<Map<Object, Object>> nodeList = new ArrayList<>();
if (rawNodes instanceof List<?> list) {
for (Object o : list) {
if (o instanceof Map<?, ?> map)
nodeList.add((Map<Object, Object>) map);
}
}
List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, effect);
return parse(new Core(name, worlds, nodes, nodesAllowList, policyAllowList, effect, weight), raw);
}
Policy parse(Core corePolicy, Map<?, ?> raw);
} }

View file

@ -1,9 +1,6 @@
package io.github.adrianvic.nemesiseye.policy; package io.github.adrianvic.nemesiseye.policy;
import io.github.adrianvic.nemesiseye.policy.parser.GlobalPolicyParser;
import io.github.adrianvic.nemesiseye.policy.parser.LocationPolicyParser; import io.github.adrianvic.nemesiseye.policy.parser.LocationPolicyParser;
import io.github.adrianvic.nemesiseye.policy.parser.PermissionPolicyParser;
import io.github.adrianvic.nemesiseye.policy.parser.PlayerNamePolicyParser;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -12,10 +9,7 @@ public class PolicyParsers {
private static final Map<String, PolicyParser> handlers = new HashMap<>(); private static final Map<String, PolicyParser> handlers = new HashMap<>();
static { static {
handlers.put("location", new LocationPolicyParser()); handlers.put("Location", new LocationPolicyParser());
handlers.put("global", new GlobalPolicyParser());
handlers.put("playerName", new PlayerNamePolicyParser());
handlers.put("permission", new PermissionPolicyParser());
} }
public static PolicyParser get(String type) { public static PolicyParser get(String type) {

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.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import org.bukkit.event.block.BlockPlaceEvent;
import java.util.List;
public class BePlaced implements NodeHandler {
@Override
public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) {
if (event instanceof BlockPlaceEvent bpe) {
String type = bpe.getBlock().getType().toString();
List<String> parsedValue = DataShifter.parseValueToStringList(node.values());
return DataShifter.safeMatches(parsedValue, type);
}
return false;
}
}

View file

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

View file

@ -1,14 +0,0 @@
package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
public class Glyde implements NodeHandler {
@Override
public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) {
return true;
}
}

View file

@ -1,21 +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.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

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

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

@ -0,0 +1,25 @@
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;
public class attackWith implements NodeHandler {
private final static Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
if (action == Action.HIT) {
for (String s : DataShifter.parseValueToStringList(node.values())) {
boolean matches = DataShifter.safeMatches(s, glim.getItemInMainHandHumanEntity(entity).getType().toString());
if (matches) return node.isWhitelist();
}
}
return !node.isWhitelist();
}
}

View file

@ -0,0 +1,27 @@
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.inventory.ItemStack;
public class useEnchantment implements NodeHandler {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
ItemStack item = glim.getItemInMainHandHumanEntity(entity);
if (!glim.hasItemMeta(item)) return true;
if (!glim.hasAnyEnchantment(item)) return true;
boolean matches = glim.hasEnchantment(item,
DataShifter.parseValueToStringMap(node.values()));
return matches ? node.isWhitelist() : !node.isWhitelist();
}
}

View file

@ -0,0 +1,25 @@
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;
public class useItem implements NodeHandler {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
String type = glim.getItemInMainHandHumanEntity(entity).getType().toString();
for (String s : DataShifter.parseValueToStringList(node.values())) {
boolean matches = DataShifter.safeMatches(s, type);
if (matches) return node.isWhitelist();
}
return !node.isWhitelist();
}
}

View file

@ -1,15 +0,0 @@
package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParser;
import io.github.adrianvic.nemesiseye.policy.policies.Core;
import io.github.adrianvic.nemesiseye.policy.policies.GlobalPolicy;
import java.util.Map;
public class GlobalPolicyParser implements PolicyParser {
@Override
public Policy parse(Core corePolicy, Map<?, ?> raw) {
return new GlobalPolicy(corePolicy.name(), corePolicy.worlds(), corePolicy.nodes(), corePolicy.policyAllowList(), corePolicy.effect(), corePolicy.weight());
}
}

View file

@ -1,8 +1,8 @@
package io.github.adrianvic.nemesiseye.policy.parser; package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis; import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.*; import io.github.adrianvic.nemesiseye.policy.*;
import io.github.adrianvic.nemesiseye.policy.policies.Core;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy; import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Location; import org.bukkit.Location;
@ -14,49 +14,57 @@ import java.util.Map;
public class LocationPolicyParser implements PolicyParser { public class LocationPolicyParser implements PolicyParser {
private Glimmer glim = Nemesis.getInstance().getGlimmer(); private Glimmer glim = Nemesis.getInstance().getGlimmer();
public Policy parse(Core corePolicy, Map<?, ?> raw) { public List<Policy> parse(List<?> raw) {
Object rawLocations = raw.get("locations"); List<Policy> out = new ArrayList<>(raw.size());
Object rawCoordinates = null; List<Map<?, ?>> parsedRawMap = DataShifter.parseValueToListOfMaps(raw);
List<String> worlds = new ArrayList<>();
if (rawLocations instanceof Map<?,?> rawLocationMap) {
rawCoordinates = rawLocationMap.get("coordinates");
if (rawLocationMap.get("worlds") instanceof List<?> rawWorldsList) { for (Map<?, ?> m : parsedRawMap) {
for (Object worldObject : rawWorldsList) { String name = (String) m.get("name");
if (worldObject instanceof String worldString) { boolean allowlist = Boolean.TRUE.equals(m.get("allowList"));
worlds.add(worldString);
} // Nodes
Object rawNodes = m.get("nodes");
List<Map<String, Object>> nodeList = new ArrayList<>();
if (rawNodes instanceof List<?> list) {
for (Object o : list) {
if (o instanceof Map<?, ?> map)
nodeList.add((Map<String, Object>) map);
} }
} else {
worlds.add("world");
} }
List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, allowlist);
// Parsing locations
List<ArrayList<Glimmer.Box>> locations = new ArrayList<>();
Object rawLocations = m.get("locations");
List<?> groups = rawLocations instanceof List ? (List<?>) rawLocations : List.of();
ArrayList<Glimmer.Box> boxes = new ArrayList<>(groups.size());
// Now iterate over regions
for (Object rObj : groups) {
Map<?, ?> region = (Map<?, ?>) rObj;
Map<?, ?> c1 = (Map<?, ?>) region.get("corner1");
Map<?, ?> c2 = (Map<?, ?>) region.get("corner2");
double x1 = ((Number) c1.get("x")).doubleValue();
double y1 = ((Number) c1.get("y")).doubleValue();
double z1 = ((Number) c1.get("z")).doubleValue();
double x2 = ((Number) c2.get("x")).doubleValue();
double y2 = ((Number) c2.get("y")).doubleValue();
double z2 = ((Number) c2.get("z")).doubleValue();
Location loc1 = new Location(glim.getWorlds().getFirst(), x1, y1, z1);
Location loc2 = new Location(glim.getWorlds().getFirst(), x2, y2, z2);
boxes.add(Glimmer.Box.of(loc1, loc2));
}
locations.add(boxes);
out.add(new LocationPolicy(name, locations, nodes, allowlist));
} }
return out;
List<Glimmer.Box> locations = new ArrayList<>();
// Parsing locations
List<?> groups = rawCoordinates instanceof List ? (List<?>) rawCoordinates : List.of();
// Now iterate over regions
for (Object rObj : groups) {
Map<?, ?> region = (Map<?, ?>) rObj;
Map<?, ?> c1 = (Map<?, ?>) region.get("corner1");
Map<?, ?> c2 = (Map<?, ?>) region.get("corner2");
double x1 = ((Number) c1.get("x")).doubleValue();
double y1 = ((Number) c1.get("y")).doubleValue();
double z1 = ((Number) c1.get("z")).doubleValue();
double x2 = ((Number) c2.get("x")).doubleValue();
double y2 = ((Number) c2.get("y")).doubleValue();
double z2 = ((Number) c2.get("z")).doubleValue();
Location loc1 = new Location(glim.getWorlds().getFirst(), x1, y1, z1);
Location loc2 = new Location(glim.getWorlds().getFirst(), x2, y2, z2);
locations.add(Glimmer.Box.of(loc1, loc2));
}
return new LocationPolicy(corePolicy.name(), corePolicy.worlds(), locations, corePolicy.nodes(), corePolicy.nodeAllowlist(), corePolicy.policyAllowList(), corePolicy.effect(), corePolicy.weight());
} }
} }

View file

@ -1,28 +0,0 @@
package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParser;
import io.github.adrianvic.nemesiseye.policy.policies.Core;
import io.github.adrianvic.nemesiseye.policy.policies.PermissionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class PermissionPolicyParser implements PolicyParser {
@Override
public Policy parse(Core corePolicy, Map<?, ?> raw) {
Object rawPerms = raw.get("permissions");
List<String> permissions = new ArrayList<>();
if (rawPerms instanceof List<?> list) {
for (Object o : list) {
if (o instanceof String s) {
permissions.add(s);
}
}
}
return new PermissionPolicy(corePolicy.name(), corePolicy.worlds(), permissions, corePolicy.nodes(), corePolicy.policyAllowList(), corePolicy.effect(), corePolicy.weight());
}
}

View file

@ -1,28 +0,0 @@
package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParser;
import io.github.adrianvic.nemesiseye.policy.policies.Core;
import io.github.adrianvic.nemesiseye.policy.policies.PlayerNamePolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class PlayerNamePolicyParser implements PolicyParser {
@Override
public Policy parse(Core corePolicy, Map<?, ?> raw) {
Object rawNames = raw.get("names");
List<String> names = new ArrayList<>();
if (rawNames instanceof List<?> list) {
for (Object o : list) {
if (o instanceof String s) {
names.add(s);
}
}
}
return new PlayerNamePolicy(corePolicy.name(), corePolicy.worlds(), names, corePolicy.nodes(), corePolicy.effect(), corePolicy.policyAllowList(), corePolicy.weight());
}
}

View file

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

View file

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

View file

@ -1,22 +1,10 @@
package io.github.adrianvic.nemesiseye.policy.policies; package io.github.adrianvic.nemesiseye.policy.policies;
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 io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.LivingEntity;
import java.util.ArrayList;
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<ArrayList<Glimmer.Box>> locations, List<PolicyNode> nodes, boolean allowlist) implements Policy {}
@Override
public boolean applies(LivingEntity entity) {
for (Glimmer.Box box : locations) {
if (box.contains(entity.getLocation().toVector(), entity.getWorld())) {
return !policyAllowList;
}
}
return policyAllowList;
}
}

View file

@ -1,24 +1,8 @@
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.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.entity.HumanEntity; import org.bukkit.permissions.Permission;
import org.bukkit.entity.LivingEntity;
import java.util.List; import java.util.ArrayList;
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, ArrayList<Permission> permissions, PolicyNode nodes, boolean allowlist) {}
@Override
public boolean applies(LivingEntity entity) {
for (String perm : permissions) {
if (Nemesis.getInstance().getGlimmer().hasPermission(entity, perm)) {
return true;
}
}
return false;
}
}

View file

@ -1,21 +1,7 @@
package io.github.adrianvic.nemesiseye.policy.policies; package io.github.adrianvic.nemesiseye.policy.policies;
import io.github.adrianvic.nemesiseye.policy.Effect;
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.LivingEntity;
import java.util.List; import java.util.ArrayList;
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, ArrayList<String> playerName, PolicyNode nodes, boolean allowlist) {}
@Override
public boolean applies(LivingEntity entity) {
if (playerName.contains(entity.getName())) {
return !policyAllowList();
} else {
return policyAllowList();
}
}
}

View file

@ -3,7 +3,6 @@ package io.github.adrianvic.nemesiseye.reflection;
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;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
@ -11,49 +10,28 @@ import org.bukkit.util.Vector;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
public interface Glimmer { public interface Glimmer {
void onLoad();
List<World> getWorlds();
// Configuration
File loadConfigFile(); File loadConfigFile();
List<Policy> loadPoliciesFromFile(File file); List<Policy> loadPoliciesFromFile(File file);
List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies);
// Items void onLoad();
ItemStack getItemInMainHandHumanEntity(HumanEntity entity);
boolean hasItemMeta(ItemStack item); boolean hasItemMeta(ItemStack item);
List<World> getWorlds();
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);
// Players
boolean isGliding(org.bukkit.entity.Player player);
void setGliding(org.bukkit.entity.Player player, boolean gliding);
boolean hasPermission(org.bukkit.command.CommandSender sender, String permission);
// Events
boolean isArmorEquipAttempt(org.bukkit.event.Event event);
ItemStack getEquippedItem(org.bukkit.event.Event event);
// Commands
void sendMessage(CommandSender commandSender, String text);
class Box { class Box {
public final double x1, y1, z1, x2, y2, z2; public final double x1, y1, z1, x2, y2, z2;
public final String world;
public Box(String world, double x1, double y1, double z1, double x2, double y2, double z2) { public Box(double x1, double y1, double z1, double x2, double y2, double z2) {
this.x1 = Math.min(x1, x2); this.x1 = Math.min(x1, x2);
this.y1 = Math.min(y1, y2); this.y1 = Math.min(y1, y2);
this.z1 = Math.min(z1, z2); this.z1 = Math.min(z1, z2);
this.x2 = Math.max(x1, x2); this.x2 = Math.max(x1, x2);
this.y2 = Math.max(y1, y2); this.y2 = Math.max(y1, y2);
this.z2 = Math.max(z1, z2); this.z2 = Math.max(z1, z2);
this.world = world;
} }
public boolean contains(double x, double y, double z) { public boolean contains(double x, double y, double z) {
@ -61,22 +39,14 @@ public interface Glimmer {
&& y >= y1 && y <= y2 && y >= y1 && y <= y2
&& z >= z1 && z <= z2; && z >= z1 && z <= z2;
} }
public boolean contains(Vector v) { public boolean contains(Vector v) {
return v.getX() >= x1 && v.getX() <= x2 return v.getX() >= x1 && v.getX() <= x2
&& v.getY() >= y1 && v.getY() <= y2 && v.getY() >= y1 && v.getY() <= y2
&& v.getZ() >= z1 && v.getZ() <= z2; && v.getZ() >= z1 && v.getZ() <= z2;
} }
public boolean contains(Vector v, String w) {
return (Objects.equals(w, world)) && contains(v);
}
public boolean contains(Vector v, World w) { public static Box of(Location loc1, Location loc2) { return new Box(loc1.getX(), loc1.getY(), loc1.getZ(), loc2.getX(), loc2.getY(), loc2.getZ()); }
return contains(v, w.getName());
}
public static Box of(Location loc1, Location loc2) { return new Box(loc1.getWorld().getName(), loc1.getX(), loc1.getY(), loc1.getZ(), loc2.getX(), loc2.getY(), loc2.getZ()); }
} }
} }

View file

@ -1,171 +0,0 @@
package io.github.adrianvic.nemesiseye.reflection;
import io.github.adrianvic.nemesiseye.DataShifter;
import org.bukkit.Bukkit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class VersionMatcher {
public VersionMatcher() {}
private record Entry(String pattern, String classSuffix) {}
public String getVersion(String type, String serverVersion) {
if (type == null || serverVersion == null) {
return "";
}
Map<String, List<Entry>> map = populateMap();
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;
}
}
List<Entry> sorted = new ArrayList<>(entries);
Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern));
Entry oldest = sorted.get(0);
Entry newest = sorted.get(sorted.size() - 1);
int cmpOldest = compareVersions(serverVersion, oldest.pattern);
int cmpNewest = compareVersions(serverVersion, newest.pattern);
if (cmpOldest < 0) {
return oldest.pattern + "|" + oldest.classSuffix;
} else if (cmpNewest > 0) {
return newest.pattern + "|" + newest.classSuffix;
}
// should not happen because we already tried all patterns
return newest.pattern + "|" + newest.classSuffix;
}
private Map<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 = null;
try {
rawVersion = Bukkit.getMinecraftVersion();
} catch (NoSuchMethodError ignored) {}
if (rawVersion == null || rawVersion.isEmpty()) {
String v = Bukkit.getVersion(); // e.g. "git-Bukkit-0.0.0-1060-... (MC: 1.7.3)"
int start = v.lastIndexOf("MC: ");
if (start != -1) {
rawVersion = v.substring(start + 4, v.length() - 1);
} else {
rawVersion = "unknown";
}
}
String matchInfo = getVersion("release", rawVersion);
if (matchInfo.isEmpty()) {
matchInfo = getVersion("beta", rawVersion);
}
if (matchInfo.isEmpty()) {
// Fallback to b1.7.3 if everything fails, or throw error?
// User said it supports b1.7.3 and 1.21.x.
Glimmer fallback = tryInstantiate("io.github.adrianvic.nemesiseye.impl.b1_7_3");
if (fallback != null) return fallback;
throw new IllegalStateException("No suitable implementation found for version " + rawVersion);
}
String[] partsInfo = matchInfo.split("\\|");
String classSuffix = partsInfo[1];
Glimmer glimmer = tryInstantiate("io.github.adrianvic.nemesiseye.impl." + classSuffix);
if (glimmer != null) return glimmer;
// Backward search for older implementations
String[] versionParts = rawVersion.split("\\.");
int major = parseInt(versionParts[0]);
int minor = versionParts.length > 1 ? parseInt(versionParts[1]) : 0;
int patch = versionParts.length > 2 ? parseInt(versionParts[2]) : 0;
while (major >= 0) {
while (minor >= 0) {
while (patch >= 0) {
String className = String.format("io.github.adrianvic.nemesiseye.impl.r%d_%d_%d", major, minor, patch);
glimmer = tryInstantiate(className);
if (glimmer != null) return glimmer;
className = String.format("io.github.adrianvic.nemesiseye.impl.r%d_%d", major, minor);
glimmer = tryInstantiate(className);
if (glimmer != null) return glimmer;
patch--;
}
minor--;
patch = 20; // Search up to .20 patch of previous minor
}
major--;
minor = 30; // Search up to .30 minor of previous major
}
throw new IllegalStateException("No suitable implementation found for version " + rawVersion);
}
private Glimmer tryInstantiate(String className) {
try {
Class<?> clazz = Class.forName(className, true, getClass().getClassLoader());
if (!Glimmer.class.isAssignableFrom(clazz)) {
return null;
}
return (Glimmer) clazz.getDeclaredConstructor().newInstance();
} catch (ReflectiveOperationException ignored) {
return null;
}
}
}

View file

@ -1,19 +0,0 @@
name: "Eye-of-Nemesis"
version: ${version}
main: io.github.adrianvic.nemesiseye.Nemesis
api-version: '1.13'
author: 'Adrian Victor'
website: "https://github.io/adrianvic/NemesisEye"
description: "Change what players can do based in custom criteria."
commands:
eye:
usage: "/eye <option> (help for all available options)"
permissions:
nemesiseye.reload:
default: op
nemesiseye.policy.list.all:
default: op
nemesiseye.policy.list.self:
default: true
nemesiseye.help:
default: true

View file

@ -1,171 +1,121 @@
# __ _______ __ ___________________ _______
# / / \ _ \ \ \ \_ _____/\_____ \ \ \
# / / / /_\ \ \ \ | __)_ / | \ / | \
# \ \ \ \_/ \ / / | \/ | \/ | \
# \_\ \_____ / /_/ /_______ /\_______ /\____|__ /
# \/ \/ \/ \/
# EYE OF NEMESIS - Example config file.
# Documentation in our wiki: https://github.com/adrianvic/NemesisEye
Policies: Policies:
- name: "Bedrock-allow-admins" - Location:
type: "permission" # NO SPACES
worlds: [world] - name: "Beta-1.7.3-items-only"
effect: ALLOW # Will deny anything that's not allowed by the nodes if set to true
weight: 3 allowList: true
permissions: nodes:
- "server.usebedrock" - useItem:
nodes: - AIR
- [BREAK, PLACE, HIT, INTERACT]: - STONE
values: - COBBLESTONE
- BEDROCK - "^(OAK|SPRUCE|BIRCH)_(LOG|SAPLING|PLANKS|LEAVES)$"
- "^(DIAMOND|GOLD|IRON|COAL|LAPIS|REDSTONE)_ORE$"
- name: "Bedrock-deny" - "^(DIAMOND|GOLD|IRON|LAPIS)_BLOCK$"
type: "global" - GRAVEL
worlds: [world] - BEDROCK
effect: DENY - SAND
weight: 2 - SPONGE
nodes: - WET_SPONGE
- [BREAK, PLACE, HIT, INTERACT]: - GLASS
values: - LAPIS_LAZULI
- BEDROCK - COBWEB
- PISTON
- name: "Block-non-beta-items" # No spaces here or else commands which need the name as argument will not work - STICKY_PISTON
type: "location" # global | location | permission | playerName - GRASS
effect: DENY # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met) - DISPENSER
worlds: [world] - NOTE_BLOCK
weight: 0 # Greater weight overrides lower - SANDSTONE
policyAllowList: false # Inverts the policy validation logic, for example a location policy will affect all players NOT inside it's location - RED_BED
locations: - "^(POWERED|DETECTOR)_RAIL$"
worlds: [world] - RAIL
coordinates: - SHORT_GRASS
- corner1: { x: 2100, y: 256, z: 1400 } - "^(WHITE|BLACK|GREEN|YELLOW|PINK|PURPLE|CYAN|BLUE|RED|LIME|BROWN|LIGHT_GRAY|GRAY)_(WOOL|DYE)$"
corner2: { x: 1000, y: -64, z: 2200 } - POPPY
nodes: - DANDELION
- [INTERACT, BREAK, HIT, PLACE]: - "^(RED|BROWN)_MUSHROOM$"
values: - "^(OAK|COBBLESTONE)_SLAB$"
- '.*' - BRICK_BLOCK
- [USE_ENCHANTMENT]: - TNT
values: - BOOKSHELF
- ".*": ".*" - OBSIDIAN
- MOSSY_COBBLESTONE
- name: "Allow-beta-items" - TORCH
type: "location" - SPAWNER
effect: ALLOW - REDSTONE
weight: 1 - CHEST
worlds: [world] - CRAFTING_TABLE
locations: - FARMLAND
coordinates: - FURNACE
- corner1: { x: 2100, y: 256, z: 1400 } - SIGN
corner2: { x: 1000, y: -64, z: 2200 } - LADDER
nodes: - "^(COBBLESTONE|OAK)_STAIRS$"
- [INTERACT, BREAK, HIT, PLACE]: - LEVER
values: - "^(OAK|STONE)_PRESSURE_PLATE$"
- AIR - "^(OAK|IRON)_DOOR$"
- STONE - BLUE_ICE
- COBBLESTONE - REDSTONE_TORCH
- "^(OAK|SPRUCE|BIRCH)_(LOG|SAPLING|PLANKS|LEAVES)$" - STONE_BUTTON
- "^(DIAMOND|GOLD|IRON|COAL|LAPIS|REDSTONE)_ORE$" - SNOW
- "^(DIAMOND|GOLD|IRON|LAPIS)_BLOCK$" - SNOW_BLOCK
- GRAVEL - CLAY
- BEDROCK - SUGAR_CANE
- SAND - JUKEBOX
- SPONGE - OAK_FENCE
- WET_SPONGE - PUMPKIN
- GLASS - NETHERRACK
- LAPIS_LAZULI - SOUL_SAND
- COBWEB - GLOWSTONE
- PISTON - JACK_O_LANTERN
- STICKY_PISTON - CAKE
- GRASS_BLOCK - REPEATER
- DISPENSER - OAK_TRAPDOOR
- NOTE_BLOCK - "^(IRON|STONE|DIAMOND|WOODEN|GOLDEN)_(SHOVEL|AXE|PICKAXE|SWORD|HOE)$"
- SANDSTONE - "^(IRON|LEATHER|DIAMOND|GOLDEN)_(HELMET|CHESTPLATE|LEGGINGS|BOOTS)$"
- RED_BED - STICK
- "^(POWERED|DETECTOR)_RAIL$" - BOWL
- RAIL - MUSHROOM_STEW
- SHORT_GRASS - FEATHER
- "^(WHITE|BLACK|GREEN|YELLOW|PINK|PURPLE|CYAN|BLUE|RED|LIME|BROWN|LIGHT_GRAY|GRAY)_(WOOL|DYE)$" - STRING
- POPPY - GUNPOWDER
- DANDELION - WHEAT_SEEDS
- "^(RED|BROWN)_MUSHROOM$" - WHEAT
- "^(OAK|COBBLESTONE|SMOOTH_STONE)_SLAB$" - FLINT
- BRICK_BLOCK - FLINT_AND_STEEL
- TNT - PORKCHOP
- BOOKSHELF - "^(COOKED|RAW)_(PORKCHOP|FISH)$"
- OBSIDIAN - PAINTING
- MOSSY_COBBLESTONE - GOLDEN_APPLE
- TORCH - BUCKET
- SPAWNER - "^(LAVA|MILK|WATER)_BUCKET$"
- REDSTONE - MINECART
- CHEST - SADDLE
- CRAFTING_TABLE - SNOWBALL
- FARMLAND - OAK_BOAT
- FURNACE - LEATHER
- SIGN - "^(FURNACE|CHEST)_MINECART$"
- LADDER - EGG
- "^(COBBLESTONE|OAK)_STAIRS$" - BOOK
- LEVER - PAPER
- "^(OAK|STONE)_PRESSURE_PLATE$" - BRICK
- "^(OAK|IRON)_DOOR$" - SLIME_BALL
- BLUE_ICE - COMPASS
- REDSTONE_TORCH - FISHING_ROD
- STONE_BUTTON - CLOCK
- SNOW - GLOWSTONE_DUST
- SNOW_BLOCK - INK_SACw
- CLAY - BONE_MEAL
- SUGAR_CANE - SUGAR
- JUKEBOX - COOKIE
- OAK_FENCE - MAP
- PUMPKIN - FILLED_MAP
- NETHERRACK - SHEARS
- SOUL_SAND - MUSIC_DISK_CAT
- GLOWSTONE - MUSIC_DISK_13
- JACK_O_LANTERN - DIRT
- CAKE - BREAD
- REPEATER - useEnchantment:
- OAK_TRAPDOOR "theresnoenchantmentwiththisname": "3"
- "^(IRON|STONE|DIAMOND|WOODEN|GOLDEN)_(SHOVEL|AXE|PICKAXE|SWORD|HOE)$" locations:
- "^(IRON|LEATHER|DIAMOND|GOLDEN)_(HELMET|CHESTPLATE|LEGGINGS|BOOTS)$" - corner1: { x: 2100, y: 256, z: 1400 }
- STICK corner2: { x: 1000, y: -64, z: 2200 }
- BOWL
- MUSHROOM_STEW
- FEATHER
- STRING
- GUNPOWDER
- WHEAT_SEEDS
- WHEAT
- FLINT
- FLINT_AND_STEEL
- PORKCHOP
- "^(COOKED|RAW)_(PORKCHOP|FISH)$"
- PAINTING
- GOLDEN_APPLE
- BUCKET
- "^(LAVA|MILK|WATER)_BUCKET$"
- MINECART
- SADDLE
- SNOWBALL
- OAK_BOAT
- LEATHER
- "^(FURNACE|CHEST)_MINECART$"
- EGG
- BOOK
- PAPER
- BRICK
- SLIME_BALL
- COMPASS
- FISHING_ROD
- CLOCK
- GLOWSTONE_DUST
- INK_SAC
- BONE_MEAL
- SUGAR
- COOKIE
- MAP
- FILLED_MAP
- SHEARS
- MUSIC_DISK_CAT
- MUSIC_DISK_13
- DIRT
- BREAD

View file

@ -2,16 +2,10 @@ package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Events; import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
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.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.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
public class EventListener implements Listener { public class EventListener implements Listener {
@EventHandler @EventHandler
@ -28,18 +22,4 @@ public class EventListener implements Listener {
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
Events.onEntityDamageByEntityEvent(event); Events.onEntityDamageByEntityEvent(event);
} }
}
@EventHandler
public void onBlockPlaceEvent(BlockPlaceEvent event) {
Events.onBlockPlaceEvent(event);
}
@EventHandler
public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); }
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event); }
@EventHandler
public void onCreatureSpawnEvent(CreatureSpawnEvent event) { Events.onCreatureSpawnEvent(event); }
}

View file

@ -1,6 +1,7 @@
package io.github.adrianvic.nemesiseye.impl.commands; package io.github.adrianvic.nemesiseye.impl.commands;
import io.github.adrianvic.nemesiseye.commands.EyeCore; import io.github.adrianvic.nemesiseye.commands.EyeCore;
import io.github.adrianvic.nemesiseye.commands.sub.*;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -11,7 +12,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
public class Eye implements CommandExecutor, TabCompleter { public class Eye implements CommandExecutor, TabCompleter {
private final EyeCore core; private EyeCore core;
public Eye() { public Eye() {
core = new EyeCore(); core = new EyeCore();

View file

@ -5,10 +5,10 @@ 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.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.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@SuppressWarnings("unused")
public class r1_21 implements Glimmer { public class r1_21 implements Glimmer {
@Override @Override
public File loadConfigFile() { public File loadConfigFile() {
@ -45,16 +44,34 @@ public class r1_21 implements Glimmer {
List<Map<?, ?>> rawPolicies = config.getMapList("Policies"); List<Map<?, ?>> rawPolicies = config.getMapList("Policies");
List<Policy> allPolicies = new ArrayList<>(); List<Policy> allPolicies = new ArrayList<>();
for (Map<?, ?> map : rawPolicies) {
for (Map<?, ?> policyMap : rawPolicies) { for (Map.Entry<?, ?> entry : map.entrySet()) {
if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) { if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
allPolicies.add(PolicyParsers.get(type).parse(policyMap)); List<Policy> parsed = PolicyParsers.get(k).parse(v);
allPolicies.addAll(parsed);
}
} }
} }
return allPolicies; return allPolicies;
} }
@Override
public List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies) {
List<Policy> applyingLPS = new ArrayList<>();
for (Policy p : policies) {
if (p instanceof LocationPolicy lp) {
for (ArrayList<Box> boxes : lp.locations()) {
for (Box box : boxes) {
if (box.contains(entity.getLocation().toVector())) {
applyingLPS.add(lp);
}
}
}
}
}
return applyingLPS;
}
@Override @Override
public void onLoad() { public void onLoad() {
PluginManager pm = Nemesis.getInstance().getPluginManager(); PluginManager pm = Nemesis.getInstance().getPluginManager();
@ -68,91 +85,11 @@ public class r1_21 implements Glimmer {
} }
@Override @Override
public boolean isAir(ItemStack item) { public boolean hasItemMeta(ItemStack item) {
return item == null || item.getType().isAir(); if (item.getItemMeta() == null) {
}
@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; return false;
} }
return true;
if (e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) {
return true;
}
if (e.isShiftClick()) {
return isArmor(e.getCurrentItem());
}
if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY
&& e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR
&& e.getWhoClicked() instanceof org.bukkit.entity.Player player) {
return isArmor(
player.getInventory().getItem(e.getHotbarButton())
);
}
return false;
}
@Override
public ItemStack getEquippedItem(org.bukkit.event.Event event) {
if (event instanceof org.bukkit.event.inventory.InventoryClickEvent e) {
org.bukkit.event.inventory.InventoryType.SlotType slotType = e.getSlotType();
if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY // hotbar key swap
&& slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR
&& e.getWhoClicked() instanceof org.bukkit.entity.Player player) {
return player.getInventory().getItem(e.getHotbarButton());
}
if (e.isShiftClick()) {
ItemStack current = e.getCurrentItem();
if (isArmor(current)) return current;
}
// regular click
if (slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) {
ItemStack cursor = e.getCursor();
if (isArmor(cursor)) return cursor;
}
}
// Try Paper's PlayerArmorChangeEvent via reflection or just check if class exists
try {
if (event instanceof com.destroystokyo.paper.event.player.PlayerArmorChangeEvent e) {
return e.getNewItem();
}
} catch (NoClassDefFoundError | Exception ignored) {}
return null;
}
@Override
public void sendMessage(CommandSender commandSender, String text) {
commandSender.sendMessage(text);
}
@Override
public boolean hasItemMeta(ItemStack item) {
return item.getItemMeta() != null;
} }
@Override @Override
@ -162,17 +99,10 @@ public class r1_21 implements Glimmer {
@Override @Override
public boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap) { public boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap) {
Map<Enchantment, Integer> enchantments = item.getEnchantments(); Map<Enchantment, Integer> enchantmentList = item.getEnchantments();
for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentList.entrySet()) {
for (Map.Entry<Enchantment, Integer> ench : enchantments.entrySet()) { for (Map.Entry<String, String> valueEntry : valuesmap.entrySet()) {
String enchKey = ench.getKey().getKey().getKey(); if (enchantmentEntry.getKey().getKey().getKey().equals(valueEntry.getKey()) && enchantmentEntry.getValue().toString().equals(valueEntry.getValue())) {
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 true;
} }
} }
@ -183,19 +113,4 @@ 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,10 @@
name: "Eye-of-Nemesis"
version: '1.0.3-SNAPSHOT'
main: io.github.adrianvic.nemesiseye.Nemesis
api-version: '1.21'
author: 'Adrian Victor'
website: "https://github.io/adrianvic/NemesisEye"
description: "Change what players can do based in custom criteria."
commands:
eye:
usage: "/eye <option> (help for all available options)"

View file

@ -0,0 +1 @@
impl.version=r1_21

View file

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

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