Start the implementation of a custom wrapper for enchantment. (#11)

This aim to allow support for custom implementation of
enchantment.
For example: multiple implementations of custom enchantment use
Persistent Data Container as storage.

This pr aim to create a system to wrap vanilla enchantment with the aim
to replicate this system with other enchantment storage system.
This will also aim to provide an api for other devs to register
themselves their implementation if they want to.
This commit is contained in:
alexcrea 2024-06-16 08:58:05 +00:00 committed by GitHub
commit 7c03c3d38b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 476 additions and 109 deletions

View file

@ -0,0 +1,316 @@
package xyz.alexcrea.cuanvil.enchant;
import io.delilaheve.CustomAnvil;
import io.delilaheve.util.ItemUtil;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.enchant.wrapped.VanillaEnchant;
import java.util.*;
import java.util.logging.Level;
/**
* Represent any enchantment.
* One issue with the plugin is: it does not handle well duplicate key name (ignoring namespace) as the plugin was coded with vanilla enchantment in head
*/
public abstract class WrappedEnchantment {
@NotNull
private final NamespacedKey key;
@NotNull
private final String name;
@NotNull
private final EnchantmentRarity defaultRarity;
private final int defaultMaxLevel;
/**
* Constructor of Wrapped Enchantment.
* @param key The enchantment's key.
* @param defaultRarity Default rarity the enchantment should be.
* @param defaultMaxLevel Default max level the enchantment can be applied with.
*/
public WrappedEnchantment(
@NotNull NamespacedKey key,
@Nullable EnchantmentRarity defaultRarity,
int defaultMaxLevel){
this.key = key;
this.name = key.getKey();
this.defaultMaxLevel = defaultMaxLevel;
if(defaultRarity == null) this.defaultRarity = EnchantmentRarity.COMMON;
else this.defaultRarity = defaultRarity;
}
/**
* Get the default rarity of this enchant.
* @return The default rarity of this enchant.
*/
public final EnchantmentRarity defaultRarity(){
return defaultRarity;
}
/**
* Get the enchantment key.
* @return The enchantment key.
*/
@NotNull
public final NamespacedKey getKey(){
return key;
}
/**
* Get the enchantment name.
* @return The enchantment name.
*/
@NotNull
public final String getName(){
return name;
}
/**
* Get the default maximum level of this enchantment.
* @return The default maximum level of this enchantment.
*/
public final int defaultMaxLevel(){return defaultMaxLevel;}
/**
* If the enchantment have specialised group operation.
* @return If the enchantment is optimised for group operation.
*/
protected boolean isOptimised(){
return false;
}
/**
* Get current level of the enchantment.
* @param item Item to search the level for.
*/
public int getLevel(@NotNull ItemStack item){
ItemMeta meta = item.getItemMeta();
if(meta == null) return 0;
return getLevel(item, meta);
}
/**
* Get current level of the enchantment.
* @param item Item to search the level for.
* @param meta Meta of the provided item. It will not be changed and not be set on the item.
* @return Current leve of this enchantment on item. or 0 if absent.
*/
public abstract int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta);
/**
* Check if this enchantment is present on the provided level.
* @param item The item to set the enchantment level.
* @return If the enchantment have been found.
*/
public boolean isEnchantmentPresent(@NotNull ItemStack item){
ItemMeta meta = item.getItemMeta();
if(meta == null) return false;
return isEnchantmentPresent(item, meta);
}
/**
* Check if this enchantment is present on the provided level.
* @param item The item to set the enchantment level.
* @param meta Meta of the provided item. It will not be changed and not be set on the item.
* @return If the enchantment have been found.
*/
public abstract boolean isEnchantmentPresent(@NotNull ItemStack item, @NotNull ItemMeta meta);
/**
* Force add an enchantment at the provided level.
* @param item The item to set the enchantment level.
* @param level The level to set the enchantment to.
*/
public abstract void addEnchantmentUnsafe(@NotNull ItemStack item, int level);
/**
* Remove this enchantment from the provided ItemStack.
* @param item The item to remove the enchantment.
*/
public abstract void removeFrom(@NotNull ItemStack item);
// Static functions
/**
* Clear every enchantment from this item.
* @param item Item to be cleared from enchantments.
*/
public static void clearEnchants(@NotNull ItemStack item){
ItemMeta meta = item.getItemMeta();
if(meta == null) return;
// Clean Vanilla enchants
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
EnchantmentStorageMeta bookMeta = (EnchantmentStorageMeta) meta;
bookMeta.getStoredEnchants().forEach(
(enchantment, leve) -> bookMeta.removeStoredEnchant(enchantment)
);
} else {
item.getEnchantments().forEach(
(enchantment, leve) -> item.removeEnchantment(enchantment)
);
}
// Clean unoptimised enchants
for (WrappedEnchantment enchant : unoptimisedValues()) {
if(enchant.isEnchantmentPresent(item)){
enchant.removeFrom(item);
}
}
}
/**
* Get enchantments of an item.
* @param item Item to get enchantment from.
* @return A map of the set enchantments and there's respective levels.
*/
public static Map<WrappedEnchantment, Integer> getEnchants(@NotNull ItemStack item){ //TODO faster method to find vanilla enchantment
Map<WrappedEnchantment, Integer> enchantments = new HashMap<>();
ItemMeta meta = item.getItemMeta();
if(meta == null) return enchantments;
// Vanilla optimised get
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
((EnchantmentStorageMeta)meta).getStoredEnchants().forEach(
(enchantment, level) -> enchantments.put(getByKey(enchantment.getKey()), level)
);
} else {
item.getEnchantments().forEach(
(enchantment, level) -> enchantments.put(getByKey(enchantment.getKey()), level)
);
}
// Unoptimised enchantment get
findEnchantsFromSelectedList(item, meta, enchantments, unoptimisedValues());
return enchantments;
}
/**
* Find enchantments of an item. only test the enchantment from the list.
* @param item Item to get enchantment from.
* @param meta Meta of the provided item.
* @param enchantments Map of enchantment to complete.
* @param enchantmentToTest Enchantment to test
*/
private static void findEnchantsFromSelectedList(
@NotNull ItemStack item,
@NotNull ItemMeta meta,
@NotNull Map<WrappedEnchantment, Integer> enchantments,
@NotNull Collection<WrappedEnchantment> enchantmentToTest){
for (WrappedEnchantment enchantment : enchantmentToTest) {
if(enchantment.isEnchantmentPresent(item, meta)){
enchantments.put(enchantment, enchantment.getLevel(item, meta));
}
}
}
// Register enchantment functions
private static final HashMap<NamespacedKey, WrappedEnchantment> BY_KEY = new HashMap<>();
private static final HashMap<String, WrappedEnchantment> BY_NAME = new HashMap<>();
private static final List<WrappedEnchantment> UNOPTIMISED_ENCHANTMENT = new ArrayList<>();
/**
* This should only be called on main of custom anvil.
* If called more than one time, chance of thing being broken will be high.
*/
public static void registerEnchantments(){
for (Enchantment enchantment : Enchantment.values()) {
register(new VanillaEnchant(enchantment));
}
}
/**
* Can be used to register new enchantment.
* <p>
* No guarantee that the enchantment will be present on the config gui if registered late.
* (By late I mean after custom anvil startup.)
* @param enchantment The enchantment to be registered.
*/
public static void register(@NotNull WrappedEnchantment enchantment){
if(BY_KEY.containsKey(enchantment.getKey())){
CustomAnvil.instance.getLogger().log(Level.WARNING,
"Duplicate registered enchantment. This should NOT happen.",
new IllegalStateException(enchantment.getKey()+" enchantment was already registered"));
return;
}
if(BY_NAME.containsKey(enchantment.getName())){
CustomAnvil.instance.getLogger().log(Level.WARNING,
"Duplicate registered enchantment name. There will have issue. " +
"\nI hope this do not happen to you on a production server. If it do, there is probably a plugin trying to register an enchantment with the same name than another one",
new IllegalStateException(enchantment.getKey()+" enchantment name was already registered"));
}
BY_KEY.put(enchantment.getKey(), enchantment);
BY_NAME.put(enchantment.getName(), enchantment);
if(!enchantment.isOptimised()){
UNOPTIMISED_ENCHANTMENT.add(enchantment);
}
}
/**
* Can be used to unregister new enchantment.
* Please be cautious with this function.
* It should probably rarely be used.
* <p>
* No guarantee that the enchantment will absent if the config guis if unregistered late.
* (By late I mean after custom anvil startup.)
* @param enchantment The enchantment to be unregistered.
*/
public static void unregister(@NotNull WrappedEnchantment enchantment){
BY_KEY.remove(enchantment.getKey());
BY_NAME.remove(enchantment.getName());
}
/**
* Gets the enchantment by the provided key.
* @param key Key to fetch.
* @return Registered enchantment. null if absent.
*/
public static @Nullable WrappedEnchantment getByKey(@NotNull NamespacedKey key){
return BY_KEY.get(key);
}
/**
* Gets the enchantment by the provided name.
* @param name Name to fetch.
* @return Registered enchantment. null if absent.
*/
public static @Nullable WrappedEnchantment getByName(@NotNull String name){
return BY_NAME.get(name);
}
/**
* Gets an array of all the registered enchantments.
* @return Array of enchantment.
*/
@NotNull
public static WrappedEnchantment[] values() {
return BY_KEY.values().toArray(new WrappedEnchantment[0]);
}
/**
* Gets a list of all the unoptimised enchantments.
* @return List of enchantment.
*/
@NotNull
private static List<WrappedEnchantment> unoptimisedValues() {
return UNOPTIMISED_ENCHANTMENT;
}
}

