Merge pull request #1 from adrianvic/reflection

Add reflection with initial support to Bukkit CB1060 (b1.7.3)
This commit is contained in:
Tenkuma 2025-12-12 21:46:36 -03:00 committed by GitHub
commit 0db49aba02
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 772 additions and 234 deletions

1
.gitignore vendored
View file

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

View file

@ -1,39 +1,12 @@
> [!IMPORTANT]
> This project is in a very early stage, expect bugs until `Release 1.0`.
>
> Documentation will soon be moved from README.md.
# Eye of Nemesis
Eye of Nemesis is a plugin that allows server admins to write [policies](#Policies) that will deny or allow (black/whitelist) players to do specific things based on the value of [nodes](#Nodes).
Eye of Nemesis is a plugin that allows server admins to write policies that will deny or allow (black/whitelist) players to do specific things based on the value of nodes.
## Warnings
- Even though running `/eye` will tell you to run `/eye help` to list all available commands, this is not implemented yet, however all commands are available as tab-complete of `/eye`.
- This plugin is in a very early stage.
## Policies
Policy is a structure that holds nodes and tell them _where_ to act. For example, a Location policy will tell its child nodes that they work from coordinates `x1 y1 z1` to `x2 y2 z2`.
Currently, the only policy type is Location.
## Nodes
Nodes are specific rules that rely on it's value to know _when_ to act. For example, a useItem policy with value `cookie` will prevent users from doing anything with a cookie.
### `useItem`
**Triggered:** breaking/placing/interacting with anything using this item.
**Expects:** list of strings.
### `useEnchantment`
**Triggered:** breaking/placing/interacting with anything using an item with the specified enchantment, **will allow items without enchantment even on allowlist mode**.
**Expects:** map of _string: string_.
### `attackWith`
**Triggered:** attacking with this item in hand.
**Expects:** list of strings.
## 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.
## Performance
This plugin is not scalable as it is and will run unoptimized checks when your players do certain things in the server if you have policies enabled, I made it for a server with a few friends.
For every policy there's a check, for every matching policy there are its child nodes, each one introducing new checks. Keep that in mind.
This plugin is not scalable as it is and will end up running unoptimized checks when your players do things with policies in effect, I made it for a server with a few friends. I'll look forward into writing more performant code after all my other priorities are implemented.

View file

@ -1,55 +0,0 @@
plugins {
id 'java'
id("xyz.jpenilla.run-paper") version "2.3.1"
}
group = 'io.github.adrianvic'
version = '1.0.2-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
}
}

93
build.gradle.kts Normal file
View file

@ -0,0 +1,93 @@
plugins {
java
id("xyz.jpenilla.run-paper") version "2.3.1"
}
group = "io.github.adrianvic"
version = "1.0.3-SNAPSHOT"
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 */
/* ----------------------------------------- */
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<ProcessResources> {
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.10-R0.1-SNAPSHOT")
add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
add("b1_7_3CompileOnly", files("libs/craftbukkit-1060.jar"))
}
/* ----------------------------------------- */
/* BUILD TASKS */
/* ----------------------------------------- */
mcVersions.forEach { ver ->
tasks.register<Jar>("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
)
}
}
}
/* ----------------------------------------- */
/* JAVA SETTINGS */
/* ----------------------------------------- */
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
tasks.runServer {
minecraftVersion("1.21")
}

View file

@ -0,0 +1,13 @@
package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockListener;
public class BlockEventListener extends BlockListener {
@Override
public void onBlockBreak(BlockBreakEvent event) {
Events.onBlockBreak(event);
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,15 @@
package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Events;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockListener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerListener;
public class PlayerEventListener extends PlayerListener {
@Override
public void onPlayerInteract(PlayerInteractEvent event) {
Events.onInteractionEvent(event);
}
}

View file

@ -0,0 +1,110 @@
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.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.World;
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;
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<Policy> loadPoliciesFromFile(File file) {
List<?> rawPolicies = config.getList("Policies");
if (rawPolicies == null) {
return new ArrayList<>();
}
List<Map<?, ?>> result = new ArrayList<>(rawPolicies.size());
for (Object entry : rawPolicies) {
if (entry instanceof Map<?,?> m) {
result.add(m);
}
}
List<Policy> allPolicies = new ArrayList<>();
for (Map<?, ?> map : result) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
List<Policy> parsed = PolicyParsers.get(k).parse(v);
allPolicies.addAll(parsed);
}
}
}
return allPolicies;
}
@Override
public List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies) {
List<Policy> result = new ArrayList<>();
for (Policy p : policies) {
if (p instanceof LocationPolicy lp) {
for (List<Box> boxList : lp.locations()) {
for (Box b : boxList) {
if (b.contains(entity.getLocation().toVector())) result.add(p);
}
}
}
}
return result;
}
@Override
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 hasItemMeta(ItemStack item) {
return false;
}
@Override
public List<World> getWorlds() {
return plugin.getServer().getWorlds();
}
@Override
public boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap) {
return false;
}
@Override
public boolean hasAnyEnchantment(ItemStack itemStack) {
return false;
}
}

