Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurable requirements for blocks #342

Merged
merged 17 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void reload(CommandSender sender) {
plugin.config().loadOptions();
plugin.getMessageProvider().loadDefaultLanguageOption();
plugin.getRequirementManager().load();
plugin.getRequirementManager().loadBlocks();
// Load blocked/disabled worlds lists
plugin.getWorldManager().loadWorlds();
// Load skills
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
import dev.aurelium.auraskills.api.skill.Skill;
import dev.aurelium.auraskills.bukkit.AuraSkills;
import dev.aurelium.auraskills.bukkit.item.SkillsItem;
import dev.aurelium.auraskills.bukkit.requirement.blocks.BlockRequirement;
import dev.aurelium.auraskills.bukkit.requirement.blocks.RequirementNode;
import dev.aurelium.auraskills.bukkit.util.armor.ArmorEquipEvent;
import dev.aurelium.auraskills.common.config.Option;
import dev.aurelium.auraskills.common.hooks.PlaceholderHook;
import dev.aurelium.auraskills.common.message.MessageKey;
import dev.aurelium.auraskills.common.message.type.CommandMessage;
import dev.aurelium.auraskills.common.user.User;
import dev.aurelium.auraskills.common.util.math.RomanNumber;
import dev.aurelium.auraskills.common.util.text.TextUtil;
import dev.aurelium.slate.text.TextFormatter;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.*;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.event.player.PlayerHarvestBlockEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;

Expand Down Expand Up @@ -96,6 +102,9 @@ public void onBlockBreak(BlockBreakEvent event) {
if (item.getType() == Material.AIR) return;
checkItemRequirements(player, item, event);
}

if (plugin.configBoolean(Option.REQUIREMENT_BLOCKS_ENABLED)) return;
checkBlockRequirements(event.getPlayer(), event.getBlock().getType(), event);
}

@EventHandler(priority = EventPriority.HIGH)
Expand All @@ -107,8 +116,19 @@ public void onPlace(BlockPlaceEvent event) {
if (item.getType() == Material.AIR) return;
checkItemRequirements(player, item, event);
}

if (plugin.configBoolean(Option.REQUIREMENT_BLOCKS_ENABLED)) return;
checkBlockRequirements(event.getPlayer(), event.getBlock().getType(), event);
}

@EventHandler(priority = EventPriority.HIGH)
public void onHarvest(PlayerHarvestBlockEvent event) {
if (event.isCancelled()) return;
if (plugin.configBoolean(Option.REQUIREMENT_BLOCKS_ENABLED)) return;
checkBlockRequirements(event.getPlayer(), event.getHarvestedBlock().getType(), event);
}


@EventHandler(priority = EventPriority.HIGH)
public void onAttack(EntityDamageByEntityEvent event) {
if (event.isCancelled()) return;
Expand Down Expand Up @@ -163,4 +183,40 @@ private void checkItemRequirements(Player player, ItemStack item, Cancellable ev
}
}

