diff --git a/docs/Nodes.md b/docs/Nodes.md index d5a88bf..57cff1d 100644 --- a/docs/Nodes.md +++ b/docs/Nodes.md @@ -1,5 +1,5 @@ # 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. +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]: @@ -31,11 +31,4 @@ Triggered on `InventoryClickEvent` and `PlayerInteractEvent` (only if item is an Triggered on `PlayerMoveEvent`, returns true if player is gliding. ### USE_ENCHANTMENT -Triggered on `BlockBreakEvent` and `onEntityDamageByEntityEvent`, returns true when the used item has an enchantment matching the provided mapping of enchantment-level. - -### FORMBLOCK -Triggered on `BlockFormEvent` (ex. triggers when snow is formed from rain), returns true when the block being formed is in the provided list of materials. - -## ENDERMAN_ATTACK_PLAYER -Triggered on `EndermanAttackPlayerEvent` (from actions other than hitting the Enderman), returns true. - +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/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/Events.java b/src/main/java/io/github/adrianvic/nemesiseye/Events.java index bb857e2..a611890 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Events.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Events.java @@ -1,12 +1,10 @@ package io.github.adrianvic.nemesiseye; -import com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent; 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.BlockFormEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -49,7 +47,7 @@ public class Events { // Normal item interaction event.setCancelled( - !Validator.can(event.getPlayer(), List.of(Action.INTERACT, Action.INTERACTWITH), event) + !Validator.can(event.getPlayer(), Action.INTERACT, event) ); } @@ -97,13 +95,4 @@ public class Events { public static void onCreatureSpawnEvent(CreatureSpawnEvent event) { event.setCancelled(!Validator.can(event.getEntity(), Action.SPAWN, event)); } - - public static void onEndermanAttackPlayerEvent(EndermanAttackPlayerEvent event) { - event.setCancelled(!Validator.can(event.getEntity(), Action.ENDERMAN_ATTACK_PLAYER, event)); - } - - public static void onBlockFormEvent(BlockFormEvent event) { - boolean canceled = !Validator.can(event.getBlock().getWorld(), Action.FORMBLOCK, event); - event.setCancelled(canceled); - } } \ No newline at end of file diff --git a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java index 1e3e020..9989ea6 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/Validator.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/Validator.java @@ -2,8 +2,6 @@ package io.github.adrianvic.nemesiseye; import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Policy; -import io.github.adrianvic.nemesiseye.policy.policies.GlobalPolicy; -import org.bukkit.World; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; @@ -23,8 +21,11 @@ public class Validator { } public static boolean can(LivingEntity entity, Action action, Event event) { + boolean restricted = false; + boolean allowed = false; + for (Policy policy : getPoliciesForEntity(entity)) { - boolean matches = policy.matches(entity.getWorld(), action, event); + boolean matches = policy.matches(entity, action, event); switch (policy.effect()) { case ALLOW: @@ -40,33 +41,6 @@ public class Validator { return true; } - public static boolean can(World world, Action action, Event event) { - for (GlobalPolicy policy : getGlobalPolicies()) { - boolean matches = policy.matches(world, action, event); - - switch (policy.effect()) { - case ALLOW: - if (matches) return true; - break; - - case DENY: - if (matches) return false; - } - } - - return true; - } - - public static List getGlobalPolicies() { - List ps = Config.getInstance().getPolicies(); - List result = new ArrayList<>(); - - for (Policy policy : ps) { - if (policy instanceof GlobalPolicy p) result.add(p); - } - - return result; - } public static List getPoliciesForEntity(LivingEntity entity) { List ps = Config.getInstance().getPolicies(); diff --git a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java index fafdb24..6d2c799 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/commands/EyeCore.java @@ -29,6 +29,7 @@ public class EyeCore { 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; } @@ -42,11 +43,11 @@ public class EyeCore { 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)); 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 7b85c69..debbb36 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Action.java @@ -2,7 +2,6 @@ package io.github.adrianvic.nemesiseye.policy; public enum Action { INTERACT, - INTERACTWITH, BREAK, HIT, // TODO CRAFT, @@ -10,7 +9,5 @@ public enum Action { PLACE, USE_ENCHANTMENT, GLYDE, - SPAWN, - ENDERMAN_ATTACK_PLAYER, - FORMBLOCK + SPAWN } 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 e2a6f7a..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,8 +1,9 @@ package io.github.adrianvic.nemesiseye.policy; -import org.bukkit.World; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; public interface NodeHandler { - boolean check(World world, PolicyNode node, Action action, Event event); + 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 77db729..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,7 +1,6 @@ package io.github.adrianvic.nemesiseye.policy; import io.github.adrianvic.nemesiseye.policy.handlers.*; -import io.github.adrianvic.nemesiseye.policy.handlers.Boolean; import java.util.HashMap; import java.util.Map; @@ -14,13 +13,10 @@ public class NodeHandlers { handlers.put(Action.PLACE, new BePlaced()); handlers.put(Action.INTERACT, new UseItem()); handlers.put(Action.USE_ENCHANTMENT, new UseEnchantment()); - handlers.put(Action.GLYDE, new Boolean()); + 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 - handlers.put(Action.INTERACTWITH, new InteractWith()); - handlers.put(Action.ENDERMAN_ATTACK_PLAYER, new Boolean()); - handlers.put(Action.FORMBLOCK, new FormBlock()); } public static NodeHandler get(Action type) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java index eba8457..5cb69cc 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/Policy.java @@ -1,6 +1,6 @@ package io.github.adrianvic.nemesiseye.policy; -import org.bukkit.World; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; @@ -19,13 +19,13 @@ public interface Policy { nodes().add(node); } - default boolean matches(World world, Action action, Event event) { - if (!worlds().contains(world.getName())) { + default boolean matches(LivingEntity entity, Action action, Event event) { + if (!worlds().contains(entity.getWorld().getName())) { return false; } for (PolicyNode node : nodes()) { - if (node.matches(world, action, event)) { + if (node.matches(entity, action, event)) { return true; } } 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 0b9e77d..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,7 +1,8 @@ package io.github.adrianvic.nemesiseye.policy; import io.github.adrianvic.nemesiseye.DataShifter; -import org.bukkit.World; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import java.util.ArrayList; @@ -18,28 +19,27 @@ public record PolicyNode(List actions, List values) { List nodeActions = new ArrayList<>(); List nodeValues = new ArrayList<>(); - if (rawNode.getKey() instanceof List rawTypes && (rawNode.getValue() == null || rawNode.getValue() instanceof Map)) { + if (rawNode.getKey() instanceof List rawTypes && rawNode.getValue() instanceof Map rawNodeValues) { for (Object rawType : rawTypes) { - if (rawType instanceof String rts && !rts.isEmpty() && DataShifter.enumOrDefault(Action.class, rts, null) != null) { + if (rawType instanceof String rts && !rts.isEmpty() && !(rts == null) && DataShifter.enumOrDefault(Action.class, rts, null) != null) { nodeActions.add(DataShifter.enumOrDefault(Action.class, rts, null)); } } - - if (rawNode.getValue() instanceof Map rawNodeValues) { - Map semiParsedNodeValue = new HashMap<>(); - for (Map.Entry rawNodeValueEntries : rawNodeValues.entrySet()) { - if (rawNodeValueEntries.getKey() instanceof String stringKey) { - semiParsedNodeValue.put(stringKey, rawNodeValueEntries.getValue()); - } + 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) { - nodeValues.addAll(l); + if (semiParsedNodeValue.get("values") instanceof List l) { + for (Object v : l) { + nodeValues.add(v); } } } - if (!nodeActions.isEmpty()) { + if (!nodeActions.isEmpty() && !nodeValues.isEmpty()) { PolicyNode newNode = new PolicyNode(nodeActions, nodeValues); nodes.add(newNode); } @@ -56,7 +56,7 @@ public record PolicyNode(List actions, List values) { return handlers; } - public boolean matches(World world, Action action, Event event) { + public boolean matches(LivingEntity entity, Action action, Event event) { if (!actions.contains(action)) { return false; } @@ -67,6 +67,7 @@ public record PolicyNode(List actions, List values) { return false; } - return handler.check(world, this, action, event); + 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 index 4e29e18..8050873 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/PolicyParser.java @@ -34,11 +34,8 @@ public interface PolicyParser { if (rawNodes instanceof List list) { for (Object o : list) { - if (o instanceof Map map) { + if (o instanceof Map map) nodeList.add((Map) map); - } else if (o instanceof List actions) { - nodeList.add(Map.of(actions, Map.of())); - } } } 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 index f892302..ff9d08b 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/BePlaced.java @@ -4,7 +4,8 @@ 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.World; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import org.bukkit.event.block.BlockPlaceEvent; @@ -12,7 +13,7 @@ import java.util.List; public class BePlaced implements NodeHandler { @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { + 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()); 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 index 9b95756..8d5cfcf 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Equip.java @@ -6,7 +6,7 @@ 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.World; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; import org.bukkit.inventory.ItemStack; @@ -16,7 +16,7 @@ public class Equip implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { + public boolean check(LivingEntity entity, PolicyNode node, Action action, Event event) { ItemStack item = glim.getEquippedItem(event); if (!glim.isArmor(item)) { diff --git a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java deleted file mode 100644 index 56bfc45..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/FormBlock.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy.handlers; - -import io.github.adrianvic.nemesiseye.DataShifter; -import io.github.adrianvic.nemesiseye.policy.Action; -import io.github.adrianvic.nemesiseye.policy.NodeHandler; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.event.Event; -import org.bukkit.event.block.BlockFormEvent; - -import java.util.List; - -public class FormBlock implements NodeHandler { - @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { - if (event instanceof BlockFormEvent e) { - Material material = e.getNewState().getType(); - - String type = material.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/Boolean.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java similarity index 63% rename from src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java rename to src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java index 8b9f16c..46d2b11 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Boolean.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Glyde.java @@ -3,12 +3,12 @@ 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.World; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; -public class Boolean implements NodeHandler { +public class Glyde implements NodeHandler { @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { + 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/InteractWith.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/InteractWith.java deleted file mode 100644 index 33d35eb..0000000 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/InteractWith.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.adrianvic.nemesiseye.policy.handlers; - -import io.github.adrianvic.nemesiseye.DataShifter; -import io.github.adrianvic.nemesiseye.Nemesis; -import io.github.adrianvic.nemesiseye.policy.Action; -import io.github.adrianvic.nemesiseye.policy.NodeHandler; -import io.github.adrianvic.nemesiseye.policy.PolicyNode; -import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.event.Event; -import org.bukkit.event.player.PlayerInteractEvent; - -import java.util.List; - -public class InteractWith implements NodeHandler { - @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { - if (event instanceof PlayerInteractEvent e) { - Material material = e.getMaterial(); - - String type = material.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/Spawn.java b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java index cde4d9d..938f6ee 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/Spawn.java @@ -4,23 +4,18 @@ 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.World; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; -import org.bukkit.event.entity.EntityEvent; import java.util.List; public class Spawn implements NodeHandler { @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { - if (event instanceof EntityEvent e) { - String type = e.getEntity().getType().name(); - List parsedValue = DataShifter.parseValueToStringList(node.values()); + 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); - } - - return false; + 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 index f329e48..d67e9f9 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseEnchantment.java @@ -6,26 +6,27 @@ 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.World; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; -import org.bukkit.event.entity.EntityEvent; import org.bukkit.inventory.ItemStack; public class UseEnchantment implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { - if (event instanceof EntityEvent entityEvent && entityEvent.getEntity() instanceof HumanEntity e) { + 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; - return glim.hasEnchantment(item, + 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 index 545adfe..ef91a98 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/policy/handlers/UseItem.java @@ -6,10 +6,9 @@ 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.World; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; -import org.bukkit.event.entity.EntityEvent; import java.util.List; @@ -18,8 +17,8 @@ public class UseItem implements NodeHandler { private final Glimmer glim = Nemesis.getInstance().getGlimmer(); @Override - public boolean check(World world, PolicyNode node, Action action, Event event) { - if (event instanceof EntityEvent entityEvent && entityEvent.getEntity() instanceof HumanEntity e) { + 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; diff --git a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java index 1e83f2c..d8323cc 100644 --- a/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java +++ b/src/main/java/io/github/adrianvic/nemesiseye/reflection/VersionMatcher.java @@ -3,10 +3,96 @@ 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 { @@ -23,19 +109,52 @@ public class VersionMatcher { } } - String impl = DataShifter.safeMatches("^1\\.7\\.3$", rawVersion) - ? "b1_7_3" - : "r1_21"; - - String subPkg = impl.equals("b1_7_3") ? "v1_7_3" : "v1_21"; - Glimmer glim = tryInstantiate( - "io.github.adrianvic.nemesiseye.impl." + subPkg + "." + impl); - - if (glim == null) { - throw new IllegalStateException("Missing implementation: " + impl); + String matchInfo = getVersion("release", rawVersion); + if (matchInfo.isEmpty()) { + matchInfo = getVersion("beta", rawVersion); } - return glim; + 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) { diff --git a/src/main/resources/settings.yml b/src/main/resources/settings.yml index 0611c04..a94dd2f 100644 --- a/src/main/resources/settings.yml +++ b/src/main/resources/settings.yml @@ -5,19 +5,18 @@ # \_\ \_____ / /_/ /_______ /\_______ /\____|__ / # \/ \/ \/ \/ # EYE OF NEMESIS - Example config file. -# Documentation in our repository's /docs +# Documentation in our wiki: https://github.com/adrianvic/NemesisEye Policies: - # This example blocks the usage of bedrock without the server.usebedrock permissin - - name: "Bedrock-allow-admins" # No spaces here or else commands which need the name as argument will not work - type: "permission" # global | location | permission | playerName + - name: "Bedrock-allow-admins" + type: "permission" worlds: [world] - effect: ALLOW # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met) - weight: 3 # Greater weight overrides lower + effect: ALLOW + weight: 3 permissions: - "server.usebedrock" nodes: - - [PLACE]: + - [BREAK, PLACE, HIT, INTERACT]: values: - BEDROCK @@ -27,16 +26,15 @@ Policies: effect: DENY weight: 2 nodes: - - [PLACE]: + - [BREAK, PLACE, HIT, INTERACT]: values: - BEDROCK - # This example blocks all non-beta (b1.7.3) items, blocks, mobs, enchantments in a rectangular area - - name: "Block-non-beta-items" - type: "location" - effect: DENY + - name: "Block-non-beta-items" # No spaces here or else commands which need the name as argument will not work + type: "location" # global | location | permission | playerName + effect: DENY # DENY | ALLOW (overrides deny) | ALLOWONLY (allow only if met) worlds: [world] - weight: 0 + weight: 0 # Greater weight overrides lower policyAllowList: false # Inverts the policy validation logic, for example a location policy will affect all players NOT inside it's location locations: worlds: [world] @@ -44,7 +42,7 @@ Policies: - corner1: { x: 2100, y: 256, z: 1400 } corner2: { x: 1000, y: -64, z: 2200 } nodes: - - [INTERACT, BREAK, HIT, PLACE, GLYDE, EQUIP, SPAWN]: + - [INTERACT, BREAK, HIT, PLACE]: values: - '.*' - [USE_ENCHANTMENT]: @@ -61,7 +59,7 @@ Policies: - corner1: { x: 2100, y: 256, z: 1400 } corner2: { x: 1000, y: -64, z: 2200 } nodes: - - [INTERACT, BREAK, HIT, PLACE, SPAWN]: + - [INTERACT, BREAK, HIT, PLACE]: values: - AIR - STONE @@ -170,16 +168,4 @@ Policies: - MUSIC_DISK_CAT - MUSIC_DISK_13 - DIRT - - BREAD - - ZOMBIE - - SKELETON - - CHICKEN - - PIG - - SHEEP - - COW - - SQUID - - WOLF - - PIGZOMBIE - - SPIDER - - GHAST - - CREEPER \ No newline at end of file + - BREAD \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java new file mode 100644 index 0000000..ea64591 --- /dev/null +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/EventListener.java @@ -0,0 +1,45 @@ +package io.github.adrianvic.nemesiseye.impl; + +import io.github.adrianvic.nemesiseye.Events; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; + +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); + } + + @EventHandler + public void onBlockPlaceEvent(BlockPlaceEvent event) { + Events.onBlockPlaceEvent(event); + } + + @EventHandler + public void onPlayerMoveEvent(PlayerMoveEvent event) { Events.onPlayerMoveEvent(event); } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onInventoryClickEvent(InventoryClickEvent event) { Events.onInventoryClickEvent(event); } + + @EventHandler + public void onCreatureSpawnEvent(CreatureSpawnEvent event) { Events.onCreatureSpawnEvent(event); } +} \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java new file mode 100644 index 0000000..e28b412 --- /dev/null +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/commands/Eye.java @@ -0,0 +1,29 @@ +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; +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 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 onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { + return core.onTabComplete(commandSender, command, s, strings); + } +} \ No newline at end of file diff --git a/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java new file mode 100644 index 0000000..ac1b6d7 --- /dev/null +++ b/src/r1_21/java/io/github/adrianvic/nemesiseye/impl/r1_21.java @@ -0,0 +1,201 @@ +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.reflection.Glimmer; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +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; + +@SuppressWarnings("unused") +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 loadPoliciesFromFile(File file) { + YamlConfiguration config = new YamlConfiguration(); + config.options().parseComments(true); + + try { + config.load(file); + } catch (Exception e) { + e.printStackTrace(); + } + + List> rawPolicies = config.getMapList("Policies"); + List allPolicies = new ArrayList<>(); + + for (Map policyMap : rawPolicies) { + if (policyMap.get("type") != null && policyMap.get("type") instanceof String type) { + allPolicies.add(PolicyParsers.get(type).parse(policyMap)); + } + } + + return allPolicies; + } + + @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 isAir(ItemStack item) { + return item == null || item.getType().isAir(); + } + + @Override + public boolean isGliding(org.bukkit.entity.Player player) { + return player.isGliding(); + } + + @Override + public void setGliding(org.bukkit.entity.Player player, boolean gliding) { + player.setGliding(gliding); + } + + @Override + public boolean hasPermission(org.bukkit.command.CommandSender sender, String permission) { + return sender.hasPermission(permission); + } + + @Override + public boolean isArmorEquipAttempt(org.bukkit.event.Event event) { + if (!(event instanceof org.bukkit.event.inventory.InventoryClickEvent e)) { + return false; + } + + if (e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) { + return true; + } + + if (e.isShiftClick()) { + return isArmor(e.getCurrentItem()); + } + + if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY + && e.getSlotType() == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR + && e.getWhoClicked() instanceof org.bukkit.entity.Player player) { + return isArmor( + player.getInventory().getItem(e.getHotbarButton()) + ); + } + + return false; + } + + @Override + public ItemStack getEquippedItem(org.bukkit.event.Event event) { + if (event instanceof org.bukkit.event.inventory.InventoryClickEvent e) { + org.bukkit.event.inventory.InventoryType.SlotType slotType = e.getSlotType(); + + if (e.getClick() == org.bukkit.event.inventory.ClickType.NUMBER_KEY // hotbar key swap + && slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR + && e.getWhoClicked() instanceof org.bukkit.entity.Player player) { + return player.getInventory().getItem(e.getHotbarButton()); + } + + if (e.isShiftClick()) { + ItemStack current = e.getCurrentItem(); + if (isArmor(current)) return current; + } + + // regular click + if (slotType == org.bukkit.event.inventory.InventoryType.SlotType.ARMOR) { + ItemStack cursor = e.getCursor(); + if (isArmor(cursor)) return cursor; + } + } + + // Try Paper's PlayerArmorChangeEvent via reflection or just check if class exists + try { + if (event instanceof com.destroystokyo.paper.event.player.PlayerArmorChangeEvent e) { + return e.getNewItem(); + } + } catch (NoClassDefFoundError | Exception ignored) {} + + return null; + } + + @Override + public void sendMessage(CommandSender commandSender, String text) { + commandSender.sendMessage(text); + } + + @Override + public boolean hasItemMeta(ItemStack item) { + return item.getItemMeta() != null; + } + + @Override + public List getWorlds() { + return Bukkit.getWorlds(); + } + + @Override + public boolean hasEnchantment(ItemStack item, Map valuesmap) { + Map enchantments = item.getEnchantments(); + + for (Map.Entry ench : enchantments.entrySet()) { + String enchKey = ench.getKey().getKey().getKey(); + String enchLevel = ench.getValue().toString(); + + for (Map.Entry rule : valuesmap.entrySet()) { + if ( + DataShifter.safeMatches(rule.getKey(), enchKey) && + DataShifter.safeMatches(rule.getValue(), enchLevel) + ) { + return true; + } + } + } + return false; + } + + public boolean hasAnyEnchantment(ItemStack item) { + return !(item.getItemMeta().getEnchants().isEmpty()); + } + + @Override + public boolean isArmor(ItemStack item) { + if (item == null || item.getType().isAir()) { + return false; + } + + String name = item.getType().name(); + + return name.endsWith("_HELMET") + || name.endsWith("_CHESTPLATE") + || name.endsWith("_LEGGINGS") + || name.endsWith("_BOOTS") + || item.getType() == org.bukkit.Material.ELYTRA; + } +} diff --git a/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java b/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java index bd00215..fe4ef52 100644 --- a/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java +++ b/src/test/java/io/github/adrianvic/nemesiseye/ValidatorTest.java @@ -4,11 +4,16 @@ import io.github.adrianvic.nemesiseye.policy.Action; import io.github.adrianvic.nemesiseye.policy.Effect; import io.github.adrianvic.nemesiseye.policy.Policy; import io.github.adrianvic.nemesiseye.reflection.Glimmer; -import org.bukkit.World; +import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class ValidatorTest { @@ -23,12 +28,12 @@ public class ValidatorTest { @Test void testValidatorCanDeny() { - World world = mock(World.class); + Player player = mock(Player.class); Event event = mock(Event.class); Policy policy = mock(Policy.class); -// when(policy.applies(world)).thenReturn(true); - when(policy.matches(world, Action.BREAK, event)).thenReturn(true); + when(policy.applies(player)).thenReturn(true); + when(policy.matches(player, Action.BREAK, event)).thenReturn(true); when(policy.effect()).thenReturn(Effect.DENY); // We need to handle the static Config.getInstance() diff --git a/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java b/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java index 74379c8..763bf53 100644 --- a/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java +++ b/src/test/java/io/github/adrianvic/nemesiseye/impl/ImplementationContractTest.java @@ -2,8 +2,6 @@ package io.github.adrianvic.nemesiseye.impl; import be.seeseemelk.mockbukkit.MockBukkit; import be.seeseemelk.mockbukkit.ServerMock; -import io.github.adrianvic.nemesiseye.impl.v1_21.r1_21; -import io.github.adrianvic.nemesiseye.impl.v1_7_3.b1_7_3; import io.github.adrianvic.nemesiseye.reflection.Glimmer; import org.bukkit.Material; import org.bukkit.inventory.ItemStack;