View file

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

View file

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

View file

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

View file

@ -1,63 +1,38 @@
package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParser;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import org.bukkit.configuration.file.YamlConfiguration;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Config {
private final static Config instance = new Config();
private static Config instance = new Config();
private Glimmer glim = Nemesis.getInstance().getGlimmer();
private File file;
private YamlConfiguration config;
private List<Policy> policies = new ArrayList<>();
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();
List<Policy> newPolicies = glim.loadPoliciesFromFile(glim.loadConfigFile());
policies = newPolicies;
}
List<Map<?, ?>> rawPolicies = config.getMapList("Policies");
for (Map<?, ?> map : rawPolicies) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
List<Policy> parsed = PolicyParsers.get(k).parse(v);
policies.addAll(parsed);
}
}
}
}
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 save() {
// try {
// config.save(file);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public void set(String path, Object value) {
// config.set(path, value);
// save();
// }
public List<Policy> getPolicies() {
return policies;

View file

@ -21,19 +21,23 @@ public class DataShifter {
return result;
}
public static Map<String, String> parseValueToStringMap(List<Object> values) {
Map<String, String> result = new HashMap<>();
public static Map<String,String> parseValueToStringMap(List<Object> raw) {
Map<String,String> out = 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);
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 result;
return out;
}
public static List<Map<?, ?>> parseValueToListOfMaps(List<?> values) {

View file

@ -2,27 +2,22 @@ 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) {
public class Events {
public static void onBlockBreak(BlockBreakEvent event) {
event.setCancelled(!Validator.canBreak(event.getPlayer()));
}
@EventHandler
public void onInteractionEvent(PlayerInteractEvent event) {
public static void onInteractionEvent(PlayerInteractEvent event) {
if (event.getItem() != null) {
event.setCancelled(!Validator.canInteract(event.getPlayer()));
}
}
@EventHandler
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
public static void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Player) {
event.setCancelled(!Validator.canHit((HumanEntity) event.getDamager()));
}

View file

@ -1,23 +1,65 @@
package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.commands.Eye;
import io.github.adrianvic.nemesiseye.commands.EyeCore;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
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 = loadGlim();
glim.onLoad();
Config.getInstance().load();
getCommand("eye").setExecutor(new Eye());
}
private String readImplVersion() {
Properties props = new Properties();
try (InputStream is = getClass().getClassLoader()
.getResourceAsStream("version.properties")) {
if (is == null) {
throw new IllegalStateException("version.properties not found on classpath.");
}
props.load(is);
} catch (IOException e) {
throw new IllegalStateException("Failed to load version.properties", e);
}
String version = props.getProperty(VERSION_PROP);
if (version == null || version.isBlank()) {
throw new IllegalStateException(VERSION_PROP + " property missing in version.properties.");
}
return version.trim();
}
private Glimmer loadGlim() {
String implVersion = readImplVersion();
String className = "io.github.adrianvic.nemesiseye.impl." + implVersion;
try {
Class<?> clazz = Class.forName(className, true, getClass().getClassLoader());
if (!Glimmer.class.isAssignableFrom(clazz)) {
throw new IllegalStateException(className + " does not implement Glimmer.");
}
return (Glimmer) clazz.getDeclaredConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Failed to instantiate " + className, e);
}
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
public static Nemesis getInstance() {
return getPlugin(Nemesis.class);
}
public static Nemesis getInstance() { return instance; }
public Glimmer getGlimmer() { return glim; }
public PluginManager getPluginManager() { return this.getServer().getPluginManager(); }
}

View file

@ -1,16 +1,17 @@
package io.github.adrianvic.nemesiseye;
import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
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.util.BoundingBox;
import java.util.ArrayList;
import java.util.List;
public class Validator {
private final static Glimmer glim = Nemesis.getInstance().getGlimmer();
public static boolean canInteract(HumanEntity entity) {
return checkAgainstEntity(entity, Action.INTERACT);
}
@ -30,13 +31,11 @@ public class Validator {
for (PolicyNode n : nodes) {
if (!checkAgainstNode(entity, n, action)) 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;
return node.getHandler().allows(entity, node, action);
}
public static List<PolicyNode> getNodesForPolicies(List<Policy> policies) {
@ -49,18 +48,6 @@ public class Validator {
public static List<Policy> getPoliciesForEntity(HumanEntity entity) {
List<Policy> ps = Config.getInstance().getPolicies();
List<Policy> applyingLPS = new ArrayList<>();
for (Policy p : ps) {
if (p instanceof LocationPolicy lp) {
for (ArrayList<BoundingBox> boxes : lp.locations()) {
for (BoundingBox box : boxes) {
if (box.contains(entity.getLocation().toVector())) {
applyingLPS.add(lp);
}
}
}
}
}
return applyingLPS;
return glim.getApplyingPoliciesForEntity(entity, ps);
}
}

View file

@ -2,20 +2,14 @@ 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<String, Subcommand> subs = new HashMap<>();
public class EyeCore {
public final Map<String, Subcommand> subs = new HashMap<>();
public Eye() {
public EyeCore() {
register(new Reload());
register(new ListPolicies());
register(new PolicyInfo());
@ -26,14 +20,13 @@ public class Eye implements CommandExecutor, TabCompleter {
subs.put(sub.name(), sub);
}
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) {
public boolean onCommand(CommandSender commandSender, Command command, String s, String [] strings) {
if (strings.length == 0) {
commandSender.sendMessage("""
%sEye of Nemesis%s version %s
Eye of Nemesis version %s
Usage: '/eye <command>'
Use '/eye help' for a list of available commands
""".formatted(ChatColor.RED, ChatColor.RESET, Nemesis.getInstance().getDescription().getVersion()));
""".formatted(Nemesis.getInstance().getDescription().getVersion()));
} else {
Subcommand sub = subs.get(strings[0].toLowerCase());
if (sub == null) {
@ -45,8 +38,7 @@ public class Eye implements CommandExecutor, TabCompleter {
return false;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) {
public List<String> onTabComplete(CommandSender commandSender, Command command, String s, String [] strings) {
if (strings.length == 1) {
return new ArrayList<>(subs.keySet());
}
@ -56,4 +48,6 @@ public class Eye implements CommandExecutor, TabCompleter {
}
return List.of();
}
public Map<String, Subcommand> getSubs() { return subs; };
}

View file

@ -20,11 +20,11 @@ public class PolicyInfo implements Subcommand {
for (Policy policy : policies) {
if (policy.name().equals(strings[0])) {
commandSender.sendMessage(String.format("""
Showing info for policy "%s%s%s":
Showing info for policy "%s":
Type: %s
Nodes: %s
%s
""", ChatColor.UNDERLINE, policy.name(), ChatColor.RESET, "location", policy.nodes().size(), policy.allowlist() ? "Is allowlist" : "Is blacklist"));
""", policy.name(), "location", policy.nodes().size(), policy.allowlist() ? "Is allowlist" : "Is blacklist"));
}
}
return true;

View file

@ -1,20 +1,25 @@
package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity;
public class attackWith implements NodeHandler {
private final static Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
if (action == Action.HIT) {
for (String s : DataShifter.parseValueToStringList(node.values())) {
if (DataShifter.safeMatches(s, entity.getInventory().getItemInMainHand().getType().toString())) return false;
boolean matches = DataShifter.safeMatches(s, glim.getItemInMainHandHumanEntity(entity).getType().toString());
if (matches) return node.isWhitelist();
}
}
return true;
return !node.isWhitelist();
}
}

View file

@ -1,41 +1,27 @@
package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.enchantments.Enchantment;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
public class useEnchantment implements NodeHandler {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
ItemStack item = entity.getInventory().getItemInMainHand();
if (item.getItemMeta() == null) {
return !node.isWhitelist();
}
ItemStack item = glim.getItemInMainHandHumanEntity(entity);
Map<Enchantment, Integer> enchants = item.getItemMeta().getEnchants();
if (!glim.hasItemMeta(item)) return true;
if (!glim.hasAnyEnchantment(item)) return true;
if (enchants.isEmpty()) {
return !node.isWhitelist();
}
boolean matches = glim.hasEnchantment(item,
DataShifter.parseValueToStringMap(node.values()));
Map<String, String> valuesmap = DataShifter.parseValueToStringMap(node.values());
for (Map.Entry<Enchantment, Integer> e : enchants.entrySet()) {
String enchantment = e.getKey().getKey().getKey();
String level = e.getValue().toString();
for (Map.Entry<String, String> entry : valuesmap.entrySet()) {
if (DataShifter.safeMatches(entry.getKey().trim(), enchantment) && DataShifter.safeMatches(entry.getValue().trim(), level)) {
return false;
}
}
}
return true;
return matches ? node.isWhitelist() : !node.isWhitelist();
}
}

View file

@ -1,22 +1,25 @@
package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity;
public class useItem implements NodeHandler {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
String type = entity.getInventory().getItemInMainHand().getType().toString();
String type = glim.getItemInMainHandHumanEntity(entity).getType().toString();
for (String s : DataShifter.parseValueToStringList(node.values())) {
if (DataShifter.safeMatches(s, type)) {
return false;
boolean matches = DataShifter.safeMatches(s, type);
if (matches) return node.isWhitelist();
}
}
return true;
return !node.isWhitelist();
}
}

View file

@ -1,17 +1,19 @@
package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.*;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import org.bukkit.Bukkit;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Location;
import org.bukkit.util.BoundingBox;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LocationPolicyParser implements PolicyParser {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
public List<Policy> parse(List<?> raw) {
List<Policy> out = new ArrayList<>(raw.size());
List<Map<?, ?>> parsedRawMap = DataShifter.parseValueToListOfMaps(raw);
@ -33,12 +35,12 @@ public class LocationPolicyParser implements PolicyParser {
List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, allowlist);
// Parsing locations
List<ArrayList<BoundingBox>> locations = new ArrayList<>();
List<ArrayList<Glimmer.Box>> locations = new ArrayList<>();
Object rawLocations = m.get("locations");
List<?> groups = rawLocations instanceof List ? (List<?>) rawLocations : List.of();
ArrayList<BoundingBox> boxes = new ArrayList<>(groups.size());
ArrayList<Glimmer.Box> boxes = new ArrayList<>(groups.size());
// Now iterate over regions
for (Object rObj : groups) {
@ -54,10 +56,10 @@ public class LocationPolicyParser implements PolicyParser {
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);
Location loc1 = new Location(glim.getWorlds().getFirst(), x1, y1, z1);
Location loc2 = new Location(glim.getWorlds().getFirst(), x2, y2, z2);
boxes.add(BoundingBox.of(loc1, loc2));
boxes.add(Glimmer.Box.of(loc1, loc2));
}
locations.add(boxes);

View file

@ -2,9 +2,9 @@ package io.github.adrianvic.nemesiseye.policy.policies;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.util.BoundingBox;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import java.util.ArrayList;
import java.util.List;
public record LocationPolicy(String name, List<ArrayList<BoundingBox>> locations, List<PolicyNode> nodes, boolean allowlist) implements Policy {}
public record LocationPolicy(String name, List<ArrayList<Glimmer.Box>> locations, List<PolicyNode> nodes, boolean allowlist) implements Policy {}

View file

@ -0,0 +1,52 @@
package io.github.adrianvic.nemesiseye.reflection;
import io.github.adrianvic.nemesiseye.policy.Policy;
import org.bukkit.Location;
import org.bukkit.World;
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;
public interface Glimmer {
File loadConfigFile();
List<Policy> loadPoliciesFromFile(File file);
List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies);
void onLoad();
ItemStack getItemInMainHandHumanEntity(HumanEntity entity);
boolean hasItemMeta(ItemStack item);
List<World> getWorlds();
boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap);
boolean hasAnyEnchantment(ItemStack itemStack);
class Box {
public final double x1, y1, z1, x2, y2, z2;
public Box(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);
}
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 static Box of(Location loc1, Location loc2) { return new Box(loc1.getX(), loc1.getY(), loc1.getZ(), loc2.getX(), loc2.getY(), loc2.getZ()); }
}
}

View file

@ -103,7 +103,7 @@ Policies:
- FISHING_ROD
- CLOCK
- GLOWSTONE_DUST
- INK_SAC
- INK_SACw
- BONE_MEAL
- SUGAR
- COOKIE
@ -115,7 +115,7 @@ Policies:
- DIRT
- BREAD
- useEnchantment:
"gibberish": 999999
"theresnoenchantmentwiththisname": "3"
locations:
- corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 }

View file

@ -0,0 +1,25 @@
package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.Events;
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) {
Events.onBlockBreak(event);
}
@EventHandler
public void onInteractionEvent(PlayerInteractEvent event) {
Events.onInteractionEvent(event);
}
@EventHandler
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
Events.onEntityDamageByEntityEvent(event);
}
}

