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/ build/
out/ out/
.idea/ .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
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 ## 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`. - 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. - This plugin is in a very early stage.
## Policies ## Motivations
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`. 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.
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.
## Performance ## 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. 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.
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.

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

View file

@ -21,19 +21,23 @@ public class DataShifter {
return result; return result;
} }
public static Map<String, String> parseValueToStringMap(List<Object> values) { public static Map<String,String> parseValueToStringMap(List<Object> raw) {
Map<String, String> result = new HashMap<>(); Map<String,String> out = new HashMap<>();
for (Object o : values) { for (Object o : raw) {
if (o instanceof Map<?, ?> raw) { if (o instanceof Map<?,?> map) {
for (Map.Entry<?, ?> e : raw.entrySet()) { for (Map.Entry<?,?> e : map.entrySet()) {
if (e.getKey() instanceof String k && e.getValue() instanceof String v) { out.put(String.valueOf(e.getKey()), String.valueOf(e.getValue()));
result.put(k, v); }
} 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;
return result;
} }
public static List<Map<?, ?>> parseValueToListOfMaps(List<?> values) { 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.HumanEntity;
import org.bukkit.entity.Player; 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.block.BlockBreakEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
public class EventListener implements Listener { public class Events {
@EventHandler public static void onBlockBreak(BlockBreakEvent event) {
public void onBlockBreak(BlockBreakEvent event) {
event.setCancelled(!Validator.canBreak(event.getPlayer())); event.setCancelled(!Validator.canBreak(event.getPlayer()));
} }
@EventHandler public static void onInteractionEvent(PlayerInteractEvent event) {
public void onInteractionEvent(PlayerInteractEvent event) {
if (event.getItem() != null) { if (event.getItem() != null) {
event.setCancelled(!Validator.canInteract(event.getPlayer())); event.setCancelled(!Validator.canInteract(event.getPlayer()));
} }
} }
@EventHandler public static void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Player) { if (event.getDamager() instanceof Player) {
event.setCancelled(!Validator.canHit((HumanEntity) event.getDamager())); event.setCancelled(!Validator.canHit((HumanEntity) event.getDamager()));
} }

View file

@ -1,23 +1,65 @@
package io.github.adrianvic.nemesiseye; 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 org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public final class Nemesis extends JavaPlugin { public final class Nemesis extends JavaPlugin {
private Glimmer glim;
private static final String VERSION_PROP = "impl.version";
private static Nemesis instance;
@Override @Override
public void onEnable() { public void onEnable() {
getServer().getPluginManager().registerEvents(new EventListener(), this); instance = this;
glim = loadGlim();
glim.onLoad();
Config.getInstance().load(); 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 @Override
public void onDisable() { public void onDisable() {
// Plugin shutdown logic
} }
public static Nemesis getInstance() { public static Nemesis getInstance() { return instance; }
return getPlugin(Nemesis.class); public Glimmer getGlimmer() { return glim; }
} public PluginManager getPluginManager() { return this.getServer().getPluginManager(); }
} }

View file

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

View file

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

View file

@ -1,20 +1,25 @@
package io.github.adrianvic.nemesiseye.policy.handlers; package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.DataShifter; 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.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
public class attackWith implements NodeHandler { public class attackWith implements NodeHandler {
private final static Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override @Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) { public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
if (action == Action.HIT) { if (action == Action.HIT) {
for (String s : DataShifter.parseValueToStringList(node.values())) { 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; package io.github.adrianvic.nemesiseye.policy.handlers;
import io.github.adrianvic.nemesiseye.DataShifter; 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.Action;
import io.github.adrianvic.nemesiseye.policy.NodeHandler; import io.github.adrianvic.nemesiseye.policy.NodeHandler;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; import io.github.adrianvic.nemesiseye.policy.PolicyNode;
import org.bukkit.enchantments.Enchantment; import io.github.adrianvic.nemesiseye.reflection.Glimmer;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.Map;
public class useEnchantment implements NodeHandler { public class useEnchantment implements NodeHandler {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
@Override @Override
public boolean allows(HumanEntity entity, PolicyNode node, Action action) { public boolean allows(HumanEntity entity, PolicyNode node, Action action) {
ItemStack item = entity.getInventory().getItemInMainHand(); ItemStack item = glim.getItemInMainHandHumanEntity(entity);
if (item.getItemMeta() == null) {
return !node.isWhitelist();
}
Map<Enchantment, Integer> enchants = item.getItemMeta().getEnchants(); if (!glim.hasItemMeta(item)) return true;
if (!glim.hasAnyEnchantment(item)) return true;
if (enchants.isEmpty()) { boolean matches = glim.hasEnchantment(item,
return !node.isWhitelist(); DataShifter.parseValueToStringMap(node.values()));
}
Map<String, String> valuesmap = DataShifter.parseValueToStringMap(node.values()); return matches ? node.isWhitelist() : !node.isWhitelist();
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;
} }
} }

View file

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

View file

@ -1,17 +1,19 @@
package io.github.adrianvic.nemesiseye.policy.parser; package io.github.adrianvic.nemesiseye.policy.parser;
import io.github.adrianvic.nemesiseye.DataShifter; import io.github.adrianvic.nemesiseye.DataShifter;
import io.github.adrianvic.nemesiseye.Nemesis;
import io.github.adrianvic.nemesiseye.policy.*; import io.github.adrianvic.nemesiseye.policy.*;
import io.github.adrianvic.nemesiseye.policy.policies.LocationPolicy; 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.Location;
import org.bukkit.util.BoundingBox;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class LocationPolicyParser implements PolicyParser { public class LocationPolicyParser implements PolicyParser {
private Glimmer glim = Nemesis.getInstance().getGlimmer();
public List<Policy> parse(List<?> raw) { public List<Policy> parse(List<?> raw) {
List<Policy> out = new ArrayList<>(raw.size()); List<Policy> out = new ArrayList<>(raw.size());
List<Map<?, ?>> parsedRawMap = DataShifter.parseValueToListOfMaps(raw); List<Map<?, ?>> parsedRawMap = DataShifter.parseValueToListOfMaps(raw);
@ -33,12 +35,12 @@ public class LocationPolicyParser implements PolicyParser {
List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, allowlist); List<PolicyNode> nodes = PolicyNode.parseNodes(nodeList, allowlist);
// Parsing locations // Parsing locations
List<ArrayList<BoundingBox>> locations = new ArrayList<>(); List<ArrayList<Glimmer.Box>> locations = new ArrayList<>();
Object rawLocations = m.get("locations"); Object rawLocations = m.get("locations");
List<?> groups = rawLocations instanceof List ? (List<?>) rawLocations : List.of(); 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 // Now iterate over regions
for (Object rObj : groups) { for (Object rObj : groups) {
@ -54,10 +56,10 @@ public class LocationPolicyParser implements PolicyParser {
double y2 = ((Number) c2.get("y")).doubleValue(); double y2 = ((Number) c2.get("y")).doubleValue();
double z2 = ((Number) c2.get("z")).doubleValue(); double z2 = ((Number) c2.get("z")).doubleValue();
Location loc1 = new Location(Bukkit.getWorlds().getFirst(), x1, y1, z1); Location loc1 = new Location(glim.getWorlds().getFirst(), x1, y1, z1);
Location loc2 = new Location(Bukkit.getWorlds().getFirst(), x2, y2, z2); 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); 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.Policy;
import io.github.adrianvic.nemesiseye.policy.PolicyNode; 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.ArrayList;
import java.util.List; 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 - FISHING_ROD
- CLOCK - CLOCK
- GLOWSTONE_DUST - GLOWSTONE_DUST
- INK_SAC - INK_SACw
- BONE_MEAL - BONE_MEAL
- SUGAR - SUGAR
- COOKIE - COOKIE
@ -115,7 +115,7 @@ Policies:
- DIRT - DIRT
- BREAD - BREAD
- useEnchantment: - useEnchantment:
"gibberish": 999999 "theresnoenchantmentwiththisname": "3"
locations: locations:
- corner1: { x: 2100, y: 256, z: 1400 } - corner1: { x: 2100, y: 256, z: 1400 }
corner2: { x: 1000, y: -64, z: 2200 } corner2: { x: 1000, y: -64, z: 2200 }

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" name: "Eye-of-Nemesis"
version: '1.0.2-SNAPSHOT' version: '1.0.3-SNAPSHOT'
main: io.github.adrianvic.nemesiseye.Nemesis main: io.github.adrianvic.nemesiseye.Nemesis
api-version: '1.21' api-version: '1.21'
author: 'Adrian Victor' author: 'Adrian Victor'

View file

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