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

@@ -0,0 +1,44 @@
package ca.spottedleaf.moonrise.common.util;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public final class EntityUtil {
private static final ThreadLocal<DecimalFormat> THREE_DECIMAL_PLACES = ThreadLocal.withInitial(() -> {
return new DecimalFormat("#,##0.000");
});
private static String formatVec(final Vec3 vec) {
final DecimalFormat format = THREE_DECIMAL_PLACES.get();
return "(" + format.format(vec.x) + "," + format.format(vec.y) + "," + format.format(vec.z) + ")";
}
private static String dumpEntityWithoutReferences(final Entity entity) {
if (entity == null) {
return "{null}";
}
return "{type=" + entity.getClass().getSimpleName() + ",id=" + entity.getId() + ",uuid=" + entity.getUUID() + ",pos=" + formatVec(entity.position())
+ ",mot=" + formatVec(entity.getDeltaMovement()) + ",aabb=" + entity.getBoundingBox() + ",removed=" + entity.getRemovalReason() + ",has_vehicle=" + (entity.getVehicle() != null)
+ ",passenger_count=" + entity.getPassengers().size();
}
public static String dumpEntity(final Entity entity) {
final List<Entity> passengers = entity.getPassengers();
final List<String> passengerStrings = new ArrayList<>(passengers.size());
for (final Entity passenger : passengers) {
passengerStrings.add("(" + dumpEntityWithoutReferences(passenger) + ")");
}
return "{root=[" + dumpEntityWithoutReferences(entity) + "], vehicle=[" + dumpEntityWithoutReferences(entity.getVehicle())
+ "], passengers=[" + String.join(",", passengerStrings) + "]";
}
private EntityUtil() {}
}

View File

@@ -15,56 +15,81 @@ public class TickThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
private static String getThreadContext() {
return "thread=" + Thread.currentThread().getName();
}
/**
* @deprecated
*/
@Deprecated
public static void ensureTickThread(final String reason) {
if (!isTickThread()) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
LOGGER.error("Thread failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
if (!isTickThreadFor(world, pos, blockRadius)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
@@ -105,6 +130,10 @@ public class TickThread extends Thread {
return isTickThread();
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
return isTickThread();
}
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
return isTickThread();
}

View File

@@ -30,6 +30,6 @@ public class CraftGameEventTag extends CraftTag<net.minecraft.world.level.gameev
@Override
public @NotNull Set<GameEvent> getValues() {
return getHandle().stream().map((nms) -> Objects.requireNonNull(GameEvent.getByKey(CraftNamespacedKey.fromMinecraft(BuiltInRegistries.GAME_EVENT.getKey(nms.value()))), nms + " is not a recognized game event")).collect(Collectors.toUnmodifiableSet());
return getHandle().stream().map((nms) -> Objects.requireNonNull(GameEvent.getByKey(CraftNamespacedKey.fromMinecraft(BuiltInRegistries.GAME_EVENT.getKey(nms.value()))), () -> nms + " is not a recognized game event")).collect(Collectors.toUnmodifiableSet());
}
}

View File

@@ -76,6 +76,20 @@ public class DataComponentValueConverterProviderImpl implements DataComponentVal
throw new IllegalArgumentException(e);
}
}
),
DataComponentValueConverterRegistry.Conversion.convert(
DataComponentValue.TagSerializable.class,
GsonDataComponentValue.class,
(key, tagSerializable) -> {
Tag decodedSnbt;
try {
decodedSnbt = tagSerializable.asBinaryTag().get(PaperAdventure.NBT_CODEC);
} catch (final CommandSyntaxException e) {
throw new IllegalArgumentException("Unable to parse SNBT value", e);
}
return GsonDataComponentValue.gsonDataComponentValue(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, decodedSnbt));
}
)
);
}

View File