View file

@ -0,0 +1,87 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import io.delilaheve.util.ItemUtil;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.EnchantmentProperties;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import java.util.Locale;
public class VanillaEnchant extends WrappedEnchantment {
private final @NotNull Enchantment enchantment;
public VanillaEnchant(@NotNull Enchantment enchantment){
super(enchantment.getKey(),
getRarity(enchantment),
enchantment.getMaxLevel());
this.enchantment = enchantment;
}
@Override
protected boolean isOptimised() {
return true;
}
@Override
public int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
return ((EnchantmentStorageMeta)meta).getStoredEnchantLevel(this.enchantment);
} else {
return meta.getEnchantLevel(this.enchantment);
}
}
@Override
public boolean isEnchantmentPresent(@NotNull ItemStack item, @NotNull ItemMeta meta) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)meta);
return bookMeta.getStoredEnchants().containsKey(this.enchantment);
}else{
return item.containsEnchantment(this.enchantment);
}
}
@Override
public void addEnchantmentUnsafe(@NotNull ItemStack item, int level) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta());
assert bookMeta != null;
bookMeta.addStoredEnchant(this.enchantment, level, true);
item.setItemMeta(bookMeta);
} else {
item.addUnsafeEnchantment(this.enchantment, level);
}
}
@Override
public void removeFrom(@NotNull ItemStack item) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta());
assert bookMeta != null;
bookMeta.removeStoredEnchant(this.enchantment);
item.setItemMeta(bookMeta);
}else{
item.removeEnchantment(this.enchantment);
}
}
public static EnchantmentRarity getRarity(Enchantment enchantment){
try {return EnchantmentProperties.valueOf(enchantment.getKey().getKey().toUpperCase(Locale.ENGLISH)).getRarity();}
catch (IllegalArgumentException ignored) {}
return EnchantmentRarity.COMMON;
}
}