private void checkBlockRequirements(Player player, Material material, Cancellable event) {
BlockRequirement blockRequirement = null;

for (BlockRequirement block : manager.getBlocks()) {
if (block.getMaterial() == material) {
blockRequirement = block;
break;
}
}

if (blockRequirement != null) {
if (event instanceof BlockBreakEvent) {
if (!blockRequirement.checksBreaking()) return;
} else if (event instanceof BlockPlaceEvent) {
if (!blockRequirement.checksPlacing()) return;
}

for (RequirementNode node : blockRequirement.getNodes()) {
if (!node.check(player)) {
event.setCancelled(true);
String message = node.getDenyMessage();
if (!message.isEmpty()) {
TextFormatter formatter = new TextFormatter();
User user = plugin.getUser(player);

if (plugin.getHookManager().isRegistered(PlaceholderHook.class)) {
message = plugin.getHookManager().getHook(PlaceholderHook.class).setPlaceholders(user, message);
}

user.sendMessage(formatter.toComponent(message));
}
break;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import dev.aurelium.auraskills.api.item.ModifierType;
import dev.aurelium.auraskills.api.registry.NamespacedId;
import dev.aurelium.auraskills.api.skill.Skill;
import dev.aurelium.auraskills.api.stat.Stat;
import dev.aurelium.auraskills.bukkit.AuraSkills;
import dev.aurelium.auraskills.bukkit.requirement.blocks.*;
import dev.aurelium.auraskills.common.config.ConfigurateLoader;
import dev.aurelium.auraskills.common.scheduler.TaskRunnable;
import org.bukkit.Material;
Expand All @@ -20,13 +22,15 @@
public class RequirementManager implements Listener {

private Set<GlobalRequirement> globalRequirements;
private List<BlockRequirement> blockRequirements;
private final Map<UUID, Integer> errorMessageTimer;
private final AuraSkills plugin;

public RequirementManager(AuraSkills plugin) {
errorMessageTimer = new HashMap<>();
this.plugin = plugin;
load();
loadBlocks();
tickTimer();
}

Expand Down Expand Up @@ -74,10 +78,69 @@ public void load() {
}
}

public void loadBlocks() {
ConfigurateLoader loader = new ConfigurateLoader(plugin, TypeSerializerCollection.builder().build());
try {
ConfigurationNode config = loader.loadUserFile("config.yml");

this.blockRequirements = new ArrayList<>();
List<? extends ConfigurationNode> blockNodes = config.node("requirement.blocks", "list").childrenList();
for (ConfigurationNode blockNode : blockNodes) {
Material material = Material.valueOf(blockNode.node("material").getString().toUpperCase(Locale.ROOT));
boolean allowPlace = blockNode.node("allow_place").getBoolean();
boolean allowBreak = blockNode.node("allow_break").getBoolean();
boolean allowHarvest = blockNode.node("allow_harvest").getBoolean();
List<? extends ConfigurationNode> requirementNodes = blockNode.node("requirements").childrenList();
List<RequirementNode> nodes = new ArrayList<>();

for (ConfigurationNode requirementNode : requirementNodes) {
String type = requirementNode.node("type").getString();
String message = requirementNode.node("message").getString("");

switch (type) {
case "skill_level" -> {
Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromDefault(requirementNode.node("skill").getString().toLowerCase(Locale.ROOT)));
int level = requirementNode.node("level").getInt();
nodes.add(new SkillNode(plugin, skill, level, message));
}
case "permission" -> {
String permission = requirementNode.node("permission").getString();
nodes.add(new PermissionNode(plugin, permission, message));
}
case "excluded_world" -> {
String[] worlds = (String[]) requirementNode.node("world").getList(String.class).toArray();
nodes.add(new ExcludedWorldNode(plugin, worlds, message));
}
case "stat" -> {
Stat stat = plugin.getStatManager().getEnabledStats().stream().filter(s -> s.getId().equals(NamespacedId.fromDefault(requirementNode.node("stat").getString().toLowerCase(Locale.ROOT)))).findFirst().orElse(null);
int value = requirementNode.node("value").getInt();
nodes.add(new StatNode(plugin, stat, value, message));
}
default ->
plugin.logger().warn("Unknown requirement type: " + type);
}
}

BlockRequirement blockRequirement = new BlockRequirement(material, allowPlace, allowBreak, allowHarvest, nodes);
blockRequirements.add(blockRequirement);
}
if (!blockRequirements.isEmpty()) {
plugin.logger().info("Loaded " + blockRequirements.size() + " global requirement" + (blockRequirements.size() != 1 ? "s" : ""));
}
} catch (IOException e) {
plugin.logger().warn("Error loading block requirements: " + e.getMessage());
e.printStackTrace();
}
}

public Set<GlobalRequirement> getGlobalRequirements() {
return globalRequirements;
}

public List<BlockRequirement> getBlocks() {
return blockRequirements;
}

public Set<GlobalRequirement> getGlobalRequirementsType(ModifierType type) {
Set<GlobalRequirement> matched = new HashSet<>();
for (GlobalRequirement requirement : globalRequirements) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.aurelium.auraskills.bukkit.requirement.blocks;

import org.bukkit.Material;

import java.util.List;

public class BlockRequirement {

private final Material material;
private final boolean checksPlace;
private final boolean checksBreak;
private final boolean checksHarvest;
private final List<RequirementNode> nodes;

public BlockRequirement(Material material, boolean checksPlace, boolean checksBreak, boolean checksHarvest, List<RequirementNode> nodes) {
this.material = material;
this.checksPlace = checksPlace;
this.checksBreak = checksBreak;
this.checksHarvest = checksHarvest;
this.nodes = nodes;
}

public Material getMaterial() {
return material;
}

public boolean checksPlacing() {
return checksPlace;
}

public boolean checksBreaking() {
return checksBreak;
}

public boolean checksHarvesting() {
return checksHarvest;
}

public List<RequirementNode> getNodes() {
return nodes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.aurelium.auraskills.bukkit.requirement.blocks;

import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.entity.Player;

public class ExcludedWorldNode extends RequirementNode {

private final String[] worlds;

public ExcludedWorldNode(AuraSkills plugin, String[] worlds, String message) {
super(plugin, message);
this.worlds = worlds;
}

@Override
public boolean check(Player player) {
for (String world : worlds) {
if (player.getWorld().getName().equalsIgnoreCase(world)) {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.aurelium.auraskills.bukkit.requirement.blocks;

import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.entity.Player;

public class PermissionNode extends RequirementNode {

private final String permission;

public PermissionNode(AuraSkills plugin, String permission, String message) {
super(plugin, message);
this.permission = permission;
}

@Override
public boolean check(Player player) {
return player.hasPermission(permission);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.aurelium.auraskills.bukkit.requirement.blocks;

import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.entity.Player;

public abstract class RequirementNode {

protected AuraSkills plugin;
protected String denyMessage;

public RequirementNode(AuraSkills plugin, String denyMessage) {
this.plugin = plugin;
this.denyMessage = denyMessage;
}

public abstract boolean check(Player player);

public String getDenyMessage() {
return denyMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.aurelium.auraskills.bukkit.requirement.blocks;

import dev.aurelium.auraskills.api.skill.Skill;
import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.entity.Player;

public class SkillNode extends RequirementNode {

private final Skill skill;
private final int level;

public SkillNode(AuraSkills plugin, Skill skill, int level, String message) {
super(plugin, message);
this.skill = skill;
this.level = level;
}

@Override
public boolean check(Player player) {
return plugin.getUser(player).getSkillLevel(skill) >= level;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.aurelium.auraskills.bukkit.requirement.blocks;

import dev.aurelium.auraskills.api.stat.Stat;
import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.entity.Player;

public class StatNode extends RequirementNode {

private final Stat stat;
private final int value;

public StatNode(AuraSkills plugin, Stat stat, int value, String message) {
super(plugin, message);
this.stat = stat;
this.value = value;
}

@Override
public boolean check(Player player) {
return plugin.getUser(player).getStatLevel(stat) >= value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public enum Option {
REQUIREMENT_ITEM_PREVENT_BLOCK_PLACE("requirement.item.prevent_block_place", OptionType.BOOLEAN),
REQUIREMENT_ITEM_PREVENT_INTERACT("requirement.item.prevent_interact", OptionType.BOOLEAN),
REQUIREMENT_ARMOR_PREVENT_ARMOR_EQUIP("requirement.armor.prevent_armor_equip", OptionType.BOOLEAN),
REQUIREMENT_BLOCKS_ENABLED("requirement.blocks.enabled", OptionType.BOOLEAN),
// Damage options
DAMAGE_CORRECT_LAST_DAMAGE("damage.correct_last_damage", OptionType.BOOLEAN),
// Critical options
Expand Down
3 changes: 3 additions & 0 deletions common/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ requirement:
armor:
prevent_armor_equip: true
global: [ ]
blocks:
enabled: true
list: [ ]
damage:
correct_last_damage: true
critical:
Expand Down