@@ -11,6 +11,11 @@ import io.papermc.paper.plugin.provider.ProviderStatus;
import io.papermc.paper.plugin.provider.ProviderStatusHolder;
import io.papermc.paper.plugin.provider.type.paper.PaperPluginParent;
import io.papermc.paper.plugin.provider.type.spigot.SpigotPluginProvider;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
@@ -23,51 +28,38 @@ import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PaperPluginsCommand extends BukkitCommand {
private static final TextColor INFO_COLOR = TextColor.color(52, 159, 218);
// TODO: LINK?
private static final Component SERVER_PLUGIN_INFO = Component.text(" What is a server plugin?", INFO_COLOR)
.append(asPlainComponents("""
Server plugins can add new behavior to your server!
You can find new plugins on Paper's plugin repository, Hangar.
<link to hangar>
"""));
private static final Component SERVER_INITIALIZER_INFO = Component.text(" What is a server initializer?", INFO_COLOR)
.append(asPlainComponents("""
Server initializers are ran before your server
starts and are provided by paper plugins.
"""));
Server plugins can add new behavior to your server!
You can find new plugins on Paper's plugin repository, Hangar.
https://hangar.papermc.io/
"""));
private static final Component LEGACY_PLUGIN_INFO = Component.text(" What is a legacy plugin?", INFO_COLOR)
.append(asPlainComponents("""
A legacy plugin is a plugin that was made on
very old unsupported versions of the game.
It is encouraged that you replace this plugin,
as they might not work in the future and may cause
performance issues.
"""));
A legacy plugin is a plugin that was made on
very old unsupported versions of the game.
It is encouraged that you replace this plugin,
as they might not work in the future and may cause
performance issues.
"""));
private static final Component LEGACY_PLUGIN_STAR = Component.text('*', TextColor.color(255, 212, 42)).hoverEvent(LEGACY_PLUGIN_INFO);
private static final Component INFO_ICON_START = Component.text(" ", INFO_COLOR);
private static final Component PAPER_HEADER = Component.text("Paper Plugins:", TextColor.color(2, 136, 209));
private static final Component BUKKIT_HEADER = Component.text("Bukkit Plugins:", TextColor.color(237, 129, 6));
private static final Component PLUGIN_TICK = Component.text("- ", NamedTextColor.DARK_GRAY);
private static final Component PLUGIN_TICK_EMPTY = Component.text(" ");
private static final Component INFO_ICON_SERVER_PLUGIN = INFO_ICON_START.hoverEvent(SERVER_PLUGIN_INFO).clickEvent(ClickEvent.openUrl("https://docs.papermc.io/paper/adding-plugins"));
private static final Type JAVA_PLUGIN_PROVIDER_TYPE = new TypeToken<PluginProvider<JavaPlugin>>() {}.getType();
public PaperPluginsCommand() {
@@ -75,17 +67,17 @@ public class PaperPluginsCommand extends BukkitCommand {
this.description = "Gets a list of plugins running on the server";
this.usageMessage = "/plugins";
this.setPermission("bukkit.command.plugins");
this.setAliases(Arrays.asList("pl"));
this.setAliases(List.of("pl"));
}
private static <T> List<Component> formatProviders(TreeMap<String, PluginProvider<T>> plugins) {
List<Component> components = new ArrayList<>(plugins.size());
for (PluginProvider<T> entry : plugins.values()) {
private static <T> List<Component> formatProviders(final TreeMap<String, PluginProvider<T>> plugins) {
final List<Component> components = new ArrayList<>(plugins.size());
for (final PluginProvider<T> entry : plugins.values()) {
components.add(formatProvider(entry));
}
boolean isFirst = true;
List<Component> formattedSublists = new ArrayList<>();
final List<Component> formattedSubLists = new ArrayList<>();
/*
Split up the plugin list for each 10 plugins to get size down
@@ -93,30 +85,29 @@ public class PaperPluginsCommand extends BukkitCommand {
- Plugin 1, Plugin 2, .... Plugin 10,
Plugin 11, Plugin 12 ... Plugin 20,
*/
for (List<Component> componentSublist : Lists.partition(components, 10)) {
for (final List<Component> componentSublist : Lists.partition(components, 10)) {
Component component = Component.space();
if (isFirst) {
component = component.append(PLUGIN_TICK);
isFirst = false;
} else {
component = PLUGIN_TICK_EMPTY;
//formattedSublists.add(Component.empty()); // Add an empty line, the auto chat wrapping and this makes it quite jarring.
}
formattedSublists.add(component.append(Component.join(JoinConfiguration.commas(true), componentSublist)));
formattedSubLists.add(component.append(Component.join(JoinConfiguration.commas(true), componentSublist)));
}
return formattedSublists;
return formattedSubLists;
}
private static Component formatProvider(PluginProvider<?> provider) {
TextComponent.Builder builder = Component.text();
if (provider instanceof SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) {
private static Component formatProvider(final PluginProvider<?> provider) {
final TextComponent.Builder builder = Component.text();
if (provider instanceof final SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) {
builder.append(LEGACY_PLUGIN_STAR);
}
String name = provider.getMeta().getName();
Component pluginName = Component.text(name, fromStatus(provider))
final String name = provider.getMeta().getName();
final Component pluginName = Component.text(name, fromStatus(provider))
.clickEvent(ClickEvent.runCommand("/version " + name));
builder.append(pluginName);
@@ -124,9 +115,20 @@ public class PaperPluginsCommand extends BukkitCommand {
return builder.build();
}
private static Component asPlainComponents(String strings) {
net.kyori.adventure.text.TextComponent.Builder builder = Component.text();
for (String string : strings.split("\n")) {
private static Component header(final String header, final int color, final int count, final boolean showSize) {
final TextComponent.Builder componentHeader = Component.text().color(TextColor.color(color))
.append(Component.text(header));
if (showSize) {
componentHeader.appendSpace().append(Component.text("(" + count + ")"));
}
return componentHeader.append(Component.text(":")).build();
}
private static Component asPlainComponents(final String strings) {
final net.kyori.adventure.text.TextComponent.Builder builder = Component.text();
for (final String string : strings.split("\n")) {
builder.append(Component.newline());
builder.append(Component.text(string, NamedTextColor.WHITE));
}
@@ -134,13 +136,13 @@ public class PaperPluginsCommand extends BukkitCommand {
return builder.build();
}
private static TextColor fromStatus(PluginProvider<?> provider) {
if (provider instanceof ProviderStatusHolder statusHolder && statusHolder.getLastProvidedStatus() != null) {
ProviderStatus status = statusHolder.getLastProvidedStatus();
private static TextColor fromStatus(final PluginProvider<?> provider) {
if (provider instanceof final ProviderStatusHolder statusHolder && statusHolder.getLastProvidedStatus() != null) {
final ProviderStatus status = statusHolder.getLastProvidedStatus();
// Handle enabled/disabled game plugins
if (status == ProviderStatus.INITIALIZED && GenericTypeReflector.isSuperType(JAVA_PLUGIN_PROVIDER_TYPE, provider.getClass())) {
Plugin plugin = Bukkit.getPluginManager().getPlugin(provider.getMeta().getName());
final Plugin plugin = Bukkit.getPluginManager().getPlugin(provider.getMeta().getName());
// Plugin doesn't exist? Could be due to it being removed.
if (plugin == null) {
return NamedTextColor.RED;
@@ -153,7 +155,7 @@ public class PaperPluginsCommand extends BukkitCommand {
case INITIALIZED -> NamedTextColor.GREEN;
case ERRORED -> NamedTextColor.RED;
};
} else if (provider instanceof PaperPluginParent.PaperServerPluginProvider serverPluginProvider && serverPluginProvider.shouldSkipCreation()) {
} else if (provider instanceof final PaperPluginParent.PaperServerPluginProvider serverPluginProvider && serverPluginProvider.shouldSkipCreation()) {
// Paper plugins will be skipped if their provider is skipped due to their initializer failing.
// Show them as red
return NamedTextColor.RED;
@@ -165,15 +167,14 @@ public class PaperPluginsCommand extends BukkitCommand {
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
public boolean execute(final CommandSender sender, final String currentAlias, final String[] args) {
if (!this.testPermission(sender)) return true;
TreeMap<String, PluginProvider<JavaPlugin>> paperPlugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
TreeMap<String, PluginProvider<JavaPlugin>> spigotPlugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
final TreeMap<String, PluginProvider<JavaPlugin>> paperPlugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
final TreeMap<String, PluginProvider<JavaPlugin>> spigotPlugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (PluginProvider<JavaPlugin> provider : LaunchEntryPointHandler.INSTANCE.get(Entrypoint.PLUGIN).getRegisteredProviders()) {
PluginMeta configuration = provider.getMeta();
for (final PluginProvider<JavaPlugin> provider : LaunchEntryPointHandler.INSTANCE.get(Entrypoint.PLUGIN).getRegisteredProviders()) {
final PluginMeta configuration = provider.getMeta();
if (provider instanceof SpigotPluginProvider) {
spigotPlugins.put(configuration.getDisplayName(), provider);
@@ -182,34 +183,36 @@ public class PaperPluginsCommand extends BukkitCommand {
}
}
Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
//.append(INFO_ICON_START.hoverEvent(SERVER_PLUGIN_INFO)); TODO: Add docs
final int sizePaperPlugins = paperPlugins.size();
final int sizeSpigotPlugins = spigotPlugins.size();
final int sizePlugins = sizePaperPlugins + sizeSpigotPlugins;
final boolean hasAllPluginTypes = (sizePaperPlugins > 0 && sizeSpigotPlugins > 0);
final Component infoMessage = Component.text().append(INFO_ICON_SERVER_PLUGIN).append(Component.text("Server Plugins (%s):".formatted(sizePlugins), NamedTextColor.WHITE)).build();
sender.sendMessage(infoMessage);
if (!paperPlugins.isEmpty()) {
sender.sendMessage(PAPER_HEADER);
sender.sendMessage(header("Paper Plugins", 0x0288D1, sizePaperPlugins, hasAllPluginTypes));
}
for (Component component : formatProviders(paperPlugins)) {
for (final Component component : formatProviders(paperPlugins)) {
sender.sendMessage(component);
}
if (!spigotPlugins.isEmpty()) {
sender.sendMessage(BUKKIT_HEADER);
sender.sendMessage(header("Bukkit Plugins", 0xED8106, sizeSpigotPlugins, hasAllPluginTypes));
}
for (Component component : formatProviders(spigotPlugins)) {
for (final Component component : formatProviders(spigotPlugins)) {
sender.sendMessage(component);
}
return true;
}
@NotNull
@Override
public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
public List<String> tabComplete(final CommandSender sender, final String alias, final String[] args) throws IllegalArgumentException {
return Collections.emptyList();
}
}

View File

@@ -124,7 +124,7 @@ public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceSta
}
converted = this.unwrapArgumentWrapper(pureArgumentNode, customArgumentType, customArgumentType.getNativeType(), suggestionProvider);
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) {
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?, ?> nativeWrapperArgumentType) {
converted = this.unwrapArgumentWrapper(pureArgumentNode, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider
/*
@@ -140,6 +140,8 @@ public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceSta
// Unknown argument type was passed
throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType.");
}
} else if (pureNode == this) {
return (CommandNode) this.getDispatcher().getRoot();
} else {
throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
}

View File

@@ -1,24 +1,19 @@
package io.papermc.paper.command.brigadier;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
import java.util.Map;
import net.minecraft.commands.CommandSource;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import java.util.Map;
public final class PaperBrigadier {
@SuppressWarnings("DataFlowIssue")
@@ -40,7 +35,8 @@ public final class PaperBrigadier {
throw new IllegalArgumentException("Unsure how to wrap a " + node);
}
if (!(node instanceof PluginCommandNode pluginCommandNode)) {
final PluginCommandMeta meta;
if ((meta = node.pluginCommandMeta) == null) {
return new VanillaCommandWrapper(null, node);
}
CommandNode<CommandSourceStack> argumentCommandNode = node;
@@ -49,8 +45,8 @@ public final class PaperBrigadier {
}
Map<CommandNode<CommandSourceStack>, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, DUMMY);
String usage = map.isEmpty() ? pluginCommandNode.getUsageText() : pluginCommandNode.getUsageText() + " " + String.join("\n" + pluginCommandNode.getUsageText() + " ", map.values());
return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin());
String usage = map.isEmpty() ? node.getUsageText() : node.getUsageText() + " " + String.join("\n" + node.getUsageText() + " ", map.values());
return new PluginVanillaCommandWrapper(node.getName(), meta.description(), usage, meta.aliases(), node, meta.plugin());
}
/*
@@ -70,4 +66,19 @@ public final class PaperBrigadier {
}
}
}
public static <S> LiteralCommandNode<S> copyLiteral(final String newLiteral, final LiteralCommandNode<S> source) {
// logic copied from LiteralCommandNode#createBuilder
final LiteralArgumentBuilder<S> copyBuilder = LiteralArgumentBuilder.<S>literal(newLiteral)
.requires(source.getRequirement())
.forward(source.getRedirect(), source.getRedirectModifier(), source.isFork());
if (source.getCommand() != null) {
copyBuilder.executes(source.getCommand());
}
for (final CommandNode<S> child : source.getChildren()) {
copyBuilder.then(child);
}
return copyBuilder.build();
}
}

View File

@@ -1,21 +1,23 @@
package io.papermc.paper.command.brigadier;
import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
import com.google.common.base.Preconditions;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBrigadierCommandSource {
net.minecraft.commands.CommandSourceStack getHandle();
@Override
default @NotNull Location getLocation() {
default @NonNull Location getLocation() {
Vec2 rot = this.getHandle().getRotation();
Vec3 pos = this.getHandle().getPosition();
Level level = this.getHandle().getLevel();
@@ -24,7 +26,7 @@ public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBriga
}
@Override
@NotNull
@NonNull
default CommandSender getSender() {
return this.getHandle().getBukkitSender();
}
@@ -40,6 +42,12 @@ public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBriga
return nmsEntity.getBukkitEntity();
}
@Override
default CommandSourceStack withExecutor(@NonNull Entity executor) {
Preconditions.checkNotNull(executor, "Executor cannot be null.");
return this.getHandle().withEntity(((CraftEntity) executor).getHandle());
}
// OLD METHODS
@Override
default org.bukkit.entity.Entity getBukkitEntity() {

View File

@@ -21,23 +21,20 @@ import java.util.Set;
import net.minecraft.commands.CommandBuildContext;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import static java.util.Objects.requireNonNull;
@DefaultQualifier(NonNull.class)
@NullMarked
public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwner> {
public static final PaperCommands INSTANCE = new PaperCommands();
private @Nullable LifecycleEventOwner currentContext;
private @MonotonicNonNull CommandDispatcher<CommandSourceStack> dispatcher;
private @MonotonicNonNull CommandBuildContext buildContext;
private @Nullable CommandDispatcher<CommandSourceStack> dispatcher;
private @Nullable CommandBuildContext buildContext;
private boolean invalid = false;
@Override
@@ -93,65 +90,48 @@ public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwn
}
@Override
public @Unmodifiable Set<String> registerWithFlags(@NotNull final PluginMeta pluginMeta, @NotNull final LiteralCommandNode<CommandSourceStack> node, @org.jetbrains.annotations.Nullable final String description, @NotNull final Collection<String> aliases, @NotNull final Set<CommandRegistrationFlag> flags) {
final boolean hasFlattenRedirectFlag = flags.contains(CommandRegistrationFlag.FLATTEN_ALIASES);
public @Unmodifiable Set<String> registerWithFlags(final PluginMeta pluginMeta, final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description, final Collection<String> aliases, final Set<CommandRegistrationFlag> flags) {
final PluginCommandMeta meta = new PluginCommandMeta(pluginMeta, description);
final String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT);
final String literal = node.getLiteral();
final PluginCommandNode pluginLiteral = new PluginCommandNode(identifier + ":" + literal, pluginMeta, node, description); // Treat the keyed version of the command as the root
final LiteralCommandNode<CommandSourceStack> pluginLiteral = PaperBrigadier.copyLiteral(identifier + ":" + literal, node);
final Set<String> registeredLabels = new HashSet<>(aliases.size() * 2 + 2);
if (this.registerIntoDispatcher(pluginLiteral, true)) {
registeredLabels.add(pluginLiteral.getLiteral());
}
if (this.registerRedirect(literal, pluginMeta, pluginLiteral, description, true, hasFlattenRedirectFlag)) { // Plugin commands should override vanilla commands
if (this.registerIntoDispatcher(node, true)) { // Plugin commands should override vanilla commands
registeredLabels.add(literal);
}
// Add aliases
final List<String> registeredAliases = new ArrayList<>(aliases.size() * 2);
for (final String alias : aliases) {
if (this.registerRedirect(alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
if (this.registerCopy(alias, pluginLiteral, meta)) {
registeredAliases.add(alias);
}
if (this.registerRedirect(identifier + ":" + alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
if (this.registerCopy(identifier + ":" + alias, pluginLiteral, meta)) {
registeredAliases.add(identifier + ":" + alias);
}
}
if (!registeredAliases.isEmpty()) {
pluginLiteral.setAliases(registeredAliases);
}
pluginLiteral.pluginCommandMeta = new PluginCommandMeta(pluginMeta, description, registeredAliases);
node.pluginCommandMeta = pluginLiteral.pluginCommandMeta;
registeredLabels.addAll(registeredAliases);
return registeredLabels.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(registeredLabels);
}
private boolean registerRedirect(final String aliasLiteral, final PluginMeta plugin, final PluginCommandNode redirectTo, final @Nullable String description, final boolean override, boolean hasFlattenRedirectFlag) {
final LiteralCommandNode<CommandSourceStack> redirect;
if (redirectTo.getChildren().isEmpty() || hasFlattenRedirectFlag) {
redirect = Commands.literal(aliasLiteral)
.executes(redirectTo.getCommand())
.requires(redirectTo.getRequirement())
.build();
for (final CommandNode<CommandSourceStack> child : redirectTo.getChildren()) {
redirect.addChild(child);
}
} else {
redirect = Commands.literal(aliasLiteral)
.executes(redirectTo.getCommand())
.redirect(redirectTo)
.requires(redirectTo.getRequirement())
.build();
}
return this.registerIntoDispatcher(new PluginCommandNode(aliasLiteral, plugin, redirect, description), override);
private boolean registerCopy(final String aliasLiteral, final LiteralCommandNode<CommandSourceStack> redirectTo, final PluginCommandMeta meta) {
final LiteralCommandNode<CommandSourceStack> node = PaperBrigadier.copyLiteral(aliasLiteral, redirectTo);
node.pluginCommandMeta = meta;
return this.registerIntoDispatcher(node, false);
}
private boolean registerIntoDispatcher(final PluginCommandNode node, boolean override) {
final @Nullable CommandNode<CommandSourceStack> existingChild = this.getDispatcher().getRoot().getChild(node.getLiteral());
if (existingChild != null && !(existingChild instanceof PluginCommandNode) && !(existingChild instanceof BukkitCommandNode)) {
private boolean registerIntoDispatcher(final LiteralCommandNode<CommandSourceStack> node, boolean override) {
final CommandNode<CommandSourceStack> existingChild = this.getDispatcher().getRoot().getChild(node.getLiteral());
if (existingChild != null && existingChild.pluginCommandMeta == null && !(existingChild instanceof BukkitCommandNode)) {
override = true; // override vanilla commands
}
if (existingChild == null || override) { // Avoid merging behavior. Maybe something to look into in the future

View File

@@ -0,0 +1,26 @@
package io.papermc.paper.command.brigadier;
import io.papermc.paper.plugin.configuration.PluginMeta;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public record PluginCommandMeta(PluginMeta pluginMeta, @Nullable String description, List<String> aliases) {
public PluginCommandMeta(final PluginMeta pluginMeta, final @Nullable String description) {
this(pluginMeta, description, Collections.emptyList());
}
public PluginCommandMeta {
aliases = List.copyOf(aliases);
}
public Plugin plugin() {
return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.pluginMeta.getName()));
}
}

View File

@@ -1,50 +0,0 @@
package io.papermc.paper.command.brigadier;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import io.papermc.paper.plugin.configuration.PluginMeta;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PluginCommandNode extends LiteralCommandNode<CommandSourceStack> {
private final PluginMeta plugin;
private final String description;
private List<String> aliases = Collections.emptyList();
public PluginCommandNode(final @NotNull String literal, final @NotNull PluginMeta plugin, final @NotNull LiteralCommandNode<CommandSourceStack> rootLiteral, final @Nullable String description) {
super(
literal, rootLiteral.getCommand(), rootLiteral.getRequirement(),
rootLiteral.getRedirect(), rootLiteral.getRedirectModifier(), rootLiteral.isFork()
);
this.plugin = plugin;
this.description = description;
for (CommandNode<CommandSourceStack> argument : rootLiteral.getChildren()) {
this.addChild(argument);
}
}
@NotNull
public Plugin getPlugin() {
return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.plugin.getName()));
}
@NotNull
public String getDescription() {
return this.description;
}
public void setAliases(List<String> aliases) {
this.aliases = aliases;
}
public List<String> getAliases() {
return this.aliases;
}
}

View File

@@ -19,9 +19,11 @@ import io.papermc.paper.command.brigadier.argument.range.RangeProvider;
import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver;
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver;
import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver;
import io.papermc.paper.command.brigadier.argument.resolvers.RotationResolver;
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver;
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.math.Rotation;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
@@ -61,6 +63,7 @@ import net.minecraft.commands.arguments.TimeArgument;
import net.minecraft.commands.arguments.UuidArgument;
import net.minecraft.commands.arguments.blocks.BlockStateArgument;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.commands.arguments.coordinates.RotationArgument;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.commands.arguments.item.ItemArgument;
import net.minecraft.commands.arguments.item.ItemPredicateArgument;
@@ -71,6 +74,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.bukkit.GameMode;
import org.bukkit.HeightMap;
@@ -156,6 +160,15 @@ public class VanillaArgumentProviderImpl implements VanillaArgumentProvider {
});
}
@Override
public ArgumentType<RotationResolver> rotation() {
return this.wrap(RotationArgument.rotation(), (result) -> sourceStack -> {
final Vec2 vec2 = result.getRotation((CommandSourceStack) sourceStack);
return Rotation.rotation(vec2.y, vec2.x);
});
}
@Override
public ArgumentType<BlockState> blockState() {
return this.wrap(BlockStateArgument.block(PaperCommands.INSTANCE.getBuildContext()), (result) -> {
@@ -353,6 +366,11 @@ public class VanillaArgumentProviderImpl implements VanillaArgumentProvider {
return this.converter.convert(this.nmsBase.parse(reader));
}
@Override
public <S> P parse(final StringReader reader, final S source) throws CommandSyntaxException {
return this.converter.convert(this.nmsBase.parse(reader, source));
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return this.nmsBase.listSuggestions(context, builder);

View File

@@ -46,7 +46,7 @@ public class WrappedArgumentCommandNode<NMS, API> extends ArgumentCommandNode<Co
@Override
public void parse(final StringReader reader, final CommandContextBuilder<CommandSourceStack> contextBuilder) throws CommandSyntaxException {
final int start = reader.getCursor();
final API result = this.pureArgumentType.parse(reader); // Use the api argument parser
final API result = this.pureArgumentType.parse(reader, contextBuilder.getSource()); // Use the api argument parser
final ParsedArgument<CommandSourceStack, API> parsed = new ParsedArgument<>(start, reader.getCursor(), result); // Return an API parsed argument instead.
contextBuilder.withArgument(this.getName(), parsed);

View File

@@ -80,7 +80,7 @@ public abstract class Configurations<G, W> {
}
@MustBeInvokedByOverriders
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() {
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder(RegistryAccess registryAccess) {
return this.createLoaderBuilder();
}
@@ -104,7 +104,7 @@ public abstract class Configurations<G, W> {
}
public G initializeGlobalConfiguration(final RegistryAccess registryAccess) throws ConfigurateException {
return this.initializeGlobalConfiguration(creator(this.globalConfigClass, true));
return this.initializeGlobalConfiguration(registryAccess, creator(this.globalConfigClass, true));
}
private void trySaveFileNode(YamlConfigurationLoader loader, ConfigurationNode node, String filename) throws ConfigurateException {
@@ -117,9 +117,9 @@ public abstract class Configurations<G, W> {
}
}
protected G initializeGlobalConfiguration(final CheckedFunction<ConfigurationNode, G, SerializationException> creator) throws ConfigurateException {
protected G initializeGlobalConfiguration(final RegistryAccess registryAccess, final CheckedFunction<ConfigurationNode, G, SerializationException> creator) throws ConfigurateException {
final Path configFile = this.globalFolder.resolve(this.globalConfigFileName);
final YamlConfigurationLoader loader = this.createGlobalLoaderBuilder()
final YamlConfigurationLoader loader = this.createGlobalLoaderBuilder(registryAccess)
.defaultOptions(this.applyObjectMapperFactory(this.createGlobalObjectMapperFactoryBuilder().build()))
.path(configFile)
.build();

View File

@@ -5,10 +5,13 @@ import io.papermc.paper.FeatureHooks;
import io.papermc.paper.configuration.constraint.Constraints;
import io.papermc.paper.configuration.type.number.DoubleOr;
import io.papermc.paper.configuration.type.number.IntOr;
import io.papermc.paper.util.ItemObfuscationBinding;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
import net.minecraft.resources.ResourceLocation;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@@ -20,6 +23,7 @@ import org.spongepowered.configurate.objectmapping.meta.Setting;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;
@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"})
public class GlobalConfiguration extends ConfigurationPart {
@@ -69,7 +73,7 @@ public class GlobalConfiguration extends ConfigurationPart {
)
public int playerMaxConcurrentChunkGenerates = 0;
}
static void set(GlobalConfiguration instance) {
static void set(final GlobalConfiguration instance) {
GlobalConfiguration.instance = instance;
}
@@ -166,6 +170,8 @@ public class GlobalConfiguration extends ConfigurationPart {
public class UnsupportedSettings extends ConfigurationPart {
@Comment("This setting allows for exploits related to end portals, for example sand duping")
public boolean allowUnsafeEndPortalTeleportation = false;
@Comment("This setting controls the ability to enable dupes related to tripwires.")
public boolean skipTripwireHookPlacementValidation = false;
@Comment("This setting controls if players should be able to break bedrock, end portals and other intended to be permanent blocks.")
public boolean allowPermanentBlockBreakExploits = false;
@Comment("This setting controls if player should be able to use TNT duplication, but this also allows duplicating carpet, rails and potentially other items")
@@ -177,10 +183,15 @@ public class GlobalConfiguration extends ConfigurationPart {
public boolean skipVanillaDamageTickWhenShieldBlocked = false;
@Comment("This setting controls what compression format is used for region files.")
public CompressionFormat compressionFormat = CompressionFormat.ZLIB;
@Comment("This setting controls if equipment should be updated when handling certain player actions.")
public boolean updateEquipmentOnPlayerActions = true;
@Comment("Only checks an item's amount and type instead of its full data during inventory desync checks.")
public boolean simplifyRemoteItemMatching = false;
public enum CompressionFormat {
GZIP,
ZLIB,
LZ4,
NONE
}
}
@@ -189,8 +200,9 @@ public class GlobalConfiguration extends ConfigurationPart {
public class Commands extends ConfigurationPart {
public boolean suggestPlayerNamesWhenNullTabCompletions = true;
public boolean fixTargetSelectorTagCompletion = true;
public boolean timeCommandAffectsAllWorlds = false;
@Comment("Allow mounting entities to a player in the Vanilla '/ride' command.")
public boolean rideCommandAllowPlayerAsVehicle = false;
}
public Logging logging;
@@ -354,4 +366,41 @@ public class GlobalConfiguration extends ConfigurationPart {
public boolean disableChorusPlantUpdates = false;
public boolean disableMushroomBlockUpdates = false;
}
public Anticheat anticheat;
public class Anticheat extends ConfigurationPart {
public Obfuscation obfuscation;
public class Obfuscation extends ConfigurationPart {
public Items items;
public class Items extends ConfigurationPart {
public boolean enableItemObfuscation = false;
public ItemObfuscationBinding.AssetObfuscationConfiguration allModels = new ItemObfuscationBinding.AssetObfuscationConfiguration(
true,
Set.of(DataComponents.LODESTONE_TRACKER),
Set.of()
);
public Map<ResourceLocation, ItemObfuscationBinding.AssetObfuscationConfiguration> modelOverrides = Map.of(
Objects.requireNonNull(net.minecraft.world.item.Items.ELYTRA.components().get(DataComponents.ITEM_MODEL)),
new ItemObfuscationBinding.AssetObfuscationConfiguration(
true,
Set.of(DataComponents.DAMAGE),
Set.of()
)
);
public transient ItemObfuscationBinding binding;
@PostProcess
public void bindDataSanitizer() {
this.binding = new ItemObfuscationBinding(this);
}
}
}
}
}

View File

@@ -10,6 +10,7 @@ import io.papermc.paper.configuration.serializer.ComponentSerializer;
import io.papermc.paper.configuration.serializer.EnumValueSerializer;
import io.papermc.paper.configuration.serializer.NbtPathSerializer;
import io.papermc.paper.configuration.serializer.PacketClassSerializer;
import io.papermc.paper.configuration.serializer.ResourceLocationSerializer;
import io.papermc.paper.configuration.serializer.StringRepresentableSerializer;
import io.papermc.paper.configuration.serializer.collections.FastutilMapSerializer;
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
@@ -48,6 +49,7 @@ import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
@@ -180,6 +182,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
.register(Duration.SERIALIZER)
.register(DurationOrDisabled.SERIALIZER)
.register(NbtPathSerializer.SERIALIZER)
.register(ResourceLocationSerializer.INSTANCE)
);
}
@@ -193,16 +196,17 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
}
@Override
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() {
return super.createGlobalLoaderBuilder()
.defaultOptions(PaperConfigurations::defaultGlobalOptions);
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder(RegistryAccess registryAccess) {
return super.createGlobalLoaderBuilder(registryAccess)
.defaultOptions((options) -> defaultGlobalOptions(registryAccess, options));
}
private static ConfigurationOptions defaultGlobalOptions(ConfigurationOptions options) {
private static ConfigurationOptions defaultGlobalOptions(RegistryAccess registryAccess, ConfigurationOptions options) {
return options
.header(GLOBAL_HEADER)
.serializers(builder -> builder
.register(new PacketClassSerializer())
.register(new RegistryValueSerializer<>(new TypeToken<DataComponentType<?>>() {}, registryAccess, Registries.DATA_COMPONENT_TYPE, false))
);
}
@@ -316,7 +320,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
public void reloadConfigs(MinecraftServer server) {
try {
this.initializeGlobalConfiguration(reloader(this.globalConfigClass, GlobalConfiguration.get()));
this.initializeGlobalConfiguration(server.registryAccess(), reloader(this.globalConfigClass, GlobalConfiguration.get()));
this.initializeWorldDefaultsConfiguration(server.registryAccess());
for (ServerLevel level : server.getAllLevels()) {
this.createWorldConfig(createWorldContextMap(level), reloader(this.worldConfigClass, level.paperConfig()));
@@ -454,9 +458,9 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
}
@VisibleForTesting
static ConfigurationNode createForTesting() {
static ConfigurationNode createForTesting(RegistryAccess registryAccess) {
ObjectMapper.Factory factory = defaultGlobalFactoryBuilder(ObjectMapper.factoryBuilder()).build();
ConfigurationOptions options = defaultGlobalOptions(defaultOptions(ConfigurationOptions.defaults()))
ConfigurationOptions options = defaultGlobalOptions(registryAccess, defaultOptions(ConfigurationOptions.defaults()))
.serializers(builder -> builder.register(type -> ConfigurationPart.class.isAssignableFrom(erase(type)), factory.asTypeSerializer()));
return BasicConfigurationNode.root(options);
}

View File

@@ -54,7 +54,8 @@ interface RemovedConfigurations {
path("fixes", "fix-curing-zombie-villager-discount-exploit"),
path("entities", "mob-effects", "undead-immune-to-certain-effects"),
path("entities", "entities-target-with-follow-range"),
path("environment", "disable-teleportation-suffocation-check")
path("environment", "disable-teleportation-suffocation-check"),
path("misc", "light-queue-size")
};
// spawn.keep-spawn-loaded and spawn.keep-spawn-loaded-range are no longer used, but kept
// in the world default config for compatibility with old worlds being migrated to use the gamerule
@@ -79,7 +80,8 @@ interface RemovedConfigurations {
path("warnWhenSettingExcessiveVelocity"),
path("logging", "use-rgb-for-named-text-colors"),
path("unsupported-settings", "allow-grindstone-overstacking"),
path("unsupported-settings", "allow-tripwire-disarming-exploits")
path("unsupported-settings", "allow-tripwire-disarming-exploits"),
path("commands", "fix-target-selector-tag-completion"),
};
}

View File

@@ -88,17 +88,6 @@ public class WorldConfiguration extends ConfigurationPart {
public class Anticheat extends ConfigurationPart {
public Obfuscation obfuscation;
public class Obfuscation extends ConfigurationPart {
public Items items = new Items();
public class Items extends ConfigurationPart {
public boolean hideItemmeta = false;
public boolean hideDurability = false;
public boolean hideItemmetaWithVisualEffects = false;
}
}
public AntiXray antiXray;
public class AntiXray extends ConfigurationPart {
@@ -338,6 +327,9 @@ public class WorldConfiguration extends ConfigurationPart {
public int day = 5;
}
}
@Comment("Adds a cooldown to bees being released after a failed release, which can occur if the hive is blocked or it being night.")
public boolean cooldownFailedBeehiveReleases = true;
}
public TrackingRangeY trackingRangeY;
@@ -566,7 +558,6 @@ public class WorldConfiguration extends ConfigurationPart {
public Misc misc;
public class Misc extends ConfigurationPart {
public int lightQueueSize = 20;
public boolean updatePathfindingOnBlockUpdate = true;
public boolean showSignClickCommandFailureMsgsToPlayer = false;
public RedstoneImplementation redstoneImplementation = RedstoneImplementation.VANILLA;

View File

@@ -0,0 +1,26 @@
package io.papermc.paper.configuration.serializer;
import java.lang.reflect.Type;
import java.util.function.Predicate;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.SerializationException;
public class ResourceLocationSerializer extends ScalarSerializer<ResourceLocation> {
public static final ScalarSerializer<ResourceLocation> INSTANCE = new ResourceLocationSerializer();
private ResourceLocationSerializer() {
super(ResourceLocation.class);
}
@Override
public ResourceLocation deserialize(final Type type, final Object obj) throws SerializationException {
return ResourceLocation.read(obj.toString()).getOrThrow(s -> new SerializationException(ResourceLocation.class, s));
}
@Override
protected Object serialize(final ResourceLocation item, final Predicate<Class<?>> typeSupported) {
return item.toString();
}
}

View File

@@ -15,10 +15,18 @@ public record DataComponentAdapter<NMS, API>(
) {
static final Function<Void, Unit> API_TO_UNIT_CONVERTER = $ -> Unit.INSTANCE;
static final Function API_TO_UNIMPLEMENTED_CONVERTER = $ -> {
throw new UnsupportedOperationException("Cannot convert an API value to an unimplemented type");
};
public boolean isValued() {
return this.apiToVanilla != API_TO_UNIT_CONVERTER;
}
public boolean isUnimplemented() {
return this.apiToVanilla == API_TO_UNIMPLEMENTED_CONVERTER;
}
public NMS toVanilla(final API value) {
final NMS nms = this.apiToVanilla.apply(value);
if (this.codecValidation) {

View File

@@ -63,6 +63,10 @@ public final class DataComponentAdapters {
throw new UnsupportedOperationException("Cannot convert the Unit type to an API value");
};
static final Function UNIMPLEMENTED_TO_API_CONVERTER = $ -> {
throw new UnsupportedOperationException("Cannot convert the an unimplemented type to an API value");
};
static final Map<ResourceKey<DataComponentType<?>>, DataComponentAdapter<?, ?>> ADAPTERS = new HashMap<>();
public static void bootstrap() {
@@ -136,10 +140,9 @@ public final class DataComponentAdapters {
// register(DataComponents.LOCK, PaperLockCode::new);
register(DataComponents.CONTAINER_LOOT, PaperSeededContainerLoot::new);
// TODO: REMOVE THIS... we want to build the PR... so lets just make things UNTYPED!
for (final Map.Entry<ResourceKey<DataComponentType<?>>, DataComponentType<?>> componentType : BuiltInRegistries.DATA_COMPONENT_TYPE.entrySet()) {
if (!ADAPTERS.containsKey(componentType.getKey())) {
registerUntyped((DataComponentType<Unit>) componentType.getValue());
registerUnimplemented(componentType.getValue());
}
}
}
@@ -152,6 +155,10 @@ public final class DataComponentAdapters {
registerInternal(type, Function.identity(), Function.identity(), true);
}
public static <NMS> void registerUnimplemented(final DataComponentType<NMS> type) {
registerInternal(type, UNIMPLEMENTED_TO_API_CONVERTER, DataComponentAdapter.API_TO_UNIMPLEMENTED_CONVERTER, false);
}
private static <NMS, API extends Handleable<NMS>> void register(final DataComponentType<NMS> type, final Function<NMS, API> vanillaToApi) {
registerInternal(type, vanillaToApi, Handleable::getHandle, false);
}

View File

@@ -7,7 +7,6 @@ import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.registries.BuiltInRegistries;
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.jspecify.annotations.Nullable;
@@ -23,7 +22,7 @@ public abstract class PaperDataComponentType<T, NMS> implements DataComponentTyp
}
public static DataComponentType minecraftToBukkit(final net.minecraft.core.component.DataComponentType<?> type) {
return CraftRegistry.minecraftToBukkit(type, Registries.DATA_COMPONENT_TYPE, Registry.DATA_COMPONENT_TYPE);
return CraftRegistry.minecraftToBukkit(type, Registries.DATA_COMPONENT_TYPE);
}
public static Set<DataComponentType> minecraftToBukkit(final Set<net.minecraft.core.component.DataComponentType<?>> nmsTypes) {
@@ -78,7 +77,9 @@ public abstract class PaperDataComponentType<T, NMS> implements DataComponentTyp
if (adapter == null) {
throw new IllegalArgumentException("No adapter found for " + key);
}
if (adapter.isValued()) {
if (adapter.isUnimplemented()) {
return new Unimplemented<>(key, type, adapter);
} else if (adapter.isValued()) {
return new ValuedImpl<>(key, type, adapter);
} else {
return new NonValuedImpl<>(key, type, adapter);
@@ -106,4 +107,15 @@ public abstract class PaperDataComponentType<T, NMS> implements DataComponentTyp
super(key, type, adapter);
}
}
public static final class Unimplemented<T, NMS> extends PaperDataComponentType<T, NMS> {
public Unimplemented(
final NamespacedKey key,
final net.minecraft.core.component.DataComponentType<NMS> type,
final DataComponentAdapter<NMS, T> adapter
) {
super(key, type, adapter);
}
}
}

View File

@@ -1,6 +1,5 @@
package io.papermc.paper.datacomponent.item;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.util.MCUtil;
import java.util.List;
@@ -20,7 +19,7 @@ public record PaperBannerPatternLayers(
private static List<Pattern> convert(final net.minecraft.world.level.block.entity.BannerPatternLayers nmsPatterns) {
return MCUtil.transformUnmodifiable(nmsPatterns.layers(), input -> {
final Optional<PatternType> type = CraftRegistry.unwrapAndConvertHolder(RegistryAccess.registryAccess().getRegistry(RegistryKey.BANNER_PATTERN), input.pattern());
final Optional<PatternType> type = CraftRegistry.unwrapAndConvertHolder(RegistryKey.BANNER_PATTERN, input.pattern());
return new Pattern(Objects.requireNonNull(DyeColor.getByWoolData((byte) input.color().getId())), type.orElseThrow(() -> new IllegalStateException("Inlined banner patterns are not supported yet in the API!")));
});
}

View File

@@ -40,7 +40,7 @@ public record PaperCustomModelData(
@Override
public List<Color> colors() {
return MCUtil.transformUnmodifiable(this.impl.colors(), Color::fromRGB);
return MCUtil.transformUnmodifiable(this.impl.colors(), color -> Color.fromRGB(color & 0x00FFFFFF)); // skip alpha channel
}
static final class BuilderImpl implements CustomModelData.Builder {

View File

@@ -64,6 +64,6 @@ public class PaperDiscoveredDatapack implements DiscoveredDatapack {
@Override
public DatapackSource getSource() {
return PACK_SOURCES.computeIfAbsent(this.pack.location().source(), source -> new DatapackSourceImpl(source.toString()));
return PACK_SOURCES.get(this.pack.location().source());
}
}

View File

@@ -0,0 +1,20 @@
package io.papermc.paper.entity;
import java.util.Collection;
import java.util.Collections;
import org.bukkit.entity.Item;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public record PaperPlayerGiveResult(
@Unmodifiable Collection<ItemStack> leftovers,
@Unmodifiable Collection<Item> drops
) implements PlayerGiveResult {
public static final PlayerGiveResult EMPTY = new PaperPlayerGiveResult(
Collections.emptyList(), Collections.emptyList()
);
}

View File

@@ -4,6 +4,7 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.FlyingMob;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ambient.AmbientCreature;
import net.minecraft.world.entity.animal.AgeableWaterCreature;
import net.minecraft.world.entity.animal.WaterAnimal;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.npc.Villager;
@@ -28,7 +29,7 @@ public enum ActivationType {
* @return activation type
*/
public static ActivationType activationTypeFor(final Entity entity) {
if (entity instanceof WaterAnimal) {
if (entity instanceof WaterAnimal || entity instanceof AgeableWaterCreature) {
return ActivationType.WATER;
} else if (entity instanceof Villager) {
return ActivationType.VILLAGER;

View File

@@ -0,0 +1,90 @@
package io.papermc.paper.raytracing;
import com.google.common.base.Preconditions;
import java.util.EnumSet;
import java.util.OptionalDouble;
import java.util.function.Predicate;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class PositionedRayTraceConfigurationBuilderImpl implements PositionedRayTraceConfigurationBuilder {
public @Nullable Location start;
public @Nullable Vector direction;
public OptionalDouble maxDistance = OptionalDouble.empty();
public FluidCollisionMode fluidCollisionMode = FluidCollisionMode.NEVER;
public boolean ignorePassableBlocks;
public double raySize = 0.0D;
public @Nullable Predicate<? super Entity> entityFilter;
public @Nullable Predicate<? super Block> blockFilter;
public EnumSet<RayTraceTarget> targets = EnumSet.noneOf(RayTraceTarget.class);
@Override
public PositionedRayTraceConfigurationBuilder start(final Location start) {
Preconditions.checkArgument(start != null, "start must not be null");
this.start = start.clone();
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder direction(final Vector direction) {
Preconditions.checkArgument(direction != null, "direction must not be null");
this.direction = direction.clone();
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder maxDistance(final double maxDistance) {
Preconditions.checkArgument(maxDistance >= 0, "maxDistance must be non-negative");
this.maxDistance = OptionalDouble.of(maxDistance);
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder fluidCollisionMode(final FluidCollisionMode fluidCollisionMode) {
Preconditions.checkArgument(fluidCollisionMode != null, "fluidCollisionMode must not be null");
this.fluidCollisionMode = fluidCollisionMode;
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder ignorePassableBlocks(final boolean ignorePassableBlocks) {
this.ignorePassableBlocks = ignorePassableBlocks;
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder raySize(final double raySize) {
Preconditions.checkArgument(raySize >= 0, "raySize must be non-negative");
this.raySize = raySize;
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder entityFilter(final Predicate<? super Entity> entityFilter) {
Preconditions.checkArgument(entityFilter != null, "entityFilter must not be null");
this.entityFilter = entityFilter;
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder blockFilter(final Predicate<? super Block> blockFilter) {
Preconditions.checkArgument(blockFilter != null, "blockFilter must not be null");
this.blockFilter = blockFilter;
return this;
}
@Override
public PositionedRayTraceConfigurationBuilder targets(final RayTraceTarget first, final RayTraceTarget... others) {
Preconditions.checkArgument(first != null, "first must not be null");
Preconditions.checkArgument(others != null, "others must not be null");
this.targets = EnumSet.of(first, others);
return this;
}
}

View File

@@ -97,21 +97,21 @@ public final class PaperRegistries {
start(Registries.MENU, RegistryKey.MENU).craft(MenuType.class, CraftMenuType::new).build(),
start(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE).craft(Attribute.class, CraftAttribute::new).build(),
start(Registries.FLUID, RegistryKey.FLUID).craft(Fluid.class, CraftFluid::new).build(),
start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new).build(),
start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new, true).build(),
start(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE).craft(DataComponentTypes.class, PaperDataComponentType::of).build(),
// data-drivens
start(Registries.BIOME, RegistryKey.BIOME).craft(Biome.class, CraftBiome::new).build().delayed(),
start(Registries.STRUCTURE, RegistryKey.STRUCTURE).craft(Structure.class, CraftStructure::new).build().delayed(),
start(Registries.TRIM_MATERIAL, RegistryKey.TRIM_MATERIAL).craft(TrimMaterial.class, CraftTrimMaterial::new).build().delayed(),
start(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN).craft(TrimPattern.class, CraftTrimPattern::new).build().delayed(),
start(Registries.TRIM_MATERIAL, RegistryKey.TRIM_MATERIAL).craft(TrimMaterial.class, CraftTrimMaterial::new, true).build().delayed(),
start(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN).craft(TrimPattern.class, CraftTrimPattern::new, true).build().delayed(),
start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).writable(PaperDamageTypeRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT).craft(Wolf.Variant.class, CraftWolf.CraftVariant::new).build().delayed(),
start(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT).craft(Enchantment.class, CraftEnchantment::new).serializationUpdater(FieldRename.ENCHANTMENT_RENAME).writable(PaperEnchantmentRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG).craft(JukeboxSong.class, CraftJukeboxSong::new).build().delayed(),
start(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN).craft(PatternType.class, CraftPatternType::new).writable(PaperBannerPatternRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT).craft(Art.class, CraftArt::new).writable(PaperPaintingVariantRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.INSTRUMENT, RegistryKey.INSTRUMENT).craft(MusicInstrument.class, CraftMusicInstrument::new).build().delayed(),
start(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN).craft(PatternType.class, CraftPatternType::new, true).writable(PaperBannerPatternRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT).craft(Art.class, CraftArt::new, true).writable(PaperPaintingVariantRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.INSTRUMENT, RegistryKey.INSTRUMENT).craft(MusicInstrument.class, CraftMusicInstrument::new, true).build().delayed(),
// api-only
start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType),
@@ -153,12 +153,12 @@ public final class PaperRegistries {
@SuppressWarnings("unchecked")
public static <M, T> RegistryKey<T> registryFromNms(final ResourceKey<? extends Registry<M>> registryResourceKey) {
return (RegistryKey<T>) Objects.requireNonNull(BY_RESOURCE_KEY.get(registryResourceKey), registryResourceKey + " doesn't have an api RegistryKey").apiKey();
return (RegistryKey<T>) Objects.requireNonNull(BY_RESOURCE_KEY.get(registryResourceKey), () -> registryResourceKey + " doesn't have an api RegistryKey").apiKey();
}
@SuppressWarnings("unchecked")
public static <M, T> ResourceKey<? extends Registry<M>> registryToNms(final RegistryKey<T> registryKey) {
return (ResourceKey<? extends Registry<M>>) Objects.requireNonNull(BY_REGISTRY_KEY.get(registryKey), registryKey + " doesn't have an mc registry ResourceKey").mcKey();
return (ResourceKey<? extends Registry<M>>) Objects.requireNonNull(BY_REGISTRY_KEY.get(registryKey), () -> registryKey + " doesn't have an mc registry ResourceKey").mcKey();
}
public static <M, T> TypedKey<T> fromNms(final ResourceKey<M> resourceKey) {

View File

@@ -107,7 +107,7 @@ public class PaperRegistryAccess implements RegistryAccess {
public <M> void lockReferenceHolders(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey) {
final RegistryEntry<M, Keyed> entry = PaperRegistries.getEntry(resourceKey);
if (entry == null || !(entry.meta() instanceof final RegistryEntryMeta.ServerSide<M, Keyed> serverSide) || !serverSide.registryTypeMapper().supportsDirectHolders()) {
if (entry == null || !(entry.meta() instanceof final RegistryEntryMeta.ServerSide<M, Keyed> serverSide) || !serverSide.registryTypeMapper().constructorUsesHolder()) {
return;
}
final CraftRegistry<?, M> registry = (CraftRegistry<?, M>) this.getRegistry(entry.apiKey());

View File

@@ -24,7 +24,7 @@ public final class InlinedRegistryBuilderProviderImpl implements InlinedRegistry
Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders");
final PaperRegistryBuilderFactory<M, A, B> builderFactory = new PaperRegistryBuilderFactory<>(Conversions.global(), buildableMeta.builderFiller(), CraftRegistry.getMinecraftRegistry(buildableMeta.mcKey())::getValue);
value.accept(builderFactory);
return buildableMeta.registryTypeMapper().convertDirectHolder(Holder.direct(builderFactory.requireBuilder().build()));
return buildableMeta.registryTypeMapper().createBukkit(Holder.direct(builderFactory.requireBuilder().build()));
}
@Override

View File

@@ -1,11 +1,9 @@
package io.papermc.paper.registry.data;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.data.util.Checks;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.set.PaperRegistrySets;
@@ -189,7 +187,6 @@ public class PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry {
@Override
public Builder anvilCost(final @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost) {
Preconditions.checkArgument(anvilCost >= 0, "anvilCost must be non-negative");
this.anvilCost = OptionalInt.of(asArgumentMin(anvilCost, "anvilCost", 0));
return this;
}

View File

@@ -42,7 +42,11 @@ public class RegistryEntryBuilder<M, A extends Keyed> { // TODO remove Keyed
}
public CraftStage<M, A> craft(final Class<?> classToPreload, final Function<Holder<M>, ? extends A> minecraftToBukkit) {
return new CraftStage<>(this.mcKey, this.apiKey, classToPreload, new RegistryTypeMapper<>(minecraftToBukkit));
return this.craft(classToPreload, minecraftToBukkit, false);
}
public CraftStage<M, A> craft(final Class<?> classToPreload, final Function<Holder<M>, ? extends A> minecraftToBukkit, final boolean allowDirect) {
return new CraftStage<>(this.mcKey, this.apiKey, classToPreload, new RegistryTypeMapper<>(minecraftToBukkit, allowDirect));
}
public static final class CraftStage<M, A extends Keyed> extends RegistryEntryBuilder<M, A> { // TODO remove Keyed

View File

@@ -60,7 +60,7 @@ public sealed interface RegistryEntryMeta<M, A extends Keyed> permits RegistryEn
) implements ServerSide<M, A> { // TODO remove Keyed
public Craft {
Preconditions.checkArgument(!classToPreload.getPackageName().startsWith("net.minecraft"), classToPreload + " should not be in the net.minecraft package as the class-to-preload");
Preconditions.checkArgument(!classToPreload.getPackageName().startsWith("net.minecraft"), "%s should not be in the net.minecraft package as the class-to-preload", classToPreload);
}
}

View File

@@ -1,7 +1,7 @@
package io.papermc.paper.registry.entry;
import com.google.common.base.Preconditions;
import com.mojang.datafixers.util.Either;
import io.papermc.paper.util.MCUtil;
import java.util.function.BiFunction;
import java.util.function.Function;
import net.minecraft.core.Holder;
@@ -9,29 +9,40 @@ import org.bukkit.NamespacedKey;
public final class RegistryTypeMapper<M, A> {
final Either<BiFunction<? super NamespacedKey, M, ? extends A>, Function<Holder<M>, ? extends A>> minecraftToBukkit;
private static <M, A> Function<Holder<M>, ? extends A> wrap(final BiFunction<? super NamespacedKey, M, ? extends A> byValueCreator) {
return holder -> {
if (!(holder instanceof final Holder.Reference<M> reference)) {
throw new IllegalArgumentException("This type does not support direct holders: " + holder);
}
return byValueCreator.apply(MCUtil.fromResourceKey(reference.key()), reference.value());
};
}
final Either<Function<Holder<M>, ? extends A>, BiFunction<? super NamespacedKey, M, ? extends A>> minecraftToBukkit;
private final boolean supportsDirectHolders;
public RegistryTypeMapper(final BiFunction<? super NamespacedKey, M, ? extends A> byValueCreator) {
this.minecraftToBukkit = Either.left(byValueCreator);
this.minecraftToBukkit = Either.right(byValueCreator);
this.supportsDirectHolders = false;
}
public RegistryTypeMapper(final Function<Holder<M>, ? extends A> byHolderCreator) {
this.minecraftToBukkit = Either.right(byHolderCreator);
public RegistryTypeMapper(final Function<Holder<M>, ? extends A> byHolderCreator, final boolean supportsDirectHolders) {
this.minecraftToBukkit = Either.left(byHolderCreator);
this.supportsDirectHolders = supportsDirectHolders;
}
public A createBukkit(final NamespacedKey key, final Holder<M> minecraft) {
return this.minecraftToBukkit.map(
minecraftToBukkit -> minecraftToBukkit.apply(key, minecraft.value()),
minecraftToBukkit -> minecraftToBukkit.apply(minecraft)
);
public A createBukkit(final Holder<M> minecraft) {
return this.minecraftToBukkit.<Function<Holder<M>, ? extends A>>map(
Function.identity(),
RegistryTypeMapper::wrap
).apply(minecraft);
}
public boolean supportsDirectHolders() {
return this.minecraftToBukkit.right().isPresent();
return this.supportsDirectHolders;
}
public A convertDirectHolder(final Holder<M> directHolder) {
Preconditions.checkArgument(this.supportsDirectHolders() && directHolder.kind() == Holder.Kind.DIRECT);
return this.minecraftToBukkit.right().orElseThrow().apply(directHolder);
public boolean constructorUsesHolder() {
return this.minecraftToBukkit.left().isPresent();
}
}

View File

@@ -34,7 +34,7 @@ public final class RegistryEventMap {
@SuppressWarnings("unchecked")
public <T, E extends LifecycleEvent> LifecycleEventType<BootstrapContext, E, ?> getEventType(final RegistryKey<T> registryKey) {
return (LifecycleEventType<BootstrapContext, E, ?>) Objects.requireNonNull(this.eventTypes.get(registryKey), "No hook for " + registryKey);
return (LifecycleEventType<BootstrapContext, E, ?>) Objects.requireNonNull(this.eventTypes.get(registryKey), () -> "No hook for " + registryKey);
}
public boolean hasHandlers(final RegistryKey<?> registryKey) {

View File

@@ -49,6 +49,11 @@ public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> imple
return this.delegate().stream();
}
@Override
public int size() {
return this.delegate().size();
}
@Override
public @Nullable NamespacedKey getKey(final T value) {
return this.delegate().getKey(value);

View File

@@ -181,7 +181,7 @@ public final class FoliaAsyncScheduler implements AsyncScheduler {
private void setDelay(final ScheduledFuture<?> delay) {
this.delay = delay;
this.state = STATE_SCHEDULED_EXECUTOR;
this.state = delay == null ? STATE_SCHEDULED_EXECUTOR : STATE_ON_TIMER;
}
@Override

View File

@@ -4,6 +4,8 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import net.kyori.adventure.key.Key;
import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryOps;
@@ -25,7 +27,8 @@ public interface Holderable<M> extends Handleable<M> {
return this.getHolder().value();
}
static <T extends org.bukkit.Keyed, M> @Nullable T fromBukkitSerializationObject(final Object deserialized, final Codec<? extends Holder<M>> codec, final Registry<T> registry) { // TODO remove Keyed
static <T extends org.bukkit.Keyed, M> @Nullable T fromBukkitSerializationObject(final Object deserialized, final Codec<M> directCodec, final RegistryKey<T> registryKey) { // TODO remove Keyed
final Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
return switch (deserialized) {
case @Subst("key:value") final String string -> {
if (!(Key.parseable(string))) {
@@ -38,18 +41,18 @@ public interface Holderable<M> extends Handleable<M> {
throw new IllegalArgumentException("Cannot deserialize direct holders for " + registry);
}
final RegistryOps<JsonElement> ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(JsonOps.INSTANCE);
final Holder<M> holder = codec.decode(ops, element).getOrThrow().getFirst();
yield ((CraftRegistry<T, M>) registry).convertDirectHolder(holder);
final M holder = directCodec.decode(ops, element).getOrThrow().getFirst();
yield ((CraftRegistry<T, M>) registry).createBukkit(Holder.direct(holder));
}
default -> throw new IllegalArgumentException("Cannot deserialize " + deserialized);
};
}
default Object toBukkitSerializationObject(final Codec<? super Holder<M>> codec) {
default Object toBukkitSerializationObject(final Codec<? super M> directCodec) {
return switch (this.getHolder()) {
case final Holder.Direct<M> direct -> {
final RegistryOps<JsonElement> ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(JsonOps.INSTANCE);
yield new JsonObjectWrapper(codec.encodeStart(ops, direct).getOrThrow().getAsJsonObject());
yield new JsonObjectWrapper(directCodec.encodeStart(ops, direct.value()).getOrThrow().getAsJsonObject());
}
case final Holder.Reference<M> reference -> reference.key().location().toString();
default -> throw new IllegalArgumentException("Cannot serialize " + this.getHolder());

View File

@@ -0,0 +1,101 @@
package io.papermc.paper.util;
import com.google.common.collect.ImmutableMap;
import io.papermc.paper.configuration.GlobalConfiguration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.UnaryOperator;
import net.minecraft.Util;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.alchemy.PotionContents;
import net.minecraft.world.item.component.LodestoneTracker;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class ItemComponentSanitizer {
/*
* This returns for types, that when configured to be serialized, should instead return these objects.
* This is possibly because dropping the patched type may introduce visual changes.
*/
static final Map<DataComponentType<?>, UnaryOperator<?>> SANITIZATION_OVERRIDES = Util.make(ImmutableMap.<DataComponentType<?>, UnaryOperator<?>>builder(), (map) -> {
put(map, DataComponents.LODESTONE_TRACKER, empty(new LodestoneTracker(Optional.empty(), false))); // We need it to be present to keep the glint
put(map, DataComponents.POTION_CONTENTS, ItemComponentSanitizer::sanitizePotionContents); // Custom situational serialization
if (MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).size() > 0) {
put(map, DataComponents.ENCHANTMENTS, empty(dummyEnchantments())); // We need to keep it present to keep the glint
put(map, DataComponents.STORED_ENCHANTMENTS, empty(dummyEnchantments())); // We need to keep it present to keep the glint
}
}
).build();
@SuppressWarnings({"rawtypes", "unchecked"})
private static <T> void put(final ImmutableMap.Builder map, final DataComponentType<T> type, final UnaryOperator<T> object) {
map.put(type, object);
}
private static <T> UnaryOperator<T> empty(final T object) {
return (unused) -> object;
}
private static PotionContents sanitizePotionContents(final PotionContents potionContents) {
// We have a custom color! We can hide everything!
if (potionContents.customColor().isPresent()) {
return new PotionContents(Optional.empty(), potionContents.customColor(), List.of(), Optional.empty());
}
// WE cannot hide anything really, as the color is a mix of potion/potion contents, which can
// possibly be reversed.
return potionContents;
}
// We cant use the empty map from enchantments because we want to keep the glow
private static ItemEnchantments dummyEnchantments() {
final ItemEnchantments.Mutable obj = new ItemEnchantments.Mutable(ItemEnchantments.EMPTY);
obj.set(MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).getRandom(RandomSource.create()).orElseThrow(), 1);
return obj.toImmutable();
}
public static int sanitizeCount(final ItemObfuscationSession obfuscationSession, final ItemStack itemStack, final int count) {
if (obfuscationSession.obfuscationLevel() != ItemObfuscationSession.ObfuscationLevel.ALL) return count; // Ignore if we are not obfuscating
if (GlobalConfiguration.get().anticheat.obfuscation.items.binding.getAssetObfuscation(itemStack).sanitizeCount()) {
return 1;
} else {
return count;
}
}
public static boolean shouldDrop(final ItemObfuscationSession obfuscationSession, final DataComponentType<?> key) {
if (obfuscationSession.obfuscationLevel() != ItemObfuscationSession.ObfuscationLevel.ALL) return false; // Ignore if we are not obfuscating
final ItemStack targetItemstack = obfuscationSession.context().itemStack();
// Only drop if configured to do so.
return GlobalConfiguration.get().anticheat.obfuscation.items.binding.getAssetObfuscation(targetItemstack).patchStrategy().get(key) == ItemObfuscationBinding.BoundObfuscationConfiguration.MutationType.Drop.INSTANCE;
}
public static Optional<?> override(final ItemObfuscationSession obfuscationSession, final DataComponentType<?> key, final Optional<?> value) {
if (obfuscationSession.obfuscationLevel() != ItemObfuscationSession.ObfuscationLevel.ALL) return value; // Ignore if we are not obfuscating
// Ignore removed values
if (value.isEmpty()) {
return value;
}
final ItemStack targetItemstack = obfuscationSession.context().itemStack();
return switch (GlobalConfiguration.get().anticheat.obfuscation.items.binding.getAssetObfuscation(targetItemstack).patchStrategy().get(key)) {
case final ItemObfuscationBinding.BoundObfuscationConfiguration.MutationType.Drop ignored -> Optional.empty();
case final ItemObfuscationBinding.BoundObfuscationConfiguration.MutationType.Sanitize sanitize -> Optional.of(sanitize.sanitizer().apply(value.get()));
case null -> value;
};
}
}

View File

@@ -0,0 +1,133 @@
package io.papermc.paper.util;
import io.papermc.paper.configuration.GlobalConfiguration;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.UnaryOperator;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Required;
/**
* The item obfuscation binding is a state bound by the configured item obfuscation.
* It only hosts the bound and computed data from the global configuration.
*/
@NullMarked
public final class ItemObfuscationBinding {
public final ItemObfuscationSession.ObfuscationLevel level;
private final BoundObfuscationConfiguration base;
private final Map<ResourceLocation, BoundObfuscationConfiguration> overrides;
public ItemObfuscationBinding(final GlobalConfiguration.Anticheat.Obfuscation.Items items) {
this.level = items.enableItemObfuscation ? ItemObfuscationSession.ObfuscationLevel.ALL : ItemObfuscationSession.ObfuscationLevel.OVERSIZED;
this.base = bind(items.allModels);
final Map<ResourceLocation, BoundObfuscationConfiguration> overrides = new HashMap<>();
for (final Map.Entry<ResourceLocation, AssetObfuscationConfiguration> entry : items.modelOverrides.entrySet()) {
overrides.put(entry.getKey(), bind(entry.getValue()));
}
this.overrides = Collections.unmodifiableMap(overrides);
}
public record BoundObfuscationConfiguration(boolean sanitizeCount,
Map<DataComponentType<?>, MutationType> patchStrategy) {
sealed interface MutationType permits MutationType.Drop, MutationType.Sanitize {
enum Drop implements MutationType {
INSTANCE
}
record Sanitize(UnaryOperator sanitizer) implements MutationType {
}
}
}
@ConfigSerializable
public record AssetObfuscationConfiguration(@Required boolean sanitizeCount,
Set<DataComponentType<?>> dontObfuscate,
Set<DataComponentType<?>> alsoObfuscate) {
}
private static BoundObfuscationConfiguration bind(final AssetObfuscationConfiguration config) {
final Set<DataComponentType<?>> base = new HashSet<>(BASE_OVERRIDERS);
base.addAll(config.alsoObfuscate());
base.removeAll(config.dontObfuscate());
final Map<DataComponentType<?>, BoundObfuscationConfiguration.MutationType> finalStrategy = new HashMap<>();
// Configure what path the data component should go through, should it be dropped, or should it be sanitized?
for (final DataComponentType<?> type : base) {
// We require some special logic, sanitize it rather than dropping it.
final UnaryOperator<?> sanitizationOverride = ItemComponentSanitizer.SANITIZATION_OVERRIDES.get(type);
if (sanitizationOverride != null) {
finalStrategy.put(type, new BoundObfuscationConfiguration.MutationType.Sanitize(sanitizationOverride));
} else {
finalStrategy.put(type, BoundObfuscationConfiguration.MutationType.Drop.INSTANCE);
}
}
return new BoundObfuscationConfiguration(config.sanitizeCount(), finalStrategy);
}
public BoundObfuscationConfiguration getAssetObfuscation(final ItemStack itemStack) {
if (this.overrides.isEmpty()) {
return this.base;
}
return this.overrides.getOrDefault(itemStack.get(DataComponents.ITEM_MODEL), this.base);
}
static final Set<DataComponentType<?>> BASE_OVERRIDERS = Set.of(
DataComponents.MAX_STACK_SIZE,
DataComponents.MAX_DAMAGE,
DataComponents.DAMAGE,
DataComponents.UNBREAKABLE,
DataComponents.CUSTOM_NAME,
DataComponents.ITEM_NAME,
DataComponents.LORE,
DataComponents.RARITY,
DataComponents.ENCHANTMENTS,
DataComponents.CAN_PLACE_ON,
DataComponents.CAN_BREAK,
DataComponents.ATTRIBUTE_MODIFIERS,
DataComponents.HIDE_ADDITIONAL_TOOLTIP,
DataComponents.HIDE_TOOLTIP,
DataComponents.REPAIR_COST,
DataComponents.USE_REMAINDER,
DataComponents.FOOD,
DataComponents.DAMAGE_RESISTANT,
// Not important on the player
DataComponents.TOOL,
DataComponents.ENCHANTABLE,
DataComponents.REPAIRABLE,
DataComponents.GLIDER,
DataComponents.TOOLTIP_STYLE,
DataComponents.DEATH_PROTECTION,
DataComponents.STORED_ENCHANTMENTS,
DataComponents.MAP_ID,
DataComponents.POTION_CONTENTS,
DataComponents.SUSPICIOUS_STEW_EFFECTS,
DataComponents.WRITABLE_BOOK_CONTENT,
DataComponents.WRITTEN_BOOK_CONTENT,
DataComponents.CUSTOM_DATA,
DataComponents.ENTITY_DATA,
DataComponents.BUCKET_ENTITY_DATA,
DataComponents.BLOCK_ENTITY_DATA,
DataComponents.INSTRUMENT,
DataComponents.OMINOUS_BOTTLE_AMPLIFIER,
DataComponents.JUKEBOX_PLAYABLE,
DataComponents.LODESTONE_TRACKER,
DataComponents.FIREWORKS,
DataComponents.NOTE_BLOCK_SOUND,
DataComponents.BEES,
DataComponents.LOCK,
DataComponents.CONTAINER_LOOT
);
}

View File

@@ -0,0 +1,114 @@
package io.papermc.paper.util;
import java.util.function.UnaryOperator;
import com.google.common.base.Preconditions;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* The item obfuscation session may be started by a thread to indicate that items should be obfuscated when serialized
* for network usage.
* <p>
* A session is persistent throughout an entire thread and will be "activated" by passing an {@link ObfuscationContext}
* to start/switch context methods.
*/
@NullMarked
public class ItemObfuscationSession implements SafeAutoClosable {
static final ThreadLocal<ItemObfuscationSession> THREAD_LOCAL_SESSION = ThreadLocal.withInitial(ItemObfuscationSession::new);
public static ItemObfuscationSession currentSession() {
return THREAD_LOCAL_SESSION.get();
}
/**
* Obfuscation level on a specific context.
*/
public enum ObfuscationLevel {
NONE,
OVERSIZED,
ALL;
public boolean obfuscateOversized() {
return switch (this) {
case OVERSIZED, ALL -> true;
default -> false;
};
}
public boolean isObfuscating() {
return this != NONE;
}
}
public static ItemObfuscationSession start(final ObfuscationLevel level) {
final ItemObfuscationSession sanitizer = THREAD_LOCAL_SESSION.get();
sanitizer.switchContext(new ObfuscationContext(sanitizer, null, null, level));
return sanitizer;
}
/**
* Updates the context of the currently running session by requiring the unary operator to emit a new context
* based on the current one.
* The method expects the caller to use the withers on the context.
*
* @param contextUpdater the operator to construct the new context.
* @return the context callback to close once the context expires.
*/
public static SafeAutoClosable withContext(final UnaryOperator<ObfuscationContext> contextUpdater) {
final ItemObfuscationSession session = THREAD_LOCAL_SESSION.get();
// Don't pass any context if we are not currently sanitizing
if (!session.obfuscationLevel().isObfuscating()) return () -> {
};
final ObfuscationContext newContext = contextUpdater.apply(session.context());
Preconditions.checkState(newContext != session.context(), "withContext yielded same context instance, this will break the stack on close");
session.switchContext(newContext);
return newContext;
}
private final ObfuscationContext root = new ObfuscationContext(this, null, null, ObfuscationLevel.NONE);
private ObfuscationContext context = root;
public void switchContext(final ObfuscationContext context) {
this.context = context;
}
public ObfuscationContext context() {
return this.context;
}
@Override
public void close() {
this.context = root;
}
public ObfuscationLevel obfuscationLevel() {
return this.context.level;
}
public record ObfuscationContext(
ItemObfuscationSession parent,
@Nullable ObfuscationContext previousContext,
@Nullable ItemStack itemStack,
ObfuscationLevel level
) implements SafeAutoClosable {
public ObfuscationContext itemStack(final ItemStack itemStack) {
return new ObfuscationContext(this.parent, this, itemStack, this.level);
}
public ObfuscationContext level(final ObfuscationLevel obfuscationLevel) {
return new ObfuscationContext(this.parent, this, this.itemStack, obfuscationLevel);
}
@Override
public void close() {
// Restore the previous context when this context is closed.
this.parent().switchContext(this.previousContext);
}
}
}

View File

@@ -1,9 +1,8 @@
package io.papermc.paper.util;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import java.util.function.UnaryOperator;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.util.Mth;
@@ -12,26 +11,38 @@ import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.BundleContents;
import net.minecraft.world.item.component.ChargedProjectiles;
import net.minecraft.world.item.component.ItemContainerContents;
import org.apache.commons.lang3.math.Fraction;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public final class DataSanitizationUtil {
public final class OversizedItemComponentSanitizer {
private static final ThreadLocal<DataSanitizer> DATA_SANITIZER = ThreadLocal.withInitial(DataSanitizer::new);
public static DataSanitizer start(final boolean sanitize) {
final DataSanitizer sanitizer = DATA_SANITIZER.get();
if (sanitize) {
sanitizer.start();
}
return sanitizer;
}
public static final StreamCodec<RegistryFriendlyByteBuf, ChargedProjectiles> CHARGED_PROJECTILES = codec(ChargedProjectiles.STREAM_CODEC, DataSanitizationUtil::sanitizeChargedProjectiles);
public static final StreamCodec<RegistryFriendlyByteBuf, BundleContents> BUNDLE_CONTENTS = codec(BundleContents.STREAM_CODEC, DataSanitizationUtil::sanitizeBundleContents);
/*
These represent codecs that are meant to help get rid of possibly big items by ALWAYS hiding this data.
*/
public static final StreamCodec<RegistryFriendlyByteBuf, ChargedProjectiles> CHARGED_PROJECTILES = codec(ChargedProjectiles.STREAM_CODEC, OversizedItemComponentSanitizer::sanitizeChargedProjectiles);
public static final StreamCodec<RegistryFriendlyByteBuf, ItemContainerContents> CONTAINER = codec(ItemContainerContents.STREAM_CODEC, contents -> ItemContainerContents.EMPTY);
public static final StreamCodec<RegistryFriendlyByteBuf, BundleContents> BUNDLE_CONTENTS = new StreamCodec<>() {
@Override
public BundleContents decode(final RegistryFriendlyByteBuf buffer) {
return BundleContents.STREAM_CODEC.decode(buffer);
}
@Override
public void encode(final RegistryFriendlyByteBuf buffer, final BundleContents value) {
if (!ItemObfuscationSession.currentSession().obfuscationLevel().obfuscateOversized()) {
BundleContents.STREAM_CODEC.encode(buffer, value);
return;
}
// Disable further obfuscation to skip e.g. count.
try (final SafeAutoClosable ignored = ItemObfuscationSession.withContext(c -> c.level(ItemObfuscationSession.ObfuscationLevel.OVERSIZED))){
BundleContents.STREAM_CODEC.encode(buffer, sanitizeBundleContents(value));
}
}
};
private static <B, A> StreamCodec<B, A> codec(final StreamCodec<B, A> delegate, final UnaryOperator<A> sanitizer) {
return new DataSanitizationCodec<>(delegate, sanitizer);
}
private static ChargedProjectiles sanitizeChargedProjectiles(final ChargedProjectiles projectiles) {
if (projectiles.isEmpty()) {
@@ -39,21 +50,26 @@ public final class DataSanitizationUtil {
}
return ChargedProjectiles.of(List.of(
new ItemStack(projectiles.contains(Items.FIREWORK_ROCKET) ? Items.FIREWORK_ROCKET : Items.ARROW)
));
new ItemStack(
projectiles.contains(Items.FIREWORK_ROCKET)
? Items.FIREWORK_ROCKET
: Items.ARROW
)));
}
// Although bundles no longer change their size based on fullness, fullness is exposed in item models.
private static BundleContents sanitizeBundleContents(final BundleContents contents) {
if (contents.isEmpty()) {
return contents;
}
// Bundles change their texture based on their fullness.
// A bundles content weight may be anywhere from 0 to, basically, infinity.
// A weight of 1 is the usual maximum case
int sizeUsed = Mth.mulAndTruncate(contents.weight(), 64);
// Early out, *most* bundles should not be overfilled above a weight of one.
if (sizeUsed <= 64) return new BundleContents(List.of(new ItemStack(Items.PAPER, Math.max(1, sizeUsed))));
if (sizeUsed <= 64) {
return new BundleContents(List.of(new ItemStack(Items.PAPER, Math.max(1, sizeUsed))));
}
final List<ItemStack> sanitizedRepresentation = new ObjectArrayList<>(sizeUsed / 64 + 1);
while (sizeUsed > 0) {
@@ -66,20 +82,19 @@ public final class DataSanitizationUtil {
return new BundleContents(sanitizedRepresentation);
}
private static <B, A> StreamCodec<B, A> codec(final StreamCodec<B, A> delegate, final UnaryOperator<A> sanitizer) {
return new DataSanitizationCodec<>(delegate, sanitizer);
}
private record DataSanitizationCodec<B, A>(StreamCodec<B, A> delegate, UnaryOperator<A> sanitizer) implements StreamCodec<B, A> {
// Codec used to override encoding if sanitization is enabled
private record DataSanitizationCodec<B, A>(StreamCodec<B, A> delegate,
UnaryOperator<A> sanitizer) implements StreamCodec<B, A> {
@Override
public @NonNull A decode(final @NonNull B buf) {
return this.delegate.decode(buf);
}
@SuppressWarnings("resource")
@Override
public void encode(final @NonNull B buf, final @NonNull A value) {
if (!DATA_SANITIZER.get().value().get()) {
if (!ItemObfuscationSession.currentSession().obfuscationLevel().obfuscateOversized()) {
this.delegate.encode(buf, value);
} else {
this.delegate.encode(buf, this.sanitizer.apply(value));
@@ -87,22 +102,4 @@ public final class DataSanitizationUtil {
}
}
public record DataSanitizer(AtomicBoolean value) implements AutoCloseable {
public DataSanitizer() {
this(new AtomicBoolean(false));
}
public void start() {
this.value.compareAndSet(false, true);
}
@Override
public void close() {
this.value.compareAndSet(true, false);
}
}
private DataSanitizationUtil() {
}
}

View File

@@ -0,0 +1,10 @@
package io.papermc.paper.util;
/**
* A type of {@link AutoCloseable} that does not throw a checked exception.
*/
public interface SafeAutoClosable extends AutoCloseable {
@Override
void close();
}

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);
}

Some files were not shown because too many files have changed in this diff Show More