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/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..ac1941e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# 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'] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..0e8199d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,44 @@ +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 diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml new file mode 100644 index 0000000..9570712 --- /dev/null +++ b/.github/workflows/release-build.yml @@ -0,0 +1,41 @@ +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 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1ac6f97..1e3abdf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .gradle/ build/ out/ -.idea/ \ No newline at end of file +.idea/ +libs/ +run/ diff --git a/README.md b/README.md index c4aceff..bf357cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,24 @@ -# Eye of Nemesis -Eye of Nemesis is a Minecraft Paper plugin that allows server admins to write *policies* that will deny or allow (black/whitelist) players to do specific things. +eye_of_nemesis_social_cover -You can create policies based on player location, ~but other types of policies are available, like permission and player-name policy.~ (WIP) +[![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 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. + +## 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. + +## 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 +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. diff --git a/build.gradle b/build.gradle deleted file mode 100644 index a155746..0000000 --- a/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - id 'java' - id("xyz.jpenilla.run-paper") version "2.3.1" -} - -group = 'io.github.adrianvic' -version = '1.0.1-SNAPSHOT' - -repositories { - mavenCentral() - maven { - name = "papermc-repo" - url = "https://repo.papermc.io/repository/maven-public/" - } -} - -dependencies { - compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") -} - -tasks { - runServer { - // Configure the Minecraft version for our task. - // This is the only required configuration besides applying the plugin. - // Your plugin's jar (or shadowJar if present) will be used automatically. - minecraftVersion("1.21") - } -} - -def targetJavaVersion = 21 -java { - def javaVersion = JavaVersion.toVersion(targetJavaVersion) - sourceCompatibility = javaVersion - targetCompatibility = javaVersion - if (JavaVersion.current() < javaVersion) { - toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) - } -} - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' - - if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { - options.release.set(targetJavaVersion) - } -} - -processResources { - def props = [version: version] - inputs.properties props - filteringCharset 'UTF-8' - filesMatching('plugin.yml') { - expand props - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..e2d9a2e --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,154 @@ +plugins { + java + id("xyz.jpenilla.run-paper") version "2.3.1" +} + +group = "io.github.adrianvic" +version = System.getenv("NEMESIS_VERSION_NAME") ?: "unknown" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") +} + +/* ----------------------------------------- */ +/* SUPPORTED VERSIONS */ +/* ----------------------------------------- */ + +val mcVersions = listOf( + "b1_7_3", + "r1_21" +) + +/* ----------------------------------------- */ +/* CREATE SOURCE SET PER VERSION */ +/* ----------------------------------------- */ + +tasks.withType { + inputs.property("version", project.version) + + filesMatching("plugin.yml") { + expand("version" to project.version) + } +} + +mcVersions.forEach { ver -> + val ss = sourceSets.create(ver) { + java.srcDir("src/$ver/java") + + resources.setSrcDirs(listOf("src/$ver/resources", "src/main/resources")) + + compileClasspath += sourceSets["main"].output + runtimeClasspath += output + compileClasspath + } + + tasks.withType { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + + configurations[ss.implementationConfigurationName] + .extendsFrom(configurations["implementation"]) + + configurations[ss.compileOnlyConfigurationName] + .extendsFrom(configurations["compileOnly"]) + +} + +/* ----------------------------------------- */ +/* DEPENDENCIES */ +/* ----------------------------------------- */ + +dependencies { + 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() +} + +/* ----------------------------------------- */ +/* BUILD TASKS */ +/* ----------------------------------------- */ + +mcVersions.forEach { ver -> + tasks.register("jar${ver.replace(".", "_").replace("-", "_").replace("/", "_").capitalize()}") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(sourceSets["main"].output) + from(sourceSets[ver].output) + archiveClassifier.set(ver) + + manifest { + attributes( + "Nemesis-Impl-Version" to ver, + "Nemesis-Environment" to (System.getenv("NEMESIS_BUILD_CHANNEL") ?: "dev") + ) + } + + } +} + +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 */ +/* ----------------------------------------- */ + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} + +tasks.withType { + options.encoding = "UTF-8" +} + +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/settings.gradle b/settings.gradle index 52b1811..9a6526e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -rootProject.name = 'regions' +rootProject.name = 'eyeofnemesis' diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java new file mode 100644 index 0000000..a4b3327 --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/ConfigurationEx.java @@ -0,0 +1,89 @@ +package io.github.adrianvic.nemesiseye.impl; + +import io.github.adrianvic.nemesiseye.Nemesis; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.config.Configuration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +public class ConfigurationEx extends Configuration { + private final File configFile; + private final Log logger; + JavaPlugin plugin; + + public ConfigurationEx(String fileName, Log _logger) { + super(new File(Nemesis.getInstance().getDataFolder(), fileName)); + this.plugin = Nemesis.getInstance(); + logger = _logger; + this.configFile = new File(plugin.getDataFolder(), fileName); + } + + @Override + public void load() { + createParentDirectories(); + + if (!configFile.exists()) { + copyDefaultConfig(); + } + + try { + super.load(); + } catch (Exception e) { + logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); + } + } + + private void createParentDirectories() { + try { + Files.createDirectories(configFile.getParentFile().toPath()); + } catch (IOException e) { + logger.severe(String.format("Failed to generate default config directory: %s", e.getMessage())); + } + } + + private void copyDefaultConfig() { + // Load the config from the JAR directly (it is located at the root level) + String resourcePath = "/" + configFile.getName(); // Root path of JAR + + try (InputStream input = plugin.getClass().getResourceAsStream(resourcePath)) { + if (input == null) { + logger.severe(String.format("Default config '%s' wasn't found in the JAR.", configFile.getName())); + return; + } + + Files.copy(input, configFile.toPath()); + if (Files.exists(configFile.toPath())) { + logger.info(String.format("Default config '%s' generated successfully.", configFile.getName())); + } else { + logger.warning("We tried to generate the default config file, but it was not found even after the creation. Maybe your permissions are broken?"); + } + } catch (IOException e) { + logger.severe(String.format("Failed to generate default config '%s': %s", configFile.getName(), e.getMessage())); + } + } + + public void loadConfig() { + try { + this.load(); + logger.info(String.format("Config '%s' loaded successfully.", configFile.getName())); + } catch (Exception e) { + logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); + } + } + + public void saveConfig() { + try { + this.save(); + logger.info(String.format("Config '%s' saved successfully.", configFile.getName())); + } catch (Exception e) { + logger.severe(String.format("Failed to save config '%s': %s", configFile.getName(), e.getMessage())); + } + } + + public File getConfig() { + return configFile; + } +} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java new file mode 100644 index 0000000..ccba8b1 --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/Log.java @@ -0,0 +1,41 @@ +package io.github.adrianvic.nemesiseye.impl; + +import static org.bukkit.Bukkit.getServer; + +import io.github.adrianvic.nemesiseye.Nemesis; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.java.JavaPlugin; + +public class Log { + JavaPlugin plugin; + PluginDescriptionFile pdf; + + public Log() { + plugin = Nemesis.getInstance(); + pdf = plugin.getDescription(); + } + + public void info(String message) { + getServer().getLogger().info("[" + pdf.getName() + "] " + message); + } + + public void infoc(String message) { + getServer().getLogger().info("[" + pdf.getName() + "] " + message); + } + + public void warning(String message) { + getServer().getLogger().warning("[" + pdf.getName() + "] " + message); + } + + public void warningc(String message) { + getServer().getLogger().warning("[" + pdf.getName() + "] " + message); + } + + public void severe(String message) { + getServer().getLogger().severe("[" + pdf.getName() + "] " + message); + } + + public void severec(String message) { + getServer().getLogger().severe("[" + pdf.getName() + "] " + message); + } +} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java new file mode 100644 index 0000000..0a86ce9 --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/b1_7_3.java @@ -0,0 +1,152 @@ +package io.github.adrianvic.nemesiseye.impl; + +import io.github.adrianvic.nemesiseye.Nemesis; +import io.github.adrianvic.nemesiseye.impl.commands.Eye; +import io.github.adrianvic.nemesiseye.impl.events.BlockEventListener; +import io.github.adrianvic.nemesiseye.impl.events.EntityEventListener; +import io.github.adrianvic.nemesiseye.impl.events.PlayerEventListener; +import io.github.adrianvic.nemesiseye.policy.Policy; +import io.github.adrianvic.nemesiseye.policy.PolicyParsers; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@SuppressWarnings("unused") +public class b1_7_3 implements Glimmer { + JavaPlugin plugin; + PluginManager pm; + ConfigurationEx config; + + @Override + public File loadConfigFile() { + config = new ConfigurationEx("settings.yml", new Log()); + config.load(); + return config.getConfig(); + } + + @Override + public List loadPoliciesFromFile(File file) { + List rawPolicies = config.getList("Policies"); + + if (rawPolicies == null) { + return new ArrayList<>(); + } + + List> result = new ArrayList<>(rawPolicies.size()); + + for (Object entry : rawPolicies) { + if (entry instanceof Map m) { + result.add(m); + } + } + + List allPolicies = new ArrayList<>(); + for (Map policyMap : result) { + if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) { + allPolicies.add(PolicyParsers.get(type).parse(policyMap)); + } + } + + return allPolicies; + } + + @Override + public void onLoad() { + plugin = Nemesis.getInstance(); + pm = Nemesis.getInstance().getPluginManager(); + pm.registerEvent(Event.Type.ENTITY_DAMAGE, new EntityEventListener(), Event.Priority.Normal, plugin); + pm.registerEvent(Event.Type.BLOCK_BREAK, new BlockEventListener(), Event.Priority.Normal, plugin); + pm.registerEvent(Event.Type.PLAYER_INTERACT, new PlayerEventListener(), Event.Priority.Normal, plugin); + plugin.getCommand("eye").setExecutor(new Eye()); + } + + @Override + public ItemStack getItemInMainHandHumanEntity(HumanEntity entity) { + return entity.getItemInHand(); + } + + @Override + public boolean isAir(ItemStack item) { + return item == null || item.getType() == Material.AIR; + } + + @Override + public boolean isGliding(org.bukkit.entity.Player player) { + return false; + } + + @Override + public void setGliding(org.bukkit.entity.Player player, boolean gliding) { + } + + @Override + public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) { + if (sender instanceof org.bukkit.entity.Player p) { + return p.isOp(); + } + return true; // Console always has permission + } + + @Override + public boolean isArmorEquipAttempt(org.bukkit.event.Event event) { + return false; + } + + @Override + public ItemStack getEquippedItem(org.bukkit.event.Event event) { + return null; + } + + @Override + public void sendMessage(CommandSender commandSender, String text) { + String[] lines = text.split("\\r?\\n"); + for (String line : lines) { + commandSender.sendMessage(line); + } + } + + @Override + public boolean hasItemMeta(ItemStack item) { + return false; + } + + @Override + public List getWorlds() { + return plugin.getServer().getWorlds(); + } + + @Override + public boolean hasEnchantment(ItemStack item, Map valuesmap) { + return false; + } + + @Override + public boolean hasAnyEnchantment(ItemStack itemStack) { + return false; + } + + @Override + public boolean isArmor(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return false; + } + + String name = item.getType().name(); + + return name.endsWith("_HELMET") + || name.endsWith("_CHESTPLATE") + || name.endsWith("_LEGGINGS") + || name.endsWith("_BOOTS"); + } +} diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java new file mode 100644 index 0000000..b3db3e5 --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java @@ -0,0 +1,19 @@ +package io.github.adrianvic.nemesiseye.impl.commands; + +import io.github.adrianvic.nemesiseye.commands.EyeCore; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +public class Eye implements CommandExecutor { + private final EyeCore core; + + public Eye() { + core = new EyeCore(); + } + + @Override + public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) { + return core.onCommand(commandSender, command, s, strings); + } +} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java new file mode 100644 index 0000000..6ea38bf --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/BlockEventListener.java @@ -0,0 +1,18 @@ +package io.github.adrianvic.nemesiseye.impl.events; + +import io.github.adrianvic.nemesiseye.Events; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockListener; +import org.bukkit.event.block.BlockPlaceEvent; + +public class BlockEventListener extends BlockListener { + @Override + public void onBlockBreak(BlockBreakEvent event) { + Events.onBlockBreak(event); + } + + @Override + public void onBlockPlace(BlockPlaceEvent event) { + Events.onBlockPlaceEvent(event); + } +} diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java new file mode 100644 index 0000000..e6655f8 --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/EntityEventListener.java @@ -0,0 +1,13 @@ +package io.github.adrianvic.nemesiseye.impl.events; + +import io.github.adrianvic.nemesiseye.Events; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityListener; + +public class EntityEventListener extends EntityListener { + @Override + public void onEntityDamage(EntityDamageEvent event) { + if (event instanceof EntityDamageByEntityEvent e) Events.onEntityDamageByEntityEvent(e); + } +} \ No newline at end of file diff --git a/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java new file mode 100644 index 0000000..ec81484 --- /dev/null +++ b/src/b1_7_3/java/io/github/adrianvic/nemesiseye/impl/events/PlayerEventListener.java @@ -0,0 +1,13 @@ +package io.github.adrianvic.nemesiseye.impl.events; + +import io.github.adrianvic.nemesiseye.Events; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerListener; + +public class PlayerEventListener extends PlayerListener { + @Override + public void onPlayerInteract(PlayerInteractEvent event) { + Events.onInteractionEvent(event); + } +} + diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Config.java b/src/main/java/io/github/adrianvic/nemesiseye/Config.java index faca9d8..69ec351 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Config.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Config.java @@ -1,56 +1,45 @@ package io.github.adrianvic.nemesiseye; -import io.github.adrianvic.nemesiseye.policy.LocationPolicy; -import org.bukkit.configuration.file.YamlConfiguration; +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 final static Config instance = new Config(); + private static Config instance = new Config(); + private final Glimmer glim = Nemesis.getInstance().getGlimmer(); private File file; - private YamlConfiguration config; + private List policies = new ArrayList<>(); - private List locationPolicies; -// private List permissionPolicies; -// private List playerNamePolicies; - - private Config() { - } + private Config() {} public void load() { - file = new File(Nemesis.getInstance().getDataFolder(), "settings.yml"); - - if (!file.exists()) - Nemesis.getInstance().saveResource("settings.yml", false); - - config = new YamlConfiguration(); - config.options().parseComments(true); - - try { - config.load(file); - } catch (Exception e) { - e.printStackTrace(); - } - - locationPolicies = LocationPolicy.parseLocationPolicy(config.getMapList("Policies.Location")); + policies = glim.loadPoliciesFromFile(glim.loadConfigFile()); + policies.sort(Comparator.comparingInt(Policy::weight).reversed()); } - public void save() { - try { - config.save(file); - } catch (Exception e) { - e.printStackTrace(); - } - } +// TODO: Implement config saving +// +// public void save() { +// try { +// config.save(file); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// public void set(String path, Object value) { +// config.set(path, value); +// save(); +// } - public void set(String path, Object value) { - config.set(path, value); - save(); - } - - public List getLocationPolicies() { - return locationPolicies; + public List getPolicies() { + return policies; } public static Config getInstance() { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java b/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java index a9712d0..45ec7b5 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/DataShifter.java @@ -1,5 +1,9 @@ package io.github.adrianvic.nemesiseye; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; public class DataShifter { @@ -8,4 +12,52 @@ public class DataShifter { Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE); return pattern.matcher(against).matches(); } + + public static boolean safeMatches(List expressions, String against) { + for (String s : expressions) { + if (DataShifter.safeMatches(s, against)) { + return true; + } + } + + return false; + } + + public static List parseValueToStringList(List values) { + List result = new ArrayList<>(); + for (Object o : values) { + if (o instanceof String) result.add((String) o); + } + return result; + } + + public static Map parseValueToStringMap(List raw) { + Map out = new HashMap<>(); + + for (Object o : raw) { + if (o instanceof Map map) { + for (Map.Entry e : map.entrySet()) { + out.put(String.valueOf(e.getKey()), String.valueOf(e.getValue())); + } + } else if (o instanceof String s) { + + String[] parts = s.split(":", 2); + if (parts.length == 2) { + out.put(parts[0].trim(), parts[1].trim()); + } + } + } + return out; + } + + public static > T enumOrDefault(Class type, String string, T def) { + try { + return Enum.valueOf(type, string); + } catch (IllegalArgumentException e) { + return def; + } catch (Exception e) { + e.printStackTrace(); + return def; + } + } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/EventListener.java b/src/main/java/io/github/adrianvic/nemesiseye/EventListener.java deleted file mode 100644 index 4666861..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/EventListener.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.adrianvic.nemesiseye; - -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.player.PlayerInteractEvent; - -public class EventListener implements Listener { - @EventHandler - public void onBlockBreak(BlockBreakEvent event) { - event.setCancelled(!Validator.canBreak(event.getPlayer())); - } - - @EventHandler - public void onInteractionEvent(PlayerInteractEvent event) { - if (event.getItem() != null) { - event.setCancelled(!Validator.canInteract(event.getPlayer())); - } - } - - @EventHandler - public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { - if (event.getDamager() instanceof Player) { - event.setCancelled(!Validator.canHit((HumanEntity) event.getDamager())); - } - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Events.java b/src/main/java/io/github/adrianvic/nemesiseye/Events.java new file mode 100644 index 0000000..a611890 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/Events.java @@ -0,0 +1,98 @@ +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.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class Events { + + private static Glimmer g() { + return Nemesis.getInstance().getGlimmer(); + } + + public static void onBlockBreak(BlockBreakEvent event) { + event.setCancelled( + !Validator.can( + event.getPlayer(), + List.of(Action.BREAK, Action.USE_ENCHANTMENT), + event + ) + ); + } + + public static void onInteractionEvent(PlayerInteractEvent event) { + ItemStack item = event.getItem(); + + 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) { + if (event.getDamager() instanceof Player player) { + event.setCancelled( + !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)); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Nemesis.java b/src/main/java/io/github/adrianvic/nemesiseye/Nemesis.java index 03e95ef..292a35c 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Nemesis.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Nemesis.java @@ -1,23 +1,28 @@ package io.github.adrianvic.nemesiseye; -import io.github.adrianvic.nemesiseye.commands.Eye; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import io.github.adrianvic.nemesiseye.reflection.VersionMatcher; +import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; public final class Nemesis extends JavaPlugin { + private Glimmer glim; + private static final String VERSION_PROP = "impl.version"; + private static Nemesis instance; @Override public void onEnable() { - getServer().getPluginManager().registerEvents(new EventListener(), this); + instance = this; + glim = new VersionMatcher().loadGlim(); + glim.onLoad(); Config.getInstance().load(); - getCommand("eye").setExecutor(new Eye()); } @Override public void onDisable() { - // Plugin shutdown logic } - public static Nemesis getInstance() { - return getPlugin(Nemesis.class); - } -} \ No newline at end of file + public static Nemesis getInstance() { return instance; } + public Glimmer getGlimmer() { return glim; } + public PluginManager getPluginManager() { return this.getServer().getPluginManager(); } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java index 7b6af1b..9989ea6 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java @@ -1,63 +1,55 @@ package io.github.adrianvic.nemesiseye; import io.github.adrianvic.nemesiseye.policy.Action; -import io.github.adrianvic.nemesiseye.policy.LocationPolicy; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import io.github.adrianvic.nemesiseye.policy.Policy; import org.bukkit.entity.HumanEntity; -import org.bukkit.util.BoundingBox; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; import java.util.ArrayList; import java.util.List; public class Validator { - 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 nodes, Action action) { - for (PolicyNode n : nodes) { - if (!checkAgainstNode(entity, n, action)) return false; + public static boolean can(HumanEntity entity, List actions, Event event) { + for (Action action : actions) { + if (!can(entity, action, event)) { + return false; + } } return true; } - public static boolean checkAgainstNode(HumanEntity entity, PolicyNode node, Action action) { - boolean allowed = node.getHandler().allows(entity, node, action); - return node.isWhitelist() != allowed; - } + public static boolean can(LivingEntity entity, Action action, Event event) { + boolean restricted = false; + boolean allowed = false; - public static List getNodesForPolicies(List policies) { - List nodes = new ArrayList<>(); - for (LocationPolicy p : policies) { - nodes.addAll(p.nodes()); - } - return nodes; - } + for (Policy policy : getPoliciesForEntity(entity)) { + boolean matches = policy.matches(entity, action, event); - public static List getPoliciesForEntity(HumanEntity entity) { - List lps = Config.getInstance().getLocationPolicies(); - List applyingLPS = new ArrayList<>(); - for (LocationPolicy lp : lps) { - for (ArrayList boxes : lp.locations()) { - for (BoundingBox box : boxes) { - if (box.contains(entity.getLocation().toVector())) { - applyingLPS.add(lp); - } - } + switch (policy.effect()) { + case ALLOW: + if (matches) return true; + break; + + case DENY: + if (matches) return false; + break; } } - return applyingLPS; + + return true; + } + + + public static List getPoliciesForEntity(LivingEntity entity) { + List ps = Config.getInstance().getPolicies(); + List result = new ArrayList<>(); + + for (Policy policy : ps) { + if (policy.applies(entity)) result.add(policy); + } + + return result; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/Commands.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/Commands.java new file mode 100644 index 0000000..a550f20 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/Commands.java @@ -0,0 +1,35 @@ +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 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 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."); + } + +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/Eye.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/Eye.java deleted file mode 100644 index 6929f18..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/Eye.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.adrianvic.nemesiseye.commands; - -import io.github.adrianvic.nemesiseye.Nemesis; -import io.github.adrianvic.nemesiseye.commands.sub.*; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -public class Eye implements CommandExecutor, TabCompleter { - private final Map subs = new HashMap<>(); - - public Eye() { - register(new Reload()); - register(new ListPolicies()); - register(new PolicyInfo()); - register(new CurrentPolicies()); - } - - private void register(Subcommand sub) { - subs.put(sub.name(), sub); - } - - @Override - public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { - if (strings.length == 0) { - commandSender.sendMessage(""" - %sEye of Nemesis%s version %s - Usage: '/eye ' - Use '/eye help' for a list of available commands - """.formatted(ChatColor.RED, ChatColor.RESET, Nemesis.getInstance().getDescription().getVersion())); - } else { - Subcommand sub = subs.get(strings[0].toLowerCase()); - if (sub == null) { - commandSender.sendMessage("Unknown command, try '/eye help' to list available commands."); - return true; - } - return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); - } - return false; - } - - @Override - public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { - if (strings.length == 1) { - return new ArrayList<>(subs.keySet()); - } - Subcommand sub = subs.get(strings[0].toLowerCase()); - if (sub != null) { - return sub.onTabComplete(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); - } - return List.of(); - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java new file mode 100644 index 0000000..6d2c799 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java @@ -0,0 +1,57 @@ +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.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import java.util.*; + +public class EyeCore { + private final Glimmer glim = Nemesis.getInstance().getGlimmer(); + + public EyeCore() {} + + public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) { + if (strings.length == 0) { + glim.sendMessage(commandSender, """ + %sEye of Nemesis%s version %s%s%s + Usage: '/eye ' + Use '/eye help' for a list of available commands + """.formatted(ChatColor.AQUA, ChatColor.WHITE, ChatColor.GRAY, Nemesis.getInstance().getDescription().getVersion(), ChatColor.WHITE)); + } else { + Subcommand sub = Commands.get(strings[0].toLowerCase()); + if (sub == null) { + commandSender.sendMessage("Unknown command, try '/eye help' to list available commands."); + return true; + } + else if (glim.hasPermission(commandSender, sub.permission())) { + return sub.execute(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); + } else { + // Nemesis.getInstance().getLogger().info("does not have %s".formatted(sub.permission())); + Commands.sendNoPermissionError(commandSender); + return true; + } + } + return false; + } + + public List onTabComplete(CommandSender commandSender, Command command, String s, String [] strings) { + if (strings.length == 1) { + Map cmds = new HashMap<>(); + for (Map.Entry e : Commands.getAll().entrySet()) { + if (glim.hasPermission(commandSender, e.getValue().permission())) { + cmds.put(e.getKey(), e.getValue()); + cmds.put(e.getKey(), e.getValue()); + } + } + return new ArrayList<>(cmds.keySet()); + } + Subcommand sub = Commands.get(strings[0].toLowerCase()); + if (sub != null && glim.hasPermission(commandSender, sub.permission())) { + return sub.onTabComplete(commandSender, Arrays.copyOfRange(strings, 1, strings.length)); + } + return List.of(); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/CurrentPolicies.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/CurrentPolicies.java index 764419e..09133e4 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/CurrentPolicies.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/CurrentPolicies.java @@ -1,7 +1,7 @@ package io.github.adrianvic.nemesiseye.commands.sub; import io.github.adrianvic.nemesiseye.Validator; -import io.github.adrianvic.nemesiseye.policy.LocationPolicy; +import io.github.adrianvic.nemesiseye.policy.Policy; import org.bukkit.command.CommandSender; import org.bukkit.entity.HumanEntity; @@ -9,16 +9,11 @@ import java.util.ArrayList; import java.util.List; public class CurrentPolicies implements Subcommand { - @Override - public String name() { - return "currentpolicies"; - } - @Override public boolean execute(CommandSender commandSender, String[] args) { - List policies = Validator.getPoliciesForEntity((HumanEntity) commandSender); + List policies = Validator.getPoliciesForEntity((HumanEntity) commandSender); List pstrings = new ArrayList<>(); - for (LocationPolicy p : policies) { + for (Policy p : policies) { pstrings.add(" %s (%s nodes)".formatted(p.name(), p.nodes().size())); } if (pstrings.isEmpty()) { @@ -34,4 +29,19 @@ public class CurrentPolicies implements Subcommand { public List onTabComplete(CommandSender sender, String[] args) { 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"; + } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Help.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Help.java new file mode 100644 index 0000000..0526a6b --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Help.java @@ -0,0 +1,54 @@ +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 rstr = new ArrayList<>(); + Map allSubcommands = Commands.getAll(); + if (allSubcommands.isEmpty()) { + rstr.add("No commands found."); + } + for (Map.Entry 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 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"; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/ListPolicies.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/ListPolicies.java index ea9a2f5..d4169ac 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/ListPolicies.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/ListPolicies.java @@ -1,23 +1,17 @@ package io.github.adrianvic.nemesiseye.commands.sub; import io.github.adrianvic.nemesiseye.Config; -import io.github.adrianvic.nemesiseye.policy.LocationPolicy; +import io.github.adrianvic.nemesiseye.policy.Policy; import org.bukkit.command.CommandSender; import java.util.ArrayList; import java.util.List; public class ListPolicies implements Subcommand { - - @Override - public String name() { - return "listpolicies"; - } - @Override public boolean execute(CommandSender commandSender, String[] args) { List rstr = new ArrayList<>(); - for (LocationPolicy p : Config.getInstance().getLocationPolicies()) { + for (Policy p : Config.getInstance().getPolicies()) { rstr.add(p.name()); } commandSender.sendMessage(String.join(", ", rstr) + "."); @@ -28,4 +22,19 @@ public class ListPolicies implements Subcommand { public List onTabComplete(CommandSender sender, String[] args) { 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"; + } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/PolicyInfo.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/PolicyInfo.java index ad969ea..3092088 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/PolicyInfo.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/PolicyInfo.java @@ -1,7 +1,9 @@ package io.github.adrianvic.nemesiseye.commands.sub; import io.github.adrianvic.nemesiseye.Config; -import io.github.adrianvic.nemesiseye.policy.LocationPolicy; +import io.github.adrianvic.nemesiseye.Nemesis; +import io.github.adrianvic.nemesiseye.policy.Policy; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -9,25 +11,18 @@ import java.util.ArrayList; import java.util.List; public class PolicyInfo implements Subcommand { - @Override - public String name() { - return "policyinfo"; - } + private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override public boolean execute(CommandSender commandSender, String[] strings) { - List policies = Config.getInstance().getLocationPolicies(); - for (LocationPolicy lp : policies) { - if (lp.name().equals(strings[0])) { - String locations = lp.locations().toString(); - - commandSender.sendMessage(String.format(""" - Showing info for policy "%s%s%s": + List policies = Config.getInstance().getPolicies(); + for (Policy policy : policies) { + if (policy.name().equals(strings[0])) { + glim.sendMessage(commandSender, String.format(""" + Showing info for policy %s%s%s: Type: %s - Locations: %s Nodes: %s - %s - """, ChatColor.UNDERLINE, lp.name(), ChatColor.RESET, "location", locations, lp.nodes().size(), lp.allowlist() ? "Is allowlist" : "Is blacklist")); + """, ChatColor.GREEN, policy.name(), ChatColor.WHITE, policy.getClass().getTypeName(), policy.nodes().size())); } } return true; @@ -36,9 +31,24 @@ public class PolicyInfo implements Subcommand { @Override public List onTabComplete(CommandSender sender, String[] args) { List rstr = new ArrayList<>(); - for (LocationPolicy p : Config.getInstance().getLocationPolicies()) { + for (Policy p : Config.getInstance().getPolicies()) { rstr.add(p.name()); } return rstr; } + + @Override + public String description() { + return "Shows info for a specified policy."; + } + + @Override + public String usage() { + return ""; + } + + @Override + public String permission() { + return "nemsiseye.policy.info"; + } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Reload.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Reload.java index 23f1659..9cc49be 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Reload.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Reload.java @@ -1,16 +1,16 @@ package io.github.adrianvic.nemesiseye.commands.sub; import io.github.adrianvic.nemesiseye.Config; +import io.github.adrianvic.nemesiseye.commands.Commands; import org.bukkit.command.CommandSender; import java.util.List; public class Reload implements Subcommand { - @Override public boolean execute(CommandSender commandSender, String [] strings) { - Config.getInstance().load(); - commandSender.sendMessage("Reloading..."); + Config.getInstance().load(); + commandSender.sendMessage("Reloading..."); return true; } @@ -20,7 +20,17 @@ public class Reload implements Subcommand { } @Override - public String name() { - return "reload"; + public String permission() { + return "nemsiseye.reload"; + } + + @Override + public String description() { + return "Reloads the plugin configuration file."; + } + + @Override + public String usage() { + return ""; } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java index 1ea9426..00e0c56 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/sub/Subcommand.java @@ -1,12 +1,19 @@ package io.github.adrianvic.nemesiseye.commands.sub; +import io.github.adrianvic.nemesiseye.Nemesis; import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; import java.util.List; public interface Subcommand { - String name(); + String description(); + String usage(); @SuppressWarnings("SameReturnValue") boolean execute(CommandSender commandSender, String[] strings); List onTabComplete(CommandSender sender, String[] strings); + String permission(); + default boolean hasPermission(CommandSender sender) { + return Nemesis.getInstance().getGlimmer().hasPermission(sender, permission()); + } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java index 2bcd83e..debbb36 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java @@ -4,6 +4,10 @@ public enum Action { INTERACT, BREAK, HIT, - CRAFT, - EQUIP + // TODO CRAFT, + EQUIP, + PLACE, + USE_ENCHANTMENT, + GLYDE, + SPAWN } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Effect.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Effect.java new file mode 100644 index 0000000..1f16500 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Effect.java @@ -0,0 +1,6 @@ +package io.github.adrianvic.nemesiseye.policy; + +public enum Effect { + DENY, + ALLOW, +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/LocationPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/LocationPolicy.java deleted file mode 100644 index 4c224cd..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/LocationPolicy.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.util.BoundingBox; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public record LocationPolicy(String name, List> locations, List nodes, boolean allowlist) { - public static List parseLocationPolicy(List> raw) { - List out = new ArrayList<>(raw.size()); - for (Map m : raw) { - String name = (String) m.get("name"); - boolean allowlist = Boolean.TRUE.equals(m.get("allowList")); - - // Nodes - Object rawNodes = m.get("nodes"); - List> nodeList = new ArrayList<>(); - if (rawNodes instanceof List list) { - for (Object o : list) { - if (o instanceof Map map) - nodeList.add((Map) map); - } - } - - List nodes = PolicyNode.parseNodes(nodeList, allowlist); - - // Parsing locations - List> locations = new ArrayList<>(); - Object rawGroups = m.get("locations"); - List groups = rawGroups instanceof List ? (List) rawGroups : List.of(); - - // Getting groups - for (Object gObj : groups) { - List group = (List) gObj; - ArrayList boxes = new ArrayList<>(group.size()); - - // Now iterate over regions inside the group - for (Object rObj : group) { - 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(Bukkit.getWorlds().getFirst(), x1, y1, z1); - Location loc2 = new Location(Bukkit.getWorlds().getFirst(), x2, y2, z2); - - boxes.add(BoundingBox.of(loc1, loc2)); - } - locations.add(boxes); - } - out.add(new LocationPolicy(name, locations, nodes, allowlist)); - } - return out; - } -} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java index 6e7edf3..2202797 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandler.java @@ -1,7 +1,9 @@ package io.github.adrianvic.nemesiseye.policy; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; public interface NodeHandler { - boolean allows(HumanEntity entity, PolicyNode node, Action action); + boolean check(LivingEntity entity, PolicyNode node, Action action, Event event); } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java index 5746e1c..70f4a49 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeHandlers.java @@ -1,22 +1,25 @@ package io.github.adrianvic.nemesiseye.policy; -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 io.github.adrianvic.nemesiseye.policy.handlers.*; import java.util.HashMap; import java.util.Map; public class NodeHandlers { - private static final Map handlers = new HashMap<>(); + private static final Map handlers = new HashMap<>(); static { - handlers.put("attackWithItemInHand", new attackWith()); - handlers.put("useItem", new useItem()); - handlers.put("useEnchantment", new useEnchantment()); + handlers.put(Action.HIT, new UseItem()); + handlers.put(Action.PLACE, new BePlaced()); + handlers.put(Action.INTERACT, new UseItem()); + handlers.put(Action.USE_ENCHANTMENT, new UseEnchantment()); + handlers.put(Action.GLYDE, new Glyde()); + handlers.put(Action.EQUIP, new Equip()); + handlers.put(Action.SPAWN, new Spawn()); + handlers.put(Action.BREAK, new UseItem()); // TODO: implement place handler } - public static NodeHandler get(String type) { + public static NodeHandler get(Action type) { return handlers.get(type); } } diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeValueParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeValueParser.java deleted file mode 100644 index 84bde22..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/NodeValueParser.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy; - -import java.util.*; - -public class NodeValueParser { - public static List parseValueToStringList(List values) { - List result = new ArrayList<>(); - for (Object o : values) { - if (o instanceof String) result.add((String) o); - } - return result; - } - - public static Map parseValueToStringMap(List values) { - Map result = new HashMap<>(); - - for (Object o : values) { - if (o instanceof Map raw) { - for (Map.Entry e : raw.entrySet()) { - if (e.getKey() instanceof String k && e.getValue() instanceof String v) { - result.put(k, v); - } - } - } - } - return result; - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PermissionPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PermissionPolicy.java deleted file mode 100644 index 9f10260..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PermissionPolicy.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy; - -import org.bukkit.permissions.Permission; - -import java.util.ArrayList; - -public record PermissionPolicy(String name, ArrayList permissions, PolicyNode nodes, boolean allowlist) {} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PlayerNamePolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PlayerNamePolicy.java deleted file mode 100644 index 5038d92..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PlayerNamePolicy.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy; - -import java.util.ArrayList; - -public record PlayerNamePolicy(String name, ArrayList playerName, PolicyNode nodes, boolean allowlist) {} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java new file mode 100644 index 0000000..5cb69cc --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java @@ -0,0 +1,35 @@ +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; + +public interface Policy { + String name(); + List nodes(); + boolean policyAllowList(); + boolean applies(LivingEntity entity); + Effect effect(); + int weight(); + List worlds(); + + default void addNode(PolicyNode node) { + nodes().add(node); + } + + 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; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java index b8dc7de..8dcbc4e 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyNode.java @@ -1,34 +1,73 @@ 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.HashMap; import java.util.List; import java.util.Map; -public record PolicyNode(String type, List values, boolean isWhitelist) { - public static List parseNodes(List> raw, boolean isWhitelist) { +public record PolicyNode(List actions, List values) { + public static List parseNodes(List> raw, Effect effect) { List nodes = new ArrayList<>(); - for (Map m : raw) { - for (Map.Entry entry : m.entrySet()) { - String type = entry.getKey(); - List values = new ArrayList<>(); - Object val = entry.getValue(); + for (Map m : raw) { + for (Map.Entry rawNode : m.entrySet()) { + List nodeActions = new ArrayList<>(); + List nodeValues = new ArrayList<>(); - if (val instanceof String s) { - values.add(s); - } else if (val instanceof List l) { - values.addAll(l); - } else if (val instanceof Map map) { - values.add(map); + if (rawNode.getKey() instanceof List rawTypes && rawNode.getValue() instanceof Map rawNodeValues) { + for (Object rawType : rawTypes) { + if (rawType instanceof String rts && !rts.isEmpty() && !(rts == null) && DataShifter.enumOrDefault(Action.class, rts, null) != null) { + nodeActions.add(DataShifter.enumOrDefault(Action.class, rts, null)); + } + } + Map semiParsedNodeValue = new HashMap<>(); + for (Map.Entry rawNodeValueEntries : rawNodeValues.entrySet()) { + if (rawNodeValueEntries.getKey() instanceof String stringKey) { + semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue()); + } + } + + if (semiParsedNodeValue.get("values") instanceof List l) { + for (Object v : l) { + nodeValues.add(v); + } + } } - nodes.add(new PolicyNode(type, values, isWhitelist)); + if (!nodeActions.isEmpty() && !nodeValues.isEmpty()) { + PolicyNode newNode = new PolicyNode(nodeActions, nodeValues); + nodes.add(newNode); + } } } return nodes; } - public NodeHandler getHandler() { - return NodeHandlers.get(type); + public List getHandler() { + List handlers = new ArrayList<>(); + 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; } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java new file mode 100644 index 0000000..8050873 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java @@ -0,0 +1,47 @@ +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.Map; + +public interface PolicyParser { + default Policy parse(Map 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 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> nodeList = new ArrayList<>(); + + if (rawNodes instanceof List list) { + for (Object o : list) { + if (o instanceof Map map) + nodeList.add((Map) map); + } + } + + List nodes = PolicyNode.parseNodes(nodeList, effect); + return parse(new Core(name, worlds, nodes, nodesAllowList, policyAllowList, effect, weight), raw); + } + + Policy parse(Core corePolicy, Map raw); +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParsers.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParsers.java new file mode 100644 index 0000000..6a7c3b4 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParsers.java @@ -0,0 +1,24 @@ +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.PermissionPolicyParser; +import io.github.adrianvic.nemesiseye.policy.parser.PlayerNamePolicyParser; + +import java.util.HashMap; +import java.util.Map; + +public class PolicyParsers { + private static final Map handlers = new HashMap<>(); + + static { + 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) { + return handlers.get(type); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java new file mode 100644 index 0000000..ff9d08b --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java @@ -0,0 +1,26 @@ +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 parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java new file mode 100644 index 0000000..8d5cfcf --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java @@ -0,0 +1,31 @@ +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 parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java new file mode 100644 index 0000000..46d2b11 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java @@ -0,0 +1,14 @@ +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; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java new file mode 100644 index 0000000..938f6ee --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java @@ -0,0 +1,21 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; + +import java.util.List; + +public class Spawn implements NodeHandler { + + @Override + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + String type = entity.getType().name(); + List parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java new file mode 100644 index 0000000..d67e9f9 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java @@ -0,0 +1,34 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.Nemesis; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; + +public class UseEnchantment implements NodeHandler { + private final Glimmer glim = Nemesis.getInstance().getGlimmer(); + + @Override + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { + if (entity instanceof HumanEntity e) { + ItemStack item = glim.getItemInMainHandHumanEntity(e); + + if (glim.isAir(item)) return false; + if (!glim.hasItemMeta(item)) return false; + if (!glim.hasAnyEnchantment(item)) return false; + + boolean matches = glim.hasEnchantment(item, + DataShifter.parseValueToStringMap(node.values())); + + return matches; + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java new file mode 100644 index 0000000..ef91a98 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java @@ -0,0 +1,33 @@ +package io.github.adrianvic.nemesiseye.policy.handlers; + +import io.github.adrianvic.nemesiseye.DataShifter; +import io.github.adrianvic.nemesiseye.Nemesis; +import io.github.adrianvic.nemesiseye.policy.Action; +import io.github.adrianvic.nemesiseye.policy.NodeHandler; +import io.github.adrianvic.nemesiseye.policy.PolicyNode; +import io.github.adrianvic.nemesiseye.reflection.Glimmer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; + +import 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 parsedValue = DataShifter.parseValueToStringList(node.values()); + + return DataShifter.safeMatches(parsedValue, type); + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/attackWith.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/attackWith.java deleted file mode 100644 index 3759c2e..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/attackWith.java +++ /dev/null @@ -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.NodeValueParser; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.HumanEntity; - -public class attackWith implements NodeHandler { - - @Override - public boolean allows(HumanEntity entity, PolicyNode node, Action action) { - if (action == Action.HIT) { - for (String s : NodeValueParser.parseValueToStringList(node.values())) { - if (DataShifter.safeMatches(s, entity.getInventory().getItemInMainHand().getType().toString())) return false; - } - } - return true; - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java deleted file mode 100644 index 96bfa21..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useEnchantment.java +++ /dev/null @@ -1,42 +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.NodeValueParser; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; - -import java.util.Map; - -public class useEnchantment implements NodeHandler { - @Override - public boolean allows(HumanEntity entity, PolicyNode node, Action action) { - ItemStack item = entity.getInventory().getItemInMainHand(); - if (item.getItemMeta() == null) { - return !node.isWhitelist(); - } - - Map enchants = item.getItemMeta().getEnchants(); - - if (enchants.isEmpty()) { - return !node.isWhitelist(); - } - - Map valuesmap = NodeValueParser.parseValueToStringMap(node.values()); - - for (Map.Entry e : enchants.entrySet()) { - String enchantment = e.getKey().getKey().getKey(); - String level = e.getValue().toString(); - - for (Map.Entry entry : valuesmap.entrySet()) { - if (DataShifter.safeMatches(entry.getKey().trim(), enchantment) && DataShifter.safeMatches(entry.getValue().trim(), level)) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useItem.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useItem.java deleted file mode 100644 index 060d4fb..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/useItem.java +++ /dev/null @@ -1,23 +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.NodeValueParser; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.entity.HumanEntity; - -public class useItem implements NodeHandler { - - @Override - public boolean allows(HumanEntity entity, PolicyNode node, Action action) { - String type = entity.getInventory().getItemInMainHand().getType().toString(); - - for (String s : NodeValueParser.parseValueToStringList(node.values())) { - if (DataShifter.safeMatches(s, type)) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/GlobalPolicyParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/GlobalPolicyParser.java new file mode 100644 index 0000000..8b993a6 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/GlobalPolicyParser.java @@ -0,0 +1,15 @@ +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()); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/LocationPolicyParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/LocationPolicyParser.java new file mode 100644 index 0000000..7a11077 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/LocationPolicyParser.java @@ -0,0 +1,62 @@ +package io.github.adrianvic.nemesiseye.policy.parser; + +import io.github.adrianvic.nemesiseye.Nemesis; +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.reflection.Glimmer; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class LocationPolicyParser implements PolicyParser { + private Glimmer glim = Nemesis.getInstance().getGlimmer(); + + public Policy parse(Core corePolicy, Map raw) { + Object rawLocations = raw.get("locations"); + Object rawCoordinates = null; + List worlds = new ArrayList<>(); + if (rawLocations instanceof Map rawLocationMap) { + rawCoordinates = rawLocationMap.get("coordinates"); + + if (rawLocationMap.get("worlds") instanceof List rawWorldsList) { + for (Object worldObject : rawWorldsList) { + if (worldObject instanceof String worldString) { + worlds.add(worldString); + } + } + } else { + worlds.add("world"); + } + } + + List 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()); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/PermissionPolicyParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/PermissionPolicyParser.java new file mode 100644 index 0000000..491e5f7 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/PermissionPolicyParser.java @@ -0,0 +1,28 @@ +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 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()); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/PlayerNamePolicyParser.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/PlayerNamePolicyParser.java new file mode 100644 index 0000000..ae9f244 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/parser/PlayerNamePolicyParser.java @@ -0,0 +1,28 @@ +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 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()); + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java new file mode 100644 index 0000000..7e85467 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/Core.java @@ -0,0 +1,16 @@ +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 worlds, List nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy { + @Override + public boolean applies(LivingEntity entity) { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java new file mode 100644 index 0000000..127430c --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/GlobalPolicy.java @@ -0,0 +1,15 @@ +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 worlds, List nodes, boolean policyAllowList, Effect effect, int weight) implements Policy { + public boolean applies(LivingEntity entity) { + return true; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java new file mode 100644 index 0000000..255e433 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/LocationPolicy.java @@ -0,0 +1,22 @@ +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.reflection.Glimmer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; + +import java.util.List; + +public record LocationPolicy(String name, List worlds, List locations, List nodes, boolean nodeAllowlist, boolean policyAllowList, Effect effect, int weight) implements Policy { + @Override + public boolean applies(LivingEntity entity) { + for (Glimmer.Box box : locations) { + if (box.contains(entity.getLocation().toVector(), entity.getWorld())) { + return !policyAllowList; + } + } + return policyAllowList; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java new file mode 100644 index 0000000..309ce07 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PermissionPolicy.java @@ -0,0 +1,24 @@ +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 org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; + +import java.util.List; + +public record PermissionPolicy(String name, List worlds, List permissions, List nodes, boolean policyAllowList, Effect effect, int weight) implements Policy { + + @Override + public boolean applies(LivingEntity entity) { + for (String perm : permissions) { + if (Nemesis.getInstance().getGlimmer().hasPermission(entity, perm)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java new file mode 100644 index 0000000..ba40e4b --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/policies/PlayerNamePolicy.java @@ -0,0 +1,21 @@ +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 PlayerNamePolicy(String name, List worlds, List playerName, List nodes, Effect effect, boolean policyAllowList, int weight) implements Policy { + + @Override + public boolean applies(LivingEntity entity) { + if (playerName.contains(entity.getName())) { + return !policyAllowList(); + } else { + return policyAllowList(); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java new file mode 100644 index 0000000..ccc69b4 --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/Glimmer.java @@ -0,0 +1,82 @@ +package io.github.adrianvic.nemesiseye.reflection; + +import io.github.adrianvic.nemesiseye.policy.Policy; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public interface Glimmer { + void onLoad(); + + List getWorlds(); + + // Configuration + File loadConfigFile(); + List loadPoliciesFromFile(File file); + + // Items + boolean hasItemMeta(ItemStack item); + boolean hasEnchantment(ItemStack item, Map valuesmap); + boolean hasAnyEnchantment(ItemStack itemStack); + boolean isArmor(ItemStack item); + boolean isAir(ItemStack item); + ItemStack getItemInMainHandHumanEntity(HumanEntity entity); + + // Players + boolean isGliding(org.bukkit.entity.Player player); + void setGliding(org.bukkit.entity.Player player, boolean gliding); + boolean hasPermission(org.bukkit.command.CommandSender sender, String permission); + + // Events + boolean isArmorEquipAttempt(org.bukkit.event.Event event); + ItemStack getEquippedItem(org.bukkit.event.Event event); + + // Commands + void sendMessage(CommandSender commandSender, String text); + + class Box { + 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) { + this.x1 = Math.min(x1, x2); + this.y1 = Math.min(y1, y2); + this.z1 = Math.min(z1, z2); + this.x2 = Math.max(x1, x2); + this.y2 = Math.max(y1, y2); + this.z2 = Math.max(z1, z2); + this.world = world; + } + + public boolean contains(double x, double y, double z) { + return x >= x1 && x <= x2 + && y >= y1 && y <= y2 + && z >= z1 && z <= z2; + } + + public boolean contains(Vector v) { + return v.getX() >= x1 && v.getX() <= x2 + && v.getY() >= y1 && v.getY() <= y2 + && 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) { + 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()); } + } + +} diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java new file mode 100644 index 0000000..d8323cc --- /dev/null +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java @@ -0,0 +1,171 @@ +package io.github.adrianvic.nemesiseye.reflection; + +import io.github.adrianvic.nemesiseye.DataShifter; +import org.bukkit.Bukkit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VersionMatcher { + + public VersionMatcher() {} + + private record Entry(String pattern, String classSuffix) {} + + public String getVersion(String type, String serverVersion) { + if (type == null || serverVersion == null) { + return ""; + } + + Map> map = populateMap(); + List entries = map.get(type.toLowerCase()); + if (entries == null || entries.isEmpty()) { + return ""; + } + + for (Entry e : entries) { + if (DataShifter.safeMatches(e.pattern, serverVersion)) { + return e.pattern + "|" + e.classSuffix; + } + } + + List sorted = new ArrayList<>(entries); + Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern)); + + Entry oldest = sorted.get(0); + Entry newest = sorted.get(sorted.size() - 1); + + int cmpOldest = compareVersions(serverVersion, oldest.pattern); + int cmpNewest = compareVersions(serverVersion, newest.pattern); + + if (cmpOldest < 0) { + return oldest.pattern + "|" + oldest.classSuffix; + } else if (cmpNewest > 0) { + return newest.pattern + "|" + newest.classSuffix; + } + + // should not happen because we already tried all patterns + return newest.pattern + "|" + newest.classSuffix; + } + + private Map> populateMap() { + Map> map = new HashMap<>(); + + // RELEASE patterns, newest first (order does not matter for matching) + map.put("release", List.of( + new Entry("^1\\.21\\..*$", "r1_21") + )); + + // BETA patterns + map.put("beta", List.of( + new Entry("^1\\.7\\.3$", "b1_7_3") + )); + + return map; + } + + private int compareVersions(String v1, String v2) { + String clean1 = v1.replaceAll("[^0-9.]", ""); + String clean2 = v2.replaceAll("[^0-9.]", ""); + + String[] a1 = clean1.split("\\."); + String[] a2 = clean2.split("\\."); + + int len = Math.max(a1.length, a2.length); + for (int i = 0; i < len; i++) { + int n1 = i < a1.length ? parseInt(a1[i]) : 0; + int n2 = i < a2.length ? parseInt(a2[i]) : 0; + if (n1 != n2) { + return n1 - n2; + } + } + return 0; + } + + private int parseInt(String s) { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + return 0; + } + } + + public Glimmer loadGlim() { + String rawVersion = null; + try { + 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; + } + } +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1c97eb4..e9273aa 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,10 +1,19 @@ name: "Eye-of-Nemesis" -version: '1.0.1-SNAPSHOT' +version: ${version} main: io.github.adrianvic.nemesiseye.Nemesis -api-version: '1.21' +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