View file

@ -1,15 +1,15 @@
package xyz.alexcrea.cuanvil.gui.config;
import org.bukkit.enchantments.Enchantment;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import java.util.Set;
public interface SelectEnchantmentContainer {
Set<Enchantment> getSelectedEnchantments();
Set<WrappedEnchantment> getSelectedEnchantments();
boolean setSelectedEnchantments(Set<Enchantment> enchantments);
boolean setSelectedEnchantments(Set<WrappedEnchantment> enchantments);
Set<Enchantment> illegalEnchantments();
Set<WrappedEnchantment> illegalEnchantments();
}

View file

@ -4,7 +4,7 @@ import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.pane.Orientable;
import com.github.stefvanschie.inventoryframework.pane.OutlinePane;
import io.delilaheve.CustomAnvil;
import org.bukkit.enchantments.Enchantment;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.settings.AbstractSettingGui;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
@ -55,7 +55,7 @@ public abstract class AbstractEnchantConfigGui<T extends AbstractSettingGui.Sett
protected void prepareValues() {
bookItemFactoryList = new ArrayList<>();
for (Enchantment enchant : GuiSharedConstant.SORTED_ENCHANTMENT_LIST) {
for (WrappedEnchantment enchant : GuiSharedConstant.SORTED_ENCHANTMENT_LIST) {
T factory = getFactoryFromEnchant(enchant);
bookItemFactoryList.add(factory);
@ -80,7 +80,7 @@ public abstract class AbstractEnchantConfigGui<T extends AbstractSettingGui.Sett
}
public abstract T getFactoryFromEnchant(Enchantment enchant);
public abstract T getFactoryFromEnchant(WrappedEnchantment enchant);
public abstract GuiItem getItemFromFactory(T inventoryFactory);

View file

@ -2,12 +2,12 @@ package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.EnchantmentProperties;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import xyz.alexcrea.cuanvil.gui.config.settings.EnchantCostSettingsGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
@ -39,12 +39,12 @@ public class EnchantCostConfigGui extends AbstractEnchantConfigGui<EnchantCostSe
}
@Override
public EnchantCostSettingsGui.EnchantCostSettingFactory getFactoryFromEnchant(Enchantment enchant) {
public EnchantCostSettingsGui.EnchantCostSettingFactory getFactoryFromEnchant(WrappedEnchantment enchant) {
String key = enchant.getKey().getKey().toLowerCase(Locale.ENGLISH);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key);
// try to find rarity. default to 0 if not found
EnchantmentRarity rarity = EnchantmentRarity.NO_RARITY;
EnchantmentRarity rarity = enchant.defaultRarity();
try {
rarity = EnchantmentProperties.valueOf(key.toUpperCase(Locale.ENGLISH)).getRarity();
} catch (IllegalArgumentException ignored) {

View file

@ -2,8 +2,8 @@ package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
@ -32,7 +32,7 @@ public class EnchantLimitConfigGui extends AbstractEnchantConfigGui<IntSettingsG
}
@Override
public IntSettingsGui.IntSettingFactory getFactoryFromEnchant(Enchantment enchant) {
public IntSettingsGui.IntSettingFactory getFactoryFromEnchant(WrappedEnchantment enchant) {
String key = enchant.getKey().getKey().toLowerCase(Locale.ROOT);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key);
@ -42,7 +42,7 @@ public class EnchantLimitConfigGui extends AbstractEnchantConfigGui<IntSettingsG
"\u00A77Maximum applied level of " + prettyKey
),
0, 255,
enchant.getMaxLevel(),
enchant.defaultMaxLevel(),
1, 5, 10, 50, 100);
}