View file

@ -0,0 +1,30 @@
package io.github.adrianvic.nemesiseye.impl.commands;
import io.github.adrianvic.nemesiseye.commands.EyeCore;
import io.github.adrianvic.nemesiseye.commands.sub.*;
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 EyeCore core;
public Eye() {
core = new EyeCore();
}
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) {
return core.onCommand(commandSender, command, s, strings);
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) {
return core.onTabComplete(commandSender, command, s, strings);
}
}

View file

@ -0,0 +1,116 @@
package io.github.adrianvic.nemesiseye.impl;
import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.impl.commands.Eye;
import io.github.adrianvic.nemesiseye.policy.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyParsers;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.PluginManager;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class r1_21 implements Glimmer {
@Override
public File loadConfigFile() {
File file = new File(Nemesis.getInstance().getDataFolder(), "settings.yml");
if (!file.exists())
Nemesis.getInstance().saveResource("settings.yml", false);
return file;
}
@Override
public List<Policy> loadPoliciesFromFile(File file) {
YamlConfiguration config = new YamlConfiguration();
config.options().parseComments(true);
try {
config.load(file);
} catch (Exception e) {
e.printStackTrace();
}
List<Map<?, ?>> rawPolicies = config.getMapList("Policies");
List<Policy> allPolicies = new ArrayList<>();
for (Map<?, ?> map : rawPolicies) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() instanceof String k && entry.getValue() instanceof List<?> v) {
List<Policy> parsed = PolicyParsers.get(k).parse(v);
allPolicies.addAll(parsed);
}
}
}
return allPolicies;
}
@Override
public List<Policy> getApplyingPoliciesForEntity(HumanEntity entity, List<Policy> policies) {
List<Policy> applyingLPS = new ArrayList<>();
for (Policy p : policies) {
if (p instanceof LocationPolicy lp) {
for (ArrayList<Box> boxes : lp.locations()) {
for (Box box : boxes) {
if (box.contains(entity.getLocation().toVector())) {
applyingLPS.add(lp);
}
}
}
}
}
return applyingLPS;
}
@Override
public void onLoad() {
PluginManager pm = Nemesis.getInstance().getPluginManager();
Nemesis.getInstance().getCommand("eye").setExecutor(new Eye());
pm.registerEvents(new EventListener(), Nemesis.getInstance());
}
@Override
public ItemStack getItemInMainHandHumanEntity(HumanEntity entity) {
return entity.getInventory().getItemInMainHand();
}
@Override
public boolean hasItemMeta(ItemStack item) {
if (item.getItemMeta() == null) {
return false;
}
return true;
}
@Override
public List<World> getWorlds() {
return Bukkit.getWorlds();
}
@Override
public boolean hasEnchantment(ItemStack item, Map<String, String> valuesmap) {
Map<Enchantment, Integer> enchantmentList = item.getEnchantments();
for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentList.entrySet()) {
for (Map.Entry<String, String> valueEntry : valuesmap.entrySet()) {
if (enchantmentEntry.getKey().getKey().getKey().equals(valueEntry.getKey()) && enchantmentEntry.getValue().toString().equals(valueEntry.getValue())) {
return true;
}
}
}
return false;
}
public boolean hasAnyEnchantment(ItemStack item) {
return !(item.getItemMeta().getEnchants().isEmpty());
}
}

View file

@ -1,5 +1,5 @@
name: "Eye-of-Nemesis"
version: '1.0.2-SNAPSHOT'
version: '1.0.3-SNAPSHOT'
main: io.github.adrianvic.nemesiseye.Nemesis
api-version: '1.21'
author: 'Adrian Victor'

View file

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