diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
new file mode 100644
index 0000000..149d2f5
--- /dev/null
+++ b/.forgejo/workflows/build.yml
@@ -0,0 +1,43 @@
+name: Build
+
+on:
+ push:
+ paths:
+ - 'src/**'
+ - '.github/**'
+ - 'build.gradle.kts'
+ - 'gradle.properties'
+ - 'settings.gradle'
+
+jobs:
+ build:
+ runs-on: 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
diff --git a/.forgejo/workflows/release-build.yml b/.forgejo/workflows/release-build.yml
new file mode 100644
index 0000000..392e32e
--- /dev/null
+++ b/.forgejo/workflows/release-build.yml
@@ -0,0 +1,40 @@
+name: Release Build
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build-and-release:
+ runs-on: 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 }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c3c036b..0e8199d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,6 +1,7 @@
name: Build
on:
+ workflow_dispatch:
push:
paths:
- 'src/**'
@@ -38,6 +39,6 @@ jobs:
- name: Upload artifacts
- uses: actions/upload-artifact@v6.0.0
+ uses: actions/upload-artifact@v3
with:
path: build/libs/*.jar
diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml
index dcc83d3..9570712 100644
--- a/.github/workflows/release-build.yml
+++ b/.github/workflows/release-build.yml
@@ -32,9 +32,10 @@ jobs:
- name: Build with Gradle
run: ./gradlew buildAll
- - name: Upload JARs to release
- uses: softprops/action-gh-release@v2.5.0
- with:
- files: build/libs/*.jar
- tag_name: ${{ github.ref_name }}
- name: ${{ github.ref_name }}
+ - name: Upload artifacts to release
+ run: |
+ curl -X POST \
+ -H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
+ -H "Content-Type: application/octet-stream" \
+ https://inspiran.beetal-castor.ts.net/git/api/v1/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}/assets \
+ --data-binary @build/libs/your-artifact.jar
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 026e990..1e3abdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
build/
out/
.idea/
-libs/
\ No newline at end of file
+libs/
+run/
diff --git a/README.md b/README.md
index e771bc2..bf357cf 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,6 @@
-
+
[](https://modrinth.com/plugin/eye-of-nemesis)
-[](https://github.com/adrianvic/NemesisEye/actions/workflows/build.yml)
-[](https://github.com/adrianvic/NemesisEye/releases)
[](https://github.com/adrianvic/NemesisEye/wiki)
[](https://mgr.rf.gd/w/Eye_of_Nemesis)
diff --git a/build.gradle.kts b/build.gradle.kts
index 4aabc3c..e2d9a2e 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -59,9 +59,24 @@ mcVersions.forEach { ver ->
/* ----------------------------------------- */
dependencies {
- add("compileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
- add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
+ add("compileOnly", "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
+ add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
add("b1_7_3CompileOnly", files("libs/craftbukkit-1060.jar"))
+
+ testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
+ testImplementation("org.mockito:mockito-core:5.5.0")
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+ testImplementation("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
+ testImplementation("com.github.seeseemelk:MockBukkit-v1.21:3.102.0")
+
+ // Allow tests to see the versioned implementations
+ mcVersions.forEach { ver ->
+ testImplementation(sourceSets[ver].output)
+ }
+}
+
+tasks.test {
+ useJUnitPlatform()
}
/* ----------------------------------------- */
@@ -89,6 +104,25 @@ tasks.register("buildAll") {
dependsOn(tasks.withType())
}
+tasks.register("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 */
/* ----------------------------------------- */
@@ -103,4 +137,18 @@ tasks.withType {
tasks.runServer {
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")
}
\ No newline at end of file
diff --git a/docs/Nodes.md b/docs/Nodes.md
new file mode 100644
index 0000000..57cff1d
--- /dev/null
+++ b/docs/Nodes.md
@@ -0,0 +1,34 @@
+# 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.
\ No newline at end of file
diff --git a/docs/Policies.md b/docs/Policies.md
new file mode 100644
index 0000000..21d468d
--- /dev/null
+++ b/docs/Policies.md
@@ -0,0 +1,89 @@
+# 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.
diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java
index 19d08fb..0a86ce9 100644
--- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java
+++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java
@@ -2,9 +2,13 @@ package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.impl.commands.Eye;
+import io.github.adrianvic.nemesiseye.impl.events.BlockEventListener;
+import io.github.adrianvic.nemesiseye.impl.events.EntityEventListener;
+import io.github.adrianvic.nemesiseye.impl.events.PlayerEventListener;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
+import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.HumanEntity;
@@ -72,6 +76,38 @@ public class b1_7_3 implements Glimmer {
return entity.getItemInHand();
}
+ @Override
+ public boolean isAir(ItemStack item) {
+ return item == null || item.getType() == Material.AIR;
+ }
+
+ @Override
+ public boolean isGliding(org.bukkit.entity.Player player) {
+ return false;
+ }
+
+ @Override
+ public void setGliding(org.bukkit.entity.Player player, boolean gliding) {
+ }
+
+ @Override
+ public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) {
+ if (sender instanceof org.bukkit.entity.Player p) {
+ return p.isOp();
+ }
+ return true; // Console always has permission
+ }
+
+ @Override
+ public boolean isArmorEquipAttempt(org.bukkit.event.Event event) {
+ return false;
+ }
+
+ @Override
+ public ItemStack getEquippedItem(org.bukkit.event.Event event) {
+ return null;
+ }
+
@Override
public void sendMessage(CommandSender commandSender, String text) {
String[] lines = text.split("\\r?\\n");
@@ -99,4 +135,18 @@ public class b1_7_3 implements Glimmer {
public boolean hasAnyEnchantment(ItemStack itemStack) {
return false;
}
+
+ @Override
+ public boolean isArmor(ItemStack item) {
+ if (item == null || item.getType() == Material.AIR) {
+ return false;
+ }
+
+ String name = item.getType().name();
+
+ return name.endsWith("_HELMET")
+ || name.endsWith("_CHESTPLATE")
+ || name.endsWith("_LEGGINGS")
+ || name.endsWith("_BOOTS");
+ }
}
diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/BlockEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java
similarity index 59%
rename from src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/BlockEventListener.java
rename to src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java
index bcad2f3..6ea38bf 100644
--- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/BlockEventListener.java
+++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java
@@ -1,8 +1,9 @@
-package io.github.adrianvic.nemesiseye.impl;
+package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockListener;
+import org.bukkit.event.block.BlockPlaceEvent;
public class BlockEventListener extends BlockListener {
@Override
@@ -10,4 +11,8 @@ public class BlockEventListener extends BlockListener {
Events.onBlockBreak(event);
}
+ @Override
+ public void onBlockPlace(BlockPlaceEvent event) {
+ Events.onBlockPlaceEvent(event);
+ }
}
diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/EntityEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java
similarity index 89%
rename from src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/EntityEventListener.java
rename to src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java
index cf49da1..e6655f8 100644
--- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/EntityEventListener.java
+++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java
@@ -1,4 +1,4 @@
-package io.github.adrianvic.nemesiseye.impl;
+package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/PlayerEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java
similarity index 86%
rename from src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/PlayerEventListener.java
rename to src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java
index b77a56a..ec81484 100644
--- a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/PlayerEventListener.java
+++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java
@@ -1,4 +1,4 @@
-package io.github.adrianvic.nemesiseye.impl;
+package io.github.adrianvic.nemesiseye.impl.events;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.player.PlayerInteractEvent;
diff --git a/src/b1_7_3/resources/plugin.yml b/src/b1_7_3/resources/plugin.yml
deleted file mode 100644
index bdb6b5a..0000000
--- a/src/b1_7_3/resources/plugin.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: "Eye-of-Nemesis"
-version: ${version}
-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."
-permissions:
- nemesiseye.reload:
- default: op
- nemesiseye.policy.list.all:
- default: op
- nemesiseye.policy.list.self:
- default: true
- nemesiseye.help:
- default: true
\ No newline at end of file
diff --git a/src/b1_7_3/resources/settings.yml b/src/b1_7_3/resources/settings.yml
deleted file mode 100644
index 09921c7..0000000
--- a/src/b1_7_3/resources/settings.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-Policies:
- # NO SPACES
- - name: "Block-illegal-items"
- type: global # global / location / permission / list of types
- allowList: false # Will deny anything that's not allowed by the nodes if set to true
- nodes:
- - useItem:
- value:
- - SAND
\ No newline at end of file
diff --git a/src/b1_7_3/resources/version.properties b/src/b1_7_3/resources/version.properties
deleted file mode 100644
index 5c6ec00..0000000
--- a/src/b1_7_3/resources/version.properties
+++ /dev/null
@@ -1 +0,0 @@
-impl.version=b1_7_3
\ No newline at end of file
diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Config.java b/src/main/java/io/github/adrianvic/nemesiseye/Config.java
index 52bc4dc..69ec351 100644
--- a/src/main/java/io/github/adrianvic/nemesiseye/Config.java
+++ b/src/main/java/io/github/adrianvic/nemesiseye/Config.java
@@ -1,24 +1,28 @@
package io.github.adrianvic.nemesiseye;
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 java.io.File;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
public class Config {
private static Config instance = new Config();
private final Glimmer glim = Nemesis.getInstance().getGlimmer();
private File file;
-
private List policies = new ArrayList<>();
private Config() {}
public void load() {
policies = glim.loadPoliciesFromFile(glim.loadConfigFile());
+ policies.sort(Comparator.comparingInt(Policy::weight).reversed());
}
+
// TODO: Implement config saving
//
// public void save() {
diff --git a/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java b/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java
index 39331d6..45ec7b5 100644
--- a/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java
+++ b/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java
@@ -1,8 +1,5 @@
package io.github.adrianvic.nemesiseye;
-import io.github.adrianvic.nemesiseye.reflection.Glimmer;
-import org.bukkit.Location;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -10,13 +7,22 @@ import java.util.Map;
import java.util.regex.Pattern;
public class DataShifter {
-
public static boolean safeMatches(String expression, String against) {
String cleanPattern = expression.trim();
Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE);
return pattern.matcher(against).matches();
}
+ public static boolean safeMatches(List expressions, String against) {
+ for (String s : expressions) {
+ if (DataShifter.safeMatches(s, against)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public static List parseValueToStringList(List