View file

@ -5,12 +5,12 @@ import com.github.stefvanschie.inventoryframework.pane.PatternPane;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup;
import xyz.alexcrea.cuanvil.group.EnchantConflictGroup;
import xyz.alexcrea.cuanvil.group.EnchantConflictManager;
@ -118,7 +118,7 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
EnchantConflictManager manager = ConfigHolder.CONFLICT_HOLDER.getConflictManager();
// Remove from manager
for (Enchantment enchantment : this.enchantConflict.getEnchants()) {
for (WrappedEnchantment enchantment : this.enchantConflict.getEnchants()) {
manager.removeConflictFromMap(enchantment, this.enchantConflict);
}
manager.conflictList.remove(this.enchantConflict);
@ -164,12 +164,12 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
// Prepare enchantment lore
ArrayList<String> enchantLore = new ArrayList<>();
enchantLore.add("\u00A77Allow you to select a list of \u00A75Enchantments \u00A77that this conflict should include");
Set<Enchantment> enchants = getSelectedEnchantments();
Set<WrappedEnchantment> enchants = getSelectedEnchantments();
if (enchants.isEmpty()) {
enchantLore.add("\u00A77There is no included enchantment for this conflict.");
} else {
enchantLore.add("\u00A77List of included enchantment for this conflict:");
Iterator<Enchantment> enchantIterator = enchants.iterator();
Iterator<WrappedEnchantment> enchantIterator = enchants.iterator();
boolean greaterThanMax = enchants.size() > 5;
int maxindex = (greaterThanMax ? 4 : enchants.size());
@ -243,12 +243,12 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
// Select enchantment container methods
@Override
public Set<Enchantment> getSelectedEnchantments() {
public Set<WrappedEnchantment> getSelectedEnchantments() {
return this.enchantConflict.getEnchants();
}
@Override
public boolean setSelectedEnchantments(Set<Enchantment> enchantments) {
public boolean setSelectedEnchantments(Set<WrappedEnchantment> enchantments) {
if (!this.shouldWork) {
CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict + " enchants but sub config is destroyed");
return false;
@ -260,7 +260,7 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
// Save on file configuration
String[] enchantKeys = new String[enchantments.size()];
int index = 0;
for (Enchantment enchantment : enchantments) {
for (WrappedEnchantment enchantment : enchantments) {
enchantKeys[index++] = enchantment.getKey().getKey();
}
ConfigHolder.CONFLICT_HOLDER.getConfig().set(enchantConflict + ".enchantments", enchantKeys);
@ -280,7 +280,7 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
}
@Override
public Set<Enchantment> illegalEnchantments() {
public Set<WrappedEnchantment> illegalEnchantments() {
return Collections.emptySet();
}

View file

@ -13,6 +13,7 @@ import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.SelectEnchantmentContainer;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
@ -29,7 +30,7 @@ public class EnchantSelectSettingGui extends AbstractSettingGui {
SelectEnchantmentContainer enchantContainer;
int page;
Set<Enchantment> selectedEnchant;
Set<WrappedEnchantment> selectedEnchant;
public EnchantSelectSettingGui(@NotNull String title, ValueUpdatableGui parent, SelectEnchantmentContainer enchantContainer, int page) {
super(6, title, parent);
@ -64,8 +65,8 @@ public class EnchantSelectSettingGui extends AbstractSettingGui {
filledEnchant.align(OutlinePane.Alignment.BEGIN);
filledEnchant.setOrientation(Orientable.Orientation.HORIZONTAL);
Set<Enchantment> illegalEnchant = this.enchantContainer.illegalEnchantments();
for (Enchantment enchant : GuiSharedConstant.SORTED_ENCHANTMENT_LIST) {
Set<WrappedEnchantment> illegalEnchant = this.enchantContainer.illegalEnchantments();
for (WrappedEnchantment enchant : GuiSharedConstant.SORTED_ENCHANTMENT_LIST) {
if (illegalEnchant.contains(enchant)) {
return;
}
@ -76,7 +77,7 @@ public class EnchantSelectSettingGui extends AbstractSettingGui {
}
private GuiItem getGuiItemFromEnchant(Enchantment enchantment) {
private GuiItem getGuiItemFromEnchant(WrappedEnchantment enchantment) {
boolean isIn = this.selectedEnchant.contains(enchantment);
Material usedMaterial;
@ -122,7 +123,7 @@ public class EnchantSelectSettingGui extends AbstractSettingGui {
item.setItemMeta(meta);
}
private Consumer<InventoryClickEvent> getEnchantItemConsumer(Enchantment enchant, GuiItem guiItem) {
private Consumer<InventoryClickEvent> getEnchantItemConsumer(WrappedEnchantment enchant, GuiItem guiItem) {
return event -> {
event.setCancelled(true);
@ -151,7 +152,7 @@ public class EnchantSelectSettingGui extends AbstractSettingGui {
@Override
public boolean hadChange() {
Set<Enchantment> baseGroup = this.enchantContainer.getSelectedEnchantments();
Set<WrappedEnchantment> baseGroup = this.enchantContainer.getSelectedEnchantments();
return baseGroup.size() != this.selectedEnchant.size() ||
!baseGroup.containsAll(this.selectedEnchant);
}

View file

@ -5,9 +5,9 @@ import com.github.stefvanschie.inventoryframework.pane.Pane;
import com.github.stefvanschie.inventoryframework.pane.PatternPane;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment;
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui;
import java.util.Arrays;
@ -17,10 +17,10 @@ import java.util.List;
public class GuiSharedConstant {
public static final List<Enchantment> SORTED_ENCHANTMENT_LIST;
public static final List<WrappedEnchantment> SORTED_ENCHANTMENT_LIST;
static {
SORTED_ENCHANTMENT_LIST = Arrays.asList(Enchantment.values());
SORTED_ENCHANTMENT_LIST = Arrays.asList(WrappedEnchantment.values());
SORTED_ENCHANTMENT_LIST.sort(Comparator.comparing(ench -> ench.getKey().getKey()));
}

View file

@ -452,6 +452,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
if (ConflictType.BIG_CONFLICT == conflictType) {
illegalPenalty += ConfigOptions.sacrificeIllegalCost
CustomAnvil.verboseLog("Big conflict. Adding illegal price penalty")
}
continue
}

View file

@ -7,6 +7,7 @@ import org.bukkit.plugin.java.JavaPlugin
import xyz.alexcrea.cuanvil.command.EditConfigExecutor
import xyz.alexcrea.cuanvil.command.ReloadExecutor
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant
import xyz.alexcrea.cuanvil.listener.ChatEventListener
@ -93,6 +94,9 @@ class CustomAnvil : JavaPlugin() {
logger.warning("Please note CustomAnvil is a more recent version of UnsafeEnchantsPlus")
}
// Register enchantments
WrappedEnchantment.registerEnchantments()
// Load ProtocolLib dependency if exist
packetManager = if(pluginManager.isPluginEnabled("ProtocolLib"))
{ ProtocoLibWrapper(); }

View file

@ -2,8 +2,8 @@ package io.delilaheve.util
import io.delilaheve.CustomAnvil
import io.delilaheve.util.EnchantmentUtil.enchantmentName
import org.bukkit.enchantments.Enchantment
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment
/**
* Config option accessors
@ -239,7 +239,7 @@ object ConfigOptions {
/**
* Get the given [enchantment]'s limit
*/
fun enchantLimit(enchantment: Enchantment): Int {
fun enchantLimit(enchantment: WrappedEnchantment): Int {
return enchantLimit(enchantment.enchantmentName)
}
@ -273,7 +273,7 @@ object ConfigOptions {
* it's source [isFromBook]
*/
fun enchantmentValue(
enchantment: Enchantment,
enchantment: WrappedEnchantment,
isFromBook: Boolean
): Int {
return enchantmentValue(enchantment.enchantmentName, isFromBook)
@ -309,22 +309,4 @@ object ConfigOptions {
return DEFAULT_ENCHANT_VALUE
}
/**
* Get an array of key of basic config options
*/
fun getBasicConfigKeys(): Array<String> {
return arrayOf(
DEFAULT_LIMIT_PATH,
CAP_ANVIL_COST,
MAX_ANVIL_COST,
REPLACE_TOO_EXPENSIVE,
ITEM_REPAIR_COST,
UNIT_REPAIR_COST,
ITEM_RENAME_COST,
SACRIFICE_ILLEGAL_COST,
REMOVE_ANVIL_COST_LIMIT
)
}
}

View file

@ -2,9 +2,9 @@ package io.delilaheve.util
import io.delilaheve.CustomAnvil
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.HumanEntity
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment
import xyz.alexcrea.cuanvil.group.ConflictType
import kotlin.math.max
import kotlin.math.min
@ -17,17 +17,17 @@ object EnchantmentUtil {
/**
* Enchantment name without namespace
*/
val Enchantment.enchantmentName: String
val WrappedEnchantment.enchantmentName: String
get() = key.key
/**
* Combine 2 sets of enchantments according to our configuration
*/
fun Map<Enchantment, Int>.combineWith(
other: Map<Enchantment, Int>,
fun Map<WrappedEnchantment, Int>.combineWith(
other: Map<WrappedEnchantment, Int>,
mat: Material,
player: HumanEntity
) = mutableMapOf<Enchantment, Int>().apply {
) = mutableMapOf<WrappedEnchantment, Int>().apply {
putAll(this@combineWith)
other.forEach { (enchantment, level) ->
// Get max level or 255 if player can bypass

View file

@ -1,11 +1,9 @@
package io.delilaheve.util
import io.delilaheve.CustomAnvil
import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.EnchantmentStorageMeta
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@ -23,44 +21,19 @@ object ItemUtil {
/**
* Find the enchantment map for this [ItemStack] and return it as a [MutableMap]
*/
fun ItemStack.findEnchantments() = if (isEnchantedBook()) {
(itemMeta as? EnchantmentStorageMeta)?.storedEnchants ?: emptyMap()
} else {
itemMeta?.enchants ?: emptyMap()
}
fun ItemStack.findEnchantments(): MutableMap<WrappedEnchantment, Int> = WrappedEnchantment.getEnchants(this)
/**
* Apply an [enchantments] map to this [ItemStack]
*/
fun ItemStack.setEnchantmentsUnsafe(enchantments: Map<Enchantment, Int>) {
if (isEnchantedBook()) {
/* For some god-forsaken reason, item meta is not mutable
* so, we have to get the instance, modify it, then set it
* back to the item... #BecauseMinecraft */
val bookMeta = (itemMeta as? EnchantmentStorageMeta)
bookMeta?.replaceEnchants(enchantments)
itemMeta = bookMeta
} else {
itemMeta?.enchants?.forEach { (enchant, _) ->
removeEnchantment(enchant)
}
addUnsafeEnchantments(enchantments)
}
fun ItemStack.setEnchantmentsUnsafe(enchantments: Map<WrappedEnchantment, Int>) {
WrappedEnchantment.clearEnchants(this)
//TODO maybe faster methode to add vanilla enchantment. maybe move this function to wrapped enchantment
enchantments.forEach { (enchantment, level) ->
enchantment.addEnchantmentUnsafe(this, level)
}
/**
* Apply an [enchantments] map to this book
*/
private fun EnchantmentStorageMeta.replaceEnchants(
enchantments: Map<Enchantment, Int>
) {
storedEnchants.forEach { (enchant, _) ->
removeStoredEnchant(enchant)
}
enchantments.forEach { (enchant, level) ->
val added = addStoredEnchant(enchant, level, true)
CustomAnvil.log("${enchant.key} added to item? $added")
}
}
/**

View file

@ -2,7 +2,7 @@ package xyz.alexcrea.cuanvil.group
import io.delilaheve.CustomAnvil
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment
class EnchantConflictGroup(
private val name: String,
@ -10,14 +10,15 @@ class EnchantConflictGroup(
var minBeforeBlock: Int
) {
private val enchantments = HashSet<Enchantment>()
private val enchantments = HashSet<WrappedEnchantment>()
fun addEnchantment(enchant: Enchantment) {
fun addEnchantment(enchant: WrappedEnchantment) {
enchantments.add(enchant)
}
fun allowed(enchants: Set<Enchantment>, mat: Material): Boolean {
fun allowed(enchants: Set<WrappedEnchantment>, mat: Material): Boolean {
if (enchantments.size < minBeforeBlock) {
CustomAnvil.verboseLog("Conflicting bc of to many enchantments")
return true
}
@ -31,6 +32,7 @@ class EnchantConflictGroup(
if (enchantment !in enchantments) continue
CustomAnvil.verboseLog("Enchant ${enchantment.key} is in: ${enchantAmount + 1}/$minBeforeBlock ")
if (++enchantAmount > minBeforeBlock) {
CustomAnvil.verboseLog("it is not allowed bc of to many enchantment in conflict")
return false
}
@ -42,11 +44,11 @@ class EnchantConflictGroup(
return this.cantConflict
}
fun getEnchants(): HashSet<Enchantment> {
fun getEnchants(): HashSet<WrappedEnchantment> {
return enchantments
}
fun setEnchants(enchants: Set<Enchantment>) {
fun setEnchants(enchants: Set<WrappedEnchantment>) {
enchantments.clear()
enchantments.addAll(enchants)
}

View file

@ -5,6 +5,7 @@ import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.enchantments.Enchantment
import xyz.alexcrea.cuanvil.enchant.WrappedEnchantment
class EnchantConflictManager {
@ -27,12 +28,12 @@ class EnchantConflictManager {
// 1.20.5 compatibility TODO better update system
private val SWEEPING_EDGE_ENCHANT =
Enchantment.getByKey(NamespacedKey.minecraft("sweeping_edge")) ?:
Enchantment.SWEEPING_EDGE
WrappedEnchantment.getByKey(NamespacedKey.minecraft("sweeping_edge")) ?:
WrappedEnchantment.getByKey(Enchantment.SWEEPING_EDGE.key)
}
private lateinit var conflictMap: HashMap<Enchantment, ArrayList<EnchantConflictGroup>>
private lateinit var conflictMap: HashMap<WrappedEnchantment, ArrayList<EnchantConflictGroup>>
lateinit var conflictList: ArrayList<EnchantConflictGroup>
// Read and prepare all conflict
@ -58,14 +59,14 @@ class EnchantConflictManager {
}
}
fun addConflictToConflictMap(enchant: Enchantment, conflict: EnchantConflictGroup) {
fun addConflictToConflictMap(enchant: WrappedEnchantment, conflict: EnchantConflictGroup) {
if (!conflictMap.containsKey(enchant)) {
conflictMap[enchant] = ArrayList()
}
conflictMap[enchant]!!.add(conflict)
}
fun removeConflictFromMap(enchant: Enchantment, conflict: EnchantConflictGroup): Boolean {
fun removeConflictFromMap(enchant: WrappedEnchantment, conflict: EnchantConflictGroup): Boolean {
return conflictMap[enchant]!!.remove(conflict)
}
@ -100,7 +101,7 @@ class EnchantConflictManager {
return conflict
}
private fun getEnchantByName(enchantName: String): Enchantment? {
private fun getEnchantByName(enchantName: String): WrappedEnchantment? {
// Temporary solution for 1.20.5
when(enchantName){
@ -109,8 +110,7 @@ class EnchantConflictManager {
}
}
val enchantKey = NamespacedKey.minecraft(enchantName)
return Enchantment.getByKey(enchantKey)
return WrappedEnchantment.getByName(enchantName)
}
@ -151,7 +151,7 @@ class EnchantConflictManager {
return group
}
fun isConflicting(base: Set<Enchantment>, mat: Material, newEnchant: Enchantment): ConflictType {
fun isConflicting(base: Set<WrappedEnchantment>, mat: Material, newEnchant: WrappedEnchantment): ConflictType {
CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${mat.key}")
val conflictList = conflictMap[newEnchant] ?: return ConflictType.NO_CONFLICT
CustomAnvil.verboseLog("Did not get skipped")
@ -159,13 +159,14 @@ class EnchantConflictManager {
var result = ConflictType.NO_CONFLICT
for (conflict in conflictList) {
CustomAnvil.verboseLog("Is against $conflict")
val conflicting = conflict.allowed(base, mat)
CustomAnvil.verboseLog("Was against $conflict and conflicting: $conflicting ")
if (!conflicting) {
val allowed = conflict.allowed(base, mat)
CustomAnvil.verboseLog("Was against $conflict and conflicting: ${!allowed} ")
if (!allowed) {
if (conflict.getEnchants().size <= 1) {
result = ConflictType.SMALL_CONFLICT
CustomAnvil.verboseLog("Small conflict, continuing")
} else {
CustomAnvil.verboseLog("Big conflict, probably stoping")
return ConflictType.BIG_CONFLICT
}
}