Skip to content
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 @@ -27,6 +27,7 @@
import org.bukkit.damage.DamageEffect;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.enchantments.EnchantmentOffer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
Expand Down Expand Up @@ -127,4 +128,6 @@ default VersionFetcher getVersionFetcher() {
ComponentFlattener componentFlattener();

<C> PersistentDataKey<C> createPersistentDataKey(Key key, PersistentDataType<?, C> type);

@Nullable EnchantmentOffer[] rollEnchantmentOffers(ItemStack targetStack, int seed, int bookshelfCount, int offerAmount);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package org.bukkit.enchantments;

import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import io.papermc.paper.InternalAPIBridge;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
import org.checkerframework.checker.index.qual.Positive;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import static io.papermc.paper.util.BoundChecker.requirePositive;

/**
* A class for the available enchantment offers in the enchantment table.
*/
@NullMarked
public class EnchantmentOffer {

private Enchantment enchantment;
private int enchantmentLevel;
private int cost;

public EnchantmentOffer(@NotNull Enchantment enchantment, int enchantmentLevel, int cost) {
public EnchantmentOffer(final Enchantment enchantment, final int enchantmentLevel, final int cost) {
this.enchantment = enchantment;
this.enchantmentLevel = enchantmentLevel;
this.cost = cost;
Expand All @@ -23,17 +31,16 @@ public EnchantmentOffer(@NotNull Enchantment enchantment, int enchantmentLevel,
*
* @return type of enchantment
*/
@NotNull
public Enchantment getEnchantment() {
return enchantment;
return this.enchantment;
}

/**
* Sets the type of the enchantment.
*
* @param enchantment type of the enchantment
*/
public void setEnchantment(@NotNull Enchantment enchantment) {
public void setEnchantment(final Enchantment enchantment) {
Preconditions.checkArgument(enchantment != null, "The enchantment may not be null!");

this.enchantment = enchantment;
Expand All @@ -45,16 +52,16 @@ public void setEnchantment(@NotNull Enchantment enchantment) {
* @return level of the enchantment
*/
public int getEnchantmentLevel() {
return enchantmentLevel;
return this.enchantmentLevel;
}

/**
* Sets the level of the enchantment.
*
* @param enchantmentLevel level of the enchantment
*/
public void setEnchantmentLevel(int enchantmentLevel) {
Preconditions.checkArgument(enchantmentLevel > 0, "The enchantment level must be greater than 0!");
public void setEnchantmentLevel(final @Positive int enchantmentLevel) {
requirePositive(enchantmentLevel, "enchantmentLevel");

this.enchantmentLevel = enchantmentLevel;
}
Expand All @@ -66,7 +73,7 @@ public void setEnchantmentLevel(int enchantmentLevel) {
* @return cost for this enchantment
*/
public int getCost() {
return cost;
return this.cost;
}

/**
Expand All @@ -75,9 +82,42 @@ public int getCost() {
*
* @param cost cost for this enchantment
*/
public void setCost(int cost) {
Preconditions.checkArgument(cost > 0, "The cost must be greater than 0!");
public void setCost(final @Positive int cost) {
requirePositive(cost, "cost");

this.cost = cost;
}

@Override
public boolean equals(final @Nullable Object o) {
if (o == null || this.getClass() != o.getClass()) {
return false;
}
final EnchantmentOffer that = (EnchantmentOffer) o;
return this.getEnchantmentLevel() == that.getEnchantmentLevel() && this.getCost() == that.getCost() && Objects.equals(this.getEnchantment(), that.getEnchantment());
}

@Override
public int hashCode() {
return Objects.hash(this.getEnchantment(), this.getEnchantmentLevel(), this.getCost());
}

/**
* Rolls a new set of enchantment offers based on a target item stack, a seed and a bookshelf count.
*
* @param targetStack the itemstack for which the enchantment offers are rolled.
* @param seed the seed used for random selection of enchantments.
* Mirrors {@link org.bukkit.inventory.view.EnchantmentView#getEnchantmentSeed()}.
* @param bookshelfCount the number of virtual bookshelves to consider when rolling the enchantments.
* @param offerAmount the amount of enchantment offers to roll.
* @return an array of enchantment offers with size {@code offerAmount}.
*/
public static @Nullable EnchantmentOffer[] rollEnchantmentOffers(
final ItemStack targetStack,
final int seed,
final int bookshelfCount,
final int offerAmount
) {
return InternalAPIBridge.get().rollEnchantmentOffers(targetStack, seed, bookshelfCount, offerAmount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,32 @@
this.access.execute((level, pos) -> {
IdMap<Holder<Enchantment>> holders = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap();
int bookcases = 0;
@@ -119,6 +_,42 @@
@@ -97,27 +_,42 @@
}
}

- this.random.setSeed(this.enchantmentSeed.get());
-
- for (int ixx = 0; ixx < 3; ixx++) {
- this.costs[ixx] = EnchantmentHelper.getEnchantmentCost(this.random, ixx, bookcases, itemStack);
- this.enchantClue[ixx] = -1;
- this.levelClue[ixx] = -1;
- if (this.costs[ixx] < ixx + 1) {
- this.costs[ixx] = 0;
- }
- }
-
- for (int ix = 0; ix < 3; ix++) {
- if (this.costs[ix] > 0) {
- List<EnchantmentInstance> list = this.getEnchantmentList(level.registryAccess(), itemStack, ix, this.costs[ix]);
- if (!list.isEmpty()) {
- EnchantmentInstance ench = list.get(this.random.nextInt(list.size()));
- this.enchantClue[ix] = holders.getId(ench.enchantment());
- this.levelClue[ix] = ench.level();
- }
- }
- }
+ fillEnchantmentOffers(this.random, this.enchantmentSeed.get(), bookcases, itemStack, level.registryAccess(), this.costs, this.enchantClue, this.levelClue); // Paper - extracted into net.minecraft.world.inventory.EnchantmentMenu.fillEnchantmentOffers
+ // CraftBukkit start
+ org.bukkit.inventory.ItemStack craftItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitMirror(itemStack);
+ org.bukkit.enchantments.EnchantmentOffer[] offers = new org.bukkit.enchantments.EnchantmentOffer[3];
Expand Down Expand Up @@ -103,10 +125,46 @@
+ }
+ }
+ // CraftBukkit end
+
this.broadcastChanges();
});
} else {
@@ -131,6 +_,36 @@
}
}

+ // Paper start - extract offer generation
+ // Extracted over modifications in place given nearly every line has diff to account for non-instance fields for
+ // cost, enchantment and level clues.
+ public static void fillEnchantmentOffers(final RandomSource random, final int seed, final int bookcases, final ItemStack itemStack, final RegistryAccess registryAccess, final int[] costs, final int[] enchantClue, final int[] levelClue) {
+ random.setSeed(seed);
+
+ for (int ixx = 0; ixx < costs.length; ixx++) {
+ costs[ixx] = EnchantmentHelper.getEnchantmentCost(random, ixx, bookcases, itemStack);
+ enchantClue[ixx] = -1;
+ levelClue[ixx] = -1;
+ if (costs[ixx] < ixx + 1) {
+ costs[ixx] = 0;
+ }
+ }
+
+ final IdMap<Holder<Enchantment>> holders = registryAccess.lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap();
+
+ for (int ix = 0; ix < costs.length; ix++) {
+ if (costs[ix] > 0) {
+ List<EnchantmentInstance> list = getEnchantmentList(registryAccess, itemStack, ix, costs[ix], random, seed);
+ if (!list.isEmpty()) {
+ EnchantmentInstance ench = list.get(random.nextInt(list.size()));
+ enchantClue[ix] = holders.getId(ench.enchantment());
+ levelClue[ix] = ench.level();
+ }
+ }
+ }
+ }
+ // Paper end - extract offer generation
+
@Override
public boolean clickMenuButton(final Player player, final int buttonId) {
if (buttonId >= 0 && buttonId < this.costs.length) {
@@ -148,19 +_,52 @@
}

Expand Down Expand Up @@ -167,6 +225,30 @@
currency.consume(enchantmentCost, player);
if (currency.isEmpty()) {
this.enchantSlots.setItem(1, ItemStack.EMPTY);
@@ -185,15 +_,20 @@
}

private List<EnchantmentInstance> getEnchantmentList(final RegistryAccess access, final ItemStack itemStack, final int slot, final int enchantmentCost) {
- this.random.setSeed(this.enchantmentSeed.get() + slot);
+ // Paper start - make static and public
+ return getEnchantmentList(access, itemStack, slot, enchantmentCost, this.random, this.enchantmentSeed.get());
+ }
+ public static List<EnchantmentInstance> getEnchantmentList(final RegistryAccess access, final ItemStack itemStack, final int slot, final int enchantmentCost, final RandomSource random, final int seed) {
+ random.setSeed(seed + slot);
+ // Paper end - make static and public
Optional<HolderSet.Named<Enchantment>> tag = access.lookupOrThrow(Registries.ENCHANTMENT).get(EnchantmentTags.IN_ENCHANTING_TABLE);
if (tag.isEmpty()) {
return List.of();
}

- List<EnchantmentInstance> list = EnchantmentHelper.selectEnchantment(this.random, itemStack, enchantmentCost, tag.get().stream());
+ List<EnchantmentInstance> list = EnchantmentHelper.selectEnchantment(random, itemStack, enchantmentCost, tag.get().stream()); // Paper - make static and public
if (itemStack.is(Items.BOOK) && list.size() > 1) {
- list.remove(this.random.nextInt(list.size()));
+ list.remove(random.nextInt(list.size())); // Paper - make static and public
}

return list;
@@ -204,6 +_,12 @@
return goldStack.isEmpty() ? 0 : goldStack.getCount();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,21 @@
import io.papermc.paper.world.damagesource.FallLocationType;
import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper;
import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper;
import java.util.Random;
import java.util.function.Predicate;
import java.io.IOException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Predicate;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.minecraft.Optionull;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.util.RandomSource;
import net.minecraft.commands.Commands;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
Expand All @@ -42,36 +47,42 @@
import net.minecraft.world.entity.ai.attributes.DefaultAttributes;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.decoration.Mannequin;
import net.minecraft.world.inventory.EnchantmentMenu;
import net.minecraft.world.item.enchantment.Enchantment;
import org.bukkit.block.Biome;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.GameRule;
import org.bukkit.NamespacedKey;
import org.bukkit.Statistic;
import org.bukkit.attribute.Attributable;
import org.bukkit.block.Biome;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftGameRule;
import org.bukkit.craftbukkit.CraftStatistic;
import org.bukkit.craftbukkit.block.CraftBiome;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.damage.CraftDamageEffect;
import org.bukkit.craftbukkit.damage.CraftDamageSource;
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.RandomSourceWrapper;
import org.bukkit.damage.DamageEffect;
import org.bukkit.damage.DamageSource;
import org.bukkit.enchantments.EnchantmentOffer;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.craftbukkit.damage.CraftDamageSourceBuilder;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftEntityType;
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.entity.CraftMannequin;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.CraftSpawnCategory;
import org.bukkit.damage.DamageEffect;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Pose;
import org.bukkit.entity.SpawnCategory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
Expand Down Expand Up @@ -269,4 +280,29 @@ public <C> PersistentDataKey<C> createPersistentDataKey(final Key key, final Per
: new NamespacedKey(key.namespace(), key.value());
return new PaperPersistentDataKey<>(namespacedKey, type);
}

@Override
public @Nullable EnchantmentOffer[] rollEnchantmentOffers(final ItemStack targetStack, final int seed, final int bookshelfCount, final int offerAmount) {
final int[] costs = new int[offerAmount];
final int[] enchantClue = new int[offerAmount];
final int[] levelClue = new int[offerAmount];

final RandomSource randomSource = new RandomSourceWrapper(new Random());
EnchantmentMenu.fillEnchantmentOffers(randomSource, seed, bookshelfCount, CraftItemStack.unwrap(targetStack), CraftRegistry.getMinecraftRegistry(), costs, enchantClue, levelClue);

final IdMap<Holder<Enchantment>> holderIdMap = CraftRegistry.getMinecraftRegistry()
.lookupOrThrow(Registries.ENCHANTMENT)
.asHolderIdMap();

final @Nullable EnchantmentOffer[] offers = new EnchantmentOffer[offerAmount];
for (int i = 0; i < costs.length; i++) {
if (enchantClue[i] < 0) {
continue;
}

offers[i] = new EnchantmentOffer(CraftEnchantment.minecraftHolderToBukkit(holderIdMap.byId(enchantClue[i])), levelClue[i], costs[i]);
}

return offers;
}
}
Loading