Merge remote-tracking branch 'upstream/main' into update/1.21.4

This commit is contained in:
2025-03-30 11:43:18 +02:00
519 changed files with 7311 additions and 3127 deletions

View File

@@ -7,18 +7,17 @@ import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.decoration.PaintingVariant;
import org.bukkit.Art;
import org.bukkit.Registry;
public class CraftArt extends OldEnumHolderable<Art, PaintingVariant> implements Art {
private static int count = 0;
public static Art minecraftToBukkit(PaintingVariant minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.PAINTING_VARIANT, Registry.ART);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.PAINTING_VARIANT);
}
public static Art minecraftHolderToBukkit(Holder<PaintingVariant> minecraft) {
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registry.ART);
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.PAINTING_VARIANT);
}
public static PaintingVariant bukkitToMinecraft(Art bukkit) {

View File

@@ -4,7 +4,6 @@ import java.util.Locale;
import net.minecraft.core.registries.Registries;
import org.bukkit.Fluid;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.Handleable;
import org.jetbrains.annotations.NotNull;
@@ -13,7 +12,7 @@ public class CraftFluid implements Fluid, Handleable<net.minecraft.world.level.m
private static int count = 0;
public static Fluid minecraftToBukkit(net.minecraft.world.level.material.Fluid minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.FLUID, Registry.FLUID);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.FLUID);
}
public static net.minecraft.world.level.material.Fluid bukkitToMinecraft(Fluid bukkit) {

View File

@@ -3,14 +3,13 @@ package org.bukkit.craftbukkit;
import net.minecraft.core.registries.Registries;
import org.bukkit.GameEvent;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.Handleable;
import org.jetbrains.annotations.NotNull;
public class CraftGameEvent extends GameEvent implements Handleable<net.minecraft.world.level.gameevent.GameEvent> {
public static GameEvent minecraftToBukkit(net.minecraft.world.level.gameevent.GameEvent minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.GAME_EVENT, Registry.GAME_EVENT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.GAME_EVENT);
}
public static net.minecraft.world.level.gameevent.GameEvent bukkitToMinecraft(GameEvent bukkit) {

View File

@@ -6,14 +6,13 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.contents.TranslatableContents;
import org.bukkit.JukeboxSong;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.Handleable;
import org.jetbrains.annotations.NotNull;
public class CraftJukeboxSong implements JukeboxSong, Handleable<net.minecraft.world.item.JukeboxSong> {
public static JukeboxSong minecraftToBukkit(net.minecraft.world.item.JukeboxSong minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.JUKEBOX_SONG, Registry.JUKEBOX_SONG);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.JUKEBOX_SONG);
}
public static JukeboxSong minecraftHolderToBukkit(Holder<net.minecraft.world.item.JukeboxSong> minecraft) {

View File

@@ -27,6 +27,7 @@ import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.RandomSourceWrapper;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
@@ -68,8 +69,8 @@ public class CraftLootTable implements org.bukkit.loot.LootTable {
@Override
public Collection<ItemStack> populateLoot(Random random, LootContext context) {
Preconditions.checkArgument(context != null, "LootContext cannot be null");
LootParams nmsContext = this.convertContext(context, random);
List<net.minecraft.world.item.ItemStack> nmsItems = this.handle.getRandomItems(nmsContext);
LootParams nmsContext = this.convertContext(context);
List<net.minecraft.world.item.ItemStack> nmsItems = this.handle.getRandomItems(nmsContext, random == null ? null : new RandomSourceWrapper(random));
Collection<ItemStack> bukkit = new ArrayList<>(nmsItems.size());
for (net.minecraft.world.item.ItemStack item : nmsItems) {
@@ -86,12 +87,12 @@ public class CraftLootTable implements org.bukkit.loot.LootTable {
public void fillInventory(Inventory inventory, Random random, LootContext context) {
Preconditions.checkArgument(inventory != null, "Inventory cannot be null");
Preconditions.checkArgument(context != null, "LootContext cannot be null");
LootParams nmsContext = this.convertContext(context, random);
LootParams nmsContext = this.convertContext(context);
CraftInventory craftInventory = (CraftInventory) inventory;
Container handle = craftInventory.getInventory();
// TODO: When events are added, call event here w/ custom reason?
this.getHandle().fillInventory(handle, nmsContext, random.nextLong(), true);
this.getHandle().fill(handle, nmsContext, random == null ? null : new RandomSourceWrapper(random), true);
}
@Override
@@ -99,19 +100,16 @@ public class CraftLootTable implements org.bukkit.loot.LootTable {
return this.key;
}
private LootParams convertContext(LootContext context, Random random) {
private LootParams convertContext(LootContext context) {
Preconditions.checkArgument(context != null, "LootContext cannot be null");
Location loc = context.getLocation();
Preconditions.checkArgument(loc.getWorld() != null, "LootContext.getLocation#getWorld cannot be null");
ServerLevel handle = ((CraftWorld) loc.getWorld()).getHandle();
LootParams.Builder builder = new LootParams.Builder(handle);
if (random != null) {
// builder = builder.withRandom(new RandomSourceWrapper(random));
}
this.setMaybe(builder, LootContextParams.ORIGIN, CraftLocation.toVec3D(loc));
if (this.getHandle() != LootTable.EMPTY) {
// builder.luck(context.getLuck());
builder.withLuck(context.getLuck());
if (context.getLootedEntity() != null) {
Entity nmsLootedEntity = ((CraftEntity) context.getLootedEntity()).getHandle();

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.util.Holderable;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
@@ -8,17 +9,16 @@ import net.minecraft.world.item.Instrument;
import org.bukkit.MusicInstrument;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.Handleable;
import org.jetbrains.annotations.NotNull;
public class CraftMusicInstrument extends MusicInstrument implements io.papermc.paper.util.Holderable<Instrument> {
public static MusicInstrument minecraftToBukkit(Instrument minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.INSTRUMENT, Registry.INSTRUMENT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.INSTRUMENT);
}
public static MusicInstrument minecraftHolderToBukkit(Holder<Instrument> minecraft) {
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registry.INSTRUMENT); // Paper - switch to Holder
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.INSTRUMENT); // Paper - switch to Holder
}
public static Instrument bukkitToMinecraft(MusicInstrument bukkit) {
@@ -32,13 +32,13 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc.
public static Object bukkitToString(MusicInstrument bukkit) { // Paper - switch to Holder
Preconditions.checkArgument(bukkit != null);
return ((CraftMusicInstrument) bukkit).toBukkitSerializationObject(Instrument.CODEC); // Paper - switch to Holder
return ((CraftMusicInstrument) bukkit).toBukkitSerializationObject(Instrument.DIRECT_CODEC); // Paper - switch to Holder
}
public static MusicInstrument stringToBukkit(Object string) { // Paper - switch to Holder
Preconditions.checkArgument(string != null);
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(string, Instrument.CODEC, Registry.INSTRUMENT); // Paper - switch to Holder
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(string, Instrument.CODEC, RegistryKey.INSTRUMENT); // Paper - switch to Holder
}
// Paper start - switch to Holder

View File

@@ -1,12 +1,15 @@
package org.bukkit.craftbukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.entry.RegistryEntryMeta;
import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.util.Holderable;
import java.util.Collection;
import io.papermc.paper.util.MCUtil;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -15,7 +18,6 @@ import java.util.function.BiFunction;
import java.util.stream.Stream;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderOwner;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
@@ -30,14 +32,14 @@ import org.jetbrains.annotations.NotNull;
public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
private static RegistryAccess registry;
private static net.minecraft.core.RegistryAccess registry;
public static void setMinecraftRegistry(RegistryAccess registry) {
public static void setMinecraftRegistry(final net.minecraft.core.RegistryAccess registry) {
Preconditions.checkState(CraftRegistry.registry == null, "Registry already set");
CraftRegistry.registry = registry;
}
public static RegistryAccess getMinecraftRegistry() {
public static net.minecraft.core.RegistryAccess getMinecraftRegistry() {
return CraftRegistry.registry;
}
@@ -49,49 +51,46 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
* Usage note: Only use this method to delegate the conversion methods from the individual Craft classes to here.
* Do not use it in other parts of CraftBukkit, use the methods in the respective Craft classes instead.
*
* @param minecraft the minecraft representation
* @param minecraft the minecraft representation
* @param registryKey the registry key of the minecraft registry to use
* @param bukkitRegistry the bukkit registry to use
* @return the bukkit representation of the minecraft value
*/
public static <B extends Keyed, M> B minecraftToBukkit(M minecraft, ResourceKey<net.minecraft.core.Registry<M>> registryKey, Registry<B> bukkitRegistry) {
public static <B extends Keyed, M> B minecraftToBukkit(M minecraft, ResourceKey<? extends net.minecraft.core.Registry<M>> registryKey) {
Preconditions.checkArgument(minecraft != null);
net.minecraft.core.Registry<M> registry = CraftRegistry.getMinecraftRegistry(registryKey);
// Paper start - support direct Holders
final Registry<B> bukkitRegistry = RegistryAccess.registryAccess().getRegistry(PaperRegistries.registryFromNms(registryKey));
final java.util.Optional<ResourceKey<M>> resourceKey = registry.getResourceKey(minecraft);
if (resourceKey.isEmpty() && bukkitRegistry instanceof final CraftRegistry<?, ?> craftRegistry && craftRegistry.supportsDirectHolders()) {
return ((CraftRegistry<B, M>) bukkitRegistry).convertDirectHolder(Holder.direct(minecraft));
return ((CraftRegistry<B, M>) bukkitRegistry).createBukkit(Holder.direct(minecraft));
} else if (resourceKey.isEmpty()) {
throw new IllegalStateException(String.format("Cannot convert '%s' to bukkit representation, since it is not registered.", minecraft));
}
final B bukkit = bukkitRegistry.get(CraftNamespacedKey.fromMinecraft(resourceKey.get().location()));
// Paper end - support direct Holders
Preconditions.checkArgument(bukkit != null);
return bukkit;
}
// Paper start - support direct Holders
public static <B extends Keyed, M> B minecraftHolderToBukkit(final Holder<M> minecraft, final Registry<B> bukkitRegistry) {
public static <B extends Keyed, M> B minecraftHolderToBukkit(final Holder<M> minecraft, final ResourceKey<? extends net.minecraft.core.Registry<M>> registryKey) {
Preconditions.checkArgument(minecraft != null);
final Registry<B> bukkitRegistry = RegistryAccess.registryAccess().getRegistry(PaperRegistries.registryFromNms(registryKey));
final B bukkit = switch (minecraft) {
case final Holder.Direct<M> direct -> {
if (!(bukkitRegistry instanceof final CraftRegistry<?, ?> craftRegistry) || !craftRegistry.supportsDirectHolders()) {
throw new IllegalArgumentException("Cannot convert direct holder to bukkit representation");
}
yield ((CraftRegistry<B, M>) bukkitRegistry).convertDirectHolder(direct);
yield ((CraftRegistry<B, M>) bukkitRegistry).createBukkit(direct);
}
case final Holder.Reference<M> reference -> bukkitRegistry.get(io.papermc.paper.util.MCUtil.fromResourceKey(reference.key()));
case final Holder.Reference<M> reference -> bukkitRegistry.get(MCUtil.fromResourceKey(reference.key()));
default -> throw new IllegalArgumentException("Unknown holder: " + minecraft);
};
Preconditions.checkArgument(bukkit != null);
return bukkit;
}
// Paper end - support direct Holders
/**
* Usage note: Only use this method to delegate the conversion methods from the individual Craft classes to here.
@@ -125,13 +124,10 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
}
// Paper start - fixup upstream being dum
public static <T extends org.bukkit.Keyed, M> java.util.Optional<T> unwrapAndConvertHolder(final io.papermc.paper.registry.RegistryKey<T> registryKey, final Holder<M> value) {
return unwrapAndConvertHolder(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(registryKey), value);
}
public static <T extends org.bukkit.Keyed, M> java.util.Optional<T> unwrapAndConvertHolder(final Registry<T> registry, final Holder<M> value) {
if (registry instanceof CraftRegistry<?,?> craftRegistry && craftRegistry.supportsDirectHolders() && value.kind() == Holder.Kind.DIRECT) {
return java.util.Optional.of(((CraftRegistry<T, M>) registry).convertDirectHolder(value));
public static <T extends Keyed, M> Optional<T> unwrapAndConvertHolder(final RegistryKey<T> registryKey, final Holder<M> value) {
final Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
if (registry instanceof final CraftRegistry<?,?> craftRegistry && craftRegistry.supportsDirectHolders() && value.kind() == Holder.Kind.DIRECT) {
return Optional.of(((CraftRegistry<T, M>) registry).createBukkit(value));
}
return value.unwrapKey().map(key -> registry.get(CraftNamespacedKey.fromMinecraft(key.location())));
}
@@ -140,7 +136,8 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
// Paper - move to PaperRegistries
// Paper - NOTE: As long as all uses of the method below relate to *serialization* via ConfigurationSerializable, it's fine
public static <B extends Keyed> B get(Registry<B> bukkit, NamespacedKey namespacedKey, ApiVersion apiVersion) {
public static <B extends Keyed> B get(RegistryKey<B> bukkitKey, NamespacedKey namespacedKey, ApiVersion apiVersion) {
final Registry<B> bukkit = RegistryAccess.registryAccess().getRegistry(bukkitKey);
if (bukkit instanceof CraftRegistry<B, ?> craft) {
return craft.get(craft.serializationUpdater.apply(namespacedKey, apiVersion)); // Paper
}
@@ -181,7 +178,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
this.minecraftRegistry = minecraftRegistry;
this.minecraftToBukkit = minecraftToBukkit;
this.serializationUpdater = serializationUpdater;
this.lockReferenceHolders = !this.minecraftToBukkit.supportsDirectHolders();
this.lockReferenceHolders = !this.minecraftToBukkit.constructorUsesHolder();
}
public void lockReferenceHolders() {
@@ -192,7 +189,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
} catch (final ClassNotFoundException e) {
throw new IllegalStateException("Failed to load class " + this.bukkitClass.getSimpleName(), e);
}
if (!this.minecraftToBukkit.supportsDirectHolders()) {
if (!this.minecraftToBukkit.constructorUsesHolder()) {
return;
}
Preconditions.checkState(!this.lockReferenceHolders, "Reference holders are already locked");
@@ -212,7 +209,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
final Holder.Reference<M> holder;
if (holderOptional.isPresent()) {
holder = holderOptional.get();
} else if (!this.lockReferenceHolders && this.minecraftToBukkit.supportsDirectHolders()) { // only works if its Holderable
} else if (!this.lockReferenceHolders && this.minecraftToBukkit.constructorUsesHolder()) { // only works if its Holderable
// we lock the reference holders after the preload class has been initialized
// this is to support the vanilla mechanic of preventing vanilla registry entries being loaded. We need
// to create something to fill the API constant fields, so we create a dummy reference holder.
@@ -220,7 +217,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
} else {
holder = null;
}
final B bukkit = this.createBukkit(namespacedKey, holder);
final B bukkit = this.createBukkit(holder);
if (bukkit == null) {
return null;
}
@@ -230,45 +227,34 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return bukkit;
}
@NotNull
@Override
public B getOrThrow(@NotNull NamespacedKey namespacedKey) {
B object = this.get(namespacedKey);
Preconditions.checkArgument(object != null, "No %s registry entry found for key %s.", this.minecraftRegistry.key(), namespacedKey);
return object;
}
@NotNull
@Override
public Stream<B> stream() {
return this.minecraftRegistry.keySet().stream().map(minecraftKey -> this.get(CraftNamespacedKey.fromMinecraft(minecraftKey)));
}
@Override
public int size() {
return this.minecraftRegistry.size();
}
@Override
public Iterator<B> iterator() {
return this.stream().iterator();
}
public B createBukkit(NamespacedKey namespacedKey, Holder<M> minecraft) { // Paper - switch to Holder
public B createBukkit(Holder<M> minecraft) {
if (minecraft == null) {
return null;
}
return this.minecraftToBukkit.createBukkit(namespacedKey, minecraft); // Paper - switch to Holder
return this.minecraftToBukkit.createBukkit(minecraft);
}
// Paper start - support Direct Holders
public boolean supportsDirectHolders() {
return this.minecraftToBukkit.supportsDirectHolders();
}
public B convertDirectHolder(Holder<M> holder) {
return this.minecraftToBukkit.convertDirectHolder(holder);
}
// Paper end - support Direct Holders
// Paper start - improve Registry
@Override
public NamespacedKey getKey(final B value) {

View File

@@ -131,6 +131,7 @@ import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.WorldBorder;
import org.bukkit.WorldCreator;
import org.bukkit.block.BlockType;
import org.bukkit.block.data.BlockData;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
@@ -258,6 +259,7 @@ import org.bukkit.scoreboard.Criteria;
import org.bukkit.structure.StructureManager;
import org.bukkit.util.StringUtil;
import org.bukkit.util.permissions.DefaultPermissions;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
@@ -883,7 +885,7 @@ public final class CraftServer implements Server {
@Override
public boolean hasWhitelist() {
return this.getProperties().whiteList.get();
return this.playerList.isUsingWhitelist();
}
// NOTE: Temporary calls through to server.properies until its replaced
@@ -1723,6 +1725,7 @@ public final class CraftServer implements Server {
if (recipe.isPresent()) {
RecipeHolder<CraftingRecipe> recipeCrafting = recipe.get();
inventoryCrafting.setCurrentRecipe(recipeCrafting);
if (craftResult.setRecipeUsed(craftPlayer.getHandle(), recipeCrafting)) {
itemstack = recipeCrafting.value().assemble(inventoryCrafting.asCraftInput(), craftWorld.getHandle().registryAccess());
}
@@ -1863,22 +1866,12 @@ public final class CraftServer implements Server {
return result;
}
public void removeBukkitSpawnRadius() {
this.configuration.set("settings.spawn-radius", null);
this.saveConfig();
}
public int getBukkitSpawnRadius() {
return this.configuration.getInt("settings.spawn-radius", -1);
}
// Paper start
@Override
public net.kyori.adventure.text.Component shutdownMessage() {
String msg = getShutdownMessage();
return msg != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(msg) : null;
}
// Paper end
@Override
@Deprecated // Paper
public String getShutdownMessage() {
@@ -1896,11 +1889,6 @@ public final class CraftServer implements Server {
this.saveConfig();
}
@Override
public boolean shouldSendChatPreviews() {
return false;
}
@Override
public boolean isEnforcingSecureProfiles() {
return this.getServer().enforceSecureProfile();
@@ -2485,6 +2473,11 @@ public final class CraftServer implements Server {
return new CraftMerchantCustom(title == null ? InventoryType.MERCHANT.getDefaultTitle() : title);
}
@Override
public @NotNull Merchant createMerchant() {
return new CraftMerchantCustom();
}
@Override
public int getMaxChainedNeighborUpdates() {
return this.getServer().getMaxChainedNeighborUpdates();
@@ -2550,7 +2543,7 @@ public final class CraftServer implements Server {
@Override
public boolean isPrimaryThread() {
return Thread.currentThread().equals(this.console.serverThread) || this.console.hasStopped() || !org.spigotmc.AsyncCatcher.enabled; // All bets are off if we have shut down (e.g. due to watchdog)
return ca.spottedleaf.moonrise.common.util.TickThread.isTickThread(); // Paper - rewrite chunk system
}
// Paper start - Adventure
@@ -2856,8 +2849,13 @@ public final class CraftServer implements Server {
@Override
public BlockData createBlockData(org.bukkit.Material material, String data) {
Preconditions.checkArgument(material != null || data != null, "Must provide one of material or data");
BlockType type = null;
if (material != null) {
type = material.asBlockType();
Preconditions.checkArgument(type != null, "Provided material must be a block");
}
return CraftBlockData.newData((material != null) ? material.asBlockType() : null, data);
return CraftBlockData.newData(type, data);
}
@Override
@@ -3051,7 +3049,7 @@ public final class CraftServer implements Server {
@Override
public void restart() {
org.spigotmc.RestartCommand.restart();
CraftServer.this.restart();
}
@Override
@@ -3075,6 +3073,11 @@ public final class CraftServer implements Server {
}
// Spigot end
@Override
public void restart() {
org.spigotmc.RestartCommand.restart();
}
@Override
public double[] getTPS() {
return new double[] {

View File

@@ -72,7 +72,7 @@ final class CraftServerTickManager implements ServerTickManager {
@Override
public boolean requestGameToSprint(final int ticks) {
return this.manager.requestGameToSprint(ticks);
return this.manager.requestGameToSprint(ticks, true);
}
@Override

View File

@@ -4,7 +4,6 @@ import io.papermc.paper.util.OldEnumHolderable;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.sounds.SoundEvent;
import org.bukkit.Registry;
import org.bukkit.Sound;
public class CraftSound extends OldEnumHolderable<Sound, SoundEvent> implements Sound {
@@ -12,7 +11,7 @@ public class CraftSound extends OldEnumHolderable<Sound, SoundEvent> implements
private static int count = 0;
public static Sound minecraftToBukkit(SoundEvent minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.SOUND_EVENT, Registry.SOUNDS);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.SOUND_EVENT);
}
public static SoundEvent bukkitToMinecraft(Sound bukkit) {

View File

@@ -2,11 +2,13 @@ package org.bukkit.craftbukkit;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.mojang.datafixers.util.Pair;
import io.papermc.paper.FeatureHooks;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import io.papermc.paper.raytracing.RayTraceTarget;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilderImpl;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.File;
import java.util.ArrayList;
@@ -40,13 +42,11 @@ import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.Ticket;
import net.minecraft.server.level.TicketType;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.util.SortedArraySet;
import net.minecraft.util.Unit;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
@@ -82,7 +82,6 @@ import org.bukkit.NamespacedKey;
import org.bukkit.Note;
import org.bukkit.Particle;
import org.bukkit.Raid;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.TreeType;
import org.bukkit.World;
@@ -204,8 +203,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getEntityCount() {
int ret = 0;
for (net.minecraft.world.entity.Entity entity : world.getEntities().getAll()) {
if (entity.isChunkLoaded()) {
for (net.minecraft.world.entity.Entity entity : this.world.getEntities().getAll()) {
if (entity.getBukkitEntity().isValid()) {
++ret;
}
}
@@ -351,7 +350,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Paper start
private static void warnUnsafeChunk(String reason, int x, int z) {
// if any chunk coord is outside of 30 million blocks
if (x > 1875000 || z > 1875000 || x < -1875000 || z < -1875000) {
int max = (30_000_000 / 16) + 625;
if (x > max || z > max || x < -max || z < -max) {
Plugin plugin = io.papermc.paper.util.StackWalkerUtil.getFirstPluginCaller();
if (plugin != null) {
plugin.getLogger().warning("Plugin is %s at (%s, %s), this might cause issues.".formatted(reason, x, z));
@@ -470,33 +470,6 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return !this.isChunkLoaded(x, z);
}
@Override
public boolean regenerateChunk(int x, int z) {
org.spigotmc.AsyncCatcher.catchOp("chunk regenerate"); // Spigot
throw new UnsupportedOperationException("Not supported in this Minecraft version! Unless you can fix it, this is not a bug :)");
/*
if (!unloadChunk0(x, z, false)) {
return false;
}
warnUnsafeChunk("regenerating a faraway chunk", x, z); // Paper
final long chunkKey = ChunkCoordIntPair.pair(x, z);
world.getChunkProvider().unloadQueue.remove(chunkKey);
net.minecraft.server.Chunk chunk = world.getChunkProvider().generateChunk(x, z);
PlayerChunk playerChunk = world.getPlayerChunkMap().getChunk(x, z);
if (playerChunk != null) {
playerChunk.chunk = chunk;
}
if (chunk != null) {
refreshChunk(x, z);
}
return chunk != null;
*/
}
@Override
public boolean refreshChunk(int x, int z) {
ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
@@ -854,7 +827,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
CraftPlayer cp = (CraftPlayer) p;
if (cp.getHandle().connection == null) continue;
cp.getHandle().connection.send(new ClientboundSetTimePacket(cp.getHandle().level().getGameTime(), cp.getHandle().getPlayerTime(), cp.getHandle().serverLevel().getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)));
cp.getHandle().connection.send(new ClientboundSetTimePacket(cp.getHandle().level().getGameTime(), cp.getHandle().getPlayerTime(), cp.getHandle().relativeTime && cp.getHandle().serverLevel().getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)));
}
}
@@ -1246,6 +1219,26 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return blockHit;
}
@Override
public RayTraceResult rayTrace(Consumer<PositionedRayTraceConfigurationBuilder> builderConsumer) {
PositionedRayTraceConfigurationBuilderImpl builder = new PositionedRayTraceConfigurationBuilderImpl();
builderConsumer.accept(builder);
Preconditions.checkArgument(builder.start != null, "Start location cannot be null");
Preconditions.checkArgument(builder.direction != null, "Direction vector cannot be null");
Preconditions.checkArgument(builder.maxDistance.isPresent(), "Max distance must be set");
Preconditions.checkArgument(!builder.targets.isEmpty(), "At least one target");
final double maxDistance = builder.maxDistance.getAsDouble();
if (builder.targets.contains(RayTraceTarget.ENTITY)) {
if (builder.targets.contains(RayTraceTarget.BLOCK)) {
return this.rayTrace(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.ignorePassableBlocks, builder.raySize, builder.entityFilter, builder.blockFilter);
}
return this.rayTraceEntities(builder.start, builder.direction, maxDistance, builder.raySize, builder.entityFilter);
}
return this.rayTraceBlocks(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.ignorePassableBlocks, builder.blockFilter);
}
@Override
public List<Player> getPlayers() {
List<Player> list = new ArrayList<Player>(this.world.players().size());
@@ -1271,13 +1264,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Paper end
@Override
public void save() {
public void save(boolean flush) {
org.spigotmc.AsyncCatcher.catchOp("world save"); // Spigot
this.server.checkSaveState();
boolean oldSave = this.world.noSave;
this.world.noSave = false;
this.world.save(null, false, false);
this.world.save(null, flush, false);
this.world.noSave = oldSave;
}
@@ -2052,13 +2045,19 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public <T> T getGameRuleValue(GameRule<T> rule) {
Preconditions.checkArgument(rule != null, "GameRule cannot be null");
return this.convert(rule, this.getHandle().getGameRules().getRule(this.getGameRulesNMS().get(rule.getName())));
GameRules.Key<? extends GameRules.Value<?>> key = this.getGameRulesNMS().get(rule.getName());
Preconditions.checkArgument(key != null, "GameRule '%s' is not available", rule.getName());
return this.getGameRuleResult(rule, this.getHandle().getGameRules().getRule(key));
}
@Override
public <T> T getGameRuleDefault(GameRule<T> rule) {
Preconditions.checkArgument(rule != null, "GameRule cannot be null");
return this.convert(rule, this.getGameRuleDefinitions().get(rule.getName()).createRule());
GameRules.Type<?> type = this.getGameRuleDefinitions().get(rule.getName());
Preconditions.checkArgument(type != null, "GameRule '%s' is not available", rule.getName());
return this.getGameRuleResult(rule, type.createRule());
}
@Override
@@ -2078,7 +2077,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return true;
}
private <T> T convert(GameRule<T> rule, GameRules.Value<?> value) {
private <T> T getGameRuleResult(GameRule<T> rule, GameRules.Value<?> value) {
if (value == null) {
return null;
}
@@ -2242,7 +2241,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public StructureSearchResult locateNearestStructure(Location origin, StructureType structureType, int radius, boolean findUnexplored) {
List<Structure> structures = new ArrayList<>();
for (Structure structure : Registry.STRUCTURE) {
for (Structure structure : RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE)) {
if (structure.getStructureType() == structureType) {
structures.add(structure);
}

View File

@@ -263,11 +263,11 @@ public class Main {
Date buildDate = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(Main.class.getPackage().getImplementationVendor()); // Paper
Calendar deadline = Calendar.getInstance();
deadline.add(Calendar.DAY_OF_YEAR, -2);
deadline.add(Calendar.DAY_OF_YEAR, -14);
if (buildDate.before(deadline.getTime())) {
// Paper start - This is some stupid bullshit
System.err.println("*** Warning, you've not updated in a while! ***");
System.err.println("*** Please download a new build as per instructions from https://papermc.io/downloads/paper ***"); // Paper
System.err.println("*** Please download a new build from https://papermc.io/downloads/paper ***"); // Paper
//System.err.println("*** Server will start in 20 seconds ***");
//Thread.sleep(TimeUnit.SECONDS.toMillis(20));
// Paper end

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit.attribute;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import java.util.Locale;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
@@ -18,7 +19,7 @@ public class CraftAttribute implements Attribute, Handleable<net.minecraft.world
private static int count = 0;
public static Attribute minecraftToBukkit(net.minecraft.world.entity.ai.attributes.Attribute minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ATTRIBUTE, Registry.ATTRIBUTE);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ATTRIBUTE);
}
public static Attribute minecraftHolderToBukkit(Holder<net.minecraft.world.entity.ai.attributes.Attribute> minecraft) {
@@ -36,7 +37,7 @@ public class CraftAttribute implements Attribute, Handleable<net.minecraft.world
if (key == null) return null; // Paper - Fixup NamespacedKey handling
// Now also convert from when keys where saved
return CraftRegistry.get(Registry.ATTRIBUTE, key, ApiVersion.CURRENT);
return CraftRegistry.get(RegistryKey.ATTRIBUTE, key, ApiVersion.CURRENT);
}
public static net.minecraft.world.entity.ai.attributes.Attribute bukkitToMinecraft(Attribute bukkit) {

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit.block;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.world.level.block.AbstractBannerBlock;
@@ -39,7 +40,7 @@ public class CraftBanner extends CraftBlockEntityState<BannerBlockEntity> implem
for (int i = 0; i < banner.getPatterns().layers().size(); i++) {
BannerPatternLayers.Layer p = banner.getPatterns().layers().get(i);
// Paper start - fix upstream not handling inlined banner pattern
java.util.Optional<org.bukkit.block.banner.PatternType> type = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.BANNER_PATTERN, p.pattern());
java.util.Optional<org.bukkit.block.banner.PatternType> type = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(RegistryKey.BANNER_PATTERN, p.pattern());
if (type.isEmpty()) continue;
this.patterns.add(new Pattern(DyeColor.getByWoolData((byte) p.color().getId()), type.get()));
// Paper end - fix upstream not handling inlined banner pattern

View File

@@ -4,7 +4,6 @@ import java.util.Locale;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.block.Biome;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
@@ -15,7 +14,7 @@ public class CraftBiome implements Biome, Handleable<net.minecraft.world.level.b
private static int count = 0;
public static Biome minecraftToBukkit(net.minecraft.world.level.biome.Biome minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BIOME, Registry.BIOME);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BIOME);
}
public static Biome minecraftHolderToBukkit(Holder<net.minecraft.world.level.biome.Biome> minecraft) {

View File

@@ -50,7 +50,12 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
if (thr instanceof ThreadDeath) {
throw (ThreadDeath)thr;
}
throw new RuntimeException("Failed to read BlockState at: world: " + this.getWorld().getName() + " location: (" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")", thr);
throw new RuntimeException(
world == null
? "Failed to read non-placed BlockState"
: "Failed to read BlockState at: world: " + world.getName() + " location: (" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")",
thr
);
}
// Paper end - Show blockstate location if we failed to read it
}

View File

@@ -3,7 +3,9 @@ package org.bukkit.craftbukkit.block;
import com.google.common.base.Preconditions;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.InteractionHand;
@@ -49,7 +51,7 @@ public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>,
}
public static BlockType minecraftToBukkitNew(Block minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BLOCK, Registry.BLOCK);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BLOCK);
}
public static Block bukkitToMinecraftNew(BlockType bukkit) {
@@ -147,6 +149,16 @@ public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>,
return this.createBlockData((String) null);
}
@Override
public @NotNull Collection<B> createBlockDataStates() {
final ImmutableList<BlockState> possibleStates = this.block.getStateDefinition().getPossibleStates();
final ImmutableList.Builder<B> builder = ImmutableList.builderWithExpectedSize(possibleStates.size());
for (final BlockState possibleState : possibleStates) {
builder.add(this.blockDataClass.cast(possibleState.createCraftBlockData()));
}
return builder.build();
}
@Override
public B createBlockData(Consumer<? super B> consumer) {
B data = this.createBlockData();

View File

@@ -2,6 +2,8 @@ package org.bukkit.craftbukkit.block;
import com.google.common.base.Preconditions;
import com.mojang.authlib.GameProfile;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.Component;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
@@ -216,4 +218,16 @@ public class CraftSkull extends CraftBlockEntityState<SkullBlockEntity> implemen
public CraftSkull copy(Location location) {
return new CraftSkull(this, location);
}
@Override
public @Nullable Component customName() {
SkullBlockEntity snapshot = getSnapshot();
return snapshot.customName == null ? null : PaperAdventure.asAdventure(snapshot.customName);
}
@Override
public void customName(@Nullable Component customName) {
SkullBlockEntity snapshot = getSnapshot();
snapshot.customName = customName == null ? null : PaperAdventure.asVanilla(customName);
}
}

View File

@@ -33,6 +33,26 @@ public class CraftTrialSpawner extends CraftBlockEntityState<TrialSpawnerBlockEn
this.ominousConfig = state.ominousConfig;
}
@Override
public long getCooldownEnd() {
return this.getSnapshot().trialSpawner.getData().cooldownEndsAt;
}
@Override
public void setCooldownEnd(long ticks) {
this.getSnapshot().trialSpawner.getData().cooldownEndsAt = ticks;
}
@Override
public long getNextSpawnAttempt() {
return this.getSnapshot().trialSpawner.getData().nextMobSpawnsAt;
}
@Override
public void setNextSpawnAttempt(long ticks) {
this.getSnapshot().trialSpawner.getData().nextMobSpawnsAt = ticks;
}
@Override
public int getCooldownLength() {
return this.getSnapshot().trialSpawner.getTargetCooldownLength();

View File

@@ -1,17 +1,33 @@
package org.bukkit.craftbukkit.block;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.block.entity.vault.VaultBlockEntity;
import net.minecraft.world.level.block.entity.vault.VaultConfig;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Vault;
import org.bukkit.craftbukkit.CraftLootTable;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootTable;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@NullMarked
public class CraftVault extends CraftBlockEntityState<VaultBlockEntity> implements Vault {
public CraftVault(World world, VaultBlockEntity tileEntity) {
super(world, tileEntity);
}
protected CraftVault(CraftVault state, Location location) {
protected CraftVault(CraftVault state, @Nullable Location location) {
super(state, location);
}
@@ -24,4 +40,124 @@ public class CraftVault extends CraftBlockEntityState<VaultBlockEntity> implemen
public CraftVault copy(Location location) {
return new CraftVault(this, location);
}
@Override
public double getActivationRange() {
return this.getSnapshot().getConfig().activationRange();
}
@Override
public void setActivationRange(final double activationRange) {
Preconditions.checkArgument(Double.isFinite(activationRange), "activation range must not be NaN or infinite");
Preconditions.checkArgument(activationRange <= this.getDeactivationRange(), "New activation range (%s) must be less or equal to deactivation range (%s)", activationRange, this.getDeactivationRange());
final VaultConfig config = this.getSnapshot().getConfig();
this.getSnapshot().setConfig(new VaultConfig(config.lootTable(), activationRange, config.deactivationRange(), config.keyItem(), config.overrideLootTableToDisplay()));
}
@Override
public double getDeactivationRange() {
return this.getSnapshot().getConfig().deactivationRange();
}
@Override
public void setDeactivationRange(final double deactivationRange) {
Preconditions.checkArgument(Double.isFinite(deactivationRange), "deactivation range must not be NaN or infinite");
Preconditions.checkArgument(deactivationRange >= this.getActivationRange(), "New deactivation range (%s) must be more or equal to activation range (%s)", deactivationRange, this.getActivationRange());
final VaultConfig config = this.getSnapshot().getConfig();
this.getSnapshot().setConfig(new VaultConfig(config.lootTable(), config.activationRange(), deactivationRange, config.keyItem(), config.overrideLootTableToDisplay()));
}
@Override
public ItemStack getKeyItem() {
return this.getSnapshot().getConfig().keyItem().asBukkitCopy();
}
@Override
public void setKeyItem(final ItemStack key) {
Preconditions.checkArgument(key != null, "key must not be null");
final VaultConfig config = this.getSnapshot().getConfig();
this.getSnapshot().setConfig(new VaultConfig(config.lootTable(), config.activationRange(), config.deactivationRange(), CraftItemStack.asNMSCopy(key), config.overrideLootTableToDisplay()));
}
@Override
public LootTable getLootTable() {
return CraftLootTable.minecraftToBukkit(this.getSnapshot().getConfig().lootTable());
}
@Override
public void setLootTable(final LootTable lootTable) {
final ResourceKey<net.minecraft.world.level.storage.loot.LootTable> lootTableKey = CraftLootTable.bukkitToMinecraft(lootTable);
Preconditions.checkArgument(lootTableKey != null, "lootTable must not be null");
final VaultConfig config = this.getSnapshot().getConfig();
this.getSnapshot().setConfig(new VaultConfig(lootTableKey, config.activationRange(), config.deactivationRange(), config.keyItem(), config.overrideLootTableToDisplay()));
}
@Override
public @Nullable LootTable getDisplayedLootTable() {
return this.getSnapshot().getConfig().overrideLootTableToDisplay().map(CraftLootTable::minecraftToBukkit).orElse(null);
}
@Override
public void setDisplayedLootTable(final @Nullable LootTable lootTable) {
final VaultConfig config = this.getSnapshot().getConfig();
this.getSnapshot().setConfig(new VaultConfig(config.lootTable(), config.activationRange(), config.deactivationRange(), config.keyItem(), Optional.ofNullable(CraftLootTable.bukkitToMinecraft(lootTable))));
}
@Override
public long getNextStateUpdateTime() {
return this.getSnapshot().serverData.stateUpdatingResumesAt();
}
@Override
public void setNextStateUpdateTime(final long nextStateUpdateTime) {
this.getSnapshot().serverData.pauseStateUpdatingUntil(nextStateUpdateTime);
}
@Override
public @Unmodifiable Collection<UUID> getRewardedPlayers() {
return ImmutableSet.copyOf(this.getSnapshot().serverData.getRewardedPlayers());
}
@Override
public boolean addRewardedPlayer(final UUID playerUUID) {
Preconditions.checkArgument(playerUUID != null, "playerUUID must not be null");
return this.getSnapshot().serverData.addToRewardedPlayers(playerUUID);
}
@Override
public boolean removeRewardedPlayer(final UUID playerUUID) {
Preconditions.checkArgument(playerUUID != null, "playerUUID must not be null");
return this.getSnapshot().serverData.removeFromRewardedPlayers(playerUUID);
}
@Override
public boolean hasRewardedPlayer(final UUID playerUUID) {
return this.getSnapshot().serverData.getRewardedPlayers().contains(playerUUID);
}
@Override
public @Unmodifiable Set<UUID> getConnectedPlayers() {
return ImmutableSet.copyOf(this.getSnapshot().getSharedData().getConnectedPlayers());
}
@Override
public boolean hasConnectedPlayer(final UUID playerUUID) {
return this.getSnapshot().getSharedData().getConnectedPlayers().contains(playerUUID);
}
@Override
public ItemStack getDisplayedItem() {
return CraftItemStack.asBukkitCopy(this.getSnapshot().getSharedData().getDisplayItem());
}
@Override
public void setDisplayedItem(final ItemStack displayedItem) {
Preconditions.checkArgument(displayedItem != null, "displayedItem must not be null");
this.getSnapshot().getSharedData().setDisplayItem(CraftItemStack.asNMSCopy(displayedItem));
}
}

View File

@@ -4,7 +4,6 @@ import io.papermc.paper.util.OldEnumHolderable;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.block.entity.BannerPattern;
import org.bukkit.Registry;
import org.bukkit.block.banner.PatternType;
import org.bukkit.craftbukkit.CraftRegistry;
@@ -13,11 +12,11 @@ public class CraftPatternType extends OldEnumHolderable<PatternType, BannerPatte
private static int count = 0;
public static PatternType minecraftToBukkit(BannerPattern minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BANNER_PATTERN, Registry.BANNER_PATTERN);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BANNER_PATTERN);
}
public static PatternType minecraftHolderToBukkit(Holder<BannerPattern> minecraft) {
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registry.BANNER_PATTERN);
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.BANNER_PATTERN);
}
public static BannerPattern bukkitToMinecraft(PatternType bukkit) {

View File

@@ -1,10 +1,10 @@
package org.bukkit.craftbukkit.damage;
import java.util.Objects;
import net.minecraft.Optionull;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.damage.DamageSource;
@@ -26,12 +26,7 @@ public class CraftDamageSource implements DamageSource {
}
public World getCausingEntityWorld() {
org.bukkit.entity.Entity causingEntity = this.getCausingEntity();
return causingEntity != null ? causingEntity.getWorld() : null;
}
public Block getDirectBlock() {
return this.getHandle().getDirectBlock();
return Optionull.map(this.getCausingEntity(), Entity::getWorld);
}
@Override
@@ -41,26 +36,22 @@ public class CraftDamageSource implements DamageSource {
@Override
public org.bukkit.entity.Entity getCausingEntity() {
net.minecraft.world.entity.Entity entity = this.getHandle().getEntity(); // Paper - fix DamageSource API - revert to vanilla
return (entity != null) ? entity.getBukkitEntity() : null;
return Optionull.map(this.getHandle().getEntity(), net.minecraft.world.entity.Entity::getBukkitEntity);
}
@Override
public org.bukkit.entity.Entity getDirectEntity() {
net.minecraft.world.entity.Entity entity = this.getHandle().getDirectEntity(); // Paper - fix DamageSource API
return (entity != null) ? entity.getBukkitEntity() : null;
return Optionull.map(this.getHandle().getDirectEntity(), net.minecraft.world.entity.Entity::getBukkitEntity);
}
@Override
public Location getDamageLocation() {
Vec3 vec3D = this.getHandle().sourcePositionRaw();
return (vec3D != null) ? CraftLocation.toBukkit(vec3D, this.getCausingEntityWorld()) : null;
return Optionull.map(this.getHandle().sourcePositionRaw(), sourcePos -> CraftLocation.toBukkit(sourcePos, this.getCausingEntityWorld()));
}
@Override
public Location getSourceLocation() {
Vec3 vec3D = this.getHandle().getSourcePosition();
return (vec3D != null) ? CraftLocation.toBukkit(vec3D, this.getCausingEntityWorld()) : null;
return Optionull.map(this.getHandle().getSourcePosition(), sourcePos -> CraftLocation.toBukkit(sourcePos, this.getCausingEntityWorld()));
}
@Override
@@ -70,7 +61,7 @@ public class CraftDamageSource implements DamageSource {
@Override
public float getFoodExhaustion() {
return this.damageType.getExhaustion();
return this.getHandle().getFoodExhaustion();
}
@Override
@@ -84,28 +75,27 @@ public class CraftDamageSource implements DamageSource {
return true;
}
if (!(obj instanceof DamageSource)) {
if (!(obj instanceof DamageSource other)) {
return false;
}
DamageSource other = (DamageSource) obj;
return Objects.equals(this.getDamageType(), other.getDamageType()) && Objects.equals(this.getCausingEntity(), other.getCausingEntity())
&& Objects.equals(this.getDirectEntity(), other.getDirectEntity()) && Objects.equals(this.getDamageLocation(), other.getDamageLocation());
&& Objects.equals(this.getDirectEntity(), other.getDirectEntity()) && Objects.equals(this.getDamageLocation(), other.getDamageLocation());
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + this.damageType.hashCode();
result = 31 * result + (this.getCausingEntity() != null ? this.getCausingEntity().hashCode() : 0);
result = 31 * result + (this.getDirectEntity() != null ? this.getDirectEntity().hashCode() : 0);
result = 31 * result + (this.getDamageLocation() != null ? this.getDamageLocation().hashCode() : 0);
result = 31 * result + Objects.hashCode(this.getCausingEntity());
result = 31 * result + Objects.hashCode(this.getDirectEntity());
result = 31 * result + Objects.hashCode(this.getDamageLocation());
return result;
}
@Override
public String toString() {
return "DamageSource{damageType=" + this.getDamageType() + ",causingEntity=" + this.getCausingEntity() + ",directEntity=" + this.getDirectEntity() + ",damageLocation=" + this.getDamageLocation() + "}";
return "DamageSource{damageType=" + this.getDamageType() + ", causingEntity=" + this.getCausingEntity() + ", directEntity=" + this.getDirectEntity() + ", damageLocation=" + this.getDamageLocation() + "}";
}
public static DamageSource buildFromBukkit(DamageType damageType, Entity causingEntity, Entity directEntity, Location damageLocation) {
@@ -121,8 +111,8 @@ public class CraftDamageSource implements DamageSource {
nmsDirectEntity = craftDirectEntity.getHandle();
}
Vec3 vec3D = (damageLocation == null) ? null : CraftLocation.toVec3D(damageLocation);
Vec3 sourcePos = (damageLocation == null) ? null : CraftLocation.toVec3D(damageLocation);
return new CraftDamageSource(new net.minecraft.world.damagesource.DamageSource(holderDamageType, nmsDirectEntity, nmsCausingEntity, vec3D));
return new CraftDamageSource(new net.minecraft.world.damagesource.DamageSource(holderDamageType, nmsDirectEntity, nmsCausingEntity, sourcePos));
}
}

View File

@@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.damage.DamageEffect;
@@ -59,7 +58,7 @@ public class CraftDamageType implements DamageType, Handleable<net.minecraft.wor
@Override
public String toString() {
return "CraftDamageType{" + "key=" + this.getKey() + ",damageScaling=" + this.getDamageScaling() + ",damageEffect=" + this.getDamageEffect() + ",deathMessageType=" + this.getDeathMessageType() + ",exhaustion=" + this.getExhaustion() + "}";
return "CraftDamageType{" + "key=" + this.getKey() + ", damageScaling=" + this.getDamageScaling() + ", damageEffect=" + this.getDamageEffect() + ", deathMessageType=" + this.getDeathMessageType() + ", exhaustion=" + this.getExhaustion() + "}";
}
public static DeathMessageType deathMessageTypeToBukkit(net.minecraft.world.damagesource.DeathMessageType deathMessageType) {
@@ -120,6 +119,6 @@ public class CraftDamageType implements DamageType, Handleable<net.minecraft.wor
}
public static DamageType minecraftToBukkit(net.minecraft.world.damagesource.DamageType minecraftDamageType) {
return CraftRegistry.minecraftToBukkit(minecraftDamageType, Registries.DAMAGE_TYPE, Registry.DAMAGE_TYPE);
return CraftRegistry.minecraftToBukkit(minecraftDamageType, Registries.DAMAGE_TYPE);
}
}

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit.enchantments;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.util.Holderable;
import java.util.Locale;
import net.minecraft.Util;
@@ -21,7 +22,7 @@ import org.bukkit.inventory.ItemStack;
public class CraftEnchantment extends Enchantment implements Holderable<net.minecraft.world.item.enchantment.Enchantment> {
public static Enchantment minecraftToBukkit(net.minecraft.world.item.enchantment.Enchantment minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ENCHANTMENT, Registry.ENCHANTMENT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ENCHANTMENT);
}
public static Enchantment minecraftHolderToBukkit(Holder<net.minecraft.world.item.enchantment.Enchantment> minecraft) {
@@ -52,7 +53,7 @@ public class CraftEnchantment extends Enchantment implements Holderable<net.mine
NamespacedKey key = NamespacedKey.fromString(string);
// Now also convert from when keys where saved
return CraftRegistry.get(Registry.ENCHANTMENT, key, ApiVersion.CURRENT);
return CraftRegistry.get(RegistryKey.ENCHANTMENT, key, ApiVersion.CURRENT);
}
private final Holder<net.minecraft.world.item.enchantment.Enchantment> handle;

View File

@@ -1,15 +1,20 @@
package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.BlockCollisions;
import net.minecraft.world.phys.AABB;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.projectiles.ProjectileSource;
import java.util.List;
public class CraftAbstractArrow extends AbstractProjectile implements AbstractArrow {
@@ -68,12 +73,16 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr
@Override
public Block getAttachedBlock() {
return Iterables.getFirst(getAttachedBlocks(), null);
}
@Override
public List<Block> getAttachedBlocks() {
if (!this.isInBlock()) {
return null;
return ImmutableList.of();
}
BlockPos pos = this.getHandle().blockPosition();
return this.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
return ImmutableList.copyOf(new BlockCollisions<>(this.getHandle().level(), (Entity) null, new AABB(this.getHandle().position(), this.getHandle().position()).inflate(0.06), false, (mutableBlockPos, voxelShape) -> CraftBlock.at(this.getHandle().level(), mutableBlockPos)));
}
@Override
@@ -138,7 +147,7 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr
@Override
public String toString() {
return "CraftArrow";
return "CraftAbstractArrow";
}
// Paper start

View File

@@ -31,7 +31,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow {
@Override
public String toString() {
return "CraftTippedArrow";
return "CraftArrow";
}
@Override

View File

@@ -7,7 +7,6 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.animal.CatVariant;
import org.bukkit.DyeColor;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.Handleable;
@@ -55,7 +54,7 @@ public class CraftCat extends CraftTameableAnimal implements Cat {
private static int count = 0;
public static Type minecraftToBukkit(CatVariant minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.CAT_VARIANT, Registry.CAT_VARIANT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.CAT_VARIANT);
}
public static Type minecraftHolderToBukkit(Holder<CatVariant> minecraft) {

View File

@@ -1,11 +1,19 @@
package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions;
import net.minecraft.Optionull;
import net.minecraft.world.entity.monster.creaking.Creaking;
import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.entity.Player;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class CraftCreaking extends CraftMonster implements org.bukkit.entity.Creaking {
public CraftCreaking(CraftServer server, Creaking entity) {
public CraftCreaking(final CraftServer server, final Creaking entity) {
super(server, entity);
}
@@ -14,6 +22,28 @@ public class CraftCreaking extends CraftMonster implements org.bukkit.entity.Cre
return (Creaking) this.entity;
}
@Nullable
@Override
public Location getHome() {
return Optionull.map(this.getHandle().getHomePos(), pos -> CraftLocation.toBukkit(pos, this.getHandle().level()));
}
@Override
public void activate(final Player player) {
Preconditions.checkArgument(player != null, "player cannot be null");
this.getHandle().activate(((CraftPlayer) player).getHandle());
}
@Override
public void deactivate() {
this.getHandle().deactivate();
}
@Override
public boolean isActive() {
return this.getHandle().isActive();
}
@Override
public String toString() {
return "CraftCreaking";

View File

@@ -5,6 +5,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -243,9 +244,9 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
Preconditions.checkArgument(location != null, "location cannot be null");
location.checkFinite();
// Paper start - Teleport passenger API
Set<io.papermc.paper.entity.TeleportFlag> flagSet = Set.of(flags);
Set<io.papermc.paper.entity.TeleportFlag> flagSet = new HashSet<>(List.of(flags)); // Wrap into list while multiple old flags link to the same new one
boolean dismount = !flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_VEHICLE);
boolean ignorePassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS);
boolean retainPassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS);
// Don't allow teleporting between worlds while keeping passengers
if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) {
return false;
@@ -257,7 +258,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
}
// Paper end
if ((!ignorePassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API
if ((!retainPassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API
return false;
}
@@ -288,6 +289,9 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
// SPIGOT-619: Force sync head rotation also
this.entity.setYHeadRot(location.getYaw());
// Ensure passengers of entity are teleported
if (retainPassengers && this.entity.isVehicle()) this.entity.teleportPassengers();
return true;
}
@@ -410,7 +414,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@Override
public boolean isValid() {
return this.entity.isAlive() && this.entity.valid && this.entity.isChunkLoaded() && this.isInWorld();
return this.entity.isAlive() && this.entity.valid;
}
@Override
@@ -515,13 +519,14 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@Override
public int getTicksLived() {
return this.getHandle().tickCount;
return this.getHandle().totalEntityAge;
}
@Override
public void setTicksLived(int value) {
Preconditions.checkArgument(value > 0, "Age value (%s) must be greater than 0", value);
this.getHandle().tickCount = value;
this.getHandle().totalEntityAge = value;
}
public Entity getHandle() {
@@ -540,13 +545,12 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
}
@Override
public void playEffect(EntityEffect type) {
Preconditions.checkArgument(type != null, "Type cannot be null");
public void playEffect(EntityEffect effect) {
Preconditions.checkArgument(effect != null, "Entity effect cannot be null");
Preconditions.checkState(!this.entity.generation, "Cannot play effect during world generation");
Preconditions.checkArgument(effect.isApplicableTo(this), "Entity effect cannot apply to this entity");
if (type.getApplicable().isInstance(this)) {
this.getHandle().level().broadcastEntityEvent(this.getHandle(), type.getData());
}
this.getHandle().level().broadcastEntityEvent(this.getHandle(), effect.getData());
}
@Override

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import java.util.Locale;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
@@ -61,6 +62,6 @@ public class CraftEntityType {
NamespacedKey key = NamespacedKey.fromString(string);
// Now also convert from when keys where saved
return CraftRegistry.get(Registry.ENTITY_TYPE, key, ApiVersion.CURRENT);
return CraftRegistry.get(RegistryKey.ENTITY_TYPE, key, ApiVersion.CURRENT);
}
}

View File

@@ -7,7 +7,6 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.animal.FrogVariant;
import net.minecraft.world.entity.animal.frog.Frog;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.Handleable;
@@ -59,7 +58,7 @@ public class CraftFrog extends CraftAnimals implements org.bukkit.entity.Frog {
private static int count = 0;
public static Variant minecraftToBukkit(FrogVariant minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.FROG_VARIANT, Registry.FROG_VARIANT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.FROG_VARIANT);
}
public static Variant minecraftHolderToBukkit(Holder<FrogVariant> minecraft) {

View File

@@ -11,6 +11,7 @@ import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundHorseScreenOpenPacket;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClosePacket;
import net.minecraft.resources.ResourceLocation;
@@ -24,7 +25,10 @@ import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.HorseInventoryMenu;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.MerchantMenu;
import net.minecraft.world.item.ItemCooldowns;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
@@ -50,6 +54,8 @@ import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftMerchantCustom;
import org.bukkit.craftbukkit.inventory.CraftRecipe;
import org.bukkit.craftbukkit.inventory.util.CraftMenus;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.entity.Firework;
import org.bukkit.entity.HumanEntity;
@@ -151,7 +157,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
// Paper start - Potential bed api
@Override
public Location getPotentialBedLocation() {
public Location getPotentialRespawnLocation() {
ServerPlayer handle = (ServerPlayer) getHandle();
BlockPos bed = handle.getRespawnPosition();
if (bed == null) {
@@ -452,6 +458,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
AbstractContainerMenu container;
if (inventory instanceof CraftInventoryView) {
container = ((CraftInventoryView) inventory).getHandle();
Preconditions.checkArgument(!(container instanceof InventoryMenu), "Can not open player's InventoryView");
} else {
container = new CraftContainer(inventory, this.getHandle(), player.nextContainerCounter());
}
@@ -467,13 +474,24 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
// Now open the window
MenuType<?> windowType = CraftContainer.getNotchInventoryType(inventory.getTopInventory());
// we can open these now, delegate for now
if (windowType == MenuType.MERCHANT) {
CraftMenus.openMerchantMenu(player, (MerchantMenu) container);
return;
}
//String title = inventory.getTitle(); // Paper - comment
net.kyori.adventure.text.Component adventure$title = inventory.title(); // Paper
if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(inventory.getTitle()); // Paper
if (result.getFirst() != null) adventure$title = result.getFirst(); // Paper - Add titleOverride to InventoryOpenEvent
//player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper - Prevent opening inventories when frozen
if (!player.isImmobile()) {
if (container instanceof HorseInventoryMenu horse) {
player.connection.send(new ClientboundHorseScreenOpenPacket(horse.containerId, horse.horse.getInventoryColumns(), horse.horse.getId()));
} else {
player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title)));
}
}
player.containerMenu = container;
player.initMenu(container);
}

View File

@@ -1,13 +1,16 @@
package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import io.netty.buffer.Unpooled;
import io.papermc.paper.FeatureHooks;
import io.papermc.paper.configuration.GlobalConfiguration;
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.PaperPlayerGiveResult;
import io.papermc.paper.entity.PlayerGiveResult;
import it.unimi.dsi.fastutil.shorts.ShortArraySet;
import it.unimi.dsi.fastutil.shorts.ShortSet;
import java.io.ByteArrayOutputStream;
@@ -24,6 +27,7 @@ import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -98,8 +102,8 @@ import net.minecraft.server.players.UserWhiteListEntry;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeMap;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.food.FoodData;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.GameType;
@@ -115,6 +119,7 @@ import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Effect;
import org.bukkit.EntityEffect;
import org.bukkit.GameMode;
import org.bukkit.Input;
import org.bukkit.Instrument;
@@ -167,7 +172,6 @@ import org.bukkit.craftbukkit.map.CraftMapView;
import org.bukkit.craftbukkit.map.RenderData;
import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
import org.bukkit.craftbukkit.potion.CraftPotionUtil;
import org.bukkit.craftbukkit.profile.CraftPlayerProfile;
import org.bukkit.craftbukkit.scoreboard.CraftScoreboard;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftLocation;
@@ -175,13 +179,13 @@ import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.entity.EnderPearl;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerExpCooldownChangeEvent;
import org.bukkit.event.player.PlayerHideEntityEvent;
import org.bukkit.event.player.PlayerRegisterChannelEvent;
import org.bukkit.event.player.PlayerShowEntityEvent;
import org.bukkit.event.player.PlayerSpawnChangeEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerUnregisterChannelEvent;
import org.bukkit.inventory.EquipmentSlot;
@@ -194,7 +198,6 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.messaging.StandardMessenger;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.profile.PlayerProfile;
import org.bukkit.scoreboard.Scoreboard;
import org.jetbrains.annotations.NotNull;
@@ -218,6 +221,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener();
public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API
private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit
private boolean simplifyContainerDesyncCheck = GlobalConfiguration.get().unsupportedSettings.simplifyRemoteItemMatching;
private long lastSaveTime; // Paper - getLastPlayed replacement API
public CraftPlayer(CraftServer server, ServerPlayer entity) {
@@ -1160,6 +1164,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void sendEquipmentChange(LivingEntity entity, Map<EquipmentSlot, ItemStack> items) {
Preconditions.checkArgument(entity != null, "Entity cannot be null");
Preconditions.checkArgument(items != null, "items cannot be null");
Preconditions.checkArgument(!items.isEmpty(), "items cannot be empty");
if (this.getHandle().connection == null) {
return;
@@ -2453,7 +2458,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
private void sendCustomPayload(ResourceLocation id, byte[] message) {
ClientboundCustomPayloadPacket packet = new ClientboundCustomPayloadPacket(new DiscardedPayload(id, Unpooled.wrappedBuffer(message)));
ClientboundCustomPayloadPacket packet = new ClientboundCustomPayloadPacket(new DiscardedPayload(id, message));
this.getHandle().connection.send(packet);
}
@@ -2819,14 +2824,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void updateScaledHealth(boolean sendHealth) {
AttributeMap attributemapserver = this.getHandle().getAttributes();
Collection<AttributeInstance> set = attributemapserver.getSyncableAttributes();
this.injectScaledMaxHealth(set, true);
// SPIGOT-3813: Attributes before health
if (this.getHandle().connection != null) {
this.getHandle().connection.send(new ClientboundUpdateAttributesPacket(this.getHandle().getId(), set));
this.getHandle().connection.send(new ClientboundUpdateAttributesPacket(this.getHandle().getId(), Set.of(this.getScaledMaxHealth())));
if (sendHealth) {
this.sendHealthUpdate();
}
@@ -2858,14 +2858,19 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (!this.scaledHealth && !force) {
return;
}
for (AttributeInstance genericInstance : collection) {
Iterator<AttributeInstance> iterator = collection.iterator();
while (iterator.hasNext()) {
AttributeInstance genericInstance = iterator.next();
if (genericInstance.getAttribute() == Attributes.MAX_HEALTH) {
collection.remove(genericInstance);
iterator.remove();
break;
}
}
collection.add(getScaledMaxHealth());
}
public AttributeInstance getScaledMaxHealth() {
AttributeInstance dummy = new AttributeInstance(Attributes.MAX_HEALTH, (attribute) -> { });
// Spigot start
double healthMod = this.scaledHealth ? this.healthScale : this.getMaxHealth();
if ( healthMod >= Float.MAX_VALUE || healthMod <= 0 )
{
@@ -2873,8 +2878,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.getServer().getLogger().warning( this.getName() + " tried to crash the server with a large health attribute" );
}
dummy.setBaseValue(healthMod);
// Spigot end
collection.add(dummy);
return dummy;
}
@Override
@@ -3534,11 +3538,85 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
// Paper start - entity effect API
@Override
public void sendEntityEffect(final org.bukkit.EntityEffect effect, final org.bukkit.entity.Entity target) {
if (this.getHandle().connection == null || !effect.isApplicableTo(target)) {
public void sendEntityEffect(final EntityEffect effect, final org.bukkit.entity.Entity target) {
if (this.getHandle().connection == null) {
return;
}
Preconditions.checkArgument(effect.isApplicableTo(target), "Entity effect cannot apply to the target");
this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData()));
}
// Paper end - entity effect API
@Override
public @NotNull PlayerGiveResult give(@NotNull final Collection<@NotNull ItemStack> items, final boolean dropIfFull) {
Preconditions.checkArgument(items != null, "items cannot be null");
if (items.isEmpty()) return PaperPlayerGiveResult.EMPTY; // Early opt out for empty input.
// Validate all items before attempting to spawn any.
for (final ItemStack item : items) {
Preconditions.checkArgument(item != null, "ItemStack cannot be null");
Preconditions.checkArgument(!item.isEmpty(), "ItemStack cannot be empty");
Preconditions.checkArgument(item.getAmount() <= item.getMaxStackSize(), "ItemStack amount cannot be greater than its max stack size");
}
final ServerPlayer handle = this.getHandle();
final ImmutableList.Builder<Item> drops = ImmutableList.builder();
final ImmutableList.Builder<ItemStack> leftovers = ImmutableList.builder();
for (final ItemStack item : items) {
final net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(item);
final boolean added = handle.getInventory().add(nmsStack);
if (added && nmsStack.isEmpty()) continue; // Item was fully added, neither a drop nor a leftover is needed.
leftovers.add(CraftItemStack.asBukkitCopy(nmsStack)); // Insert copy to avoid mutation to the dropped item from affecting leftovers
if (!dropIfFull) continue;
final ItemEntity entity = handle.drop(nmsStack, false, true, false);
if (entity != null) drops.add((Item) entity.getBukkitEntity());
}
handle.containerMenu.broadcastChanges();
return new PaperPlayerGiveResult(leftovers.build(), drops.build());
}
@Override
public float getSidewaysMovement() {
final boolean leftMovement = this.getHandle().getLastClientInput().left();
final boolean rightMovement = this.getHandle().getLastClientInput().right();
return leftMovement == rightMovement ? 0 : leftMovement ? 1 : -1;
}
@Override
public float getForwardsMovement() {
final boolean forwardMovement = this.getHandle().getLastClientInput().forward();
final boolean backwardMovement = this.getHandle().getLastClientInput().backward();
return forwardMovement == backwardMovement ? 0 : forwardMovement ? 1 : -1;
}
@Override
public int getDeathScreenScore() {
return getHandle().getScore();
}
@Override
public void setDeathScreenScore(final int score) {
getHandle().setScore(score);
}
/**
* Returns whether container desync checks should skip the full item comparison of remote carried and changed slots
* and should instead only check their type and amount.
* <p>
* This is useful if the client is not able to produce the same item stack (or as of 1.21.5, its data hashes) as the server.
*
* @return whether to simplify container desync checks
*/
public boolean simplifyContainerDesyncCheck() {
return simplifyContainerDesyncCheck;
}
public void setSimplifyContainerDesyncCheck(final boolean simplifyContainerDesyncCheck) {
this.simplifyContainerDesyncCheck = simplifyContainerDesyncCheck;
}
}

View File

@@ -58,13 +58,13 @@ public class CraftTextDisplay extends CraftDisplay implements TextDisplay {
public Color getBackgroundColor() {
int color = this.getHandle().getBackgroundColor();
return (color == -1) ? null : Color.fromARGB(color);
return color == Display.TextDisplay.INITIAL_BACKGROUND ? null : Color.fromARGB(color);
}
@Override
public void setBackgroundColor(Color color) {
if (color == null) {
this.getHandle().getEntityData().set(Display.TextDisplay.DATA_BACKGROUND_COLOR_ID, -1);
this.getHandle().getEntityData().set(Display.TextDisplay.DATA_BACKGROUND_COLOR_ID, Display.TextDisplay.INITIAL_BACKGROUND);
} else {
this.getHandle().getEntityData().set(Display.TextDisplay.DATA_BACKGROUND_COLOR_ID, color.asARGB());
}

View File

@@ -29,7 +29,6 @@ public class CraftTurtle extends CraftAnimals implements Turtle {
return this.getHandle().isLayingEgg();
}
// Paper start
@Override
public org.bukkit.Location getHome() {
return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level(), this.getHandle().getHomePos());
@@ -45,14 +44,8 @@ public class CraftTurtle extends CraftAnimals implements Turtle {
return this.getHandle().isGoingHome();
}
@Override
public boolean isDigging() {
return this.getHandle().isLayingEgg();
}
@Override
public void setHasEgg(boolean hasEgg) {
this.getHandle().setHasEgg(hasEgg);
}
// Paper end
}

View File

@@ -11,7 +11,6 @@ import net.minecraft.world.level.block.BedBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.CraftLocation;
@@ -23,7 +22,6 @@ import org.bukkit.event.entity.EntityTransformEvent;
// Paper start
import com.destroystokyo.paper.entity.villager.Reputation;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.UUID;
// Paper end
@@ -177,7 +175,7 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
private static int count = 0;
public static Type minecraftToBukkit(VillagerType minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.VILLAGER_TYPE, Registry.VILLAGER_TYPE);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.VILLAGER_TYPE);
}
public static VillagerType bukkitToMinecraft(Type bukkit) {
@@ -258,7 +256,7 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
private static int count = 0;
public static Profession minecraftToBukkit(VillagerProfession minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.VILLAGER_PROFESSION, Registry.VILLAGER_PROFESSION);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.VILLAGER_PROFESSION);
}
public static VillagerProfession bukkitToMinecraft(Profession bukkit) {

View File

@@ -6,7 +6,6 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.animal.WolfVariant;
import org.bukkit.DyeColor;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.Handleable;
@@ -81,7 +80,7 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf {
public static class CraftVariant implements Variant, Handleable<WolfVariant> {
public static Variant minecraftToBukkit(WolfVariant minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.WOLF_VARIANT, Registry.WOLF_VARIANT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.WOLF_VARIANT);
}
public static Variant minecraftHolderToBukkit(Holder<WolfVariant> minecraft) {

View File

@@ -12,6 +12,7 @@ import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@@ -87,7 +88,6 @@ import org.bukkit.craftbukkit.damage.CraftDamageSource;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.entity.CraftRaider;
import org.bukkit.craftbukkit.entity.CraftSpellcaster;
import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
@@ -1092,17 +1092,19 @@ public class CraftEventFactory {
private static EntityDamageEvent handleEntityDamageEvent(Entity entity, DamageSource source, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled) {
CraftDamageSource bukkitDamageSource = new CraftDamageSource(source);
final Entity damager = source.getCustomEventDamager(); // Paper - fix DamageSource API
final Entity damager = source.eventEntityDamager() != null ? source.eventEntityDamager() : source.getDirectEntity(); // Paper - fix DamageSource API
if (source.is(DamageTypeTags.IS_EXPLOSION)) {
if (damager == null) {
return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.BLOCK_EXPLOSION, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, DamageCause.BLOCK_EXPLOSION, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
}
DamageCause damageCause = (damager.getBukkitEntity() instanceof org.bukkit.entity.TNTPrimed) ? DamageCause.BLOCK_EXPLOSION : DamageCause.ENTITY_EXPLOSION;
return CraftEventFactory.callEntityDamageEvent(damager, entity, damageCause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
} else if (damager != null || source.getDirectEntity() != null) {
DamageCause cause = (source.isSweep()) ? DamageCause.ENTITY_SWEEP_ATTACK : DamageCause.ENTITY_ATTACK;
DamageCause cause = DamageCause.ENTITY_ATTACK;
if (damager instanceof net.minecraft.world.entity.projectile.Projectile) {
if (source.knownCause() != null) {
cause = source.knownCause();
} else if (damager instanceof net.minecraft.world.entity.projectile.Projectile) {
if (damager.getBukkitEntity() instanceof ThrownPotion) {
cause = DamageCause.MAGIC;
} else if (damager.getBukkitEntity() instanceof Projectile) {
@@ -1126,12 +1128,14 @@ public class CraftEventFactory {
return CraftEventFactory.callEntityDamageEvent(damager, entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
} else if (source.is(DamageTypes.FELL_OUT_OF_WORLD)) {
return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.VOID, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, DamageCause.VOID, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
} else if (source.is(DamageTypes.LAVA)) {
return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
} else if (source.getDirectBlock() != null) {
return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
} else if (source.eventBlockDamager() != null) {
DamageCause cause;
if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) {
if (source.knownCause() != null) {
cause = source.knownCause();
} else if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) {
cause = DamageCause.CONTACT;
} else if (source.is(DamageTypes.HOT_FLOOR)) {
cause = DamageCause.HOT_FLOOR;
@@ -1142,13 +1146,15 @@ public class CraftEventFactory {
} else if (source.is(DamageTypes.CAMPFIRE)) {
cause = DamageCause.CAMPFIRE;
} else {
throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s [%s]", entity, source.getDirectBlock(), source.getMsgId(), source.typeHolder().getRegisteredName()));
cause = DamageCause.CUSTOM;
}
return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
}
DamageCause cause;
if (source.is(DamageTypes.IN_FIRE)) {
if (source.knownCause() != null) {
cause = source.knownCause();
} else if (source.is(DamageTypes.IN_FIRE)) {
cause = DamageCause.FIRE;
} else if (source.is(DamageTypes.STARVE)) {
cause = DamageCause.STARVATION;
@@ -1160,10 +1166,6 @@ public class CraftEventFactory {
cause = DamageCause.DROWNING;
} else if (source.is(DamageTypes.ON_FIRE)) {
cause = DamageCause.FIRE_TICK;
} else if (source.isMelting()) {
cause = DamageCause.MELTING;
} else if (source.isPoison()) {
cause = DamageCause.POISON;
} else if (source.is(DamageTypes.MAGIC)) {
cause = DamageCause.MAGIC;
} else if (source.is(DamageTypes.FALL)) {
@@ -1430,6 +1432,7 @@ public class CraftEventFactory {
}
public static com.mojang.datafixers.util.Pair<net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component, @org.jetbrains.annotations.Nullable AbstractContainerMenu> callInventoryOpenEventWithTitle(ServerPlayer player, AbstractContainerMenu container, boolean cancelled) {
// Paper end - Add titleOverride to InventoryOpenEvent
container.startOpen(); // delegate start open logic to before InventoryOpenEvent is fired
if (player.containerMenu != player.inventoryMenu) { // fire INVENTORY_CLOSE if one already open
player.connection.handleContainerClose(new ServerboundContainerClosePacket(player.containerMenu.containerId), InventoryCloseEvent.Reason.OPEN_NEW); // Paper - Inventory close reason
}
@@ -1759,10 +1762,13 @@ public class CraftEventFactory {
return (Cancellable) event;
}
public static FireworkExplodeEvent callFireworkExplodeEvent(FireworkRocketEntity firework) {
public static boolean callFireworkExplodeEvent(FireworkRocketEntity firework) {
FireworkExplodeEvent event = new FireworkExplodeEvent((Firework) firework.getBukkitEntity());
firework.level().getCraftServer().getPluginManager().callEvent(event);
return event;
if (!event.callEvent()) {
firework.discard(null);
return false;
}
return true;
}
public static PrepareAnvilEvent callPrepareAnvilEvent(AnvilView view, ItemStack item) {
@@ -1895,11 +1901,11 @@ public class CraftEventFactory {
}
public static EntityBreedEvent callEntityBreedEvent(net.minecraft.world.entity.LivingEntity child, net.minecraft.world.entity.LivingEntity mother, net.minecraft.world.entity.LivingEntity father, net.minecraft.world.entity.LivingEntity breeder, ItemStack bredWith, int experience) {
org.bukkit.entity.LivingEntity breederEntity = (LivingEntity) (breeder == null ? null : breeder.getBukkitEntity());
LivingEntity breederEntity = breeder == null ? null : (LivingEntity) breeder.getBukkitEntity();
CraftItemStack bredWithStack = bredWith == null ? null : CraftItemStack.asCraftMirror(bredWith).clone();
EntityBreedEvent event = new EntityBreedEvent((LivingEntity) child.getBukkitEntity(), (LivingEntity) mother.getBukkitEntity(), (LivingEntity) father.getBukkitEntity(), breederEntity, bredWithStack, experience);
child.level().getCraftServer().getPluginManager().callEvent(event);
event.callEvent();
return event;
}
@@ -2014,14 +2020,14 @@ public class CraftEventFactory {
Bukkit.getPluginManager().callEvent(event);
}
public static void callRaidSpawnWaveEvent(Raid raid, net.minecraft.world.entity.raid.Raider leader, List<net.minecraft.world.entity.raid.Raider> raiders) {
Raider craftLeader = (CraftRaider) leader.getBukkitEntity();
List<Raider> craftRaiders = new ArrayList<>();
for (net.minecraft.world.entity.raid.Raider entityRaider : raiders) {
craftRaiders.add((Raider) entityRaider.getBukkitEntity());
public static void callRaidSpawnWaveEvent(Raid raid, net.minecraft.world.entity.raid.Raider leader, Set<net.minecraft.world.entity.raid.Raider> raiders) {
Raider bukkitLeader = (Raider) leader.getBukkitEntity();
List<Raider> bukkitRaiders = new ArrayList<>(raiders.size());
for (net.minecraft.world.entity.raid.Raider raider : raiders) {
bukkitRaiders.add((Raider) raider.getBukkitEntity());
}
RaidSpawnWaveEvent event = new RaidSpawnWaveEvent(new CraftRaid(raid), raid.getLevel().getWorld(), craftLeader, craftRaiders);
Bukkit.getPluginManager().callEvent(event);
RaidSpawnWaveEvent event = new RaidSpawnWaveEvent(new CraftRaid(raid), raid.getLevel().getWorld(), bukkitLeader, bukkitRaiders);
event.callEvent();
}
public static LootGenerateEvent callLootGenerateEvent(Container inventory, LootTable lootTable, LootContext lootInfo, List<ItemStack> loot, boolean plugin) {

View File

@@ -8,12 +8,13 @@ import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.bukkit.HeightMap;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.CraftHeightMap;
import org.bukkit.craftbukkit.block.CraftBiome;
import org.bukkit.craftbukkit.block.CraftBlockType;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.generator.ChunkGenerator;
@@ -180,4 +181,12 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
access.removeBlockEntity(blockPosition);
}
}
@Override
public int getHeight(final HeightMap heightMap, final int x, final int z) {
Preconditions.checkArgument(heightMap != null, "HeightMap cannot be null");
Preconditions.checkArgument(x >= 0 && x <= 15 && z >= 0 && z <= 15, "Cannot get height outside of a chunks bounds, must be between 0 and 15, got x: %s, z: %s", x, z);
return getHandle().getHeight(CraftHeightMap.toNMS(heightMap), x, z);
}
}

View File

@@ -8,6 +8,7 @@ import net.minecraft.core.Registry;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.bukkit.HeightMap;
import org.bukkit.Material;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
@@ -200,4 +201,9 @@ public final class OldCraftChunkData implements ChunkGenerator.ChunkData {
Set<BlockPos> getLights() {
return this.lights;
}
@Override
public int getHeight(HeightMap heightMap, final int x, final int z) {
throw new UnsupportedOperationException("Unsupported, in older chunk generator api");
}
}

View File

@@ -4,7 +4,6 @@ import com.google.common.base.Suppliers;
import java.util.function.Supplier;
import net.minecraft.core.registries.Registries;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.generator.structure.Structure;
@@ -13,7 +12,7 @@ import org.bukkit.generator.structure.StructureType;
public class CraftStructure extends Structure implements Handleable<net.minecraft.world.level.levelgen.structure.Structure> {
public static Structure minecraftToBukkit(net.minecraft.world.level.levelgen.structure.Structure minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.STRUCTURE, Registry.STRUCTURE);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.STRUCTURE);
}
public static net.minecraft.world.level.levelgen.structure.Structure bukkitToMinecraft(Structure bukkit) {

View File

@@ -2,7 +2,6 @@ package org.bukkit.craftbukkit.generator.structure;
import net.minecraft.core.registries.Registries;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.generator.structure.StructureType;
@@ -10,7 +9,7 @@ import org.bukkit.generator.structure.StructureType;
public class CraftStructureType extends StructureType implements Handleable<net.minecraft.world.level.levelgen.structure.StructureType<?>> {
public static StructureType minecraftToBukkit(net.minecraft.world.level.levelgen.structure.StructureType<?> minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.STRUCTURE_TYPE, Registry.STRUCTURE_TYPE);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.STRUCTURE_TYPE);
}
public static net.minecraft.world.level.levelgen.structure.StructureType<?> bukkitToMinecraft(StructureType bukkit) {

View File

@@ -208,6 +208,11 @@ public abstract class CraftAbstractInventoryView implements InventoryView {
return type;
}
@Override
public void open() {
getPlayer().openInventory(this);
}
@Override
public void close() {
this.getPlayer().closeInventory();

View File

@@ -132,7 +132,7 @@ public class CraftContainer extends AbstractContainerMenu {
if (menu == null) {
return net.minecraft.world.inventory.MenuType.GENERIC_9x3;
} else {
return ((CraftMenuType<?>) menu).getHandle();
return ((CraftMenuType<?, ?>) menu).getHandle();
}
}
}

View File

@@ -153,7 +153,9 @@ public final class CraftItemFactory implements ItemFactory {
@Override
public ItemStack createItemStack(String input) throws IllegalArgumentException {
try {
ItemParser.ItemResult arg = new ItemParser(MinecraftServer.getDefaultRegistryAccess()).parse(new StringReader(input));
StringReader reader = new StringReader(input);
ItemParser.ItemResult arg = new ItemParser(MinecraftServer.getDefaultRegistryAccess()).parse(reader);
Preconditions.checkArgument(!reader.canRead(), "Trailing input found when parsing ItemStack: %s", input);
Item item = arg.item().value();
net.minecraft.world.item.ItemStack nmsItemStack = new net.minecraft.world.item.ItemStack(item);
@@ -349,7 +351,6 @@ public final class CraftItemFactory implements ItemFactory {
) {
Preconditions.checkArgument(itemStack != null, "Argument 'itemStack' must not be null");
Preconditions.checkArgument(!itemStack.isEmpty(), "Argument 'itemStack' cannot be empty");
Preconditions.checkArgument(levels > 0 && levels <= 30, "Argument 'levels' must be in range [1, 30] (attempted " + levels + ")");
Preconditions.checkArgument(random != null, "Argument 'random' must not be null");
final net.minecraft.world.item.ItemStack internalStack = CraftItemStack.asNMSCopy(itemStack);
if (internalStack.isEnchanted()) {

View File

@@ -2,10 +2,12 @@ package org.bukkit.craftbukkit.inventory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import io.papermc.paper.adventure.PaperAdventure;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import io.papermc.paper.adventure.PaperAdventure;
import java.util.function.Consumer;
import java.util.function.Predicate;
import net.kyori.adventure.text.Component;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.advancements.critereon.MinMaxBounds;
@@ -14,19 +16,24 @@ import net.minecraft.core.HolderSet;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponentPredicate;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.component.PatchedDataComponentMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.MaterialData;
import org.bukkit.persistence.PersistentDataContainer;
import org.jetbrains.annotations.NotNull;
@DelegateDeserialization(ItemStack.class)
@@ -49,7 +56,7 @@ public final class CraftItemStack extends ItemStack {
if (bukkit instanceof final CraftItemStack craftItemStack) {
return craftItemStack;
} else {
return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
}
}
@@ -66,12 +73,12 @@ public final class CraftItemStack extends ItemStack {
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false;
if (!(obj instanceof final ItemStack bukkit)) return false;
final CraftItemStack craftStack = getCraftStack(bukkit);
if (this.handle == craftStack.handle) return true;
else if (this.handle == null || craftStack.handle == null) return false;
else if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
else return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
if (this.handle == null || craftStack.handle == null) return false;
if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
}
// Paper end
@@ -159,7 +166,6 @@ public final class CraftItemStack extends ItemStack {
}
public net.minecraft.world.item.ItemStack handle;
private boolean isForInventoryDrop;
/**
* Mirror
@@ -522,7 +528,7 @@ public final class CraftItemStack extends ItemStack {
}
// Paper end
// Paper start - pdc
public static final String PDC_CUSTOM_DATA_KEY = "PublicBukkitValues";
private net.minecraft.nbt.CompoundTag getPdcTag() {
if (this.handle == null) {
return new net.minecraft.nbt.CompoundTag();
@@ -530,7 +536,7 @@ public final class CraftItemStack extends ItemStack {
final net.minecraft.world.item.component.CustomData customData = this.handle.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY);
// getUnsafe is OK here because we are only ever *reading* the data so immutability is preserved
//noinspection deprecation
return customData.getUnsafe().getCompound("PublicBukkitValues");
return customData.getUnsafe().getCompound(PDC_CUSTOM_DATA_KEY);
}
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
@@ -550,7 +556,30 @@ public final class CraftItemStack extends ItemStack {
public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
return this.pdcView;
}
// Paper end - pdc
@Override
public boolean editPersistentDataContainer(final Consumer<PersistentDataContainer> consumer) {
if (this.handle == null || this.handle.isEmpty()) return false;
final CraftPersistentDataContainer container = new CraftPersistentDataContainer(REGISTRY);
CustomData customData = this.handle.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
//noinspection deprecation // we copy only the pdc tag
final CompoundTag pdcTag = customData.getUnsafe().getCompound(PDC_CUSTOM_DATA_KEY).copy();
container.putAll(pdcTag);
consumer.accept(container);
final CompoundTag newPdcTag = container.toTagCompound();
if (!newPdcTag.isEmpty()) {
customData = customData.update(tag -> tag.put(PDC_CUSTOM_DATA_KEY, newPdcTag));
} else if (newPdcTag.isEmpty() && customData.contains(PDC_CUSTOM_DATA_KEY)) {
customData = customData.update(tag -> tag.remove(PDC_CUSTOM_DATA_KEY));
}
// mirror CraftMetaItem behavior of clearing component if it's empty.
this.handle.set(DataComponents.CUSTOM_DATA, customData.isEmpty() ? null : customData);
return true;
}
// Paper start - data component API
@Override
public <T> T getData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type) {
@@ -621,14 +650,32 @@ public final class CraftItemStack extends ItemStack {
this.handle.set(nms, nmsValue);
}
@Override
public void copyDataFrom(final ItemStack source, final Predicate<io.papermc.paper.datacomponent.DataComponentType> filter) {
Preconditions.checkArgument(source != null, "source cannot be null");
Preconditions.checkArgument(filter != null, "filter cannot be null");
if (this.isEmpty() || source.isEmpty()) {
return;
}
final Predicate<DataComponentType<?>> nmsFilter = nms -> filter.test(io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(nms));
net.minecraft.world.item.ItemStack sourceNmsStack = getCraftStack(source).handle;
this.handle.applyComponents(sourceNmsStack.getPrototype().filter(nmsType -> {
return !sourceNmsStack.hasNonDefault(nmsType) && nmsFilter.test(nmsType);
}));
final DataComponentPatch.SplitResult split = sourceNmsStack.getComponentsPatch().split();
this.handle.applyComponents(split.added().filter(nmsFilter));
split.removed().stream().filter(nmsFilter).forEach(this.handle::remove);
}
@Override
public boolean isDataOverridden(final io.papermc.paper.datacomponent.DataComponentType type) {
if (this.isEmpty()) {
return false;
}
final net.minecraft.core.component.DataComponentType<?> nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type);
// maybe a more efficient way is to expose the "patch" map in PatchedDataComponentMap and just check if the type exists as a key
return !java.util.Objects.equals(this.handle.get(nms), this.handle.getPrototype().get(nms));
return this.handle.hasNonDefault(nms);
}
@Override

View File

@@ -51,7 +51,7 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
}
public static ItemType minecraftToBukkitNew(Item minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ITEM, Registry.ITEM);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ITEM);
}
public static Item bukkitToMinecraftNew(ItemType bukkit) {

View File

@@ -8,21 +8,21 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.util.CraftMenus;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.MenuType;
import org.bukkit.inventory.view.builder.InventoryViewBuilder;
import org.jetbrains.annotations.NotNull;
public class CraftMenuType<V extends InventoryView> implements MenuType.Typed<V>, Handleable<net.minecraft.world.inventory.MenuType<?>>, io.papermc.paper.world.flag.PaperFeatureDependent { // Paper - make FeatureDependant
public class CraftMenuType<V extends InventoryView, B extends InventoryViewBuilder<V>> implements MenuType.Typed<V, B>, Handleable<net.minecraft.world.inventory.MenuType<?>>, io.papermc.paper.world.flag.PaperFeatureDependent { // Paper - make FeatureDependant
private final NamespacedKey key;
private final net.minecraft.world.inventory.MenuType<?> handle;
private final Supplier<CraftMenus.MenuTypeData<V>> typeData;
private final Supplier<CraftMenus.MenuTypeData<V, B>> typeData;
public CraftMenuType(NamespacedKey key, net.minecraft.world.inventory.MenuType<?> handle) {
this.key = key;
@@ -38,33 +38,28 @@ public class CraftMenuType<V extends InventoryView> implements MenuType.Typed<V>
@Override
public V create(final HumanEntity player, final String title) {
// Paper start - adventure
return create(player, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(title));
return builder().title(title != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(title) : null).build(player);
}
@Override
public V create(final HumanEntity player, final net.kyori.adventure.text.Component title) {
// Paper end - adventure
Preconditions.checkArgument(player != null, "The given player must not be null");
Preconditions.checkArgument(title != null, "The given title must not be null");
Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity");
final CraftHumanEntity craftHuman = (CraftHumanEntity) player;
Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an EntityPlayer");
final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle();
final AbstractContainerMenu container = this.typeData.get().menuBuilder().build(serverPlayer, this.handle);
container.setTitle(io.papermc.paper.adventure.PaperAdventure.asVanilla(title)); // Paper - adventure
container.checkReachable = false;
return (V) container.getBukkitView();
return builder().title(title).build(player);
}
@Override
public Typed<InventoryView> typed() {
public B builder() {
return typeData.get().viewBuilder().get();
}
@Override
public Typed<InventoryView, InventoryViewBuilder<InventoryView>> typed() {
return this.typed(InventoryView.class);
}
@Override
public <V extends InventoryView> Typed<V> typed(Class<V> clazz) {
public <V extends InventoryView, B extends InventoryViewBuilder<V>> Typed<V, B> typed(Class<V> clazz) {
if (clazz.isAssignableFrom(this.typeData.get().viewClass())) {
return (Typed<V>) this;
return (Typed<V, B>) this;
}
throw new IllegalArgumentException("Cannot type InventoryView " + this.key.toString() + " to InventoryView type " + clazz.getSimpleName());
@@ -85,7 +80,7 @@ public class CraftMenuType<V extends InventoryView> implements MenuType.Typed<V>
}
public static MenuType minecraftToBukkit(net.minecraft.world.inventory.MenuType<?> minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.MENU, Registry.MENU);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.MENU);
}
public static MenuType minecraftHolderToBukkit(Holder<net.minecraft.world.inventory.MenuType<?>> minecraft) {

View File

@@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.trading.Merchant;
@@ -25,6 +26,11 @@ public class CraftMerchantCustom implements CraftMerchant {
this.merchant = new MinecraftMerchant(title);
getMerchant().craftMerchant = this;
}
public CraftMerchantCustom() {
this.merchant = new MinecraftMerchant();
getMerchant().craftMerchant = this;
}
// Paper end
@Override
@@ -54,6 +60,10 @@ public class CraftMerchantCustom implements CraftMerchant {
Preconditions.checkArgument(title != null, "Title cannot be null");
this.title = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
}
public MinecraftMerchant() {
this.title = EntityType.VILLAGER.getDescription();
}
// Paper end
@Override

View File

@@ -3,6 +3,7 @@ package org.bukkit.craftbukkit.inventory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.papermc.paper.registry.RegistryKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -42,7 +43,7 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta {
for (int i = 0; i < Math.min(patterns.size(), 20); i++) {
BannerPatternLayers.Layer p = patterns.get(i);
DyeColor color = DyeColor.getByWoolData((byte) p.color().getId());
PatternType pattern = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.BANNER_PATTERN, p.pattern()).orElse(null); // Paper - fix upstream not handling inlined banner pattern
PatternType pattern = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(RegistryKey.BANNER_PATTERN, p.pattern()).orElse(null); // Paper - fix upstream not handling inlined banner pattern
if (color != null && pattern != null) {
this.patterns.add(new Pattern(color, pattern));

View File

@@ -273,7 +273,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
static final ItemMetaKeyType<Integer> MAX_DAMAGE = new ItemMetaKeyType<>(DataComponents.MAX_DAMAGE, "max-damage");
@Specific(Specific.To.NBT)
static final ItemMetaKeyType<BlockItemStateProperties> BLOCK_DATA = new ItemMetaKeyType<>(DataComponents.BLOCK_STATE, "BlockStateTag");
static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey("PublicBukkitValues");
static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey(CraftItemStack.PDC_CUSTOM_DATA_KEY);
@Specific(Specific.To.NBT)
static final ItemMetaKeyType<Unit> HIDE_ADDITIONAL_TOOLTIP = new ItemMetaKeyType(DataComponents.HIDE_ADDITIONAL_TOOLTIP);
@Specific(Specific.To.NBT)
@@ -1250,8 +1250,18 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
@Override
public void lore(final List<? extends net.kyori.adventure.text.Component> lore) {
Preconditions.checkArgument(lore == null || lore.size() <= ItemLore.MAX_LINES, "lore cannot have more than %s lines", ItemLore.MAX_LINES); // Paper - limit lore lines
this.lore = lore != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(lore) : null;
if (lore == null) {
this.lore = null;
return;
}
Preconditions.checkArgument(lore.size() <= ItemLore.MAX_LINES, "lore cannot have more than %s lines", ItemLore.MAX_LINES); // Paper - limit lore lines
for (int i = 0; i < lore.size(); i++) {
Preconditions.checkArgument(lore.get(i) != null, "lore contains null entry at index: %s", i);
}
this.lore = io.papermc.paper.adventure.PaperAdventure.asVanilla(lore);
}
// Paper end

View File

@@ -1,6 +1,5 @@
package org.bukkit.craftbukkit.inventory.components;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -57,7 +56,7 @@ public final class CraftCustomModelDataComponent implements CustomModelDataCompo
@Override
public void setFloats(List<Float> floats) {
this.handle = new CustomModelData(new ArrayList<>(floats), this.handle.flags(), this.handle.strings(), this.handle.colors());
this.handle = new CustomModelData(List.copyOf(floats), this.handle.flags(), this.handle.strings(), this.handle.colors());
}
@Override
@@ -67,7 +66,7 @@ public final class CraftCustomModelDataComponent implements CustomModelDataCompo
@Override
public void setFlags(List<Boolean> flags) {
this.handle = new CustomModelData(this.handle.floats(), List.copyOf(flags), this.handle.strings(), this.handle.colors()); // Paper
this.handle = new CustomModelData(this.handle.floats(), List.copyOf(flags), this.handle.strings(), this.handle.colors());
}
@Override
@@ -77,17 +76,17 @@ public final class CraftCustomModelDataComponent implements CustomModelDataCompo
@Override
public void setStrings(List<String> strings) {
this.handle = new CustomModelData(this.handle.floats(), this.handle.flags(), List.copyOf(strings), this.handle.colors()); // Paper
this.handle = new CustomModelData(this.handle.floats(), this.handle.flags(), List.copyOf(strings), this.handle.colors());
}
@Override
public List<Color> getColors() {
return this.getHandle().colors().stream().map(Color::fromRGB).toList();
return this.getHandle().colors().stream().map(color -> Color.fromRGB(color & 0x00FFFFFF)).toList(); // skip alpha channel
}
@Override
public void setColors(List<Color> colors) {
this.handle = new CustomModelData(this.handle.floats(), this.handle.flags(), this.handle.strings(), colors.stream().map(Color::asRGB).toList()); // Paper
this.handle = new CustomModelData(this.handle.floats(), this.handle.flags(), this.handle.strings(), colors.stream().map(Color::asRGB).toList());
}
@Override
@@ -95,14 +94,11 @@ public final class CraftCustomModelDataComponent implements CustomModelDataCompo
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
final CraftCustomModelDataComponent other = (CraftCustomModelDataComponent) obj;
return Objects.equals(this.handle, other.handle);
return this.handle.equals(other.handle);
}
@Override
@@ -114,6 +110,6 @@ public final class CraftCustomModelDataComponent implements CustomModelDataCompo
@Override
public String toString() {
return "CraftCustomModelDataComponent{" + "handle=" + this.handle + '}';
return "CraftCustomModelDataComponent{component=" + this.handle + '}';
}
}

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit.inventory.trim;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.util.Holderable;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
@@ -8,18 +9,17 @@ import net.minecraft.network.chat.contents.TranslatableContents;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.jetbrains.annotations.NotNull;
public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Holderable<net.minecraft.world.item.equipment.trim.TrimMaterial> { // Paper - switch to Holder
public static TrimMaterial minecraftToBukkit(net.minecraft.world.item.equipment.trim.TrimMaterial minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.TRIM_MATERIAL, Registry.TRIM_MATERIAL);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.TRIM_MATERIAL);
}
public static TrimMaterial minecraftHolderToBukkit(Holder<net.minecraft.world.item.equipment.trim.TrimMaterial> minecraft) {
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registry.TRIM_MATERIAL); // Paper - switch to Holder
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.TRIM_MATERIAL); // Paper - switch to Holder
}
public static net.minecraft.world.item.equipment.trim.TrimMaterial bukkitToMinecraft(TrimMaterial bukkit) {
@@ -36,13 +36,13 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho
public static Object bukkitToObject(TrimMaterial bukkit) {
Preconditions.checkArgument(bukkit != null);
return ((CraftTrimMaterial) bukkit).toBukkitSerializationObject(net.minecraft.world.item.equipment.trim.TrimMaterial.CODEC); // Paper - switch to Holder
return ((CraftTrimMaterial) bukkit).toBukkitSerializationObject(net.minecraft.world.item.equipment.trim.TrimMaterial.DIRECT_CODEC); // Paper - switch to Holder
}
public static TrimMaterial objectToBukkit(Object object) {
Preconditions.checkArgument(object != null);
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(object, net.minecraft.world.item.equipment.trim.TrimMaterial.CODEC, Registry.TRIM_MATERIAL); // Paper - switch to Holder
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(object, net.minecraft.world.item.equipment.trim.TrimMaterial.CODEC, RegistryKey.TRIM_MATERIAL); // Paper - switch to Holder
}
@Override

View File

@@ -1,6 +1,7 @@
package org.bukkit.craftbukkit.inventory.trim;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.util.Holderable;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
@@ -8,18 +9,17 @@ import net.minecraft.network.chat.contents.TranslatableContents;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.jetbrains.annotations.NotNull;
public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Holderable<net.minecraft.world.item.equipment.trim.TrimPattern> { // Paper - switch to Holder
public static TrimPattern minecraftToBukkit(net.minecraft.world.item.equipment.trim.TrimPattern minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.TRIM_PATTERN, Registry.TRIM_PATTERN);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.TRIM_PATTERN);
}
public static TrimPattern minecraftHolderToBukkit(Holder<net.minecraft.world.item.equipment.trim.TrimPattern> minecraft) {
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registry.TRIM_PATTERN); // Paper - switch to Holder
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.TRIM_PATTERN); // Paper - switch to Holder
}
public static net.minecraft.world.item.equipment.trim.TrimPattern bukkitToMinecraft(TrimPattern bukkit) {
@@ -36,13 +36,13 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold
public static Object bukkitToObject(TrimPattern bukkit) {
Preconditions.checkArgument(bukkit != null);
return ((CraftTrimPattern) bukkit).toBukkitSerializationObject(net.minecraft.world.item.equipment.trim.TrimPattern.CODEC); // Paper - switch to Holder
return ((CraftTrimPattern) bukkit).toBukkitSerializationObject(net.minecraft.world.item.equipment.trim.TrimPattern.DIRECT_CODEC); // Paper - switch to Holder
}
public static TrimPattern objectToBukkit(Object object) {
Preconditions.checkArgument(object != null);
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(object, net.minecraft.world.item.equipment.trim.TrimPattern.CODEC, Registry.TRIM_PATTERN); // Paper - switch to Holder
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(object, net.minecraft.world.item.equipment.trim.TrimPattern.CODEC, RegistryKey.TRIM_PATTERN); // Paper - switch to Holder
}
@Override

View File

@@ -1,27 +1,38 @@
package org.bukkit.craftbukkit.inventory.util;
import static org.bukkit.craftbukkit.inventory.util.CraftMenuBuilder.*;
import net.minecraft.network.chat.Component;
import net.minecraft.world.SimpleMenuProvider;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.inventory.AnvilMenu;
import net.minecraft.world.inventory.CartographyTableMenu;
import net.minecraft.world.inventory.CraftingMenu;
import net.minecraft.world.inventory.EnchantmentMenu;
import net.minecraft.world.inventory.GrindstoneMenu;
import net.minecraft.world.inventory.MerchantMenu;
import net.minecraft.world.inventory.SmithingMenu;
import net.minecraft.world.inventory.StonecutterMenu;
import net.minecraft.world.item.trading.Merchant;
import net.minecraft.world.item.trading.MerchantOffers;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BeaconBlockEntity;
import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity;
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.entity.CrafterBlockEntity;
import net.minecraft.world.level.block.entity.DispenserBlockEntity;
import net.minecraft.world.level.block.entity.FurnaceBlockEntity;
import net.minecraft.world.level.block.entity.HopperBlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import net.minecraft.world.level.block.entity.SmokerBlockEntity;
import org.bukkit.craftbukkit.inventory.CraftMenuType;
import org.bukkit.craftbukkit.inventory.CraftMerchant;
import org.bukkit.craftbukkit.inventory.view.builder.CraftAccessLocationInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftBlockEntityInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftDoubleChestInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftEnchantmentInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftMerchantInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftStandardInventoryViewBuilder;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.MenuType;
import org.bukkit.inventory.view.AnvilView;
@@ -34,83 +45,120 @@ import org.bukkit.inventory.view.LecternView;
import org.bukkit.inventory.view.LoomView;
import org.bukkit.inventory.view.MerchantView;
import org.bukkit.inventory.view.StonecutterView;
import org.bukkit.inventory.view.builder.InventoryViewBuilder;
import org.jspecify.annotations.NullMarked;
import java.util.function.Supplier;
@NullMarked
public final class CraftMenus {
public record MenuTypeData<V extends InventoryView>(Class<V> viewClass, CraftMenuBuilder menuBuilder) {
public record MenuTypeData<V extends InventoryView, B extends InventoryViewBuilder<V>>(Class<V> viewClass, Supplier<B> viewBuilder) {
}
private static final CraftMenuBuilder STANDARD = (player, menuType) -> menuType.create(player.nextContainerCounter(), player.getInventory());
// This is a temporary measure that will likely be removed with the rewrite of HumanEntity#open[] methods
public static void openMerchantMenu(final ServerPlayer player, final MerchantMenu merchant) {
final Merchant minecraftMerchant = ((CraftMerchant) merchant.getBukkitView().getMerchant()).getMerchant();
int level = 1;
if (minecraftMerchant instanceof final Villager villager) {
level = villager.getVillagerData().getLevel();
}
public static <V extends InventoryView> MenuTypeData<V> getMenuTypeData(CraftMenuType<?> menuType) {
if (minecraftMerchant.getTradingPlayer() != null) { // merchant's can only have one trader
minecraftMerchant.getTradingPlayer().closeContainer();
}
minecraftMerchant.setTradingPlayer(player);
player.connection.send(new ClientboundOpenScreenPacket(merchant.containerId, net.minecraft.world.inventory.MenuType.MERCHANT, merchant.getTitle()));
player.containerMenu = merchant;
player.initMenu(merchant);
// Copy IMerchant#openTradingScreen
MerchantOffers merchantrecipelist = minecraftMerchant.getOffers();
if (!merchantrecipelist.isEmpty()) {
player.sendMerchantOffers(merchant.containerId, merchantrecipelist, level, minecraftMerchant.getVillagerXp(), minecraftMerchant.showProgressBar(), minecraftMerchant.canRestock());
}
// End Copy IMerchant#openTradingScreen
}
public static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuTypeData<V, B> getMenuTypeData(final CraftMenuType<?, ?> menuType) {
final net.minecraft.world.inventory.MenuType<?> handle = menuType.getHandle();
// this sucks horribly but it should work for now
if (menuType == MenuType.GENERIC_9X6) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftDoubleChestInventoryViewBuilder<>(handle)));
}
if (menuType == MenuType.GENERIC_9X3) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, ChestBlockEntity::new, false)));
}
// this isn't ideal as both dispenser and dropper are 3x3, InventoryType can't currently handle generic 3x3s with size 9
// this needs to be removed when inventory creation is overhauled
if (menuType == MenuType.GENERIC_3X3) {
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, tileEntity(DispenserBlockEntity::new, Blocks.DISPENSER)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.DISPENSER, DispenserBlockEntity::new)));
}
if (menuType == MenuType.CRAFTER_3X3) {
return CraftMenus.asType(new MenuTypeData<>(CrafterView.class, tileEntity(CrafterBlockEntity::new, Blocks.CRAFTER)));
return asType(new MenuTypeData<>(CrafterView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CRAFTER, CrafterBlockEntity::new)));
}
if (menuType == MenuType.ANVIL) {
return CraftMenus.asType(new MenuTypeData<>(AnvilView.class, worldAccess(AnvilMenu::new)));
return asType(new MenuTypeData<>(AnvilView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.ANVIL)));
}
if (menuType == MenuType.BEACON) {
return CraftMenus.asType(new MenuTypeData<>(BeaconView.class, tileEntity(BeaconBlockEntity::new, Blocks.BEACON)));
return asType(new MenuTypeData<>(BeaconView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BEACON, BeaconBlockEntity::new)));
}
if (menuType == MenuType.BLAST_FURNACE) {
return CraftMenus.asType(new MenuTypeData<>(FurnaceView.class, tileEntity(BlastFurnaceBlockEntity::new, Blocks.BLAST_FURNACE)));
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BLAST_FURNACE, BlastFurnaceBlockEntity::new)));
}
if (menuType == MenuType.BREWING_STAND) {
return CraftMenus.asType(new MenuTypeData<>(BrewingStandView.class, tileEntity(BrewingStandBlockEntity::new, Blocks.BREWING_STAND)));
return asType(new MenuTypeData<>(BrewingStandView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BREWING_STAND, BrewingStandBlockEntity::new)));
}
if (menuType == MenuType.CRAFTING) {
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, worldAccess(CraftingMenu::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.CRAFTING_TABLE)));
}
if (menuType == MenuType.ENCHANTMENT) {
return CraftMenus.asType(new MenuTypeData<>(EnchantmentView.class, (player, type) -> {
return new SimpleMenuProvider((syncId, inventory, human) -> {
return worldAccess(EnchantmentMenu::new).build(player, type);
}, Component.empty()).createMenu(player.nextContainerCounter(), player.getInventory(), player);
}));
return asType(new MenuTypeData<>(EnchantmentView.class, () -> new CraftEnchantmentInventoryViewBuilder(handle)));
}
if (menuType == MenuType.FURNACE) {
return CraftMenus.asType(new MenuTypeData<>(FurnaceView.class, tileEntity(FurnaceBlockEntity::new, Blocks.FURNACE)));
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.FURNACE, FurnaceBlockEntity::new)));
}
if (menuType == MenuType.GRINDSTONE) {
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, worldAccess(GrindstoneMenu::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.GRINDSTONE)));
}
// We really don't need to be creating a tile entity for hopper but currently InventoryType doesn't have capacity
// to understand otherwise
if (menuType == MenuType.HOPPER) {
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, tileEntity(HopperBlockEntity::new, Blocks.HOPPER)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.HOPPER, HopperBlockEntity::new)));
}
// We also don't need to create a tile entity for lectern, but again InventoryType isn't smart enough to know any better
if (menuType == MenuType.LECTERN) {
return CraftMenus.asType(new MenuTypeData<>(LecternView.class, tileEntity(LecternBlockEntity::new, Blocks.LECTERN)));
return asType(new MenuTypeData<>(LecternView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.LECTERN, LecternBlockEntity::new)));
}
if (menuType == MenuType.LOOM) {
return CraftMenus.asType(new MenuTypeData<>(LoomView.class, CraftMenus.STANDARD));
return asType(new MenuTypeData<>(LoomView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.LOOM)));
}
if (menuType == MenuType.MERCHANT) {
return CraftMenus.asType(new MenuTypeData<>(MerchantView.class, CraftMenus.STANDARD));
return asType(new MenuTypeData<>(MerchantView.class, () -> new CraftMerchantInventoryViewBuilder<>(handle)));
}
if (menuType == MenuType.SHULKER_BOX) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SHULKER_BOX, ShulkerBoxBlockEntity::new)));
}
if (menuType == MenuType.SMITHING) {
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, worldAccess(SmithingMenu::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.SMITHING_TABLE)));
}
if (menuType == MenuType.SMOKER) {
return CraftMenus.asType(new MenuTypeData<>(FurnaceView.class, tileEntity(SmokerBlockEntity::new, Blocks.SMOKER)));
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SMOKER, SmokerBlockEntity::new)));
}
if (menuType == MenuType.CARTOGRAPHY_TABLE) {
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, worldAccess(CartographyTableMenu::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.CARTOGRAPHY_TABLE)));
}
if (menuType == MenuType.STONECUTTER) {
return CraftMenus.asType(new MenuTypeData<>(StonecutterView.class, worldAccess(StonecutterMenu::new)));
return asType(new MenuTypeData<>(StonecutterView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.STONECUTTER)));
}
return CraftMenus.asType(new MenuTypeData<>(InventoryView.class, CraftMenus.STANDARD));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle)));
}
private static <V extends InventoryView> MenuTypeData<V> asType(MenuTypeData<?> data) {
return (MenuTypeData<V>) data;
@SuppressWarnings("unchecked")
private static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuTypeData<V, B> asType(final MenuTypeData<?, ?> data) {
return (MenuTypeData<V, B>) data;
}
}

View File

@@ -0,0 +1,49 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import com.google.common.base.Preconditions;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.InventoryViewBuilder;
import org.jspecify.annotations.Nullable;
public abstract class CraftAbstractInventoryViewBuilder<V extends InventoryView> implements InventoryViewBuilder<V> {
protected final MenuType<?> handle;
protected boolean checkReachable = false;
protected @Nullable Component title = null;
protected net.minecraft.network.chat.Component defaultTitle = null;
public CraftAbstractInventoryViewBuilder(final MenuType<?> handle) {
this.handle = handle;
}
@Override
public InventoryViewBuilder<V> title(final @Nullable Component title) {
this.title = title;
return this;
}
@SuppressWarnings("unchecked")
@Override
public V build(final HumanEntity player) {
Preconditions.checkArgument(player != null, "The given player must not be null");
Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity");
final CraftHumanEntity craftHuman = (CraftHumanEntity) player;
Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an ServerPlayer");
final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle();
final AbstractContainerMenu container = buildContainer(serverPlayer);
container.checkReachable = this.checkReachable;
container.setTitle(this.title != null ? PaperAdventure.asVanilla(this.title) : this.defaultTitle);
return (V) container.getBukkitView();
}
protected abstract AbstractContainerMenu buildContainer(ServerPlayer player);
}

View File

@@ -0,0 +1,48 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.minecraft.core.BlockPos;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
import org.jspecify.annotations.Nullable;
public abstract class CraftAbstractLocationInventoryViewBuilder<V extends InventoryView> extends CraftAbstractInventoryViewBuilder<V> implements LocationInventoryViewBuilder<V> {
protected @Nullable Level world;
protected @Nullable BlockPos position;
public CraftAbstractLocationInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
}
@Override
public LocationInventoryViewBuilder<V> title(final @Nullable Component title) {
return (LocationInventoryViewBuilder<V>) super.title(title);
}
@Override
public LocationInventoryViewBuilder<V> copy() {
throw new UnsupportedOperationException("copy is not implemented on CraftAbstractLocationInventoryViewBuilder");
}
@Override
public LocationInventoryViewBuilder<V> checkReachable(final boolean checkReachable) {
super.checkReachable = checkReachable;
return this;
}
@Override
public LocationInventoryViewBuilder<V> location(final Location location) {
Preconditions.checkArgument(location != null, "The provided location must not be null");
Preconditions.checkArgument(location.getWorld() != null, "The provided location must be associated with a world");
this.world = ((CraftWorld) location.getWorld()).getHandle();
this.position = CraftLocation.toBlockPosition(location);
return this;
}
}

View File

@@ -0,0 +1,58 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
public class CraftAccessLocationInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {
private final Block block;
public CraftAccessLocationInventoryViewBuilder(final MenuType<?> handle, final Block block) {
super(handle);
this.block = block;
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
final BlockState effectiveBlockState;
final BlockPos effectiveBlockPos;
final Level effectiveLevel;
if (super.position != null) {
effectiveBlockPos = super.position;
effectiveLevel = super.world;
effectiveBlockState = super.world.getBlockState(position);
} else {
effectiveBlockPos = player.blockPosition();
effectiveLevel = player.level();
effectiveBlockState = block.defaultBlockState();
}
final MenuProvider provider = block.getMenuProvider(effectiveBlockState, effectiveLevel, effectiveBlockPos);
super.defaultTitle = provider.getDisplayName();
return provider.createMenu(player.nextContainerCounter(), player.getInventory(), player);
}
@Override
public LocationInventoryViewBuilder<V> copy() {
final CraftAccessLocationInventoryViewBuilder<V> copy = new CraftAccessLocationInventoryViewBuilder<>(this.handle, this.block);
copy.world = super.world;
copy.position = super.position;
copy.checkReachable = super.checkReachable;
copy.title = title;
return copy;
}
public interface CraftAccessContainerObjectBuilder {
AbstractContainerMenu build(final int syncId, final Inventory inventory, ContainerLevelAccess access);
}
}

View File

@@ -0,0 +1,98 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuConstructor;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
import org.jspecify.annotations.Nullable;
public class CraftBlockEntityInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {
private final Block block;
private final boolean useFakeBlockEntity;
private final @Nullable CraftBlockInventoryBuilder builder;
public CraftBlockEntityInventoryViewBuilder(
final MenuType<?> handle,
final Block block,
final @Nullable CraftBlockInventoryBuilder builder
) {
this(handle, block, builder, true);
}
public CraftBlockEntityInventoryViewBuilder(
final MenuType<?> handle,
final Block block,
final @Nullable CraftBlockInventoryBuilder builder,
final boolean useFakeBlockEntity
) {
super(handle);
this.useFakeBlockEntity = useFakeBlockEntity;
this.block = block;
this.builder = builder;
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
if (this.world == null) {
this.world = player.level();
}
if (this.position == null) {
this.position = player.blockPosition();
return buildFakeBlockEntity(player);
}
final BlockEntity entity = this.world.getBlockEntity(position);
if (!(entity instanceof final MenuConstructor container)) {
return buildFakeBlockEntity(player);
}
final AbstractContainerMenu atBlock = container.createMenu(player.nextContainerCounter(), player.getInventory(), player);
if (atBlock.getType() != super.handle) {
return buildFakeBlockEntity(player);
}
if (!(entity instanceof final MenuProvider provider)) {
throw new IllegalStateException("Provided blockEntity during MenuType creation can not find a default title! This is a bug!");
}
super.defaultTitle = provider.getDisplayName();
return atBlock;
}
private AbstractContainerMenu buildFakeBlockEntity(final ServerPlayer player) {
final MenuProvider inventory = this.builder.build(this.position, this.block.defaultBlockState());
if (inventory instanceof final BlockEntity blockEntity) {
blockEntity.setLevel(this.world);
super.defaultTitle = inventory.getDisplayName();
}
if (!this.useFakeBlockEntity) { // gets around open noise for chest
return handle.create(player.nextContainerCounter(), player.getInventory());
}
return inventory.createMenu(player.nextContainerCounter(), player.getInventory(), player);
}
@Override
public LocationInventoryViewBuilder<V> copy() {
final CraftBlockEntityInventoryViewBuilder<V> copy = new CraftBlockEntityInventoryViewBuilder<>(super.handle, this.block, this.builder, this.useFakeBlockEntity);
copy.world = this.world;
copy.position = this.position;
copy.checkReachable = super.checkReachable;
copy.title = title;
return copy;
}
public interface CraftBlockInventoryBuilder {
MenuProvider build(BlockPos blockPosition, BlockState blockData);
}
}

View File

@@ -0,0 +1,53 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.DoubleBlockCombiner;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
public class CraftDoubleChestInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {
public CraftDoubleChestInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
super.defaultTitle = Component.translatable("container.chestDouble");
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
if (super.world == null) {
return handle.create(player.nextContainerCounter(), player.getInventory());
}
final ChestBlock chest = (ChestBlock) Blocks.CHEST;
final DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> result = chest.combine(
super.world.getBlockState(super.position), super.world, super.position, false
);
if (result instanceof DoubleBlockCombiner.NeighborCombineResult.Single<? extends ChestBlockEntity>) {
return handle.create(player.nextContainerCounter(), player.getInventory());
}
final MenuProvider combined = result.apply(ChestBlock.MENU_PROVIDER_COMBINER).orElse(null);
if (combined == null) {
return handle.create(player.nextContainerCounter(), player.getInventory());
}
return combined.createMenu(player.nextContainerCounter(), player.getInventory(), player);
}
@Override
public LocationInventoryViewBuilder<V> copy() {
final CraftDoubleChestInventoryViewBuilder<V> copy = new CraftDoubleChestInventoryViewBuilder<>(super.handle);
copy.world = this.world;
copy.position = this.position;
copy.checkReachable = super.checkReachable;
copy.title = title;
return copy;
}
}

View File

@@ -0,0 +1,40 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.EnchantmentMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.EnchantingTableBlockEntity;
import org.bukkit.inventory.view.EnchantmentView;
public class CraftEnchantmentInventoryViewBuilder extends CraftAbstractLocationInventoryViewBuilder<EnchantmentView> {
public CraftEnchantmentInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
if (this.world == null) {
this.world = player.level();
}
if (this.position == null) {
this.position = player.blockPosition();
super.defaultTitle = new EnchantingTableBlockEntity(this.position, Blocks.ENCHANTING_TABLE.defaultBlockState()).getDisplayName();
return new EnchantmentMenu(player.nextContainerCounter(), player.getInventory(), ContainerLevelAccess.create(this.world, this.position));
}
final BlockEntity entity = this.world.getBlockEntity(position);
if (entity instanceof final EnchantingTableBlockEntity enchantingBlockEntity) {
super.defaultTitle = enchantingBlockEntity.getDisplayName();
} else {
super.defaultTitle = new EnchantingTableBlockEntity(this.position, Blocks.ENCHANTING_TABLE.defaultBlockState()).getDisplayName();
}
return new EnchantmentMenu(player.nextContainerCounter(), player.getInventory(), ContainerLevelAccess.create(this.world, this.position));
}
}

View File

@@ -0,0 +1,89 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import com.google.common.base.Preconditions;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.npc.AbstractVillager;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.MerchantMenu;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftMerchant;
import org.bukkit.craftbukkit.inventory.CraftMerchantCustom;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.Merchant;
import org.bukkit.inventory.view.builder.MerchantInventoryViewBuilder;
import org.jspecify.annotations.Nullable;
public class CraftMerchantInventoryViewBuilder<V extends InventoryView> extends CraftAbstractInventoryViewBuilder<V> implements MerchantInventoryViewBuilder<V> {
private net.minecraft.world.item.trading.@Nullable Merchant merchant;
public CraftMerchantInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
}
@Override
public MerchantInventoryViewBuilder<V> title(final @Nullable Component title) {
return (MerchantInventoryViewBuilder<V>) super.title(title);
}
@Override
public MerchantInventoryViewBuilder<V> merchant(final Merchant merchant) {
this.merchant = ((CraftMerchant) merchant).getMerchant();
return this;
}
@Override
public MerchantInventoryViewBuilder<V> checkReachable(final boolean checkReachable) {
super.checkReachable = checkReachable;
return this;
}
@Override
public V build(final HumanEntity player) {
Preconditions.checkArgument(player != null, "The given player must not be null");
Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity");
final CraftHumanEntity craftHuman = (CraftHumanEntity) player;
Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an ServerPlayer");
final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle();
final MerchantMenu container;
if (this.merchant == null) {
this.merchant = this.title == null ? new CraftMerchantCustom().getMerchant() : new CraftMerchantCustom(title).getMerchant();
}
container = new MerchantMenu(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), this.merchant);
container.checkReachable = super.checkReachable;
setDefaultTitle(this.merchant);
container.setTitle(super.title != null ? PaperAdventure.asVanilla(this.title) : super.defaultTitle);
return (V) container.getBukkitView();
}
private void setDefaultTitle(final net.minecraft.world.item.trading.Merchant merchant) {
if (merchant instanceof final AbstractVillager villager) {
super.defaultTitle = villager.getDisplayName();
} else if (merchant instanceof final CraftMerchantCustom.MinecraftMerchant custom) {
super.defaultTitle = custom.getScoreboardDisplayName();
} else {
throw new IllegalStateException("Provided merchant during MenuType creation can not find a default title! This is a bug!");
}
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
throw new UnsupportedOperationException("buildContainer is not supported for CraftMerchantInventoryViewBuilder");
}
@Override
public MerchantInventoryViewBuilder<V> copy() {
final CraftMerchantInventoryViewBuilder<V> copy = new CraftMerchantInventoryViewBuilder<>(super.handle);
copy.checkReachable = super.checkReachable;
copy.merchant = this.merchant;
copy.title = title;
return copy;
}
}

View File

@@ -0,0 +1,28 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.InventoryViewBuilder;
public class CraftStandardInventoryViewBuilder<V extends InventoryView> extends CraftAbstractInventoryViewBuilder<V> {
public CraftStandardInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
super.defaultTitle = Component.translatable("container.chest");
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
return super.handle.create(player.nextContainerCounter(), player.getInventory());
}
@Override
public InventoryViewBuilder<V> copy() {
final CraftStandardInventoryViewBuilder<V> copy = new CraftStandardInventoryViewBuilder<>(handle);
copy.title = this.title;
return copy;
}
}

View File

@@ -0,0 +1,4 @@
@NullMarked
package org.bukkit.craftbukkit.inventory.view.builder;
import org.jspecify.annotations.NullMarked;

View File

@@ -40,6 +40,7 @@ import org.bukkit.entity.Villager;
import org.bukkit.map.MapCursor;
import org.bukkit.util.OldEnum;
@Deprecated
@NotInBukkit
@RequireCompatibility("enum-compatibility-mode")
@RequirePluginVersion(maxInclusive = "1.20.6")

View File

@@ -3,6 +3,7 @@ package org.bukkit.craftbukkit.map;
import com.google.common.base.Preconditions;
import java.awt.Color;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapCursorCollection;
@@ -92,22 +93,33 @@ public class CraftMapCanvas implements MapCanvas {
@Override
public void drawImage(int x, int y, Image image) {
// Paper start - Reduce work done by limiting size of image and using System.arraycopy
int width = 128 - x;
int height = 128 - y;
if (image.getHeight(null) < height)
height = image.getHeight(null);
final int imageWidth = image.getWidth(null);
final int imageHeight = image.getHeight(null);
// The source x value *may* be negative, meaning we'd need to "offset" the source image before drawing it.
final int sourceX = Math.max(-x, 0);
final int sourceY = Math.max(-y, 0);
final int destX = Math.max(x, 0);
final int destY = Math.max(y, 0);
// The effective width/height to draw on the canvas.
final int effectiveWidth = Math.min(imageWidth - sourceX, 128 - destX);
final int effectiveHeight = Math.min(imageHeight - sourceY, 128 - destY);
if (effectiveWidth <= 0 || effectiveHeight <= 0)
return;
// Create a subimage if the image is larger than the max allowed size
java.awt.image.BufferedImage temp;
if (image.getWidth(null) >= width && image instanceof java.awt.image.BufferedImage bImage) {
BufferedImage temp;
if (imageWidth >= effectiveWidth && image instanceof BufferedImage bImage) {
// If the image is larger than the max allowed size, get a subimage, otherwise use the image as is
if (image.getWidth(null) > width || image.getHeight(null) > height) {
temp = bImage.getSubimage(0, 0, width, height);
if (imageWidth > effectiveWidth || imageHeight > effectiveHeight) {
temp = bImage.getSubimage(sourceX, sourceY, effectiveWidth, effectiveHeight);
} else {
temp = bImage;
}
} else {
temp = new java.awt.image.BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB);
temp = new BufferedImage(effectiveWidth, effectiveHeight, BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D graphics = temp.createGraphics();
graphics.drawImage(image, 0, 0, null);
graphics.dispose();
@@ -117,14 +129,20 @@ public class CraftMapCanvas implements MapCanvas {
// Since we now control the size of the image, we can safely use System.arraycopy
// If x is 0, we can just copy the entire image as width is 128 and height is <=(128-y)
if (x == 0) {
System.arraycopy(bytes, 0, this.buffer, y * 128, width * height);
return;
if (x == 0 && effectiveWidth == 128) { // This only works great if the width is 128, otherwise an empty area appears
System.arraycopy(bytes, 0, this.buffer, destY * effectiveWidth, effectiveWidth * effectiveHeight);
} else {
for (int yToCopy = 0; yToCopy < effectiveHeight; ++yToCopy) {
final int src = yToCopy * effectiveWidth;
final int dest = (destY + yToCopy) * 128 + destX;
System.arraycopy(bytes, src, this.buffer, dest, effectiveWidth);
}
}
for (int y2 = 0; y2 < height; ++y2) {
System.arraycopy(bytes, 0, this.buffer, (y + y2) * 128 + x, width);
}
// Mark all colors within the image as dirty
this.mapView.worldMap.setColorsDirty(destX, destY);
this.mapView.worldMap.setColorsDirty(destX + effectiveWidth - 1, destY + effectiveHeight - 1);
// Paper end
}

View File

@@ -5,7 +5,6 @@ import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.saveddata.maps.MapDecorationType;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.map.MapCursor;
@@ -17,7 +16,7 @@ public final class CraftMapCursor {
private static int count = 0;
public static MapCursor.Type minecraftToBukkit(MapDecorationType minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.MAP_DECORATION_TYPE, Registry.MAP_DECORATION_TYPE);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.MAP_DECORATION_TYPE);
}
public static MapCursor.Type minecraftHolderToBukkit(Holder<MapDecorationType> minecraft) {

View File

@@ -5,7 +5,6 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.world.effect.MobEffect;
import org.bukkit.Color;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.potion.PotionEffect;
@@ -20,7 +19,7 @@ public class CraftPotionEffectType extends PotionEffectType implements Handleabl
}
public static PotionEffectType minecraftToBukkit(MobEffect minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.MOB_EFFECT, Registry.EFFECT);
return CraftRegistry.minecraftToBukkit(minecraft, Registries.MOB_EFFECT);
}
public static MobEffect bukkitToMinecraft(PotionEffectType bukkit) {

View File

@@ -2,6 +2,7 @@ package org.bukkit.craftbukkit.potion;
import com.google.common.base.Preconditions;
import com.google.common.base.Suppliers;
import io.papermc.paper.registry.RegistryKey;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
@@ -72,7 +73,7 @@ public class CraftPotionType implements PotionType.InternalPotionData {
if (key == null) return null; // Paper - Fixup NamespacedKey handling
// Now also convert from when keys where saved
return CraftRegistry.get(Registry.POTION, key, ApiVersion.CURRENT);
return CraftRegistry.get(RegistryKey.POTION, key, ApiVersion.CURRENT);
}
private final NamespacedKey key;

View File

@@ -12,17 +12,16 @@ import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Dynamic;
import com.mojang.serialization.JsonOps;
import io.papermc.paper.registry.RegistryKey;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import io.papermc.paper.entity.EntitySerializationFlag;
import net.minecraft.SharedConstants;
@@ -48,11 +47,9 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.LevelResource;
import org.bukkit.Bukkit;
import org.bukkit.FeatureFlag;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.UnsafeValues;
import org.bukkit.World;
import org.bukkit.advancement.Advancement;
@@ -66,14 +63,12 @@ import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBiome;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.damage.CraftDamageEffect;
import org.bukkit.craftbukkit.damage.CraftDamageSourceBuilder;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.legacy.CraftLegacy;
import org.bukkit.craftbukkit.legacy.FieldRename;
import org.bukkit.craftbukkit.potion.CraftPotionType;
import org.bukkit.damage.DamageEffect;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.enchantments.Enchantment;
@@ -495,7 +490,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
@Override
public <B extends Keyed> B get(Registry<B> registry, NamespacedKey namespacedKey) {
public <B extends Keyed> B get(RegistryKey<B> registry, NamespacedKey namespacedKey) {
// We currently do not have any version-dependent remapping, so we can use current version
return CraftRegistry.get(registry, namespacedKey, ApiVersion.CURRENT);
}
@@ -595,7 +590,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
e.getStringUUID()
);
} else {
// Ensure player flag is not needed
// Ensure misc flag is not needed
Preconditions.checkArgument(
nmsEntity.getType().canSerialize() || allowMiscSerialization,
"Cannot serialize misc non-saveable entity %s(%s) without the MISC flag",
@@ -756,9 +751,9 @@ public final class CraftMagicNumbers implements UnsafeValues {
final net.minecraft.world.entity.EntityType<?> nmsType = org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(entityType);
final net.minecraft.world.item.SpawnEggItem eggItem = net.minecraft.world.item.SpawnEggItem.byId(nmsType);
if (eggItem != null) {
throw new UnsupportedOperationException("Not yet implemented");
throw new UnsupportedOperationException();
}
return eggItem == null ? null : org.bukkit.Color.fromRGB(1); // TODO
return null;
}
// Paper end - spawn egg color visibility