Merge remote-tracking branch 'upstream/main' into update/1.21.4
This commit is contained in:
@@ -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() {}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!")));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,7 +31,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CraftTippedArrow";
|
||||
return "CraftArrow";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user