diff --git a/AITask.md b/AITask.md new file mode 100644 index 0000000..7a202b3 --- /dev/null +++ b/AITask.md @@ -0,0 +1,18 @@ +# 任务:多平台模组项目迁移(1.21.1 → 26.1.2) + +## 背景 +我的 Minecraft 模组当前基于 **MultiLoader 1.21.1**(对应 Minecraft 1.21.1)。 +目标版本为 **26.1.2**(对应 Minecraft 26.1.2)。 +从 1.21.1 升级到 26.1.x,期间 API 发生了破坏性变更。 + +# 请只关注 common、fabric、neoforge文件下的src文件里的java部分 +其它以外的文件已经配置好了 + +## 核心变更参考 +请参考 项目里的中的迁移指南(Primers):[Doc](/primers-doc) + +特别关注以下版本的变更(按从低到高依次应用): +- 1.21 → 1.21.1 +- 1.21.1 → 1.21.2 +- 1.21.2 → 1.21.3 +- 依此类推,直到 26.1.2 对应的版本 diff --git a/common/src/main/java/top/r3944realms/lib39/core/command/ICommandHelpManager.java b/common/src/main/java/top/r3944realms/lib39/core/command/ICommandHelpManager.java index 41d2ea1..cb3d601 100644 --- a/common/src/main/java/top/r3944realms/lib39/core/command/ICommandHelpManager.java +++ b/common/src/main/java/top/r3944realms/lib39/core/command/ICommandHelpManager.java @@ -291,12 +291,10 @@ public interface ICommandHelpManager { return Component.literal(rootCommand) .withStyle(ChatFormatting.AQUA) .withStyle(Style.EMPTY - .withClickEvent(new ClickEvent( - ClickEvent.Action.SUGGEST_COMMAND, + .withClickEvent(new ClickEvent.SuggestCommand( rootCommand + " " )) - .withHoverEvent(new HoverEvent( - HoverEvent.Action.SHOW_TEXT, + .withHoverEvent(new HoverEvent.ShowText( Component.translatable(Lib39LangKey.Message.HELP_HOVER_COPY_TIP.getKey()) )) ); @@ -316,12 +314,10 @@ public interface ICommandHelpManager { MutableComponent commandName = Component.literal(node.getName()) .withStyle(ChatFormatting.DARK_AQUA) .withStyle(Style.EMPTY - .withClickEvent(new ClickEvent( - ClickEvent.Action.SUGGEST_COMMAND, + .withClickEvent(new ClickEvent.SuggestCommand( suggestedCommand )) - .withHoverEvent(new HoverEvent( - HoverEvent.Action.SHOW_TEXT, + .withHoverEvent(new HoverEvent.ShowText( Component.translatable(Lib39LangKey.Message.HELP_HOVER_COPY_TIP.getKey(), suggestedCommand) )) ); @@ -370,12 +366,10 @@ public interface ICommandHelpManager { // 为按钮添加点击事件 toggleButton.withStyle(Style.EMPTY - .withClickEvent(new ClickEvent( - ClickEvent.Action.RUN_COMMAND, + .withClickEvent(new ClickEvent.RunCommand( "/" + getCommandHead() + " help toggle " + node.hashCode() )) - .withHoverEvent(new HoverEvent( - HoverEvent.Action.SHOW_TEXT, + .withHoverEvent(new HoverEvent.ShowText( Component.translatable(Lib39LangKey.Message.HELP_CLICK_EXPAND.getKey()) .withStyle(ChatFormatting.GRAY) )) @@ -467,11 +461,12 @@ public interface ICommandHelpManager { ).withStyle(ChatFormatting.GRAY)); collapsedInfo.withStyle(Style.EMPTY - .withClickEvent(new ClickEvent( - ClickEvent.Action.RUN_COMMAND, + .withClickEvent(new ClickEvent.RunCommand( "/" + getCommandHead() + " help toggle " + node.hashCode() )) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable(Lib39LangKey.Message.HELP_CLICK_EXPAND.getKey()))) + .withHoverEvent(new HoverEvent.ShowText( + Component.translatable(Lib39LangKey.Message.HELP_CLICK_EXPAND.getKey()) + )) ); result.add(collapsedInfo.append(Component.literal(NEWLINE))); diff --git a/common/src/main/java/top/r3944realms/lib39/core/sync/ILib39SyncDataHolder.java b/common/src/main/java/top/r3944realms/lib39/core/sync/ILib39SyncDataHolder.java index 64a36bc..06c5696 100644 --- a/common/src/main/java/top/r3944realms/lib39/core/sync/ILib39SyncDataHolder.java +++ b/common/src/main/java/top/r3944realms/lib39/core/sync/ILib39SyncDataHolder.java @@ -3,8 +3,12 @@ package top.r3944realms.lib39.core.sync; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + /** * The interface Lib 39 sync data holder. */ @@ -26,24 +30,25 @@ public interface ILib39SyncDataHolder { /** * Lib 39 inject save sync data compound tag. * - * @param tag the tag - * @return the compound tag + * @param output the tag */ - default CompoundTag lib39$injectSaveSyncData(CompoundTag tag) { + default void lib39$injectSaveSyncData(ValueOutput output) { if (lib39$getSyncData() != null && !lib39$getSyncData().isEmpty()) { - tag.put("Lib39Data", lib39$getSyncData()); + output.store("Lib39Data", CompoundTag.CODEC, lib39$getSyncData()); } - return tag; } /** * Lib 39 inject load sync data. * - * @param tag the tag + * @param input the input */ - default void lib39$injectLoadSyncData(@NotNull CompoundTag tag) { - if (tag.contains("Lib39Data", Tag.TAG_COMPOUND)) { - lib39$setSyncData(tag.getCompound("Lib39Data")); + default void lib39$injectLoadSyncData(@NotNull ValueInput input) { + Optional lib39DataOpt = input.child("Lib39Data"); + if (lib39DataOpt.isPresent()) { + ValueInput valueInput = lib39DataOpt.get(); + Optional lib39Data = valueInput.read("Lib39Data", CompoundTag.CODEC); + lib39$setSyncData(lib39Data.get()); } } @@ -63,7 +68,10 @@ public interface ILib39SyncDataHolder { */ default void loadSyncData(NBTEntitySyncData syncData) { if (syncData != null && lib39$getSyncData().contains(syncData.id.toDebugFileName())) { - syncData.deserializeNBT(lib39$getSyncData().getCompound(syncData.id.toDebugFileName())); + Optional compound = lib39$getSyncData().getCompound(syncData.id.toDebugFileName()); + if (compound.isPresent()) { + syncData.deserializeNBT(compound.get()); + } } } diff --git a/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractFabricItem.java b/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractFabricItem.java index 92fca2c..1f7abdd 100644 --- a/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractFabricItem.java +++ b/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractFabricItem.java @@ -3,7 +3,7 @@ package top.r3944realms.lib39.example.content.item; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; @@ -11,16 +11,16 @@ import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.TooltipDisplay; import net.minecraft.world.level.Level; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData; -import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; /** * 用于执行数据查询并检查同步状态的物品 @@ -39,7 +39,7 @@ public abstract class AbstractFabricItem extends Item { } @Override - public @NotNull InteractionResultHolder use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) { + public InteractionResult use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); if (level.isClientSide()) { @@ -64,10 +64,10 @@ public abstract class AbstractFabricItem extends Item { } // 添加冷却时间 - player.getCooldowns().addCooldown(this, 20); // 1秒冷却 + player.getCooldowns().addCooldown(itemStack, 20); // 1秒冷却 } - return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide()); + return InteractionResult.SUCCESS.heldItemTransformedTo(itemStack); } /** @@ -193,7 +193,7 @@ public abstract class AbstractFabricItem extends Item { Thread.sleep(3000); // 在服务器线程中执行结果处理 - player.server.execute(() -> { + player.level().getServer().execute(() -> { displayServerSingleEndResults(player, target); }); @@ -203,7 +203,7 @@ public abstract class AbstractFabricItem extends Item { player.sendSystemMessage(Component.literal("§c数据查询被中断")); } catch (Exception e) { Lib39.LOGGER.error("[FabricItem] 数据查询出错", e); - player.server.execute(() -> + player.level().getServer().execute(() -> player.sendSystemMessage(Component.literal("§c数据查询出错: " + e.getMessage())) ); } @@ -530,29 +530,28 @@ public abstract class AbstractFabricItem extends Item { } @Override - public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { - super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); - - tooltipComponents.add(Component.literal("§7右键点击在 3 秒后执行")); - tooltipComponents.add(Component.literal("§7§e准星瞄准生物§7的数据查询")); - tooltipComponents.add(Component.literal("§7§oShift + 右键§7进行§e客户端-服务器双端同步检查§7")); - tooltipComponents.add(Component.literal("")); - tooltipComponents.add(Component.literal("§6查询延迟: §e3秒")); - tooltipComponents.add(Component.literal("§6瞄准距离: §e20格")); - tooltipComponents.add(Component.literal("§6冷却时间: §e1秒")); - tooltipComponents.add(Component.literal("")); - tooltipComponents.add(Component.literal("§a单端查询内容:")); - tooltipComponents.add(Component.literal("§7- 基础数据字段")); - tooltipComponents.add(Component.literal("§7- 自定义数据结构")); - tooltipComponents.add(Component.literal("§7- 数据验证状态")); - tooltipComponents.add(Component.literal("§7- 同步状态信息")); - tooltipComponents.add(Component.literal("")); - tooltipComponents.add(Component.literal("§e双端同步检查:")); - tooltipComponents.add(Component.literal("§7- 客户端和服务器同时查询")); - tooltipComponents.add(Component.literal("§7- 字段级同步状态对比")); - tooltipComponents.add(Component.literal("§7- 总体同步率计算")); - tooltipComponents.add(Component.literal("§7- 双端数据状态差异")); - tooltipComponents.add(Component.literal("§7- 同步建议")); + public void appendHoverText(ItemStack itemStack, TooltipContext context, TooltipDisplay display, Consumer componentConsumer, TooltipFlag tooltipFlag) { + super.appendHoverText(itemStack, context, display, componentConsumer, tooltipFlag); + componentConsumer.accept(Component.literal("§7右键点击在 3 秒后执行")); + componentConsumer.accept(Component.literal("§7§e准星瞄准生物§7的数据查询")); + componentConsumer.accept(Component.literal("§7§oShift + 右键§7进行§e客户端-服务器双端同步检查§7")); + componentConsumer.accept(Component.literal("")); + componentConsumer.accept(Component.literal("§6查询延迟: §e3秒")); + componentConsumer.accept(Component.literal("§6瞄准距离: §e20格")); + componentConsumer.accept(Component.literal("§6冷却时间: §e1秒")); + componentConsumer.accept(Component.literal("")); + componentConsumer.accept(Component.literal("§a单端查询内容:")); + componentConsumer.accept(Component.literal("§7- 基础数据字段")); + componentConsumer.accept(Component.literal("§7- 自定义数据结构")); + componentConsumer.accept(Component.literal("§7- 数据验证状态")); + componentConsumer.accept(Component.literal("§7- 同步状态信息")); + componentConsumer.accept(Component.literal("")); + componentConsumer.accept(Component.literal("§e双端同步检查:")); + componentConsumer.accept(Component.literal("§7- 客户端和服务器同时查询")); + componentConsumer.accept(Component.literal("§7- 字段级同步状态对比")); + componentConsumer.accept(Component.literal("§7- 总体同步率计算")); + componentConsumer.accept(Component.literal("§7- 双端数据状态差异")); + componentConsumer.accept(Component.literal("§7- 同步建议")); } } \ No newline at end of file diff --git a/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractNeoForgeItem.java b/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractNeoForgeItem.java index 7b9b006..e88abe8 100644 --- a/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractNeoForgeItem.java +++ b/common/src/main/java/top/r3944realms/lib39/example/content/item/AbstractNeoForgeItem.java @@ -3,7 +3,7 @@ package top.r3944realms.lib39.example.content.item; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; @@ -11,16 +11,16 @@ import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.TooltipDisplay; import net.minecraft.world.level.Level; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData; -import java.util.List; import java.util.Random; +import java.util.function.Consumer; /** * 用于对准星生物触发 TestSyncData 随机变换的物品 @@ -40,7 +40,7 @@ public abstract class AbstractNeoForgeItem extends Item { } @Override - public @NotNull InteractionResultHolder use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) { + public InteractionResult use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); if (!level.isClientSide()) { @@ -55,10 +55,10 @@ public abstract class AbstractNeoForgeItem extends Item { } // 添加冷却时间 - player.getCooldowns().addCooldown(this, 20); // 1秒冷却 + player.getCooldowns().addCooldown(itemStack, 20); // 1秒冷却 } - return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide()); + return InteractionResult.SUCCESS.heldItemTransformedTo(itemStack); } /** @@ -268,27 +268,25 @@ public abstract class AbstractNeoForgeItem extends Item { } @Override - public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { - super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); - - - tooltipComponents.add(Component.literal("§7右键点击触发§e准星瞄准生物§7的")); - tooltipComponents.add(Component.literal("§7测试数据随机变换")); - tooltipComponents.add(Component.literal("§7§oShift + 右键§7操作§e自身§7数据")); - tooltipComponents.add(Component.literal("")); - tooltipComponents.add(Component.literal("§6冷却时间: §e1秒")); - tooltipComponents.add(Component.literal("§6瞄准距离: §e20格")); - tooltipComponents.add(Component.literal("")); - tooltipComponents.add(Component.literal("§a变换类型:")); - tooltipComponents.add(Component.literal("§7- 完全随机数据")); - tooltipComponents.add(Component.literal("§7- 字符串+计数器")); - tooltipComponents.add(Component.literal("§7- 数值数据")); - tooltipComponents.add(Component.literal("§7- 自定义数据")); - tooltipComponents.add(Component.literal("§7- 重置默认值")); - tooltipComponents.add(Component.literal("§7- 玩家专属数据")); - tooltipComponents.add(Component.literal("")); - tooltipComponents.add(Component.literal("§e自身操作特性:")); - tooltipComponents.add(Component.literal("§7- 显示数据预览")); - tooltipComponents.add(Component.literal("§7- 玩家专属数据变换")); + public void appendHoverText(ItemStack itemStack, TooltipContext context, TooltipDisplay display, Consumer componentConsumer, TooltipFlag tooltipFlag) { + super.appendHoverText(itemStack, context, display, componentConsumer, tooltipFlag); + componentConsumer.accept(Component.literal("§7右键点击触发§e准星瞄准生物§7的")); + componentConsumer.accept(Component.literal("§7测试数据随机变换")); + componentConsumer.accept(Component.literal("§7§oShift + 右键§7操作§e自身§7数据")); + componentConsumer.accept(Component.literal("")); + componentConsumer.accept(Component.literal("§6冷却时间: §e1秒")); + componentConsumer.accept(Component.literal("§6瞄准距离: §e20格")); + componentConsumer.accept(Component.literal("")); + componentConsumer.accept(Component.literal("§a变换类型:")); + componentConsumer.accept(Component.literal("§7- 完全随机数据")); + componentConsumer.accept(Component.literal("§7- 字符串+计数器")); + componentConsumer.accept(Component.literal("§7- 数值数据")); + componentConsumer.accept(Component.literal("§7- 自定义数据")); + componentConsumer.accept(Component.literal("§7- 重置默认值")); + componentConsumer.accept(Component.literal("§7- 玩家专属数据")); + componentConsumer.accept(Component.literal("")); + componentConsumer.accept(Component.literal("§e自身操作特性:")); + componentConsumer.accept(Component.literal("§7- 显示数据预览")); + componentConsumer.accept(Component.literal("§7- 玩家专属数据变换")); } } \ No newline at end of file diff --git a/common/src/main/java/top/r3944realms/lib39/example/content/item/ForgeItem.java b/common/src/main/java/top/r3944realms/lib39/example/content/item/ForgeItem.java index dde81e7..fe92aab 100644 --- a/common/src/main/java/top/r3944realms/lib39/example/content/item/ForgeItem.java +++ b/common/src/main/java/top/r3944realms/lib39/example/content/item/ForgeItem.java @@ -2,10 +2,10 @@ package top.r3944realms.lib39.example.content.item; import net.minecraft.client.Minecraft; import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResultHolder; + +import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; import top.r3944realms.lib39.example.client.screen.ForgeScreen; @@ -25,7 +25,7 @@ public class ForgeItem extends Item { } @Override - public @NotNull InteractionResultHolder use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand usedHand) { + public @NotNull InteractionResult use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand usedHand) { if (level.isClientSide() && usedHand == InteractionHand.MAIN_HAND) { ClientOpt.clientUse(usedHand); } diff --git a/common/src/main/java/top/r3944realms/lib39/mixin/carryon/MixinCarriedObjectRender.java b/common/src/main/java/top/r3944realms/lib39/mixin/carryon/MixinCarriedObjectRender.java index 0de324d..055209f 100644 --- a/common/src/main/java/top/r3944realms/lib39/mixin/carryon/MixinCarriedObjectRender.java +++ b/common/src/main/java/top/r3944realms/lib39/mixin/carryon/MixinCarriedObjectRender.java @@ -1,7 +1,6 @@ package top.r3944realms.lib39.mixin.carryon; import com.llamalad7.mixinextras.sugar.Local; -import com.mojang.authlib.GameProfile; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -12,11 +11,12 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; import top.r3944realms.lib39.content.item.DollItem; import top.r3944realms.lib39.util.GameProfileHelper; -import top.r3944realms.lib39.util.nbt.NBTReader; +import top.r3944realms.lib39.util.storage.nbt.NBTReader; import tschipp.carryon.client.render.CarriedObjectRender; import tschipp.carryon.common.carry.CarryOnData; import tschipp.carryon.common.carry.CarryOnDataManager; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; /** @@ -39,11 +39,11 @@ public class MixinCarriedObjectRender { if (renderStack.getItem() instanceof DollItem) { CarryOnData carry = CarryOnDataManager.getCarryData(player); if (carry != null) { - var compound = carry.getNbt().getCompound("tile"); - var profileHolder = new AtomicReference(); + Optional compound = carry.getNbt().getCompound("tile"); + AtomicReference profileHolder = new AtomicReference<>(); NBTReader.ofOpt(compound).gameProfile("profile", profileHolder::set); if (profileHolder.get() != null) { - GameProfileHelper.saveProfileToItemStack(renderStack, new ResolvableProfile(profileHolder.get())); + GameProfileHelper.saveProfileToItemStack(renderStack,profileHolder.get()); } } } diff --git a/common/src/main/java/top/r3944realms/lib39/mixin/minecraft/MixinEntity.java b/common/src/main/java/top/r3944realms/lib39/mixin/minecraft/MixinEntity.java index a6b04d8..09e5d8f 100644 --- a/common/src/main/java/top/r3944realms/lib39/mixin/minecraft/MixinEntity.java +++ b/common/src/main/java/top/r3944realms/lib39/mixin/minecraft/MixinEntity.java @@ -4,7 +4,10 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -43,19 +46,20 @@ public abstract class MixinEntity implements ILib39SyncDataHolder { } @WrapMethod(method = "saveWithoutId") - private CompoundTag wrapSave(CompoundTag compound, @NotNull Operation original) { + private void wrapSave(ValueOutput output, @NonNull Operation original) { Services.PLATFORM.getUtilHelper().getSyncData2Manager().forEach((id, manager) -> { ISyncData o = manager.getSyncMap().get(getUUID()); if (o instanceof NBTEntitySyncData syncData) { saveSyncData(syncData); } }); - return lib39$injectSaveSyncData(original.call(compound)); + original.call(output); + lib39$injectSaveSyncData(output); } @WrapMethod(method = "load") - private void warpLoad(CompoundTag compound, @NotNull Operation original) { - original.call(compound); - lib39$injectLoadSyncData(compound); + private void warpLoad(ValueInput input, @NonNull Operation original) { + original.call(input); + lib39$injectLoadSyncData(input); } } diff --git a/common/src/main/java/top/r3944realms/lib39/util/GameProfileHelper.java b/common/src/main/java/top/r3944realms/lib39/util/GameProfileHelper.java index 2717706..3cadfa8 100644 --- a/common/src/main/java/top/r3944realms/lib39/util/GameProfileHelper.java +++ b/common/src/main/java/top/r3944realms/lib39/util/GameProfileHelper.java @@ -10,11 +10,14 @@ import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.core.component.DataComponents; import net.minecraft.resources.Identifier; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.PlayerModelType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.block.entity.SkullBlockEntity; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; import top.r3944realms.lib39.Lib39; import java.nio.charset.StandardCharsets; @@ -28,7 +31,8 @@ public class GameProfileHelper { /** * 获取 GameProfile(通过玩家名) */ - public static ResolvableProfile fetchGameProfileByName(String name) { + @Contract("_ -> new") + public static @NonNull ResolvableProfile fetchGameProfileByName(String name) { return ResolvableProfile.createUnresolved(name); } @@ -36,43 +40,35 @@ public class GameProfileHelper { * 获取 GameProfile(通过 UUID) * 直接复用 SkullBlockEntity 的实现 */ - public static ResolvableProfile fetchGameProfileByUUID(UUID uuid) { + @Contract("_ -> new") + public static @NonNull ResolvableProfile fetchGameProfileByUUID(UUID uuid) { return ResolvableProfile.createUnresolved(uuid); } /** - * 异步获取 GameProfile(自动识别类型) + * 获取 GameProfile(自动识别类型) */ @SuppressWarnings("unchecked") - public static CompletableFuture> fetchGameProfile(T identifier) { + public static ResolvableProfile fetchGameProfile(T identifier) { if (identifier instanceof UUID uuid) { return fetchGameProfileByUUID(uuid); } else if (identifier instanceof String name) { return fetchGameProfileByName(name); + } else if (identifier instanceof GameProfile gameProfile) { + return ResolvableProfile.createResolved(gameProfile); } - return CompletableFuture.completedFuture(Optional.empty()); + throw new IllegalArgumentException("Identifier type not supported: " + identifier.getClass()); } /** - * 从 ItemStack 异步获取完整的 GameProfile - * 需要配合 ResolvableProfile 使用 + * 从 ItemStack 获取完整的 ResolvableProfile */ - public static CompletableFuture> fetchProfileFromItemStack(@NotNull ItemStack stack) { + public static ResolvableProfile fetchProfileFromItemStack(@NotNull ItemStack stack) { ResolvableProfile resolvable = stack.get(DataComponents.PROFILE); if (resolvable == null) { - return CompletableFuture.completedFuture(Optional.empty()); + return ResolvableProfile.Static.EMPTY; } - - // 如果已经有 UUID,直接用 SkullBlockEntity 的方法 - GameProfile profile = resolvable.gameProfile(); - if (profile != null && profile.getId() != null) { - return fetchGameProfileByUUID(profile.getId()); - } - - // 否则异步解析 - return resolvable.resolve().thenApply(resolved -> - Optional.ofNullable(resolved.gameProfile()) - ); + return resolvable; } /** * Client Only Class @@ -87,7 +83,7 @@ public class GameProfileHelper { public static @NotNull Identifier resolveSkinTexture(@NotNull GameProfile gameProfile) { return IClientOnly.check(() -> Minecraft.getInstance().getSkinManager() - .getInsecureSkin(gameProfile)).texture(); + .createLookup(gameProfile, true)).get().body().texturePath(); } /** @@ -117,7 +113,7 @@ public class GameProfileHelper { PlayerInfo playerInfo = Objects.requireNonNull(Minecraft.getInstance() .getConnection()) .getPlayerInfo(clientPlayer.getUUID()); - return playerInfo != null && "slim".equals(playerInfo.getSkin().model().id()); + return playerInfo != null && PlayerModelType.SLIM.equals(playerInfo.getSkin().model()); } return false; }); @@ -131,12 +127,12 @@ public class GameProfileHelper { */ public static @NotNull String getSkinModelName(@NotNull Player player) { return IClientOnly.check(() -> { - if (player.level().isClientSide && player instanceof AbstractClientPlayer) { + if (player.level().isClientSide() && player instanceof AbstractClientPlayer) { PlayerInfo info = Objects.requireNonNull(Minecraft.getInstance().getConnection()) .getPlayerInfo(player.getUUID()); - return info != null ? info.getSkin().model().id() : "default"; + return info != null ? info.getSkin().model().name() : PlayerModelType.WIDE.name(); } - return "default"; + return PlayerModelType.WIDE.name(); }); } @@ -175,7 +171,7 @@ public class GameProfileHelper { * @return the boolean */ public static boolean hasSlimArms(@NotNull Player player) { - if (player.level().isClientSide) { + if (player.level().isClientSide()) { return hasSlimArmsClient(player); } else { return hasSlimArmsServer(player); @@ -190,7 +186,7 @@ public class GameProfileHelper { // 服务器端判断 private static boolean hasSlimArmsServer(@NotNull Player player) { GameProfile profile = player.getGameProfile(); - for (Property property : profile.getProperties().get("textures")) { + for (Property property : profile.properties().get("textures")) { try { String json = new String(Base64.getDecoder().decode(property.value())); JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); @@ -232,7 +228,7 @@ public class GameProfileHelper { } // 获取textures属性 - Collection textures = profile.getProperties().get("textures"); + Collection textures = profile.properties().get("textures"); if (textures.isEmpty()) { return false; // 没有皮肤数据,使用默认 } @@ -304,7 +300,7 @@ public class GameProfileHelper { return "default"; } - Collection textures = profile.getProperties().get("textures"); + Collection textures = profile.properties().get("textures"); if (textures.isEmpty()) { return "default"; } diff --git a/common/src/main/java/top/r3944realms/lib39/util/PlantHelper.java b/common/src/main/java/top/r3944realms/lib39/util/PlantHelper.java index 6a88682..43a20e7 100644 --- a/common/src/main/java/top/r3944realms/lib39/util/PlantHelper.java +++ b/common/src/main/java/top/r3944realms/lib39/util/PlantHelper.java @@ -11,6 +11,7 @@ import top.r3944realms.lib39.Lib39; /** * The type Plant helper. */ +//todo: 需添加26.1.2的新的Plant,部分条目可能需要重命名 public class PlantHelper { /** * The enum Plant. diff --git a/common/src/main/java/top/r3944realms/lib39/util/nbt/NBTReader.java b/common/src/main/java/top/r3944realms/lib39/util/storage/nbt/NBTReader.java similarity index 94% rename from common/src/main/java/top/r3944realms/lib39/util/nbt/NBTReader.java rename to common/src/main/java/top/r3944realms/lib39/util/storage/nbt/NBTReader.java index d98365e..8527513 100644 --- a/common/src/main/java/top/r3944realms/lib39/util/nbt/NBTReader.java +++ b/common/src/main/java/top/r3944realms/lib39/util/storage/nbt/NBTReader.java @@ -13,12 +13,11 @@ * along with this program. If not, see . */ -package top.r3944realms.lib39.util.nbt; +package top.r3944realms.lib39.util.storage.nbt; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -26,7 +25,6 @@ import org.jetbrains.annotations.Nullable; import org.jspecify.annotations.NonNull; import java.util.Optional; -import java.util.UUID; import java.util.function.Consumer; /** @@ -434,6 +432,35 @@ public class NBTReader { } return this; } + + public NBTReader gameProfile(String key, Consumer setter) { + Optional read = nbt.read(key, ResolvableProfile.CODEC); + if (read.isPresent()) { + try { + ResolvableProfile profile = read.get(); + setter.accept(profile); + return this; + } catch (IllegalArgumentException ignored) { + + } + } + return this; + } + + public NBTReader gameProfile(String key, Consumer setter, ResolvableProfile defaultValue) { + Optional read = nbt.read(key, ResolvableProfile.CODEC); + if (read.isPresent()) { + try { + ResolvableProfile profile = read.get(); + setter.accept(profile); + return this; + } catch (IllegalArgumentException ignored) { + + } + } + setter.accept(defaultValue); + return this; + } /** * Vec 3 nbt reader. * diff --git a/common/src/main/java/top/r3944realms/lib39/util/nbt/NBTWriter.java b/common/src/main/java/top/r3944realms/lib39/util/storage/nbt/NBTWriter.java similarity index 98% rename from common/src/main/java/top/r3944realms/lib39/util/nbt/NBTWriter.java rename to common/src/main/java/top/r3944realms/lib39/util/storage/nbt/NBTWriter.java index ec97619..d99f769 100644 --- a/common/src/main/java/top/r3944realms/lib39/util/nbt/NBTWriter.java +++ b/common/src/main/java/top/r3944realms/lib39/util/storage/nbt/NBTWriter.java @@ -1,15 +1,14 @@ -package top.r3944realms.lib39.util.nbt; +package top.r3944realms.lib39.util.storage.nbt; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; -import io.netty.util.internal.StringUtil; import net.minecraft.nbt.*; +import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; -import java.util.UUID; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; @@ -164,6 +163,17 @@ public class NBTWriter { } return this; } + public NBTWriter gameProfile(String key, ResolvableProfile profile) { + root.store(key, ResolvableProfile.CODEC, profile); + return this; + } + + public NBTWriter gameProfile(String key, @NonNull Optional profile) { + if (profile.isPresent()) { + root.store(key, ResolvableProfile.CODEC, profile.get()); + } + return this; + } /** diff --git a/common/src/main/java/top/r3944realms/lib39/util/storage/vauleio/ValueInputWriter.java b/common/src/main/java/top/r3944realms/lib39/util/storage/vauleio/ValueInputWriter.java new file mode 100644 index 0000000..aadea09 --- /dev/null +++ b/common/src/main/java/top/r3944realms/lib39/util/storage/vauleio/ValueInputWriter.java @@ -0,0 +1,557 @@ +package top.r3944realms.lib39.util.storage.vauleio; + + +import com.mojang.serialization.Codec; +import net.minecraft.world.item.component.ResolvableProfile; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.phys.Vec3; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Consumer; + +/** + * ValueInput Helper类 + * 提供链式操作和类型安全的ValueInput数据读取 + */ +@SuppressWarnings("unused") +public class ValueInputWriter { + private final ValueInput valueInput; + + private ValueInputWriter(ValueInput valueInput) { + this.valueInput = valueInput; + } + + /** + * 从ValueInput创建读取器 + * + * @param valueInput the value input + * @return the value input reader + */ + @NotNull + public static ValueInputWriter of(@NotNull ValueInput valueInput) { + return new ValueInputWriter(valueInput); + } + + /** + * String value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter string(String key, Consumer setter) { + valueInput.getString(key).ifPresent(setter); + return this; + } + + /** + * String value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter string(String key, @NotNull Consumer setter, String defaultValue) { + setter.accept(valueInput.getStringOr(key, defaultValue)); + return this; + } + + /** + * Boolean value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter booleanValue(String key, Consumer setter) { + valueInput.read(key, Codec.BOOL).ifPresent(setter); + return this; + } + + /** + * Boolean value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter booleanValue(String key, @NotNull Consumer setter, boolean defaultValue) { + setter.accept(valueInput.getBooleanOr(key, defaultValue)); + return this; + } + + /** + * Byte value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter byteValue(String key, Consumer setter) { + valueInput.read(key, Codec.BYTE).ifPresent(setter); + return this; + } + + /** + * Byte value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter byteValue(String key, @NotNull Consumer setter, byte defaultValue) { + setter.accept(valueInput.getByteOr(key, defaultValue)); + return this; + } + + /** + * Short value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter shortValue(String key, Consumer setter) { + valueInput.read(key, Codec.SHORT).ifPresent(setter); + return this; + } + + /** + * Short value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter shortValue(String key, @NotNull Consumer setter, short defaultValue) { + setter.accept((short) valueInput.getShortOr(key, defaultValue)); + return this; + } + + /** + * Int value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter intValue(String key, Consumer setter) { + valueInput.getInt(key).ifPresent(setter); + return this; + } + + /** + * Int value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter intValue(String key, @NotNull Consumer setter, int defaultValue) { + setter.accept(valueInput.getIntOr(key, defaultValue)); + return this; + } + + /** + * Long value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter longValue(String key, Consumer setter) { + valueInput.getLong(key).ifPresent(setter); + return this; + } + + /** + * Long value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter longValue(String key, @NotNull Consumer setter, long defaultValue) { + setter.accept(valueInput.getLongOr(key, defaultValue)); + return this; + } + + /** + * Float value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter floatValue(String key, Consumer setter) { + valueInput.read(key, Codec.FLOAT).ifPresent(setter); + return this; + } + + /** + * Float value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter floatValue(String key, @NotNull Consumer setter, float defaultValue) { + setter.accept(valueInput.getFloatOr(key, defaultValue)); + return this; + } + + /** + * Double value value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter doubleValue(String key, Consumer setter) { + valueInput.read(key, Codec.DOUBLE).ifPresent(setter); + return this; + } + + /** + * Double value value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter doubleValue(String key, @NotNull Consumer setter, double defaultValue) { + setter.accept(valueInput.getDoubleOr(key, defaultValue)); + return this; + } + + /** + * Int array value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter intArray(String key, Consumer setter) { + valueInput.getIntArray(key).ifPresent(setter); + return this; + } + + /** + * Int array value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter intArray(String key, @NotNull Consumer setter, int[] defaultValue) { + setter.accept(valueInput.getIntArray(key).orElse(defaultValue)); + return this; + } + + /** + * Codec value value input reader. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter codecValue(String key, Codec codec, Consumer setter) { + valueInput.read(key, codec).ifPresent(setter); + return this; + } + + /** + * Codec value value input reader. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter codecValue(String key, Codec codec, @NotNull Consumer setter, T defaultValue) { + setter.accept(valueInput.read(key, codec).orElse(defaultValue)); + return this; + } + + /** + * Vec 3 value input reader. + * + * @param key the key + * @param setter the setter + * @return the value input reader + */ + public ValueInputWriter vec3(String key, Consumer setter) { + valueInput.child(key).ifPresent(child -> { + try { + Vec3 vec = readVec3(child); + setter.accept(vec); + } catch (IllegalArgumentException ignored) { + // 忽略解析错误 + } + }); + return this; + } + + /** + * Vec 3 value input reader. + * + * @param key the key + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter vec3(String key, Consumer setter, Vec3 defaultValue) { + Optional child = valueInput.child(key); + if (child.isPresent()) { + try { + Vec3 vec = readVec3(child.get()); + setter.accept(vec); + return this; + } catch (IllegalArgumentException ignored) { + // 忽略解析错误,使用默认值 + } + } + setter.accept(defaultValue); + return this; + } + + public ValueInputWriter gameProfile(String key, Consumer setter) { + Optional read = valueInput.read(key, ResolvableProfile.CODEC); + if (read.isPresent()) { + try { + ResolvableProfile profile = read.get(); + setter.accept(profile); + return this; + } catch (IllegalArgumentException ignored) { + + } + } + return this; + } + + public ValueInputWriter gameProfile(String key, Consumer setter, ResolvableProfile defaultValue) { + Optional read = valueInput.read(key, ResolvableProfile.CODEC); + if (read.isPresent()) { + try { + ResolvableProfile profile = read.get(); + setter.accept(profile); + return this; + } catch (IllegalArgumentException ignored) { + + } + } + setter.accept(defaultValue); + return this; + } + + /** + * Enum value value input reader. + * + * @param the type parameter + * @param key the key + * @param enumClass the enum class + * @param setter the setter + * @return the value input reader + */ + public > ValueInputWriter enumValue(String key, Class enumClass, Consumer setter) { + valueInput.getString(key).ifPresent(value -> { + try { + setter.accept(Enum.valueOf(enumClass, value.toUpperCase())); + } catch (IllegalArgumentException ignored) { + // 忽略枚举解析错误 + } + }); + return this; + } + + /** + * Enum value value input reader. + * + * @param the type parameter + * @param key the key + * @param enumClass the enum class + * @param setter the setter + * @param defaultValue the default value + * @return the value input reader + */ + public > ValueInputWriter enumValue(String key, Class enumClass, Consumer setter, T defaultValue) { + Optional value = valueInput.getString(key); + if (value.isPresent()) { + try { + setter.accept(Enum.valueOf(enumClass, value.get().toUpperCase())); + return this; + } catch (IllegalArgumentException ignored) { + // 忽略枚举解析错误 + } + } + setter.accept(defaultValue); + return this; + } + + /** + * Nested value input reader. + * + * @param key the key + * @param consumer the consumer + * @return the value input reader + */ + public ValueInputWriter nested(String key, Consumer consumer) { + valueInput.child(key).ifPresent(child -> consumer.accept(new ValueInputWriter(child))); + return this; + } + + /** + * Nested value input reader. + * + * @param key the key + * @param consumer the consumer + * @param orElse the or else + * @return the value input reader + */ + public ValueInputWriter nested(String key, Consumer consumer, Runnable orElse) { + Optional child = valueInput.child(key); + if (child.isPresent()) { + consumer.accept(new ValueInputWriter(child.get())); + } else { + orElse.run(); + } + return this; + } + + /** + * List value input reader. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param consumer the consumer + * @return the value input reader + */ + public ValueInputWriter list(String key, Codec codec, Consumer> consumer) { + valueInput.list(key, codec).ifPresent(list -> { + if (!list.isEmpty()) { + consumer.accept(list.stream()); + } + }); + return this; + } + + /** + * List value input reader. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param consumer the consumer + * @param defaultValue the default value + * @return the value input reader + */ + public ValueInputWriter list(String key, Codec codec, @NotNull Consumer> consumer, java.util.stream.Stream defaultValue) { + Optional> list = valueInput.list(key, codec); + if (list.isPresent() && !list.get().isEmpty()) { + consumer.accept(list.get().stream()); + } else { + consumer.accept(defaultValue); + } + return this; + } + + /** + * Children list value input reader. + * + * @param key the key + * @param consumer the consumer + * @return the value input reader + */ + public ValueInputWriter childrenList(String key, Consumer> consumer) { + valueInput.childrenList(key).ifPresent(list -> { + if (!list.isEmpty()) { + consumer.accept(list.stream().map(ValueInputWriter::new)); + } + }); + return this; + } + + /** + * If present value input reader. + * + * @param key the key + * @param action the action + * @return the value input reader + */ + public ValueInputWriter ifPresent(String key, Runnable action) { + if (valueInput.child(key).isPresent()) { + action.run(); + } + return this; + } + + /** + * If absent value input reader. + * + * @param key the key + * @param action the action + * @return the value input reader + */ + public ValueInputWriter ifAbsent(String key, Runnable action) { + if (valueInput.child(key).isEmpty()) { + action.run(); + } + return this; + } + + /** + * Gets raw. + * + * @return the raw + */ + @NotNull + public ValueInput getRaw() { + return valueInput; + } + + /** + * Read vec 3 vec 3. + * + * @param valueInput the value input + * @return the vec 3 + */ + @NotNull + public static Vec3 readVec3(@NotNull ValueInput valueInput) { + double x = valueInput.getDoubleOr("X", 0.0); + double y = valueInput.getDoubleOr("Y", 0.0); + double z = valueInput.getDoubleOr("Z", 0.0); + return new Vec3(x, y, z); + } + + /** + * Read vec 3 safe vec 3. + * + * @param valueInput the value input + * @return the vec 3 + */ + @Nullable + public static Vec3 readVec3Safe(@NotNull ValueInput valueInput) { + try { + return readVec3(valueInput); + } catch (Exception e) { + return null; + } + } +} diff --git a/common/src/main/java/top/r3944realms/lib39/util/storage/vauleio/ValueOutputReader.java b/common/src/main/java/top/r3944realms/lib39/util/storage/vauleio/ValueOutputReader.java new file mode 100644 index 0000000..5110f41 --- /dev/null +++ b/common/src/main/java/top/r3944realms/lib39/util/storage/vauleio/ValueOutputReader.java @@ -0,0 +1,648 @@ +package top.r3944realms.lib39.util.storage.vauleio; + +import com.mojang.serialization.Codec; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.item.component.ResolvableProfile; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueOutput; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * ValueOutput Helper类 + * 提供链式操作和类型安全的ValueOutput数据写入 + */ +@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType", "UnusedReturnValue"}) +public class ValueOutputReader { + private final ValueOutput valueOutput; + private ValueOutputReader() { + this.valueOutput = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING); + } + private ValueOutputReader(ValueOutput valueOutput) { + this.valueOutput = valueOutput; + } + + /** + * 创建一个新的NBT构建器 + * + * @return the nbt writer + */ + @Contract(value = " -> new", pure = true) + public static @NotNull ValueOutputReader builder() { + return new ValueOutputReader(); + } + + /** + * 从ValueOutput创建写入器 + * + * @param valueOutput the value output + * @return the value output writer + */ + @NotNull + public static ValueOutputReader of(@NotNull ValueOutput valueOutput) { + return new ValueOutputReader(valueOutput); + } + + /** + * String value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ +// 基本写入方法 + public ValueOutputReader string(String key, String value) { + if (value != null) { + valueOutput.putString(key, value); + } + return this; + } + + /** + * String value output writer. + * + * @param key the key + * @param value the value + * @param defaultValue the default value + * @return the value output writer + */ + public ValueOutputReader string(String key, @Nullable String value, String defaultValue) { + valueOutput.putString(key, value != null ? value : defaultValue); + return this; + } + + /** + * String value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader string(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putString(key, v)); + return this; + } + + /** + * Boolean value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader booleanValue(String key, boolean value) { + valueOutput.putBoolean(key, value); + return this; + } + + /** + * Boolean value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader booleanValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putBoolean(key, v)); + return this; + } + + /** + * Byte value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader byteValue(String key, byte value) { + valueOutput.putByte(key, value); + return this; + } + + /** + * Byte value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader byteValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putByte(key, v)); + return this; + } + + /** + * Short value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader shortValue(String key, short value) { + valueOutput.putShort(key, value); + return this; + } + + /** + * Short value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader shortValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putShort(key, v)); + return this; + } + + /** + * Int value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader intValue(String key, int value) { + valueOutput.putInt(key, value); + return this; + } + + /** + * Int value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader intValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putInt(key, v)); + return this; + } + + /** + * Long value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader longValue(String key, long value) { + valueOutput.putLong(key, value); + return this; + } + + /** + * Long value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader longValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putLong(key, v)); + return this; + } + + /** + * Float value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader floatValue(String key, float value) { + valueOutput.putFloat(key, value); + return this; + } + + /** + * Float value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader floatValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putFloat(key, v)); + return this; + } + + /** + * Double value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader doubleValue(String key, double value) { + valueOutput.putDouble(key, value); + return this; + } + + /** + * Double value value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader doubleValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putDouble(key, v)); + return this; + } + + /** + * Int array value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader intArray(String key, int[] value) { + if (value != null && value.length > 0) { + valueOutput.putIntArray(key, value); + } + return this; + } + + /** + * Int array value output writer. + * + * @param key the key + * @param value the value + * @return the value output writer + */ + public ValueOutputReader intArray(String key, @NonNull Optional value) { + value.ifPresent(v -> { + if (v.length > 0) { + valueOutput.putIntArray(key, v); + } + }); + return this; + } + + /** + * Codec value value output writer. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param value the value + * @return the value output writer + */ + public ValueOutputReader codecValue(String key, Codec codec, T value) { + if (value != null) { + valueOutput.store(key, codec, value); + } + return this; + } + + /** + * Codec value value output writer. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param value the value + * @return the value output writer + */ + public ValueOutputReader codecValue(String key, Codec codec, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.store(key, codec, v)); + return this; + } + + /** + * Codec nullable value value output writer. + * + * @param the type parameter + * @param key the key + * @param codec the codec + * @param value the value + * @return the value output writer + */ + public ValueOutputReader codecNullable(String key, Codec codec, @Nullable T value) { + valueOutput.storeNullable(key, codec, value); + return this; + } + + public ValueOutputReader gameProfile(String key, ResolvableProfile profile) { + valueOutput.store(key, ResolvableProfile.CODEC, profile); + return this; + } + + public ValueOutputReader gameProfile(String key, @NonNull Optional profile) { + if (profile.isPresent()) { + valueOutput.store(key, ResolvableProfile.CODEC, profile.get()); + } + return this; + } + /** + * Vec 3 value output writer. + * + * @param key the key + * @param vec the vec + * @return the value output writer + */ + public ValueOutputReader vec3(String key, Vec3 vec) { + if (vec != null) { + ValueOutput child = valueOutput.child(key); + writeVec3(child, vec); + } + return this; + } + + /** + * Vec 3 value output writer. + * + * @param key the key + * @param vec the vec + * @return the value output writer + */ + public ValueOutputReader vec3(String key, @NonNull Optional vec) { + vec.ifPresent(v -> vec3(key, v)); + return this; + } + + /** + * Enum value value output writer. + * + * @param the type parameter + * @param key the key + * @param value the value + * @return the value output writer + */ + public > ValueOutputReader enumValue(String key, T value) { + if (value != null) { + valueOutput.putString(key, value.name().toLowerCase()); + } + return this; + } + + /** + * Enum value value output writer. + * + * @param the type parameter + * @param key the key + * @param value the value + * @return the value output writer + */ + public > ValueOutputReader enumValue(String key, @NonNull Optional value) { + value.ifPresent(v -> valueOutput.putString(key, v.name().toLowerCase())); + return this; + } + + /** + * Nested value output writer. + * + * @param key the key + * @param consumer the consumer + * @return the value output writer + */ + public ValueOutputReader nested(String key, @NonNull Consumer consumer) { + ValueOutput child = valueOutput.child(key); + consumer.accept(new ValueOutputReader(child)); + return this; + } + + /** + * Nested if present value output writer. + * + * @param key the key + * @param supplier the supplier + * @param consumer the consumer + * @return the value output writer + */ + public ValueOutputReader nestedIfPresent(String key, @NonNull Supplier supplier, Consumer consumer) { + if (supplier.get()) { + ValueOutput child = valueOutput.child(key); + consumer.accept(new ValueOutputReader(child)); + } + return this; + } + + /** + * List value output writer. + * + * @param the type parameter + * @param key the key + * @param elementCodec the element codec + * @param elements the elements + * @param consumer the consumer + * @return the value output writer + */ + public ValueOutputReader list(String key, Codec elementCodec, Iterable elements, Consumer> consumer) { + ValueOutput.TypedOutputList list = valueOutput.list(key, elementCodec); + if (!list.isEmpty()) { + TypedListWriter writer = new TypedListWriter<>(list); + consumer.accept(writer); + } + return this; + } + + /** + * List value output writer. + * + * @param the type parameter + * @param key the key + * @param elementCodec the element codec + * @param elements the elements + * @return the value output writer + */ + public ValueOutputReader list(String key, Codec elementCodec, @NonNull Iterable elements) { + ValueOutput.TypedOutputList list = valueOutput.list(key, elementCodec); + for (T element : elements) { + if (element != null) { + list.add(element); + } + } + return this; + } + + /** + * Children list value output writer. + * + * @param key the key + * @param consumer the consumer + * @return the value output writer + */ + public ValueOutputReader childrenList(String key, Consumer consumer) { + ValueOutput.ValueOutputList list = valueOutput.childrenList(key); + if (!list.isEmpty()) { + ChildrenListWriter writer = new ChildrenListWriter(list); + consumer.accept(writer); + } + return this; + } + + /** + * If present value output writer. + * + * @param condition the condition + * @param action the action + * @return the value output writer + */ + public ValueOutputReader ifPresent(boolean condition, Consumer action) { + if (condition) { + action.accept(this); + } + return this; + } + + /** + * If present value output writer. + * + * @param condition the condition + * @param action the action + * @return the value output writer + */ + public ValueOutputReader ifPresent(@NonNull Supplier condition, Consumer action) { + if (condition.get()) { + action.accept(this); + } + return this; + } + + /** + * Discard value output writer. + * + * @param key the key + * @return the value output writer + */ + public ValueOutputReader discard(String key) { + valueOutput.discard(key); + return this; + } + + /** + * Gets raw. + * + * @return the raw + */ + @NotNull + public ValueOutput getRaw() { + return valueOutput; + } + + /** + * Write vec 3. + * + * @param valueOutput the value output + * @param vec the vec + */ + public static void writeVec3(@NotNull ValueOutput valueOutput, @NotNull Vec3 vec) { + valueOutput.putDouble("X", vec.x); + valueOutput.putDouble("Y", vec.y); + valueOutput.putDouble("Z", vec.z); + } + + /** + * Write vec 3 safe. + * + * @param valueOutput the value output + * @param vec the vec + */ + public static void writeVec3Safe(@NotNull ValueOutput valueOutput, @Nullable Vec3 vec) { + if (vec != null) { + writeVec3(valueOutput, vec); + } + } + + /** + * The type Typed list writer. + * + * @param the type parameter + */ + public static class TypedListWriter { + private final ValueOutput.TypedOutputList list; + + private TypedListWriter(ValueOutput.TypedOutputList list) { + this.list = list; + } + + /** + * Add typed list writer. + * + * @param element the element + * @return the typed list writer + */ + public TypedListWriter add(T element) { + if (element != null) { + list.add(element); + } + return this; + } + + /** + * Add all typed list writer. + * + * @param elements the elements + * @return the typed list writer + */ + public TypedListWriter addAll(@NonNull Iterable elements) { + for (T element : elements) { + if (element != null) { + list.add(element); + } + } + return this; + } + + /** + * Is empty boolean. + * + * @return the boolean + */ + public boolean isEmpty() { + return list.isEmpty(); + } + } + + /** + * The type Children list writer. + */ + public static class ChildrenListWriter { + private final ValueOutput.ValueOutputList list; + + private ChildrenListWriter(ValueOutput.ValueOutputList list) { + this.list = list; + } + + /** + * Add child value output writer. + * + * @param consumer the consumer + * @return the children list writer + */ + public ChildrenListWriter addChild(@NonNull Consumer consumer) { + ValueOutput child = list.addChild(); + consumer.accept(new ValueOutputReader(child)); + return this; + } + + /** + * Discard last children list writer. + * + * @return the children list writer + */ + public ChildrenListWriter discardLast() { + list.discardLast(); + return this; + } + + /** + * Is empty boolean. + * + * @return the boolean + */ + public boolean isEmpty() { + return list.isEmpty(); + } + } +} diff --git a/fabric/src/main/java/top/r3944realms/lib39/example/content/data/FabricTestSyncData.java b/fabric/src/main/java/top/r3944realms/lib39/example/content/data/FabricTestSyncData.java index a888364..8ee3dfa 100644 --- a/fabric/src/main/java/top/r3944realms/lib39/example/content/data/FabricTestSyncData.java +++ b/fabric/src/main/java/top/r3944realms/lib39/example/content/data/FabricTestSyncData.java @@ -8,8 +8,8 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.core.sync.IFabricUpdate; -import top.r3944realms.lib39.util.nbt.NBTReader; -import top.r3944realms.lib39.util.nbt.NBTWriter; +import top.r3944realms.lib39.util.storage.nbt.NBTReader; +import top.r3944realms.lib39.util.storage.nbt.NBTWriter; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; diff --git a/neoforge/src/main/java/top/r3944realms/lib39/example/content/data/TestSyncData.java b/neoforge/src/main/java/top/r3944realms/lib39/example/content/data/TestSyncData.java index 6c01baf..7c29bce 100644 --- a/neoforge/src/main/java/top/r3944realms/lib39/example/content/data/TestSyncData.java +++ b/neoforge/src/main/java/top/r3944realms/lib39/example/content/data/TestSyncData.java @@ -7,8 +7,8 @@ import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.NotNull; import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.core.sync.INeoForgeUpdate; -import top.r3944realms.lib39.util.nbt.NBTReader; -import top.r3944realms.lib39.util.nbt.NBTWriter; +import top.r3944realms.lib39.util.storage.nbt.NBTReader; +import top.r3944realms.lib39.util.storage.nbt.NBTWriter; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; diff --git a/primers-doc/1.21.10-from-1.21.9.md b/primers-doc/1.21.10-from-1.21.9.md new file mode 100644 index 0000000..964af77 --- /dev/null +++ b/primers-doc/1.21.10-from-1.21.9.md @@ -0,0 +1,27 @@ +# Minecraft 1.21.9 -> 1.21.10 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.9 迁移到 1.21.10。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +感谢: + +- @melanx 指出了一个拼写错误 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.10&tab=changelog) 中找到它们的列表。 + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 新增列表 + +- `net.minecraft.world.entity.decoration.HangingEntity#getPopBox` - 返回一个边界框,指示如果发生碰撞,实体将从何处弹出。 + +### 变更列表 + +- `net.minecraft.world.level.block.state.BlockBehaviour#entityInside` 现在接受一个 `boolean` 参数,指示实体是与方块相交还是在方块内部。 diff --git a/primers-doc/1.21.11-from-1.21.10.md b/primers-doc/1.21.11-from-1.21.10.md new file mode 100644 index 0000000..c025667 --- /dev/null +++ b/primers-doc/1.21.11-from-1.21.10.md @@ -0,0 +1,3146 @@ +# Minecraft 1.21.10 -> 1.21.11 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.10 迁移到 1.21.11。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +感谢: + +- @xfacthd 对使用注解的一些合理推测 +- @dinnerbone 指出在单机世界中,gizmo 也可以在服务器上提交 +- @thatgravyboat 指出 `Mth#clampedLerp` 参数顺序的变化 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.22&tab=changelog) 中找到它们的列表。 + +## 重命名混乱 + +许多核心类、方法和参数被重新组织和重命名,同时保留了它们各自的功能。以下是最重要变更的列表。 + +### `ResourceLocation` 到 `Identifier` + +所有对 `ResourceLocation` 的引用,无论是在方法名称、参数还是其他类中,都已替换为 `Identifier`。 + +### `util` 包 + +大多数工具类已移至 `net.minecraft.util`。这些需要重新导入。 + +### `critereon` 到 `criterion` + +`net.minecraft.advancements.critereon` 已重命名为 `net.minecraft.advancements.criterion`。这些需要重新导入。 + +### 实体和对象子包 + +`net.minecraft.client.model` 和 `net.minecraft.world.entity` 都已根据支持对象的类型重新组织为额外的子包。这些需要重新导入。 + +- `net.minecraft` + - `BlockUtil` -> `.util.BlockUtil` + - `FileUtil` -> `.util.FileUtil` + - `ResourceLocationException` -> `IdentifierException` + - `Util` -> `.util.Util` +- `net.minecraft.advancements.critereon` -> `.advancements.criterion` +- `net.minecraft.client.gui.screens.inventory.JigsawBlockEditScreen#isValidResourceLocation` -> `isValidIdentifier` +- `net.minecraft.client.model` + - `AbstractBoatModel` -> `.object.boat.AbstractBoatModel` + - `AbstractEquineModel` -> `.animal.equine.AbstractEquineModel` + - `AbstractPiglinModel` -> `.monster.piglin.AbstractPiglinModel` + - `AbstractZombieModel` -> `.monster.zombie.AbstractZombieModel` + - `AllayModel` -> `.animal.allay.AllayModel` + - `ArmadilloModel` -> `.animal.armadillo.ArmadilloModel` + - `ArmorStandArmorModel` -> `.object.armorstand.ArmorStandArmorModel` + - `ArmorStandModel` -> `.object.armorstand.ArmorStandModel` + - `ArrowModel` -> `.object.projectile.ArrowModel` + - `AxolotlModel` -> `.animal.axolotl.AxolotlModel` + - `BannerFlagModel` -> `.object.banner.BannerFlagModel` + - `BannerModel` -> `.object.banner.BannerModel` + - `BatModel` -> `.ambient.BatModel` + - `BeeModel` -> `.animal.bee.BeeModel` + - `BeeStingerModel` -> `.animal.bee.BeeStingerModel` + - `BellModel` -> `.object.bell.BellModel` + - `BlazeModel` -> `.monster.blaze.BlazeModel` + - `BoatModel` -> `.object.boat.BoatModel` + - `BoggedModel` -> `.monster.skeleton.BoggedModel` + - `BookModel` -> `.object.book.BookModel` + - `BreezeModel` -> `.monster.breeze.BreezeModel` + - `CamelModel` -> `.animal.camel.CamelModel` + - `CamelSaddleModel` -> `.animal.camel.CamelSaddleModel` + - `CatModel` -> `.animal.feline.CatModel` + - `ChestModel` -> `.object.chest.ChestModel` + - `ChickenModel` -> `.animal.chicken.ChickenModel` + - `CodModel` -> `.animal.fish.CodModel` + - `ColdChickenModel` -> `.animal.chicken.ColdChickenModel` + - `ColdCowModel` -> `.animal.cow.ColdCowModel` + - `ColdPigModel` -> `.animal.pig.ColdPigModel` + - `CopperGolemModel` -> `.animal.golem.CopperGolemModel` + - `CopperGolemStatueModel` -> `.object.statue.CopperGolemStatueModel` + - `CowModel` -> `.animal.cow.CowModel` + - `CreakingModel` -> `.monster.creaking.CreakingModel` + - `CreeperModel` -> `.monster.creeper.CreeperModel` + - `DolphinModel` -> `.animal.dolphin.DolphinModel` + - `DonkeyModel` -> `.animal.equine.DonkeyModel` + - `DrownedModel` -> `.monster.zombie.DrownedModel` + - `ElytraModel` -> `.object.equipment.ElytraModel` + - `EndCrystalModel` -> `.object.crystal.EndCrystalModel` + - `EndermanModel` -> `.monster.enderman.EndermanModel` + - `EndermiteModel` -> `.monster.endermite.EndermiteModel` + - `EquineSaddleModel` -> `.animal.equine.EquineSaddleModel` + - `EvokerFangsModel` -> `.effects.EvokerFangsModel` + - `FelineModel` -> `.animal.feline.FelineModel` + - `FoxModel` -> `.animal.fox.FoxModel` + - `FrogModel` -> `.animal.frog.FrogModel` + - `GhastModel` -> `.monster.ghast.GhastModel` + - `GiantZombieModel` -> `.monster.zombie.GiantZombieModel` + - `GoatModel` -> `.animal.goat.GoatModel` + - `GuardianModel` -> `.monster.guardian.GuardianModel` + - `GuardianParticleModel` -> `.monster.guardian.GuardianParticleModel` + - `HappyGhastHarnessModel` -> `.animal.ghast.HappyGhastHarnessModel` + - `HappyGhastModel` -> `.animal.ghast.HappyGhastModel` + - `HoglinModel` -> `.monster.hoglin.HoglinModel` + - `HorseModel` -> `.animal.equine.HorseModel` + - `IllagerModel` -> `.monster.illager.IllagerModel` + - `IronGolemModel` -> `.animal.golem.IronGolemModel` + - `LavaSlimeModel` -> `.monster.slime.MagmaCubeModel` + - `LeashKnotModel` -> `.object.leash.LeashKnotModel` + - `LlamaModel` -> `.animal.llama.LlamaModel` + - `LlamaSpitModel` -> `.animal.llama.LlamaSpitModel` + - `MinecartModel` -> `.object.cart.MinecartModel` + - `OcelotModel` -> `.animal.feline.OcelotModel` + - `PandaModel` -> `.animal.panda.PandaModel` + - `ParrotModel` -> `.animal.parrot.ParrotModel` + - `PhantomModel` -> `.monster.phantom.PhantomModel` + - `PiglinHeadModel` -> `.object.skull.PiglinHeadModel` + - `PiglinModel` -> `.monster.piglin.PiglinModel` + - `PigModel` -> `.animal.pig.PigModel` + - `PlayerCapeModel` -> `.player.PlayerCapeModel` + - `PlayerEarsModel` -> `.player.PlayerEarsModel` + - `PlayerModel` -> `.player.PlayerModel` + - `PolarBearModel` -> `.animal.polarbear.PolarBearModel` + - `PufferfishBigModel` -> `.animal.fish.PufferfishBigModel` + - `PufferfishMidModel` -> `.animal.fish.PufferfishMidModel` + - `PufferfishSmallModel` -> `.animal.fish.PufferfishSmallModel` + - `RabbitModel` -> `.animal.rabbit.RabbitModel` + - `RaftModel` -> `.object.boat.RaftModel` + - `RavagerModel` -> `.monster.ravager.RavagerModel` + - `SalmonModel` -> `.animal.fish.SalmonModel` + - `SheepFurModel` -> `.animal.sheep.SheepFurModel` + - `SheepModel` -> `.animal.sheep.SheepModel` + - `ShieldModel` -> `.object.equipment.ShieldModel` + - `ShulkerBulletModel` -> `.object.projectile.ShulkerBulletModel` + - `ShulkerModel` -> `.monster.shulker.ShulkerModel` + - `SilverfishModel` -> `.monster.silverfish.SilverfishModel` + - `SkeletonModel` -> `.monster.skeleton.SkeletonModel` + - `SkullModel` -> `.object.skull.SkullModel` + - `SkullModelBase` -> `.object.skull.SkullModelBase` + - `SlimeModel` -> `.monster.slime.SlimeModel` + - `SnifferModel` -> `.animal.sniffer.SnifferModel` + - `SnowGolemModel` -> `.animal.golem.SnowGolemModel` + - `SpiderModel` -> `.monster.spider.SpiderModel` + - `SpinAttackEffectModel` -> `.effects.SpinAttackEffectModel` + - `SquidModel` -> `.animal.squid.SquidModel` + - `StriderModel` -> `.monster.strider.StriderModel` + - `TadpoleModel` -> `.animal.frog.TadpoleModel` + - `TridentModel` -> `.object.projectile.TridentModel` + - `TropicalFishModelA` -> `.animal.fish.TropicalFishSmallModel` + - `TropicalFishModelB` -> `.animal.fish.TropicalFishLargeModel` + - `TurtleModel` -> `.animal.turtle.TurtleModel` + - `VexModel` -> `.monster.vex.VexModel` + - `VillagerModel` -> `.npc.VillagerModel` + - `WardenModel` -> `.monster.warden.WardenModel` + - `WarmCowModel` -> `.animal.cow.WarmCowModel` + - `WindChargeModel` -> `.object.projectile.WindChargeModel` + - `WitchModel` -> `.monster.witch.WitchModel` + - `WitherBossModel` -> `.monster.wither.WitherBossModel` + - `WolfModel` -> `.animal.wolf.WolfModel` + - `ZombieModel` -> `.monster.zombie.ZombieModel` + - `ZombieVillagerModel` -> `.monster.zombie.ZombieVillagerModel` + - `ZombifiedPiglinModel` -> `.monster.piglin.ZombifiedPiglinModel` +- `net.minecraft.client.model.dragon` + - `DragonHeadModel` -> `.model.object.skull.DragonHeadModel` + - `EnderDragonModel` -> `.model.monster.dragon.EnderDragonModel` +- `net.minecraft.client.resources.sounds` + - `AbstractSoundInstance#location` -> `identifier` + - `SoundInstance#getLocation` -> `getIdentifier` +- `net.minecraft.client.searchtree` + - `IdSearchTree` + - `resourceLocationSearchTree` -> `identifierSearchTree` + - `searchResourceLocation` -> `searchIdentifier` + - `ResourceLocationSearchTree` -> `IdentifierSearchTree` +- `net.minecraft.commands.arguments.ResourceLocationArgument` -> `IdentifierArgument` +- `net.minecraft.network.FriendlyByteBuf#readResourceLocation`, `writeResourceLocation` -> `readIdentifier`, `writeIdentifier` +- `net.minecraft.resources` + - `ResourceKey#location` -> `identifier` + - `ResourceLocation` -> `Identifier` +- `net.minecraft.util.ResourceLocationPattern` -> `IdentifierPattern` +- `net.minecraft.util.parsing.packrat.commands.ResourceLocationParseRule` -> `IdentifierParseRule` +- `net.minecraft.world.entity.GlowSquid` -> `.animal.squid.GlowSquid` +- `net.minecraft.world.entity.animal` + - `AbstractCow` -> `.cow.AbstractCow` + - `AbstractFish` -> `.fish.AbstractFish` + - `AbstractGolem` -> `.golem.AbstractGolem` + - `AbstractSchoolingFish` -> `.fish.AbstractSchoolingFish` + - `Bee` -> `.bee.Bee` + - `Cat` -> `.feline.Cat` + - `CatVariant` -> `.feline.CatVariant` + - `CatVariants` -> `.feline.CatVariants` + - `Chicken` -> `.chicken.Chicken` + - `ChickenVariant` -> `.chicken.ChickenVariant` + - `ChickenVariants` -> `.chicken.ChickenVariants` + - `Cod` -> `.fish.Cod` + - `Cow` -> `.cow.Cow` + - `CowVariant` -> `.cow.CowVariant` + - `CowVariants` -> `.cow.CowVariants` + - `Dolphin` -> `.dolphin.Dolphin` + - `Fox` -> `.fox.Fox` + - `HappyGhast` -> `.happyghast.HappyGhast` + - `HappyGhastAi` -> `.happyghast.HappyGhastAi` + - `IronGolem` -> `.golem.IronGolem` + - `MushroomCow` -> `.cow.MushroomCow` + - `Ocelot` -> `.feline.Ocelot` + - `Panda` -> `.panda.Panda` + - `Parrot` -> `.parrot.Parrot` + - `Pig` -> `.pig.Pig` + - `PigVariant` -> `.pig.PigVariant` + - `PigVariants` -> `.pig.PigVariants` + - `PolarBear` -> `.polarbear.PolarBear` + - `Pufferfish` -> `.fish.Pufferfish` + - `Rabbit` -> `.rabbit.Rabbit` + - `Salmon` -> `.fish.Salmon` + - `ShoulderRidingEntity` -> `.parrot.ShoulderRidingEntity` + - `SnowGolem` -> `.golem.SnowGolem` + - `Squid` -> `.squid.Squid` + - `TropicalFish` -> `.fish.TropicalFish` + - `Turtle` -> `.turtle.Turtle` + - `WaterAnimal` -> `.fish.WaterAnimal` +- `net.minecraft.world.entity.animal.coppergolem.*` -> `.animal.golem.*` +- `net.minecraft.world.entity.animal.horse.*` -> `.animal.equine.*` +- `net.minecraft.world.entity.boss.EnderDragonPart` -> `.enderdragon.EnderDragonPart` +- `net.minecraft.world.entity.decoration` + - `Painting` -> `.painting.Painting` + - `PaintingVariant` -> `.painting.PaintingVariant` + - `PaintingVariants` -> `.painting.PaintingVariants` +- `net.minecraft.world.entity.monster` + - `AbstractIllager` -> `.illager.AbstractIllager` + - `AbstractSkeleton` -> `.skeleton.AbstractSkeleton` + - `Bogged` -> `.skeleton.Bogged` + - `CaveSpider` -> `.spider.CaveSpider` + - `Drowned` -> `.zombie.Drowned` + - `Evoker` -> `.illager.Evoker` + - `Husk` -> `.zombie.Husk` + - `Illusioner` -> `.illager.Illusioner` + - `Parched` -> `.skeleton.Parached` + - `Pillager` -> `.illager.Pillager` + - `Skeleton` -> `.skeleton.Skeleton` + - `SpellcasterIllager` -> `.illager.SpellcasterIllager` + - `Spider` -> `.spider.Spider` + - `Stray` -> `.skeleton.Stray` + - `Vindicator` -> `.illager.Vindicator` + - `WitherSkeleton` -> `.skeleton.WitherSkeleton` + - `Zombie` -> `.zombie.Zombie` + - `ZombieVillager` -> `.zombie.ZombieVillager` + - `ZombifiedPiglin` -> `.zombie.ZombifiedPiglin` +- `net.minecraft.world.entity.npc` + - `AbstractVillager` -> `.villager.AbstractVillager` + - `Villager` -> `.villager.Villager` + - `VillagerData` -> `.villager.VillagerData` + - `VillagerDataHolder` -> `.villager.VillagerDataHolder` + - `VillagerProfession` -> `.villager.VillagerProfession` + - `VillagerTrades` -> `.villager.VillagerTrades` + - `VillagerType` -> `.villager.VillagerType` + - `WanderingTrader` -> `.wanderingtrader.WanderingTrader` + - `WanderingTraderSpawner` -> `.wanderingtrader.WanderingTraderSpawner` +- `net.minecraft.world.entity.projectile` + - `AbstractArrow` -> `.arrow.AbstractArrow` + - `AbstractHurtingProjectile` -> `.hurtingprojectile.AbstractHurtingProjectile` + - `AbstractThrownPotion` -> `.throwableitemprojectile.AbstractThrownPotion` + - `Arrow` -> `.arrow.Arrow` + - `DragonFireball` -> `.hurtingprojectile.DragonFireball` + - `Fireball` -> `.hurtingprojectile.Fireball` + - `LargeFireball` -> `.hurtingprojectile.LargeFireball` + - `SmallFireball` -> `.hurtingprojectile.SmallFireball` + - `Snowball` -> `.throwableitemprojectile.Snowball` + - `SpectralArrow` -> `.arrow.SpectralArrow` + - `ThrowableItemProjectile` -> `.throwableitemprojectile.ThrowableItemProjectile` + - `ThrownEgg` -> `.throwableitemprojectile.ThrownEgg` + - `ThrownEnderpearl` -> `.throwableitemprojectile.ThrownEnderpearl` + - `ThrownExperienceBottle` -> `.throwableitemprojectile.ThrownExperienceBottle` + - `ThrownLingeringPotion` -> `.throwableitemprojectile.ThrownLingeringPotion` + - `ThrownSplashPotion` -> `.throwableitemprojectile.ThrownSplashPotion` + - `ThrownTrident` -> `.arrow.ThrownTrident` + - `WitherSkull` -> `.hurtingprojectile.WitherSkull` +- `net.minecraft.world.entity.projectile.windcharge.*` -> `.projectile.hurtingprojectile.windcharge.*` +- `net.minecraft.world.entity.vehicle` + - `AbstractBoat` -> `.boat.AbstractBoat` + - `AbstractChestBoat` -> `.boat.AbstractChestBoat` + - `AbstractMinecart` -> `.minecart.AbstractMinecart` + - `AbstractMinecartContainer` -> `.minecart.AbstractMinecartContainer` + - `Boat` -> `.boat.Boat` + - `ChestBoat` -> `.boat.ChestBoat` + - `ChestRaft` -> `.boat.ChestRaft` + - `Minecart` -> `.minecart.Minecart` + - `MinecartBehavior` -> `.minecart.MinecartBehavior` + - `MinecartChest` -> `.minecart.MinecartChest` + - `MinecartCommandBlock` -> `.minecart.MinecartCommandBlock` + - `MinecartFurnace` -> `.minecart.MinecartFurnace` + - `MinecartHopper` -> `.minecart.MinecartHopper` + - `MinecartSpawner` -> `.minecart.MinecartSpawner` + - `MinecartTNT` -> `.minecart.MinecartTNT` + - `NewMinecartBehavior` -> `.minecart.NewMinecartBehavior` + - `OldMinecartBehavior` -> `.minecart.OldMinecartBehavior` + - `Raft` -> `.boat.Raft` +- `net.minecraft.world.level.gamerules.GameRule#getResourceLocation` -> `getIdentifier` + +## 哦,又来了,一次渲染重写 + +渲染管线的更多部分已被重写,主要集中在采样器、`RenderType` 的创建和 mipmap 上。 + +### 采样器的分离 + +Blaze3d 已将读取纹理数据时的 `AddressMode` 和 `FilterMode` 设置分离到 `GpuSampler` 中。顾名思义,`GpuSampler` 定义了如何从缓冲区(如纹理)采样数据。`GpuSampler` 包含四个方法:`getAddressModeU` / `getAddressModeV` 用于确定采样器在读取 UV 位置时的行为(重复或钳位),`getMinFilter` / `getMagFilter` 用于分别确定如何缩小或放大纹理(最近邻或线性),`getMaxAnisotropy` 用于可用的最大各向异性过滤级别,以及 `getMaxLod` 用于纹理的最大细节级别。 + +采样器可以通过 `GpuDevice#createSampler` 创建,但除非您想指定大于 `1` 的不同各向异性过滤级别,或者最大细节级别不是 `0` 或 `1000`,否则没有必要。由于只有 32 种可能的组合,原版创建了所有 `GpuSampler` 并将它们存储在一个缓存中,可通过 `RenderSystem#getSamplerCache` 然后 `SamplerCache#getSampler` 访问: + +```java +// 直接调用 +GpuSampler sampler = RenderSystem.getDevice().createSampler( + // U 地址模式 + AddressMode.CLAMP_TO_EDGE, + // V 地址模式 + AddressMode.CLAMP_TO_EDGE, + // 缩小过滤器 + FilterMode.LINEAR, + // 放大过滤器 + FilterMode.NEAREST, + // 最大各向异性过滤级别 + // 原版对于等级渲染使用 1、2、4 或 8 + 4f, + // 纹理的最大细节级别 + // 原版对于默认使用 0, + // 或对于移动对象和上传到图集使用空 optional。 + OptionalDouble.of(0.0) +); + +// 采样器缓存方法 +GpuSampler sampler = RenderSystem.getSamplerCache().getSampler( + // U 地址模式 + AddressMode.CLAMP_TO_EDGE, + // V 地址模式 + AddressMode.CLAMP_TO_EDGE, + // 缩小过滤器 + FilterMode.LINEAR, + // 放大过滤器 + FilterMode.NEAREST, + // 是否为最大细节级别使用 1000 或 0 + true +); +``` + +要使采样器用于纹理,当在渲染通道中绑定纹理时(通过 `RenderPass#bindTexture`),您现在必须指定要使用的采样器以及纹理视图: + +```java +try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(...)) { + // 设置其他参数 + pass.bindTexture( + // sampler2D 统一变量的名称,通常在片段着色器中 + "Sampler0", + // 要采样的纹理 + ..., + // 要使用的采样器 + sampler + ); + + // 绘制缓冲区 +} +``` + +从用户角度来看,设置后处理器没有变化,因为只能选择钳位到边缘的地址模式。 + +### `RenderType` 洗牌 + +创建 `RenderType` 已在一定程度上被重做。虽然先前实现中的大多数功能仍然存在,但它们已更改以匹配新的渲染系统,其中直接 OpenGL 被抽象化,仅通过其定义的管道和 `RenderSystem` 访问。 + +#### 现有类型 + +现有类型已从 `RenderType` 移至 `RenderTypes`(例如,`RenderType#solid` -> `RenderTypes#solid`)。 + +#### 自定义类型 + +最初,要创建 `RenderType`,您会使用 `RenderStateShard` 构造一个 `$CompositeState`。每个 `RenderStateShard` 将定义在构建某些网格时如何设置和拆除通道,无论是设置纹理、渲染目标、模型变换等。然后,`$CompositeState` 将被构建以供任何需要的渲染应用使用。 + +新系统将渲染定义拆分为两部分:`RenderSetup` 和我们的 `RenderType`。顾名思义,`RenderSetup` 设置渲染器,以便在绘制到纹理时使用。拆除被完全移除,因为它要么在绘制 `RenderType` 时直接处理,要么使用可以丢弃的新构造的状态。另一方面,`RenderType` 只是一个命名的 `RenderSetup`。它只处理绘制网格数据,并使设置的某些字段公开以供在其他缓冲区实现中使用。多个类型可以具有相同的 `RenderSetup`,因为许多现有类型动态填充采样器使用的纹理和/或对象的轮廓。 + +可以通过其构建器 `RenderSetup#Builder` 创建 `RenderSetup`,提供要使用的 `RenderPipeline`。一旦设置了构建器属性,就可以通过 `RenderSetup$RenderSetupBuilder#createRenderSetup` 创建实际的设置: + +```java +public static final RenderSetup EXAMPLE_SETUP = RenderSetup.builder( + // 要使用的管线。 + // 这可能会影响设置允许哪些设置。 + RenderPipelines.ITEM_ENTITY_TRANSLUCENT_CULL +) + // 指定要绑定到提供的采样器的纹理。 + // 采样器必须由管线通过 `RenderPipeline$Builder#withSampler` 定义。 + // 纹理表示为绝对位置。 + .withTexture( + // 'Sampler0' 由管线定义。 + "Sampler0", + // 指向 'assets/minecraft/entity/wolf/wolf_armor_crackiness_low.png'。 + Identifier.withDefaultNamespace("textures/entity/wolf/wolf_armor_crackiness_low.png"), + // 一个可选的提供的 `GpuSampler`,用于对纹理进行采样。 + // 返回的值将在第一次解析后被缓存。 + () -> RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST) + ) + // 设置后,允许管线使用光照纹理。 + // 'Sampler2' 必须由管线通过 `RenderPipeline$Builder#withSampler` 定义。 + .useLightmap() + // 设置后,允许管线使用覆盖纹理。 + // 'Sampler1' 必须由管线通过 `RenderPipeline$Builder#withSampler` 定义。 + .useOverlay() + // 设置后,使用 `RenderTypes#crumbling` 根据实体模型的破坏进度叠加方块破坏阶段。 + // 这仅在 `ModelFeatureRenderer` 中实现。 + .affectsCrumbling() + // 设置后,根据 `RenderSystem#getProjectionType` 中设置的 `ProjectionType` 对四边形进行排序。 + // 这仅在从 `MultiBufferSource$BufferSource` 获取缓冲区时实现。 + .sortOnUpload() + // 设置使用的缓冲区的初始容量。 + // 这仅在 `RenderBuffers` 中构造初始缓冲区时使用。 + // 所有自定义的源应用都已经有一些具有确定大小的缓冲区。 + .bufferSize(786432) + // 一个对象包装的消费者,用于变换模型视图矩阵。 + // 原版实现在 `LayeringTransform` 中,通过投影类型应用变换: + // - `NO_LAYERING`: 不执行任何操作。 + // - `VIEW_OFFSET_Z_LAYERING`: 根据其 `ProjectionType` 将 Z 偏移 1 + // - `VIEW_OFFSET_Z_LAYERING_FORWARD`: 根据其 `ProjectionType` 将 Z 偏移 -11 + .setLayeringTransform( + // 我们也可以构造一个新的变换 + new LayeringTransform( + // 变换的名称 + "examplemod:example_layer", + // 变换不应推入或弹出堆栈 + // 仅平移、缩放或旋转 + stack -> stack.translate(0f, 0.1f, 0f) + ) + ) + // 设置此设置应写入的输出目标, + // 除非被 `RenderSystem#output*Override` 纹理覆盖。 + // 这通常是主目标,尽管它可以是其他原版目标, + // 或者如果您计划处理它,也可以是自定义目标。 + .setOutputTarget(OutputTarget.MAIN_TARGET) + // 一个对象包装的供应商,提供纹理矩阵。 + // 这通常用于在片段着色器中采样之前修改顶点着色器中的纹理 UV 坐标。 + // 原版仅将此用于闪光效果和旋风人/能量效果: + // - `DEFAULT_TEXTURING`: 不执行任何操作。 + // - `GLINT_TEXTURING`: 根据闪光速度平移,旋转 pi/18,并按 8 缩放。 + // - `ENTITY_GLINT_TEXTURING`: 根据闪光速度平移,旋转 pi/18,并按 0.5 缩放。 + // - `ARMOR_ENTITY_GLINT_TEXTURING`: 根据闪光速度平移,旋转 pi/18,并按 0.16 缩放。 + // - `$OffsetTextureTransform`: 按提供的 XY 坐标平移纹理。 + .setTextureTransform( + // 我们也可以构造一个新的变换 + new TextureTransform( + // 变换的名称 + "examplemod:example_texture", + // 要应用于纹理的变换 + () -> new Matrix4f().translation(0f, 1f, 0f).scale(1.5f) + ) + ) + // 设置应如何处理网格的轮廓: + // - `NONE`: 不执行任何操作。 + // - `IS_OUTLINE`: 这是一个轮廓,应写入轮廓缓冲区源。 + // - `AFFECTS_OUTLINE`: 这定义了轮廓的形状,应使用 `RenderTypes#OUTLINE` 绘制它。 + // 在写入轮廓缓冲区源时检查, + // 或者如果功能的轮廓颜色不为 0 + .setOutline(RenderSetup.OutlineProperty.AFFECTS_OUTLINE) + // 构建用于渲染类型的设置。 + .createRenderSetup(); +``` + +然后,可以通过 `create` 创建 `RenderType`。 + +```java +public static final RenderType EXAMPLE_TYPE = RenderType.create( + // 用于调试的类型名称 + "examplemod:example_type", + // 要使用的渲染设置 + EXAMPLE_SETUP +); +``` + +可以使用 `RenderType#draw` 将 `MeshData` 写入输出目标。 + +### Mipmap 策略元数据 + +纹理的 `mcmeta` 现在可以在 `textures` 部分中指定要使用的 `mipmap_strategy`。有四种可用的策略,`auto` 在没有透明度时默认为 `mean`,在有透明度时默认为 `cutout`。 + +| 策略 | 描述 | +|:---------------:|:--------------------------------------------------------------------------------------------------------------------------| +| `mean` | 默认策略,对当前 mipmap 级别的四个像素之间的颜色进行平均。 | +| `cutout` | 与 `mean` 相同,但所有级别都是从原始纹理生成的,使用 0.2 的阈值将 alpha 捕捉到 0 或 1。 | +| `strict_cutout` | 与 `cutout` 相同,但使用 `0.6` 的阈值设置 alpha 捕捉。 | +| `dark_cutout` | 与 `mean` 相同,但仅当周围像素的 alpha 不为 `0` 时才将其包含在平均值中。 | + +```json5 +// 在 `assets/examplemod/textures/block/example/example_block.png.mcmeta +{ + "texture": { + // 使用选定的策略 + "mipmap_strategy": "cutout", + // 确定在判断像素是完全不透明还是完全透明时, + // 截止值应偏置多少。 + // 较大的数字意味着更高的 alpha 截止值, + // 而较低的值使用较低的 alpha 截止值。 + "alpha_cutoff_bias": 0.2 + } +} +``` + +### 方块和地形拆分 + +由独立方块和地形使用的 `RenderPipeline` 已被拆分为单独的管线:分别带有前缀 `_BLOCK` 和 `_TERRAIN`。这包括实体、切割、半透明和绊线管线。半透明管线没有方块变体。 + +### 物品图集 + +方块图集不再包含专门用于物品的纹理。这些已移至它们自己的图集,名为 `minecraft:items`,其 id 存储在 `AtlasIds#ITEMS`。 + +如果给定的 `Material` 可以同时使用方块和物品纹理,则应使用 `ModelManager#BLOCK_OR_ITEM` 特殊情况提供。 + +- `com.mojang.blaze3d.buffers` + - `GpuBuffer`,`size` 现在使用 `long` 作为大小 + - `slice` 现在使用 `long` 作为长度和偏移 + - `GpuBufferSlice` 现在使用 `long` 作为长度和偏移 +- `com.mojang.blaze3d.opengl` + - `BufferStorage` + - `createBuffer` 现在使用 `long` 作为大小 + - `mapBuffer` 现在使用 `long` 作为长度和偏移 + - `DirectStateAccess` + - `bufferSubData` 现在使用 `long` 作为偏移 + - `mapBufferRange`、`flushMappedBufferRange`、`copyBufferSubData` 现在使用 `long` 作为长度和偏移 + - `GlBuffer` 现在使用 `long` 作为大小 + - `GlDevice` 现在接受 `ShaderSource` 而不是 `BiFunction` + - `getOrCompileShader` 现在接受 `ShaderSource` 而不是 `BiFunction` + - `GlRenderPass` + - `samplers` 现在是一个字符串到 `GlRenderPass$TextureViewAndSampler` 的哈希映射 + - `$TextureViewAndSampler` - 一个记录,定义了一个采样器及其采样的纹理。 + - `GlSampler` - GPU 采样器的 OpenGL 实现。 + - `GlStateManager` + - `_glBufferSubData` 现在使用 `long` 作为偏移 + - `_glMapBufferRange` 现在使用 `long` 作为长度和偏移 + - `GlTexture#modesDirty`、`flushModeChanges` 已移除 + - `GlTextureView#getFbo` - 获取纹理的帧缓冲区对象,如果存在则使用缓存。 +- `com.mojang.blaze3d.pipeline.RenderTarget#filterMode`、`setFilterMode` 已移除 +- `com.mojang.blaze3d.platform.TextureUtil` + - `solidify` - 通过打包和解包像素来修改纹理,以更好地帮助 mipmap 内的非暗化内部。 + - `fillEmptyAreasWithDarkColor` - 将空像素设置为一个 RGB 值为图像中最暗颜色的空像素。 +- `com.mojang.blaze3d.shaders.ShaderSource` - 一个函数式接口,从其 id 和类型以字符串形式获取着色器源。 +- `com.mojang.blaze3d.systems` + - `CommandEncoder#copyTextureToBuffer` 现在使用 `long` 作为偏移 + - `GpuDevice` + - `createSampler` - 为某个源到目标创建一个采样器,具有所需的地址和过滤模式。 + - `precompilePipeline` 现在接受 `ShaderSource` 而不是 `BiFunction` + - `getMaxSupportedAnisotropy` - 硬件支持的最大各向异性过滤级别。 + - `createBuffer` 现在使用 `long` 作为大小 + - `RenderPass#bindTexture` 现在接受 `GpuSampler` + - `RenderSystem` 现在使用 `long` 作为大小 + - `samplerCache` - 返回一个包含所有可能组合的采样器缓存。 + - `TEXTURE_COUNT` 已移除 + - `setupOverlayColor`、`teardownOverlayColor` 已移除 + - `setShaderTexture`、`getShaderTexture` 已移除 + - `setTextureMatrix`、`resetTextureMatrix`、`getTextureMatrix` 已移除 + - `lineWidth` -> `VertexConsumer#setLineWidth` + - `getShaderLineWidth` -> `Window#getAppropriateLineWidth` + - `initRenderer` 现在接受 `ShaderSource` 而不是 `BiFunction` + - `SamplerCache` - 一个缓存,包含渲染器可能使用的所有采样器。 +- `com.mojang.blaze3d.textures` + - `GpuSampler` - 一个具有指定 UV 地址模式和缩小及放大过滤器的缓冲区采样器。 + - `GpuTexture` + - `addressModeU` -> `GpuSampler#getAddressModeU` + - `addressModeV` -> `GpuSampler#getAddressModeV` + - `minFilter` -> `GpuSampler#getMinFilter` + - `magFilter` -> `GpuSampler#getMagFilter` + - `setAddressMode`、`setTextureFilter` 已被 `SamplerCache#getSampler` 取代,不是一对一 + - `useMipmaps`、`setUseMipmaps` 已移除 +- `net.minecraft.client` + - `Options#textureFiltering` - 从一定角度或远处观看时选择的纹理采样方法。 + - `TextureFilteringMethod` - 从一定角度或远处观看纹理时的采样方法。 +- `net.minecraft.client.gui.render.TextureSetup` 现在接受每个纹理的 `GpuSampler` + - 这也包括静态构造函数 +- `net.minecraft.client.particle.SingleQuadParticle$Layer#ITEMS` - 用于具有物品纹理的粒子的层。 +- `net.minecraft.client.renderer` + - `FaceInfo` + - `$Constants` -> `$Extent` + - `$VertexInfo` 现在是一个记录 + - `ItemBlockRenderTypes#getRenderType(ItemStack)` + - `LightTexture#turnOffLightLayer`、`turnOnLightLayer` 已移除 + - `LevelRenderer#resetSampler` - 重置区块层采样器。 + - `PostPass` + - `$Input#bilinear` - 是否使用双线性过滤器。 + - `$TextureInput` 现在接受一个 `boolean`,表示是否使用双线性过滤器 + - `RenderPipelines` + - `SOLID` -> `SOLID_BLOCK`、`SOLID_TERRAIN` + - `CUTOUT` -> `CUTOUT_BLOCK`、`CUTOUT_TERRAIN` + - `CUTOUT_MIPPED` 已移除 + - `TRANSLUCENT` -> `TRANSLUCENT_TERRAIN` + - `TRIPWIRE` -> `TRIPWIRE_BLOCK`、`TRIPWIRE_TERRAIN` + - `ANIMATE_SPRITE_SNIPPET`、`ANIMATE_SPRITE_BLIT`、`ANIMATE_SPRITE_INTERPOLATION` - 用于动画精灵的管线。 + - `RenderStateShard` 已被 `RenderSetup` 取代,不是一对一 + - `$LightmapStateShard` -> `RenderSetup#useLightmap` + - `$OverlayStateShard` -> `RenderSetup#useOverlay` + - `$MultiTextureStateShard`、`$TextureStateShard` -> `RenderSetup#textures` + - `$LayeringStateShard` -> `RenderSetup#layeringTransform`、`LayeringTransform` + - `$LineStateShard` -> `VertexConsumer#setLineWidth` + - `$OutputStateShard` -> `RenderSetup#outputTarget`、`OutputTarget` + - `$TexturingStateShard`、`$OffsetTexturingStateShard` -> `RenderSetup#textureTransform`、`TextureTransform` + - `RenderType` 已被拆分为两个独立的概念,不是一对一 + - 所有存储的 `RenderType` 已移至 `RenderTypes` + - 实际的类用法已移至 `.rendertype.RenderType`,在那里它执行 `$CompositeRenderType` 的工作 + - `Sheets#translucentBlockItemSheet` - 用于半透明方块物品的渲染类型。 +- `net.minecraft.client.renderer.block` + - `BlockRenderDispatcher` 不再接受提供的 `SpecialBlockModelRenderer` + - `LiquidBlockRenderer` 现在接受 `MaterialSet` + - `setupSprites` 已移至 `LiquidBlockRenderer` 构造函数中 +- `net.minecraft.client.renderer.block.model` + - `BakedQuad` + - `vertices` -> `position*`、`packedUV*`,不是一对一 + - `position` - 根据索引获取位置向量。 + - `packedUV` - 根据索引获取打包的 UV。 + - `BlockElementRotation` 现在接受 `Vector3fc` 作为原点,以及 `Matrix4fc` 变换 + - 构造函数也接受 `$RotationValue` 而不是 `Direction$Axis` 和角度 `float` + - `$EulerXYZRotation` - 以度为单位的 XYZ 旋转。 + - `$RotationValue` - 一个定义旋转变换的接口。 + - `$SingleAxisRotation` - 围绕单个轴以度为单位的旋转。 + - `FaceBakery` + - `VERTEX_COUNT` -> `BakedQuad#VERTEX_COUNT` + - `VERTEX_INT_SIZE`、`COLOR_INDEX`、`UV_INDEX` 已移除 + - `bakeQuad` 现在接受 `ModelBaker$PartCache` + - `extractPositions` 已移除 + - `SimpleModelWrapper#bake` 现在返回 `BlockModelPart` + - `SimpleUnbakedGeometry#bake` 现在接受 `ModelBaker` 而不是 `SpriteGetter` + - `TextureSlots$parseTextureMap` 不再接受 `Identifier` + - `Variant` + - `withZRot` - 围绕 Z 轴旋转模型状态。 + - `$SimpleModelState` 现在接受一个 Z `Quadrant` + - `withZ` - 设置模型状态的 Z 象限。 + - `VariantMutator#Z_ROT` - 围绕 Z 轴旋转模型。 +- `net.minecraft.client.renderer.chunk` + - `ChunkSectionLayer` 不再接受是否使用 mipmap + - `CUTOUT_MIPPED` 已移除 + - `texture` 已移除 + - `ChunkSectionsToRender` 现在接受 `GpuTextureView` + - `dynamicTransforms` -> `chunkSectionInfos` + - `renderGroup` 现在接受 `GpuSampler` +- `net.minecraft.client.renderer.item` + - `BlockModelWrapper` 构造函数现在是包私有的 + - `computeExtents` 现在返回 `Vector3fc` 的数组 + - `ItemStackRenderState$LayerRenderState` + - `NO_EXTENTS_SUPPLIER` 现在是一个提供的 `Vector3fc` 数组 + - `setExtents` 现在接受一个提供的 `Vector3fc` 数组 +- `net.minecraft.client.renderer.rendertype.RenderTypes` + - `MOVING_BLOCK_SAMPLER` - 用于移动中方块的采样器。 + - `solid` -> `solidMovingBlock` + - `cutout` -> `cutoutMovingBlock` + - `tripwire` -> `tripwireMovingBlock` +- `net.minecraft.client.renderer.texture` + - `AbstractTexture#setUseMipmaps` 已移除 + - `MipmapGenerator#generateMipLevels` 现在接受纹理的名称、一个 `MipmapStrategy` 来确定特定纹理应如何进行 mipmap,以及一个用于 alpha 截止偏置的 `float` + - `MipmapStrategy` - 一个枚举,定义了在为纹理构造 mipmap 时使用的策略。 + - `OverlayTexture#setupOverlayColor`、`teardownOverlayColor` 被 `getTextureView` 取代,不是一对一 + - `SpriteContents` 现在接受一个可选的 `TextureMetadataSection` 来确定精灵的元数据 + - `UBO_SIZE` - 精灵内容的统一缓冲区对象大小。 + - `createTicker` -> `createAnimationState`,不是一对一 + - `uploadFirstFrame` 不再接受纹理 `int`,而是接受一个 mip 级别 `int` + - `$AnimatedTexture#createTicker`、`uploadFirstFrame` -> `createAnimationState`,不是一对一 + - `$Ticker` -> `$AnimationState`,不是一对一 + - `tickAndUpload` -> `tick`、`getDrawUbo`、`needsToDraw`、`drawToAtlas`;不是一对一 + - `SpriteTicker` 接口已移除 + - `Stitcher` 现在接受各向异性过滤级别 + - `$Holder(T, int)` 已移除 + - `$Region#walk` 现在接受一个填充 `int` + - `$SpriteLoader` 不再接受最小宽度/高度 + - `load` 现在接受一个填充 `int` + - `TextureAtlas` 现在实现 `TickableTexture` 而不是 `Tickable` + - `TextureAtlasSprite` 现在实现 `AutoCloseable` + - 构造函数接受一个填充 `int` + - `createTicker` -> `createAnimationState`,不是一对一 + - `getUOffset`、`getVOffset`、`uvShrinkRatio` 已移除 + - `uploadFirstFrame` 现在接受 mip 级别 `int` + - `uploadSpriteUbo` - 将图集精灵上传到缓冲区。 + - `$Ticker` 接口已移除 + - `TextureManager` 不再实现 `Tickable` + - `Tickable` -> `TickableTexture` +- `net.minecraft.client.renderer.texture.atlas` + - `SpriteSource$SpriteSupplier` -> `$DiscardableLoader` + - `Function` 超接口现在表示为 `$Loader` + - `apply` -> `get` + - `SpriteSourceList#list` 现在返回一个 `SpriteSource$Loader` 列表 +- `net.minecraft.client.resources.metadata.texture.TextureMetadataSection` 现在接受一个 `MipmapStrategy` 来确定特定纹理应如何进行 mipmap,以及一个用于 alpha 截止偏置的 `float` +- `net.minecraft.client.resources.model` + - `ModelBaker` + - `missingBlockModelPart` - 缺失的方块模型。 + - `parts` - 先前构造的向量的缓存。 + - `$PartCache` - 一个缓存,用于内部存储四边形中先前构造的顶点。 + - `ModelBakery` + - `*_STILL` - 流体的静态纹理位置。 + - `$MissingModels` 现在接受一个 `BlockModelPart` 缺失模型 + - `ModelManager` + - `BLOCK_OR_ITEM` - 一个特殊情况,导致模型管理器同时检查物品和方块图集。 + - `specialBlockModelRenderer` 现在返回原始渲染器而不是提供的值。 +- `net.minecraft.data.AtlasIds#ITEMS` - 物品图集标识符。 +- `net.minecraft.world.level.block.LeavesBlock#setCutoutLeaves` - 设置树叶是否使用切割渲染。 + +## Gizmo 控件 + +Gizmo 是提交和渲染解耦的最新迭代,这次用于调试渲染器。然而,提交 gizmo 进行渲染的底层结构要复杂得多,因为调试渲染器几乎可以在客户端进程的任何时刻提交对象。 + +### 什么是 Gizmo? + +一个 `Gizmo` 基本上是一个对象,它提交一些对象图元——特别是点、线、三角形扇、四边形和文本——进行渲染。每个 gizmo 通过 `emit` 将这些图元串在一起形成所需的形状。在渲染过程中,它们使用 `RenderPipelines#DEBUG_*` 管线将其图元渲染到屏幕。创建一个新的 gizmo 就像扩展接口一样简单: + +```java +// 存储一些参数,如渲染状态,以提交元素图元 +public record ExampleGizmo(Vec3 start, Vec3 end) implements Gizmo { + + @Override + public void emit(GizmoPrimitives gizmos, float alphaMultiplier) { + // 在此处提交任何元素 + gizmos.addLine(this.start, this.end, ARGB.multiplyAlpha(0, alphaMultiplier), 3f); + } +} +``` + +实际提交元素通过 `Gizmos#addGizmo` 发生。这会将 gizmo 存储起来,以便在 `Minecraft#tick` 或客户端上的任何渲染期间发出并绘制到屏幕——这也是调试渲染器发出 gizmo 的方式,在单机世界中通过 `IntegratedServer#tickServer`,或通过任一侧的数据包处理。`Gizmos` 中的所有方法都在内部调用 `addGizmo`,这就是为什么该方法通常在其类之外不存在的原因: + +```java +// 在 GameRenderer#render 的某处 + +Gizmos.addGizmo(new ExampleGizmo(Vec3.ZERO, Vec3.X_AXIS)); + +// 内部调用 addGizmo +Gizmos.point(Vec3.ZERO, 0, 5f); +``` + +调用 `addGizmo` 返回一个 `GizmoProperties`,它在绘制元素时设置一些属性,假设 `GizmoCollector` 不是 `NOOP`。`GizmoProperties` 提供了三个方法: + +| 方法 | 描述 | +|:------------------:|:---------------------------------------------------------------------------| +| `setAlwaysOnTop` | 在渲染前清除深度纹理。 | +| `persistForMillis` | 在消失前将 gizmo 在屏幕上保持指定的时间。 | +| `fadeOut` | 当持续一定时间后,使消失过程淡出。 | + +### 组合在一起 + +那么,您如何几乎在客户端管道的任何地方提交 gizmo 进行渲染?这一切都从 `Gizmos#withCollector` 和 `SimpleGizmoCollector` 开始。 + +`SimpleGizmoCollector` 基本上只是一个保存收集到的要渲染的 gizmo 的列表。在渲染过程中,调用 `SimpleGizmoCollector#drainGizmos`,将 gizmo 复制到一个单独的列表中,供渲染器通过熟悉的帧通道和缓冲区源在 `Gizmo#emit` 之前使用。然后,`drainGizmos` 根据 `GizmoProperties#persistForMillis` 清除内部列表,如果未指定则立即清除,以供下一帧使用。 + +为了实际收集这些元素,有一个相当复杂的过程。`Minecraft`、`LevelRenderer` 和 `IntegratedServer` 都有自己的 `SimpleGizmoCollector`。这是通过使用 `Gizmo#withCollector` 设置收集器来完成的,它返回一个 `Gizmos$TemporaryCollection`。该集合是 `AutoCloseable` 的,当关闭时,会释放本地线程上持有的收集器。因此,收集器被包装在 try-with-resources 中,以便在这些期间促进提交。然后,在调试通道期间,每Tick的 gizmo 与每帧的 gizmo 合并,并通过 `addTemporaryGizmos` 绘制到屏幕上,实际上是在 `LevelRenderer#renderLevel` 的最后一刻。在单机世界中,`IntegratedServer` 的 gizmo 存储在一个易失性字段中,允许从客户端线程访问它。 + +- `net.minecraft.client.Minecraft` + - `collectPerTickGizmos` - 返回所有要发出的 gizmo 的集合。 + - `getPerTickGizmos` - 获取要绘制到屏幕的 gizmo。 +- `net.minecraft.client.renderer` + - `LevelRenderer#collectPerFrameGizmos` - 返回所有要发出的 gizmo 的集合。 + - `OrderedSubmitNodeCollector#submitHitbox` 已移除 + - `ShapeRenderer` + - `renderShape` 现在接受一个线宽 `float` + - `renderLineBox` -> `Gizmos#cuboid`,不是一对一 + - `addChainedFilledBoxVertices` -> `Gizmos#cuboid`,不是一对一 + - `renderFace` -> `Gizmos#rect`,不是一对一 + - `renderVector` -> `Gizmos#line`,不是一对一 + - `SubmitNodeCollection#getHitboxSubmits` 已移除 + - `SubmitNodeStorage$HitboxSubmit` 记录已移除 +- `net.minecraft.client.renderer.debug` + - `DebugRenderer` + - `render` -> `emitGizmos`,不再接受 `PoseStack`、`BufferSource` 或 `boolean`,现在接受部分刻 `float` + - `renderFilledUnitCube` -> `Gizmos#cuboid`,不是一对一 + - `renderFilledBox` -> `Gizmos#cuboid`,不是一对一 + - `renderTextOverBlock` -> `Gizmos#billboardTextOverBlock`,不是一对一 + - `renderTextOverMob` -> `Gizmos#billboardTextOverMob`,不是一对一 + - `renderFloatingText` -> `Gizmos#billboardText`,不是一对一 + - `renderVoxelShape` -> `LevelRenderer#renderHitOutline`,现在是私有的,不是一对一 + - `SimpleDebugRenderer$render` -> `emitGizmos`,不再接受 `PoseStack`、`BufferSource` 或 `boolean`,现在接受部分刻 `float` + - `GameTestBlockHighlightRenderer` + - `render` -> `emitGizmos`,不接受参数 + - `renderMarker` 不再接受 `PoseStack` 或缓冲区源 + - `$Marker#get*` 已移除 + - `LightDebugRenderer` 现在接受两个标志,决定是否显示块光或天空光 + - `PathfindingRenderer#renderPath`、`renderPathLine` 不再接受 `PoseStack` 或缓冲区源 +- `net.minecraft.client.renderer.entity.EntityRenderer#extractAdditionalHitboxes` 已移除 +- `net.minecraft.client.renderer.entity.state` + - `EntityRenderState#hitboxesRenderState`、`serverHitboxesRenderState` 已移除 + - `HitboxesRenderState` 类已移除 + - `ServerHitboxesRenderState` 类已移除 +- `net.minecraft.client.renderer.feature.HitboxFeatureRenderer` -> `EntityHitboxDebugRenderer`,不是一对一 +- `net.minecraft.client.renderer.gizmos.DrawableGizmoPrimitives` - 用于图元形状或 gizmo 的存储和渲染器。 +- `net.minecraft.client.renderer.texture` + - `AbstractTexture` + - `sampler`、`getSampler` - 返回纹理使用的 `GpuSampler`。 + - `setClamp` -> `GpuSampler#getAddressMode*`,不是一对一 + - `setFilter` -> `GpuSampler#get*Filter`,不是一对一 + - `ReloadableTexture` 不再接受地址模式和过滤 `boolean` +- `net.minecraft.client.server.IntegratedServer#getPerTickGizmos` - 获取当前Tick要绘制到屏幕的 gizmo。 +- `net.minecraft.gizmos` + - `ArrowGizmo` - 一个绘制箭头的 gizmo。 + - `CircleGizmo` - 一个绘制近似圆形(使用二十个顶点)的 gizmo。 + - `CuboidGizmo` - 一个绘制矩形棱柱的 gizmo。 + - `Gizmo` - 一个可以发出简单形状图元进行绘制的对象。 + - `GizmoCollector` - 一个只添加的收集器。 + - `GizmoPrimitives` - 可以绘制的形状图元。 + - `GizmoProperties` - 应用于 gizmo 应如何绘制的属性。 + - `Gizmos` - 一个用于创建 gizmo 和收集它们的静态方法集合。 + - `GizmoStyle` - 一个属性持有者,用于定义 gizmo 应如何绘制。这些由 gizmo 本身使用,而不是实际图元。 + - `LineGizmo` - 一个绘制线的 gizmo。 + - `PointGizmo` - 一个绘制点的 gizmo。 + - `RectGizmo` - 一个绘制矩形的 gizmo。 + - `SimpleGizmoCollector` - 一个收集器实现,用于在将 gizmo 发送到渲染之前添加它们。 + - `TextGizmo` - 一个绘制文本的 gizmo。 +- `net.minecraft.server.MinecraftServer#processPacketsAndTick` - 处理服务器Tick和数据包处理。 + +## 权限大修 + +权限级别整数已被扩展为一个既简单又复杂的新系统。有三个主要部分:`Permission`、`PermissionSet` 和 `PermissionCheck`。 + +### 权限 + +`Permission` 在功能上是定义某种状态的数据对象。原版提供了两种类型的权限:`Permission$Atom`,它只是一个唯一的单元对象;以及 `Permission$HasCommandLevel`,它持有命令所需的 `PermissionLevel`。这两个数据对象都注册为映射编解码器,用于转储命令报告。 + +```java +// 尝试查询管理员级别权限。 +public static final Permission COMMANDS_MODERATOR = new Permission.HasCommandLevel(PermissionLevel.MODERATORS); +``` + +可以通过某个扩展 `Permission` 的类或记录来创建自定义权限,并将关联的 `MapCodec` 注册到其静态注册表: + +```java +// 这不检查用户是否具有给定的权限 +// 它仅仅只是持有表示权限状态的数据 +public record HasExamplePermission(int state) implements Permission { + public static final MapCodec MAP_CODEC = Codec.INT.fieldOf("state") + .xmap(HasExamplePermission::new, HasExamplePermission::state); + + @Override + public MapCodec codec() { + return HasExamplePermission.MAP_CODEC; + } +} + +// 在某个注册处理程序中 +Registry.register( + BuiltInRegistries.PERMISSION_TYPE + Identifier.withNamespaceAndPath("examplemod", "has_example_permission"), + HasExamplePermission.MAP_CODEC +); + +// 存储权限以供使用 +public static final Permission HAS_STATE_ONE = new HasExamplePermission(1); +``` + +### 权限集 + +如果 `Permission` 定义了可查询的状态,那么 `PermissionSet` 就是用户实际拥有的权限。`PermissionSet` 是一个函数式接口,用于检查用户是否具有所需的状态(通过 `hasPermission`)。原版使用 `LevelBasedPermissionSet` 来检查查询的权限是否与当前的 `PermissionLevel` 匹配。由于通常会对一组权限检查权限集,因此可以通过 `PermissionSet#union` 将多个权限集组合成一个。它在功能上执行 OR 操作,意味着如果一个集合不检查某个权限,它应默认为 `false`。 + +```java +public interface ExamplePermissionSet extends PermissionSet { + + // 为我们的权限跟踪用户的状态 + int state(); + + @Override + default boolean hasPermission(Permission permission) { + // 检查我们的权限 + if (permission instanceof HasExamplePermission example) { + return this.state() >= example.state(); + } + + // 否则忽略 + return false; + } +} + +// 存储一个权限集 +// 也可以在所需的目标上实现或存储 +public static ExamplePermissionSet STATE_ONE = () -> 1; + +// 检查权限集是否具有所需的权限 +STATE_ONE.hasPermission(HAS_STATE_ONE); +``` + +目前,没有简单的方法在所需用户上存储自定义权限集。`CommandSourceStack` 确实有一个通过 `withMaximumPermission` 合并其他权限集的方法,但这需要在关联的 `createCommandSourceStack` 方法中处理。通常可以通过 `permissions` 方法查询普通的 `LevelBasedPermissionSet`,尽管对象之间没有公共接口,即使 `PermissionSetSupplier` 似乎为此目的而存在。 + +### 权限检查 + +现在,`PermissionSet` 永远不会直接在代码库中检查 `Permission`。这需要对象始终可访问。相反,会创建一个 `PermissionCheck` 对象,它接受 `PermissionSet` 并 `check` 用户是否具有继续执行所需的数据。原版提供了两种类型的检查:`$AlwaysPass`,意味着它将始终返回 true;以及 `$Require`,要求集合具有所需的 `Permission`。这些检查也有一个用于转储命令报告的映射编解码器。 + +```java +// 要求权限集具有管理员命令的访问权限 +public static final PermissionCheck LEVEL_MODERATORS = new PermissionCheck.Require(COMMANDS_MODERATOR); +``` + +可以通过实现 `check` 的某个类或记录来创建自定义权限检查,并将映射编解码器注册到其静态注册表: + +```java +public static record AnyOf(List permissions) implements PermissionCheck { + public static final MapCodec MAP_CODEC = Permission.CODEC.listOf().fieldOf("permissions") + .xmap(AnyOf::new, AnyOf::permissions); + + @Override + public boolean check(PermissionSet permissionSet) { + return this.permissions.stream().filter(perm -> permissionSet.hasPermission(perm)).findAny().isPresent(); + } + + @Override + public MapCodec codec() { + return MAP_CODEC; + } +} + +// 在某个注册处理程序中 +Registry.register( + BuiltInRegistries.PERMISSION_CHECK_TYPE + Identifier.withNamespaceAndPath("examplemod", "any_of"), + AnyOf.MAP_CODEC +); + +// 存储检查以供在命令中使用 +public static final PermissionCheck CHECK_STATE_ONE = new AnyOf(List.of( + HAS_STATE_ONE, + Permissions.COMMANDS_GAMEMASTER +)); + +// 对于某个命令 +Commands.literal("example").requires(Commands.hasPermission(CHECK_STATE_ONE)); +``` + +- `net.minecraft.client.multiplayer.ClientSuggestionProvider` 不再实现 `PermissionSource` + - 构造函数现在接受 `PermissionSet` 而不是 `boolean` + - `allowsRestrictedCommands` -> `ClientPacketListener#ALLOW_RESTRICTED_COMMANDS`,现在是私有的,不是一对一 +- `net.minecraft.client.player.LocalPlayer#setPermissionLevel` -> `setPermissions`,不是一对一 +- `net.minecraft.commands` + - `Commands` + - `LEVEL_*` 现在是 `PermissionCheck` 而不是 `int` + - `hasPermission` 现在接受 `PermissionCheck` 而不是 `int`,并返回 `PermissionProviderCheck` 而不是 `PermissionCheck` + - `createCompilationContext` - 使用给定的权限创建源堆栈。 + - `CommandSourceStack` 不再实现 `PermissionSource` + - 构造函数现在接受 `PermissionSet` 而不是 `int` + - `protected` 构造函数现在是 `private` + - `withPermission` 现在接受 `PermissionSet` 而不是 `int` + - `withMaximumPermission` 现在接受 `PermissionSet` 而不是 `int` + - `ExecutionCommandSource` 现在扩展 `PermissionSetSupplier` 而不是 `PermissionSource` + - `PermissionSource` 接口已移除 + - `SharedSuggestionProvider` 现在扩展 `PermissionSetSupplier` +- `net.minecraft.commands.arguments.selector.EntitySelectorParser#allowSelectors` 现在有一个接受 `PermissionSetSupplier` 的重载 +- `net.minecraft.core.registries` + - `BuiltInRegistries#PERMISSION_TYPE`、`Registries#PERMISSION_TYPE` - 一个定义某些数据需求的对象。 + - `BuiltInRegistries#PERMISSION_CHECK_TYPE`、`Registries#PERMISSION_CHECK_TYPE` - 一个谓词,检查集合是否具有所需的数据。 +- `net.minecraft.server` + - `MinecraftServer` + - `operatorUserPermissionLevel` -> `operatorUserPermissions`,不是一对一 + - `getFunctionCompilationLevel` -> `getFunctionCompilationPermissions`,不是一对一 + - `getProfilePermissions` 现在返回 `LevelBasedPermissionSet` + - `ReloadableServerResources#loadResources` 现在接受 `PermissionSet` 而不是 `int` + - `ServerFunctionLibrary` 现在接受 `PermissionSet` 而不是 `int` + - `WorldLoader$InitConfig` 现在接受 `PermissionSet` 而不是 `int` +- `net.minecraft.server.commands.PermissionCheck` -> `.server.permissions.PermissionCheck`,不是一对一 +- `net.minecraft.server.dedicated.DedicatedServerProperties` + - `opPermissionLevel` -> `opPermissions`,不是一对一 + - `functionPermissionLevel` -> `functionPermissions`,不是一对一 + - `deserializePermissions`、`serializePermission` - 读取和写入选定的级别权限集。 +- `net.minecraft.server.jsonrpc.internalapi` + - `MinecraftOperatorListService#op` 现在接受一个可选的 `PermissionLevel` 而不是 `int` + - `MinecraftServerSettingsService` + - `getOperatorUserPermissionLevel` -> `getOperatorUserPermissions`,不是一对一 + - `setOperatorUserPermissionLevel` -> `setOperatorUserPermissions`,不是一对一 +- `net.minecraft.server.jsonrpc.methods` + - `OperatorService$OperatorDto#permissionLevel` 现在接受一个可选的 `PermissionLevel` 而不是 `int` + - `ServerSettingsService` + - `operatorUserPermissionLevel` 现在返回 `PermissionLevel` 而不是 `int` + - `setOperatorUserPermissionLevel` 现在接受并返回 `PermissionLevel` 而不是 `int` +- `net.minecraft.server.permissions` + - `LevelBasedPermissionSet` - 一组权限,检查用户是否具有相等或更高的命令权限级别。 + - `Permission` - 与用户权限相关的数据,例如命令级别。 + - `PermissionCheckTypes` - 原版提供的权限检查类型。 + - `PermissionLevel` - 定义权限的级别序列。 + - `PermissionProviderCheck` - 一个谓词,检查供应商的权限集与检查的匹配。 + - `Permissions` - 原版提供的权限。 + - `PermissionSet` - 一组用户拥有的权限,但主要定义一个方法来确定用户是否具有所需的权限。 + - `PermissionSetSupplier` - 一个提供 `PermissionSet` 的对象。 + - `PermissionSetUnion` - 多个权限集的联合。 + - `PermissionTypes` - 原版提供的权限类型。 +- `net.minecraft.server.players` + - `PlayersList#op` 现在接受一个可选的 `LevelBasedPermissionSet` 而不是 `int` + - `ServerOpListEntry` 现在接受 `LevelBasedPermissionSet` 而不是 `int` + - `getLevel` -> `permissions`,不是一对一 +- `net.minecraft.world.entity.player.Player#getPermissionLevel`、`hasPermissions` -> `permissions`,不是一对一 +- `net.minecraft.world.entity.projectile.ProjectileUtils` + - `getHitEntitiesAlong` - 获取沿提供路径击中的实体。 + - `getManyEntityHitResult` - 获取沿两点之间路径在边界框内击中的所有实体。 +- `net.minecraft.world.entity.projectile.arrow.AbstractArrow#findHitEntities` - 获取被向量击中的所有实体。 + +## 新数据组件 + +随着矛的加入,添加了许多数据组件以提供相关功能。以下是对这些组件的简要概述。 + +### 使用效果 + +`DataComponents#USE_EFFECTS` 定义了一些效果,应用于正在使用(例如,右键单击)物品的玩家。目前,只有三种类型的效果:玩家在使用物品时是否可以冲刺、使用交互是否引起振动,以及应用于玩家水平移动的标量。 + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.USE_EFFECTS, + new UseEffects( + // 玩家在使用物品时是否可以冲刺 + true, + // 是否在物品使用时从玩家处发送振动 + false + // 应用于玩家水平移动的标量 + 0.5f + ) +)); +``` + +### 伤害类型 + +`DataComponents#DAMAGE_TYPE` 定义了用此物品击中实体时应用于实体的伤害类型。它接受伤害类型的 `ResourceKey` 或 `DamageType` 对象本身。 + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.DAMAGE_TYPE, + new EitherHolder<>( + // 此物品对攻击实体施加的伤害类型 + DamageTypes.FALLING_ANVIL + ) +)); +``` + +### 挥动动画 + +`DataComponents#SWING_ANIMATION` 定义了使用物品挥动或攻击(例如,左键单击)时播放的动画。有三种类型的动画可以播放:`SwingAnimationType#NONE`,什么都不做;`WHACK`,播放标准的挥动动画;以及 `STAB`,播放矛的刺击动画。也可以指定动画的长度。 + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.SWING_ANIMATION, + new SwingAnimation( + // 要播放的动画 + SwingAnimationType.NONE, + // 播放动画的时间,以刻为单位 + 20 + ) +)); +``` + +### 最小攻击充能 + +`DataComponents#MINIMUM_ATTACK_CHARGE` 决定玩家在使用该物品进行另一次攻击之前必须等待多长时间。充能值介于 0 和 1 之间,决定了在另一次攻击之前等待延迟的百分比。延迟由玩家的攻击速度决定。如果玩家的动作是刺击,则检查两次。 + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.MINIMUM_ATTACK_CHARGE, + // 玩家在使用此物品进行另一次攻击之前必须等待的时间百分比 + 0.5f +)); +``` + +### 攻击范围 + +`DataComponents#ATTACK_RANGE` 决定了当实体使用此物品攻击另一个实体时,可以攻击的范围。如果未设置,则默认为实体的交互范围属性。指定的范围适用于玩家,生物的攻击范围由范围乘以生物因子决定。当玩家处于创造模式时,也可以指定一个范围,覆盖默认范围。 + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.ATTACK_RANGE, + new AttackRange( + // 此物品击中实体的最小范围(以方块为单位)。 + // 必须在 [0, 64] 之间;默认为 0。 + 0.4f, + // 此物品击中实体的最大范围(以方块为单位)。 + // 必须在 [0,64] 之间;默认为 3。 + 4.5f, + // 此物品击中实体的最小范围(以方块为单位), + // 前提是持有实体是处于创造模式的玩家。 + // 这取代了最小范围。 + // 必须在 [0, 64] 之间;默认为 0。 + 0f, + // 此物品击中实体的最大范围(以方块为单位), + // 前提是持有实体是处于创造模式的玩家。 + // 这取代了最大范围。 + // 必须在 [0,64] 之间;默认为 3。 + 5f, + // 用于膨胀碰撞箱的边距(以方块为单位), + // 补偿潜在的精度问题。 + // 必须在 [0,1] 之间;默认为 0.3。 + 0.25f, + // 乘以最小和最大范围的标量,以确定 + // 非玩家实体的触及范围。 + // 必须在 [0,2] 之间;默认为 1。 + 1.1f + ) +)); +``` + +### 穿刺武器 + +`DataComponents#PIERCING_WEAPON` 将玩家的攻击设置为不是攻击,而是刺击或穿刺攻击。这是一个与挥动不同的动作,后者要么攻击实体,要么破坏方块。穿刺武器可以攻击实体,但无法破坏方块。它也会对冲刺应用任何附魔效果。穿刺武器仅适用于玩家。 + +逻辑流程如下: + +- 如果 `Player#cannotAttackWithItem` 返回 true,则终止流程 +- 穿刺攻击通过以下方式处理: + - 客户端 - `MultiPlayerGameMode#piercingAttack` + - 服务器 - `PiercingWeapon#attack` +- 仅限服务器: + - 获取所有满足以下条件的实体: + - 在实体的攻击范围内 `DataComponents#ATTACK_RANGE` + - 在从玩家眼睛位置开始构建的碰撞箱内 + - 如果 `PiercingWeapon#canHitEntity` 返回 true: + - 玩家不是无敌或死亡,并且 + - 要么: + - 实体是 `Interaction` 实体 + - 或者: + - 实体可以被投射物击中 + - 如果双方都是玩家,则此玩家可以伤害对方玩家 + - 不是同一载具的乘客 + - 对每个实体调用 `LivingEntity#stabAttack` +- 触发 `LivingEntity#onAttack` +- 触发 `LivingEntity#lungeForwardMaybe` +- 仅限服务器: + - 如果至少击中了一个实体,则播放 `PiercingWeapon#makeHitSound` + - 播放 `PiercingWeapon#makeSound` +- 触发 `LivingEntity#swing` + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.PIERCING_WEAPON, + new PiercingWeapon( + // 被此物品击中是否会对实体造成击退。 + true, + // 被此物品击中是否会使实体从载具上下来。 + true, + // 使用此物品攻击时播放的声音。 + // 如果 optional 为空,则不播放声音。 + Optional.of(SoundEvents.LLAMA_SWAG), + // 此物品击中实体时播放的声音。 + // 如果 optional 为空,则不播放声音。 + Optional.of(SoundEvents.ITEM_BREAK) + ) +)); +``` + +### 动能武器 + +`DataComponents#KINETIC_WEAPON` 影响实体的使用(例如,右键单击)行为。在右键单击时,如果物品具有该组件,则每刻调用 `KineticWeapon#damageEntities` 而不是 `Item#onUseTick`,仅在服务器上。动能武器也会调用 `LivingEntity#stabAttack` 来伤害其实体,类似于穿刺攻击。实际上,该组件本身与 `PiercingWeapon` 类似,只是多了几个字段来处理应用的动能伤害,并使其适用于所有活体实体而不仅仅是玩家。 + +要使刺击攻击发生,必须满足其中一个条件(下马、击退、伤害)并返回 true。攻击范围从 `DataComponents#ATTACK_RANGE` 组件获得。如果发生刺击攻击,则会在服务器上触发 `SPEAR_MOBS_TRIGGER` 条件。 + +```java +// 对于某个物品注册 +new Item(new Item.Properties.component( + DataComponents.KINETIC_WEAPON, + new KineticWeapon( + // 此实体在尝试接触(例如,伤害)另一个实体之前等待的刻数。 + 10, + // 在尝试刺击范围内的任何实体之前等待的刻数。 + 20, + // 检查此物品的攻击是否会使载具中的实体下马的条件。 + // 如果 optional 不存在,则默认为 false。 + Optional.of(new KineticWeapon.Condition( + // 从此条件可能返回 true 的首次使用加延迟的最大刻数。 + 100, + // 此条件成功所需的实体移动的最小速度。 + // 速度计算为增量运动与视图向量的点积乘以 20。 + // 原版矛对下马使用 7-14 的值,对击退使用 5.1。 + 9f, + // 此条件成功所需的,相对于攻击实体的此实体的最小移动速度。 + // 原版矛对伤害使用 4.6。 + 5f + )), + // 检查此物品的攻击是否会对实体造成击退的条件。 + // 如果 optional 不存在,则默认为 false。 + Optional.of(KineticWeapon.Condition.ofAttackerSpeed( + // 最大刻数 + 100, + // 实体移动速度 + 5.1f + )), + // 检查此物品的攻击是否会伤害实体的条件。 + // 如果 optional 不存在,则默认为 false。 + Optional.of(KineticWeapon.Condition.ofRelativeSpeed( + // 最大刻数 + 100, + // 相对移动速度 + 4.6f + )), + // 第三人称攻击动画期间物品的移动 + // 原版矛使用 0.38。 + 0.38f, + // 应用于实体伤害的乘数 + // 伤害计算为此实体相对于其目标的相对移动速度。 + 4f, + // 首次使用此物品时播放的声音。 + // 如果 optional 为空,则不播放声音。 + Optional.of(SoundEvents.LLAMA_SWAG), + // 此物品击中实体时播放的声音。 + // 如果 optional 为空,则不播放声音。 + Optiona.of(SoundEvents.ITEM_BREAK) + ) +)); +``` + +- `net.minecraft.core.components` + - `DataComponents` + - `USE_EFFECTS` - 使用物品时应用于实体的效果。 + - `MINIMUM_ATTACK_CHARGE` - 使用物品攻击的最短时间。 + - `DAMAGE_TYPE` - 物品造成的 `DamageType` + - `PIERCING_WEAPON` - 一种具有某些碰撞箱范围并向实体冲刺的武器。 + - `KINETIC_WEAPON` - 一种需要一定前进动量的具有碰撞箱范围的武器。 + - `SWING_ANIMATION` - 挥动物品时应用的动画。 + - `ATTACK_RANGE` - 使用物品时设置自定义攻击范围,覆盖正常的实体交互范围。 +- `net.minecraft.core.component.DataComponentType#ignoreSwapAnimation`、`$Builder#ignoreSwapAnimation` - 当为 true 时,交换动画不会影响数据组件的“使用”。 +- `net.minecraft.core.component.predicates` + - `AnyValue` - 一个谓词,检查获取器上是否存在该组件。 + - `DataComponentPredicate` + - `$Type` 现在是一个接口 + - 其原始实现已被 `$TypeBase` 取代 + - `$AnyValueType` - 一个使用 `AnyValue` 谓词的类型。 + - `$ConcreteType` - 一个定义特定谓词的类型。 +- `net.minecraft.network.protocol.game.ServerboundInteractPacket#isWithinRange` - 玩家的交互是否在有效范围内以执行。 +- `net.minecraft.world.entity` + - `LivingEntity` + - `SWING_DURATION` -> `SwingAnimation#duration`,不是一对一 + - `stabbedEntities` - 最近被动能武器攻击的实体数量。 + - `entityAttackRange` - 此实体可以攻击的范围。 + - `getActiveItem` - 当前使用的物品,或主手物品。 + - `Mob` + - `chargeSpeedModifier` - 冲刺时应用于移动速度的修改器。 + - `canFireProjectileWeapon` -> `canUseNonMeleeWeapon`,现在接受 `ItemStack` 而不是 `ProjectileWeaponItem` + - `getAttackBoundingBox` 现在接受一个水平膨胀偏移 +- `net.minecraft.world.entity.ai.behavior` + - `ChargeAttack` - 处理生物执行的冲刺攻击。 + - `SpearApproach` - 当持有动能武器时接近敌人。 + - `SpearAttack` - 使用动能武器攻击敌人。 + - `SpearRetreat` - 使用动能武器后从攻击目标逃跑。 +- `net.minecraft.world.entity.ai.goal.SpearUseGoal` - 处理生物使用矛。 +- `net.minecraft.world.entity.ai.memory.MemoryModuleType` + - `SPEAR_FLEEING_TIME` - 使用动能武器后实体已逃跑的刻数。 + - `SPEAR_FLEEING_POSITION` - 使用动能武器后实体逃跑到的位置。 + - `SPEAR_CHARGE_POSITION` - 使用动能武器时实体冲刺到的位置。 + - `SPEAR_ENGAGE_TIME` - 使用动能武器时此实体与敌人交战的时间。 + - `SPEAR_STATUS` - 使用动能武器时实体的状态。 +- `net.minecraft.world.entity.player.Player` + - `hasEnoughFoodToDoExhaustiveManoeuvres` - 返回玩家是否可以进行消耗体力的机动。 + - `canInteractWithEntity` -> `isWithinEntityInteractionRange` + - `isWithinAttackRange` - 被瞄准的边界框是否在玩家的范围内。 + - `canInteractWithBlock` -> `isWithinBlockInteractionRange` + - `CREATIVE_ENTITY_INTERACTION_RANGE_MODIFIER_VALUE` - 一个修饰符,将交互的最大范围增加给定的数量。 +- `net.minecraft.world.item` + - `Item#getDamageSource` -> `getItemDamageSource`,现已弃用 + - `ItemStack` + - `getSwingAnimation` - 返回物品的挥动动画。 + - `getDamageSource` - 返回物品被击中时提供的伤害来源。 + - `causeUseVibration` - 如果使用中的物品可以引起振动,则发送游戏事件。 + - `SwingAnimationType` - 挥动物品时播放的动画类型。 +- `net.minecraft.world.item.component` + - `AttackRange` - 此物品的碰撞箱范围。 + - `KineticWeapon` - 一种需要一定前进动量的武器。 + - `PiercingWeapon` - 一种向实体冲刺的武器。 + - `SwingAnimation` - 挥动物品时应用的动画。 + - `UseEffects` - 使用物品时应用于实体的效果。 + +## 环境属性的时间线 + +环境属性,顾名思义,为给定的维度(dimension)和/或生物群系(biome)(统称为“环境”)定义了一组属性或修改(“attributes”)。它们直接存储在生物群系或维度类型的 `attributes` 字段下,或作为可变时间线下 `tracks` 字段的一部分。每个属性可以代表从视觉设置到游戏行为的任何内容,在不同值之间进行插值。原版在 `EnvironmentAttributes` 中提供了它们可用的属性,而存储的属性则从 `Level#environmentAttributes` 获得。 + +```json5 +// 对于某个 DimensionType json +// 在 `data/examplemod/dimension_type/example_dimension.json` +{ + // 定义在维度内应用的属性 + "attributes": { + // 设置云高度 + // 更技术地说,通过覆盖来修改值 + "minecraft:visual/cloud_height": 90 + }, + // ... +} + +// 对于某个 Biome json +// 在 `data/examplemod/worldgen/biome/example_biome.json` +{ + // 定义在生物群系内应用的属性 + // 默认或修改维度中的属性 + // 这些属性必须是位置性的 + "attributes": { + "minecraft:visual/cloud_height": { + // 不设置值,而是应用一个修改器 + "modifier": "add", + // 将 60 加到 90,使该生物群系的云高度为 150 + "argument": 60 + } + } + // ... +} + +// 对于某个 Timeline json +// 在 `data/examplemod/timeline/example_timeline.json +{ + // 此轨道在重复之前花费的刻数 + "period_ticks": 24000, + // 定义要基于定义的 + // 关键帧进行插值的属性。 + // 默认或修改生物群系或维度中的属性 + "tracks": { + "minecraft:visual/cloud_height": { + // 定义属性特定值的关键帧 + "keyframes": [ + { + // 表示关键帧的刻 + "tick": 12000, + // 修改值的参数 + // 在这种情况下,将 1 加到 60 + 90,使云高度为 151 + "value": 1 + }, + { + // 表示关键帧的刻 + "tick": 23999, + // 修改值的参数 + // 在这种情况下,将 0 加到 60 + 90,使云高度为 150 + "value": 0 + } + ], + // 不设置值,而是将修改器应用于参数 + "modifier": "add", + // 在刻之间进行插值时应用的采样函数 + "ease": "linear" + } + } +} +``` + +当调用 `EnvironmentAttributeSystem#getValue` 时,属性值通过 `Level` 定义的层获得: + +1. 从注册的属性中读取默认值(通过 `EnvironmentAttribute#defaultValue`)。 +2. 应用来自维度的修改器,如果该属性不存在则不执行任何操作。 +3. 应用来自生物群系的修改器,如果不存在则不执行任何操作。 +4. 应用 `DimensionType` 中定义的所有活动时间线的修改器,如果不存在则不执行任何操作。时间线顺序不保证。 +5. 如果维度可以有天气(天空光照、无天花板、且不是末地),则应用 `WeatherAttributes` 的修改器。 +6. 如果在客户端(即 `ClientLevel`),则应用天空闪光修改器。 +7. 将最终值清理到 `EnvironmentAttribute` 定义的范围内。 + +这是高度简化的,并引入了许多新概念,因此让我们通过创建我们自己的环境属性和时间线来进一步分解。 + +### 自定义环境属性 + +环境属性通过 `$Builder` 创建,使用 `EnvironmentAttribute#builder`,接受其表示的类型值(例如,浮点数、整数、对象)。构建器只需要设置一个值:`defaultValue`。如果维度或生物群系未覆盖该属性,则使用此值。如果属性值应具有一组有效的状态,则可以通过 `valueRange` 设置 `AttributeRange`。`AttributeRange` 基本上是一个一元运算符,通过 `sanitize` 将输入转换为其“有效”状态。它还通过 `validate` 验证通过 JSON 传入的值是否处于“有效”状态。 + +从那里开始,还有三个方法负责确定用于计算值的逻辑。`$Builder#syncable` 将属性同步到客户端,这对于任何导致某种变化的属性(例如,视觉、音频或通用代码)是必需的。`notPositional` 意味着该属性不能应用于生物群系(仍可在维度或时间线中设置),否则会抛出异常。最后,`spatiallyInterpolated` 将尝试使用属性类型在不同的生物群系之间进行插值,以应用更无缝的过渡。原版仅处理客户端属性的空间插值。服务器上的任何内容都必须处理自己的 `SpatialAttributeInterpolator`。 + +最后,实际属性可以通过 `$Builder#build` 获得。该值必须注册到 `BuiltInRegistries#ENVIRONMENT_ATTRIBUTE`: + +```java +public static final EnvironmentAttribute EXAMPLE_ATTRIBUTE = Registry.register( + BuiltInRegistries.ENVIRONMENT_ATTRIBUTE, + Identifier.withNamespaceAndPath("examplemod", "example_attribute"), + EnvironmentAttribute.builder( + // 属性类型 + // 必须与属性值的泛型匹配 + AttributeTypes.BOOLEAN + ) + // 此属性应默认具有的值 + .defaultValue(false) + // 将此值同步到客户端 + .syncable() + .build() +); +``` + +```json5 +// 对于某个 DimensionType json +// 在 `data/examplemod/dimension_type/example_dimension.json` +{ + "attributes": { + "examplemod:example_attribute": true + }, + // ... +} + +// 对于某个 Biome json +// 在 `data/examplemod/worldgen/biome/example_biome.json` +{ + "attributes": { + "examplemod:example_attribute": { + "modifier": "xor", + "argument": true + } + } + // ... +} + +// 对于某个 Timeline json +// 在 `data/examplemod/timeline/example_timeline.json +{ + "period_ticks": 24000, + "tracks": { + "examplemod:example_attribute": { + "keyframes": [ + { + "tick": 12000, + "value": false + }, + { + "tick": 23999, + "value": true + } + ], + "modifier": "and", + "ease": "linear" + } + } +} +``` + +### 自定义属性类型 + +每个环境属性都有一个关联的属性类型,静态注册到 `BuiltInRegistries#ATTRIBUTE_TYPE`。这不仅定义了如何序列化对象值,还包含可以应用于值的修改以及如何在空间和帧之间进行插值。实际上,包括 `syncable` 和 `spatiallyInterpolated` 在内的所有构建器设置都依赖于属性类型来确定执行该操作意味着什么。如果没有它,甚至无法从维度或生物群系 JSON 中读取属性,更不用说获取属性值的实际逻辑了。 + +因此,属性类型可以分为三个部分:序列化编解码器、修改器库和插值函数。 + +```java +// 我们将使用此示例对象来解释属性类型 +public record ExampleObject(int value1, boolean value2) { + // 默认值 + public static final ExampleObject DEFAULT = new ExampleObject(0, false); +} +``` + +#### 类型序列化 + +属性类型的序列化通过该类型的编解码器处理,包括到磁盘(生物群系和维度 JSON)和网络(`$Builder#syncable`): + +```java +// 用于序列化属性类型值的编解码器 +public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + Codec.INT.fieldOf("value1").forGetter(ExampleObject::value1), + Codec.BOOL.fieldOf("value2").forGetter(ExampleObject::value2) + ).apply(instance, ExampleObject::new) +); +``` + +#### 修改器库 + +修改器库是一个 `AttributeModifier$OperationId` 到 `AttributeModifier` 的映射,确定可以对默认值执行哪些操作。如果映射不包含任何操作,则默认值不能更改。`AttributeType` 的所有静态构造函数都添加了 `OVERRIDE` 修改器,允许维度和/或生物群系设置值。这个映射应该被视为一个伪注册表(基本上是键到唯一值的映射),因为用于序列化的默认编解码器是通过 id 解析器为 `BiMap` 提供的。 + +一个 `AttributeModifier` 定义了两个泛型:第一个是环境属性值类型,第二个是用于应用操作的任意对象。修改器有两个方法:`apply`,接受值和参数以返回一个新值;以及 `argumentCodec`,用于正确序列化参数。对于任何操作,所有可能的修改都必须实现在单个 `AttributeModifier` 中: + +```java +// 仅处理对象一部分的修改器 +public static final AttributeModifier ADD = new AttributeModifier<>() { + + @Override + public ExampleObject apply(ExampleObject subject, Integer argument) { + // 将操作应用于 subject + return new ExampleObject(subject.value1() + argument, subject.value2()); + } + + @Override + public Codec argumentCodec(EnvironmentAttribute attribute) { + // 构造用于反序列化参数的编解码器 + return Codec.INT; + } +}; + +public static final AttributeModifier OR = new AttributeModifier<>() { + + @Override + public ExampleObject apply(ExampleObject subject, Boolean argument) { + // 将操作应用于 subject + return new ExampleObject(subject.value1(), subject.value2() || argument); + } + + @Override + public Codec argumentCodec(EnvironmentAttribute attribute) { + // 构造用于反序列化参数的编解码器 + return Codec.BOOL; + } +}; + +// 一个处理所有可能对象组合的修改器 +public static final AttributeModifier>> AND = new AttributeModifier<>() { + + @Override + public ExampleObject apply(ExampleObject subject, Either> argument) { + return argument.map( + arg -> new ExampleObject(subject.value1() & arg.value1(), subject.value2() && arg.value2()), + either -> either.map( + arg -> new ExampleObject(subject.value1() & arg, subject.value2()), + arg -> new ExampleObject(subject.value1(), subject.value2() && arg) + ) + ); + } + + @Override + public Codec>> argumentCodec(EnvironmentAttribute attribute) { + // 构造用于反序列化参数的编解码器 + // 我们可以为值类型使用属性编解码器 + return Codec.either(attribute.valueCodec(), Codec.either(Codec.INT, Codec.BOOL)); + } +}; + +// 构造库 +// 参数可以是任何值,只要它可以被序列化和处理 +// 如果使用属性类型的静态构造函数之一,则会自动添加覆盖 +// 处理程序以及映射的关联修改器编解码器 +public static final Map> EXAMPLE_LIBRARY = Map.of( + AttributeModifier.OperationId.ADD, ADD, + AttributeModifier.OperationId.OR, OR, + AttributeModifier.OperationId.AND, AND +); +``` + +#### 类型插值 + +为了支持插值,无论是对于客户端帧(由于 `$Builder#syncable`)、空间(`$Builder#spatiallyInterpolated`)、状态(天气)还是关键帧(时间线),都需要一个函数,在给定 0 和 1 之间的某个步长(时间或位置)时,如何合并两个值。这通过 `LerpFunction` 处理,其泛型是环境属性值类型。对于非插值值,这通常使用 `LerpFunction#ofStep`,它类似于两个值之间的简单阈值。更具体地说,空间插值将阈值设置为 0.5,而部分刻、关键帧和状态更改将仅考虑完整步长(意味着总是下一个值)。然而,这个函数可以根据您的选择定义: + +```java +// Step 表示 0 和 1 之间的插值 +// Original 表示步长为 0 的值 +// Next 表示步长为 1 的值 +public static final LerpFunction EXAMPLE_SPATIAL_LERP = (step, original, next) -> { + return new ExampleObject( + Mth.lerp(step, original.value1(), next.value1()), + step >= 0.5f ? next.value2() : original.value2() + ); +} + +public static final LerpFunction EXAMPLE_PARTIAL_LERP = (step, original, next) -> { + return new ExampleObject( + Mth.lerp(step, original.value1(), next.value1()), + next.value2() + ); +} + +// 将始终返回第一个值 +public static final LerpFunction EXAMPLE_KEYFRAME_LERP = LerpFunction.ofConstant(); + +// 在步长过去 0.1 后更改为下一个状态 +public static final LerpFunction EXAMPLE_STATE_CHANGE_LERP = LerpFunction.ofStep(0.1f); +``` + +#### 整合在一起 + +有了这些部分,现在可以构造一个 `AttributeType`。这通常使用静态构造函数之一完成:`ofInterpolated` 用于定义其插值函数的值,或 `ofNotInterpolated` 用于可以在两个值之间直接切换的值。对于常见用例,除非您的值在玩家自然可见的内容(例如,雾或天空颜色的视觉)之间过渡,否则插值通常是不必要的。 + +如果您决定改用 `AttributeType` 实例构造函数,您还需要创建一个编解码器来序列化修改器映射。请参阅 `AttributeType#createModifierCodec` 了解如何操作。 + +```java +// 属性类型必须静态注册才能被正确处理 +public static final AttributeType EXAMPLE_ATTRIBUTE_TYPE = Registry.register( + BuiltInRegistries.ATTRIBUTE_TYPE, + Identifier.withNamespaceAndPath("examplemod", "example_attribute_type"), + new AttributeType<>( + // 值的编解码器 + ExampleObject.CODEC, + // 可以修改的操作映射 + // `OVERRIDE` 会自动添加用于序列化 + EXAMPLE_LIBRARY, + // 用于序列化修改器库的编解码器 + Util.make(() -> { + ImmutableBiMap> map = ImmutableBiMap.builder() + .put(AttributeModifier.OperationId.OVERRIDE, AttributeModifier.override()) + .putAll(EXAMPLE_LIBRARY) + .buildOrThrow(); + return ExtraCodecs.idResolverCodec(AttributeModifier.OperationId.CODEC, map::get, map.inverse()::get); + }), + // 在时间线中两个关键帧之间插值的函数 + EXAMPLE_KEYFRAME_LERP, + // 在两个状态之间插值的函数(仅用于天气映射中的属性) + EXAMPLE_STATE_CHANGE_LERP, + // 在两个空间坐标之间插值的函数 + EXAMPLE_SPATIAL_LERP, + // 在客户端帧之间插值的函数 + EXAMPLE_PARTIAL_LERP + ) +); +``` + +从那里,我们可以创建一个使用该类型的 `EnvironmentAttribute`: + +```java +public static final EnvironmentAttribute EXAMPLE_OBJECT_ATTRIBUTE = Registry.register( + BuiltInRegistries.ENVIRONMENT_ATTRIBUTE, + Identifier.withNamespaceAndPath("examplemod", "example_object_attribute"), + EnvironmentAttribute.builder( + EXAMPLE_ATTRIBUTE_TYPE + ) + .defaultValue(ExampleObject.DEFAULT) + // 由于编解码器和部分刻插值而可能 + .syncable() + // 由于空间插值而可能 + .spatiallyInterpolated() + .build() +); +``` + +```json5 +// 对于某个 DimensionType json +// 在 `data/examplemod/dimension_type/example_dimension.json` +{ + "attributes": { + "examplemod:example_object_attribute": { + "value1": 10, + "value2": true + } + }, + // ... +} + +// 对于某个 Biome json +// 在 `data/examplemod/worldgen/biome/example_biome.json` +{ + "attributes": { + "examplemod:example_object_attribute": { + // 必须使用库中定义的参数之一 + // 在这种情况下是 'add'、'or' 或 'and' + "modifier": "and", + // 这可以是布尔值、整数或对象 + // 因为序列化器是这样定义的 + "argument": false + } + } + // ... +} + +// 对于某个 Timeline json +// 在 `data/examplemod/timeline/example_timeline.json +{ + "period_ticks": 24000, + "tracks": { + "examplemod:example_object_attribute": { + "keyframes": [ + { + "tick": 12000, + // 这可以是布尔值、整数或对象 + // 因为序列化器是这样定义的 + "value": 1 + }, + { + "tick": 23999, + // 这可以是布尔值、整数或对象 + // 因为序列化器是这样定义的 + "value": { + "value1": 0, + "value2": false + } + } + ], + // 必须使用库中定义的参数之一 + // 在这种情况下是 'add'、'or' 或 'and' + "modifier": "and", + "ease": "linear" + } + } +} +``` + +### 时间线 + +`Timeline` 是一种基于当前游戏时间修改属性的方法。更具体地说,它们定义了一些关键帧,值在关键帧之间进行插值,首先使用 `EasingType` 函数确定步长,其次使用 `AttributeType#keyframeLerp` 获取值。这不仅是大脑中 `Schedule` 的替代品,也是与昼夜循环相关的属性(例如,天空颜色、史莱姆生成几率等)的替代品。它们作为生物群系修改器之后的一层,用于位置性和非位置性属性。 + +时间线基于 `DimensionType#timelines` 标签激活,这些标签以 `in_` 为前缀(例如,`minecraft:in_overworld` 是主世界的时间线标签)。所有维度标签都包括 `minecraft:universal` 标签,这意味着所有标记的时间线将在所有维度内运行(前提是它们添加了 universal 标签)。 + +原版时间线如下: + +- `minecraft:day`: 昼夜循环 +- `minecraft:moon`: 月相和生成几率 +- `minecraft:villager_schedule`: 村民执行什么 `Activity` +- `minecraft:early_game`: 在最初几天阻止掠夺者巡逻队生成 + +关联的标签如下: + +- `minecraft:universal` + - `minecraft:villager_schedule` +- `minecraft:in_overworld` - 主世界维度 + - `#minecraft:universal` + - `minecraft:day` + - `minecraft:moon` + - `minecraft:early_game` +- `minecraft:in_nether` - 下界维度 + - `#minecraft:universal` +- `minecraft:in_end` - 末地维度 + - `#minecraft:universal` + +#### 关键帧 + +每个 `Timeline` 由关键帧组成,负责确定在给定刻时修改器的参数应该是什么。然后,这些关键帧被编译成一个称为 `KeyframeTrack` 的列表,在构造属性层时烘焙成一个 `KeyframeTrackSampler`。每两个相邻的关键帧(包括第一个和最后一个)被认为是一个 `KeyframeTrackSampler$Segment`。这就是用于在给定刻采样属性的内容。 + +假设我们有以下(关键帧,值)段 (100, 0) -> (200, 1),并且我们当前在 150 刻。我们如何选择使用什么参数?这通过两个操作执行。首先,我们计算步长:介于 `0` 和 `1` 之间的值,决定与值进行多少插值。步长首先线性计算:(current_tick - start_segment_tick) / (end_segment_tick - start_segment_tick)。然后,将步长传递给所需的 `EasingType`,这是一个接受 0-1 值并返回 0-1 值的函数,例如 `in_out_bounce` 或 `out_back`。您也可以像这样创建自己的 `EasingType`: + +```java +// `EasingType#registerSimple` 必须设为公开 +EasyingType.registerSimple( + // 函数的名称 + "examplemod:ease", + // 应用于值的函数 + // 对于平滑过渡,函数应将 0 -> 0 和 1 -> 1 + original -> 0.5f * (float) Mth.sin(Math.PI * (3 * original - 0.5)) + 0.5f +); +``` + +然后,它将步长与两个参数一起传递给 `AttributeType#keyframeLerp` 函数,以获得要应用的插值参数。 + +#### 属性轨道 + +通过确定参数的关键帧,我们通过一个 `AttributeTrack` 将参数应用于属性,在构造属性层时烘焙成一个 `AttributeTrackSampler`。一个 `AttributeTrack` 包含获取参数的 `KeyframeTrack`,以及将参数应用于值的 `ArgumentModifier`。请注意,对于给定的轨道只能有一个修改器。 + +这些 `AttributeTrack` 然后存储在一个属性到轨道的映射中,这定义了我们的 `Timeline`。时间线还包含一个可选的整数,表示轨道的周期。这里的“period”充当时间线中所有轨道的一次完整运行。超出周期的值取模。大多数时间线使用 `24000` 作为周期,因为这代表 Minecraft 一天中的刻数。 + +#### 自定义时间线 + +自定义 `Timeline` 被添加到 `timeline` 数据包注册表: + +```json5 +// 对于某个 Timeline json +// 在 `data/examplemod/timeline/example_timeline.json +{ + // 每 3000 刻运行一次(一天的 1/8) + "period_ticks": 3000, + "tracks": { + // 要修改的属性 + "examplemod:example_object_attribute": { + // 确定参数之间步长的缓动函数 + "ease": "examplemod:ease", + // 定义在设置刻的参数的关键帧列表 + // 然后使用缓动函数和关键帧插值对参数进行插值 + "keyframes": [ + { + // 此参数为给定值的刻 + "tick": 1500, + // 将 10 加到属性 + "value": 10 + }, + // 在中间,使用缓动函数在 10 和 0 之间逐步下降 + { + "tick": 2999, + // 将 0 加到属性 + "value": 0 + } + // 在中间,使用缓动函数在 0 和 10 之间逐步上升 + ], + // 将参数应用于值时要使用的修改器 + "modifier": "add" + } + } +} +``` + +- `net.minecraft.client` + - `Camera#attributeProbe` - 获取客户端环境属性探测器的值和插值。 + - `Minecraft` + - `getSituationalMusic` 现在返回 `Music` 而不是 `MusicInfo` + - `getMusicVolume` - 获取背景音乐的音量,如果打开的屏幕有背景音乐则获取正常音量。 +- `net.minecraft.client.multiplayer.ClientLevel` + - `effects` 已移除 + - `getSkyDarken` -> `EnvironmentAttributes#SKY_LIGHT_COLOR`、`SKY_LIGHT_FACTOR`;不是一对一 + - `getSkyColor` -> `EnvironmentAttributes#SKY_COLOR`,不是一对一 + - `getCloudColor` -> `EnvironmentAttributes#CLOUD_COLOR`,不是一对一 + - `getStarBrightness` -> `EnvironmentAttributes#STAR_BRIGHTNESS`,不是一对一 + - `getSkyFlashTime` 现在是私有的 +- `net.minecraft.client.renderer` + - `DimensionSpecialEffects` 类已移除,完全被 `EnvironmentAttribute` 取代 + - `SkyRenderer` + - `renderSkyDisc` 现在接受一个 ARGB `int` 而不是三个 RGB `float` + - `renderSunMoonAndStars` 现在接受两个额外的 `float` 用于月亮和星星的旋转 +- `net.minecraft.client.renderer.state.SkyRenderState` + - `skyType` -> `skybox`,不是一对一 + - `isSunriseOrSunset`、`timeOfDay` 已移除 + - `moonAngle`、`starAngle` - 月亮和星星的角度。 +- `net.minecraft.client.resources.sounds.BiomeAmbientSoundsHandler` 不再接受 `BiomeManager` +- `net.minecraft.client.sounds` + - `MusicInfo` -> `Minecraft#getSituationalMusic`、`getMusicVolume`;不是一对一 + - `MusicManager#startPlaying` 现在接受 `Music` 而不是 `MusicInfo` +- `net.minecraft.core.registries` + - `BuiltInRegistries`、`Registries#ENVIRONMENT_ATTRIBUTE` - 环境属性的注册表。 + - `BuiltInRegistries`、`Registries#ATTRIBUTE_TYPE` - 属性类型的注册表。 + - `BuiltInRegistires`、`Registries#SCHEDULE` 已移除 + - `Registries#TIMELINE` - 时间线的注册表键。 +- `net.minecraft.data.tags.TimelineTagsProvider` - 时间线的标签提供者。 +- `net.minecraft.server.level.ServerLevel#getMoonBrightness` - 返回月亮的亮度。 +- `net.minecraft.sounds.Music#event` -> `sound` +- `net.minecraft.tags.TimelineTags` - 时间线的标签。 +- `net.minecraft.util` + - `BinaryAnimator$EasingFunction` -> `EasingType` + - `CubicSampler` -> `GaussianSampler`、`SpatialAttributeInterpolator`;不是一对一 + - `KeyframeTrack` - 一个关键帧轨道以及它们之间执行的缓动。 + - `KeyframeTrackSampler` - 一个基于周期重播的关键帧轨道,使用提供的函数在值之间进行插值。 +- `net.minecraft.world.attribute` + - `AmbientSounds` - 在环境中环境播放的声音。 + - `AttributeRange` - 一个接口,用于验证输入并将相应值清理到适当的边界。 + - `AttributeType` - 属性可以执行的操作和修改的类型定义。 + - `AttributeTypes` - 所有原版属性类型的注册表。 + - `BackgroundMusic` - 在环境中播放的背景音乐。 + - `BedRule` - 环境中床的功能规则。 + - `EnvironmentAttribute` - 环境中某个属性的定义。 + - `EnvironmentAttributeLayer` - 一个修改值的层。 + - `EnvironmentAttributeMap` - 属性定义到其参数和修改器的映射。 + - `EnvironmentAttributeProbe` - 用于获取和插值值的属性处理程序。仅由相机使用。 + - `EnvironmentAttributeReader` - 一个可以通过维度或位置查找环境属性的读取器。 + - `EnvironmentAttributes` - 所有原版环境属性的注册表。 + - `EnvironmentAttributeSystem` - 一个获取和空间插值环境属性的读取器实现。 + - `LerpFunction` - 一个函数式接口,接受 0-1 之间的某个值以及起始和结束值以在之间进行插值。 + - `WeatherAttributes` - 用于应用天气层的属性映射。 +- `net.minecraft.world.attribute.holder` + - `AttributeModifier` - 一个修改器,接受属性值以及某个参数(通常是相同类型的值)以产生修改后的值。 + - `BooleanModifier` - 带有布尔参数的布尔值修改器。 + - `ColorModifier` - 带有某个参数(通常是整数)的 ARGB 整数修改器。 + - `FloatModifier` - 带有某个参数(通常是浮点数或带有 alpha 插值器的浮点数)的浮点数修改器。 + - `FloatWithAlpha` - 一个包含某个值和通常用于混合的 alpha 的记录。 +- `net.minecraft.world.entity.ai.Brain` + - `getSchedule` 已移除 + - `setSchedule` 现在接受 `EnvrionmentAttribute` 而不是 `Schedule` + - `updateActivityFromSchedule` 现在接受 `EnvironmentAttributeSystem` 和位置而不是白天时间 +- `net.minecraft.world.entity.animal.bee.Bee#isNightOrRaining` 被 `EnvironmentAttributes#BEES_STAY_IN_HIVE` 取代 +- `net.minecraft.world.entity.player.Player$BedSleepingProblem` 现在是一个记录 + - `NOT_POSSIBLE_HERE` -> `BedRule#EXPLODES` + - `NOT_POSSIBLE_NOW` -> `BedRule#CAN_SLEEP_WHEN_DARK` +- `net.minecraft.world.entity.schedule` + - `Keyframe` -> `.minecraft.util.Keyframe`,不是一对一 + - `Schedule` 已移除,其逻辑被 `Timeline`、`Timelines` 取代 + - `ScheduleBuilder` 已移除,其逻辑被 `Timeline$Builder` 取代 + - `Timeline` -> `.world.timeline.Timeline`,不是一对一 +- `net.minecraft.world.entity.variant.SpawnContext` 现在接受 `EnvironmentAttributeReader` +- `net.minecraft.world.level` + - `Level` + - `isMoonVisible` 被 `EnvironmentAttributes#MOON_ANGLE` 取代 + - `getSunAngle` 被 `EnvironmentAttributes#SUN_ANGLE` 取代 + - `canHaveWeather` 现在是 `public` + - `LevelAccessor` 现在实现 `LevelReader` 而不是 `LevelTimeAccess` + - `LevelReader#environmentAttributes` - 返回管理器,用于获取维度及其关联生物群系内的环境属性。 + - `LevelTimeAccess` 接口已移除 + - `MoonPhase` + - `CODEC` - 月相的编解码器。 + - `PHASE_LENGTH` - 月相存在的刻数。 + - `startTick` - 特定相位的开始刻。 +- `net.minecraft.world.level.biome` + - `AmbientAdditionsSettings` -> `.world.attribute.AmbientAdditionsSettings` + - `AmbientMoodSettings` -> `.world.attribute.AmbientMoodSettings` + - `AmbientParticleSettings` -> `.world.attribute.AmbientParticle` + - `Biome` 现在接受 `EnvironmentAttributeMap` + - `getSkyColor` -> `EnvironmentAttributes#SKY_COLOR` + - `getFogColor` -> `EnvironmentAttributes#FOG_COLOR` + - `getAttributes` - 获取此生物群系的属性。 + - `getWaterFogColor` -> `EnvironmentAttributes#WATER_FOG_COLOR` + - `getAmbientParticle` -> `EnvironmentAttributes#AMBIENT_PARTICLES` + - `getAmbientLoop` -> `AmbientSounds#loop` 环境属性 + - `getAmbientMood` -> `AmbientSounds#mood` 环境属性 + - `getAmbientAdditions` -> `AmbientSounds#additions` 环境属性 + - `getBackgroundMusic` -> `EnvironmentAttributes#BACKGROUND_MUSIC` + - `getBackgroundMusicVolume` -> `EnvironmentAttributes#MUSIC_VOLUME` + - `$Builder` + - `putAttributes` - 从另一个映射放入所有属性。 + - `setAttribute` - 设置一个环境属性。 + - `modifyAttribute` - 修改生物群系的属性源。 + - `BiomeSpecialEffects` 现在是一个记录 + - `getFogColor` -> `EnvironmentAttributes#FOG_COLOR` + - `getWaterFogColor` -> `EnvironmentAttributes#WATER_FOG_COLOR` + - `getSkyColor` -> `EnvironmentAttributes#SKY_COLOR` + - `getAmbientParticleSettings` -> `EnvironmentAttributes#AMBIENT_PARTICLES` + - `getAmbientLoopSoundEvent` -> `AmbientSounds#loop` 环境属性 + - `getAmbientMoodSettings` -> `AmbientSounds#mood` 环境属性 + - `getAmbientAdditionsSettings` -> `AmbientSounds#additions` 环境属性 + - `getBackgroundMusic` -> `EnvironmentAttributes#BACKGROUND_MUSIC` + - `getBackgroundMusicVolume` -> `EnvironmentAttributes#MUSIC_VOLUME` +- `net.minecraft.world.level.block` + - `BedBlock#canSetSpawn` -> `BedRule#canSetSpawn` 环境属性 + - `CreakingHeartBlock#isNaturalNight` 被 `EnvironmentAttributes#CREAKING_ACTIVE` 取代 + - `RespawnAnchorBlock#canSetSpawn` 现在接受 `ServerLevel` 和 `BlockPos` +- `net.minecraft.world.level.dimension` + - `BuiltinDimensionTypes#*_EFFECTS` 已移除 + - `DimensionDefaults#OVERWORLD_CLOUD_HEIGHT` 现在是一个 `float` + - `DimensionType` + - `fixedTime` -> `hasFixedTime`,现在是 `boolean` 而不是 `OptionalLong` + - `natural`、`effectsLocation` 已移除 + - `skybox` - 在维度内显示的天空盒。 + - `cardinalLightType` - 贯穿维度的光线类型。 + - `timelines` - 一组修改此维度环境属性的时间线。 + - `ultraWarm` -> `EnvironmentAttributes#WATER_EVAPORATES`、`FAST_LAVA`、`DEFAULT_DRIPSTONE_PARTICLE` + - `bedWorks` -> `EnvironmentAttributes#BED_RULE` + - `respawnAnchorWorks` -> `EnvironmentAttributes#RESPAWN_ANCHOR_WORKS` + - `cloudHeight` -> `EnvironmentAttributes#CLOUD_HEIGHT` + - `attribute` - 获取此维度的属性。 + - `piglinSafe`、`$MonsterSettings#piglinSafe` -> `EnvironmentAttributes#PIGLINS_ZOMBIFY` + - `hasRaids`、`$MonsterSettings#hasRaids` -> `EnvironmentAttributes#CAN_START_RAID` + - `timeOfDay` 已移除 + - `moonPhase` 被 `EnvironmentAttributes#MOON_PHASE` 取代 + - `hasEndFlashes` - 返回天空盒是否是末地。 + - `$CardinalLightType` - 贯穿维度的光线。 + - `$Skybox` - 维度的天空盒。 +- `net.minecraft.world.level.material.FogType#DIMENSION_OR_BOSS` 已移除 +- `net.minecraft.world.timeline` + - `AttributeTrack` - 一个轨道,应用属性修改器,其参数从给定的关键帧轨道采样。 + - `AttributeTrackSampler` - 一个烘焙的属性轨道。 + - `Timeline` - 一个属性到轨道的映射,基于时间(以刻为单位)模周期应用。 + - `Timelines` - 所有原版时间线。 + +## 游戏规则洗牌 + +游戏规则系统已在一定程度上被改造,允许将其键存储为适当的注册表对象,同时仍将其值限制为整数或布尔值。大多数类基本上只是其他类的组合。 + +### 现有游戏规则 + +现有的游戏规则仍然在 `GameRules` 类中,只是移到了不同的位置。它们的字段已被重命名,并似乎遵循一些基本规则: + +1. 规则不再有 `RULE_` 前缀 +1. 规则现在使用下划线分隔单词 +1. `DO` 前缀已从规则名称中移除(例如,`RULE_DOENTITYDROPS` -> `ENTITY_DROPS`) +1. `SPAWNING` 后缀已替换为 `SPAWN_` 前缀(例如,`RULE_DOMOBSPAWNING` -> `SPAWN_MOBS`) +1. `DISABLE` 前缀已移除,意味着它们的值被反转(例如,`RULE_DISABLERAIDS` -> `RAIDS`) + +虽然有一些边缘情况,但在之前的游戏规则名称中搜索特定单词很可能会引导您找到新名称(例如,在 `RULE_ANNOUNCEADVANCEMENTS` 中搜索 `ADVANCEMENT` 会找到 `SHOW_ADVANCEMENT_MESSAGES`)。 + +要实际从游戏规则中获取值,您将使用 `GameRules#get` 而不是之前的 `getBoolean` 和 `getInteger`。类型从注册的 `GameRule` 上的泛型获得。 + +```java +// 使用 ServerLevel level +boolean fallDamage = level.getGameRules().get(GameRules.FALL_DAMAGE); +``` + +此外,设置游戏规则现在简化为调用 `GameRules#set`——接受 `GameRule`、值和当前服务器,如果更改通过 `MinecraftServer#onGameRuleChanged` 传播,通常应该这样做。 + +```java +// 使用 ServerLevel level +level.getGameRules().set(GameRules.FALL_DAMAGE, false, level.getServer()); +``` + +### 创建游戏规则 + +游戏规则通过 `GameRule` 类创建,它基本上是一个类型定义,说明游戏规则如何根据其调用者运行。其泛型表示所持有的值的类型。唯一将此与通用类型区分开的硬编码概念是,实际参数可以被限制在特定范围内,并且它们存储默认值。否则,这些字段与其在 `GameRules$Type` 和 `GameRules$Key` 中的前身基本相同。 + +然后,一旦创建,`GameRule` 必须静态注册到 `BuiltInRegistries#GAME_RULE` + +```java +public static final GameRule EXAMPLE_RULE = Registry.register( + BuiltInRegistries.GAME_RULE + Identifier.withNamespaceAndPath("examplemod", "example_rule"), + new GameRule( + // 最能代表游戏规则的类别。 + // 这仅在首次构造世界时 + // 由编辑游戏规则屏幕使用。 + // 可以通过调用 `GameRuleCategory#register` 或仅使用其构造函数 + // 创建自定义类别,因为排序顺序未使用 + GameRuleCategory.register( + Identifier.withNamespaceAndPath("examplemod", "example_category") + ), + // 游戏规则的类型,表示泛型的 + // JSON 模式版本。 + // 这仅由管理系统用于 + // 检查非类型化规则。 + GameRuleType.INT, + // 用于在命令中序列化值的参数类型。 + // 这可以根据构造函数进行范围限制。 + IntegerArgumentType.integer(0, 5), + // 一个调用者,通常在访问过程中运行 + // 对于每个游戏规则。 + // 此调用者仅由编辑游戏规则屏幕使用 + // 用于添加修改值的正确组件。 + // 不应在此处使用 `GameRuleTypeVisitor#visit` + // 因为访问者已经调用了该函数。 + GameRuleTypeVisitor::visitInteger, + // 用于将游戏规则序列化到磁盘 + // 或用于管理服务的编解码器。 + // 这可以根据构造函数进行范围限制。 + Codec.intRange(0, 5), + // 一个将设置值映射到整数结果的函数 + // 在通过命令设置或查询游戏规则时使用。 + // 这是唯一 `0` 的结果不意味着命令失败的情况。 + gameRuleValue -> gameRuleValue, + // 此规则的默认值。 + 3, + // 此规则在游戏中启用所需的功能标志集。 + // 空的标志集意味着它应始终启用。 + FeatureFlagSet.of() + ) +); +``` + +- `net.minecraft.client.gui.screens.worldselection` + - `EditGameRulesScreen` + - `$BooleanRuleEntry` 现在接受 `GameRule` 而不是 `GameRules$BooleanValue` + - `$EntryFactory` 不再限制其泛型 + - `$IntegerRuleEntry` 现在接受 `GameRule` 而不是 `GameRules$IntegerValue` + - `InitialWorldCreationOptions#disabledGameRules` 现在是一个 `GameRuleMap` +- `net.minecraft.core.registries.BuiltInRegistries#GAME_RULE`、`Registries#GAME_RULE` - 游戏规则注册表。 +- `net.minecraft.gametest.framework.TestEnvironmentDefinition` + - `$SetGameRules` 现在接受 `GameRulesMap` 而不是 `$Entry` + - `entry`、`$Entry` 已移除 +- `net.minecraft.server.MinecraftServer#onGameRuleChanged` 现在接受 `GameRule` 和值,而不是字符串键和 `$Value` 包装器 +- `net.minecraft.server.jsonrpc.api.Schema` + - `RULE_TYPE_SCHEMA` 现在是 `GameRuleType` 而不是 `GameRulesService$RuleType` + - `TYPED_GAME_RULE_SCHEMA` 现在是 `GameRulesService$GameRuleUpdate` 而不是 `GameRulesService$TypedRule` + - `UNTYPED_GAME_RULE_SCHEMA` 现在是 `GameRulesService$GameRuleUpdate` 而不是 `GameRulesService$UntypedRule` +- `net.minecraft.server.jsonrpc.internalapi` + - `GameRules` 接口已移除 + - `MinecraftGameRuleService#getRule` -> `getRuleValue` +- `net.minecraft.server.jsonrpc.methods.GameRulesService` + - `$RuleType` 已移除 + - `$TypedRule`、`$UntypedRule` -> `$GameRuleUpdate`,不是一对一 +- `net.minecraft.server.notifications.NotificationService#onGameRuleChanged` 现在接受 `GameRule` 和值,而不是字符串键和 `$Value` 包装器 +- `net.minecraft.world.level.GameRules` + - 静态规则键现在位于 `.gamerules.GameRules` 中,没有 `RULE_` 前缀,单词之间使用下划线 + - `DO` 已从名称中移除(例如,`RULE_DOENTITYDROPS` -> `ENTITY_DROPS`) + - `SPAWNING` 名称现在以 `SPAWN_` 开头(例如,`RULE_DOMOBSPAWNING` -> `SPAWN_MOBS`) + - 将键映射到其关联值的映射行为现在由 `GameRuleMap` 处理 + - `getBoolean`、`getInteger` -> `get` + - `$Key`、`$Type` -> `GameRule`,不是一对一 + - `GameRule` 实现 `FeatureElement` + - `$Category` -> `GameRuleCategory`,不是一对一 + - `$Value`、`$BooleanValue`、`$IntegerValue` 已移除,被直接包装的对象取代 + - `$GameRuleTypeVisitor` -> `GameRuleTypeVisitor` + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 使用注解 + +Mojang 最近为一些整数值和标志添加了注解,标记其预期用途。这不会以任何方式影响模组制作者,因为它似乎是一种对传递的值执行静态分析的方法,可能用于某种验证。 + +- `com.mojang.blaze3d.buffers.GpuBuffer$Usage` - 一个注解,标记给定的整数是否定义了特定缓冲区的用法标志。 +- `com.mojang.blaze3d.platform.InputConstants$Value` - 一个注解,标记给定的整数是否定义了设备的输入。 +- `com.mojang.blaze3d.buffers.GpuTexture$Usage` - 一个注解,标记给定的整数是否定义了特定纹理的用法标志。 +- `net.minecraft.client.input` + - `InputWithModifiers$Modifiers` - 一个注解,标记给定的整数是否定义了输入的修饰符。 + - `KeyEvent$Action` - 一个注解,标记给定的整数是否定义了输入正在执行的操作(即,按下、释放、重复)。 + - `MouseButtonInfo` + - `$Action` - 一个注解,标记给定的整数是否定义了鼠标正在执行的操作(即,按下、释放、重复)。 + - `$MouseButton` - 一个注解,标记给定的整数是否定义了鼠标的输入。 +- `net.minecraft.server.level.TicketType$Flags` - 一个注解,标记给定的整数是否定义了加载票类型的标志。 +- `net.minecraft.world.level.block.Block$UpdateFlags` - 一个注解,标记给定的整数是否定义了方块更新的标志。 + +### 文本收集器 + +`ActiveTextCollector` 是一种提交字符串和组件进行渲染的方法,旨在提供对齐的通用实用程序,特别是对于超出屏幕的文本。虽然这不一定取代 `GuiGraphics#drawString`,但一些小部件需要使用 `ActiveTextCollector`,例如 `AbstractStringWidget#renderLines`。 + +可以通过调用 `GuiRenderer#textRenderer*` 方法之一来创建 `ActiveTextCollector`。它们接受一个 `$HoveredTextEffects`,它处理如何渲染组件的悬停和点击事件,以及一个用于任何额外处理的 `Style` 消费者回调。它还存储一组默认参数,这些参数基本上代表当前的姿势不透明度和屏幕矩形。 + +有两种方法可以提交一段文本进行渲染:`accept` 用于标准字符串,以及 `acceptScrolling*` 用于超出矩形的屏幕,以大约每秒一个单位的速度在屏幕上来回滚动(请参阅辅助功能设置中的示例)。`accept` 最多接受五个参数:X 位置的对齐方式(`TextAlignment#LEFT` 像正常一样,`CENTER` 文本的中心,`RIGHT` 文本的末尾)、对齐的 X 位置、Y 位置、要覆盖的参数以及文本。`acceptScrolling` 最多接受七个参数:文本、居中对齐的起始 X 位置、最左边的 X 位置、最右边的 X 位置、最顶部的 Y 位置、最底部的 Y 位置以及要覆盖的参数。 + +```java +// 在某个带有 GuiGraphics graphics 的方法中 +ActiveTextCollector collector = graphics.textRenderer( + // 渲染悬停和点击事件 + HoveredTextEffects.TOOLTIP_AND_CURSOR; +); + +collector.accept( + // 将文本对齐到中心 + TextAlignment.CENTER, + // 起始 X(在这种情况下是中心位置) + 20, + // 起始 Y + 0, + // 要使用的参数 + collector.defaultParameters(), + // 要显示的文本 + Component.literal("世界你好!") +); +``` + +- `net.minecraft.client.gui` + - `ActiveTextCollector` - 一个用于渲染具有某些参数和对齐方式的文本的辅助工具。 + - `GuiGraphics` 现在接受鼠标 XY + - `textRenderer*` - 用于构造在适当位置提交文本的辅助工具的方法。 + - `$HoveredTextEffects` - 一个枚举,定义在使用文本收集器时应用的文本效果。 +- `net.minecraft.client.gui.components` + - `AbstractButton` 现在扩展 `AbstractWidget$WithInactiveMessage` 而不是 `AbstractWidget` + - `renderWidget` 现在是 final + - 请改用 `renderContents` 来提交元素 + - 应在 `renderContents` 中调用 `renderDefaultSprite` 以 blit 默认精灵 + - `renderString` -> `renderDefaultLabel`,不是一对一 + - `AbstractSliderButton` 现在扩展 `AbstractWidget$WithInactiveMessage` 而不是 `AbstractWidget` + - `AbstractStringWidget` + - `visitLines` - 处理将文本元素提交到屏幕。 + - `setColor`、`getColor` 已移除 + - 请改用 `visitLines` 中的 `ActiveTextCollector` + - `setComponentClickHandler` - 设置当点击具有提供样式的组件时的处理程序。 + - `AbstractWidget` + - `renderScrollingString` -> `renderScrollingStringOverContents`,不是一对一 + - `getAlpha` - 获取小部件的 alpha 值。 + - `$WithInactiveMessage` - 一个小部件,可以在不活动时更改显示的消息。 + - `Button` 现在是抽象的 + - `$Plain` 复制了以前的行为 + - `ChatComponent` + - `MESSAGE_BOTTOM_TO_MESSAGE_TOP` - 聊天组件的高度。 + - `render` 现在接受 `Font` 和一个 `boolean`,表示是否在插入时更改光标 + - `captureClickableText` - 捕获要提交的可点击文本。 + - `handleChatQueueClicked` 被 `QUEUE_EXPAND_ID` 取代,不是一对一 + - `getClickedComponentStyleAt` -> `$ChatGraphicsAccess#handleMessage`,不是一对一 + - `getMessageTagAt` -> `$ChatGraphicsAccess#handleTag`、`handleTagIcon`;不是一对一 + - `getWidth`、`getHeight`、`getScale` 现在是私有的 + - `$AlphaCalculator` - 计算给定聊天行的 alpha 值。 + - `$ChatGraphicsAccess` - 一个用于处理聊天输入提交的接口。 + - `$LineConsumer` 不再接受前三个 `int` + - `FittingMultilineTextWidget#setColor` 已移除 + - 请改用 `visitLines` 中的 `ActiveTextCollector` + - `MultiLineLabel` + - `render`、`getStyle` -> `visitLines`,不是一对一 + - `$Align` -> `TextAlignment` + - `MultiLineTextWidget#setColor`、`configureStyleHandling` 已移除 + - 请改用 `visitLines` 中的 `ActiveTextCollector` + - `SplashRenderer` 现在接受 `Component` 而不是 `String` + - `SpriteIconButton#renderSprite` - 提交精灵图标。 + - `StringWidget#setColor` 已移除 + - 请改用 `visitLines` 中的 `ActiveTextCollector` + - `TabButton` 现在扩展 `AbstractWidget$WithInactiveMessage` 而不是 `AbstractWidget` + - `renderString` -> `renderLabel`,现在是私有的,不是一对一 +- `net.minecraft.client.gui.screens.inventory.BookViewScreen#getClickedComponentStyleAt` -> `visitText`,现在是私有的,不是一对一 + +### 共享文本区域调试器 + +添加了一个新的调试器,用于绘制每个字形的边界框,包括空字形。颜色在每个字形之间略有偏移以便于区分,并且根据是否存在点击或悬停事件的某种组合而完全改变。 + +- `net.minecraft.SharedConstants#DEBUG_ACTIVE_TEXT_AREAS` - 用于调试器绘制每个字形的边界和效果的标志。 +- `net.minecraft.client.gui.Font` + - `prepareText` 现在有一个重载,表示是否在空区域渲染某些内容 + - `$GlyphVisitor` + - `acceptGlyph` 现在接受 `TextRenderable$Styled` 而不是 `TextRenderable` + - `acceptEmptyArea` - 接受一个空区域以绘制到屏幕。 + - `$PreparedTextBuilder` 现在接受是否包括空区域以进行渲染 +- `net.minecraft.client.gui.font` + - `ActiveArea` - 定义要绘制区域的边界和样式。 + - `EmptyArea` - 一个内部没有任何内容的区域。 + - `PlainTextRenderable` 现在实现 `TextRenderable$Styled` 而不是 `TextRenderable` + - `width`、`height`、`ascent` - 对象的边界。 + - `TextRenderable$Styled` - 一个为其边界定义某个活动区域的文本可渲染对象。 +- `net.minecraft.client.gui.font.glyphs.BakedGlyph#createGlyph` 现在返回 `TextRenderable$Styled` + +### JSpecify 注解 + +Mojang 已从使用他们自己的注解混合,转向在需要时使用 JSpecify 提供的注解。因此,不再默认所有字段、方法和参数都被标记为非空,而是由 `NullMarked` 取代,它认为类型用法是非空的,除非显式注解为 `Nullable`,除了一些特殊情况。 + +- `com.mojang.blaze3d.FieldsAreNonnullByDefault`、`MethodsReturnNonnullByDefault` 已移除 +- `com.mojang.math.FieldsAreNonnullByDefault`、`MethodsReturnNonnullByDefault` 已移除 +- `net.minecraft.FieldsAreNonnullByDefault`、`MethodsReturnNonnullByDefault` 已移除 + +### 槽位来源 + +槽位来源是对之前在潜影盒中的内容掉落系统的扩展,允许任何战利品表从某些容器槽位中提取其条目。这可以在任何启用 `LootContext` 的位置使用,尽管目前它仅作为战利品池条目实现。 + +在原版中,槽位来源的工作方式是让某个 `LootContextArg`(指向某个战利品上下文参数值)返回一个实现 `SlotProvider` 的对象。目前,这指的是任何 `Container` 或 `Entity` 实现。然后,`SlotProvider` 被 `SlotSource#provide` 用于构造一个 `SlotCollection`:一个深拷贝 `ItemStack` 的流。存储在集合中的堆栈然后被传递到池的输出。由于这一切都在某个 `SlotSource#provide` 实现中完成,它可以引用任何东西(不仅仅是 `SlotProvider`),只要它可以将该数据转换为 `SlotCollection`。 + +```java +// 一个槽位来源,其“槽位”是物品标签中的元素。 +public record TagSlotSource(TagKey tag) implements SlotSource { + + public static final MapCodec MAP_CODEC = TagKey.codec(Registries.ITEM) + .fieldOf("tag").xmap(TagSlotSource::new, TagSlotSource::tag); + + @Override + public SlotCollection provide(LootContext ctx) { + // 获取标签的持有者集 + Optional> holderSetOpt = ctx.getResolver() + .lookup(Registries.ITEM).flatMap(getter -> getter.get(this.tag)); + + // 流式传输元素并映射到 SlotCollection + return holderSetOpt.map(holderSet -> + // `Item#getDefaultInstance` 返回一个新副本,因此可以使用。 + // 如果 ItemStack 已经存在,则应对每个调用 `ItemStack#copy`。 + (SlotCollection) () -> holderSet.stream().map(holder -> holder.value().getDefaultInstance()) + ).orElse(SlotCollection.EMPTY); + } + + @Override + public MapCodec codec() { + // 用于序列化槽位来源的编解码器 + return MAP_CODEC; + } +} + +// 映射编解码器需要注册到槽位来源类型注册表 +Registry.register( + BuiltInRegistries.SLOT_SOURCE_TYPE + Identifier.withNamespaceAndPath("examplemod", "tag"), + TagSlotSource.MAP_CODEC +); +``` + +```json5 +// 一个示例战利品表 +{ + // ... + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + // 使用槽位来源战利品池 + "type": "minecraft:slots", + "slot_source": { + // 我们的槽位来源 + "type": "examplemod:tag", + "tag": "minecraft:planks" + } + } + ] + } + // ... + ] +} +``` + +- `net.minecraft.advancements.criterion.SlotsPredicate#matches` 现在接受 `SlotProvider` 而不是 `Entity` +- `net.minecraft.core.registries.BuiltInRegistries#SLOT_SOURCE_TYPE`、`Registries#SLOT_SOURCE_TYPE` - 槽位来源类型注册表。 +- `net.minecraft.world.Container` 现在扩展 `SlotProvider` + - `getSlot` - 获取单个物品的访问。 +- `net.minecraft.world.entity` + - `Entity` 现在实现 `SlotProvider` + - `SlotAccess` + - `NULL` 已移除 + - `forContainer` -> `forListElement`,不是一对一 + - `SlotProvider` - 一个通过槽位提供对其内部存储的某些访问的对象。 +- `net.minecraft.world.item.slot` + - `CompositeSlotSource` - 多个槽位来源的组合。 + - `ContentsSlotSource` - 获取槽位内容。 + - `EmptySlotSource` - 一个空的槽位来源。 + - `FilteredSlotSource` - 根据物品谓词过滤提供的槽位来源。 + - `GroupSlotSource` - 将多个槽位来源组合成一个连接的集合。 + - `LimitSlotSource` - 将提供的槽位来源限制为最大大小。 + - `RangeSlotSource` - 获取所需的槽位范围。 + - `SlotCollection` - 一个槽位的集合,用于获取物品副本。 + - `SlotSource` - 给定战利品上下文,返回一个要提供的槽位集合。 + - `SlotSources` - 原版提供的槽位来源。 + - `TransformedSlotSource` - 转换提供的槽位来源。 +- `net.minecraft.world.level.storage.loot.ContainerComponentManipulator#getSlots` - 获取堆栈上数据组件的槽位。 +- `net.minecraft.world.level.storage.loot.entries` + - `LootPoolEntries#SLOTS` - 一个使用来自源的槽位的池。 + - `SlotLoot` - 一个从某个槽位来源获取其物品的池。 + +### 僵尸鹦鹉螺变体 + +僵尸鹦鹉螺是变体数据包注册表对象的最新添加,接受熟悉的模型和纹理覆盖以及生成条件: + +```json5 +// 文件位于: +// - `data/examplemod/zombie_nautilus_variant/example_zombie_nautilus.json` +{ + // 指向 `assets/examplemod/textures/entity/nautilus/example_zombie_nautilus.png` 的纹理 + "asset_id": "examplemod:entity/nautilus/example_zombie_nautilus", + // 定义用于选择渲染僵尸鹦鹉螺变体的实体模型的 `ZombieNautilusVariant$ModelType` + "model": "warm", + "spawn_conditions": [ + // 此变体生成的条件 + { + "priority": 0 + } + ] +} +``` + +- `net.minecraft.core.component.DataComponents#ZOMBIE_NAUTILUS_VARIANT` - 僵尸鹦鹉螺的变体。 +- `net.minecraft.core.registries.Registries#ZOMBIE_NAUTILUS_VARIANT` - 僵尸鹦鹉螺变体的注册表键。 +- `net.minecraft.network.syncher.EntityDataSerializers#ZOMBIE_NAUTILUS_VARIANT` - 僵尸鹦鹉螺的变体。 +- `net.minecraft.world.entity.animal.nautilus` + - `ZombieNautilusVariant` - 僵尸鹦鹉螺的变体。 + - `ZombieNautilusVariants` - 所有原版僵尸鹦鹉螺变体。 + +### `OptionEnum` 移除 + +`OptionEnum` 已被移除,转而使用带有期望值和编解码器的 `OptionInstance$Enum` 构造函数。因此,大多数 `byId` 方法已被一些编解码器取代,并且可翻译条目现在存储为 `Component` 而不是翻译键字符串。 + +- `net.minecraft.client` + - `AttackIndicatorStatus` 不再实现 `OptionEnum` + - `byId` -> `LEGACY_CODEC`,不是一对一 + - `getKey` -> `caption`,不是一对一 + - `CloudStatus` 不再实现 `OptionEnum` + - `getKey` -> `caption`,不是一对一 + - `InactivityFpsLimit` 不再实现 `OptionEnum` + - `getKey` -> `caption`,不是一对一 + - `OptionInstance#forOptionEnum` 已移除 + - `PrioritizeChunkUpdate` 不再实现 `OptionEnum` + - `getKey` -> `caption`,不是一对一 + - `byId` -> `LEGACY_CODEC`,不是一对一 +- `net.minecraft.client.sounds.MusicManager$MusicFrequency` 不再实现 `OptionEnum` + - `getKey` -> `caption`,不是一对一 +- `net.minecraft.server.level.ParticleStatus` 不再实现 `OptionEnum` + - `getKey` -> `caption`,不是一对一 + - `byId` -> `LEGACY_CODEC`,不是一对一 +- `net.minecraft.util.OptionEnum` 已移除 +- `net.minecraft.world.entity.HumanoidArm` 不再实现 `OptionEnum` + - `BY_ID` 现在是私有的 + - `getKey` -> `caption`,不是一对一 +- `net.minecraft.world.entity.player.ChatVisbility` 不再实现 `OptionEnum` + - `byId` -> `LEGACY_CODEC`,不是一对一 + - `getKey` -> `caption`,不是一对一 + +### 特定逻辑变更 + +- `net.minecraft.client.renderer.entity.EntityRenderState#lightCoords` 现在默认为 0xF000F0。 +- `net.minecraft.client.gui.screens.inventory.AbstractContainerScreen#keyPressed` 如果键未被屏幕处理,不再返回 `true`,而是返回 `false`。 +- `net.minecraft.util.Mth#clampedLerp` 两个重载的参数顺序已重新排列。这些方法现在接受步长、原始值和下一个值;而不是原始值、下一个值和步长值。 + +### 标签变更 + +- `minecraft:biome` + - `plays_underwater_music` 已移除 + - 被 `BackgroundMusic#underwaterMusic` 环境属性取代 + - `has_closer_water_fog` 已移除 + - 被 `EnvironmentAttributes#WATER_FOG_END_DISTANCE` 取代 + - `increased_fire_burnout` 已移除 + - 被 `EnvironmentAttributes#INCREASED_FIRE_BURNOUT` 取代 + - `snow_golem_melts` 已移除 + - 被 `EnvironmentAttributes#SNOW_GOLEM_MELTS` 取代 + - `without_patrol_spawns` 已移除 + - 被 `EnvironmentAttributes#CAN_PILLAGER_PATROL_SPAWN` 取代 + - `spawns_coral_variant_zombie_nautilus` +- `minecraft:block` + - `can_glide_through` +- `minecraft:entity_type` + - `burn_in_daylight` + - `can_float_while_ridden` + - `can_wear_nautilus_armor` + - `nautilus_hostiles` +- `minecraft:item` + - `camel_husk_food` + - `zombie_horse_food` + - `nautilus_bucket_food` + - `nautilus_food` + - `nautilus_taming_items` + - `spears` + - `enchantable/lunge` + - `enchantable/sword` -> `enchantable/melee_weapon`、`enchantable/sweeping` +- `minecraft:timeline` + - `universal` + - `in_overworld` + - `in_nether` + - `in_end` + +### 新增列表 + +- `com.mojang.blaze3d.GraphicsWorkarounds#isAmd` - GPU 供应商是否为 AMD。 +- `com.mojang.blaze3d.opengl` + - `GlConst#GL_POINTS` - 定义点图元作为要渲染的类型。 + - `GlTimerQuery` - 查询对象(通常是经过的时间)的 OpenGL 实现。 +- `com.mojang.blaze3d.platform.InputConstants#MOUSE_BUTTON_*` - 鼠标点击的输入,用数字表示,因为它们可能有不同的预期用途。 +- `com.mojang.blaze3d.systems` + - `CommandEncoder#timerQueryBegin`、`timerQueryEnd` - 用于跟踪经过时间的处理程序。 + - `GpuQuery` - 对任意对象的查询,例如经过的时间。 +- `com.mojang.blaze3d.vertex` + - `DefaultVertexFormat` + - `POSITION_COLOR_LINE_WIDTH` - 一个指定位置、颜色和线宽的顶点格式。 + - `POSITION_COLOR_NORMAL_LINE_WIDTH` - 一个指定位置、颜色、法线和线宽的顶点格式。 + - `VertexFormat$Mode#POINTS` - 一个绘制点的顶点模式。 + - `VertexFormatElement#LINE_WIDTH` - 一个接受一个表示宽度的浮点数的顶点元素。 +- `com.mojang.math` + - `OctahedralGroup` + - `BLOCK_ROT_*` - 表示方块旋转的常量。 + - `permutation` - 返回对称群。 + - `Quadrant#fromXYZAngles` - 获取表示三个象限旋转的八面体群。 + - `SymmetricGroup3#inverse` - 返回逆群。 +- `net.minecraft` + - `SharedConstants` + - `MAX_CLOUD_DISTANCE` - 玩家可以渲染的最大云范围。 + - `DEFAULT_RANDOM_TICK_SPEED` - 默认随机刻速度。 + - `Util#localizedDateFormatter` - 返回给定样式的本地化 `DateTimeFormatter`。 +- `net.minecraft.advancements.criterion` + - `DataComponentMatchers$Builder#any` - 匹配组件的某些数据是否存在。 + - `SpearMobsTrigger` - 一个触发器,检查玩家使用动能武器刺穿的实体数量。 +- `net.minecraft.client` + - `GuiMessage` + - `splitLines` - 将组件以所需宽度拆分为行。 + - `getTagIconLeft` - 获取内容的宽度,外加四个像素的填充。 + - `KeyMapping$Category#DEBUG` - 调试键盘类别。 + - `MusicToastDisplayState` - 一个枚举,表示音乐吐司应如何显示。 + - `NarratorStatus#LEGACY_CODEC` - 用于反序列化枚举旁白状态的编解码器。 + - `OptionInstance` + - `$IntRangeBase` + - `next` - 获取下一个值。 + - `previous` - 获取上一个值。 + - `$SliderableEnum` - 一个在枚举选项之间选择的滑块。 + - `$SliderableValueSet` + - `next` - 获取下一个值。 + - `previous` - 获取上一个值。 + - `Options` + - `keyToggleGui` - 一个切换游戏内 GUI 的按键映射。 + - `keyToggleSpectatorShaderEffects` - 一个切换与相机实体绑定的着色器效果的按键映射。 + - `keyDebug*`、`debugKeys` - 调试渲染器的按键映射。 + - `weatherRadius` - 区域中天气粒子渲染的半径。 + - `cutoutLeaves` - 树叶是否应以切割或实体模式渲染。 + - `vignette` - 是否应在屏幕上应用晕影。 + - `improvedTransparency` - 是否使用透明度后处理器。 + - `chunkSectionFadeInTime` - 区块首次渲染时淡入应花费的秒数。 + - `maxAnisotropyBit` - 各向异性过滤级别的位值。 + - `maxAnisotropyValue` - 各向异性过滤级别。 +- `net.minecraft.client.animation.definitions.NautilusAnimation` - 鹦鹉螺的动画定义。 +- `net.minecraft.client.data.models.ItemModelGenerators` + - `generateSpear` - 生成矛物品模型。 + - `generateItemWithTintedBaseLayer` - 生成一个基础层被染色的双层物品模型。 +- `net.minecraft.client.data.models.model.ModelTemplates#SPEAR_IN_HAND` - 手持矛模型的模板。 +- `net.minecraft.client.gui.components` + - `AbstractButton#setOverrideRenderHighlightedSprite` - 覆盖是否使用聚焦启用/禁用精灵。 + - `Checkbox#adjustWidth` - 使用消息、字体及其初始 X 位置设置小部件的宽度。 + - `CycleButton` + - `$Builder#withSprite` - 设置根据当前按钮状态获取精灵的供应商。 + - `$DisplayState` - 按钮应如何显示。 + - `$SpriteSupplier` - 根据当前按钮状态获取精灵位置。 + - `EditBox#setInvertHighlightedTextColor` - 设置是否反转高亮文本颜色。 + - `FocusableTextWidget` + - `getPadding` - 返回文本填充。 + - `updateWidth` - 更新文本可以占用的宽度。 + - `updateHeight` - 更新文本可以占用的高度。 + - `$Builder` - 构建组件。 + - `MultiLineTextWidget#getTextX`、`getTextY` - 获取文本位置。 + - `OptionsList` + - `addHeader` - 添加一个标题条目。 + - `resetOption` - 重置选项值。 + - `$AbstractEntry` - 定义选择列表中的元素。 + - `$HeaderEntry` - 一个表示部分标题的条目。 + - `$OptionInstanceWidget` - 一个包含小部件和可选的选项实例的记录。 + - `ResettableOptionWidget` - 一个可以将其值重置为默认值的小部件。 + - `SelectableEntry` - 一个用于检查鼠标是否在特定区域的实用程序。 +- `net.minecraft.client.gui.layouts.HeaderAndFooterLayout#MAGIC_PADDING` - 元素之间的常见填充。 +- `net.minecraft.client.gui.screens.advancements` + - `AdvancementTab#canScrollHorizontally`、`canScrollVertically` - 检查标签页数据是否可以在给定方向上滚动。 + - `AdvancementTabType#getWidth`、`getHeight` - 获取标签页的宽度/高度。 +- `net.minecraft.client.gui.screens.debug.DebugOptionsScreen#getOptionList` - 返回调试屏幕的选项列表。 +- `net.minecraft.client.gui.screens.inventory` + - `AbstractMountInventoryScreen` - 一个表示坐骑库存的屏幕。 + - `EffectsInInventory` + - `SPACING` - 效果之间的间距。 + - `SPRITE_SQUARE_SIZE` - 效果图标的大小。 + - `NautilusInventoryScreen` - 鹦鹉螺库存的屏幕。 +- `net.minecraft.client.gui.screens.options` + - `OptionsSubScreen#resetOption` - 将选项值重置为默认值。 + - `VideoSettingsScreen#updateTransparencyButton` - 将透明度按钮设置为当前选项值。 +- `net.minecraft.client.gui.screens.packs.TransferableSelectionList$PackEntry#ICON_SIZE` - 资源包图标的大小。 +- `net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton#select`、`unselect` - 处理标签页显示选择。 +- `net.minecraft.client.input.InputQuirks#EDIT_SHORTCUT_KEY_LEFT`、`EDIT_SHORTCUT_KEY_RIGHT` -> `InputWithModifiers#hasControlDownWithQuirk`,不是一对一 +- `net.minecraft.client.model.HumanoidModel$ArmPose` + - `SPEAR` - 矛的第三人称手臂姿势。 + - `animateUseItem` - 给定实体状态、使用时间、手臂和堆栈,修改 `PoseStack`。 + - `affectsOffhandPose` - 手臂动画是否会影响副手姿势。 +- `net.minecraft.client.model.animal.nautilus` + - `NautilusArmorModel` - 鹦鹉螺的盔甲模型。 + - `NautilusModel` - 鹦鹉螺的模型。 + - `NautilusSaddleModel` - 鹦鹉螺的马鞍模型。 +- `net.minecraft.client.model.effects.SpearAnimations` - 使用矛时执行的动画。 +- `net.minecraft.client.model.geom` + - `ModelLayers` + - `*NAUTILUS*` - 鹦鹉螺的模型层。 + - `UNDEAD_HORSE*_ARMOR` - 亡灵马的盔甲模型层。 + - `PartName` + - `INNER_MOUTH`、`LOWER_MOUTH` - 嘴的部件名称。 + - `SHELL` - 壳的部件名称。 + - `*_CORAL*` - 僵尸鹦鹉螺上珊瑚的部件名称。 +- `net.minecraft.client.model.geom.builders.UVPair#pack`、`unpack*` - 处理将 UV 打包/解包为 `long`。 +- `net.minecraft.client.model.monster.nautilus.ZombieNautilusCoralModel` - 僵尸鹦鹉螺温暖变体的模型。 +- `net.minecraft.client.model.monster.skeleton.SkeletonModel#createSingleModelDualBodyLayer` - 创建一个焦尸层定义。 +- `net.minecraft.client.multiplayer` + - `ClientPacketListener#hasClientLoaded` - 客户端是否已加载。 + - `MultiPlayerGameMode#piercingAttack` - 发起冲刺攻击。 +- `net.minecraft.client.player.LocalPlayer#raycastHitResult` - 获取给定部分刻下相机实体的命中结果。 +- `net.minecraft.client.renderer` + - `DynamicUniforms` + - `CHUNK_SECTION_UBO_SIZE` - 区块部分的统一缓冲区对象大小。 + - `writeChunkSections` - 将可变参数的区块部分写入统一存储。 + - `$ChunkSectionInfo` - 区块部分的动态统一变量。 + - `GameRenderer` + - `updateCamera` - 调用相机的设置函数。 + - `getPanoramicScreenshotParameters` - 获取全景模式的截图参数。 + - `PanoramicScreenshotParameters` - 全景模式的截图参数。 + - `Sheets#CELESTIAL_SHEET` - 天体纹理的图集。 +- `net.minecraft.client.renderer.blockentity.BlockEntityWithBoundingBoxRenderer#STRUCTURE_VOIDS_COLOR` - 结构的虚空颜色。 +- `net.minecraft.client.renderer.chunk.SectionRenderDispatcher$RenderSection` + - `getVisibility` - 返回区块当前的 alpha 值。 + - `setFadeDuration` - 设置区块淡入所需的时间。 + - `setWasPreviouslyEmpty`、`wasPreviouslyEmpty` - 处理该部分之前是否不存在。 +- `net.minecraft.client.renderer.entity` + - `CamelHuskRenderer` - 骆驼干尸的实体渲染器。 + - `CamelRenderer#createCamelSaddleLayer` - 创建骆驼的马鞍层。 + - `NautilusRenderer` - 鹦鹉螺的实体渲染器。 + - `ParchedRenderer` - 焦尸的实体渲染器。 + - `ZombieNautilusRenderer` - 僵尸鹦鹉螺的实体渲染器。 +- `net.minecraft.client.renderer.entity.state` + - `ArmedEntityRenderState` + - `swingAnimationType` - 挥动手臂时播放的动画。 + - `ticksUsingItem` - 物品已被使用的刻数。 + - `getUseItemStackForArm` - 根据手臂返回持有的物品堆栈。 + - `LivingEntityRenderState#ticksSinceKineticHitFeedback` - 自此实体被动能武器击中以来经过的刻数。 + - `NautilusRenderState` - 鹦鹉螺的实体渲染状态。 + - `UndeadRenderState` - 亡灵人形生物的实体渲染状态。 +- `net.minecraft.client.renderer.item.ItemModelResolver#swapAnimationScale` - 获取堆栈交换动画的缩放。 +- `net.minecraft.client.renderer.state.LevelRenderState#gameTime` - 当前游戏时间。 +- `net.minecraft.client.resources.SplashManager` 组件字段 - 特殊消息的组件。 +- `net.minecraft.client.resources.model` + - `BlockModelRotation#IDENTITY` - 恒等旋转。 + - `EquipmentClientInfo#NAUTILUS_*` - 鹦鹉螺的层。 +- `net.minecraft.core.Vec3i` + - `multiply` - 将每个分量乘以提供的标量。 + - `toMutable` - 返回一个可变的 `Vector3i`。 +- `net.minecraft.data.AtlasIds#CELESTIAL_SHEET` - 天体纹理的图集。 +- `net.minecraft.data.recipes.RecipeProvider#waxedChiseled` - 打蜡的雕纹方块的配方。 +- `net.minecraft.gametest.framework.GameTestHelper#getAbsoluteDirection` - 从测试相对方向返回绝对方向。 +- `net.minecraft.nbt.NbtAccounter` + - `defaultQuota` - 一个最大分配 2 MiB 的计数器。 + - `uncompressedQuota` - 一个最大分配 100 MiB 的计数器。 +- `net.minecraft.network.chat.MutableComponent#withoutShadow`、`Style#withoutShadow` - 从文本中移除阴影。 +- `net.minecraft.network.protocol.game.ServerboundPlayerActionPacket$Action#STAB` - 玩家执行了刺击动作。 +- `net.minecraft.network.syncher.EntityDataSerializers#HUMANOID_ARM` - 人形生物的主手。 +- `net.minecraft.resources.Identifier#toShortString` - 返回位置的字符串。如果命名空间是 `minecraft`,则省略。 +- `net.minecraft.server` + - `MinecraftServer` + - `getServerActivityMonitor` - 返回发送服务器活动通知的监视器。 + - `getStopwatches` - 返回 id 到计时器的映射。 + - `ServerScoreboard#storeToSaveDataIfDirty` - 如果脏则写入数据。 +- `net.minecraft.server.commands.StopwatchCommand` - 一个启动或停止秒表的命令。 +- `net.minecraft.server.dedicated.DedicatedServerProperties#managementServerAllowedOrigins` - 管理服务器的请求可以来自的来源。 +- `net.minecraft.server.jsonrpc.OutgoingRpcMethods#SERVER_ACTIVITY_OCCURRED` - Minecraft 服务器发出的关于服务器活动发生的请求。 +- `net.minecraft.server.jsonrpc.api.Schema` + - `BOOL_OR_INT_SCHEMA` - 一个可以是布尔值或整数的字段的模式。 + - `typedCodec` - 返回模式的编解码器。 + - `info` - 返回模式的副本。 +- `net.minecraft.server.level` + - `ChunkMap#getChunkDataFixContextTag` - 返回区块数据的数据修复标签。 + - `ServerLevel` + - `getDayCount` - 获取经过的天数。 + - `canSpreadFireAround` - 火是否可以在给定的方块位置周围蔓延。 +- `net.minecraft.server.network` + - `EventLoopGroupHolder` - 一个用于管理与某个端点(无论是本地还是基于套接字)通信的事件循环和通道的持有者。 + - `ServerGamePacketListenerImpl#resetFlyingTicks` - 重置玩家已飞行的刻数。 +- `net.minecraft.server.notifications` + - `NotificationService#serverActivityOccurred` - 通知管理服务器活动已发生。 + - `ServerActivityMonitor` - 发送服务器活动通知的监视器。 +- `net.minecraft.util` + - `ARGB` + - `srgbToLinearChannel` - 将 sRGB 值转换为线性颜色空间。 + - `linearToSrgbChannel` - 将线性值转换为 sRGB 颜色空间。 + - `meanLinear` - 使用线性颜色空间计算四个值的平均值,然后将其转换回 sRGB。 + - `addRgb` - 添加 RGB 通道,使用第一个值的 alpha。 + - `subtractRgb` - 减去 RGB 通道,使用第一个值的 alpha。 + - `multiplyAlpha` - 将 alpha 值乘入提供的 ARGB 值。 + - `linearLerp` - 通过转换为线性颜色空间来线性插值颜色。 + - `white`、`black` - 具有提供 alpha 的颜色。 + - `alphaBlend` - 混合两种颜色及其 alpha 值。 + - `vector4fFromARGB32` - 将 ARGB 值转换为四个浮点数。 + - `Ease` - 一个充满缓动函数的实用工具。 + - `ExtraCodecs` + - `NON_NEGATIVE_LONG`、`POSITIVE_LONG` - 具有列出约束的长整数。 + - `longRange` - 一个验证其是否在提供范围内的长整数编解码器。 + - `STRING_RGB_COLOR`、`STRING_ARGB_COLOR` - 一个允许以十六进制字符串形式表示 (A)RGB 值的编解码器。 + - `MAX_PROPERTY_NAME_LENGTH`、`MAX_PROPERTY_VALUE_LENGTH`、`MAX_PROPERTY_SIGNATURE_LENGTH`、`MAX_PROPERTIES` - 与序列化属性映射相关的常量。 + - `Mth` + - `cube` - 对数字进行立方。 + - `chessboardDistance` - 计算两对坐标之间的绝对最大差值;返回较大的轴差。 + - `SpecialDates` - 一个包含 Mojang 更改某些行为或渲染的日期的实用工具。 + - `TriState` + - `CODEC` - 三态布尔值的编解码器。 + - `from` - 将布尔值转换为三态布尔值。 +- `net.minecraft.util.profiling.jfr.JvmProfiler#onClientTick` - 在客户端Tick上运行,接受当前的 FPS。 +- `net.minecraft.util.profiling.jfr.event.ClientFpsEvent` - 一个跟踪客户端 FPS 的事件。 +- `net.minecraft.util.profiling.jfr.stats.FpsStat` - 一个包含客户端 FPS 的记录。 +- `net.minecraft.world` + - `LockCode#canUnlock` - 给定的玩家是否可以解锁此代码。 + - `Stopwatch` - 一个包含创建时间和已用时间的记录。 + - `Stopwatches` - 一个用于启动、管理和停止秒表的跟踪器。 +- `net.minecraft.world.effect` + - `MobEffects#BREATH_OF_THE_NAUTILUS` - 防止用户在水下失去空气。 + - `MobEffectUtil#shouldEffectsRefillAirsupply` - 实体是否具有在液体中补充空气供应的效果。 +- `net.minecraft.world.entity` + - `Entity` + - `getHeadLookAngle` - 计算头部旋转的视图向量。 + - `updateDataBeforeSync` - 在同步到客户端之前更新实体中存储的数据。 + - `computeSpeed` - 计算实体的最后已知速度和位置。 + - `getKnownSpeed` - 获取实体的最后已知速度。 + - `hasMovedHorizontallyRecently` - 如果最后已知速度的水平距离大于 0,更具体地说是误差范围。 + - `EntityProcessor` - 加载实体时的后处理器。 + - `EntityEvent#KINETIC_HIT` - 当实体被动能武器击中时触发的事件。 + - `HumanoidArm#STREAM_CODEC` - 手臂枚举的网络编解码器。 + - `LivingEntity` + - `DEFAULT_KNOCKBACK` - 击中时应用于实体的默认击退。 + - `itemSwapTicker` - 交换物品时花费的时间。 + - `recentKineticEnemies` - 最近使用动能武器攻击的攻击者。 + - `lungeForwardMaybe` - 应用冲刺效果。 + - `causeExtraKnockback` - 对击退应用乘法力。 + - `wasRecentlyStabbed`、`rememberStabbedEntity` - 处理被动能武器刺中的敌人。 + - `stabAttack` - 处理生物被此实体刺中时的情况。 + - `onAttack` - 处理此实体攻击另一个实体时的情况。 + - `getTicksUsingItem` - 返回此物品已被使用的刻数。 + - `getTicksSinceLastKineticHitFeedback` - 自上次被动能武器击中此实体以来经过的刻数。 + - `shouldTravelInFluid` - 此实体是否应在给定的流体中移动。 + - `travelInWater` - 移动实体,就像它们在水中一样。 + - `Mob#sunProtectionSlot` - 保护实体免受阳光照射的装备槽位。 + - `NeutralMob#level` - 返回实体所在的等级。 + - `PlayerRideableJumping#getPlayerJumpPendingScale` - 返回玩家跳跃时应应用于实体的标量。 +- `net.minecraft.world.entity.ai.attributes.Attributes#DEFAULT_ATTACK_SPEED` - 默认攻击速度。 +- `net.minecraft.world.entity.ai.memory.MemoryModuleType` + - `CHARGE_COOLDOWN_TICKS` - 冲刺攻击后的冷却刻数。 + - `ATTACK_TARGET_COOLDOWN` - 攻击目标前的冷却刻数。 +- `net.minecraft.world.entity.ai.sensing.TemptingSensor#forAnimal` - 一个传感器,特别处理动物实体,检查所需物品是否是食物。 +- `net.minecraft.world.entity.animal.camel` + - `Camel` + - `getDashingSound`、`getDashReadySound` - 骆驼冲刺声音。 + - `getStandUpSound`、`getSitDownSound` - 骆驼站/坐声音。 + - `getSaddleSound` - 骆驼马鞍声音。 + - `CamelHusk` - 骆驼干尸实体。 +- `net.minecraft.world.entity.animal.equine.AbstractHorse#isMobControlled` - 生物是否可以控制这匹马。 +- `net.minecraft.world.entity.animal.nautilus` + - `AbstractNautilus` - 鹦鹉螺实体的核心。 + - `Nautilus` - 鹦鹉螺实体。 + - `NautilusAi` - 鹦鹉螺的大脑。 + - `ZombieNautilus` - 僵尸鹦鹉螺实体。 + - `ZombieNautilusAi` - 僵尸鹦鹉螺的大脑。 +- `net.minecraft.world.entity.decoration.HangingEntity#hasLevelCollision` - 此实体是否在给定边界内与方块或边界碰撞。 +- `net.minecraft.world.entity.monster.skeleton.Parched` - 焦尸实体。 +- `net.minecraft.world.entity.monster.zombie.Husk$HuskGroupData` - 尸壳的群组数据。 +- `net.minecraft.world.entity.player.Player` + - `cannotAttackWithItem` - 检查玩家是否无法使用该物品攻击。 + - `getItemSwapScale` - 返回用于物品交换动画的标量。 + - `resetOnlyAttackStrengthTicker` - 重置攻击强度Tick器。 + - `openNautilusInventory` - 打开交互的鹦鹉螺的库存。 + - `applyPostImpulseGraceTime`、`isInPostImpulseGraceTime` - 处理脉冲之间的宽限期。 +- `net.minecraft.world.food.FoodData#hasEnoughFood` - 当前食物水平是否大于 6 饥饿值(或三个完整的饥饿条)。 +- `net.minecraft.world.inventory` + - `AbstractMountInventoryMenu` - 坐骑的库存菜单。 + - `NautilusInventoryMenu` - 鹦鹉螺的库存菜单。 +- `net.minecraft.world.item` + - `HoneycombItem#WAXED_RECIPES` - 打蜡方块到其配方类别和名称的映射。 + - `Item$Properties` + - `spear` - 添加矛组件。 + - `nautilusArmor` - 添加鹦鹉螺盔甲组件。 + - `ItemStack#matchesIgnoringComponents` - 堆栈是否匹配,忽略所有匹配谓词的组件。 + - `ItemUseAnimation` + - `TRIDENT` - 三叉戟使用动画。 + - `hasCustomArmTransform` - 动画是否提供自定义的手臂变换。 +- `net.minecraft.world.item.enchantment` + - `Enchantment#doLunge` - 应用穿刺后攻击效果。 + - `EnchantmentEffectComponents#POST_PIERCING_ATTACK` - 穿刺攻击后应用的效果。 + - `EnchantmentHelper#doLungeEffects` - 在冲刺时应用效果。 + - `LevelBasedValue$Exponent` - 应用给定基数和指数的指数。 +- `net.minecraft.world.item.enchantment.effects` + - `ApplyEntityImpulse` - 一个实体效果,在视角方向添加一个冲量。 + - `ApplyExhaustion` - 一个实体效果,如果玩家正在使用附魔物品,则应用食物消耗。 + - `ScaleExponentially` - 一个值效果,将值乘以某个数的指数次幂。 +- `net.minecraft.world.level` + - `Chunk#isValid` - 区块位置是否在允许的最大世界坐标内(在 3000 万方块半径内)。 + - `CollisionGetter` + - `noEntityCollision` - 实体在给定边界内是否不与另一个实体碰撞。 + - `noBorderCollision` - 实体在给定边界内是否不与世界边界碰撞。 + - `Level#isInValidBounds` - 方块位置是否不在允许的最大世界坐标外(Y 轴为建筑高度,XZ 轴为 3000 万方块半径)。 + - `MoonPhase` - 一个表示月相的枚举。 +- `net.minecraft.world.level.border.WorldBorder$MovingBorderExtent#getPreviousSize` - 获取边界的先前大小。 +- `net.minecraft.world.level.chunk.storage` + - `IOWorker#STORE_EMPTY` - 一个提供的 `null` 标签。 + - `LegacyTagFixer` - 一个处理如何升级标签的接口,例如对于区块。 + - `SimpleRegionStorage` + - `isOldChunkAround` - 来自先前版本的区块是否仍然存在于此版本中。 + - `injectDatafixingContext` - 当上下文不为 `null` 时,将其添加到给定的标签。 + - `markChunkDone` - 将区块标记为已完成升级到当前版本。 + - `chunkScanner` - 获取用于扫描区块的访问。 +- `net.minecraft.world.level.levelgen.structure.LegacyStructureDataHandler#LAST_MONOLYTH_STRUCTURE_DATA_VERSION` - 返回包含故障巨石的最后数据版本。 +- `net.minecraft.world.level.storage.loot.LootContextArg` - 用于查询的战利品上下文参数。 +- `net.minecraft.world.level.storage.loot.functions.DiscardItem` - 一个丢弃战利品、返回空堆栈的战利品函数。 +- `net.minecraft.world.phys.Vec3` + - `offsetRandomXZ` - 在 XZ 方向上按随机量偏移点。 + - `rotation` - 计算向量的旋转。 + - `applyLocalCoordinatesToRotation` - 将相对于向量当前旋转的分量相加。 + - `isFinite` - 返回向量的所有分量是否为有限值(不是 NaN 或无穷大)。 +- `net.minecraft.world.scores` + - `Scoreboard` + - `packPlayerTeams` - 将玩家队伍打包成可序列化的格式。 + - `packObjectives` - 将目标打包成可序列化的格式。 + - `packDisplaySlots` - 将显示槽位打包成可序列化的格式。 + - `ScoreboardSaveData` + - `getData`、`setData` - 处理打包的记分板。 + - `Packed$EMPTY` - 表示一个空的记分板。 +- `net.minecraft.world.waypoints.Waypoint$Icon#copyFrom` - 从另一个图标复制图标颜色和样式。 + +### 变更列表 + +- `com.mojang.blaze3d.platform.Lighting#updateLevel` 现在接受 `DimensionType$CardinalLightType` 而不是一个布尔值,表示等级是否不是下界 +- `com.mojang.blaze3d.systems.GpuDevice#createTexture` 现在有一个接受提供的标签而不是原始字符串的重载 +- `com.mojang.blaze3d.vertex.VertexConsumer` + - `addVertex`、`addVertexWith2DPose` 现在接受其参数的接口、“只读”变体(例如,`Vector3f` -> `Vector3fc`) + - `putBulkData` 不再接受最后的 `boolean` 来读取缓冲区数据以确定初始颜色 +- `com.mojang.math` + - `OctahedralGroup` + - `fromXYAngles` -> `Quadrant#fromXYAngles` + - `permute` -> `SymmetricGroup3#permuteAxis` + - `SymmetricGroup3` + - `permutation` -> `permute` + - `permuteVector` -> `OctahedralGroup#rotate` + - `Transformation` 现在接受其参数的接口、“只读”变体(例如,`Vector3f` -> `Vector3fc`) + - 这也适用于参数获取器方法 +- `net.minecraft` + - `FileUtil#isValidStrictPathSegment` -> `containsAllowedCharactersOnly`,现在是私有的 + - 被 `isValidPathSegment` 取代 + - `Minecraft` + - `disconnectWithProgressScreen` 现在接受一个 `boolean`,表示是否停止声音引擎 + - `disconnect` 现在接受一个 `boolean`,表示是否停止声音引擎 + - `SharedConstants` + - `DEBUG_WATER` -> `DebugScreenEntries#VISUALIZE_WATER_LEVELS`,不是一对一 + - `DEBUG_HEIGHTMAP` -> `DebugScreenEntries#VISUALIZE_HEIGHTMAP`,不是一对一 + - `DEBUG_COLLISION` -> `DebugScreenEntries#VISUALIZE_COLLISION_BOXES`,不是一对一 + - `DEBUG_SUPPORT_BLOCKS` -> `DebugScreenEntries#VISUALIZE_ENTITY_SUPPORTING_BLOCKS`,不是一对一 + - `DEBUG_LIGHT` -> `DebugScreenEntries#VISUALIZE_BLOCK_LIGHT_LEVELS`、`VISUALIZE_SKY_LIGHT_LEVELS`;不是一对一 + - `DEBUG_SKY_LIGHT_SECTIONS` -> `DebugScreenEntries#VISUALIZE_SKY_LIGHT_SECTIONS`,不是一对一 + - `DEBUG_SOLID_FACE` -> `DebugScreenEntries#VISUALIZE_SOLID_FACES`,不是一对一 + - `DEBUG_CHUNKS` -> `DebugScreenEntries#VISUALIZE_CHUNKS_ON_SERVER`,不是一对一 +- `net.minecraft.advancements.criterion.EntityFlagsPredicate` 现在接受可选的布尔值,表示实体是否在水中或坠落飞行 + - 关联的 `$Builder` 方法也已添加 +- `net.minecraft.client` + - `Camera` + - `setup` 现在接受 `Level` 而不是 `BlockGetter` + - `get*` 已被其记录替代(例如,`getEntity` -> `entity`) + - `Vector3f` 返回值被 `Vector3fc` 取代 + - `GraphicsStatus` -> `GraphicsPreset`,不是一对一 + - `KeyMapping` 现在有一个接受排序顺序的重载 + - `MouseHandler#lastClickTime` -> `lastClick`,现在是私有的,不是一对一 + - `OptionInstance$OptionInstanceSliderButton` 现在实现 `ResettableOptionWidget` + - `Options` + - `graphicsMode` -> `graphicsPreset`、`applyGraphicsPreset` + - `showNowPlayingToast` -> `musicToast`,不是一对一 +- `net.minecraft.client.data.models` + - `EquipmentAssetProvider#humanoidAndHorse` -> `humanoidAndMountArmor` + - `ItemModelGenerators` + - `getSpans` -> `getSideFaces`,不是一对一 + - `$SpanFacing` -> `$SideDirection`,不是一对一 + - `$Span` -> `$SideFace`,不是一对一 + - `ItemModelOutput#accept` 现在有一个接受 `ClientItem$Properties` 的重载 +- `net.minecraft.client.gui` + - `Font#NO_SHADOW` -> `Style#NO_SHADOW` + - `GuiGraphics` + - `textHighlight` 现在接受一个 `boolean`,表示是否渲染背景矩形 + - `submitOutline` -> `renderOutline` +- `net.minecraft.client.gui.components` + - `AbstractButton#handleCursor` -> `handleCursor`,现在是 protected + - `AbstractSliderButton` + - `HANDLE_WIDTH` 现在是 protected + - `canChangeValue`、`setValue` 现在是 protected + - `AbstractWidget#message` 现在是 protected + - `CycleButton` 现在实现 `ResettableOptionWidget` + - `builder` 现在有一个接受提供的默认值的重载 + - `booleanBuilder` 现在接受一个布尔值来选择默认使用哪个组件 + - `$Builder` 现在接受一个提供的默认值 + - `displayOnlyValue(boolean)` -> `displayState`,不是一对一 + - `FocusableTextWidget` 构造函数现在是包私有的,请使用 `builder` + - `OptionsList` 现在将 `$AbstractEntry` 传递到泛型,而不是 `$Entry` + - `addSmall` 现在有一个接受 `OptionInstance` 的重载 + - `$Entry` 现在扩展 `$AbstractEntry` + - `$OptionEntry` 类已移除 + - `big` -> `$Entry#big` + - `small` -> `$Entry#small` + - `StringWidget#clipText` 现在是公开的静态方法,接受 `Font` +- `net.minecraft.client.gui.components.debug` + - `DebugScreenEntryList` + - `toggleF3Visible` -> `toggleDebugOverlay` + - `setF3Visible` -> `setOverlayVisible` + - `isF3Visible` -> `isOverlayVisible` + - `DebugScreenEntryStatus#IN_F3` -> `IN_OVERLAY` +- `net.minecraft.client.gui.components.debugchart.AbstractDebugChart#COLOR_GREY` -> `CommonColors#TEXT_GRAY` +- `net.minecraft.client.gui.components.toasts.ToastManager` + - `createNowPlayingToast` -> `initializeMusicToast`,现在是私有的,不是一对一 + - `removeNowPlayingToast` -> `setMusicToastDisplayState`,不是一对一 +- `net.minecraft.client.gui.navigation.ScreenRectangle#transform*` 方法现在接受其参数的接口、“只读”变体(例如,`Vector3f` -> `Vector3fc`) +- `net.minecraft.client.gui.render.state.*` 现在接受其 `pose` 的接口、“只读”变体(例如,`Vector3f` -> `Vector3fc`) + - `GuiTextRenderState` 现在接受是否绘制每个字形周围的空白区域 +- `net.minecraft.client.gui.screens` + - `DeathScreen` 现在接受 `LocalPlayer` + - `Screen` 现在有一个接受要使用的 `Minecraft` 实例和 `Font` 的重载 + - `minecraft` 现在是 final + - `font` 现在是 final + - `init(Minecraft, int, int)` -> `init(int, int)` + - `resize(Minecraft, int, int)` -> `init(int, int)` + - `handleComponentClicked` -> `ChatScreen#handleComponentClicked`,现在是私有的 + - `handleClickEvent` 已移至其关联的类,而不是一个超接口(例如,`BookViewScreen#handleClickEvent`) +- `net.minecraft.client.gui.screens.advancements` + - `AdvancementsScreen#renderWindow` 现在接受鼠标 XY `int` + - `AdvancementTab#drawTab` 现在接受鼠标 XY `int` +- `net.minecraft.client.gui.screens.debug.DebugOptionsScreen$OptionList` 现在是公开的 +- `net.minecraft.client.gui.screens.inventory` + - `AbstractCommandBlockEditScreen#populateAndSendPacket` 不再接受 `BaseCommandBlock` + - `AbstractContainerScreen#renderSlots`、`renderSlot` 现在接受鼠标 XY `int` + - `CreativeModeInventoryScreen#renderTabButton` 现在接受鼠标 XY `int` + - `EffectsInInventory#renderEffects` -> `render` + - `HorseInventoryScreen` 现在扩展 `AbstractMountInventoryScreen` + - `MinecartCommandBlockEditScreen` 现在接受 `MinecartCommandBlock` 而不是 `BaseCommandBlock` +- `net.minecraft.client.gui.screens.multiplayer.ServerSelectionList$OnlineServerEntry` 现在实现 `SelectableEntry` +- `net.minecraft.client.gui.screens.packs.TransferableSelectionList$PackEntry` 现在实现 `SelectableEntry` +- `net.minecraft.client.gui.screens.recipebook` + - `RecipeBookComponent#initFilterButtonTextures` -> `getFilterButtonTextures`,不是一对一 + - `RecipeBookTabButton` 现在实现 `ImageButton` 而不是 `StateSwitchingButton` + - 构造函数现在接受 XY 位置以及 `Button$OnPress` 消费者 +- `net.minecraft.client.gui.screens.worldselection.WorldSelectionList$WorldListEntry` 不再是静态的,现在实现 `SelectableEntry` +- `net.minecraft.client.model` + - `AnimationUtils` + - `animateCrossbowCharge` 现在接受 `float` 而不是 `int` + - `animateZombieArms` 现在接受 `UndeadRenderState` 而不是两个 `float` + - `HumanoidModel` + - `setupAttackAnimation` 不再接受 `float` + - `getArm` 现在从 protected 变为 public +- `net.minecraft.client.model.geom.ModelPart#getExtentsForGui` 现在接受 `Consumer` 而不是集合 +- `net.minecraft.client.model.geom.builders.UVPair` 现在是一个记录 +- `net.minecraft.client.multiplayer` + - `MultiPlayerGameMode#isAlwaysFlying` -> `isSpectator` + - `ServerStatusPinger#pingServer` 现在接受一个 `EventLoopGroupHolder` +- `net.minecraft.client.renderer` + - `CloudRenderer#render` 现在接受游戏时间 `long` + - `DynamicUniforms#writeTransform`、`$Transform` 不再接受线宽 `float` + - `GameRenderer#setPanoramicMode` -> `setPanoramicScreenshotParameters`,不是一对一 + - `GlobalSettingsUniform#update` 现在接受 `Camera` 以及是否使用旋转网格超采样(RGSS) + - `ItemBlockRenderTypes#setFancy` -> `setCutoutLeaves` + - `ItemInHandRenderer` 不再接受 `ItemRenderer` + - `LevelRenderer#isSectionCompiled` -> `isSectionCompiledAndVisible` + - `RenderPipelines` + - `LINE_STRIP` -> `LINES` 或 `LINES_TRANSLUCENT`,不是一对一 + - `DEBUG_LINE_STRIP` -> `DEBUG_POINTS`,不是一对一 + - `RenderType` + - `LINE_STRIP`、`lineStrip` -> `RenderTypes#LINES`、`LINES_TRANSLUCENT`、`linesTranslucent`;不是一对一 + - `debugLineStrip` -> `debugPoint`,不是一对一 + - `SkyRenderer` 现在接受 `TextureManager` 和 `AtlasManager` + - `extractRenderState` 现在接受 `Camera` 而不是相机位置 + - `renderSunMoonAndStars` 现在接受 `MoonPhase` 而不是 `int` + - `UniformValue` + - `$IVec3Uniform` 现在接受 `Vector3ic` 而不是 `Vector3i` + - `$Vec2Uniform` 现在接受 `Vector2fc` 而不是 `Vector2f` + - `$Vec3Uniform` 现在接受 `Vector3fc` 而不是 `Vector3f` + - `$Vec4Uniform` 现在接受 `Vector4fc` 而不是 `Vector4f` + - `WeatherEffectRenderer#tickRainParticles` 现在接受一个 `int` 作为天气半径 + - `WorldBorderRenderer#extract` 现在接受一个 `float` 作为部分刻 +- `net.minecraft.client.renderer.blockentity` + - `BannerRenderer#getExtents` 现在接受 `Consumer` 而不是集合 + - `BedRenderer#getExtents` 现在接受 `Consumer` 而不是集合 + - `BellRenderer#BELL_RESOURCE_LOCATION` -> `BELL_TEXTURE` + - `DecoratedPotRenderer#getExtents` 现在接受 `Consumer` 而不是集合 + - `EnchantTableRenderer#BOOK_LOCATION` -> `BOOK_TEXTURE` + - `ShulkerBoxRenderer#getExtents` 现在接受 `Consumer` 而不是集合 + - `TestInstanceRenderer` 不再接受 `BlockEntityRendererProvider$Context` +- `net.minecraft.client.renderer.blockentity.state.BlockEntityWithBoundingBoxRenderState$InvisibleBlockType$STRUCUTRE_VOID` -> `STRUCTURE_VOID` +- `net.minecraft.client.renderer.chunk.ChunkSectionLayer#textureView` -> `texture`,不是一对一 +- `net.minecraft.client.renderer.entity.EntityRenderDispatcher` 不再接受 `ItemRenderer` +- `net.minecraft.client.renderer.entity.layers` + - `CarriedBLockLayer` 不再接受 `BlockRenderDispatcher` + - `IronGolemFlowerLayer` 不再接受 `BlockRenderDispatcher` + - `ItemInHandLayer#submitArmWithItem` 现在接受持有的 `ItemStack` +- `net.minecraft.client.renderer.entity.state` + - `ArmedEntityRenderState` + - `*HandItem` -> `*HandItemState`、`*HandItemStack`;不是一对一 + - `extractArmedRenderState` 现在接受部分刻 `float` + - `HorseRenderState#bodyArmorItem` -> `EquineRenderState#bodyArmorItem` + - `HumanoidRenderState` + - `attackTime` -> `ArmedEntityRenderState#attackTime` + - `ticksUsingItem` 现在是一个浮点数 + - `IllagerRenderState` 现在扩展 `UndeadRenderState` + - `ticksUsingItem` 现在是一个浮点数 + - `ZombieRenderState` 现在扩展 `UndeadRenderState` + - `ZombifiedPiglinRenderState` 现在扩展 `UndeadRenderState` +- `net.minecraft.client.renderer.fog.FogRenderer#setupFog` 不再接受 `boolean` +- `net.minecraft.client.renderer.fog.environment` + - `AtmosphericFogEnvironment` 现在扩展 `FogEnvironment` 而不是 `AirBasedFogEnvironment` + - `FogEnvironment#setupFog` 不再接受 `Entity` 和 `BlockPos`,而是接受 `Camera` +- `net.minecraft.client.renderer.item.ClientItem$Properties` 现在接受一个浮点数,用于更改交换动画的缩放 +- `net.minecraft.client.renderer.special.SpecialModelRenderer#getExtents` 现在接受 `Consumer` 而不是集合 +- `net.minecraft.client.renderer.state.SkyRenderState#moonPhase` 现在是 `MoonPhase` 而不是 `int` +- `net.minecraft.client.resources.SplashManager` + - `prepare` 现在返回 `Component` 列表而不是字符串 + - `apply` 现在接受 `Component` 列表而不是字符串 +- `net.minecraft.client.resources.model.BlockModelRotation` 现在是一个类 + - `by` -> `get`,不是一对一 +- `net.minecraft.client.resources.sounds` + - `RidingHappyGhastSoundInstance` -> `RidingEntitySoundInstance`,不是一对一 + - `RidingMinecartSoundInstance` 现在扩展 `RidingEntitySoundInstance` 而不是 `AbstractTickableSoundInstance` + - 构造函数现在接受 `SoundEvent`、最小和最大音量,以及放大器 + - `SimpleSoundInstance#forMusic` 不再接受音量 +- `net.minecraft.client.sounds` + - `SoundEngine` 不再接受 `MusicManager` + - `updateCategoryVolume` -> `refreshCategoryVolume` + - `setVolume` -> `updateCategoryVolume`,不是一对一 + - `SoundManager` 不再接受 `MusicManager` + - `updateSourceVolume` -> `refreshCategoryVolume` + - `setVolume` -> `updateCategoryVolume`,不是一对一 +- `net.minecraft.gametest.framework.GameTestHelper` + - `spawn` 现在有一个接受 `EntitySpawnReason` 或三个 `int` 作为位置的重载 + - `assetTrue`、`assetFalse`、`assertValueEqual` 现在有一个接受 `String` 而不是 `Component` 的重载 + - `assertEntityData` 现在有一个接受 `AABB` 边界框的重载 + - `getRelativeBounds` 现在是公开的 + - `assertEntityPosition` -> `assertEntityPresent`,不是一对一 +- `net.minecraft.nbt` + - `CompoundTag#remove` 现在返回被移除的标签 + - `NbtUtils#getDataVersion` 现在有一个只接受 `CompoundTag` 的重载 +- `net.minecraft.network` + - `Connection` + - `NETWORK_WORKER_GROUP` -> `EventLoopGroupHolder#NIO`,不是一对一 + - `NETWORK_EPOLL_WORKER_GROUP` -> `EventLoopGroupHolder#EPOLL`,不是一对一 + - `LOCAL_WORKER_GROUP` -> `EventLoopGroupHolder#LOCAL`,不是一对一 + - `connectToServer`、`connect` 现在接受 `EventLoopGroupHolder` 而不是 `boolean` + - `FriendlyByteBuf` + - `writeVector3f` 现在接受 `Vector3fc` 而不是 `Vector3f` + - `writeQuaternion` 现在接受 `Quaternionfc` 而不是 `Quaternionf` + - `DEFAULT_NBT_QUOTA` -> `NbtAccounter#DEFAULT_NBT_QUOTA` +- `net.minecraft.network.codec` + - `ByteBufCodecs` + - `VECTOR3F` 现在使用 `Vector3fc` 而不是 `Vector3f` + - `QUATERNIONF` 现在使用 `Quaternionfc` 而不是 `Quaternionf` + - `StreamCodec#composite` 现在有十个和十二个参数变体 +- `net.minecraft.network.chat` + - `ComponentUtils#mergeStyles` 现在有一个接受并返回 `Component` 的重载 + - `MutableComponent` 现在是 final +- `net.minecraft.network.protocol.game` + - `ClientboundHorseScreenOpenPacket` -> `ClientboundMountScreenOpenPacket` + - `ClientGamePacketListener#handleHorseScreenOpen` -> `handleMountScreenOpen` + - `GamePacketTypes#CLIENTBOUND_HORSE_SCREEN_OPEN` -> `CLIENTBOUND_MOUNT_SCREEN_OPEN` +- `net.minecraft.network.numbers` + - `FixedFormat` 现在是一个记录 + - `StyledFormat` 现在是一个记录 +- `net.minecraft.network.syncher.EntityDataSerializers` + - `VECTOR3` 现在使用 `Vector3fc` 而不是 `Vector3f` + - `QUATERNION` 现在使用 `Quaternionfc` 而不是 `Quaternionf` +- `net.minecraft.server` + - `MinecraftServer` + - `isAllowedToEnterPortal` -> `ServerLevel#isAllowedToEnterPortal` + - `isSpawningMonsters` -> `ServerLevel#isSpawningMonsters` + - `isPvpAllowed` -> `ServerLevel#isPvpAllowed` + - `isCommandBlockEnabled` -> `ServerLevel#isCommandBlockEnabled` + - `isSpawnerBlockEnabled` -> `ServerLevel#isSpawnerBlockEnabled` + - `getGameRules` -> `ServerLevel#getGameRules` + - `isEpollEnabled` -> `useNativeTransport` + - `ServerScoreboard` 不再实现其自己的保存数据类型,而是使用打包的 `ScoreboardSaveData` + - `TYPE` -> `ScoreboardSavedData#TYPE` +- `net.minecraft.server.jsonrpc` + - `IncomingRpcMethod` 现在接受两个泛型,分别用于请求的参数和结果响应 + - `$Builder` 现在有无参数和参数函数的构造函数,取代了 `$Factory` + - `response`、`param` 现在接受它们的 `Schema` + - `OutgoingRpcMethod$Factory` 现在接受泛型参数和结果 +- `net.minecraft.server.jsonrpc.api` + - `MethodInfo`、`$Named` 现在接受两个泛型,分别用于请求的参数和结果响应 + - `PARAMS_CODEC` -> `paramsTypedCodec`,现在是私有的,不是一对一 + - `MAP_CODEC` -> `typedCodec`,现在是包私有的,不是一对一 + - `ParamInfo` 现在接受一个参数的泛型 + - `CODEC` -> `typedCodec`,不是一对一 + - `ResultInfo` 现在接受一个结果响应的泛型 + - `CODEC` -> `typedCodec`,不是一对一 + - `Schema` 现在接受一个其表示类型的泛型 + - 构造函数现在接受一个类型列表而不是一个可选项、一个非可选的属性映射、非可选的枚举值,以及用于序列化类型的编解码器 + - `ofTypes` 现在有一个接受类型列表的重载 + - `SchemaComponent` 现在接受一个其表示类型的泛型 +- `net.minecraft.server.jsonrpc.security.AuthenticationHandler` 现在实现 `ChannelDuplexHandler` 而不是 `ChannelInboundHandlerAdapter` + - 构造函数现在接受一个允许的来源的字符串集合 + - `$SecurityCheckResult#allowed` 现在有一个重载,指定令牌是否通过 websocket 协议发送 +- `net.minecraft.server.level` + - `ChunkMap` 现在扩展 `SimpleRegionStorage` 而不是 `ChunkStorage` + - `ServerLevel#drop` 不再返回 `boolean` +- `net.minecraft.server.network.ServerConnectionListener` + - `SERVER_EVENT_GROUP` -> `EventLoopGroupHolder#NIO`,不是一对一 + - `SERVER_EPOLL_EVENT_GROUP` -> `EventLoopGroupHolder#EPOLL`,不是一对一 +- `net.minecraft.stats.ServerStatsCounter` 现在接受 `Path` 而不是 `File` + - `parseLocal` -> `parse`,不是一对一 + - `toJson` 现在返回 `JsonElement` 而不是 `String` +- `net.minecraft.util` + - `ARGB#lerp` -> `srgbLerp` + - `ExtraCodecs` 现在使用其泛型的接口、“只读”变体(例如,`Vector3f` -> `Vector3fc`) + - `Mth` + - `easeInOutSine` -> `Ease#inOutSine` + - `sin`、`cos` 现在接受 `double` 而不是 `float` + - `absMax` 现在有使用 `int` 或 `float` 的重载 + - `TriState` 现在实现 `StringRepresentable` +- `net.minecraft.util.profiling.jfr.Percentiles#evaluate` 现在有一个接受 `int[]` 的重载 +- `net.minecraft.util.profiling.jfr.parse.JfrStatsResult` 现在接受一个 FPS 统计信息 + - `tickTimes` -> `serverTickTimes` +- `net.minecraft.util.profiling.jfr.stats.TimedStatSummary#summary` 现在返回 `TimeStatSummary` 的可选值 +- `net.minecraft.util.worldupdate.WorldUpgrader` + - `$AbstractUpgrader` 不再接受泛型 + - `createStorage` 现在返回 `SimpleRegionStorage` 而不是泛型 + - `tryProcessOnePosition` 现在接受 `SimpleRegionStorage` 而不是泛型 + - `$DimensionToUpgrade` 不再接受泛型,而是使用 `SimpleRegionStorage` +- `net.minecraft.world.RandomSequences` 不再接受世界种子 + - `codec` -> `CODEC` + - `get`、`reset` 现在接受世界种子 +- `net.minecraft.world.entity` + - `Avatar#DATA_PLAYER_MAIN_HAND` 现在使用 `HumanoidArm` 泛型而不是字节 + - `Entity#hasImpulse` -> `needsSync` + - `EntityType#loadEntityRecursive` 现在接受 `EntityProcessor` 而不是 `Function` + - `LivingEntity#invulnerableDuration` -> `INVULNERABLE_DURATION` + - `Mob#playAttackSound` -> `LivingEntity#playAttackSound` + - `NeutralMob` + - `TAG_ANGER_TIME` -> `TAG_ANGER_END_TIME`,不是一对一 + - `getRemainingPersistentAngerTime` -> `getPersistentAngerEndTime`,不是一对一 + - `setRemainingPersistentAngerTime` -> `setTimeToRemainAngry`、`setPersistentAngerEndTime`;第二个不是一对一 + - `getPersistentAngerTarget`、`setPersistentAngerTarget` 现在处理 `EntityReference` +- `net.minecraft.world.entity.ai.sensing.SensorType#*_TEMPTATIONS` -> `FOOD_TEMPTATIONS`,不是一对一 +- `net.minecraft.world.entity.ai.util` + - `GoalUtils` + - `mobRestricted` 现在接受 `double` 而不是 `int` + - `isRestricted` 现在有一个接受 `Vec3` 的重载 + - `LandRandomPos` + - `getPosAway` 现在有一个接受额外 `double` 作为起始/结束弧度的重载 + - `generateRandomPosTowardDirection` 现在接受 `double` 而不是 `int` + - `RandomPos` + - `generateRandomDirectionWithinRadians` 现在接受 `double` 作为起始/结束弧度 + - `generateRandomPosTowardDirection` 现在接受 `double` 而不是 `int` +- `net.minecraft.world.entity.animal.equine.AbstractHorse#getInventorySize` -> `AbstractMountInventoryMenu#getInventorySize` +- `net.minecraft.world.entity.monster.Monster#checkMonsterSpawnRules` 现在将其类型泛型扩展为 `Mob` 而不是 `Monster` +- `net.minecraft.world.entity.monster.skeleton.Bogged#*_ATTACK_INTERVAL` -> `AbstractSkeleton#INCREASED_*_ATTACK_INTERVAL` +- `net.minecraft.world.entity.monster.zombie` + - `Husk#checkHuskSpawnRules` -> `Monster#checkSurfaceMonsterSpawnRules`,不是一对一 + - `Zombie` + - `doUnderWaterConversion` 现在接受 `ServerLevel` + - `convertToZombieType` 现在接受 `ServerLevel` +- `net.minecraft.world.entity.npc.villager` + - `AbstractVillager` + - `updateTrades` 现在接受 `ServerLevel` + - `addOffersFromItemListings` 现在接受 `ServerLevel` + - `Villager#shouldRestock` 现在接受 `ServerLevel` + - `VillagerTrades$ItemListing#getOffer` 现在接受 `ServerLevel` +- `net.minecraft.world.entity.player.Player` + - `openMinecartCommandBlock` 现在接受 `MinecartCommandBlock` 而不是 `BaseCommandBlock` + - `sweepAttack` -> `doSweepAttack`,现在是私有的,不是一对一 + - `respawn` -> `LocalPlayer#respawn` + - `CLIENT_LOADED_TIMEOUT_TIME` -> `ServerGamePacketListenerImpl#CLIENT_LOADED_TIMEOUT_TIME` + - `clientLoadedTimeoutTimer`、`tickClientLoadTimeout` -> `ServerGamePacketListenerImpl#tickClientLoadTimeout` + - `hasClientLoaded` -> `ServerGamePacketListenerImpl#hasClientLoaded` + - `setClientLoaded` -> `ServerGamePacketListenerImpl#markClientLoaded`、`markClientUnloadedAfterDeath`、`restartClientLoadTimerAfterRespawn`;不是一对一 +- `net.minecraft.world.entity.projectile.Projectile` 构造函数现在是 `protected` 而不是包私有的 +- `net.minecraft.world.entity.vehicle.VehicleEntity#shouldSourceDestroy` 现在是 `protected` 而不是包私有的 +- `net.minecraft.world.entity.vehicle.minecart` + - `AbstractMinecart` 现在接受 `ServerLevel` + - `MinecartCommandBlock$MinecartCommandBase` 现在是包私有的 +- `net.minecraft.world.inventory.HorseInventoryMenu` 现在扩展 `AbstractMountInventoryMenu` +- `net.minecraft.world.item.component.ItemAttributeModifiers#compute` 现在接受 `Attribute` 持有者 +- `net.minecraft.world.item.enchantment.effects.PlaySoundEffect` 现在接受一个声音事件列表而不是单个 +- `net.minecraft.world.level` + - `BaseCommandBlock` + - `performCommand` 现在接受 `ServerLevel` 而不是 `Level` + - `onUpdated` 现在接受 `ServerLevel` + - `createCommandSourceStack` 现在接受 `ServerLevel` + - `$CloseableCommandBlockSource` 现在接受 `ServerLevel`,其构造函数是 protected + - `CollisionGetter#noBlockCollision` 现在有一个接受额外 `boolean` 的重载,表示是否检查液体碰撞。 + - `Level#getGameTime` -> `LevelAccessor#getGameTime` + - `LevelAccessor#getCurrentDifficultyAt` -> `ServerLevelAccessor#getCurrentDifficultyAt` + - `LevelTimeAccess#getMoonPhase` 现在返回 `MoonPhase` 而不是 `int` +- `net.minecraft.world.level.biome` + - `AmbientAdditionsSettings` 现在是一个记录 + - `AmbientMoodSettings` 现在是一个记录 + - `AmbientParticleSettings` 现在是一个记录 +- `net.minecraft.world.level.block.entity.BaseContainerBlockEntity#canUnlock` -> `sendChestLockedNotifications`,不是一对一 +- `net.minecraft.world.level.border` + - `BorderChangeListener#onLerpSize` 现在接受一个额外的 `long` 作为游戏时间 + - `WorldBorder` 现在可以接受 `WorldBorder$Settings` + - `getMin*`、`getMax*` 现在有一个接受部分刻 `float` 的重载 + - `lerpSizeBetween` 现在接受一个额外的 `long` 作为游戏时间 + - `applySettings` -> `applyInitialSettings`,不是一对一 + - 原始行为可以通过将设置传递给构造函数来复制 + - `$BorderExtent#getMin*`、`getMax*` 现在接受部分刻 `float` +- `net.minecraft.world.level.chunk.storage` + - `RecreatingSimpleRegionStorage` 现在接受一个提供的 `LegacyTagFixer` + - `SimpleRegionStorage` 现在接受一个提供的 `LegacyTagFixer` + - `write` 现在有一个接受提供的 `CompoundTag` 的重载 + - `upgradeChunkTag` 现在有一个接受可为空的标签上下文的重载 +- `net.minecraft.world.level.dimension.DimensionType` + - `MOON_PHASES` 现在是一个 `MoonPhase` 数组并且是私有的 + - `moonPhase` 现在返回 `MoonPhase` 而不是 `int` +- `net.minecraft.world.level.levelgen.structure.LegacyStructureDataHandler` 现在实现 `LegacyTagFixer` + - 构造函数现在接受 `DataFixer` + - `removeIndex` -> `LegacyTagFixer#markChunkDone` + - `updateFromLegacy` 现在是私有的 + - `getLegacyStructureHandler` 现在接受 `DataFixer`、一个提供的 `DimensionDataStorage`,并返回一个提供的 `LegacyTagFixer` +- `net.minecraft.world.level.levelgen.structure.structures.NetherFortressPieces$StartPiece` 字段现在是包私有的 +- `net.minecraft.world.level.saveddata.SavedDataType` 不再接受 `SavedData$Context`,移除了函数参数构造函数 +- `net.minecraft.world.level.storage` + - `DimensionDataStorage` 不再接受 `SavedData$Context` + - `FileNameDateFormatter#create` -> `FORMATTER` + - `LevelStorageSource` + - `UNCOMPRESSED_NBT_QUOTA` -> `NbtAccounter#UNCOMPRESSED_NBT_QUOTA`,现在是 `public` + - `$LevelDirectory#corruptedDataFile`、`rawDataFile` 现在接受 `ZonedDateTime` 而不是 `LocalDateTime` +- `net.minecraft.world.level.storage.loot.LootContext` + - `$BlockEntityTarget` 现在实现 `LootContextArg$SimpleGetter` + - `getParam` -> `contextParam` + - `$EntityTarget` 现在实现 `LootContextArg$SimpleGetter` + - `getParam` -> `contextParam` + - `$ItemStackTarget` 现在实现 `LootContextArg$SimpleGetter` + - `getParam` -> `contextParam` +- `net.minecraft.world.level.storage.loot.functions` + - `CopyComponentsFunction` + - `$*Source` -> `$DirectSource`,不是一对一 + - `$Source` -> `LootContextArg$Getter`,不是一对一 + - `CopyNameFunction#copyName` 现在接受 `LootContextArg` 而不是 `$Source` + - `$Source` -> `LootContextArg`,不是一对一 + - `FilteredFunction` 现在接受一个可选的通过和失败的 `LootItemFunction` 而不是仅仅一个修改器 + - 该函数现在可以通过 `$Builder` 通过 `filtered` 构建 +- `net.minecraft.world.phys.Vec3` 现在接受 `Vector3fc` 而不是 `Vector3f` +- `net.minecraft.world.phys.shapes.Shapes#rotateHorizontal`、`rotateAll`、`rotateAttachFace` 现在有接受 `OctahedralGroup` 的重载 +- `net.minecraft.world.scores` + - `Score` 现在有一个用于 `$Packed` 值的公共构造函数 + - `MAP_CODEC` -> `Score$Packed` 及其 `$Packed#MAP_CODEC` + - `Scoreboard$PackedScore#score` 现在接受 `Score$Packed` 而不是 `Score` + - `ScoreboardSavedData` 现在接受 `ScoreboardSaveData$Packed` 而不是 `Scoreboard` + - `FILE_ID` 合并到类型中 + - `loadFrom` -> `ServerScoreboard#load` + - `pack` -> `ServerScoreboard#store`,现在是私有的,不是一对一 + +### 移除列表 + +- `com.mojang.blaze3d.vertex.VertexFormat$Mode#LINE_STRIP` +- `net.minecraft.Util#lastOf` +- `net.minecraft.client` + - `Minecraft#useFancyGraphics` + - `GuiMessage#icon` + - `StringSplitter` + - `formattedIndexByWidth`、`componentStyleAtWidth` + - `splitLines(FormattedText, int, Style, FormattedText)` +- `net.minecraft.client.gui.Font#wordWrapHeight(String, int)` +- `net.minecraft.client.gui.components` + - `CycleButton` + - `onOffBuilder()` + - `$Builder#withInitialValue` + - `StateSwitchingButton` +- `net.minecraft.client.gui.screens.inventory` + - `EffectsInInventory#renderTooltip` + - `InventoryScreen#renderEntityInInventory` +- `net.minecraft.client.gui.screens.packs.PackSelectionScreen#clearSelected` +- `net.minecraft.client.player.LocalPlayer#USING_ITEM_SPEED_FACTOR` +- `net.minecraft.client.renderer` + - `ItemModelGenerator#createOrExpandSpan` + - `GpuWarnlistManager#dismissWarningAndSkipFabulous`、`isSkippingFabulous` + - `RenderPipelines` + - `DEBUG_STRUCTURE_QUADS`、`DEBUG_SECTION_QUADS` + - `SkyRenderer#initTextures` +- `net.minecraft.client.renderer.fog.environment` + - `AirBasedFogEnvironment` + - `DimensionOrBossFogEnvironment` + - `FogEnvironment#onNotApplicable` +- `net.minecraft.client.resources.model.BlockModelRotation#actualRotation` +- `net.minecraft.gametest.framework.GameTestHelper#setNight`、`setDayTime` +- `net.minecraft.network.FriendlyByteBuf#readDate`、`writeDate` +- `net.minecraft.server` + - `MinecraftServer#hasGui` + - `ServerScoreboard#createData`、`addDirtyListener` +- `net.minecraft.server.jsonrpc.IncomingRpcMethod$Factory` +- `net.minecraft.server.jsonrpc.methods.IllegalMethodDefinitionException` +- `net.minecraft.server.jsonrpc.security.AuthenticationHandler#AUTH_HEADER` +- `net.minecraft.util` + - `DebugBuffer` + - `LazyLoadedValue` +- `net.minecraft.util.thread.NamedThreadFactory` +- `net.minecraft.world.entity.Mob#isSunBurnTick` +- `net.minecraft.world.entity.animal.armadillo.ArmadilloAi#getTemptations` +- `net.minecraft.world.entity.animal.axolotl.AxolotlAi#getTemptations` +- `net.minecraft.world.entity.animal.camel.CamelAi#getTemptations` +- `net.minecraft.world.entity.animal.equine.ZombieHorse#checkZombieHorseSpawnRules` + - 请改用 `Monster#checkMonsterSpawnRules` +- `net.minecraft.world.entity.animal.goat.GoatAi#getTemptations` +- `net.minecraft.world.entity.animal.sniffer.SnifferAi#getTemptations` +- `net.minecraft.world.entity.player.Player#playNotifySound` +- `net.minecraft.world.entity.raid.Raid#TICKS_PER_DAY` +- `net.minecraft.world.level` + - `BaseCommandBlock` + - `getLevel` + - `getUsedBy`、`getPosition` + - `Level#TICKS_PER_DAY` +- `net.minecraft.world.level.border.WorldBorder$Settings#toWorldBorder` + - 请改用 `WorldBorder` 构造函数 +- `net.minecraft.world.level.chunk.storage` + - `ChunkStorage` + - `RecreatingChunkStorage` +- `net.minecraft.world.level.saveddata.SavedData$Context` +- `net.minecraft.world.phys.Vec3#fromRGB24` diff --git a/primers-doc/1.21.2-from-1.21.1.md b/primers-doc/1.21.2-from-1.21.1.md new file mode 100644 index 0000000..f2654ee --- /dev/null +++ b/primers-doc/1.21.2-from-1.21.1.md @@ -0,0 +1,3195 @@ +# Minecraft 1.21.1 -> 1.21.2 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.1 迁移到 1.21.2。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.2&tab=changelog) 中找到它们的列表。 + +## 持有者集 过渡 + +许多之前使用 `TagKey` 或原始注册表对象的方法已被直接的 `HolderSet` 对象取代。`HolderSet` 本质上是一个注册表对象引用的列表,游戏可以根据需要动态更新和管理它。实际上有两种 `HolderSet`:直接和命名。命名的 `HolderSet` 是游戏中标签的对象表示。之所以称为命名集,是因为 `HolderSet` 通过标签名称引用。而直接的 `HolderSet` 是通过 `HolderSet#direct` 创建的,它作为一个内联的值列表。当不需要定义单独对象来构造某个值时,这些很有用。 + +JSON 示例: +```json5 +// HolderSet#direct 包含一个元素 +{ + "holder_set": "minecraft:apple" +} + +// HolderSet#direct 包含多个元素 +{ + "holder_set": [ + "minecraft:apple", + "minecraft:stick" + ] +} + +// HolderSet 引用(标签) +{ + "holder_set": "#minecraft:planks" +} +``` + +通常,除了在提供者生成期间,您永远不应该在代码中构造持有者集。每种集类型都有不同的构造方法。 + +首先,要处理 `Holder` 或 `HolderSet`,您需要通过 `Registry` 访问静态注册表实例,或者通过 `HolderGetter` 访问数据包注册表。`HolderGetter` 可以在数据包注册表生成期间从 `BootstrapContext#lookup` 获得,或者在生成期间或游戏过程中的 `MinecraftServer#registryAccess` 从 `HolderLookup$Provider#lookupOrThrow` 获得。 + +一旦可用,对于直接的 `HolderSet`,您需要获取注册表对象的 `Holder` 形式。对于静态注册表,通过 `Registry#wrapAsHolder` 完成。对于数据包注册表,通过 `HolderGetter#getOrThrow` 完成。 + +```java +// 物品的直接 HolderSet +HolderSet items = HolderSet.direct(BuiltInRegistries.ITEM.wrapAsHolder(Items.APPLE)); + +// 配置功能的直接 HolderSet +// 假设我们可以访问 HolderGetter> 注册表 +Holderset> features = HolderSet.direct(registry.getOrThrow(OreFeatures.ORE_IRON)); +``` + +对于命名的 `HolderSet`,过程类似。对于静态和动态注册表,都调用 `HolderGetter#getOrThrow`。 + +```java +// 物品的命名 HolderSet +HolderSet items = BuiltInRegistries.ITEM.getOrThrow(ItemTags.PLANKS); + +// 生物群系的命名 HolderSet +// 假设我们可以访问 HolderGetter 注册表 +Holderset biomes = registry.getOrThrow(BiomeTags.IS_OCEAN); +``` + +由于这些变更遍及整个代码库,它们将在更相关的子章节中列出。 + +## GUI 渲染类型 + +`GuiGraphics` 中的 GUI 渲染方法现在接受一个 `Function` 来确定如何渲染图像。此外,`blit` 方法现在需要指定 PNG 的大小。 + +```java +// 对于某个 GuiGraphics graphics +graphics.blit( + // 如何渲染纹理 + RenderType::guiTextured, + // 之前的纹理参数 + ..., + // 要使用的 PNG 大小 + 256, 256); +``` + +这意味着那些提供设置纹理或其他可在着色器中指定属性的辅助方法已被移除。 + +- `com.mojang.blaze3d.pipeline.RenderTarget#blitToScreen(int, int, boolean)` -> `blitAndBlendToScreen` +- `net.minecraft.client.gui.GuiGraphics` + - `drawManaged` 已移除 + - `setColor` 已移除 - 现在是 `blit` 和 `blitSprite` 方法中的一个参数 + - `blit(int, int, int, int, int, TextureAtlasSprite, *)` 已移除 + - `bufferSource` -> `drawSpecial`,不是一对一,因为该方法接受一个 `MultiBufferSource` 的消费者,并结束当前批次,而不是仅仅返回 `MultiBufferSource` +- `net.minecraft.client.gui.components.PlayerFaceRenderer` + - 除 `draw(GuiGraphics, PlayerSkin, int, int, int)` 之外的所有 `draw` 方法都接受一个额外的 `int` 参数,用于定义颜色 +- `net.minecraft.client.renderer.RenderType` + - `guiTexturedOverlay` - 获取叠加在游戏屏幕上的图像的渲染类型。 + - `guiOpaqueTexturedBackground` - 获取应用于菜单背景的 GUI 纹理的渲染类型。 + - `guiNauseaOverlay` - 获取恶心覆盖层的渲染类型。 + - `guiTextured` - 获取 GUI 菜单内图像的渲染类型。 +- `net.minecraft.client.resources.metadata.gui.GuiSpriteScaling$NineSlice` 现在接受一个布尔值,表示纹理的中心部分是否应拉伸以适应大小。 + +## 着色器重写 + +着色器的内部实现已被大量重写。 + +### 着色器文件 + +主要变更是定义的采样器和后期着色器。 + +`DiffuseSampler` 和 `DiffuseDepthSampler` 已根据应用目标被赋予新名称:`InSampler`、`MainSampler` 和 `MainDepthSampler`。`InSampler` 用于除 `transparency` 程序着色器之外的所有场景。 + +```json5 +// 在某个着色器 JSON 中 +{ + "samplers": [ + { "name": "MainSampler" }, + // ... + ] +} +``` + +在后处理效果着色器中,它们已被完全改变。有关变更的完整分析,请参见 `PostChainConfig`,但总的来说,所有目标现在都是对象的键,所有通道输入和过滤器现在是采样器输入的列表。如下所示: + +```json5 +// 旧的后处理效果着色器 JSON +// 在 assets//shaders/post 中 +{ + "targets": [ + "swap" + ], + "passes": [ + { + "name": "invert", + "intarget": "minecraft:main", + "outtarget": "swap", + "use_linear_filter": true, + "uniforms": [ + { + "name": "InverseAmount", + "values": [ 0.8 ] + } + ] + }, + { + "name": "blit", + "intarget": "swap", + "outtarget": "minecraft:main" + } + ] +} + +// 新的后处理 JSON +// 在 assets//post_effect 中 +{ + "targets": { + "swap": {} // swap 现在是一个目标对象(除非另有指定,否则为全屏) + }, + "passes": [ + { + // 要应用的程序名称(之前为 'name') + // assets/minecraft/shaders/post/invert.json + "program": "minecraft:post/invert", + // inputs 现在是一个列表 + "inputs": [ + { + // 目标是 InSampler + // 采样器必须在程序着色器 JSON 中可用 + "sampler_name": "In", + // 从主屏幕读取(之前为 'intarget') + "target": "minecraft:main", + // 使用 GL_LINEAR(之前为 'use_linear_filter') + "bilinear": true + } + ], + // 写入 swap 目标(之前为 'outtarget') + "output": "swap", + "uniforms": [ + { + "name": "InverseAmount", + "values": [ 0.8 ] + } + ] + }, + { + "program": "minecraft:post/blit", + "inputs": [ + { + "sampler_name": "In", + "target": "swap" + } + ], + "output": "minecraft:main" + } + ] +} +``` + +### 着色器程序 + +所有着色器,无论它们在何处使用(作为程序或后处理效果的一部分),都在 `assets//shaders` 中有一个 JSON。该 JSON 定义了着色器将使用的所有内容,由 `ShaderProgramConfig` 定义。主要增加是对 `ResourceLocation` 相对引用的更改,以及在加载期间动态添加 `defines` 头。 + +```json5 +// 对于某个 assets/my_mod/shaders/my_shader.json +{ + // 指向 assets/my_mod/shaders/my_shader.vsh(之前为 'my_shader',无 id 指定) + "vertex": "my_mod:my_shader", + // 指向 assets/my_mod/shaders/my_shader.fsh(之前为 'my_shader',无 id 指定) + "fragment": "my_mod:my_shader", + // 向着色器添加 '#define' 头 + "defines": { + // #define + "values": { + "ALPHA_CUTOUT": "0.1" + }, + // #define flag + "flags": [ + "NO_OVERLAY" + ] + }, + // 要在着色器中使用的采样器统一变量列表 + // 有 12 个纹理采样器统一变量 Sampler0-Sampler11,但通常只提供 Sampler0 + // 此外,对于后处理着色器,有动态的 '*Sampler',它们被绑定以读取指定的目标或 'minecraft:main' + "samplers": [ + { "name": "Sampler0" } + ], + // 可在着色器中访问的统一变量列表 + // 可用统一变量的列表可以在 CompiledShaderProgram#setUniforms 中找到 + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ModelOffset", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } + ] +} +``` + +```glsl +// 对于某个 assets/my_mod/shaders/my_shader.vsh(顶点着色器) + +// GLSL 版本 +#version 150 + +// 导入 Mojang GLSL 文件 +// 位于 assets//shaders/include/ +#moj_import + +// 注入定义(可以使用 'ALPHA_CUTOUT' 和 'NO_OVERLAY') + +// 由下面传递给 ShaderProgram 的 VertexFormat 定义 +in vec3 Position; // vec3 浮点数 +in vec4 Color; // vec4 无符号字节(0-255) + +// 由 JSON 定义的采样器 +uniform sampler2D Sampler0; + +// 由 JSON 提供的统一变量 +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; +uniform vec3 ModelOffset; + +// 输出到片段着色器的值 +out float vertexDistance; +out vec4 vertexColor; +out vec2 texCoord0; + +void main() { + // 在此处设置输出值 +} +``` + +```glsl +// 对于某个 assets/my_mod/shaders/my_shader.fsh(片段着色器) + +// GLSL 版本 +#version 150 + +// 导入 Mojang GLSL 文件 +// 位于 assets//shaders/include/ +#moj_import + +// 注入定义(可以使用 'ALPHA_CUTOUT' 和 'NO_OVERLAY') + +// 由上面顶点着色器的输出定义 +in float vertexDistance; +in vec4 vertexColor; +in vec2 texCoord0; + +// 由 JSON 定义的采样器 +uniform sampler2D Sampler0; + +// 由 JSON 提供的统一变量 +uniform vec4 ColorModulator; + +// 输出到帧缓冲区的值(像素的颜色) +out vec4 fragColor; + +void main() { + // 在此处设置输出值 +} +``` + +在代码方面,着色器在内部存储为 `ShaderProgram` 或 `CompiledShaderProgram`。`ShaderProgram` 表示标识符,而 `CompiledShaderProgram` 表示要运行的着色器本身。两者通过 `ShaderManager` 链接在一起。 + +除非指定为核心着色器,否则着色器程序是动态编译的。这通过将 `ShaderProgram` 注册到 `CoreShaders#PROGRAMS` 来完成。 + +```java +// 访问 List PROGRAMS +ShaderProgram MY_SHADER = new ShaderProgram( + // 指向 assets/my_mod/shaders/my_shader.json + ResourceLocation.fromNamespaceAndPath("my_mod", "my_shader"), + // 传入着色器使用的顶点格式 + DefaultVertexFormat.POSITION_COLOR, + // 列出 '#define' 值和标志 + // 值:'#define ' + // 标志:'#define ' + ShaderDefines.EMPTY +) +``` + +然后通过调用 `RenderSystem#setShader` 并传入相应的 `ShaderProgram` 来设置着色器程序。实际上,所有对 `GameRenderer#get*Shader` 的引用都应替换为 `ShaderProgram` 引用。 + +```java +// 在某个渲染方法中 +RenderSystem.setShader(MY_SHADER); + +// 为 RenderType 创建新的 ShaderStateShard +ShaderStateShard MY_SHARD = new ShaderStateShard(MY_SHADER); +``` + +- `com.mojang.blaze3d.ProjectionType` - 一个枚举,包含投影矩阵应如何渲染的逻辑。 +- `com.mojang.blaze3d.buffers` + - `BufferType` - 一个枚举,指定 GL 目标缓冲区类型。 + - `GpuBuffer` - 围绕 GL 缓冲区调用的包装器,用于处理屏幕渲染。 + - `GpuFence` - 用于管理 GPU 围栏同步状态的句柄。 +- `com.mojang.blaze3d.platform.GlStateManager` + - `glShaderSource` 现在接受一个 `String` 而不是 `List` + - `_glMapBufferRange` - 委托给 `GL30#glMapBufferRange`。 + - `_glFenceSync` - 委托给 `GL32#glFenceSync`。 + - `_glClientWaitSync` - 委托给 `GL32#glClientWaitSync`。 + - `_glDeleteSync` - 委托给 `GL32#glDeleteSync`。 + - `_glBuffserSubData` - 委托给 `GL15#glBufferSubData`。 +- `com.mojang.blaze3d.preprocessor.GlslPreprocessor#injectDefines` - 将任何定义的源注入到加载的 `.*sh` 文件的顶部。 +- `com.mojang.blaze3d.shaders` + - `BlendMode`、`Effect`、`EffectProgram`、`Program`、`ProgramManager`、`Shader` 已被整合到 `CompiledShader` 中 + - `Unform` 不再接受 `Shader` + - `glGetAttribLocation` 已移除 + - `glBindAttribLocation` -> `VertexFormat#bindAttributes` + - `setFromConfig` - 根据另一个统一配置的值和计数设置统一参数。 +- `com.mojang.blaze3d.systems.RenderSystem` + - `setShader` 现在接受 `CompiledShaderProgram` 或 `ShaderProgram` + - `clearShader` - 清除当前系统着色器。 + - `runAsFancy` 已移除,由 `LevelRenderer#getTransparencyChain` 内部处理 + - `setProjectionMatrix` 现在接受一个 `ProjectionType` 而不仅仅是 `VertexSorting` + - `getVertexSorting` -> `getProjectionType`;不是一对一,但 `VertexSorting` 可在 `ProjectionType` 上访问 +- `com.mojang.blaze3d.vertex.VertexBuffer` + - `drawWithShader` 在传入 `null` 的 `CompiledShaderProgram` 时将成为空操作 + - `$Usage` -> `com.mojang.blaze3d.buffers.BufferUsage` +- `net.minecraft.client.Minecraft#getShaderManager` - 返回管理所有着色器和后处理效果的管理器。 +- `net.minecract.client.renderer` + - `EffectInstance` 类已移除,在大多数情况下被 `CompiledShaderProgram` 取代 + - `GameRenderer` + - `get*Shader` -> `CoreShaders#*` + - `shutdownEffect` -> `clearPostEffect` + - `createReloadListener` -> `ShaderManager` + - `currentEffect` -> `currentPostEffect` + - `ItemBlockRenderTypes#getRenderType` 不再接受一个表示是否使用半透明渲染类型的布尔值 + - `ShaderInstance` -> `CompiledShaderProgram` + - `CHUNK_OFFSET` -> `MODEL_OFFSET` + - JSON 着色器:`ChunkOffset` -> `ModelOffset` + - `getUniformConfig` - 返回给定名称的统一变量的配置。 + - `LevelRenderer#graphicsChanged` 已移除,由 `LevelRenderer#getTransparencyChain` 内部处理 + - `PostChainConfig` - 表示后处理效果着色器 JSON 如何构建的配置。 + - `PostPass` 现在接受代表输出目标的 `ResourceLocation` 而不是输入和输出 `RenderTarget` 或 `boolean` 过滤模式,要使用的 `CompiledShaderProgram` 而不是 `ResourceProvider`,以及着色器要使用的一组统一变量 + - 不再实现 `AutoCloseable` + - `addToFrame` 不再接受 `float` 时间 + - `getEffect` -> `getShader` + - `addAuxAsset` -> `addInput` + - `process` -> `addToFrame` + - `$Input` - 表示后处理效果着色器的输入。 + - `$TargetInput` - 来自 `RenderTarget` 的输入。 + - `$TextureInput` - 来自纹理的输入。 + - `PostChain` 构造函数现在通过 `load` 创建 + - 不再实现 `AutoCloseable` + - `MAIN_RENDER_TARGET` 现在是公开的 + - `getName` 已移除,被 `ShaderProgram#configId` 取代 + - `process` 不再接受 `DeltaTracker` + - `$TargetBundle` - 处理链中资源句柄的获取和替换。 + - `RenderType` + - `entityTranslucentCull`、`entityGlintDirect` 已移除 + - `armorTranslucent` - 一种渲染类型,用于渲染可以具有半透明纹理的盔甲。 + - `ShaderDefines` - 着色器用作常量的定义值和标志。 + - `ShaderManager` - 加载着色器的资源监听器。 + - `ShaderProgram` - 着色器的标识符。 + - `ShaderProgramConfig` - 程序着色器 JSON 的定义。 + - `Sheets#translucentCullBlockSheet` 已移除 + - `SkyRenderer` 现在实现 `AutoCloseable` +- `net.minecraft.client.renderer.entity.ItemRenderer` + - `getFoilBufferDirect` 已移除,被 `getFoilBuffer` 取代 + - `ITEM_COUNT_BLIT_OFFSET` -> `ITEM_DECORATION_BLIT_OFFSET` + +## 实体渲染状态 + +由于 `EntityRenderState` 的加入,实体模型和渲染器几乎完全重做。`EntityRenderState` 本质上是数据对象类,只公开渲染实体所需的计算信息。例如,`Llama` 不需要知道它的背包里有什么,只需要知道它有一个箱子要在层中渲染。 + +首先,您需要选择要使用的 `EntityRenderState`,或者如果需要将额外信息传递给渲染器,则使用子类创建一个。最常见的子类状态是 `EntityRenderState` 或用于活体的 `LivingEntityRenderState`。这些字段应该是可变的,因为状态类只为每个渲染器创建一次。 + +```java +// 假设 MyEntity 继承 LivingEntity +public class MyEntityRenderState extends LivingEntityRenderState { + // 字段示例 + boolean hasExampleData; +} +``` + +然后,您创建将渲染实体的 `EntityModel`。`EntityModel` 有一个泛型,接受 `EntityRenderState`,并在其父类中接受 `ModelPart` 根,以及可选的 `RenderType` 工厂。默认情况下没有需要实现的方法;但是,如果您需要设置任何类型的模型运动,您需要覆盖 `setupAnim`,它使用渲染状态修改 `ModelPart` 的可变字段。如果您的模型没有任何动画,则可以使用 `Model$Simple` 实现。它不需要实现任何东西。 + +```java +public class MyEntityModel extends EntityModel { + + public MyEntityModel(ModelPart root) { + super(root); + // ... + } + + @Override + public void setupAnim(MyEntityRenderState state) { + // 调用 resetPose 以及父类所做的任何其他变换 + super.setupAnim(state); + + // 在此处对模型部件执行变换 + } +} +``` + +`EntityModel` 还有三个来自 `Model` 子类的 `final` 方法:`root`,获取根 `ModelPart`;`allParts`,返回所有 `ModelPart` 的扁平列表;以及 `resetPose`,将 `ModelPart` 恢复到其默认状态。 + +`LayerDefinition`、`MeshDefinition`、`PartDefinition` 和 `CubeDeformation` 在 `LayerDefinitions` 中 `ModelLayerLocation` -> `LayerDefinition` 映射的实现和构造保持不变。 + +模型变换呢?例如,实体的幼年版本,或者模型完全切换的情况?在这些情况下,为每个模型注册一个单独的层定义。例如,Llama 将有一个用于主 Llama 模型的模型层、幼年模型层、成年和幼年装饰层,以及最后用于唾沫的层。由于模型通常彼此相似,只有轻微的变换,`LayerDefinition` 添加了一个新方法来接受 `MeshTransformer`。`MeshTransformer` 基本上是 `MeshDefinition` 上的一元运算符。对于幼年模型,提供了一个 `BabyModelTransform` 网格变换器,可以通过 `LayerDefinition#apply` 应用。 + +```java +public class MyEntityModel extends EntityModel { + public static final MeshTransformer BABY_TRANSFORMS = ...; + + public static LayerDefinition create() { + // ... + } +} + +// 在注册模型层的地方 +ModelLayerLocation MY_ENTITY = layers.register("examplemod:my_entity"); +ModelLayerLocation MY_ENTITY_BABY = layers.register("examplemod:my_entity_baby"); + +// 在注册层定义的地方 +defns.register(MY_ENTITY, MyEntityModel.create()); +defns.register(MY_ENTITY_BABY, MyEntityModel.create().apply(MyEntityModel.BABY_TRANSFORMS)); +``` + +但是模型如何知道使用哪个渲染状态?这就是 `EntityRenderer` 的作用。`EntityRenderer` 有两个泛型:`Entity` 的类型和 `EntityRenderState` 的类型。`EntityRenderer` 接受一个 `Context` 对象,与之前类似。此外,需要实现 `getTextureLocation`,不过这次它接受渲染状态而不是实体。需要实现/覆盖的新方法是 `createRenderState` 和 `extractRenderState`。`createRenderState` 构造默认渲染状态对象。而 `extractRenderState` 则填充当前正在渲染的实体的渲染状态。如果您没有使用现有的渲染状态类,则需要覆盖 `extractRenderState`。 + +当然,还有 `EntityRenderer` 的子类。首先,有 `LivingEntityRenderer`。它有一个额外的泛型,即正在渲染的 `EntityModel`,并在构造函数中接受该值以及阴影半径。这个渲染器还接受 `RenderLayer`,如果您通过渲染状态访问之前的参数,这些层基本保持不变。然后是 `MobRenderer`,所有实体都扩展它。最后是 `AgeableMobRenderer`,它接受两个模型——成年和幼年——并根据 `LivingEntityRenderState#isBaby` 决定渲染哪个。如果实体有幼年形态,应该使用 `AgeableMobRenderer` 配合 `BabyModelTransform`。否则,您很可能使用 `MobRenderer` 或 `EntityRenderer`。 + +```java +public class MyEntityRenderer extends AgeableMobRenderer { + + public MyEntityRenderer(EntityRendererProvider.Context ctx) { + super( + ctx, + new MyEntityModel(ctx.bakeLayer(MY_ENTITY)), // 成年模型 + new MyEntityModel(ctx.bakeLayer(MY_ENTITY_BABY)), // 幼年模型 + 0.7f // 阴影半径 + ); + + // ... + } + + @Override + public ResourceLocation getTextureLocation(MyEntityRenderState state) { + // 在此处返回实体纹理 + } + + @Override + public MyEntityRenderState createRenderState() { + // 构造可重用的状态 + return new MyEntityRenderState(); + } + + @Override + public void extractRenderState(MyEntity entity, MyEntityRenderState state, float partialTick) { + // 设置活体实体和实体渲染状态信息 + super.extractRenderState(entity, state, partialTick); + // 设置我们自己的变量 + state.hasExampleData = entity.hasExampleData(); + } +} + +// 在注册实体渲染器的地方 +renderers.register(MyEntityTypes.MY_ENTITY, MyEntityRenderer::new); +``` + +- `net.minecraft.client.model` + - `AbstractBoatModel` - 一个模型,假设存在 `left_paddle` 和 `right_paddle`,并根据船的划桨时间进行动画。 + - `AgeableHierarchicalModel`、`ColorableAgeableListModel`、`AgeableListModel` -> `BabyModelTransform` + - `AnimationUtils` + - `animateCrossbowCharge` 现在接受一个表示充能持续时间的 `float` 和一个表示使用刻数的 `int`,而不是 `LivingEntity` + - `swingWeaponDown` 现在接受一个 `HumanoidArm` 而不是 `Mob` + - `BabyModelTransform` - 一种网格变换器,应用模型的幼年缩放形式。 + - `BoatModel` + - `createPartsBuilder` 已移除 + - `createChildren` -> `addCommonParts`,现在是私有的 + - `createBodyModel` -> `createBoatModel`、`createChestBoatModel` + - `waterPatch` -> `createWaterPatch` + - `parts` 已移除 + - `ChestBoatModel` -> `BoatModel#createChestBoatModel` + - `ChestedHorseModel` 类已移除,现在完全存在于 `LlamaModel` 和 `DonkeyModel` 中 + - `ChestRaftModel` -> `RaftModel#createChestRaftModel` + - `ColorableHierarchicalModel` 现在存储在单独的 `EntityRenderState` 中 + - `EntityModel` + - 泛型现在接受 `EntityRenderState` + - `setupAnim` 只接受 `EntityRenderState` 泛型 + - `prepareMobModel` 已移除 + - `copyPropertiesTo` 已移除,仍然存在于 `HumanoidModel` 中 + - `HierarchicalModel` 类已移除 + - `HumanoidModel#rotLerpRad` -> `Mth#rotLerpRad` + - `ListModel` 类已移除 + - `Model` + - `renderToBuffer` 现在是 final 的 + - `root` - 返回根 `ModelPart`。 + - `getAnyDescendantWithName` - 返回根中具有指定名称的第一个后代。 + - `animate` - 给定动画的当前状态和定义,在当前时间和最大时间之间变换模型以播放动画。 + - `animateWalk` - 对模型的行走循环进行动画。 + - `applyStatic` - 将即时动画应用于指定状态。 + - `$Simple` - 构造一个没有额外动画的简单模型。 + - `ModelUtils` 类已移除 + - `ParrotModel#getState` -> `getPose`,现在是公开的 + - `PlayerModel` 不再有泛型 + - `renderEars` -> `PlayerEarsModel` + - `renderCape` -> `PlayerCapeModel` + - `getRandomModelPart` -> `getRandomBodyPart` + - `getArmPose` - 返回玩家给定其渲染状态的手臂姿势。 + - `RaftModel#createBodyModel` -> `createRaftModel` + - `WardenModel#getTendrilsLayerModelParts`、`getHeartLayerModelParts`、`getBioluminescentLayerModelParts`、`getPulsatingSpotsLayerModelParts` 现在接受 `WardenRenderState` + - `WaterPatchModel` -> `BoatModel#createWaterPatch` 和 `Model$Simple` +- `net.minecraft.client.model.geom` + - `ModelLayerLocation` 现在是一个记录 + - `ModelLayers` + - `createRaftModelName`、`createChestRaftModelName` 已移除 + - `createSignModelName` -> `createStandingSignModelName`、`createWallSignModelName` + - `createBoatModelName`、`createChestBoatModelName` 已移除 + - `ModelPart` + - `rotateBy` - 使用给定的 `Quaternionf` 旋转部件。 + - `$Cube#polygons`、`$Polygon`、`$Vertex` 现在是公开的 + - `PartPose` 现在是一个记录 + - `translated` - 平移一个姿势。 + - `withScale`、`scaled` - 缩放一个姿势。 +- `net.minecraft.client.model.geom.builders` + - `LayerDefinition#apply` - 对定义应用网格变换器并返回一个新的。 + - `MeshDefinition#transformed` - 对根姿势应用变换并返回一个新的。 + - `MeshTransformer` - 将现有的 `MeshDefinition` 转换为给定形式。 + - `PartDefinition` + - `addOrReplaceChild` 现在有一个接受 `PartDefinition` 的重载 + - `clearChild` - 从部件定义中移除子部件。 + - `getChildren` - 获取当前部件的所有子部件。 + - `transformed` - 对当前姿势应用变换并返回一个新的。 +- `net.minecraft.client.renderer.entity` + - `AbstractBoatRenderer` - 一个船渲染器,包含用于船模型和船本身任何附加内容的方法。 + - `AgeableMobRenderer` - 一个接受幼年和成年模型的生物渲染器。 + - `BoatRenderer` 现在接受一个 `ModelLayerLocation` 而不是 `boolean` + - `EntityRenderDispatcher` 现在接受一个 `MapRenderer` + - `render` 不再接受实体的 Y 旋转 + - `EntityRenderer` 现在接受 `EntityRenderState` 的泛型 + - `getRenderOffset` 只接受 `EntityRenderState` + - `getBoundingBoxForCulling` - 返回实体的边界框以确定是否剔除。 + - `affectedByCulling` - 返回实体是否可以被剔除。 + - `render` 只接受渲染状态,以及堆栈、缓冲区源和包光 + - `shouldShowName` 现在接受一个 `double` 表示相机到实体的平方距离 + - `getTextureLocation` 已移除,被移动到使用它的类中,如 `LivingEntityRenderer` + - 后续的 `getTextureLocation` 实现可能是 protected 或 private + - `renderNameTag` 现在接受渲染状态而不是实体,并移除了部分刻 `float` + - `getNameTag` - 从实体获取名称标签。 + - `getShadowRadius` 现在接受渲染状态而不是实体 + - `createRenderState` - 创建渲染状态对象。 + - `extractRenderState` - 将实体中的任何数据读取到渲染状态。 + - `EntityRendererProvider$Context` 接受 `MapRenderer` 而不是 `ItemInHandRenderer` + - `LivingRenderer` + - `isShaking` 现在接受渲染状态而不是实体 + - `setupRotations` 现在接受渲染状态而不是实体 + - `getAttackAnim`、`getBob` 现在在渲染状态中 + - `getFlipDegrees` 不再接受实体 + - `getWhiteOverlayProgress` 现在接受渲染状态而不是实体,并且不再接受实体的 Y 旋转 + - `scale` 现在接受渲染状态而不是实体,并且不再接受实体的 Y 旋转 + - `shouldShowName` 现在接受一个 `double` 表示到相机的平方距离 + - `getShadowRadius` 现在接受渲染状态而不是实体 + - `RaftRenderer` - 一个实现 `AbstractBoatRenderer` 的木筏渲染器。 + - `RenderLayerParent#getTextureLocation` 已移除 +- `net.minecraft.client.renderer.entity.layers` + - `EnergySwirlLayer#isPowered` - 返回能量是否被充能。 + - `CustomHeadLayer` 和 `#translateToHead` 接受一个 `CustomHeadLayer$Transforms` 而不是硬编码变换的缩放信息 + - `PlayerItemInHandRenderer` 接受一个 `ItemRenderer` 而不是 `ItemInHandRenderer` + - `RenderLayer` 接受 `EntityRenderState` 泛型而不是 `Entity` 泛型 + - `coloredCutoutModelCopyLayerRender` 接受一个 `EntityModel`,状态信息捆绑在渲染状态中 + - `renderColoredCutoutModel` 接受非泛型形式的渲染信息,假设为 `LivingEntityRenderState` + - `getTextureLocation` 已移除,改为直接传递到适当的位置 + - `render` 现在接受渲染状态而不是实体和参数信息 + - `SaddleLayer` 有一个接受幼年模型的构造函数。 + - `SheepFurLayer` -> `SheepWoolLayer` + - `StuckInBodyLayer` 现在接受要应用卡住对象的模型、卡住对象的纹理以及对象的放置样式 + - `numStuck` 现在接受渲染状态而不是实体 + - `renderStuckItem` 现在是私有的 + - `WardenEmissiveLayer` -> `LivingEntityEmissiveLayer`,一个更通用的实现 +- `net.minecraft.client.renderer.entity.player.PlayerRenderer` + - `renderRightHand`、`renderLeftHand` 现在接受一个 `ResourceLocation` 而不是 `AbstractClientPlayer`,以及一个 `boolean` 表示是否渲染左袖和/或右袖 + - `setupRotations` 现在接受渲染状态而不是实体和参数信息 +- `net.minecraft.world.entity` + - `AnimationState#copyFrom` - 从另一个状态复制动画状态。 + - `Entity` + - `noCulling` -> `EntityRenderer#affectedByCulling` + - `getBoundingBoxForCulling` -> `EntityRenderer#getBoundingBoxForCulling` + - `LerpingModel` 类已移除 + - `PowerableMob` 类已移除 + +### 模型烘焙 + +`UnbakedModel` 现在有一个不同的方法来解析任何依赖项。不再获取依赖项并解析父级,而是通过一个称为 `resolveDependencies` 的单一方法完成。该方法接受 `Resolver`。`Resolver` 负责获取 `ResourceLocation` 的 `UnbakedModel`。 + +```java +// 对于某个 UnbakedModel 实例 +public class MyUnbakedModel implements UnbakedModel { + + @Nullable + protected ResourceLocation parentLocation; + @Nullable + protected UnbakedModel parent; + private final List overrides; + + // ... + + @Override + public void resolveDependencies(UnbakedModel.Resolver resolver) { + // 获取父模型以进行委托解析 + if (this.parentLocation != null) { + this.parent = resolver.resolve(this.parentLocation); + } + } +} +``` + +- `net.minecraft.client.renderer.block` + - `BlockModel#getDependencies`、`resolveParents` -> `resolveDependencies` + - `BlockModelDefintion` 现在接受一个 `MultiPart$Definition`,不存在接受 `List` 的构造函数 + - `fromStream`、`fromJsonElement` 不再接受 `$Context` + - `getVariants` 已移除 + - `isMultiPart` 已移除 + - `instantiate` -> `MultiPart$Definition#instantiate` + - `MultiVariant` 现在是一个记录 + - `UnbakedBlockStateModel` - 一个表示方块状态模型的接口,包含一个将具有相同模型的状态分组在一起的单一方法。 + - `VariantSelector` - 用于从模型描述符构建状态定义的工具。 +- `net.minecraft.client.renderer.block.model` + - `BlockModel` + - `MISSING_MATERIAL` - 缺失方块纹理的材质。 + - `bake` 不再接受 `ModelBaker` 和 `BlockModel` + - `$LoopException` 类已移除 +- `net.minecraft.client.renderer.block.model.multipart.MultiPart` 现在实现 `UnbakedBlockStateModel` + - `getSelectors` -> `$Definition#selectors` + - `getMultiVariants` ->` $Definition#getMultiVariants` +- `net.minecraft.client.resources.model` + - `BakedModel#getOverrides` -> `overrides`,方法默认为空覆盖 + - `BlockStateModelLoader` 只接受缺失的未烘焙模型 + - `loadAllBlockStates` 已移除 + - `definitionLocationToBlockMapper` - 从给定的资源位置获取状态定义 + - `loadBlockStateDefinitions` -> `loadBlockStateDefinitionStack` + - `getModelGroups` -> `ModelGroupCollector` + - `$LoadedJson` -> `$LoadedBlockModelDefinition` + - `$LoadedModel` 现在是公开的 + - `$LoadedModels` - 一个将模型位置映射到已加载模型的记录。 + - `BuiltInModel` 不再接受 `ItemOverrides` + - `DelegateBakedModel` - 一个将所有逻辑委托给提供的 `BakedModel` 的工具实现 + - `Material#buffer` 接受另一个 `boolean`,用于处理是否应用闪光效果 + - `MissingBlockModel` - 方块的缺失模型。 + - `ModelBaker#getModel` 已移除,`ModelBakery$ModelBakerImpl` 中的实现是私有的 + - `ModelBakery` 只接受顶层模型、所有未烘焙模型和缺失模型 + - `BUILTIN_SLASH` -> `SpecialModels#builtinModelId` + - `BUILTIN_SLASH_GENERATED` -> `SpecialModels#BUILTIN_GENERATED` + - `BUILTIN_BLOCK_ENTITY` -> `SpecialModels#BUILTIN_BLOCK_ENTITY` + - `MISSING_MODEL_LOCATION` -> `MissingBlockModel#LOCATION` + - `MISSING_MODEL_VARIANT` -> `MissingBlockModel#VARIANT` + - `GENERATION_MARKER` -> `SpecialModels#GENERATED_MARKER` + - `BLOCK_ENTITY_MARKER` -> `SpecialModels#BLOCK_ENTITY_MARKER` + - `getModelGroups` -> `ModelGroupCollector` + - `ModelDiscovery` - 方块和物品模型的加载器,例如在读取时如何解析它们。 + - `ModelGroupCollector` - 一个方块状态收集器,用于将状态映射到它们关联的方块模型。 + - `ModelResourceLocation#vanilla` 已移除 + - `MultiPartBakedModel` 字段现在从选择器中的第一个模型获取,并且是私有的 + - `$Builder` 类已移除,被 `$Selector` 取代 + - `SimpleBakedModel`、`SimpleBakedModel$Builder` 不再接受 `ItemOverrides` + - `SpecialModels` - 内置模型的工具。 + - `UnbakedModel` + - `getDependencies`、`resolveParents` -> `resolveDependencies` + - `bake` 不再可为 null + - `$Resolver` - 确定在顶层或覆盖时如何加载未烘焙模型。 + - `WeightedBakedModel` 现在接受一个 `SimpleWeightedRandomList` 而不是 `WeightedEntry` 的列表 + +## 装备与物品、模型等等 + +装备和物品经历了重大改革,其中大部分分散在整个文档中。这是一些核心变更,虽然它们很重要,但由于易于更改,不值得详细解释。 + +### 物品名称和模型 + +物品名称和模型现在直接通过属性中的 `ITEM_NAME` 和 `ITEM_MODEL` 数据组件设置。默认情况下,这将使用与以前相同的名称和模型位置,但可以通过 `Item$Properties#overrideDescription` 和 `#overrideModel` 设置。`overrideDescription` 接受要使用的翻译键。还有 `useBlockDescriptionPrefix` 和 `useItemDescriptionPrefix` 分别将其更改为默认的方块和物品翻译键。`overrideModel` 接受模型 JSON 的相对 `ResourceLocation`。例如,值 `examplemod:example_item` 将映射到 `examplemod:example_item#inventory` 的 `ModelResourceLocation`。这旨在链接到 `assets/examplemod/models/item/example_item.json` 的模型 JSON。 + +> 物品模型有一个小特性。如果模组制作者决定在 `inventory` 变体下在该位置加载特殊模型,则相同的键也可以指向 `assets/examplemod/models/example_item.json`。因此,建议避免在根 `models` 和 `models/item` 子目录中使用同名模型名称。 + +### 可附魔、可修复物品 + +附魔值和修复物品检查正在被数据组件取代:分别是 `DataComponents#ENCHANTABLE` 和 `DataComponents#REPAIRABLE`。这些可以通过 `Item$Properties#enchantable` 和 `#repairable` 设置。因此,`Item#getEnchantmentValue` 和 `isValidRepairItem` 已移除。 + +### 鞘翅 -> 滑翔翼 + +任何物品如果装备了 `DataComponents#GLIDER` 值,都可以像鞘翅一样行动。这基本上作为一个标志,表示该物品可用于滑翔。这仅在该物品也有 `DataComponents#EQUIPPABLE` 条目时有效。 + +```java +new Item( + new Item.Properties() + .component(DataComponents.GLIDER, Unit.INSTANCE) // 设置为滑翔翼 + .component(DataComponents.EQUIPPABLE, /*...*/) // 确定要检查的槽位以查看是否可以使用 +); +``` + +### 工具,通过工具材料 + +物品中的 `Tier` 已被 `ToolMaterial` 取代,后者更好地处理工具和剑的创建,而无需手动实现每个方法。`ToolMaterial` 接受与 `Tier` 相同的参数,只是作为单个构造函数的参数,而不是作为可实现的方法。然后,将 `ToolMaterial` 传递给 `DiggerItem` 子类型,以及两个表示攻击伤害和攻击速度的浮点数。内部调用 `ToolMaterial#apply*Properties`,它将 `ToolMaterial` 信息应用到 `DataComponents#TOOL` 以及给定 `float` 中的属性。 + +```java +// 某个工具材料 +public static final ToolMaterial WOOD = new ToolMaterial( + BlockTags.INCORRECT_FOR_WOODEN_TOOL, // Tier#getIncorrectBlocksForDrops + 59, // Tier#getUses + 2.0F, // Tier#getSpeed + 0.0F, // Tier#getAttackDamageBonus + 15, // Tier#getEnchantmentValue + ItemTags.WOODEN_TOOL_MATERIALS // Tier#getRepairIngredient +); + +// 构造 DiggerItem 子类型时 +new PickaxeItem( + WOOD, // 工具材料 + 1.0f, // 攻击伤害 + -2.8f, // 攻击速度 + new Item.Properties() +) +``` + +## 盔甲材料、装备和模型(纹理) + +到目前为止,这是除消耗品之外最大的物品变更。`ArmorMaterial` 实际上已被废弃,因为几乎所有逻辑都在数据组件中处理,并附加到某些资源包 JSON 以加载关联的纹理。乍一看令人烦恼地复杂,但一旦熟悉了流程,就会相当直观。 + +### `ArmorMaterial` + +`ArmorMaterial` 本质上是一个记录,将属性列表转换为它们在数据组件上的适当位置,而不是一个注册表对象。这是通过将物品属性和一个额外的设置传递给 `#humanoidProperties` 或 `#animalProperties` 来完成的。这些设置应该很熟悉,因为它们与以前版本相比没有变化,唯一的区别是它们现在指定了一个“模型 id”,我们将在下面介绍。盔甲材料与 `ArmorType` 一起使用:一个枚举,定义了盔甲放置的装备槽位、每种盔甲类型的单位耐久度以及名称(仅用于构造属性修饰符 id)。 + +```java +ArmorMaterial exampleArmorMaterial = new ArmorMaterial( + 15, // 与盔甲类型相乘的耐久度标量 + // 一个 ArmorType -> 要应用到实体 ARMOR 属性的半盔甲条数的映射 + // 应该为所有 ArmorType 设置 + Util.make(new EnumMap<>(ArmorType.class), map -> { + map.put(ArmorType.BOOTS, 2); + map.put(ArmorType.LEGGINGS, 5); + map.put(ArmorType.CHESTPLATE, 6); + map.put(ArmorType.HELMET, 2); + map.put(ArmorType.BODY, 5); + }), + 25, // 盔甲的附魔值 + SoundEvents.ARMOR_EQUIP_IRON, // 包装的声音事件持有者,表示装备物品时应发出的声音 + 0f, // ARMOR_TOUGHNESS 属性值 + 2f, // KNOCKBACK_RESISTANCE 属性值, + ItemTags.REPAIRS_DIAMOND_ARMOR, // 表示可以修复此盔甲的物品的物品标签 + // EquipmentModel JSON 的相对位置 + // 指向 assets/examplemod/models/equipment/example_armor_material.json + ResourceLocation.fromNamespaceAndPath("examplemod", "example_armor_material") +) +``` + +使用 `ArmorMaterial`,可以将其应用于物品属性,通过调用 `humanoidProperties` 将盔甲应用到特定的 `ArmorType`;或者调用 `animalProperties` 将盔甲应用到 `BODY` 并只允许特定实体穿戴它们。 + +这是否意味着 `ArmorItem` 和 `AnimalArmorItem` 实际上毫无意义?对于 `AnimalArmorItem`,这可以争论。`AnimalArmorItem` 所做的只是有一个 `$BodyType` 参数,这意味着盔甲只能应用于马或狼,并指定物品破碎声音。另一方面,`ArmorItem` 只有一个特定用例:确定物品是否可以脱下或交换。这隐式检查当前穿戴的盔甲物品,看它是否不能脱下(通过 `PREVENT_ARMOR_CHANGE` 附魔),并计算替换盔甲材料上的属性,以便任何热交换只会提高穿戴者的盔甲属性值。 + +让我们深入一层。 + +### 数据组件 + +`ArmorMaterial` 指定了要应用于物品的八个数据组件: + +- `MAX_DAMAGE` - 设置为物品的最大耐久度乘以 `ArmorType` 单位耐久度 +- `MAX_STACK_SIZE` - 设置为 1 +- `DAMAGE` - 设置为 0 +- `ATTRIBUTE_MODIFIERS` - 设置 `ARMOR` 和 `ARMOR_TOUGHNESS` 属性,以及当大于 0 时的 `KNOCKBACK_RESISTANCE` +- `ENCHANTABLE` - 设置为附魔值(调用 `animalProperties` 时不设置) +- `REPAIRABLE` - 设置为表示修复原料的标签键的 `HolderSet` +- `EQUIPPABLE` - 设置槽位、装备声音、模型 id、哪些实体可以穿戴该物品,以及是否可从发射器发射 + +除 `EQUIPPABLE` 之外的所有内容都已在上文解释或从先前版本就已存在,因此本入门文档从现在起只关注 `EQUIPPABLE`。 + +### Equippable + +`Equippable` 曾经是一个接口,现在是一个数据组件,包含实体如何装备此物品以及装备是否应被渲染。因此,一个物品只能装备到一个槽位。这可以通过 `Equippable` 构造函数或通过 `Equippable#builder` 构建器完成。 + +```java +new Item( + new Item.Properties() + .component(DataComponents.EQUIPPABLE, new Equippable( + EquipmentSlot.HEAD, // 物品可以装备到的槽位 + SoundEvents.ARMOR_EQUIP_IRON, // 装备物品时播放的声音 + // EquipmentModel JSON 的相对位置 + // 指向 assets/examplemod/models/equipment/example_armor_material.json + // 当设置为空 optional 时,物品不会尝试渲染为装备 + Optional.of(ResourceLocation.fromNamespaceAndPath("examplemod", "example_armor_material")), + // 穿戴时覆盖在玩家屏幕上的纹理的相对位置 + // 指向 assets/examplemod/textures/example_overlay.png + // 当设置为空 optional 时,不在玩家屏幕上渲染 + Optional.of(ResourceLocation.withDefaultNamespace("examplemod", "example_overlay")), + // 可以装备此物品的实体的 HolderSet(直接或标签) + // 当设置为空 optional 时,任何实体都可以装备此物品 + Optional.of(HolderSet.direct(EntityType::builtInRegistryHolder, EntityType.ZOMBIE)), + // 物品是否可以从发射器中发射时装备 + true, + // 在快速装备期间物品是否可以从玩家身上交换下来 + false, + // 物品在受到攻击时是否应该损坏(通常用于装备) + // 也必须是可损坏的物品 + false + )) +); +``` + +### 装备模型? + +如上所述,`Equippable` 物品以及 `ArmorMaterial` 委托可以指定一个模型 id 来确定装备应如何渲染。但是,这个 id 链接到什么?它指向一个序列化为 JSON 的 `EquipmentModel`,位于资源包的 `models/equipment` 中。此 JSON 定义了要渲染的可装备物品的层和纹理。这不指定模型,因此这个记录有些用词不当。最好将其视为应用于传入模型的装备纹理。 + +`EquipmentModel` 作为先前 `ArmorMaterial$Layer` 的一个更具功能通用性的版本,后者已被移除。每个 `EquipmentModel` 实际上是一个 `$LayerType` 到要渲染的 `$Layer` 列表的映射。 + +`$LayerType` 是一个枚举,表示要渲染装备模型的层。虽然这些是非特定的,但它们由特定的实体渲染器通过层渲染器实现和读取。例如,`HUMANOID` 由 `HumanoidArmorLayer` 用于渲染头部、胸部和脚部;因此,任何对 `HUMANOID` 的使用都将使用该系统进行渲染。另一个例子是 `WOLF_BODY` 由 `WolfArmorLayer` 用于渲染身体盔甲。因此,如果使用现有的层类型(除非您的模组加载器支持枚举扩展,否则这是唯一的情况),请确保它们与现有的渲染器兼容。 + +`$Layer` 列表指定了在传入模型上渲染时使用的纹理和可染色选项。第一个参数指定纹理位置,相对于 `textures/entity/equipment`。第二个参数指定一个可选项,表示纹理是否可以被染色(通过 `ItemTags#DYEABLE` 与 `DYED_COLOR` 数据组件一起存储)。当指定时,可以指定一个可选颜色用于物品未染色时。如果为空,则物品未染色时盔甲将不可见。最后一个参数指示是否应改用提供给渲染器的纹理,例如为玩家渲染自定义鞘翅纹理时。 + +```json5 +// 在 assets/examplemod/models/equipment/example_armor_material.json +{ + // 层映射 + "layers": { + // 要应用的 EquipmentModel$LayerType 的序列化名称 + "humanoid": [ + // 按列表中提供的顺序渲染的层列表 + { + // 层的相对纹理 + // 指向 assets/examplemod/textures/entity/equipment/example.png + "texture": "examplemod:example", + // 当指定时,允许纹理被 DYED_COLOR 数据组件中的颜色染色 + // 否则,不能染色 + "dyeable": { + // 一个 RGB 值(总是不透明的颜色) + // 0x7683DE 作为十进制 + // 当未指定时,设置为 0(意味着透明或不可见) + "color_when_undyed": 7767006 + }, + // 为 true 时,改用传递给层渲染器的纹理 + "use_player_texture": true + } + // ... + ] + // ... + } +} +``` + +```java +EquipmentModel.builder() + .addLayers(EquipmentModel.LayerType.HUMANOID, new EquipmentModel.Layer( + // 层的相对纹理 + // 指向 assets/examplemod/textures/entity/equipment/example.png + ResourceLocation.fromNamespaceAndPath("examplemod", "example"), + // 当指定时,允许纹理被 DYED_COLOR 数据组件中的颜色染色 + // 否则,不能染色 + Optional.of(new EquipmentModel.Dyeable( + // 一个 RGB 值(总是不透明的颜色) + // 当未指定时,设置为 0(意味着透明或不可见) + Optional.of(0x7683DE) + )), + // 为 true 时,改用传递给层渲染器的纹理 + true + )/*, ... */) + .build(); +``` + +然后通过在 `EntityRenderer` 或 `RenderLayer` 的渲染函数中调用 `EquipmentLayerRenderer#renderLayers` 来渲染装备模型。`EquipementLayerRenderer` 作为渲染上下文的一部分通过 `EntityRendererProvider$Context#getEquipmentRenderer` 传入。 + +```java +// 在某个渲染方法中,其中 EquipmentLayerRenderer equipmentLayerRenderer 是一个字段 +this.equipmentLayerRenderer.renderLayers( + // 要渲染的层类型 + EquipmentModel.LayerType.HUMANOID, + // 表示 EquipmentModel JSON 的模型 id + // 这将在 `EQUIPPABLE` 数据组件中通过 `model` 设置 + ResourceLocation.fromNamespaceAndPath("examplemod", "example_armor_material"), + // 要将纹理应用到的模型 + // 这些通常是与实体模型分开的模型 + // 并且是链接到 LayerDefinition 的单独 ModelLayer + model, + // 表示作为模型渲染的物品的物品堆栈 + // 这仅用于获取可染色、闪光和盔甲纹饰信息 + stack, + // 用于在正确位置渲染模型的堆栈 + poseStack, + // 用于获取渲染类型顶点消费者的缓冲区源 + bufferSource, + // 打包的光照纹理 + lighting, + // 当某个层的 use_player_texture 为 true 时,如果不为 null,则渲染的纹理的绝对路径 + ResourceLocation.fromNamespaceAndPath("examplemod", "textures/other_texture.png"); +) +``` + +### 物品的技术性变更 + +- `net.minecraft.client.Minecraft#getEquipmentModels` - 获取包含当前装备模型纹理的 `EquipmentModelSet`。 +- `net.minecraft.client.gui.GuiGraphics#renderTooltip`、`renderComponentTooltip` 现在有一个参数,用于接受工具提示背景和框架纹理的相对目录,如果为 `null` 则使用默认值 +- `net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil#renderTooltipBackground` 现在有一个参数,用于接受工具提示背景和框架纹理的相对目录,如果为 `null` 则使用默认值 +- `net.minecraft.client.renderer.block.model` + - `ItemOverrides` -> `BakedOverrides` + - 构造函数不再接受父级 `BlockModel` + - `resolve` -> `findOverride`,不再接受后备模型 + - `ItemOverride`、`ItemOverride$Predicate` 现在是一个记录 + - `getPredicates` 已移除,使用 `predicates` + - `getModel` -> `model` +- `net.minecraft.client.renderer.entity` + - `EntityRenderDispatcher` 现在接受 `EquipmentModelSet` + - `EntityRendererProvider$Context` + - `getEquipmentModels` - 获取当前装备纹理。 + - `getEquipmentRenderer` - 获取装备的渲染器。 + - `ItemRenderer` 不再接受 `Minecraft` 实例和 `TextureManager` + - `TRIDENT_MODEL`、`SPYGLASS_MODEL` 现在是公开的 + - `TRIDENT_IN_HAND_MODEL`、`SPYGLASS_IN_HAND_MODEL` 已移除 + - `getItemModelShaper` 已移除 + - `renderBundleWithSelectedItem` -> `renderBundleItem`,不是一对一 +- `net.minecraft.client.renderer.entity.layers` + - `CapeLayer` 现在接受 `EquipmentModelSet` + - `ElytraLayer` -> `WingsLayer` + - 构造函数现在接受 `EquipmentLayerRenderer` + - `EquipmentLayerRenderer` - 在提供的模型上渲染装备层的渲染器。 + - `HorseArmorLayer` 现在接受 `EquipmentLayerRenderer` + - `HumanoidArmorLayer` 现在接受 `EquipmentLayerRenderer` 而不是 `ModelManager` + - `shouldRender` - 返回可装备物品是否应在给定槽位中渲染。 + - `LlamaDecorLayer` 现在接受 `EquipmentLayerRenderer` + - `WolfArmorLayer` 现在接受 `EquipmentLayerRenderer` +- `net.minecraft.client.renderer.entity.player.PlayerRenderer#getArmPose` 现在是私有的,被一个只接受 `HumanoidArm` 和 `PlayerRenderState` 的公共方法取代 +- `net.minecraft.client.resources.model` + - `EquipmentModelSet` - 从 `models/equipment` 加载 `EquipmentModel` 的资源监听器。 + - `ItemModel` - 物品的模型。 +- `net.minecraft.core.component.DataComponents` + - `ITEM_MODEL` - 返回物品的模型。`item/` 被剥离,意味着 `minecraft:apple` 指向 `minecraft/textures/models/item/apple.json`。 + - `EQUIPPABLE` - 表示物品在给定槽位中是可装备的。还包含要渲染的装备模型。 + - `GLIDER` - 表示物品可用于在空中滑翔。必须与 `EQUIPPABLE` 一起使用。 + - `TOOLTIP_STYLE` - 确定表示工具提示应如何渲染的相对位置 +- `net.minecraft.core.dispenser.EquipmentDispenseItemBehavior` - 处理如何从发射器中发射装备。 +- `net.minecraft.core.registries.BuiltInRegistries#`、`Registries#ARMOR_MATERIAL` 不再是一个注册表,完全通过数据组件处理 +- `net.minecraft.world.entity` + - `EquipmentSlot#getFilterFlag` -> `getId` + - 还有一个方法 `getFilterBit` 用于将 ID 转换为位掩码 + - `LivingEntity` + - `canContinueToGlide` -> `canGlide`,不再接受 `ItemStack` + - `canTakeItem` 被 `DataComponents#EQUIPPABLE` 取代 + - `canEquipWithDispenser` - 返回堆栈在从发射器发射时是否可以装备。 + - `canDispenserEquipIntoSlot` - 一个实体覆盖,指定发射器是否可以将装备放入给定槽位。 + - `isEquippableInSlot` - 返回堆栈是否可以在给定槽位中装备。 + - `canGlideUsing` - 实体是否可以使用提供的槽位中的堆栈滑翔。 + - `Mob` + - `canReplaceCurrentItem` 现在接受 `EquipmentSlot` + - `isBodyArmorItem` 被 `DataComponents#EQUIPPABLE` 取代 +- `net.minecraft.world.entity.animal.horse` + - `Horse#isBodyArmorItem` 被 `DataComponents#EQUIPPABLE` 取代 + - `Llama#isBodyArmorItem`、`getSwag` 被 `DataComponents#EQUIPPABLE` 取代 +- `net.minecraft.world.item` + - `AnimalArmorItem` 不再继承 `ArmorItem` + - 构造函数不再接受一个表示覆盖纹理的布尔值,因为现在这是 `EquipmentModel` 的一部分 + - 构造函数可以接受一个可选的 `Holder` 作为装备声音 + - 构造函数可以接受一个 `boolean` 表示如果实体受伤,盔甲是否应该损坏 + - `$BodyType` 现在接受允许穿戴盔甲的实体,而不是指向纹理的路径工厂 + - `ArmorItem` 不再是可装备的 + - 基本上作为一个物品类,其剩余的唯一用途是在附魔时防止盔甲更换并获取关联的属性 + - `$Type` -> `ArmorType` + - `ArmorMaterial` -> `.equipment.ArmorMaterial` + - 基本上是一个虚拟记录,用于轻松处理应用关联的数据组件(`MAX_DAMAGE`、`ATTRIBUTE_MODIFIERS`、`ENCHANTABLE`、`EQUIPPABLE`、`REPAIRABLE`) + - `ArmorMaterials` -> `.equipment.ArmorMaterials` + - `BookItem`、`EnchantedBookItem` -> `DataComponents#WRITTEN_BOOK_CONTENT` + - `BundleItem` 现在接受一个 `ResourceLocation` 作为模型,而不是仅仅字符串 + - `$Mutable#setSelectedItem` -> `toggleSelectedItem` + - `ComplexItem` 类已移除 + - `ElytraItem` 类已移除,现在只是带有 `DataComponents#GLIDER` 的物品 + - `Equippable` -> `.equipment.Equippable`,现在是一个定义物品如何装备的记录 + - `FoodOnAStackItem` 参数顺序已交换 + - `InstrumentItem` 参数顺序已交换 + - `Item` + - `descriptionId` 现在是 protected + - `getDescription` -> `getName` + - `getOrCreateDescriptionId` 已移除 + - `getDescriptionId(ItemStack)` -> `DataComponents#ITEM_NAME` + - `isEnchantable`、`getEnchantmentValue` 已移除 + - `isValidRepairItem` 已移除 + - `getDefaultAttributeModifiers` 已移除 + - `getDamageSource` - 返回此物品对 `LivingEntity` 造成的伤害来源 + - `isComplex` 已移除 + - `$Properties` + - `equippable` - 设置一个可装备组件,定义物品如何装备 + - `equippableUnswappable` - 设置一个无法通过按键快捷方式交换的可装备组件。 + - `overrideDescription` - 设置物品的翻译键。 + - `overrideModel` - 设置模型资源位置。 + - `getCraftingRemainingItem`、`hasCraftingRemainingItem` -> `getCraftingRemainder` + - `ItemNameBlockItem` 类已移除,只是一个带有 `useItemDescriptionPrefix` 属性的普通 `Item` + - `ItemStack` + - `ITEM_NON_AIR_CODEC` -> `Item#CODEC` + - `isValidRepairItem` - 返回堆栈是否可以由此堆栈修复。 + - `nextDamageWillBreak` - 检查下一次受到的伤害是否会破坏物品。 + - `getDescriptionId` -> `getItemName`,不是一对一,因为现在它返回完整的组件 + - `ShieldItem` 不再实现 `Equippable`,通过 `DataComponents#EQUIPPABLE` 传入 + - `SignItem` 参数顺序已交换 + - `SmithingTemplateItem` 参数顺序已交换,移除了 `FeatureFlag` + - `StandingAndWallBlockItem` 参数顺序已交换 + - `AxeItem` 现在接受两个表示攻击伤害和攻击速度的浮点数 + - `DiggerItem` 现在接受两个表示攻击伤害和攻击速度的浮点数 + - `createAttributes` -> `ToolMaterial#applyToolProperties` + - `HoeItem` 现在接受两个表示攻击伤害和攻击速度的浮点数 + - `PickaxeItem` 现在接受两个表示攻击伤害和攻击速度的浮点数 + - `ShovelItem` 现在接受两个表示攻击伤害和攻击速度的浮点数 + - `SwordItem` 现在接受两个表示攻击伤害和攻击速度的浮点数 + - `createAttributes` -> `ToolMaterial#applySwordProperties` + - `Tier` -> `ToolMaterial` + - `TieredItem` 类已移除 + - `Tiers` 常量存储在 `ToolMaterial` 上 +- `net.minecraft.world.item.alchemy.Potion` 名称现在是必需的 + - `getName` -> `name`,不是一对一,因为这直接存储在药水上,没有任何其他处理 +- `net.minecraft.world.item.armortrim.*` -> `.equipment.trim.*` +- `net.minecraft.world.item.component` + - `Tool` 中返回 `Tool$Rule` 的方法现在只接受 `HolderSet` 的方块,而不是列表或标签键 + - `DamageResistant` - 一个组件,包含物品作为实体或穿戴时对其免疫的伤害类型标签 +- `net.minecraft.world.item.enchantment` + - `Enchantable` - 物品附魔值的数据组件对象。 + - `Repairable` - 可以修复此物品的物品的数据组件对象。 +- `net.minecraft.world.level.block` + - `AbstractSkullBlock` 不再实现 `Equippable` + - `EquipableCarvedPumpkinBlock` 类已移除,被 `DataComponents#EQUIPPABLE` 取代 + - `WoolCarpetBlock` 不再实现 `Equippable` + +## 交互结果 + +`InteractionResult` 已被完全修改,将所有内容包含在一系列密封的实现中。新的 `InteractionResult` 实现结合了 `InteractionResultHolder` 和 `ItemInteractionResult`,因此所有用途也已被替换。 + +`InteractionResult` 现在是一个接口,根据结果类型有四个实现。首先是 `$Pass`,表示交互检查应传递给调用堆栈中的下一个对象。`$Fail`,当用于物品和方块时,阻止调用堆栈中的任何进一步执行。对于实体,这将被忽略。最后,`$TryEmptyHandInteraction` 告诉调用堆栈尝试用空手应用点击,专门用于物品-方块交互。 + +还有 `$Success`,表示交互成功并且可以被消费。成功指定了两条信息:`$SwingSource`,表示挥动来源(`CLIENT` 或 `SERVER`)或 `NONE`(如果未指定),以及 `$ItemContext`,处理手中物品是否有交互,以及物品被转换成了什么。 + +这些对象都不应直接初始化。这些实现通过 `InteractionResult` 接口上的六个常量处理: + +- `SUCCESS` - 一个在客户端挥动手的 `$Success` 对象。 +- `SUCCESS_SERVER` - 一个在服务器端挥动手的 `$Success` 对象。 +- `CONSUME` - 一个不挥动手的 `$Success` 对象。 +- `FAIL` - 一个 `$Fail` 对象。 +- `PASS` - 一个 `$Pass` 对象。 +- `TRY_WITH_EMPTY_HAND` - 一个 `$TryEmptyHandInteraction` 对象。 + +```java +// 对于某个返回 InteractionResult 的方法 +return InteractionResult.PASS; +``` + +对于成功对象,如果物品交互应转换持有的堆栈,则调用 `$Success#heldItemTransformedTo`,或者如果没有物品用于交互,则调用 `$Success#withoutItem`。 + +```java +// 对于某个返回 InteractionResult 的方法 +return InteractionResult.SUCCESS.heldItemTransformedTo(new ItemStack(Items.APPLE)); + +// 或者 +return InteractionResult.SUCCESS.withoutItem(); +``` + +- `net.minecraft.core.cauldron.CauldronInteraction` + - `interact` 现在返回一个 `InteractionResult` + - `fillBucket`、`emptyBucket` 现在返回一个 `InteractionResult` +- `net.minecraft.world` + - `InteractionResultHolder`、`ItemInteractionResult` -> `InteractionResult` +- `net.minecraft.world.item` + - `Equipable#swapWithEquipmentSlot` 现在返回一个 `InteractionResult` + - `Item#use`、`ItemStack#use` 现在返回一个 `InteractionResult` + - `ItemUtils#startUsingInstantly` 现在返回一个 `InteractionResult` + - `JukeboxPlayable#tryInsertIntoJukebox` 现在返回一个 `InteractionResult` +- `net.minecraft.world.level.block.state.BlockBehaviour#useItemOn`、`$BlockStateBase#useItemOn` 现在返回一个 `InteractionResult` + +## 乐器 数据包版 + +`Instrument`(不是 `NoteBlockInstrument`)现在是一个数据包注册表,意味着它们必须在 JSON 中定义或通过数据生成生成。 + +```json5 +// 在 data/examplemod/instrument/example_instrument.json +{ + // 声音事件的注册表名称 + "sound_event": "minecraft:entity.arrow.hit", + // 使用乐器的秒数 + "use_duration": 7.0, + // 方块范围,每个方块为 16 个单位 + "range": 256.0, + // 乐器的描述 + "description": { + "translate": "instrument.examplemod.example_instrument" + }, +} +``` + +```java +// 对于某个 RegistrySetBuilder builder +builder.add(Registries.INSTRUMENT, bootstrap -> { + bootstrap.register( + ResourceKey.create(Registries.INSTRUMENT, ResourceLocation.fromNamespaceAndPath("examplemod", "example_instrument")), + new Instrument( + BuiltInRegistries.SOUND_EVENT.wrapAsHolder(SoundEvents.ARROW_HIT), + 7f, + 256f, + Component.translatable(Util.makeDescriptionId("instrument", ResourceLocation.fromNamespaceAndPath("examplemod", "example_instrument"))) + ) + ) +}); +``` + +- `net.minecraft.world.item` + - `Instrument` 接受一个 `float` 作为使用持续时间和一个 `Component` 描述。 + - `InstrumentItem#setRandom` 已移除 + +## 试炼刷怪笼配置,现在采用数据包形式 + +`TrialSpawnConfig` 现在是一个数据包注册表,意味着它们必须在 JSON 中定义或通过数据生成生成。 + +```json5 +// 在 data/examplemod/trial_spawner/example_config.json +{ + // 实体可以从试炼刷怪笼方块生成的区域范围 + "spawn_range": 2, + // 可以生成的生物总数 + "total_mobs": 10.0, + // 一次可以生成的生物数量 + "simultaneous_mobs": 4.0, + // 试炼中每个玩家增加的生物数量 + "total_mobs_added_per_player": 3.0, + // 试炼中每个玩家增加的一次可以生成的生物数量 + "simultaneous_mobs_added_per_player": 2.0, + // 每次生成之间的刻数 + "ticks_between_spawn": 100, + // 生成时选择的实体的加权列表 + "spawn_potentials": [ + { + // SpawnData + "data": { + // 要生成的实体 + "entity": { + "id": "minecraft:zombie" + } + }, + // 权重值 + "weight": 1 + } + ], + // 给予奖励时选择的战利品表的权重列表 + "loot_tables_to_eject": [ + { + // 战利品键 + "data": "minecraft:spawners/ominous/trial_chamber/key", + // 权重值 + "weight": 1 + } + ], + // 试炼刷怪笼为不祥时使用的战利品表 + "items_to_drop_when_ominous": "minecraft:shearing/bogged" +} +``` + +```java +// 对于某个 RegistrySetBuilder builder +builder.add(Registries.TRIAL_SPAWNER_CONFIG, bootstrap -> { + var entityTag = new CompoundTag(); + entityTag.putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(EntityType.ZOMBIE).toString()); + + bootstrap.register( + ResourceKey.create(Registries.INSTRUMENT, ResourceLocation.fromNamespaceAndPath("examplemod", "example_config")), + TrialSpawnerConfig.builder() + .spawnRange(2) + .totalMobs(10.0) + .simultaneousMobs(4.0) + .totalMobsAddedPerPlayer(3.0) + .simultaneousMobsAddedPerPlayer(2.0) + .ticksBetweenSpawn(100) + .spawnPotentialsDefinition( + SimpleWeightedRandomList.single(new SpawnData(entityTag, Optional.empty(), Optional.empty())) + ) + .lootTablesToEject( + SimpleWeightedRandomList.single(BuiltInLootTables.SPAWNER_OMINOUS_TRIAL_CHAMBER_KEY) + ) + .itemsToDropWhenOminous( + BuiltInLootTables.BOGGED_SHEAR + ) + .build() + ) +}); +``` + +- `net.minecraft.world.level.block.entity.trialspawner` + - `TrialSpawner` 现在接受 `TrialSpawnerConfig` 的 `Holder` + - `canSpawnInLevel` 现在接受一个 `ServerLevel` + - `TrialSpawnerConfig` + - `CODEC` -> `DIRECT_CODEC` + - `$Builder`、`builder` - 试炼刷怪笼配置的构建器 + +## 配方提供者,数据提供者的“并非真正” + +`RecipeProvider` 不再是 `DataProvider`。相反,通过实现 `createRecipeProvider`,使用 `RecipeProvider$Runner` 构造 `RecipeProvider`。还必须指定提供者的名称。 + +```java +public class MyRecipeProvider extends RecipeProvider { + + // 参数存储在 protected 字段中 + public MyRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { + super(registries, output); + } + + @Override + protected void buildRecipes() { + // 在此处注册配方 + } + + // runner 类,这应该作为 DataProvider 添加到 DataGenerator 中 + public static class Runner extends RecipeProvider.Runner { + + public Runner(PackOutput output, CompletableFuture registries) { + super(output, registries) + } + + @Override + protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { + return new VanillaRecipeProvider(registries, output); + } + + @Override + public String getName() { + return "My Recipes"; + } + } +} +``` + +- `net.minecraft.data.recipes` + - `RecipeOutput#includeRootAdvancement` - 生成配方的根进度。 + - `RecipeProvider` 不再继承 `DataProvider` + - 构造函数接受查找提供者和一个 `RecipeOutput`,它们是 protected 字段 + - `buildRecipes` 不接受任何参数 + - 所有生成方法都不接受 `RecipeOutput` 并且是实例方法 + - `$FamilyRecipeProvider` - 通过传入结果方块和基础方块的 `Block` 为 `BlockFamily` 创建配方。 + - `$Runner` - 一个 `DataProvider`,通过 `createRecipeProvider` 构造 `RecipeProvider` + - `ShapedRecipeBuilder`、`ShapelessRecipeBuilder` 现在有私有构造函数,并接受物品的 holder getter + +## 原料的转变 + +`Ingredient` 已被重新实现,使用 `HolderSet` 作为其基础,而不是其自己的内部 `Ingredient$Value`。这主要改变了 `Ingredient#of` 的调用,因为您需要为其提供 `Item` 对象或代表标签的 `HolderSet`。有关如何执行此操作的更多信息,请参阅[持有者集部分](#持有者集-过渡)。 + +- `net.minecraft.world.item.crafting.Ingredient` + - `EMPTY` -> `Ingredient#of`,但默认用例不允许空原料 + - `CODEC` 已移除 + - `CODEC_NONEMPTY` -> `CODEC` + - `testOptionalIngredient` - 如果存在,则测试堆栈是否在原料内,否则默认为空检查。 + - `getItems` -> `items` + - `getStackingIds` 已移除 + - `of(ItemStack...)`、`of(Stream)` 已移除 + - `of(TagKey)` -> `of(HolderSet)`,需要解析标签键 + +## BlockEntityTypes 私有化了! + +`BlockEntityType` 已被完全私有化,构建器也被移除!这意味着,如果模组加载器或模组没有提供对构造函数的某种访问扩宽,您将无法创建新的方块实体。唯一的其他变更是,数据修复器的 `Type` 已被移除,意味着只需要提供客户端构造函数和方块实体可以位于的有效方块集合。 + +```java +// 如果 BlockEntityType 构造函数被设为公共 +// MyBlockEntity(BlockPos, BlockState) 构造函数 +BlockEntityType type = new BlockEntityType(MyBlockEntity::new, MyBlocks.EXAMPLE_BLOCK); +``` + +## 消耗品 + +使用物品已被进一步扩展,大部分已过渡到单独的数据组件条目。 + +### `Consumable` 数据组件 + +`Consumable` 数据组件定义了物品在使用完成时如何使用。这实际上起到了以前 `FoodProperties` 的作用,除了所有消耗逻辑都集中在这个组件中。一个消耗品有五个属性:消耗或使用物品所需的秒数、消耗时播放的动画、消耗时播放的声音、消耗期间是否应出现粒子,以及[消耗完成后要应用的效果](#consumeeffect)。 + +可以使用 `food` 物品属性应用 `Consumable`。如果只应添加 `Consumable`,则应调用 `component`。原版消耗品和构建器的列表可以在 `Consumables` 中找到。 + +```java +// 对于某个物品 +Item exampleItem = new Item(new Item.Properties().component(DataComponents.CONSUMABLE, + Consumable.builder() + .consumeSeconds(1.6f) // 将在 1.6 秒或 32 刻内使用该物品 + .animation(ItemUseAnimation.EAT) // 使用时要播放的动画 + .sound(SoundEvents.GENERIC_EAT) // 使用消耗品时要播放的声音 + .soundAfterConsume(SoundEvents.GENERIC_DRINK) // 消耗后播放的声音(委托给 'onConsume') + .hasConsumeParticles(true) // 设置是否显示粒子 + .onConsume( + // 完成消耗后,以 30% 的概率应用效果 + new ApplyStatusEffectsConsumeEffect(new MobEffectInstance(MobEffects.HUNGER, 600, 0), 0.3F) + ) + // 可以有多个 + .onConsume( + // 在 50 格半径内随机传送实体 + new TeleportRandomlyConsumeEffect(100f) + ) + .build() +)); +``` + +#### `OnOverrideSound` + +有时,实体在消耗物品时可能想要播放不同的声音。在这种情况下,实体可以实现 `Consumable$OverrideConsumeSound` 并返回应播放的声音事件。 + +```java +// 在您自己的实体上 +public class MyEntity extends Mob implements Consumable.OverrideCustomSound { + // ... + + @Override + public SoundEvent getConsumeSound(ItemStack stack) { + // 返回要播放的声音事件 + } +} +``` + +### `ConsumableListener` + +`ConsumableListener` 是表示在堆栈被“消耗”后要应用的动作的数据组件。这意味着自从玩家开始使用消耗品以来,`Consumable#consumeTicks` 过去之后。这方面的一个例子是 `FoodProperties`。`ConsumableListener` 只有一个方法 `#onConsume`,它接受消耗发生的等级、实体、进行消耗的堆栈以及已完成消耗的 `Consumable`。 + +```java +// 在您自己的数据组件上 +public record MyDataComponent() implements ConsumableListener { + + // ... + + @Override + public void onConsume(Level level, LivingEntity entity, ItemStack stack, Consumable consumable) { + // 物品被消耗后执行操作。 + } +} +``` + +### `ConsumeEffect` + +现在有一个数据组件处理物品被实体消耗时发生的事情,恰当地称为 `ConsumeEffect`。当前的效果范围从添加/移除状态效果、随机传送玩家,或仅仅播放声音。这些效果通过将效果传递给 `Consumable` 或构建器中的 `onConsume` 来应用。 + +```java +// 构造消耗品时 +Consumable exampleConsumable = Consumable.builder() + .onConsume( + // 完成消耗后,以 30% 的概率应用效果 + new ApplyStatusEffectsConsumeEffect(new MobEffectInstance(MobEffects.HUNGER, 600, 0), 0.3F) + ) + // 可以有多个 + .onConsume( + // 在 50 格半径内随机传送实体 + // 注意:当前有 bug,只允许 8 格半径 + new TeleportRandomlyConsumeEffect(100f) + ) + .build(); +``` + +### 使用转换 + +消耗时将物品转换为另一个堆栈现在通过 `DataComponents#USE_REMAINDER` 处理。只有当使用后堆栈为空时,剩余物才会被转换。否则,它将返回当前堆栈,只是使用了一个物品。 + +```java +// 对于某个物品 +Item exampleItem = new Item(new Item.Properties().usingConvertsTo( + Items.APPLE // 消耗时将此物品转换为苹果 +)); +Item exampleItem2 = new Item(new Item.Properties().component(DataComponents.USE_REMAINDER, + new UseCooldown( + new ItemStack(Items.APPLE, 3) // 消耗时转换为三个苹果 + ) +)); +``` + +### 冷却时间 + +物品冷却时间现在通过 `DataComponents#USE_COOLDOWN` 处理;然而,它们已被扩展,可以根据其定义的组对堆栈应用冷却时间。一个冷却组要么引用 `Item` 注册表名称(如果未指定),要么引用一个自定义资源位置。应用冷却时间时,它将冷却实例存储在匹配定义组的任何东西上。这意味着,如果某个堆栈定义了冷却组,当使用普通物品时,它不会受到影响。 + +```java +// 对于某个物品 +Item exampleItem = new Item(new Item.Properties().useCooldown( + 60 // 等待 60 秒 + // 将对 'my_mod:example_item' 组中的物品应用冷却时间(假设这是注册表名称) +)); +Item exampleItem2 = new Item(new Item.Properties().component(DataComponents.USE_COOLDOWN, + new UseCooldown( + 60, // 等待 60 秒 + // 将对 'my_mod:custom_group' 组中的物品应用冷却时间 + Optional.of(ResourceLocation.fromNamespaceAndPath("my_mod", "custom_group")) + ) +)); +``` + +- `net.minecraft.core.component.DataComponents#FOOD` -> `CONSUMABLE` +- `net.minecraft.world.entity.LivingEntity` + - `getDrinkingSound`、`getEatingSound` 已移除,由 `ConsumeEffect` 处理 + - `triggerItemUseEffects` 已移除 + - `eat` 已移除 +- `net.minecraft.world.entity.npc.WanderingTrader` 现在实现 `Consumable$OverrideConsumeSound` +- `net.minecraft.world.food` + - `net.minecraft.world.food.FoodData` + - `tick` 现在接受一个 `ServerPlayer` + - `getLastFoodLevel`、`getExhaustionLevel`、`setExhaustion` 已移除 + - `FoodProperties` 现在是一个 `ConsumableListener` + - `eatDurationTicks`、`eatSeconds` -> `Consumable#consumeSeconds` + - `usingConvertsTo` -> `DataComponents#USE_REMAINDER`, + - `effects` -> `ConsumeEffect` +- `net.minecraft.world.item` + - `ChorusFruitItem` 类已移除 + - `HoneyBottleItem` 类已移除 + - `Item` + - `getDrinkingSound`、`#getEatingSound` 已移除,由 `ConsumeEffect` 处理 + - `releaseUsing` 现在返回一个 `boolean` 表示是否成功释放 + - `$Properties#food` 现在可以接受一个 `Consumable` 用于自定义逻辑 + - `$Properties#usingConvertsTo` - 使用后要转换成的物品。 + - `$Properties#useCooldown` - 再次使用物品前等待的秒数。 + - `ItemCooldowns` 现在接受 `ItemStack` 或 `ResourceLocation` 作为其方法的参数,而不仅仅是 `Item` + - `getCooldownGroup` - 返回表示应用冷却时间的组的键 + - `ItemStack#getDrinkingSound`、`getEatingSound` 已移除 + - `MilkBucketItem` 类已移除 + - `OminousBottleItem` 类已移除 + - `SuspiciousStewItem` 类已移除 +- `net.minecraft.world.item.alchemy.PotionContents` 现在实现 `ConsumableListener` + - 构造函数接受一个可选的字符串,表示自定义名称的翻译键后缀 + - `applyToLivingEntity` - 将所有效果应用到提供的实体。 + - `getName` - 通过将自定义名称附加到提供的内容字符串末尾来获取名称组件。 +- `net.minecraft.world.item.component` + - `Consumable` - 一个定义何时可以消耗物品的数据组件。 + - `ConsumableListener` - 应用于可被消耗的数据组件的接口,在消耗完成后执行。 + - `SuspiciousStewEffects` 现在实现 `ConsumableListener` + - `UseCooldown` - 一个定义如何应用堆栈冷却时间的数据组件。 + - `UseRemainder` - 一个定义物品用完后应如何替换的数据组件。 + - `DeathProtection` - 一个包含 `ConsumeEffect` 列表的数据组件,用于定义使用物品以在死亡中幸存时要执行的操作。 +- `net.minecraft.world.item.consume_effects.ConsumeEffect` - 物品完成消耗后要应用的效果。 + +## 注册表对象 ID,在属性里? + +当向 `Block` 提供 `BlockBehaviour$Properties` 或向 `Item` 提供 `Item$Properties` 时,必须通过调用 `#setId` 直接在方块中设置 `ResourceKey`。如果在传入之前未设置此值,将抛出错误。 + +```java +new Block(BlockBehaviour.Properties.of() + .setId(ResourceKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("examplemod", "example_block")))); + +new BlockItem(exampleBlock, new Item.Properties() + .useBlockDescriptionPrefix() // 为方块物品制作描述 id + .setId(ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath("examplemod", "example_item")))); + +new Item(new Item.Properties() + .setId(ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath("examplemod", "example_item")))); +``` + +- `net.minecraft.world.item.Item$Properties` + - `setId` - 设置物品的资源键,以获取默认描述和模型。此属性必须设置。 + - `useBlockDescriptionPrefix` - 使用 `block.` 前缀创建描述 id。 + - `useItemDescriptionPrefix` - 使用 `item.` 前缀创建描述 id。 +- `net.minecraft.world.level.block.state.BlockBehaviour$Properties#setId` - 设置方块的资源键,以获取默认掉落和描述。此属性必须设置。 + +## 属性变更 + +`DirectionProperty` 已被移除,现在必须通过 `EnumProperty#create` 调用并带有 `Direction` 泛型来引用。此外,所有属性类都已成为 final,并且必须通过公开的 `create` 方法之一来构造。 + +- `net.minecraft.world.level.block.state.properties` + - `BooleanProperty` 现在是 final + - `DirectionProperty` 类已移除 + - `EnumProperty` 现在是 final + - `create` 现在接受一个 `List` 而不是 `Collection` + - `IntegerProperty` 现在是 final + - `Property#getPossibleValues` 现在返回一个 `List` 而不是 `Collection` + +## 配方,现在采用注册表格式 + +配方已升级为数据包注册表,类似于战利品表的处理方式。它们仍然以相同的方式查询,只是简单地使用一个伪注册表支持的实例。一些更常见的变化是,`RecipeHolder` 可能被 `RecipeDisplayId`、`RecipeDisplay` 或 `RecipeDisplayEntry` 取代,如果不需要 holder 本身的话。随之而来的是,配方书的处理方式也有一些变化。 + +### 配方书 + +`RecipeBookComponent` 已被修改,以容纳一个要渲染的菜单的泛型实例。因此,该组件不再实现 `PlacedRecipe`,而是接受一个代表 `RecipeBookMenu` 的泛型。菜单通过其构造函数传递给组件,而不是通过 `init` 方法。这也意味着 `RecipeBookMenu` 没有任何关联的泛型。要创建一个组件,需要扩展该类。 + +```java +// 假设某个 MyRecipeMenu 继承 AbstractContainerMenu +public class MyRecipeBookComponent extends RecipeBookComponent { + + public MyRecipeBookComponent(MyRecipeMenu menu, List tabInfos) { + super(menu, tabInfos); + // ... + } + + @Override + protected void initFilterButtonTextures() { + // ... + } + + @Override + protected boolean isCraftingSlot(Slot slot) { + // ... + } + + @Override + protected void selectMatchingRecipes(RecipeCollection collection, StackedItemContents contents) { + // ... + } + + @Override + protected Component getRecipeFilterName() { + // ... + } + + @Override + protected void fillGhostRecipe(GhostSlots slots, RecipeDisplay display, ContextMap ctx) { + + } +} + +public class MyContainerScreen extends AbstractContainerScreen implements RecipeUpdateListener { + + public MyContainerScreen(MyRecipeMenu menu, List tabInfos, ...) { + super(menu, ...); + this.recipeBookComponent = new MyRecipeBookComponent(menu, tabInfos); + } + + + // 完整实现请参见 AbstractFurnaceScreen +} +``` + +### 配方显示 + +但是,配方如何理解应在配方书中显示什么?这属于两个新的静态注册表:`RecipeDisplay` 和 `SlotDisplay`。 + +`SlotDisplay` 表示配方中单个槽位内显示的内容。该显示只有一个方法(忽略类型):`resolve`。`resolve` 接受包含数据的 `ContextMap` 和 `DisplayContentsFactory`,后者接受将在该槽位中显示的堆栈和剩余物。`SlotDisplay` 还有许多辅助实现,例如 `$Composite` 接受一个显示列表,或 `$ItemStackSlotDisplay` 接受要显示的堆栈。该显示通过其 `$Type` 注册,该类型接受映射编解码器和流编解码器。 + +槽位还有通过 `resolveForStacks` 和 `resolveForFirstStack` 获取可显示的相关堆栈的方法。 + +```java +public static record MySlotDisplay() implements SlotDisplay { + + @Override + public Stream resolve(ContextMap ctx, DisplayContentsFactory output) { + // 调用 output.forStack(...) 或 addRemainder(..., ...) 使用 instanceof 来显示物品 + if (output instanceof ForStacks stacks) { + stacks.forStack(...); + } else if (output instanceof ForRemainders remainders) { + remainders.addRemainder(..., ...); + } + } + + @Override + public SlotDisplay.Type type() { + // 返回在此处注册到 Registries#SLOT_DISPLAY 的注册对象 + } +} +``` + +`RecipeDisplay` 表示如何显示一个配方。作为实现细节,`RecipeDisplay` 只需要知道结果(通过 `result` 槽位显示)和配方使用的位置(通过 `craftingStation` 槽位显示),因为这是配方书关心的唯一两个细节。但是,也建议为原料设置槽位显示,然后由您的 `RecipeBookComponent` 消耗它们。该显示通过其 `$Type` 注册,该类型接受映射编解码器和流编解码器。 + +```java +public record MyRecipeDisplay(SlotDisplay result, SlotDisplay craftingStation, ...) implements RecipeDisplay { + + @Override + public RecipeDisplay.Type type() { + // 返回在此处注册到 Registries#RECIPE_DISPLAY 的注册对象 + } +} +``` + +### 配方放置 + +配方书中的配方原料和放置现在通过 `Recipe#placementInfo` 处理。`PlacementInfo` 基本上是一个定义配方包含的物品以及它们在菜单中应放置的位置(如果支持)的定义。如果配方无法放置,例如如果它不是 `Item` 或使用堆栈信息,则应返回 `PlacementInfo#NOT_PLACEABLE`。 + +`PlacementInfo` 可以通过 `Ingredient`、`List` 或 `List>` 使用 `create` 或 `createFromOptionals` 分别创建。 + +```java +public class MyRecipe implements Recipe { + + private PlacementInfo info; + + public MyRecipe(Ingredient input) { + // ... + } + + // ... + + @Override + public PlacementInfo placementInfo() { + // 这种委托是因为支持原料的 HolderSet 可能在构造函数中没有完全填充 + if (this.info == null) { + this.info = PlacementInfo.create(input); + } + + return this.info; + } +} +``` + +如果使用 `Optional`,可以通过 `Ingredient#testOptionalIngredient` 进行测试。 + +- `net.minecraft.world.item.crafting` + - `Ingredient#display` - 返回显示此原料的 `SlotDisplay`。 + - `PlacementInfo` - 定义构造配方结果所需的所有原料。 + - `Recipe` + - `getToastSymbol` -> `getCategoryIconItem` + - `getIngredients`、`isIncomplete` -> `placementInfo` + - `getIngredients` -> `PlacementInfo#stackedRecipeContents`, + - `isIncomplete` -> `PlacementInfo#isImpossibleToPlace` + - `RecipeManager#getSynchronizedRecipes` - 返回所有可以放置的配方并将它们发送到客户端。没有其他配方被同步。 + - `ShapedRecipePattern` 现在接受一个 `List>` 而不是 `NonNullList` + - `ShapelessRecipe` 现在接受一个 `List` 而不是 `NonNullList` + - `SmithingTransformRecipe`、`SmithingTrimRecipe` 现在接受 `Optional` 而不是 `Ingredient` + - `SuspiciousStewRecipe` 类已移除 + +### 配方变更 + +配方类本身有一些变化,它们反映了上述所有变化。首先,`canCraftInDimensions` 已移除,现在硬编码到匹配函数中。`getResultItem` 和 `getCategoryIconItem` 已被 `RecipeDisplay` 通过 `display` 取代。`getRemainingItems` 已移至 `CraftingRecipe`。最后,所有配方现在通过 `recipeBookCategory` 返回它们的 `RecipeBookCategory`。 + +```java +public class MyRecipe implements Recipe { + + @Override + public String group() { + // 在此处返回之前 `getGroup` 的内容 + } + + @Override + public List display() { + return List.of( + // 某个 RecipeDisplay 实例 + // RecipeDisplay#result 应返回 `getResultItem` + // RecipeDisplay#craftingStation 应返回 `getCategoryIconItem` + ) + } + + @Override + public RecipeBookCategory recipeBookCategory() { + // 功能类似于数据生成期间传递给配方构建器的书类别 + return RecipeBookCategories.CRAFTING_MISC; + } +} +``` + +### 创建配方书类别 + +配方书类别通过 `ExtendedRecipeBookCategory` 统一,并分为两个部分:`RecipeBookCategory` 用于实际类别,以及 `SearchRecipeBookCategory` 用于聚合类别。`SearchRecipeBookCategory` 是枚举,而 `RecipeBookCategory` 像任何其他静态注册表对象一样。这是通过创建一个新的 `RecipeBookCategory` 来完成的。 + +```java +// 使用标准的原版注册表方法 +public static final RecipeBookCategory EXAMPLE_CATEGORY = Registry.register( + BuiltInRegistries.RECIPE_BOOK_CATEGORY, + // 注册表对象名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "example_category"), + // 这将创建一个新的配方书类别。它作为一个标记对象。 + new RecipeBookCategory() +); +``` + +### 技术性变更 + +- `net.minecraft.advancements.AdvancementRewards` 现在接受一个 `ResourceKey` 列表而不是 `ResourceLocation` 作为配方 + - `$Builder#recipe`、`addRecipe` 现在接受一个 `ResourceKey` +- `net.minecraft.advancements.critereon` + - `PlayerPredicate` 现在接受一个 `ResourceKey` 作为配方映射 + - `$Builder#addRecipe` 现在接受一个 `ResourceKey` + - `RecipeCraftedTrigger` + - `trigger` 现在接受一个 `ResourceKey` + - `$TriggerInstance` 现在接受一个 `ResourceKey` + - `$TriggerInstance#craftedItem`、`crafterCraftedItem` 现在接受一个 `ResourceKey` + - `RecipeUnlockedTrigger` + - `unlocked` 现在接受一个 `ResourceKey` + - `$TriggerInstance` 现在接受一个 `ResourceKey` +- `net.minecraft.client` + - `ClientRecipeBook` + - `setupCollections` -> `rebuildCollections`,不是一对一 + - `getCollection(RecipeBookCategories)` -> `getCollection(ExtendedRecipeBookCategory)` + - `add`、`remove` - 处理在配方书中添加/移除要显示的配方条目。 + - `addHighLight`、`removeHighlight`、`hasHighlight` - 处理当被玩家过滤或选择时条目是否高亮显示。 + - `clear` - 清除已知和高亮显示的配方。 + - `RecipeBookCategories#*_MISC` -> `SearchRecipeBookCategory#*` + - 这也可以在方法中被 `RecipeBookComponent$TabInfo`、`ExtendedRecipeBookCategory` 或 `RecipeBookCategory` 取代 +- `net.minecraft.client.gui.components.toasts` + - `RecipeToast(RecipeHolder)` -> `RecipeToast()`,现在是私有的 + - `addOrUpdate` 现在接受一个 `RecipeDisplay` 而不是 `RecipeHolder` +- `net.minecraft.client.gui.screens.inventory.AbstractFurnaceScreen` + - `recipeBookComponent` 现在是私有的 + - `AbstractFurnaceScreen(T, AbstractFurnaceRecipeBookComponent, Inventory, Component, ResourceLocation, ResourceLocation, ResourceLocation)` - `AbstractFurnaceRecipeBookComponent` 已被 `Component` 取代,因为配方书不是在内部构造的,并且现在接受一个 `RecipeBookComponent$TabInfo` 列表 +- `net.minecraft.client.gui.screens.recipebook` + - `AbstractFurnaceReipceBookComponent`、`BlastingFurnaceReipceBookComponent`、`SmeltingFurnaceReipceBookComponent`、`SmokingFurnaceReipceBookComponent` -> `FurnaceReipceBookComponent` + - `GhostRecipe` -> `GhostSlots`,不是一对一,因为配方本身作为 `RecipeHolder` 存储在 `RecipeBookComponent` 的一个私有字段中 + - `addResult` -> `setResult`,不是一对一 + - `addIngredient` -> `setIngredient`,不是一对一 + - `setSlot`、`setInput`、`setResult` 现在接受一个 `ContextMap` + - `OverlayRecipeComponent()` -> `OverlayRecipeComponent(SlotSelectTime, boolean)` + - `init` 接受一个包含注册表数据的 `ContextMap` 以在组件中显示,以及一个 `boolean` 表示配方书是否正在过滤,而不是从 `Minecraft` 实例计算 + - `getLastRecipeClicked` 现在返回一个 `RecipeDisplayId` + - `$OverlayRecipeButton` 现在是一个抽象的包私有类,接受 `ContextMap` + - `$Pos` 现在是一个记录 + - `RecipeBookComponent` 不再实现 `RecipeShownListener` + - 构造函数接受一个 `$TabInfo` 列表,其中包含书中显示的标签页 + - `init` 不再接受一个 `RecipeBookMenu` + - `initVisuals` 现在是私有的 + - `initFilterButtonTextures` 现在是抽象的 + - `updateCollections` 现在接受另一个布尔值,表示书是否正在过滤 + - `renderTooltip` 现在接受一个可为 null 的 `Slot` 而不是一个表示槽位索引的 `int` + - `renderGhostRecipe` 不再接受一个表示延迟时间的 `float` + - `setupGhostRecipe` -> `fillGhostRecipe`,不再接受要放置的 `List`,这存储在组件本身中 + - `selectMatchingRecipes` 不再接受 `RecipeBook` + - `recipesShown` 现在接受一个 `RecipeDisplayId` + - `setupGhostRecipeSlots` -> `fillGhostRecipe`,接受 `ContextMap` + - `$TabInfo` - 一个记录,表示配方书页面中要显示的图标和配方类别。 + - `RecipeBookPage()` -> `RecipeBookPage(RecipeBookComponent, SlotSelectTime, boolean)` + - `updateCollections` 现在接受一个布尔值,表示书是否正在过滤 + - `getMinecraft` 已移除 + - `addListener` 已移除 + - `getLastRecipeClicked` 现在返回一个 `RecipeDisplayId` + - `recipesShown` 现在接受一个 `RecipeDisplayId` + - `getRecipeBook` 现在返回一个 `ClientRecipeBook` + - `RecipeBookTabButton` 现在接受一个 `RecipeBookComponent$TabInfo` + - `startAnimation(Minecraft)` -> `startAnimation(ClientRecipeBook, boolean)` + - `getCategory` 现在返回一个 `ExtendedRecipeBookCategory` + - `RecipeButton()` -> `RecipeButton(SlotSelectTime)` + - `init` 现在接受一个 `boolean` 表示书是否正在过滤,以及一个 `ContextMap` 保存注册表数据 + - `getRecipe` -> `getCurrentRecipe`,不是一对一 + - `getDisplayStack` - 返回配方的结果堆栈。 + - `getTooltipText` 现在接受 `ItemStack` + - `RecipeCollection(RegistryAccess, List)` -> `RecipeCollection(List)` + - `canCraft` -> `selectRecipes` + - `getRecipes`、`getDisplayRecipes` -> `getSelectedRecipes` + - `registryAccess`、`hasKnownRecipes`、`updateKnownRecipes` 已移除 + - `isCraftable` 现在接受一个 `RecipeDisplayId` + - `hasFitting` -> `hasAnySelected` + - `getRecipes` 现在返回一个 `RecipeDisplayEntry` 列表 + - `RecipeShownListener` 类已移除 + - `RecipeUpdateListener` + - `getRecipeBookComponent` 已移除 + - `fillGhostRecipe` -> 给定 `RecipeDisplay` 填充幽灵配方 + - `SearchRecipeBookCategory` - 一个枚举,包含聚合类型的配方书类别。 + - `SlotSelectTime` - 表示玩家选择的当前槽位索引。 +- `net.minecraft.client.multiplayer` + - `ClientPacketListener#getRecipeManager` -> `recipes`,返回 `RecipeAccess` + - `ClientRecipeContainer` - 当从服务器同步时,`RecipeAccess` 的客户端实现。 + - `MultiPlayerGameMode#handlePlaceRecipe` 现在接受一个 `RecipeDisplayId` + - `SessionSearchTrees#updateRecipes` 现在接受一个 `Level` 而不是 `RegistryAccess$Frozen` +- `net.minecraft.client.player.LocalPlayer#removeRecipeHightlight` 现在接受一个 `RecipeDisplayId` +- `net.minecraft.commands.SharedSuggestionProvider#getRecipeNames` 已移除,因为它可以从注册表访问中查询 +- `net.minecraft.commands.arguments.ResourceLocationArgument` + - `getRecipe` -> `ResourceKeyArgument#getRecipe` + - `getAdvancement` -> `ResourceKeyArgument#getAdvancement` +- `net.minecraft.commands.synchronization.SuggestionProviders#ALL_RECIPES` 已移除 +- `net.minecraft.core.component.DataComponents#RECIPES` 现在接受一个 `ResourceKey` 列表 +- `net.minecraft.data.recipes` + - `RecipeBuilder#save` 现在接受一个 `ResourceKey` 而不是 `ResourceLocation` + - `RecipeOutput#accept` 现在接受一个 `ResourceKey` 而不是 `ResourceLocation` + - `RecipeProvider#trimSmithing` 现在接受一个 `ResourceKey` 而不是 `ResourceLocation` +- `net.minecraft.network.protocol.game` + - `ClientboundPlaceGhostRecipePacket` - 一个包含容器 id 和 `RecipeDisplay` 的数据包 + - `ClientboundRecipeBookAddPacket` - 向配方书添加条目的数据包 + - `ClientboundRecipeBookRemovePacket` - 从配方书移除条目的数据包 + - `ClientboundRecipeBookSettingsPacket` - 指定配方书设置的数据包 + - `ClientboundRecipePacket` 类已移除 + - `ClientboundUpdateRecipesPacket` 现在是一个记录,接受配方的属性集和切石机配方 + - `getRecipes` 已移除 + - `ServerboundPlaceRecipePacket` 现在是一个记录 + - `ServerboundRecipeBookSeenRecipePacket` 现在是一个记录 +- `net.minecraft.recipebook` + - `PlaceRecipe` -> `PlaceRecipeHelper` + - `addItemToSlot` -> `$Output#addItemToSlot` + - `placeRecipe` 现在接受一个 `Recipe` 而不是 `RecipeHolder` + - 有一个重载接受两个额外的 int,表示 `ShapedRecipe` 的模式高度和宽度,或者只是重复前两个 int + - `RecipeBook` + - `add`、`contains`、`remove` -> `ServerRecipeBook#add`、`contains`、`remove` + - `addHighlight`、`removeHighlight`、`willHighlight` -> `ServerRecipeBook#addHighlight`、`removeHighlight`、`ClientRecipeBook#hasHighlight` + - `bookSettings` 现在是 protected + - `RecipeBookSettings#read`、`write` 现在是私有的 + - `ServerPlaceRecipe` 不再直接可访问,而是通过 `#placeRecipe` 作为 `RecipeBookMenu$PostPlaceAction` 访问和返回 + - `$CraftingMenuAccess` - 定义如何与可放置配方菜单进行交互。 + - `ServerRecipeBook` + - `fromNbt` 现在接受一个 `ResourceKey` 的谓词而不是 `RecipeManager` + - `copyOverData` - 从另一个配方书读取数据。 + - `$DisplayResolver` - 通过传入 `RecipeDisplayEntry` 来解析要显示的配方 +- `net.minecraft.stats.RecipeBook#isFiltering(RecipeBookMenu)` 已移除 +- `net.minecraft.world.entity.player` + - `Player#awardRecipesByKey` 现在接受一个 `ResourceKey` 列表 + - `StackedItemContents#canCraft` 接受原料信息列表的重载 +- `net.minecraft.world.inventory` + - `AbstractCraftingMenu` - 一个用于合成界面的菜单。 + - `AbstractFurnaceMenu` 现在接受 `RecipePropertySet` 键 + - `CraftingMenu#slotChangedCraftingGrid` 现在接受一个 `ServerLevel` 而不是 `Level` + - `ItemCombinerMenu` 现在接受一个 `ItemCombinerMenuSlotDefinition` + - `mayPickup` 现在默认为 `true` + - `ItemCombinerMenuSlotDefinition#hasSlot`、`getInputSlotIndexes` 已移除 + - `RecipeBookMenu` 不再接受任何泛型 + - `handlePlacement` 现在是抽象的,并返回一个 `$PostPlaceAction`,接受一个额外的 `ServerLevel` + - 这将移除所有基本的配方放置调用,因为这将在内部由 `ServerPlaceRecipe` 处理 + - `RecipeCraftingHolder#setRecipeUser` 不再接受一个 `Level` + - `SmithingMenu#hasRecipeError` - 返回当物品放置在库存中时配方是否有错误。 +- `net.minecraft.world.item.crafting` + - `AbstractCookingRecipe` 现在实现 `SingleItemRecipe` + - 构造函数不再接受 `RecipeType`,因此用户需要覆盖 `getType` 方法 + - `getExperience` -> `experience` + - `getCookingTime` -> `cookingTime` + - `furnaceIcon` - 返回炉子的图标。 + - `$Serializer` - 烹饪配方序列化器实例的便利实现。 + - `CookingBookCategory` 现在有一个整数 id + - `CraftingRecipe#defaultCrafingRemainder` - 获取合成配方中应保留的堆栈。 + - `CustomRecipe$Serializer` - 自定义配方序列化器实例的便利实现。 + - `ExtendedRecipeBookCategory` - 表示配方书内类别的统一接口。 + - `Ingredient#optionalIngredientToDisplay` - 将可选原料转换为 `SlotDisplay`。 + - `Recipe#getRemainingItems` -> `CraftingRecipe#getRemainingItems` + - `RecipeAccess` - 一个访问器,返回包含可用配方输入的属性集。 + - `RecipeBookCategory` - 表示配方书内单个类别的对象。 + - `RecipeCache#get` 现在接受一个 `ServerLevel` 而不是 `Level` + - `RecipeHolder` 现在接受一个 `ResourceKey` + - `RecipeManager` 现在继承 `SimplePreparableReloadLsitener` 并实现 `RecipeAccess` + - `prepare` - 从配方注册表创建配方映射 + - `logImpossibleRecipes`、`hasErrorsLoading` 已移除 + - `getRecipeFor` 现在接受一个 `ResourceKey`,之前是 `ResourceLocation` + - `getRecipesFor`、`getAllRecipesFor` -> `RecipeMap#getRecipesFor` + - `byType` 已移除 + - `getRemainingItemsFor` 已移除 + - `byKey`、`byKeyTyped` 现在接受一个 `ResourceKey` + - `getOrderedRecipes` 已移除 + - `getSynchronizedRecipes` -> `getSynchronizedItemProperties`、`getSynchronizedStonecutterRecipes`;不是一对一 + - `getRecipeIds` 已移除 + - `getRecipeFromDisplay` - 根据其 id 获取配方显示信息。 + - `listDisplaysForRecipe` - 接受要显示的配方的显示条目列表。 + - `replaceRecipes` 已移除 + - `$CachedCheck#getRecipeFor` 现在接受一个 `ServerLevel` 而不是 `Level` + - `$IngredientCollector` - 一个从配方中提取原料并将其添加到 `RecipePropertySet` 的配方消费者 + - `$IngredientExtractor` - 一个在存在时获取配方的原料的方法。 + - `$ServerDisplayInfo` - 一个将显示条目链接到其配方持有者的记录。 + - `RecipeMap` - 一个通过配方类型和资源键映射配方持有者的类。 + - `RecipePropertySet` - 一组可用作给定配方槽位输入的原料。用于只允许特定输入到屏幕上的槽位。 + - `SelectableRecipe` - 一个包含槽位显示及其关联配方的记录。目前仅用于切石机菜单。 + - `SimpleCookingSerializer` -> `AbstractCookingRecipe$Serializer` + - `SingleItemRecipe` 不再接受 `RecipeType` 或 `RecipeSerializer` + - `ingredient`、`result`、`group` 现在是私有的 + - `input`、`result` - 配方的槽位。 +- `net.minecraft.world.item.crafting.display` + - `DisplayContentsFactory` - 用于接受配方内容的一个工厂。其子类型接受配方的堆栈和剩余物。 + - `RecipeDisplay` - 用于显示配方内容的显示处理器。 + - `RecipeDisplayEntry` - 一个将配方显示链接到其标识符、类别和合成需求的记录。 + - `RecipeDisplayId` - 配方显示的标识符。 + - `SlotDisplay` - 用于显示配方内槽位内容的显示处理器。 + - `SlotDisplayContext` - 槽位显示使用的上下文键。 +- `net.minecraft.world.level.Level#getRecipeManager` -> `recipeAccess`,在 `Level` 上返回 `RecipeAccess`,但在 `ServerLevel` 上返回 `RecipeManager` +- `net.minecraft.world.level.block.CrafterBlock#getPotentialResults` 现在接受一个 `ServerLevel` 而不是 `Level` +- `net.minecraft.world.level.block.entity.CampfireBlockEntity` + - `getCookableRecipe` 已移除 + - `placeFood` 现在接受一个 `ServerLevel` 而不是 `Level` + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 语言文件的移除与重命名 + +`assets/minecraft/lang` 中翻译键的所有移除和重命名现在显示在 `deprecated.json` 中。 + +### 条件,通过 HolderGetter 提供 + +在构造期间,所有条件构建器现在都接受一个 `HolderGetter`。虽然这可能不会被使用,但这是用来代替直接调用静态注册表来获取关联的 `Holder` 和 `HolderSet`。 + +- `net.minecraft.advancement.critereon` + - `BlockPredicate$Builder#of` + - `ConsumeItemTrigger$TriggerInstance#usedItem` + - `EntityEquipmentPredicate#captainPredicate` + - `EntityPredicate$Builder#of` + - `EntityTypePredicate#of` + - `ItemPredicate$Builder#of` + - `PlayerTrigger$TriggerInstance#walkOnBlockWithEquipment` + - `ShotCrossbowTrigger$TriggerInstance#shotCrossbow` + - `UsedTotemTrigger$TriggerInstance#usedToItem` + +### MacosUtil#IS_MACOS + +`com.mojang.blaze3d.platform.MacosUtil#IS_MACOS` 已被添加,以替换在渲染过程中指定布尔值。 + +- `com.mojang.blaze3d.pipeline` + - `RenderTarget#clear(boolean)` -> `clear()` + - `TextureTarget(int, int, boolean, boolean)` -> `TextureTarget(int, int, boolean)` +- `com.mojang.blaze3d.platform.GlStateManager#_clear(boolean)` -> `_clear()` +- `com.mojang.blaze3d.systems.RenderSystem#clear(int, boolean)` -> `clear(int)` + +### 雾参数 + +单个值的雾方法已被 `FogParameters` 数据对象取代。 + +- `com.mojang.blaze3d.systems.RenderSystem` + - `setShaderFogStart`、`setShaderFogEnd`、`setShaderFogColor`、`setShaderFogShape` -> `setShaderFog` + - `getShaderFogStart`、`getShaderFogEnd`、`getShaderFogColor`、`getShaderFogShape` -> `getShaderFog` +- `net.minecraft.client.renderer.FogRenderer` + - `setupColor` -> `computeFogColor`,返回一个 `Vector4f` + - `setupNoFog` -> `FogParameters#NO_FOG` + - `setupFog` 现在接受一个 `Vector4f` 作为颜色,并返回 `FogParameters` + - `levelFogColor` 已移除 + +### 新标签 + +- `minecraft:banner_pattern` + - `bordure_indented` + - `field_masoned` +- `minecraft:block` + - `bats_spawnable_on` + - `pale_oak_logs` +- `minecraft:damage_type` + - `mace_smash` +- `minecraft:item` + - `diamond_tool_materials` + - `furnace_minecart_fuel` + - `gold_tool_materials` + - `iron_tool_materials` + - `netherite_tool_materials` + - `villager_picks_up` + - `wooden_tool_materials` + - `piglin_safe_armor` + - `repairs_leather_armor` + - `repairs_chain_armor` + - `repairs_iron_armor` + - `repairs_gold_armor` + - `repairs_diamond_armor` + - `repairs_netherite_armor` + - `repairs_turtle_helmet` + - `repairs_wolf_armor` + - `duplicates_allays` + - `brewing_fuel` + - `panda_eats_from_ground` + - `shulker_boxes` + - `bundles` + - `map_invisibility_equipment` + - `pale_oak_logs` + - `gaze_disguise_equipment` +- `minecraft:entity_type` + - `boat` + +### 更智能的帧率限制 + +不再仅仅在玩家不在等级中或在屏幕或覆盖层中时限制帧率,而是根据不同的动作有不同的行为。这是通过 `FramerateLimitTracker` 使用 `InactivityFpsLimit` 完成的。这增加了两个额外的检查。如果窗口最小化,游戏以 10 fps 运行。如果用户一分钟没有输入,那么游戏以 30 fps 运行。十分钟没有输入后为 10 fps。 + +- `com.mojang.blaze3d.platform.FramerateLimitTracker` - 一个根据设定值限制帧率的跟踪器。 +- `com.mojang.blaze3d.platform#Window#setFramerateLimit`、`getFramerateLimit` 已移除 +- `net.minecraft.client` + - `InactivityFpsLimit` - 一个枚举,定义了当窗口最小化或玩家离开键盘时如何限制 FPS。 + - `Minecraft#getFramerateLimitTracker` - 返回帧率限制器。 + +### 燃料值 + +`FuelValues` 已取代 `AbstractFurnaceBlockEntity` 中的静态映射。它的功能与该映射相同,只是燃料值存储在 `MinecraftServer` 本身上,并可提供给单个 `Level` 实例。可以通过访问 `MinecraftServer` 或 `Level` 并调用 `fuelValues` 方法来获取映射。 + +- `net.minecraft.client.multiplayer.ClientPacketListener#fuelValues` - 返回燃料的燃烧时间。 +- `net.minecraft.server.MinecraftServer#fuelValues` - 返回燃料的燃烧时间。 +- `net.minecraft.server.level.Level#fuelValues` - 返回燃料的燃烧时间。 +- `net.minecraft.world.level.block.entity` + - `AbstractFurnaceBlockEntity` + - `invalidateCache`、`getFuel` -> `Level#fuelValues` + - `getBurnDuration` 现在接受 `FuelValues` + - `isFuel` -> `FuelValues#isFuel` + - `FuelValues` - 一个包含燃料物品列表及其关联燃烧时间的类 + +### 发光强度 + +发光强度数据现在被烘焙到四边形中,可以使用 `light_emission` 标签添加到面中。 + +- `net.minecraft.client.renderer.block.model` + - `BakedQuad` 现在接受一个 `int` 表示发光强度 + - `getLightEmission` - 返回四边形的发光强度。 + - `BlockElement` 现在接受一个 `int` 表示发光强度 + - `FaceBakery#bakeQuad` 现在接受一个 `int` 表示发光强度 + +### 地图纹理 + +地图纹理现在通过 `MapTextureManager`(处理动态纹理)和 `MapRenderer`(处理地图渲染)来处理。地图装饰仍然通过 `map_decorations` 精灵文件夹加载。 + +- `net.minecraft.client` + - `Minecraft` + - `getMapRenderer` - 获取地图的渲染器。 + - `getMapTextureManager` - 获取地图的纹理管理器。 +- `net.minecraft.client.resources#MapTextureManager` - 处理为地图创建动态纹理。 +- `net.minecraft.client.gui.MapRenderer` -> `net.minecraft.client.renderer.MapRenderer` +- `net.minecraft.client.renderer#GameRenderer#getMapRenderer` -> `Minecraft#getMapRenderer` + +### 朝向 + +随着红石线实验的加入,邻居变化带来了一个新类:`Orientation`。`Orientation` 实际上是两个方向和一个侧面偏置的组合。`Orientation` 用作一种方式,根据上下文的连接方向和偏置来传播更新。目前,这对不使用新红石线系统的人来说没有任何意义,因为所有对邻居方法的其他调用都将其设置为 `null`。然而,它确实提供了一种简单的方法来逐步传播行为。 + +- `net.minecraft.client.renderer.debug.RedstoneWireOrientationsRenderer` - 红石线朝向的调试渲染器。 +- `net.minecraft.world.level.Level` + - `updateNeighborsAt` - 使用指定的 `Orientation` 更新给定位置的邻居。 + - `updateNeighborsAtExceptFromFacing`、`neighborChanged` 现在接受一个 `Orientation` +- `net.minecraft.world.level.block.RedStoneWireBlock` + - `getBlockSignal` - 返回方块信号的强度。 +- `net.minecraft.world.level.block.state.BlockBehaviour` + - `neighborChanged`、`$BlockStateBase#handleNeighborChanged` 现在接受一个 `Orientation` 而不是邻居 `BlockPos` + - `updateShape` 现在接受 `LevelReader`、`ScheduledTickAccess` 和 `RandomSource` 而不是 `LevelAccessor`;`Direction` 和 `BlockState` 参数的顺序已重新排列 + - `$BlockStateBase#updateShape` 现在接受 `LevelReader`、`ScheduledTickAccess` 和 `RandomSource` 而不是 `LevelAccessor`;`Direction` 和 `BlockState` 参数的顺序已重新排列 +- `net.minecraft.world.level.redstone` + - `CollectingNeighborUpdater$ShapeUpdate#state` -> `neighborState` + - `NeighborUpdater` + - `neighborChanged`、`updateNeighborsAtExceptFromFacing`、`executeUpdate` 现在接受一个 `Orientation` 而不是邻居 `BlockPos` + - `executeShapeUpdate` 交换了 `BlockState` 和邻居 `BlockPos` 的顺序 + - `Orientation` - 方块上连接 `Direction` 的一组,并偏向正面或上面。 + - `RedstoneWireEvaluator` - 传入和传出信号的强度评估器。 + +### 矿车行为 + +矿车现在有一个 `MinecartBehavior` 类,处理实体应如何移动和渲染。 + +- `net.minecraft.core.dispenser.MinecartDispenseItemBehavior` - 定义矿车在从发射器发射时应如何表现。 +- `net.minecraft.world.entity.vehicle` + - `AbstractMinecart` + - `getMinecartBehavior` - 返回矿车的行为。 + - `exits` 现在是公开的 + - `isFirstTick` - 返回这是否是实体存活的第一个刻。 + - `getCurrentBlockPosOrRailBelow` - 获取矿车的当前位置或下方的铁轨。 + - `moveAlongTrack` -> `makeStepAlongTrack` + - `setOnRails` - 设置矿车是否在铁轨上。 + - `isFlipped`、`setFlipped` - 返回矿车是否倒置。 + - `getRedstoneDirection` - 返回红石供电的方向。 + - `isRedstoneConductor` 现在是公开的 + - `applyNaturalSlowdown` 现在返回要减速的向量。 + - `getPosOffs` -> `MinecartBehavior#getPos` + - `setInitialPos` - 设置矿车的初始位置。 + - `createMinecart` 在其创建中现在是抽象的,意味着它可以用于根据提供的参数创建任何矿车 + - `getMinecartType` 已移除 + - `getPickResult` 现在是抽象的 + - `$Type` 和 `getMinecartType` 被 `isRideable` 和 `isFurnace` 取代,不是一对一。 + - `AbstractMinecartContainer(EntityType, double, double, double, Level)` 已移除 + - `MinecartBehavior` - 包含实体在移动过程中应如何渲染和定位。 + - `MinecartFurnace#xPush`、`zPush` -> `push` +- `net.minecraft.world.level.block.state.properties.RailShape#isAscending` -> `isSlope` +- `net.minecraft.world.phys.shapes.MinecartCollisionContext` - 处理矿车与其他碰撞对象碰撞的实体碰撞上下文。 + +### 爆——炸——! + +`Explosion` 现在是一个定义爆炸元数据的接口。它不包含任何实际爆炸自身的方法。然而,`ServerExplosion` 仍然在内部用于处理等级爆炸等。 + +- `net.minecraft.world.level` + - `Explosion` -> `ServerExplosion` + - `Explosion` - 一个定义爆炸应如何发生的接口。 + - `getDefaultDamageSource` - 返回爆炸实例的默认伤害来源。 + - `shouldAffectBlocklikeEntities` - 返回方块实体是否应受到爆炸影响。 + - `level` - 获取 `ServerLevel` + - `ExplosionDamageCalculator#getEntityDamageAmount` 现在接受一个额外的 `float` 表示所见百分比 + - `Level#explode` 不再返回任何内容 +- `net.minecraft.world.level.block.Block#wasExploded` 现在接受一个 `ServerLevel` 而不是 `Level` +- `net.minecraft.world.level.block.state.BlockBehaviour#onExplosionHit`、`$BlockStateBase#onExplosionHit` 现在接受一个 `ServerLevel` 而不是 `Level` + +### 移除雕刻生成步骤 + +`GenerationStep$Carving` 已被移除,意味着所有 `ConfiguredWorldCarver` 都作为单个 `HolderSet` 提供。 + +```json +// 在某个 BiomeGenerationSettings JSON 中 +{ + "carvers": [ + // 在此处放置雕刻器 + ] +} +``` + +- `net.minecraft.world.level.biome.BiomeGenerationSettings` + - `getCarvers` 不再接受 `GenerationStep$Carving` + - `$Builder#addCarver` 不再接受 `GenerationStep$Carving` + - `$PlainBuilder#addCarver` 不再接受 `GenerationStep$Carving` +- `net.minecraft.world.level.chunk` + - `ChunkGenerator#applyCarvers` 不再接受 `GenerationStep$Carving` + - `ProtoChunk#getCarvingMask`、`getOrCreateCarvingMask`、`setCarvingMask` 不再接受 `GenerationStep$Carving` +- `net.minecraft.world.level.levelgen.placement` + - `CarvingMaskPlacement` 类已移除 + - `PlacementContext#getCarvingMask` 不再接受 `GenerationStep$Carving` + +### 可编解码的 JSON 重载监听器 + +`SimpleJsonResourceReloadListener` 已被重写,使用编解码器而不是纯 `Gson`。 + +```java +public class MyJsonListener extends SimpleJsonResourceReloadListener { + + // 如果不需要注册表访问,可以移除 HolderLookup$Provider 参数 + public MyJsonListener(HolderLookup.Provider registries, Codec codec, String directory) { + super(registries, codec, directory); + } +} +``` + +- `net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener` 现在接受一个代表 JSON 数据对象的泛型 + - 构造函数现在是 protected,接受数据对象的编解码器、目录的字符串,以及一个可选的 `HolderLookup$Provider` 以在必要时构造 `RegistryOps` 序列化上下文 + - `prepare` 现在返回一个名称到对象的映射 + - `scanDirectory` 现在接受 `DynamicOps` 和 `Codec` + +### 连续执行器 + +`ProcessorMailbox` 和 `ProcessorHandle` 已被分别替换为 `AbstractConsecutiveExecutor` 和 `TaskScheduler`。它们在使用上基本相同,只是可能方法名称不同。 + +- `net.minecraft.util.thread` + - `ProcessorMailbox` -> `AbstractConsecutiveExecutor`,不是一对一 + - `ConsecutiveExecutor` 将是等效的实现 + - `PriorityConsecutiveExecutor` - 一个指定调度时任务优先级的执行器。 + - `BlockableEventLoop#wrapRunnable` -> `AbstractConsecutiveExecutor#wrapRunnable` + - `ProcessorHandle` -> `TaskScheduler`,其中泛型是 `Runnable` 的子类型 + - `tell` -> `schedule` + - `ask`、`askEither` -> `scheduleWithResult`,不是一对一 + - `of` -> `wrapExecutor` + - `StrictQueue` 不再接受 `F` 泛型,并使 `T` 成为 `Runnable` 的子类型 + - `pop` 现在返回一个 `Runnable` + - `$IntRunnable` -> `$RunnableWithPriority` + +### 生物转化 + +通过 `#convertTo` 转化的生物,其逻辑由 `ConversionType`、`ConversionParams` 处理。`ConversionType` 是一个枚举,指示通过 `#convert` 将信息从一个生物复制到另一个生物时要应用的逻辑。常见属性通过 `#convertCommon` 处理,该方法在 `#convert` 方法内部调用。目前有两种类型:`SINGLE`,其中实体一对一地转换为另一个实体;以及 `SPLIT_ON_DEATH`,其中 `Mob#convertTo` 方法被多次调用,例如当史莱姆死亡时。`ConversionParams` 包含关于转化过程的元数据:类型、实体是否可以保留其装备或拾取战利品,以及实体所在的队伍。`Mob#convertTo` 还接受一个生物消费者,用于对实体本身应用任何最终化设置。 + +```java +// 对于某个 Mob exampleMob +exampleMob.convertTo( + EntityType.SHEEP, // 要转化成的实体 + new ConversionParams( + ConversionType.SINGLE, // 一对一 + true, // 保留装备 + false // 不保留拾取战利品 + ), + EntitySpawnReason.CONVERSION, // 实体生成原因 + sheep -> { + // 对新建的转化实体执行任何其他设置 + }, +) +``` + +- `net.minecraft.world.entity` + - `ConversionParams` - 一个包含当生物转化为另一个实体时发生什么的设置的记录 + - `ConversionType` - 一个枚举,定义一个生物如何转化为另一个生物。目前要么是 `SINGLE` 用于一对一,要么是 `SPLIT_ON_DEATH` 用于一对多(仅用于史莱姆) + - `Mob#convertTo` 现在接受 `ConversionParams`、一个可选的实体 `EntitySpawnReason`(默认为 `CONVERSION`),以及一个用于在转化后设置任何其他信息的生物消费者 + +### 末影珍珠加载区块 + +末影珍珠现在通过向区块源添加一个加载票并将实体存储在玩家上来加载它们穿过的区块。 + +- `net.minecraft.server.level.ServerPlayer` + - `registerEnderPearl`、`deregisterEnderPearl`、`getEnderPearls` - 处理玩家投掷的末影珍珠。 + - `registerAndUpdateEnderPearlTicket`、`placeEnderPearlTicket` - 处理投掷的末影珍珠的区域加载票。 + +### 分析器与 Tracy 客户端 + +分析器已与 Minecraft 实例分离,现在通过 `Profiler#get` 获得。可以通过 `Profiler#use` 上的 try-resource 块添加一个新的分析器实例。此外,分析器添加了一个名为 Tracy 的新库,用于跟踪当前堆栈帧以及捕获屏幕上的图像,如果传入了关联的 `--tracy` 参数。这些部分可以拆分为“区域”,以更细粒度地区分正在发生的事情。 + +```java +Profiler.get().push("section"); +// 在此处执行代码 +Profiler.get().pop(); +``` + +- `com.mojang.blaze3d.systems.RenderSystem#flipFrame` 现在接受一个 `TracyFrameCapture`,或 `null` +- `net.minecraft.client.Minecraft#getProfiler` -> `Profiler#get` +- `net.minecraft.client.main.GameConfig$GameData` 现在接受一个布尔值,表示是否通过 tracy 客户端捕获屏幕。 +- `net.minecraft.client.multiplayer.ClientLevel` 不再接受 `ProfilerFiller` +- `net.minecraft.server.MinecraftServer#getProfiler` -> `Profiler#get` +- `net.minecraft.server.packs.resources.PreparableReloadListener#reload` 不再接受 `ProfilerFiller` +- `net.minecraft.util.profiling` + - `Profiler` - 一个用于管理当前活动 `ProfilerFiller` 的静态处理器。 + - `ProfilerFiller` + - `addZoneText` - 添加文本以在分析当前帧时标记。 + - `addZoneValue` - 在分析当前帧时添加区域的值。 + - `setZoneColor` - 在分析当前帧时设置区域的颜色。 + - `zone` - 添加一个分析器部分,同时创建一个新区域来调用上述方法。 + - `tee` -> `combine` + - `$CombinedProfileFiller` - 一个写入多个分析器的分析器。 + - `TracyZoneFiller` - 由 tracy 客户端使用的分析器,用于跟踪当前正在分析的区域。 + - `Zone` - 当前正在被 Tracy 分析和解释的部分。 +- `net.minecraft.world.entity.ai.goal.GoalSelector` 不再接受提供的 `ProfilerFiller` +- `net.minecraft.world.level` + - `Level` 不再接受 `ProfilerFiller` + - `getProfiler`、`getProfilerSupplier` -> `Profiler#get` + - `PathNavigationRegion#getProfiler` -> `Profiler#get` +- `net.minecraft.world.ticks.LevelTicks` 不再接受 `ProfilerFiller` + +### Tick节流器 + +为了防止玩家垃圾邮件某些动作,添加了 `TickThrottler`。节流器接受阈值和要添加到计数的增量。如果计数小于阈值,则可以发生该动作。计数每刻减少。 + +- `net.minecraft.util.TickThrottler` - 一个用于限制某些动作发生频率的工具。 + +### 上下文键 + +战利品上下文参数已被上下文键取代,这只是对之前类的一个更通用的命名方案。这也导致上下文键用于可能有任意数据的其他上下文中。 + +简要描述,上下文键系统实际上是一个通用的类型化字典,其中每个 `ContextKey` 保存值类型,然后存储在 `ContextMap` 中的后备映射中。为了强制执行必需和可选参数,`ContextMap` 使用 `ContextKeySet` 构建,它定义了字典映射的键。 + +- `net.minecraft.advancements.critereon.CriterionValidator#validate` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` +- `net.minecraft.data.loot.LootTableProvider$SubProviderEntry#paramSet` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` +- `net.minecraft.util.context` + - `ContextKey` - 一个表示对象的键。可以把它看作一个指定值类型的字典键。 + - `ContextKeySet` - 一个键集,指示后备字典必须具有哪些键,以及可以指定的可选键。 + - `ContextMap` - 上下文键到其类型化对象的映射。 +- `net.minecraft.world.item.enchantment` + - `ConditionalEffect#codec` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` + - `TargetedConditionalEffect#codec` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` +- `net.minecraft.world.level.storage.loot` + - `LootContext` + - `hasParam` -> `hasParameter` + - `getParam` -> `getParameter` + - `getParamOrNull` - `getOptionalParameter` + - `$EntityTraget#getParam` 现在返回一个 `ContextKey` 而不是 `LootContextParam` + - `LootContextUser#getReferencedContextParams` 现在接受一组 `ContextKey` 而不是一组 `LootContextParam` + - `LootParams` 现在接受一个 `ContextMap` 而不是参数到对象的映射 + - `hasParam`、`getParameter`、`getOptionalParameter`、`getParamOrNull` 可以通过 `ContextMap` 以不同的名称访问 + - `$Builder#withParameter`、`withOptionalParameter`、`getParameter`、`getOptionalParameter` 现在接受一个 `ContextKey` 而不是 `LootContextParam` + - `$Builder#create` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` + - `LootTable` + - `getParameSet` 现在返回一个 `ContextKeySet` 而不是 `LootContextParamSet` + - `$Builder#setParamSet` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` + - `ValidationContext` 现在接受一个 `ContextKeySet` 而不是 `LootContextParamSet` + - `validateUser` -> `validateContextUsage` + - `setParams` - `setContextKeySet` +- `net.minecraft.world.level.storage.loot.functions` + - `CopyComponentsFunction$Source#getReferencedContextParams` 现在接受一组 `ContextKey` 而不是一组 `LootContextParam` +- `net.minecraft.world.level.storage.loot.parameters` + - `LootContextParam` -> `net.minecraft.util.context.ContextKey` + - `LootContextParamSet` -> `net.minecraft.util.context.ContextKeySet` +- `net.minecraft.world.level.storage.loot.providers.nbt` + - `ContextNbtProvider$Getter#getReferencedContextParams` 现在接受一组 `ContextKey` 而不是一组 `LootContextParam` + - `NbtProvider#getReferencedContextParams` 现在接受一组 `ContextKey` 而不是一组 `LootContextParam` +- `net.minecraft.world.level.storage.loot.providers.score.ScoreboardNameProvider#getReferencedContextParams` 现在接受一组 `ContextKey` 而不是一组 `LootContextParam` + +### 新增列表 + +- `com.mojang.blaze3d.framegraph` + - `FrameGraphBuilder` - 一个构建帧图的构建器,定义渲染使用的资源和帧通道。 + - `FramePass` - 一个接口,定义如何在帧图内读取/写入资源并执行它们进行渲染。 +- `com.mojang.blaze3d.platform` + - `ClientShutdownWatchdog` - 为客户端关闭时创建的一个看门狗。 + - `NativeImage#getPixelsABGR` - 以 ABGR 格式获取图像的像素。 + - `Window` + - `isIconified` - 返回窗口当前是否被图标化(通常最小化到任务栏)。 + - `setWindowCloseCallback` - 设置窗口关闭时要运行的回调。 +- `com.mojang.blaze3d.resource` + - `CrossFrameResourcePool` - 处理应跨多帧渲染的资源 + - `GraphicsResourceAllocator` - 处理要渲染和移除的资源。 + - `RenderTargetDescriptor` - 定义要分配和释放的渲染目标。 + - `ResourceDescriptor` - 定义一个资源以及如何分配和释放它。 + - `ResourceHandle` - 定义一个指向单个资源的指针。 +- `com.mojang.blaze3d.systems.RenderSystem#overlayBlendFunc` - 设置具有透明度的层之间的默认覆盖混合函数。 +- `com.mojang.blaze3d.vertex` + - `PoseStack#translate(Vec3)` - 使用向量平移顶部姿势 + - `VertexConsumer#setNormal(PoseStack$Pose, Vec3)` - 使用向量设置顶点的法线 +- `net.minecraft` + - `Optionull#orElse` - 如果第一个对象为 null,则返回第二个对象。 + - `TracingExecutor` - 一个跟踪正在执行的类引用的堆栈帧的执行器。 + - `Util` + - `allOf` - 对所有提供的谓词或谓词列表进行 AND 操作。如果没有提供谓词,该方法将默认为 `true`。 + - `anyOf` - 对所有提供的谓词或谓词列表进行 OR 操作。如果没有提供谓词,该方法将默认为 `false`。 + - `makeEnumMap` - 根据枚举类和一个将枚举转换为值的函数创建一个枚举映射。 +- `net.minecraft.advancements.critereon` + - `InputPredicate` - 一个匹配玩家正在进行的输入的谓词。 + - `SheepPredicate` - 用于实体是绵羊时的谓词。 +- `net.minecraft.client` + - `Minecraft` + - `saveReport` - 将崩溃报告保存到给定文件。 + - `triggerResourcePackRecovery` - 当发生编译异常时尝试保存游戏的函数,目前在加载着色器时使用。 + - `Options#highContrastBlockOutline` - 启用时,在悬停在范围内方块上时提供更高的对比度。 + - `ScrollWheelHandler` - 一个用于在鼠标滚轮滚动时存储信息的处理器。 +- `ItemSlotMouseAction` - 一个接口,定义鼠标悬停在槽位上时如何与槽位交互。 +- `net.minecraft.client.gui.components` + - `AbstractSelectionList#setSelectedIndex` - 根据索引设置选中的条目。 + - `AbstractWidget#playButtonClickSound` - 播放按钮点击声音。 + - `DebugScreenOverlay#getProfilerPieChart` - 获取饼图分析器渲染器。 +- `net.minecraft.client.gui.components.debugchart.AbstractDebugChart#getFullHeight` - 返回渲染图表的高度。 +- `net.minecraft.client.gui.components.toasts` + - `Toast` + - `getWantedVisbility` - 返回要渲染的吐司的可见性。 + - `update` - 更新吐司内的数据。 + - `TutorialToast` 有一个接受 `int` 表示显示时间(以毫秒为单位)的构造函数。 +- `net.minecraft.client.gui.font.glyphs.BakedGlyph` + - `renderChar` - 以指定颜色渲染一个字符。 + - `$GlyphInstance` - 一个带有其屏幕位置元数据的字形实例。 +- `net.minecraft.client.gui.screens` + - `BackupConfirmScreen` 有一个构造函数,接受另一个 `Component` 表示清除缓存的提示。 + - `Screen` + - `getFont` - 返回当前用于渲染屏幕的字体。 + - `showsActiveEffects` - 当为 true 时,显示当前应用于玩家的状态效果,假设相关屏幕添加了该功能。 + - `net.minecraft.client.gui.screens.inventory` + - `AbstractContainerScreen` + - `BACKGROUND_TEXTURE_WIDTH`、`BACKGROUND_TEXTURE_HEIGHT` - 都设置为 256。 + - `addItemSlotMouseAction` - 当悬停在槽位上时添加鼠标动作。 + - `renderSlots` - 渲染菜单内的所有活动槽位。 + - `AbstractRecipeBookScreen` - 一个屏幕,具有从构造函数提供的可渲染和可交互的 `RecipeBookComponent`。 +- `net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent#showTooltipWithItemInHand`- 返回当物品在玩家手中时是否应渲染工具提示。 +- `net.minecraft.client.gui.screens.worldselection` + - `CreateWorldCallback` - 一个接口,在给定当前屏幕、注册表、等级数据和路径目录的情况下创建世界。 + - `CreateWorldScreen#testWorld` - 尝试使用提供的生成设置上下文打开世界创建屏幕。 + - `InitialWorldCreationOptions` - 包含创建要生成的世界时设置的选项。 + - `WorldCreationContextMapper` - 一个接口,从可用的资源重载器和注册表创建世界上下文。 +- `net.minecraft.client.multiplayer` + - `ClientChunkCache` + - `getLoadedEmptySections` - 返回已由游戏加载但没有数据的部分。 + - `ClientLevel` + - `isTickingEntity` - 返回实体是否在等级中Tick。 + - `setSectionRangeDirty` - 将一个区域标记为脏,以便在持久性和网络调用期间更新。 + - `onSectionBecomingNonEmpty` - 当部分有数据时更新该部分。 + - `PlayerInfo#setTabListOrder`、`getTabListOrder` - 处理玩家标签页中循环切换玩家的顺序。 +- `net.minecraft.client.multiplayer.chat.report.ReportReason#getIncompatibleCategories` - 获取对于给定类型无法报告的所有原因。 +- `net.minecraft.client.particle.TrailParticle` - 一个从其当前位置到目标位置的拖尾粒子。 +- `net.minecraft.client.player.LocalPlayer#getDropSpamThrottler` - 返回一个节流器,确定玩家何时可以丢弃下一个物品。 +- `net.minecract.client.renderer` + - `CloudRenderer` - 处理云纹理数据的渲染和加载。 + - `DimensionSpecialEffects#isSunriseOrSunset` - 返回维度时间是否代表游戏中的日出或日落。 + - `LevelEventHandler` - 处理由 `Level#levelEvent` 方法发送的事件。 + - `LevelRenderer` + - `getCapturedFrustrum` - 返回渲染器的平截头体框。 + - `getCloudRenderer` - 返回天空盒中云的渲染器。 + - `onSectionBecomingNonEmpty` - 当部分有数据时更新该部分。 + - `LevelTargetBundle` - 保存渲染阶段的资源句柄和渲染目标。 + - `LightTexture` + - `getBrightness` - 返回给定环境光和天空光的亮度。 + - `lightCoordsWithEmission` - 返回打包的光照坐标。 + - `RenderType` + - `entitySolidZOffsetForward` - 获取一个实体的固体渲染类型,其中 z 从单个渲染对象偏移。 + - `flatClouds` - 获取平坦云的渲染类型。 + - `debugTriangleFan` - 获取调试三角形的渲染类型。 + - `vignette` - 获取晕影类型。 + - `crosshair` - 获取玩家准星的渲染类型。 + - `mojangLogo` - 获取 Mojang 徽标的渲染类型 + - `Octree` - 一个用于定义部分应在平截头体中渲染顺序的遍历实现。 + - `ShapeRenderer` - 用于在 Minecraft 等级中渲染基本形状的工具。 + - `SkyRenderer` - 渲染天空。 + - `WeatherEffectRenderer` - 渲染天气效果。 + - `WorldBorderRenderer` - 渲染世界边界。 +- `net.minecraft.client.renderer` + - `SectionOcclusionGraph#getOctree` - 返回用于处理渲染部分遍历的八叉树。 + - `ViewArea#getCameraSectionPos` - 获取相机的部分位置。 +- `net.minecraft.client.renderer.culling.Frustum` + - `getFrustumPoints` - 将平截头体矩阵作为 `Vector4f` 数组返回。 + - `getCamX`、`getCamY`、`getCamZ` - 返回平截头体相机坐标。 +- `net.minecraft.client.renderer.chunk.CompileTaskDynamicQueue` - 一个处理块渲染部分编译任务的同步队列。 +- `net.minecraft.client.renderer.debug` + - `ChunkCullingDebugRenderer` - 用于当块被剔除时的调试渲染器。 + - `DebugRenderer` + - `renderAfterTranslucents` - 在半透明渲染后渲染块剔除渲染器。 + - `renderVoxelShape` - 渲染体素形状的轮廓。 + - `toggleRenderOctree` - 切换是否渲染 `OctreeDebugRenderer`。 + - `OctreeDebugRenderer` - 渲染部分节点的顺序。 +- `net.minecraft.client.renderer.texture.AbstractTexture#defaultBlur`、`getDefaultBlur` - 返回正在应用的模糊是否是默认模糊。 +- `net.minecraft.client.resources.DefaultPlayerSkin#getDefaultSkin` - 返回默认的 `PlayerSkin`。 +- `net.minecraft.commands.CommandBuildContext#enabledFeatures` - 返回功能标志 +- `net.minecraft.commands.arguments.selector.SelectorPattern` - 一个记录,定义从某种模式解析的 `EntitySelector`。 +- `net.minecraft.core` + - `BlockPos#betweenClosed` - 返回边界框内所有位置的迭代器。 + - `Direction` + - `getYRot` - 返回给定方向的 Y 旋转。 + - `getNearest` - 给定某个 XYZ 坐标,返回最近的方向,如果没有更近的方向则返回后备方向。 + - `getUnitVec3` - 返回法线单位向量。 + - `$Axis#getPositive`、`getNegative`、`getDirections` - 获取沿轴的方向。 + - `GlobalPos#isCloseEnough` - 返回此位置到另一个维度中块位置的距离是否在给定半径内。 + - `HolderLookup$Provider` + - `listRegistries` - 返回每个注册表的注册表查找。 + - `allRegistriesLifecycle` - 返回所有注册表组合的生命周期。 + - `HolderSet#isBound` - 返回集合是否绑定到某个值。 + - `Registry$PendingTags#size` - 获取要加载的标签数量。 + - `Vec3i#distChessboard` - 获取向量分量之间的最大绝对距离。 +- `net.minecraft.core.component` + - `DataComponentHolder#getAllOfType` - 返回属于特定类类型的所有数据组件。 + - `DataComponentPredicate` + - `someOf` - 构造一个数据组件谓词,其中提供的映射包含提供的组件类型。 + - `$Builder#expect` - 添加我们应该期望数据组件具有某个值。 + - `PatchedDataComponentMap#clearPatch` - 清除对象上数据组件的所有补丁。 +- `net.minecraft.core.particles.TargetColorParticleOption` - 一个指定粒子目标位置和颜色的粒子选项。 +- `net.minecraft.data.DataProvider` + - `saveAll` - 使用提供的编解码器将资源位置到值的映射中的所有值写入 `PathProvider`。 + - `saveStable` - 使用给定的编解码器将值写入提供的路径。 +- `net.minecraft.data.loot#BlockLootSubProvider` + - `createMossyCarpetBlockDrops` - 为苔藓地毯块创建一个战利品表。 + - `createShearsOrSlikTouchOnlyDrop` - 创建一个只有在用剪刀或带有精准采集附魔的物品挖掘时才能掉落其物品的战利品表。 +- `net.minecraft.data.worldgen.Pools#createKey` - 为模板池创建一个 `ResourceKey`。 +- `net.minecraft.data.models.EquipmentModelProvider` - 装备模型的模型提供者,仅包含原版引导。 +- `net.minecraft.data.info.DatapackStructureReport` - 一个返回数据包结构的提供者。 +- `net.minecraft.gametest.framework` + - `GameTestHelper` + - `absoluteAABB`、`relativeAABB` - 在绝对坐标和相对于测试位置的坐标之间移动边界框 + - `assertEntityData` - 断言提供的块位置的实体匹配谓词。 + - `hurt` - 从某个来源对实体造成指定数量的伤害。 + - `kill` - 杀死实体。 + - `GameTestInfo#getTestOrigin` - 获取测试的生成结构的原点。 + - `StructureUtils#getStartCorner` - 获取要运行的测试的起始位置。 +- `net.minecraft.network` + - `FriendlyByteBuf` + - `readVec3`、`writeVec3` - 读取和写入向量的静态方法。 + - `readContainerId`、`writeContainerId` - 读取和写入菜单标识符的方法。 + - `readChunkPos`、`writeChunkPos` - 读取和写入区块位置的方法。 + - `StreamCodec#composite` - 一个接受七个/八个参数的组合方法。 +- `net.minecraft.network.codec.ByteBufCodecs` + - `CONTAINER_ID` - 处理菜单标识符的流编解码器。 + - `ROTATION_BYTE` - 打包到字节中的旋转。 + - `LONG` - 长整型(64 字节)的流编解码器。 + - `OPTIONAL_VAR_INT` - 可选整数的流编解码器,当不存在时序列化 `0`,否则序列化存储值加一。 + - 使用此流编解码器无法正确发送 `-1`。 +- `net.minecraft.network.protocol.game` + - `ClientboundEntityPositionSyncPacket` - 一个同步实体位置的数据包。 + - `ClientboundPlayerRotationPacket` - 一个包含玩家旋转的数据包。 +- `net.minecraft.server` + - `MinecraftServer` + - `tickConnection` - Tick连接以处理数据包。 + - `reportPacketHandlingException` - 报告尝试处理数据包时抛出的异常 + - `pauseWhileEmptySeconds` - 确定当没有玩家在线时服务器应暂停多少刻。 + - `SuppressedExceptionCollector` - 一个处理被服务器抑制的异常的处理器。 +- `net.minecraft.server.commands.LookAt` - 一个接口,定义当命令运行时实体应该发生什么,通常是移动它以看向另一个实体。 +- `net.minecraft.server.level` + - `ChunkHolder#hasChangesToBroadcast` - 返回区块内是否有任何更新要发送给客户端。 + - `ChunkTaskDispatcher` - 区块的任务调度器。 + - `DistanceManager` + - `getSpawnCandidateChunks` - 返回玩家可以生成的所有区块。 + - `getTickingChunks` - 返回当前正在Tick的所有区块。 + - `ServerChunkCache#onChunkReadyToSend` - 将一个区块持有者添加到队列中进行广播。 + - `ServerEntityGetter` - 一个在 `ServerLevel` 上操作的实体获取器接口实现。 + - 替换了 `EntityGetter` 中缺失的方法 + - `ServerPlayer` + - `getTabListOrder` - 处理玩家标签页中循环切换玩家的顺序。 + - `getLastClientInput`、`setLastClientInput`、`getLastClientMoveIntent` - 处理服务器玩家如何解释客户端脉冲。 + - `commandSource` - 返回玩家的命令源。 + - `createCommandSourceStack` - 创建发出命令的玩家的源堆栈。 + - `ThrottlingChunkTaskDispatcher` - 一个设置最大同时执行区块数的区块任务调度器。 + - `TickingTracker#getTickingChunks` - 返回当前正在Tick的所有区块。 +- `net.minecraft.server.packs.repository.PackRepository#isAbleToClearAnyPack` - 重建选定的资源包并返回它是否与当前选定的资源包不同。 +- `net.minecraft.resources.DependantName` - 一个将某个注册表对象 `ResourceKey` 映射到一个值的引用对象。类似于 `Holder`,但作为一个函数式接口。 +- `net.minecraft.tags.TagKey#streamCodec` - 为标签键构造一个流编解码器。 +- `net.minecraft.util` + - `ARGB#vector3fFromRGB24` - 使用整数的低 24 位创建一个包含 RGB 分量的 `Vector3f`。 + - `BinaryAnimator` - 一个使用缓动函数在两个状态之间进行动画的基本动画器。 + - `ExtraCodecs` + - `NON_NEGATIVE_FLOAT` - 一个验证值不能为负的浮点编解码器。 + - `RGB_COLOR_CODEC` - 一个表示 RGB 颜色的整数、浮点数或三维向量浮点编解码器。 + - `nonEmptyMap` - 一个验证映射不为空的映射编解码器。 + - `Mth` + - `wrapDegrees` - 将度数设置为 (-180, 180] 范围内的值。 + - `lerp` - 使用分量在两个向量之间进行线性插值。 + - `length` - 获取空间中 2D 点的长度。 + - `easeInOutSine` - 一个从 (0,0) 开始,每 pi 在 1 和 0 之间交替的余弦函数。 + - `packDegrees`、`unpackDegrees` - 将 `float` 形式的度数存储到 `byte` 中并读取。 + - `RandomSource#triangle` - 使用三角形分布在两个 `float`(包含,不包含)之间返回一个随机的 `float`。 + - `StringRepresentable$EnumCodec#byName` - 通过字符串名称获取枚举,如果为 null 则获取提供的供应商值。 + - `TriState` - 一个表示三种可能状态的枚举:true、false 或 default。 +- `net.minecraft.util.datafix.ExtraDataFixUtils` + - `patchSubType` - 将第一个类型中的第二个类型重写为第三个类型。 + - `blockState` - 返回方块状态的动态实例 + - `fixStringField` - 修改动态中的字符串字段。 +- `net.minecraft.util.thread.BlockableEventLookup` + - `BLOCK_TIME_NANOS` - 返回事件将阻塞线程的时间(以纳秒为单位)。 + - `isNonRecoverable` - 返回异常是否可以恢复。 +- `net.minecraft.world.damagesource.DamageSources` + - `enderPearl` - 返回末影珍珠击中时的伤害来源。 + - `mace` - 返回一个实体直接用狼牙棒击中另一个实体时的伤害来源。 +- `net.minecraft.world.entity` + - `Entity` + - `applyEffectsFromBlocks` - 通过 `Block#entityInside` 或硬编码检查(如雪或雨)应用方块产生的任何效果。 + - `isAffectedByBlocks` - 返回实体在内部时是否受方块影响。 + - `checkInsideBlocks` - 获取玩家经过的所有方块,并检查实体是否在一个方块内,如果存在则将其添加到一个集合中。 + - `oldPosition`、`setOldPosAndrot`、`setOldPos`、`setOldRot` - 更新实体最后位置和旋转的辅助方法。 + - `getXRot`、`getYRot` - 返回给定部分刻下实体的线性插值旋转。 + - `isAlliedTo(Entity)` - 返回实体是否与此实体结盟。 + - `teleportSetPosition` - 通过 `DimensionTransition` 设置被传送实体的位置和旋转数据 + - `getLootTable` - 返回实体应使用的战利品表的 `ResourceKey`(如果存在)。 + - `isControlledByOrIsLocalPlayer` - 返回实体是否是本地玩家或由本地玩家控制。 + - `shouldPlayLavaHurtSound` - 当为 `true` 时,实体受到熔岩伤害时播放熔岩伤害声音。 + - `onRemoval` - 当实体被移除时调用的方法。 + - `cancelLerp` - 停止任何插值移动。 + - `forceSetRotation` - 设置实体的旋转。 + - `isControlledByClient` - 返回实体是否由客户端输入控制。 + - `EntityType` + - `getDefaultLootTable` 现在返回一个 `Optional`,以防战利品表不存在 + - `$Builder#noLootTable` - 设置实体类型在死亡时不生成战利品。 + - `$Builder#build` 现在接受实体类型的资源键 + - `EntitySelector#CAN_BE_PICKED` - 返回一个选择器,获取所有不在旁观模式下的可拾取实体。 + - `LivingEntity` + - `dropFromShearingLootTable` - 使用剪毛上下文解析战利品表。 + - `getItemHeldByArm` - 返回特定手臂持有的堆栈。 + - `getEffectiveGravity` - 返回应用于实体的重力。 + - `canContinueToGlide` - 返回实体是否仍能在空中滑翔。 + - `getItemBlockingWith` - 返回玩家当前正在格挡的堆栈。 + - `canPickUpLoot` - 返回实体是否可以拾取物品。 + - `dropFromGiftLootTable` - 使用礼物上下文解析战利品表。 + - `handleExtraItemsCreatedOnUse` - 处理活体实体因使用另一个物品而获得新物品时的情况。 + - `isLookingAtMe` - 检查提供的实体是否正在看着这个实体。 + - `PositionMoveRotation` - 用于处理上下文中实体的位置和旋转的辅助工具。 + - `WalkAnimationState#stop` - 停止实体的行走动画。 +- `net.minecraft.world.entity.ai.attributes` + - `AttributeInstance` + - `getPermanentModifiers` - 返回应用于实体的所有永久修饰符。 + - `addPermanentModifiers` - 添加一组要应用的永久修饰符。 + - `AttributeMap#assignPermanentModifiers` - 从另一个映射复制永久修饰符。 +- `net.minecraft.world.entity.ai.control.Control#rotateTowards` - 返回一个浮点数,通过提供的差值在钳制值内旋转到某个最终旋转。 +- `net.minecraft.world.entity.ai.goal.Goal#getServerLevel` - 根据实体或等级获取服务器等级。 +- `net.minecraft.world.entity.ai.navigation.PathNavigation` + - `updatePathfinderMaxVisitedNodes` - 更新实体可以访问的最大节点数。 + - `setRequiredPathLength` - 设置实体必须采取的最小路径长度。 + - `getMaxPathLength` - 返回实体可以采取的最大路径长度。 +- `net.minecraft.world.entity.ai.sensing` + - `PlayerSensor#getFollowDistance` - 返回此实体的跟随距离。 + - `Sensor#wasEntityAttackableLastNTicks` - 返回一个谓词,检查实体在指定的刻数内是否可攻击。 +- `net.minecraft.world.entity.ai.village.poi.PoiRecord#pack`、`PoiSection#pack` - 打包必要的兴趣点信息。这只移除了脏 runnable。 +- `net.minecraft.world.entity.animal` + - `AgeableWaterCreature` - 一个有年龄状态的水生生物。 + - `Animal` + - `createAnimalAttributes` - 为动物创建属性提供者。 + - `playEatingSound` - 播放动物在进食时发出的声音。 + - `Bee#isNightOrRaining` - 返回当前等级是否有天空光,并且是夜晚或下雨。 + - `Cat#isLyingOnTopOfSleepingPlayer` - 返回猫是否在睡觉的玩家身上。 + - `Salmon#getSalmonScale` - 返回应用于实体边界框的缩放因子。 + - `Wolf#DEFAULT_TAIL_ANGLE` - 返回狼的默认尾巴角度。 +- `net.minecraft.world.entity.boss.enderdragon.DragonFlightHistory` - 保存龙在天空飞行时的 Y 轴和旋转。用于更好地动画龙身体部位的运动。 +- `net.minecraft.world.entity.monster.Zombie#canSpawnInLiquids` - 当为 true 时,僵尸可以在液体中生成。 +- `net.minecraft.world.entity.player` + - `Inventory` + - `isUsableForCrafting` - 返回状态是否可以用于合成配方。 + - `createInventoryUpdatePacket` - 创建用于更新库存中物品的数据包。 + - `Player` + - `handleCreativeModeItemDrop` - 处理玩家从创造模式丢弃物品时的情况。 + - `shouldRotateWithMinecart` - 返回玩家是否也应随矿车旋转。 + - `canDropItems` - 当为 `true` 时,玩家可以从菜单中丢弃物品。 + - `getPermissionLevel`、`hasPermissions` - 返回玩家的权限。 + - `StackedContents` - 保存内容列表及其关联大小。 + - `$Output` - 一个接口,定义在选择内容时如何接受它们。 +- `net.minecraft.world.entity.projectile.Projectile` + - `spawnProjectileFromRotation` - 生成一个投射物并从给定的旋转角度射出。 + - `spawnProjectileUsingShoot` - 生成一个投射物并通过 `#shoot` 设置初始冲量。 + - `spawnProjectile` - 生成一个投射物。 + - `applyOnProjectileSpawned` - 根据给定的等级和 `ItemStack` 应用任何额外的配置。 + - `onItemBreak` - 处理发射投射物的物品损坏时的情况。 + - `shouldBounceOnWorldBorder` - 返回投射物是否应弹离世界边界。 + - `setOwnerThroughUUID` - 通过查询其 UUID 来设置投射物的拥有者。 + - `$ProjectileFactory` - 定义投射物如何从某个 `ItemStack` 由实体生成。 +- `net.minecraft.world.entity.vehicle` + - `AbstractBoat` - 一个代表船的实体。 + - `AbstractChestBoat` - 一个代表带有某种库存的船的实体。 + - `ChestRaft` - 一个代表带有某种库存的木筏的实体。 + - `Raft` - 一个代表木筏的实体。 +- `net.minecraft.world.inventory.AbstractContainerMenu` + - `addInventoryHotbarSlots` - 在给定的 x 和 y 位置为给定容器添加快捷栏槽位。 + - `addInventoryExtendedSlots` - 在给定的 x 和 y 位置为给定容器添加玩家库存槽位。 + - `addStandardInventorySlots` - 在给定的 x 和 y 位置为给定容器在其正常位置添加快捷栏和玩家库存槽位。 + - `setSelectedBundleItemIndex` - 切换槽位中选中的捆绑包。 +- `net.minecraft.world.item` + - `BundleItem` + - `getOpenBundleModelFrontLocation`、`getOpenBundleModelBackLocation` - 返回捆绑包的模型位置。 + - `toggleSelectedItem`、`hasSelectedItem`、`getSelectedItem`、`getSelectedItemStack` - 处理捆绑包内的物品选择。 + - `getNumberOfItemsToShow` - 确定一次显示的捆绑包中的物品数量。 + - `getByColor` - 处理从捆绑包到染色捆绑包的可用链接。 + - `getAllBundleItemColors` - 返回所有染色捆绑包的流。 + - `ItemStack` + - `clearComponents` - 清除对堆栈所做的补丁,而不是物品组件。 + - `isBroken` - 返回堆栈是否已损坏。 + - `hurtWithoutBreaking` - 损坏堆栈而不使其损坏。 + - `getStyledHoverName` - 获取堆栈的样式化名称组件。 +- `net.minecraft.world.item.component.BundleContents` + - `canItemBeInBundle` - 物品是否可以放入捆绑包。 + - `getNumberOfItemsToShow` - 确定一次显示的捆绑包中的物品数量。 + - `hasSelectedItem`、`getSelectedItem` - 处理捆绑包内的物品选择。 +- `net.minecraft.world.item.enchantment.EnchantmentHelper` + - `createBook` - 创建一个附魔书堆栈。 + - `doPostAttackEffectsWithItemSourceOnBreak` - 在物品损坏时,在攻击后应用附魔。 +- `net.minecraft.world.level` + - `BlockCollisions` 有一个接受 `CollisionContext` 的构造函数 + - `BlockGetter#boxTraverseBlocks` - 返回在给定边界框中沿向量遍历的位置的迭代器。 + - `CollisionGetter` + - `noCollision` - 返回实体与方块、实体以及如果提供的 `boolean` 为 `true` 则液体之间是否存在碰撞。 + - `getBlockAndLiquidCollisions` - 返回边界框内实体的方块和液体碰撞。 + - `clipIncludingBorder` - 获取指定剪辑上下文的方块命中结果,必要时由世界边界钳制。 + - `EmptyBlockAndTintGetter` - 一个虚拟的 `BlockAndTintGetter` 实例。 + - `GameType#isValidId` - 检查 id 是否与现有的游戏类型匹配。 + - `LevelHeightAccessor#isInsideBuildHeight` - 返回指定的 Y 坐标是否在等级的边界内。 +- `net.minecraft.world.level.block` + - `Block#UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE` - 一个方块标志,当启用时,不更新红石线的形状。 + - `BonemealableFeaturePlacerBlock` - 一个放置配置功能并可以施骨粉的方块。 +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData#resetStatistics` - 将生成的数据重置为空设置,但不清除当前生物或下一个生成实体。 +- `net.minecraft.world.level.block.piston.PistonMovingBlockEntity#getPushDirection` - 返回移动活塞的推动方向。 +- `net.minecraft.world.level.block.state` + - `BlockBehaviour` + - `getEntityInsideCollisionShape`、`$BlockStateBase#getEntityInsideCollisionShape` - 确定实体在内部时方块的体素形状。 + - `$Properties#overrideDescription` - 设置方块名称的翻译键。 + - `StateHolder` + - `getValueOrElse` - 返回属性的值,否则返回提供的默认值。 + - `getNullableValue` - 返回属性的值,如果不存在则返回 null。 +- `net.minecraft.world.level.block.state.properties.Property#getInternalIndex` - 当为 true 时将提供的布尔值转换为 0,否则转换为 1。 +- `net.minecraft.world.level.border.WorldBorder#clampVec3ToBound` - 将向量钳制在世界边界内。 +- `net.minecraft.world.level.chunk` + - `ChunkAccess#canBeSerialized` - 返回 true,允许将区块写入磁盘。 + - `ChunkSource#onSectionEmptinessChanged` - 当部分有数据时更新该部分。 + - `LevelChunkSection` + - `copy` - 制作区块部分的浅拷贝。 + - `setUnsavedListener` - 添加一个监听器,每当区块被标记为脏时,该监听器接受区块位置。 + - `$UnsavedListener` - 一个当区块被标记为脏时被调用的区块位置消费者。 + - `PalettedContainerRO#copy` - 创建 `PalettedContainer` 的浅拷贝。 + - `UpgradeData#copy` - 创建 `UpgradeData` 的深拷贝。 +- `net.minecraft.world.level.chunk.storage.IOWorker#store` - 将区块的写入存储到工作器中。 +- `net.minecraft.world.level.levelgen` + - `SurfaceRules$Context#getSeaLevel`、`SurfaceSystem#getSeaLevel` - 获取生成器设置的海平面。 + - `WorldOptions#testWorldWithRandomSeed` - 使用随机生成的种子创建一个测试世界。 +- `net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator$Context#checkBlock` - 检查给定位置的方块是否匹配谓词。 +- `net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate` + - `getJigsaws` - 返回在给定位置和旋转下具有给定旋转的拼图方块。 + - `getJointType` - 返回拼图方块的连接类型。 + - `$JigsawBlockInfo` - 一个包含拼图方块块信息的记录。 + - 大多数涉及拼图的方法已将 `$StructureBlockInfo` 替换为 `$JigsawBlockInfo`。 +- `net.minecraft.world.level.lighting.LayerLightSectionStorage#lightOnInColumn` - 返回零节点部分位置是否有光。 +- `net.minecraft.world.level.pathfinder.PathFinder#setMaxVisitedNodes` - 设置可以访问的最大节点数。 +- `net.minecraft.world.level.portal` + - `DimensionTransition#withRotation` - 更新实体的生成旋转。 + - `PortalShape#findAnyShape` - 找到一个可以位于给定方块位置并面向特定方向的 `PortalShape`。 +- `net.minecraft.world.phys` + - `AABB` + - `clip` - 在给定边界框内剪辑向量,如果没有交点则返回空 optional。 + - `collidedAlongVector` - 返回此框是否与列表中提供的边界框之一沿提供的移动向量发生碰撞。 + - `getBottomCenter` - 获取边界框底部中心的向量。 + - `Vec3` + - `add`、`subtract` - 平移向量并返回一个新对象。 + - `horizontal` - 返回向量的水平分量。 + - `projectedOn` - 获取表示此向量投影到另一个向量上的单位向量。 +- `net.minecraft.world.phys.shapes` + - `CollisionContext` + - `of(Entity, boolean)` - 创建一个新的实体碰撞上下文,其中 `boolean` 确定实体是否总是可以站在提供的流体状态上。 + - `getCollisionShape` - 返回碰撞到的碰撞形状。 + - `VoxelShape#move(Vec3)` - 将体素形状按提供的向量偏移。 +- `net.minecraft.world.ticks.ScheduledTick#toSavedTick` - 将计划Tick转换为保存的Tick。 + +### 变更列表 + +- `F3 + F` 现在切换雾渲染 +- `com.mojang.blaze3d.platform` + - `NativeImage` + - `getPixelRGBA`、`setPixelRGBA` 现在是私有的。它们分别被 `getPixel` 和 `setPixel` 取代 + - `getPixelsRGBA` -> `getPixels` + - `Window#updateDisplay` 现在接受一个 `TraceyFrrameCapture`,或 `null` +- `net.minecraft.Util` + - `backgroundExecutor`、`ioPool` 和 `nonCriticalIoPool` 现在返回 `TracingExecutor` 而不是 `ExecutorService` + - `wrapThreadWithTaskName` -> `runNamed`,参数翻转,无返回值 +- `net.minecraft.advancements.critereon` + - `KilledByCrossbowTrigger` -> `KilledByArrowTrigger`,不是一对一,接受相关的堆栈 + - `PlayerPredicate` 现在可以匹配玩家的输入 +- `net.minecraft.client` + - `Minecraft` + - `debugFpsMeterKeyPress` -> `ProfilerPieChart#profilerPieChartKeyPress`,通过 `Minecraft#getDebugOverlay` 然后 `DebugScreenOverlay#getProfilerPieChart` 获得 + - `getTimer` -> `getDeltaTracker` + - `getToasts` -> `getToastManager` + - `Options#setModelPart` 现在是公开的,取代了 `toggleModelPart` 但不广播更改 + - `ParticleStatus` -> `net.minecraft.server.level.ParticleStatus` +- `net.minecraft.client.animation.KeyframeAnimations#animate` 现在接受一个 `Model` 而不是 `HierarchicalModel` +- `net.minecraft.client.gui.Font` + - `drawInBatch(String, float, float, int, boolean, Matrix4f, MultiBufferSource, Font.DisplayMode, int, int, boolean)` 已移除,应使用 `Component` 替代 + - 还有一个委托,在 `Component` 的 `drawInBatch` 方法中默认将逆深度布尔值设置为 true + - `$StringRenderOutput` 现在接受 `Font`、一个可选背景颜色,以及一个布尔值,表示在绘制文本时是否应使用逆深度 + - `$StringRenderOutput#finish` 现在是包私有的 +- `net.minecraft.client.gui.components` + - `AbstractSelectionList` + - `replaceEntries` 现在是公开的 + - `getRowTop`、`getRowBottom` 现在是公开的 + - `PlayerFaceRenderer#draw(GuiGraphics, ResourceLocation, int, int, int, int)` 接受一个 `PlayerSkin` 而不是 `ResourceLocation` +- `net.minecraft.client.gui.components.toasts` + - `Toast` + - `Toast$Visibility render(GuiGraphics, ToastComponent, long)` -> `void render(GuiGraphics, Font, long)` + - `slotCount` - `occupiedSlotCount` + - `ToastComponent` -> `ToastManager` +- `net.minecraft.client.gui.font.glyphs.BakedGlyph` + - `render` 现在接受一个表示颜色的整数而不是四个浮点数,并且是私有的 + - `renderChar` 是公共替代,接受 `$GlyphInstance`、`Matrix4f`、`VertexConsumer` 和颜色整数 + - `$Effect` 是一个记录,现在接受一个表示颜色的整数而不是四个浮点数 +- `net.minecraft.client.gui.screens` + - `LoadingOverlay#MOJANG_STUDIOS_LOGO_LOCATION` 现在是公开的 + - `Screen` + - `renderBlurredBackground(float)` -> `renderBlurredBackground()` + - `wrapScreenError` -> `fillCrashDetails`,不是一对一,因为它只添加相关的崩溃信息,而不是实际抛出错误 +- `net.minecraft.client.gui.screens.inventory` + - `AbstractContainerScreen#renderSlotHighlight` -> `renderSlotHighlightBack`、`renderSlotHighlightFront`,现在是私有的 + - `BookEditScreen` 现在接受 `WritableBookContent` + - `AbstractSignEditScreen` + - `sign` 现在是 protected + - `renderSignBackground` 不再接受 `BlockState` + - `EffectRenderingInventoryScreen` -> `Screen#hasActiveEffects`、`EffectsInInventory`。不是一对一,因为 `EffectsInInventory` 现在作为一个辅助类,帮助屏幕在指定位置渲染其效果。 +- `net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent` + - `getHeight()` -> `getHeight(Font)` + - `renderImage` 现在接受渲染工具提示的 `int` 宽度和高度 +- `net.minecraft.client.gui.screens.recipebook` + - `GhostSlots#render` 不再接受 x 和 y 偏移。 + - `RecipeBookComponent` 不再接受 x 和 y 偏移。 +- `net.minecraft.client.gui.screens.reporting.ReportReasonSelectionScreen` 现在接受一个 `ReportType` +- `net.minecraft.client.gui.screens.worldselection` + - `CreateWorldScreen` + - `$DataPackReloadCookie` -> `DataPackReloadCookie` + - `openFresh` 现在有一个接受 `CreateWorldCallback` 的重载 + - `WorldCreationContext` 现在接受 `InitialWorldCreationOptions` + - `WorldOpenFlows#createFreshLevel` 接受一个 `Function` 而不是 `Function` +- `net.minecraft.client.gui.spectator.SpectatorMenuItem#renderIcon` 现在接受一个 `float` 而不是 `int` 来表示 alpha 值 +- `net.minecraft.client.multiplayer` + - `ClientLevel` 现在接受一个 `int` 表示海平面 + - `getSkyColor` 现在返回一个 `int` 而不是 `Vec3` + - `getCloudColor` 现在返回一个 `int` 而不是 `Vec3` + - `setGameTime`、`setDayTime` -> `setTimeFromServer` + - `TagCollector` -> `RegistryDataCollector$TagCollector`,现在是包私有的 +- `net.minecraft.client.player` + - `AbstractClientPlayer#getFieldOfViewModifier` 现在接受一个布尔值表示相机是否在第一人称,以及一个浮点数表示部分刻 + - `Input` -> `ClientInput` 和 `net.minecraft.world.entity.player.Input` + - `KeyboardInput` 现在继承 `ClientInput` + - `LocalPlayer#input` 现在是 `ClientInput` +- `net.minecraft.client.renderer` + - `DimensionSpecialEffects#getSunriseColor` -> `getSunriseOrSunsetColor` + - `GameRenderer` + - `processBlurEffect` 不再接受部分刻 `float` + - `getFov` 返回 `float` 而不是 `double` + - `getProjectionMatrix` 现在接受 `float` 而不是 `double` + - `ItemModelShaper` + - `shapes` 现在是私有的 + - `getItemModel(Item)` 已移除 + - `getItemModel(ResourceLocation)` - 获取与提供的 `ResourceLocation` 关联的烘焙模型。 + - `register` 已移除 + - `getModelManager` 已移除 + - `invalidateCache` - 清除模型映射。 + - `LevelRenderer` + - `renderSnowAndRain` -> `WeatherEffectRenderer` + - `tickRain` -> `tickParticles` + - `renderLevel` 现在接受一个 `GraphicsResourceAllocator` + - `renderClouds` -> `CloudRenderer` + - `addParticle` 现在是公开的 + - `globalLevelEvent` -> `LevelEventHandler` + - `entityTarget` -> `entityOutlineTarget` + - `$TransparencyShaderException` 不再接受可抛出原因 + - `SectionOcclusionGraph` + - `onSectionCompiled` -> `schedulePropagationFrom` + - `update` 现在接受一个保存当前加载的部分节点的 `LongOpenHashSet` + - `$GraphState` 现在是包私有的 + - `addSectionsInFrustum` 现在接受一个列表以添加渲染部分 + - `ShapeRenderer#renderShape` 现在接受一个整数表示颜色,而不是四个浮点数 + - `ViewArea` + - `repositionCamera` 现在接受 `SectionPos` 而不是两个 `double` + - `getRenderSectionAt` -> `getRenderSection` +- `net.minecraft.client.renderer.blockentity` + - `BannerRenderer#renderPatterns` 现在接受一个 `boolean` 确定要使用的闪光渲染类型 + - 构造 `LayerDefinition` 的 `*Renderer` 类现在已移动到它们关联的 `*Model` 类 + - `SignRenderer$SignModel` -> `SignModel` +- `net.minecraft.client.renderer.chunk.SectionRenderDispatcher` 现在接受一个 `TracingExecutor` 而不是仅仅一个 `Executor` + - `$CompiledSection#hasNoRenderableLayers` -> `hasRenderableLayers` + - `$RenderSection` 现在接受一个编译的 `long` 表示部分节点 + - `setOrigin` -> `setSectionNode` + - `getRelativeOrigin` -> `getNeighborSectionNode` + - `cancelTasks` 现在不返回任何内容 + - `pointOfView` - 一个指向半透明渲染类型渲染位置的引用。 + - `resortTransparency` 不再接受 `RenderType` 并且不返回任何内容 + - `hasTranslucentGeometry` - 返回编译的方块是否具有半透明渲染类型。 + - `transparencyResortingScheduled` - 返回最后一个任务是否已调度但未完成。 + - `isAxisAlignedWith` -> `$TranslucencyPointOfView#isAxisAligned` + - `$CompileTask` 现在是公开的 + - 不再实现 `Comparable` + - 构造函数不再接受创建时的距离 + - `isHighPriority` -> `isRecompile` + - `$TranslucencyPointOfView` - 返回表示此部分中半透明渲染类型视图点的坐标。 +- `net.minecraft.client.renderer.culling.Frustum#cubeInFrustum` 现在返回一个 `int`,表示剔除该框的第一个平面的索引 +- `net.minecraft.client.renderer.DebugRenderer#render` 现在接受 `Frustum` +- `net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations#loadPaletteEntryFromImage` 现在是私有的 +- `net.minecraft.client.tutorial` + - `Tutorial` + - `addTimedToast`、`#removeTimedToast`、`$TimedToast` -> `TutorialToast` 参数 + - `onInput` 接受一个 `ClientInput` 而不是 `Input` + - `TutorialStepInstance` + - `onInput` 接受一个 `ClientInput` 而不是 `Input` +- `net.minecraft.core` + - `Direction` + - `getNearest` -> `getApproximateNearest` + - `getNormal` -> `getUnitVec3i` + - `HolderGetter$Provider#get` 不再接受注册表键,而是从 `ResourceKey` 读取它 + - `HolderLookup$Provider` 现在实现 `HolderGetter$Provider` + - `asGetterLookup` 已移除,因为该接口是一个 `HolderGetter$Provider` + - `listRegistries` -> `listRegistryKeys` + - `Registry` 现在实现 `HolderLookup$RegistryLookup` + - `getTags` 只返回命名持有者集的流 + - `asTagAddingLookup` -> `prepareTagReload` + - `bindTags` -> `WritabelRegistry#bindTag` + - `get` -> `getValue` + - `getOrThrow` -> `getValueOrThrow` + - `getHolder` -> `get` + - `getHolderOrThrow` -> `getOrThrow` + - `holders` -> `listElements` + - `getTag` -> `get` + - `holderOwner`、`asLookup` 已移除,因为 `Registry` 是它们的实例 + - `RegistryAccess` + - `registry` -> `lookup` + - `registryOrThrow` -> `lookupOrThrow` + - `RegistrySynchronization#NETWORKABLE_REGISTRIES` -> `isNetworkable` +- `net.minecraft.core.cauldron.CauldronInteraction` + - `FILL_WATER` -> `fillWaterInteraction`,现在是私有的 + - `FILL_LAVA` -> `fillLavaInteraction`,现在是私有的 + - `FILL_POWDER_SNOW` -> `fillPowderSnowInteraction`,现在是私有的 + - `SHULKER_BOX` -> `shulkerBoxInteraction`,现在是私有的 + - `BANNER` -> `bannerInteraction`,现在是私有的 + - `DYED_ITEM` -> `dyedItemIteration`,现在是私有的 +- `net.minecraft.core.dispenser.BoatDispenseItemBehavior` 现在接受要生成的 `EntityType` 而不是变体和是否有箱子的布尔值 +- `net.minecraft.core.particles.DustColorTransitionOptions`、`DustParticleOptions` 现在接受表示 RGB 值的整数而不是 `Vector3f`。 +- `net.minecraft.data.loot` + - `BlockLootSubProvider` + - `HAS_SHEARS` -> `hasShears` + - `createShearsOnlyDrop` 现在是一个实例方法 + - `EntityLootSubProvider` + - `killedByFrog`、`killedByFrogVariant` 现在接受 `EntityType` 注册表的获取器 + - `createSheepTable` -> `createSheepDispatchPool`,不是一对一,因为该表被替换为一个池构建器,给定一个染料颜色到战利品表的映射 +- `net.minecraft.gametest.framework` + - `GameTestHelper#assertEntityPresent`、`assertEntityNotPresent` 接受一个边界框而不是两个向量 + - `GameTestInfo#getOrCalculateNorthwestCorner` 现在是公开的 +- `net.minecraft.network.chat.Component#score` 现在接受一个 `SelectorPattern` +- `net.minecraft.network.chat.contents.ScoreContents`、`SelectorContents` 现在是一个记录 +- `net.minecraft.network.protocol.login.ClientboundGameProfilePacket` -> `ClientboundLoginFinishedPacket` +- `net.minecraft.network.protocol.game` + - `ClientboundMoveEntityPacket#getyRot`、`getxRot` 现在返回一个 `float` 表示度数 + - `ClientboundPlayerPositionPacket` 现在是一个记录,接受一个表示变化的 `PositionMoverotation` + - `relativeArguments` -> `relatives` + - `yRot`、`xRot` -> `ClientboundPalyerRotationPacket` + - `ClientboundSetTimePacket` 现在是一个记录 + - `ClientboundRotateHeadPacket#getYHeadRot` 现在返回一个 `float` 表示度数 + - `ClientboundTeleportEntityPacket` 现在是一个记录,其中必要的参数被传入数据包而不是实体 + - `ServerboundPlayerInputPacket` 现在是一个记录,接受一个 `Input` +- `net.minecraft.resources.RegistryDataLoader$Loader#loadFromNetwork` 现在接受一个 `$NetworkedRegistryData`,其中包含打包的注册表条目 +- `net.minecraft.server` + - `MinecraftServer` 不再实现 `AutoCloseable` + - `tickChildren` 现在是 protected + - `wrapRunnable` 现在是公开的 + - `ReloadableServerRegistries#reload` 现在接受一个待处理标签的列表,并返回一个 `$LoadResult` 而不是一个分层注册表访问 + - `ReloadableServerResources` + - `loadResources` 现在接受一个待处理标签的列表和服务器 `Executor` + - `updateRegistryTags` -> `updateStaticRegistryTags` + - `ServerFunctionLibrary#getTag`、`ServerFunctionManager#getTag` 返回一个命令函数列表 +- `net.minecraft.server.level` + - `ChunkHolder` + - `blockChanged`、`sectionLightChanged` 现在返回 `boolean` 表示信息是否已更改 + - `addSaveDependency` 现在是 protected,`GenerationChunkHolder` 中的一个方法 + - `ChunkTaskPriorityQueue` 不再接受泛型 + - 构造函数不再接受最大任务数 + - `submit` 现在接受一个 `Runnable` 而不是 `Optional` + - `pop` 返回一个 `$TasksForChunk` 而不是原始的 `Stream` + - `ChunkTaskPriorityQueueSorter` -> `ChunkTaskDispatcher` + - `ServerPlayer` + - `teleportTo` 接受一个 `boolean`,决定是否应设置相机 + - `INTERACTION_DISTANCE_VERIFICATION_BUFFER` -> `BLOCK_INTERACTION_DISTANCE_VERIFICATION_BUFFER` + - 还拆分为 `ENTITY_INTERACTION_DISTANCE_VERIFICATION_BUFFER`,设置为 3.0 + - `findRespawnPositionAndUseSpawnBlock` 现在处理 `TeleportTransition` + - `TextFilterClient` -> `ServerTextFilter` + - `ThreadedLevelLightEngine` 现在接受一个 `ConsecutiveExecutor` 和 `ChunkTaskDispatcher` 而不是分别接受 `ProcessorMailbox` 和 `ProcessorHandle` +- `net.minecraft.server.packs.resources.ProfiledReloadInstance$State` 现在是一个记录 +- `net.minecraft.sounds.SoundEvent` 现在是一个记录 +- `net.minecraft.tags` + - `TagEntry$Lookup#element` 现在接受一个 `boolean` 表示元素是否必需 + - `TagLoader` 现在接受一个 `$ElementLookup`,其功能与之前的函数参数相同 + - `build` 现在返回一个列表的值 + - `loadAndBuild` -> `loadTagsFromNetwork`、`loadTagsForExistingRegistries`、`loadTagsForRegistry`、`buildUpdatedLookups` + - `TagNetworkSerialization$NetworkPayload` + - `size` -> `isEmpty` + - `applyToRegistry` -> `resolve` +- `net.minecraft.util` + - `FastColor` -> `ARGB` + - `scaleRGB` 重载,带有一个 alpha 整数和三个浮点数。 + - `Mth#color` -> `ARGB#color` +- `net.minecraft.util.profiling.metrics.MetricCategory#MAIL_BOXES` -> `CONSECUTIVE_EXECUTORS` +- `net.minecraft.util.thread` + - `BlockableEventLoop#waitForTasks` 现在是 protected + - `ProcessorMailbox` 不再实现 `AutoCloseable` +- `net.minecraft.util.worldupdate.WorldUpgrader` 实现 `AutoCloseable` +- `net.minecraft.world.LockCode` 现在接受一个 `ItemPredicate` 而不是表示物品名称的 `String` + - `addToTag`、`fromTag` 现在接受一个 `HolderLookup$Provider` +- `net.minecraft.world.effect` + - `MobEffect#applyEffectTick`、`applyInstantenousEffect`、`onMobRemoved`、`onMobHurt` 现在接受 `ServerLevel` + - `MobEffectInstance#onMobRemoved`、`onMobHurt` 现在接受 `ServerLevel` +- `net.minecraft.world.entity` + - `AgeableMob$AgeableMobGroupData` 现在有一个公共构造函数 + - `AnimationState#getAccumulatedTime` -> `getTimeInMillis` + - `Entity` 不再实现 `CommandSource` + - `setOnGroundWithMovement` 现在接受一个额外的 `boolean` 表示是否有任何水平碰撞。 + - `getInputVector` 现在是 protected + - `isAlliedTo(Entity)` -> `considersEntityAsAlly` + - `teleportTo` 现在接受一个额外的 `boolean`,决定是否应设置相机 + - `checkInsideBlocks()` -> `recordMovementThroughBlocks`,不是一对一,因为它接受移动向量 + - `checkInsideBlocks(Set)` -> `collectBlockCollidedWith`,现在是私有的 + - `kill` 现在接受 `ServerLevel` + - `hurt` 已被标记为已弃用,将被 `hurtServer` 和 `hurtClient` 取代 + - `hurtOrSimulate` 作为一个辅助方法,决定调用哪一个,也被标记为已弃用 + - `spawnAtLocation` 现在接受 `ServerLevel` + - `isInvulnerableTo` -> `isInvulnerableToBase`,现在是 protected 和 final + - `isInvulnerableTo` 已移至 `LivingEntity#isInvulnerableTo` + - `teleportSetPosition` 现在是公开的,接受 `PositionMoveRotation` 和 `Relative` 集而不是 `DimensionTransition` + - `createCommandSourceStack` -> `createCommandSourceStackForNameResolution`,不是一对一,因为它接受 `ServerLevel` + - `mayInteract` 现在接受 `ServerLevel` 而不是仅仅 `Level` + - `setOldRot` 现在是公开的 + - `changeDimension` -> `teleport`,给定 `TeleportTransition` 返回 `ServerPlayer` + - `canChangeDimensions` -> `canTeleport` + - `EntitySpawnReason#SPAWN_EGG` -> `SPAWN_ITEM_USE`,不是一对一,因为这表示实体可以从任何物品生成 + - `EntityType` + - `create`、`loadEntityRecursive`、`loadEntitiesRecursive`、`loadStaticEntity` 现在接受一个 `EntitySpawnReason` + - `*StackConfig` 现在接受一个 `Level` 而不是 `ServerLevel` + - `EquipmentTable` 现在有一个构造函数,接受一个单一的 `float` 表示所有装备槽位的槽位掉落几率 + - `MobSpawnType` -> `EntitySpawnReason` + - `Leashable#tickLeash` 现在接受 `ServerLevel` + - `LivingEntity` + - `getScale` 现在是 final + - `onAttributeUpdated` 现在是 protected + - `activeLocationDependentEnchantments` 现在接受一个 `EquipmentSlot` + - `handleRelativeFrictionAndCalculateMovement` 现在是私有的 + - `updateFallFlying` 现在是 protected + - `onEffectRemoved` -> `onEffectsRemoved` + - `spawnItemParticles` 现在是公开的 + - `getLootTable` -> `Entity#getLootTable`,包装在 optional 中 + - `getBaseExperienceReward` 现在接受 `ServerLevel` + - `triggerOnDeathMobEffects` 现在接受 `ServerLevel` + - `canAttack` 已移除 + - `dropEquipment` 现在接受 `ServerLevel` + - `dropExperience` 现在接受 `ServerLevel` + - `dropFromLootTable` 现在接受 `ServerLevel` + - `actuallyHurt`、`doHurtTarget` 现在接受 `ServerLevel` + - `hasLineOfSight` 重载,带有剪辑上下文和眼睛 Y 提供者 + - `makePoofParticles` 现在是公开的 + - `Mob` + - `pickUpItem`、`wantsToPickUp` 现在接受 `ServerLevel` + - `equipItemIfPossible` 现在接受 `ServerLevel` + - `customServerAiStep` 现在接受 `ServerLevel` + - `dropPreservedEquipment` 现在接受 `ServerLevel` + - `NeutralMob` + - `isAngryAt`、`isAngryAtAllPlayers` 现在接受 `ServerLevel` + - `playerDied` 现在接受 `ServerLevel` + - `PortalProcessor#getPortalDestination` 现在返回一个 `TeleportTransition` + - `PositionMoveRotation` + - `of(ClientboundPlayerPositionPacket)` -> `ofEntityUsingLerpTarget(Entity)` + - `of(DimensionTransition)` -> `of(TeleportTransition)` + - `Shearable#shear` 现在接受 `ServerLevel` 和正在剪毛的 `ItemStack` + - `RelativeMovement` -> `Relative`,扩展为包含增量移动 + - `WalkAnimationState#update` 现在接受一个额外的 `float` 表示移动时的位置缩放。 +- `net.minecraft.world.entity.ai.behavior` + - `StartAttacking` 现在接受一个 `$TargetFinder` 和一个 `$StartAttackingCondition` + - 两者都是函数式接口,取代了之前的函数/谓词,但多了一个 `ServerLevel` 参数 + - `StopAttackingIfTargetInvalid` 现在接受一个 `$TargetErasedCallback` 和/或一个 `$StopAttackCondition` + - 两者都是函数式接口,取代了之前的消费者/谓词,但多了一个 `ServerLevel` 参数 + - `MeleeAttack#create` 现在可以接受一个谓词来测试生物 + - `Swim` 现在接受一个代表生物的泛型 +- `net.minecraft.world.entity.ai.control.LookControl#rotateTowards` -> `Control#rotateTowards` +- `net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal` 现在接受一个 `$Selector` + - 这是一个函数式接口,取代了之前的谓词,但多了一个 `ServerLevel` 参数 +- `net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.ai.sensing` + - `NearestLivingEntitySensor` + - `radiusXZ`、`radiusY` -> `Attributes#FOLLOW_RANGE` + - `isMatchingEntity` 现在接受一个 `ServerLevel` + - `Sensor` + - `TARGETING_RANGE` 现在是私有的 + - `isEntityTargetable`、`isEntityAttackable`、`isEntityAttackableIgnoringLineOfSight` 现在接受一个 `ServerLevel` + - `wasEntityAttackableLastNTicks`、`rememberPositives` 现在处理 `BiPredicate` 而不是 `Predicate` +- `net.minecraft.world.entity.ai.targeting.TargetingConditions` + - `selector` 现在接受一个 `$Selector` + - 这是一个函数式接口,取代了之前的谓词,但多了一个 `ServerLevel` 参数 + - `test` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.ai.village.poi.PoiRecord#codec`、`PoiSection#codec` -> `$Packed#CODEC` +- `net.minecraft.world.entity.animal` + - `Fox$Type` -> `$Variant` + - `MushroomCow$MushroomType` -> `$Variant` + - `$Variant` 不再接受战利品表 + - `Salmon` 现在有一个表示其大小的变体 + - `Wolf` + - `getBodyRollAngle` -> `#getShakeAnim`,不是一对一,因为角度是在渲染状态下计算的 + - `hasArmor` 已移除 +- `net.minecraft.world.entity.animal.horse.AbstractHorse#followMommy` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.boss.enderdragon.EnderDragon#onCrystalDestroyed` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.boss.enderdragon.phases.DragonPhaseInstance#doServerTick` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.boss.wither.WitherBoss#getHead*Rot` -> `getHead*Rots`,返回所有旋转而不是仅返回提供的索引 +- `net.minecraft.world.entity.decoration` + - `ArmorStand` 默认旋转现在是公开的 + - `isShowArms` -> `showArms` + - `isNoBasePlate` -> `showBasePlate` + - `PaintingVariant` 现在接受一个标题和作者 `Component` +- `net.minecraft.world.entity.item.ItemEntity#getSpin` 现在是静态的 +- `net.minecraft.world.entity.monster.Monster#isPreventingPlayerRest` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.monster.breeze.Breeze#getSnoutYPosition` -> `getFiringYPosition` +- `net.minecraft.world.entity.monster.hoglin.HoglinBase#hurtAndThrowTarget` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.monster.piglin.PiglinAi#isWearingGold` -> `#isWearingSafeArmor` +- `net.minecraft.world.entity.npc.InventoryCarrier#pickUpItem` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.player` + - `Player#disableShield` 现在接受要应用冷却时间的堆栈 + - `Inventory` + - `findSlotMatchingUnusedItem` -> `findSlotMatchingCraftingIngredient` + - `swapPaint` -> `setSelectedHotbarSlot` + - `StackedContents` -> `StackedItemContents` +- `net.minecraft.world.entity.projectile` + - `AbstractArrow#inGround` -> `IN_GROUND`,现在是一个 `EntityDataAccessor` + - 可通过 `isInGround` 和 `setInGround` 受保护访问 + - `ThrowableItemProjectile` 现在可以接受一个被投掷物品的 `ItemStack` +- `net.minecraft.world.entity.raid.Raid#getLeaderBannerInstance` -> `getOminousBannerInstance` +- `net.minecraft.world.entity.vehicle` + - `Boat$Type` 现在接受提供的船物品和物品的翻译键,但不再接受它们由什么木板制成 + - `ContainerEntity` + - `*LootTable*` -> `ContainerLootTable` + - `chestVehicleDestroyed` 现在接受一个 `ServerLevel` + - `VehicleEntity` + - `destroy` 现在接受一个 `ServerLevel` + - `getDropItem` 现在是 protected +- `net.minecraft.world.item` + - `BoatItem` 现在接受一个 `EntityType` 而不是变体和是否有箱子的布尔值 + - `ItemStack#hurtEnemy`、`postHurtEnemy` 现在接受一个 `LivingEntity` 而不是 `Player` + - `SmithingTemplateItem` 现在接受 `Item.Properties` 而不是硬编码,对于静态初始化器也是如此 + - `UseAnim` -> `ItemUseAnimation` +- `net.minecraft.world.item.crafting.ShulkerBoxColoring` -> `TransmuteRecipe`,扩展为将存储在物品上的任何数据复制到结果物品 +- `net.minecraft.world.item.enchantment.EnchantmentHelper` + - `onProjectileSpawned` 现在接受一个 `Projectile` 而不是 `AbstractArrow` +- `net.minecraft.world.item.enchantment.effects.DamageItem` -> `ChangeItemDamage` +- `net.minecraft.world.level` + - `GameRules` 在任何类型的构造期间都接受一个 `FeatureFlagSet` + - `$IntegerValue#create` 接受一个 `FeatureFlagSet` + - `$Type` 接受一个 `FeatureFlagSet` + - `Level` + - `setSpawnSettings` 不再接受一个 `boolean` 来决定是否生成友好生物 + - `getGameRules` -> `ServerLevel#getGameRules` + - `LevelAccessor` 现在实现 `ScheduledTickAccess`,一个现在包含原本在 `LevelAccessor` 上的Tick调度方法的接口 + - `neighborShapeChanged` 交换了 `BlockState` 和邻居 `BlockPos` 参数的顺序 + - `LevelHeightAccessor` + - `getMinBuildHeight` -> `getMinY` + - `getMaxBuildHeight` -> `getMaxY`,该值比之前版本少一 + - `getMinSection` -> `getMinSectionY` + - `getMaxSection` -> `getMaxSectionY`,该值比之前版本少一 + - `NaturalSpawner#spawnForChunk` 已被拆分为两个方法:`getFilteredSpawningCategories` 和 `spawnForChunk` +- `net.minecraft.world.level.biome#Biome#getPrecipitationAt`、`coldEnoughToSnow`、`warmEnoughToRain`、`shouldMeltFrozenOceanIcebergSlightly` 现在接受一个 `int` 表示生物群系的基础高度 +- `net.minecraft.world.level.block` + - `Block` + - `shouldRenderFace` 接受正在检查的面的相对状态,不再传入 `BlockGetter` 或 `BlockPos`。 + - `updateEntityAfterFallOn` -> `updateEntityMovementAfterFallOn` + - `$BlockStatePairKey` -> `FlowingFluid$BlockStatePairKey`,现在是包私有的 + - `getDescriptionId` -> `BlockBehaviour#getDescriptionId`,也是一个 protected 字段 `descriptionId` + - `ChestBlock` 构造函数交换了参数顺序 + - `Portal#getPortalDestination` 现在返回 `TeleportTransition` +- `net.minecraft.world.level.block.entity` + - `AbstractFurnaceBlockEntity#serverTick` 现在接受一个 `ServerLevel` 而不是 `Level` + - `BrushableBlockEntity` + - `brush` 现在接受等级和执行刷洗行为的堆栈 + - `unpackLootTable` 现在是私有的 + - `checkReset` 现在接受服务器等级 +- `net.minecraft.world.level.block.state` + - `BlockBehaviour` + - `getOcclusionShape`、`getLightBlock`、`propagatesSkylightDown` 只接受 `BlockState`,不接受 `BlockGetter` 或 `BlockPos` + - `getLootTable` 现在返回一个 `Optional`,也是一个 protected 字段 `drops` + - `$BlockStateBase#getOcclusionShape`、`getLightBlock`、`getFaceOcclusionShape`、`propagatesSkylightDown`、`isSolidRender` 不再接受 `BlockGetter` 或 `BlockPos` + - `$BlockStateBase#getOffset` 不再接受 `BlockGetter` + - `$OffsetFunction#evaluate` 不再接受 `BlockGetter` + - `$Properties#dropsLike` -> `overrideLootTable` + - `StateHolder#findNextInCollection` 现在接受一个 `List` 而不是 `Collection` +- `net.minecraft.world.level.chunk` + - `ChunkAccess` + - `addPackedPostProcess` 现在接受一个 `ShortList` 而不是一个 `short` + - `getTicksForSerialization` 现在接受一个 `long` 表示游戏时间 + - `unsaved` 现在是私有的 + - `setUnsaved` -> `markUnsaved`、`tryMarkSaved` + - `$TicksToSave` -> `$PackedTicks` + - `ChunkSource#setSpawnSettings` 不再接受一个 `boolean` 来决定是否生成友好生物 + - `LevelChunk#postProcessGeneration` 现在接受一个 `ServerLevel` + - `Palette#copy` 现在接受一个 `PaletteResize` +- `net.minecraft.world.level.chunk.status.WorldGenContext` 现在接受一个 `Executor` 或主线程,而不是一个处理器句柄邮箱 + - 构造函数还接受一个 `LevelChunk$UnsavedListener`,用于当区块被标记为脏时 +- `net.minecraft.world.level.chunk.storage` + - `ChunkSerializer` -> `SerializableChunkData` + - `ChunkStorage#write` 现在接受一个提供的 `CompoundTag` 而不是实例本身 + - `SectionStorage` 现在接受第二个泛型,表示存储数据的打包形式 + - 构造函数现在接受打包编解码器、一个将存储转换为打包格式的函数,以及一个将打包数据和脏 runnable 转换回存储的函数。 +- `net.minecraft.world.level.levelgen` + - `Aquifer$FluidStatus` 现在是一个记录 + - `WorldDimensions#withOverworld` 现在接受一个 `HolderLookup` 而不是 `Registry` 本身 + - `BlendingData` 现在有一个打包和解包状态,用于将内部数据序列化为一个简单对象 +- `net.minecraft.world.level.levelgen.material.MaterialRuleList` 现在接受一个数组而不是一个列表 +- `net.minecraft.world.level.levelgen.placement.PlacementContext#getMinBuildHeight` -> `getMinY` +- `net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement#getShuffledJigsawBlocks` 现在返回 `StructureTemplate$JigsawBlockInfo` +- `net.minecraft.world.level.lighting` + - `LevelLightEngine#lightOnInSection` -> `lightOnInColumn` + - `LightEngine` + - `hasDifferentLightProperties`、`getOcclusionShape` 不再接受 `BlockGetter` 或 `BlockPos` + - `getOpacity` 不再接受 `BlockPos` + - `shapeOccludes` 不再接受两个 `long` 表示打包的位置 +- `net.minecraft.world.level.material` + - `FlowingFluid` + - `spread` 现在接受当前位置的 `BlockState` + - `getSlopeDistance` 之前的参数已被合并到一个 `$SpreadContext` 对象中 + - `spread`、`getNewLiquid`、`canConvertToSource`、`getSpread` 现在接受一个 `ServerLevel` + - `Fluid` + - `tick` 现在接受当前位置的 `BlockState` + - `tick` 和 `randomTick` 现在接受 `ServerLevel` + - `FluidState` + - `tick` 现在接受当前位置的 `BlockState` + - `tick` 和 `randomTick` 现在接受 `ServerLevel` + - `MapColor#calculateRGBColor` -> `calculateARGBColor` +- `net.minecraft.world.level.portal` + - `DimensionTransition` -> `TeleportTransition` + - `pos` -> `position` + - `speed` -> `deltaMovement` + - 构造函数现在可以接受一组 `Relatives` 来指示位置应相对于另一个位置移动的哪些运动 + - `PortalShape#createPortalBlocks` 现在接受一个 `LevelAccessor` +- `net.minecraft.world.level.saveddata.SavedData#save(File, HolderLookup$Provider)` 现在返回 `CompoundTag`,不在方法中将数据写入文件 +- `net.minecraft.world.level.storage` + - `DimensionDataStorage` 现在实现 `AutoCloseable` + - 构造函数接受一个 `Path` 而不是 `File` + - `save` -> `scheduleSave` 和 `saveAndJoin` + - `LevelData#getGameRules` -> `ServerLevelData#getGameRules` +- `net.minecraft.world.phys.BlockHitResult` 现在接受一个布尔值表示是否击中了世界边界 + - 添加了两个辅助方法 `hitBorder`、`isWorldBorderHit` +- `net.minecraft.world.ticks` + - `ProtoChunkTicks#load` 现在接受一个保存的Tick列表 + - `SavedTick#loadTickList` 现在返回一个保存的Tick列表,而不是消费它们 + - `SerializableTickContainer#save` -> `pack` + +### 移除列表 + +- `com.mojang.blaze3d.Blaze3D` + - `process` + - `render` +- `com.mojang.blaze3d.pipeline.RenderPipeline` + - 被 `com.mojang.blaze3d.framegraph.*` 和 `com.mojang.blaze3d.resources.*` 取代 +- `com.mojang.blaze3d.platform.NativeImage` + - `setPixelLuminance` + - `getRedOrLuminance`、`getGreenOrLuminance`、`getBlueOrLuminance` + - `blendPixel` + - `asByteArray` +- `com.mojang.blaze3d.systems.RenderSystem` + - `glGenBuffers` + - `glGenVertexArrays` + - `_setShaderTexture` + - `applyModelViewMatrix` +- `net.minecraft.Util#wrapThreadWithTaskName(String, Supplier)` +- `net.minecraft.advancements.critereon.EntitySubPredicates#BOAT` +- `net.minecraft.client.Options#setKey` +- `net.minecraft.client.gui.screens.inventory.EnchantmentScreen#time` +- `net.minecraft.client.multiplayer` + - `ClientCommonPacketListenerImpl#strictErrorHandling` + - `ClientLevel#isLightUpdateQueueEmpty` + - `CommonListenerCookie#strictErrorHandling` +- `net.minecraft.client.particle.ParticleRenderType#PARTICLE_SHEET_LIT` +- `net.minecraft.client.renderer` + - `GameRenderer#resetProjectionMatrix` + - `LevelRenderer` + - `playJukeboxSong` + - `clear` + - `PostChain` + - `getTempTarget`、`addTempTarget` + - `PostPass` + - `setOrthoMatrix` + - `getFilterMode` +- `net.minecraft.client.renderer.block.model.BlockModel#fromString` +- `net.minecraft.client.renderer.texture` + - `AbstractTexture#blur`、`mipmap` + - `TextureManager#bindForSetup` +- `net.minecraft.commands.arguments.coordinates.WorldCoordinates#current` +- `net.minecraft.core` + - `Direction#fromDelta` + - `Registry#getOrCreateTag`、`getTagNames`、`resetTags` +- `net.minecraft.server.MinecraftServer` + - `isSpawningAnimals` + - `areNpcsEnabled` +- `net.minecraft.server.level` + - `GenerationChunkHolder#getGenerationRefCount` + - `ServerPlayer` + - `setPlayerInput` + - `teleportTo(ServerLevel, double, double, double, float, float, boolean)` +- `net.minecraft.tags` + - `TagManager` + - `TagManagerSerialization$TagOutput` +- `net.minecraft.world.entity` + - `AnimationState#updateTime` + - `Entity` + - `walkDist0`、`walkDist` + - `wasOnFire` + - `tryCheckInsideBlocks` + - `EntitySelector$MobCanWearArmorEntitySelector` +- `net.minecraft.world.entity.ai.sensing` + - `BreezeAttackEntitySensor#BREEZE_SENSOR_RADIUS` + - `TemptingSensor#TEMPTATION_RANGE` +- `net.minecraft.world.entity.animal` + - `Cat#getTextureId` + - `Squid#setMovementVector` + - `Wolf#isWet` +- `net.minecraft.world.entity.boss.dragon.EnderDragon` + - `getLatencyPos` + - `getHeadPartYOffset` +- `net.minecraft.world.entity.monster.Zombie#supportsBreakDoorGoal` +- `net.minecraft.world.entity.npc.Villager#setChasing`、`isChasing` +- `net.minecraft.world.entity.projectile` + - `AbstractArrow#shotFromCrossbow` + - `ThrowableProjectile(EntityType, LivingEntity, Level)` +- `net.minecraft.world.item` + - `BannerPatternItem#getDisplayName` + - `ItemStack#LIST_STREAM_CODEC` +- `net.minecraft.world.level.BlockGetter#getMaxLightLevel` +- `net.minecraft.world.level.block.entity.JigsawBlockEntity$JointType#byName` +- `net.minecraft.world.level.block.state.BlockBehaviour#isOcclusionShapeFullBlock` +- `net.minecraft.world.level.chunk.ChunkAccess#setBlendingData` +- `net.minecraft.world.level.storage.loot.LootDataType#deserialize` +- `net.minecraft.world.phys.AABB#getBottomCenter` +- `net.minecraft.world.phys.shapes.Shapes#getFaceShape` +- `net.minecraft.world.ticks.SavedTick#saveTick` diff --git a/primers-doc/1.21.4-from-1.21.2-3.md b/primers-doc/1.21.4-from-1.21.2-3.md new file mode 100644 index 0000000..e53b4fe --- /dev/null +++ b/primers-doc/1.21.4-from-1.21.2-3.md @@ -0,0 +1,1448 @@ +# Minecraft 1.21.2/3 -> 1.21.4 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.2/3 迁移到 1.21.4。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.4&tab=changelog) 中找到它们的列表。 + +## 客户端物品 + +Minecraft 已将物品应如何渲染的查找和定义移到了自己的数据生成系统中,该系统被称为**客户端物品**,位于 `assets//items/.json`。客户端物品类似于方块状态模型定义,但将来有可能包含更多信息。目前,它只是作为一个链接器,链接到用于渲染的模型。 + +所有客户端物品都包含一个使用 `model` 字段的 `ItemModel$Unbaked`。每个未烘焙模型都有一个关联的类型,该类型定义了物品应如何设置渲染,或在特定情况下如何渲染。这些 `type` 可以在 `ItemModels` 中找到。本文将介绍除一种类型外的所有类型,因为该未烘焙模型类型专门用于在选择物品时的捆绑包。 + +物品还包含一个 `properties` 字段,其中包含一些与元数据相关的参数。目前,它只指定了一个布尔值,当为 false 时,使手部立即交换当前持有的物品,而不是播放手部抬起的动画。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "" // 在此处设置类型 + // 添加其他参数 + }, + "properties": { + // 当为 false 时,禁用将此物品交换到手中时的动画 + "hand_animation_on_swap": false + } +} +``` + +### 基本模型 + +基本模型定义由 `minecraft:model` 类型处理。它包含两个字段:`model`,用于定义模型 JSON 的相对位置;以及一个可选的 `tints` 列表,用于定义如何对每个索引进行染色。 + +`model` 指向模型 JSON,相对于 `assets//models/.json`。在大多数情况下,客户端物品定义看起来像这样: + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:model", + // 指向 'assets/examplemod/models/item/example_item.json' + "model": "examplemod:item/example_item" + } +} +``` + +#### 染色源 + +在模型 JSON 中,一些元素面会有一个 `tintindex` 字段,它引用 `minecraft:model` 未烘焙模型类型中 `tints` 列表的某个索引。tints 列表是 `ItemTintSource`,它们都在 `net.minecraft.client.color.item.*` 中定义。所有定义的染色源都可以在 `ItemTintSources` 中找到,例如 `minecraft:constant` 用于常量颜色,或 `minecraft:dye` 用于使用 `DataComponents#DYED_COLOR` 的颜色,如果不存在则使用默认值。所有染色源都必须返回一个不透明的颜色,不过所有源通常通过调用 `ARGB#opaque` 来应用。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:model", + // 指向 'assets/examplemod/models/item/example_item.json' + "model": "examplemod:item/example_item", + // 要应用的 tints 列表 + "tints": [ + { + // 当 tintindex: 0 时 + "type": "minecraft:constant", + // 0x00FF00(或纯绿色) + "value": 65280 + }, + { + // 当 tintindex: 1 时 + "type": "minecraft:dye", + // 0x0000FF(或纯蓝色) + // 仅在未设置 `DataComponents#DYED_COLOR` 时调用 + "default": 255 + } + ] + } +} +``` + +要创建自己的 `ItemTintSource`,需要实现 `calculate` 方法,并注册与 `type` 字段关联的 `MapCodec`。`calculate` 接受当前的 `ItemStack`、等级和持有实体,并返回一个带有不透明 alpha 的 RGB 整数,定义层应如何染色。 + +然后,需要将 `MapCodec` 注册到 `ItemTintSources#ID_MAPPER`,但该字段默认是私有的,因此需要一些访问更改或反射。 + +```java +// 物品源类 +public record FromDamage(int defaultColor) implements ItemTintSource { + + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + ExtraCodecs.RGB_COLOR_CODEC.fieldOf("default").forGetter(FromDamage::defaultColor) + ).apply(instance, FromDamage::new) + ); + + public FromDamage(int defaultColor) { + this.defaultColor = ARGB.opaque(defaultColor); + } + + @Override + public int calculate(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity) { + return stack.isDamaged() ? ARGB.opaque(stack.getBarColor()) : defaultColor; + } + + @Override + public MapCodec type() { + return MAP_CODEC; + } +} + +// 然后,在某个暴露 ItemTintSources#ID_MAPPER 的初始化位置 +ItemTintSources.ID_MAPPER.put( + // 注册表名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "from_damage"), + // 映射编解码器 + FromDamage.MAP_CODEC +); +``` + +```json5 +// 对于 'tints' 数组中的某个对象 +{ + "type": "examplemod:from_damage", + // 0x0000FF(或纯蓝色) + // 仅在物品尚未损坏时调用 + "default": 255 +} +``` + +### 范围属性模型 + +范围属性模型,由 `minecraft:range_dispatch` 未烘焙模型类型定义,与之前的物品覆盖系统最为相似。本质上,该类型定义了一些可以缩放的物品属性,以及一系列阈值和关联的模型。选择的模型是具有最接近且不超过属性值的阈值的模型(例如,如果属性值为 `4`,我们有阈值 `3` 和 `5`,则会选择 `3`,因为它是最接近且不超过的)。物品属性通过 `RangeSelectItemModelProperty` 定义,它接受堆栈、等级、实体和一些种子值,返回一个浮点数,通常根据实现缩放在 0 和 1 之间。所有属性都可以在 `net.minecraft.client.renderer.item.properties.numeric.*` 中找到,并在 `RangeSelectItemModelProperties` 中注册,例如 `minecraft:cooldown` 用于冷却时间百分比,或 `minecraft:count` 用于堆栈中的当前物品数量或标准化后的最大堆栈大小百分比。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:range_dispatch", + + // 要使用的 `RangeSelectItemModelProperty` + "property": "minecraft:count", + // 乘以计算出的属性值的标量 + // 如果 count 为 0.3,scale 为 0.2,则检查的阈值为 0.3*0.2=0.06 + "scale": 1, + "fallback": { + // 如果没有匹配的阈值,则使用的后备模型 + // 可以是任何未烘焙模型类型 + "type": "minecraft:model", + "model": "examplemod:item/example_item" + }, + + // ~~ 由 `Count` 定义的属性 ~~ + // 当为 true 时,使用其最大堆栈大小对计数进行归一化 + "normalize": true, + + // ~~ 包含阈值信息的条目 ~~ + "entries": [ + { + // 当计数为其当前最大堆栈大小的三分之一时 + "threshold": 0.33, + "model": { + // 可以是任何未烘焙模型类型 + } + }, + { + // 当计数为其当前最大堆栈大小的三分之二时 + "threshold": 0.66, + "model": { + // 可以是任何未烘焙模型类型 + } + } + ] + } +} +``` + +要创建自己的 `RangeSelectItemModelProperty`,需要实现 `get` 方法,并注册与 `type` 字段关联的 `MapCodec`。`get` 接受堆栈、等级、实体和种子值,并返回一个任意浮点数,供范围分发模型解释。 + +然后,需要将 `MapCodec` 注册到 `RangeSelectItemModelProperties#ID_MAPPER`,但该字段默认是私有的,因此需要一些访问更改或反射。 + +```java +// 范围属性类 +public record AppliedEnchantments() implements RangeSelectItemModelProperty { + + public static final MapCodec MAP_CODEC = MapCodec.unit(new AppliedEnchantments()); + + @Override + public float get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed) { + return (float) stack.getEnchantments().size(); + } + + @Override + public MapCodec type() { + return MAP_CODEC; + } +} + +// 然后,在某个暴露 RangeSelectItemModelProperties#ID_MAPPER 的初始化位置 +RangeSelectItemModelProperties.ID_MAPPER.put( + // 注册表名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "applied_enchantments"), + // 映射编解码器 + AppliedEnchantments.MAP_CODEC +); +``` + +```json5 +// 对于 'model' 中的某个客户端物品 +{ + "type": "minecraft:range_dispatch", + + // 要使用的 `RangeSelectItemModelProperty` + "property": "examplemod:applied_enchantments", + // 乘以计算出的属性值的标量 + "scale": 0.5, + "fallback": { + // 如果没有匹配的阈值,则使用的后备模型 + // 可以是任何未烘焙模型类型 + "type": "minecraft:model", + "model": "examplemod:item/example_item" + }, + + // ~~ 由 `AppliedEnchantments` 定义的属性 ~~ + // 无(构造函数无参数) + + // ~~ 包含阈值信息的条目 ~~ + "entries": [ + { + // 当存在一个附魔时 + // 因为 1 * 标量 0.5 = 0.5 + "threshold": 0.5, + "model": { + // 可以是任何未烘焙模型类型 + } + }, + { + // 当存在两个附魔时 + "threshold": 1, + "model": { + // 可以是任何未烘焙模型类型 + } + } + ] +} +``` + +### 选择属性模型 + +选择属性模型,由 `minecraft:select` 未烘焙模型类型定义,在功能上类似于范围属性模型,但现在它根据某个属性(通常是枚举)进行切换。物品属性通过 `SelectItemModelProperty` 定义,它接受堆栈、等级、实体、一些种子值和当前显示上下文,以获取其中一个属性值。所有属性都可以在 `net.minecraft.client.renderer.item.properties.select.*` 中找到,并在 `SelectItemModelProperties` 中注册,例如 `minecraft:block_state` 用于指定方块状态属性的字符串值,或 `minecraft:display_context` 用于当前的 `ItemDisplayContext`。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:select", + + // 要使用的 `SelectItemModelProperty` + "property": "minecraft:display_context", + "fallback": { + // 如果没有匹配的阈值,则使用的后备模型 + // 可以是任何未烘焙模型类型 + "type": "minecraft:model", + "model": "examplemod:item/example_item" + }, + + // ~~ 由 `DisplayContext` 定义的属性 ~~ + // 无(构造函数无参数) + + // ~~ 基于可选择的属性的开关情况 ~~ + "cases": [ + { + // 当显示上下文为 `ItemDisplayContext#GUI` 时 + "when": "gui", + "model": { + // 可以是任何未烘焙模型类型 + } + }, + { + // 当显示上下文为 `ItemDisplayContext#FIRST_PERSON_RIGHT_HAND` 时 + "when": "firstperson_righthand", + "model": { + // 可以是任何未烘焙模型类型 + } + } + ] + } +} +``` + +要创建自己的 `SelectItemModelProperty`,需要实现 `get` 方法,并注册与 `type` 字段关联的 `SelectItemModelProperty$Type`。`get` 接受堆栈、等级、实体、种子值和显示上下文,并返回一个可编码的对象,供选择模型解释。 + +然后,需要将 `MapCodec` 注册到 `SelectItemModelProperties#ID_MAPPER`,但该字段默认是私有的,因此需要一些访问更改或反射。 + +```java +// 选择属性类 +public record StackRarity() implements SelectItemModelProperty { + + public static final SelectItemModelProperty.Type TYPE = SelectItemModelProperty.Type.create( + // 此属性的映射编解码器 + MapCodec.unit(new StackRarity()), + // 被选择对象的编解码器 + Rarity.CODEC + ); + + @Nullable + @Override + public Rarity get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, ItemDisplayContext displayContext) { + // 当为 null 时,使用后备模型 + return stack.get(DataComponents.RARITY); + } + + @Override + public SelectItemModelProperty.Type type() { + return TYPE; + } +} + +// 然后,在某个暴露 SelectItemModelProperties#ID_MAPPER 的初始化位置 +SelectItemModelProperties.ID_MAPPER.put( + // 注册表名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "rarity"), + // 属性类型 + StackRarity.TYPE +); +``` + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:select", + + // 要使用的 `SelectItemModelProperty` + "property": "examplemod:rarity", + "fallback": { + // 如果没有匹配的阈值,则使用的后备模型 + // 可以是任何未烘焙模型类型 + "type": "minecraft:model", + "model": "examplemod:item/example_item" + }, + + // ~~ 由 `StackRarity` 定义的属性 ~~ + // 无(构造函数无参数) + + // ~~ 基于可选择的属性的开关情况 ~~ + "cases": [ + { + // 当稀有度为 `Rarity#UNCOMMON` 时 + "when": "uncommon", + "model": { + // 可以是任何未烘焙模型类型 + } + }, + { + // 当稀有度为 `Rarity#RARE` 时 + "when": "rare", + "model": { + // 可以是任何未烘焙模型类型 + } + } + ] + } +} +``` + +### 条件属性模型 + +条件属性模型,由 `minecraft:condition` 未烘焙模型类型定义,在功能上类似于范围属性模型,但现在它根据布尔值进行切换。这些通常与范围分发结合使用,例如拉弓时。物品属性通过 `ConditionalItemModelProperty` 定义,它接受堆栈、等级、实体、一些种子值和当前显示上下文,以获取一个 true 或 false 语句。所有属性都可以在 `net.minecraft.client.renderer.item.properties.conditional.*` 中找到,并在 `ConditionalItemModelProperties` 中注册,例如 `minecraft:damaged` 用于物品是否损坏,或 `minecraft:has_component` 用于是否具有给定的数据组件。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:condition", + + // 要使用的 `SelectItemModelProperty` + "property": "minecraft:damaged", + + // ~~ 由 `Damaged` 定义的属性 ~~ + // 无(构造函数无参数) + + // ~~ 布尔结果的含义 ~~ + "on_true": { + // 可以是任何未烘焙模型类型 + }, + "on_false": { + // 可以是任何未烘焙模型类型 + } + } +} +``` + +要创建自己的 `ConditionalItemModelProperty`,需要实现 `get` 方法,并注册与 `type` 字段关联的 `MapCodec`。`get` 接受堆栈、等级、实体、种子值和显示上下文,并返回一个布尔值,分别由 `on_true` 和 `on_false` 解释。 + +然后,需要将 `MapCodec` 注册到 `ConditionalItemModelProperties#ID_MAPPER`,但该字段默认是私有的,因此需要一些访问更改或反射。 + +```java +// 谓词属性类 +public record TimePeriod(int month, MinMaxBounds.Ints dates, boolean enabled) implements ConditionalItemModelProperty { + + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + Codec.intRange(1, 12).fieldOf("month").forGetter(TimePeriod::month), + MinMaxBounds.Ints.CODEC.fieldOf("dates").forGetter(TimePeriod::dates) + ).apply(instance, TimePeriod::new) + ); + + public TimePeriod(int month, MinMaxBounds.Ints dates) { + this.month = month; + this.dates = dates; + + Calendar cal = Calendar.getInstance(); + this.enabled = cal.get(Calendar.MONTH) + 1 == this.month + && this.dates.matches(cal.get(Calendar.DATE)); + } + + @Override + public boolean get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, ItemDisplayContext context) { + return this.enabled; + } + + @Override + public MapCodec type() { + return MAP_CODEC; + } +} + +// 然后,在某个暴露 ConditionalItemModelProperties#ID_MAPPER 的初始化位置 +ConditionalItemModelProperties.ID_MAPPER.put( + // 注册表名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "time_period"), + // 映射编解码器 + TimePeriod.MAP_CODEC +); +``` + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:condition", + + // 要使用的 `SelectItemModelProperty` + "property": "examplemod:time_period", + + // ~~ 由 `TimePeriod` 定义的属性 ~~ + // 七月 + "month": 7, + "dates": { + // 7 月 1 日至 14 日之间 + "min": 1, + "max": 14 + }, + + // ~~ 布尔结果的含义 ~~ + "on_true": { + // 可以是任何未烘焙模型类型 + }, + "on_false": { + // 可以是任何未烘焙模型类型 + } + } +} +``` + +### 复合模型 + +复合模型,由 `minecraft:composite` 定义,本质上是其他模型类型的组合,用于渲染。具体来说,它设置了多个层,在渲染时将一个模型叠加在另一个模型之上。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:composite", + + // 将按照在列表中出现的顺序进行渲染 + "models": [ + { + // 可以是任何未烘焙模型类型 + }, + { + // 可以是任何未烘焙模型类型 + } + ] + } +} +``` + +### 特殊动态模型 + +特殊动态模型,由 `minecraft:special` 未烘焙模型类型定义,是用于无等级渲染器的方块实体(例如箱子、旗帜等)的新系统。这些模型不存储烘焙模型,而是提供一个要调用的渲染方法。特殊模型包装器接受一个用于获取基本模型设置(不是元素)的基础模型和一个 `SpecialModelRenderer`。所有特殊模型渲染器都可以在 `net.minecraft.client.renderer.special.*` 中找到,并在 `SpecialModelRenderers` 中注册。 + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:special", + + // 从中读取粒子纹理和显示变换的模型 + "base": "minecraft:item/template_skull", + "model": { + // 要使用的特殊模型渲染器 + "type": "minecraft:head", + + // ~~ 由 `SkullSpecialRenderer.Unbaked` 定义的属性 ~~ + // 骷髅头的类型 + "kind": "wither_skeleton" + } + } +} +``` + +要创建自己的 `SpecialModelRenderer`,需要同时实现渲染器和 `$Unbaked` 模型,以便从 JSON 读取数据。`$Unbaked` 模型通过 `bake` 创建 `SpecialModelRenderer`,并使用其 `type` 的 `MapCodec` 进行注册。然后,`SpecialModelRenderer` 通过 `extractArgument` 从堆栈中提取渲染所需的数据,并将其传递给 `render` 方法。如果不需要从堆栈中获取任何信息,可以实现 `NoDataSpecialModelRenderer`。 + +然后,需要将 `MapCodec` 注册到 `SpecialModelRenderers#ID_MAPPER`,但该字段默认是私有的,因此需要一些访问更改或反射。 + +如果您的物品是一个持有的方块,还需要将其添加到 `SpecialModelRenderers#STATIC_BLOCK_MAPPING` 中,以便在特定渲染场景下(例如在矿车中,或被末影人捡起)通过 `BlockRenderDispatcher#renderSingleBLock` 进行渲染。默认模型渲染器和特殊模型渲染器都会在此方法中被调用;允许同时渲染静态方块模型和动态特殊模型。由于此映射是不可变的,您需要替换它或挂钩到 `SpecialBlockModelRenderer` 并以某种方式添加到存储的映射中。 + +```java +// 特殊渲染器 +public record SignSpecialRenderer(WoodType defaultType, Model model) implements SpecialModelRenderer { + + // 渲染模型 + @Override + public void render(@Nullable WoodType type, ItemDisplayContext displayContext, PoseStack pose, MultiBufferSource bufferSource, int light, int overlay, boolean hasFoil) { + VertexConsumer consumer = Sheets.getSignMaterial(type).buffer(bufferSource, this.model::renderType); + this.model.renderToBuffer(pose, consumer, light, overlay); + } + + // 从堆栈中获取木材类型 + @Nullable + @Override + public WoodType extractArgument(ItemStack stack) { + return (stack.getItem() instanceof BlockItem item && item.getBlock() instanceof SignBlock sign) + ? sign.type() : this.defaultType; + } + + // 从中读取 JSON 的模型 + public static record Unbaked(WoodType defaultType) implements SpecialModelRenderer.Unbaked { + + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + WoodType.CODEC.fieldOf("default").forGetter(SignSpecialRenderer.Unbaked::defaultType) + ).apply(instance, SignSpecialRenderer.Unbaked::new) + ); + + // 创建特殊模型渲染器,如果失败则返回 null + @Nullable + @Override + public SpecialModelRenderer bake(EntityModelSet modelSet) { + return new SignSpecialRenderer( + this.defaultType, + SignRenderer.createSignModel(modelSet, defaultType, true) + ) + } + + @Overrides + public MapCodec type() { + return MAP_CODEC; + } + } +} + +// 然后,在某个暴露 SpecialModelRenderers#ID_MAPPER 的初始化位置 +SpecialModelRenderers.ID_MAPPER.put( + // 注册表名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "sign"), + // 映射编解码器 + SignSpecialRenderer.Unbaked.MAP_CODEC +); +// 假设我们也可以直接添加到 SpecialModelRenderers#STATIC_BLOCK_MAPPING +// 我们有一个方块 EXAMPLE_SIGN +SpecialModelRenderers.STATIC_BLOCK_MAPPING.put( + // 作为物品具有特殊渲染的方块 + EXAMPLE_SIGN, + // 要使用的未烘焙渲染器 + new SignSpecialRenderer.Unbaked(WoodType.BAMBOO) +); +``` + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "minecraft:special", + + // 从中读取粒子纹理和显示变换的模型 + "base": "minecraft:item/bamboo_sign", + "model": { + // 要使用的特殊模型渲染器 + "type": "examplemod:sign", + + // ~~ 由 `SignSpecialRenderer.Unbaked` 定义的属性 ~~ + // 如果找不到,则使用的默认木材类型 + "default": "bamboo" + } + } +} +``` + +### 渲染物品 + +现在,通过 `ItemModelResolver` 和 `ItemStackRenderState` 来渲染物品。这与 `EntityRenderState` 的工作方式类似:首先,`ItemModelResolver` 设置 `ItemStackRenderState`,然后通过 `ItemStackRenderState#render` 渲染状态。 + +让我们从 `ItemStackRenderState` 开始。对于渲染,我们只关心以下方法:`isEmpty`、`isGui3d`、`usesBlockLight`、`transform` 和 `render`。`isEmpty` 用于确定堆栈是否应该被渲染。然后,`isGui3d`、`usesBlockLight` 和 `transform` 在其关联的上下文中用于正确定位要渲染的堆栈。最后,`render` 接受姿势堆栈、缓冲区源、打包光照和覆盖纹理,以在适当的位置渲染物品。 + +`ItemModelResolver` 负责设置渲染所需的 `ItemStackRenderState` 上的信息。这是通过 `updateForLiving`(用于活体实体持有的物品)、`updateForNonLiving`(用于其他类型的实体持有的物品)和 `updateforTopItem`(用于所有其他情况)完成的。`updateForItem`(前两个方法委托给它)接受渲染状态、堆栈、显示上下文、堆栈是否在左手、等级、实体和一些种子值。这将通过 `ItemStackRenderState#clear` 清除先前状态,然后通过委托给 `ItemModel#update` 来设置新状态。如果您不在渲染器上下文中(例如,方块实体、实体),则始终可以通过 `Minecraft#getItemModelResolver` 获取 `ItemModelResolver`。 + +```java +// 在最简单的形式中,假设您没有进行任何变换(您应该根据需要这样做) +public class ExampleRenderer { + private final ItemStackRenderState state = new ItemStackRenderState(); + + public void render(ItemStack stack, Level level, PoseStack pose, MultiBufferSource bufferSource) { + // 首先更新渲染状态 + Minecraft.getInstance().getItemModelResolver().updateForTopItem( + // 渲染状态 + this.state, + // 用于更新状态的堆栈 + stack, + // 要在其中渲染的显示上下文 + ItemDisplayContext.NONE, + // 是否在实体的左手中(当未知时使用 false) + false, + // 当前等级(可以为 null) + level, + // 持有实体(可以为 null) + null, + // 任意种子值 + 0 + ); + + // 在此处执行任何所需的变换 + + // 然后渲染状态 + this.state.render( + // 带有所需变换的姿势堆栈 + pose, + // 缓冲区源 + bufferSource, + // 打包的光照值 + LightTexture.FULL_BRIGHT, + // 覆盖纹理值 + OverlayTexture.NO_OVERLAY + ); + } +} +``` + +### 自定义物品模型定义 + +要制作自定义物品模型定义,我们需要查看 `ItemStackRenderState` 中的更多方法,虽然您通常不会使用它们,但了解它们很有用:`ensureCapacity` 和 `newLayer`。这两个方法负责确保有足够的 `ItemStackRenderState$LayerRenderState`,如果您碰巧同时叠加多个模型的话。实际上,每次您计划在未烘焙模型中渲染某些东西时,都应该调用 `newLayer`。如果您计划将多个东西叠加在一起渲染,那么应该在调用 `newLayer` 之前使用计划渲染的层数设置 `ensureCapacity`。 + +一个物品模型定义由 `ItemModel` 组成,它在功能上定义了一个“烘焙模型”及其用于序列化的 `ItemModel$Unbaked`。 + +未烘焙变体有两个方法:`bake`,用于创建 `ItemModel`;以及 `type`,它引用要注册到 `ItemModels#ID_MAPPER` 的 `MapCodec`,但该字段默认是私有的,因此需要一些访问更改或反射。`bake` 接受 `$BakingContext`,其中包含用于获取 `BakedModel` 的 `ModelBaker`、用于实体模型的 `EntityModelSet` 以及缺失的 `ItemModel`。 + +烘焙变体只有一个方法 `update`,负责在 `ItemStackRenderState` 上设置所有必要的信息。模型本身不进行任何渲染。 + +```java +public record RenderTypeModelWrapper(BakedModel model, RenderType type) implements ItemModel { + + // 更新渲染状态 + @Override + public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed) { + ItemStackRenderState.LayerRenderState layerState = state.newLayer(); + if (stack.hasFoil()) { + layerState.setFoilType(ItemStackRenderState.FoilType.STANDARD); + } + layerState.setupBlockModel(this.model, this.type); + } + + public static record Unbaked(ResourceLocation model, RenderType type) implements ItemModel.Unbaked { + // 为编解码器创建渲染类型映射 + private static final BiMap RENDER_TYPES = Util.make(HashBiMap.create(), map -> { + map.put("translucent_item", Sheets.translucentItemSheet()); + map.put("cutout_block", Sheets.cutoutBlockSheet()); + }); + private static final Codec RENDER_TYPE_CODEC = ExtraCodecs.idResolverCodec(Codec.STRING, RENDER_TYPES::get, RENDER_TYPES.inverse()::get); + + // 要注册的映射编解码器 + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + ResourceLocation.CODEC.fieldOf("model").forGetter(RenderTypeModelWrapper.Unbaked::model), + RENDER_TYPE_CODEC.fieldOf("render_type").forGetter(RenderTypeModelWrapper.Unbaked::type) + ) + .apply(instance, RenderTypeModelWrapper.Unbaked::new) + ); + + @Override + public void resolveDependencies(ResolvableModel.Resolver resolver) { + // 解析模型依赖项,因此传入所有已知的资源位置 + resolver.resolve(this.model); + } + + @Override + public ItemModel bake(ItemModel.BakingContext context) { + // 获取烘焙模型并返回 + BakedModel baked = context.bake(this.model); + return new RenderTypeModelWrapper(baked, this.type); + } + + @Override + public MapCodec type() { + return MAP_CODEC; + } + } +} + +// 然后,在某个暴露 ItemModels#ID_MAPPER 的初始化位置 +ItemModels.ID_MAPPER.put( + // 注册表名称 + ResourceLocation.fromNamespaceAndPath("examplemod", "render_type"), + // 映射编解码器 + RenderTypeModelWrapper.Unbaked.MAP_CODEC +); +``` + +```json5 +// 对于某个物品 'examplemod:example_item' +// JSON 位于 'assets/examplemod/items/example_item.json' +{ + "model": { + "type": "examplemod:render_type", + // 指向 'assets/examplemod/models/item/example_item.json' + "model": "examplemod:item/example_item", + // 设置渲染时使用的渲染类型 + "render_type": "cutout_block" + } +} +``` + +- `net.minecraft.client` + - `ClientBootstrap` - 注册支持客户端的映射;目前用于物品模型定义。 + - `Minecraft` + - `getEquipmentModels` 已移除,只能在 `EntityRendererProvider$Context#getEquipmentAssets` 中直接访问 + - `getItemModelResolver` - 返回用于解析当前要在 `ItemStackRenderState$LayerRenderState` 中渲染的模型的更新器。 + - `KeyMapping#get` - 根据其翻译键获取按键映射。 +- `net.minecraft.client.color.item` + - `Constant` - 用于对物品纹理进行染色的常量。 + - `CustomModelDataSource` - 根据 `DataComponent#CUSTOM_MODEL_DATA` 数据组件中的索引获取要染色的颜色。如果未找到索引或超出范围,则使用默认颜色。 + - `Dye` - 使用 `DataComponent#DYED_COLOR` 数据组件获取要染色的颜色。 + - `Firework` - 使用 `DataComponent#FIRE_EXPLOSION` 数据组件获取要染色的颜色。 + - `GrassColorSource` - 根据提供的温度和降水值获取要染色的颜色。 + - `ItemColor` -> `ItemTintSource`,不是一对一,因为通过在模型列表中提供多个 `ItemTintSource` 来设置索引。 + - `ItemColors` 类已移除,现在作为 `ItemTintSource` 进行数据生成 + - `ItemTintSources` - 用于在模型中对物品纹理进行染色的源的注册表。 + - `MapColor` - 使用 `DataComponent#MAP_COLOR` 数据组件获取要染色的颜色。 + - `Potion` - 使用 `DataComponent#POTION_CONTENTS` 数据组件获取要染色的颜色。 + - `TeamColor` - 根据持有实体的队伍颜色获取颜色。 +- `net.minecraft.client.data.Main` - 客户端数据生成的入口点。 +- `net.minecraft.client.particle.BreakingItemParticle` 现在接受 `ItemStackRenderState` 而不是 `ItemStack` + - `$ItemParticleProvider` - 一个抽象的粒子提供者,提供一个计算 `ItemStackRenderState` 的简单方法。 +- `net.minecraft.client.renderer` + - `BlockEntityWithoutLevelRenderer` 类已移除,被 `NoDataSpecialModelRenderer` 数据生成系统取代 + - `ItemInHandRenderer` 现在接受 `ItemModelResolver` + - `ItemModelShaper` 已移除,因为这些方法在 `ModelManager` 中可用 + - `Sheets` + - `getBedMaterial` - 根据染料颜色获取床的材质。 + - `colorToResourceMaterial` - 获取染料颜色的资源位置。 + - `createBedMaterial` - 根据染料颜色或资源位置创建床的材质。 + - `getShulkerBoxMaterial` - 根据染料颜色获取潜影盒的材质。 + - `colorToShulkerMaterial` - 获取潜影盒的染料颜色的资源位置。 + - `createShulkerMaterial` - 根据染料颜色或资源位置创建潜影盒的材质。 + - `chestMaterial` - 使用给定的资源位置为箱子创建一个新的材质。 + - `SpecialBlockModelRenderer` - 方块到物品变体特殊渲染器的映射。 +- `net.minecraft.client.renderer.block.BlockRenderDispatcher` 现在接受一个提供的 `SpecialBlockModelRenderer` 而不是 `BlockEntityWithoutLevelRenderer` +- `net.minecraft.client.renderer.block.model` + - `BakedOverrides` 类已移除,被 `RangeSelectItemModelProperty` 数据生成系统取代 + - `BlockModel` 现在接受 `TextureSlots$Data` 而不是材质映射,并且不再接受 `ItemOverride` 列表 + - `MISSING_MATERIAL` 已移除,被 `minecraft:missingno` 取代 + - `textureMap` -> `textureSlots`,现在是私有的,不是一对一 + - `parent` 现在是私有的,不是一对一 + - `parentLocation` 现在是私有的 + - `hasAmbientOcclusion` -> `getAmbientOcclusion` + - `isResolved` 已移除 + - `getOverrides` 已移除 + - `getParent` - 返回未烘焙的父模型。 + - `getTextureSlots` - 返回模型的纹理数据。 + - `getElements` 现在是包私有的 + - `$GuiLight` -> `UnbakedModel$GuiLight` + - `FaceBakery` + - `bakeQuad` 现在是静态的 + - `calculateFacing` 现在是私有的 + - `ItemModelGenerator` 现在实现 `UnbakedModel` + - `ItemOverride` 类已移除,被 `RangeSelectItemModelProperty` 数据生成系统取代 + - `ItemTransforms` 现在是一个记录 + - `hasTransform` 已移除 + - `TextureSlots` - 一个处理模型内纹理映射的类。数据从 `$Data` 读取,并作为 `$SlotContents` 存储,直到在烘焙过程中解析为 `Material`。 + - `UnbakedBlockStateModel` 现在继承 `ResolvableModel` 而不是 `UnbakedModel` + - `bake` - 将方块状态烘焙到其可选择的模型中。 + - `Variant` 现在是一个记录 +- `net.minecraft.client.renderer.blockentity` + - `BannerRenderer` 现在有一个接受 `EntityModelSet` 的重载构造函数 + - `renderInHand` - 渲染旗帜的物品模型。 + - `BedRenderer` 现在有一个接受 `EntityModelSet` 的重载构造函数 + - `renderInHand` - 渲染床的物品模型。 + - `BlockEntityRenderDispatcher(Font, EntityModelSet, Supplier, Supplier, Supplier)` -> `BlockEntityRenderDispatcher(Font, Supplier, BlockRenderDispatcher, ItemModelResolver, ItemRenderer, EntityRenderDispatcher)` + - `renderItem` 已移除,在其特定类中实现 + - `BlockEntityRendererProvider` 现在接受 `ItemModelResolver` + - `getItemModelResolver` - 获取返回物品模型的解析器。 + - `ChestRenderer#xmasTextures` - 返回是否应在箱子上渲染圣诞纹理。 + - `DecoratedPotRenderer` 现在有一个接受 `EntityModelSet` 的重载构造函数 + - `renderInHand` - 渲染饰纹陶罐的物品模型。 + - `ShulkerBoxRenderer` 现在有一个接受 `EntityModelSet` 的重载构造函数 + - `render` - 渲染潜影盒。 + - `$ShulkerBoxModel#animate` 不再接受 `ShulkerBoxBlockEntity` + - `SkullblockRenderer#createSkullRenderers` -> `createModel`,不是一对一 +- `net.minecraft.client.renderer.entity` + - `EntityRenderDispatcher` 现在接受一个 `IteModelResolver`、一个提供的 `EntityModelSet` 而不是实例,以及一个 `EquipmentAssetManager` 而不是 `EquipmentModelSet` + - `EntityRendererProvider$Context` 现在接受一个 `ItemModelResolver` 而不是 `ItemRenderer`,以及一个 `EquipmentAssetManager` 而不是 `EquipmentModelSet` + - `getItemRenderer` -> `getItemModelResolver`,不是一对一 + - `getEquipmentModels` -> `getEquipmentAssets` + - `FishingHookRenderer` - 返回钓鱼钩的持有手臂。 + - `HumanoidMobRenderer` + - `getArmPose` - 返回实体的手臂姿势。 + - `extractHumanoidRenderState` 现在接受一个 `ItemModelResolver` + - `ItemEntityRenderer` + - `getSeedForItemStack` 已移除 + - `renderMultipleFromCount` 现在接受 `ItemClusterRenderState`,并移除了 `ItemRenderer`、`ItemStack`、`BakedModel` 和 3d 布尔值 + - `ItemRenderer` 不再实现 `ResourceManagerReloadListener` + - 构造函数现在只接受 `ItemModelResolver` + - `render` -> `renderItem`,不是一对一 + - `renderBundleItem` 已移除 + - `getModel`、`resolveItemModel` 已移除 + - `LivingEntityRenderer#itemRenderer` -> `itemModelResolver`,不是一对一 + - `OminousItemSpawnerRenderer` 现在使用 `ItemClusterRenderState` + - `SkeletonRenderer#getArmPose` -> `AbstractSkeletonRenderer#getArmPose` + - `SnowGolemRenderer` 现在使用 `SnowGolemRenderState` +- `net.minecraft.client.renderer.entity.layers` + - `CrossArmsItemLayer` 现在使用 `HoldingEntityRenderState` + - `CustomHeadLayer` 不再接受 `ItemRenderer` + - `DolphinCarryingItemLayer` 不再接受 `ItemRenderer` + - `EquipmentLayerRenderer$TrimSpriteKey` 现在接受 `ResourceKey` + - `textureId` - 获取纹饰的纹理 id。 + - `FoxHeldItemLayer` 不再接受 `ItemRenderer` + - `ItemInHandLayer` 现在使用 `ArmedEntityRenderState` + - 构造函数不再接受 `ItemRenderer` + - `renderArmWithItem` 不再接受 `BakedModel`、`ItemStack` 或 `ItemDisplayContext`,而是接受 `ItemStackRenderState` + - `LivingEntityEmissiveLayer` 现在接受一个布尔值,确定该层是否始终可见 + - `PandaHoldsItemLayer` 不再接受 `ItemRenderer` + - `PlayerItemInHandLayer` 不再接受 `ItemRenderer` + - `renderArmWithItem` 不再接受 `BakedModel`、`ItemStack` 或 `ItemDisplayContext`,而是接受 `ItemStackRenderState` + - `SnowGolemHeadLayer` 现在使用 `SnowGolemRenderState` + - `WitchItemLayer` 不再接受 `ItemRenderer` +- `net.minecraft.client.renderer.entity.player.PlayerRenderer#getArmPose` 现在是私有的 +- `net.minecraft.client.renderer.entity.state` + - `ArmedEntityRenderState` - 一个用于在右手和左手持有物品的实体的渲染状态。 + - `HoldingEntityRenderState` - 一个用于持有单个物品的实体的渲染状态。 + - `ItemClusterRenderState` - 一个用于应多次渲染的物品的渲染状态。 + - `ItemDisplayEntityRenderState#itemRenderState`、`itemModel` -> `item`,不是一对一 + - `ItemEntityRenderState#itemModel`、`item` -> `ItemClusterRenderState#item`,不是一对一 + - `ItemFrameRenderState#itemStack`、`itemModel` -> `item`,不是一对一 + - `LivingEntityRenderState` + - `headItemModel`、`headItem` -> `headItem`,不是一对一 + - 手臂和手部方法已移至 `ArmedEntityRenderState` + - `OminousItemSpawnerRenderState` -> `ItemClusterRenderState` + - `PlayerRenderState` + - `mainHandState`、`offHandState` -> `ArmedEntityRenderState` 方法 + - `heldOnHead` - 表示玩家头上的物品堆栈。 + - `SkeletonRenderState#isHoldingBow` - 表示骷髅是否在持弓。 + - `SnowGolemRenderState` - 雪傀儡的渲染状态。 + - `ThrownItemRenderState#item`、`itemModel` -> `item`,不是一对一 + - `WitchRenderState#isHoldingPotion` - 女巫是否在持药水。 +- `net.minecraft.client.renderer.item` + - `BlockModelWrapper` - 包含模型及其关联染色的基本模型定义。 + - `BundleSelectedItemSpecialRenderer` - 用于捆绑包选择的堆栈的特殊渲染器。 + - `ClampedItemPropertyFunction`、`ItemPropertyFunction` -> 根据情况和属性,使用 `.properties.numeric.*` 类 + - `ClientItem` - 表示 `assets//items` 中模型定义的基础物品。 + - `CompositeModel` - 将多个模型叠加在一起。 + - `ConditionalItemModel` - 根据布尔值显示不同模型的模型。 + - `EmptyModel` - 不渲染任何内容的模型。 + - `ItemModel` - 根据需要更新堆栈渲染状态的基础物品模型。 + - `ItemModelResolver` - 更新堆栈渲染状态的解析器。 + - `ItemModels` - 包含 `ClientItem` 的所有潜在物品模型。 + - `ItemProperties` 类已移除 + - `ItemStackRenderState` - 表示要渲染的堆栈的渲染状态。 + - `MissingItemModel` - 表示缺失模型的模型。 + - `RangeSelectItemModel` - 包含一定范围值的模型,应用满足阈值的关联模型。 + - `SelectItemModel` - 根据提供的属性进行切换的物品模型。 + - `SpecialModelWrapper` - 用于动态渲染的模型(例如箱子)的物品模型。 +- `net.minecraft.client.renderer.item.properties.conditional` + - `Broken` - 如果物品只剩下一点耐久。 + - `BundleHasSelectedItem` - 如果捆绑包持有选中的物品。 + - `ConditionalItemModelProperties` - 包含所有潜在的条件属性类型。 + - `ConditionalItemModelProperty` - 表示返回某个布尔值的属性。 + - `CustomModelDataProperty` - 如果当前索引在 `DataComponents#CUSTOM_MODEL_DATA` 中被设置为 true。 + - `Damaged` - 如果物品已损坏。 + - `ExtendedView` - 如果显示上下文是 GUI 并且按下了 shift 键。 + - `FishingRodCast` - 如果鱼竿正在使用。 + - `HasComponent` - 是否具有关联的数据组件。 + - `IsCarried` - 如果物品正在当前菜单中被携带。 + - `IsKeybindDown` - 如果按键映射正在被按下。 + - `IsSelected` - 如果物品在快捷栏中被选中。 + - `IsUsingItem` - 如果物品正在被使用。 + - `IsViewEntity` - 持有实体是否是当前的相机实体。 +- `net.minecraft.client.renderer.item.properties.numeric` + - `BundleFullness` - 基于捆绑包内容的阈值。 + - `CompassAngle` - 基于当前角度状态的阈值。 + - `CompassAngleState` - 基于指南针当前朝向目标角度的阈值。 + - `Cooldown` - 基于当前冷却百分比的阈值。 + - `Count` - 基于堆栈数量的阈值。 + - `CrossbowPull` - 基于弩被拉开的阈值。 + - `CustomModelDataProperty` - 如果当前索引在 `DataComponents#CUSTOM_MODEL_DATA` 中设置了阈值。 + - `Damage` - 基于剩余耐久百分比的阈值。 + - `NeedleDirectionHelper` - 一个抽象类,帮助将指针指向正确的方向。 + - `RangeSelectItemModelProperties` - 包含所有潜在的范围属性类型。 + - `RangeSelectItemModelProperty` - 表示返回某个浮点数阈值的属性。 + - `Time` - 基于当前时间的阈值。 + - `UseCycle` - 基于正在使用的堆栈中归一化到某个周期模数的剩余时间的阈值。 + - `UseDuration` - 基于正在使用的堆栈中剩余时间的阈值。 +- `net.minecraft.client.renderer.item.properties.select` + - `Charge` - 基于弩的充能类型的情况。 + - `ContextDimension` - 基于物品当前所在维度的情况。 + - `ContextEntityType` - 基于持有实体类型的情况。 + - `CustomModelDataProperty` - 如果当前索引在 `DataComponents#CUSTOM_MODEL_DATA` 中被设置为字符串。 + - `DisplayContext` - 基于显示上下文的情况。 + - `ItemBlockState` - 基于从持有方块状态属性的物品中获取属性值的情况。 + - `LocalTime` - 基于简单日期格式模式的情况。 + - `MainHand` - 基于持有物品的手臂的情况。 + - `SelectItemModelProperties` - 包含所有潜在的选择情况属性类型。 + - `SelectItemModelProperty` - 表示返回某种选择情况的属性。 + - `TrimMaterialProperty` - 基于物品上纹饰材料的情况。 +- `net.minecraft.client.renderer.special` + - `BannerSpecialRenderer` - 旗帜的物品渲染器。 + - `BedSpecialRenderer` - 床的物品渲染器。 + - `ChestSpecialRenderer` - 箱子的物品渲染器。 + - `ConduitSpecialRenderer` - 潮涌核心的物品渲染器。 + - `DecoratedPotSpecialRenderer` - 饰纹陶罐的物品渲染器。 + - `HangingSignSpecialRenderer` - 悬挂式告示牌的物品渲染器。 + - `NoDataSpecialModelRenderer` - 不需要从堆栈读取任何数据的物品渲染器。 + - `ShieldSpecialRenderer` - 盾牌的物品渲染器。 + - `ShulkerBoxSpecialRenderer` - 潜影盒的物品渲染器。 + - `SkullSpecialRenderer` - 骷髅头的物品渲染器。 + - `SpecialModelRenderer` - 表示从堆栈读取数据并渲染对象而不需要渲染状态的模型。 + - `SpecialModelRenderers` - 包含所有潜在的特殊渲染器。 + - `StandingSignSpecialRenderer` - 站立式告示牌的物品渲染器。 + - `TridentSpecialRenderer` - 三叉戟的物品渲染器。 +- `net.minecraft.client.resources.model` + - `BakedModel` + - `isCustomRenderer` 已移除,被特殊渲染器系统取代 + - `overrides` 已移除,被属性渲染器系统取代 + - `BlockStateModelLoader` 不再接受缺失模型 + - `definitionLocationToBlockMapper` 现在是私有的 + - `loadBlockStateDefinitionStack` 现在是私有的 + - `loadBlockStates` - 获取方块状态的已加载模型。 + - `$LoadedBlockModelDefinition` 现在是包私有的 + - `$LoadedModel` 现在接受 `UnbakedBlockStateModel` 而不是 `UnbakedModel` + - `$LoadedModels` + - `forResolving` - 返回所有需要解析的模型。 + - `plainModels` - 返回从模型位置到未烘焙模型的映射。 + - `BuiltInModel` 类已移除 + - `ClientItemInfoLoader` - 加载所有物品堆栈的所有模型。 + - `EquipmentModelSet` -> `EquipmentAssetManager` + - `ItemModel` -> `net.minecraft.client.renderer.item.ItemModel` + - `MissingBlockModel#MISSING` 现在是私有的 + - `ModelBaker` + - `sprites` - 返回获取精灵的获取器。 + - `rootName` - 获取用于调试的模型名称。 + - `ModelBakery(Map, Map, UnbakedModel)` -> `ModelBakery(EntityModelSet, Map, Map, Map, UnbakedModel)` + - `bakeModels` 现在返回一个 `$BakingResult` + - `getBakedTopLevelModels` 已移除 + - `$BakingResult` - 包含所有已加载的模型。 + - `$TextureGetter` + - `get` 现在接受 `ModelDebugName` 而不是 `ModelResourceLocation` + - `reportingMissingReference` - 处理当纹理未设置时如何报告。 + - `bind` - 创建一个绑定到当前模型的独立获取器。 + - `ModelDebugName` - 返回用于调试的模型名称。 + - `ModelDiscovery` + - `registerStandardModels` 已移除 + - `registerSpecialModels` - 添加系统加载的内部模型。 + - `addRoot` - 添加一个可以解析的新模型。 + - `getUnreferencedModels` - 返回已加载模型与已使用模型之间的差异。 + - `getTopModels` 已移除 + - `ModelGroupCollector$GroupKey#create` 现在接受 `UnbakedBlockStateModel` 而不是 `UnbakedModel` + - `ModelManager` + - `specialBlockModelRenderer` - 返回特殊方块模型的渲染器。 + - `entityModels` - 返回实体的模型集。 + - `getItemProeprties` - 根据其资源位置返回客户端物品的属性。 + - `ModelResourceLocation#inventory` 已移除 + - `ResolvableModel` - 基础模型,通常是未烘焙的,具有需要解析的引用。 + - `SimpleBakedModel` 字段现在都是私有的 + - `bakeElements` - 根据方块元素烘焙模型。 + - `$Builder` 不再有一个接受 `BlockModel` 的重载 + - `SpecialModels` 类已移除 + - `SpriteGetter` - 用于关联材质的图集精灵的获取器。 + - `UnbakedModel` 现在是一个 `ResolvableModel` + - `bake(ModelBaker, Function, ModelState)` -> `bake(TextureSlots, ModelBaker, ModelState, boolean, boolean, ItemTransforms)` + - `getAmbientOcclusion`、`getTopAmbientOcclusion` - 返回是否应在物品上启用环境光遮蔽。 + - `getGuiLight`、`getTopGuiLight` - 返回 GUI 内的光照面。 + - `getTransforms`、`getTopTransform`、`getTopTransforms` - 返回基于显示上下文要应用的变换。 + - `getTextureSlots`、`getTopTextureSlots` - 返回模型的纹理数据。 + - `getParent` - 返回此模型的父模型。 + - `bakeWithTopModelValues` - 烘焙模型。 +- `net.minecraft.data.models.*` -> `net.minecraft.client.data.models.*` +- `net.minecraft.world.item` + - `BundleItem` 不再接受任何 `ResourceLocation` + - `openFrontModel`、`openBackModel` 已移除 + - `CrossbowItem$ChargeType` - 弩正在充能的物品。 + - `DyeColor#getMixedColor` - 返回最接近混合颜色的染料。 + - `Item$Properties#overrideModel` 已移除 + - `SpawnEggItem` 不再接受其染色颜色 + - `getColor` 已移除 +- `net.minecraft.world.item.alchemy.PotionContents` + - `getColor(*)` 已移除 + - `getColorOr` - 获取药水的自定义颜色,如果不存在则获取默认颜色。 +- `net.minecraft.world.item.component.CustomModelData` 现在接受一个浮点数、标志、字符串和颜色的列表,用于根据提供的索引在自定义模型属性中使用 +- `net.minecraft.world.item.equipment` + - `ArmorMaterial` 现在接受 `ResourceKey` 而不是仅仅模型 id + - `EquipmentAsset` - 一个表示装备客户端信息键的标记 + - `EquipmentAssets` - 所有原版装备资源。 + - `EquipmentModel` -> `net.minecraft.client.resources.model.EquipmentClientInfo` + - `EquipmentModels` -> `net.minecraft.client.data.models.EquipmentAssetProvider`,不是一对一 + - `Equippable` 现在接受 `ResourceKey` 而不是仅仅模型 id + - `$Builder#setModel` -> `setAsset` +- `net.minecraft.world.item.equipment.trim` + - `ArmorTrim#getTexture` 已移除 + - `TrimMaterial` 不再接受物品模型索引,并且覆盖盔甲材料的键指向 `ResourceKey` +- `net.minecraft.world.level.FoliageColor` + - `getEvergreenColor` -> `FOLIAGE_EVERGREEN` + - `getBirchColor` -> `FOLIAGE_BIRCH` + - `getDefaultColor` -> `FOLIAGE_DEFAULT` + - `getMangroveColor` -> `FOLIAGE_MANGROVE` +- `net.minecraft.world.level.block.RenderShape#ENTITYBLOCK_ANIMATED` 已移除 +- `net.minecraft.world.level.block.entity` + - `BannerBlockEntity#fromItem` 已移除 + - `BedBlockEntitty#setColor` 已移除 + - `BlockEntity#saveToItem` 已移除 + - `DecoratedPotBlockEntity#setFromItem`、`getPotAsItem` 已移除 +- `net.minecraft.world.level.storage.loot.functions.SetCustomModelDataFunction` 现在接受一个浮点数、标志、字符串和颜色的列表,用于根据提供的索引在自定义模型属性中使用 + +## 生物 替换当前物品 + +与工具和盔甲分别是 `DiggerItem` 和 `ArmorItem` 子类型相关的最后一个硬编码实例已被重做:`Mob#canReplaceCurrentItem`。现在,它从 `DataComponents#EQUIPPABLE` 数据组件中读取堆栈的 `EquipmentSlot`。然后,根据情况使用不同的逻辑。 + +对于盔甲槽位,如果盔甲附有 `EnchantmentEffectComponents#PREVENT_ARMOR_CHANGE` 效果组件,则无法更换。否则,它将首先尝试比较盔甲属性,如果相等则比较盔甲韧性。 + +对于武器(通过手部槽位),它将首先检查生物是否有偏好的武器类型标签。如果有,它将把物品切换到标签中的武器,前提是标签中有一个物品而另一个没有。否则,它将尝试比较攻击伤害属性。 + +如果所有属性都相等,则它们都将默认采用以下逻辑。首先,它会尝试选择附魔最多的物品。然后,它会尝试选择剩余耐久度最多的物品(原始值,而不是百分比)。最后,它会检查其中一个物品是否通过 `DataComponents#CUSTOM_NAME` 具有自定义名称。 + +> 一个小问题是,`BambooSaplingBlock` 和 `BambooStalkBLock` 仍然硬编码检查主手物品是否为 `SwordItem`,尽管这将来可能会被替换为对 `ToolMaterial#applySwordProperties` 的更改。 + +## 粒子,通过渲染类型渲染 + +粒子现在使用 `RenderType` 进行渲染,而不是自己设置缓冲区构建器。唯一的特殊情况是 `ParticleRenderType#CUSTOM`,它允许模组制作者通过 `Particle#renderCustom` 实现自己的渲染;以及 `ParticleRenderType#NO_RENDER`,它不渲染任何内容。 + +要创建新的 `ParticleRenderType`,可以通过传入其名称(用于日志记录)和要使用的 `RenderType` 来创建。然后,在 `Particle#getRenderType` 中返回该类型。 + +```java +public static final ParticleRenderType TERRAIN_SHEET_OPAQUE = new ParticleRenderType( + "TERRAIN_SHEET_OPAQUE", // 通常是可识别的内容,例如字段名称 + RenderType.opaqueParticle(TextureAtlas.LOCATION_BLOCKS) // 要使用的 RenderType +); +``` + +- `net.minecraft.client.particle` + - `CherryParticle` -> `FallingLeavesParticle`,不是一对一,因为新类对其泛化有更大的配置 + - `ItemPickupParticle` 不再接受 `RenderBuffers` + - `Particle#renderCustom` - 使用 `ParticleRenderType#CUSTOM` 渲染类型渲染粒子。 + - `ParticleEngine#render(LightTexture, Camera, float)` -> `render(Camera, float, MutliBufferSource$BufferSource)` + - `ParticleRenderType` 现在是一个记录,接受名称和它使用的 `RenderType`。 + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### `SimpleJsonResourceReloadListener` + +`SimpleJsonResourceReloadListener` 现在接受一个转换器,用于将某个键映射到资源位置。已为注册表键提供了一个抽象。这是通过 `FileToIdConverter` 完成的,它本质上持有一个前缀和扩展名,应用于某个 `ResourceLocation`。 + +```java +// 我们假设这是一个服务器重载监听器(意味着在 'data' 文件夹中) +public class MyLoader extends SimpleJsonResourceReloadListener { + + public MyLoader() { + super( + // 用于编码/解码对象的编解码器 + ExampleObject.CODEC, + // 文件转换器 + // 会将文件放置在 data//example/object/.json + FileToIdConverter.json( + // 前缀 + "example/object" + ) + ); + } + + // 下面相同 +} +``` + +- `net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener` 现在接受一个注册表的资源键,或者一个文件到 id 的转换器,而不仅仅是一个字符串 + - `scanDirectory` 现在接受一个注册表的资源键,或者一个文件到 id 的转换器,而不仅仅是一个字符串 + +### MetadataSectionSerializer,被 Codec 取代 + +`MetadataSectionSerializer` 已被移除,转而使用 `Codec` 来序列化元数据部分。因此,所有 `MetadataSectionSerializer` 都已被其 `MetadataSectionType` 取代,它包含部分的名称和该元数据部分的编解码器。 + +- `net.minecraft.client.renderer.texture` + - `HttpTexture` -> `SkinTextureDownloader`,不是一对一,因为新类只是一个返回要存储内容的工具 + - `MissingTextureAtlasSprite` + - `getTexture` -> `generateMissingImage`,不是一对一 + - `getMissingImage(int, int)` 现在是公开的 + - `SpriteLoader#loadAndStitch` 现在接受 `MetadataSectionType` 的集合而不是 `MetadataSectionSerializer` +- `net.minecraft.client.resources.SkinManager` 不再接受 `TextureManager` + - `getOrLoad` 现在返回一个 `Optional` 的 future,而不仅仅是 `PlayerSkin` +- `net.minecraft.client.resources.metadata.animation` + - `AnimationFrame` 现在是一个记录 + - `AnimationMetadataSection` 现在是一个记录 + - `AnimationMetadataSectionSerializer` 类已移除 + - `VillagerMetaDataSection` -> `VillagerMetadataSection` + - `VillagerMetadataSectionSerializer` 类已移除 +- `net.minecraft.client.resources.metadata.texture` + - `TextureMetadataSection` 现在是一个记录 + - `TextureMetadataSectionSerializer` 类已移除 +- `net.minecraft.server.packs.PackResources#getMetadataSection` 现在接受 `MetadataSectionType` 而不是 `MetadataSectionSerializer` +- `net.minecraft.server.packs.metadata` + - `MetadataSectionSerializer` 已移除,转而使用部分编解码器 + - `MetadataSectionType` 现在是一个记录,而不是 `MetadataSectionSerializer` 的扩展 +- `net.minecraft.server.packs.resources.ResourceMetadata` + - `getSection` 现在接受 `MetadataSectionType` 而不是 `MetadataSectionSerializer` + - `copySections` 现在接受 `MetadataSectionType` 的集合而不是 `MetadataSectionSerializer` + +### 音乐,现在带有音量控制 + +背景音乐现在通过 `MusicInfo` 类处理,它还存储音量以及关联的 `Music`。 + +- `net.minecraft.client.Minecraft#getSituationalMusic` 现在返回 `MusicInfo` 而不是 `Music` +- `net.minecraft.client.sounds` + - `MusicInfo` - 一个记录,包含当前正在播放的 `Music` 及其音量。 + - `MusicManager#startPlaying` 现在接受 `MusicInfo` 而不是 `Music` + - `SoundEngine#setVolume`、`SoundManager#setVolume` - 设置关联声音实例的音量。 +- `net.minecraft.world.level.biome` + - `Biome` + - `getBackgroundMusic` 现在返回一个可选的 `SimpleWeightedRandomList` 的音乐。 + - `getBackgroundMusicVolume` - 获取背景音乐的音量。 + - `BiomeSpecialEffects$Builder#silenceAllBackgroundMusic`、`backgroundMusic(SimpleWeightedRandomList)` - 处理为生物群系设置背景音乐。 + +### 标签变更 + +- `minecraft:block` + - `tall_flowers` -> `bee_attractive` +- `minecraft:item` + - `tall_flowers`、`flowers` 已移除 + - `trim_templates` 已移除 + - `skeleton_preferred_weapons` + - `drowned_preferred_weapons` + - `piglin_preferred_weapons` + - `pillager_preferred_weapons` + - `wither_skeleton_disliked_weapons` + +### 新增列表 + +- `com.mojang.blaze3d.platform.Window#isMinimized` - 返回应用程序窗口是否最小化。 +- `com.mojang.blaze3d.vertex.VertexBuffer` + - `uploadStatic` - 通过使用带有 `STATIC_WRITE` `VertexBuffer` 的 `Tesselator` 的 `Consumer` 立即上传提供的顶点数据。 + - `drawWithRenderType` - 使用给定的 `RenderType` 将当前缓冲区绘制到屏幕。 +- `com.mojang.math.MatrixUtil#isIdentity` - 检查当前 `Matrix4f` 是否为单位矩阵。 +- `net.minecraft` + - `SuppressForbidden` - 一个包含某些原因的注解,通常与需要 sysout 流有关。 + - `Util#maxAllowedExecutorThreads` - 返回在 1 和最大线程数之间钳制的可用处理器数量。 +- `net.minecraft.client.gui.components.events.GuiEventListener#getBorderForArrowNavigation` - 返回绑定到当前方向的 `ScreenRectangle`。 +- `net.minecraft.client.gui.navigation.ScreenRectangle#transformAxisAligned` - 通过使用提供的 `Matrix4f` 变换位置来创建一个新的 `ScreenRectangle`。 +- `net.minecraft.client.gui.narration.NarratableEntry#getNarratables` - 返回当前对象内可叙述对象的列表。 +- `net.minecraft.client.gui.screens.recipebook.RecipeCollection#EMPTY` - 一个空的配方集合。 +- `net.minecraft.client.gui.screens.worldselection` + - `ExperimentsScreen$ScrollArea` - 表示当前可用实验的一个可叙述滚动区域。 + - `SwitchGrid#layout` - 返回要访问的网格布局。 +- `net.minecraft.client.model` + - `BannerFlagModel`、`BannerModel` - 旗帜和悬挂式旗帜的模型。 + - `VillagerLikeModel#translateToArms` - 平移姿势堆栈,使当前相对位置位于实体的手臂处。 +- `net.minecraft.client.model.geom.EntityModelSet#vanilla` - 创建一个包含所有原版模型的新模型集。 +- `net.minecraft.client.multiplayer.PlayerInfo#setShowHat`、`showHat` - 处理在标签页覆盖层中显示玩家的帽子层。 +- `net.minecraft.client.renderer.blockentity` + - `AbstractSignRenderer` - 告示牌如何作为方块实体渲染。 + - `HangingSignRenderer` + - `createSignModel` - 根据木材和附着位置创建一个告示牌模型。 + - `renderInHand` - 在实体手中渲染模型。 + - `$AttachmentType` - 一个枚举,根据其属性表示模型附着的位置。 + - `$ModelKey` - 模型的键,结合了 `WoodType` 及其 `$AttachmentType`。 + - `SignRenderer` + - `renderInHand` - 在实体手中渲染模型。 +- `net.minecraft.client.renderer.entity.EntityRenderer#getShadowStrength` - 返回显示阴影的原始不透明度。 +- `net.minecraft.client.renderer.entity.layers.CrossedArmsItemLayer#applyTranslation` - 应用平移以在模型的手臂中渲染物品。 +- `net.minecraft.client.renderer.texture` + - `ReloadableTexture` - 可以从其关联内容重新加载的纹理。 + - `TextureContents` - 包含与给定纹理关联的图像和元数据。 + - `TextureManager` + - `registerAndLoad` - 使用给定名称注册一个可重新加载的纹理。 + - `registerForNextReload` - 通过其资源位置注册一个纹理,以便在下一次重载时加载。 +- `net.minecraft.commands.SharedSuggestionProvider#MATCH_SPLITTER` - 定义一个匹配句点、下划线或正斜杠的匹配器。 +- `net.minecraft.core.BlockPos$TraversalNodeStatus` - 一个标记,指示 `BlockPos` 是否应被使用、跳过或停止任何进一步的遍历。 +- `net.minecraft.core.component.PatchedDataComponentMap` + - `toImmutableMap` - 返回不可变的补丁或当前映射的副本。 + - `hasNonDefault` - 返回数据组件是否有自定义值而不是仅仅默认值。 +- `net.minecraft.data.PackOutput$PathProvider#json` - 从资源键获取 JSON 路径。 +- `net.minecraft.data.loot.BlockLootSubProvider#createMultifaceBlockDrops` - 根据挖掘的方块面掉落方块。 +- `net.minecraft.data.worldgen.placement.PlacementUtils#HEIGHTMAP_NO_LEAVES` - 使用 `Heightmap$Types#MOTION_BLOCKING_NO_LEAVES` 高度图创建一个 Y 轴放置。 +- `net.minecraft.network.chat.Style#getShadowColor`、`withShadowColor` - 处理组件阴影颜色的方法。 +- `net.minecraft.network.protocol.game.ServerboundPlayerLoadedPacket` - 当客户端玩家加载到客户端世界时发送的数据包。 +- `net.minecraft.resources.FileToIdConverter#registry` - 从注册表键获取文件转换器。 +- `net.minecraft.util.ExtraCodecs` + - `idResolverCodec` - 创建一个将某个键映射到某个值的编解码器。 + - `compactListCodec` - 创建一个可以是元素或元素列表的编解码器。 + - `floatRange` - 创建一个必须在两个浮点值之间的编解码器。 + - `$LateBoundIdMapper` - 一个在功能上类似于具有关联编解码器的注册表的映射器。 +- `net.minecraft.util.profiling.jfr.JvmProfiler#onStructureGenerate` - 返回当结构尝试在世界中生成时的分析持续时间。 +- `net.minecraft.util.profiling.jfr.event.StructureGenerationEvent` - 当结构正在生成时的分析器事件。 +- `net.minecraft.util.profiling.jfr.stats.StructureGenStat` - 分析的结构生成的结果。 +- `net.minecraft.world.entity` + - `LivingEntity` + - `resolvePlayerResponsibleForDamage` - 获取对当前实体造成伤害的玩家。 + - `canBeNameTagged` - 当为 true 时,实体可以用命名牌设置名称。 + - `Mob#getPreferredWeaponType` - 获取表示实体想要拾取的武器的标签。 +- `net.minecraft.world.entity.ai.attributes.AttributeMap#resetBaseValue` - 将属性实例重置为其默认值。 +- `net.minecraft.world.entity.monster.creaking` + - `Creaking` + - `activate`、`deactivate` - 处理嘎枝的大脑逻辑的激活。 + - `setTransient`、`isHeartBound`、`setHomePos`、`getHomePos` - 处理家的位置。 + - `blameSourceForDamage` - 找到对伤害负责的玩家。 + - `tearDown` - 处理嘎枝被摧毁时的情况。 + - `creakingDeathEffects` - 处理嘎枝的死亡。 + - `playerIsStuckInYou` - 检查是否有至少四个玩家卡在嘎枝中。 + - `setTearingDown`、`isTearingDown` - 处理拆除状态。 + - `hasGlowingEyes`、`checkEyeBlink` - 处理眼睛状态。 +- `net.minecraft.world.entity.player.Player` + - `hasClientLoaded`、`setClientLoaded` - 客户端玩家是否已加载。 + - `tickClientLoadTimeout` - Tick计时器,用于在客户端玩家未加载时等待多长时间后将其踢出。 +- `net.minecraft.world.item` + - `Item#shouldPrintOpWarning` - 是否应根据存储的方块实体数据和管理员权限向玩家打印警告。 + - `ItemStack` + - `getCustomName` - 返回物品的自定义名称,如果不存在组件则返回 `null`。 + - `immutableComponents` - 返回不可变的补丁或堆栈组件映射的副本。 + - `hasNonDefault` - 返回数据组件是否有自定义值而不是仅仅默认值。 +- `net.minecraft.world.item.component.CustomData` + - `parseEntityId` - 从组件中读取实体 id。 + - `parseEntityType` - 从 id 中读取实体类型,并将其映射到其注册表对象。 +- `net.minecraft.world.item.crafting.Ingredient#isEmpty` - 返回原料是否没有值。 +- `net.minecraft.world.item.trading.Merchant#stillValid` - 检查玩家是否仍然可以访问该商人。 +- `net.minecraft.world.level` + - `Level#dragonParts` - 返回作为末影龙部件的实体列表。 + - `ServerExplosion#getDamageSource` - 返回爆炸的伤害来源。 +- `net.minecraft.world.level.block` + - `EyeblossomBlock$Type` + - `block` - 获取当前类型的方块。 + - `state` - 获取当前类型的方块状态。 + - `transform` - 返回此类型的相反状态。 + - `FlowerBlock#getBeeInteractionEffect` - 返回蜜蜂与花互动时获得的效果。 + - `FlowerPotBlock#opposite` - 返回方块的反向状态,仅适用于花盆中的 eyeblossom。 + - `MultifaceBlock#canAttachTo` - 返回此方块是否可以附着到另一个方块。 + - `MultifaceSpreadeableBlock` - 一个可以自然蔓延的多面方块。 +- `net.minecraft.world.level.block.entity.trialspawner` + - `TrialSpawner#overrideEntityToSpawn` - 更改要在试炼中生成的实体。 + - `TrialSpawnerConfig#withSpawning` - 设置在试炼中生成的实体。 + +### 变更列表 + +- `com.mojang.blaze3d.platform.NativeImage#upload` 不再接受三个设置 `TEXTURE_2D` 的过滤模式或纹理包裹钳位的布尔值 + - 这已移至 `AbstractTexture#setClamp` 和 `#setFilter` +- `net.minecraft.client.gui` + - `Gui#clear` -> `clearTitles` + - `GuiGraphics#drawWordWrap` 有一个新的重载,接受是否应对文本应用阴影 + - 默认版本启用阴影而不是禁用它 +- `net.minecraft.client.gui.components` + - `AbstractContainerWidget` 现在实现 `AbstractScrollArea` + - `AbstractScrollWidget` -> 根据用例使用 `AbstractScrollArea` 或 `AbstractTextAreaWidget`,不是一对一 + - `AbstractSelectionList` + - `setRenderHeader` 现在被捆绑到一个带有额外整数的新构造函数中 + - `getMaxScroll` -> `AbstractScrollArea#maxScrollAmount` + - `getScrollAmount` -> `AbstractScrollArea#scrollAmount` + - `scrollbarVisible` -> `AbstractScrollArea#scrollbarVisible` + - `setClampedScrollAmount`、`setScrollAmount` -> `AbstractScrollArea#setScrollAmount` + - `clampScrollAmount` -> `refreshScrollAmount` + - `updateScrollingState` -> `AbstractScrollArea#updateScrolling` + - `getScrollbarPosition`、`getDefaultScrollbarPosition` -> `scrollBarY`,不是一对一 + - `AbstractWidget#clicked` -> `isMouseOver`,已经存在 +- `net.minecraft.client.gui.components.toasts.TutorialToast` 现在在其构造函数中需要 `Font` 作为第一个参数 +- `net.minecraft.client.gui.font.glyphs.BakedGlyph$Effect` 和 `$GlyphInstance` 现在接受文本阴影的颜色和偏移 +- `net.minecraft.client.gui.screens` + - `LoadingOverlay#registerTextures` 现在接受 `TextureManager` 而不是 `Minecraft` 实例 + - `TitleScreen#preloadResources` -> `registerTextures`,不是一对一 +- `net.minecraft.client.gui.screens.debug.GameModeSwitcherScreen$GameModeSlot` 现在是一个静态内部类 +- `net.minecraft.client.gui.screens.reporting.ChatSelectionScreen$Entry`、`$PaddingEntry` 现在是静态内部类 +- `net.minecraft.client.gui.screens.worldselection.SwitchGrid$Builder#build` 不再接受 `Consumer` +- `net.minecraft.client.model` + - `DonkeyModel#createBodyLayer`、`createBabyLayer` 现在接受一个缩放因子 + - `VillagerHeadModel` -> `VillagerLikeModel` +- `net.minecraft.client.model.geom.EntityModelSet` 不再是 `ResourceManagerReloadListener` +- `net.minecraft.client.multiplayer.MultiPlayerGameMode#handlePickItem` -> `handlePickItemFromBlock` 或 `handlePickItemFromEntity`,同时提供要同步的实际对象数据和一个关于是否包含被拾取对象数据的 `boolean` +- `net.minecraft.client.particle.CherryParticle` -> `FallingLeavesParticle`,不是一对一,因为新类对其泛化有更大的配置 +- `net.minecraft.client.player.ClientInput#tick` 不再接受任何参数 +- `net.minecraft.client.renderer` + - `CubeMap#preload` -> `registerTextures`,不是一对一 + - `LevelRenderer` + - `renderLevel` 不再接受 `LightTexture` + - `onChunkLoaded` -> `onChunkReadyToRender` + - `PostChainConfig$Pass#program` -> `programId` + - `program` 现在返回具有给定 `programId` 的 `ShaderProgram` + - `ScreenEffectRenderer#renderScreenEffect` 现在接受一个 `MultiBufferSource` + - `SectionOcclusionGraph#onChunkLoaded` -> `onChunkReadyToRender` + - `Sheets#createSignMaterial`、`createHangingSignMaterial` 现在有一个接受 `ResourceLocation` 的重载 + - `SkyRenderer` + - `renderSunMoonAndStars`、`renderSunriseAndSunset` 现在接受一个 `MultiBufferSource$BufferSource` 而不是 `Tesselator` + - `renderEndSky` 不再接受 `PoseStack` + - `WeatherEffectRenderer#render` 现在接受一个 `MultiBufferSource$BufferSource` 而不是 `LightTexture` +- `net.minecraft.client.renderer.blockentity` + - `BannerRenderer#createBodyLayer` -> `BannerModel#createBodyLayer`,不是一对一 + - `HangingSignRenderer` + - `createHangingSignLayer` 现在接受一个 `HangingSignRenderer$AttachmentType` + - `$HangingSignModel` 现在被 `Model$Simple` 取代,尽管其字段可以从根获取 + - `SkullBlockRenderer#getRenderType` 现在有一个接受 `ResourceLocation` 的重载,用于覆盖表示玩家纹理 +- `net.minecraft.client.renderer.entity.AbstractHorseRenderer`、`DonkeyRenderer` 不再接受浮点缩放 +- `net.minecraft.client.renderer.entity.layers.CrossedArmsItemLayer` 现在要求泛型 `M` 是 `VillagerLikeModel` +- `net.minecraft.client.renderer.entity.state.CreakingRenderState#isActive` -> `eyesGlowing` + - 原始参数仍然存在于 `Creaking` 上,但对于渲染不是必需的 +- `net.minecraft.core.BlockPos#breadthFirstTraversal` 现在接受一个返回 `$TraversalNodeStatus` 的函数,而不是一个简单的谓词,以允许跳过某些位置 +- `net.minecraft.core.particles.TargetColorParticleOption` -> `TrailParticleOption`,不是一对一 +- `net.minecraft.data.DataProvider#savelAll` 现在有接受带有键函数的映射的重载,以获取关联的路径 +- `net.minecraft.network` + - `NoOpFrameEncoder` 被 `LocalFrameEncoder` 取代,不是一对一 + - `NoOpFrameDecoder` 被 `LocalFrameDecoder` 取代,不是一对一 + - `MonitorFrameDecoder` 被 `MonitoredLocalFrameDecoder` 取代,不是一对一 +- `net.minecraft.network.protocol.game` + - `ClientboundLevelParticlesPacket` 现在接受一个布尔值,确定粒子是否应始终渲染 + - `ClientboundMoveVehiclePacket` 现在是一个记录 + - `ClientboundPlayerInfoUpdatePacket$Entry` 现在接受一个布尔值,表示是否应显示帽子 + - `ClientboundSetHeldSlotPacket` 现在是一个记录 + - `ServerboundMoveVehiclePacket` 现在是一个记录 + - `ServerboundPickItemPacket` -> `ServerboundPickItemFromBlockPacket`、`ServerboundPickItemFromEntityPacket`;不是一对一 +- `net.minecraft.server.level + - `ServerLevel#sendParticles` 现在有一个接受覆盖限制距离和粒子是否应始终显示的重载 + - 其他接受覆盖限制器的重载现在也接受粒子是否应始终显示的布尔值 + - `ServerPlayer#doCheckFallDamage` -> `Entity#doCheckFallDamage`,现在是 final +- `net.minecraft.util` + - `ARGB#from8BitChannel` 现在是私有的,单个浮点分量可以通过 `alphaFloat`、`redFloat`、`greenFloat` 和 `blueFloat` 获得 + - `SpawnUtil#trySpawnMob` 现在接受一个布尔值,当为 false 时,允许实体无论与周围区域的碰撞状态如何都可以生成 +- `net.minecraft.util.profiling.jfr.callback.ProfiledDuration#finish` 现在接受一个布尔值,表示分析的事件是否成功 +- `net.minecraft.util.profiling.jfr.parse.JfrStatsResults` 现在接受一个结构生成统计信息的列表 +- `net.minecraft.world.effect.PoisonMobEffect`、`WitherMobEffect` 现在是公开的 +- `net.minecraft.world.entity` + - `Entity` + - `setOnGroundWithMovement` 有一个重载,将水平碰撞设置为实体当前状态的任何值。 + - `awardKillScore` 不再接受整数 + - `makeBoundingBox()` 现在是 final + - `makeBoundingBox(Vec3)` 现在 + - `onlyOpCanSetNbt` -> `EntityType#onlyOpCanSetNbt` + - `Leashable` + - `readLeashData` 现在是私有的,被一个不返回任何内容的方法取代 + - `dropLeash(boolean, boolean)` -> `dropLeash()`、`removeLeash`、`onLeashRemoved`;不是一对一,因为它们都在内部调用私有的 `dropLeash` + - `LivingEntity` + - `isLookingAtMe` 不再接受 `Predicate`,并且 `DoubleSupplier` 数组现在是一个 `double` 数组 + - `hasLineOfSight` 接受一个 double 而不是 `DoubleSupplier` +- `net.minecraft.world.entity.ai.behavior.AcquirePoi#create` 现在有接受 `BiPredicate` 用于过滤 POI 位置的重载 +- `net.minecraft.world.entity.animal.Bee#attractsBees` 现在是公开的 +- `net.minecraft.world.entity.monster.Shulker#getProgressAabb`、`getProgressDeltaAabb` 现在接受一个移动 `Vec3` +- `net.minecraft.world.entity.player` + - `Inventory` + - `setPickedItem` -> `addAndPickItem` + - `findSlotMatchingCraftingIngredient` 现在接受一个 `ItemStack` 进行比较 + - `Player#getPermissionLevel` 现在是公开的 + - `StackedContents$IngredientInfo` 现在是一个接口,像一个接受某些物品的谓词 +- `net.minecraft.world.entity.projectile.FishingHook` 不再接受 `ItemStack` +- `net.minecraft.world.inventory.Slot#getNoItemIcon` 现在返回单个 `ResourceLocation` 而不是一对 +- `net.minecraft.world.item` + - `Item$TooltipContext#of` 现在接受查看物品的 `Player` + - `MobBucketItem` 现在需要一个 `Mob` 实体类型 + -` SpawnEggItem#spawnsEntity`、`getType` 现在接受一个 `HolderLookup$Provider` +- `net.minecraft.world.item.crafting` + - `Ingredient` 现在实现 `StackedContents$IngredientInfo>` + - `items` 现在返回一个流而不是一个列表 + - `PlacementInfo#slotInfo` -> `slotsToIngredientIndex`,不是一对一 +- `net.minecraft.world.level.Level#addParticle` 现在接受一个布尔值,表示粒子是否应始终显示 +- `net.minecraft.world.level.block` + - `Block#getCloneItemStack` -> `state.BlockBehaviour#getCloneItemStack`,现在是 protected + - `CherryLeavesBlock` -> `ParticleLeavesBlock` + - `CreakingHeartBlock#canSummonCreaking` -> `isNaturalNight` + - `MultifaceBlock` 不再是抽象的,并且实现 `SimpleWaterloggedBlock` + - `getSpreader` -> `MultifaceSpreadeableBlock#getSpreader` + - `SculkVeinBlock` 现在是 `MultifaceSpreadeableBlock` 的一个实例 + - `SnowyDirtBlock#isSnowySetting` 现在是 protected +- `net.minecraft.world.level.block.entity` + - `AbstractFurnaceBlockEntity` + - `litTime` -> `litTimeRemaining` + - `litDuration` -> `litTotalTime` + - `cookingProgress` -> `cookingTimer` + - `BeehiveBlockEntity#addOccupant` 现在接受一个 `Bee` 而不是 `Entity` + - `CreakingHeartBlockEntity#setCreakingInfo` - 设置方块实体所附着的嘎枝。 +- `net.minecraft.world.level.block.state.BlockBehaviour#getCloneItemStack`、`$BlockStateBase#getCloneItemStack` 现在接受一个布尔值,表示是否有无限材料以及是否应保存当前方块数据。 +- `net.minecraft.world.level.chunk.ChunkGenerator#createStructures` 现在接受 `Level` 资源键,仅用于分析 +- `net.minecraft.world.level.levelgen.feature.configurations` + - `MultifaceGrowthConfiguration` 现在接受 `MultifaceSpreadableBlock` 而不是 `MultifaceBlock` + - `SimpleBlockConfiguration` 现在接受一个布尔值,表示是否安排Tick更新 +- `net.minecraft.world.level.levelgen.structure.Structure#generate` 现在接受 `Structure` 持有者和 `Level` 资源键,仅用于分析 + +### 移除列表 + +- `com.mojang.blaze3d.systems.RenderSystem#overlayBlendFunc` +- `net.minecraft.client.gui.components.AbstractSelectionList` + - `clickedHeader` + - `isValidMouseClick` +- `net.minecraft.client.gui.screens.recipebook.RecipeCollection#hasSingleResultItem` +- `net.minecraft.client.model` + - `DrownedModel#getArmPose`,现在是 `ArmedEntityRenderState` 的一部分 + - `FelineModel#CAT_TRANSFORMER` + - `HumanoidModel#getArmPose`,现在是 `ArmedEntityRenderState` 的一部分 + - `PlayerModel#getArmPose`,现在是 `ArmedEntityRenderState` 的一部分 + - `SkeletonModel#getArmPose`,现在是 `ArmedEntityRenderState` 的一部分 + - `VillagerModel#BABY_TRANSFORMER` +- `net.minecraft.client.renderer.texture` + - `AbstractTexture` + - `load` + - `reset` + - `getDefaultBlur` + - `PreloadedTexture` + - `TextureManager` + - `getTexture(ResourceLocation, AbstractTexture)` + - `register(String, DynamicTexture)` + - `preload` +- `net.minecraft.server.level.TicketType#POST_TELEPORT` +- `net.minecraft.world.entity.LivingEntity#deathScore` +- `net.minecraft.world.entity.ai.navigation.FlyingPathNavigation`、`GroundPathNavigation` + - `canPassDoors`、`setCanPassDoors` + - `canOpenDoors` +- `net.minecraft.world.entity.monster.creaking.CreakingTransient` +- `net.minecraft.world.entity.player.StackedItemContents#convertIngredientContents` +- `net.minecraft.world.item` + - `CompassItem#getSpawnPosition` + - `ItemStack#clearComponents` +- `net.minecraft.world.item.crafting.PlacementInfo` + - `ingredientToContents` + - `unpackedIngredients` + - `$SlotInfo` +- `net.minecraft.world.level.block.CreakingHeartBlock$CreakingHeartState` +- `net.minecraft.world.level.block.entity.BlockEntity#onlyOpCanSetNbt` +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData#setEntityId` diff --git a/primers-doc/1.21.5-from-1.21.4.md b/primers-doc/1.21.5-from-1.21.4.md new file mode 100644 index 0000000..5e9ebed --- /dev/null +++ b/primers-doc/1.21.5-from-1.21.4.md @@ -0,0 +1,3058 @@ +# Minecraft 1.21.4 -> 1.21.5 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.4 迁移到 1.21.5。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +感谢: + +- @TelepathicGrunt 提供的“非常技术性的变更”部分的信息 +- @RogueLogix 对“渲染管线重做”部分的审阅和评论 +- @Tslat 发现关于 `equipOnInteract` 的错误 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.5&tab=changelog) 中找到它们的列表。 + +## 正确处理方块实体的移除 + +以前,`BlockEntity` 会在 `BlockBehaviour#onRemove` 中处理其所有移除逻辑,包括丢弃任何存储的物品和移除方块实体本身。然而,根据方法的使用方式,由于方块实体的可变状态,可能会导致一些奇怪的行为。出于这个原因,构成移除过程的逻辑已被拆分为两个方法:`BlockEntity#preRemoveSideEffects` 和 `BlockBehaviour#affectNeighborsAfterRemoval`。 + +`BlockEntity#preRemoveSideEffects` 现在负责在方块实体从等级中移除之前从中移除任何东西。默认情况下,如果 `BlockEntity` 是一个 `Container` 实例,它会将容器的内容丢弃到等级中。其他逻辑可以在这里处理,但它通常应避免移除 `BlockEntity` 本身,除非方块实体的位置倾向于动态变化,例如活塞。 + +然后,`LevelChunk` 逻辑将在调用 `BlockBehaviour#affectNeighborsAfterRemoval` 之前调用 `removeBlockEntity`。这应该只向其他方块发送更新,指示此方块已从等级中移除。对于 `BlockEntity` 持有者,可以通过调用 `Containers#updateNeighboursAfterDestroy` 轻松完成。否则,可能希望根据情况自己调用 `Level#updateNeighborsAt`。 + +- `net.minecraft.world.Containers` + - `updateNeighboursAfterDestroy` - 在销毁指定位置的方块后更新邻居状态。 + - `dropContentsOnDestroy` 已移除,对于 `Container` 实例在 `BlockEntity#preRemoveSideEffects` 中处理 +- `net.minecraft.world.level.block.entity.BlockEntity#preRemoveSideEffects` - 处理在从等级中移除方块实体之前应发生的逻辑。 +- `net.minecraft.world.level.block.state.BlockBehaviour#onRemove`、`$BlockStateBase#onRemove` -> `affectNeighborsAfterRemoval`,应只处理更新周围邻居的逻辑,而不是丢弃容器数据 + +## 体素形状辅助类 + +`VoxelShape` 获得了许多辅助方法,用于对其基本状态进行更常见的变换。有用于创建居中(如果需要)盒子的 `Block` 方法,以及用于将 `VoxelShape` 旋转到其适当轴或方向的 `Shapes` 方法。还有一个 `Shapes#rotateAttachFace` 方法,用于旋转附着在另一个方块面上的某个 `VoxelShape`。结果要么存储在某个键到 `VoxelShape` 的 `Map` 中,要么在使用 `Block#getShapeForEachState` 时存储为 `Function`。 + +大多数之前有公开或受保护 `VoxelShape` 的 `Block` 子类现在都是私有的,重命名为通常称为 `SHAPE` 或 `SHAPES` 的字段。存储的 `VoxelShape` 也可能在 `Function` 中,而不是直接存储映射本身。 + +- `com.mojang.math.OctahedralGroup` + - `permute` - 返回给定轴在指定组内被置换到的轴。 + - `fromAngles` - 使用提供的 X 和 Y 旋转创建一个组。 +- `net.minecraft.core.Direction$Axis#choose` 现在有一个接受三个布尔值的重载 +- `net.minecraft.world.level.block.Block` + - `boxes` - 创建比指定数量多一个的盒子,使用索引作为创建 `VoxelShape` 的函数的一部分。 + - `cube` - 创建一个指定大小的居中立方体。 + - `column` - 创建一个指定大小的水平居中柱体。 + - `boxZ` - 创建一个指定大小的垂直居中(围绕 X 轴)的立方体/柱体。 + - `getShapeForEachState` 现在返回一个包装 `ImmutableMap` 的 `Function`,还有一个方法只考虑指定的属性而不是所有可能的状态。 +- `net.minecraft.world.phys.shapes` + - `DiscreteVoxelShape#rotate` - 根据 `OctahedralGroup` 的置换旋转体素形状。 + - `Shapes` + - `blockOccudes` -> `blockOccludes` + - `rotate` - 根据 `OctahedralGroup` 的置换围绕提供的向量(如果未指定则围绕方块中心)旋转给定的体素形状。 + - `equal` - 检查两个体素形状是否等效。 + - `rotateHorizontalAxis` - 创建一个轴到 `VoxelShape` 的映射,表示围绕 Y 轴旋转的方块。 + - `rotateAllAxis` - 创建一个轴到 `VoxelShape` 的映射,表示围绕任何轴旋转的方块。 + - `rotateHorizontal` - 创建一个方向到 `VoxelShape` 的映射,表示围绕 Y 轴旋转的方块。 + - `rotateAll` - 创建一个方向到 `VoxelShape` 的映射,表示围绕任何轴旋转的方块。 + - `rotateAttachFace` - 创建一个面到方向到 `VoxelShape` 的映射,表示当附着在其他方块面上时围绕 Y 轴旋转的方块。 + - `VoxelShape#move` 现在有一个接受 `Vec3i` 的重载 + +## 武器、工具和盔甲:去除冗余 + +武器、工具和盔甲有很多更新,分别移除了对 `SwordItem`、`DiggerItem` 和 `ArmorItem` 硬编码基类的依赖。这些已被其关联的数据组件取代:`WEAPON` 用于伤害,`TOOL` 用于挖掘,`ARMOR` 用于保护,`BLOCKS_ATTACKS` 用于盾牌。此外,缺失的属性通常通过设置 `ATTRIBUTE_MODIFIERS`、`MAX_DAMAGE`、`MAX_STACK_SIZE`、`DAMAGE`、`REPAIRABLE` 和 `ENCHANTABLE` 来指定。鉴于几乎所有非特定逻辑都已移至数据组件,这些类现在已被完全移除。使用可用的物品属性方法之一或直接调用 `Item$Properties#component` 将每个物品设置为武器、工具、盔甲或三者的某种组合。 + +为类似盾牌的物品构造一个 `BlockAttacks` 组件: + +```java +var blocker = new BlocksAttacks( + // 使用物品时,在格挡效果应用之前等待的秒数。 + 1.2f, + // 一个标量,用于更改格挡器被禁用的刻数。 + // 如果为负数,则格挡器通常不能被禁用。 + 0.5f, + // 一个减少量列表,说明此格挡器阻挡哪种伤害类型以及阻挡多少。 + List.of( + new DamageReduction( + // 应用减少量所需的盾牌水平格挡角度 + 90f, + // 此减少量应应用的伤害类型集合。 + // 当为空时,它适用于所有伤害类型。 + Optional.empty(), + // 减少攻击的基础伤害。 + 1f, + // 表示被阻挡的伤害分数的标量。 + 0.5f + ) + ), + // 一个函数,决定要从格挡器中移除多少耐久度。 + new ItemDamageFunction( + // 指定从格挡器移除耐久度所需的最小伤害量的阈值。 + 4f, + // 从格挡器中移除的基础耐久度。 + 1f, + // 表示要转换为移除耐久度的伤害分数的标量。 + 0.5f + ), + // 一个标签键,包含可以绕过格挡器并对持有实体直接造成伤害的物品。如果为空,则没有物品可以绕过格挡器。 + Optional.of(DamageTypeTags.BYPASSES_SHIELD), + // 当格挡器成功减轻一些伤害时播放的声音。 + Optional.of(SoundEvents.SHIELD_BLOCK), + // 当格挡器被武器禁用时播放的声音。 + Optional.of(SoundEvents.SHIELD_BREAK) +); +``` + +为类似剑的物品构造一个 `Weapon` 组件: + +```java +var weapon = new Weapon( + // 从物品中移除的耐久度。 + 3, + // 当被此武器击中时,`BlocksAttack`s 组件物品应被禁用的秒数。 + 5f +); +``` + +- `net.minecraft.core.component.DataComponents` + - `UNBREAKABLE` 现在是一个 `Unit` 实例 + - `HIDE_ADDITIONAL_TOOLTIP`、`HIDE_TOOLTIP` 已被整合到 `TOOLTIP_DISPLAY` 中,接受一个 `TooltipDisplay` + - `BLOCKS_ATTACKS` - 一个组件,决定持有的物品是否可以阻挡来自某些伤害源的攻击 + - `INSTRUMENT` 现在接受一个 `InstrumentComponent` + - `PROVIDES_TRIM_MATERIAL`、`PROVIDES_BANNER_PATTERNS` 处理其关联类型的提供者。 + - `BEES` 现在接受一个 `Bees` 组件 + - `BREAK_SOUND` - 物品损坏时播放的声音。 +- `net.minecraft.data.recipes` + - `RecipeProvider#trimSmithing` 现在接受 `TrimPattern` 的键 + - `SmithingTrimRecipeBuilder` 现在接受 `TrimPattern` 的持有者 +- `net.minecraft.world.entity.LivingEntity` + - `blockUsingShield` -> `blockUsingItem` + - `blockedByShield` -> `blockedByItem` + - `hurtCurrentlyUsedShield` 已移除 + - `canDisableBlocking` -> `getSecondsToDisableBlocking`,不是一对一 + - `applyItemBlocking` - 应用用物品格挡攻击时所做的伤害减少。 + - `isDamageSourceBlocked` 已移除 +- `net.minecraft.world.entity.player.Player#disableShield` -> `net.minecraft.world.item.component.BlocksAttacks#disable` +- `net.minecraft.world.item` + - `AnimalArmorItem` 类已移除 + - `ArmorItem` 类已移除 + - `AxeItem` 现在继承 `Item` + - `BannerPatternItem` 类已移除 + - `DiggerItem` 类已移除 + - `FireworkStarItem` 类已移除 + - `HoeItem` 现在继承 `Item` + - `InstrumentItem` 不再接受标签键 + - `Item` + - `getBreakingSound` 已移除 + - `$Properties` + - `tool` - 将物品设置为工具。 + - `pickaxe` - 将物品设置为镐。 + - `sword` - 将物品设置为剑。 + - `axe` - 将物品设置为斧。 + - `hoe` - 将物品设置为锄。 + - `shovel` - 将物品设置为锹。 + - `trimMaterial` - 将物品设置为提供纹饰材料。 + - `ItemStack#getBreakingSound` 已移除 + - `PickaxeItem` 类已移除 + - `ShovelItem` 现在继承 `Item` + - `SwordItem` 类已移除 + - `ToolMaterial#applyToolProperties` 现在接受一个布尔值,表示武器是否可以禁用格挡器(例如盾牌) +- `net.minecraft.world.item.component` + - `Bees` - 一个包含蜂巢居住者的组件。 + - `BlocksAttacks` - 用于用持有的物品格挡攻击的组件。 + - `InstrumentComponent` - 一个包含乐器播放声音的组件。 + - `ProvidesTrimMaterial` - 一个为某些盔甲提供要使用的纹饰材料的组件。 + - `Tool` 现在接受一个布尔值,表示该工具是否可以在创造模式下破坏方块 + - `Unbreakable` 类已移除 + - `Weapon` - 一个数据组件,包含物品可以造成多少伤害以及它禁用格挡器(例如盾牌)的时间。 +- `net.minecraft.world.item.equipment` + - `AllowedEntitiesProvider` - 一个函数式接口,用于获取允许处理关联逻辑的实体。 + - `ArmorMaterial` + - `humanoidProperties` -> `Item$Properties#humanoidArmor` + - `animalProperties` -> `Item$Properties#wolfArmor`、`horseArmor` + - `createAttributes` 现在是公开的 + - `Equippable` + - `equipOnInteract` - 当为 true 时,该物品可以在与另一个实体交互时装备到该实体上。 + - `saddle` - 为马鞍创建一个可装备。 + - `equipOnTarget` - 将物品装备到目标实体上。 + +### 推断马鞍:装备变更 + +为马鞍添加了一个新的 `EquipmentSlot`,这带来了用于泛化槽位逻辑的新变化。 + +首先,现在可以通过一个称为 `SimpleEquipmentLayer` 的附加 `RenderLayer` 来处理实体的装备槽位渲染。这接受实体渲染器、`EquipmentLayerRenderer`、要渲染的层类型、从实体状态获取 `ItemStack` 的函数,以及成年和幼年模型。渲染器将尝试从关联的可装备数据组件中查找客户端信息,并使用该信息在必要时渲染层。 + +接下来,实体上不再为每个装备槽位设置单独的列表,而是有一个通用的 `EntityEquipment` 对象,它持有一个槽位到 `ItemStack` 的映射委托。这大大简化了存储逻辑。 + +最后,可装备现在可以通过设置 `equipOnInteract` 来指定物品是否应在交互时(通常是右键单击)装备到生物上。 + +- `net.minecraft.client.model` + - `CamelModel` + - `head` 现在是公开的 + - `createBodyMesh` - 创建骆驼的网格定义。 + - `CamelSaddleModel` - 带有马鞍的骆驼模型。 + - `DonkeyModel#createSaddleLayer` - 为带有马鞍的驴创建层定义。 + - `EquineSaddleModel` - 带有马鞍的马科动物的模型。 + - `PolarBearModel#createBodyLayer` 现在接受一个布尔值,表示实体是否为幼年 +- `net.minecraft.client.renderer.entity.layers.HorseArmorLayer`、`SaddleLayer` -> `SimpleEquipmentLayer` +- `net.minecraft.client.renderer.entity.state` + - `CamelRenderState#isSaddled` -> `saddle`,不是一对一 + - `EquineRenderState#isSaddled` -> `saddle`,不是一对一 + - `PigRenderState#isSaddled` -> `saddle`,不是一对一 + - `SaddleableRenderState` 类已移除 + - `StriderRenderState#isSaddled` -> `saddle`,不是一对一 + - `CamelRenderState#isSaddled` -> `saddle`,不是一对一 +- `net.minecraft.client.resources.model.EquipmentClientInfo$LayerType` 现在包含: + - `PIG_SADDLE` + - `STRIDER_SADDLE` + - `CAMEL_SADDLE` + - `HORSE_SADDLE` + - `DONKEY_SADDLE` + - `MULE_SADDLE` + - `ZOMBIE_HORSE_SADDLE` + - `SKELETON_HORSE_SADDLE` + - `trimAssetPrefix` - 返回应用于包含关联类型的盔甲纹饰的纹理的前缀。 +- `net.minecraft.world.entity` + - `EntityEquipment` - 一个槽位到物品堆栈的映射,表示实体的装备。 + - `EquipmentSlot` + - `SADDLE`、`$Type#SADDLE` + - `canIncreaseExperience` - 该槽位是否可以在杀死生物时增加获得的经验值数量。 + - `EquipmentSlotGroup` 现在是一个可迭代对象 + - `SADDLE` + - `slots` - 返回组内的槽位。 + - `LivingEntity` + - `getEquipSound` - 获取将物品装备到槽位时要播放的声音。 + - `getArmorSlots`、`getHandSlots`、`getArmorAndBodyArmorSlots`、`getAllSlots` 已移除 + - `equipment` - 实体穿戴的装备。 + - `createEquipment` - 设置实体穿戴的默认装备。 + - `drop` - 丢弃指定的堆栈。 + - `getItemBySlot`、`setItemBySlot` 不再是抽象的。 + - `verfiyEquippedItem` 已移除 + - `Mob` + - `isSaddled` - 检查马鞍槽位中是否有物品。 + - `createEquipmentSlotContainer` - 为装备槽位创建一个单物品容器。 + - `OwnableEntity#getRootOwner` - 获取实体的最高级所有者。 + - `Saddleable` 接口已移除 +- `net.minecraft.world.entity.animal.horse.AbstractHorse` + - `syncSaddletoClients` 已移除 + - `getBodyArmorAccess` 已移除 +- `net.minecraft.world.entity.player` + - `Inventory` + - `armor`、`offhand` -> `EQUIPMENT_SLOT_MAPPING`,不是一对一 + - `selected` 现在是私有的 + - `setSelectedHotbarSlot` -> `setSelectedSlot` + - 还存在获取器 `getSelectedSlot` + - `getSelected` -> `getSelectedItem` + - 还存在设置器 `setSelectedItem` + - `getNonEquipmentItems` - 返回库存中的非装备物品列表。 + - `getDestroySpeed` 已移除 + - `getArmor` 已移除 + - `PlayerEquipment` - 玩家穿戴的装备。 +- `net.minecraft.world.item` + - `Item#inventoryTick(ItemStack, Level, Entity, int, boolean)` -> `inventoryTick(ItemStack, ServerLevel, Entity, EquipmentSlot)` + - `SaddleItem` 类已移除 + +## 加权列表重做 + +加权随机列表已被重新设计为一个基本类,用于保存加权条目,以及一个辅助类,可以从对象本身获取权重。 + +首先是 `WeightedList`。它实际上是 `SimpleWeightedRandomList` 的替代品,通过将 `Weighted`(`WeightedEntry` 的替代品)条目存储在列表本身中,以完全相同的方式工作。在内部,列表要么存储为对象条目的扁平数组,要么在总权重大于 64 时存储为紧凑加权列表。然后,要获取一个随机元素,可以调用 `getRandom` 或 `getRandomOrThrow` 来获取一个条目。如果列表中没有元素,这两个方法都将返回某种形式的空对象或异常。 + +然后是 `WeightedRandom` 中的静态辅助方法。这些方法接受原始列表和某个 `ToIntFunction`,该函数从列表的对象中获取权重。一些方法还接受一个整数,表示要选择的最大索引或与加权索引关联的条目。 + +- `net.minecraft.client.resources.model.WeightedBakedModel` 现在接受 `WeightedList` 而不是 `SimpleWeightedRandomList` +- `net.minecraft.util.random` + - `SimpleWeightedRandomList`、`WeightedRandomList` -> `WeightedList`,现在是 final,不是一对一 + - `contains` - 检查列表是否包含此元素。 + - `Weight` 类已移除 + - `WeightedEntry` -> `Weighted` + - 所有 `WeightedRandom` 静态方法现在接受一个 `ToIntFunction` 来获取提供的列表中某个条目的权重 +- `net.minecraft.util.valueproviders.WeightedListInt` 现在接受 `WeightedList` +- `net.minecraft.world.level.SpawnData#LIST_CODEC` 现在是一个 `WeightedList` 的 `SpawnData` +- `net.minecraft.world.level.biome` + - `Biome#getBackgroundMusic` 现在是一个 `WeightedList` 的 `Music` + - `BiomeSpecialEffects#getBackgroundMusic`、`$Builder#backgroundMusic` 现在是一个 `WeightedList` 的 `Music` + - `MobSpawnSettings#EMPTY_MOB_LIST`、`getMobs` 现在是一个 `WeightedList` +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig#spawnPotentialsDefinition`、`lootTablesToEject` 现在接受 `WeightedList` +- `net.minecraft.world.level.chunk.ChunkGenerator#getMobsAt` 现在返回一个 `WeightedList` +- `net.minecraft.world.level.levelgen.feature.stateproviders.WeightedStateProvider` 现在使用 `WeightedList` +- `net.minecraft.world.level.levelgen.heightproviders.WeightedListHeight` 现在使用 `WeightedList` +- `net.minecraft.world.level.levelgen.structure.StructureSpawnOverride` 现在接受 `WeightedList` +- `net.minecraft.world.level.levelgen.structure.pools.alias` + - `PoolAliasBinding#random`、`randomGroup` 现在接受 `WeightedList` + - `Random` 现在接受 `WeightedList` + - `RandomGroup` 现在接受 `WeightedList` +- `net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure#FORTRESS_ENEMIES` 现在是一个 `WeightedList` + +## 加载票 + +加载票已被重新实现为一个半类型注册表、半硬编码的系统。使区块在特定时间段内保持加载或模拟的底层逻辑仍然存在;然而,与每个加载票关联的逻辑被硬编码到它们适当的位置,例如强制加载或玩家加载加载票。 + +加载票从其注册的 `TicketType` 开始,其中包含有关加载票应持续多少刻(如果永久则为 `0`)、是否应将加载票保存到磁盘以及加载票的用途的信息。加载票有两种潜在用途:一种用于加载区块并保持其加载,另一种用于根据加载票创建者的预期移动来模拟区块。大多数加载票指定它们同时用于加载和模拟。 + +有两种特殊类型具有关联的额外行为。`TicketType#FORCED` 有一些立即加载区块并保持其加载的逻辑。`TicketType#UNKNOWN` 不能自动超时,意味着除非明确指定,否则它们永远不会被移除。 + +```java +// 您需要将加载票类型注册到 `BuiltInRegistries#TICKET_TYPE` +public static final TicketType EXAMPLE = new TicketType( + // 加载票被移除前的刻数 + // 如果不应移除,则设置为 0 + 0L, + // 是否应将加载票保存到磁盘 + true, + // 加载票的用途 + TicketType.TicketUse.LOADING_AND_SIMULATION +); +``` + +然后是 `Ticket` 类,它实际存储在 `TicketStorage` 中并由其处理。`Ticket` 类接受加载票的类型,并使用它来自动填充它到期的时间。它还接受加载票等级,通常是一个值 31(用于实体Tick和方块Tick)、32(用于方块Tick)或 33(只能访问静态或修改,不能自然更新)减去可以加载的区块半径。然后通过调用 `TicketStorage#addTicketWithRadius` 或其委托 `ServerChunkCache#addTicketWithRadius` 将加载票添加到进程中。如果您希望手动指定加载票而不是根据其半径计算,也可以使用 `addTicket`。 + +- `net.minecraft.server.level` + - `ChunkMap` 现在接受 `TicketStorage` + - `$TrackedEntity#broadcastIgnorePlayers` - 将数据包广播给所有玩家,但 UUID 列表中的玩家除外。 + - `DistanceManager` + - `chunksToUpdateFutures` 现在是 protected,并接受一个 `TicketStorage` + - `purgeStaleTickets` -> `net.minecraft.world.level.TicketStorage#purgeStaleTickets` + - `getTicketDebugString` -> `net.minecraft.world.level.TicketStorage#getTicketDebugString` + - `getChunkLevel` - 返回当前区块等级,或当提供的布尔值为 true 时返回模拟等级。 + - `getTickingChunks` 已移除 + - `removeTicketsOnClosing` 已移除 + - `$ChunkTicketTracker` -> `LoadingChunkTracker` 或 `SimulationChunkTracker` + - `ServerChunkCache` + - `addRegionTicket` -> `addTicketWithRadius` 或 `addTicket` + - `removeRegionTicket` -> `removeTicketWithRadius` + - `removeTicketsOnClosing` -> `deactivateTicketsOnClosing` + - `Ticket` 不再是 final,也不再实现 `Comparable` + - 构造函数不再接受键 + - `CODEC` + - `setCreatedTick`、`timedOut` -> `resetTicksLeft`、`decreaseTicksLeft`、`isTimedOut`;不是一对一 + - `TicketType` 现在是一个记录,不再有泛型 + - `getComparator` 已移除 + - `doesLoad`、`doesSimulate` - 检查加载票用途是否用于其特定实例。 + - `$TicketUse` - 加载票可以用于什么。 + - `TickingTracker` -> `SimulationChunkTracker` +- `net.minecraft.world.level.ForcedChunksSavedData` -> `TicketStorage` +- `net.minecraft.world.level.chunk.ChunkSource` + - `updateChunkForced` 现在返回一个布尔值,指示区块是否已被强制加载 + - `getForceLoadedChunks` - 返回所有已被强制加载的区块。 + +## 游戏测试大修 + +游戏测试已被完全改造为一个基于注册表的系统,与之前自动注解驱动的系统完全不同。然而,使用该系统所需的大多数实现必须由您自己完成,而不是由原版提供。因此,此说明将涵盖整个系统,包括哪些部分需要大量工作才能使其类似于先前版本的注解驱动系统。 + +### 环境 + +所有游戏测试都在某个环境中发生。大多数情况下,测试可以独立于区域进行,但有时环境需要以某种方式进行管理,例如检查实体或方块在给定时间是否执行了某些操作。为了促进给定测试实例的环境设置和拆除,创建了一个 `TestEnvironmentDefinition`。 + +`TestEnvironmentDefinition` 的工作方式类似于 `BeforeBatch` 和 `AfterBatch` 注解。环境包含两个方法 `setup` 和 `teardown`,用于管理测试的 `ServerLevel`。环境以基于类型的注册表系统构建,这意味着每个环境将一个 `MapCodec` 注册到内置注册表 `minecraft:test_environment_definition_type`,然后通过数据包注册表 `minecraft:test_environment` 中的 `TestEnvironmentDefinition` 使用。 + +默认情况下,原版提供了 `minecraft:default` 测试环境,它什么都不做。然而,可以使用可用的测试定义类型创建其他测试环境。 + +#### 游戏规则 + +此环境类型设置测试要使用的游戏规则。在拆除期间,游戏规则将恢复为其默认值。 + +```json5 +// examplemod:example_environment +// 在 'data/examplemod/test_environment/example_environment.json' +{ + "type": "minecraft:game_rules", + + // 要设置的具有布尔值的游戏规则列表 + "bool_rules": [ + { + // 规则的名称 + "rule": "doFireTick", + "value": false + } + // ... + ], + + // 要设置的具有整数值的游戏规则列表 + "int_rules": [ + { + "rule": "playersSleepingPercentage", + "value": 50 + } + // ... + ] +} +``` + +#### 时间 + +此环境类型将时间设置为某个非负整数,类似于 `/time set ` 命令的使用方式。 + +```json5 +// examplemod:example_environment +// 在 'data/examplemod/test_environment/example_environment.json' +{ + "type": "minecraft:time_of_day", + + // 设置世界中的时间 + // 常见值: + // - 白天 -> 1000 + // - 中午 -> 6000 + // - 夜晚 -> 13000 + // - 午夜 -> 18000 + "time": 13000 +} +``` + +#### 天气 + +此环境类型设置天气,类似于 `/weather` 命令的使用方式。 + +```json5 +// examplemod:example_environment +// 在 'data/examplemod/test_environment/example_environment.json' +{ + "type": "minecraft:weather", + + // 可以是三个值之一: + // - clear (无天气) + // - rain (雨) + // - thunder (雷雨) + "weather": "thunder" +} +``` + +#### 函数 + +此环境类型提供两个 `ResourceLocation` 指向 mcfunctions,分别用于设置和拆除等级。 + +```json5 +// examplemod:example_environment +// 在 'data/examplemod/test_environment/example_environment.json' +{ + "type": "minecraft:function", + + // 要使用的设置 mcfunction + // 如果未指定,则不会运行任何内容 + // 指向 'data/examplemod/function/example/setup.mcfunction' + "setup": "examplemod:example/setup", + + // 要使用的拆除 mcfunction + // 如果未指定,则不会运行任何内容 + // 指向 'data/examplemod/function/example/teardown.mcfunction' + "teardown": "examplemod:example/teardown" +} +``` + +#### 复合 + +如果需要多种组合,则可以使用复合环境类型(恰当地命名为 `all_of`)将上述多个环境类型串联在一起。 + +```json5 +// examplemod:example_environment +// 在 'data/examplemod/test_environment/example_environment.json' +{ + "type": "minecraft:all_of", + + // 要使用的测试环境列表 + // 可以指定注册表名称或环境本身 + "definitions": [ + // 指向 'data/minecraft/test_environment/default.json' + "minecraft:default", + { + // 原始环境定义 + "type": "..." + } + // ... + ] +} +``` + +### 自定义类型 + +如果上述类型都不起作用,则可以通过实现 `TestEnvironmentDefinition` 并创建关联的 `MapCodec` 来创建自定义定义: + +```java +public record ExampleEnvironmentType(int value1, boolean value2) implements TestEnvironmentDefinition { + + // 构造要注册的映射编解码器 + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.INT.fieldOf("value1").forGetter(ExampleEnvironmentType::value1), + Codec.BOOL.fieldOf("value2").forGetter(ExampleEnvironmentType::value2) + ).apply(instance, ExampleEnvironmentType::new) + ); + + @Override + public void setup(ServerLevel level) { + // 在此处设置任何必要的内容 + } + + @Override + public void teardown(ServerLevel level) { + // 撤消在 setup 方法中更改的任何内容 + // 这应恢复到默认值或之前的值 + } + + @Override + public MapCodec codec() { + return CODEC; + } +} +``` + +然后使用您的模组加载器所需的任何注册表方法注册 `MapCodec`: + +```java +Registry.register( + BuiltInRegistries.TEST_ENVIRONMENT_DEFINITION_TYPE, + ResourceLocation.fromNamespaceAndPath("examplemod", "example_environment_type"), + ExampleEnvironmentType.CODEC +); +``` + +最后,您可以在环境定义中使用它: + +```json5 +// examplemod:example_environment +// 在 'data/examplemod/test_environment/example_environment.json' +{ + "type": "examplemod:example_environment_type", + + "value1": 0, + "value2": true +} +``` + +### 测试函数 + +游戏测试的最初概念是围绕运行 `GameTestHelper` 中的函数来判定测试成功或失败。测试函数是这些函数的注册表驱动表示。本质上,每个测试函数都是一个接受 `GameTestHelper` 的方法。 + +目前,原版只提供 `minecraft:always_pass`,它只调用 `GameTestHelper#succeed`。测试函数也不是生成的,意味着它只是用提供的任何内容运行该值。因此,一个测试函数通常应代表一个旧的游戏测试: + +```java +Registry.register( + BuiltInRegistries.TEST_FUNCTION, + ResourceLocation.fromNamespaceAndPath("examplemod", "example_function"), + (GameTestHelper helper) -> { + // 运行您想要的任何游戏测试命令 + helper.assertBlockPresent(...); + + // 确保有某种方式成功 + helper.succeedIf(() -> ...); + } +); +``` + +### 测试数据 + +现在我们有了环境和测试函数,就可以开始定义我们的游戏测试了。这是通过 `TestData` 完成的,它相当于 `GameTest` 注解。唯一的变化是,结构现在通过 `structure` 由其 `ResourceLocation` 引用,`GameTest#timeoutTicks` 现在重命名为 `TestData#maxTicks`,并且不再指定 `GameTest#rotationSteps`,而是通过 `TestData#rotation` 提供 `Rotation`。其他所有内容保持不变,只是以不同的格式表示。 + +### 游戏测试实例 + +有了 `TestData`,我们现在可以通过 `GameTestInstance` 将所有内容链接在一起。这个实例实际上代表了一个单独的测试。再次,原版只提供默认的 `minecraft:always_pass`,因此我们需要自己构造实例。 + +#### 原始实例 + +以前的游戏测试使用 `minecraft:function` 实现,它将测试函数链接到测试数据。 + +```json +// examplemod:example_test +// 在 'data/examplemod/test_instance/example_test.json' +{ + "type": "minecraft:function", + + // 指向测试函数注册表中的 'Consumer' + "function": "examplemod:example_function", + + // 'TestData' 信息 + + // 运行测试的环境 + // 指向 'data/examplemod/test_environment/example_environment.json' + "environment": "examplemod:example_environment", + // 用于游戏测试的结构 + // 指向 'data/examplemod/structure/example_structure.nbt' + "structure": "examplemod:example_structure", + // 游戏测试将运行直到自动失败的刻数 + "max_ticks": 400, + // 用于设置游戏测试所需所有内容的刻数 + // 这不计入测试可以进行的最大刻数 + // 如果未指定,默认为 0 + "setup_ticks": 50, + // 测试是否必须成功才能将批次运行标记为成功 + // 如果未指定,默认为 true + "required": true, + // 指定结构和所有后续辅助方法应如何为测试旋转 + // 如果未指定,则不旋转任何内容 + "rotation": "clockwise_90", + // 当为 true 时,测试只能通过 `/test` 命令运行 + // 如果未指定,默认为 false + "manual_only": true, + // 指定测试可以重新运行的最大次数 + // 如果未指定,默认为 1 + "max_attempts": 3, + // 指定测试必须发生的最小成功次数,才能将测试标记为成功 + // 这必须小于或等于允许的最大尝试次数 + // 如果未指定,默认为 1 + "required_successes": 1, + // 返回结构边界是否应保持顶部为空 + // 这目前仅用于基于方块的测试实例 + // 如果未指定,默认为 false + "sky_access": false +} +``` + +#### 基于方块的实例 + +原版还通过 `minecraft:block_based` 提供了一个基于方块的测试实例。这是通过结构处理的,测试方块通过 `Level#hasNeighborSignal` 接收信号。要开始,结构必须有一个设置为启动模式的测试方块。然后触发该方块,发送一个持续一刻的十五信号脉冲。然后,结构可以有任意数量的测试方块,设置为日志、接受或失败模式。日志测试方块在激活时也会发送十五信号脉冲。接受和失败测试方块如果其中任何一个被激活,则游戏测试成功或失败(成功优先于失败)。 + +由于此测试依赖于结构中的测试方块,因此除了测试数据之外,不需要额外的信息: + +```json +// examplemod:example_test +// 在 'data/examplemod/test_instance/example_test.json' +{ + "type": "minecraft:block_based", + + // 'TestData' 信息 + + // 指向 'data/examplemod/test_environment/example_environment.json' + "environment": "examplemod:example_environment", + // 指向 'data/examplemod/structure/example_structure.nbt' + "structure": "examplemod:example_structure", + "max_ticks": 400, + "setup_ticks": 50, + "required": true, + "rotation": "clockwise_90", + "manual_only": true, + "max_attempts": 3, + "required_successes": 1, + "sky_access": false +} +``` + +#### 自定义测试 + +如果您需要实现自己的基于测试的逻辑,无论是使用更动态的功能 ~~还是因为您懒得将所有数据逻辑迁移到新系统~~,您可以通过扩展 `GameTestInstance` 并创建关联的 `MapCodec` 来创建自己的自定义测试实例: + +```java +public class ExampleTestInstance extends GameTestInstance { + + // 构造要注册的映射编解码器 + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.INT.fieldOf("value1").forGetter(test -> test.value1), + Codec.BOOL.fieldOf("value2").forGetter(test -> test.value2), + TestData.CODEC.forGetter(ExampleTestInstance::info) + ).apply(instance, ExampleTestInstance::new) + ); + + public ExampleTestInstance(int value1, boolean value2, TestData> info) { + super(info); + } + + @Override + public void run(GameTestHelper helper) { + // 运行您想要的任何游戏测试命令 + helper.assertBlockPresent(...); + + // 确保有某种方式成功 + helper.succeedIf(() -> ...); + } + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + protected MutableComponent typeDescription() { + // 提供关于此测试应该是什么的描述 + // 应使用可翻译组件 + return Component.literal("Example Test Instance"); + } +} +``` + +然后使用您的模组加载器所需的任何注册表方法注册 `MapCodec`: + +```java +Registry.register( + BuiltInRegistries.TEST_INSTANCE_TYPE, + ResourceLocation.fromNamespaceAndPath("examplemod", "example_test_instance"), + ExampleTestInstance.CODEC +); +``` + +最后,您可以在测试实例中使用它: + +```json +// examplemod:example_test +// 在 'data/examplemod/test_instance/example_test.json' +{ + "type": "examplemod:example_test_instance", + + "value1": 0, + "value2": true, + + // 'TestData' 信息 + + // 指向 'data/examplemod/test_environment/example_environment.json' + "environment": "examplemod:example_environment", + // 指向 'data/examplemod/structure/example_structure.nbt' + "structure": "examplemod:example_structure", + "max_ticks": 400, + "setup_ticks": 50, + "required": true, + "rotation": "clockwise_90", + "manual_only": true, + "max_attempts": 3, + "required_successes": 1, + "sky_access": false +} +``` + +- `net.minecraft.client.renderer.blockentity` + - `BeaconRenderer` 现在有一个泛型,接受 `BlockEntity` 和 `BeaconBeamOwner` 的子类型 + - `StructureBlockRenderer` -> `BlockEntityWithBoundingBoxRenderer`,不是一对一 +- `net.minecraft.core.registries.Registries#TEST_FUNCTION`、`TEST_ENVIRONMENT_DEFINITION_TYPE`、`TEST_INSTANCE_TYPE` +- `net.minecraft.gametest.Main` - 游戏测试服务器的入口点。 +- `net.minecraft.gametest.framework` + - `AfterBatch`、`BeforeBatch` 注解已移除 + - `BlockBasedTestInstance` - 用于测试测试方块的测试实例。 + - `BuiltinTestFunctions` - 包含所有已注册的测试函数。 + - `FailedTestTracker` - 用于保存所有失败的测试的对象。 + - `FunctionGameTestInstance` - 用于运行测试函数的测试实例。 + - `GameTest` 注解已移除 + - `GameTestAssertException` 现在继承 `GameTestException` + - `GameTestException` - 游戏测试执行期间抛出的异常。 + - `GameTestBatch` 现在接受索引和环境定义,而不是名称和批次设置 + - `GameTestBatchFactory` + - `fromTestFunction` -> `divideIntoBatches`,不是一对一 + - `toGameTestInfo` 已移除 + - `toGameTestBatch` 现在接受环境定义和索引 + - `$TestDecorator` - 从测试实例和等级创建测试信息列表。 + - `GameTestEnvironments` - 包含用于批处理游戏测试实例的所有环境。 + - `GameTestGenerator` 注解已移除 + - `GameTestHelper` + - `tickBlock` - 在特定位置Tick方块。 + - `assertionException` - 返回一个在出错时要抛出的新异常。 + - `getBlockEntity` 现在接受一个 `Class` 来将方块实体转换为 + - `assertBlockTag` - 检查该位置的方块是否在提供的标签内。 + - `assertBlock` 现在接受一个方块 -> 组件函数,用于错误消息。 + - `assertBlockProperty` 现在接受 `Component` 而不是字符串 + - `assertBlockState` 现在接受什么都不接受、一个方块状态 -> 组件函数或一个提供的组件 + - `assertRedstoneSignal` 现在接受一个提供的组件 + - `assertContainerSingle` - 断言容器恰好包含一个指定物品。 + - `assertEntityPosition`、`assertEntityProperty` 现在接受一个组件 + - `fail` 现在接受一个 `Component` 作为错误消息 + - `assertTrue`、`assertValueEqual`、`assertFalse` 现在接受一个组件 + - `GameTestInfo` 现在接受一个持有者包装的 `GameTestInstance` 而不是 `TestFunction` + - `setStructureBlockPos` -> `setTestBlockPos` + - `placeStructure` 现在不返回任何内容 + - `getTestName` - `id`,不是一对一 + - `getStructureBlockPos` -> `getTestBlockPos` + - `getStructureBlockEntity` -> `getTestInstanceBlockEntity` + - `getStructureName` -> `getStructure` + - `getTestFunction` -> `getTest`、`getTestHolder`,不是一对一 + - `getOrCalculateNorthwestCorner`、`setNorthwestCorner` 已移除 + - `fail` 现在接受 `Component` 或 `GameTestException` 而不是 `Throwable` + - `getError` 现在返回 `GameTestException` 而不是 `Throwable` + - `GameTestInstance` - 定义要运行的测试。 + - `GameTestInstances` - 包含所有已注册的测试。 + - `GameTestMainUtil` - 用于运行游戏测试服务器的工具。 + - `GameTestRegistry` 类已移除 + - `GameTestSequence` + - `tickAndContinue`、`tickAndFailIfNotComplete` 现在接受一个整数作为刻,而不是 long + - `thenFail` 现在接受一个提供的 `GameTestException` 而不是 `Throwable` + - `GameTestServer#create` 现在接受一个可选的字符串和布尔值,而不是测试函数集合和起始位置 + - `GeneratedTest` - 一个包含要在给定环境下运行的测试和要应用的函数的对象 + - `GameTestTicker$State` - 一个枚举,包含游戏测试Tick器当前正在执行的状态。 + - `GameTestTimeoutException` 现在继承 `GameTestException` + - `ReportGameListener#spawnBeacon` 已移除 + - `StructureBlockPosFinder` -> `TestPosFinder` + - `StructureUtils` + - `testStructuresDir` 现在是一个路径 + - `getStructureBounds`、`getStructureBoundingBox`、`getStructureOrigin`、`addCommandBlockAndButtonToStartTest` 已移除 + - `createNewEmptyStructureBlock` -> `createNewEmptyTest`,不是一对一 + - `getStartCorner`、`prepareTestStructure`、`encaseStructure`、`removeBarriers` 已移除 + - `findStructureBlockContainingPos` -> `findTestContainingPos` + - `findNearestStructureBlock` -> `findNearestTest` + - `findStructureByTestFunction`、`createStructureBlock` 已移除 + - `findStructureBlocks` -> `findTestBlocks` + - `lookedAtStructureBlockPos` -> `lookedAtTestPos` + - `TestClassNameArgument` 已移除 + - `TestEnvironmentDefinition` - 定义测试运行的环境,通过在等级上适当设置数据。 + - `TestFinder` 不再包含上下文的泛型 + - `$Builder#allTests`、`allTestsInClass`、`locateByName` 已移除 + - `$Builder#byArgument` -> `byResourceSelection` + - `TestFunction` -> `TestData`,不是一对一 + - `TestFunctionArgument` -> `net.minecraft.commands.arguments.ResourceSelectorArgument` + - `TestFunctionFinder` -> `TestInstanceFinder` + - `TestFunctionLoader` - 保存要加载和运行的测试函数列表。 + - `UnknownGameTestException` - 当游戏测试的错误未知时抛出的异常。 +- `net.minecraft.network.protocol.game` + - `ClientboundTestInstanceBlockState` - 发送到客户端的数据包,包含测试的状态及其大小。 + - `ServerboundSetTestBlockPacket` - 发送到服务器的数据包,用于设置测试方块中要运行的信息。 + - `ServerboundTestInstanceBlockActionPacket` - 发送到服务器的数据包,用于在测试方块中设置测试实例。 +- `net.minecraft.world.entity.player.Player` + - `openTestBlock` - 打开一个测试方块。 + - `openTestInstanceBlock` - 为游戏测试实例打开一个测试方块。 +- `net.minecraft.world.level.block` + - `TestBlock` - 用于运行游戏测试的方块。 + - `TestInstanceBlock` - 用于管理单个游戏测试的方块。 +- `net.minecraft.world.level.block.entity` + - `BeaconBeamOwner` - 一个接口,表示具有信标光束的方块实体。 + - `BeaconBlockEntity` 现在实现 `BeaconBeamOwner` + - `BeaconBeamSection` -> `BeaconBeamOwner$Section` + - `BoundingBoxRenderable` - 一个接口,表示可以渲染任意大小边界框的方块实体。 + - `StructureBlockEntity` 现在实现 `BoundingBoxRenderable` + - `TestBlockEntity` - 用于运行游戏测试的方块实体。 + - `TestInstanceBlockEntity` - 用于管理单个游戏测试的方块实体。 +- `net.minecraft.world.level.block.state.properties.TestBlockMode` - 一个用于表示与测试方块关联的游戏测试当前状态的属性。 + +## 数据组件获取器 + +数据组件系统现在可以通过使用 `DataComponentGetter` 在任意对象上表示。顾名思义,获取器负责从关联的类型键获取组件。方块实体和实体都使用 `DataComponentGetter` 来允许查询内部数据,例如变体信息或自定义名称。它们都有从另一个持有者收集数据组件的方法(通过 `applyImplicitComponents` 或 `applyImplicitComponent`)。方块实体还包含通过 `collectImplicitComponents` 收集到另一个持有者的方法。 + +### 物品 + +`ItemSubPredicate` 已被完全替换为 `DataComponentPredicate`。每个子谓词在系统中都有其适当的对应项。 + +- `net.minecraft.advancements.critereon.*` -> `net.minecraft.core.component.predicates.*` + - `ItemAttributeModifiersPredicate` -> `AttributeModifiersPredicate` + - `ItemBundlePredicate` -> `BundlePredicate` + - `ItemContainerPredicate` -> `ContainerPredicate` + - `ItemCustomDataPredicate` -> `CustomDataPredicate` + - `ItemDamagePredicate` -> `DamagePredicate` + - `ItemEnchantmentsPredicate` -> `EnchantmentsPredicate` + - `ItemFireworkExplosionPredicate` -> `FireworkExplosionPredicate` + - `ItemFireworksPredicate` -> `FireworksPredicate` + - `ItemJukeboxPlayablePredicate` -> `JukeboxPlayablePredicate` + - `ItemPotionsPredicate` -> `PotionsPredicate` + - `ItemSubPredicate` -> `DataComponentPredicate`,不是一对一 + - `SINGLE_STREAM_CODEC` + - `ItemSubPredicates` -> `DataComponentPredicates`,不是一对一 + - `ItemTrimPredicate` -> `TrimPredicate` + - `ItemWritableBookPredicate` -> `WritableBookPredicate` + - `ItemWrittenBookPredicate` -> `WrittenBookPredicate` +- `net.minecraft.advancements.critereon` + - `BlockPredicate` 现在接受一个 `DataComponentMatchers` 用于匹配任何委托的组件数据 + - `DataComponentMatchers` - 一个对 `DataComponentGetter` 进行操作的谓词,匹配提供者上的任何精确和部分组件数据。 + - `EntityPredicate` 现在接受一个 `DataComponentMatchers` 而不是 `Optional` + - `ItemPredicate` 现在接受一个 `DataComponentMatchers` 用于匹配任何委托的组件数据 + - `NbtPredicate#matches` 现在接受一个 `DataComponentGetter` 而不是 `ItemStack` + - `SingleComponentItemPredicate` 现在实现 `DataComponentPredicate` 而不是 `ItemSubPredicate` + - `matches(ItemStack, T)` -> `matches(T)` +- `net.minecraft.core.component` + - `DataComponentPatch` + - `DELIMITED_STREAM_CODEC` + - `$CodecGetter` - 获取给定组件类型的编解码器。 + - `DataComponentPredicate` -> `DataComponentExactPredicate` + - `isEmpty` - 检查谓词内的预期组件列表是否为空。 +- `net.minecraft.core.registries.Registries#ITEM_SUB_PREDICATE_TYPE` -> `DATA_COMPONENT_PREDICATE_TYPE`,不是一对一 +- `net.minecraft.world.item.AdventureModePredicate` 不再接受一个布尔值来显示在工具提示中 +- `net.minecraft.world.item` + - `BannerItem#appendHoverTextFromBannerBlockEntityTag` 已移除 + - `Item#appendHoverText(ItemStack, Item.TooltipContext, List, TooltipFlag)` -> `appendHoverText(ItemStack, Item.TooltipContext, TooltipDisplay, Consumer, TooltipFlag)`,现已弃用 + - `ItemStack` + - `addToTooltip` 现在是公开的 + - `addDetailsToTooltip` - 将物品的组件详细信息附加到工具提示。 + - `JukeboxPlayable#showInTooltip` 已移除 +- `net.minecraft.world.item.component` + - `BlockItemStateProperties` 现在实现 `TooltipProvider` + - `ChargedProjectiles` 现在实现 `TooltipProvider` + - `CustomData#itemMatcher` 已移除 + - `DyedItemColor#showInTooltip` 已移除 + - `FireworkExplosion#addShapeNameTooltip` 已移除 + - `ItemAttributeModifiers#showInTooltip` 已移除 + - `ItemContainerContents` 现在实现 `TooltipProvider` + - `SeededContainerLoot` 现在实现 `TooltipProvider` + - `TooltipDisplay` - 一个组件,处理物品工具提示中应隐藏的内容。 + - `TooltipProvider#addToTooltip` 现在接受一个 `DataComponentGetter` +- `net.minecraft.world.item.enchantment.ItemEnchantments#showInTooltip` 已移除 +- `net.minecraft.world.item.equipment.trim.ArmorTrim#showInTooltip` 已移除 +- `net.minecraft.world.item.trading.ItemCost` 现在接受 `DataComponentExactPredicate` 而不是 `DataComponentPredicate` +- `net.minecraft.world.level.block.Block#appendHoverText` 已移除 +- `net.minecraft.world.level.block.entity` + - `BannerPatternLayers` 现在实现 `TooltipProvider` + - `PotDecorations` 现在实现 `TooltipProvider` +- `net.minecraft.world.level.saveddata.maps.MapId` 现在实现 `TooltipProvider` + +### 实体 + +由于 `EntityPredicate` 现在接受一个 `DataComponentExactPredicate` 来匹配实体上的槽位,一些用于实体变体的 `EntitySubPredicate` 已转换为存储在持有的物品上的数据组件。 + +- `net.minecraft.advancements.critereon` + - `EntityPredicate` 现在接受一个 `DataComponentExactPredicate` 来匹配检查的装备槽位 + - `EntitySubPredicate` + - `AXOLTOL` -> `DataComponents#AXOLOTL_VARIANT` + - `FOX` -> `DataComponents#FOX_VARIANT` + - `MOOSHROOM` -> `DataComponents#MOOSHROOM_VARIANT` + - `RABBIT` -> `DataComponents#RABBIT_VARIANT` + - `HORSE` -> `DataComponents#HORSE_VARIANT` + - `LLAMA` -> `DataComponents#LLAMA_VARIANT` + - `VILLAGER` -> `DataComponents#VILLAGER_VARIANT` + - `PARROT` -> `DataComponents#PARROT_VARIANT` + - `SALMON` -> `DataComponents#SALMON_SIZE` + - `TROPICAL_FISH` -> `DataComponents#TROPICAL_FISH_PATTERN`、`TROPICAL_FISH_BASE_COLOR`、`TROPICAL_FISH_PATTERN_COLOR` + - `PAINTING` -> `DataComponents#PAINTING_VARIANT` + - `CAT` -> `DataComponents#CAT_VARIANT`、`CAT_COLLAR` + - `FROG` -> `DataComponents#FROG_VARIANT` + - `WOLF` -> `DataComponents#WOLF_VARIANT`、`WOLF_COLLAR` + - `PIG` -> `DataComponents#PIG_VARIANT` + - 使用变体子谓词的 `register` 已移除 + - `catVariant`、`frogVariant`、`wolfVariant` 已移除 + - `$EntityHolderVariantPredicateType`、`$EntityVariantPredicateType` 已移除 + - `SheepPredicate` 不再接受 `DyeColor` +- `net.minecraft.client.renderer.entity.state.TropicalFishRenderState#variant` -> `pattern` +- `net.minecraft.core.component` + - `DataComponentGetter` - 一个从某个对象获取数据组件的获取器。 + - `DataComponentHolder`、`DataComponentMap` 现在继承 `DataComponentGetter` + - `DataComponentExactPredicate` 现在是一个 `DataComponentGetter` 的谓词 + - `expect` - 一个期望数据组件具有某个值的谓词。 + - `test(DataComponentHolder)` 已移除 + - `DataComponents` + - `SHEEP_COLOR` - 绵羊的染料颜色。 + - `SHULKER_COLOR` - 潜影贝(盒)的染料颜色。 + - `COW_VARIANT` - 奶牛的变体。 + - `CHICKEN_VARIANT` - 鸡的变体。 + - `WOLF_SOUND_VARIANT` - 狼发出的声音。 +- `net.minecraft.world.entity` + - `Entity` 现在实现 `DataComponentGetter` + - `applyImplicitComponents` - 将获取器中的组件应用到实体上。这应由模组制作者覆盖。 + - `applyComponentsFromItemStack` - 将堆栈中的组件应用到实体上。 + - `castComponentValue` - 将对象的类型转换为组件类型。 + - `setComponent` - 将组件数据设置到实体上。 + - `applyImplicitComponent` - 将组件数据应用到实体上。这应由模组制作者覆盖。 + - `applyImplicitComponentIfPresent` - 如果获取器中存在该组件,则应用它。 + - `EntityType#appendCustomNameConfig` -> `appendComponentsConfig` + - `VariantHolder` 接口已移除 + - 因此,相关实体上的所有 `setVariant` 方法都是私有的,而关联的数据也可以从 `DataComponentGetter` 获得 +- `net.minecraft.world.entity.animal` + - `CatVariant#CODEC` + - `Fox$Variant#STREAM_CODEC` + - `FrogVariant#CODEC` + - `MushroomCow$Variant#STREAM_CODEC` + - `Parrot$Variant#STREAM_CODEC` + - `Rabbit$Variant#STREAM_CODEC` + - `Salmon$Variant#STREAM_CODEC` + - `TropicalFish` + - `getVariant` -> `getPattern` + - `$Pattern` 现在实现 `TooltipProvider` + - `Wolf` -> `.wolf.Wolf` + - `WolfVariant` -> `.wolf.WolfVariant`,现在是一个记录,接受一个 `$AssetInfo` 和一个 `SpawnPrioritySelectors` + - `WolfVariants` -> `.wolf.WolfVariants` +- `net.minecraft.world.entity.animal.axolotl.Axolotl$Variant#STREAM_CODEC` +- `net.minecraft.world.entity.animal.horse` + - `Llama$Variant#STREAM_CODEC` + - `Variant#STREAM_CODEC` +- `net.minecraft.world.entity.animal.wolf` + - `WolfSoundVariant` - 狼发出的声音。 + - `WolfSoundVariants` - 所有原版狼声音变体。 +- `net.minecraft.world.entity.decoration.Painting` + - `VARIANT_MAP_CODEC` 已移除 + - `VARIANT_CODEC` 现在是私有的 +- `net.minecraft.world.entity.npc.VillagerDataHolder#getVariant`、`setVariant` 已移除 +- `net.minecraft.world.entity.variant.VariantUtils`- 用于获取实体变体信息的工具。 +- `net.minecraft.world.item` + - `ItemStack#copyFrom` - 从获取器复制组件。 + - `MobBucketItem#VARIANT_FIELD_CODEC` -> `TropicalFish$Pattern#STREAM_CODEC` +- `net.minecraft.world.level.block.entity.BlockEntity#applyImplicitComponents` 现在接受一个 `DataComponentGetter` + - `$DataComponentInput` -> `DataComponentGetter` +- `net.minecraft.world.level.Spawner` 方法现在接受 `CustomData` 而不是 `ItemStack` 本身 + +#### 生成条件 + +为了允许实体在给定条件下随机生成变体,添加了一个名为 `SPAWN_CONDITION_TYPE` 的新注册表。这些接受 `SpawnCondition`:一个选择器,像一个谓词,接受上下文以查看给定的变体是否可以在那里生成。所有变体都被放入一个列表中,然后根据存储在 `SpawnProritySelectors` 中的选定优先级进行排序。优先级较高的将首先被检查,相同优先级的多个将按提供的顺序选择。然后,在相同优先级级别上,所有满足条件的变体将被随机选择。 + +```json5 +// 对于某个存在生成条件的对象 +[ + { + // 正在检查的生成条件 + "condition": { + "type": "minecraft:biome", + // 将检查变体尝试生成的生物群系是否在森林中 + "biomes": "#minecraft:is_forest" + }, + // 将首先检查此条件 + "priority": 1 + }, + { + // 表示条件始终为真 + "priority": 0 + } +] +``` + +- `net.minecraft.core.registries.Registries#SPAWN_CONDITION_TYPE` +- `net.minecraft.world.entity.variant` + - `BiomeCheck` - 一个生成条件,检查实体是否在给定的生物群系之一中。 + - `MoonBrightnessCheck` - 一个生成条件,检查月亮的亮度。 + - `PriorityProvider` - 一个基于某个优先级整数对条件选择器进行排序的接口。 + - `SpawnCondition` - 检查实体是否可以在该位置生成。 + - `SpawnConditions` - 可供选择的生成条件。 + - `SpawnContext` - 一个包含实体生成所在的当前位置、等级和生物群系的对象。 + - `SpawnPrioritySelectors` - 要针对实体检查的生成条件列表。用于在给定位置随机选择一个变体生成。 + - `StructureCheck` - 一个生成条件,检查实体是否在结构内。 + +#### 变体数据包注册表 + +青蛙、猫、奶牛、鸡、猪、狼和狼的声音变体是数据包注册表对象,意味着现在大多数引用需要通过 `RegistryAccess` 或 `HolderLookup$Provider` 实例来引用。 + +对于青蛙、猫或狼: + +```json5 +// 文件位于: +// - `data/examplemod/frog_variant/example_frog.json` +// - `data/examplemod/cat_variant/example_cat.json` +// - `data/examplemod/wolf_variant/example_wolf.json` +{ + // 指向 `assets/examplemod/textures/entity/cat/example_cat.png` 的纹理 + "asset_id": "examplemod:entity/cat/example_cat", + "spawn_conditions": [ + // 此变体生成的条件 + { + "priority": 0 + } + ] +} +``` + +对于猪、奶牛或鸡: +```json5 +// 文件位于: +// - `data/examplemod/pig_variant/example_pig.json` +// - `data/examplemod/cow_variant/example_cow.json` +// - `data/examplemod/chicken_variant/example_chicken.json` +{ + // 指向 `assets/examplemod/textures/entity/pig/example_pig.png` 的纹理 + "asset_id": "examplemod:entity/pig/example_pig", + // 定义用于选择渲染猪变体的实体模型的 `PigVariant$ModelType` + "model": "cold", + "spawn_conditions": [ + // 此变体生成的条件 + { + "priority": 0 + } + ] +} +``` + +对于狼的声音变体: +```json5 +// 文件位于: +// - `data/examplemod/wolf_sound_variant/example_wolf_sound.json`` +{ + // 空闲时随机播放的声音事件的注册表名称 + "ambient_sound": "minecraft:entity.wolf.ambient", + // 死亡时播放的声音事件的注册表名称 + "death_sound": "minecraft:entity.wolf.death", + // 生气时空闲时随机播放的声音事件的注册表名称 + "growl_sound": "minecraft:entity.wolf.growl", + // 受伤时播放的声音事件的注册表名称 + "hurt_sound": "minecraft:entity.wolf.hurt", + // 空闲时在生命值满的情况下,随机播放(1/3 的概率)的声音事件的注册表名称 + "pant_sound": "minecraft:entity.wolf.pant", + // 空闲时在生命值未满的情况下,随机播放(1/3 的概率)的声音事件的注册表名称 + "whine_sound": "minecraft:entity.wolf.whine" +} +``` + +#### 客户端资源 + +客户端文件中的原始 `ResourceLocation`(用于标识符或纹理)正在被定义标识符以及潜在纹理路径的对象所取代。有三个主要对象需要了解:`ClientAsset`、`ModelAndTexture` 和 `MaterialAssetGroup`。 + +`ClientAsset` 是一个 id/纹理对,用于指向纹理位置。默认情况下,纹理路径由 id 构造,路径以 `textures` 为前缀,并以 PNG 扩展名为后缀。 + +`ModelAndTexture` 是一个对象/客户端资源对,当渲染器应在多个模型之间选择时使用。通常,渲染器会创建一个对象类型到模型的映射,并提供给 `ModelAndTexture` 的对象用作映射中的查找键。 + +`MaterialAssetGroup` 是一个处理用某些纹饰材料渲染装备资源的处理器。它接受用于叠加到盔甲上的基础纹理,以及针对给定装备资源的任何覆盖。 + +- `net.minecraft.advancements.DisplayInfo` 现在接受一个 `ClientAsset` 而不是仅一个 `ResourceLocation` 作为背景纹理 +- `net.minecraft.client.model` + - `AdultAndBabyModelPair` - 保存两个 `Model` 实例,表示某个实体的成年和幼年形态。 + - `ChickenModel#createBaseChickenModel` - 创建默认的鸡模型。 + - `ColdChickenModel` - 寒冷温度下鸡的变体模型。 + - `ColdCowModel` - 寒冷温度下奶牛的变体模型。 + - `ColdPigModel` - 寒冷温度下猪的变体模型。 + - `CowModel#createBaseCowModel` - 创建奶牛的基础模型。 + - `PigModel#createBasePigModel` - 创建默认的猪模型。 + - `WarmCowModel` - 温暖温度下奶牛的变体模型。 +- `net.minecraft.client.renderer.entity` + - `ChickenRenderer` 现在继承 `MobRenderer` 而不是 `AgeableMobRenderer` + - `CowRenderer` 现在继承 `MobRenderer` 而不是 `AgeableMobRenderer` + - `PigRenderer` 现在继承 `MobRenderer` 而不是 `AgeableMobRenderer` +- `net.minecraft.client.renderer.entity.layers.SheepWoolUndercoatLayer` - 一个渲染绵羊羊毛底层的层。 +- `net.minecraft.client.renderer.entity.state` + - `CowRenderState` - 奶牛实体的渲染状态。 + - `SheepRenderState` + - `getWoolColor` - 返回绵羊羊毛的整数颜色。 + - `isJebSheep` - 返回绵羊的名称是否包含 `jeb_` 前缀。 +- `net.minecraft.core.ClientAsset` - 一个包含标识符和指向某个纹理的路径的对象。 +- `net.minecraft.data.loot.EntityLootSubProvider#killedByFrogVariant` 现在接受 `FrogVariant` 的 `HolderGetter` +- `net.minecraft.data.tags.CatVariantTagsProvider` 类已移除 +- `net.minecraft.tags.CatVariantTags` 类已移除 +- `net.minecraft.world.entity.animal` + - `AbstractCow` - 一个代表奶牛(牛)的抽象动物。 + - `Chicken#setVariant`、`getVariant` - 处理鸡的变体信息。 + - `ChickenVariant` - 一个定义给定鸡的通用可渲染信息和生物群系生成的类。 + - `ChickenVariants` - 保存所有原版鸡变体的键。 + - `Cow` 现在继承 `AbstractCow`。 + - `CowVariant` - 一个定义给定奶牛的通用可渲染信息和生物群系生成的类。 + - `CowVariants` - 保存所有原版奶牛变体的键。 + - `CatVariant(ResourceLocation)` -> `CatVariant(ClientAsset, SpawnPrioritySelectors)` + - `CatVariants` - 保存所有原版猫变体的键。 + - `FrogVariant` -> `.frog.FrogVariant` + - `FrogVariant(ResourceLocation)` -> `FrogVariant(ClientAsset, SpawnPrioritySelectors)` + - `MushroomCow` 现在继承 `AbstractCow` + - `PigVariant` - 一个定义给定猪的通用可渲染信息和生物群系生成的类。 + - `TemperatureVariants` - 一个接口,保存指示不同温度下实体的 `ResourceLocation`。 +- `net.minecraft.world.entity.variant.ModelAndTexture` - 定义带有其关联纹理的模型。 +- `net.minecraft.world.item.equipment.trim` + - `MaterialAssetGroup` - 一个资源定义了一些基础以及基于所穿戴装备的排列。 + - `TrimMaterial` 现在接受 `MaterialAssetGroup` 而不是原始基础和覆盖 + +## 标签与解析 + +标签已被重写,移除了任何对类型的直接引用,同时密封并最终确定了相关类。从标签获取值现在返回一个 `Optional` 包装的条目,除非您调用 `get*Or` 方法之一,在其中指定默认值。另一方面,对象不接受默认值,而是返回所需标签的空变体。 + +```java +// 对于某个 `CompoundTag` tag + +// 读取一个值 +Optional value1 = tag.getInt("value1"); +int value1Raw = tag.getIntOr("value1", 0); + +// 读取另一个对象 +Optional childTag = tag.getCompound("childTag"); +CompoundTag childTagRaw = tag.getCopmoundOrEmpty("childTag"); +``` + +### 使用编解码器写入 + +`CompoundTag` 现在有使用 `Codec` 或 `MapCodec` 进行写入和读取的方法。对于 `Codec`,它将序列化的数据存储在指定的键内。对于 `MapCodec`,它将字段合并到顶层标签上。 + +```java +// 对于某个 Codec CODEC 和 MapCodec MAP_CODEC +// 我们还有一个 ExampleObject example +CompoundTag tag = new CompoundTag(); + +// 对于编解码器 +tag.store("example_key", CODEC, example); +Optional fromCodec = tag.read("example_key", CODEC); + +// 对于映射编解码器 +tag.store(MAP_CODEC, example); +Optional fromMapCodec = tag.read(MAP_CODEC); +``` + +### 命令解析器 + +packrat 解析器已更新了新的规则和系统,允许命令具有基于解析器的参数。这来自于 `CommandArgumentParser`,它解析某些语法以返回所需的对象。然后解析器被 `ParserBasedArgument` 使用,它尝试解析字符串并根据您当前键入的内容构建任何建议。这些都由 `Grammar` 类处理,它实现了 `CommandArgumentParser`,使用原子、字典、规则和术语的组合构建。 + +- `net.minecraft.commands.ParserUtils#parseJson` +- `net.minecraft.commands.arguments` + - `ComponentArgument` 现在继承 `ParserBasedArgument` + - `NbtTagArgument` 现在继承 `ParserBasedArgument` + - `StyleArgument` 现在继承 `ParserBasedArgument` +- `net.minecraft.commands.arguments.item.ItemPredicateArgument` 现在继承 `ParserBasedArgument` +- `net.minecraft.nbt` + - `ByteArrayTag`,现在是 final,不再接受列表对象 + - `ByteTag` 现在是一个记录 + - `CollectionTag` 现在是一个密封接口,不再继承 `AbstractList` 或具有泛型 + - `set`、`add` 已移除 + - `remove` 现在返回一个 `Tag` + - `get` - 返回指定索引处的标签。 + - `getElementType` 已移除 + - `size` - 返回集合的大小。 + - `isEmpty` - 返回集合是否没有元素。 + - `stream` - 流式传输集合的元素。 + - `CompoundTag` 现在是 final + - `store` - 将编解码器或映射编解码器写入标签。 + - `read` - 从标签中读取编解码器或映射编解码器编码的值。 + - `getFloatOrDefault`、`getIntOrDefault`、`getLongOrDefault` - 获取具有关联键的值,如果不存在或抛出异常则返回默认值。 + - `storeNullable` - 当不为 null 时,使用编解码器将值写入标签。 + - `putUUID`、`getUUID`、`hasUUID` 已移除 + - `getAllKeys` -> `keySet` + - `values`、`forEach` - 实现标准的映射操作。 + - 接受列表对象的 `putByteArray`、`putIntArray` 已移除 + - `getTagType` 已移除 + - `contains` 已移除 + - `get*`、`get*Or` - 返回键的可选包装对象,如果使用 `Or` 方法,则返回指定的默认值。 + - `DoubleTag` 现在是一个记录 + - `EndTag` 现在是一个记录 + - `FloatTag` 现在是一个记录 + - `IntArrayTag`,现在是 final,不再接受列表对象 + - `IntTag` 现在是一个记录 + - `ListTag`,现在是 final,继承 `AbstractList` + - `addAndUnwrap` - 将标签添加到列表中,如果是一个包含单个元素的复合标签,则改为添加内部标签。 + - `get*`、`get*Or` - 返回键的可选包装对象,如果使用 `Or` 方法,则返回指定的默认值。 + - `compoundStream` - 返回列表中所有 `CompoundTag` 的扁平映射。 + - `LongArrayTag`,现在是 final,不再接受列表对象 + - `LongTag` 现在是一个记录 + - `NbtIo#readUnnamedTag` 现在是公开的,用于测试 + - `NbtOps` 现在有一个私有构造函数 + - `NbtUtils` + - `getDataVersion` 现在有一个接受 `Dynamic` 的重载 + - `createUUID`、`loadUUID` 已移除 + - `readBlockPos`、`writeBlockPos` 已移除 + - `NumericTag` 现在是一个实现 `PrimitiveTag` 的密封接口 + - `getAsLong` -> `longValue` + - `getAsInt` -> `intValue` + - `getAsShort` -> `shortValue` + - `getAsByte` -> `byteValue` + - `getAsDouble` -> `doubleValue` + - `getAsFloat` -> `floatValue` + - `getAsNumber` -> `box` + - `as*` - 返回数值的可选包装。 + - `PrimitiveTag` - 一个密封接口,表示标签数据是一个原始对象。 + - `ShortTag` 现在是一个记录 + - `SnbtGrammar` - 用于字符串化 NBT 的解析器创建器。 + - `SnbtOperations` - 一个包含用于解析某些值的内置操作的辅助工具。 + - `StringTag` 现在是一个记录 + - `StringTagVisitor` + - `visit` -> `build`,不是一对一 + - `handleEscape` -> `handleKeyEscape`,现在是私有的 + - `Tag` 现在是一个密封接口 + - `as*` -> 尝试将标签转换为其子类型之一,失败时返回空 optional。 + - `getAsString` -> `asString`,不是一对一 + - `TagParser` 现在持有一个泛型,引用要解析到的中间对象的类型 + - 构造函数现在接受一个语法,或者 `create` 从 `DynamicOps` 构造语法 + - `AS_CODEC` -> `FLATTENED_CODEC` + - `parseTag` -> `parseCompoundFully` 或 `parseCompoundAsArgument` + - 其他方法如 `parseFully`、`parseAsArgument` 解析到某个中间对象 + - 这些都是实例方法 + - `readKey`、`readTypedValue` 已移除 + - `TagType#isValue` 已移除 +- `net.minecraft.util.parsing.packrat` + - `CachedParseState` - 一个缓存已解析位置并在读取时控制的解析状态。 + - `Control#hasCut` - 返回语法的控制流对于读取对象是否有 cut。 + - `DelayedException` - 一个创建要抛出的异常的接口。 + - `Dictionary` + - `put` 现在返回一个 `NamedRule` + - `put(Atom, Term, Rule.RuleAction)` -> `putComplex` + - `get` -> `getOtThrow`,不是一对一 + - `forward` - 获取或写入术语到字典。 + - `namedWithAlias` - 创建一个对命名原子或其别名的新引用。 + - `ErrorCollector$Nop` - 一个什么都不做的错误收集器。 + - `NamedRule` - 一个具有关联名称的规则。 + - `ParseState` 现在是一个接口 + - 缓存逻辑已移至 `CachedParseState` + - `scope` - 返回解析对象内当前正在分析的范围。 + - `parse` 现在接受 `NamedRule` 而不是 `Atom` + - `acquireControl`、`releaseControl` - 处理获取解析期间使用的 `Control`。 + - `silent` - 返回一个不收集任何错误的 `ParseState`。 + - `Rule` + - `parse`、`$RuleAction#run` 现在返回一个可为 null 的值,而不是 optional + - `SimpleRuleAction` 现在实现 `$RuleAction` + - `Scope#pushFrame`、`popFrame`、`splitFrame`、`clearFrameValues`、`mergeFrame` - 处理将解析术语管理到称为帧的部分中。 + - `Term` + - `named` -> `Dictionary#named`,不是一对一 + - `repeated`、`repeatedWithTrailingSeparator`、`repeatedWithoutTrailingSeparator` - 处理类似于 varargs 的重复术语,并将它们放入列表中。 + - `positiveLookahead`、`negativeLookahead` - 处理基于后续内容匹配信息的术语。 + - `fail` - 将术语标记为解析期间失败。 +- `net.minecraft.util.parsing.packrat.commands` + - `CommandArgumentParser` - 将字符串解析为与命令一起使用的参数。 + - `Grammar` 现在接受顶部的 `NamedRule` 而不是 `Atom` + - `GreedyPatternParseRule` - 一个尝试贪婪匹配提供的模式的规则,假设如果某个区域匹配,则可以获得匹配的组。 + - `GreedyPredicateParseRule` - 一个尝试贪婪匹配接受的字符的规则,确保字符串达到最小大小。 + - `NumberRunParseRule` - 一个尝试从字符串解析数字的规则。 + - `ParserBasedArgument` - 一个使用解析器提取值的命令参数。 + - `ResourceLookupRule` 现在接受 id 解析器的 `NamedRule` 而不是 `Atom` + - `StringReaderParserState` 现在继承 `CachedParsedState` + - 不再接受 `Dictoionary` + - `StringReaderTerms#characters` - 匹配字符串中的多个字符,通常用于捕获小写和大写变体。 + - `UnquotedStringParseRule` - 一个将序列的一部分作为未引用字符串读取的规则,确保它达到最小大小。 + +## 保存数据,现在带有类型 + +`SavedData` 已被重写,将其大部分保存和加载逻辑抽象到单独的 `SavedDataType` 中。这意味着 `save` 覆盖以及额外的 `load` 和 `factory` 方法现在都在 `SavedDataType` 本身内部处理。 + +要构造一个 `SavedDataType`,您需要传入四个参数。首先是字符串标识符,用于解析保存您信息的 `.dat` 文件。这必须是一个有效的路径。然后是构造函数,当没有信息存在时,它接受 `SavedData$Context` 来返回您的数据对象的实例。接下来是编解码器,它接受 `SavedData$Context` 并返回一个用于读取和写入您的保存数据的 `Codec`。最后是用于数据修复器的 `DataFixTypes`。由于这是一个静态枚举,如果您计划使用原版数据修复器,您将需要注入到枚举本身中,或者修补 `DimensionDataStorage#readTagFromDisk` 中的 `update` 调用以传入 null 值。 + +```java +// 我们的保存数据实例 +public class ExampleSavedData extends SavedData { + + // 保存数据类型 + public static final SavedDataType TYPE = new SavedDataType<>( + // 最好用您的模组 id 后跟下划线作为标识符的前缀 + // 斜杠会抛出错误,因为文件夹不存在 + // 将解析为 `saves//data/examplemod_example.dat` + "examplemod_example", + // 新实例的构造函数 + ExampleSavedData::new, + // 用于编码和解码数据的编解码器工厂 + ctx -> RecordCodecBuilder.create(instance -> instance.group( + RecordCodecBuilder.point(ctx.levelOrThrow()), + Codec.INT.fieldOf("value1").forGetter(data -> data.value1), + Codec.BOOL.fieldOf("value2").forGetter(data -> data.value2) + ).apply(instance, ExampleSavedData::new)); + ); + + private final ServerLevel level; + private final int value1; + private final boolean value2; + + + // 用于新实例 + private ExampleSavedData(ServerLevel.Context ctx) { + this(ctx.levelOrThrow(), 0, false); + } + + // 用于编解码器 + // 如果不使用 `DimensionDataStorage#set`,构造函数不需要是公开的 + private ExampleSavedData(ServerLevel level, int value1, boolean value2) { + this.level = level; + this.value1 = value1; + this.value2 = value2; + } + + // 其他方法在此处 +} + +// 在可以访问 DimensionDataStorage storage 的地方 +ExampleSavedData data = storage.computeIfAbsent(ExampleSavedData.TYPE); +``` + +- `net.minecraft.server.ServerScoreboard` + - `dataFactory` 已移除 + - `createData` 现在接受一个 `$Packed` 实例 +- `net.minecraft.world.RandomSequences` + - `factory`、`load` 已移除 + - `codec` - 根据当前世界种子为随机序列构造一个编解码器。 +- `net.minecraft.world.entity.raid.Raids` 不再接受任何参数 + - `getType` - 根据当前维度返回保存数据类型。 + - `factory` 已移除 + - `tick` 现在接受 `ServerLevel` + - `getId` - 获取突袭实例的标识符。 + - `canJoinRaid` 不再接受突袭实例 + - `load` 不再接受 `ServerLevel` +- `net.minecraft.world.level.levelgen.structure.structures.StructureFeatureIndexSavedData` + - `factory`、`load` 已移除 + - `type` - 返回具有指定 id 的要素保存数据类型。 +- `net.minecraft.world.level.saveddata` + - `SavedData` + - `save` 已移除 + - `$Factory` 记录已移除 + - `$Context` - 保存保存数据正在写入的当前上下文。 + - `SavedDataType` - 一个表示保存数据类型的记录,包括关于如何构造、保存和加载数据的信息。 +- `net.minecraft.world.level.saveddata.maps` + - `MapIndex` 现在有一个接受最后一个地图 id 的构造函数 + - `factory`、`load` 已移除 + - `getFreeAuxValueForMap` -> `getNextMapId` + - `MapItemSavedData` + - `factory`、`load` 已移除 + - `type` - 使用地图 id 的键返回保存数据类型。 +- `net.minecraft.world.level.storage.DimensionDataStorage` 现在接受一个 `SavedData$Context` + - `computeIfAbsent`、`get` 现在只接受 `SavedDataType` + - `set` 现在接受 `SavedDataType` 以及数据实例 +- `net.minecraft.world.scores.ScoreboardSaveData` + - `load` -> `loadFrom` + - `pack` - 将数据打包到其保存数据格式中。 + - `$Packed` - 表示可序列化的打包数据。 + +## 渲染管线重做 + +无论您之前使用的是着色器还是 `RenderType`,将对象渲染到屏幕的方式都已完全或部分重做。因此,很多事情需要重新解释,下面将进行更深入的探讨。然而,对于不关心细节的人,这里是 TL;DR。 + +首先,着色器 JSON 不再存在。这被 `RenderPipeline` 取代,它实际上是一个代码中的替代品。其次,`RenderPipeline` 强制将大多数任意值转换为对象。例如,您不再存储混合函数模式 id,而是存储一个 `BlendFunction` 对象。同样,您不再存储或设置直接的纹理对象,而是通过 `GpuTexture` 进行管理。最后,`VertexBuffer` 可以通过直接传入 `RenderPipeline` 并更新消费者中任何必要的统一变量来绘制到帧缓冲区,或者通过传入 `RenderType` 来绘制。 + +现在,对于需要细节的人,让我们深入探讨。 + +### 抽象化 Open GL + +众所周知,Minecraft 一直在抽象化其 OpenGL 调用和常量,此版本也不例外。除 `BufferUsage` 外,所有对 GL 代码的调用都已移出对象引用,通常通过调用 `GlConst$toGl` 来获得。然而,随着所有其他渲染重做,有许多变化和复杂性需要学习一个全新的系统,假设您不使用 `RenderType`。 + +从头开始,对底层渲染系统的所有调用都通过 `GpuDevice`,这是一个像 OpenGL 或 Vulkan 这样的渲染库的通用实现的接口。该设备负责创建缓冲区和纹理,执行任何所需的命令。可以通过 `RenderSystem` 通过 `getDevice` 获取当前的 `GpuDevice`,如下所示: + +```java +GpuDevice device = RenderSystem.getDevice(); +``` + +然后,`GpuDevice` 可以分别使用 `createBuffer` 和 `createTexture` 创建带有所需数据的缓冲区或包含要渲染信息的纹理。为冗余起见,缓冲区保存顶点数据,而纹理保存纹理(颜色和深度)数据。您通常应缓存缓冲区或纹理对象以供以后使用,并根据需要更新任何附加数据。作为参考,缓冲区通常通过使用 `BufferBuilder` 和 `ByteBufferBuilder` 首先构建 `MeshData`,然后再将其传递给 `createBuffer` 来创建。 + +设置好所需的缓冲区和纹理后,我们如何实际修改将它们渲染到屏幕?这由 `CommandEncoder` 处理,它也可以通过 `GpuDevice#createCommandEncoder` 从设备获得。编码器包含熟悉的读写方法,以及一些额外的将纹理清除为给定颜色或直接将纹理 blit 到屏幕(`presentTexture`)的方法。然而,这里最重要的方法是 `createRenderPass`。它接受要绘制到屏幕的 `GpuTexture` 以及背景的默认 ARGB 颜色。此外,它还可以接受深度纹理。这应该使用 try-with-resources 块来创建,如下所示: + +```java +// 我们假设您已经构造了一个用于颜色数据的 `GpuTexture` texture +try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(this.texture, OptionalInt.of(0xFFFFFFFF))) { + // 在此处设置内容 + +} +``` + +在 `RenderPass` 内部,您可以设置要使用的 `RenderPipeline`,它定义了关联的着色器,绑定来自其他目标的任何采样器或设置统一变量,剪切屏幕的一部分以进行渲染,以及设置用于定义要渲染的顶点的顶点和索引缓冲区。最后,可以使用 `draw` 方法之一将所有内容绘制到屏幕上,提供起始索引和顶点计数。 + +```java +// 如果缓冲区/纹理尚未创建或缓存,请在此处创建它们 +// 在渲染通道打开时,不能运行来自 `CommandEncoder` 的任何方法 +RenderSystem.AutoStorageIndexBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); +GpuBuffer vertexBuffer = RenderSystem.getQuadVertexBuffer(); +GpuBuffer indexBuffer = indices.getBuffer(6); + +// 我们假设您已经构造了一个用于颜色数据的 `GpuTexture` texture +try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(this.texture, OptionalInt.of(0xFFFFFFFF))) { + + // 设置管道信息以及任何采样器和统一变量 + pass.setPipeline(EXAMPLE_PIPELINE); + pass.setVertexBuffer(0, vertexBuffer); + pass.setIndexBuffer(indexBuffer, indices.type()); + pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); + + // 然后,将所有内容绘制到屏幕 + // 在此示例中,缓冲区只包含一个四边形 + // 对于那些不知道的人来说,顶点计数为 6,因为四边形由 2 个三角形组成,所以有 2 个顶点重叠 + pass.drawIndexed(0, 6); +} +``` + +然而,除非您需要如此精细的控制,否则建议在必要时使用带有 `MultiBufferSource` 的 `RenderType`,因为它会为您设置大部分内容。 + +### 对象引用 + +大多数用于确定模式和处理纹理的 GL 代码的原始引用已被对象取代。正如 TL;DR 之前提到的,这些通常存储为某种枚举或对象,然后可以解析为其 GL 对应项。一些对象直接包含其引用标识符,例如 `BlendFunction`。其他对象只是占位符,在适当的位置解析,例如 `DepthTestFunction`,其枚举值通过 `RenderPipeline#toGl` 转换。 + +然而,最大的变化是增加了 `GpuTexture`。它负责管理与创建、写入和释放写入到某个缓冲区的纹理相关的任何事情。在初始化时,纹理被创建和绑定,并设置任何必要的参数用于 mipmap 和纹理格式。这些 `GpuTexture` 被存储和引用在任何地方,从 `RenderTarget` 的深度和颜色目标到支持 `TextureAtlas` 的纹理。然后,一旦不再需要,纹理通过调用 `#close` 释放。请注意,尽管技术上可以再次调用 `#bind`,但纹理已被视为删除,不应再使用。 + +如果由于某种原因,您需要使用 `GpuTexture`,实际上使用起来非常简单。首先,您只需通过 `GpuDevice#createTexture` 构造 `GpuTexture`。然后,如果您需要更改任何寻址或纹理 mipmap 过滤器,您可以在写入之前随时应用它们。 + +```java +public class MyTextureManager { + + private final GpuTexture texture; + + public MyTextureManager() { + this.texture = RenderSystem.getDevice().createTexture( + // 纹理名称,用于日志记录和调试 + "Example Texture", + // 纹理像素的格式,可以是三个值之一: + // 值: (纹理内部格式, 纹素数据格式, 纹素数据类型, 像素大小) + // - RGBA8 (GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, 4) + // - RED8 (GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1) + // - DEPTH32 (GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_FLOAT, 4) + TextureFormat.RGBA8, + // 纹理宽度 + 16, + // 纹理高度 + 16, + // mipmap 级别和最大细节级别(最小为 1) + 1 + ); + + // 设置 UV 分量的纹理模式 + // 值: + // - REPEAT (GL_REPEAT) + // - CLAMP_TO_EDGE (GL_CLAMP_TO_EDGE) + this.texture.setAddressMode( + // 用于 U 分量的模式 (GL_TEXTURE_WRAP_S) + AddressMode.CLAMP_TO_EDGE, + // 用于 V 分量的模式 (GL_TEXTURE_WRAP_R) + AddressMode.REPEAT + ); + + // 设置用于在屏幕上缩放纹理的过滤函数 + // 值 (默认, 用于 mipmap): + // - NEAREST (GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR) + // - LINEAR (GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR) + this.texture.setTextureFilter( + // 用于纹理缩小函数的模式 (GL_TEXTURE_MIN_FILTER) + FilterMode.LINEAR, + // 用于纹理放大函数的模式 (GL_TEXTURE_MAG_FILTER) + FilterMode.NEAREST, + // 是否应为缩小函数使用 mipmap(当为 true 时,应具有高于 1 的 mipmap 级别) + false + ); + } +} +``` + +然后,每当您想要将某些内容上传到纹理时,您可以调用 `CommandEncoder#writeToTexture` 或 `CommandEncoder#copyTextureToTexture`。这要么接受要写入的 `NativeImage`,要么接受带有纹理数据和要使用的 `NativeImage$Format` 的 `IntBuffer`。 + +```java +// 像其他缓冲区/纹理修改方法一样,这必须在渲染通道之外完成 +// 我们假设您有一些要加载到纹理中的 `NativeImage` image +RenderSystem.getDevice().createCommandEncoder().writeToTexture( + // 正在写入的纹理(目标) + this.texture, + // 正在读取的图像(源) + image, + // mipmap 级别 + 0, + // 起始目标 x 偏移 + 0, + // 起始目标 y 偏移 + 0, + // 目标宽度(x 大小) + 16, + // 目标高度(y 大小) + 16, + // 起始源 x 偏移 + 0, + // 起始源 y 偏移 + 0 +) +``` + +最后,当您使用完纹理后,如果它没有被自动处理,不要忘记通过 `#close` 释放它。 + +### 渲染管线 + +以前,管道是使用 JSON 构造的,其中包含从顶点和片段着色器到其定义的值、采样器和统一变量的所有元数据。然而,这已被一个代码内解决方案取代,该方案将 JSON 的某些部分和原本归入 `RenderType` 的部分更加本地化。这被称为 `RenderPipeline`。 + +可以使用其构建器通过 `RenderPipeline#builder` 构造 `RenderPipeline`。然后可以通过调用 `build` 来构建管道。如果您希望着色器无需任何额外工作即可预编译,则可以将最终的管道传递给 `RenderPipeline#register`。但是,如果您希望更优雅地处理失败状态,也可以自己处理编译。如果您有跨多个管道使用的代码片段,则可以构建一个部分管道(通过 `$Builder#buildSnippet`),然后在 `builder` 方法中将其传递给构造管道。 + +> 示例中描述的以下枚举已提供其 GL 代码,因为它们已被抽象化。 + +```java +// 这假设 RenderPipeline#register 已通过某种形式变为公开 +public static final RenderPipeline EXAMPLE_PIPELINE = RenderPipelines.register( + RenderPipeline.builder() + // 管道的名称(必需) + .withLocation(ResourceLocation.fromNamespaceAndPath("examplemod", "pipeline/example")) + // 顶点着色器的位置,相对于 'shaders'(必需) + // 指向 'assets/examplemod/shaders/example.vsh' + .withVertexShader(ResourceLocation.fromNamespaceAndPath("examplemod", "example")) + // 片段着色器的位置,相对于 'shaders'(必需) + // 指向 'assets/examplemod/shaders/example.fsh' + .withFragmentShader(ResourceLocation.fromNamespaceAndPath("examplemod", "example")) + // 着色器内顶点的格式(必需) + .withVertexFormat( + // 顶点格式 + DefaultVertexFormat.POSITION_TEX_COLOR, + // 格式的模式 + VertexFormat.Mode.QUADS + ) + // 添加可以在着色器中引用的常量 + // 可以指定名称以及一个 int / float 来表示其值 + // 如果未指定值,则应使用 #ifdef / #endif 块进行门控 + .withShaderDefines("ALPHA_CUTOUT", 0.5) + // 添加可以在着色器中引用的纹理 sampler2D + // 通常,存储在 `RenderSystem` 中的着色器纹理通过 `Sampler0` - `Sampler11` 引用 + // - `Sampler0` 通常总是存在,但这些应该事先设置好 + // 此外,对于渲染目标,`InSampler` 通常存在,以及在后处理通道中定义的任何采样器 + .withSampler("Sampler0") + // 添加可以在着色器中引用的统一变量 + // 这些只是定义,然后根据情况由调用者填充或默认填充 + // 默认值可以在 `CompiledShaderProgram#setupUniforms` 中找到 + .withUniform("ModelOffset", UniformType.VEC3) + // 自定义统一变量必须手动设置,因为原版批处理系统不支持这样的操作 + .withUniform("CustomUniform", UniformType.INT) + // 设置用于在离相机不同距离处渲染对象的深度测试函数 + // 值: + // - NO_DEPTH_TEST (GL_ALWAYS) + // - EQUAL_DEPTH_TEST (GL_EQUAL) + // - LEQUAL_DEPTH_TEST (GL_LEQUAL) + // - LESS_DEPTH_TEST (GL_LESS) + // - GREATER_DEPTH_TEST (GL_GREATER) + .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) + // 设置多边形应如何渲染 + // 值: + // - FILL (GL_FILL) + // - WIREFRAME (GL_LINE) + .withPolygonMode(PolygonMode.FILL) + // 当为 true 时,可以剔除正面或背面的多边形 + .withCull(false) + // 指定将两个带有 alpha 的颜色混合在一起时要使用的函数 + // 由 `GlStateManager$SourceFactor` 和 `GlStateManager$DestFactor` 组成 + // 前两个用于 RGB,后两个用于 alpha + // 如果未指定任何内容,则禁用混合 + .withBlend(BlendFunction.TRANSLUCENT) + // 决定是否屏蔽写入颜色和 alpha 到绘制缓冲区 + .withColorWrite( + // 屏蔽 RGB + false, + // 屏蔽 alpha + false + ) + // 决定是否屏蔽写入值到深度缓冲区 + .withDepthWrite(false) + // 决定将 RGBA 颜色应用到帧缓冲区时要应用的逻辑操作 + .withColorLogic(LogicOp.NONE) + // 设置用于计算多边形深度值的比例和单位。 + // 这取代了多边形偏移。 + .withDepthBias(0f, 0f) + .build() +); +``` + +从那里,管道可以直接使用,也可以通过某些 `RenderType` 使用: + +```java +// 这将假设 RenderType#create 是公开的 +public static final RenderType EXAMPLE_RENDER_TYPE = RenderType.create( + // 渲染类型的名称 + "examplemod:example", + // 缓冲区的大小 + // 或 4MB + 4194304, + // 是否影响应用于方块实体的破碎效果 + false, + // 顶点在上传前是否应排序 + true, + // 要使用的管道 + EXAMPLE_PIPIELINE, + // 要应用的任何其他复合状态设置 + RenderType.CompositeState.builder().createCompositeState(RenderType.OutlineProperty.NONE) +); +``` + +然后可以通过创建 `RenderPass` 并将 `RenderPipeline` 设置为使用您的管道来绘制管道。对于 `RenderType`,可以使用 `MultiBufferSource#getBuffer` 获得关联的缓冲区。请注意,不应在 `RenderType` 中使用自定义统一变量,因为它们不容易设置。 + +```java +// 由于我们使用的是自定义统一变量,我们必须自己处理 +// 我们假设我们有某个 `GpuTexture` texture 要写入 + +// 创建要使用的渲染通道 +try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( + // 要写入的 GPU 颜色纹理 + this.texture, + // ARGB 格式的清除颜色 + OptionalInt.of(0xFFFFFFFF), + // 深度纹理和清除深度值也可以在此处构造 + ) +) { + // 添加管道和我们的统一变量 + pass.setPipeline(EXAMPLE_PIPELINE); + pass.setUniform("CustomUniform", 1); + + // 设置任何其他采样器和要使用的顶点/索引缓冲区 + + // 最后,调用 draw 函数之一 + // 接受要绘制的顶点的第一个索引和索引计数 + pass.draw(...); +} +``` + +### 后处理效果 + +鉴于管道 JSON 已被剥离,这也影响了后处理效果。`program` 被直接指定 `vertex_shader` 和 `fragment_shader` 所取代。此外,统一变量必须指定其 `type`。 + +```json5 +// 1.21.5 之前(对于 'passes' 中的某个 pass) +{ + // 与之前相同 + "inputs": [ /*...*/ ], + "output": "swap", + + // 被 'vertex_shader', 'fragment_shader' 取代 + "program": "minecraft:post/box_blur", + + "uniforms": [ + { + "name": "BlurDir", + // 必需 + "values": [ 1.0, 0.0 ] + }, + { + "name": "Radius", + // 必需 + "values": [ 0.0 ] + } + ] +} + +// 1.21.5(对于 'passes' 中的某个 pass) +{ + // 与之前相同 + "inputs": [ /*...*/ ], + "output": "swap", + + // 相对于 'shaders' + // 指向 'assets/minecraft/shaders/post/blur.vsh' + "vertex_shader": "minecraft:post/blur", + // 指向 'assets/minecraft/shaders/post/box_blur.fsh' + "fragment_shader": "minecraft:post/box_blur", + + + "uniforms": [ + { + "name": "BlurDir", + // 指定为此统一变量使用的类型 + // `Uniform$Type` 之一: + // - int + // - ivec3 + // - float + // - vec2 + // - vec3 + // - vec4 + // - matrix4x4 + "type": "vec2", + "values": [ 1.0, 0.0 ] + }, + { + "name": "Radius", + "type": "float" + // values 不再是必需的 + } + ] +} +``` + +请注意,如果您没有为统一变量定义值,则必须在处理 `PostChain` 之前在 `PostChain#process` 的 `RenderPass` 消费者中通过调用 `#setUniform` 来指定它们。 + +```java +// 假设我们已经得到了 `PostChain` post +post.process(Minecraft.getInstance().getMainRenderTarget(), GraphicsResourceAllocator.UNPOOLED, pass -> { + pass.setUniform("Radius", 0.4f); +}); +``` + +- `com.mojang.blaze3d.GpuOutOfMemoryException` - 当无法在 GPU 上分配纹理时抛出的异常。 +- `com.mojang.blaze3d.buffers` + - `BufferType` 不再存储 GL 代码,现在在 `GlConst#toGl` 中 + - `BufferUsage` 不再存储 GL 代码,现在在 `GlConst#toGl` 中 + - `isReadable`、`isWritable` - 返回缓冲区是否可读或可写。 + - `GpuBuffer` 现在是抽象的 + - 带有 `ByteBuffer` 的构造函数已移除 + - `size` - 返回缓冲区的大小。 + - `type` - 返回缓冲区的类型。 + - `resize`、`write`、`read`、`bind` 已移除 + - `usage` - 返回缓冲区的用途。 + - `close` 现在是抽象的 + - `isClosed` - 返回缓冲区是否已关闭。 + - `$ReadView` 现在是一个接口,定义了缓冲区数据以及如何关闭视图 +- `com.mojang.blaze3d.font.SheetGlyphInfo#upload` 现在接受一个 `GpuTexture` +- `com.mojang.blaze3d.opengl` + - `DirectStateAccess` - 一个创建数据并将其绑定到某个帧缓冲区的接口。 + - `$Core` - 一种修改帧缓冲区而不将其绑定到上下文的 DSA 实现。 + - `$Emulated` - 一种仍然绑定上下文的 DSA 抽象。 + - `GlBuffer` - 用于 OpenGL 的 `GpuBuffer` 实现。 + - `GlCommandEncoder` - 用于 OpenGL 的 `CommandEncoder` 实现。 + - `GlDebugLabel` - 用于处理对 GL 指定数据结构的调试引用的标签器。 + - `GlDevice` - 用于 OpenGL 的 `GpuDevice` 实现。 + - `GlRenderPass` - 用于 OpenGL 的 `RenderPass` 实现。 + - `GlRenderPipeline` - 用于 OpenGL 的 `CompiledRenderPipeline` 实现。 + - `GlTexture` - 用于 OpenGL 的 `GpuTexture` 实现。 + - `VertexArrayCache` - 用于绑定顶点数组并将其上传到 OpenGL 管道的缓存。 +- `com.mojang.blaze3d.pipeline` + - `BlendFunction` - 一个类,包含在目标中叠加像素时要应用的源和目标颜色及 alpha。这也包含所有原版混合函数。 + - `CompiledRenderPipeline` - 一个接口,包含具有渲染到屏幕所需所有信息的管道。 + - `RenderPipeline` - 一个类,包含将某个对象渲染到屏幕所需的一切。它在应用之前类似于渲染状态。 + - `RenderTarget` 现在接受一个表示目标名称的字符串 + - `colorTextureId` -> `colorTexture`,现在是一个 `GpuTexture` + - 同样 `getColorTextureId` -> `getColorTexture` + - `depthBufferId` -> `depthTexture`,现在是一个 `GpuTexture` + - 同样 `getDepthTextureId` -> `getDepthTexture` + - `filterMode` 现在是一个 `FilterMode` + - 同样 `setFilterMode` 用于 int 参数 + - `blitAndBlendToScreen` 不再接受视口大小参数 + - `framebufferId` 已移除 + - `checkStatus` 已移除 + - `bindWrite`、`unbindWrite`、`setClearColor` 已移除 + - `blitToScreen` 不再接受任何参数 + - `blitAndBlendToScreen` -> `blitAndBlendToTexture`,不是一对一 + - `clear` 已移除 + - `unbindRead` 已移除 +- `com.mojang.blaze3d.platform` + - `DepthTestFunction` - 一个枚举,表示在将样本渲染到帧缓冲区时要应用的受支持深度测试。 + - `DisplayData` 现在是一个记录 + - `withSize` - 创建一个具有指定宽度/高度的新实例。 + - `withFullscreen` - 创建一个具有指定全屏标志的新实例。 + - `FramerateLimitTracker` + - `getThrottleReason` - 返回游戏帧率被限制的原因。 + - `isHeavilyThrottled` - 返回当前限制是否显著影响游戏速度。 + - `$FramerateThrottleReason` - 帧率被限制的原因。 + - `GlConst` -> `com.mojang.blaze3d.opengl.GlConst` + - `#toGl` - 将某个引用对象映射到其关联的 OpenGL 代码。 + - `GlDebug` -> `com.mojang.blaze3d.opengl.GlDebug` + - `enableDebugCallback` 现在接受一组启用的扩展。 + - `GlStateManager` -> `com.mojang.blaze3d.opengl.GlStateManager` + - `_blendFunc`、`_blendEquation` 已移除 + - `_glUniform2(int, IntBuffer)`、`_glUniform4(int, IntBuffer)` 已移除 + - `_glUniformMatrix2(int, boolean, FloatBuffer)`、`_glUniformMatrix3(int, boolean, FloatBuffer)` 已移除 + - `_glUniformMatrix4(int, boolean, FloatBuffer)` -> `_glUniformMatrix4(int, FloatBuffer)`,transpose 现在始终为 false + - `_glGetAttribLocation` 已移除 + - `_glMapBuffer` 已移除 + - `_glCopyTexSubImage2D` 已移除 + - `_glBindRenderbuffer`、`_glDeleteRenderbuffers` 已移除 + - `glGenRenderbuffers`、`_glRenderbufferStorage`、`_glFramebufferRenderbuffer` 已移除 + - `_texParameter(int, int, float)` 已移除 + - `_genTextures`、`_deleteTextures` 已移除 + - `_texSubImage2D` 现在有一个接受 `IntBuffer` 而不是 `long` 作为像素数据的重载 + - `upload` 已移除 + - `_stencilFunc`、`_stencilMask`、`_stencilOp`、`_clearStencil` 已移除 + - `_getTexImage` 已移除 + - `_glDrawPixels`、`_readPixels` 已移除 + - `$CullState#mode` 已移除 + - `$DestFactor` -> `DestFactor`,代码已移除,通过 `GlConst#toGl` 调用 + - `$FramebufferState` 枚举已移除 + - `$LogicOp` -> `LogicOp`,代码已移除,通过 `GlConst#toGl` 调用 + - 除 `OR_REVERSE` 外,所有都已移除 + - `NONE` - 不执行逻辑操作。 + - `$PolygonOffsetState#line` 已移除 + - `$SourceFactor` -> `SourceFactor`,代码已移除,通过 `GlConst#toGl` 调用 + - `$StencilFunc`、`$StencilState` 类已移除 + - `$Viewport` 枚举已移除 + - `GlUtil` 类已移除 + - `getVendor`、`getRenderer`、`getOpenGlVersion`(现在为 `getVersion`)已移至 `GpuDevice` 上的实例抽象方法 + - `getCpuInfo` -> `GLX#_getCpuInfo` + - `GLX` + - `getOpenGLVersionString` 已移除 + - `_init` -> `_getCpuInfo`,不是一对一 + - `_renderCrosshair`、`com.mojang.blaze3d.systems.RenderSystem#renderCrosshair` -> `net.minecraft.client.gui.components.DebugScreenOverlay#render3dCrosshair`,不是一对一 + - `PolygonMode` - 一个枚举,定义多边形在缓冲区中将如何渲染。 + - `NativeImage` 构造函数现在是公开的 + - `upload` 已移除 + - `getPointer` - 返回指向图像数据的指针。 + - `setPixelABGR` 现在是公开的 + - `applyToAllPixels` 已移除 + - `downloadTexture`、`downloadDepthBuffer` 已移除 + - `flipY` 已移除 + - `setPackPixelStoreState`、`setUnpackPixelStoreState` 已移除 + - `$InternalGlFormat` 枚举已移除 + - `$Format` 不再包含 GL 代码,现在在 `GlConst#toGl` 中 + - `TextureUtil` + - `generateTextureId`、`releaseTextureId` 已移除 + - `prepareImage` 已移除 + - `writeAsPNG` 现在接受一个 `GpuTexture` 而不是直接的三个整数 + - 没有 `IntUnaryOperator` 的重载已移除 +- `com.mojang.blaze3d.resource` + - `RenderTargetDescriptor` 现在接受一个表示要清除到的颜色的整数 + - `ResourceDescriptor` + - `prepare` - 在分配后准备资源以供使用。 + - `canUsePhysicalResource` - 通常返回一个描述符是否已经分配了相同的信息。 +- `com.mojang.blaze3d.shaders` + - `AbstractUniform` -> `com.mojang.blaze3d.opengl.AbstractUniform` + - `setSafe` 方法已移除 + - `setMat*` 方法已移除 + - `set(Matrix3f)` 已移除 + - `CompiledShader` -> `com.mojang.blaze3d.opengl.GlShaderModule` + - `$Type` -> `com.mojang.blaze3d.shaders.ShaderType` + - `Uniform` -> `com.mojang.blaze3d.opengl.Uniform` + - 构造函数现在接受一个 `$Type` 而不是计数和一个表示类型的整数 + - `UT_*` 字段已移除 + - `setFromConfig(ShaderProgramConfig.Uniform)` 已移除 + - `getTypeFromString` 已移除 + - `getType` 现在返回一个 `$Type` + - `set(int, float)` 已移除 + - `setSafe` 现在是私有的 + - `$Type` - 保存类型名称以及它包含多少个值。 + - `getLocation` 已移除 + - `getCount` -> `$Type#count` + - `getIntBuffer`、`getFloatBuffer` 已移除 + - `$Type` -> `com.mojang.blaze3d.shaders.UniformType` +- `com.mojang.blaze3d.systems` + - `CommandEncoder` - 一个接口,定义如何将各种命令编码到底层渲染系统,例如创建通道、清除和写入纹理,或从缓冲区读取。 + - `GpuDevice` - 一个接口,定义用于绘制到屏幕的设备或底层渲染系统。它负责创建缓冲区和纹理,同时编译任何管道。 + - `RenderPass` - 一个接口,定义如何使用底层渲染系统将给定的通道渲染到某个缓冲区。这允许绑定任何采样器并设置所需的统一变量。 + - `RenderSystem` + - `isOnRenderThreadOrInit`、`assertOnRenderThreadOrInit` 已移除 + - `recordRenderCall`、`replayQueue` 已移除 + - `blendFunc`、`blendFuncSeparate`、`blendEquation` 已移除 + - `texParameter`、`deleteTexture`、`bindTextureForSetup` 已移除 + - `stencilFunc`、`stencilMask`、`stencilOp` 已移除 + - `clearDepth` 已移除 + - `glBindBuffer`、`glBindVertexArray`、`glBufferData`、`glDeleteBuffers` 已移除 + - `glUniform1i` 已移除 + - `glUniform1`、`glUniform2`、`glUniform3`、`glUniform4` 已移除 + - `glUniformMatrix2`、`glUniformMatrix3`、`glUniformMatrix4` 已移除 + - `setupOverlayColor` 现在接受一个 `GpuTexture` 而不是两个 int + - `beginInitialization`、`finishInitialization` 已移除 + - `renderThreadTesselator` 已移除 + - `setShader`、`clearShader`、`getShader` 已移除 + - `setShaderTexture` 现在接受一个 `GpuTexture` 而不是一个绑定地址 + - `getShaderTexture` 现在返回一个 `GpuTexture`,如果不存在则返回 null + - `pixelStore`、`readPixels` 已移除 + - `queueFencedTask`、`executePendingTasks` - 处理发送在 GPU 上异步运行的任务。 + - `SCISSOR_STATE` - 保存主要的剪裁状态。 + - `disableDepthTest`、`enableDepthTest` 已移除 + - `depthFunc`、`depthMask` 已移除 + - `enableBlend`、`disableBlend` 已移除 + - `neableCull`、`disableCull` 已移除 + - `polygonMode`、`enablePolygonOffset`、`disablePolygonOffset`、`polygonOffset` 已移除 + - `enableColorLogicOp`、`disableColorLogicOp`、`logicOp` 已移除 + - `bindTexture`、`viewport` 已移除 + - `colorMask`、`clearColor`、`clear` 已移除 + - `setupShaderLights(CompiledShaderProgram)` 已移除 + - `getShaderLights` - 返回表示块光和天空光的向量。 + - `drawElements`、`getString` 已移除 + - `initRenderer` 现在接受窗口指针、默认着色器源以及一个是否使用调试标签的布尔值 + - `setupDefaultState` 不再接受任何参数 + - `maxSupportTextureSize` 已移除 + - `glDeleteVertexArrays` 已移除 + - `defaultBlendFunc` 已移除 + - `setShaderTexture` 已移除 + - `getQuadVertexBuffer` - 返回一个绑定了一个四边形的顶点缓冲区。 + - `getDevice`、`tryGetDevice` - 返回表示要使用的底层渲染系统的 `GpuDevice`。 + - `getCapsString` 已移除 + - `activeTexture` 已移除 + - `setModelOffset`、`resetModelOffset`、`getModelOffset` - 处理在渲染模型时应用于 `ModelOffset` 统一变量的偏移量。通常用于云和世界边界。 + - `$AutoStorageIndexBuffer#bind` -> `getBuffer`,不是一对一 + - `$GpuAsyncTask` - 一个保存回调和围栏对象的记录,用于将信息同步到 GPU。 + - `ScissorState` - 一个类,保存要渲染的屏幕部分。 +- `com.mojang.blaze3d.textures` + - `AddressMode` - 设置为如何将纹理渲染到特定位置的模式。 + - `FilterMode` - 设置为当细节级别函数确定纹理应如何最大化或最小化时如何渲染纹理的模式。 + - `GpuTexture` - 根据需要绑定和写入 GPU 的纹理。 + - `TextureFormat` - 指定纹理应分配的格式。 +- `com.mojang.blaze3d.vertex` + - `PoseStack` + - `mulPose(Quaternionf)`、`rotateAround` 现在接受 `Quaternionfc` 而不是 `Quaternionf` + - `clear` -> `isEmpty` + - `mulPose(Matrix4f)` -> `mulPose(Matrix4fc)` + - `$Pose` + - `computeNormalMatrix` 现在是私有的 + - `transformNormal` 现在接受 `Vector3fc` 作为其第一个参数 + - `translate`、`scale`、`rotate`、`rotateAround`、`setIdentity`、`mulPose` 现在除了在堆栈上之外,在姿势本身上也可用 + - `VertexBuffer` -> `com.mojang.blaze3d.buffers.GpuBuffer`,不是一对一 + - 一些逻辑也移至 `VertexFormat` + - `VertexFormat` + - `bindAttributes` 已移除 + - `setupBufferState`、`clearBufferState`、`getImmediateDrawVertexBuffer` -> `uploadImmediateVertexBuffer`、`uploadImmediateIndexBuffer`;不是一对一 + - `$IndexType` 不再存储 GL 代码,现在在 `GlConst#toGl` 中 + - `$Mode` 不再存储 GL 代码,现在在 `GlConst#toGl` 中 + - `VertexFormatElement` + - `setupBufferState` 已移除 + - `$Type` 不再存储 GL 代码,现在在 `GlConst#toGl` 中 + - `$Usage` 不再存储 GL 函数调用,现在在 `VertexArrayCache#setupCombinedAttributes` 中 +- `com.mojang.math` + - `MatrixUtil` + - `isIdentity`、`isPureTranslation`、`isOrthonormal` 现在接受一个 `Matrix4fc` + - `checkProperty` - 检查提供的属性是否在矩阵中表示。 + - `OctahedralGroup` + - `transformation` 现在返回一个 `Matrix3fc` + - `fromAnges` -> `fromXYAngles`,不是一对一 + - `Quadrant` - 一个包含 90 度增量旋转的枚举。 + - `SymmetricGroup3#transformation` 现在返回一个 `Matrix3fc` + - `Transformation` 现在接受一个 `Matrix4fc` + - `getMatrix` 现在返回一个 `Matrix4fc` + - `getMatrixCopy` - 返回当前矩阵的深拷贝。 +- `net.minecraft.client.gui.font.FontTexture` 现在接受一个提供的标签字符串 +- `net.minecraft.client.main.GameConfig` 现在接受一个布尔值,表示是否渲染调试标签 +- `net.minecraft.client.renderer` + - `CloudRenderer#render` 不再接受用于投影或姿势的 `Matrix4f` + - `CompiledShaderProgram` -> `com.mojang.blaze3d.opengl.GlProgram` + - `link` 现在接受一个字符串作为着色器名称 + - `setupUniforms` 现在接受 `$UniformDescription` 列表以及采样器使用的名称列表 + - `getUniformConfig` 已移除 + - `bindSampler` 现在接受一个 `GpuTexture` 而不是整数绑定标识符 + - `parseUniformNode` 已移除 + - `CoreShaders` -> `RenderPipelines`,不是一对一 + - `LightTexture#getTarget` - 返回包含基于玩家的当前等级的光照纹理的 `GpuTexture`。 + - `PostChain` + - `load` 不再接受 `ShaderManager`,现在接受一个表示链名称的 `ResourceLocation` + - `addToFrame`、`process` 现在接受一个 `RenderPass` 消费者,用于将任何其他设置应用于要渲染的通道 + - `setUniform` 已移除 + - `setOnRenderPass` - 在 `RenderPass` 上的后处理链中设置统一变量,以供着色器使用。 + - `PostChainConfig` + - `$Pass` 现在接受顶点和片段着色器的 id,而不是程序 id + - `referencedTargets` - 返回通道中引用的要应用的目标。 + - `program` 已移除 + - `$Uniform` 现在接受统一变量的类型以及一个可选的浮点数列表(如果值不需要被覆盖) + - `PostPass` 不再接受 `CompiledShaderProgram`,现在接受 `RenderPipeline` 而不是表示通道名称的字符串 + - `addToFrame` 现在接受一个 `RenderPass` 消费者,用于将任何其他设置应用于要渲染的通道 + - `getShader` 已移除 + - `$Input#bindTo` 现在接受 `RenderPass` 而不是 `CompiledShaderProgram` + - `RenderStateShard` + - 使用多边形偏移的 `$LayerStateShard` 已移除 + - `getName` - 返回着色器的名称。 + - `$TransparencyStateShard` 类已移除 + - 现在通过 `BlendFunction` 处理 + - `$ShaderStateShard` 类已移除 + - 由 `VertexBuffer` 直接引用 + - `$CullStateShard` 类已移除 + - 现在作为 `RenderPipeline` 上的设置处理 + - `$DepthTestStateShard` 类已移除 + - 现在通过 `DepthTestFunction` 处理 + - `$WriteMaskStateShard` 类已移除 + - 现在作为 `RenderPipeline` 上的设置处理 + - `$ColorLogicStateShard` 类已移除 + - 现在作为 `RenderPipeline` 上的设置处理 + - `$OutputStateShard` 现在接受一个提供的 `RenderTarget` 而不是用于启动和拆除状态的 runnable + - `RenderType` 不再接受 `VertexFormat` 或 `VertexFormat$Mode` + - `SKY`、`END_SKY`、`sky`、`endSky`、`stars` 已移除 + - `ENTITY_OUTLINE_BLIT`、`entityOutlineBlit` 已移除 + - `PANORAMA`、`panorama` 已移除 + - `CREATE_LIGHTMAP`、`createLightmap` 已移除 + - `createClouds`、`flatClouds`、`clouds`、`cloudsDepthOnly` 已移除 + - `worldBorder` 已移除 + - `debugLine` - 返回与调试线关联的 `RenderType`。 + - `entityOutlineBlit` - 返回用于渲染实体轮廓的 `RenderType`。 + - `panorama` - 返回用于渲染全景模式的 `RenderType`。 + - `createLightmap` - 返回用于渲染光照图纹理的 `RenderType`。 + - `create` 不再接受 `VertexFormat` 或 `VertexFormat$Mode`,而是接受 `RenderPipeline` + - `getRenderTarget`、`getRenderPipeline` - 返回用于渲染的目标和管道。 + - `format`、`mode`、`draw` 现在是抽象的 + - `$CompositeStateBuilder` 方法现在是 protected + - `$OutlineProperty` 现在是 protected + - `ShaderDefines$Builder#define` 现在有一个接受整数的重载 + - `ShaderManager` + - `SHADER_INCLUDE_PATH` 现在是私有的 + - `MAX_LOG_LENGTH` 已移除 + - `preloadForStartup` 已移除,被 `GpuDevice#precompilePipeline` 取代 + - `getProgram`、`getProgramForLoading` -> `getShader`,不是一对一 + - `linkProgram` 现在接受 `RenderPipeline` 而不是 `ShaderProgram` 和 `ShaderProgramConfig` + - `$CompilationCache#getOrCompileProgram`、`getOrCompileShader` -> `getShaderSource`,不是一对一 + - `$Configs` 不再接受程序映射 + - `$ShaderCompilationKey` 记录已移除 + - `ShaderProgram`、`ShaderProgramConfig` -> `RenderPipeline`,不是一对一 + - `SkyRenderer#renderDarkDisc` 不再接受 `PoseStack` +- `net.minecraft.client.renderer.chunk.SectionRenderDispatcher` + - `uploadSectionLayer`、`uploadSectionIndexBuffer` -> `$RenderSection#uploadSectionLayer`、`uploadSectionIndexBuffer` + - `$SectionBuffers` - 一个保存用于渲染部分的缓冲区的类。 +- `net.minecraft.client.renderer.texture` + - `AbstractTexture` + - `NOT_ASSIGNED` 已移除 + - `texture`、`getTexture` - 保存要渲染的纹理的引用。 + - `getId`、`releaseId` 已移除 + - `bind` 已移除 + - `DynamicTexture` 现在接受纹理的标签 + - `SpriteContents#uploadFirstFrame`、`$AnimatedTexture#uploadFirstFrame` 现在接受一个 `GpuTexture` + - `SpriteTicker#tickAndUpload` 现在接受 `GpuTexture` + - `TextureAtlasSprite#uploadFirstFrame`、`$Ticker#tickAndUpload` 现在接受 `GpuTexture` + +## 模型重做 + +模型系统已进一步分离为用于方块状态、方块和物品的模型。因此,统一的 `BakedModel` 已被完全移除,并分离到它们自己的部分中,分三步加载:从 JSON 加载、解析依赖关系,然后烘焙以供关联的方块状态模型或物品模型使用。作为参考,下面讨论的所有内容都是在 `ModelManager#reload` 中并行发生的。 + +首先,让我们从方块和物品之间使用的基础模型 JSON 开始。这些被加载到 `UnbakedModel`(具体来说是 `BlockModel`)中,其中包含熟悉的属性,例如 gui 光照和纹理槽位。然而,一个变化是将元素与其渲染设置分离。这些持有渲染四边形的元素存储在 `UnbakedGeometry` 中。`UnbakedGeometry` 负责将模型烘焙成 `QuadCollection`,它实际上保存了要渲染的 `BakedQuad` 列表。目前,原版只有 `SimpleUnbakedGeometry`,它保存了熟悉的 `BlockElement` 列表。这些 `UnbakedModel` 一旦加载,就会被传递给 `ModelDiscovery` 以解析方块状态和物品模型。 + +接下来是 `ResolvableModel`,它是方块状态和物品模型的基础。这些模型本质上作为标记,请求它们将使用的 `UnbakedModel`。然后,我们有它们的子类型 `BlockStateModel$UnbakedRoot`(用于方块状态 JSON)和 `ItemModel$Unbaked`(用于客户端物品 JSON 中引用的模型)。每个都以某种方式实现 `resolveDependencies`,以调用 `ResolvableModel$Resolver#markDependency`,传入它们想要使用的模型位置。 + +> 从技术上讲,`BlockStateModel` 更复杂一些,因为变体在加载期间使用 `BlockStateModel$Unbaked`,然后在初始化期间转换为 `$UnbakedRoot`。 + +现在我们知道了应该加载哪些模型,它们必须被放入一个可用于烘焙的状态。这是 `ModelDiscovery` 的工作,它接受一个 `ResolvableModel`,并在第一次引用时将 `UnbakedModel` 加载到 `ResolvedModel` 中。顾名思义,`ResolvedModel` 是围绕 `UnbakedModel` 的功能包装器,用于解析所有依赖链。 + +从那里,为 `BlockState` 构建模型组,并加载纹理,从而进入实际烘焙 `BlockStateModel` 和 `ItemModel` 的最后一步。这通过 `$UnbakedRoot`(或 `$Unbaked`)和 `ModelBakery` 上提供的 `bake` 方法处理。简而言之,`bake` 构建 `BakedQuad` 列表,其中包含方块状态或物品模型本身所需的任何其他信息。`ResolvedModel` 从烘焙器中获得,然后调用其实例方法。对于 `BlockStateModel`,这通过 `SimpleModelWrapper#bake` 解析,从中从 `Variant` 数据获得 `ModelState`。它们将烘焙的四边形存储在 `BlockModelPart` 中。对于 `ItemModel`,它直接使用 `BakedQuad` 列表以及由 `ModelRenderProperties#fromResolvedModel` 提供的信息。这确实意味着每个 `BlockStateModel` 和 `ItemModel` 如果同一个模型在多个位置被引用,则可能包含重复(但唯一)的 `BakedQuad`。 + +### 方块生成器:变体修改器 + +鉴于所有分离出方块状态 JSON 加载的变化,`BlockModelGenerators` 也有许多变化。虽然其中大部分只是重命名(例如,`BlockStateGenerator` -> `BlockModelDefinitionGenerator`),但主要变化是增加了 `VariantMutator`。`VariantMutator` 在功能上是 `Variant` 上的一元运算符,用于设置某些设置。这一增加简化了(或者说更像是编解码器构造)`PropertyDispatch` 的使用,以便更快速地根据其属性分派具有变体的方块。 + +```java +// 在水平朝向属性上创建一个属性分派 +// 应用关联的变体,但如果需要,也可以提供一个函数式接口 +public static final PropertyDispatch ROTATION_HORIZONTAL_FACING = PropertyDispatch.modify(BlockStateProperties.HORIZONTAL_FACING) + .select(Direction.EAST, BlockModelGenerators.Y_ROT_90) + .select(Direction.SOUTH, BlockModelGenerators.Y_ROT_180) + .select(Direction.WEST, BlockModelGenerators.Y_ROT_270) + .select(Direction.NORTH, BlockModelGenerators.NOP); + +// 然后,在可以访问 `Consumer` blockStateOutput 的地方 +this.blockStateOutput.accept( + MultiVariantGenerator.dispatch(EXAMPLE_BLOCK).with(ROTATION_HORIZONTAL_FACING) +); +``` + +- `net.minecraft.client.data.models` + - `BlockModelGenerators` + - `nonOrientableTrapdoor` -> `NON_ORIENTABLE_TRAPDOOR`,现在是静态的 + - 常量现在可用于常见的 `VariantMutator`,例如将方块模型旋转一定角度 + - `texturedModels` -> `TEXTURED_MODELS`,现在是静态的 + - `MULTIFACE_GENERATOR` 现在是私有的 + - `plainModel` - 从模型位置创建一个变体。 + - `variant`、`variants` - 从一些 `Variant` 创建一个常规的 `MultiVariant` + - `plainVariant` - 从其位置创建一个只有一个模型的 `MultiVariant`。 + - `condition` - 为多部分模型创建一个新的条件构建器 + - `or` - 对多个条件进行 OR 操作。 + - 大多数生成器方法现在返回 `BlockModelDefinitionGenerator`、`Variant` 或 `MultiVariant`,并接受一个 `Variant` 或 `MultiVariant` 而不是指向所需模型的 `ResourceLocation` + - `VariantProperties` 已被 `VariantMutator` 取代 + - `Condition$TerminalCondition` 被 `Condition` 取代 + - `createHorizontalFacingDispatch`、`createHorizontalFacingDispatchAlt`、`createTorchHorizontalDispatch` 已移除 + - `createFacingDispatch` 已移除 + - `createRotatedVariant(Block, ResourceLocation)` 已移除 + - `selectMultifaceProperties` - 基于提供的 `BlockState` 和方向到属性函数,创建一个属性到 `VariantMutator` 的映射。 + - `applyRotation` 不再接受 `Variant`,并返回一个 `VariantMutator` + - `ItemModelGenerators#generateSpawnEgg` 已移除 + - `ModelProvider#saveAll` 已移除 +- `net.minecraft.client.data.models.blockstates` + - `BlockStateGenerator` -> `BlockModelDefinitionGenerator`,不是一对一 + - `Condition` -> `net.minecraft.client.renderer.block.model.multipart.Condition`,不是一对一 + - `validate` -> `instantiate`,不是一对一 + - `ConditionBuilder` - 使用属性值构建一个条件 + - `MultiPartGenerator` 现在实现 `BlockModelDefinitionGenerator` + - `with(List)` -> `with(MultiVariant)` + - `with(Variant)` 已移除 + - `with(Condition, ...)` -> `with(Condition, MultiVariant)` + - 接受 `ConditionBuilder` 的重载 + - `$ConditionalEntry`、`$Entry` 已移除 + - `MultiVariantGenerator` 现在实现 `BlockModelDefinitionGenerator` + - `multiVariant` -> `dispatch` + - `multiVariant(Block, ...)` -> `dispatch(Block, MultiVariant)` + - `$Empty` - 一个匹配每个方块状态的多变体条目。 + - `PropertyDispatch` 有一个包含分派值的泛型 + - 泛型 `V` 替换了所有 `List` 的值 + - `property`、`properties` -> `initial` 或 `modify` + - `$C*#generateList` 方法已移除 + - `$*Function` 已移除 + - `Selector` -> `PropertyValueList`,不是一对一 + - `Variant` -> `net.minecraft.client.renderer.block.model.Variant`,不是一对一 + - `VariantProperties` -> `net.minecraft.client.renderer.block.model.VariantMutator`,不是一对一 + - `VariantProperty` -> `net.minecraft.client.renderer.block.model.VariantMutator$VariantProperty`,不是一对一 +- `net.minecraft.client.renderer.ItemInHandRenderer#renderItem` 不再接受表示物品是否在左手中的布尔值 +- `net.minecraft.client.renderer.block` + - `BlockModelShaper#stateToModelLocation`、`statePropertiesToString` 已移除 + - `BlockRenderDispatcher#renderBatched` 现在接受一个 `BlockModelPart` 列表而不是 `RandomSource` + - `ModelBlockRenderer` + - `tesselateBlock`、`tesselateWithAO`、`tesselateWithoutAO` 不再接受 `RandomSource`,并将 `BlockStateModel` 替换为 `BlockModelPart` 列表 + - `renderModel` 现在是静态的,不再接受 `BlockState` + - `$AmbientOcclusionFace` -> `$AmbientOcclusionRenderStorage` + - `$CommonRenderStorage` - 一个类,保存用于在其给定位置渲染方块的一些元数据。 + - `$SizeInfo` 现在接受直接索引,而不是从其方向和翻转布尔值计算信息 +- `net.minecraft.client.renderer.block.model` + - `BakedQuad` 现在是一个记录 + - `BlockElement` 现在是一个记录 + - `from`、`to` 现在是 `Vector3fc` + - `BlockElementFace` 现在接受一个 `Quadrant` 作为面旋转 + - `getU`、`getV` - 返回旋转后的纹理坐标。 + - `$Deserializer#getTintIndex` 现在是私有的和静态的 + - `BlockFaceUV` -> `BlockElementFace$UVs`,不是一对一 + - `BlockModel` 现在是一个记录,接受一个 `UnbakedGeometry` 而不是直接的 `BlockElement` 列表 + - `$Deserializer#getElements` 现在返回一个 `UnbakedGeometry` + - `BlockModelDefinition` 现在是一个记录,接受 `$SimpleModelSelectors` 和 `$MultiPartDefinition` + - `GSON`、`fromStream`、`fromJsonElement` -> `CODEC`,不是一对一 + - `instantiate` 现在接受一个提供的字符串而不是直接的字符串 + - `$Deserializer` 已移除 + - `$MultiPartDefinition` - 一个记录,保存用于获取多部分模型的选择器列表。 + - `$SimpleModelSelectors` - 一个记录,保存变体到其未烘焙模型实例的映射。 + - `BlockModelPart` - 方块的烘焙模型表示。 + - `BlockStateModel` - 方块状态的烘焙表示。 + - `collectParts` - 获取用于渲染此状态的烘焙模型列表。 + - `$SimpleCachedUnbakedRoot` - 一个类,表示某个 `$Unbaked` 模型的委托。 + - `$Unbaked` - 一个可以创建 `$SimpleCachedUnbakedRoot` 的 `$UnbakedRoot` 的扩展 + - `FaceBakery` + - `bakeQuad` 现在接受 `Vector3fc` 而不是 `Vector3f` + - `recomputeUVs` 已移除 + - `extractPositions` - 提取面的位置并将它们传递给一个消费者以供使用。 + - `ItemTransform` 现在是一个记录,向量是 `Vector3fc` + - `MultiVariant` -> `net.minecraft.client.data.models.MultiVariant` + - `CODEC` + - `with` - 创建一个带有指定修改器的 `MultiVariant`。 + - `$Deserializer` 类已移除 + - `SimpleModelWrapper` 现在实现 `BlockModelPart` + - `SimpleUnbakedGeometry` - 一个保存要烘焙的 `BlockElement` 列表的未烘焙几何体。 + - `SingleVariant` - 一个 `BlockStateModel` 实现,其状态只有一个模型。 + - `UnbakedBlockStateModel` -> `BlockStateModel$UnbakedRoot` + - `Variant` 不再实现 `ModelState`,现在接受 `$SimpleModelState` 而不是直接的旋转和 uv 锁定 + - 构造函数现在有一个只提供 `ResourceLocation` 的重载,不再接受权重,将其留给 `MultiVariant` + - `CODEC` + - `withXRot`、`withYRot`、`withUvLock`、`withModel`、`withState`、`with` - 将变体变异为一个应用了给定设置的新对象。 + - `$Deserializer` 类已移除 + - `$SimpleModelState` - 一个记录,保存 x/y 旋转和 uv 锁定。 + - `VariantMutator` - 一个变体上的一元运算符,将指定设置应用于变体。在状态生成期间使用。 +- `net.minecraft.client.renderer.block.model.multipart` + - `AndCondition`、`OrCondition` -> `CombinedCondition`,不是一对一 + - `KeyValueCondition` 现在是一个记录,接受一个要测试的键到术语的映射 + - `MultiPart` -> `MultiPartModel$Unbaked` + - `$Definition` + - `CODEC` + - `getMultiVariants` 已移除 + - `$Deserializer` 类已移除 + - `Selector` 现在是一个记录,接受一个 `BlockStateModel$Unbaked` 而不是 `MultiVariant` + - `$Deserializer` 类已移除 +- `net.minecraft.client.renderer.entity.ItemRenderer` + - `renderItem` 现在接受一个 `List` 而不是 `BakedModel` + - `renderStatic` 不再接受一个表示物品握在哪只手中的布尔值 +- `net.minecraft.client.renderer.item` + - `BlockModelWrapper` 现在有一个公共构造函数,接受染色源列表、四边形列表和 `ModelRenderProperties` + - 四边形列表和 `ModelRenderProperties` 取代了直接的 `BakedModel`,或者现在的 `BlockStateModel` + - `computeExtents` - 将烘焙四边形的顶点提取到一个数组中。 + - `ItemModel$BakingContext#bake` 已移除 + - `ItemModelResolver#updateForLiving`、`updateForTopItem` 不再接受表示物品是否在左手中的布尔值 + - `ItemStackReenderState` + - `isGui3d` 已移除 + - `transform` 已移除 + - `visitExtents` - 访问模型要渲染的所有顶点,并将它们传递给提供的消费者。 + - `$LayerRenderState` + - `NO_EXTENTS_SUPPLIER` - 一个空的顶点列表。 + - `setupBlockModel` 已被拆分为 `prepareQuadList`、`setRenderType`、`setUsesBlockLight`、`setExtents`、`setParticleIcon`、`setTransform` + - `setupSpecialModel` 不再接受基础 `BakedModel` + - `MissingItemModel` 现在接受一个 `BakedQuad` 列表和 `ModelRenderProperties`,而不是直接的 `BakedModel` + - `ModelRenderProperties` - 用于渲染模型的属性,通常从 `ResolvedModel` 检索。 + - `SpecialModelRenderer` 现在接受 `ModelRenderProperties` 而不是基础 `BakedModel` +- `net.minecraft.client.resources.model` + - `BakedModel` -> `net.minecraft.client.resources.model.QuadCollection`,不是一对一 + - `BlockModelRotation` + - `by` 现在接受 `Quadrant` 而不是整数 + - `withUvLock` - 返回带有旋转和声明锁定 UV 用于旋转的模型状态。 + - `BlockStateDefinitions` - 一个用于创建方块名称到其状态定义映射的管理器。 + - `BlockStateModelLoader` + - `ModelResourceLocation` 字段已移除 + - `loadBlockState` 不再接受缺失模型 + - `$LoadedModel` 类已移除 + - `$LoadedModels` 现在接受一个 `BlockStateModel$UnbakedRoot` 而不是 `$Unbaked` + - `forResolving`、`plainModels` 已移除 + - `DelegateBakedModel` -> `net.minecraft.client.renderer.block.model.SimpleModelWrapper`,不是一对一 + - `MissingBlockModel#VARIANT` 已移除 + - `ModelBaker` + - `bake` -> `getModel`,不是一对一 + - 烘焙器只是检索 `ResolvedModel` + - `rootName` 已移除 + - `compute` - 计算包含 `ModelBaker` 的提供的键。通常用于烘焙 `BlockStateModel` + - `$SharedOperationKey` - 一个接口,通常计算未烘焙模型的某个烘焙过程。 + - `ModelBakery` 现在接受一个 `Map` 用于未烘焙的方块状态模型,一个 `Map` 用于加载的模型,以及一个 `ResolvedModel` 用于缺失模型 + - `bakeModels` 现在接受一个 `SpriteGetter` 和一个 `Executor`,同时返回一个 `CompletableFuture` 用于并行加载和烘焙 + - `$BakingResult` 现在接受一个 `$MissingModels` 用于缺失的方块状态和物品模型,以及一个 `Map` 用于烘焙的方块状态模型;缺失的物品模型存储在 `$MissingModels` 中 + - `$MissingModels` - 保存方块状态和物品的缺失模型。 + - `$TextureGetter` 接口已移除 + - `ModelDebugName` 不再继承 `Supplier`,而是使用 `debugName` + - `ModelDiscovery` + - `registerSpecialModels` 已移除 + - `discoverDependencies` 现在是私有的 + - `getReferencedModels`、`getUnreferencedModels` 已移除 + - `addSpecialModel` - 将一个根模型添加到任意加载的模型列表中。 + - `missingModel` - 返回缺失模型 + - `resolve` - 解析所有模型依赖关系,返回模型名称到其模型的映射。 + - `ModelGroupCollector$GroupKey#create` 现在接受一个 `BlockStateModel$UnbakedRoot` 而不是 `$Unbaked` + - `ModelManager` + - `getModel` 已移除 + - `getMissingModel` -> `getMissingBlockStateModel` + - `$ResolvedModels` - 一个包含已解析依赖关系的模型的映射。 + - `ModelResourceLocation` 记录已移除 + - `ModelState` + - `getRotation` -> `transformation` + - `isUvLocked` 已移除 + - `faceTransfomration`、`inverseFaceTransformation` - 处理返回用于烘焙面顶点的变换后的 `Matrix4fc`。 + - `MultiPartBakedModel` -> `net.minecraft.client.renderer.block.model.multipart.MultiPartModel` + - 现在实现 `BlockStateModel` 而不是继承 `DelegateBakedModel` + - `$SharedBlockState` - 一个持有者,包含映射到其 `$Selector` 的 `BlockStateModel`。 + - `QuadCollection` - 一个包含要基于关联方向和剔除渲染的四边形列表的数据对象。 + - `ResolvableModel$Resolver#resolve` -> `markDependency`,不是一对一 + - 不再直接解析,而是标记依赖关系以供后续后处理步骤使用 + - `ResolvedModel` - 一个 `UnbakedModel`,其模型和纹理依赖关系已被完全解析。 + - `SimpleBakedModel` -> `net.minecraft.client.renderer.block.model.SimpleModelWrapper` 或 `net.minecraft.client.renderer.block.model.SimpleUnbakedGeometry`,不是一对一 + - `SpriteGetter` + - `get`、`reportMissingReference` 现在接受 `ModelDebugName` + - `resolveSlot` - 将 `TextureSlot` 中的键解析为其 `TextureAtlasSprite`。 + - `UnbakedGeometry` - 一个接口,构造在烘焙时要渲染的四边形集合。 + - `UnbakedModel` 不再实现 `ResolvableModel` + - `DEFAULT_AMBIENT_OCCLUSION`、`DEFAULT_GUI_LIGHT` 已移除 + - `PARTICLE_TEXTURE_REFERENCE` - 保存表示粒子纹理的键。 + - `bake` 已移除 + - `getAmbientOcclusion` -> `ambientOcclusion` + - `getGuiLight` -> `guiLight` + - `getTransforms` - `transforms` + - `getTextureSlots` - `textureSlots` + - `geometry` - 保存表示模型元素的未烘焙几何体。 + - `getParent` -> `parent`,不是一对一 + - `bakeWithTopModelValues` 已移除 + - `getTopTextureSlots`、`getTopAmbientOcclusion`、`getTopGuiLight`、`getTopTransform`、`getTopTransforms` 已移除 + - `WeightedBakedModel` -> `WeightedVariants` + - 现在实现 `BlockStateModel` 而不是继承 `DelegateBakedModel` +- `net.minecraft.world.item.ItemDisplayContext#leftHand` - 返回显示上下文是否正在使用实体的左手渲染。 + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 实体引用 + +通常,存储另一个实体的 UUID 是为了稍后获取该实体以执行某些逻辑。然而,如果实体在某个时间点被移除,存储原始实体可能会导致问题。因此,添加了 `EntityReference` 来处理从 UUID 解析实体,同时确保在查询时它仍然存在。 + +`EntityReference` 只是一个包装的 `Either`,要么持有实体实例,要么持有 UUID。当通过 `getEntity` 解析时,它将尝试验证存储的实体(如果存在)是否未被移除。如果被移除,它将获取 UUID 以再次查找实体本身。如果该实体确实存在,则返回它,否则返回 null。 + +实体中对 UUID 的大多数引用已被替换为 `EntityReference` 以促进此更改。 + +- `net.minecraft.network.syncher.EntityDataSerializers#OPTIONAL_UUID` -> `OPTIONAL_LIVING_ENTITY_REFERENCE`,不是一对一,因为它可以持有实体引用 +- `net.minecraft.server.level.ServerLevel#getEntity(UUID)` -> `Level#getEntity(UUID)` +- `net.minecraft.world.entity` + - `EntityReference` - 一个对实体的引用,要么通过其在世界中的实体实例,要么通过 UUID。 + - `LivingEntity#lastHurtByPlayer`、`lastHurtByMob` 现在是 `EntityReference` + - `OwnableEntity` + - `getOwnerUUID` -> `getOwnerReference`,不是一对一 + - `level` 现在返回一个 `Level` 而不是 `EntityGetter` + - `TamableAnimal#setOwnerUUID` -> `setOwner` 或 `setOwnerReference`;不是一对一 +- `net.minecraft.world.entity.animal.horse.AbstractHorse#setOwnerUUID` -> `setOwner`,不是一对一 +- `net.minecraft.world.level.Level` 现在实现 `UUIDLookup` +- `net.minecraft.world.level.entity` + - `EntityAccess` 现在实现 `UniquelyIdentifyable` + - `UniquelyIdentifyable` - 一个声称对象具有 UUID 并跟踪对象是否被移除的接口。 + - `UUIDLookup` - 一个通过 UUID 查找类型的接口。 + +### 解作用域玩家参数 + +许多接受 `Player` 的方法已被解作用域,根据用例接受 `LivingEntity` 或 `Entity`。以下方法是此情况的非详尽列表。 + +- `net.minecraft.world.entity.EntityType` + - `spawn` + - `createDefaultStackConfig`、`appendDefaultStackConfig` + - `appendCustomEntityStackConfig`、`updateCustomEntityTag` +- `net.minecraft.world.item` + - `BucketItem#playEmptySound` + - `DispensibleContainerItem#checkExtraContent`、`emptyContents` +- `net.minecraft.world.level` + - `Level` + - `playSeededSound` + - `mayInteract` + - `LevelAccessor` + - `playSound` + - `levelEvent` +- `net.minecraft.world.level.block` + - `BucketPickup#pickupBlock` + - `LiquidBlockContainer#canPlaceLiquid` +- `net.minecraft.world.level.block.entity.BrushableBlockEntity#brush` + +### 组件交互事件 + +`MutableComponent` 上的点击和悬停事件已被重做为类似 `MapCodec` 注册表的系统。它们现在都是接口,将其编解码器注册到一个 `$Action` 枚举。然后,该实现创建一个引用 `$Action` 类型的编解码器,并存储逻辑应用所需的任何必要信息。然而,没有与组件交互关联的直接“动作”逻辑。相反,它们被硬编码到它们的使用位置。对于点击事件,是在 `Screen#handleComponentClicked` 中。对于悬停事件,是在 `GuiGraphics#renderComponentHoverEffect` 中。因此,任何添加的额外事件都需要注入到枚举以及这两个位置之一或两者中。 + +- `net.minecraft.network.chat` + - `ClickEvent` 现在是一个接口 + - `getAction` -> `action` + - `getValue` 现在根据需要在其子类上,用于其各自的类型 + - `HoverEvent` 现在是一个接口 + - `getAction` -> `action` + - `$EntityTooltipInfo` + - `CODEC` 现在是一个 `MapCodec` + - `legacyCreate` 已移除 + - `$ItemStackInfo` 已移除,被 `$ShowItem` 取代 + - `$LegacyConverter` 接口已移除 + +### 纹理图集重做 + +纹理图集逻辑已最终确定为一个注册表编解码器系统;然而,图集数据的查询方式已更改。首先,所有图集标识符存储在 `AtlasIds` 中,而相应的纹理位置存储在 `Sheets` 中。要从图集中获取材质,使用 `MaterialMapper` 作为纹理位置和要附加到材质的关联前缀的包装器。然后可以通过 `apply` 传入您想要使用的材质的 id 来获得 `Material`。 + +例如: + +```java +// 在 sheets 中找到 +public static final MaterialMapper ITEMS_MAPPER = new MaterialMapper(TextureAtlas.LOCATION_BLOCKS, "item"); +public static final MaterialMapper BLOCKS_MAPPER = new MaterialMapper(TextureAtlas.LOCATION_BLOCKS, "block"); + +// 查找位于 `assets/examplemod/textures/item/example_item.png` 的纹理的材质 +public static final Material EXAMPLE_ITEM = ITEMS_MAPPER.apply(ResourceLocation.fromNamespaceAndPath("examplemod", "example_item")); + +// 查找位于 `assets/examplemod/textures/block/example/block.png` 的纹理的材质 +public static final Material EXAMPLE_BLOCK = ITEMS_MAPPER.apply(ResourceLocation.fromNamespaceAndPath("examplemod", "example/block")); +``` + +- `net.minecraft.client.data.AtlasProvider` - 用于生成纹理图集提供者的数据提供者。 +- `net.minecraft.client.data.models.ItemModelGenerators` + - `SLOT_*` -> `TRIM_PREFIX_*`,现在是公开的,并且是 `ResourceLocation` + - `TRIM_MATERIAL_MODELS` 现在是公开的 + - `generateTrimmableItem` 现在接受 `ResourceLocation` 而不是 `String` + - `$TrimMaterialData` 现在是公开的,接受 `MaterialAssetGroup` 而不是名称和覆盖材质 +- `net.minecraft.client.renderer` + - `MaterialMapper` - 一个存储图集纹理位置和应用于纹理中 id 的前缀的对象。 + - `Sheets` + - `*_MAPPER` - 每个纹理图集纹理的 `MaterialMapper`。 + - `createBedMaterial(ResourceLocation)` 已移除 + - `createShulkerMaterial(ResourceLocation)` 已移除 + - `createSignMaterial(ResourceLocation)` 已移除 + - `chestMaterial(String)`、`chestMaterial(ResourceLocation)` 已移除 + - `createDecoratedPotMaterial(ResourceLocation)` 已移除 +- `net.minecraft.client.renderer.blockentity.ConduitRenderer#MAPPER` - 一个从方块图集获取潮涌核心纹理的映射器。 +- `net.minecraft.client.renderer.texture.atlas` + - `SpriteSource#type` -> `codec`,不是一对一 + - `SpriteSources` 现在包含类似于客户端注册表的逻辑,通过其 id 映射器 + - `SpriteSourceType` 记录已移除 +- `net.minecraft.client.renderer.texture.atlas.sources` + - `DirectoryLister` 现在是一个记录 + - `PalettedPermutations` 现在是一个记录 + - `SingleFile` 现在是一个记录 + - `SourceFilter` 现在是一个记录 + - `Unstitcher` 现在是一个记录 + - `$Region` 现在是公开的 +- `net.minecraft.client.resources.model.AtlasIds` - 一个保存所有原版纹理图集的 `ResourceLocation` 的类。 + +### 注册表上下文交换器 + +客户端物品现在存储一个 `RegistryContextSwapper`,用于正确检查访问注册表对象的客户端物品信息。在等级加载之前,它会获得一个占位符以避免崩溃,并在渲染期间用正确的值填充。 + +- `net.minecraft.client.multiplayer` + - `CacheSlot` - 一个包含从某个上下文计算出的值的对象。更新时,先前的值被覆盖,上下文注册该槽位以进行清理。 + - `ClientLevel` 现在实现 `CacheSlot$Cleaner` +- `net.minecraft.client.renderer.item` + - `ClientItem` 现在可以接受一个可为 null 的 `RegistryContextSwapper` + - `withRegistrySwapper` - 在 `ClientItem` 中设置 `RegistryContextSwapper` + - `ItemModel$BakingContext` 现在接受一个 `RegistryContextSwapper` +- `net.minecraft.util` + - `PlaceholderLookupProvider` - 一个包含引用对象的占位符的提供者。在客户端物品中使用,因为它们将在 `RegistyAccess` 填充之前加载。 + - `RegistryContextSwapper` - 一个用于将某个对象换成另一个不同对象的接口。由客户端物品用于将占位符换成加载的 `RegistryAccess`。 + +### 重载实例创建 + +重载实例已被稍微重新排列。`SimpleReloadInstance` 基础现在只接受 `List`,其他字段被传入 `of` 函数,以便可以立即调用 `#startTasks`。 + +- `net.minecraft.server.packs.resources` + - `ProfiledReloadInstance` 构造函数现在是私有的,通过 `of` 访问 + - `SimpleReloadInstance` 只接受 `List` + - `of` 现在返回一个 `ReloadInstance`,不是一对一 + - `allPreparations` 现在是包私有的 + - `allDone` 现在是私有的 + - `startTasks` - 开始重载监听器。 + - `prepareTasks` - 运行执行器并设置读取和加载所有所需数据所需的 future。 + - `StateFactory$SIMPLE` - 一个调用 `PreparableReloadListener#reload` 的工厂 + +### 方块效果应用器 + +当实体在方块内部时应用于实体的效果现在通过 `InsideBlockEffectApplier` 和 `InsideBlockEffectType` 处理。`InsideBlockEffectType` 是一个枚举,包含一个消费者,用于在调用时应用到实体上。另一方面,`InsideBlockEffectApplier` 存储在实体上,作为一种基于枚举序数以有序方式应用效果的方法。 + +要调用其中一种效果类型,您必须覆盖 `BlockBehaviour#entityInside` 或 `Fluid#entityInside` 并调用 `InsideBlockEffectApplier#apply`。如果某些内容应在效果类型之前应用,例如在细雪中冻结之前灭火,则应在 `apply` 之前调用 `InsideBlockEffectApplier#runBefore`。类似地,如果某些内容应在之后运行,例如在被放入熔岩后伤害敌人,则应调用 `runAfter`。 + +```java +// 在某个方块或流体子类中 +@Override +protected void entityInside(Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier applier) { + applier.runBefore(InsideBlockEffectType.EXTINGUISH, entity -> { + // 在此处修改实体。 + }); + + // 执行存储在类型上的基本应用逻辑 + applier.apply(InsideBlockEffectType.FIRE_IGNITE); + + applier.runAfter(InsideBlockEffectType.FIRE_IGNITE, entity -> { + // 执行由于效果应用而产生的任何最终检查 + entity.hurt(...); + }); +} +``` + +- `net.minecraft.world.entity` + - `InsideBlockEffectApplier` - 一个接口,定义实体在给定方块内时应如何交互。 + - `InsideBlockEffectType` - 一个枚举,定义当在引用该类型的特定方块内部时要执行的行为。 +- `net.minecraft.world.level.block.state.BlockBehaviour#entityInside`、`$BlockStateBase#entityInside` 现在接受一个 `InsideBlockEffectApplier` +- `net.minecraft.world.level.material.Fluid#entityInside`、`FluidState#entityInside` - 每当实体被认为在流体的边界框内时调用的方法。 + +### 定时器回调,加入编解码器俱乐部! + +用于在服务器计划中执行事件的 `TimerCallback`,通常是数据包中的 mcfunctions,现在已被重做为编解码器形式。这意味着可以通过将 `MapCodec` 传递给 `TimerCallbacks#register`(通过 `TimerCallbacks#SERVER_CALLBACKS`)而不是序列化器,将回调注册到可用回调列表中。 + +- `net.minecraft.world.level.timers` + - `FunctionCallback` 现在是一个记录 + - `FunctionTagCallback` 现在是一个记录 + - `TimerCallback` + - `codec` - 返回用于序列化的编解码器。 + - `$Serializer` 类已移除 + - `TimerCallbacks` + - `serialize`、`deserialize` -> `codec`,不是一对一 + +### JOML 后端接口 + +Mojang 选择通过传递其逻辑对象的实现接口(通常附加一个 `c`)来减少对 JOML 对象的限制。例如,`Vector3f` 变成 `Vector3fc`,`Matrix4f` 变成 `Matrix4fc`。这不会改变任何逻辑本身,因为 `c` 接口由类组件实现。 + +### 标签变更 + +- `minecraft:worldgen/biome` + - `spawns_cold_variant_farm_animals` + - `spawns_warm_variant_farm_animals` +- `minecraft:block` + - `sword_instantly_mines` + - `replaceable_by_mushrooms` + - `plays_ambient_desert_block_sounds` + - `edible_for_sheep` + - `dead_bush_may_place_on` -> `dry_vegetation_may_place_on` + - `camels_spawnable_on` +- `minecraft:cat_variant` 已移除 +- `minecraft:entity_type` + - `can_equip_saddle` + - `can_wear_horse_armor` +- `minecraft:item` + - `book_cloning_target` + - `eggs` + - `flowers` + +### 状态效果字段重命名 + +一些状态效果已重命名为其游戏内名称,而不是某些内部描述符。 + +- `MOVEMENT_SPEED` -> `SPEED` +- `MOVEMENT_SLOWDOWN` -> `SLOWNESS` +- `DIG_SPEED` -> `HASTE` +- `DIG_SLOWDOWN` -> `MINING_FATIGUE` +- `DAMAGE_BOOST` -> `STRENGTH` +- `HEAL` -> `INSTANT_HEALTH` +- `HARM` -> `INSTANT_DAMAGE` +- `JUMP` -> `JUMP_BOOST` +- `CONFUSION` -> `NAUSEA` +- `DAMAGE_RESISTANCE` -> `RESISTANCE` + +### 非常技术性的变更 + +这是一个技术变更列表,根据您的具体设置可能导致高度特定的错误。 + +- `minecraft:patch_sugar_cane` 功能和 `minecraft:patch_pumpkin` 功能的顺序已交换(先南瓜,后甘蔗),这意味着生成这两个功能的模组生物群系需要将其 JSON 更新为新顺序。 + +- 几个原版橡树和树选择器功能现在末尾附加了 `_leaf_litter`。 + - 例如:`trees_birch_and_oak` -> `trees_birch_and_oak_leaf_litter` + +### 新增列表 + +- `net.minecraft` + - `ChatFormatting#COLOR_CODEC` + - `CrashReportCategory#populateBlockLocationDetails` - 将方块位置详细信息添加到崩溃报告。 +- `net.minecraft.advancements.critereon.MinMaxBounds#createStreamCodec` - 为 `MinMaxBounds` 实现构造一个流编解码器。 +- `net.minecraft.client.Options#startedCleanly` - 设置游戏上次启动时是否干净启动。 +- `net.minecraft.client.data.models` + - `BlockModelGenerators#createSegmentedBlock` - 生成一个具有水平旋转的多部分方块状态定义,根据某个整数属性显示最多四个模型。 + - `ItemModelGenerators#prefixForSlotTrim` - 为某个槽位中的纹饰生成一个原版 `ResourceLocation`。 +- `net.minecraft.client.MouseHandler` + - `fillMousePositionDetails` - 将有关当前鼠标位置和屏幕大小的详细信息添加到崩溃报告。 + - `getScaledXPos` - 获取由 GUI 缩放选项缩放的当前 x 位置。 + - `getScaledYPos` - 获取由 GUI 缩放选项缩放的当前 y 位置。 + - `drawDebugMouseInfo` - 将有关鼠标缩放位置的信息绘制到屏幕。 +- `net.minecraft.client.gui.components.toasts.Toast#getSoundEvent` - 返回显示吐司时要播放的声音。 +- `net.minecraft.client.gui.screens.options.VideoSettingsScreen#updateFullscreenButton` - 将全屏选项设置为指定的布尔值。 +- `net.minecraft.client.model.geom.builders` + - `MeshDefinition#apply` - 在返回新实例之前将给定的变换器应用于网格。 + - `MeshTransformer#IDENTITY`- 执行恒等变换。 +- `net.minecraft.client.multiplayer.ClientPacketListener#decoratedHashOpsGenenerator` - 返回用于创建数据组件及其值的哈希值的生成器。 +- `net.minecraft.client.particle` + - `FallingLeavesParticle$TintedLeavesProvider` - 一个 `FallingLeavesParticle` 的提供者,使用粒子生成位置上方的方块指定的颜色。 + - `FireflyParticle` - 一个在给定的非空气方块位置周围生成萤火虫的粒子。 +- `net.minecraft.client.renderer` + - `BiomeColors#getAverageDryFoliageColor` - 返回干燥生物群系的平均树叶颜色。 + - `LevelRenderer$BrightnessGetter` - 一个在给定方块位置获取打包亮度的接口。 + - `WorldBorderRenderer#invalidate` - 使世界边界的当前渲染无效,以便重新渲染。 +- `net.minecraft.client.renderer.entity` + - `EntityRenderDispatcher#getRenderer` - 从渲染状态上存储的数据中获取要使用的渲染器。 + - `EntityRenderer#extractAdditionalHitboxes` - 当启用“显示碰撞箱”调试状态时,获取要渲染的任何其他碰撞箱。 +- `net.minecraft.client.renderer.entity.state` + - `EntityRenderState` + - `entityType` - 实体的类型。 + - `hitboxesRenderState` - 相对于实体位置的实体的碰撞箱信息。 + - `serverHitboxesRenderState` - 从服务器同步的实体的碰撞箱信息。 + - `fillCrashReportCategory` - 设置与渲染状态相关的任何崩溃的详细信息。 + - `HitboxesRenderState` - 相对于实体位置的实体碰撞箱的渲染状态。 + - `HitboxRenderState` - 要渲染的单个碰撞箱的渲染状态及其颜色,例如实体的眼睛高度。 + - `ServerHitboxesRenderState` - 包含来自相关服务器实体的最后同步信息的渲染状态。 + - `PigRenderState#variant` - 猪的变体。 +- `net.minecraft.client.renderer.item.SelectItemModel$ModelSelector` - 一个函数式接口,根据开关情况和级别选择物品模型。 +- `net.minecraft.client.renderer.item.properties.conditional.ComponentMatches` - 一个条件属性,检查给定的谓词是否匹配组件数据。 +- `net.minecraft.client.renderer.item.properties.select` + - `ComponentContents` - 一个开关情况属性,作用于数据组件内的内容。 + - `SelectItemModelProperty#valueCodec` - 返回属性类型的 `Codec`。 +- `net.minecraft.client.resources.DryFoliageColorReloadListener` - 一个加载干燥树叶颜色图的重新加载监听器。 +- `net.minecraft.commands.arguments.ComponentArgument#getResolvedComponent` - 构造一个包含其内容已解析信息的组件。 +- `net.minecraft.core` + - `Direction#getUnitVec3f` - 返回方向的浮点单位向量。 + - `HolderGetter$Provider#getOrThrow` - 从资源键获取一个持有者引用。 + - `SectionPos#sectionToChunk` - 将压缩的部分位置转换为压缩的区块位置。 + - `Vec3i#STREAM_CODEC` +- `net.minecraft.network` + - `HashedPatchMap` - 一个记录,包含组件到其哈希类型/值的映射,以及一组已移除的组件。 + - `HashedStack` - 一个 `ItemStack` 表示,对存储的组件进行哈希处理。 + - `ProtocolInfo$DetailsProvider` - 为给定的协议提供详细信息。 + - `SkipPacketDecoderException` - 当解码期间发生错误时抛出的异常,其数据被忽略。 + - `SkipPacketEncoderException` - 当编码期间发生错误时抛出的异常,其数据被忽略。 +- `net.minecraft.network.chat` + - `LastSeenMessages` + - `computeChecksum` - 计算表示所有消息签名的合并校验和的字节。 + - `$Update#verifyChecksum` - 验证更新校验和是否与最后看到的消息中的校验和匹配。 + - `LastSeenMessagesValidator$ValidationException` - 如果无法验证消息则抛出的异常。 + - `MessageSignature` + - `describe` - 返回消息签名的字符串化版本。 + - `checksum` - 将签名中的字节哈希为单个整数。 + - `PlayerChatMessage#describeSigned` - 返回聊天消息的字符串化版本。 +- `net.minecraft.network.codec` + - `ByteBufCodecs` + - `LONG_ARRAY` + - `lengthPrefixed` - 返回一个将缓冲区大小限制为给定大小的操作。 + - `IdDispatchCodec$DontDecorateException` - 一个接口,告诉异常处理程序重新抛出原始异常,而不是将其包装在 `EncoderException` 中。 +- `net.minecraft.network.protocol` + - `CodecModifier` - 一个使用给定对象修改某个编解码器的函数。 + - `ProtocolInfoBuilder#context*Protocol` - 使用给定的上下文构建一个 `UnboundProtocol`,用于修改要发送的编解码器。 +- `net.minecraft.network.protocol.game.GameProtocols` + - `HAS_INFINITE_MATERIALS` - 一个修饰符,检查 `ServerboundSetCreativeModeSlotPacket` 中玩家是否具有必要的设置。如果没有,则丢弃数据包。 + - `$Context` - 返回数据包用于修改传入编解码器的上下文。 +- `net.minecraft.resources.DelegatingOps` + - `$DelegateListBuilder` - 一个列表构建器,如果需要可以被子类化。 + - `$DelegateRecordBuilder` - 一个记录构建器,如果需要可以被子类化。 +- `net.minecraft.server.bossevents.CustomBossEvent$Packed` - 一个记录,支持用于序列化的事件信息。 +- `net.minecraft.server.commands.InCommandFunction` - 一个接受某些输入并返回结果的命令函数。 +- `net.minecraft.server.level` + - `DistanceManager#forEachBlockTickingChucnks` - 为每个启用了方块Tick的区块应用提供的消费者。 + - `ServerLevel` + - `areEntitiesActuallyLoadedAndTicking` - 返回实体管理器是否实际上正在Tick并加载给定区块中的实体。 + - `tickThunder` - 在给定等级内Tick雷声逻辑。 + - `anyPlayerCloseEnoughForSpawning` - 如果玩家足够接近以在给定位置生成实体,则返回。 + - `ServerPlayer$RespawnConfig` - 一个包含玩家重生信息的记录。 +- `net.minecraft.util` + - `AbstractListBuilder` - 一个 ops 列表构建器,将实现简化为三个方法:初始化、追加和构建最终列表。 + - `Brightness` + - `block` - 从打包值中返回块光。 + - `sky` - 从打包值中返回天空光。 + - `HashOps` - 一个为数据生成哈希码的动态 ops。 + - `ExtraCodecs` + - `UNTRUSTED_URI` - 一个不受游戏信任的 URI 的编解码器。 + - `CHAT_STRING` - 聊天消息中字符串的编解码器。 + - `legacyEnum` - 一个将枚举映射到其在 `Enum#toString` 中的输出的编解码器。 + - `FileSystemUtil` - 一个用于与文件系统交互的工具。 + - `GsonHelper#encodesLongerThan` - 返回提供的元素是否可以用指定数量的字符写入。 + - `Unit#STREAM_CODEC` - 一个用于单位实例的流编解码器。 + - `Util` + - `mapValues` - 使用给定的函数更新映射的值。 + - `mapValuesLazy` - 使用给定的函数更新映射的值,但每个值在首次访问时解析。 + - `growByHalf` - 返回一个整数乘以 1.5,向下取整,将值钳制到某个最小值和最大整数大小。 +- `net.minecraft.util.random.Weighted#map`、`WeightedList#map` - 将存储的对象转换为新类型。 +- `net.minecraft.util.thread.ParallelMapTransform` - 一个辅助工具,用于并行处理和批处理任务。 +- `net.minecraft.world.effect.MobEffectInstance#withScaledDuration` - 构造一个新的实例,其持续时间按某个浮点值缩放。 +- `net.minecraft.world.entity` + - `AreaEffectCloud#setPotionDurationScale` - 设置药水应应用的持续时间比例。 + - `DropChances` - 一个槽位到概率的映射,表示实体掉落该装备的可能性。 + - `Entity` + - `isInterpolating` - 返回实体是否在两个步骤之间插值。 + - `sendBubbleColumnParticles` - 从服务器生成气泡柱粒子。 + - `canSimulateMovement` - 实体的移动是否可以模拟,通常来自玩家。 + - `propagateFallToPassengers` - 将载具的坠落伤害传播给其乘客。 + - `lavaIgnite` - 如果实体不免疫,则点燃实体 15 秒。 + - `clearFreeze` - 将实体被冻结的刻数设置为 0。 + - `removeLatestMovementRecordingBatch` - 从本Tick执行的所有移动中删除最后一个元素。 + - `InterpolationHandler` - 一个旨在根据需要轻松处理给定实体的位置和旋转插值的类。 + - `LivingEntity` + - `getLuck` - 返回实体在随机事件中的运气。 + - `getLastHurtByPlayer`、`setLastHurtByPlayer` - 处理最后伤害此实体的玩家。 + - `getEffectBlendFactor` - 获取已应用状态效果的混合因子。 + - `applyInput` - 应用实体的输入作为其 AI,通常用于本地玩家。 + - `INPUT_FRICTION` - 应用于实体移动的标量。 +- `net.minecraft.world.entity.animal.camel.Camel#checkCamelSpawnRules` - 检查骆驼是否可以在特定位置生成。 +- `net.minecraft.world.entity.animal.sheep.SheepColorSpawnRules` - 一个包含绵羊在给定气候下生成时羊毛的颜色生成配置的类。 +- `net.minecraft.world.entity.npc.Villager#createDefaultVillagerData` - 返回在未设置数据时要使用的村民的默认类型和职业。 +- `net.minecraft.world.entity.player.Player` + - `preventsBlockDrops` - 玩家是否不能掉落任何被破坏的方块。 + - `gameMode` - 返回玩家的当前游戏模式。 + - `debugInfo` - 将关于玩家的常见信息作为单个字符串返回。 +- `net.minecraft.world.inventory` + - `ContainerSynchronizer#createSlot` - 创建一个表示对侧槽位的 `RemoteSlot`。 + - `RemoteSlot` - 一个表示对侧数据的槽位,当数据不一致时进行同步。 +- `net.minecraft.world.item` + - `EitherHolder#key` - 返回持有的注册表对象的资源键。 + - `Item#STREAM_CODEC` + - `ItemStack` + - `OPTIONAL_UNTRUSTED_STREAM_CODEC` + - `MAP_CODEC` + - `canDestroyBlock` - 返回此物品是否可以破坏提供的方块状态。 +- `net.minecraft.world.item.alchemy.PotionContents#getPotionDescription` - 返回带有某些放大器的状态效果的描述。 +- `net.minecraft.world.item.crafting` + - `Recipe#KEY_CODEC` + - `TransmuteResult` - 一个配方结果对象,表示一个物品、数量和应用到的组件。 +- `net.minecraft.world.item.equipment.trim.ArmorTrim#layerAssetId` - 返回纹饰资源的位置。 +- `net.minecraft.world.level` + - `BlockGetter$BlockStepVisitor` - 一个消费者,接受当前位置以及期望行进路径内的碰撞次数。 + - `ColorMapColorUtil` - 一个辅助工具,根据生物群系的温度、降水、颜色映射和默认颜色从地图中获取颜色。 + - `DryFoliageColor` - 用于具有干燥树叶的生物群系的颜色解析器。 + - `GameRules` + - `getType` - 从其键获取游戏规则类型。 + - `keyCodec` - 为游戏规则类型的键创建编解码器。 + - `Level` + - `isMoonVisible` - 返回月亮当前是否在天空中可见。 + - `getPushableEntities` - 获取指定目标之外的在提供的边界框内的所有实体。 + - `getClientLeafTintColor` - 返回指定位置的树叶染色颜色。 + - `playPlayerSound` - 在客户端向当前玩家播放声音。 + - `LevelReader#getHeight` - 返回地图在给定位置的高度。 + - `NaturalSpawner#INSCRIBED_SQUARE_SPAWN_DISTANCE_CHUNK` - 提供玩家足够接近以进行生成的最小距离。 +- `net.minecraft.world.level.biome` + - `Biome` + - `getDryFoliageColor`、`getDryFoliageColorFromTexture` - 获取生物群系的干燥树叶颜色,要么从效果中获取,要么从气候设置中获取。 + - `BiomeSpecialEffects#getDryFoliageColorOverride`、`$Builder#dryFoliageColorOverride` - 当不从颜色映射纹理中提取时,返回默认的干燥树叶颜色。 +- `net.minecraft.world.level.block` + - `BaseFireBlock#fireIgnite` - 点燃一个实体。 + - `Block` + - `UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS` - 一个标志,在更新方块实体时跳过所有潜在的副作用。 + - `UPDATE_SKIP_ALL_SIDEEFFECTS` - 一个标志,通过跳过某些方块实体逻辑、抑制掉落和更新已知形状来跳过所有副作用。 + - `UPDATE_SKIP_ON_PLACE` - 一个标志,在设置时跳过调用 `BlockState#onPlace`。 + - `BonemealableBlock#hasSpreadableNeighbourPos`、`findSpreadableNeighbourPos` - 处理在施骨粉时植被可以传播到的其他位置。 + - `CactusFlowerBlock` - 生长在仙人掌上的花。 + - `FireflyBushBlock` - 一个在其周围生成萤火虫粒子的灌木。 + - `SandBlock` - 一个可以播放环境声音的彩色沙子方块。 + - `SegmentableBlock` - 一个通常可以分解成具有独特大小和位置的部分的方块。 + - `ShortDryGrassBlock` - 一个已经干枯的单个草方块。 + - `TallDryGrassBlock` - 一个已经干枯的双高草方块。 + - `TerracottaBlock` - 一个可以播放环境声音的陶瓦方块。 + - `TintParticleLeavesBlock` - 一个其粒子被染色的树叶方块。 + - `UntintedParticleLeavesBlock` - 一个其粒子未被染色的树叶方块。 + - `VegetationBlock` - 一个表示某种植被的方块,可以传播光线,并需要某种耕地或泥土才能生存。 +- `net.minecraft.world.level.block.entity.StructureBlockEntity#isStrict`、`setStrict` - 在生成结构时设置严格模式。 +- `net.minecraft.world.level.block.sounds.AmbientDesertBlockSoundsPlayer` - 一个辅助工具,用于为给定的方块播放声音,通常在 `animateTick` 期间。 +- `net.minecraft.world.level.block.state.BlockBehaviour#getEntityInsideCollisionShape` - 获取实体在其内部时方块的碰撞形状。 +- `net.minecraft.world.level.border.WorldBorder` + - `closestBorder` - 根据玩家的水平方向返回离玩家最近的边界列表。 + - `$DistancePerDirection` - 一个记录,包含给定方向上世界边界到实体的距离。 +- `net.minecraft.world.level.chunk.status.ChunkStatus#CODEC` +- `net.minecraft.world.level.entity.PersistentEntitySectionManager#isTicking` - 返回指定的区块当前是否正在Tick。 +- `net.minecraft.world.level.levelgen.Heightmap$Types#STREAM_CODEC` +- `net.minecraft.world.level.levelgen.feature` + - `AbstractHugeMushroomFeature#placeMushroomBlock` - 在指定位置放置一个蘑菇方块,如果可以则替换方块。 + - `FallenTreeFeature` - 一个生成带有给定长度树桩的倒伏树木的功能。 + - `TreeFeature#getLowestTrunkOrRootOfTree` - 返回树装饰器的最低树干位置。 +- `net.minecraft.world.level.levelgen.feature.configurations.FallenTreeConfiguration` - 用于带有树桩的倒伏树的配置。 +- `net.minecraft.world.level.levelgen.feature.treedecorators` + - `AttachedToLogsDecorator` - 一个装饰器,以一定的概率将随机方块附加到原木的给定方向。 + - `PlaceOnGroundDecorator` - 一个装饰器,将树放置在有效的方块位置上。 +- `net.minecraft.world.level.levelgen.structure.pools` + - `ListPoolElement#getElements` - 返回结构池的元素。 + - `SinglePoolElement#getTemplateLocation` - 返回元素使用的模板的位置。 + - `StructureTemplatePool#getTemplates` - 返回一个带有权重的元素列表。 +- `net.minecraft.world.level.levelgen.structure.structures.JigsawStructure` + - `getStartPool` - 返回要生成的拼图的起始池。 + - `getPoolAliases` - 返回拼图使用的所有池。 +- `net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate#getDefaultJointType` - 如果未指定或加载期间发生错误,返回两个拼图块之间的默认连接类型。 +- `net.minecraft.world.level.material.Fluid#getAABB`、`FluidState#getAABB` - 返回流体的边界框。 +- `net.minecraft.world.scores` + - `Objective#pack`、`$Packed` - 处理目标数据的可序列化形式。 + - `PlayerTeam#pack`、`$Packed` - 处理玩家队伍数据的可序列化形式。 + - `Scoreboard` + - `loadPlayerTeam`、`loadObjective` - 从打包对象加载数据。 + - `$PackedScore` - 处理记分板数据的可序列化形式。 +- `net.minecraft.world.level.storage.loot.LootTable#KEY_CODEC` +- `net.minecraft.world.phys` + - `AABB$Builder` - 一个用于通过提供内部向量来构造边界框的构建器。 + - `Vec2#CODEC` +- `net.minecraft.world.phys.shapes.CollisionContext` + - `placementContext` - 当从其物品放置方块时构造上下文。 + - `isPlacement` - 返回上下文是否用于放置方块。 +- `net.minecraft.world.ticks.TickPriority#CODEC` + +### 变更列表 + +- `net.minecraft.client.Screenshot` 现在是一个实用程序而不是实例类,意味着所有实例方法都已移除 + - `takeScreenshot(RenderTarget)` -> `takeScreenshot(RenderTarget, Consumer)`,不返回任何内容 +- `net.minecraft.client.multiplayer` + - `ClientChunkCache#replaceWithPacketData` 现在接受一个 `Map` 而不是 `CompoundTag` + - `MultiPlayerGameMode#hasInfiniteItems` -> `net.minecraft.world.entity.LivingEntity#hasInfiniteMaterials` + - `ClientPacketListener#markMessageAsProcessed` 现在接受一个 `MessageSignature` 而不是 `PlayerChatMessage` +- `net.minecraft.client.multiplayer.chat.ChatListener#handleChatMessageError` 现在接受一个可为 null 的 `MessageSignature` +- `net.minecraft.client.player` + - `ClientInput#leftImpulse`、`forwardImpulse` -> `moveVector`,现在是 protected + - `LocalPlayer#spinningEffectIntensity`、`oSpinningEffectIntensity` -> `portalEffectIntensity`、`oPortalEffectIntensity` +- `net.minecraft.client.renderer.LevelRenderer#getLightColor(BlockAndTintGetter, BlockState, BlockPos)` -> `getLightColor(LevelRenderer$BrightnessGetter, BlockAndTintGetter, BlockState, BlockPos)` +- `net.minecraft.client.renderer.blockentity.BlockEntityRenderer#render` 现在接受一个 `Vec3` 表示相机的位置 +- `net.minecraft.client.renderer.chunk.SectionRenderDispatcher` + - `$RenderSection` + - `getOrigin` -> `getRenderOrigin` + - `reset` 现在是公开的 + - `releaseBuffers` 已移除 + - `$CompileTask#getOrigin` -> `getRenderOrigin` +- `net.minecraft.client.renderer.entity` + - `DonkeyRenderer` 现在接受一个 `DonekyRenderer$Type`,包含纹理、模型层和装备信息 + - `ItemEntityRenderer#renderMultipleFromCount` 现在有一个接受模型边界框的重载 + - `UndeadHorseRenderer` 现在接受一个 `UndeadHorseRenderer$Type`,包含纹理、模型层和装备信息 +- `net.minecraft.client.renderer.entity.layers` + - `EquipmentLayerRenderer$TrimSpriteKey#textureId` -> `spriteId` + - `VillagerProfessionLayer#getHatData` 现在接受一个资源键到元数据部分的映射,并将注册表和值交换为一个持有者实例 +- `net.minecraft.client.renderer.item` + - `ConditionalItemModel` 现在接受一个 `ItemModelPropertyTest` 而不是 `ConditionalItemModelProperty` + - `SelectItemModel` 现在接受一个 `$ModelSelector` 而不是一个对象映射 +- `net.minecraft.client.renderer.item.properties.conditional.ConditionalItemModelProperty` 现在实现 `ItemModelPropertyTest` + - `ItemModelPropertyTest` 持有之前在 `ConditionalItemModelProperty` 中的 `get` 方法 +- `net.minecraft.commands.arguments` + - `ComponentArgument` + - `ERROR_INVALID_JSON` -> `ERROR_INVALID_COMPONENT` + - `getComponent` -> `getRawComponent` + - `ResourceKeyArgument#getRegistryKey` 现在是公开的 + - `StyleArgument#ERROR_INVALID_JSON` -> `ERROR_INVALID_STYLE` +- `net.minecraft.commands.arguments.item` + - `ComponentPredicateParser$Context#createComponentTest`、`createPredicateTest` 现在接受一个 `Dynamic` 而不是 `Tag` + - `ItemPredicateArgument` + - `$ComponentWrapper#decode` 现在接受一个 `Dynamic` 而不是 `RegistryOps`、`Tag` 对 + - `$PredicateWrapper#decode` 现在接受一个 `Dynamic` 而不是 `RegistryOps`、`Tag` 对 +- `net.minecraft.core` + - `BlockMath` + - `VANILLA_UV_TRANSFORM_LOCAL_TO_GLOBAL`、`VANILLA_UV_TRANSFORM_GLOBAL_TO_LOCAL` 现在是私有的 + - `getUVLockTransform` -> `getFaceTransformation` + - `Direction#rotate` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` + - `Rotations` 现在是一个记录 +- `net.minecraft.data.loot.BlockLootSubProvider#createPetalDrops` -> `createSegmentedBlockDrops` +- `net.minecraft.network` + - `FriendlyByteBuf` + - `writeLongArray`、`readLongArray` 现在有静态委托,接受 `ByteBuf`,并且 `*Fixed*` 版本用于固定大小的数组 + - `ProtocolInfo$Unbound` -> `$Details`、`net.minecraft.network.protocol.SimpleUnboundProtocol`、`net.minecraft.network.protocol.UnboundProtocol`;不是一对一 + - `#bind` -> `net.minecraft.network.protocol.SimpleUnboundProtocol#bind`、`UnboundProtocol#bind`;不是一对一 + - `SkipPacketException` 现在是一个接口,而不是 `EncoderException` 的子类 +- `net.minecraft.network.chat` + - `ComponentSerialization#flatCodec` -> `flatRestrictedCodec` + - `LastSeenMessages$Update` 现在接受一个表示校验和值的字节 + - `LastSeenMessagesValidator` + - `applyOffset` 现在不返回任何内容,并且可以抛出 `$ValidationException` + - `applyUpdate` 现在返回原始消息,并且可以抛出 `$ValidationException` +- `net.minecraft.network.codec.StreamCodec#composite` 现在有一个九个参数的重载 +- `net.minecraft.network.protocol.ProtocolInfoBuilder` 现在接受第三个泛型,表示如何修改提供的编解码器。 + - `addPacket` 现在有一个接受 `CodecModifier` 的重载 + - `build` -> `buildUnbound`,不是一对一 + - `protocol`、`serverboundProtocol`、`clientboundProtocol` 现在返回一个 `SimpleUnboundProtocol` +- `net.minecraft.network.protocol.ConfigurationProtocols` 现在包含 `SimpleUnboundProtocol` 常量 +- `net.minecraft.network.protocol.game` + - `ClientboundContainerSetContentPacket` 现在是一个记录 + - `ClientboundMoveEntityPacket#getyRot`、`getxRot` -> `getYRot`、`getXRot` + - `ClientboundPlayerChatPacket` 现在接受一个聊天消息的全局索引 + - `ClientboundLevelChunkPacketdata#getHeightmaps` 现在返回一个 `Map` + - `ClientboundUpdateAdvancementsPacket` 现在接受一个布尔值,表示是否将进度显示为吐司 + - `GameProtocols` 常量现在是 `SimpleUnboundProtocol` 或 `UnboundProtocol` + - `ServerboundContainerClickPacket` 现在是一个记录 + - `ServerboundMovePlayerPacket$Pos`、`$PosRot` 现在有一个接受 `Vec3` 作为位置的重载 + - `ServerboundSetStructureBlockPacket` 现在接受一个额外的布尔值,表示是否应在严格模式下生成结构 +- `net.minecraft.network.protocol.handshake.HandshakeProtocols#SERVERBOUND_TEMPLATE` 现在是一个 `SimpleUnboundProtocol` +- `net.minecraft.network.protocol.login.LoginProtocols#SERVERBOUND_TEMPLATE` 常量现在是 `SimpleUnboundProtocol` +- `net.minecraft.network.protocol.status.StatusProtocols#SERVERBOUND_TEMPLATE` 常量现在是 `SimpleUnboundProtocol` +- `net.minecraft.server.PlayerAdvancements#flushDirty` 现在接受一个布尔值,表示进度是否显示为吐司 +- `net.minecraft.server.bossevents.CustomBossEvent` + - `save` -> `pack`,不是一对一 + - `load` 现在接受 id 和打包变体以解包 +- `net.minecraft.server.level` + - `DistanceManager` + - `hasPlayersNearby` 现在返回一个 `TriState` + - `forEachBlockTickingChunks` -> `forEachEntityTickingChunk`,不是一对一 + - `ServerEntity` 现在接受一个消费者,用于向所有玩家广播数据包,但忽略列表中的玩家除外 + - `ServerLevel` + - `getForcedChunks` -> `getForceLoadedChunks` + - `isPositionTickingWithEntitiesLoaded` 现在是公开的 + - `isNaturalSpawningAllowed` -> `canSpawnEntitiesInChunk`,`BlockPos` 变体已移除 + - `ServerPlayer` + - `getRespawnPosition`、`getRespawnAngle`、`getRespawnDimension`、`isRespawnForced` -> `getRespawnConfig`,不是一对一 + - `setRespawnPosition` 现在接受一个 `$RespawnConfig` 而不是单独的重生信息 + - `loadAndSpawnParentVehicle`、`loadAndSpawnEnderpearls` 现在接受一个 `CompoundTag`,没有可选的包装 +- `net.minecraft.server.network.ServerGamePacketListenerImpl` 现在实现 `GameProtocols$Context` +- `net.minecraft.sounds.SoundEvents` 有以下声音现在是 `Holder` 包装的: + - `ITEM_BREAK` + - `SHIELD_BLOCK`、`SHIELD_BREAK`、 + - `WOLF_ARMOR_BREAK` +- `net.minecraft.util` + - `Brightness` + - `FULL_BRIGHT` 现在是 final + - `pack` 现在有一个静态重载,接受块光和天空光。 + - `ExtraCodecs#MATRIX4f` 现在是一个 `Codec` + - `Util#makeEnumMap` 返回 `Map` 超实例而不是具体的 `EnumMap` +- `net.minecraft.util.parsing.packrat.commands.TagParseRule` 现在接受一个标签类型的泛型 + - 构造函数现在是公开的,接受一个 `DynamicOps` +- `net.minecraft.util.profiling` + - `ActiveProfiler` 现在接受一个 `BooleanSupplier` 而不是一个布尔值 + - `ContinuousProfiler` 现在接受一个 `BooleanSupplier` 而不是一个布尔值 +- `net.minecraft.util.worldupdate.WorldUpgrader` 现在接受当前的 `WorldData` +- `net.minecraft.world` + - `BossEvent$BossBarColor`、`$BossBarOverlay` 现在实现 `StringRepresentable` + - `Container` 现在实现 `Iterable` +- `net.minecraft.world.effect` + - `MobEffect` + - `getBlendDurationTicks` -> `getBlendInDurationTicks`、`getBlendOutDurationTicks`、`getBlendOutAdvanceTicks`;不是一对一 + - `setBlendDuration` 现在有一个接受三个整数来设置淡入、淡出和淡出提前刻的重载 + - `MobEffectInstance#tick` -> `tickServer`、`tickClient`;不是一对一 +- `net.minecraft.world.entity` + - `Entity` + - `cancelLerp` -> `InterpolationHandler#cancel` + - `lerpTo` -> `moveOrInterpolateTo` + - `lerpTargetX`、`lerpTargetY`、`lerpTargetZ`、`lerpTargetXRot`、`lerpTargetYRot` -> `getInterpolation` + - `onAboveBubbleCol` -> `onAboveBubbleColumn` 现在接受一个 `BlockPos` 作为气泡柱粒子的生成位置 + - 逻辑委托给受保护的静态 `handleOnAboveBubbleColumn` + - `isControlledByOrIsLocalPlayer` -> `isLocalInstanceAuthoritative`,现在是 final + - `isControlledByLocalInstance` -> `isLocalClientAuthoritative`,现在是 protected + - `isControlledByClient` -> `isClientAuthoritative` + - `fallDistance`、`causeFallDamage` 现在是 double + - `absMoveto` -> `absSnapTo` + - `absRotateTo` -> `asbSnapRotationTo` + - `moveTo` -> `snapTo` + - `sendBubbleColumnParticles` 现在是静态的,接受 `Level` + - `onInsideBubbleColumn` 逻辑委托给受保护的静态 `handleOnInsideBubbleColumn` + - `EntityType` + - `POTION` -> `SPLASH_POTION`、`LINGERING_POTION`,不是一对一 + - `$EntityFactory#create` 现在可以返回一个 null 实例 + - `ExperienceOrb#value` -> `DATA_VALUE` + - `ItemBasedSteering` 不再接受用于判断是否有马鞍的访问器 + - `LivingEntity` + - `lastHurtByPlayerTime` -> `lastHurtByPlayerMemoryTime` + - `lerpSteps`、`lerpX`、`lerpY`、`lerpZ`、`lerpYRot`、`lerpXRot` -> `interpolation`,不是一对一 + - `isAffectedByFluids` 现在是公开的 + - `removeEffectNoUpdate` 现在是 final + - `tickHeadTurn` 现在不返回任何内容 + - `canDisableShield` -> `canDisableBlocking`,现在通过 `WEAPON` 数据组件设置 + - `calculateFallDamage` 现在接受 double 而不是 float + - `Mob` + - `handDropChances`、`armorDropChances`、`bodyArmorDropChance` -> `dropChances`,不是一对一 + - `getEquipmentDropChance` -> `getDropChances`,不是一对一 +- `net.minecraft.world.entity.ai.Brain#addActivityWithConditions` 现在有一个接受整数表示起始优先级的重载 +- `net.minecraft.world.entity.ai.behavior` + - `LongJumpToRandomPos$PossibleJump` 现在是一个记录 + - `VillagerGoalPackages#get*Package` 现在接受一个持有者包装的职业 +- `net.minecraft.world.entity.ai.gossip.GossipContainer#store`、`update` -> `clear`、`putAll`、`copy`;不是一对一 +- `net.minecraft.world.entity.animal` + - `Pig` 现在是一个 `VariantHolder` + - `Sheep` -> `.sheep.Sheep` + - `WaterAnimal#handleAirSupply` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.animal.axolotl.Axolotl#handleAirSupply` 现在接受一个 `ServerLevel` +- `net.minecraft.world.entity.monster.ZombieVillager#setGossips` 现在接受一个 `GossipContainer` +- `net.minecraft.world.entity.monster.warden.WardenSpawnTracker` 现在有一个重载,将初始参数设置为零 +- `net.minecraft.world.entity.npc` + - `Villager` 现在接受一个键或一个 `VillagerType` 的持有者 + - `setGossips` 现在接受一个 `GossipContainer` + - `VillagerData` 现在是一个记录 + - `set*` -> `with*` + - `VillagerProfession` 现在接受一个 `Component` 作为名称 + - `VillagerTrades` + - `TRADES` 现在接受一个资源键作为映射的键 + - 这对于所有其他特定类型的交易类似 + - `$FailureItemListing` 现在是私有的 +- `net.minecraft.world.entity.player.Player` + - `stopFallFlying` -> `LivingEntity#stopFallFlying` + - `isSpectator`、`isCreative` 在 `Player` 类中不再是抽象的 +- `net.minecraft.world.entity.projectile.ThrownPotion` -> `AbstractThrownPotion`,在 `ThrownLingeringPotion` 和 `ThrownSplashPotion` 中实现 +- `net.minecraft.world.entity.raid.Raid(int, ServerLevel, BlockPos)` -> `Raid(BlockPos, Difficulty)` + - `tick`、`addWaveMob` 现在接受 `ServerLevel` +- `net.minecraft.world.entity.vehicle` + - `AbstractMinecart#setDisplayBlockState` -> `setCustomDisplayBlockState` + - `MinecartBehavior` + - `cancelLerp` -> `InterpolationHandler#cancel` + - `lerpTargetX`、`lerpTargetY`、`lerpTargetZ`、`lerpTargetXRot`、`lerpTargetYRot` -> `getInterpolation` + - `MinecartTNT#primeFuse` 现在接受 `DamageSource` 原因 +- `net.minecraft.world.inventory` + - `AbstractContainerMenu` + - `setRemoteSlotNoCopy` -> `setRemoteSlotUnsafe`,不是一对一 + - `setRemoteCarried` 现在接受一个 `HashedStack` + - `ClickType` 现在接受一个表示其表示的 id + - `ContainerSynchronizer#sendInitialData` 现在接受一个堆栈列表而不是 `NonNullList` +- `net.minecraft.world.item` + - `EitherHolder` 现在接受一个 `Either` 实例而不是仅仅一个可选的持有者和 `ResourceKey` + - `Item` + - `canAttackBlock` -> `canDestroyBlock` + - `hurtEnemy` 不再返回任何内容 + - `onCraftedBy` 不再接受一个单独的 `Level` 实例,现在依赖于 `Player` 提供的实例 + - `ItemStack` + - `validateStrict` 现在是公开的 + - `onCraftedBy` 不再接受一个单独的 `Level` 实例,现在依赖于 `Player` 提供的实例 + - `MapItem` + - `create` 现在接受一个 `ServerLevel` 而不是 `Level` + - `lockMap` 现在是私有的 + - `ThrowablePotionItem` 现在是抽象的,包含两个创建 `AbstractThrownPotion` 实体的方法 + - `WrittenBookItem#resolveBookComponents` -> `WrittenBookContent#resolveForItem` +- `net.minecraft.world.item.alchemy.PotionContents` 现在实现 `TooltipProvider` + - `forEachEffect`、`applyToLivingEntity` 现在接受一个 float 表示持续时间的标量 +- `net.minecraft.world.item.component.WrittenBookContent` 现在实现 `TooltipProvider` +- `net.minecraft.world.item.crafting` + - `SmithingRecipe#baseIngredient` 现在返回一个 `Ingredient` + - `SmithingTransformRecipe` 现在接受一个 `TransmuteResult` 而不是一个 `ItemStack` 和一个 `Ingredient` 作为基础 + - `SmithingTrimRecipe` 现在接受 `Ingredient` 而不是 `Optional` 包装的条目,以及一个 `TrimPattern` 持有者 + - `TransmuteRecipe` 现在接受一个 `TransmuteResult` 而不是一个 `Item` 持有者 +- `net.minecraft.world.item.crafting.display.SlotDisplay$SmithingTrimDemoSlotDisplay` 现在接受一个 `TrimPattern` 持有者 +- `net.minecraft.world.item.enchantment.EnchantmentInstance` 现在是一个记录 +- `net.minecraft.world.level` + - `BlockGetter#boxTraverseBlocks` -> `forEachBlockIntersectedBetween`,不是一对一 + - `CustomSpawner#tick` 不再返回任何内容 + - `GameRules$Type` 现在接受一个值类 + - `Level` + - `onBlockStateChange` -> `updatePOIOnBlockStateChange` + - `isDay` -> `isBrightOutside` + - `isNight` -> `isDarkOutside` + - `setMapData` -> `net.minecraft.server.level.ServerLevel#setMapData` + - `getFreeMapId` -> `net.minecraft.server.level.ServerLevel#getFreeMapId` + - `LevelAccessor#blockUpdated` -> `updateNeighborsAt` +- `net.minecraft.world.level.biome.MobSpawnSettings$SpawnerData` 现在是一个记录 +- `net.minecraft.world.level.block` + - `AttachedStemBlock` 现在继承 `VegetationBlock` + - `AzaleaBlock` 现在继承 `VegetationBlock` + - `Block#fallOn` 现在接受一个 double 作为坠落伤害,而不是 float + - `BushBlock` 现在继承 `VegetationBlock` 并实现 `BonemealableBlock` + - `ColoredFallingBlock#dustColor` 现在是 protected + - `CropBlock` 现在继承 `VegetationBlock` + - `DeadBushBlock` -> `DryVegetationBlock` + - `DoublePlantBlock` 现在继承 `VegetationBlock` + - `FallingBlock#getDustColor` 现在是抽象的 + - `FlowerBedBlock` 现在继承 `VegetationBlock` + - `FlowerBlock` 现在继承 `VegetationBlock` + - `FungusBlock` 现在继承 `VegetationBlock` + - `LeafLitterBlock` 现在继承 `VegetationBlock` + - `LeavesBlock` 现在是抽象的,接受粒子生成的概率 + - 粒子通过 `spawnFallingLeavesParticle` 生成 + - `MangroveLeavesBlock` 现在继承 `TintedParticleLeavesBlock` + - `MushroomBlock` 现在继承 `VegetationBlock` + - `NetherSproutsBlock` 现在继承 `VegetationBlock` + - `NetherWartBlock` 现在继承 `VegetationBlock` + - `ParticleLeavesBlock` -> `LeafLitterBlock` + - `PinkPetalsBlock` -> `FlowerBedBlock` + - `RootsBlock` 现在继承 `VegetationBlock` + - `Rotation` 现在有一个用于网络同步的索引 + - `SaplingBlock` 现在继承 `VegetationBlock` + - `SeagrassBlock` 现在继承 `VegetationBlock` + - `SeaPickleBlock` 现在继承 `VegetationBlock` + - `StemBlock` 现在继承 `VegetationBlock` + - `SweetBerryBushBlock` 现在继承 `VegetationBlock` + - `TallGrassBlock` 现在继承 `VegetationBlock` + - `TntBlock#prime` 现在返回是否生成了点燃的 TNT。 + - `WaterlilyBlock` 现在继承 `VegetationBlock` +- `net.minecraft.world.level.block.entity` + - `BlockEntity` + - `parseCustomNameSafe` 现在接受一个可为 null 的 `Tag` 而不是字符串 + - `getPosFromTag` 现在接受 `ChunkPos` + - `$ComponentHolder#COMPONENTS_CODEC` 现在是一个 `MapCodec` + - `BLockEntityType#create` 不再是可为 null 的 +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawner#codec` 现在返回一个 `MapCodec` +- `net.minecraft.world.level.block.state.StateHolder` + - `getNullableValue` 现在是私有的 + - `hasProperty` 不再包含泛型 +- `net.minecraft.world.level.chunk` + - `ChunkAccess#setBlockState` 现在接受块标志而不是布尔值,并有一个重载来更新所有设置 + - `LevelChunk#replaceWithPacketData` 现在接受一个 `Map` 而不是 `CompoundTag` +- `net.minecraft.world.level.chunk.storage.SerializableChunkData#getChunkTypeFromTag` -> `getChunkStatusFromTag`,不是一对一 +- `net.minecraft.world.level.gameevent.vibrations.VibrationSystem#DEFAULT_VIBRATION_FREQUENCY` -> `NO_VIBRATION_FREQUENCY` +- `net.minecraft.world.level.levelgen.feature.TreeFeature#isVine` 现在是公开的 +- `net.minecraft.world.level.levelgen.structure.pools.alias` + - `Direct` -> `DirectPoolAlias` + - `Random` -> `RandomPoolAlias` + - `RandomGroup` -> `RandomGroupPoolAlias` +- `net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate$JigsawBlockInfo` 现在接受 `StructureTemplatePool` 的 `ResourceKey` 而不是原始 `ResourceLocation` +- `net.minecraft.world.level.saveddata.maps.MapFrame` 现在是一个记录 + - `save`、`load` -> `CODEC`,不是一对一 +- `net.minecraft.world.level.storage.loot.functions.SetWrittenBookPagesFunction#PAGE_CODEC` -> `WrittenBookContent#PAGES_CODEC` +- `net.minecraft.world.scores` + - `Score#write` -> `CODEC`,不是一对一 + - `Scoreboard` + - `savePlayerScores` -> `packPlayerScores`,不是一对一 + - `loadPlayerScores` -> `loadPlayerScore`,不是一对一 + - `Team$CollisionRule`、`$Visibility` 现在是 `StringRepresentable` +- `net.minecraft.world.phys.shapes.EntityCollisionContext` 现在接受一个布尔值,表示是否用于放置方块 +- `net.minecraft.world.ticks.SavedTick` + - `loadTick`、`saveTick`、`save` -> `codec`,不是一对一 + - `loadTickList` -> `filterTickListForChunk`,不是一对一 + +### 移除列表 + +- `com.mojang.blaze3d.vertex.BufferUploader` +- `net.minecraft.core.Rotations#getWrapped*` +- `net.minecraft.network.chat.ComponentSerialization#FLAT_CODEC` +- `net.minecraft.network.protocol.game` + - `ClientboundAddExperimentOrbPacket` + - `ClientGamePacketListener#handleAddExperienceOrb` +- `net.minecraft.resources.ResourceLocation$Serializer` +- `net.minecraft.server.network.ServerGamePacketListenerImpl#addPendingMessage` +- `net.minecraft.world` + - `BossEvent$BossBarColor#byName`、`$BossBarOverlay#byName` + - `Clearable#tryClear` +- `net.minecraft.world.effect.MobEffectInstance#save`、`load` +- `net.minecraft.world.entity` + - `Entity` + - `isInBubbleColumn` + - `isInWaterRainOrBubble`、`isInWaterOrBubble` + - `newDoubleList`、`newFloatList` + - `recordMovementThroughBlocks` + - `EntityEvent#ATTACK_BLOCKED`、`SHIELD_DISABLED` + - `ItemBasedSteering` + - `addAdditionalSaveData`、`readAdditionalSaveData` + - `setSaddle`、`hasSadddle` + - `LivingEntity` + - `timeOffs`、`rotOffs` + - `rotA` + - `oRun`、`run` + - `animStep`、`animStep0` + - `appliedScale` + - `canBeNameTagged` + - `Mob` + - `DEFAULT_EQUIPMENT_DROP_CHANCE` + - `PRESERVE_ITEM_DROP_CHANCE_THRESHOLD`、`PRESERVE_ITEM_DROP_CHANCE` + - `NeutralMob#setLastHurtByPlayer` + - `PositionMoveRotation#ofEntityUsingLerpTarget` +- `net.minecraft.world.entity.ai.attributes.AttributeModifier#save`、`load` +- `net.minecraft.world.entity.animal` + - `Dolphin#setTreasurePos`、`getTreasurePos` + - `Fox$Variant#byName` + - `MushroomCow$Variant#byName` + - `Panda$Gene#byName` + - `Salmon$Variant#byName` + - `Turtle` + - `getHomePos` + - `setTravelPos`、`getTravelPos` + - `isGoingHome`、`setGoingHome` + - `isTravelling`、`setTravelling` +- `net.minecraft.world.entity.animal.armadillo.Armadillo$ArmadilloState#fromName` +- `net.minecraft.world.entity.npc.VillagerTrades#EXPERIMENTAL_WANDERING_TRADER_TRADES` +- `net.minecraft.world.entity.projectile.AbstractArrow#getBaseDamage` +- `net.minecraft.world.entity.raid.Raid` + - `getLevel`、`getId` + - `save` +- `net.minecraft.world.entity.vehicle.AbstractMinecart#hasCustomDisplay`、`setCustomDisplay` +- `net.minecraft.world.item.ItemStack#parseOptional`、`saveOptional` +- `net.minecraft.world.item.equipment.trim.TrimPattern#templateItem` +- `net.minecraft.world.level.Level#updateNeighborsAt(BlockPos, Block)` +- `net.minecraft.world.level.block.entity` + - `CampfireBlockEntity#dowse` + - `PotDecorations#save`、`load` +- `net.minecraft.world.level.levelgen.BelowZeroRetrogen#read` +- `net.minecraft.world.level.levelgen.structure.structures.RuinedPortalPiece$VerticalPlacement#byName` +- `net.minecraft.world.level.saveddata.maps.MapBanner#LIST_CODEC` +- `net.minecraft.world.scores.Team` + - `$CollisionRule#byName` + - `$Visibility#getAllNames`、`byName` +- `net.minecraft.world.ticks.LevelChunkTicks#save`、`load` diff --git a/primers-doc/1.21.6-from-1.21.5.md b/primers-doc/1.21.6-from-1.21.5.md new file mode 100644 index 0000000..8e83668 --- /dev/null +++ b/primers-doc/1.21.6-from-1.21.5.md @@ -0,0 +1,2203 @@ +# Minecraft 1.21.5 -> 1.21.6 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.5 迁移到 1.21.6。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +感谢: + +- @Soaryn 提供了一些 `ItemStack` 最佳实践和拼写修复 +- @earthcomputer 提供了绘制字符串时的颜色更改 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.6&tab=changelog) 中找到它们的列表。 + +## GUI 变更 + +GUI 渲染系统与之前的版本相比发生了重大变化。虽然表面上看起来有些相似,但实际的实现细节要复杂得多且迂回。本文将以高层次概述新系统,并提供一些基本示例。 + +### 准备与渲染 + +渲染 GUI 已分为两个阶段:“准备”和“渲染”。 + +准备阶段是我们通常认为的 GUI 渲染方法(例如,`Gui#render`、`AbstractContainerScreen#renderBg` 等)。这些方法现在不再实际渲染组件,而是将它们提交给 `GuiRenderState` 以存储在“渲染”阶段使用。`GuiRenderState` 存储在 `GameRenderer` 上,并传递给 `GuiGraphics`,后者使用前述方法将所需对象添加到渲染状态。 + +渲染阶段实际处理所有对象的渲染。这是通过 `GuiRenderer` 处理的,它从 `GuiRenderState` 读取数据,并在经过一些委托的准备和排序后,将对象渲染到屏幕上。`GuiRenderer` 也存储在 `GameRenderer` 上。 + +### 元素排序 + +现在,元素是如何渲染到屏幕上的?在之前的版本中,这主要基于渲染调用的顺序。然而,新系统使用两种不同的排序方法。第一种方法使用层和线性树来处理深度。第二种方法基于三个因素(按优先级顺序)使用比较器:`ScreenRectangle` 剪裁、`RenderPipeline` 顺序和 `TextureSetup` 的哈希码。这些都存储在通过 `GuiRenderState#submitGuiElement` 传递给 `GuiRenderState` 的 `GuiElementRenderState` 上。 + +一旦元素排序完毕,它们将从 Z 值为 `0` 开始渲染,每个元素比前一个元素向前渲染 `0.01`。 + +作为警告,由于元素排序顺序,某些自定义配置可能导致屏幕渲染不正确。因此,您必须理解这些方法及其分配的值在排序元素时的工作原理。 + +#### 层与树 + +第一种排序方法处理对象渲染的 z 深度。 + +首先从线性树开始。顾名思义,它基本上是一个双向链接的 `GuiRenderState$Node` 列表。每个节点包含自己的要渲染的元素列表。使用 `GuiRenderState#up` 或 `down` 来导航节点列表,每个方法要么获取下一个节点,要么如果不存在则创建一个新节点。给定树中的节点从底部到顶部渲染,这意味着 `down` 将在当前节点之前渲染任何提交的元素,而 `up` 将在当前节点之后渲染任何提交的元素。 + +提交元素时,使用其 `ScreenArea` 自动计算元素被添加到哪个节点。`ScreenArea` 定义了要渲染的元素的 `bounds`。本质上,如果元素的边界与当前节点上的任何其他元素相交,则将在 `up` 方向添加一个节点。 + +然后是层(strata)。一个层本质上是一个线性树。层按照它们被创建的顺序渲染,这意味着调用 `nextStratum` 将在前一层之后渲染所有元素。如果您想将元素分组到特定层中,可以使用此功能。注意:您无法导航到之前的层。 + +#### 比较器 + +比较器处理层中线性树内给定节点中的元素排序。 + +##### 屏幕矩形剪裁 + +`ScreenRectangle` 就是允许元素绘制的区域,通过 `GuiElementRenderState#scissorArea` 存储。没有指定 `ScreenRectangle` 的元素将首先排序,然后是最小 Y、最大 Y、最小 X 和最大 X。 + +##### 渲染管线 + +`RenderPipeline` 定义了用于将某些对象渲染到屏幕的管线,包括其着色器、格式、统一变量等。这通过 `GuiElementRenderState#pipeline` 存储。管线按照它们构建的顺序排序。这意味着,如果在同一层和剪裁矩形上,`RenderPipelines#ENTITY_TRANSLUCENT` 将在 `RenderPipelines#GUI` 之前渲染。由于这是一个依赖于类加载常量的系统,如果您想添加新元素,请确保您的模组加载器支持某种动态管线排序。 + +##### 纹理哈希码 + +`TextureSetup` 定义了在渲染管线中使用的 `Sampler0`、`Sampler1` 和 `Sampler2`,通过 `GuiElementRenderState#textureSetup` 存储。没有纹理的元素将首先排序,然后是记录对象的 `getSortKey`。注意,目前这返回 `TextureSetup` 的 `hashCode`,由于 `GpuTextureView` 不实现 `hashCode`,而是依赖于身份对象哈希,这可能是不确定的。 + +### GuiElementRenderState + +现在我们理解了排序,我们一直在使用的 `GuiElementRenderState` 到底是什么?实际上,渲染到屏幕的每个对象都由一个 `GuiElementRenderState` 表示,从库存菜单中看到的玩家到每个单独的物品。一个 `GuiElementRenderState` 定义了四个方法。首先,是用于排序以及如何渲染到屏幕的常用方法(`pipeline`、`scissorArea`、`textureSetup`、`bounds`)。然后是 `buildVertices`,它接受 `VertexConsumer` 来写入顶点和 z 深度。对于 GUI,这通常调用 `VertexConsumer#addVertexWith2DPose`。 + +原版提供了三种类型的 `GuiElementRenderState`:`BlitRenderState`、`ColoredRectangleRenderState`、`GlyphEffectRenderState` 和 `GlyphRenderState`。`ColoredRectangleRenderState` 和 `GlyphRenderState`/`GlyphEffectRenderState` 分别是处理基本颜色矩形和文本字符的简单情况。`BlitRenderState` 覆盖所有其他情况,因为几乎所有方法最终都会写入一个 `GpuTexture`,然后由它消费。 + +`GuiElementRenderState` 通过 `GuiRenderState$Node#submitGuiElement` 添加到 `GuiRenderState`。`GuiRenderState` 使 `submitBlitToCurrentLayer` 和 `submitGlyphToCurrentLayer` 可用于添加纹理和字形。例如,这些由 `GuiGraphics#*line`、`fill` 和 `blit*` 方法调用。 + +#### GuiItemRenderState + +`GuiItemRenderState` 是一个特殊情况,用于将物品渲染到屏幕。它接受物品的字符串化名称、当前姿势、其 XY 坐标及其剪裁区域。它持有的 `ItemStackRenderState` 定义了物品的渲染方式。当 `ClientItem$Properties#oversizedInGui` 为 true 时,渲染边界基于物品模型边界框计算;否则,当为 false 时,使用 16x16 的正方形。 + +就在“渲染”阶段之前,`GuiRenderer` 有效地将 `GuiItemRenderState` 转换为 `GuiElementRenderState`,更具体地说是 `BlitRenderState`。这是通过构造一个物品图集 `GpuTexture` 来完成的,物品被绘制到该纹理上,然后该纹理作为 `BlitRenderState` 提交。所有 `GuiItemRenderState` 都使用 `RenderPipelines#GUI_TEXTURED_PREMULTIPLIED_ALPHA`。 + +`GuiElementRenderState` 通过 `GuiRenderState#submitItem` 添加到 `GuiRenderState`。这由 `GuiGraphics#render*Item*` 方法调用。 + +#### GuiTextRenderState + +`GuiTextRenderState` 是一个特殊情况,用于将文本渲染到屏幕。它接受 `Font`、`FormattedCharSequence`、当前姿势、其 XY 坐标、颜色、背景颜色、是否有阴影及其渲染边界。 + +就在“渲染”阶段之前,`GuiRenderer` 将 `GuiTextRenderState` 转换为 `GuiElementRenderState`,更具体地说是 `GlyphRenderState` 或 `GlyphEffectRenderState`,具体取决于通过 `GuiTextRenderState#ensurePrepared` 应渲染的内容。这执行与物品渲染状态类似的过程,其中文本被写入 `GpuTexture` 以供消费。任何背景首先被渲染,然后是背景效果,然后是字符,最后是前景效果。 + +`GuiElementRenderState` 通过 `GuiRenderState#submitText` 添加到 `GuiRenderState`。这由 `GuiGraphics#draw*String` 和 `render*Tooltip` 方法调用。 + +#### 画中画 + +画中画是一个特殊情况,用于将任意对象渲染到 `GpuTexture`,然后传递给 `BlitRenderState`。画中画由两个组件组成:`PictureInPictureRenderState` 和 `PictureInPictureRenderer`。 + +`PictureInPictureRenderState` 是一个接口,可以存储一些用于将对象渲染到纹理的数据。默认情况下,它必须提供最小和最大 XY 坐标、纹理缩放、其剪裁区域及其渲染边界。您还可以选择通过 `pose` 指定变换矩阵。任何其他数据都可以由实现者添加。 + +```java +public record ExamplePIPRenderState(boolean data, int x0, int x1, int y0, int y1, float scale, @Nullable ScreenRectangle scissorArea, @Nullable ScreenRectangle bounds) + implements PictureInPictureRenderState {} +``` + +`PictureInPictureRenderer` 是将数据写入 `GpuTexture` 的渲染器。它利用这样一个事实:如果 `RenderSystem#output*Override` 纹理不为 null,则它们会被写入 `BufferSource`。一个 `PictureInPictureRenderer` 方法必须实现三个方法。`getRenderStateClass` 返回 `PictureInPictureRenderState` 实现的类。`getTextureLabel` 返回用于调试的纹理标签。`renderToTexture` 是调用渲染逻辑以将数据写入纹理的地方。 + +```java +public class ExamplePIPRenderer extends PictureInPictureRenderer { + + // 接受来自 `RenderBuffers` 的缓冲区源 + public ExamplePIPRenderer(MultiBufferSource.BufferSource bufferSource) { + super(bufferSource); + } + + @Override + public Class getRenderStateClass() { + // 渲染状态的类 + return ExamplePIPRenderState.class; + } + + @Override + protected void renderToTexture(ExamplePIPRenderState renderState, PoseStack pose) { + // 在此处渲染您想要的任何内容 + // 您可以通过 `this.bufferSource` 使用缓冲区源 + } + + @Override + protected void blitTexture(ExamplePIPRenderState renderState, GuiRenderState guiState) { + // 如果您想更改图层提交到渲染状态的方式,可以覆盖此方法 + // 默认情况下,这使用 `BlitRenderState` + + // 如果您想自己处理提交,请删除此项 + super.blitTexture(renderState, guiState); + } + + @Override + protected boolean textureIsReadyToBlit(ExamplePIPRenderState renderState) { + // 当为 true 时,将跳过设置纹理和投影矩阵,并使用当前可用的任何内容 + return false; + } + + @Override + protected String getTextureLabel() { + // 这可以是任何内容,但应该是唯一的 + return "examplemod: example pip"; + } +} +``` + +要能够使用渲染器,必须将其添加到 `GuiRenderer#pictureInPictureRenderers`。由于构造函数接受一个不可变列表,而渲染器存储一个不可变映射,请使用您的模组加载器提供的任何方法。 + +```java +// 我们将假设: +// - `GuiRenderer#bufferSource` 是可访问的 +// - 映射是可变的 +// 对于某个 GuiRenderer renderer + +var examplePIP = new ExamplePIPRenderer(renderer.bufferSource); + +renderer.pictureInPictureRenderers.put( + examplePIP.getRenderStateClass(), + examplePIP +); +``` + +`PictureInPictureRenderState` 通过 `GuiRenderState#submitPicturesInPictureState` 添加到 `GuiRenderState`。这由 `GuiGraphics#submit*RenderState` 方法调用。 + +### 逻辑变更 + +`GuiGraphics#drawString` 现在允许 `int` 颜色参数接受 alpha 通道(高八位),从而与其他 `GuiGraphics` 方法保持一致。这意味着任何没有指定 alpha 的颜色字符串将不会被渲染。可以通过将您的 `int` 颜色与 `0xFF000000` 进行 OR 操作,或者将您的颜色传递给 `ARGB#opaque` 来复制以前的行为。 + +### 上下文栏 + +屏幕上的许多 HUD 元素会占据经验条渲染的位置。然而,这些元素如何渲染以及哪个元素被优先考虑是通过上下文栏系统处理的。一个上下文栏由两部分组成:`ContextualBarRenderer`,负责将栏渲染到屏幕;以及 `Gui$ContextualInfo`,用作栏的标识符。 + +一个 `ContextualBarRenderer` 需要实现两个方法:`renderBackground`,用于渲染栏本身;以及 `render`,用于渲染出现在栏顶部的元素。 + +```java +public class ExampleContextualBarRenderer implements ContextualBarRenderer { + + @Override + public void renderBackground(GuiGraphics graphics, DeltaTracker delta) { + // 在此处渲染背景栏精灵 + } + + @Override + public void render(GuiGraphics graphics, DeltaTracker delta) { + // 渲染任何可能位于栏顶部的元素 + } +} +``` + +`Gui$ContextInfo` 是一个枚举,因此您的模组加载器需要允许扩展或某种其他方法来标识栏渲染器。然后,需要将渲染器添加到存储在 `Gui#contextualInfoBarRenderers` 中的渲染器映射中。由于这是一个不可变映射,请使用您的模组加载器提供的任何方法。 + +```java +// 我们将假设: +// - 创建了一个新的枚举值 `EXAMPLEMOD_EXAMPLE` +// - 映射是可变的 +// 对于某个 Gui gui +gui.contextualInfoBarRenderers.put( + EXAMPLEMOD_EXAMPLE, + ExampleContextualBarRenderer::new +); +``` + +最后,为了让您的栏被调用并优先考虑,您需要修改 `Gui#nextContextualInfoState` 以返回您的枚举值,或者使用您的模组加载器提供的任何方法。 + +### 对话框 + +为了更通用地处理提供某些基本功能的屏幕——例如确认屏幕、按钮选择或用户输入,原版提供了一个通用的对话框系统。这些对话框可以在数据包中构建,并通过调用 `Player#openDialog` 在客户端上传递。基本的 JSON 描述记录在 [Minecraft Snapshot 25w20a](https://www.minecraft.net/en-us/article/minecraft-snapshot-25w20a) 中。 + +快速概述,一个基本的 `Dialog` 包含以下组件:一个标题、一个可选的用于导航的外部标题、是否可以通过按“esc”关闭屏幕,以及它的 `DialogBody` 内容。其他所有内容由对话框本身决定,但它具有通过 `InputControl` 进行用户输入的功能。按钮通常通过 `ClickAction` 添加,它包含常见的按钮数据和点击时要执行的事件。如果对话框被取消(例如,关闭),则运行 `onCancel`。 + +`DialogBody` 和 `InputControl` 只是没有定义结构的通用接口。实现它们,或者任何对话框,都需要在客户端和服务器上向可用类型进行一些注册。 + +#### 自定义主体 + +一个 `DialogBody` 就是对话框屏幕的内容。这通常是应该始终显示的不可变信息。每个对话框都持有一个这些 `DialogBody` 的列表,因为每个屏幕都有一些文本来解释它的用途。它只包含一个方法 `mapCodec`,用作主体信息的注册表键和编码器。 + +```java +// 显然,一个主体会有实际内容,但这只是一个示例实现 +public record ExampleDialogBody(boolean val1, int val2) implements DialogBody { + public static final MapCodec BODY_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.BOOL.fieldOf("val1").forGetter(ExampleDialogBody::val1), + Codec.INT.fieldOf("val2").forGetter(ExampleDialogBody::val2) + ).apply(ExampleDialogBody::new)); + + @Override + public MapCodec mapCodec() { + return BODY_CODEC; + } +} + +// 将编解码器注册到注册表 +Registry.register(BuiltInRegistries.DIALOG_BODY_TYPE, "examplemod:example_body", ExampleDialogBody.BODY_CODEC); +``` + +```json5 +// 对于某个对话框 +{ + // ... + "body": [ + { + // 我们的主体 + "type": "examplemod:example_body", + "val1": true, + "val2": 0 + }, + // ... + ] +} +``` + +那么这个主体实际上是如何渲染的呢?这是通过 `DialogBodyHandler` 完成的。它包含一个单一方法 `createControls`,用于生成给定 `DialogScreen` 和对话框数据的 `LayoutElement` 进行渲染。这个主体处理程序可以通过 `DialogBodyHandlers#register` 链接到 `MapCodec`。 + +```java +public class ExampleDialogBodyHandler implements DialogBodyHandler { + + @Override + public LayoutElement createControls(DialogScreen screen, ExampleDialogBody body) { + // 创建元素(小部件、布局等) + return new StringWidget(...); + } +} + +// 注意 `register` 不是公开的,因此需要访问扩宽 +DialogBodyHandlers.register(BODY_CODEC, new ExampleDialogBodyHandler()); +``` + +#### 自定义输入 + +一个 `InputControl` 表示用户可以提供的某些输入,无论是文本还是按钮提交点击。这通常由可以根据状态接受或提供某些字符串值或标签的组件组成。所有对话框都可以通过 `DialogControlSet` 提供这些输入。它只包含一个方法 `mapCodec`,用作输入控件的注册表键和编码器。 + +```java +public record ExampleInputControl(int value) implements InputControl { + public static final MapCodec INPUT_CODEC = Codec.INT.fieldOf("value").xmap( + ExampleInputControl::new, ExampleInputControl::value + ); + + @Override + public MapCodec mapCodec() { + return INPUT_CODEC; + } +} + +// 将编解码器注册到注册表 +Registry.register(BuiltInRegistries.INPUT_CONTROL_TYPE, "examplemod:example_input", ExampleInputControl.INPUT_CODEC); +``` + +```json5 +// 对于某个对话框(假设 `minecraft:simple_input_form`) +{ + "inputs": [ + { + // 我们的输入 + "type": "examplemod:example_input", + + // 此输入数据的标识符 + "key": "example_input", + + "value": 0 + }, + // ... + ] +} +``` + +与上面一样,输入通过 `InputControlHandler` 渲染。它包含一个单一方法 `addControl`,提供 `Screen` 和输入控件,并通过 `$Output` 创建 `LayoutElement` 及其关联的 `Action$ValueGetter`。这个输入处理程序可以通过 `InputControlHandlers#register` 链接到 `MapCodec`。 + +```java +public class ExampleInputControlHandler implements InputControlHandler { + + @Override + public void addControl(ExampleInputControl control, Screen screen, InputControlHandler.Output output) { + EditBox box = new EditBox(...); + box.setValue(String.valueOf(control.value())); + + // 将元素添加到输出 + output.accept( + // 要渲染的元素 + box, + // 输入的值输出 + Action.ValueGetter.of(box::getValue) + ); + } +} + +// 注意 `register` 不是公开的,因此需要访问扩宽 +InputControlHandlers.register(INPUT_CODEC, new ExampleInputControlHandler()); +``` + +### 自定义动作 + +如上所示,输入可以提供一些值传递给 `Action`。前者被称为 `$ValueGetter`,它基本上获取一个字符串化或标记的输入以供使用。后者则最终创建发送到服务器的 `ClickEvent`。这些通常通过 `ActionButton` 创建,它定义了一些常见的按钮数据以及要执行的 `Action`。 + +一个自定义动作包含两个方法:一个返回在编码期间使用的 `MapCodec`(`codec`),另一个根据输入字符串到其 `$ValueGetter` 的映射创建 `ClickEvent`。 + +```java +public record ExampleAction() implements Action { + public static final MapCodec ACTION_CODEC = MapCodec.unit(new ExampleAction()); + + @Override + public MapCodec codec() { + return ACTION_CODEC; + } + + @Override + public Optional createAction(Map keysToGetters) { + // 处理如何将键输入映射映射到某个点击事件 + return Optional.empty(); + } +} + +// 将编解码器注册到注册表 +Registry.register(BuiltInRegistries.DIALOG_ACTION_TYPE, "examplemod:example_action", ExampleAction.ACTION_CODEC); +``` + +```json5 +// 对于某个对话框(假设 `minecraft:notice`) +{ + "action": { + // 按钮数据 + "label": "示例!", + "tooltip": "这是一个示例!", + "width": 80, + + // 要执行的动作 + "action": { + // 我们的动作类型 + "type": "examplemod:example_action" + } + } +} +``` + +根据您在下面实现 `Dialog` 的方式,动作按钮将自动添加到屏幕,或者您需要通过 `DialogControlSet#createActionButton` 在其中一个方法中添加它: + +```java +// 对于某个 DialogScreen 实现,我们假设某个 `SimpleDialog` +@Override +protected void updateHeaderAndFooter(HeaderAndFooterLayout layout, DialogControlSet controls, SimpleDialog dialog, DialogConnectionAccess access) { + dialog.mainActions().forEach(actionButton -> layout.addToFooter(controls.createActionButton(actionButton).build())); +} +``` + +#### 自定义对话框 + +一个 `Dialog` 基本上就是上述所有组件的组合,按需组合。由用户决定如何实现它们。每个 `Dialog` 必须提供其 `CommonDialogData`,它定义了基本标题、主体内容和功能。此外,一个 `Dialog` 可以选择在关闭时通过 `onCancel` 执行一个 `Action`。 + +```java +// `common` 已由记录实现 +public record ExampleDialog(CommonDialogData common, boolean val1, int val2) implements Dialog { + public static final MapCodec DIALOG_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + CommonDialogData.MAP_CODEC.forGetter(ExampleDialog::common), + Codec.BOOL.fieldOf("val1").forGetter(ExampleDialog::val1), + Codec.INT.fieldOf("val2").forGetter(ExampleDialog::val2) + ).apply(ExampleDialog::new)); + + @Override + public MapCodec codec() { + return DIALOG_CODEC; + } + + @Override + public Optional onCancel() { + // 您可以选择在此处返回一些内容,或者如果什么都不做则返回空 + return Optional.empty(); + } +} + +// 将编解码器注册到注册表 +Registry.register(BuiltInRegistries.DIALOG_TYPE, "examplemod:example_dialog", ExampleDialog.DIALOG_CODEC); +``` + +```json5 +// 对于 `data/examplemod/dialog/example.json` 中的某个对话框 +{ + // 我们的对话框类型 + "type": "examplemod:example_dialog", + + // 常见按钮数据 + "title": "示例对话框!", + "can_close_with_escape": true, + "after_action": "wait_for_response", + + // 我们的自定义参数 + "val1": true, + "val2": 0 +} +``` + +与其他一样,对话框类型只能通过 `DialogScreen$Factory` 渲染。这个接口构造一个 `DialogScreen`,给定父 `Screen`、`Dialog` 实例和用于与服务器通信的 `DialogConnectionAccess`。然后可以通过 `DialogScreens#register` 将这个对话框屏幕链接到 `MapCodec`。 + +```java +public class ExampleDialogScreen extends DialogScreen { + + public ExampleDialogScreen(@Nullable Screen previousScreen, ExampleDialog dialog, DialogConnectionAccess connectionAccess) { + super(previousScreen, dialog, connectionAccess); + } + + // 您可以根据需要选择实现其他方法 + // 有关示例,请参阅现有的对话框屏幕 + + @Override + protected void populateBodyElements(LinearLayout layout, DialogControlSet controls, ExampleDialog dialog, DialogConnectionAccess access) { + // 将元素和动作添加到屏幕的主体(通常是中心) + } + + @Override + protected void updateHeaderAndFooter(HeaderAndFooterLayout layout, DialogControlSet controls, ExampleDialog dialog, DialogConnectionAccess access) { + // 将元素和动作添加到屏幕的页眉和页脚(顶部和底部) + } +} + +// 注意 `register` 不是公开的,因此需要访问扩宽 +DialogScreens.register(DIALOG_CODEC, ExampleDialogScreen::new); +``` + +- `com.mojang.blaze3d.vertex.VertexConsumer#addVertexWith2DPose` - 添加一个要在 2D 空间中渲染的顶点。 +- `net.minecraft.client.gui` + - `Font` + - `drawInBatch` 不再返回任何内容 + - `prepareText` - 准备要渲染到屏幕的文本。 + - `splitIgnoringLanguage` - 按提供的顺序拆分文本,而不经过语言处理程序处理。 + - `ALPHA_CUTOFF` 已移除 + - `$GlyphVisitor` - 一个处理要渲染的字形或效果的接口。 + - `$PreparedText` - 一个接口,通过访问每个字形来定义应如何渲染一段文本。 + - `$StringRenderOutput` -> `$PreparedTextBuilder`,不再接受 `MultiBufferSource`、`Matrix4f`、`$DisplayMode`、打包的光照坐标和逆深度布尔值 + - `Gui` + - `shouldRenderDebugCrosshair` - 如果应渲染调试准星,则返回 true。 + - `$RenderFunction` - 一个接口,定义如何在 GUI 上渲染某个部分或元素。 + - `GuiGraphics` 现在接受一个 `GuiRenderState` 而不是 `MultiBufferSource$BufferSource` + - `MAX_GUI_Z`、`MIN_GUI_Z` 已移除 + - `pose` 现在返回一个 `Matrix3x2fStack` + - `flush` 已移除 + - `nextStratum` - 添加另一个层,用于遍历在先前树中的所有节点之后渲染。 + - 所有方法不再接受 `RenderType`、`VertexConsumer` 或 `Function`,而是根据调用指定 `RenderPipeline` 和 `TextureSetup` + - `drawString`、`drawStringWithBackdrop` 不再返回任何内容 + - `draw*String` 方法现在必须传入一个 ARGB 值,其中 A 不能为 0。 + - `renderItem(ItemStack, int, int, int, int)` 已移除 + - `drawSpecial` 已移除,根据特殊情况由单独的 `submit*RenderState` 取代 + - `blurBeforeThisStratum` - 通知渲染状态,模糊效果应在该层和所有先前渲染的层之间渲染。每帧只能应用一次。 + - `render*Tooltip` -> `set*TooltipForNextFrame`,不直接添加到渲染状态,而是在不存在或被覆盖时等待调用 `renderDeferredTooltip` + - `renderDeferredTooltip` - 添加要在新层上渲染的工具提示信息。 + - `blitSprite` 现在有一个接受浮点 R 色调颜色的重载 + - `$ScissorStack#peek` - 获取堆栈上的最后一个矩形。 + - `LayeredDraw` 类已移除 +- `net.minecraft.client.gui.components` + - `AbstractTextAreaWidget` 现在有一个重载,接受两个额外的布尔值,表示是否显示背景或装饰 + - `AbstractWidget#getTooltip` 已移除 + - `CycleButton$Builder#displayOnlyValue` 现在有一个接受 `boolean` 设置的重载 + - `EditBox` + - `setCentered` - 设置文本位置是否应居中。 + - `setTextShadow` - 设置文本是否应有阴影效果。 + - `FocusableTextWidget` 现在可以接受一个布尔值,表示是否填充背景 + - `DEFAULT_PADDING` 现在是公开的 + - `ImageWidget#updateResource` - 更新组件上图像的精灵。 + - `ItemDisplayWidget` - 一个显示 `ItemStack` 的小部件。 + - `LogoRenderer#keepLogoThroughFade` - 当为 true 时,即使标题屏幕淡出,也保持徽标可见。 + - `MultiLineEditBox` 现在是包私有的,应通过 `builder` 构造,调用 `$Builder#set*` 方法 + - `setLineLimit` - 设置文本字段的行限制。 + - `setValue` 现在有一个接受 `boolean` 的重载,用于确定是否应强制通过最大行限制 + - `MultiLineLabel` + - `getStyleAtCentered` - 计算居中文本的组件样式。 + - `getStyleAtLeftAligned` - 计算左对齐文本的组件样式。 + - `MultilineTextField#NO_CHARACTER_LIMIT` -> `NO_LIMIT` + - `setLineLimit` - 设置文本字段上可以写入的最大行数。 + - `hasLineLimit` - 返回文本字段是否有行限制。 + - `setValue` 现在有一个接受 `boolean` 的重载,用于确定是否应强制通过最大行限制 + - `MultiLineTextWidget#configureStyleHandling` - 设置在悬停组件时是否显示某些内容以及点击时的操作。 + - `ScrollableLayout` - 一个具有某些定义边界并且可以滚动的布局。 + - `SplashRenderer#render` 现在接受一个浮点数作为 R 颜色,而不是 int + - `WidgetTooltipHolder#refreshTooltipForNextRenderPass` 现在接受 `GuiGraphics` 和 XY 位置 +- `net.minecraft.client.gui.components.spectator.SpectatorGui#renderTooltip` -> `renderAction` +- `net.minecraft.client.gui.components.tabs` + - `LoadingTab` - 一个表示当前正在加载信息的标签页。 + - `Tab#getTabExtraNarration` - 返回标签页的提示旁白。 + - `TabManager` 现在可以接受两个 `Consumer`,用于在选中或取消选中标签页时执行的操作 + - `TabNavigationBar` + - `getTabs` - 返回导航栏上的标签页列表。 + - `setTabActiveState` - 设置给定标签页索引的活动状态。 + - `setTabTooltip` - 设置给定标签页索引的工具提示信息。 +- `net.minecraft.client.gui.components.toasts` + - `NowPlayingToast` - 一个显示当前正在播放的背景音乐的吐司。 + - `Toast` + - `xPos`、`yPos` - 获取相对于当前吐司索引的 x 和 y 位置。 + - `onFinishedRendering` - 吐司在屏幕上完成渲染后调用的方法。 + - `ToastManager` 现在接受 `Options` + - `showNowPlayingToast`、`hideNowPlayingToast`、`createNowPlayingToast`、`removeNowPlayingToast` - 处理“正在播放”音乐吐司。 + - `$ToastInstance#resetToast` - 重置吐司。 +- `net.minecraft.client.gui.contextualbar` + - `ContextualBarRenderer` - 一个接口,定义了一个具有某些要渲染的背景的对象。 + - `ExperienceBarRenderer` - 绘制经验条。 + - `JumpableVehicleBarRenderer` - 绘制跳跃力量条(例如,马跳跃)。 + - `LocatorBarRenderer` - 绘制指向给定路径点的条。 +- `net.minecraft.client.gui.font.GlyphRenderTypes` 现在接受一个用于 GUI 渲染的 `RenderPipeline` +- `net.minecraft.client.gui.font.glyphs.BakedGlyph` 现在接受一个 `GpuTextureView` + - `extractEffect` - 提取一个字形效果(例如阴影)并提交要渲染的元素。 + - `extractBackground` - 提取一个字形效果并提交要在背景 Z 上渲染的元素。 + - `left`、`top`、`right`、`bottom` - 计算字形的边界。 + - `textureView` - 返回构成字形的纹理视图。 + - `guiPipeline` - 返回渲染字形的管线。 + - `renderChar`、`renderEffect` 现在接受一个额外的布尔值,当为 true 时将 Z 偏移设置为 `0`,为 false 时设置为 `0.001` + - `$Effect#left`、`top`、`right`、`bottom` - 计算字形效果的边界。 + - `$GlyphInstance#left`、`top`、`right`、`bottom` - 计算字形实例的边界。 +- `net.minecraft.client.gui.navigation.ScreenRectangle` + - `transformAxisAligned` 现在接受 `Matrix3x2f` 而不是 `Matrix4f` + - `intersects` - 返回此矩形是否与另一个矩形重叠。 + - `encompasses` - 返回此矩形是否完全包含另一个矩形。 + - `transformMaxBounds` - 返回一个新矩形,该矩形通过提供的矩阵移动到给定位置。 +- `net.minecraft.client.gui.render` + - `GuiRenderer` - 一个将所有提交的元素渲染到屏幕的类。 + - `TextureSetup` - 一个记录,指定在渲染管线中使用的采样器 0-2。前两个纹理视图是任意的,第三个用于当前的光照图纹理。 +- `net.minecraft.client.gui.render.pip` + - `GuiBannerResultRenderer` - 旗帜结果预览的渲染器。 + - `GuiBookModelRenderer` - 附魔屏幕中书模型的渲染器。 + - `GuiEntityRenderer` - 给定实体的渲染器。 + - `GuiProfilerChartRenderer` - 分析器图表的渲染器。 + - `GuiSignRenderer` - 编辑屏幕中告示牌背景的渲染器。 + - `GuiSkinRenderer` - 具有给定皮肤的玩家的渲染器。 + - `OversizedItemRenderer` - 用于物品模型应渲染得比其物品槽位大时的渲染器。 + - `PictureInPictureRenderer` - 一个抽象类,用于渲染非标准 2D 元素、物品或文本的动态元素。 +- `net.minecraft.client.gui.render.state` + - `BlitRenderState` - 用于基本 2D 纹理 blit 的元素状态。 + - `ColoredRectangleRenderState` - 用于带有色调的简单矩形的元素状态。 + - `GlyphEffectRenderState` - 用于字形效果(例如,字体文本阴影)的元素状态。 + - `GlyphRenderState` - 用于字形(字体文本)的元素状态。 + - `GuiElementRenderState` - 一个表示要渲染的元素状态的接口。 + - `GuiItemRenderState` - 一个表示要渲染的物品状态的记录。 + - `GuiRenderState` - 要渲染到屏幕的 GUI 的状态。 + - `GuiTextRenderState` - 一个表示文本及其渲染位置的记录。 + - `ScreenArea` - 一个定义元素渲染区域的接口。这不会影响剪裁,而是影响层顺序。 +- `net.minecraft.client.gui.render.state.pip` + - `GuiBannerResultRenderState` - 旗帜结果预览的状态。 + - `GuiBookModelRenderState` - 附魔屏幕中书模型的状态。 + - `GuiEntityRenderState` - 给定实体的状态。 + - `GuiProfilerChartRenderState` - 分析器图表的状态。 + - `GuiSignRenderState` - 编辑屏幕中告示牌背景的状态。 + - `GuiSkinRenderState` - 具有给定皮肤的玩家的状态。 + - `OversizedItemRenderState` - 可以以任意大小渲染的物品模型的状态。 + - `PictureInPictureRenderState` - 一个定义将画中画渲染到屏幕所需基本状态的接口。 +- `net.minecraft.client.gui.screens` + - `ConfirmScreen` + - `layout` - 定义一个由八个单位间隔的垂直元素列表。 + - `yesButton`、`noButton` -> `yesButtonComponent`、`noButtonComponent` + - `yesButton`、`noButton` 现在表示实际的按钮 + - `addAdditionalText` - 在布局中的按钮之前添加任何附加文本。 + - `addButtons` - 在布局中添加按钮。 + - `PauseScreen` + - `rendersNowPlayingToast` - 返回是否应渲染“正在播放”吐司。 + - `onDisconnect` -> `disconnectFromWorld`,现在是公开和静态的;不是一对一 + - `Screen` + - `CUBE_MAP` -> `net.minecraft.client.renderer.GameRenderer#cubeMap` + - `PANORAMA` -> `net.minecraft.client.renderer.GameRenderer#panorama` + - `renderBlurredBackground` 现在接受 `GuiGraphics` + - `*TooltipForNextRenderPass` 方法要么已移除,要么移至 `GuiGraphics` + - `FADE_IN_TIME` - 表示某个元素淡入需要多少毫秒。 + - `fadeWidgets` - 设置作为屏幕子级添加的 `AbstractWidget` 的 alpha 值。 + - `handleClickEvent` - 处理点击组件时要播放的事件。 + - `defaultHandleClickEvent` - 点击组件时要执行的默认逻辑。 + - `clickUrlAction` - 点击 URL 时要执行的逻辑(具有打开 URL 样式)。 + - `clickCommandAction` - 应执行命令时要执行的逻辑(具有 * 命令样式)。 +- `net.minecraft.client.gui.screens.dialog` + - `ButtonListDialogScreen` - 一个包含一些按钮列表的对话框屏幕。 + - `DialogConnectionAccess` - 一个处理来自对话框的通用交互信息的客户端接口。 + - `DialogControlSet` - 对话框屏幕的输入处理程序。 + - `DialogListDialogScreen` - 一个指向其他对话框的按钮列表的 `DialogListDialog`。 + - `DialogScreen` - 某个对话框模态的屏幕。 + - `DialogScreens` - 对话框模态到其关联屏幕的工厂注册表。 + - `MultiButtonDialogScreen` - 一个 `MultiActionDialog` 的按钮列表。 + - `ServerLinksDialogScreen` - 一个 `ServerLinksDialog` 的按钮列表。 + - `SimpleDialogScreen` - 某个简单对话框的对话框屏幕。 + - `WaitingForResponseScreen` - 处理客户端/服务器之间对话框提交的停机时间的屏幕。 +- `net.minecraft.client.gui.screens.dialog.body` + - `DialogBodyHandler` - 描述标题和动作/输入之间内容的主体元素列表。 + - `DialogBodyHandlers` - `DialogBody` 到其 `DialogBodyHandler` 的注册表。 +- `net.minecraft.client.gui.screens.dialog.input` + - `InputControlHandler` - 用户输入处理程序。 + - `InputControlHandlers`- `InputControl` 到其 `InputControlHandler` 的注册表。 +- `net.minecraft.client.gui.screens.inventory` + - `AbstractContainerScreen` + - `SLOT_ITEM_BLIT_OFFSET` 已移除 + - `renderContents` - 渲染库存的槽位和标签。 + - `renderCarriedItem` - 渲染持有的物品。 + - `renderSnapbackItem` - 渲染与持有物品交换的物品。 + - `$SnapbackData` - 保存关于拖拽或交换物品从持有位置移动到其槽位位置的信息。 + - `AbstractSignEditScreen#offsetSign` -> `getSignYOffset`,不是一对一 + - `BookEditScreen` + - `TEXT_*`、`IMAGE_*`、`BACKGROUND_TEXTURE_*` 现在是公开的 + - `BookSignScreen` - 用于签署书的屏幕。 + - `BookViewScreen` + - `closeScreen` -> `closeContainerOnServer`,不是一对一 + - `$BookAccess#getPage` 现在返回一个 `Component` + - `EffectsInInventory` + - `renderEffects` 现在是公开的 + - `renderTooltip` 已被重载为一个公共方法 + - `InventoryScreen#renderEntityInInventory` 不再接受 XY 偏移,而是接受 4 个 `int` 来表示要渲染到的区域 + - `ItemCombinerScreen#renderFg` 已移除 +- `net.minecraft.client.gui.screens.inventory.tooltip` + - `ClientTooltipComponent#renderText` 不再接受姿势和缓冲区,而是接受 `GuiGraphics` 来提交要渲染的文本 + - `TooltipRenderUtil#renderTooltipBackground` 不再接受 Z 偏移 +- `net.minecraft.client.gui.screens.multiplayer.ServerLinksScreen` 类已移除,被对话框模态取代 +- `net.minecraft.client.gui.screens.social` + - `PlayerEntry#refreshHasDraftReport` - 设置当前上下文是否具有针对此玩家的报告。 + - `SocialInteractionsPlayerList#refreshHasDraftReport` - 刷新当前上下文是否对所有玩家有报告。 +- `net.minecraft.client.gui.screens.worldselection.ExperimentsScreen$ScrollArea` 类已移除,被 `ScrollableLayout` 取代 +- `net.minecraft.client.model.geom.ModelPart#getExtentsForGui` - 获取表示已变换到适当空间的部件的向量集。 +- `net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl` + - `showDialog` - 显示当前对话框,动态创建屏幕。 + - `serverLinks` - 返回客户端可以访问的服务器链接条目。 + - `createDialogAccess` - 创建用于通信的客户端对话框处理程序。 + - `clearDialog` - 关闭当前对话框屏幕。 +- `net.minecraft.client.player.LocalPlayer#experienceDisplayStartTick` - 表示经验显示应被优先考虑的开始刻。 +- `net.minecraft.client.renderer` + - `GameRenderer` 不再接受 `ResourceManager` + - `ITEM_ACTIVATION_ANIMATION_LENGTH` 已移除 + - `setRenderHand` 已移除 + - `renderZoomed` 已移除 + - `getPanorama` - 返回全景渲染器。 + - `LightTexture#getTexture` -> `getTextureView`,不是一对一 + - `MapRenderer#WIDTH`、`HEIGHT` 现在是公开的 + - `PanoramaRenderer#registerTextures` - 注册纹理以供立方体贴图使用。 + - `PostPass$Input#texture` 现在返回 `GpuTextureView` 而不是 `GpuTexture` + - `RenderPipelines` + - `GUI_OVERLAY`、`GUI_GHOST_RECIPE_OVERLAY`、`GUI_TEXTURED_OVERLAY` 已移除 + - `GUI_TEXTURED_PREMULTIPLIED_ALPHA` - 一个假设纹理在合成阶段已经预乘了透明度的管线。 + - `RenderStateShard` + - `$Builder#add` 不再接受是否应模糊着色器 + - `$TextureStateShard` 不再接受设置模糊模式的 `TriState` + - `RenderType` + - `debugLine` 已移除 + - `gui`、`guiOverlay`、`guiTexturedOverlay`、`guiOpaqueTexturedBackground`、`guiNauseaOverlay`、`guiTextHighlight`、`guiGhostRecipeOverlay`、`guiTextured` 已移除 + - `vignette` 已移除 + - `crosshair` 已移除 + - `mojangLogo` 已移除 + - `ScreenEffectRenderer` 现在是一个实例实现,而不是仅仅一个静态方法 + - 构造函数接受当前的 `Minecraft` 实例和一个 `MultiBufferSource` + - `renderScreenEffect` 现在是一个实例方法,接受实体是否在睡觉以及部分刻 + - `resetItemActivation`、`displayItemActivation` - 处理物品自动激活时的情况(例如,图腾)。 +- `net.minecraft.client.renderer.blockentity` + - `*Renderer#getExtents` - 将所有模型表示的变换后的向量添加到一个集合中。 + - `HangingSignRenderer` + - `MODEL_RENDER_SCALE` 现在是公开的 + - `translateBase` 现在是公开的 + - `SignRenderer` + - `RENDER_SCALE` 现在是公开的 + - `applyInHandTransforms` - 变换堆栈以正确表示所持告示牌的位置。 + - `SkullBlockRenderer#getRenderType(SkullBlock$Type, ResolvableProfile, ResourceLocation)` -> `getSkullRenderType`、`getPlayerSkinRenderType`;不是一对一 +- `net.minecraft.client.renderer.entity.ItemRenderer` + - `GUI_SLOT_CENTER_X`、`GUI_SLOT_CENTER_Y`、`ITEM_DECORATION_BLIT_OFFSET` 已移除 + - `COMPASS_*` -> `SPECIAL_*` +- `net.minecraft.client.renderer.item` + - `ClientItem$Properties#oversizedInGui` - 当为 true 时,允许物品在 GUI 中渲染到 16x16 框之外;否则,将大小裁剪到框内。 + - `ItemStackRenderState` + - `setAnimated`、`isAnimated` - 返回物品是否有动画(例如,闪光)。 + - `appendModelIdentityElement`、`getModelIdentity`、`clearModelIdentity` - 处理正在渲染的标识组件。 + - `getModelBoundingBox` - 计算模型的边界框。 + - `setOversizedInGui`、`isOversizedInGui` - 处理物品何时可以根据其属性在 GUI 中过大。 +- `net.minecraft.client.renderer.special` + - `PlayerHeadSpecialRenderer` - 根据其渲染信息渲染玩家头颅。 + - `SkullSpecialRenderer` 现在实现 `NoDataSpecialModelRenderer`,不再接受模型或纹理覆盖,而是接受要使用的 `RenderType` + - `SpecialModelRenderer#getExtents` - 将表示此渲染器使用的所有模型的变换向量添加到一个集合中。 +- `net.minecraft.client.renderer.texture.TextureAtlasSprite#isAnimated` - 返回精灵是否有任何动画。 +- `net.minecraft.commands.arguments.ResourceOrIdArgument#dialog`、`getDialog`、`$DialogArgument` - 处理对话框屏幕的命令参数。 +- `net.minecraft.core.registries` + - `BuiltInRegistries#DIALOG_TYPE`、`DIALOG_ACTION_TYPE`、`INPUT_CONTROL_TYPE`、`DIALOG_BODY_TYPE` + - `Registries#DIALOG_TYPE`、`DIALOG_ACTION_TYPE`、`INPUT_CONTROL_TYPE`、`DIALOG_BODY_TYPE`、`DIALOG` +- `net.minecraft.data.tags.DialogTagsProvider` - 对话框的标签提供者。 +- `net.minecraft.network.chat` + - `ClientEvent` + - `$Action#SHOW_DIALOG`、`CUSTOM` + - `valueCodec` - 返回用于编码此动作的映射编解码器。 + - `$Custom` - 一个包含一些要发送到服务器的 nbt 有效负载的事件,目前什么都不做。 + - `$ShowDialog` - 一个显示指定对话框的事件。 + - `CommonComponents` + - `GUI_RETURN_TO_MENU` - 显示“返回菜单”文本的组件。 + - `disconnectButtonLabel` - 根据服务器是否是本地服务器返回组件。 +- `net.minecraft.network.protocol.common` + - `ClientboundClearDialogPacket` - 关闭当前对话框屏幕。 + - `ClientboundShowDialogPacket` - 打开一个新的对话框屏幕。 + - `ServerboundCustomClickActionPacket` - 向服务器发送一个自定义动作。 +- `net.minecraft.server.dialog` + - `ActionButton` - 一个可以在点击时执行某些 `Action` 的按钮。 + - `ButtonListDialog` - 一个具有若干列可点击按钮的对话框模态。 + - `CommonButtonData` - 与对话框模态中每个按钮关联的数据。 + - `CommonDialogData` - 与每个对话框模态关联的数据。 + - `ConfirmationDialog` - 一个可以选择是或否的对话框模态。 + - `Dialog` - 定义某个对话框模态的基础接口。 + - `DialogAction` - 通常在点击某个动作按钮后执行的动作。 + - `DialogListDialog` - 一个指向其他对话框的按钮的可滚动列表。 + - `Dialogs` - 一个注册 `Dialog` 的数据包引导程序。 + - `DialogTypes` - 用于编码某个对话框模型的映射编解码器注册表。 + - `Input` - 一个将某个键映射到 `InputControl` 的处理程序。 + - `MultiActionDialog` - 一个按列排列的动作的可滚动列表。 + - `NoticeDialog` - 一个在页脚中只有一个动作的简单屏幕。 + - `ServerLinksDialog` - 一个从服务器接收的链接的可滚动列表,按列排列。 + - `SimpleDialog` - 一个定义可以采取的主要动作的对话框。 +- `net.minecraft.server.dialog.action` + - `Action` - 对某些输入执行的通用操作,通常是按钮点击。 + - `ActionTypes` - 用于编码某个动作的映射编解码器注册表。 + - `CustomTemplate` - 构建一个命令并请求服务器运行它。 + - `CustomAll` - 从所有输入构建一个自定义服务器点击动作,并请求服务器运行它。 + - `ParsedTemplate` - 一个编码某些字符串的模板,类似于函数宏的工作方式。 + - `StaticAction` - 一个在激活时触发 `ClickEvent` 的动作。 +- `net.minecraft.server.dialog.body` + - `DialogBody` - 描述标题和动作/输入之间内容的主体元素。 + - `DialogBodyTypes` - 用于编码某个主体的映射编解码器注册表。 + - `ItemBody` - 一个带有可选描述的物品。 + - `PlainMessage` - 一个多行标签。 +- `net.minecraft.server.dialog.input` + - `BooleanInput` - 一个带有标签的普通复选框。 + - `InputControl` - 一个接受用户输入的控制机制。 + - `InputControlTypes` - 用于编码某个输入控件的映射编解码器注册表。 + - `NumberRangeInput` - 一个用于从某个范围内选择数值的滑块。 + - `SingleOptionInput` - 一个在一组选项之间循环的按钮。 + - `TextInput` - 简单的文本输入。 +- `net.minecraft.world.entity.player.Player#openDialog` - 打开指定的对话框屏幕。 + +## 路径点 + +路径点只是一种跟踪游戏中某些对象位置的方法。底层系统由 `WaypointManager` 处理,负责保存它正在跟踪的路径点,同时允许根据需要更新和移除。服务器通过 `ServerWaypointManager`(通过 `ServerLevel#getWaypointManager` 获得)处理路径点,它持有一个到发射器的活动连接以接收实时更新。客户端通过 `ClientWaypointManager`(通过 `ClientPacketListener#getWaypointManager` 获得)接收这些路径点,它仅保存某些标识符以及精确位置、如果位置不在视野距离内的区块,或者如果距离大于存储的 `Waypoint$Fade#farDist`(默认超过 332 个方块)则保存一个角度。 + +实体默认跟踪路径点。 + +### 样式和图标 + +每个路径点都由某种图标表示,由其 `WaypointStyle` 和图标的颜色定义。`WaypointStyle` 类似于客户端物品或装备模型,它具有一个由 `WaypointStyleAsset` 指向的键,位于 `assets//waypoint_style/.json` 中。这包含一个位于 `assets//textures/gui/sprites/hud/locator_bar_dot/.png` 中的精灵列表,根据与跟踪器的距离选择。精灵在由近和远距离指定的范围内变化。 + +```json5 +// 对于某个样式 examplemod:example_style +// 它将在 `assets/examplemod/waypoint_style/example_style.json` 中找到 +{ + // 表示任何更近的值将在渲染条时使用第一个精灵 + // 未指定时默认为 128 + "near_distance": 100, + // 表示任何更远的值将在渲染条时使用最后一个精灵 + // 未指定时默认为 332 + // 必须大于近距离 + "far_distance": 400, + // 一个相对于 `assets//textures/gui/sprites/hud/locator_bar_dot/.png` 的非空纹理列表 + // 这是用于在两个距离之间进行插值的内容 + "sprites": [ + // 指向 `assets/examplemod/textures/gui/sprites/hud/locator_bar_dot/example_style_0.png` + "examplemod:example_style_0", + "examplemod:example_style_1", + "examplemod:example_style_2" + ] +} +``` + +然后可以使用其构造函数构造图标,并通过服务器上的 `WaypointTransmitter#waypointIcon` 或客户端上的 `TrackedWaypoint#icon` 引用。 + +```java +// 我们将假设此构造函数被公开以用于更动态的用法 +// 目前,它只能通过其 `CompoundTag` 在 `LivingEntity` 上通过 `locator_bar_icon` 设置 +public static Waypoint.Icon EXAMPLE_ICON = new Waypoint.Icon( + // 路径点样式的注册表键 + // 指向 `assets/examplemod/waypoint_style/example_style.json` + ResourceKey.create(WaypointStyleAssets.ROOT_ID, ResourceLocation.fromNamespaceAndPath("examplemod", "example_style")), + // 路径点的颜色 + // 当不存在时,使用路径点标识符的哈希码 + Optional.of(0xFFFFFF) +); +``` + +### 连接 + +当跟踪路径点时,它们通过 `WaypointTransmitter$Connection` 进行管理。一个连接负责将信息同步到客户端,无论是连接、断开连接还是更新。 + +```java +public class ExampleBlockConnection implements WaypointTransmitter.Connection { + + private final BlockState state; + private final BlockPos pos; + private final Waypoint.Icon icon; + private final ServerPlayer receiver; + + public ExampleBlockConnection(BlockState state, BlockPos pos, Waypoint.Icon icon, ServerPlayer receiver) { + this.state = state; + this.pos = pos; + this.icon = icon; + this.recevier = receiver; + } + + public static boolean doesSourceIgnoreReceiver(BlockPos pos, ServerPlayer player) { + double receiveRange = player.getAttributeValue(Attributes.WAYPOINT_RECEIVE_RANGE); + return pos.distSqr(player.blockPosition()) >= receiveRange * receiveRange; + } + + @Override + public boolean isBroken() { + // 当为 true 时,它将尝试重新建立到此发射器的连接 + // 仅在更新位置时调用 + return ExampleBlockConnection.doesSourceIgnoreReceiver(this.pos, this.receiver); + } + + @Override + public void connect() { + // 这会将跟踪数据包发送到客户端 + this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointPosition(this.state.toString() + ": " + this.pos.toString(), this.icon, this.pos)); + } + + @Override + public void disconnect() { + // 这会将移除数据包发送到客户端 + this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.state.toString() + ": " + this.pos.toString())); + } + + @Override + public void update() { + // 这会在客户端上更新跟踪值,假设连接没有中断 + // 在我们的例子中,我们可以假设这永远不会改变,因为方块的位置应该是一致的 + } +} +``` + +### 发射器 + +一个 `WaypointTransmitter` 负责在其和服务器之间建立连接。为了让您的对象传输位置,它必须实现 `WaypointTransmitter` 及其三个方法。`waypointIcon` 仅返回要显示的图标。`isTransmittingWaypoint` 将确定是否可以从该对象传输路径点。`makeWaypointConnectionWith` 实际处理用于跟踪连接的位置或角度的连接的构造。 + +```java +public class ExampleWaypointBlock extends BlockEntity implements WaypointTransmitter { + + // ... + + @Override + public boolean isTransmittingWaypoint() { + // 如果不应该建立连接,则应返回 false + return true; + } + + @Override + public Optional makeWaypointConnectionWith(ServerPlayer player) { + // 如果没有被忽略,则创建一个连接 + return ExampleBlockConnection.doesSourceIgnoreReceiver(this.getBlockPos(), player) + ? Optional.empty() + : Optional.of(new ExampleBlockConnection(this.getBlockState(), this.getBlockPos(), this.waypointIcon(), player)); + } + + @Override + public Waypoint.Icon waypointIcon() { + return EXAMPLE_ICON; + } +} +``` + +然后,您需要做的就是根据需要跟踪、更新或取消跟踪发射器。这可以使用 `ServerWaypointManager` 提供的方法来完成: + +```java +// 我们将假设我们可以访问: +// - ServerLevel serverLevel +// - ExampleWaypointBlock be + +// 跟踪路径点,例如在某个初始化时 +serverLevel.getWaypointManager().trackWaypoint(be); + +// 如果位置改变,更新路径点 +serverLevel.getWaypointManager().updateWaypoint(be); + +// 一旦路径点不再存在,将其移除 +serverLevel.getWaypointManager().untrackWaypoint(be); +``` + +- `net.minecraft.client` + - `Camera` 现在实现 `TrackedWaypoint$Camera` + - `Minecraft#getWaypointStyles` - 返回一个键到路径点样式的管理器。 +- `net.minecraft.client.data.models.WaypointStyleProvider` - 一个生成路径点样式的数据提供者。 +- `net.minecraft.client.multiplayer.ClientPacketListener#getWaypointManager` - 获取用于跟踪路径点的客户端管理器。 +- `net.minecraft.client.renderer.GameRenderer` 现在实现 `TrackedWaypoint$Projector` +- `net.minecraft.client.resources` + - `WaypointStyle` - 定义用于渲染路径点的样式。 + - `WaypointStyleManager` - 将某个键映射到路径点样式的管理器。 +- `net.minecraft.client.waypoints.ClientWaypointManager` - 用于跟踪路径点的客户端管理器。 +- `net.minecraft.commands.arguments.WaypointArgument` - 一个静态方法持有者,用于从实体获取某个路径点发射器。 +- `net.minecraft.network.protocol.game` + - `ClientboundTrackedWaypointPacket` - 一个向客户端发送某个路径点操作的数据包。 + - `ClientGamePacketListener#handleWaypoint` - 在客户端上处理路径点数据包。 +- `net.minecraft.server.ServerScoreboard$Method` 枚举已移除 +- `net.minecraft.server.level.ServerLevel#getWaypointManager` - 获取用于跟踪路径点的服务器管理器。 +- `net.minecraft.server.level.ServerPlayer#isReceivingWaypoints` - 玩家是否从其他位置或实体接收路径点。 +- `net.minecraft.server.waypoints.ServerWaypointManager` - 用于跟踪路径点(例如,玩家和发射器)的服务器端管理器。 +- `net.minecraft.world.entity` + - `Entity#getRequiresPrecisePosition`、`setRequiresPrecisePosition` - 处理何时需要更精确的位置进行跟踪。 + - `LivingEntity` 现在实现 `WaypointTransmitter` +- `net.minecraft.world.waypoints` + - `TrackedWaypoint` - 一个由某个 UUID 或字符串标识、通过 `Waypoint$Icon` 显示并由 `$Type` 指定的路径点。 + - `TrackedWaypointManager` - 一个用于 `TrackedWaypoint` 的路径点管理器。 + - `Waypoint` - 一个表示某个位置位置的接口。它不保存任何信息,通常在 `TrackedWaypoint` 的上下文中使用。 + - `WaypointManager` - 一个跟踪和更新路径点的接口。 + - `WaypointStyleAsset` - 一个表示样式资源的类。 + - `WaypointStyleAssets` - 一个定义所有原版样式资源的类。 + - `WaypointTransmitter` - 一个传输某个可以连接和跟踪的位置的对象。 + +## Blaze3d 变更 + +就像上一次更新一样,Blaze3d 有新的重新设计,改变了渲染的处理方式。 + +### 缓冲区切片 + +渲染系统中最重要的是使用 `GpuBufferSlice`。顾名思义,它只是获取 `GpuBuffer` 的某个切片,使用 `offset` 指示起始索引,`length` 指示其大小。在处理任何与渲染相关的事情时,即使传递给着色器,您几乎总是会处理 `GpuBufferSlice`。可以这样理解:缓冲区只是某个对象的任意列表,而切片代表一个特定的对象。 + +要创建一个切片,只需调用 `GpuBuffer#slice`,可选择提供起始索引和长度。在大多数情况下,您不会直接调用此方法,而是处理为您调用它的其他方法。请注意,由于您通常将数据写入这些切片,它们应该在创建 try-with-resources `RenderPass` 之前构建。 + +### 统一变量重做 + +统一变量系统已被完全改造,以至于除非您熟悉特定功能,否则它可能看起来完全陌生。简而言之,统一变量现在存储为接口对象、纹素缓冲区或采样器。这些由 `GpuBuffer` 或 `GpuBufferSlice` 填充。 + +#### 统一变量类型 + +一个统一变量目前表示为两种 `UniformType` 之一:`UNIFORM_BUFFER`/接口块,或 `TEXEL_BUFFER`。在设置管道并调用 `withUniform` 时,您必须使用上述类型之一指定您的统一变量。如果您选择使用纹素缓冲区,还必须指定纹理数据的格式。采样器没有变化。 + +```java +public static final RenderPipeline EXAMPLE_PIPELINE = RenderPipeline.builder(...) + // 使用名为 'ExampleUniform' 的统一接口块 + .withUniform("ExampleUniform", UniformType.UNIFORM_BUFFER) + // 使用名为 'ExampleTexel' 的缓冲区,将纹理数据存储为 8 位 R 值 + .withUniform("ExampleTexel", UniformType.TEXEL_BUFFER, TextureFormat.RED8I) + // 执行其他设置 + .build(); +``` + +#### 接口块 + +一个 `UNIFORM_BUFFER` 存储为一个 `std140` 接口块。在 GLSL 中,它表示如下: + +```glsl +// 在 assets/examplemod/shaders/core/example_shader.json + +// ExampleUniform 是块的名称 +layout(std140) uniform ExampleUniform { + // 块内的数据 + // 后处理效果只能使用 int, float, vec2, vec3, ivec3, vec4, 或 mat4 + vec4 Param1; + float Param2; +}; +``` + +可以在主块内自由使用这些值,如下所示: + +```glsl +// 在 assets/examplemod/shaders/core/example_shader.json + +out vec4 fragColor; + +void main() { + fragColor = Param1; +} +``` + +统一块可以位于与主函数相同的文件中,或者如果将被重用,可以作为 `moj_import`,其中统一文件位于 `assets//shaders/include/` 内。请注意,在资源包加载之前使用的任何着色器都不能使用 `moj_import`。 + +后处理效果在 `uniform` 输入中定义其接口块: + +```json5 +// 在 assets/examplemod/post_effect/example_post_effect.json +// 在 'passes' 对象内部 +{ + "vertex_shader": "...", + // ... + "uniforms": { + // 接口块的名称 + "ExampleUniform": [ + // 统一块内的一个参数 + // 有关编解码器格式,请参阅 `UniformValue` + { + // 参数的名称 + "name": "Param1", + // 参数类型,之一: + // - int + // - ivec3 + // - float + // - vec2 + // - vec3 + // - vec4 + // - matrix4x4 (mat4) + "type": "vec4", + // 存储在统一变量中的值 + // 不再支持来自代码库的动态值 + "value": [ 0.0, 0.0, 0.0, 0.0 ] + }, + { + "name": "Param2", + "type": "float", + "value": 0 + } + ] + } +} +``` + +#### 纹素缓冲区 + +纹素缓冲区通常表示为 `isamplerBuffer` 以供查询,通常使用 `texelFetch`: + +```glsl +// 在 assets/examplemod/shaders/core/example_shader.json + +uniform isamplerBuffer ExampleTexel; + +void main() { + // 阅读文档以了解 texel fetch 的工作原理 + texelFetch(ExampleTexel, gl_InstanceID); +} +``` + +#### 编写自定义统一变量 + +只有当您负责创建 `RenderPass` 时,才能编写自定义统一变量。与之前一样,您在打开 `RenderPass` 之前创建并缓存对象,然后通过调用 `RenderPass#withUniform` 设置统一变量。唯一的区别是,现在您必须提供一个 `GpuBuffer` 或 `GpuBufferSlice`,其中写入了统一数据,而不是提供任意对象。对于纹素缓冲区,这通常是一些所需纹理格式的编码数据(例如网格)。对于接口块,最简单的方法是使用 `Std140Builder` 用正确的值填充缓冲区。 + +写入 `GpuBuffer` 或 `GpuBufferSlice` 有很多不同的方法,例如使用新创建的 `MappableRingBuffer`。由您决定哪种方法最适合您的场景。以下只是众多解决方案中的一种。 + +```java +// 用于 'ExampleUniform' 的缓冲区 +// 接受缓冲区的名称、其用法和大小 +private final MappableRingBuffer ubo = new MappableRingBuffer( + // 缓冲区名称 + () -> "Example UBO", + // 缓冲区用法 + // 我们设置 128,因为它用于统一变量,并且设置 2 因为我们正在写入它 + // 其他位可以在 `GpuBuffer` 中作为常量找到 + GpuBuffer.USAGE_UNIFORM | GpuBuffer.USAGE_MAP_WRITE, + // 缓冲区的大小 + // 最简单的方法是使用 Std140SizeCalculator 正确计算 + new Std140SizeCalculator() + // Param1 + .putVec4() + // Param2 + .putFloat() + .get() +); + +// 用于 'ExampleTexel' 的缓冲区 +private final MappableRingBuffer utb = new MappableRingBuffer( + // 缓冲区名称 + () -> "Example UTB", + // 我们设置 256,因为它用于纹素缓冲区,并且设置 2 因为我们正在写入它 + GpuBuffer.USAGE_UNIFORM_TEXEL_BUFFER | GpuBuffer.USAGE_MAP_WRITE, + // 缓冲区的大小 + // 对您来说可能会更大 + 3 +); + +// 在某个渲染方法中 + +// 根据需要填充缓冲区 + +// 由于我们使用环形缓冲区,这只是使用列表中的下一个可用缓冲区 +this.ubo.rotate(); +// 将数据写入缓冲区 +try (GpuBuffer.MappedView view = RenderSystem.getDevice().createCommandEncoder().mapBuffer(this.ubo.currentBuffer(), false, true)) { + Std140Builder.intoBuffer(view.data()) + .putVec4(0f, 0f, 0f, 0f) + .putFloat(0f); +} + +// 类似的事情 +this.utb.rotate(); +try (GpuBuffer.MappedView view = RenderSystem.getDevice().createCommandEncoder().mapBuffer(this.utb.currentBuffer(), false, true)) { + view.data() + .put((byte) 0) + .put((byte) 0) + .put((byte) 0); +} + +// 创建渲染通道并传入缓冲区作为统一变量的一部分 +try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(...)) { + // .. + pass.setUniform("ExampleUniform", this.ubo.currentBuffer()); + pass.setUniform("ExampleTexel", this.utb.currentBuffer()); + // ... +} +``` + +### 雾环境 + +雾系统已被重做为环境/渲染状态式的处理程序。每个环境主要负责设置雾数据和颜色。为各个 `FogType` 和特殊状态效果提供了一些额外的处理。 + +所有环境都通过 `FogEnvironment` 处理。通过 `setupFog` 方法设置环境,该方法负责将传入的 `FogData` 更改为与该环境关联的值。如果雾对其周围环境着色,则 `providesColor` 应返回 true,并在 `getBaseColor` 中应用基本颜色色调。同样,如果雾使其周围环境变暗,则 `modifiesDarkness` 应返回 true,并通过 `getModifiedDarkness` 返回修改后的暗度。要确定是否应应用环境,会检查 `isApplicable`。如果不可用,则调用 `onNotApplicable`。 + +所有环境都注册到 `FogRenderer#FOG_ENVIRONMENTS`;然而,使用哪个环境基于 `isApplicable` 和列表顺序。对于颜色和暗度,选择的雾环境是列表中最后一个 `provides*` 方法之一返回 true 的环境。对于实际的雾设置,选择的雾环境是列表中的第一个。 + +### 渲染通道剪裁现在仅限 OpenGL + +剪裁状态已从通用管线代码中移除,现在只能通过 OpenGL 实现访问。通用区域处理已委托给 `CommandEncoder#clearColorAndDepthTextures`。请注意,这不会影响处理面向 blit 的剪裁的现有 `ScreenRectangle` 系统。 + +- `com.mojang.blaze3d.buffers` + - `BufferType` 枚举已移除 + - `BufferUsage` 枚举已移除 + - `GpuBuffer` 现在接受两个整数表示大小和用法,不再指定类型 + - `type` 已移除 + - `usage` 现在返回一个 int + - `slice` - 返回一个表示某个缓冲区切片的记录。实际缓冲区不以任何方式修改。 + - `$ReadView` -> `$MappedView` + - `GpuBufferSlice` - 一个记录,通过持有对整个缓冲区的引用、起点的偏移索引和切片的长度来表示某个缓冲区的切片。 + - `GpuFence` 现在是一个接口,其 OpenGL 实现已移至 `blaze3d.opengl.GlFence` + - `Std140Builder` - 一个用于以 `std140` 格式布局的接口块的缓冲区插入器。用于着色器中的统一块。 + - `Std140SizeCalculator` - 一个用于跟踪某个接口块大小的对象。 +- `com.mojang.blaze3d.opengl` + - `AbstractUniform` 已移除,被 `Std140*` 类取代 + - `BufferStorage` - 一个根据其类型创建缓冲区的类。其实现定义了其一般用途。 + - `DirectStateAccess` + - `createBuffer` - 创建一个缓冲区。 + - `bufferData` - 初始化缓冲区的数据存储。 + - `bufferSubData` - 更新缓冲区数据存储的子集。 + - `bufferStorage` - 创建缓冲区的数据存储。 + - `mapBufferRange` - 映射缓冲区数据存储的一部分。 + - `unmapBuffer` - 释放缓冲区的映射并使指向其数据存储的指针无效。 + - `GlBuffer` 现在接受一个 `DirectStateAccess`、两个整数和一个 `ByteBuffer`,而不是 `GlDebugLabel` 及其 `Buffer*` 枚举 + - `initialized` 已移除 + - `persistentBuffer` - 持有对某个缓冲区不可变部分的引用。 + - `$ReadView` -> `$GlMappedView` + - `GlCommandEncoder` + - `executeDrawMultiple` 现在接受一个字符串集合,定义所需统一变量的列表,以及一个表示绘制调用对象的泛型 + - `executeDraw` 现在接受两个 `int`,要渲染的指定索引范围的实例数和基础顶点 + - `GlConst#toGl` 用于 `BufferType` 和 `BufferUsage` -> `bufferUsageToGlFlag`、`bufferUsageToGlEnum` + - `GlDebugLabel#pushDebugGroup`、`popDebugGroup` - 用于对类似调用进行分组的分析器命令。 + - `GlDevice` + - `USE_GL_ARB_buffer_storage` - 设置 `GL_ARB_buffer_storage` 的扩展标志。 + - `getBufferStorage` - 返回负责创建缓冲区的存储。 + - `GlProgram` + - `Uniform` 字段已移除 + - `safeGetUniform`、`setDefaultUniforms` 已移除 + - `bindSampler` 已移除 + - `getSamplerLocations`、`getSamplers`、`getUniforms` -> `getUniforms`,返回名称到其 `Uniform` 对象的映射 + - `GlRenderPass` + - `uniforms` 现在是一个 `HashMap` + - `dirtSamplers` 已移除 + - `samplers` 映射现在持有 `GpuTextureView` 值 + - `pushedDebugGroups` - 返回推送到堆栈上的组数。关闭渲染通道时不得有任何调试组打开。 + - `isScissorEnabled` - 返回是否启用剪裁状态,该状态会裁剪渲染到屏幕的区域。 + - `getScissorX`、`getScissorY`、`getScissorWidth`、`getScissorHeight` - 返回表示剪裁矩形的值。 + - `GlTexture` 现在接受一个额外的整数用于用法和深度/层数 + - `flushModeChanges` 现在接受一个表示纹理目标的 `int` + - `addViews`、`removeViews` - 管理针对某些 mip 级别的纹理视图。 + - `GlTextureView` - 针对某些 mip 级别的纹理的视图实现。 + - `Uniform` 现在是一个密封接口,实现为缓冲区对象、纹素缓冲区或采样器 +- `com.mojang.blaze3d.pipeline` + - `BlendFunction#PANORAMA` 已移除 + - `CompiledRenderPipeline#containsUniform` 已移除 + - `RenderPipeline` + - `$Builder#withUniform` 现在有一个可以接受 `TextureFormat` 的重载 + - `$UniformDescription` 现在接受一个可为 null 的 `TextureFormat` + - `RenderTarget` + - `colorTextureView`、`depthTextureView`、`getColorTextureView`、`getDepthTextureView` - 当前颜色和深度纹理的视图。 + - `blitAndBlendToTexture` 现在接受 `GpuTextureView` 而不是 `GpuTexture` +- `com.mojang.blaze3d.platform.Lightning` 现在是 `AutoCloseable` + - `setup*` -> `setupFor`,一个接受 `$Entry` 的实例方法 + - `updateLevel` - 更新光照缓冲区,接受是否使用下界漫射光照。 +- `com.mojang.blaze3d.shaders` + - `FogShape` -> `net.minecraft.client.renderer.fog.FogData`,不是一对一 + - `UniformType` 现在只包含两种类型:`UNIFORM_BUFFER` 或 `TEXEL_BUFFER`,不再接受计数 +- `com.mojang.blaze3d.systems` + - `CommandEncoder` + - `clearColorAndDepthTextures` 现在有一个接受四个 `int` 的重载,表示要清除纹理信息的区域 + - `writeToBuffer`、`mapBuffer(GpuBuffer, int, int)` 现在接受 `GpuBufferSlice` 而不是 `GpuBuffer` + - `createFence` - 创建一个新的同步围栏。 + - `createRenderPass` 现在接受一个 `Supplier`,用于确定用作调试组的通道名称,以及 `GpuTextureView` 而不是 `GpuTexture` + - `writeToTexture` 现在接受一个额外的 `int`,表示要写入的深度或层 + - `presentTexture` 现在接受 `GpuTextureView` 而不是 `GpuTexture` + - `GpuDevice` + - `createBuffer` 不再接受 `BufferUsage`,`BufferType` 被 `int` 取代 + - `getUniformOffsetAlignment` - 返回统一缓冲区偏移对齐。 + - `createTexture` 现在接受额外的 `int` 用于用法和深度/层数,请参阅 `GpuTexture` 常量 + - `createTextureView` - 为某个 mip 级别范围创建纹理视图。 + - `RenderPass` + - `enableScissor` 已移除 + - `bindSampler` 现在可以接受一个可为 null 的 `GpuTextureView` + - `setUniform` 现在可以接受 `GpuBuffer` 或 `GpuBufferSlice` 而不是原始输入 + - `drawIndexed` 现在接受以下 `int`:基础顶点、起始索引、元素数和原始计数 + - `drawMultipleIndexed` 现在接受一个字符串集合,定义所需统一变量的列表,以及一个表示绘制调用对象的泛型 + - `pushDebugGroup`、`popDebugGroup` - 用于对类似调用进行分组的分析器命令。 + - `$UniformUploader#upload` 现在接受 `GpuBufferSlice` 而不是 `float` 数组 + - `$Draw` 现在有一个泛型,用于将任何统一变量上传到缓冲区 + - `RenderSystem` + - `SCISSOR_STATE` -> `scissorStateForRenderTypeDraws`,现在是私有的 + - 可通过 `getScissorStateForRenderTypeDraws` 访问 + - `enableScissor`、`disableScissor` -> `enableScissorForRenderTypeDraws`、`disableScissorForRenderTypeDraws`,不是一对一 + - `PROJECTION_MATRIX_UBO_SIZE` - 返回投影矩阵统一变量的大小 + - `setShaderFog`、`getShaderFog` 现在处理 `GpuBufferSlice` + - `setShaderGlintAlpha`、`getShaderGlintAlpha` 已移除 + - `setShaderLights`、`getShaderLights` 现在处理 `GpuBufferSlice` + - `setShaderColor`、`getShaderColor` + - `setup*Lighting` 方法已移除 + - `setProjectionMatrix`、`getProjectionMatrix`(现在为 `getProjectionMatrixBuffer`)现在处理 `GpuBufferSlice` + - `setShaderGameTime`、`getShaderGameTime` -> `setGlobalSettingsUniform`、`getGlobalSettingsUniform`;不是一对一 + - `getDynamicUniforms` - 返回要为着色器写入的统一变量列表。 + - `bindDefaultUniforms` - 绑定要在 `RenderPass` 内使用的默认统一变量 + - `outputColorTextureOverride`、`outputDepthTextureOverride` 现在是 `GpuTextureView` + - `setupOverlayColor`、`setShaderTexture`、`getShaderTexture` 现在操作 `GpuTextureView` 而不是 `GpuTexture` +- `com.mojang.blaze3d.textures` + - `GpuTexture` 现在接受一个表示用法标志和深度/层数的 int + - `usage` - 定义纹理如何使用的标志。 + - `depthOrLayers`、`getDepthOrLayers` - 定义给定纹理可用的层数或深度。这是作为可用纹理编码的通用计数。目前仅支持立方体贴图(意味着层数必须是 6 的倍数)。 + - `GpuTextureView` - 针对某个 mip 级别范围的纹理视图。 + - `TextureFormat#RED8I` - 处理红色通道的 8 位有符号整数。 +- `com.mojang.blaze3d.vertex` + - `ByteBufferBuilder` 现在接受一个 long 作为最大容量 + - `exactlySized` - 返回一个达到其最大容量的缓冲区。应优先于公共构造函数调用此方法。 + - `DefaultVertexFormat#EMPTY` - 一个没有元素的顶点格式。 +- `net.minecraft.client.renderer` + - `CachedOrthoProjectionMatrixBuffer` - 一个缓存正交投影矩阵的对象,如果屏幕宽度或高度改变,则重新构建。 + - `CachedPerspectiveProjectionMatrixBuffer` - 一个缓存透视投影矩阵的对象,如果宽度、高度或视野改变,则重新构建。 + - `CloudRenderer` + - `FLAG_INSIDE_FACE`、`FLAG_USE_TOP_COLOR` 现在是私有的 + - `RADIUS_BLOCKS` 已移除 + - `endFrame` - 通过构造围栏结束当前正在渲染的帧。 + - `CubeMap` 现在是 `AutoCloseable` + - `render` 不再接受部分刻的浮点数。 + - `DynamicUniforms` - 一个将统一接口块写入缓冲区以供着色器使用的类。 + - `DynamicUniformStorage` - 一个将统一变量保存在可映射环形缓冲区切片中的类。 + - `FogParameters` 记录已移除,现在存储在一个通用的 `GpuBufferSlice` 中 + - `FogRenderer` 现在实现 `AutoCloseable` -> `.fog.FogRenderer` + - `endFrame` - 通过构造围栏结束当前正在渲染的帧。 + - `getBuffer` - 获取持有当前雾模式统一变量的缓冲区切片。 + - `setupFog` 不再接受 `FogMode`,不返回任何内容 + - `$FogData#mode` 已移除,被 `skyEnd`、`cloudEnd` 取代 + - `$FogMode` 枚举已被 `NONE` 和 `WORLD` 取代,不是一对一 + - `GameRenderer` + - `getGlobalSettingsUniform` - 返回游戏设置的统一变量。 + - `getLighting` - 获取光照渲染器。 + - `setLevel` - 设置渲染器正在渲染的当前等级。 + - `GlobalSettingsUniform` - 一个用于处理持有当前游戏设置的统一变量的对象。 + - `LevelRenderer` + - `endFrame` - 通过构造围栏结束当前正在渲染的帧。 + - `renderLevel` 现在接受一个 `GpuBufferSlice`、雾向量以及当前位置是否有雾,而不是 `GameRenderer` + - `MappableRingBuffer` - 一个包含三个缓冲区的对象,根据需要写入,然后在结束时旋转到下一个要使用的缓冲区。这些使用围栏进行同步。 + - `PanoramaRenderer#render` 现在接受一个布尔值,表示是否改变旋转,而不是两个浮点数。 + - `PerspectiveProjectionMatrixBuffer` - 一个持有投影矩阵统一变量的对象。 + - `PostChain` + - `load` 现在接受 `CachedOrthoProjectionMatrixBuffer` + - `addToFrame` 不再接受 `Consumer` + - `process` 不再接受 `Consumer` + - `PostChainConfig` + - `$FixedSizedTarget` 记录已移除 + - `$FullScreenTarget` 记录已移除 + - `$InternalTarget` 现在是一个记录,可以接受可选的宽度、高度、目标是否持久以及要使用的清除颜色 + - `$Pass#uniforms` 现在是一个 `Map>` + - `$Uniform` 记录已移除 + - `PostPass` 现在是 `AutoCloseable`,接受一个 `Map>` 作为统一变量,以及一个 `PostPass$Input` 列表 + - `addToFrame` 不再接受 `Consumer`,`Matrix4f` 作为 `GpuBufferSlice` 传入 + - `$Input` + - `bindTo` 已移除 + - `texture` - 根据资源映射为输入构造一个 `GpuTexture`。 + - `samplerName` - 返回采样器的名称。 + - `SkyRenderer#renderSunMoonAndStars` 不再接受 `FogParameters` + - `UniformValue` - 一个表示存储在接口块内的统一变量的接口。 +- `net.minecraft.client.renderer.chunk.SectionRendererDispatcher$RenderSection#setDynamicTransformIndex`、`getDynamicTransformIndex` - 处理用于查询给定部分的正确动态变换的索引。 +- `net.minecraft.client.renderer.fog.environment` + - `AirBasedFogEnvironment` - 一个其颜色源自生物群系空气的环境 + - `AtmosphericFogEnvironment` - 如果没有其他特殊情况匹配,则为默认雾环境。 + - `BlindessFogEnvironment` - 如果实体有失明效果,则激活的环境。 + - `DarknessFogEnvironment` - 如果实体有黑暗效果,则激活的环境。 + - `DimensionOrBossFogEnvrionment` - 根据是否有任何 Boss 或维度特殊效果而激活的环境。 + - `FogEnvironment` - 一个抽象类,确定雾在给定位置应如何为实体渲染。 + - `LavaFogEnvironment` - 如果实体在熔岩中,则激活的环境。 + - `MobEffectFogEnvironment` - 根据实体是否有给定的状态效果而激活的环境。 + - `PowderedSnowFogEnvironment` - 如果实体在细雪中,则激活的环境。 + - `WaterFogEnvironment` - 如果实体在水中,则激活的环境。 +- `net.minecraft.client.renderer.texture` + - `AbstractTexture` + - `setUseMipmaps` - 设置纹理是否应使用 mipmapping。 + - `textureView`、`getTextureView` - 表示当前的纹理视图。 + - `CubeMapTexture` - 一个兼容立方体贴图的纹理,纹理预期具有后缀 `_0` 到 `_5`。 + - `ReloadableTexture#doLoad` 现在是 protected +- `net.minecraft.world.level.material.FogType` + - `DIMENSION_OR_BOSS` - 用于维度特殊效果或 Boss 的雾。 + - `ATMOSPHERIC` - 默认雾类型。 + +## TagAppender 重写 + +`TagAppender` 已被重写,改变了 `TagsProvider` 的基本实现。 + +默认情况下,`TagsProvider` 不再提供任何用于向 `TagBuilder` 添加内容的有用方法。您最多可以使用 `TagsProvider#getOrCreateRawBuilder` 构造构建器,并使用可用的 `add*` 方法之一通过其 `ResourceLocation` 添加元素或标签。 + +```java +// 我们将假设存在某个 TagKey EXAMPLE_TAG +public class ExampleTagsProvider extends TagsProvider { + + public ExampleTagsProvider(PackOutput output, CompletableFuture registries, CompletableFuture> parentProvider) { + super( + // 资源包的输出位置 + output, + // 要为其生成标签的注册表键 + Registries.ITEM, + // 注册表的注册表 + registries, + // 可选:要使用其生成标签的父提供者 + // 通常从 TagsProvider#contentsGetter 获得 + parentProvider + ); + } + + @Override + protected void addTags(HolderLookup.Provider registries) { + // 在此处添加标签 + TagBuilder builder = this.getOrCreateRawBuilder(EXAMPLE_TAG); + builder + // 添加单个元素 + .addElement(ResourceLocation.fromNamespaceAndPath("minecraft", "apple")) + // 将标签添加到构建器 + .addTag(ItemTags.WOOL.location()); + } +} +``` + +但是,如果我们想通过其 `ResourceKey` 引用标签呢?或者注册表对象?这就是重写的 `TagAppender` 的作用。`TagAppender` 是一个具有两个泛型的接口,`E` 表示要添加的条目的类型,`T` 表示标签内对象的类型。一个 `TagAppender` 有五个常见方法:三个添加条目(`add`、`addAll`、`addOptional`),两个添加标签(`addTag`、`addOptionalTag`)。可以通过 `forBuilder` 创建 `TagAppender`,它接受条目的 `ResourceKey`。 + +`KeyTagProvider` 通过添加一个 `tag` 方法为您提供此功能,该方法从 `TagKey` 创建追加器。通常,数据包注册表对象使用此提供者生成其标签: + +```java +// 我们将假设存在某个 TagKey EXAMPLE_TAG +public class ExampleTagsProvider extends KeyTagProvider { + + public ExampleTagsProvider(PackOutput output, CompletableFuture registries) { + super( + // 资源包的输出位置 + output, + // 要为其生成标签的注册表键 + Registries.ITEM, + // 注册表的注册表 + registries + ); + } + + @Override + protected void addTags(HolderLookup.Provider registries) { + // 在此处添加标签 + TagAppender, Item> builder = this.tag(EXAMPLE_TAG); + builder + // 添加单个元素 + .add(ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath("minecraft", "apple"))) + // 将标签添加到构建器 + .addTag(ItemTags.WOOL); + } +} +``` + +`TagAppender` 也可以使用 `map` 方法来更改其条目类型。这接受一个将新条目类型映射到原始条目类型的函数。`IntrinsicHolderTagsProvider` 通过一个 `tag` 方法为您提供此功能,该方法从 `TagKey` 创建追加器,并使用键提取器进行映射。通常,内置注册表对象使用此提供者生成其标签: + +```java +// 我们将假设存在某个 TagKey EXAMPLE_TAG +public class ExampleTagsProvider extends IntrinsicHolderTagsProvider { + + public ExampleTagsProvider(PackOutput output, CompletableFuture registries) { + super( + // 资源包的输出位置 + output, + // 要为其生成标签的注册表键 + Registries.ITEM, + // 注册表的注册表 + registries, + // 将注册表对象映射到其资源键 + item -> item.builtInRegistryHolder().key() + ); + } + + @Override + protected void addTags(HolderLookup.Provider registries) { + // 在此处添加标签 + TagAppender builder = this.tag(EXAMPLE_TAG); + builder + // 添加单个元素 + .add(Items.APPLE) + // 将标签添加到构建器 + .addTag(ItemTags.WOOL); + } +} +``` + +### 复制标签:方块和物品 + +复制标签不再以传统意义存在,即遍历现有标签的元素。相反,实现已被缩小到一个通用的 `BlockItemTagsProvider`。此方法通过同时接受方块和物品标签,并在 `Vanilla*TagsProvider` 中适当地转换它们,来提供 `TagAppender`。因此,与其说是复制,不如说是将方块映射到其关联的物品。 + +一个缺点是,在其他标签中使用的标签(调用 `addTag`)必须具有相同的名称。例如,添加 `BlockTags#LOGS` 之所以有效,是因为存在一个 `minecraft:logs` 同时用于方块和物品标签。同时,添加 `BlockTags#CEILING_HANGING_SIGNS` 会失败,因为关联的物品标签是 `minecraft:hanging_signs` 而不是 `minecraft:ceiling_hanging_signs`。 + +```java +// 我们将假设存在某个 TagKey BLOCK_EXAMPLE_TAG +// 我们将假设存在某个 TagKey ITEM_EXAMPLE_TAG +public class ExampleBlockItemTagsProvider extends BlockItemTagsProvider { + + @Override + public void run() { + // 在此处添加方块物品标签 + // 将根据提供者向方块或物品标签添加条目 + TagAppender builder = this.tag(BLOCK_EXAMPLE_TAG, ITEM_EXAMPLE_TAG); + builder + // 添加单个元素 + .add(Blocks.TERRACOTTA) + // 将标签添加到构建器 + // 在方块和物品标签中都存在 `minecraft:logs` + .addTag(BlockTags.LOGS); + } +} + +// 对于某个 IntrinsicHolderTagsProvider 或 IntrinsicHolderTagsProvider +@Override +protected void addTags(HolderLookup.Provider registries) { + // 在此处添加标签 + new ExampleBlockItemTagsProvider() { + // 或 取决于情况 + protected TagAppender tag(TagKey blockTag, TagKey itemTag) { + // 返回一个 TagAppender + // 有关物品示例,请参阅 VanillaItemTagsProvider$BlockToItemConverter + // 有关方块示例,请参阅 VanillaBlockTagsProvider + } + }.run(); +} +``` + +- `net.minecraft.data.tags` + - `BlockItemTagsProvider` - 一个为方块物品生成标签的提供者,使用方块和物品标签等效项作为起点。 + - `IntrinsicHolderTagsProvider` + - `tag` 现在返回原始的 `TagAppender` + - `$IntrinsicTagAppender` 类已移除 + - `ItemTagsProvider` 类已移除 + - `KeyTagProvider` - 一个通过其 `ResourceKey` 追加元素的提供者。 + - `TagsProvider` + - `tag` 已移除 + - `$TagAppender` -> `TagAppender`,不是一对一 + - `VanillaItemTagsProvider` 现在实现 `IntrinsicHolderTagsProvider` + - `BlockToItemConverter` - 一个将方块添加到物品标签的标签追加器。 + +## 通用编码与解码:替换直接 NBT 访问 + +从更高级别的对象(如实体和方块实体)中获取某些 NBT 数据的直接访问已被完全移除。这意味着,通常,在序列化过程中您不能直接接触 `CompoundTag`。相反,通过 `ValueInput` 和 `ValueOutput` 提供了对 nbt 标签的间接访问。顾名思义,它们分别从数据对象读取值和向数据对象写入值。可用的方法类似于 `CompoundTag` 上的方法。对于 `ValueInput`,有通过提供关联键的 `get*` 方法,以及如果不存在则获取或默认的 `get*Or`。还有用于处理 `Codec` 的 `read`。对于 `ValueOutput`,有通过提供关联键和值的 `put*` 方法,以及用于 `Codec` 的 `store`。列表变体在输入/输出访问上单独存在。 + +因此,大多数接受 `CompoundTag` 的方法现在改为在 `read`/`load` 时接受 `ValueInput`,或在 `write`/`save` 时接受 `ValueOutput`。 + +```java +// 对于某个带有 ItemStack stack 的 Entity +@Override +protected void readAdditionalSaveData(ValueInput in) { + super.readAdditionalSaveData(in); + // 默认情况下,输入使用注册表 ops + this.stack = in.read("example_stack", ItemStack.CODEC).orElse(ItemStack.EMPTY); +} + +@Override +protected void addAdditionalSaveData(ValueOutput out) { + super.addAdditionalSaveData(out); + // 默认情况下,输出使用注册表 ops + in.storeNullable("example_stack", ItemStack.CODEC, this.stack); +} + +// 对于某个带有 int value 的 BlockEntity +@Override + protected void loadAdditional(ValueInput in) { + super.loadAdditional(in); + this.value = in.getIntOr("value", 0); + } + + @Override + protected void saveAdditional(ValueOutput out) { + super.saveAdditional(out); + out.putInt("value", this.value); + } +``` + +### NBT 实现 + +为了提供对 `CompoundTag` 的间接访问,有分别称为 `TagValueInput` 和 `TagValueOutput` 的 `ValueInput` 和 `ValueOutput` 的实现。 + +可以使用 `createWithContext` 或 `createWithoutContext` 创建 `TagValueOutput`。“有上下文”意味着输出可以访问注册表对象的 `HolderLookup$Provider`,而“无上下文”则不能。目前,没有位置使用 `createWithoutContext`。内部会创建一个新的 `CompoundTag`。然后,这个输出被传递以填充,最后通过 `buildResult` 返回用于写入的 NBT。 + +另一方面,`TagValueInput` 可以通过 `create` 创建,接受 `HolderLookup$Provider` 和要读取的 `CompoundTag`。如果数据存储为对象列表而不是单个对象,您可以传入一个 `List` 并返回一个 `ValueInputList`。请注意,这不处理索引,只提供可迭代或流关联的实现。 + +### 问题报告器 + +除了上述内容之外,`create*` 方法还接受一个 `ProblemReporter`,用于收集在尝试序列化/反序列化数据时遇到的所有问题。由实现决定此报告是导致崩溃还是发出警告。一个 `ProblemReporter` 包含两个方法:`report`,用于报告一个 `$Problem`;以及 `forChild`,用于使用 `$PathElement` 进一步对 `$Problem` 进行分组。问题和路径元素都只是接口对象,分别返回指示问题是什么或分组的字符串。 + +有两个常用的 `ProblemReporter`:`$Collector`,通常与数据提供者一起使用;以及 `$ScopedCollector`,与磁盘对象(例如,实体、方块实体、区块等)一起使用。`$Collector` 通常使用 `forEach` 逐个报告每个问题,`getReport`/`getTreeReport` 返回字符串化的问题,或 `isEmpty` 列出是否存在任何问题。`$ScopedCollector` 做同样的事情,只是它是 `AutoCloseable`,以防输出没有做任何操作。在这些情况下,报告会写入日志记录器。 + +每个收集器都接受一个初始的 `$PathElement`。这通常来自对象本身,其中包含某个称为 `problemPath` 的方法,该方法实现了该接口。`HolderLookup$Provider` 以相同的方式提供。 + +```java +// 对于某个带有 HolderLookup.Provider registries 的对象 +// 还有一个 Logger LOGGER + +// 我们假设我们的根路径元素实现如下 +public record ExamplePathElement(String name) implements ProblemReporter.PathElement { + + @Override + public String get() { + return "Example: " + this.name(); + } +} + +// 对于数据提供者 +ProblemReporter.Collector problems = new ProblemReporter.Collector( + // 对于非指定的根,可以为空 + new ExamplePathElement("data_provider") +); +// 传递提供者 + +// 对于基于磁盘的对象 +try (ProblemReporter.ScopedCollector problems = new ProblemReporter.ScopedCollector(new ExamplePathElement("EXAMPLE TEST"), LOGGER)) { + TagValueOutput out = TagValueOutput.createWithContext(problems, this.registries); + // 传递输出以写入数据 + + // 对于输入 + // 最后一个参数可以是任何 CompoundTag,以输出为例 + TagValueInput in = TagValueInput.create(problems, this.registries, out.buildResult()); + // 传递输入以读取数据 +} +``` +- `net.minecraft.nbt.StringTag#escapeWithoutQuotes` - 创建一个转义控制字符、引号、撇号和反斜杠的字符串。 +- `net.minecraft.server.level.ServerPlayer` + - `loadAndSpawnParentVehicle` 现在接受一个 `ValueInput` + - `loadAndSpawnEnderPearls` 现在接受一个 `ValueInput` + - `loadGameTypes` 现在接受一个 `ValueInput` +- `net.minecraft.server.players.PlayerList#load` 接受一个 `ProblemReporter`,返回一个可选的 `ValueInput` +- `net.minecraft.util.ProblemReporter` + - `DISCARDING` - 一个丢弃所有报告的报告器。 + - `forChild` 现在接受一个 `$PathElement` + - `report` 现在接受一个 `$Problem` + - `$Collector` 现在有一个接受根 `$PathElement` 的构造函数 + - `isEmpty` - 返回是否没有报告。 + - `forEach` - 遍历所有可用问题。 + - `getReport` 现在返回一个普通的 `String` + - `getTreeReport` - 使用 DFS 获取报告及其所有子级。 + - `$ElementReferencePathElement` - 一个引用某个 `ResourceKey` 的路径元素。 + - `$FieldPathElement` - 一个引用某个字符串的路径元素。 + - `$IndexedFieldPathElement` - 一个在索引处引用某个字符串的路径元素。 + - `$IndexedPathElement` - 一个引用某个索引的路径元素。 + - `$PathElement` - 一个定义分组或元素的接口。 + - `$Problem` - 一个定义元素问题的接口。 + - `$RootElementPathElement` - 一个引用某个 `ResourceKey` 作为根的路径元素。 + - `$RootFieldPathElement` - 一个引用某个字符串作为根的路径元素。 + - `$ScopedCollector` - 一个在出现问题时记录警告的收集器。 +- `net.minecraft.world` + - `ContainerHelper` + - `saveAllItems` 现在接受一个 `ValueOutput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider`,不返回任何内容 + - `loadAllItems` 现在接受一个 `ValueInput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider` + - `LockCode#addToTag`、`fromTag` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider` + - `RandomziableContainer#tryLoadLootTable`、`trySaveLootTable` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag` + - `SimpleContainer` + - `fromTag` -> `fromItemList`,不是一对一 + - `createTag` -> `storeAsItemList`,不是一对一 +- `net.minecraft.world.entity` + - `Entity` + - `saveAsPassenger` 现在接受一个 `ValueOutput` 而不是 `CompoundTag` + - `save` 现在接受一个 `ValueOutput` 而不是 `CompoundTag` + - `saveWithoutId` 现在接受一个 `ValueOutput` 而不是 `CompoundTag`,不返回任何内容 + - `load` 现在接受一个 `ValueInput` 而不是 `CompoundTag` + - `readAdditionalSaveData` 现在接受一个 `ValueInput` 而不是 `CompoundTag` + - `addAdditionalSaveData` 现在接受一个 `ValueOutput` 而不是 `CompoundTag` + - `problemPath` - 返回报告问题的路径元素。 + - `EntityRenference` + - `store` 现在接受一个 `ValueOutput` 而不是 `CompoundTag` + - `read`、`readWithOldOwnerConversion` 现在接受一个 `ValueInput` 而不是 `CompoundTag` + - `Entity` + - `UUID_TAG` -> `TAG_UUID` + - `create`、`by` 现在接受一个 `ValueInput` 而不是 `CompoundTag` + - `loadEntityRecursive` 现在接受一个 `ValueInput` 而不是 `CompoundTag` + - `loadEntitiesRecursive` 现在接受一个 `ValueInput$ValueInputList` 而不是 nbt 标签列表 + - `loadStaticEntity` 现在接受一个 `ValueInput` 而不是 `CompoundTag` + - `EntityReference#store` - 将引用数据写入 `ValueOutput`。 + - `Leashable#readLeashData`、`writeLeashData` 现在接受 `ValueInput`/`ValueOutput` 而不是 `CompoundTag` + - `LivingEntity#ATTRIBUTES_FIELD` -> `TAG_ATTRIBUTES` + - `NeutralMob#addPersistentAngerSaveData`、`readPersistentAngerSaveData` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag` +- `net.minecraft.world.entity.npc.InventoryCarrier#readInventoryFromTag`、`writeInventoryToTag` 现在接受 `ValueInput`/`ValueOutput` 而不是 `CompoundTag` +- `net.minecraft.world.entity.player.Inventory` + - `save` 现在接受一个 `ValueOutput$TypedOutputList`,不返回任何内容 + - `load` 现在接受一个 `ValueOutput$TypedInputList` +- `net.minecraft.world.entity.variant.VariantUtils` + - `writeVariant` 现在接受一个 `ValueOutput` 而不是 `CompoundTag` + - `readVariant` 现在接受一个 `ValueInput` 而不是 `CompoundTag`,不再接受 `RegistryAccess` +- `net.minecraft.world.entity.vehicle.ContainerEntity#addChestVehicleSaveData`、`readChestVehicleSaveData` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider` +- `net.minecraft.world.inventory.PlayerEnderChestContainer` + - `fromTag` -> `fromSlots`,不是一对一 + - `createTag` -> `storeAsSlots`,不是一对一 +- `net.minecraft.world.item` + - `BlockItem#setBlockEntityData` 现在接受一个 `TagValueOutput` 而不是 `CompoundTag` + - `ItemStack#parse`、`save` 已移除 +- `net.minecraft.world.level` + - `BaseCommandBlock#save`、`load` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider`,不返回任何内容 + - `BaseSpawner#save`、`load` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag`,不返回任何内容 +- `net.minecraft.world.level.block.SculkSpreader#save`、`load` 现在接受 `ValueOutput`/`ValueInput` 而不是 `CompoundTag` +- `net.minecraft.world.level.block.entity.BlockEntity` + - `load*` 方法现在接受 `ValueInput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider` + - `save*` 方法现在接受 `ValueOutput` 而不是 `CompoundTag`,不再接受 `HolderLookup$Provider` + - `removeComponentsFromTag` 现在接受一个 `ValueOutput` 而不是 `CompoundTag` + - `parseCustomNameSafe` 现在接受一个 `ValueInput` 和键,而不是标签和 `HolderLookup$Provider` + - `problemPath` - 返回报告问题的路径元素。 +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawner#load`、`store` - 处理写入生成器数据。 +- `net.minecraft.world.level.chunk.ChunkAccess#problemPath` - 返回报告问题的路径元素。 +- `net.minecraft.world.level.storage` + - `PlayerDataStorage#load` 现在返回一个 `ValueInput` 而不是 `CompoundTag`,接受一个 `ProblemReporter` + - `TagValueInput` - 一个复合标签输入。 + - `TagValueOutput` - 一个复合标签输出。 + - `ValueInput` - 一个定义如何从某个对象读取数据的接口。 + - `ValueInputContextHelper` - 一个包含用于读取对象数据的上下文的类。 + - `ValueOutput` - 一个定义如何向某个对象写入数据的接口。 +- `net.minecraft.world.level.storage.loot.ValidationContext` + - `forChild`、`enterElement` 现在接受 `ProblemReporter$PathElement` 而不是 `String` + - `reportProblem` 现在接受 `ProblemReporter$Problem` 而不是 `String` + - `$MissingReferenceProblem` - 一个引用对象缺失的问题。 + - `$ParametersNotProvidedProblem` - 一个战利品上下文参数缺失的问题。 + - `$RecursiveReferenceProblem` - 一个引用对象正在引用自身的问题。 + - `$ReferenceNotAllowedProblem` - 一个引用对象不允许被引用的问题。 +- `net.minecraft.world.level.storage.loot.entries` + - `AlternativesEntry#UNREACHABLE_PROBLEM` - 一个替代条目永远无法执行的问题。 + - `CompositeEntryBase#NO_CHILDREN_PROBLEM` - 一个组合体没有条目的问题。 + - `NestedLootTable#INLINE_LOOT_TABLE_PATH_ELEMENT` - 一个表示表是内联的元素。 + +## 服务端玩家变更 + +`MinecraftServer` 不再在 `ServerPlayer` 上公开。此外,`serverLevel` 已被移除,现在用重载的 `level` 来返回 `ServerLevel`。 + +- `net.minecraft.server.level.ServerPlayer` + - `server` 字段现在是私有的 + - `serverLevel` -> `level`,仍然返回 `ServerLevel` + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 拴绳 + +拴绳系统已更新,最多支持四个拴绳同时拴在一个实体上。此外,物理效果已更新,以更恰当地处理某些可拉伸物体的现实弹性。 + +- `net.minecraft.client.renderer.entity.state.EntityRenderState` + - `leashState` 现在返回一个 `$LeashState` 列表 + - `$LeashState#slack` - 拴绳是否有松弛。 +- `net.minecraft.world.entity` + - `Entity` + - `shearOffAllLeashConnections` - 处理使用剪刀移除拴绳时的情况。 + - `dropAllLeashConnections` - 处理应从实体移除所有拴绳时的情况。 + - `getQuadLeashHolderOffsets` - 获取四个拴绳拴在实体上时的偏移量。 + - `supportQuadLeashAsHolder` - 返回实体是否可以被拴最多四次。 + - `notifyLeashHolder`、`notifyLeasheeRemoved` - 处理拴绳在实体上Tick以及被移除时的情况。 + - `setLeashOffset` 已移除 + - `getLeashOffset` -> `Leashable#getLeashOffset` + - `Leashable` + - `MAXIMUM_ALLOWED_LEASHED_DIST` - 拴绳可以拴到实体的最大距离。 + - `AXIS_SPECIFIC_ELASTICITY` - 拴绳沿每个轴的阻力。 + - `SPRING_DAMPENING` - 拴绳拉伸时振荡的能量损失。 + - `TORSIONAL_ELASTICITY` - 拴绳在扭矩下的阻力。 + - `STIFFNESS` - 拴绳的刚度。 + - `ENTITY_ATTACHMENT_POINT` - 指定拴绳在实体上的附着点。 + - `LEASHER_ATTACHMENT_POINT` - 指定拴绳在持有者上的附着点。 + - `SHARED_QUAD_ATTACHMENT_POINTS` - 指定拴绳在实体上的附着点。 + - `canHaveALeashAttachedToIt` -> `canHaveALeashAttachedTo`,不是一对一 + - `leashDistanceTo` - 返回拴绳在持有者和被拴者之间经过的距离。 + - `onElasticLeashPull` - 处理拴绳被拉拽时的情况。 + - `leashSnapDistance` - 返回拴绳将尝试从实体上移除的距离。 + - `leashElasticDistance` - 返回拴绳的弹性成为物理因素之前的最大距离。 + - `handleLeashAtDistance` 已移除 + - `angularFriction` - 返回实体在方块上或液体中的角摩擦。 + - `whenLeashedTo` - 通知实体拴绳已附着。 + - `elasticRangeLeashBehaviour`、`legacyElasticRangeLeashBehaviour` -> `checkElasticInteractions`,不是一对一 + - `supportQuadLeash` - 实体是否可以被拴最多四次。 + - `getQuadLeashOffsets` - 返回附着在实体上的每个拴绳的偏移量。 + - `createQuadLeashOffsets` - 为四个可能的拴绳位置中的每一个创建偏移量。 + - `leashableLeashedTo` - 获取所有在 32 格半径内被拴到此持有者的实体。 + - `$LeashData#angularMomentum` - 返回实体被拴时的角动量。 + - `$Wrench` - 一个记录,处理施加在拴绳上的力和扭矩。 +- `net.minecraft.world.item.LeadItem#leashableInArea` -> `Leashable#leashableInArea` + +### 移除生物效果图集 + +生物效果图集已被移除,并与 GUI 图集合并。 + +- `net.minecraft.client.Minecraft#getMobEffectTextures` 已移除 +- `net.minecraft.client.gui.Gui#getMobEffectSprite` - 获取状态效果精灵的位置。 +- `net.minecraft.client.resources.MobEffectTextureManage` 类已移除 +- `AtlasIds#MOB_EFFECTS` 已移除 + +### 权限来源 + +命令的权限检查已被抽象到其自己的 `PermissionSource` 接口中。这提供了之前提供的 `hasPermission` 方法,以及一个新方法 `allowsSelectors`,它返回源是否有选择其他实体所需的权限(默认为 2 级权限)。您可以通过在 `ArgumentBuilder#requires` 中使用所需的级别调用 `Commands#hasPermission`,将权限检查合并到您的命令中。 + +- `net.minecraft.client.multiplayer` + - `ClientPacketListener` + - `getCommands` 现在返回一个 `ClientSuggestionProvider` 泛型 + - `sendUnattendedCommand` 现在接受一个 `Screen` 而不是 `boolean` + - `ClientSuggestionListener` 现在实现 `PermissionSource`,接受一个布尔值表示是否允许受限命令 + - `allowRestrictedCommands` - 返回是否建议受限命令。 +- `net.minecraft.commands` + - `Commands#hasPermission` - 返回给定级别的权限检查。 + - `CommandSourceStack` 现在实现 `PermissionSource` + - `ExecutionCommandSource` 现在实现 `PermissionSource` + - `PermissionSource` - 返回运行命令的权限源。 + - `SharedSuggestionProvider` + - `suggestResgitryElements` 现在接受 `HolderLookup` 而不是 `Registry` + - `listSuggestions` - 列出某些注册表元素的建议。 + - `hasPermission` 已移除 +- `net.minecraft.commands.synchronization.SuggestionProviders` + - `AVAILABLE_SOUNDS`、`SUMMONABLE_ENTITIES` 现在接受 `SharedSuggestionProvider` 作为其泛型 + - `cast` - 将建议提供者转换为正确的类型。 + - `safelySwap` 已移除 + - `$Wrapper` -> `$RegisteredSuggestion` +- `net.minecraft.world.entity.Entity#getCommandSenderWorld` 已移除 + +### 动画烘焙 + +动画现在被烘焙到 `KeyframeAnimation` 中。每个 `KeyframeAnimation` 由将关键帧应用于给定 `ModelPart` 的条目组成。要创建动画,应在模型构造函数中通过 `AnimationDefinition#bake` 烘焙定义,然后在 `EntityModel#setupAnim` 期间根据需要调用 `#apply` 或 `#applyWalk`。 + +```java +// 对于某个实体模型 +// 假设存在某个 AnimationDefinition EXAMPLE_DEFN +// 假设我们的 ExampleEntityState 有一个 AnimationState exampleAnimState +public class ExampleModel extends EntityModel { + + private final KeyframeAnimation exampleAnim; + + public ExampleModel(ModelPart root) { + // 我们传入任何可以应用所有动画的 'root' + this.exampleAnim = EXAMPLE_DEFN.bake(root); + } + + @Override + public void setupAnim(ExampleEntityState state) { + super.setupAnim(state); + this.exampleAnim.apply(state.exampleAnimState, state.ageInTicks); + } +} +``` + +- `net.minecraft.client.animation` + - `AnimationDefinition#bake` - 烘焙一个已定义的动画,以便在 `Model` 上使用。 + - `KeyframeAnimation` - 一个用于在给定 `Model` 上移动 `ModelPart` 的烘焙动画。 + - `KeyframeAnimations#animate` -> `KeyframeAnimation$Entry#apply` +- `net.minecraft.client.model.Model` + - `getAnyDescendantWithName` 已移除 + - `animate` -> `KeyframeAnimation#apply` + - `animateWalk` - `KeyframeAnimation#applyWalk` + - `applyStatic` -> `KeyframeAnimation#applyStatic` +- `net.minecraft.client.model.geom.ModelPart` + - `getAllParts` 现在返回一个 `List` + - `createPartLookup` - 创建一个部件名称到其 `ModelPart` 的查找表,任何重复的名称将被忽略。 + +### ChunkSectionLayers + +用于定义方块或流体应如何渲染的 `RenderType` 现在已被 `ChunkSectionLayer` 取代。它们在功能上与 `RenderType` 相同;然而,它们只指定要使用的 `RenderPipeline` 以及缓冲区信息。这也意味着某些 `RenderType` 被移除,例如 `TRANSLUCENT`,因为它们仅用于区块渲染。 + +这也意味着添加到 `ItemBlockRenderTypes#TYPE_BY_BLOCK` 必须指定 `ChunkSectionLayer` 而不是关联的 `RenderType`。 + +- `net.minecraft.client.renderer` + - `ItemBlockRenderTypes` + - `getChunkRenderType` 现在返回一个 `ChunkSectionLayer` + - `getRenderLayer(FluidState)` 现在返回一个 `ChunkSectionLayer` + - `RenderType` + - `translucent` 已移除 + - `getRenderTarget`、`getRenderPipeline` 已移除 + - `chunkBufferLayers` 已移除 +- `net.minecraft.client.renderer.chunk` + - `ChunkSectionLayer` - 一个枚举,定义如何渲染单个区块层(例如,实心方块、半透明方块)。 + - `ChunkSectionLayerGroup` - 一个枚举,将层分组以进行渲染。 + - `ChunkSectionsToRender` - 一个记录,包含给定区块的绘制,允许按层组渲染它们。 + - `RenderChunk` -> `SectionCopy` + - `RenderChunkRegion` -> `RenderSectionRegion` + - `RenderRegionCache#createRegion` 现在接受一个 `long` 而不是 `SectionPos` + - `SectionCompiler$Results` + - `globalBlockEntities` -> - `net.minecraft.client.multiplayer.ClientLevel#getGloballyRenderedBlockEntities` + - `renderedLayers` 现在接受 `ChunkSectionLayer` 作为键 + - `SectionMesh` - 一个定义区块内给定部分的网格的接口 + - `SectionRenderDispatcher` + - `getBatchToCount` -> `getCompileQueueSize` + - `setCamera`、`getCameraPosition` 已移除 + - `blockUntilClear` 已移除 + - `clearBatchQueue` -> `clearCompileQueue`,现在是公开的 + - `$CompiledSection` -> `CompiledSectionMesh` + - `$RenderSection` + - `getBuffers` 已移除 + - `uploadSectionLayer(RenderType, MeshData)` -> `upload(Map, CompiledSectionMesh)`,不是一对一 + - `uploadSectionIndexBuffer` 现在接受一个 `CompiledSectionMesh` 和一个 `ChunkSectionLayer` 而不是 `RenderType` + - `getDistToPlayerSqr` 已移除 + - `getCompiled` -> `getSectionMesh`,不是一对一 + - `rebuildSectionAsync` 不再接受 `SectionRenderDispatcher` + - `setDynamicTransformIndex`、`getDynamicTransformIndex` 已移除 + - `$SectionBuffers` -> `SectionBuffers` + - `$TranslucencyPointOfView` -> `TranslucencyPointOfView` +- `net.minecraft.server.level.ChunkMap#getUpdatingChunkIfPresent` 现在是公开的 +- `net.minecraft.world.level.TicketStorage` + - `purgeStaleTickets` 现在接受 `ChunkMap` + - `removeTicketIf` 现在接受一个 `BiPredicate` 而不是 `Predicate`,接受区块位置和加载票 +- `net.minecraft.world.level.chunk.ChunkAccess#isSectionEmpty` 已移除 + +### 标签变更 + +- `minecraft:block` + - `plays_ambient_desert_block_sounds` 拆分为 `triggers_ambient_desert_sand_block_sounds`、`triggers_ambient_desert_dry_vegetation_block_sounds` + - `happy_ghast_avoids` + - `triggers_ambient_dried_ghast_block_sounds` +- `minecraft:dialog` + - `pause_screen_additions` + - `quick_actions` +- `minecraft:entity_type` + - `can_equip_harness` + - `followable_friendly_mobs` +- `minecraft:item` + - `harnesses` + - `happy_ghast_food` + - `happy_ghast_tempt_items` + +### 新增列表 + +- `com.mojang.blaze3d.pipeline` + - `BlendFunction#TRANSLUCENT_PREMULTIPLIED_ALPHA` - 一个假设目标具有来自合成步骤的预乘 alpha 的混合函数。 + - `RenderPipeline` + - `getSortKey` - 返回一个表示元素应如何排序以进行渲染的值。用于层排序。 + - `updateSortKeySeed` - 更新排序键的种子,当前未使用。 +- `com.mojang.blaze3d.systems.RenderSystem` + - `outputColorTextureOverride` - 持有一个包含覆盖颜色的纹理,该覆盖颜色将代替 `RenderType` 目标中指定的任何内容使用。 + - `outputDepthTextureOverride` - 持有一个包含覆盖深度的纹理,该覆盖深度将代替 `RenderType` 目标中指定的任何内容使用。 +- `com.mojang.blaze3d.textures.GpuTexture#setUseMipmaps` - 设置纹理在不同距离是否应使用 mipmap。 +- `net.minecraft` + - `FileUtil#isPathPartPortable` - 返回提供的字符串是否与任何 Windows 保留文件名不匹配。 + - `WorldVersion$Simple` - 当前世界版本的简单实现。 +- `net.minecraft.advancements.critereon` + - `ItemUsedOnLocationTrigger$TriggerInstance#placedBlockWithProperties` - 创建一个触发器,其中放置了一个具有指定属性的方块。 + - `PlayerInteractTrigger$TriggerInstance#equipmentSheared` - 创建一个条件触发器,作用于玩家从某个实体上取下物品。 +- `net.minecraft.client` + - `GameNarrator#saySystemChatQueued` - 如果启用了系统或聊天消息旁白,则旁白一个组件。 + - `Minecraft#disconnectWithSavingScreen` - 断开当前客户端实例并显示“保存等级”屏幕。 + - `Options` + - `keyQuickActions` - 用于显示快速操作对话框的按键映射。 + - `cloudRange` - 返回云可以渲染的最大距离。 + - `musicFrequency` - 返回 `MusicManager` 处理的背景音乐应播放的频率。 + - `showNowPlayingToast` - 返回是否显示“正在播放”吐司。 + - `getFinalSoundSourceVolume` - 计算给定声音源的音量,非主声音源由主声音源缩放。 + - `NarratorStatus#shouldNarrateSystemOrChat` - 返回当前旁白状态是否为除 `OFF` 之外的任何状态。 +- `net.minecraft.client.color.ColorLerper` - 一个基于部分刻在颜色类型之间进行插值的实用工具类。 +- `net.minecraft.client.data.models.BlockModelGenerators#createDriedGhastBlock` - 创建干燥恶魂方块模型定义。 +- `net.minecraft.client.data.models.model` + - `ModelTemplates#DRIED_GHAST` - 一个使用 `minecraft:block/dried_ghast` 父级的模板。 + - `TextureMapping#driedGhast` - 为干燥恶魂模型创建默认纹理映射。 + - `TextureSlot#TENTACLES` - 提供一个纹理键 `tentacles`。 +- `net.minecraft.client.model` + - `GhastModel#animateTentacles` - 动画恶魂的触手。 + - `HappyGhastHarnessModel` - 一个代表恶魂挽具的模型。 + - `HappyGhastModel` - 一个代表“驯服”恶魂的模型。 + - `QuadrupedModel#createBodyMesh` 现在接受两个布尔值,分别用于处理左后腿和右后腿纹理是否镜像。 +- `net.minecraft.client.multiplayer.ClientLevel` + - `DEFAULT_QUIT_MESSAGE` - 持有退出游戏文本的组件。 + - `disconnect(Copmonent)` - 断开与当前等级实例的连接,显示关联的组件。 +- `net.minecraft.client.renderer.entity.HappyGhastRenderer` - “驯服”恶魂的渲染器。 +- `net.minecraft.client.renderer.entity.layers.RopesLayer` - 用于“驯服”恶魂上的绳索的渲染层。 +- `net.mienecraft.client.renderer.entity.state.HappyGhastRenderState` - “驯服”恶魂的状态。 +- `net.minecraft.client.resources.model.EquipmentclientInfo$LayerType#HAPPY_GHAST_BODY` - 一个代表快乐恶魂身体的层。 +- `net.minecraft.client.resources.sounds.RidingHappyGhastSoundInstance` - 一个在骑乘快乐恶魂时播放的可Tick声音实例。 +- `net.minecraft.client.sounds` + - `MusicManager` + - `getCurrentMusicTranslationKey` - 返回当前正在播放的音乐的翻译键。 + - `setMinutesBetweenSongs` - 设置背景曲目之间的频率。 + - `showNowPlayingToastIfNeeded` - 如果需要,显示正在播放吐司。 + - `$MusicFrequency` - 正在播放的背景曲目的频率。 + - `SoundEngine$PlayResult` - 尝试播放的声音的起始状态。 +- `net.minecraft.commands.arguments` + - `HexColorArgument` - 一个接受十六进制颜色的整数参数。 + - `ResourceOrIdArgument` + - `createGrammar` - 创建用于解析参数的语法。 + - `$InlineResult` - 一个返回直接持有者的结果。 + - `$ReferenceResult` - 一个返回引用持有者的结果。 + - `$Result` - 一个表示某个参数解析结果的接口。 +- `net.minecraft.data.loot.LootTableProvider$MissingTableProblem` - 一个记录,保存某个缺失的内置表生成器的键。 +- `net.minecraft.data.recipes.RecipeProvider` + - `dryGhast` - 干燥恶魂的配方。 + - `harness` - 染色挽具的配方。 +- `net.minecraft.gametest.framework.GameTestTicker#startTicking` - 开始为游戏测试Tick运行器。 +- `net.minecraft.nbt.NbtUtils` + - `addCurrentDataVersion`、`addDataVersion`,将数据版本添加到某个 nbt 标签。 +- `net.minecraft.network.FriendlyByteBuf#writeEither`、`readEither` - 使用给定的流编码器/解码器处理 `Either`。 +- `net.minecraft.network.codec.ByteBufCodecs` + - `RGB_COLOR` - 一个使用三个字节写入 RGB 的流编解码器。 + - `lenientJson` - 创建一个以宽松模式解析 json 的流编解码器。 + - `optionalTagCodec` - 创建一个使用提供的 `NbtAccounter` 解析 `Optional` 包装的 `Tag` 的流编解码器。 +- `net.minecraft.network.protocol.game` + - `ServerboundChangeGameModePacket` - 更改当前游戏模式。 + - `ServerboundCustomClickActionPacket` - 在服务器上执行自定义操作,目前什么都不做。 +- `net.minecraft.server.MinecraftServer#handleCustomClickAction` - 处理从点击事件发送的自定义操作。 +- `net.minecraft.server.level.ServerLevel` + - `updateNeighboursOnBlockSet` - 更新当前位置的邻居。如果方块不相同(不包括其属性),则调用 `BlockState#affectNeighborsAfterRemoval`。 + - `waitForChunkAndEntities` - 添加一个任务,使服务器等待,直到实体在提供的区块范围内加载。 +- `net.minecraft.sources.SoundSource#UI` - 来自某些用户界面的声音。 +- `net.minecraft.stats` + - `RecipeBookSettings#MAP_CODEC` + - `ServerRecipeBook#pack`、`loadUntrusted`、`$Packed` - 处理编码和解码配方书的数据。 +- `net.minecraft.util` + - `ARGB` + - `setBrightness` - 使用 0 到 1 之间的浮点数返回某个颜色的亮度。 + - `color` - 从浮点红色和整数 alpha 返回一个 ARGB 颜色。 + - `ExtraCodecs` + - `VECTOR2F` + - `VECTOR3I` + - `NBT` + - `LenientJsonParser` - 一个使用宽松规则的 json 解析器。 + - `Mth#smallestSquareSide` - 取一个数的平方根的上取整。 + - `StrictJsonParser` - 一个使用严格规则的 json 解析器。 +- `net.minecraft.world` + - `Difficulty#STREAM_CODEC` + - `ItemStackWithSlot` - 一个记录,包含一个堆栈及其槽位索引。 +- `net.minecraft.world.entity` + - `Entity` + - `MAX_MOVEMENTS_HANDELED_PER_TICK` - 在给定Tick中可以应用于实体的最大移动数。 + - `isInClouds` - 返回实体的 Y 位置是否在云高度和其上方四个单位之间。 + - `teleportSpectators` - 传送当前从玩家视角观看的旁观者。 + - `isFlyingVehicle` - 返回载具是否可以飞行。 + - `clearMovementThisTick` - 清除实体在本Tick中将进行的所有移动。 + - `EntityAttachments#getAverage` - 返回所有附着点的平均位置。 + - `ExperienceOrb` + - `awardWithDirection` - 添加一个通过指定向量移动的经验球。 + - `unstuckIfPossible` - 尝试查找并将球移动到空闲位置。 + - `Mob` + - `isWithinHome` - 返回该位置是否在实体的限制半径内。 + - `canShearEquipment` - 返回当前玩家是否可以从该生物上剪下装备。 +- `net.minecraft.world.entity.ai.control.MoveControl#setWait` - 将操作设置为 `WAIT`。 +- `net.minecraft.world.entity.ai.goal.TemptGoal` + - `stopNavigation`、`navigateTowards` - 处理向玩家的导航。 + - `$ForNonPathfinders` - 一个引诱目标,它导航到想要的位置,而不是立即寻路。 +- `net.minecraft.world.entity.ai.navigation.PathNavigation#canNavigateGround` - 返回实体是否可以在陆地上寻路。 +- `net.minecraft.world.entity.ai.sensing.AdultSensorAnyType` - 一个忽略实体是否与幼崽相同类型的成年传感器。 +- `net.minecraft.world.entity.animal` + - `HappyGhast` - 一个代表快乐恶魂的实体。 + - `HappyGhastAi` - 快乐恶魂的大脑。 +- `net.minecraft.world.entity.decoration.ArmorStand` + - `setArmorStandPose`、`getArmorStandPose`、`$ArmorStandPose` - 处理盔甲架的姿势。 +- `net.minecraft.world.entity.monster.Ghast` + - `faceMovementDirection` - 旋转实体以面对其当前移动方向。 + - `$RandomFloatAroundGoal#getSuitableFlyToPosition` - 获取恶魂应飞向的位置。 +- `net.minecraft.world.entity.player.Inventory#SLOT_BODY_ARMOR`、`SLOT_SADDLE` - 相应槽位的索引。 +- `net.minecraft.world.entity.projectile.ProjectileUtil#computeMargin` - 根据其Tick计数计算要检查的给定实体的边界框边距。 +- `net.minecraft.world.item.component` + - `ItemAttributeModifiers` + - `forEach` - 将消费者应用于槽位组内的所有属性。 + - `$Builder#add` - 添加一个要应用于给定槽位组的属性,并带有显示。 + - `$Display` - 定义属性修改器应如何在其工具提示中显示。 + - `$Default` - 显示默认属性显示。 + - `$Hidden` - 不显示任何属性信息。 + - `$OverrideText` - 用提供的组件覆盖属性文本。 + - `Equippable$Builder` + - `setCanBeSheared` - 设置装备是否可以从实体上剪下。 + - `setShearingSound` - 设置从实体上剪下一件装备时要播放的声音。 + - `ResolvableProfile#pollResolve` - 返回存储的 id 或名称的配置文件。 +- `net.minecraft.world.item.equipment.Equippable#harness` - 表示要装备的挽具。 +- `net.minecraft.world.level` + - `CollisionGetter` + - `getPreMoveCollisions` - 返回一个包含给定边界框和未来移动方向上的实体和方块碰撞的形状的可迭代对象。 + - `getBlockCollisionsFromContext` - 从给定的碰撞上下文中获取方块形状。 + - `GameType#STREAM_CODEC` + - `Level` + - `precipitationAt` - 返回给定位置的降水。 + - `onBlockEntityAdded` - 当方块实体被添加到等级时要运行的逻辑。 +- `net.minecraft.world.level.block` + - `BaseRailBlock#rotate` - 在关联方向上旋转当前的铁轨形状。 + - `DriedGhastBlock` - 一个代表干燥恶魂的方块。 +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawner$FullConfig` - 代表试炼的整个配置。 +- `net.minecraft.world.level.dimension.DimensionDefaults` + - `CLOUD_THICKNESS` - 云的方块厚度。 + - `OVERWORLD_CLOUD_HEIGHT` - 主世界的云高度等级。 +- `net.minecraft.world.level.levelgen.flat.FlatLayerInfo#heightLimited` - 返回一个新的层信息,当前高度是否限制为指定值,只要该值不在最大范围内。 +- `net.minecraft.world.phys` + - `AABB` + - `intersects` - 返回 `BlockPos` 是否与此框相交。 + - `distanceToSqr` - 返回边界框从其最远点的平方距离。 + - `Vec3#rotateClockwise90` - 将向量顺时针旋转 90 度(翻转 x 和 z 并反转新的 x 值)。 +- `net.minecraft.world.phys.shapes.CollisionContext` + - `withPosition` - 返回实体的碰撞上下文及其底部 y 位置。 + +### 变更列表 + +- `com.mojang.blaze3d.platform.Window#setGuiScale`、`getGuiScale` 现在处理 `int` 而不是 `double` +- `net.mineraft` + - `DetectedVersion` 不再实现 `WorldVersion` + - `WorldVersion` 方法由于使用 `WorldVersion$Simple` 而使用记录命名模式 + - `getDataVersion` -> `dataVersion` + - `getId` -> `id` + - `getName` -> `name` + - `getProtocolVersion` -> `protocolVersion` + - `getPackVersion` -> `packVersion` + - `getBuildTime` -> `buildTime` + - `isStable` -> `stable` +- `net.minecraft.client` + - `GameNarrator` + - `sayChat` -> `sayChatQueued` + - `say` -> `saySystemQueued` + - `sayNow` -> `saySystemNow` + - `Minecraft` + - `grabPanoramixScreenshot` 不再接受要设置的窗口宽度和高度 + - `disconnect()` -> `disconnectWithProgressScreen` + - `Screenshot#grab`、`takeScreenshot` 现在接受一个表示缩小因子的 `int` +- `net.minecraft.client.main.GameConfig$QuickPlayData` 现在接受一个 `$QuickPlayVariant` + - `path` -> `logPath` + - `singleplayer` -> `variant` 带有 `$QuickPlaySinglePlayerData` + - `multiplayer` -> `variant` 带有 `$QuickPlayMultiplayerData` + - `realms` -> `variant` 带有 `$QuickPlayRealmsData` + - 对于 `singleplayer`、`multiplayer`、`realms` 的 null 由带有 `$QuickPlayDisabled` 的 `variant` 表示 +- `net.minecraft.client.data.models.ItemModelGenerators#generateWolfArmor` -> `generateTwoLayerDyedItem` +- `net.minecraft.client.gui.components.DebugScreenOverlay#render3dCrosshair` 现在接受当前的 `Camera` +- `net.minecraft.client.gui.components.debugchart.ProfilerPieChart` + - `RADIUS` 现在是公开的 + - `CHART_Z_OFFSET` -> `PIE_CHART_THICKNESS`,现在是公开的 +- `net.minecraft.client.multiplayer` + - `ClientLevel$ClientLevelData#getClearColorScale` -> `voidDarknessOnsetRange`,不是一对一 + - `MultiPlayerGameMode#createPlayer` 现在接受一个 `Input` 而不是 `boolean` +- `net.minecraft.client.player.LocalPlayer` 现在接受最后发送的 `Input` 而不是 shift 键的 `boolean` + - `getLastSentInput` - 获取从服务器最后发送的输入。 +- `net.minecraft.client.quickplay.QuickPlay#connect` 现在接受 `GameConfig$QuickPlayVariant` 而不是 `GameConfig$QuickPlayData` +- `net.minecraft.client.renderer` + - `DimensionSpecialEffects` 不再接受当前云级别以及是否有地面 + - `LightTexture#getTarget` -> `getTexture` +- `net.minecraft.client.renderer.blockentity.BlockEntityRenderer#shouldRenderOffscreen` 不再接受 `BlockEntity` +- `net.minecraft.client.resources` + - `AbstractSoundInstance#sound` 现在是 `Nullable` + - `SoundInstance#getSound` 现在是 `Nullable` +- `net.minecraft.client.sounds` + - `SimpleSoundInstance#forMusic` 现在也接受 `float` 音量 + - `SoundEngine` 现在接受 `MusicManager` + - `pause` -> `pauseAllExcept`,不是一对一 + - `play` 现在返回一个 `$PlayResult` + - `SoundManager` 现在接受 `MusicManager` + - `pause` -> `pauseAllExcept`,不是一对一 + - `play` 现在返回一个 `SoundEngine$PlayResult` +- `net.minecraft.commands.arguments` + - `ResourceOrIdArgument` 现在接受一个任意编解码器,而不是 `Holder` 包装的值 + - `ERROR_INVALID` -> `ERROR_NO_SUCH_ELEMENT`,现在是公开的,不是一对一 + - `VALUE_PARSER` -> `OPS`,现在是公开的,不是一对一 + - `ResourceSelectorArgument#getSelectedResources` 不再接受 `ResourceKey` +- `net.minecraft.commands.functions.StringTemplate` + - `fromString` 不再接受行号 + - `isValidVariableName` 现在是公开的 +- `net.minecraft.data.recipes.RecipeProvider#colorBlockWithDye` -> `colorItemWithDye`,现在接受 `RecipeCategory` +- `net.minecraft.gametest.framework.GameTestInfo#prepareTestStructure` 现在可为 null +- `net.minecraft.network` + - `Connection#send` 现在接受一个 `ChannelFutureListener` 而不是 `PacketSendListener` + - `FriendlyByteBuf#readJsonWithCodec` -> `readLenientJsonWithCodec` + - `PacketSendListener` 现在是一个类,其方法返回 `ChannelFutureListener` 而不是 `PacketSendListener` + - `onSuccess`、`onFailure` 已移除 +- `net.minecraft.network.codec` + - `ByteBufCodecs#fromCodec` 现在有一个接受某些 ops 和一个编解码器的重载 + - `StreamCodec#composite` 现在有一个接受十个参数的重载 +- `net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket` 现在是一个记录 +- `net.minecraft.network.protocol.game` + - `ClientboundChangeDifficultyPacket` 现在是一个记录 + - `ClientboundCommandsPacket` 现在接受一个 `$NodeInspector` + - `getRoot` 现在是泛型的,接受一个 `$NodeBuilder` + - `$NodeBuilder` - 一个给定命令的构建器。 + - `$NodeInspector` - 一个检查给定命令节点信息的代理。 + - `ServerboundChangeDifficultyPacket` 现在是一个记录 +- `net.minecraft.server.ReloadableServerRegistries$Holder#lookup` 返回一个 `HolderLookup$Provider` +- `net.minecraft.server.network.ServerCommonPacketListenerImpl#send` 现在接受一个 `ChannelFutureListener` 而不是 `PacketSendListener` +- `net.minecraft.sounds.Music` 现在是一个记录 +- `net.minecraft.stats.RecipeBookSettings` + - `getSettings` 现在是公开的 + - `$TypeSettings` 现在是公开的 +- `net.minecraft.world.entity` + - `AreaEffectCloud#setParticle` -> `setCustomParticle` + - `Entity` + - `checkSlowFallDistance` -> `checkFallDistanceAccumulation` + - `collidedWithFluid`、`collidedWithShapeMovingFrom` 现在是公开的 + - `canBeCollidedWith` 现在接受与其碰撞的实体 + - `spawnAtLocation` 现在有一个接受 `Vec3` 作为偏移位置的重载 + - `removeLatestMovementRecordingBatch` -> `removeLatestMovementRecording` + - `EntityReference` 现在是 final + - `ExperienceOrb` 现在有一个接受两个向量作为位置和移动的重载 + - `FlyingMob` 被调用 `LivingEntity#travelFlying` 取代 + - `LivingEntity#canBreatheUnderwater` 不再是 `final` + - `Mob` + - `restrictTo` -> `setHomeTo` + - `getRestrictCenter` -> `getHomePosition` + - `getRestrictRadius` -> `getHomeRadius` + - `clearRestriction` -> `clearHome` + - `hasRestriction` -> `hasHome` +- `net.minecraft.world.entity.ai.attributes` + - `AttributeInstance` + - `save` -> `pack`、`$Packed`;不是一对一 + - `load` -> `apply`,不是一对一 + - `AttributeMap` + - `save` -> `pack`;不是一对一 + - `load` -> `apply`,不是一对一 +- `net.minecraft.world.entity.ai.behavior` + - `AnimalPanic` 现在有接受半径或位置获取器的重载 + - `BabyFollowAdult#create` 现在返回一个 `OneShot`,并且可以接受一个 `boolean` 表示是否瞄准眼睛位置 + - `EntityTracker` 现在可以接受一个 `boolean` 表示是否瞄准眼睛位置 + - `FollowTemptation` 现在有一个重载,检查实体是否需要跟踪实体的眼睛高度。 +- `net.minecraft.world.entity.ai.goal.TemptGoal` 现在有一个接受停止距离的重载 + - `mob` 现在是一个 `Mob` + - `speedModifier` 现在是 `protected` +- `net.minecraft.world.entity.ai.memory.MemoryModuleType#NEAREST_VISIBLE_ADULT` 现在持有 `LivingEntity` +- `net.minecraft.world.entity.ai.navigation` + - `FlyingPathNavigation`、`GroundPathNavigation#setCanOpenDoors` -> `PathNavigation#setCanOpenDoors` +- `net.minecraft.world.entity.ai.sensing.AdultSensor` 现在查找 `LivingEntity` + - `setNearestVisibleAdult` 现在是 `protected` +- `net.minecraft.world.entity.animal.*Variants#selectVariantToSpawn` -> `entity.variant.VariantUtils#selectVariantToSpawn`,不是一对一 +- `net.minecraft.world.entity.animal.Fox#isJumping` -> `LivingEntity#isJumping` +- `net.minecraft.world.entity.animal.horse.AbstractHorse` + - `isJumping` -> `LivingEntity#isJumping` + - `setStanding` 现在接受一个 `int` 而不是 `boolean` 作为站立计数器 + - `false` 逻辑已移至 `clearStanding` +- `net.minecraft.world.entity.monster` + - `Ghast` 现在实现 `Mob` + - `$GhastLookGoal` 现在是公开的,接受一个 `Mob` + - `$GhastMoveControl` 现在是公开的,接受一个布尔值表示移动时是否应小心,以及一个提供的布尔值表示恶魂是否应停止移动 + - `$RandomFloatAroundGoal` 现在是 `public`,接受一个 `Mob` 和一个方块距离 + - `Phantom` 现在实现 `Mob` +- `net.minecraft.world.entity.player` + - `Abilities` + - `addSaveData` -> `pack`、`$Packed`;不是一对一 + - `loadSaveData` -> `apply`,不是一对一 + - `Player` 不再接受 `BlockPos` 和 y 旋转 +- `net.minecraft.world.entity.projectile` + - `AbstractThrownPotion#onHitAsPostion` 现在接受一个 `HitResult` 而不是可为 null 的 `Entity` + - `EyeOfEnder#signalTo` 现在接受一个 `Vec3` 而不是 `BlockPos` + - `Projectile` + - `ownerUUID`、`cachedOwner` -> `owner`,现在是 protected;不是一对一 + - `setOwner` 现在有一个接受 `EntityReference` 的重载 + - `ProjectileUtil` + - `DEFAULT_ENTITY_HIT_RESULT_MARGIN` 现在是公开的 + - `getEntityHitResult` 现在接受一个 `Projectile` 而不是 `Entity` +- `net.minecraft.world.item.ItemStack` + - `forEachModifier` 现在接受一个提供修改器显示的 `TriConsumer` + - `hurtAndBreak` 现在有一个重载,从 `InteractionHand` 获取 `EquipmentSlot` +- `net.minecraft.world.item.equipment.Equippable` 现在接受装备是否可以从实体上剪下以及剪下时要播放的声音 +- `net.minecraft.world.level.BlockGetter` + - `forEachBlockIntersectedBetween` 现在返回一个布尔值,表示在相交区域中访问的每个方块是否可以成功访问 + - `$BlockStepVisitor#visit` 现在返回是否可以成功移动到该位置 +- `net.minecraft.world.level.block.AbstractCauldronBlock#SHAPE` 现在是 protected +- `net.minecraft.world.level.block.entity` + - `BlockEntity#getNameForReporting` 现在是公开的 + - `SignBlockEntity#executeClickCommandsIfPresent` 现在接受一个 `ServerLevel` 而不是 `Level`,参数重新排序 + - `StructureBlockEntity#saveStructure` 现在接受一个要忽略的方块列表 +- `net.minecraft.world.level.block.entity.trialspawner` + - `TrialSpawner` 现在接受一个 `$FullConfig` + - `getConfig` -> `activeConfig` + - `get*Config` -> `*config` + - `getData` -> `getStateData` + - `TrialSpawnerData` -> `TrialSpawnerStateData`,序列化形式为 `TrialSpawnerStateData$Packed`,不是一对一 +- `net.minecraft.world.level.block.sounds.AmbientDesertBlockSoundsPlayer#playAmbientBlockSounds` 已拆分为 `playAmbientSandSounds`、`playAmbientDryGrassSounds`、`playAmbientDeadBushSounds`、`shouldPlayDesertDryVegetationBlockSounds`;不是一对一 +- `net.minecraft.world.level.dimension.DimensionType` 现在接受一个可选的整数表示云高度等级 +- `net.minecraft.world.level.entity` + - `PersistentEntitySectionManager#processPendingLoads` 现在是公开的 + - `UUIDLookup#getEntity` 现在可以返回 null +- `net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate#fillFromWorld` 现在接受一个要忽略的方块列表,而不是单个 `Block` +- `net.minecraft.world.level.storage.DataVersion` 现在是一个记录 +- `net.minecraft.world.phys.shapes.CollisionContext#placementContext` 现在接受一个 `Player` 而不是 `Entity` + +### 移除列表 + +- `net.minecraft.client.Minecraft#disconnect(Screen)` +- `net.minecraft.client.renderer` + - `DimensionSpecialEffects#getCloudHeight`、`hasGround` + - `LevelRenderer#updateGlobalBlockEntities` +- `net.minecraft.client.renderer.texture.AbstractTexture` + - `defaultBlur` + - `setFilter` +- `net.minecraft.network.chat.Component$Serializer`、`$SerializerAdapter` +- `net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket$Action#*_SHIFT_KEY` +- `net.minecraft.server.ReloadableServerRegistries$Holder#getKeys` +- `net.minecraft.server.players.PlayerList#getPlayerForLogin` +- `net.minecraft.stats` + - `RecipeBookSettings#read`、`write` + - `ServerRecipeBook#toNbt`、`fromNbt` +- `net.minecraft.util` + - `GsonHelper#fromNullableJson(..., boolean)`、`fromJson(..., boolean)` + - `LowerCaseEnumTypeAdapterFactory` +- `net.minecraft.world.entity.ai.attributes.AttributeInstance#ID_FIELD`、`TYPE_CODEC` +- `net.minecraft.world.entity.animal.horse.AbstractHorse#setIsJumping` +- `net.minecraft.world.entity.animal.sheep.Sheep#getColor` +- `net.minecraft.world.entity.monster.Drowned#waterNavigation`、`groundNavigation` +- `net.minecraft.world.entity.projectile.Projectile#findOwner`、`setOwnerThroughUUID` +- `net.minecraft.world.level.Level#disconnect()` +- `net.minecraft.world.level.block` + - `AbstractCauldronBlock#isEntityInsideContent` + - `TerracottaBlock` +- `net.minecraft.world.level.block.entity.trialspawner.TrialSpawner` + - `*_CONFIG_TAG_NAME` + - `codec` +- `net.minecraft.world.level.dimension.DimensionType#parseLegacy` diff --git a/primers-doc/1.21.7-from-1.21.6.md b/primers-doc/1.21.7-from-1.21.6.md new file mode 100644 index 0000000..ffa3c81 --- /dev/null +++ b/primers-doc/1.21.7-from-1.21.6.md @@ -0,0 +1,38 @@ +# Minecraft 1.21.6 -> 1.21.7 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.6 迁移到 1.21.7。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.7&tab=changelog) 中找到它们的列表。 + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 新增列表 + +- `com.mojang.blaze3d.opengl.DirectStateAccess#copyBufferSubData` - 将一个缓冲区对象的数据存储的全部或部分复制到另一个缓冲区对象的数据存储。 +- `com.mojang.blaze3d.pipeline.BlendFunction#INVERT` - 反转 RGB 源和目标的混合因子。Alpha 使用源的默认因子,目标使用零。 +- `com.mojang.blaze3d.systems.CommandEncoder#copyToBuffer` - 将一个缓冲区切片的数据存储复制到另一个缓冲区切片。 +- `net.minecraft.Util#isAarch64` - 返回操作系统架构是否使用 aarch64。 +- `net.minecraft.client.gui.GuiGraphics#textHighlight` - 在提供的边界周围添加一个高亮框。 +- `net.minecraft.client.renderer.RenderPipelines#GUI_INVERT` - 用于绘制具有反色效果的 GUI 元素的渲染管线。 +- `net.minecraft.client.renderer.item.TrackingItemRenderState` - 一个跟踪用于渲染物品堆栈的模型源的渲染状态。 + +### 变更列表 + +- `com.mojang.blaze3d.pipeline.RenderPipeline$Builder#withColorLogic` 现已弃用 +- `net.minecraft.client.gui.renderer.GuiRenderer#MIN_GUI_Z` 现在是私有的 +- `net.minecraft.client.gui.render.state.GuiItemRenderState` 现在接受 `TrackingItemRenderState` 而不是 `ItemStackRenderState` + - `itemStackRenderState` 现在返回 `TrackingItemRenderState` +- `net.minecraft.client.renderer.RenderPipelines#GUI_TEXT_HIGHLIGHT` 现在使用 `ADDITIVE` 混合函数而不是 `OR_REVERSE` 颜色逻辑 +- `net.minecraft.client.renderer.item.ItemStackRenderState#getModelIdentity` -> `TrackingItemRenderState#getModelIdentity` + +### 移除列表 + +- `net.minecraft.client.renderer.item.ItemStackRenderState#clearModelIdentity` diff --git a/primers-doc/1.21.8-from-1.21.7.md b/primers-doc/1.21.8-from-1.21.7.md new file mode 100644 index 0000000..062bbd1 --- /dev/null +++ b/primers-doc/1.21.8-from-1.21.7.md @@ -0,0 +1,19 @@ +# Minecraft 1.21.7 -> 1.21.8 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.7 迁移到 1.21.8。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.8&tab=changelog) 中找到它们的列表。 + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 新增列表 + +- `com.mojang.blaze3d.GraphicsWorkarounds` - 一个用于解决特定图形硬件问题的辅助工具。 diff --git a/primers-doc/1.21.9-from-1.21.8.md b/primers-doc/1.21.9-from-1.21.8.md new file mode 100644 index 0000000..5e0532b --- /dev/null +++ b/primers-doc/1.21.9-from-1.21.8.md @@ -0,0 +1,2532 @@ +# Minecraft 1.21.8 -> 1.21.9 模组迁移入门文档 + +本文档是一个高层次、非详尽的概述,介绍如何将您的模组从 1.21.8 迁移到 1.21.9。本文不涉及任何特定的模组加载器,只关注原版类的变更。所有提供的名称均使用官方的 Mojang 映射。 + +本入门文档采用 [知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/) 授权,因此您可以自由地将其用作参考,并请留下链接以便其他读者查阅。 + +如果存在任何不正确或缺失的信息,请在本仓库提交 issue,或在 Neoforged Discord 服务器中 @ChampionAsh5357。 + +感谢: + +- @Soaryn 指出了一些不推荐的渲染用法 +- @Deximus-Maximus 修正了一些拼写错误 + +## 资源包变更 + +原版中有许多面向用户的变更为未在下面讨论,但这些变更可能与模组制作者相关。您可以在 [Misode 的版本更新日志](https://misode.github.io/versions/?id=1.21.9&tab=changelog) 中找到它们的列表。 + +## 调试大修 + +整个调试系统已被彻底改造,从暴露内部调试工具到调试屏幕。本文档旨在为处理您自己的调试屏幕和渲染器添加提供一个高层次概述。 + +### 调试渲染器 + +原版现在允许用户通过 JVM 属性启用 `-DMC_DEBUG_ENABLED` 以及其他所需标志来查看内部 API 提供的调试渲染器。用户可以利用这些暴露的功能,通过原版提供的管道来处理自己的渲染器。本概述将通过修补现有渲染器和订阅者(根据需要)进行说明,但这些通常可以在任何需要的地方设置。原版提供的好处是将其集成到现有对象(例如,实体、方块实体)以及跨网络的通用同步中。当然,您始终可以使用简单的 `boolean` 代替。毕竟,尽管 `SharedConstants` 中的标志是 final 的,但它们仍然每刻都会被检查。 + +#### 订阅调试器 + +在大多数情况下,您想要渲染和调试的信息存储在服务器端。有时,相关信息会在客户端同步,但大多数情况下,它通常是某种仅用于渲染的部分状态。 + +```java +// 服务器上的示例对象 +public record ExampleObject(Block held, int count) {} + +// 客户端上的示例 +// Count 不用于渲染,仅用于服务器逻辑 +public class ExampleRenderState { + Block held; +} +``` + +因此,如果我们想查看来自服务器的额外数据,我们需要一种方法不仅能将其同步到客户端,还能在值发生变化时更新它。为此,原版提供了 `DebugSubscription`:一个类,用于存储同步对象所需的信息(如果它已更改)。构造函数包含两个字段:用于跨网络同步对象的 `StreamCodec`,以及一个可选的 `int`,当大于零时,如果在指定时间内没有更多更新,将从客户端清除同步的值。 + +为了处理与同步相关的逻辑,服务器使用 `TrackingDebugSynchronizer` 来处理玩家监听器并在必要时同步对象,并使用 `LevelDebugSynchronizers` 来处理同步器的通用跟踪和Tick。然后,这些数据被发送到 `ClientDebugSubscriber` 进行存储,并发送到 `DebugRenderer` 进行渲染。请注意,客户端只有在他们是单机世界的拥有者或服务器管理员时才能看到调试信息。此外,客户端只能请求添加到 `ClientDebugSubscriber#requestedSubscriptions` 提供的集合中的订阅。 + +`DebugSubscription` 必须注册到 `BuiltInRegistries#DEBUG_SUBSCRIPTION`: + +```java +public static final DebugSubscription EXAMPLE_OBJECt = Registry.register( + BuiltInRegistries.DEBUG_SUBSCRIPTION + ResourceLocation.withNamespaceAndPath("examplemod", "example_object"), + new DebugSubscription<>( + // 用于同步示例对象的流编解码器 + StreamCodec.composite( + ByteBufCodecs.registry(Registries.BLOCK), ExampleObject::block, + ByteBufCodecs.VAR_INT, ExampleObject::count, + ExampleObject::new + ), + // 更新之间的最大刻数 + // 在此之后数据将从客户端清除 + // 如果永不过期则设置为零 + 0 + ) +); +``` + +为了能够正确检查更新,所使用的对象必须正确实现 `hashCode` 和 `equals`,而不是依赖对象标识。 + +#### 调试源 + +那么,我们如何告诉同步器跟踪和更新我们的 `DebugSubscription`?您可以扩展 `TrackingDebugSynchronizer` 或其子类,并通过修补 `LevelDebugSynchronizers` 或创建自己的类来实现跟踪和同步逻辑。但是,如果您要跟踪的数据直接附加到 `LevelChunk`、`Entity` 或 `BlockEntity`,并且可以从其关联的服务器对象更新,则可以使用 `DebugValueSource`。 + +`DebugValueSource` 是一种将 `DebugSubscription` 注册为 `TrackingDebugSynchronizer$SourceSynchronizer` 的方法。这将每刻轮询并向每个启用订阅的跟踪源玩家发送更新。注册 `DebugSubscription` 通过 `DebugValueSource#registerDebugValues` 完成,它接受服务器等级和 `$Registration` 接口。然后通过 `$Registration#register` 传入订阅和构造订阅值的供应商来处理注册。 + +```java +// 假设我们在下面的类中有某个 ExampleObject exampleObject + +// 对于某个 BlockEntity、Entity 或 LevelChunk 子类 +@Override +public void registerDebugValues(ServerLevel level, DebugValueSource.Registration registrar) { + super.registerDebugValues(level, registrar); + // 注册我们的订阅 + registrar.register( + // 订阅 + EXAMPLE_OBJECT, + // 提供的订阅对象 + () -> this.exampleObject + ); +} +``` + +#### 渲染调试信息 + +一旦信息同步到客户端并存储在 `ClientDebugSubscriber` 中(假设您使用上述方法),我们现在需要将该信息渲染到屏幕上。这通常通过 `DebugRenderer#render` 处理,它在运行关联的渲染器之前通过 `refreshRendererList` 检查启用的调试渲染器。从技术上讲,数据在渲染过程中的任何点都可以获得,所以位置并不特别重要,但本文将假设您修补 `refreshRendererList` 以将您自己的渲染器添加到不透明或半透明渲染器中。 + +所有渲染器都实现 `DebugRenderer$SimpleDebugRenderer` 来调用 `render`,它提供当前的 `PoseStack`、缓冲区源、相机 XYZ 和 `Frustum`。此外,原版通过 `Connection#createDebugValueAccess` 传入一个 `DebugValueAccess`,以从 `ClientDebugSubscriber` 获取同步的调试信息。`DebugRenderer` 提供了使用 `render*` 在特定位置渲染文本或框的简单方法。 + +`DebugValueAccess` 包含两种类型的方法:`get*Value` 用于获取特定源(例如,位置、实体)的调试对象;以及 `forEach*`,用于遍历所有发送调试对象的源。您使用哪个取决于您将 `DebugSubscription` 注册到哪个源。 + +```java +// 我们将假设我们的示例对象已注册到一个实体 +public class ExampleObjectRenderer implements DebugRenderer.SimpleDebugRenderer { + + @Override + public void render(PoseStack poseStack, MultiBufferSource bufferSource, double x, double y, double z, DebugValueAccess access, Frustum frustum) { + // 遍历所有带有我们示例对象的块 + access.forEachEntity(EXAMPLE_OBJECT, (entity, exampleObject) -> { + // 渲染调试信息 + DebugRenderer.renderTextOverMob( + poseStack, bufferSource, entity, + // 文本 Y 偏移(实体显示很多信息) + 100, + // 要渲染的文本 + "持有计数: " + exampleObject.count(), + // 文本颜色 + 0xFFFFFFFF, + // 文本缩放 + 1f + ); + }); + } +} +``` + +## 调试屏幕 + +调试屏幕允许用户启用、禁用或仅在 F3 中显示特定组件。这个模块化系统允许模组制作者将自己的调试条目添加到屏幕中。并非本文说明的所有部分都可以在没有更多模组工作的情况下访问,因此这些区域将被特别指出。 + +#### `DebugScreenEntry` + +每个调试选项都有自己的条目,要么定义显示的内容(例如,fps、内存),要么是无操作,由单独的实现处理(例如,实体碰撞箱、区块边界)。这被称为 `DebugScreenEntry`,它定义了三个方法。 + +首先是 `category`。在原版中,这几乎总是 `DebugEntryCategory#SCREEN_TEXT`,因为所有调试条目所做的就是在屏幕上绘制文本。另一个可用选项 `RENDERER` 仅用于无操作,因为渲染选项总是独立于调试屏幕渲染。`DebugEntryCategory` 只是一个带有标签和某种排序键值的记录,因此可以通过调用构造函数添加更多。该类别仅用于在调试选项屏幕中搜索。 + +接下来是 `isAllowed`。此方法决定调试选项是否应独立于条目状态在屏幕上渲染。默认情况下,仅当 `Minecraft#showOnlyReducedInfo` 辅助功能选项为 false 时才为 true。一些调试条目覆盖此方法以始终返回 true,或者如果某个其他检查通过。 + +最后是 `display` 方法。它负责使用 `DebugScreenDisplayer` 将文本绘制到屏幕。它还接受当前的 `Level`、客户端区块和服务器区块。`DebugScreenDisplayer` 有四个方法,每个方法都将文本绘制到屏幕。首先,有标准的 `addLine` 方法,它只是将字符串添加到左侧或右侧,具体取决于其渲染的元素。这些将一个接一个地出现。然后,有 `addPriorityLine`,它将始终添加到左侧或右侧的顶部。最后,有 `addToGroup`,它接受一个额外的键,将行渲染为一个单独的组,并在末尾添加一个额外的换行符。 + +```java +public class ExampleDebugEntry implements DebugScreenEntry { + + public static final ResourceLocation GROUP_ONE = ResourceLocation.fromNamespaceAndPath("examplemod", "group_one"); + public static final ResourceLocation GROUP_TWO = ResourceLocation.fromNamespaceAndPath("examplemod", "group_two"); + + + @Override + public void display(DebugScreenDisplayer displayer, @Nullable Level level, @Nullable LevelChunk clientChunk, @Nullable LevelChunk serverChunk) { + // 如果它是屏幕上唯一的条目,以下将显示如下: + // 左侧第一! 右侧第一! + // + // 世界你好! 随机文本! + // 洛雷姆·伊普苏姆。 + // 我是另一个组! + // 我是一个组 这将出现在没有换行符之后! + // 全部排成一行 + // 以列表形式提供。 + // + + displayer.addLine("世界你好!"); + displayer.addLine("洛雷姆·伊普苏姆。"); + displayer.addLine("随机文本!"); + + // 这些将首先显示 + displayer.addPriorityLine("左侧第一!"); + displayer.addPriorityLine("右侧第一!"); + + // 这些将根据键单独分组 + displayer.addToGroup(GROUP_ONE, List.of( + "我是一个组", + "全部排成一行", + "以列表形式提供。" + )); + + displayer.addToGroup(GROUP_TWO, "我是另一个组!"); + displayer.addToGroup(GROUP_TWO, "这将出现在没有换行符之后!"); + } + + @Override + public boolean isAllowed(boolean reducedDebugInfo) { + // 无论辅助功能选项如何,始终显示 + return true; + } +} +``` + +然后,只需将您的条目注册到 `DebugScreenEntries` 即可显示。可以通过调试菜单使用提供的键进行切换。 + +```java +// 此方法是私有的,因此需要扩宽其访问权限 +DebugScreenEntries.register( + // id,这将显示在选项屏幕上 + ResourceLocation.fromNamespaceAndPath("examplemod", "example_entry"), + // 屏幕条目 + new ExampleScreenEntry(); +); +``` + +#### 外部切换和检查 + +如果您想从选项菜单单独切换活动状态怎么办?如果您想检查条目是否已启用以在游戏中显示调试数据怎么办?这可以通过 `Minecraft` 实例访问 `DebugScreenEntryList` 来完成。 + +切换当前状态可以通过 `DebugScreenEntryList#toggleStatus` 完成。其行为取决于当前活动的屏幕。基本上,调用 toggle 将始终翻转调试条目的开关:如果它当前不在屏幕上,它将在屏幕上渲染,反之亦然。如果在 F3 中,那么 toggle on 将仅在 F3 打开时渲染该调试条目。 + +然后可以使用 `DebugScreenEntryList#isCurrentlyEnabled` 检查条目的状态。这将仅检查调试屏幕是否在当前打开的列表中,而不检查 `DebugScreenEntry#isAllowed`。 + +```java +// 让我们创建另一个条目 +public static final ResourceLocation EXAMPLE_TOGGLE = DebugScreenEntries.register( + ResourceLocation.fromNamespaceAndPath("examplemod", "example_toggle"), + // 我们使用无操作,因为没有文本显示 + new DebugEntryNoop(); +); + +// 切换: +Minecraft.getInstance().debugEntries.toggleStatus(EXAMPLE_TOGGLE); + +// 检查是否启用: +if (Minecraft.getInstance().debugEntries.isCurrentlyEnabled(EXAMPLE_TOGGLE)) { + // ... +} +``` + +#### 配置文件 + +配置文件是根据用户意愿配置的预定义预设。目前,配置文件被硬编码为默认或性能。要扩展系统,您需要能够动态地向 `DebugScreenProfile` 枚举添加条目,使 `DebugScreenEntries#PROFILES` 映射可变以添加您的配置文件和预设,并使用您的配置文件按钮修改调试选项屏幕。 + +- `net.minecraft.SharedConstants` + - `DEBUG_SHUFFLE_MODELS` - 一个可能打乱模型加载顺序的标志。 + - `DEBUG_FLAG_PREFIX` - 放在每个调试标志前面的前缀。 + - `USE_DEBUG_FEATURES` -> `DEBUG_ENABLED` + - `DEBUG_RENDER` 已移除 + - `DEBUG_WORLDGENATTEMPT` 已移除 + - `debugGenerateStripedTerrainWithoutNoise` 已移除 + - `DEBUG_RESOURCE_GENERATION_OVERRIDE` 已移除 + - `DEBUG_POI` - 启用 POI 调试渲染器。 + - `DEBUG_PANORAMA_SCREENSHOT` - 启用时,允许用户拍摄全景截图。 + - `DEBUG_CHASE_COMMAND` - 启用时,添加 chase 命令。 + - `FAKE_MS_LATENCY` -> `DEBUG_FAKE_LATENCY_MS` + - `FAKE_MS_JITTER` -> `DEBUG_FAKE_JITTER_MS` + - `DEBUG_VERBOSE_COMMAND_ERRORS` - 启用时,通过聊天框输出详细错误。 + - `DEBUG_DEV_COMMANDS` - 启用时,添加用于调试游戏的命令。 +- `net.minecraft.client.Minecraft` + - `debugEntries` - 返回一个调试功能列表以及应在屏幕上显示的内容。 + - `fpsString`、`sectionPath`、`sectionVisibility` 已移除 + - `debugRenderer` -> `LevelRenderer#debugRenderer` +- `net.minecraft.client.gui.Gui` + - `renderDebugOverlay` 现在是公开的 + - `shouldRenderDebugCrosshair` 已移除 +- `net.minecraft.client.gui.components.DebugScreenOverlay` + - `drawGameInformation`、`drawSystemInformation` 已移除 + - `getGameInformation`、`getSystemInformation` 已移除 + - `toggleOverlay` 已移除 +- `net.minecraft.client.gui.components.debug` + - `DebugEntryBiome` - 一个显示相机实体所在生物群系的调试条目。 + - `DebugEntryCategory` - 一个描述调试条目如何显示的类别。 + - `DebugEntryChunkGeneration` - 一个显示当前区块生成信息的调试条目。 + - `DebugEntryChunkRenderStats` - 一个显示当前区块统计信息的调试条目。 + - `DebugEntryChunkSourceStats` - 一个显示区块源通用元数据的调试条目。 + - `DebugEntryEntityRenderStats` - 一个显示实体存储通用元数据的调试条目。 + - `DebugEntryFps` - 一个显示每秒帧数和垂直同步信息的调试条目。 + - `DebugEntryGpuUtilization` - 一个显示 GPU 利用率的调试条目。 + - `DebugEntryHeightmap` - 一个显示当前位置高度图的调试条目。 + - `DebugEntryLight` - 一个显示客户端光照信息的调试条目。 + - `DebugEntryLocalDifficulty` - 一个显示当前世界难度和时间的调试条目。 + - `DebugEntryLookingAtBlock` - 一个显示相机当前正在查看的方块的调试条目。 + - `DebugEntryLookingAtEntity` - 一个显示相机当前正在查看的实体的调试条目。 + - `DebugEntryLookingAtFluid` - 一个显示相机当前正在查看的流体的调试条目。 + - `DebugEntryMemory` - 一个显示游戏分配和使用的内存的调试条目。 + - `DebugEntryNoop` - 一个不显示任何内容的调试条目。 + - `DebugEntryParticleRenderState` - 一个显示正在渲染的粒子数量的调试条目。 + - `DebugEntryPosition` - 一个显示相机实体的当前位置和旋转的调试条目。 + - `DebugEntryPostEffect` - 一个显示当前应用的后处理效果的调试条目。 + - `DebugEntrySectionPosition` - 一个显示当前部分位置的调试条目。 + - `DebugEntrySimplePerformanceImpactors` - 一个显示图形模式、云状态和生物群系混合半径的调试条目。 + - `DebugEntrySoundMood` - 一个显示当前播放的声音和玩家情绪的调试条目。 + - `DebugEntrySpawnCounts` - 一个显示每个生物类别的实体生成数量的调试条目。 + - `DebugEntrySystemSpecs` - 一个显示运行机器规格的调试条目。 + - `DebugEntryTps` - 一个显示通用每秒刻数的调试条目。 + - `DebugEntryVersion` - 一个显示当前 Minecraft 版本的调试条目。 + - `DebugScreenDisplayer` - 一个接口,调试条目使用它来将元素显示到屏幕。 + - `DebugScreenEntries` - Minecraft 注册的调试条目。 + - `DebugScreenEntry` - 一个元素,如果启用,则在调试覆盖层启用时显示。 + - `DebugScreenEntryList` - 有关在屏幕上显示哪些调试元素的选项信息。 + - `DebugScreenEntryStatus` - 调试条目应显示的状态。 + - `DebugScreenProfile` - 一个枚举,表示调试屏幕在决定显示哪些条目时可以设置的配置文件。 +- `net.minecraft.client.gui.screen.debug.DebugOptionsScreen` - 一个允许用户更改配置文件的显示调试条目的屏幕。 +- `net.minecraft.client.renderer.LevelRenderer` + - `getSectionStatistics` 现在可为 null + - `getEntityStatistics` 现在可为 null + - `gameTestBlockHighlightRenderer` - 游戏测试中方块高亮的渲染器。 +- `net.minecraft.client.renderer.debug.DebugRenderer` + - `switchRenderChunkborder` -> `DebugScreenEntries#CHUNK_BORDERS`,不是一对一 + - `toggleRenderOctree` -> `DebugScreenEntries#CHUNK_SECTION_OCTREE`,不是一对一 +- `net.minecraft.client.multiplayer` + - `DebugSampleSubscriber` -> `ClientDebugSubscriber`,不是一对一 + - `ClientPacketListener#createDebugValueAccess` - 创建用于获取当前调试值的访问器。 +- `net.minecraft.client.renderer.debug` + - `BeeDebugRenderer#addOrUpdateHiveInfo`、`addOrUpdateBeeInfo`、`removeBeeInfo` 已移除 + - `BrainDebugRenderer` + - `addPoi`、`removePoi`、`$PoiInfo` 已移除 + - `setFreeTicketCount` 已移除 + - `addOrUpdateBrainDump`、`removeBrainDump` 已移除 + - `BreezeDebugRenderer` 现在实现 `DebugRenderer$SimpleDebugRenderer` + - `render` 现在接受一个 `DebugValueAccess` + - `clear`、`add` 已移除 + - `DebugRenderer` 不再接受 `Minecraft` 实例 + - 所有字段渲染器已从公共访问中移除,而是存储在某个 `*Renderers` 列表中 + - `worldGenAttemptRenderer` 已移除 + - `renderTextOverBlock` - 在提供的方块位置上方渲染给定的流。 + - `renderTextOverMob` - 在提供的实体上方渲染给定的字符串。 + - `refreshRendererList` - 使用启用的调试渲染器填充渲染器列表。 + - `render`、`renderAfterTranslucents` 已合并到 `render` 中,其中 `boolean` 决定是渲染半透明还是不透明的渲染器 + - `$SimpleDebugRenderer` + - `render` 现在接受一个 `DebugValueAccess` 和 `Frustum` + - `clear` 已移除 + - `EntityBlockIntersectionDebugRenderer` - 一个用于显示实体相交的方块的调试渲染器。 + - `GameEventListenerRenderer` 不再接受 `Minecraft` 实例 + - `trackGameEvent`、`trackListener` 已移除 + - `GameTestDebugRenderer` -> `GameTestBlockHighlightRenderer`,不是一对一 + - `addMarker` -> `highlightPos`,不是一对一 + - `GoalSelectorDebugRenderer#addGoalSelector`、`removeGoalSelector` 已移除 + - `NeighborsUpdateRenderer` 不再接受 `Minecraft` 实例 + - `addUpdate` 已移除 + - `OctreeDebugRenderer` 现在实现 `DebugRenderer$SimpleDebugRenderer` + - `PathfindingRenderer#addPath` 已移除 + - `PoiDebugRenderer` - 一个用于显示兴趣点的调试渲染器。 + - `RaidDebugRenderer#setRaidCenters` 已移除 + - `RedstoneWireOrientationsRenderer` 不再接受 `Minecraft` 实例 + - `addWireOrientation` 已移除 + - `StructureRenderer` 不再接受 `Minecraft` 实例 + - `addBoundingBox` 已移除 + - `VillagerSectionsDebugRenderer#setVillageSection`、`setNotVillageSection` 已移除 + - `WorldGenAttemptRenderer` 类已移除 +- `net.minecraft.core.registries.BuiltInRegistries`、`Registries#DEBUG_SUBSCRIPTION` - 调试处理程序订阅的注册表。 +- `net.minecraft.gametest.framework` + - `GameTestAssertPosException#getMessageToShowAtBlock` 现在返回一个 `Component` + - `GameTestRunner#clearMarkers` 已移除 +- `net.minecraft.network.protocol.common.custom` + - 所有类已移至 `net.minecraft.util.debug` + - 它们不再是有效负载,而是仅包含对象信息和关联流编解码器的记录 + - 如果有效负载类有一个关联的内部对象类,则该类被移动,有效负载类被移除 + - 否则,有效负载类被添加,没有 `*Payload` 后缀,大多数情况下带有 `*Info` 后缀 +- `net.minecraft.network.protocol.game` + - `ClientboundDebugBlockValuePacket` - 发送到客户端的数据包,关于方块位置上的调试值更改。 + - `ClientboundDebugChunkValuePacket` - 发送到客户端的数据包,关于区块位置上的调试值更改。 + - `ClientboundDebugEntityValuePacket` - 发送到客户端的数据包,关于实体上的调试值更改。 + - `ClientboundDebugEventPacket` - 发送到客户端的数据包,关于触发的调试事件。 + - `ClientboundGameTestHighlightPosPacket` - 发送到客户端的数据包,关于要高亮的游戏测试位置。 + - `ClientGamePacketListener` + - `handleDebugChunkValue` - 处理调试区块位置数据包。 + - `handleDebugBlockValue` - 处理调试方块位置数据包。 + - `handleDebugEntityValue` - 处理调试实体数据包。 + - `handleDebugEvent` - 处理触发的调试事件。 + - `handleGameTestHighlightPos` - 处理提供的高亮位置。 + - `DebugPackets` 类已移除 + - `ServerboundDebugSampleSubscriptionPacket` -> `ServerboundDebugSubscriptionRequestPacket`,不是一对一 + - `ServerGamePacketListener#handleDebugSampleSubscription` -> `handleDebugSubscriptionRequest`,不是一对一 +- `net.minecraft.server.MinecraftServer` + - `subscribeToDebugSample` 已移除 + - `debugSubscribers` - 返回已跟踪订阅到已启用它的玩家列表的映射。 +- `net.minecraft.server.level` + - `ChunkMap` + - `isChunkTracked` 现在是公开的 + - `getChunks` 已移除 + - `ServerLevel#debugSynchronizers` - 返回等级的调试器处理程序和同步器。 + - `ServerPlayer` + - `requestDebugSubscriptions` - 设置玩家正在监听的调试器。 + - `debugSubscriptions` - 返回玩家正在监听的调试器。 +- `net.minecraft.util.debug` + - `DebugSubscription` - 一个可以监听或订阅的跟踪数据点。 + - `DebugSubscriptions` - 原版调试订阅。 + - `DebugValueAccess` - 访问调试订阅跟踪的值,在客户端上用于调试渲染器。 + - `DebugValueSource` - 定义一个提供要跟踪的调试值的源对象,例如一个实体。 + - `LevelDebugSynchronizers` - 处理将订阅数据通过网络发送到跟踪客户端。 + - `ServerDebugSubscribers` - 处理订阅了当前启用订阅的玩家的全局状态。 + - `TrackingDebugSynchronizer` - 处理订阅了某个订阅的玩家列表。 +- `net.minecraft.util.debugchart` + - `DebugSampleSubscriptionTracker` 类已移除 + - `RemoteDebugSampleType` 现在接受一个 `DebugSubscription` + - `subscription` - 返回样本类型报告的订阅。 + - `RemoteSampleLogger` 现在接受 `ServerDebugSubscribers` 而不是 `DebugSampleSubscriptionTracker` +- `net.minecraft.world.entity` + - `Entity` 现在实现 `DebugValueSource` + - `Mob#sendDebugPackets` 已移除 +- `net.minecraft.world.entity.ai.village.poi` + - `PoiManager#getFreeTickets` -> `getDebugPoiInfo`,不是一对一 + - `PoiSection#getDebugPoiInfo` - 返回给定位置的调试 POI 信息。 +- `net.minecraft.world.level.block.entity` + - `BlockEntity` 现在实现 `DebugValueSource` + - `TestInstanceBlockEntity#markError`、`clearErrorMarkers`、`getErrorMarkers`、`$ErrorMarker` - 处理测试实例设置的错误标记。 +- `net.minecraft.world.level.chunk.LevelChunk` 现在实现 `DebugValueSource` +- `net.minecraft.world.level.pathfinder.PathFinder#setCaptureDebug` - 设置是否应捕获路径以进行调试。 +- `net.minecraft.world.level.redstone.CollectingNeighborUpdater#setDebugListener` - 设置用于调试的方块位置更改的监听器。 + +## 功能提交:电影版 + +整个渲染管线,从实体到方块实体再到粒子,已被重做为称为“功能”的提交/渲染阶段系统。本指南将介绍功能系统的基础知识,然后介绍如何使用它实现每种主要类型。 + +### 提交与渲染 + +功能系统,就像 GUI 一样,分为两个阶段:提交和渲染。提交阶段由 `SubmitNodeCollector` 处理,它基本上收集将对象抽象渲染到屏幕所需的数据。这一切都是通过 `submit*` 方法完成的,这些方法通常接受一个 `PoseStack` 来将对象放置在 3D 空间中,以及任何其他所需的数据,如模型、状态、渲染类型等。 + +以下是每个方法接受的参数的快速概述: + +方法 | 参数 +:---------------------:|:---------- +`submitHitbox` | 一个姿势堆栈、实体的渲染状态和碰撞箱渲染状态 +`submitShadow` | 一个姿势堆栈、阴影半径和阴影碎片 +`submitNameTag` | 一个姿势堆栈、一个可选位置、Y 偏移、文本组件、文本是否应透明(如潜行时)、光照坐标和相机渲染状态 +`submitText` | 一个姿势堆栈、XY 偏移、文本序列、是否添加阴影、字体显示模式、光照坐标、颜色、背景颜色和轮廓颜色 +`submitFlame` | 一个姿势堆栈、实体的渲染状态和一个旋转四元数 +`submitLeash` | 一个姿势堆栈和拴绳状态 +`submitModel` | 一个姿势堆栈、实体模型、渲染状态、渲染类型、光照坐标、覆盖坐标、色调颜色、一个可选纹理、轮廓颜色和一个可选的破碎覆盖 +`submitModelPart` | 一个姿势堆栈、模型部件、渲染类型、光照坐标、覆盖坐标、一个可选纹理、如果渲染类型不透明则是否对物品使用闪光而非实体闪光、是否渲染闪光覆盖、色调颜色、一个可选的破碎覆盖和轮廓颜色 +`submitBlock` | 一个姿势堆栈、方块状态、光照坐标、覆盖坐标和轮廓颜色 +`submitMovingBlock` | 一个姿势堆栈和移动方块的渲染状态 +`submitBlockModel` | 一个姿势堆栈、渲染类型、方块状态模型、RGB 浮点数、光照坐标、覆盖坐标和轮廓颜色 +`submitItem` | 一个姿势堆栈、物品显示上下文、光照坐标、覆盖坐标、轮廓颜色、色调层、四边形、渲染类型和闪光类型 +`submitCustomGeometry` | 一个姿势堆栈、渲染类型和一个接受当前姿势和 `VertexConsumer` 以创建网格的函数 +`submitParticleGroup` | 一个 `SubmitNodeCollector$ParticleGroupRenderer` + +从技术上讲,`submit*` 方法由 `OrderedSubmitNodeCollector` 提供,`SubmitNodeCollector` 扩展了它。这是因为功能可以提交到不同的顺序,其功能类似于 GUI 中的层(strata)。默认情况下,所有提交调用都被推送到顺序 0。使用 `SubmitNodeCollector#order` 加上某个整数,然后调用 `submit*` 方法,您可以让一个对象在给定顺序上的所有功能之前或之后渲染。这存储为一个 AVL 树,其中每个顺序的数据存储在一个 `SubmitNodeCollection` 中。使用当前的默认功能渲染顺序,这仅在非常特定的情况下使用,例如渲染史莱姆的外层身体或装备层。 + +```java +// 假设我们可以访问 `SubmitNodeCollector` collector + +// 这将在顺序 0 中渲染 +collector.submitModel(...); + +// 这将在 `submitModel` 调用之前渲染 +collector.order(-1).submitShadow(...); + +// 这将在 `submitModel` 调用之后渲染 +collector.order(1).submitNameTag(...); +``` + +渲染阶段由 `FeatureRenderDispatcher` 处理,它使用其提交的功能渲染器渲染对象。什么是功能渲染器?实际上就是一个任意方法,它遍历它将推送到缓冲区的节点内容。目前,对于给定的顺序,功能按如下方式推送其顶点:阴影、模型、模型部件、火焰动画、实体名称标签、任意文本、碰撞箱、拴绳、物品、方块、自定义渲染管线,最后是粒子。每个顺序,从最小数字到最大数字,将重新运行所有功能渲染,直到到达树的末尾。然后所有提交将被清除以供下次使用。 + +大多数功能分派器只是对其集合运行一个循环。那些存储渲染类型的会将渲染调用批处理到一个缓冲区。同时,`ModelFeatureRenderer` 更进一步,将其半透明模型按距离相机的距离排序,并在所有不透明模型之后将它们发送到缓冲区。 + +### 实体模型 + +那么,这如何影响实体?让我们从构成所有实体模型的根 `Model` 开始。`Model` 现在有一个泛型,用于将支持对象的状态传递给 `setupAnim`,该方法也已移至 `Model`。这意味着基本模型类很少被传递,而是选择某个子类型,例如用于告示牌的 `Model$Simple`。鉴于大多数 `EntityModel` 已经需要 `EntityRenderState` 的泛型,这不会影响任何东西。 + +主要变化来自模型部件可见性的工作方式,例如盔甲和披风。每一个单独的部件(例如,头盔、胸甲)现在都有自己独立的模型,这意味着通用的部件可见性系统已被完全移除。您仍然可以在 `setupAnim` 中通过可变的模型部件提供可见性,但通常的做法是简单地将应该分开的模型部件作为单独的模型。 + +为了促进这一点,`PartDefinition` 现在有一些方法可以选择性地保留模型的某些部分并移除其他部分。这是通过 `clear*` 和 `retain*` 方法完成的。基本上,所有这些方法所做的就是保留部件姿势,同时移除与子查询关联的任何立方体。`retain*` 允许某些部分和可能的子部分保留它们的立方体。这一增加提供了双重好处:模型层可以确定性地使用 `setupAnim` 以与基础模型类似的方式设置部件,并且模型纹理将只需要包含保留的元素。 + +以下是为苦力怕创建盔甲模型的示例: + +```java +// 人形盔甲的变形 +private static final CubeDeformation OUTER_ARMOR_DEFORMATION = new CubeDeformation(1.0F); +private static final CubeDeformation INNER_ARMOR_DEFORMATION = new CubeDeformation(0.5F); + +// 苦力怕有以下部分: +// - head +// - body +// - right_hind_leg, left_hind_leg +// - right_front_leg, left_front_leg + +// 我们使用单独的网格,因为我们基本上要隔离我们想要保留在每一个中的部分 + +public static ArmorModelSet createCreeperArmor() { + // 头盔 + + // 创建网格 + MeshDefinition helmetMesh = CreeperModel.createBodyLayer(OUTER_ARMOR_DEFORMATION); + // 仅保留所需部分 + // 注意 body 和腿仍然存在,它们只是没有立方体 + helmetMesh.getRoot().retainExactParts(Set.of("head")); + + // 胸甲 + + // 创建网格 + var chestplateMesh = CreeperModel.createBodyLayer(OUTER_ARMOR_DEFORMATION); + // 仅保留所需部分 + chestplateMesh.getRoot().retainExactParts(Set.of("body")); + + // 护腿 + + // 创建网格 + var leggingsMesh = CreeperModel.createBodyLayer(INNER_ARMOR_DEFORMATION); + // 仅保留所需部分 + leggingsMesh.getRoot().retainExactParts(Set.of("right_hind_leg", "left_hind_leg", "right_front_leg", "left_front_leg")); + + // 靴子 + + // 创建网格 + var bootsMesh = CreeperModel.createBodyLayer(OUTER_ARMOR_DEFORMATION); + // 仅保留所需部分 + bootsMesh.getRoot().retainExactParts(Set.of("right_hind_leg", "left_hind_leg", "right_front_leg", "left_front_leg")); + + // 将所有内容存储在 ArmorModelSet 中,基本上只是一个对象持有者和映射器 + return new ArmorModelSet<>( + helmetMesh, + chestplateMesh, + leggingsMesh, + bootsMesh + ); +} + +// 要注册层定义,基本上使用 ArmorModelSet 的相同过程 +public static final ArmorModelSet CREEPER_ARMOR = new ArmorModelSet<>( + new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath("examplemod", "creeper"), "helmet"), + new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath("examplemod", "creeper"), "chestplate"), + new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath("examplemod", "creeper"), "leggings"), + new ModelLayerLocation(ResourceLocation.fromNamespaceAndPath("examplemod", "creeper"), "boots") +); + +// 在某个可以访问 Map builder 的方法中 +ArmorModelSet creeperArmorLayers = createCreeperArmor().map(mesh -> LayerDefinition.create(mesh, 64, 32)); +CREEPER_ARMOR.putFrom(creeperArmorLayers, builder); +``` + +### 实体渲染器 + +随着提交的更改,`EntityRenderer` 及其关联的 `RenderLayer` 也发生了变化。基本上,您可以假设几乎所有带有 `render` 一词的方法都已更改为 `submit`,并且 `MultiBufferSource` 和光照坐标整数通常已被 `SubmitNodeCollector` 和关联的实体渲染状态取代。 + +在 `EntityRenderer` 中替换 `render` 的新 `submit` 方法现在接受实体的渲染状态、`PoseStack`、`SubmitNodeCollector` 和 `CameraRenderState`。提交任何元素时,3D 空间中的位置通过获取 `PoseStack` 上的最后一个姿势并将其存储以供将来使用。 + +```java +// 一个基本的实体渲染器 + +// 我们将假设所有列出的类都存在 +public class ExampleEntityRenderer extends MobRenderer { + + public ExampleEntityRenderer(EntityRendererProvider.Context ctx) { + super(ctx, ctx.bakeLayer(EXAMPLE_MODEL_LAYER), 0.5f); + } + + @Override + public void submit(ExampleEntityRenderState renderState, PoseStack poseStack, SubmitNodeCollector collector, CameraRenderState cameraState) { + super.submit(renderState, poseStack, collector, cameraState); + + // 提交某些内容的示例 + collector.submitCustomGeometry( + poseStack, // 当前姿势 + RenderType.solid(), // 要使用的渲染类型 + ExampleEntityRenderer::addVertices // 写入几何数据的方法 + ); + } + + private static void addVertices(PoseStack.Pose pose, VertexConsumer consumer) { + // 添加自定义几何体 + } +} + +// 一个渲染层 + +public class CreeperArmorLayer extends RenderLayer { + + private final ArmorModelSet modelSet; + private final EquipmentLayerRenderer equipment; + + public CreeperArmorLayer(RenderLayerParent parent, ArmorModelSet modelSet, EquipmentLayerRenderer equipment) { + super(parent); + this.modelSet = modelSet; + this.equipment = equipment; + } + + // 我们将假设我们以某种方式向 CreeperRenderState 添加了 headEquipment、chestEquipment、legsEquipment、feetEquipment + @Override + public void submit(PoseStack poseStack, SubmitNodeCollector collector, int lightCoords, CreeperRenderState renderState, float yRot, float xRot) { + this.renderArmorPiece(poseStack, collector, renderState.chestEquipment, EquipmentSlot.CHEST, lightCoords, renderState); + this.renderArmorPiece(poseStack, collector, renderState.legsEquipment, EquipmentSlot.LEGS, lightCoords, renderState); + this.renderArmorPiece(poseStack, collector, renderState.feetEquipment, EquipmentSlot.FEET, lightCoords, renderState); + this.renderArmorPiece(poseStack, collector, renderState.headEquipment, EquipmentSlot.HEAD, lightCoords, renderState); + } + + // 取自人形盔甲层 + private void renderArmorPiece(PoseStack poseStack, SubmitNodeCollector collector, ItemStack stack, EquipmentSlot slot, int lightCoords, CreeperRenderState renderState) { + Equippable equippable = stack.get(DataComponents.EQUIPPABLE); + if (equippable != null && equippable.assetId().isPresent() && equippable.slot() == slot) { + CreeperModel model = this.modelSet.get(slot); + EquipmentClientInfo.LayerType layer = slot == EquipmentSlot.LEGS + ? EquipmentClientInfo.LayerType.HUMANOID_LEGGINGS + : EquipmentClientInfo.LayerType.HUMANOID; + this.equipmentRenderer.renderLayers( + layer, // 要使用的装备层 + equippable.assetId().orElseThrow(), // 要拉取的装备资源 + model, // 盔甲模型 + renderState, // 实体渲染状态 + stack, // 盔甲堆栈 + poseStack, // 姿势堆栈 + collector, // 用于添加模型数据的收集器 + lightCoords, // 光照坐标 + renderState.outlineColor // 实体的轮廓颜色 + ); + } + } +} + +// 然后,将其添加到苦力怕渲染器的构造函数中 +public CreeperRenderer(EntityRendererProvider.Context ctx) { + // ... + this.addLayer(new CreeperArmorLayer( + this, // 父级是渲染器本身 + ArmorModelSet.bake( // 烘焙模型集 + CREEPER_ARMOR, // 模型层位置 + ctx.getModelSet(), // 用于从层位置映射模型的模型集 + CreeperModel::new // 根部件到模型的映射器 + ), + ctx.getEquipmentRenderer() // 装备的渲染器 + )); +} +``` + +### 方块实体渲染器 + +`BlockEntityRenderer` 也使用新的提交方法,几乎将所有 `render*` 替换为 `submit`。它们借鉴了实体的做法,现在有自己的 `BlockEntityRenderState`,从 `BlockEntity` 中提取。因此,`BlockEntityRenderer` 现在有一个新的泛型 `S`,表示 `BlockEntityRenderState`。 + +默认情况下,`BlockEntityRenderState` 包含关于其位置、方块状态、类型、光照坐标以及作为 `ModelFeatureRenderer$CrumblingOverlay` 的当前破坏进度的信息。这些信息都通过 `BlockEntityRenderState#extractBase` 填充,该方法在 `BlockEntityRenderer#extractRenderState` 中调用。与实体一样,渲染状态首先通过 `BlockEntityRenderer#createRenderState` 构造,然后从方块实体中提取值。`extractRenderState` 确实包含部分刻和相机位置,但默认情况下不会传递给 `BlockEntityRenderState`。 + +因此,接管 `render` 方法的 `submit` 方法接受渲染状态、用于 3D 空间位置的 `PoseStack`、用于推送要渲染的元素的 `SubmitNodeCollector` 以及 `CameraRenderState`。 + +```java +// 我们将假设所有未在此处指定的类都存在 + +// 一个简单的渲染状态 +public class ExampleRenderState extends BlockEntityRenderState { + public float partialTick; +} + +// 一个基本的方块实体渲染器 +public class ExampleBlockEntityRenderer implements BlockEntityRenderer { + + public ExampleBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { + // 从上下文中获取任何需要的内容 + } + + @Override + public ExampleRenderState createRenderState() { + // 创建用于将方块实体提交到功能渲染器的渲染状态 + return new ExampleRenderState(); + } + + @Override + public void extractRenderState(ExampleBlockEntity blockEntity, ExampleRenderState renderState, float partialTick, Vec3 cameraPos, @Nullable ModelFeatureRenderer.CrumblingOverlay crumblingOverlay) { + // 从方块实体中提取必要的渲染值到渲染状态 + // 始终调用 super 或 BlockEntityRenderState#extractBase + super.extractRenderState(blockEntity, renderState, partialTick, cameraPos, crumblingOverlay); + + // 填充任何所需的值 + renderState.partialTick = partialTick; + } + + + @Override + public void submit(ExampleRenderState renderState, PoseStack poseStack, SubmitNodeCollector collector, CameraRenderState cameraState) { + // 提交某些内容的示例 + collector.submitModel(..., renderState.breakProgress); + } +} +``` + +### 特殊物品模型 + +由于特殊物品模型也使用自定义渲染,它们已更新为 `submit` 更改,仅将 `MultiBufferSource` 替换为 `SubmitNodeCollector`。 + +```java +// 一个基本的特殊物品模型 + +// 我们将假设所有列出的类都存在 +public class ExampleSpecialModelRenderer implements NoDataSpecialModelRenderer { + + public ExampleSpecialModelRenderer() {} + + @Override + public void submit(ItemDisplayContext displayContext, PoseStack poseStack, SubmitNodeCollector collector, int lightCoords, int overlayCoords, boolean hasFoil, int outlineColor) { + // 提交某些内容的示例 + collector.submitModelPart(...); + } + + @Override + public void getExtents(Set extents) {} +} +``` + +### 粒子 + +粒子已被添加到提交过程中;然而,根据粒子的复杂程度,有多条路径。一些类和通用名称也被重用于额外的目的,有时使理解每个部分的工作原理变得困难。因此,本文档将介绍两种创建粒子的方法:一种更熟悉旧系统,另一种从头开始解释底层细微差别。 + +#### 引擎和资源的分离 + +在我们讨论这两种方法之前,首先,让我们了解总体变化。`ParticleEngine` 在功能上被拆分为两个类:`ParticleEngine`,负责处理实际的Tick和提取(而不是提交)渲染状态;以及 `ParticleResources`,它是注册 `ParticleProvider` 和可选的 `ParticleResources$SpriteParticleRegistration` 并从其 `ParticleDescription` 重新加载 `SpriteSet` 的重载监听器。这种底层行为没有改变(除了整个提取和提交过程),方法只是被移动了。 + +`ParticleProvider#createParticle` 现在也提供了一个 `RandomSource`,可以根据需要使用。 + +至于实际的提交和渲染过程,这由 `ParticleEngine` 之外处理。更具体地说,`LevelRenderer` 通过 `ParticleEngine#extract` 将所有粒子提取到 `ParticlesRenderState` 中。然后,在 `LevelRenderer#addParticlesPass` 中,资源句柄被设置到粒子的 `FramePass`,在执行时,粒子通过 `ParticlesRenderState#submit` 提交,然后通过 `ParticleFeatureRenderer` 通过功能分派器渲染。 + +#### 单个四边形 + +旧系统中的许多粒子仅仅是由一个带有纹理的单个四边形组成。这些粒子是 `SingleQuadParticle`,它将之前的 `SingleQuadParticle` 和 `TextureSheetParticle` 合并为一个。`SingleQuadParticle` 现在接受一个初始的 `TextureAtlasSprite` 来设置第一个纹理,然后可以通过覆盖 `Particle#tick` 并调用 `SingleQuadParticle#setSpriteFromAge` 来更新 `SpriteSet`,或者直接使用 `setSprite` 来更新。色调也可以在Tick中使用 `setColor` 和 `setAlpha` 修改。有些也在 `SingleQuadParticle#extract` 中直接设置,但使用哪个取决于您是否需要覆盖整个Tick。 + +要确定用于渲染四边形的 `RenderType`,必须将 `SingleQuadParticle#getLayer` 设置为所需的 `$Layer`。`$Layer` 基本上是一个记录,定义四边形是否可以具有半透明、它从哪个纹理图集拉取以及要使用的渲染管线。原版提供了类似于旧 `Particle#getRenderType` 的 `TERRAIN`、`OPAQUE` 和 `TRANSLUCENT`,它取代了这些。`TERRAIN` 和 `TRANSLUCENT` 都允许透明度,`OPAQUE` 和 `TRANSLUCENT` 从粒子图集拉取,而 `TERRAIN` 使用方块图集。可以使用构造函数创建自定义的 `$Layer`。 + +```java +public static final SingleQuadParticle.Layer EXAMPLE_LAYER = new SingleQuadParticle.Layer(true, TextureAtlas.LOCATION_PARTICLES, RenderPipelines.WEATHER_DEPTH_WRITE); +``` + +除此之外,您还可以通过覆盖 `SingleQuadParticle#getFacingCameraMode` 来设置粒子的旋转方式。`$FacingCameraMode` 是一个函数式接口,在提取粒子时设置粒子的旋转。默认情况下,这意味着纹理将始终面向相机。任何其他方法的更改和添加都在下面的列表中。 + +从那里开始,创建 `ParticleProvider` 并注册它的所有其他内容都是相同的。 + +```java +// 我们将假设我们的粒子有某个 SimpleParticleType EXAMPLE_QUAD +// 此外,我们将假设有某个粒子描述,其中包含要使用的纹理 +public class ExampleQuadParticle extends SingleQuadParticle { + + private final SpriteSet spriteSet; + + // 这可以是包私有、受保护或公开的 + // 如果粒子将在提供者之外构造,则应使用公开 + ExampleQuadParticle(ClientLevel level, double x, double y, double z, SpriteSet spriteSet) { + // 我们使用 first 来设置初始粒子纹理 + super(level, x, y, z, spriteSet.first()); + } + + @Override + public void tick() { + super.tick(); + // 更新粒子图像 + this.setSpriteFromAge(spriteSet); + } + + @Override + public SingleQuadParticle.Layer getLayer() { + return EXAMPLE_LAYER; + } + + // 创建提供者 + public static class Provider implements ParticleProvider { + + private final SpriteSet spriteSet; + + public Provider(SpriteSet spriteSet) { + this.spriteSet = spriteSet; + } + + @Override + public Particle createParticle(SimpleParticleType options, ClientLevel level, double x, double y, double z, double xd, double yd, double zd, RandomSource random) { + // 创建粒子 + return new ExampleQuadParticle(level, x, y, z, this.spriteSet); + } + } +} + +// 将提供者注册到 `ParticleResources#register` +// 假设可以访问 ParticleResources resources 并且 register 已设为公开 +resources.register(EXAMPLE_QUAD, ExampleQuadParticle.Provider::new); +``` + +#### 从头开始 + +如果要渲染更复杂或自定义的内容呢?在这些情况下,我们需要更深入地了解 `ParticleEngine` 如何提取粒子。`Particle` 类本身实际上不进行任何提取、提交或渲染。它只是每刻处理物理更新。实际的提取逻辑由 `ParticleGroup` 处理,而提交由 `ParticleGroupRenderState` 处理。 + +那么,什么是 `ParticleGroup`?顾名思义,一个粒子组持有一组粒子,并负责跟踪、Tick和提取其粒子的渲染状态。泛型表示它可以跟踪的 `Particle` 类型,每个组最多 16,384 个(尽管单个粒子可以通过 `Particle#getParticleLimit` 设置自己的子组限制)。所有 `SingleQuadParticle` 都是 `QuadParticleGroup` 的一部分。为了提取渲染状态,`ParticleEngine` 调用 `ParticleGroup#extractRenderState`,它接受当前的平截头体、相机和部分刻,并返回一个 `ParticleGroupRenderState`。 + +`ParticleGroupRenderState` 有点像渲染状态、提交处理器和缓存的混合体。它包含两个方法:`submit`,接受 `SubmitNodeCollector` 并提交组;以及 `clear`,清除所有先前缓存的粒子状态。从技术上讲,任何东西都可以使用收集器提交,但粒子有 `SubmitNodeCollector$ParticleGroupRenderer`:一个有助于缓存和渲染的附加实用程序。组渲染器包含两个方法:`prepare`,将网格数据写入环形缓冲区;以及 `render`,通常使用缓存的缓冲区,使用提供的 `RenderPass` 将数据写入共享的顺序缓冲区,并将其绘制到屏幕。只有 `QuadParticleRenderState` 使用缓存和 `ParticleGroupRenderer`,因为渲染状态在渲染后立即被清除。 + +要将 `ParticleGroup` 链接到 `Particle` 以供使用,我们必须使用 `Particle#getGroup` 设置 `ParticleRenderType`。与之前的版本不同,`ParticleRenderType` 只是 `ParticleGroup` 的一个键。此键通过 `ParticleEngine#createParticleGroup` 映射到组,提交/渲染顺序由 `ParticleEngine#RENDER_ORDER` 确定。必须修补该方法和列表,才能使您的组正确管理粒子并提取以进行提交。 + +```java +// 我们将假设我们的粒子有某个 SimpleParticleType EXAMPLE_ONE, EXAMPLE_TWO +// 此示例将构造两个具有相同基础类型的粒子,以展示组的工作原理 + +// 创建粒子类型 +public static final ParticleRenderType EXAMPLE_TYPE = new ParticleRenderType("examplemod:example_type"); + +// 创建我们的粒子 +public abstract class ExampleParticle extends Particle { + + // 您可以以任何方式处理传递给粒子组 + // 使字段可访问或拥有专用方法 + public final Model model; + + protected ExampleParticle(ClientLevel level, double x, double y, double z, Function> modelFactory) { + super(level, x, y, z); + this.model = modelFactory.apply(Minecraft.getInstance().getEntityModels()); + } + + @Override + public ParticleRenderType getGroup() { + // 将粒子类型设置为我们的组 + return EXAMPLE_TYPE; + } + + @FunctionalInterface + public interface ExampleParticleFactory

{ + + P create(ClientLevel level, double x, double y, double z); + } + + protected static

ParticleProvider createProvider(ExampleParticleFactory

factory) { + return (options, level, x, y, z, xd, yd, zd, random) -> factory.create(level, x, y, z); + } +} + +public class ExampleOneParticle extends ExampleParticle { + + ExampleOneParticle(ClientLevel level, double x, double y, double z) { + super(level, x, y, z, modelSet -> new Model.Simple(new ModelPart(Collections.emptyList(), Collections.emptyMap()), RenderType::entityCutoutNoCull)); + } + + public static ParticleProvider provider() { + return ExampleParticle.createProvider(ExampleOneParticle::new); + } +} + +public class ExampleTwoParticle extends ExampleParticle { + + private static final ParticleLimit LIMIT = new ParticleLimit(5); + + ExampleTwoParticle(ClientLevel level, double x, double y, double z) { + super(level, x, y, z, modelSet -> new Model.Simple(new ModelPart(Collections.emptyList(), Collections.emptyMap()), RenderType::entityCutoutNoCull)); + } + + @Override + public Optional getParticleLimit() { + // 将使用 LIMIT 子组的粒子数量限制为 5 + // 注意,由于 ParticleLimit 是一个记录,任何具有相同限制的都将被视为相同的键 + return Optional.of(LIMIT); + } + + public static ParticleProvider provider() { + return ExampleParticle.createProvider(ExampleTwoParticle::new); + } +} + +// 将提供者注册到 `ParticleResources#register` +// 假设可以访问 ParticleResources resources 并且 register 已设为公开 +resources.register(EXAMPLE_ONE, ExampleOneParticle.provider()); +resources.register(EXAMPLE_TWO, ExampleTwoParticle.provider()); + +// 创建渲染状态以提交组中的所有粒子 +// 存储您需要提交到节点收集器的任何内容 +public record ExampleGroupRenderState(List> models) implements ParticleGroupRenderState { + + @Override + public void submit(SubmitNodeCollector collector) { + // 提交每个粒子 + for (var model : this.models) { + collector.submitModel(model, ...); + } + } +} + +// 创建粒子组以跟踪粒子并创建渲染状态 +// EXAMPLE_ONE 和 EXAMPLE_TWO 都将被添加到此组 +public class ExampleParticleGroup extends ParticleGroup { + + public ExampleParticleGroup(ParticleEngine engine) { + super(engine); + } + + @Override + public ParticleGroupRenderState extractRenderState(Frustum frustum, Camera camera, float partialTick) { + // 创建要提交粒子的粒子组 + return new ExampleGroupRenderState( + this.particles.stream().map(particle -> particle.model).toList() + ); + } +} + +// 将 ParticleRenderType 链接到其 ParticleGroup +// 假设我们可以访问 ParticleEngine engine +// 假设 ParticleEngine#RENDER_ORDER 是可变的并且是公开的 +// 假设我们可以修补 ParticleEngine#createParticleGroup +engine.RENDER_ORDER.add(EXAMPLE_TYPE); + +// 在 ParticleEngine 中 +private ParticleGroup createParticleGroup(ParticleRenderType renderType) { + if (renderType == EXAMPLE_TYPE) { + // this 指的是 ParticleEngine + return new ExampleParticleGroup(this); + } + // ... +} +``` + +### 图集处理器整合 + +图集处理器已修改了一些逻辑,以整合其他精灵并更改获取 `TextureAtlasSprite` 的方式。 + +首先,地图装饰、绘画和 GUI 精灵现在是带有自己图集的适当图集:分别是 `Sheets#MAP_DECORATIONS_SHEET`、`PAINTINGS_SHEET` 和 `GUI_SHEET`。 + +从这些图集中获取 `TextureAtlasSprite` 现在完全通过 `MaterialSet` 路由:一个函数式接口,接受一个 `Material`(基本上是图集位置和纹理位置),并返回关联的 `TextureAtlasSprite`。`MaterialSet` 处理物品模型、方块实体渲染器和实体渲染器的纹理获取: + +```java +// 这是一个从适当图集中获取苹果纹理的材质示例 +public static final Material APPLE = new Material( + TextureAtlas.LOCATION_BLOCKS, // 存储物品纹理的图集 + ResourceLocation.fromNamespaceAndPath("minecraft", "item/apple") // 根据精灵内容的纹理名称 +); +// 您也可以使用 Sheets.ITEMS_MAPPER.defaultNamespaceApply("apple") 做同样的事情 + +// 对于某个物品模型 +public class ExampleUnbakedItemModel implements ItemModel.Unbaked { + + // ... + + @Override + public ItemModel bake(ItemModel.BakingContext ctx) { + TextureAtlasSprite appleTexture = ctx.materials().get(APPLE); + // ... + } +} + +// 对于某个特殊物品模型 +public class ExampleUnbakedSpecialModel implements SpecialModelRenderer.Unbaked { + + // ... + + @Override + @Nullable + public SpecialModelRenderer bake(SpecialModelRenderer.BakingContext ctx) { + TextureAtlasSprite appleTexture = ctx.materials().get(APPLE); + // ... + } +} + +// 对于某个方块实体渲染器 +public class ExampleBlockEntityRenderer implements BlockEntityRenderer { + + public ExampleBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { + TextureAtlasSprite appleTexture = ctx.materials().get(APPLE); + // ... + } + + // ... +} + + +// 对于某个实体渲染器 +public class ExampleEntityRenderer implements EntityRenderer { + + public ExampleEntityRenderer(EntityRendererProvider.Context ctx) { + TextureAtlasSprite appleTexture = ctx.getMaterials().get(APPLE); + // ... + } + + // ... +} +``` + +- `assets/minecraft/shaders/core` + - `blit_screen.json` -> `screenquad.json`,使用无格式三角形而不是定位四边形 + - `position_color_lightmap.*` 已移除 + - `position_color_tex_lightmap.*` 已移除 +- `com.mojang.blaze3d.vertex` + - `CompactVectorArray` - 一个将浮点向量列表压缩到单个顺序数组中的持有者。 + - `MeshData$SortState#centroids` 现在返回一个 `CompactVectorArray` + - `VertexSorting` + - `byDistance` 现在接受一个 `Vector3fc` 而不是 `Vector3f` + - `sort` 现在接受一个 `CompactVectorArray` 而不是 `Vector3f[]` +- `net.minecraft.client.Minecraft` + - `getTextureAtlas` -> `AtlasManager#getAtlasOrThrow`,不是一对一 + - `getPaintingTextures`、`getMapDecorationTextures`、`getGuiSprites` -> `getAtlasManager`,不是一对一 +- `net.minecraft.client.animation.Keyframe` 现在有一个重载,接受 `preTarget` 和 `postTarget` 而不是一个简单的 `target`,接受 `Vector3fc` 而不是 `Vector3f` +- `net.minecraft.client.entity` + - `ClientAvatarEntity` - 化身实体的客户端数据。 + - `ClientAvatarState` - 化身的移动状态。 + - `ClientMannequin` - `Mannequin` 实体的客户端版本。 +- `net.minecraft.client.gui` + - `GuiGraphics` + - `renderOutline` -> `submitOutline` + - `renderDeferredTooltip` -> `renderDeferredElements`,不是一对一 + - `submitBannerPatternRenderState` 现在接受 `BannerFlagModel` 而不是 `ModelPart` + `GuiSpriteManager` 类已移除 +- `net.minecraft.client.gui.render.GuiRenderer` 现在接受 `SubmitNodeCollector` 和 `FeatureRenderDispatcher` + - `MIN_GUI_Z` 现在是公开的 +- `net.minecraft.client.gui.render.pip` + - `GuiBannerResultRenderer` 现在接受一个 `MaterialSet` + - `GuiSignRenderer` 现在接受一个 `MaterialSet` +- `net.minecraft.client.gui.render.state.TiledBlitRenderState` - 一个用于使用平铺构建精灵的渲染状态,通常用于平铺或九切片纹理。 +- `net.minecraft.client.gui.render.state.pip.GuiBannerResultRenderState` 现在接受 `BannerFlagModel` 而不是 `ModelPart` +- `net.minecraft.client.model` + - `AbstractPiglinModel#createArmorMeshSet` - 为每个人形盔甲槽位创建模型网格。 + - `ArmedModel` 现在有一个 `EntityRenderState` 的泛型 + - `translateToHand` 现在接受实体渲染状态 + - `ArmorStandArmorModel#createBodyLayer` -> `createArmorLayerSet`,不是一对一 + - `BellModel$State` - 表示支持对象的状态。 + - `BookModel$State` - 表示支持对象的状态。 + - `BreezeModel` + - `createBodyLayer` -> `createBaseMesh`,现在是私有的 + - 被 `createBodyLayer`、`createWindLayer`、`createEyesLayer` 取代 + - `CopperGolemModel` - 铜傀儡实体的模型。 + - `CopperGolemStatueModel` - 铜傀儡雕像的模型。 + - `CreakingModel` + - `NO_PARTS`、`getHeadModelParts` 已移除 + - `createEyesLayer` - 创建模型的眼睛。 + - `EntityModel#setupAnim` -> `Model#setupAnim` + - `GuardianParticleModel` - 从守卫者生成的粒子。 + - `HeadedModel#translateToHead` - 将姿势堆栈变换到头部的位置和旋转。 + - `HumanoidArmorModel` -> `HumanoidModel#createArmorMeshSet`,不是一对一 + - `HumanoidModel#copyPropertiesTo` 已移除 + - `Model` 现在接受一个表示渲染状态的泛型 + - `PlayerCapeModel` 现在继承 `PlayerModel` + - `PlayerEarsModel` 现在继承 `PlayerModel` + - `PlayerModel` 渲染状态已拓宽到 `AvatarRenderState` + - 静态字段现在是 `protected` + - `createArmorMeshSet` - 为每个人形盔甲槽位创建模型网格。 + - `SkullModelBase$State` - 表示支持对象的状态。 + - `SpinAttackEffectModel` 泛型已拓宽到 `AvatarRenderState` + - `VillagerLikeModel` 现在接受渲染状态的泛型 + - `hatVisible` 已移除 + - 被 `VillagerModel#createNoHatModel` 取代 + - `translateToArms` 现在接受渲染状态 + - `WardenModel` + - `createTendrilsLayer`、`createHeartLayer`、`createBioluminescentLayer`、`createPulsatingSpotsLayer` - 创建监守者的 `RenderLayer` 使用的层。 + - `getTendrilsLayerModelParts`、`getHeartLayerModelParts`、`getBioluminescentLayerModelParts`、`getPulsatingSpotsLayerModelParts` 已移除 + - `ZombieVillagerModel` + - `createArmorLayer` -> `createArmorLayerSet`,不是一对一 + - `createNoHatLayer` - 创建没有帽子层的模型。 +- `net.minecraft.client.model.geom.ModelPart` + - `copyFrom` 已移除 + - `$Polygon#normal` 现在是 `Vector3fc` 而不是 `Vector3f` + - `$Vertex` + - `pos` -> `x`、`y`、`z` + - `worldX`、`worldY`、`worldZ` - 返回坐标缩小 16 倍后的值。 +- `net.minecraft.client.model.geom.builders.PartDefinition` + - `clearRecursively` - 清除所有子部件及其子子部件。 + - `retainPartsAndChildren` - 从其根和任何子子部件中保留指定的部件。 + - `retainExactParts` - 仅保留顶级部件,清除所有其他部件和子子部件。 +- `net.minecraft.client.particle` + - `AttackSweepParticle` 现在继承 `SingleQuadParticle` + - `BaseAshSmokeParticle` 现在继承 `SingleQuadParticle` 并且是 `abstract` + - `BlockMarker` 现在继承 `SingleQuadParticle` + - `BreakingItemParticle` 现在继承 `SingleQuadParticle` + - 构造函数接受 `TextureAtlasSprite` 而不是 `ItemStackRenderState` + - `$ItemParticleProvider#calculateState` -> `getSprite`,不是一对一 + - `BubbleColumnUpParticle` 现在继承 `SingleQuadParticle` + - `BubbleParticle` 现在继承 `SingleQuadParticle` + - `BubblePopParticle` 现在继承 `SingleQuadParticle` + - `CampfireSmokeParticle` 现在继承 `SingleQuadParticle` + - `CritParticle` 现在继承 `SingleQuadParticle` + - `DragonBreathParticle` 现在继承 `SingleQuadParticle` + - `$Provider` 泛型现在使用 `PowerParticleOption` + - `DripParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `create*Particle` 方法 -> `$*Provider` 类 + - `DustParticleBase` 现在继承 `SingleQuadParticle` + - `ElderGuardianParticleGroup` - 负责设置和提交远古守卫者粒子的粒子组。 + - `ExplodeParticle` 现在继承 `SingleQuadParticle` + - `FallingDustParticle` 现在继承 `SingleQuadParticle` + - `FallingLeavesParticle` 现在继承 `SingleQuadParticle` 并接受 `TextureAtlasSprite` 而不是 `SpriteSet` + - `FireflyParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `FireworkParticles` + - `$FlashProvider` 泛型现在使用 `ColorParticleOption` + - `$OverlayParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `FlameParticle` 现在接受一个 `TextureAtlasSprite` + - `FlyStraightTowardsParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `FlyTowardsPositionParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `GlowParticle` 现在继承 `SingleQuadParticle` + - `GustParticle` 现在继承 `SingleQuadParticle` + - `HeartParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `HugeExplosionParticle` 现在继承 `SingleQuadParticle` + - `ItemPickupParticle` 现在接受 `EntityRenderState` 而不是 `EntityRenderDispatcher` + - 除目标实体外,所有字段现在都是 `protected` + - `ItemPickupParticleGroup` - 负责设置和提交物品拾取粒子的粒子组。 + - `LavaParticle` 现在继承 `SingleQuadParticle` + - `MobAppearanceParticle` -> `ElderGuardianParticle` + - `NoRenderParticleGroup` - 什么都不做的粒子组。 + - `NoteParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `Particle` + - `rCol`、`gCol`、`bCol`、`alpha` -> `SingleQuadParticle#rCol`、`gCol`、`bCol`、`alpha` + - `roll`、`oRoll` -> `SingleQuadParticle#roll`、`oRoll` + - `setColor`、`setAlpha` -> `SingleQuadParticle#setColor`、`setAlpha` + - `render`、`renderCustom` -> `ParticleGroupRenderState#submit`,不是一对一 + - `getRenderType` -> `getGroup` + - 此方法的原始用途已移至 `SingleQuadParticle#getLayer` + - `getParticleGroup` -> `getParticleLimit`,不是一对一 + - `ParticleEngine` 不再实现 `PreparableReloadListener` + - 构造函数现在接受 `ParticleResources` 而不是 `TextureManager` + - `close` 已移除 + - `updateCount` 现在是 `protected` + - `render` -> `extract`,不是一对一 + - `destroy` -> `ClientLevel#addDestroyBlockEffect` + - `crack` -> `ClientLevel#addBreakingBlockEffect` + - `clearParticles` 现在是 `public` + - `$MutableSpriteSet` -> `ParticleResources$MutableSpriteSet` + - `$SpriteParticleRegistration` -> `ParticleResources$SpriteParticleRegistration` + - `ParticleGroup` - 特定 `ParticleRenderType` 的粒子持有者,负责Tick和提取通用渲染状态。 + - `ParticleProvider` + - `createParticle` 现在接受 `RandomSource` + - `$Sprite#createParticle` 现在接受 `RandomSource` 并返回 `SingleQuadParticle` 而不是 `TextureSheetParticle` + - `ParticleRenderType` 不再接受 `RenderType` + - 此记录已被重新用于表示粒子组的键 + - `TERRAIN_SHEET` -> `SingleQuadParticle$Layer#TERRAIN` + - `PARTICLE_SHEET_OPAQUE` -> `SingleQuadParticle$Layer#OPAQUE` + - `PARTICLE_SHEET_TRANSLUCENT` -> `SingleQuadParticle$Layer#TRANSLUCENT` + - `CUSTOM` 被一个不是为 `ParticleRenderType#SINGLE_QUADS` 的粒子组取代 + - `ParticleResources` - 加载粒子提供者、任何必要的描述,并将它们计算到所需的精灵集中。 + - `PlayerCloudParticle` 现在继承 `SingleQuadParticle` + - `PortalParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `QuadParticleGroup` - 负责设置和提交单个四边形粒子的粒子组。 + - `ReversePortalParticle` 现在接受一个 `TextureAtlasSprite` + - `RisingParticle` 现在继承 `SingleQuadParticle` + - `SculkChargeParticle` 现在继承 `SingleQuadParticle` + - `SculkChargePopParticle` 现在继承 `SingleQuadParticle` + - `SculkChargePopParticle` 现在继承 `SingleQuadParticle` + - `ShriekParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `SimpleAnimatedParticle` 现在继承 `SingleQuadParticle` 并且是 `abstract` + - `SingleQuadParticle` 现在接受一个 `TextureAtlasSprite` + - `sprite` - 粒子的纹理。 + - `render` -> `extract`,不是一对一,现在接受 `QuadParticleRenderState` 而不是 `VertexConsumer` + - `renderRotatedQuad` -> `extractRotatedQuad`,不是一对一,现在接受 `QuadParticleRenderState` 而不是 `VertexConsumer` + - `getU0`、`getU1`、`getV0`、`getV1` 不再是抽象的 + - `getLayer` - 设置单个四边形的渲染层。 + - `$Layer` - 单个四边形应渲染的层。 + - `SnowflakeParticle` 现在继承 `SingleQuadParticle` + - `SpellParticle` 现在继承 `SingleQuadParticle` + - `InstantProvider` 泛型现在使用 `SpellParticleOption` + - `SplashParticle` 现在接受一个 `TextureAtlasSprite` + - `SpriteSet#first` - 返回精灵集中的第一个纹理。 + - `SuspendedParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `SuspendedTownParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `TerrainParticle` 现在继承 `SingleQuadParticle` + - `TextureSheetParticle` 类已移除,请改用 `SingleQuadParticle` + - `TrailParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `TrialSpawnerDetectionParticle` 现在继承 `SingleQuadParticle` + - `VibrationSignalParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `WakeParticle` 现在继承 `SingleQuadParticle` + - `WaterCurrentDownParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` + - `WaterDropParticle` 现在继承 `SingleQuadParticle` 并接受一个 `TextureAtlasSprite` +- `net.minecraft.client.player.AbstractClientPlayer` 字段现在存储在 `ClientAvatarState` 中 + - `elytraRot*` -> `*Cloak` + - `clientLevel` 已移除 + - `getDeltaMovementLerped` -> `addWalkedDistance`,不是一对一 + - `updateBob` - 更新相机的摆动运动。 +- `net.minecraft.client.renderer` + - `EndFlashState` - 末地闪光的渲染状态。 + - `GameRenderer` 现在接受 `BlockRenderDispatcher` + - `getSubmitNodeStorage` - 获取用于功能类对象的节点提交。 + - `getFeatureRenderDispatcher` - 获取用于渲染功能类对象的分派器。 + - `getLevelRenderState` - 获取等级中动态功能的渲染状态。 + - `ItemInHandRenderer` + - `renderItem` 现在接受 `SubmitNodeCollector` 而不是 `MultiBufferSource` + - `renderHandsWithItems` 现在接受 `SubmitNodeCollector` 而不是 `MultiBufferSource$BufferSource` + - `LevelRenderer` 现在接受 `LevelRenderState` 和 `FeatureRenderDispatcher` + - `getSectionRenderDispatcher` 现在可为 null + - `tickParticles` 已移除 + - `addParticle` 已移除 + - `MapRenderer` 现在接受 `AtlasManager` 而不是 `MapDecorationTextureManager` + - `render` 现在接受 `SubmitNodeCollector` 而不是 `MultiBufferSource` + - `OrderedSubmitNodeCollector` - 一个提交处理器,用于在功能被分派时以给定顺序将元素绘制到屏幕。 + - `OutlineBufferSource` 不再接受任何参数 + - `setColor` 现在接受一个整数 + - `ParticleGroupRenderState` - 一组粒子的渲染状态。 + - `ParticlesRenderState` - 所有粒子的渲染状态。 + - `QuadParticleRenderState` - 所有单个四边形粒子的渲染组状态。 + - `RenderPipelines` + - `GUI_TEXT` - GUI 中文本的管线。 + - `GUI_TEXT_INTENSITY` - GUI 中未着色文本的强度管线。 + - `RenderStateShard#TRANSLUCENT_TARGET`、`PARTICLES_TARGET` 已移除 + - `RenderType` + - `pipeline` - 该类型使用的 `RenderPipeline`。 + - `opaqueParticle`、`translucentParticle` 已移除 + - `sunriseSunset`、`celestial` 已移除 + - `ScreenEffectRenderer` 现在接受一个 `MaterialSet` + - `renderScreenEffect` 现在接受一个 `SubmitNodeCollector` + - `ShapeRenderer` + - `renderLineBox` 现在接受 `PoseStack$Pose` 而不是 `PoseStack` + - `renderFace` 现在接受 `Matrix4f` 而不是 `PoseStack` + - `Sheets` + - `GUI_SHEET`、`MAP_DECORATIONS_SHEET`、`PAINTINGS_SHEET` - 图集纹理。 + - `BLOCK_ENTITIES_MAPPER` - 将方块纹理映射到方块实体的映射器。 + - `*COPPER*` - 铜箱子的材质。 + - `chooseMaterial` 现在接受 `ChestRenderState$ChestMaterialType` 而不是 `BlockEntity` 和 `boolean` + - `SkyRenderer` + - `END_SKY_LOCATION` 现在是私有的 + - `renderSunMoonAndStars` 不再接受缓冲区源 + - `renderEndFlash` 不再接受缓冲区源 + - `renderSunriseAndSunset` 不再接受缓冲区源 + - `SpecialBlockModelRenderer` + - `vanilla` 现在接受 `SpecialModelRenderer$BakingContext` 而不是 `EntityModelSet` + - `renderByBlock` 现在接受 `SubmitNodeCollector` 而不是 `MultiBufferSource`,以及一个轮廓颜色 + - `SubmitNodeCollection` - `OrderedSubmitNodeCollector` 的一个实现,将提交的功能保存在单独的列表中。 + - `SubmitNodeCollector` - 一个 `OrderedSubmitNodeCollector`,提供更改元素将渲染的当前顺序的方法。 + - `SubmitNodeStorage` - 由某个顺序持有的集合的存储。 + - `SkyRenderer` + - `renderEndFlash` - 渲染末地闪光。 + - `initTextures` - 获取所用元素的纹理。 + - `extractRenderState` - 从当前等级提取 `SkyRenderState`。 + - `WeatherEffectRenderer` + - `render` 现在接受 `WeatherRenderState` 而不是 `int`、`float` 和 `Level` + - 这些字段已移至 `extractRenderState` + - `extractRenderState` - 从当前等级提取 `WeatherRenderState`。 + - `$ColumnInstance` 现在是公开的 + - `WorldBorderRenderer` + - `render` 现在接受 `WorldBorderRenderState` 而不是 `WorldBorder` + - `extract` - 从当前世界边界提取 `WorldBorderRenderState`。 +- `net.minecraft.client.renderer.block` + - `BlockRenderDispatcher` 现在接受一个 `MaterialSet` + - `MovingBlockRenderState` - 一个实现 `BlockAndTintGetter` 的移动方块的渲染状态。 + - `LiquidBlockRenderer#setupSprites` 现在接受 `BlockModelShaper` 和 `MaterialSet` +- `net.minecraft.client.renderer.blockentity` + - 这里的大多数接受 `MultiBufferSource` 的方法已被 `SubmitNodeCollector` 取代,如果该方法不用于物品渲染,则还有 `ModelFeatureRenderer$CrumblingOverlay` + - 大多数方法从 `render*` 更名为 `submit*`,主要的 submit 方法现在使用 `BlockEntityRenderState` + - 所有 `BlockEntityRenderer` 现在都有一个 `BlockEntityRenderState` 泛型 + - `AbstractEndPortalRenderer` - 末地传送门的方块实体渲染器。 + - `AbstractSignRenderer` + - `getSignModel` 现在返回一个 `Model$Simple` + - `renderSign` -> `submitSign`,现在接受一个 `Model$Simple` 并且不再接受色调颜色 + - `BannerRenderer` 有一个接受 `SpecialModelRenderer$BakingContext` 的重载 + - `EntityModelSet` 构造函数现在接受 `MaterialSet` + - `renderPatterns` -> `submitPatterns` 现在接受 `MaterialSet` 和 `ModelFeatureRenderer$CrumblingOverlay`,`ModelPart` 已被 `Model` 及其渲染状态取代,一个 `boolean` 表示是否使用实体闪光,以及一个轮廓颜色 + - 带有两个额外 `boolean` 的重载已移除 + - `renderSpecial` -> `submitSpecial`,现在接受轮廓颜色 + - `BeaconRenderer#renderBeaconBeam` -> `submitBeaconBeam`,不再接受游戏时间 `long` + - `BedRenderer` 有一个接受 `SpecialModelRenderer$BakingContext` 的重载 + - `EntityModelSet` 构造函数现在接受 `MaterialSet` + - `renderSpecial` -> `submitSpecial`,现在接受轮廓颜色 + - `BlockEntityRenderDispatcher` 现在接受 `MaterialSet` 和 `PlayerSkinRenderCache` + - `render` -> `submit`,现在接受 `BlockEntityRenderState` 而不是 `BlockEntity`,不再接受部分刻 `float`,并接受 `CameraRenderState` + - `getRenderer` 现在有一个重载,可以从其 `BlockEntityRenderState` 获取渲染器 + - `tryExtractRenderState` - 从 `BlockEntity` 获取 `BlockEntityRenderState` + - `level`、`camera`、`cameraHitResult` 已移除 + - `prepare` 现在只接受 `Camera` + - `setLevel` 已移除 + - `BlockEntityRenderer` 现在有另一个泛型 `S` 表示 `BlockEntityRenderState` + - `render` -> `submit`,接受 `BlockEntityRenderState`、`PoseStack`、`SubmitNodeCollector` 和 `CameraRenderState` + - `createRenderState` - 创建渲染状态对象。 + - `extractRenderState` - 从方块实体中提取渲染状态。 + - `BlockEntityRendererProvider$Context` 现在是一个记录,接受 `MaterialSet` 和 `PlayerSkinRenderCache` + - 它现在有另一个泛型 `S` 表示 `BlockEntityRenderState` + - `CopperGolemStatueBlockRenderer` - 铜傀儡雕像的方块实体渲染器。 + - `DecoratedPotRenderer` 有一个接受 `SpecialModelRenderer$BakingContext` 的重载 + - `EntityModelSet` 构造函数现在接受 `MaterialSet` + - `render` 重载 -> `submit`,现在接受轮廓颜色 + - `HangingSignRenderer` + - `createSignModel` 现在返回一个 `Model$Simple` + - `renderInHand` 现在接受一个 `MaterialSet` + - `ShelfRenderer` - 架子的方块实体渲染器。 + - `ShulkerBoxRenderer` 有一个接受 `SpecialModelRenderer$BakingContext` 的重载 + - `EntityModelSet` 构造函数现在接受 `MaterialSet` + - `render` 重载 -> `submit`,现在接受轮廓颜色 + - `SignRenderer` + - `createSignModel` 现在返回一个 `Model$Simple` + - `renderInHand` 现在接受一个 `MaterialSet` + - `SkullBlockRenderer#submitSkull` - 将骷髅头模型提交给收集器。 + - `SpawnerRenderer#renderEntityInSpawner` -> `submitEntityInSpawner`,现在接受 `CameraRenderState` + - `TestInstanceREnderer` 现在接受 `BlockEntityRendererProvider$Context` +- `net.minecraft.client.renderer.blockentity.state` + - `BannerRenderState` - 旗帜方块实体的渲染状态。 + - `BeaconRenderState` - 信标方块实体的渲染状态。 + - `BedRenderState` - 床方块实体的渲染状态。 + - `BellRenderState` - 钟方块实体的渲染状态。 + - `BlockEntityRenderState` - 所有方块实体的基础渲染状态。 + - `BlockEntityWithBoundingBoxRenderState` - 具有自定义边界框的方块实体的渲染状态。 + - `BrushableBlockRenderState` - 可刷扫方块实体的渲染状态。 + - `CampfireRenderState` - 营火方块实体的渲染状态。 + - `ChestRenderState` - 箱子方块实体的渲染状态。 + - `CondiutRenderState` - 潮涌核心方块实体的渲染状态。 + - `CopperGolemStatueRenderState` - 铜傀儡方块实体的渲染状态。 + - `DecoratedPotRenderState` - 饰纹陶罐方块实体的渲染状态。 + - `EnchantTableRenderState` - 附魔台方块实体的渲染状态。 + - `EndGatewayRenderState` - 末地折跃门方块实体的渲染状态。 + - `EndPortalRenderState` - 末地传送门方块实体的渲染状态。 + - `LecternRenderState` - 讲台方块实体的渲染状态。 + - `PistonHeadRenderState` - 活塞头方块实体的渲染状态。 + - `ShelfRenderState` - 架子方块实体的渲染状态。 + - `ShulkerBoxRenderState` - 潜影盒方块实体的渲染状态。 + - `SignRenderState` - 告示牌方块实体的渲染状态。 + - `SkullBlockRenderState` - 骷髅头方块实体的渲染状态。 + - `SpawnerRenderState` - 刷怪笼方块实体的渲染状态。 + - `TestInstanceRenderState` - 测试实例方块实体的渲染状态。 + - `VaultRenderState` - vault 方块实体的渲染状态。 +- `net.minecraft.client.renderer.culling.Frustum` + - `offset` - 偏移位置。 + - `pointInFrustum` - 检查提供的坐标是否在平截头体内。 +- `net.minecraft.client.renderer.entity` + - 这里的大多数接受 `MultiBufferSource` 和光照坐标整数的方法已被 `SubmitNodeCollector` 和渲染状态参数取代 + - 大多数方法从 `render*` 更名为 `submit*` + - `AbstractBoatRenderer#renderTypeAdditions` -> `submitTypeAdditions` + - `AbstractMinecartRenderer#renderMinecartContents` -> `submitMinecartContents` + - `AbstractSkeletonRenderer` 接受 `ArmorModelSet` 而不是 `ModelLayerLocation` + - `AbstractZombieRenderer` 接受 `ArmorModelSet` 而不是模型 + - `ArmorModelSet` - 一个将某个对象映射到每个人形盔甲槽位的持有者。通常保存层定义,然后将其烘焙到关联的模型中。 + - `BreezeRenderer#enable` 已移除 + - `CopperGolemRenderer` - 铜傀儡实体的渲染器。 + - `DisplayRenderer#renderInner` -> `submitInner` + - `EnderDragonRenderer#renderCrystalBeams` -> `submitCrystalBeams` + - `EntityRenderDispatcher` 现在接受 `AtlasManager` + - `prepare` 不再接受 `Level` + - `setRenderShadow`、`setRenderHitBoxes`、`shouldRenderHitBoxes` 已移除 + - `extractEntity` - 从实体和部分刻创建渲染状态。 + - `render` -> `submit`,现在接受 `CameraRenderState` + - `setLevel` -> `resetCamera`,不是一对一 + - `getPlayerRenderer` - 从给定的客户端玩家获取 `AvatarRenderer`。 + - `overrideCameraOrientation`、`distanceToSqr`、`cameraOrientation` 已移除 + - `EntityRenderer` + - `NAMETAG_SCALE` 现在是公开的 + - `render(S, PoseStack, MultiBufferSource, int)` -> `submit(S, PoseStack, SubmitNodeCollector, CameraRenderState)` + - `renderNameTag` -> `submitNameTag`,现在接受 `CameraRenderState` + - `finalizeRenderState` - 在 `extractRenderState` 之后作为最后一步提取渲染状态的信息,例如阴影。 + - `EntityRendererProvider$Context` 现在接受 `PlayerSkinRenderCache` 和 `AtlasManager` + - `getModelManager` 已移除 + - `getMaterials` - 返回材质到图集精灵的映射器。 + - `getPlayerSkinRenderCache` - 获取玩家皮肤的渲染缓存。 + - `getAtlas` - 返回该位置的图集。 + - `EntityRenderers#createPlayerRenderers` 现在返回一个 `PlayerModelType` 到 `AvatarRenderer` 的映射 + - `ItemEntityRenderer` + - `renderMultipleFromCount` -> `submitMultipleFromCount` + - `renderMultipleFromCount(PoseStack, MultiBufferSource, int, ItemClusterRenderState, RandomSource)` -> `renderMultipleFromCount(PoseStack, SubmitNodeCollector, int, ItemClusterRenderState, RandomSource)` + - `ItemRenderer` 不再接受 `ItemModelResolver` + - `getArmorFoilBuffer` -> `getFoilRenderTypes`,不是一对一 + - `renderStatic` 方法已移除 + - `MobRenderer#checkMagicName` - 返回自定义名称是否与给定字符串匹配。 + - `PiglinRenderer` 接受 `ArmorModelSet` 而不是 `ModelLayerLocation` + - `TntMinecartRenderer#renderWhiteSolidBlock` -> `submitWhiteSolidBlock`,现在接受一个轮廓颜色 + - `ZombieRenderer` 接受 `ArmorModelSet` 而不是 `ModelLayerLocation` + - `ZombifiedPiglinPiglinRenderer` 接受 `ArmorModelSet` 而不是 `ModelLayerLocation` +- `net.minecraft.client.renderer.entity.layers` + - `ArrowLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `BeeStingerLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `BlockDecorationLayer` - 一个处理由实体变换的方块模型的层。 + - `BreezeWindLayer` 现在接受 `EntityModelSet` 而不是 `EntityRendererProvider$Context` + - `CapeLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `CustomHeadLayer` 现在接受 `PlayerSkinRenderCache` + - `Deadmau5EarsLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `EquipmentLayerRenderer#renderLayers` 现在接受渲染状态、`SubmitNodeCollector`、轮廓颜色和一个初始顺序,而不是 `MultiBufferSource` + - `HumanoidArmorLayer` 现在接受 `ArmorModelSet` 而不是模型 + - `setPartVisibility` 已移除 + - `ItemInHandLayer#renderArmWithItem` -> `submitArmWithItem` + - `LivingEntityEmissiveLayer` 现在接受一个用于纹理的函数而不是 `ResourceLocation`,以及一个模型而不是 `$DrawSelector` + - `$DrawSelector` 已移除 + - `ParrotOnShoulderLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `PlayerItemInHandLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `RenderLayer` + - `renderColoredCutoutModel`、`coloredCutoutModelCopyLayerRender` 现在接受 `Model` 而不是 `EntityModel`,接受 `SubmitNodeCollector` 而不是 `MultiBufferSource`,以及一个表示渲染顺序的整数 + - `render` -> `submit`,接受 `SubmitNodeCollector` 而不是 `MultiBufferSource` + - `SimpleEquipmentLayer` 现在接受一个顺序整数 + - `SpinAttackEffectLayer` 现在处理 `AvatarRenderState` 而不是 `PlayerRenderState` + - `StuckInBodyLayer` 现在有一个用于渲染状态的额外泛型,在构造函数中也接受渲染状态 + - `numStuck` 现在接受 `AvatarRenderState` 而不是 `PlayerRenderState` + - `VillagerProfessionLayer` 现在接受两个模型 +- `net.minecraft.client.renderer.entity.player.PlayerRenderer` -> `AvatarRenderer` + - `render*Hand` 现在接受 `SubmitNodeCollector` 而不是 `MultiBufferSource` +- `net.minecraft.client.renderer.entity.state` + - `CopperGolemRenderState` - 铜傀儡实体的渲染状态。 + - `DisplayEntityRenderState#cameraYRot`、`cameraXRot` - 相机的旋转。 + - `EntityRenderState` + - `NO_OUTLINE` - 一个表示无轮廓颜色的常量。 + - `outlineColor` - 实体的轮廓颜色。 + - `lightCoords` - 用于照亮实体的打包光照坐标。 + - `shadowPieces`、`$ShadowPiece` - 表示实体投射的阴影的相对坐标。 + - `FallingBlockRenderState` 字段和实现已全部移至 `MovingBlockRenderState` + - `LivingEntityRenderState` + - `appearsGlowing` -> `EntityRenderState#appearsGlowing`,现在是一个方法 + - `customName` 已移除 + - `PaintingRenderState#lightCoords` -> `lightCoordsPerBlock` + - `PlayerRenderState` -> `AvatarRenderState` + - `useItemRemainingTicks`、`swinging` 已移除 + - `showDeadMouseEars` -> `showExtraEars` + - `SheepRenderState` + - `id` 已移除 + - `isJebSheep` 现在是一个字段而不是方法 + - `WitherSkullRenderState#xRot`、`yRot` -> `modeState`,不是一对一 +- `net.minecraft.client.renderer.feature` + - `BlockFeatureRenderer` - 渲染提交的方块、方块模型或下落中的方块。 + - `CustomFeatureRenderer` - 通过传递的函数渲染提交的几何体。 + - `FeatureRenderDispatcher` - 分派所有功能以从提交的节点收集器对象渲染。 + - `FlameFeatureRenderer` - 渲染提交的着火的实体动画。 + - `HitboxFeatureRenderer` - 渲染提交的实体碰撞箱。 + - `ItemFeatureRenderer` - 渲染提交的物品。 + - `LeashFeatureRenderer` - 渲染提交的附着在实体上的拴绳。 + - `ModelFeatureRenderer` - 渲染提交的 `Model`。 + - `ModelPartFeatureRenderer` - 渲染提交的 `ModelPart`。 + - `NameTagFeatureRenderer` - 渲染提交的名称标签。 + - `ParticleFeatureRenderer` - 渲染提交的粒子。 + - `ShadowFeatureRenderer` - 渲染提交的实体阴影。 + - `TextFeatureRenderer` - 渲染提交的文本。 +- `net.minecraft.client.renderer.item` + - `ItemModel$BakingContext` 现在接受 `MaterialSet` 和 `PlayerSkinRenderCache`,并实现 `SpecialModelRenderer$BakingContext` + - `ItemStackRenderState#render` -> `submit`,接受 `SubmitNodeCollector` 而不是 `MultiBufferSource` 以及一个轮廓颜色 +- `net.minecraft.client.renderer.special` + - 这里的大多数接受 `MultiBufferSource` 的方法已被 `SubmitNodeCollector` 取代 + - `ChestSpecialRenderer` 现在接受一个 `MaterialSet` + - `*COPPER*` - 铜箱子的纹理。 + - `ConduitSpecialRenderer` 现在接受一个 `MaterialSet` + - `CopperGolemStatueSpecialRenderer` - 铜傀儡雕像作为物品的特殊渲染器。 + - `HangingSignSpecialRenderer` 现在接受一个 `MaterialSet` 和一个 `Model$Simple` 而不是 `Model` + - `NoDataSpecialModelRenderer#render` -> `submit`,现在接受一个轮廓颜色 + - `PlayerHeadSpecialRenderer` 现在接受 `PlayerSkinRenderCache` + - `ShieldSpecialRenderer` 现在接受一个 `MaterialSet` + - `SpecialModelRenderer` + - `render` -> `submit`,现在接受一个轮廓颜色 + - `$BakingContext` - 用于烘焙特殊物品模型的上下文。 + - `$Unbaked#bake` 现在接受 `SpecialModelRenderer$BakingContext` 而不是 `EntityModelSet` + - `SpecialModelRenderers#createBlockRenderers` 现在接受 `SpecialModelRenderer$BakingContext` 而不是 `EntityModelSet` + - `StandingSignSpecialRenderer` 现在接受一个 `MaterialSet` 和一个 `Model$Simple` 而不是 `Model` +- `net.minecraft.client.renderer.state` + - `BlockBreakingRenderState` - 当前方块被破坏进度的渲染状态。 + - `BlockOutlineRenderState` - 通过其 `VoxelShape` 的方块轮廓的渲染状态。 + - `CameraRenderState` - 相机的渲染状态。 + - `LevelRenderState` - 等级中动态功能的渲染状态。 + - `ParticleGroupRenderState` - 一组粒子的渲染状态。 + - `ParticlesRenderState` - 所有粒子的渲染状态。 + - `QuadParticleRenderState` - 所有单个四边形粒子的渲染组状态。 + - `SkyRenderState` - 天空的渲染状态,包括月亮和星星。 + - `WeatherRenderState` - 当前天气的渲染状态。 + - `WorldBorderRenderState` - 世界边界的渲染状态。 +- `net.minecraft.client.renderer.texture` + - `SkinTextureDownloader` 现在是一个实例类而不是静态方法持有者,接受 `Proxy`、`TextureManager` 和主线程 `Executor` + - 以前是静态的大多数方法现在是实例方法 + - `SpriteContents` 现在接受一个可选的 `AnimationMetadataSection` 和 `MetadataSectionType$WithValue` 列表,而不是 `ResourceMetadata` + - `metadata` -> `getAdditionalMetadata`,不是一对一 + - `SpriteLoader` + - `DEFAULT_METADATA_SECTIONS` 已移除 + - `stitch` 现在是私有的 + - `runSpriteSuppliers` 现在是私有的 + - `loadAndStitch(ResourceManager, ResourceLocation, int, Executor)` 已移除 + - `loadAndStitch` 现在接受一组 `MetadataSectionType` 而不是一个集合 + - `$Preparations` + - `waitForUpload` 已移除 + - `getSprite` - 返回给定资源位置的图集精灵。 + - `TextureAtlasSprite#isAnimated` -> `SpriteContents#isAnimated` +- `net.minecraft.client.renderer.texture.atlas.SpriteResourceLoader#create` 现在接受一组 `MetadataSectionType` 而不是一个集合 +- `net.minecraft.client.resources` + - `MapDecorationTextureManager` 类已移除 + - `PaintingTextureManager` 类已移除 + - `PlayerSkin$Model` -> `PlayerModelType`,不是一对一 + - 构造函数现在接受遗留服务名称 + - `byName` -> `byLegacyServicesName` + - `SkinManager` 现在接受 `Services` 而不是 `MinecraftSessionService`,以及一个 `SkinTextureDownloader` + - `lookupInsecure` -> `createLookup`,现在接受一个布尔值表示是否检查不安全的皮肤 + - `getOrLoad` -> `get` + - `TextureAtlasHolder` 类已移除 +- `net.minecraft.client.resources.model` + - `AtlasIds` -> `net.minecraft.data.AtlasIds` + - `AtlasSet` -> `AtlasManager`,不是一对一 + - `forEach` - 遍历每个图集图版。 + - `Material` + - `sprite` -> `MaterialSet#get` + - `buffer` 现在接受一个 `MaterialSet` + - `MaterialSet` - 材质到其图集精灵的映射。 + - `ModelBakery` 现在接受 `MaterialSet` 和 `PlayerSkinRenderCache` + - `ModelManager` 不再是 `AutoCloseable` + - 构造函数接受 `PlayerSkinRenderCache`、`AtlasManager` 而不是 `TextureManager`,以及最大 mipmap 级别整数 + - `getAtlas` -> `MaterialSet#get` + - `updateMaxMipLevel` -> `AtlasManager#updateMaxMipLevel` +- `net.minecraft.core.particles` + - `ParticleGroup` -> `ParticleLimit` + - `ParticleTypes` + - `DRAGON_BREATH` 现在使用 `PowerParticleOption` + - `EFFECT` 现在使用 `SpellParticleOption` + - `FLASH` 现在使用 `ColorParticleOption` + - `INSTANT_EFFECT` 现在使用 `SpellParticleOption` + - `PowerParticleOption` - 龙息粒子的粒子选项。 + - `SpellParticleOption` - 药水效果的粒子选项。 + +## 字体 字形 管线 + +字形的后端已被部分重做,以将字符及其效果统一到一个可渲染的对象,并添加烘焙流程(尽管是延迟的)。本文将简要概述这个新流程。 + +让我们从资源重新加载时开始,导致 `FontManager` 运行。字体定义被加载并传递到它们适当的 `GlyphProviderDefinition`,对于这个例子,它将立即解包到 `GlyphProvider$Loader` 中。加载器读取它需要的任何信息(很可能是纹理),并将其映射到关联的码点作为 `UnbakedGlyph`。一个 `UnbakedGlyph` 表示一个包含其 `GlyphInfo`(带有位置元数据)的单个字符,以及一个将其写入纹理的 `bake` 方法。`bake` 接受一个 `UnbakedGlyph$Stitcher`,它本身接受一个 `GlyphBitmap` 以正确定位和上传字形,以及用于任何偏移调整的 `GlyphInfo`。然后,在解析和最终确定之后,使用 `FontSet#reload` 创建 `FontSet`。这会重置纹理和缓存,并调用 `FontSet#selectProviders` 来存储活动的 `GlyphProvider` 列表,并通过将字符宽度映射到匹配的码点列表来填充 `glyphsByWidth`。此时,所有加载都已完成。虽然字形以未烘焙的格式存储,但它们直到该特定字符被渲染时才被烘焙和缓存。 + +现在,让我们转到 `FontDescription`。`FontDescription` 提供了与 `GlyphSource` 的一对一映射,后者负责获取字形。原版提供了三种 `FontDescription`:`$Resource` 用于映射到如上所述的 `FontSet`(字体 JSON 名称),`$AtlasSprite` 用于将图集解释为一组字形,以及 `$PlayerSprite` 用于渲染玩家头部和帽子。对于给定的 `Component`,可以通过 `Style#withFont` 将使用的 `FontDescription` 设置为其 `Style`。 + +因此,假设您使用 `FontDescription#DEFAULT` 将文本绘制到屏幕,它使用定义的 `assets/minecraft/font/default.json`。最终,这将调用 `Font#drawInBatch` 或 `drawInBatch8xOutline`。然后,无论是通过 `StringSplitter` 还是直接,`GlyphSource` 都是从 `FontDescription` 获得的。然后,调用 `GlyphSource#getGlyph`。对于 `FontDescription$AtlasSprite` 和 `$PlayerSprite`,这仅返回相关纹理的包装器。对于 `$Resource`,这内部调用 `FontSet#getGlyph`,它将 `UnbakedGlyph` 存储为 `FontSet$DelayedBake` 的一部分,然后通过调用 `UnbakedGlyph#bake` 将字形写入某个纹理,立即将其解析为 `BakedGlyph`。 + +`BakedGlyph` 包含两个方法:如前所述的用于位置元数据的 `GlyphInfo`,以及一个称为 `createGlyph` 的方法,它返回一个 `TextRenderable`:一个用于将字形绘制到屏幕的渲染器。`TextRenderable` 还有一个子接口,称为 `PlainTextRenderable`,用于帮助处理纹理精灵。在内部,所有资源 `BakedGlyph` 都是 `BakedSheetGlyph`,因为 `UnbakedGlyph$Stitcher#stitch` 调用只是包装了 `GlyphStitcher#stitch`。您可以将 `BakedSheetGlyph` 视为 256x256 `FontTexture` 上的一个视图,如果一张 `FontTexture` 上没有足够的空间,则会创建一个新的 `FontTexture`。 + +`BakedSheetGlyph` 还实现了 `EffectGlyph`,它可能应该在文本上渲染某种效果。这个功能,虽然目前仅作为另一个对象实现,但仅用于白色字形,而白色字形未被使用。 + +### 对象信息 + +`ObjectInfo` 是服务器端的实现,用于构造通过字形管线渲染任意对象所使用的 `FontDescription`。每个 info 接受 `FontDescription` 和一个常规的 `String` 来显示简单的描述。此外,它接受一个 `MapCodec` 来从对象内容进行编码和解码。 + +请注意,要使自定义 `FontDescription` 正确映射到源,您需要以某种方式修改 `FontManager$CachedFontProvider#getGlyphSource` 或实现自定义的 `Font$Provider`。 + +```java +// 一个基本的字体描述 +public static record BlockDescription(Block block) implements FontDescription {} + +// 一个简单的对象信息 +public record BlockInfo(Block block) implements ObjectInfo { + // 用于通过网络发送信息的编解码器 + public static final MapCodec MAP_CODEC = BuiltInRegistries.BLOCK.byNameCodec().fieldOf("block"); + + @Override + public FontDescription fontDescription() { + // 要渲染的字体描述 + return new BlockDescription(this.block); + } + + @Override + public String description() { + // 只是对象的文本描述 + return Objects.toString(BuiltInRegistries.BLOCK.getKey(this.block)); + } + + @Override + public MapCodec codec() { + return MAP_CODEC; + } +} + +// 创建用于渲染对象的 `GlyphSource` 和 `PlainTextRenderable` +public record BlockRenderable(...) implements PlainTextRenderable { + // ... +} + +public static GlyphSource create(Block block) { + return new SingleSpriteSource(...); +} +``` + +`ObjectInfo` 也需要渲染到 `ObjectInfos#ID_MAPPER`: + +```java +// 假设映射器已设为公开 +ObjectInfos.ID_MAPPER.put("examplemod:block", BlockInfo.MAP_CODEC); +``` + +### 组件内容 + +组件内容现在也使用 id 映射器,而不是在类型上持有 id。这意味着创建自定义组件是通过以某种方式挂钩到 `ComponentSerialization#bootstrap` 并将您的 `ComponentContents` 实现的映射编解码器注册到映射器来完成的。 + +### 数据源 + +数据源也获得了与组件内容类似的处理,现在使用 id 映射器而不是在类型上持有 id。然后通过将您的 `DataSource` 的映射编解码器注册到 `DataSources#ID_MAPPER` 来注册数据源。请注意,映射器字段是私有的,因此您可能需要使用可用的解决方法。 + +- `com.mojang.blaze3d.font` + - `GlyphInfo` + - `bake` -> `UnbakedGlyph#bake`,现在接受 `UnbakedGlyph$Stitcher` 而不是函数 + - 函数行为被 `UnbakedGlyph$Stitcher#stitch` 取代 + - `$SpaceGlyphInfo` -> `GlyphInfo#simple` 和 `EmptyGlyph`,不是一对一 + - `$Stitched` -> `UnbakedGlyph`,不是一对一 + - `GlyphProvider#getGlyph` 现在返回一个 `UnbakedGlyph` + - `SheetGlyphInfo` -> `GlyphBitmap` +- `net.minecraft.client.gui` + - `Font` 不再接受函数和布尔值,而是接受 `Font$Provider` + - `random` 现在是私有的 + - `$GlyphVisitor` + - `acceptGlyph` 现在接受 `TextRenderable` 而不是 `BakedGlyph$GlyphInstance` + - `acceptEffect` 现在只接受一个 `TextRenderable` + - `$PreparedTextBuilder#accept` 现在有一个接受 `BakedGlyph` 而不是其码点的重载 + - `$Provider` - 一个简单的接口,用于根据其描述提供字形源,以及空字形。 + - `GlyphSource` - 一个接口,根据其码点持有烘焙的字形。 +- `net.minecraft.client.gui.font` + - `AtlasGlyphProvider` - 基于纹理图集的字形提供者。 + - `FontManager` 现在接受 `AtlasManager` 和 `PlayerSkinRenderCache` + - `FontSet` 现在接受 `GlyphStitcher` 而不是 `TextureManager`,并且不再接受名称 + - `name` 已移除 + - `source` - 根据是否只应看到非可疑字形,返回字形源。 + - `getGlyphInfo`、`getGlyph` -> `getGlyph`,现在是包私有的,不是一对一 + - `getRandomGlyph` 现在接受 `RandomSource` 和一个码点而不是 `GlyphInfo`,返回一个 `BakedGlyph` + - `whiteGlyph` 现在返回一个 `EffectGlyph` + - `$GlyphSource` - 一个可以获取要烘焙的字形的源。 + - `FontTexture#add` 现在接受 `GlyphInfo` 和 `GlyphBitmap`,返回一个 `BakedSheetGlyph` + - `GlyphStitcher` - 一个从其字形信息创建 `BakedGlyph` 的类。 + - `PlainTextRenderable` - `TextRenderable` 的一个实现,确定如何渲染精灵。 + - `PlayerGlyphProvider` - 用于渲染玩家头部和帽子的字形提供者。 + - `SingleSpriteSource` - 一个只包含一个字形的字形源,例如一个纹理。 + - `TextRenderable` - 表示文本表示(如字体中的字形)如何渲染。 +- `net.minecraft.client.gui.font.glyphs` + - `BakedGlyph` 现在是一个创建可渲染对象的接口 + - 其原始用途已移至 `BakedSheetGlyph` + - `EffectGlyph` - 一个创建某个字形效果的可渲染对象的接口。 + - `EmptyGlyph` 现在实现 `UnbakedGlyph` 而不是继承 `BakedGlyph` + - 构造函数接受字形的文本宽度。 + - `SpecialGlyphs#bake` 现在返回一个 `BakedSheetGlyph` + - 此方法在技术上是新的。 +- `net.minecraft.client.gui.font.providers` + - `BitmapProvider$Glyph` 现在实现 `UnbakedGlyph` 而不是 `GlyphInfo$Stitched` + - `UnihexProvider$Glyph` 现在实现 `UnbakedGlyph` 而不是 `GlyphInfo$Stitched` +- `net.minecraft.client.gui.render.state` + - `GlyphEffectRenderState` 已移除 + - 使用 `GlyphRenderState` + - `GlyphRenderState` 现在接受 `TextRenderable` 而不是 `BakedGlyph$Instance` +- `net.minecraft.network.chat` + - `Component#object` - 创建一个带有对象内容的可变组件。 + - `ComponentContents#type` -> `codec`,丢弃字符串 id + - `$Type` 已移除 + - `ComponentSerialization` + - `createLegacyComponentMatcher` 不再需要 `StringRepresentable` 泛型,而是使用带有 `String` 键的 id 映射器 + - `$FuzzyCodec` 现在接受一组映射编解码器而不是一个列表 + - `FontDescription` - 一个描述字体的标识符,通常作为位置或精灵。 + - `Style` 现在是 final + - `getFont` 现在返回一个 `FontDescription` + - `withFont` 现在接受 `FontDescription` 而不是 `ResourceLocation` + - `DEFAULT_FONT` 已移除 +- `net.minecraft.network.chat.contents` + - `BlockDataSource` -> `.data.BlockDataSource` + - `DataSource` -> `.data.DataSource` + - `type` -> `codec`,丢弃字符串 id + - `EntityDataSource` -> `.data.EntityDataSource` + - `*Contents` + - `CODEC` -> `MAP_CODEC` + - `TYPE` 已移除 + - `ObjectContents` - 可以作为组件一部分写入的任意内容,例如精灵。 + - `StorageDataSource` -> `.data.StorageDataSource` +- `net.minecraft.network.chat.contents.data.DataSources` - 所有原版注册的数据源。 +- `net.minecraft.network.chat.contents.objects` + - `AtlasSprite` - 纹理图集中精灵的对象信息。 + - `ObjectInfo` - 通过字形管线传递任意对象的引用相关信息。 + - `ObjectInfos` - 所有原版对象信息。 + - `PlayerSprite` - 玩家头部和帽子纹理的对象信息。 + +## JSON-RPC 管理服务器 + +Minecraft 引入了通过 JSON-RPC websocket 远程管理专用服务器的支持。这可以通过 `management-server-enabled` 属性启用,默认情况下它在 `localhost:25585` 上监听服务器。整个系统不仅处理通过 `JsonElement` 发送的网络请求的构造,还处理每个请求使用的模式。然后可以通过 `JsonRpcApiSchema` 数据提供者生成这些模式。 + +系统支持两种类型的 RPC 方法:来自管理服务器的请求的 `IncomingRpcMethod`,以及通知或向管理服务器请求信息的 `OutgoingRpcMethod`。这两者都是静态注册表对象,但通常通过其关联的构建器构造和注册,而不是实现接口。 + +### 模式 + +顾名思义,`Schema` 是 JSON 对象的规范。它们以类似于 `JsonElement` 的方式构造。大多数构造为 `Schema#record`,其字段和类型通过 `withField` 填充,然后存储在 `SchemaComponent` 中,该组件将引用名称映射到模式。然后可以通过 `asRef` 或 `asArray` 分别获取对象或数组实现。`SchemaComponent` 本身被注册到列表 `Schema#SCHEMA_REGISTRY` 以进行引用解析。 + +```json5 +// 一个更改天气的 JSON 示例: +{ + "weather": "clear|rain|thunder", + "duration": -1, +} +``` + +关联的模式组件如下所示: + +```java +public static final SchemaComponent WEATHER_SCHEMA = new SchemaComponent( + // 引用名称 + // 虽然我们可以使用冒号,但需要按照 RFC 3986 定义的百分号编码 + "examplemod_weather", + // 要使用的模式 + Schema.record() + .withField("weather", Schema.ofEnum(List.of("clear", "rain", "thunder"))) + .withField("duration", Schema.INT_SCHEMA) +); + +// 将模式注册到注册表 +Schema.getSchemaRegistry().add(WEATHER_SCHEMA); +``` + +请注意,模式目前无法指定单个属性是否可选。 + +### `IncomingRpcMethod` + +`IncomingRpcMethod` 通过 `method` 构建器构造和注册。这些方法接受更新 Minecraft 服务器的处理函数、结果的编解码器,以及一个来自管理服务器的参数的可选编解码器。从那里,构建器包含用于发现服务以及如何执行方法的方法。`$IncomingRpcMethodBuilder#response` 和 `param` 定义管理服务器发送的参数和响应。`description` 由发现服务用于模式。`undiscoverable` 默认隐藏路由。最后,`notOnMainThread` 告诉该方法它可以在主线程之外运行。一旦调用了所需的方法,就可以使用 `build` 或 `register` 来构造对象,接受路由端点的命名空间和路径。 + +传入的方法接受 `MinecraftApi` 用于通过指定的“服务”与专用服务器通信,以及当前的 `ClientInfo` 连接。如果存在参数对象,它也接受该参数对象。 + +```java +// 构造 dto 对象 +public record WeatherDto(String weather, int duration) { + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + Codec.STRING.fieldOf("weather").forGetter(WeatherDto::weather), + Codec.INT.fieldOf("duration").forGetter(WeatherDto::duration), + ) + .apply(instance, WeatherDto::new) + ); + + // 创建传入方法处理程序 + public static WeatherDto handleWeather(MinecraftApi api, WeatherDto params, ClientInfo info) { + // 注意,api 使服务器私有,因为它期望逻辑通过其中一个中间层服务处理。 + // 我们将假设 `server` 字段以某种方式被设为公开。 + if (params.weather().equals("clear")) { + api.server.overworld().setWeatherParameters(params.duration(), 0, false, false); + } else if (params.weather().equals("rain")) { + api.server.overworld().setWeatherParameters(0, params.duration(), true, false); + } else if (params.weather().equals("thunder")) { + api.server.overworld().setWeatherParameters(0, params.duration(), true, true); + } + + return params; + } +} + +// 构造并注册方法端点 +IncomingRpcMethod.method( + // 处理程序方法 + WeatherDto::handleWeather, + // 参数的编解码器 + WeatherDto.CODEC, + // 结果的编解码器 + WeatherDto.CODEC +) + // 设置路由的描述 + .description("设置天气") + // 参数 + .param(new ParamInfo( + // 对象的名称 + "weather", + // 模式 + WEATHER_SCHEMA.asRef() + )) + // 响应 + .response(new ResultInfo( + // 对象的名称 + "weather", + // 模式 + WEATHER_SCHEMA.asRef() + )) + // 构建并注册路由 + .register( + BuiltInRegistries.INCOMING_RPC_METHOD, + // 端点命名空间 + "examplemod", + // 端点路径 + "weather/update" + ); +``` + +### `OutgoingRpcMethod` + +`OutgoingRpcMethod` 通过 `notification` 或 `request` 构建器构造和注册。通知通常只发送参数而不返回结果,而请求也提供结果。在任何一种情况下,它们都只接受参数或结果(如果存在)的编解码器。`$OutgoingRpcMethod#response` 和 `param` 定义 Minecraft 服务器发送的参数和响应。`description` 由发现服务用于模式。一旦调用了所需的方法,就可以使用 `register` 来构造对象,接受路由端点的命名空间和路径,并将其包装在 `Holder$Reference` 中。 + +目前,原版仅通过 `NotificationService` 广播通知:一个用于特定方法操作(如玩家加入或更改游戏规则)的远程日志记录器。虽然有结果,但必须通过 `Connection#sendRequest` 读取并异步处理。 + +```java +// 构造传出方法 +// 由于 Minecraft 服务器将发送这些,我们需要持有该对象 +public static final Holder.Reference SOMETHING_HAPPENED = OutgoingRpcMethod.notification() + .description("某件事发生了!") + .register("examplemod", "something"); + +// 发送通知,假设我们可以访问 `ManagementServer` managementServer +managementServer.forEachConnection(connection -> connection.sendNotification(SOMETHING_HAPPENED)); +``` + +- `net.minecraft.SharedConstants#RPC_MANAGEMENT_SERVER_API_VERSION` - JSON RPC 服务器管理的 API 版本。 +- `net.minecraft.core.registries` + - `BuiltInRegistries`、`Registries` + - `INCOMING_RPC_METHOD` - 对 RPC 服务的查询的注册表。 + - `OUTGOING_RPC_METHOD` - 来自 RPC 服务的广播的注册表。 +- `net.minecraft.server` + - `MinecraftServer` + - `notificationManager` - 返回当前通知管理器。 + - `onGameRuleChanged` - 处理当前服务器的游戏规则更改时的情况。 + - `getOperatorUserPermissionLevel` -> `operatorUserPermissionLevel` + - `isLevelEnabled` -> `isAllowedToEnterPortal`,不是一对一 + - `setPvpAllowed` 被 `GameRules#RULE_PVP` 取代 + - `setFlightAllowed` 被 `DedicatedServerProperties#allowFlight` 取代 + - `isCommandBlockEnabled` 不再是抽象的 + - `isSpawnerBlockEnabled` - 返回刷怪笼方块是否可以生成实体。 + - `getPlayerIdleTimeout` -> `playerIdleTimeout` + - `kickUnlistedPlayers` 不再接受 `CommandSourceStack` + - `pauseWhileEmptySeconds` -> `pauseWhenEmptySeconds` + - `getSpawnProtectionRadius` -> `DedicatedServer#spawnProtectionRadius` + - `isUsingWhiteList` - 处理为服务器启用白名单用户。 + - `setAutoSave`、`isAutoSave` - 处理服务器是否应每隔一段时间自动保存。 +- `net.minecraft.server.dedicated` + - `DedicatedServer` 现在接受 `LevelLoadListener` 而不是 `ChunkProgressListenerFactory` + - `setAllowFlight` - 设置玩家是否可以在服务器中飞行。 + - `setDifficulty` - 设置服务器的游戏难度。 + - `viewDistance`、`setViewDistance` - 处理服务器的视距(可渲染区块)。 + - `simulationDistance`、`setSimulationDistance` - 处理服务器的模拟距离(逻辑运行区块)。 + - `setMaxPlayers` - 设置可以加入服务器的最大玩家数。 + - `setSpawnProtectionRadius` - 设置应受重生保护保护的区块半径。 + - `setRepliesToStatus` - 设置游戏是否使用旧式查询处理程序。 + - `setHidesOnlinePlayers` - 设置是否应隐藏在线玩家不被其他玩家看到。 + - `setOperatorUserPermissionLevel` - 设置操作员的权限级别。 + - `statusHeartbeatInterval`、`setStatusHeartbeatInterval` - 处理管理服务器将检查 Minecraft 服务器是否仍然存活的心跳间隔。 + - `entityBroadcastRangePercentage`、`setEntityBroadcastRangePercentage` - 处理实体跟踪的广播范围标量。 + - `forceGameMode`、`setForceGameMode` - 处理强制所有用户的游戏模式。 + - `gameMode`、`setGameMode` - 设置服务器的默认游戏模式。 + - `setAcceptsTransfers` - 设置服务器是否接受来自其他服务器的客户端转移。 + - `setPauseWhenEmptySeconds` - 设置当没有玩家时服务器应等待多少秒才停止Tick。 + - `DedicatedServerProperties` + - `pvp` 被 `GameRules#RULE_PVP` 取代 + - `allowFlight`、`motd`、`forceGameMode`、`enforceWhitelist`、`difficulty`、`gameMode`、`spawnProtection`、`opPermissionLevel`、`viewDistance`、`simulationDistance`、`enableStatus`、`hideOnlinePlayers`、`entityBroadcastRangePercentage`、`pauseWhenEmptySeconds`、`acceptsTransfers` 现在是可变属性 + - `allowNether` 被 `GameRules#RULE_ALLOW_NETHER` 取代 + - `spawnMonsters` 被 `GameRules#RULE_SPAWN_MONSTERS` 取代 + - `enabledCommandBlock` 被 `GameRules#RULE_COMMAND_BLOCKS_ENABLED` 取代 + - `managementServerEnabled` - 返回是否启用了管理服务器。 + - `managementServerHost` - 返回管理服务器通信的主机。 + - `managementServerPort` - 返回管理服务器通信的端口。 + - `statusHeartbeatInterval` - 返回管理服务器发送以确保 Minecraft 服务器仍然存活的心跳间隔。 + - `MANAGEMENT_SERVER_TLS_ENABLED_KEY`、`MANAGEMENT_SERVER_TLS_KEYSTORE_KEY`、`MANAGEMENT_SERVER_TLS_KEYSTORE_PASSWORD_KEY` - 用于与管理服务器通信的 TLS 密钥库设置。 + - `managementServerSecret` - 授权令牌密钥。 + - `managementServerTlsEnabled` - 是否应使用 TLS 作为通信协议。 + - `managementServerTlsKeystore` - 指定用于 TLS 密钥库的文件路径。 + - `managementServerTlsKeystorePassword` - 指定密钥库文件的密码。 + - `Settings#getMutable` - 获取设置键和默认值的可变属性。 +- `net.minecraft.server.jsonrpc` + - `Connection` - 管理服务器发送的 json 元素的入站处理程序。 + - `IncomingRpcMethod` - 管理服务器发送的传入消息的定义和处理程序。 + - `IncomingRpcMethods` - 所有原版传入 RPC 处理程序。 + - `JsonRPCErrors` - 一个枚举,包含尝试读取、解析或执行方法时可能抛出的错误。 + - `JsonRpcLogger` - 一个用于 Minecraft 服务器执行的操作(无论是通信还是逻辑)的通用日志记录器。 + - `JsonRpcNotificationService` - 一个向所有连接的 Minecraft 服务器广播的通知服务。 + - `JsonRPCUtils` - 用于请求对象和错误的实用工具。 + - `ManagementServer` - 一个用于 Minecraft 服务器与管理服务器通信的基本 websocket 通信。 + - `OutgoingRpcMethod` - 对管理服务器的传出请求的定义和处理程序。 + - `OutgoingRpcMethods` - 所有原版传出 RPC 处理程序。 + - `PendingRpcRequest` - Minecraft 服务器向管理服务器发出的待处理请求。 +- `net.minecraft.server.jsonrpc.api` + - `MethodInfo` - 定义由管理服务器处理的方法,无论是入站还是出站。 + - `ParamInfo` - 定义特定方法接受的参数。 + - `PlayerDto` - 表示玩家的数据传输对象。 + - `ReferenceUtil` - 用于创建本地引用 URI 的实用工具。 + - `ResultInfo` - 定义特定方法返回的响应。 + - `Schema` - 用于发现服务、参数和结果的 JSON 模式的实现。 + - `SchemaComponent` - 模式中某个组件的引用定义。 +- `net.minecraft.server.jsonrpc.dataprovider.JsonRpcApiSchema` - 一个从发现服务生成 JSON 模式的数据提供者。 +- `net.minecraft.server.jsonrpc.internalapi` + - `GameRules` - 一个获取当前游戏规则值的 API。 + - `MinecraftAllowListService` - 一个处理来自管理服务器的关于允许列表的通信的 Minecraft 中间层。 + - `MinecraftAllowListServiceImpl` - 允许列表实现。 + - `MinecraftApi` - 一个处理所有与管理服务器通信的服务的 Minecraft API。 + - `MinecraftBanListService` - 一个处理来自管理服务器的关于禁令列表的通信的 Minecraft 中间层。 + - `MinecraftBanListServiceImpl` - 禁令列表实现。 + - `MinecraftExecutorService` - 一个提交任意 runnable 或 supplier 以在服务器上执行的 Minecraft 中间层。 + - `MinecraftExecutorServiceImpl` - 执行器实现。 + - `MinecraftGameRuleService` - 一个处理来自管理服务器的关于游戏规则的通信的 Minecraft 中间层。 + - `MinecraftExecutorServiceImpl` - 执行器实现。 + - `MinecraftOperatorListService` - 一个处理来自管理服务器的关于操作员命令的通信的 Minecraft 中间层。 + - `MinecraftOperatorListServiceImpl` - 操作员列表实现。 + - `MinecraftPlayerListService` - 一个处理来自管理服务器的关于玩家列表的通信的 Minecraft 中间层。 + - `MinecraftPlayerListServiceImpl` - 玩家列表实现。 + - `MinecraftServerSettingsService` - 一个处理来自管理服务器的关于服务器设置的通信的 Minecraft 中间层。 + - `MinecraftServerSettingsServiceImpl` - 服务器设置实现。 + - `MinecraftServerStateService` - 一个处理来自管理服务器的关于当前服务器状态和发送消息的通信的 Minecraft 中间层。 + - `MinecraftServerStateServiceImpl` - 服务器状态实现。 +- `net.minecraft.server.jsonrpc.methods` + - `AllowlistService` - 一个处理来自管理服务器的关于允许列表的通信的服务。 + - `BanlistService` - 一个处理来自管理服务器的关于玩家禁令列表的通信的服务。 + - `ClientInfo` - 当前 Minecraft 服务器连接到管理服务器的标识符。 + - `DiscoveryService` - 一个显示管理服务器支持的所有服务的模式的服务。 + - `EncodeJsonRpcException` - 尝试编码 JSON 数据包时抛出的异常。 + - `GameRulesService` - 一个处理来自管理服务器的关于游戏规则的通信的服务。 + - `IllegalMethodDefinitionException` - 当提供的方法定义非法时抛出的异常。 + - `InvalidParameterJsonRpcException` - 当方法的参数无效时抛出的异常。 + - `InvalidRequestJsonRpcException` - 当请求无效时抛出的异常。 + - `IpBanlistService` - 一个处理来自管理服务器的关于 IP 禁令列表的通信的服务。 + - `Message` - 表示字面量或可翻译组件的数据传输对象。 + - `MethodNotFoundJsonRpcException` - 当发生 404 错误时抛出的异常,表示方法不存在。 + - `OperatorService` - 一个处理来自管理服务器的关于操作员命令的通信的服务。 + - `PlayerService` - 一个处理来自管理服务器的关于玩家列表的通信的服务。 + - `RemoteRpcErrorException` - 当管理服务器上出现问题时抛出的异常。 + - `ServerSettingsService` - 一个处理来自管理服务器的关于服务器设置的通信的服务。 + - `ServerStateService` - 一个处理来自管理服务器的关于当前服务器状态和发送消息的通信的服务。 +- `net.minecraft.server.jsonrpc.security` + - `AuthenticationHandler` - 处理请求中认证令牌持有者的验证。 + - `JsonRpcSslContextProvider` - 为 TLS 通信提供密钥库上下文。 + - `SecurityConfig` - 处理密钥,从检查基本有效性到生成新密钥。 +- `net.minecraft.server.jsonrpc.websocket` + - `JsonToWebSocketEncoder` - 一个用于 JSON 的消息到消息编码器。 + - `WebSocketToJsonCodec` - 一个用于 JSON 的消息到消息解码器。 +- `net.minecraft.server.notifications` + - `EmptyNotificationService` - 一个什么都不做的通知服务。 + - `NotificationManager` - 一个用于处理多个通知服务的管理器。 + - `NotificationService` - 一个定义由监听器(如管理服务器)采取的预期操作的服务。 +- `net.minecraft.server.players` + - `BanListEntry` + - `getReason` 现在可以为 `null` + - `getReasonMessage` - 返回禁令原因的可翻译组件。 + - `IpBanList` 现在接受 `NotificationService` + - `add`、`remove` - 处理列表中的条目。 + - `PlayerList` 现在接受 `NotificationService` 而不是最大玩家数 + - `maxPlayers` 已移除 + - `op` 现在有一个用于权限级别和绕过限制布尔值的重载 + - `setUsingWhiteList` -> `MinecraftServer#setUsingWhiteList` + - `getPlayer` - 通过名称获取玩家。 + - `ServerOpList` 现在接受 `NotificationService` + - `add`、`remove` - 处理列表中的条目。 + - `StoredUserEntry#getUser` 现在是公开的 + - `StoredUserList` 现在接受 `NotificationService` + - `add`、`remove` 现在返回一个布尔值表示是否成功 + - `remove(StoredUserEntry)` 已移除 + - `clear` - 清除所有存储的用户。 + - `UserBanList` 现在接受 `NotificationService` + - `add`、`remove` - 处理列表中的条目。 + - `UserWhiteList` 现在接受 `NotificationService` + - `add`、`remove` - 处理列表中的条目。 + +## 输入处理整合 + +输入处理以前传递原始值,每个值都在自己单独的参数中,由 GLFW 提供。然而,相当多的处理逻辑是冗余的,因为特定的键通常被检查,或者所有值都在从一个方法到下一个方法的烫手山芋游戏中传递。考虑到这一点,`GuiEventListener` 和其他调用中的输入处理程序现在通过事件对象处理。这些对象仍然包含 GLFW 传递的信息;然而,现在它们通过超类 `InputWithModifiers` 接口进行了许多常见检查。 + +有两种类型的事件:用于按键的 `KeyEvent` 和用于鼠标按下的 `MouseButtonEvent`。键、扫描码和修饰符被包装到 `KeyEvent` 中。按钮、修饰符和 XY 屏幕位置被包装到 `MouseButtonEvent` 中。因此,这通常是一个拖放替换,只需删除上述任何参数并用它们适当的事件替换。 + +### 按键映射类别 + +按键映射略有变化,不再接受原始字符串作为其类别,而是使用 `KeyMapping$Category` 记录,它本质上是一个命名空间的字符串。可以使用 `KeyMapping$Category#register` 创建类别;否则,每当映射作为比较器的一部分使用时,都会抛出错误。 + +```java +public static final KeyMapping.Category EXAMPLE = KeyMapping.Category.register(ResourceLocation.withNamespaceAndPath("examplemod", "example")); +``` + +### 双击扩展 + +`GuiEventListener#mouseClicked` 现在接受一个布尔值,表示该点击是否实际上是双击。 + +```java +// 对于某个 Screen 子类(或 AbstractWidget 或任何 GUI 对象渲染) + +@Override +public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + // ... + return false; +} +``` + +- `com.mojang.blaze3d.platform.InputConstants` + - `MOD_SHIFT`、`MOD_ALT`、`MOD_SUPER`、`MOD_CAPS_LOCK`、`MOD_NUM_LOCK` - 用于转换键输入的修饰键。 + - `KEY_LWIN`、`KEY_RWIN` -> `KEY_LSUPER`、`KEY_RSUPER` + - `getKey` 现在接受 `KeyEvent` 而不是键和扫描码 `int` + - `isKeyDown`、`setupKeyboardCallbacks`、`setupMouseCallbacks`、`grabOrReleaseMouse`、`updateRawMouseInput` 现在接受 `Window` 而不是 `long` 句柄 +- `net.minecraft.client` + - `KeyboardHandler` + - `keyPress` 现在是私有的 + - `setup` 现在接受 `Window` 而不是 `long` 句柄 + - `KeyMapping` 现在接受 `$Category` 而不是 `String` + - `shouldSetOnIngameFocus` - 返回键是否映射到键盘上的某个值。 + - `restoreToggleStatesOnScreenClosed` - 将切换键恢复到游戏内操作期间的状态。 + - `key` 现在是 protected + - `release` 现在是 protected + - `CATEGORY_*` -> `$Category#*` + - 可以通过 `$Category#id` 获取键 + - 组件可通过 `$Category#label` 获得 + - `getCategory` 现在返回 `$Category` 而不是 `String` + - `matches` 现在接受 `KeyEvent` 而不是键和扫描码 `int` + - `matchesMouse` 现在接受 `MouseButtonEvent` 而不是按钮 `int` + - `Minecraft#ON_OSX` -> `InputQuirks#ON_OSX` + - `MouseHandler#setup` 现在接受 `Window` 而不是 `long` 句柄 + - `ToggleKeyMapping` 现在有一个接受输入类型的重载 + - 构造函数现在接受 `KeyMapping$Category` 而不是 `String`,以及一个 `boolean` 表示是否应恢复按键绑定的先前状态 +- `net.minecraft.client.gui.components` + - `AbstractButton#onPress` 现在接受 `InputWithModifiers` + - `AbstractScrollArea#updateScrolling` 现在接受 `MouseButtonEvent` 而不是按钮信息和 XY 位置 + - `AbstractWidget` + - `onClick` 现在接受 `MouseButtonEvent` 而不是 XY 位置以及按钮是否被双击 + - `onRelease` 现在接受 `MouseButtonEvent` 而不是 XY 位置 + - `onDrag` 现在接受 `MouseButtonEvent` 而不是 XY 位置 + - `isValidClickButton` 现在接受 `MouseButtonInfo` 而不是按钮 `int` + - `CommandSuggestions` + - `keyPressed` 现在接受 `KeyEvent` 而不是键、扫描码、修饰符 `int` + - `mouseClicked` 现在接受 `MouseButtonEvent` 而不是按钮信息和 XY 位置 + - `$SuggestionsList` + - `mouseClicked` 不再接受按钮 `int` + - `keyPressed` 现在接受 `KeyEvent` 而不是键、扫描码、修饰符 `int` + - `MultilineTextField#keyPressed` 现在接受 `KeyEvent` 而不是键 `int` +- `net.minecraft.client.gui.components.events.GuiEventListener` + - `DOUBLE_CLICK_THRESHOLD_MS` -> `MouseHandler#DOUBLE_CLICK_THRESHOLD_MS` + - `mouseClicked` 现在接受 `MouseButtonEvent` 而不是按钮信息和 XY 位置,以及按钮是否被双击 + - `mouseReleased` 现在接受 `MouseButtonEvent` 而不是按钮信息和 XY 位置 + - `mouseDragged` 现在接受 `MouseButtonEvent` 而不是按钮信息和 XY 位置 + - `keyPressed` 现在接受 `KeyEvent` 而不是键、扫描码、修饰符 `int` + - `keyReleased` 现在接受 `KeyEvent` 而不是键、扫描码、修饰符 `int` + - `charTyped` 现在接受 `CharacterEvent` 而不是码点 `char` 和修饰符 `int` +- `net.minecraft.client.gui.font.TextFieldHelper` + - `charTyped` 现在接受 `CharacterEvent` 而不是码点 `char` + - `keyPressed` 现在接受 `KeyEvent` 而不是键 `int` +- `net.minecraft.client.gui.navigation.CommonInputs` 类已移除 + - `selected` -> `InputWithModifiers#isSelection` + - `confirm` -> `InputWithModifiers#isConfirmation` +- `net.minecraft.client.gui.screens.Screen` + - `hasControlDown` -> `Minecraft#hasControlDown`、`InputWithModifiers#hasControlDown` + - `hasShiftDown` -> `Minecraft#hasShiftDown`、`InputWithModifiers#hasShiftDown` + - `hasAltDown` -> `Minecraft#hasAltDown`、`InputWithModifiers#hasAltDown` + - `isCut` -> `InputWithModifiers#isCut` + - `isPaste` -> `InputWithModifiers#isPaste` + - `isCopy` -> `InputWithModifiers#isCopy` + - `isSelectAll` -> `InputWithModifiers#isSelectAll` +- `net.minecraft.client.gui.screens.inventory.AbstractContainerScreen` + - `hasClickedOutside` 不再接受按钮 `int` + - `checkHotbarKeyPressed` 现在接受 `KeyEvent` 而不是键和修饰符 `int` +- `net.minecraft.client.gui.screens.options.controls.KeyBindsList$CategoryEntry` 现在接受 `KeyMapping$Category` 而不是 `Component` +- `net.minecraft.client.gui.screens.recipebook` + - `hasClickedOutside` 不再接受按钮 `int` + - `RecipeBookPage#mouseClicked` 现在接受 `MouseButtonEvent` 而不是按钮信息和 XY 位置,以及按钮是否被双击 +- `net.minecraft.client.input` + - `CharacterEvent` - 某种输入交互生成一个码点,带有当前活动的修饰符。 + - `InputQuirks` - 处理输入时操作系统之间差异的实用工具。 + - `InputWithModifiers` - 定义一个带有当前活动修饰符的输入,以及一些常见的输入检查。 + - `KeyEvent` - 某种输入交互通过扫描码生成一个键,带有当前活动的修饰符。 + - `MouseButtonEvent` - 某种输入交互在 XY 位置生成一个按钮。 + - `MouseButtonInfo` - 某种输入交互生成一个带有当前活动修饰符的按钮。 + +## `Level#isClientSide` 现在为 private + +`Level#isClientSide` 字段现在是私有的,因此所有查询都必须使用方法版本: + +```java +// 对于某个 Level level +level.isClientSide(); +``` + +- `net.minecraft.world.level.Level#isClientSide` 字段现在是私有的 + +## 小幅迁移 + +以下是有用或有趣的增加、变更和移除的列表,它们不值得在入门文档中拥有自己的章节。 + +### 物品拥有者 + +Minecraft 进一步将物品模型中的物品持有者抽象为其自己的接口:`ItemOwner`。一个 `ItemOwner` 由三件事定义:持有者所在的 `level`、持有者的 `position`,以及持有者表示朝向的 Y 旋转(以度为单位)。`ItemOwner` 在每个 `Entity` 上实现;然而,它们的位置可以通过使用 `ItemOwner#offsetFromOwner` 或创建 `ItemOwner$OffsetFromOwner` 来偏移。 + +目前,只有新的架子方块使用偏移拥有者,以便指南针在放置时不会随机旋转。 + +- `net.minecraft.client.renderer.entity.ItemRenderer#renderStatic` 现在接受一个可选的 `ItemOwner` 而不是 `LivingEntity` +- `net.minecraft.client.renderer.item` + - `ItemModel#update` 现在接受 `ItemOwner` 而不是 `LivingEntity` + - `ItemModelResolver#updateForTopItem`、`appendItemLayers` 现在接受 `ItemOwner` 而不是 `LivingEntity` +- `net.minecraft.client.renderer.item.properties.numeric` + - `NeedleDirectionHelper#get`、`calculate` 现在接受 `ItemOwner` 而不是 `LivingEntity` + - `RangeSelectItemModelProperty#get` 现在接受 `ItemOwner` 而不是 `LivingEntity` + - `Time$TimeSource#get` 现在接受 `ItemOwner` 而不是 `Entity` +- `net.minecraft.world.entity` + - `Entity` 现在实现 `ItemOwner` + - `ItemOwner` - 表示正在渲染的物品的拥有者。 + +### 容器使用者 + +由于 Minecraft 的这个新版本引入了铜傀儡(一种可以与库存交互的原版生物),可以使用容器的实体的概念已被抽象为其自己的接口:`ContainerUser`。`ContainerUser` 预计将在某个 `LivingEntity` 子类上实现。由于实际的库存逻辑在其他地方处理,`ContainerUser` 只有两个方法:`hasContainerOpen`,检查活体实体当前是否正在打开某个容器(例如,桶、箱子);以及 `getContainerInteractionRange`,确定实体可以与某个容器交互的半径(以方块为单位)。 + +- `net.minecraft.world.Container` + - `startOpen`、`stopOpen` 现在接受 `ContainerUser` 而不是 `Player` + - `getEntitiesWithContainerOpen` - 返回正在与此容器交互的 `ContainerUser` 列表。 +- `net.minecraft.world.entity.ContainerUser` - 通常是可以与容器交互并打开容器的实体。 +- `net.minecraft.world.entity.player.Player` 现在实现 `ContainerUser` +- `net.minecraft.world.level.block.entity.EnderChestBlockEntity#startOpen`、`stopOpen` 现在接受 `ContainerUser` 而不是 `Player` + +### 名称和 ID + +除非需要 `GameProfile`,否则 Minecraft 现在为每个玩家传递一个 `NameAndId`。顾名思义,`NameAndId` 包含玩家的 UUID 和名称,因为对于涉及玩家实体的大多数逻辑来说,这通常就是全部所需。 + +- `net.minecraft.commands.arguments.GameProfileArgument` + - `getGameProfiles` 现在返回 `NameAndId` 的集合 + - `$Result#getNames` 现在返回 `NameAndId` 的集合 +- `net.minecraft.network.codec.ByteBufCodecs#PLAYER_NAME` - 玩家名称的 16 字节字符串流编解码器。 +- `net.minecraft.network.protocol.status.ServerStatus$Players#Sample` 现在是一个 `NameAndId` 列表,而不是 `GameProfile` +- `net.minecraft.server` + - `MinecraftServer` + - `getProfilePermissions` 现在接受 `NameAndId` 而不是 `GameProfile` + - `isSingleplayerOwner` 现在接受 `NameAndId` 而不是 `GameProfile` + - `ANONYMOUS_PLAYER_PROFILE` 现在是一个 `NameAndId` + - `Services` 现在接受 `UserNameToIdResolver` 而不是 `GameProfileCache`,以及一个 `ProfileResolver` +- `net.minecraft.server.players` + - `GameProfileCache` -> `UserNameToIdResolver`、`CachedUserNameToIdResolver`;不是一对一 + - `getAsync` 已移除 + - `add(GameProfile)` 已移除 + - `NameAndId` - 一个包含配置文件的 UUID 和名称的对象。 + - `PlayerList` + - `load` -> `loadPlayerData`,不是一对一 + - `canPlayerLogin` 现在接受 `NameAndId` 而不是 `GameProfile` + - `disconnectAllPlayersWithProfile` 现在接受 `UUID` 而不是 `GameProfile` + - `op`、`deop` 现在接受 `NameAndId` 而不是 `GameProfile` + - `isWhiteListed`、`isOp` 现在接受 `NameAndId` 而不是 `GameProfile` + - `canBypassPlayerLimit` 现在接受 `NameAndId` 而不是 `GameProfile` + - `ServerOpList` 现在接受 `NameAndId` 作为其第一个泛型 + - `canBypassPlayerLimit` 现在接受 `NameAndId` 而不是 `GameProfile` + - `ServerOpListEntry` 现在接受 `NameAndId` 作为其泛型和构造函数 + - `UserBanList` 现在接受 `NameAndId` 作为其第一个泛型 + - `isBanned` 现在接受 `NameAndId` 而不是 `GameProfile` + - `UserBanListEntry` 现在接受 `NameAndId` 作为其泛型和构造函数 + - `UserWhiteList` 现在接受 `NameAndId` 作为其第一个泛型 + - `isWhiteListed` 现在接受 `NameAndId` 而不是 `GameProfile` + - `UserWhiteListEntry` 现在接受 `NameAndId` 作为其泛型和构造函数 +- `net.minecraft.world.entity.player.Player#nameAndId` - 返回玩家的名称和 ID。 +- `net.minecraft.world.level.storage.PlayerDataStorage#load(Player, ProblemReporter)` -> `load(NameAndId)`,返回一个 `Optional` + +### 类型化实体数据 + +用于存储方块和实体数据的数据组件已从使用 `CustomData` 更改为类型安全的变体 `TypedEntityData`。`TypedEntityData` 与 `CustomData` 几乎相同,存储一个 `CompoundTag`,不同之处在于它由某个 `IdType` 泛型(如 `EntityType` 或 `BlockEntityType`)表示。`CustomData` 中与方块实体和实体相关的所有方法都已移至 `TypedEntityData`。 + +刷怪蛋使用 `TypedEntityData` 来存储它将生成的 `EntityType`。生成的逻辑仍然与 `SpawnEggItem` 子类绑定。 + +- `net.minecraft.core.component.DataComponents#ENTITY_DATA`、`BLOCK_ENTITY_DATA` 现在是带有 `EntityType` 和 `BlockEntityType` id 的 `TypeEntityData` +- `net.minecraft.world.entity.EntityType#updateCustomEntityTag` 现在接受 `TypedEntityData` 而不是 `CustomData` +- `net.minecraft.world.item.Item$Properties#spawnEgg` - 添加带有实体类型的 `ENTITY_DATA` 组件。 +- `net.minecraft.world.item.component` + - `CustomData` + - `CODEC_WITH_ID` 已移除 + - `COMPOUND_TAG_CODEC` - 用于扁平复合标签的编解码器。 + - `parseEntityId`、`parseEntityType` 已移除 + - `loadInto` -> `TypedEntityData#loadInto` + - `update`、`read`、`size` 已移除 + - `contains` -> `TypedEntityData#contains` + - `getUnsafe` -> `TypedEntityData#getUnsafe` + - `TypedEntityData` - 一个提供类型安全标识符的自定义数据。 +- `net.minecraft.world.level.Spawner#appendHoverText`、`getSpawnEntityDisplayName` 现在接受 `TypedEntityData` 而不是 `CustomData` +- `net.minecraft.world.level.block.entity.BeehiveBlockEntity$Occupant#entityData` 现在是一个 `TypedEntityData` + +### 重载监听器共享状态 + +`PreparableReloadListener` 现在可以选择通过使用 `PreparableReloadListener#prepareSharedState` 在其他重载监听器之间共享数据。`prepareSharedState` 在调用 `reload` 之前在主线程上运行,可以将任意值存储到某个 `$SharedKey`,基本上作为一个字典查找。然后,一旦调用 `reload`,就可以通过键获取并使用存储的数据。通常,使用的共享值是某种 `CompletableFuture` 并在内部连接。 + +```java +public class FirstExampleListener implements PreparableReloadListener { + + // 创建要写入共享数据的键 + public static final PreparableReloadListener.StateKey> EXAMPLE = new PreparableReloadListener.StateKey<>(); + + @Override + public void prepareSharedState(PreparableReloadListener.SharedState state) { + // 将数据添加到共享状态 + state.set(EXAMPLE, CompletableFuture.allOf()); + } + + @Override + public CompletableFuture reload(PreparableReloadListener.SharedState state, Executor tasksExecutor, PreparableReloadListener.PreparationBarrier barrier, Executor reloadExecutor) { + // 使用共享状态 + var example = state.get(EXAMPLE); + } +} + + +public class SecondExampleListener implements PreparableReloadListener { + + @Override + public CompletableFuture reload(PreparableReloadListener.SharedState state, Executor tasksExecutor, PreparableReloadListener.PreparationBarrier barrier, Executor reloadExecutor) { + // 在不同的监听器中使用共享状态 + var example = state.get(FirstExampleListener.EXAMPLE); + } +} +``` + +- `net.minecraft.server.packs.resources` + - `PreparableReloadListener` + - `reload` 现在接受 `PreparableReloadListener$SharedState` 而不是 `ResourceManager` + - `prepareSharedState` - 在重载监听器重新加载之前调用的方法,用于在重载监听器之间共享数据。 + - `$SharedState` - 一个通用字典,可以在重载监听器之间提供资源,尽管它们通常应采用 `CompletableFuture` 的形式。 + - `$StateKey` - 共享状态的一个键。 + - `SimpleReloadInstance$StateFactory#create` 现在接受 `PreparableReloadListener$SharedState` 而不是 `ResourceManager` + +### 加载票标志 + +`TicketType` 已更新为接受一个表示加载票属性位掩码的标志,而不是布尔值和枚举。 + +```java +// 这需要注册到 BuiltInRegistries#TICKET_TYPE +public static final TicketType EXAMPLE = new TicketType( + 20L, // 加载票超时并被移除前的刻数,如果加载票永不过时则设置为 0 + // 要设置的位标志,请参阅下面的新标志的替换和说明 + TicketType.FLAG_SIMULATION | TicketType.FLAG_LOADING +); +``` + +- `net.minecraft.server.level.TicketType` 现在接受一个标志位掩码,而不是布尔值和加载票用途 + - `FLAG_PERSIST`、`persist` 取代了布尔值 + - `FLAG_LOADING` 取代了 `$TicketUse#LOADING` + - `FLAG_SIMULATION` 取代了 `$TicketUse#SIMULATION` + - `FLAG_KEEP_DIMENSION_ACTIVE`、`shouldKeepDimensionActive` - 设置后,防止等级卸载。 + - `FLAG_CAN_EXPIRE_IF_UNLOADED`、`canExpireIfUnloaded` - 设置后,如果区块被卸载,加载票将过期。 + - `START` -> `PLAYER_SPAWN`、`SPAWN_SEARCH` + - `$TicketUse` 类已移除 +- `net.minecraft.world.level.TicketStorage` + - `shouldKeepDimensionActive` - 检查存储中是否有任何加载票设置了维度活动标志。 + - `removeTicketIf` 现在接受 `$TicketPredicate` 而不是 `BiPredicate` + - `$TicketPredicate` - 测试给定区块位置的加载票。 + +### 重生数据 + +重生逻辑已被整合到一个称为 `LevelData$RespawnData` 的单一对象中。重生数据保存全局位置(等级键和方块位置)以及实体的旋转。所有对原始角度和位置方法的调用已被 `Level#setRespawnData` 或 `getRespawnData` 取代,后者委托给 `MinecraftServer` 并最终委托给等级数据。 + +- `net.minecraft.client.multiplayer.ClientLevel#setDefaultSpawnPos` -> `Level#setRespawnData` +- `net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket` 现在是一个记录 + - 参数已被一个 `LevelData$RespawnData` 对象取代 +- `net.minecraft.server.MinecraftServer` + - `findRespawnDimension` - 获取玩家应重生的默认 `ServerLevel`。 + - `setRespawnData` - 设置默认重生位置。 + - `getRespawnData` - 获取默认重生位置。 +- `net.minecraft.server.level` + - `ServerLevel#setDefaultSpawnPos` -> `Level#setRespawnData` + - `ServerPlayer$RespawnConfig` 现在接受 `LevelData$RespawnData` 而不是维度、位置和角度 + - 字段仍然可以从重生数据访问 +- `net.minecraft.world.level.Level#getSharedSpawnPos`、`getSharedSpawnAngle` -> `getRespawnData`、`getWorldBorderAdjustedRespawnData`,不是一对一 +- `net.minecraft.world.level.storage` + - `LevelData` + - `getSpawnPos`、`getSpawnAngle` -> `getRespawnData`,不是一对一 + - `$RespawnData` - 定义默认重生玩家的全局位置、偏航角和俯仰角。 + - `WritableLevelData#setSpawn` 现在只接受 `LevelData$RespawnData` + +### “在架子上”变换 + +模型现在有一个新的变换,用于确定物品如何放置在架子上,称为 `on_shelf`。 + +- `net.minecraft.client.renderer.block.model.ItemTransforms#fixedFromBottom` - 一个期望物品放置在某个固定底部(如架子)上的变换。 +- `net.minecraft.world.item.ItemDisplayContext#ON_SHELF` - 当物品被放置在架子上时。 + +### 客户端资源拆分 + +`ClientAsset` 已被重写,以更好地指定资源引用的内容。目前,原版添加了 `ClientAsset$Texture` 来指定资源是一个纹理,它进一步子类化为用于从互联网下载的纹理的 `$DownloadedTexture`,以及用于从磁盘上某些现有资源访问的纹理的 `$ResourceTexture`。 + +- `net.minecraft.advancements.DisplayInfo` 现在接受一个可选的 `ClientAsset$ResourceTexture` 作为背景,而不是 `ClientAsset` +- `net.miencraft.client.renderer.texture.SkinTextureDownloader#downloadAndRegisterSkin` 现在返回 `ClientAsset$Texture` 的 future,而不是 `ResourceLocation` +- `net.minecraft.client.resources` + - `PlayerSkin` -> `net.minecraft.world.entity.player.PlayerSkin` + - 构造函数现在接受 `ClientAsset$Texture` 而不是 `ResourceLocation` 和 `String` + - `texture`、`textureUrl` -> `body` + - `capeTexture` -> `cape` + - `elytraTexture` -> `elytra` + - `insecure` - 构造一个 `insecure` 设置为 false 的 `PlayerSkin`。 + - `with` - 从其 `$Patch` 构建 `PlayerSkin`。 + - `$Patch` - 一个打包对象,表示在其他文件或通过网络发送时的皮肤。 + - `SkinManager$TextureCache#getOrLoad` 现在返回 `ClientAsset$Texture` 的 future,而不是 `ResourceLocation` +- `net.minecraft.core.ClientAsset` 现在是一个接口而不是记录 + - 原始实现移至 `$ResourceTexture` + - `id` 仍然作为一个方法存在 + - `texturePath` -> `$Texture#texturePath` + - `CODEC`、`DEFAULT_FIELD_CODEC`、`STREAM_CODEC` -> `$ResourceTexture#*` + - `$DownloadedTexture` - 从互联网下载的纹理。 + - `$Texture` - 定义一个纹理的客户端资源。 +- `net.minecraft.world.entity.animal.CatVariant#assetInfo` 现在接受 `ClientAsset$ResourceTexture` 而不是 `ClientAsset` +- `net.minecraft.world.entity.animal.frog.FrogVariant#assetInfo` 现在接受 `ClientAsset$ResourceTexture` 而不是 `ClientAsset` +- `net.minecraft.world.entity.animal.wolf.WolfVariant$AssetInfo#*` 现在接受 `ClientAsset$ResourceTexture` 而不是 `ClientAsset` +- `net.minecraft.world.entity.variant.ModelAndTexture#asset` 现在接受 `ClientAsset$ResourceTexture` 而不是 `ClientAsset` + +### 光标类型 + +屏幕上的当前光标现在可以通过 GUI 中的 `GuiGraphics#requestCursor` 或任何其他地方的 `Window#selectCursor` 更改为本机 `CursorType`,通过 `GLFW#glfwCreateStandardCursor`。注意 `Window#setAllowCursorChanges` 必须为 true,这可以通过选项菜单设置。 + +- `com.mojang.blaze3d.platform.Window` + - `setAllowCursorChanges` - 设置光标是否可以更改为不同的标准形状。 + - `selectCursor` - 将当前光标设置为指定类型。 +- `com.mojang.blaze3d.platform.cursor` + - `CursorType` - GLFW 标准光标形状的定义。 + - `createStandardCursor` - 从其形状、名称和后备创建标准光标。 + - `CursorTypes` - Blaze3d 的光标类型。 +- `net.minecraft.client.Options#allowCursorChanges` - 设置光标是否可以更改为不同的标准形状。 +- `net.minecraft.client.gui.GuiGraphics` + - `requestCursor` - 请求光标提交以进行渲染。 + - `applyCursor` - 应用光标更改以进行渲染。 + +### 新标签 + +- `minecraft:block` + - `wooden_shelves` + - `copper_chests` + - `lightning_rods` + - `copper` + - `copper_golem_statues` + - `incorrect_for_copper_tool` + - `chains` + - `lanterns` + - `bars` +- `minecraft:entity_types` + - `cannot_be_pushed_onto_boats` + - `accepts_iron_golem_gift` + - `candidate_for_iron_golem_gift` +- `minecraft:item` + - `wooden_shelves` + - `copper_chests` + - `lightning_rods` + - `copper` + - `copper_golem_statues` + - `copper_tool_materials` + - `repairs_copper_armor` + - `chains` + - `lanterns` + - `bars` + - `shearable_from_copper_golem` + +### 新增列表 + +- `com.mojang.blaze3d.GraphicsWorkarounds#isGlOnDx12` - 返回渲染器是否使用 DirectX 12。 +- `com.mojang.blaze3d.opengl.GlStateManager#incrementTrackedBuffers` - 增加游戏使用的缓冲区数量。 +- `com.mojang.blaze3d.systems.TimerQuery#isRecording` - 返回是否有当前活动的查询。 +- `net.minecraft.SharedConstants#RESOURCE_PACK_FORMAT_MINOR`、`DATA_PACK_FORMAT_MINOR` - 资源包版本的次要组件。 +- `net.minecraft.advancements.critereon.MinMaxBounds` + - `bounds` - 返回值的边界。 + - `$FloatDegrees` - 表示某个角度的浮点数的边界。 +- `net.minecraft.client` + - `Minecraft` + - `isOfflineDevelopedMode` - 返回游戏是否处于离线开发者模式。 + - `canSwitchGameMode` - 玩家实体是否存在并具有游戏模式。 + - `playerSkinRenderCache` - 返回持有玩家渲染纹理的缓存。 + - `canInterruptScreen` - 当前屏幕是否可以被另一个屏幕关闭。 + - `packetProcessor` - 返回调度和处理数据包的处理器。 + - `Options` + - `invertMouseX` - 当为 true 时,反转 X 轴鼠标移动。 + - `toggleAttack` - 当为 true 时,将攻击键设置为可切换。 + - `toggleUse` - 当为 true 时,将使用键设置为可切换。 + - `sprintWindow` - 双击前进键激活疾跑的时间窗口(以刻为单位)。 + - `saveChatDrafts` - 返回如果输入框关闭,正在聊天中键入的消息是否应保留。 + - `keySpectatorHotbar` - 调出旁观者快捷栏菜单的键。 + - `ToggleKeyMapping#shouldRestoreStateOnScreenClosed` - 屏幕关闭后是否应恢复先前的切换状态。 +- `net.minecraft.client.animation.definitions.CopperGolemAnimation` - 铜傀儡的动画。 +- `net.minecraft.client.data.models.BlockModelGenerators` + - `and` - 将给定的条件进行 AND 运算。 + - `createShelf` - 从基础纹理和粒子纹理创建一个架子方块状态。 + - `addShelfPart` - 为架子的所有状态添加一个模型的变体。 + - `forEachHorizontalDirection` - 对一对方向和旋转执行操作。 + - `shelfCondition` - 基于架子的状态构造一个条件。 + - `createCopperGolemStatues` - 创建所有铜傀儡雕像。 + - `createCopperGolemStatue` - 为提供的方块、铜块和锈蚀状态创建一个铜傀儡雕像。 + - `createCopperChests` - 创建所有铜箱子。 + - `createCopperLantern` - 创建一个灯笼和悬挂式灯笼。 + - `createCopperChain` - 创建一个链。 + - `createCopperChainItem` - 创建一个链物品模型。 +- `net.minecraft.client.data.models.model` + - `ModelTemplate` + - `SHELF_*` - 用于将架子构建为多部分的模型模板。 + - `LIGHTNING_ROD` - 避雷针的模型模板。 + - `CHAIN` - 链的模型模板。 + - `BARS_*` - 用于构建类似栏杆方块的模型模板。 + - `TexturedModel#CHAIN` - 链的模型提供者。 + - `TextureMapping#bars` - 类似栏杆方块的纹理映射。 + - `TextureSlot#BARS` - 对 `#bars` 纹理的引用。 +- `net.minecraft.client.gui` + - `Gui#renderDeferredSubtitles` - 在屏幕上渲染字幕。 + - `GuiGraphics#getSprite` - 从其材质获取 `TextureAtlasSprite`。 +- `net.minecraft.client.gui.components` + - `AbstractScrollArea#isOverScrollbar` - 返回当前光标位置是否在滚动条上。 + - `AbstractSelectionList` + - `sort` - 对列表中的条目进行排序。 + - `swap` - 交换列表中两个条目的位置 + - `clearEntriesExcept` - 移除所有与提供的元素不匹配的条目。 + - `getNextY` - 返回渲染滚动组件的 Y 分量。 + - `entriesCanBeSelected` - 列表中的条目是否可以被选择。 + - `scrollToEntry` - 滚动条,使选中的条目显示在屏幕上。 + - `removeEntries` - 移除提供的列表中的所有条目。 + - `$Entry` + - `set*` - 设置组件大小。 + - `getContent*` - 获取内容大小和位置。 + - `ChatComponent` + - `saveAsDraft`、`discardDraft` - 处理聊天框中的草稿消息。 + - `createScreen`、`openScreen`、`preserveCurrentChatScreen`、`restoreChatScreen` - 根据草稿聊天选项处理屏幕行为。 + - `$ChatMethod` - 定义聊天的消息类型。 + - `$Draft` - 定义聊天框中的草稿消息。 + - `EditBox` + - `DEFAULT_HINT_STYLE` - 默认提示的样式。 + - `SEARCH_HINT_STYLE` - 可搜索提示的样式,例如配方书中的提示。 + - `$TextFormatter` - 用于格式化框中字符串的接口。 + - `FittingMultiLineTextWidget#minimizeHeight` - 将小部件的高度设置为内部高度和内边距。 + - `FocusableTextWidget$BackgroundFill` - 一个枚举,表示何时应填充背景。 + - `ItemDisplayWidget#renderTooltip` - 提交要渲染的工具提示。 + - `MultiLineLabel$Align` - 处理标签的对齐方式。 + - `MultilineTextField#selectWordAtCursor` - 选择光标当前所在的单词。 + - `SpriteIconButton$Builder#withTooltip` - 设置显示消息的工具提示。 + - `StringWidget` + - `setMaxWidth` - 设置字符串的最大宽度以及如何处理多余的宽度。 + - `$TextOverflow` - 如果文本长于最大宽度,则采取的操作。 +- `net.minecraft.client.gui.components.events.GuiEventListener#shouldTakeFocusAfterInteraction` - 当为 true 时,在点击时将元素设置为焦点。 +- `net.minecraft.client.gui.screens` + - `ChatScreen` + - `isDraft` - 框中是否有未发送的消息。 + - `exitReason` - 聊天屏幕被关闭的原因。 + - `shouldDiscardDraft` - 是否应丢弃当前的草稿消息。 + - `$ChatConstructor` - 基于草稿状态的聊天屏幕的构造函数。 + - `$ExitReason` - 聊天屏幕被关闭的原因。 + - `FaviconTexture#isClosed` - 返回纹理是否已关闭。 + - `LevelLoadingScreen` + - `update` - 更新当前加载跟踪器和原因。 + - `$Reason` - 更改等级加载屏幕的原因。 + - `Overlay#tick` - Tick覆盖层。 + - `Screen` + - `panoramaShouldSpin` - 返回渲染的全景图是否应旋转其相机。 + - `setNarrationSuppressTime` - 设置旁白应被抑制到什么时间。 + - `isInGameUi` - 返回屏幕是否在游戏进行中(而不是暂停菜单)打开。 + - `isAllowedInPortal` - 此屏幕是否可以在传送门过渡效果期间打开。 + - `canInterruptWithAnotherScreen` - 此屏幕是否可以被另一个屏幕关闭,默认为“按 Esc 关闭”屏幕。 +- `net.minecraft.client.gui.screens.inventory.AbstractCommandBlockScreen#addExtraControls` - 向命令方块屏幕添加任何额外的小部件。 +- `net.minecraft.client.gui.screens.multiplayer` + - `CodeOfConductScreen` - 一个显示服务器的行为准则文本的屏幕。 + - `ServerSelectionList$Entry` + - `matches` - 返回提供的条目是否与此条目匹配。 + - `join` - 处理客户端如何加入服务器条目。 +- `net.minecraft.client.gui.screens.reporting.ChatSelectionScreen$ChatSelectionList#ITEM_HEIGHT` - 列表中每个条目的高度。 +- `net.minecraft.client.gui.screens.worldselection.WorldSelectionList` + - `returnToScreen` - 重新加载世界列表后返回上一个屏幕。 + - `$Builder` - 创建一个新的 `WorldSelectionList`。 + - `$Entry#getLevelSummary` - 返回所选世界的摘要。 + - `$EntryType` - 一个枚举,表示条目的世界类型。 + - `$NoWorldsEntry` - 一个没有世界可供选择的条目。 +- `net.minecraft.client.main.GameConfig$GameData#offlineDeveloperMode` - 游戏是否离线并用于开发。 +- `net.minecraft.client.multiplayer` + - `ClientCommonPacketListenerImpl` + - `seenPlayers` - 该玩家看到的所有玩家,无论他们当前是否在线。 + - `seenInsecureChatWarning` - 玩家是否已看到不安全聊天警告。 + - `$CommonDialogAccess` - 客户端网络使用的对话框访问。 + - `ClientConfigurationPacketListenerImpl#DISCONNECTED_MESSAGE` - 因不接受行为准则而断开连接时显示的消息。 + - `ClientExplosionTracker` - 跟踪客户端上发生的爆炸,专门用于处理粒子。 + - `ClientLevel` + - `endFlashState` - 处理末地中出现的闪光状态。 + - `trackExplosionEffects` - 跟踪爆炸以处理其粒子。 + - `ClientPacketListener` + - `getSeenPlayers` - 返回该玩家看到的所有玩家。 + - `getPlayerInfoIgnoreCase` - 获取提供的配置文件名称的玩家信息。 +- `net.minecraft.client.player.LocalPlayerResolver` - 本地玩家的配置文件解析器。 +- `net.minecraft.client.resources.WaypointStyle#ICON_LOCATION_PREFIX` - 路径点图标的前缀。 +- `net.minecraft.client.server.IntegratedServer#MAX_PLAYERS` - 本地托管服务器允许的最大玩家数。 +- `net.minecraft.client.sounds` + - `DirectionalSoundInstance` - 一个根据相机方向改变位置的声音。 + - `SoundEngineExecutor#startUp` - 创建运行引擎的线程。 + - `SoundPreviewHandler` - 一个用于预览某个事件在给定设置下听起来如何的实用工具。 +- `net.minecraft.core` + - `BlockPos#betweenCornersInDirection` - 一个可迭代对象,按向量提供的方向遍历提供的边界。 + - `Direction#axisStepOrder` - 返回应检查给定向量的方向列表。 +- `net.minecraft.core.particles.ExplosionParticleInfo` - 用作爆炸一部分的粒子。 +- `net.minecraft.data.loot.BlockLootSubProvider#createCopperGolemStatueBlock` - 铜傀儡雕像的战利品表。 +- `net.minecraft.data.loot.packs` + - `VanillaBlockInteractLoot` - 来自实体交互的原版方块战利品的子提供者。 + - `VanillaChargedCreeperExplosionLoot` - 来自闪电苦力怕爆炸的原版实体战利品的子提供者。 + - `VanillaEntityInteractLoot` - 来自实体交互的原版实体战利品的子提供者。 +- `net.minecraft.data.recipes.RecipeProvider#shelf` - 从一个物品构造一个架子配方。 +- `net.minecraft.data.worldgen.BiomeDefaultFeatures#addNearWaterVegetation` - 在水体附近添加植被。 +- `net.minecraft.gametest.framework.GameTestHelper#getTestDirection` - 获取测试面向的方向。 +- `net.minecraft.network` + - `FriendlyByteBuf#readLpVec3`、`writeLpVec3` - 处理同步压缩的 `Vec3`。 + - `LpVec3` - 一个 vec3 网络处理程序,将向量压缩和解压缩为最多两个字节和两个整数。 + - `PacketProcessor` - 用于调度和处理数据包的处理器。 +- `net.minecraft.network.chat.CommonComponents#GUI_COPY_TO_CLIPBOARD` - 将聊天复制到剪贴板时显示的消息。 +- `net.minecraft.network.protocol.configuration` + - `ClientboundCodeOfConductPacket` - 一个将服务器的行为准则发送给加入客户端的数据包。 + - `ClientConfigurationPacketListener#handleCodeOfConduct` - 处理从服务器发送的行为准则。 + - `ServerboundAcceptCodeOfConductPacket` - 一个发送客户端从服务器读取行为准则的接受响应的数据包。 + - `ServerConfigurationPacketListener#handleAcceptCodeOfConduct` - 处理来自客户端的行为准则接受。 +- `net.minecraft.network.syncher.EntityDataSerializers` + - `WEATHERING_COPPER_STATE` - 傀儡上铜的风化状态。 + - `COPPER_GOLEM_STATE` - 铜傀儡的逻辑状态。 + - `RESOLVABLE_PROFILE` - 实体的可解析配置文件。 +- `net.minecraft.server.MinecraftServer` + - `selectLevelLoadFocusPos` - 返回服务器的加载中心位置,通常是共享重生位置。 + - `getLevelLoadListener` - 返回用于跟踪等级加载的监听器。 + - `getCodeOfConducts` - 返回文件名到其行为准则文本的映射。 + - `enforceGameTypeForPlayers` - 设置所有玩家的游戏类型。 + - `packetProcessor` - 返回服务器的数据包处理器。 +- `net.minecraft.server.commands.FetchProfileCommand` - 获取给定用户的配置文件。 +- `net.minecraft.server.dedicated.DedicatedServerProperties#codeOfConduct` - 服务器是否有行为准则。 +- `net.minecraft.server.level` + - `ChunkLoadCounter` - 在等级加载或玩家生成时跟踪区块加载。 + - `ChunkMap` + - `getLatestStatus` - 返回给定区块位置的最新状态。 + - `isTrackedByAnyPlayer` - 检查实体是否被任何玩家跟踪。 + - `forEachEntityTrackedBy` - 遍历给定玩家跟踪的每个实体。 + - `forEachReadyToSendChunk` - 遍历每个准备好发送给客户端的区块。 + - `ServerChunkCache` + - `hasActiveTickets` - 检查当前等级是否有任何保持其加载的活动加载票。 + - `addTicketAndLoadWithRadius` - 向某个位置添加加载票,并加载该位置及其半径内的区块。 + - `ServerEntity$Synchronizer` - 处理向跟踪实体发送数据包。 + - `ServerLevel#isSpawningMonsters` - 返回服务器是否可以生成怪物。 + - `ServerPlayer$SavedPosition` - 保存玩家在磁盘上的当前位置。 +- `net.minecraft.server.level.progress.ChunkLoadStatusView` - 加载区块的状态视图。 +- `net.minecraft.server.network.ConfigurationTask#tick` - 每刻调用该任务,直到返回 true,然后完成任务。 +- `net.minecraft.server.network.config` + - `PrepareSpawnTask` - 一个查找玩家生成点的配置任务。 + - `ServerCodeOfConductConfigurationTask` - 一个将行为准则发送给客户端的配置任务。 +- `net.minecraft.server.packs.OverlayMetadataSection` + - `codecForPackType` - 根据资源包类型为部分构造一个编解码器。 + - `forPackType` - 从资源包类型获取元数据部分类型。 +- `net.minecraft.server.packs.metadata.MetadataSectionType#withValue`、`$WithValue` - 持有元数据部分及其提供的值。 +- `net.minecraft.server.packs.metadata.pack` + - `PackFormat` - 表示资源包的主要和次要修订版本。 + - `PackMetadataSection#forPackType` - 从资源包类型获取元数据部分类型。 +- `net.minecraft.server.packs.repository.PackCompatibility#UNKNOWN` - 资源包与游戏的兼容性未知,因为主要版本设置为最大整数值。 +- `net.minecraft.server.packs.resources.ResourceMetadata#getTypedSection`、`getTypedSections` - 获取提供的类型的元数据部分。 +- `net.minecraft.server.players.ProfileResolver` - 从其名称或 UUID 解析游戏配置文件。 +- `net.minecraft.util` + - `ExtraCodecs` + - `gameProfileCodec` - 给定 UUID 编解码器,为 `GameProfile` 创建一个编解码器。 + - `$LateBoundIdMapper#values` - 返回映射器内的值集合。 + - `InclusiveRange#map` - 将一个泛型的范围映射到另一个泛型。 + - `Mth#ceilLong` - 返回向上取整到最近长整型的双精度值。 +- `net.minecraft.util.random` + - `Weighted#streamCodec` - 为加权条目构造一个流编解码器。 + - `WeightedList#streamCodec` - 为加权列表构造一个流编解码器。 +- `net.minecraft.util.thread.BlockableEventLoop#shouldRunAllTasks` - 返回是否有任何阻塞的任务。 +- `net.minecraft.world.Nameable#getPlainTextName` - 返回名称组件的字符串。 +- `net.minecraft.world.entity` + - `Avatar` - 一个构成玩家基础的实体。 + - `EntityReference` + - `getEntity` - 根据类型类和等级返回实体。 + - 静态 `getEntity`、`getLivingEntity`、`getPlayer` - 根据其 `EntityReference` 和等级返回实体。 + - `EntityType` + - `MANNEQUIN` - 人体模型实体类型。 + - `STREAM_CODEC` - 实体类型的流编解码器。 + - `$Builder#notInPeaceful` - 设置实体不在和平模式下生成。 + - `Entity` + - `canInteractWithLevel` - 实体是否可以与当前等级交互。 + - `isInShallowWater` - 返回玩家是否在水中但未在水下。 + - `getAvailableSpaceBelow` - 返回实体与碰撞体之间在 Y 方向上的空间量,按给定值偏移。 + - `collectAllColliders` - 返回所有实体碰撞。 + - `InsideBlockEffectType#CLEAR_FREEZE` - 一个清除冰冻刻数的方块内效果。 + - `LivingEntity` + - `dropFromEntityInteractLootTable` - 从实体交互的战利品表中掉落战利品。 + - `shouldTakeDrowningDamage` - 实体是否应受到溺水伤害。 + - `Mob#WEARING_ARMOR_UPGRADE_MATERIAL_CHANCE`、`WEARING_ARMOR_UPGRADE_MATERIAL_ATTEMPTS` - 盔甲材料升级的常量。 + - `PositionMoveRotation#withRotation` - 使用提供的 XY 旋转创建一个新对象。 + - `Relative` + - `rotation` - 从 XY 布尔值获取相对旋转的集合。 + - `position` - 从 XYZ 布尔值获取相对位置的集合。 + - `direction` - 从 XYZ 布尔值获取相对增量的集合。 +- `net.minecraft.world.entity.ai.Brain#isBrainDead` - 返回大脑是否没有任何记忆、传感器或行为。 +- `net.minecraft.world.entity.ai.behavior.TransportItemsBetweenContainers` - 一个实体将在访问过的容器之间移动物品的行为。 +- `net.minecraft.world.entity.ai.memory.MemoryModuleType` + - `VISITED_BLOCK_POSITIONS` - 访问过的重要方块位置。 + - `TRANSPORT_ITEMS_COOLDOWN_TICKS` - 在运输物品之前等待多少刻。 + - `UNREACHABLE_TRANSPORT_BLOCK_POSITIONS` - 保存一个无法到达的位置列表。 +- `net.minecraft.world.entity.ai.navigation.GroundPathNavigation#setCanPathToTargetsBelowSurface` - 设置实体是否可以将起始位置下方非空气方块下方的其他实体作为目标。 +- `net.minecraft.world.entity.animal.coppergolem` + - `CopperGolem` - 铜傀儡实体。 + - `CopperGolemAi` - 铜傀儡的大脑逻辑。 + - `CopperGolemOxidationLevel` - 一个记录,保存给定氧化等级的源事件和纹理。 + - `CopperGolemOxidationLevels` - 所有原版氧化等级。 + - `CopperGolemState` - 铜傀儡所处的当前逻辑状态。 +- `net.minecraft.world.entity.decoration` + - `HangingEntity#canCoexist` - 是否有任何其他悬挂实体与此实体位于同一位置。默认情况下,确保实体不是相同的实体类型且不面向相同的方向。 + - `Mannequin` - 一个没有连接玩家的人体模型。 +- `net.minecraft.world.entity.player` + - `Player` + - `isMobilityRestricted` - 返回玩家是否有失明效果。 + - `handleShoulderEntities` - 处理玩家肩膀上的实体。 + - `extractParrotVariant`、`convertParrotVariant`、`*ShoulderParrot*` - 处理玩家肩膀上的鹦鹉。 + - `PlayerModelPart#CODEC` - 模型部件的编解码器。 +- `net.minecraft.world.entity.raid.Raids#getRaidCentersInChunk` - 返回给定区块中的袭击中心数量。 +- `net.minecraft.world.entity.vehicle.MinecartFurnace#addFuel` - 向熔炉添加燃料以推动实体。 +- `net.minecraft.world.item` + - `BucketItem#getContent` - 返回桶中持有的流体。 + - `Item$TooltipContext#isPeaceful` - 如果难度设置为和平,则返回 true。 + - `ToolMaterial#COPPER` - 铜工具材料。 + - `WeatheringCopperItems` - 一个记录,表示每个风化铜状态的物品。 +- `net.minecraft.world.item.equipment` + - `ArmorMaterials#COPPER` - 铜盔甲材料。 + - `EquipmentAssets#COPPER` - 铜装备资源的键引用。 + - `ResolvableProfile` + - `skinPatch` - 返回玩家皮肤引用。 + - `$Static` - 使用已解析的游戏配置文件。 + - `$Dynamic` - 在使用时动态解析游戏配置文件。 + - `$Partial` - 根据提供给组件的任何信息表示游戏配置文件的一部分。 +- `net.minecraft.world.level` + - `BaseCommandBlock$CloseableCommandBlockSource` - 通常用于命令方块的命令源。 + - `ChunkPos#contains` - 给定的方块位置是否在区块中。 + - `GameRules#RULE_SPAWNER_BLOCKS_ENABLED` - 刷怪笼方块是否应生成实体。 + - `Level` + - `getEntityInAnyDimension` - 通过 UUID 获取实体。 + - `getPlayerInAnyDimension` - 通过 UUID 获取玩家。 + - `hasEntities` - 返回提供的边界是否具有匹配类型和谓词的实体。 + - `palettedContainerFactory` - 返回用于创建调色板容器的工厂。 +- `net.minecraft.world.level.border.WorldBorder$Settings#toWorldBorder` - 从设置构造世界边界。 +- `net.minecraft.world.level.block` + - `Block#dropFromBlockInteractLootTable` - 与方块交互时掉落战利品。 + - `ChestBlock` + - `chestCanConnectTo` - 返回箱子是否可以与另一个方块合并。 + - `getConnectedBlockPos` - 获取箱子的连接方块位置。 + - `getOpenChestSound`、`getCloseChestSound` - 返回箱子打开/关闭时播放的声音。 + - `getChestType` - 根据周围的箱子返回箱子的类型。 + - `ChiseledBookShelfBlock#FACING`、`SLOT_*_OCCUPIED` - 雕纹书架 diff --git a/primers-doc/26.1-from-1.21.11.md b/primers-doc/26.1-from-1.21.11.md new file mode 100644 index 0000000..43e11b2 --- /dev/null +++ b/primers-doc/26.1-from-1.21.11.md @@ -0,0 +1,4957 @@ +# Minecraft 1.21.11 向 26.1 版本模组迁移指南 + +本文是关于如何将模组从1.21.11版本迁移至26.1版本的高层次概览(非详尽说明)。内容不针对特定模组加载器,仅涉及原版类的变更。 + +本指南采用[知识共享署名4.0国际许可协议](http://creativecommons.org/licenses/by/4.0/)授权,欢迎作为参考资料使用,使用时请保留原文链接以便其他读者查阅。 + +若发现任何错误或遗漏信息,请在本代码库提交issue,或在Neoforged Discord服务器中@ChampionAsh5357。 + +特别鸣谢: +- @Shnupbups 的语法修正 +- @cassiancc 提供的Java 25 IDE支持信息 +- @boq 关于输入法支持的信息 +- @lolothepro 发现的拼写错误 + +## 资源包变更 + +原版还存在许多面向用户的改动未在本文讨论,这些内容可能对模组开发者具有参考价值。具体变更清单可查看[Misode的版本更新日志](https://misode.github.io/versions/?id=26.1&tab=changelog)。 + +## Java 25 与反混淆 + +26.1 在通用流程中引入了两项新变化。 + +首先,Java 开发工具包已从 21 升级到 25。原版使用了这些新特性,例如 [JEP 447](https://openjdk.org/jeps/447),它允许在构造函数中在 `super` 之前使用语句。对于模组开发社区的用户,请确保相应更新,或利用您的 IDE 或构建工具功能。Microsoft 的 OpenJDK 可以在[此处](https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-25)找到。 + +您可能需要更新 IDE 以支持 Java 25。如果使用 Eclipse,您至少需要 2025-12 版本,或者使用 Java 25 支持市场插件的 2025-09 版本。如果使用 IntelliJ IDEA,您至少需要 2025.2 版本。 + +原版也已回归到反混淆状态,这意味着所有值类型现在都具有 Mojang 提供的官方名称。由于 Java 编译过程,仍然有一些内容未被捕获,例如内联原始类型和字符串常量,但大部分现已提供。这对于使用不同于官方映射的值类型映射集的用户或模组加载器来说将产生影响。 + +## 战利品类型展开 + +战利品池条目、物品函数、物品条件、NBT 提供者、数字提供者、分数提供者、整数提供者和浮点数提供者不再使用包装对象类型作为注册实例。现在,注册表直接接受用于序列化和反序列化过程的 `MapCodec`。因此,持有编解码器的 `*Type` 类或记录已被移除。此外,`getType` 现已重命名为 `codec`,接受注册的 `MapCodec`。 + +```java +// 以下是一个 LootItemFunction 的示例,但大致也适用于其他实例 + +public record NoopItemFunction() implements LootItemFunction { + public static final NoopItemFunction INSTANCE = new NoopItemFunction(); + // 用作注册表对象的映射编解码器 + public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); + + // 取代 getType + @Override + public MapCodec codec() { + // 返回注册表对象 + return MAP_CODEC; + } +} + +// 将映射编解码器注册到适当的注册表 +Registry.register(BuiltInRegistries.LOOT_FUNCTION_TYPE, Identifier.fromNamespaceAndPath("examplemod", "noop"), NoopItemFunction.MAP_CODEC); +``` + +- `net.minecraft.core.registries.BuiltInRegistries`、`Registries` + - `LOOT_POOL_ENTRY_TYPE` 现在持有 `LootPoolEntryContainer` 的映射编解码器,而不是 `LootPoolEntryType` + - `LOOT_FUNCTION_TYPE` 现在持有 `LootItemFunction` 的映射编解码器,而不是 `LootItemFunctionType` + - `LOOT_CONDITION_TYPE` 现在持有 `LootItemCondition` 的映射编解码器,而不是 `LootItemConditionType` + - `LOOT_NUMBER_PROVIDER_TYPE` 现在持有 `NumberProvider` 的映射编解码器,而不是 `LootNumberProviderType` + - `LOOT_NBT_PROVIDER_TYPE` 现在持有 `NbtProvider` 的映射编解码器,而不是 `LootNbtProviderType` + - `LOOT_SCORE_PROVIDER_TYPE` 现在持有 `ScoreboardNameProvider` 的映射编解码器,而不是 `LootScoreProviderType` + - `FLOAT_PROVIDER_TYPE` 现在持有 `FloatProvider` 的映射编解码器,而不是 `FloatProviderType` + - `INT_PROVIDER_TYPE` 现在持有 `IntProvider` 的映射编解码器,而不是 `IntProviderType` +- `net.minecraft.util.valueproviders` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `FloatProvider` 的子类型现在都是记录 + - `IntProvider` 的子类型,除了 `WeightedListInt`,现在都是记录 + - `FloatProvider` 现在从 `class` 变为 `interface` + - `CODEC` -> `FloatProviders#CODEC` + - `codec` -> `FloatProviders#codec` + - `getType` -> `codec`,不是一对一 + - `getMinValue` -> `min` + - `getMaxValue` -> `max` + - `FloatProviders` - 所有要注册的原版浮点数提供者。 + - `FloatProviderType` 接口已移除 + - 所有单例字段已被移除,请改用每个类中的映射编解码器 + - `codec` -> `FloatProvider#codec` + - `IntProvider` 现在从 `class` 变为 `interface` + - `CODEC` -> `IntProviders#CODEC` + - `NON_NEGATIVE_CODEC` -> `IntProviders#NON_NEGATIVE_CODEC` + - `POSITIVE_CODEC` -> `IntProviders#POSITIVE_CODEC` + - `codec` -> `IntProviders#codec` + - `validateCodec` -> `IntProviders#validateCodec` + - `getMinValue` -> `minInclusive` + - `getMaxValue` -> `maxInclusive` + - `getType` -> `codec`,不是一对一 + - `IntProviders` - 所有要注册的原版整数提供者。 + - `IntProviderType` 接口已移除 + - 所有单例字段已被移除,请改用每个类中的映射编解码器 + - `codec` -> `IntProvider#codec` +- `net.minecraft.world.level.storage.loot.entries` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `LootPoolEntries` 的所有单例字段已被移除 + - 应改用每个类中的映射编解码器 + - `bootstrap` - 注册战利品池条目。 + - `LootPoolEntryContainer#getType` -> `codec`,不是一对一 + - `LootPoolEntryType` 记录已移除 +- `net.minecraft.world.level.storage.loot.functions` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `LootItemFunction#getType` -> `codec`,不是一对一 + - `LootItemFunctions` 的所有单例字段已被移除 + - 应改用每个类中的映射编解码器 + - `bootstrap` - 注册战利品物品函数。 + - `LootItemFunctionType` 记录已移除 +- `net.minecraft.world.level.storage.loot.predicates` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `LootItemCondition#getType` -> `codec`,不是一对一 + - `LootItemConditions` 的所有单例字段已被移除 + - 应改用每个类中的映射编解码器 + - `bootstrap` - 注册战利品物品条件。 + - `LootItemConditionType` 记录已移除 +- `net.minecraft.world.level.storage.loot.providers.nbt` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `LootNbtProviderType` 记录已移除 + - `NbtProvider` 现在实现 `LootContextUser` + - `getType` -> `codec`,不是一对一 + - `NbtProviders` 的所有单例字段已被移除 + - 应改用每个类中的映射编解码器 + - `bootstrap` - 注册 NBT 提供者。 + - `LootItemConditionType` 记录已移除 +- `net.minecraft.world.level.storage.loot.providers.number` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `LootNumberProviderType` 记录已移除 + - `NumberProvider#getType` -> `codec`,不是一对一 + - `NumberProviders` 的所有单例字段已被移除 + - 应改用每个类中的映射编解码器 + - `bootstrap` - 注册数字提供者。 +- `net.minecraft.world.level.storage.loot.providers.score` 现在将 `CODEC` 字段重命名为 `MAP_CODEC` + - `LootScoreProviderType` 记录已移除 + - `ScoreProvider` 现在实现 `LootContextUser` + - `getType` -> `codec`,不是一对一 + - `ScoreProviders` 的所有单例字段已被移除 + - 应改用每个类中的映射编解码器 + - `bootstrap` - 注册分数提供者。 + +## 验证大修 + +用于收集然后报告数据生成内容问题的验证处理程序已被大修。这从来就不是字段验证的替代品。验证处理程序专门用于更方便地处理多个信息之间的验证,这些信息可能无法暴露给单个字段,例如战利品提供者是否可用于给定的上下文参数。 + +所有经过验证的对象都实现 `Validatable`,或者专门针对进度条件的 `CriterionTriggerInstance`。这两种方法都提供一个方法:`validate`,用于检查对象的有效性。`validate` 接受一个 `ValidationContext`,它本质上持有用于收集问题的 `ProblemReporter`、当前上下文参数以及一个引用解析器。`CriterionTriggerInstance` 提供一个 `ValidationContextSource`,但可以使用 `context` 方法之一将其转换为 `ValidationContext`,提供要检查的上下文参数。如果无法验证特定对象,则调用 `ValidationContext#reportProblem`,详细说明具体问题。 + +```java +// 对于某个实现 Validatable 的对象 + +@Override +public void validate(ValidationContext ctx) { + // 检查特定条件是否通过验证。 + if (this.foo() != this.bar()) { + // 如果没有,则报告存在问题。 + ctx.reportProblem(() -> "'Foo' 不等于 'bar'。"); + } +} +``` + +如果对象本身没有问题,而是特定字段有问题,那么报告器可以在检查各个元素时跟踪堆栈跟踪,使用 `ValidationContext#for*` 方法之一,执行类似操作。 + +```java +// 对于某个实现 Validatable 的对象 +// 假设它有一个子对象列表。 + +@Override +public void validate(ValidationContext ctx) { + for (int i = 0; i < this.children.size(); i++) { + // 获取列表中子项的特定上下文 + var childCtx = ctx.forIndexedField("children", i); + // 检查特定条件是否通过验证。 + if (this.foo() != this.bar()) { + // 如果没有,则报告存在问题。 + childCtx.reportProblem(() -> "'Foo' 不等于 'bar'。"); + } + } +} +``` + +`Validatable` 还提供了一些用于检查其他 `Validatable` 字段的静态实用程序。 + +```java +// 对于某个实现 Validatable 的对象 +// 假设某个子对象也实现了 Validatable + +@Override +public void validate(ValidationContext ctx) { + Validatable.validate(ctx, "child", this.child); +} +``` + +在所有必需的对象上实现 `Validatable` 后,可以根据使用位置(通常在反序列化之后或序列化之前)调用验证。这些的调用堆栈通常如下: + +```java +// 对于某个 Validatable validatable +// 假设我们可以访问 HolderGetter.Provider provider。 +// 如果不可用,参数本身是可选的。 + +// 创建问题收集器和验证上下文。 +// 上下文参数应仅包括正在提供的参数。 +ProblemReporter reporter = new ProblemCollector.Collector(); +ValidationContext ctx = new ValidationContext(reporter, LootContextParamSets.ALL_PARAMS, provider); + +// 调用验证器 +validatable.validate(ctx); +``` + +这也可以通过 `Codec#validate` 附加到编解码器的末尾: + +```java +public record ExampleObject() implements Validatable { + public static final Codec CODEC = MapCodec.unitCodec( + ExampleObject::new + ).validate( + // 提供验证器以及要检查的上下文参数。 + // 此方法无法访问注册表提供者。 + Validatable.validatorForContext(LootContextParamSets.ALL_PARAMS) + ); + + @Override + public void validate(ValidationContext ctx) { + // ... + } +} +``` + +- `net.minecraft.advancements.CriterionTriggerInstance#validate` 现在接受 `ValidationContextSource` 而不是 `CriterionValidator` +- `net.minecraft.advancements.criterion` + - `ContextAwarePredicate` 现在实现 `Validatable` + - `CriterionValidator` -> `ValidationContextSource` 和 `Validatable` + - `Validatable` 包含 `validate*` 方法 + - `ValidationContextSource` 持有上下文和报告器 +- `net.minecraft.world.item.enchantment` + - `ConditionalEffect` 现在实现 `Validatable` + - `conditionCodec` 被加载后调用 `validate` 取代 + - `TargetedConditionalEffect` 现在实现 `Validatable` +- `net.minecraft.world.level.storage.loot` + - `IntRange` 现在实现 `LootContextUser` + - `getReferencedContextParams` 被 `validate` 取代 + - `LootContext$VisitedEntry` 泛型现在必须扩展 `Validatable` + - `LootContextUser` 现在实现 `Validatable` + - `LootDataType` 泛型现在必须扩展 `Validatable` + - 构造函数现在接受 `$ContextGetter` 而不是 `$Validator` + - `runValidation` 现在接受 `ValidationContextSource` 而不是 `ValidationContext` + - 还有一个接受 `HolderLookup` 而不是键值对的重载 + - `createSimpleValidator`、`createLootTableValidator`、`$Validator` 被 `Validatable` 取代 + - `$ContextGetter` - 获取某个值的 `ContextKeySet`。 + - `LootPool` 现在实现 `Validatable` + - `LootTable` 现在实现 `Validatable` + - `Validatable` - 一个接口,处理其实例在给定上下文中的验证。 + - `ValidationContext` + - `forField` - 为给定字段创建上下文。 + - `forIndexedField` - 为列表中的给定条目创建上下文。 + - `forMapField` - 为映射中的给定键创建上下文。 + - `setContextKeySet` 已移除 + - `ValidationContextSource` - 进行验证的定义上下文的源。 +- `net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer` 现在实现 `Validatable` +- `net.minecraft.world.level.storage.loot.functions` + - `SetAttributesFunction$Modifier` 现在实现 `LootContextUser` + - `SetStewEffectFunction$EffectEntry` 现在实现 `LootContextUser` + +## 数据包村民交易 + +村民交易现已从其原始的基于映射的设置变为数据生成的注册表。虽然初步印象可能使该系统看起来更受限,但实际上它同样具有可扩展性,尽管方式相当复杂,因为交易本身就是一个战利品表,用于确定商人可以提供哪些 `MerchantOffer`。为了便于理解,本节将介绍交易重写的基础知识,以及如何将每个先前的物品列表转换为 `VillagerTrade`。 + +### 理解交易格式 + +所有交易都表示为 `VillagerTrade`,其核心是确定商人 `wants`(想要)什么以及它 `gives`(给予)什么作为回报。每个交易还可以提供对物品本身或其成本的修改器,或者根据给定条件决定是否可以达成特定交易。每个交易还指定可以进行的交易次数、给予多少经验值,或者与用户声誉相乘的价格倍数。然后通过 `getOffer` 将 `VillagerTrade` 转换为 `MerchantOffer`,接受 `LootContext`,通常带有上下文参数 `LootContextParamSets#VILLAGER_TRADE`,提供商人本身(`THIS_ENTITY`)及其位置(`ORIGIN`)。 + +交易本身位于 `data//villager_trade/`。通常,路径包含职业和交易等级,例如 `examplemod:example_profession/1/example_trade` + +```json5 +// 对于某个村民交易 'examplemod:example_profession/1/example_trade' +// JSON 位于 'data/examplemod/villager_trade/example_profession/1/example_trade.json' +{ + // 商人想要的堆栈。 + "wants": { + // 堆栈的物品。 + "id": "minecraft:apple", + // 一个数字提供者,用于确定想要多少该物品。一旦确定了数量, + // `gives` 堆栈上的任何额外成本(通过 `ADDITIONAL_TRADE_COST` 组件) + // 将在钳制到最大堆栈大小之前添加到数量中。 + // 如果未指定,默认为 1。 + "count": { + "type": "minecraft:uniform", + "min": 1, + "max": 5 + }, + // 堆栈应具有的任何组件。堆栈必须具有确切指定的组件。 + // 如果未指定,则不会检查任何组件,这意味着此项被忽略。 + "components": { + // 注册表键到组件值的映射。 + "minecraft:custom_name": "苹果...?" + } + }, + // 商人额外想要的一个堆栈。 + // 如果未指定,商人只检查 `wants`。 + "additional_wants": { + "id": "minecraft:emerald" + }, + // 商人给予的堆栈模板。 + "gives": { + // 堆栈的物品。 + "id": "minecraft:golden_apple", + // 一个数字 [1, 99]。 + // 如果未指定,默认为 1。 + "count": 1, + // 要应用于堆栈的组件。 + // 如果未指定,则仅应用默认的物品组件。 + "components": { + "minecraft:custom_name": "不是苹果" + } + }, + // 一个数字提供者,用于确定玩家在商人补货之前可以进行交易的次数。 + // 该值将至少为 1。 + // 如果未指定,默认为 4。 + "max_uses": { + "type": "minecraft:uniform", + "min": 1, + "max": 20 + }, + // 一个数字提供者,用于确定根据玩家与商人的声誉以及物品需求 + // 应用于物品成本的价格乘数,该乘数根据玩家进行交易的次数计算。 + // 这通常应该是一个小数,因为用户每个商人的最大声誉是 150, + // 尽管用户最多更可能拥有 25 的声誉。 + // 然而,即使声誉折扣乘以声誉表示 100% 或更多折扣, + // 交易也必须始终至少有一个物品。 + // 如果未指定,默认为 0。 + "reputation_discount": { + "type": "minecraft:uniform", + "min": 0, + "max": 0.05 + }, + // 一个数字提供者,用于确定玩家与商人进行交易获得的经验值。 + // 对于原版交易,这通常在 5-30 经验值左右。 + // 如果未指定,默认为 1。 + "xp": { + "type": "minecraft:uniform", + "min": 10, + "max": 20 + }, + // 一个战利品物品条件,确定商人是否可以向玩家提供此交易。 + // 如果未指定,默认为始终为 true。 + "merchant_predicate": { + // 此交易只能由来自沙漠或雪地村庄的村民执行。 + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "predicates": { + "minecraft:villager/variant": [ + "minecraft:desert", + "minecraft:snow" + ] + } + } + }, + // 一个战利品物品函数列表,用于修改从 `gives` 提供给玩家的物品。 + // 如果未指定,`gives` 不会被修改。 + "given_item_modifiers": [ + { + // 从提供的标签中选择一个随机附魔 + "function": "minecraft:enchant_randomly", + // 如果为 true,则交易成本增加所需的 `wants` 物品数量。 + "include_additional_cost_component": true, + "only_compatible": false, + "options": "#minecraft:trades/desert_common" + } + ], + // 可以是一个附魔 id,例如 "minecraft:protection", + // 或一个附魔 id 列表,例如 ["minecraft:protection", "minecraft:smite", ...], + // 或一个附魔标签,例如 "#minecraft:trades/desert_common"。 + // 当提供时,如果 `gives` 物品在修饰后包含此列表中的附魔, + // 则所需的 `wants` 物品数量乘以 2。 + // 如果未指定,则不执行任何操作。 + "double_trade_price_enchantments": "#minecraft:trades/desert_common" +} +``` + +### 商人的交易 + +每个商人都可以进行许多交易,通常从称为 `TradeSet` 的指定池中选择。交易集本身是一个单独的数据包注册表,它使用 `VillagerTrade`。每组交易决定可以提供多少交易以及是否可以多次选择相同的交易。提供的交易在 `AbstractVillager#addOffersFromTradeSet` 中计算,首先调用 `TradeSet#calculateNumberOfTrades` 获取报价数量,然后使用 `AbstractVillager#addOffersFromItemListings` 或 `AbstractVillager#addOffersFromItemListingsWithoutDuplicates` 来选择要使用的报价。 + +请注意,如果允许重复交易,则存在潜在的竞争条件,如果所有交易的 `merchant_predicate` 都失败,则报价将永远循环。这是因为该方法总是假设至少存在一个可以进行的交易。 + +交易集位于 `data//trade_set/`。通常,路径包含职业和交易等级,例如 `examplemod:example_profession/level_1.json` + +```json5 +// 对于某个交易集 'examplemod:example_profession/level_1' +// JSON 位于 'data/examplemod/villager_trade/trade_set/level_1.json' +{ + // 可以是一个村民交易 id,例如 "examplemod:example_profession/1/example_trade", + // 或一个交易 id 列表,例如 ["examplemod:example_profession/1/example_trade", "minecraft:farmer/1/wheat_emerald", ...], + // 或一个交易标签,例如 "#examplemod:example_profession/level_1"。 + // 这是商人可以提供的交易集合。 + // 这应该始终是一个村民交易标签,以便其他用户 + // 可以轻松地将自己的交易添加到商人。 + "trades": "#examplemod:example_profession/level_1", + // 一个数字提供者,用于确定商人可以提供的报价数量。 + "amount": { + "type": "minecraft:uniform", + "min": 1, + "max": 5 + }, + // 是否可以使用相同的交易进行多个报价。 + // 如果未指定,默认为 false。 + "allow_duplicates": true, + // 一个标识符,用于确定在确定报价时要使用的唯一随机实例。 + // 如果未指定,则使用等级随机。 + "random_sequence": "examplemod:example_profession/level_1" +} +``` + +村民交易标签可以是: + +```json5 +// 对于某个标签 'examplemod:example_profession/level_1' +// JSON 位于 'data/examplemod/tags/villager_trade/example_profession/level_1.json' +{ + "values": [ + "examplemod:example_profession/1/example_trade" + ] +} +``` + +这也意味着可以通过添加到关联的标签来轻松地将交易添加到现有的交易集中: + +```json5 +// 对于某个标签 'minecraft:farmer/level_1' +// JSON 位于 'data/minecraft/tags/villager_trade/farmer/level_1.json' +{ + "values": [ + "examplemod:example_profession/1/example_trade" + ] +} +``` + +同时,通过将等级 int 映射到 `tradeSetsByLevel` 中的交易集键来添加到新的 `VillagerProfession`: + +```java +public static final VillagerProfession EXAMPLE = Registry.register( + BuiltInRegistries.VILLAGER_PROFESSION, + Identifier.fromNamespaceAndPath("examplemod", "example_profession"), + new VillagerProfession( + Component.literal(""), + p -> true, + p -> true, + ImmutableSet.of(), + ImmutableSet.of(), + null, + // 职业等级到交易集键的映射 + Int2ObjectMap.ofEntries( + Int2ObjectMap.entry( + // 职业等级 + 1, + // 交易集 id + ResourceKey.create(Registries.TRADE_SET, Identifier.fromNamespaceAndPath("examplemod", "example_profession/level_1")) + ) + ) + ) +); +``` + +### 物品列表转换 + +考虑到所有这些,我们现在可以将物品列表转换为其新的数据生成村民交易。 + +#### 绿宝石 <-> 物品 + +对于以下交易: + +```java +public static final VillagerTrades.ItemListing ITEM_TO_EMERALD = new VillagerTrades.EmeraldForItems( + // 商人想要的物品。 + Items.WHEAT, + // 商人想要的物品数量。 + 20, + // 补货前可以进行的最大交易次数。 + 16, + // 交易给予的经验值。 + 2, + // 给予的绿宝石数量。 + 1 +); + +public static final VillagerTrades.ItemListing EMERALD_TO_ITEM = new VillagerTrades.ItemsForEmeralds( + // 商人将给予的物品。 + Items.BREAD, + // 商人想要的绿宝石数量。 + 1, + // 商人将给予的物品数量。 + 6, + // 补货前可以进行的最大交易次数。 + 16, + // 交易给予的经验值。 + 1, + // 应用于报价的价格乘数,考虑声誉和需求。 + 0.05f +); +``` + +它们的等价形式为: + +```json5 +// 对于某个村民交易 'examplemod:item_to_emerald' +// JSON 位于 'data/examplemod/villager_trade/item_to_emerald.json' +{ + "gives": { + // 给予的绿宝石数量。 + "count": 1, + "id": "minecraft:emerald" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 16, + // 应用于报价的价格乘数,考虑声誉和需求。 + // `EmeraldForItems` 将此硬编码为 0.05 + "reputation_discount": 0.05, + "wants": { + // 商人想要的物品数量。 + "count": 20, + // 商人想要的物品。 + "id": "minecraft:wheat" + }, + // 交易给予的经验值。 + "xp": 2 +} + + +// 对于某个村民交易 'examplemod:emerald_to_item' +// JSON 位于 'data/examplemod/villager_trade/emerald_to_item.json' +{ + "gives": { + // 商人将给予的物品数量。 + "count": 6, + // 商人将给予的物品。 + "id": "minecraft:bread" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 16, + // 应用于报价的价格乘数,考虑声誉和需求。 + "reputation_discount": 0.05, + "wants": { + "id": "minecraft:emerald", + // 商人想要的绿宝石数量。 + "count": 1 + }, + // 交易给予的经验值。 + "xp": 1 +} +``` + +#### 物品和绿宝石 -> 物品 + +对于以下交易: + +```java +public static final VillagerTrades.ItemListing ITEM_EMERALD_TO_ITEM = new VillagerTrades.ItemsAndEmeraldsToItems( + // 商人想要的物品。 + Items.COD, + // 商人想要的物品数量。 + 6, + // 商人额外想要的绿宝石数量。 + 1, + // 商人将给予的物品。 + Items.COOKED_COD, + // 商人将给予的物品数量。 + 6, + // 补货前可以进行的最大交易次数。 + 16, + // 交易给予的经验值。 + 1, + // 应用于报价的价格乘数,考虑声誉和需求。 + 0.05f +); +``` + +等价形式为: + +```json5 +// 对于某个村民交易 'examplemod:item_emerald_to_item' +// JSON 位于 'data/examplemod/villager_trade/item_emerald_to_item.json' +{ + // 商人额外想要的绿宝石。 + "additional_wants": { + "id": "minecraft:emerald", + }, + "gives": { + // 商人将给予的物品数量。 + "count": 6, + // 商人将给予的物品。 + "id": "minecraft:cooked_cod" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 16, + // 应用于报价的价格乘数,考虑声誉和需求。 + "reputation_discount": 0.05, + "wants": { + // 商人想要的物品数量。 + "count": 6, + // 商人想要的物品。 + "id": "minecraft:cod" + }, + // 交易给予的经验值。 + "xp": 1 +} +``` + +#### 绿宝石 -> 染色盔甲 + +对于以下交易: + +```java +public static final VillagerTrades.ItemListing EMERALD_TO_DYED_ARMOR = new VillagerTrades.DyedArmorForEmeralds( + // 商人将给予并染色的物品。 + Items.LEATHER_HELMET, + // 商人想要的绿宝石数量。 + 5, + // 补货前可以进行的最大交易次数。 + 12, + // 交易给予的经验值。 + 5 +); +``` + +等价形式为: + +```json5 +// 对于某个村民交易 'examplemod:emerald_to_dyed_armor' +// JSON 位于 'data/examplemod/villager_trade/emerald_to_dyed_armor.json' +{ + "given_item_modifiers": [ + { + // 在盔甲上设置随机染料。 + "function": "minecraft:set_random_dyes", + "number_of_dyes": { + "type": "minecraft:sum", + "summands": [ + 1.0, + { + "type": "minecraft:binomial", + "n": 2.0, + "p": 0.75 + } + ] + } + }, + { + // 检查染料是否成功应用于物品。 + "function": "minecraft:filtered", + "item_filter": { + "items": "minecraft:leather_helmet", + "predicates": { + "minecraft:dyed_color": {} + } + }, + // 如果失败,则丢弃报价。 + "on_fail": { + "function": "minecraft:discard" + } + } + ], + "gives": { + "count": 1, + // 商人将给予并染色的物品。 + "id": "minecraft:leather_helmet" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 12, + // 应用于报价的价格乘数,考虑声誉和需求。 + "reputation_discount": 0.05, + "wants": { + // 商人想要的绿宝石数量。 + "count": 5, + "id": "minecraft:emerald" + }, + // 交易给予的经验值。 + "xp": 5 +} +``` + +#### 绿宝石 -> 附魔物品 + +对于以下交易: + +```java +public static final VillagerTrades.ItemListing EMERALD_TO_ENCHANTED_BOOK = new VillagerTrades.EnchantBookForEmeralds( + // 交易给予的经验值。 + 30, + // 选择书籍上存储附魔时使用的最小等级。 + 3, + // 选择书籍上存储附魔时使用的最大等级。 + 3, + // 包含可供书籍选择的可用附魔列表的标签。 + EnchantmentTags.TRADES_DESERT_SPECIAL +); + +public static final VillagerTrades.ItemListing EMERALD_TO_ENCHANTED_ITEM = new VillagerTrades.EnchantedItemForEmeralds( + // 商人将给予并尝试附魔的物品。 + Items.FISHING_ROD, + // 商人想要的绿宝石基础数量。 + 3, + // 补货前可以进行的最大交易次数。 + 3, + // 交易给予的经验值。 + 10, + // 应用于报价的价格乘数,考虑声誉和需求。 + 0.2f +); +``` + +等价形式为: + +```json5 +// 对于某个村民交易 'examplemod:emerald_to_enchanted_book' +// JSON 位于 'data/examplemod/villager_trade/emerald_to_enchanted_book.json' +{ + // 商人期望一本书来写入附魔。 + "additional_wants": { + "id": "minecraft:book" + }, + // 当在双倍交易价格标签中时,绿宝石交易成本增加。 + "double_trade_price_enchantments": "#minecraft:double_trade_price", + "given_item_modifiers": [ + { + "function": "minecraft:enchant_with_levels", + "include_additional_cost_component": true, + "levels": { + "type": "minecraft:uniform", + // 选择书籍上存储附魔时使用的最小等级。 + "min": 3, + // 选择书籍上存储附魔时使用的最大等级。 + "max": 3 + }, + // 可供书籍选择的可用附魔列表。 + "options": [ + "minecraft:efficiency" + ] + }, + { + // 确保附魔已成功添加且具有给定等级。 + "function": "minecraft:filtered", + "item_filter": { + "items": "minecraft:enchanted_book", + "predicates": { + "minecraft:stored_enchantments": [ + { + "levels": { + // 选择书籍上存储附魔时使用的最小等级。 + "min": 3, + // 选择书籍上存储附魔时使用的最大等级。 + "max": 3 + } + } + ] + } + }, + // 失败时丢弃 + "on_fail": { + "function": "minecraft:discard" + } + } + ], + // 商人给予附魔书。 + "gives": { + "count": 1, + "id": "minecraft:enchanted_book" + }, + // 补货前可以进行的最大交易次数被硬编码为 12。 + "max_uses": 12, + // 应用于报价的价格乘数,考虑声誉和需求,硬编码为 0.2。 + "reputation_discount": 0.2, + "wants": { + "count": { + "type": "minecraft:sum", + "summands": [ + // 基于附魔最小和最大等级的硬编码计算。 + 11.0, + { + "type": "minecraft:uniform", + "max": 35.0, + "min": 0.0 + } + ] + }, + "id": "minecraft:emerald" + }, + // 交易给予的经验值。 + "xp": 30 +} + +// 对于某个村民交易 'examplemod:emerald_to_enchanted_item' +// JSON 位于 'data/examplemod/villager_trade/emerald_to_enchanted_item.json' +{ + "given_item_modifiers": [ + { + // 将附魔应用于给定的装备。 + "function": "minecraft:enchant_with_levels", + "include_additional_cost_component": true, + "levels": { + "type": "minecraft:uniform", + "max": 20, + "min": 5 + }, + "options": "#minecraft:on_traded_equipment" + }, + { + // 检查以确保附魔已应用。 + "function": "minecraft:filtered", + "item_filter": { + "items": "minecraft:fishing_rod", + "predicates": { + "minecraft:enchantments": [ + {} + ] + } + }, + // 失败时,不给予任何东西。 + "on_fail": { + "function": "minecraft:discard" + } + } + ], + "gives": { + "count": 1, + // 商人将给予并尝试附魔的物品。 + "id": "minecraft:fishing_rod" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 3, + // 应用于报价的价格乘数,考虑声誉和需求。 + "reputation_discount": 0.2, + "wants": { + "count": { + "type": "minecraft:sum", + "summands": [ + // 商人想要的绿宝石基础数量。 + 3, + { + // 基于附魔等级的变化。 + // 最初,这将是物品函数中使用的值,但由于它们现在被隔离, + // 这些值可能不同。 + "type": "minecraft:uniform", + "max": 20, + "min": 5 + } + ] + }, + "id": "minecraft:emerald" + }, + // 交易给予的经验值。 + "xp": 10 +} +``` + +#### 物品和绿宝石 -> 药水效果物品 + +对于以下交易: + +```java +public static final VillagerTrades.ItemListing EMERALD_TO_SUSPICIOUS_STEW = new VillagerTrades.SuspiciousStewForEmerald( + // 可疑炖菜应用的效果。 + MobEffects.NIGHT_VISION, + // 效果应激活的刻数。 + 100, + // 交易给予的经验值。 + 15 +); + +public static final VillagerTrades.ItemListing ITEM_EMERALD_TO_TIPPED_ARROW = new VillagerTrades.TippedArrowForItemsAndEmeralds( + // 商人额外想要的物品。 + Items.ARROW, + // 商人额外想要的物品数量。 + 5, + // 商人将给予的物品。 + Items.TIPPED_ARROW, + // 商人将给予的物品数量。 + 5, + // 商人想要的绿宝石数量。 + 2, + // 补货前可以进行的最大交易次数。 + 12, + // 交易给予的经验值。 + 30 +); +``` + +等价形式为: + +```json5 +// 对于某个村民交易 'examplemod:emerald_to_suspicious_stew' +// JSON 位于 'data/examplemod/villager_trade/emerald_to_suspicious_stew.json' +{ + "given_item_modifiers": [ + { + "effects": [ + { + // 可疑炖菜应用的效果。 + "type": "minecraft:night_vision", + // 效果应激活的刻数。 + "duration": 100 + } + // 原版将所有可疑炖菜报价合并为一个, + // 因为此函数随机选择一个炖菜效果。 + ], + "function": "minecraft:set_stew_effect" + } + ], + "gives": { + "count": 1, + "id": "minecraft:suspicious_stew" + }, + // 补货前可以进行的最大交易次数,硬编码为 12。 + "max_uses": 12, + // 应用于报价的价格乘数,考虑声誉和需求,硬编码为 0.05。 + "reputation_discount": 0.05, + "wants": { + "id": "minecraft:emerald" + }, + // 交易给予的经验值。 + "xp": 15 +} + +// 对于某个村民交易 'examplemod:item_emerald_to_tipped_arrow' +// JSON 位于 'data/examplemod/villager_trade/item_emerald_to_tipped_arrow.json' +{ + "additional_wants": { + // 商人额外想要的物品数量。 + "count": 5, + // 商人额外想要的物品。 + "id": "minecraft:arrow" + }, + "given_item_modifiers": [ + { + // 从可交易的药水中随机应用一种药水效果。 + // 原始实现只是随机选择任何药水。 + "function": "minecraft:set_random_potion", + "options": "#minecraft:tradeable" + } + ], + "gives": { + // 商人将给予的物品数量。 + "count": 5, + // 商人将给予的物品。 + "id": "minecraft:tipped_arrow" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 12, + // 应用于报价的价格乘数,考虑声誉和需求,硬编码为 0.05。 + "reputation_discount": 0.05, + "wants": { + // 商人想要的绿宝石数量。 + "count": 2, + "id": "minecraft:emerald" + }, + // 交易给予的经验值。 + "xp": 30 +} +``` + +#### 绿宝石 -> 宝藏地图 + +对于以下交易: + +```java +public static final VillagerTrades.ItemListing EMERALD_TO_TREASURE_MAP = new VillagerTrades.TreasureMapForEmeralds( + // 商人想要的绿宝石数量。 + 8, + // 一个包含要找到最近宝藏结构的列表的标签。 + StructureTags.ON_TAIGA_VILLAGE_MAPS, + // 地图名称的翻译键。 + "filled_map.village_taiga", + // 用于装饰地图上找到的宝藏位置的图标。 + MapDecorationTypes.TAIGA_VILLAGE, + // 补货前可以进行的最大交易次数。 + 12, + // 交易给予的经验值。 + 5 +); +``` + +等价形式为: + +```json5 +// 对于某个村民交易 'examplemod:emerald_to_treasure_map' +// JSON 位于 'data/examplemod/villager_trade/emerald_to_treasure_map.json' +{ + "additional_wants": { + // 商人额外想要的物品,硬编码为指南针。 + "id": "minecraft:compass" + }, + "given_item_modifiers": [ + { + // 找到要在地图上显示的宝藏结构。 + + // 用于装饰地图上找到的宝藏位置的图标。 + "decoration": "minecraft:village_taiga", + // 一个包含要找到最近宝藏结构的列表的标签。 + "destination": "minecraft:on_taiga_village_maps", + "function": "minecraft:exploration_map", + "search_radius": 100 + }, + { + // 设置地图的名称。 + + "function": "minecraft:set_name", + "name": { + // 地图名称的翻译键。 + "translate": "filled_map.village_taiga" + }, + "target": "item_name" + }, + { + // 检查以确保找到了结构。 + + "function": "minecraft:filtered", + "item_filter": { + "items": "minecraft:filled_map", + "predicates": { + "minecraft:map_id": {} + } + }, + "on_fail": { + "function": "minecraft:discard" + } + } + ], + "gives": { + "count": 1, + // 返回一张填充了宝藏位置的地图。 + "id": "minecraft:map" + }, + // 补货前可以进行的最大交易次数。 + "max_uses": 12, + // 应用于报价的价格乘数,考虑声誉和需求,硬编码为 0.05。 + "reputation_discount": 0.05, + "wants": { + // 商人想要的绿宝石数量。 + "count": 8, + "id": "minecraft:emerald" + }, + // 交易给予的经验值。 + "xp": 5 +} +``` + +#### 村民变体 + +一些商人会根据村民类型提供不同的选项,作为一个巨大的物品列表映射。现在,每种类型都有自己独立的村民交易,使用 `merchant_predicate` 来检查是否可以向特定村民类型提供报价: + +```json5 +// 对于某个村民交易 'examplemod:villager_type_item' +// JSON 位于 'data/examplemod/villager_trade/villager_type_item.json' +{ + // ... + "merchant_predicate": { + // 检查实体。 + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "predicates": { + // 村民类型必须是沙漠才能选择此交易。 + "minecraft:villager/variant": "minecraft:desert" + } + } + }, + // ... +} +``` + +- `net.minecraft.core.registries.Registries` + - `TRADE_SET` - 指向持有交易列表的注册表的键。 + - `VILLAGER_TRADE` - 指向持有单个交易的注册表的键。 +- `net.minecraft.tags.VillagerTradeTags` - 村民交易的标签。 +- `net.minecraft.world.entity.npc.villager` + - `AbstractVillager#addOffersFromItemListings` -> `addOffersFromTradeSet`,接受交易集的键而不是 `$ItemListing` 和报价数量;不是一对一 + - `VillagerProfession` 现在接受一个交易员等级到 `TradeSet` 的映射 + - `getTrades` - 返回给定等级的交易集键。 + - `VillagerTrades` 已被拆分为许多不同的类和实现 + - `TRADES`、`EXPERIMENTAL_TRADES`、`WANDERING_TRADER_TRADES` 现在由 `VillagerProfession#tradeSetsByLevel` 表示 + - 由于它们现在是数据包条目,因此存储在其各自的数据包中 + - `TRADES`、`EXPERIMENTAL_TRADES` 由 `data/minecraft/trade_set//*` 表示 + - `WANDERING_TRADER_TRADES` 由 `data/minecraft/trade_set/wandering_trader/*` 表示 + - `$DyedArmorForEmeralds` -> `VillagerTrades#dyedItem`、`addRandomDye`;不是一对一 + - `$EmeraldForItems` -> `VillagerTrade`,不是一对一 + - `$EmeraldsForVillagerTypeItem` -> `VillagerTrades#registerBoatTrades`,参见用法,不是一对一 + - `$EnchantBookForEmeralds` -> `VillagerTrades#enchantedBook`,不是一对一 + - `$EnchantedItemForEmeralds` -> `VillagerTrades#enchantedItem`,不是一对一 + - `$ItemListing` -> `VillagerTrade` + - `$ItemsAndEmeraldsToItems` -> 带有 `additionalWants` 的 `VillagerTrade` + - `$ItemsForEmeralds` -> `VillagerTrade`,不是一对一 + - `$SuspiciousStewForEmerald` -> 带有 `SetStewEffectFunction` 的 `VillagerTrade` + - `$TippedArrowForItemsAndEmeralds` -> 带有 `SetRandomPotionFunction` 的 `VillagerTrade` + - `$TreasureMapForEmeralds` -> `VillagerTrades$VillagerExplorerMapEntry`,参见用法,不是一对一 + - `$TypeSpecificTrade` -> `VillagerTrades#villagerTypeRestriction`、`villagerTypeHolderSet`;不是一对一 +- `net.minecraft.world.item.enchantment.providers.TradeRebalanceEnchantmentProviders` 接口已移除 + - 被 `TradeRebalanceVillagerTrades`、`TradeRebalanceRegistries` 取代 +- `net.minecraft.world.item.trading` + - `TradeCost` - 正在与某个商人交易的 `ItemStack`。 + - `TradeRebalanceVillagerTrades` - 属于交易平衡数据包的所有交易。 + - `TradeSet` - 一组可以由某个等级的交易员执行的交易。 + - `TradeSets` - 某个等级的交易员的所有原版交易集。 + - `VillagerTrade` - 商人和玩家之间的交易。 + - `VillagerTrades` - 所有原版交易。 +- `net.minecraft.world.level.storage.loot.parameters.LootContextParamSets#VILLAGER_TRADE` - 进行村民交易时的战利品上下文,包含交易来源和交易实体。 + +## `Level#random` 字段现在为 protected + +`Level#random` 字段现在是 `protected` 而不是 `public`。因此,应改用公共的 `getRandom` 方法。 + +```java +// 对于某个 Level level +RandomSource random = level.getRandom(); +``` + +- `net.minecraft.world.level.Level#random` 字段现在是 `protected` 而不是 `public` + - 改用 `getRandom` 方法 + +## 数据组件初始化器 + +数据组件已开始从原始对象转移到 `Holder` 本身。这是为了正确处理在构造期间不可用的对象,例如物品的数据包注册表对象。目前,这仅针对 `Item` 实现,但该系统允许任何注册表对象(给定其包装的持有者)存储某些定义的数据组件。 + +数据组件通过 `DataComponentInitializers` 附加到其持有者。在对象构造期间,调用 `DataComponentInitializers#add`,提供注册表对象的 `ResourceKey` 标识符以及一个 `DataComponentInitializers$Initializer`。初始化器接受三个参数:组件映射的构建器、完整的注册表 `HolderLookup$Provider` 以及传递给 `DataComponentInitializers#add` 的键。初始化器的功能类似于消费者,也允许通过 `$Initializer#andThen` 链接其他初始化器,或通过 `$Initializer#add` 轻松添加组件。 + +```java +// 对于某个自定义注册表对象 +public ExampleObject(ResourceKey id) { + // 注册数据组件初始化器 + BuiltInRegistries.DATA_COMPONENT_INITIALIZERS.add( + // 注册表对象的标识符 + id, + // 初始化器函数,接受组件构建器、注册表上下文和 id + (components, context, key) -> components + .set(DataComponents.MAX_DAMAGE, 1) + .set(DataComponents.DAMAGE_TYPE, context.getOrThrow(DamageTypes.SPEAR)) + ); +} +``` + +然后,每当调用 `ReloadableServerResources#loadResources`(在数据包重载时)时,数据组件就会被初始化或重新初始化。首先加载数据包对象,然后在 `Holder$Reference` 上设置组件。然后,可以通过 `Holder#components` 收集组件。 + +```java +// 对于某个 Holder EXAMPLE_HOLDER +DataComponentMap components = EXAMPLE_HOLDER.components(); +``` + +### 物品 + +由于组件现在在资源重载期间初始化,`Item$Properties` 提供了两种方法来将组件初始化延迟到此类数据组件加载完成之后:`delayedComponent` 和 `delayedHolderComponent`。`delayedComponent` 接受组件类型和一个接受 `HolderLookup$Provider` 并返回组件值的函数。`delayedHolderComponent` 委托给 `delayedComponent`,接受一个 `ResourceKey` 并将注册表对象设置为组件值。 + +```java +public static final Item EXAMPLE_ITEM = new Item( + new Item.Properties() + .delayedComponent( + // 应延迟初始化的组件类型。 + DataComponents.JUKEBOX_PLAYABLE, + // 一个接受注册表并返回组件值的函数。 + context -> new JukeboxPlayable(context.getOrThrow( + JukeboxSongs.THIRTEEN + )) + ) + .delayedHolderComponent( + // 具有持有者值类型的组件类型。 + DataComponents.DAMAGE_TYPE + // 关联持有者泛型类型的注册表对象的资源键。 + DamageTypes.SPEAR + ) + // ... +); +``` + +### 配方 + +由于数据组件现在持有在资源重载后延迟初始化的真实值,`Recipe#assemble` 不再接受 `HolderLookup$Provider`。相反,它假设配方已将所有必需的数据存储在传递到配方的堆栈或直接存储中。 + +- `net.minecraft.core` + - `Holder` + - `areComponentsBound` - 组件是否已绑定到持有者。 + - `components` - 存储在持有者上的对象组件。 + - `direct`、`$Direct` 现在可以接受 `DataComponentMap` + - `$Reference#bindComponents` - 将组件存储在持有者引用上。 + - `Registry#componentLookup` - 获取组件到持有者的查找。 + - `WritableRegistry#bindTag` -> `bindTags`,现在接受一个键到持有者列表的映射,而不是一个映射 +- `net.minecraft.core.component` + - `DataComponentInitializers` - 一个处理为组件附加对象初始化数据组件的类。 + - `DataComponentLookup` - 一个将组件类型映射到使用它的持有者的查找表。 + - `DataComponentMap$Builder#addValidator` - 为对象上的组件添加验证器。 + - `DataComponentPatch` + - `get` 现在接受 `DataComponentGetter` 并返回原始组件值 + - `$Builder#set` 现在有一个接受 `TypedDataComponent` 可迭代对象的重载 + - `DataComponents` + - `DAMAGE_TYPE` 现在是一个持有者包装的 `DamageType`,而不是 `EitherHolder` 包装的 + - `PROVIDES_TRIM_MATERIAL` 现在是一个持有者包装的 `TrimMaterial`,而不是 `ProvidesTrimMaterial` + - `CHICKEN_VARIANT` 现在是一个持有者包装的 `ChickenVariant`,而不是 `EitherHolder` 包装的 + - `ZOMBIE_NAUTILUS_VARIANT` 现在是一个持有者包装的 `ZombieNautilusVariant`,而不是 `EitherHolder` 包装的 + - `PROVIDES_BANNER_PATTERNS` 现在是一个 `HolderSet`,而不是 `TagKey` +- `net.minecraft.core.registries` + - `BuiltInRegistries#DATA_COMPONENT_INITIALIZERS` - 附加到注册表条目的数据组件列表。 + - `Registries#componentsDirPath` - 注册表中组件的路径目录。 +- `net.minecraft.data.PackOutput#createRegistryComponentPathProvider` - 用于组件注册表报告的路径提供者。 +- `net.minecraft.data.info.ItemListReport` -> `RegistryComponentsReport`,不是一对一 +- `net.minecraft.resources` + - `NetworkRegistryLoadTask` - 一个加载任务,处理从网络注册注册表对象和标签,否则从资源注册。 + - `RegistryDataLoader` + - `$PendingRegistration` -> `RegistryLoadTask$PendingRegistration` + - `$RegistryData` 现在接受一个 `RegistryValidator` 而不是一个 `boolean` + - `$RegistryLoadTask` -> `RegistryLoadTask`,不是一对一 + - `RegistryValidator` - 一个接口,用于验证注册表中的条目,将所有错误存储在映射中。 + - `ResourceManagerRegistryLoadTask` - 一个加载任务,处理从本地资源管理器注册注册表对象和标签。 +- `net.minecraft.server.ReloadableServerResources#updateStaticRegistryTags` -> `updateComponentsAndStaticRegistryTags`,不是一对一 +- `net.minecraft.world.item` + - `EitherHolder` 类已移除 + - `Item` + - `CODEC_WITH_BOUND_COMPONENTS` - 一个验证组件已绑定的物品编解码器。 + - `$Properties` + - `delayedComponent` - 延迟设置组件,提供 `HolderLookup$Provider` 以获取任何动态元素。 + - `delayedHolderComponent` - 为某个持有者包装的注册表对象延迟设置组件。 + - `ItemStack#validateComponents` 现在从 `public` 变为 `private` + - `JukeboxPlayable` 现在持有持有者包装的 `JukeboxSong`,而不是 `EitherHolder` 包装的变体 + - `JukeboxSong#fromStack` 不再接受 `HolderLookup$Provider` + - `SpawnEggItem` + - `spawnEntity` 现在是 `static` + - `byId` 现在返回一个可选的持有者包装的 `Item`,而不是 `SpawnEggItem` + - `eggs` 已移除 + - `getType` 现在是 `static` + - `spawnOffspringFromSpawnEgg` 现在是 `static` +- `net.minecraft.world.item.component` + - `BlocksAttacks` 现在持有一个可选的持有者集包装的 `DamageType`,而不是 `TagKey` + - `DamageResistant` 现在持有一个持有者集 `DamageType`,而不是 `TagKey` + - `InstrumentComponent` 现在持有一个持有者包装的 `Instrument`,而不是 `EitherHolder` 包装的变体 + - `unwrap` 已移除 + - `ProvidesTrimMaterial` 现在持有一个持有者包装的 `TrimMaterial`,而不是 `EitherHolder` 包装的变体 +- `net.minecraft.world.item.crafting` + - `Recipe#assemble` 不再接受 `HolderLookup$Provider` + - `SmithingTrimRecipe#applyTrim` 不再接受 `HolderLookup$Provider` +- `net.minecraft.world.item.equipment.trim.TrimMaterials#getFromIngredient` 已移除 +- `net.minecraft.world.level.storage.loot.functions.SetInstrumentFunction`、`#setInstrumentOptions` 现在接受持有者集包装的 `Instrument`,而不是 `TagKey` +- `net.minecraft.world.timeline.Timeline#validateRegistry` - 验证每个时间标记仅定义一次。 + +## 物品实例与堆栈模板 + +`ItemStack` 现在有一个称为 `ItemStackTemplate` 的不可变实例。与 `ItemStack` 类似,它包含持有者包装的 `Item`、它表示的物品数量以及要应用于堆栈的组件的 `DataComponentPatch`。模板可以通过 `create` 转换为堆栈,或者如果需要添加额外数据组件,则通过 `apply` 转换。`ItemStack` 同样可以通过 `ItemStackTemplate#fromNonEmptyStack` 转换为模板。现在在需要不可变性的地方(例如,进度、配方等)使用模板代替 `ItemStack`。它们为网络通信提供了常规的 `CODEC`、`MAP_CODEC` 和 `STREAM_CODEC`。 + +```java +ItemStackTemplate apple = new ItemStackTemplate( + // 堆栈的物品 + Items.APPLE.builtInRegistryHolder(), + // 持有的物品数量 + 5, + // 应用于堆栈的组件 + DataComponentPatch.builder() + .set(DataComponents.ITEM_NAME, Component.literal("苹果?")) + .build() +); + +// 将模板转换为堆栈 +ItemStack stack = apple.create(); + +// 从非空堆栈创建模板 +ItemStackTemplate fromStack = ItemStackTemplate.fromNonEmptyStack(stack); +``` + +为了帮助标准化访问两者之间的通用组件,模板和堆栈都实现了 `ItemInstance`,它提供了对标准持有者方法检查、计数和 `DataComponentGetter` 的访问。公共接口还意味着,如果堆栈是否可变无关紧要,则可以使用 `ItemInstance` 作为类型。 + +```java +// 两者都是物品实例 +ItemInstance stack = new ItemStack(Items.APPLE); +ItemInstance template = new ItemStackTemplate(Items.APPLE); + +// 获取持有者或检查某些内容 +Holder item = stack.typeHolder(); +template.is(Items.APPLE); + +// 获取堆栈或模板中的物品数量 +int stackCount = stack.count(); +int templateCount = template.count(); + +// 获取组件值 +Identifier stackModel = stack.get(DataComponents.ITEM_MODEL); +Identifier templateModel = template.get(DataComponents.ITEM_MODEL); +``` + +### 配方构建器 + +由于 `ItemStackTemplate` 的加入,`RecipeBuilder` 的实现略有变化。首先,构建器不再为结果存储 `Item`,而是将 `defaultId` 定义为配方的资源键。因此,这允许更清晰地定义不导出 `Item` 的自定义配方。 + +当然,这种新格式的更改只需让 `defaultId` 返回带有 `ItemInstance`(堆栈或模板)的 `RecipeBuilder#getDefaultRecipeId`,如下所示: + +```java +public class ExampleRecipeBuilder implements RecipeBuilder { + + // 配方的结果 + private final ItemStackTemplate result; + + public ExampleRecipeBuilder(ItemStackTemplate result) { + this.result = result; + } + + @Override + public ResourceKey> defaultId() { + // 从结果获取默认配方 id + return RecipeBuilder.getDefaultRecipeId(this.result); + } + + // 在此处实现其他所有内容 + // ... +} +``` + +- `net.minecraft.advancements` + - `Advancement$Builder#display` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` + - `DisplayInfo` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` + - `getIcon` 现在返回 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.advancements.criterion` + - `AnyBlockInteractionTrigger#trigger` 现在接受 `ItemInstance` 而不是 `ItemStack` + - `ItemPredicate` 现在实现 `ItemInstance` 的谓词而不是 `ItemStack` + - `ItemUsedOnLocationTrigger#trigger` 现在接受 `ItemInstance` 而不是 `ItemStack` +- `net.minecraft.client.particle.BreakingItemParticle$ItemParticleProvider#getSprite` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.commands.arguments.item` + - `ItemInput` 现在是一个记录 + - `serialize` 已移除 + - `createItemStack` 不再接受用于检查大小的 `boolean` + - `ItemParser#parse` 现在返回 `ItemResult` 而不是 `ItemParser$ItemResult` + - `$ItemResult` 合并到 `ItemInput` +- `net.minecraft.core.component.predicates` + - `BundlePredicate` 现在处理 `ItemInstance` 的可迭代对象而不是 `ItemStack` + - `ContainerPredicate` 现在处理 `ItemInstance` 的可迭代对象而不是 `ItemStack` +- `net.minecraft.core.particles.ItemParticleOption` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` + - 还有一个接受常规 `Item` 的重载 + - `getItem` 现在返回 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.data.recipes` + - `RecipeBuilder` + - `getResult` 已移除 + - `defaultId` - 使用此构建器制作的配方的默认标识符。 + - `getDefaultRecipeId` 现在接受 `ItemInstance` 而不是 `ItemLike` + - `RecipeProvider` + - `oreSmelting`、`oreBlasting` 现在接受 `CookingBookCategory` + - `oreCooking` 不再接受 `RecipeSerializer`,现在接受 `CookingBookCategory` + - `cookRecipes`、`simpleCookingRecipe` 不再接受 `RecipeSerializer` + - `shapeless` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` + - `ShapedRecipeBuilder` 现在接受 `ItemStackTemplate` 作为结果,`ItemLike` 移至重载 + - 两个构造函数都是 `private` + - `ShapelessRecipeBuilder` 现在接受 `ItemStackTemplate` 作为结果而不是 `ItemStack` + - `SimpleCookingRecipeBuilder` 现在接受 `ItemStackTemplate` 作为结果,`ItemLike` 移至重载 + - `generic` 不再接受 `RecipeSerializer`,现在接受 `CookingBookCategory` + - `blasting`、`smelting` 现在接受 `CookingBookCategory` + - `SingleItemRecipeBuilder` 现在接受 `ItemStackTemplate` 作为结果,`ItemLike` 移至重载 + - `ItemStackTemplate` 构造函数是 `private` + - `SmithingTransformRecipeBuilder` 现在接受 `ItemStackTemplate` 作为结果而不是 `Item` + - `TransmuteRecipeBuilder` 现在接受 `ItemStackTemplate` 作为结果而不是 `Holder` + - 构造函数是 `private` + - `transmute` 现在有一个接受 `ItemStackTemplate` 作为结果的重载 + - `addMaterialCountToOutput`、`setMaterialCount` - 根据使用的材料数量处理结果堆栈的大小。 +- `net.minecraft.network.chat.HoverEvent$ShowItem` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.server.dialog.body.ItemBody` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.world.entity.LivingEntity` + - `dropFromEntityInteractLootTable` 现在接受 `ItemInstance` 而不是 `ItemStack` 作为工具 + - `dropFromShearingLootTable` 现在接受 `ItemInstance` 而不是 `ItemStack` 作为工具 +- `net.minecraft.world.item` + - `BundleItem#getSelectedItemStack` -> `getSelectedItem`,现在返回 `ItemStackTemplate` 而不是 `ItemStack` + - `Item` + - `getCraftingRemainder` 现在返回 `ItemStackTemplate` 而不是 `ItemStack` + - `$Properties#craftRemainder` 现在有一个接受 `ItemStackTemplate` 的重载 + - `ItemInstance` - 一个类型化的物品实例,可以查询物品、大小及其组件。 + - `ItemStack` 现在实现 `ItemInstance` + - `SINGLE_ITEM_CODEC`、`STRICT_CODEC`、`STRING_SINGLE_ITEM_CODEC`、`SIMPLE_ITEM_CODEC` 已移除 + - `getMaxStackSize` -> `ItemInstance#getMaxStackSize` + - `ItemStackTemplate` - 一个包含堆栈不可变组件的记录:物品、数量和组件。 +- `net.minecraft.world.item.component` + - `BundleContents` 现在接受 `ItemStackTemplate` 列表而不是 `ItemStack` + - `items` 现在返回 `ItemStackTemplate` 列表而不是 `ItemStack` 的可迭代对象 + - `itemsCopy` 已移除 + - `getSelectedItem` - 返回选中物品的堆栈模板,如果没有选中物品则返回 `null`。 + - `ChargedProjectile` 现在是一个记录 + - 构造函数接受 `ItemStackTemplate` 列表而不是 `ItemStack` + - `of` -> `ofNonEmpty` + - `getItems` -> `itemCopies` + - `ItemContainerContents` + - `stream` -> `allItemsCopyStream` + - `nonEmptyStream` -> `nonEmptyItemCopyStream` + - `nonEmptyItems`、`nonEmptyItemsCopy` -> `nonEmptyItems`,现在返回 `ItemStackTemplate` 的可迭代对象而不是 `ItemStack` + - `UseRemainder` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.world.item.crafting` + - `AbstractCookingRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `$Factory#create` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `BlastingRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `CampfireCookingRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `ShapedRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `ShapelessRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `SingleItemRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `result` 现在返回 `ItemStackTemplate` 而不是 `ItemStack` + - `$Factory#create` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `SmeltingRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `SmithingTransformRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `SmokingRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `StonecutterRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `TransmuteRecipe` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` 作为结果 + - `TransmuteResult` -> `ItemStackTemplate`,不是一对一 + - `isResultUnchanged` 已移除 + - `apply` -> `TransmuteRecipe#createWithOriginalComponents`,不是一对一 +- `net.minecraft.world.item.crafting.display.SlotDisplay$ItemStackSlotDisplay` 现在接受 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.world.item.enchantment.EnchantmentHelper#getItemEnchantmentLevel` 现在接受 `ItemInstance` 而不是 `ItemStack` +- `net.minecraft.world.level.block.Block` + - `dropFromBlockInteractLootTable` 现在接受 `ItemInstance` 而不是 `ItemStack` + - `getDrops` 现在接受 `ItemInstance` 而不是 `ItemStack` +- `net.minecraft.world.level.block.entity.DecoratedPotBlockEntity#createdDecoratedPotItem` -> `createdDecoratedPotInstance` + - `createDecoratedPotTemplate` 创建 `ItemStackTemplate` 而不是 `ItemStack` +- `net.minecraft.world.level.storage.loot` + - `LootContext$ItemStackTarget` 现在为参数获取器实现 `ItemInstance` 泛型,而不是 `ItemStack` + - `LootContextArg$ArgCodecBuilder#anyItemStack` 现在需要一个接受 `ItemInstance` 上下文键的函数,而不是 `ItemStack` 上下文键 +- `net.minecraft.world.level.storage.loot.parameters.LootContextParams#TOOL` 现在是 `ItemInstance` 上下文键,而不是 `ItemStack` 上下文键 + +## 序列化器记录与配方信息 + +配方的实现已略有重做。首先,`RecipeSerializer` 现在是一个记录,接受用于序列化和反序列化配方的 `MapCodec` 和 `StreamCodec`。因此,`Serializer` 类已被完全移除,取而代之的是在注册期间向记录提供编解码器: + +```java +// 假设某个 ExampleRecipe 实现了 Recipe +// 我们假设只有一个 INSTANCE +public static final RecipeSerializer EXAMPLE_RECIPE = new RecipeSerializer<>( + // 用于从磁盘读取/写入配方的映射编解码器。 + MapCodec.unit(INSTANCE), + // 用于从网络读取/写入配方的流编解码器。 + StreamCodec.unit(INSTANCE) +); +``` + +其次,一些关于配方设置和配方书信息的常见数据已被分离到单独的对象中。这些对象作为构造函数的一部分传递给配方,并用于更清晰地处理所有配方的类似实现。 + +原版提供了四个这样的常见对象类,分为两个不同的类别。`Recipe$CommonInfo` 用于通用配方设置。同时,`Recipe$BookInfo` 用于配方书信息,`CraftingRecipe$CraftingBookInfo` 用于合成配方,以及 `AbstractCookingRecipe$CookingBookInfo` 用于烹饪配方(例如,熔炼、高炉等)。这些常见对象类提供了根据需要构造编解码器的方法,然后可以将其传递给相关的配方编解码器。 + +这些类通常通过 `Recipe` 子类传递,用作实现抽象方法的样板。这些对象中的数据都不能直接在实现本身之外访问,只能通过 `Recipe` 接口中定义的方法访问。因此,可以使用 `NormalCraftingRecipe`、`CustomRecipe`、`SimpleSmithingRecipe`、`SingleItemRecipe` 和 `AbstractCookingRecipe` 等类通过实现几个方法来创建新的配方实现。 + +请注意,这些常见信息类是一种设计理念,您可以根据需要选择实现。只有在构建现有配方子类型时才需要使用它们。 + +- `net.minecraft.data.recipes` + - `CustomCraftingRecipeBuilder` - 一个配方构建器,从某些常见和合成书籍信息创建任意合成配方。 + - `RecipeBuilder` + - `determineBookCategory` -> `determineCraftingBookCategory` + - `createCraftingCommonInfo` - 创建常见的配方信息。 + - `createCraftingBookInfo` - 创建合成书籍信息。 + - `RecipeUnlockAdvancementBuilder` - 用于解锁配方的进度构建器。 + - `SpecialRecipeBuilder`、`special` 现在接受一个提供的 `Recipe` 而不是一个 `CraftingBookCategory` 到 `Recipe` 的函数 + - `unlockedBy` - 解锁配方进度所需的条件。 +- `net.minecraft.world.item.crafting` + - `AbstractCookingRecipe` 现在接受 `Recipe$CommonInfo` 和 `$CookingBookInfo` 而不是 group 和 `CookingBookCategory` + - `$Factory#create` 现在接受 `Recipe$CommonInfo` 和 `$CookingBookInfo` 而不是 group 和 `CookingBookCategory` + - `$Serializer` 被 `cookingMapCodec`、`cookingStreamCodec` 取代 + - `$CookingBookInfo` - 一个包含配方书常见烹饪信息的记录。 + - `BannerDuplicateRecipe` 现在接受旗帜 `Ingredient` 和 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `BlastingRecipe` 现在接受 `Recipe$CommonInfo` 和 `$AbstractCookingRecipeCookingBookInfo` 而不是 group 和 `CookingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `BookCloningRecipe` 现在接受源和材料的 `Ingredient`、定义可复制代数的 `MinMaxBounds$Int`s,以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `ALLOWED_BOOK_GENERATION_RANGES`、`DEFAULT_BOOK_GENERATION_RANGES` - 书籍代数克隆的范围。 + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `CampfireCookingRecipe` 现在接受 `Recipe$CommonInfo` 和 `AbstractCookingRecipe$CookingBookInfo` 而不是 group 和 `CookingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `CraftingRecipe$CraftingBookInfo` - 一个包含配方书常见合成信息的记录。 + - `CustomRecipe` 不再接受任何构造函数参数 + - `$Serializer` 已移除,被其实现的编解码器取代 + - `DecoratedPotRecipe` 现在接受每侧的 `Ingredient` 图案以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `DyeRecipe` 现在接受 `Recipe$CommonInfo` 和 `CraftingRecipe$CraftingBookInfo`,以及目标物品和染料的 `Ingredient` 和 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `FireworkRocketRecipe` 现在接受外壳、燃料和星星的 `Ingredient` 以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `FireworkStarFadeRecipe` 现在接受目标物品和染料的 `Ingredient` 以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `FireworkStarRecipe` 现在接受 `$Shape` 到 `Ingredient` 的映射;踪迹、闪烁、燃料和染料的 `Ingredient`;以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `MapCloningRecipe` 被 `TransmuteRecipe` 取代 + - `MapExtendingRecipe` 现在扩展 `CustomRecipe` 而不是 `ShapedRecipe` + - 构造函数现在接受地图和材料的 `Ingredient` 以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `NormalCraftingRecipe` - 一个定义合成配方标准实现的类。 + - `Recipe` + - `showNotification`、`group` 不再是默认的 + - `$BookInfo` - 配方书的信息。 + - `$CommonInfo` - 所有配方的常见信息。 + - `RecipeSerializer` 现在是一个包含 `MapCodec` 和 `StreamCodec` 的记录 + - 注册的条目已移至 `RecipeSerializers` + - `register` 已移除 + - `RecipeSerializers` - 所有配方的原版序列化器。 + - `RepairItemRecipe` 不再接受任何参数 + - `INSTANCE` - 配方序列化器单例。 + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `ShapedRecipe` 现在扩展 `NormalCraftingRecipe` 而不是实现 `CraftingRecipe` + - 构造函数现在接受 `Recipe$CommonInfo` 和 `CraftingRecipe$CraftingBookInfo` 而不是 group 和 `CraftingBookCategory` + - `$Serializer` -> `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER`;不是一对一 + - `ShapelessRecipe` 现在扩展 `NormalCraftingRecipe` 而不是实现 `CraftingRecipe` + - 构造函数现在接受 `Recipe$CommonInfo` 和 `CraftingRecipe$CraftingBookInfo` 而不是 group 和 `CraftingBookCategory` + - `$Serializer` -> `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER`;不是一对一 + - `ShieldDecorationRecipe` 现在接受旗帜和目标物品的 `Ingredient` 以及 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `SimpleSmithingRecipe` - 一个定义锻造配方标准实现的类。 + - `SingleItemRecipe` 现在接受 `Recipe$CommonInfo` 而不是 group + - `commonInfo` - 配方的常见信息。 + - `$Factory#create` 现在接受 `Recipe$CommonInfo` 而不是 group + - `$Serializer` -> `simpleMapCodec`、`simpleStreamCodec`;不是一对一 + - `SmeltingRecipe` 现在接受 `Recipe$CommonInfo` 和 `AbstractCookingRecipe$CookingBookInfo` 而不是 group 和 `CookingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `SmithingTransformRecipe` 现在扩展 `SimpleSmithingRecipe` 而不是实现 `SmithingRecipe` + - 构造函数现在接受 `Recipe$CommonInfo` + - `$Serializer` -> `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER`;不是一对一 + - `SmithingTrimRecipe` 现在扩展 `SimpleSmithingRecipe` 而不是实现 `SmithingRecipe` + - 构造函数现在接受 `Recipe$CommonInfo` + - `$Serializer` -> `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER`;不是一对一 + - `SmokingRecipe` 现在接受 `Recipe$CommonInfo` 和 `AbstractCookingRecipe$CookingBookInfo` 而不是 group 和 `CookingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `StonecutterRecipe` 现在接受 `Recipe$CommonInfo` 而不是 group + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `TippedArrowRecipe` -> `ImbueRecipe`,不是一对一 + - 构造函数现在接受 `Recipe$CommonInfo` 和 `CraftingRecipe$CraftingBookInfo`,以及源材料和材料的 `Ingredient` 和 `ItemStackTemplate` 结果,而不是 `CraftingBookCategory` + - `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER` - 配方的序列化器。 + - `TransmuteRecipe` 现在扩展 `NormalCraftingRecipe` 而不是实现 `CraftingRecipe` + - 构造函数现在接受 `Recipe$CommonInfo` 和 `CraftingRecipe$CraftingBookInfo`,以及处理材料数量并将其添加到结果的 `MinMaxBounds$Ints` 和 `boolean`,而不是 group 和 `CraftingBookCategory` + - `$Serializer` -> `MAP_CODEC`、`STREAM_CODEC`、`SERIALIZER`;不是一对一 +- `net.minecraft.world.item.crafting.display.SlotDisplay` + - `$OnlyWithComponent` - 仅当内容具有所需组件时才显示的显示。 + - `$WithAnyPotion` - 内容具有任何药水内容组件的显示。 +- `net.minecraft.world.level.storage.loot.functions.SmeltItemFunction#smelted` 现在可以接受是否使用输入材料数量 + +## 染料组件 + +指定物品是否可用作染料材料现在通过 `DYE` 数据组件处理。该组件指定一个 `DyeColor`,可以通过 `Item$Properties#component` 设置: + +```java +public static final Item EXAMPLE_DYE = new Item(new Item.Properties().component( + DataComponents.DYE, DyeColor.WHITE +)); +``` + +然而,染料材料的行为并不仅仅由组件本身完全涵盖。在大多数情况下,该组件与某些其他标签或子类结合使用以获得所需的行为。 + +### 实体和告示牌 + +给绵羊和告示牌染色完全通过 `DyeItem` 子类处理,检查物品是否具有 `DYE` 组件。 + +另一方面,给狼和猫的项圈染色可以是任何物品,只要它具有 `DYE` 组件即可。此外,该物品必须分别位于 `ItemTags#WOLF_COLLAR_DYES` 或 `ItemTags#CAT_COLLAR_DYES` 标签中。 + +### 染料配方 + +`DyeRecipe`(以前称为 `ArmorDyeRecipe`)可以接受任何目标原料,并应用染料原料的颜色以获得带有关联 `DYED_COLOR` 组件的所需结果。任何物品都可以被视为染料;但是,没有 `DYE` 组件的物品将默认为 `DyeColor#WHITE`。盔甲使用 `RecipeProvider#dyedItem` 来允许 `ItemTags#DYES` 标签中的任何物品给盔甲染色。然而,捆绑包和潜影盒的颜色组件是不同的物品,这意味着默认配方直接与原版 `DyeItem` 绑定,因此需要为将这些物品应用染料生成单独的配方。 + +另一方面,织布机、烟花星和烟花星面配方期望任何染料材料都具有 `DYE` 组件。织布机还有一个额外要求,即物品位于 `ItemTags#LOOM_DYES` 标签中。 + +- `net.minecraft.core.component.DataComponents#DYE` - 表示物品可以作为特定颜色的染料。 +- `net.minecraft.data.recipes.RecipeProvider` + - `dyedItem` - 创建一个染色物品配方。 + - `dyedShulkerBoxRecipe` - 创建一个染色潜影盒配方。 + - `dyedBundleRecipe` - 创建一个染色捆绑包配方。 +- `net.minecraft.world.item` + - `BundleItem` + - `getAllBundleItemColors`、`getByColor` 已移除 + - `DyeColor#VALUES` - 所有染料颜色的列表。 + - `DyeItem` 不再接受 `DyeColor` + - `getDyeColor`、`byColor` 已移除 +- `net.minecraft.world.item.component.DyedItemColor#applyDyes` 现在接受 `DyeColor` 列表而不是 `DyeItem` + - 还有一个重载可以接受 `DyedItemColor` 组件而不是 `ItemStack` +- `net.minecraft.world.item.crafting.ArmorDyeRecipe` -> `DyeRecipe`,不是一对一 +- `net.minecraft.world.item.crafting.display.SlotDisplay$DyedSlotDemo` - 用于演示物品染色的显示。 +- `net.minecraft.world.level.block` + - `BannerBlock#byColor` 已移除 + - `ShulkerBoxBlock#getBlockByColor`、`getColoredItemStack` 已移除 + +## 世界时钟与时间标记 + +世界时钟是表示从世界首次加载起每刻增加的时间的对象。这些时钟用作计时器,以正确处理基于时间的事件(例如,当前日期、睡眠)。原版提供了两个世界时钟:一个用于 `minecraft:overworld`,一个用于 `minecraft:the_end`。 + +创建时钟非常简单:只需在 `world_clock` 中创建一个空的数据包注册表对象。 + +```json5 +// 对于某个世界时钟 'examplemod:EXAMPLE_CLOCK' +// JSON 位于 'data/examplemod/world_clock/EXAMPLE_CLOCK.json' +{} +``` + +然后,您可以通过 `ClockManager` 通过 `Level#clockManager` 或 `MinecraftServer#clockManager` 查询时钟状态: + +```java +// 对于某个 Level level +// 假设我们有某个 ResourceKey EXAMPLE_CLOCK + +// 获取时钟引用 +Holder.Reference clock = level.registryAccess().getOrThrow(EXAMPLE_CLOCK); + +// 查询时钟时间 +long ticksPassed = level.clockManager().getTotalTicks(clock); +``` + +如果从服务器访问时钟,您还可以修改时钟的状态: + +```java +// 对于某个 ServerLevel level +// 假设我们有某个 ResourceKey EXAMPLE_CLOCK + +// 获取时钟引用 +Holder.Reference clock = level.registryAccess().getOrThrow(EXAMPLE_CLOCK); + +// 获取服务器时钟管理器 +ServerClockManager clockManager = level.clockManager(); + +// 设置已过去的刻数总数 +clockManager.setTotalTicks(clock, 0L); + +// 将刻数增加某个值 +clockManager.addTicks(clock, 10L); + +// 暂停时钟 +clockManager.setPaused( + clock, + // `true` 暂停。 + // `false` 恢复。 + true +); +``` + +### 标记时间线 + +世界时钟本身的范围相当有限,因为您必须跟踪已过去的刻数。然而,当与时间线一起使用时,可以标记特定的重复时间来处理基于时间的事件。 + +作为回顾,`Timeline` 是一种基于某个 `WorldClock` 修改属性的方法。目前,所有原版时间线都使用 `minecraft:overworld` 世界时钟通过 `clock` 字段来跟踪它们的时间。所有时间线都必须定义它使用的时钟;但是,请注意,时钟默认不支持任何同步,这意味着更新一个时钟的时间不会影响另一个时钟。 + +```json5 +// 对于某个时间线 'examplemod:example_timeline' +// 在 `data/examplemod/timeline/example_timeline.json +{ + // 使用自定义时钟。 + // 对时钟时间的任何更改只会影响 + // 使用此时钟的时间线。 + // 例如,`minecraft:overworld` 不会被更改。 + "clock": "examplemod:example_clock", + // 以 24,000 刻的间隔执行操作。 + "period_ticks": "24000", + // ... +} + + +// 对于某个时间线 'examplemod:example_timeline_2' +// 在 `data/examplemod/timeline/example_timeline_2.json +{ + // 使用与上面相同的时钟。 + // 对时钟时间的更改将影响这两个 + // 时间线。 + "clock": "examplemod:example_clock", + // 以 1,000 刻的间隔执行操作。 + "period_ticks": "1000", + // ... +} + +// 对于某个时间线 'examplemod:example_overworld' +// 在 `data/examplemod/timeline/example_overworld.json +{ + // 使用原版主世界时钟。 + // 对时钟时间的更改不会影响 + // 上面的时间线,因为它们使用不同的 + // 时钟。 + "clock": "minecraft:overworld", + // 以 6,000 刻的间隔执行操作。 + "period_ticks": "6000", + // ... +} +``` + +时间线还可以定义时间标记,这只是世界时钟时间线周期内某个时间的标识符。这些通常用于命令内部(例如 `/time set`),以检查是否已经过了某个时间(例如,村庄袭击),或跳转到给定时间(例如,从睡眠中醒来)。时间标记在 `time_markers` 中定义,指定周期内的 `ticks` 以及是否可以在 `show_in_commands` 中显示。由于时间标记由其世界时钟标识,因此使用相同世界时钟的时间线不能定义相同的时间标记。 + +```json5 +// 对于某个时间线 'examplemod:example_timeline' +// 在 `data/examplemod/timeline/example_timeline.json +{ + // 时钟的标识符。 + "clock": "examplemod:example_clock", + // 以 24,000 刻的间隔执行操作。 + "period_ticks": "24000", + // 时间线指定的周期内的标记 + "time_markers": { + // 一个标记 + "examplemod:example_marker": { + // 此标记表示的周期内的刻数。 + // 例如,5000、29000、53000 等。 + "ticks": 5000, + // 当为 true 时,允许在命令中建议时间标记。 + // 如果为 false,仍然可以在命令中使用时间标记, + // 只是不会被建议。 + "show_in_commands": true + } + } + // ... +} +``` + +一旦定义了时间标记,它们就会在 `ServerClockManager` 中为世界时钟注册,从而允许它们被给定 `ResourceKey` 使用。 + +```java +// 对于某个 ServerLevel level +// 假设我们有某个 ResourceKey EXAMPLE_CLOCK +// 假设我们有某个 ResourceKey EXAMPLE_MARKER + +// 获取时钟引用 +Holder.Reference clock = level.registryAccess().getOrThrow(EXAMPLE_CLOCK); + +// 获取服务器时钟管理器 +ServerClockManager clockManager = level.clockManager(); + +// 检查时间是否在指定的标记处 +// 这应在每刻检查特定时间事件时使用 +boolean atMarker = clockManager.isAtTimeMarker(clock, EXAMPLE_MARKER); + +// 跳转到标记指定的时间 +// 如果世界时钟在 3000,则将时钟时间设置为 5000 +// 如果世界时钟在 6000,则将时钟时间设置为 29000 +// 返回时间是否被设置,如果标记对于时钟存在,则始终为 true。 +boolean timeSet = clockManager.skipToTimeMarker(clock, EXAMPLE_MARKER); +``` + +- `net.minecraft.client.ClientClockManager` - 在客户端管理世界时钟的滴答。 +- `net.minecraft.client.gui.components.debug.DebugEntryDayCount` - 显示 Minecraft 世界的当前日期。 +- `net.minecraft.client.multiplayer` + - `ClientLevel` + - `setTimeFromServer` 不再接受 `long` 白天时间,也不再接受是否滴答白天时间的 `boolean` + - `$ClientLevelData#setDayTime` 已移除 + - `ClientPacketListener#clockManager` - 获取客户端时钟管理器。 +- `net.minecraft.client.renderer.EndFlashState#tick` 现在接受结束时钟时间而不是游戏时间 +- `net.minecraft.commands.arguments.ResourceArgument` + - `getClock` - 根据字符串资源标识符获取世界时钟的引用。 + - `getTimeline` - 根据字符串资源标识符获取时间线的引用。 +- `net.minecraft.core.registries.Registries#WORLD_CLOCK` - 世界时钟的注册表标识符。 +- `net.minecraft.gametest.framework.TestEnvironmentDefinition` + - `$TimeOfDay` -> `$ClockTime`,不是一对一 + - `$Timelines` - 一个使用时间线列表的测试环境。 +- `net.minecraft.network.protocol.game.ClientboundSetTimePacket` 现在接受一个时钟到其网络状态的映射,而不是白天时间 `long` 和 `boolean` +- `net.minecraft.server` + - `MinecraftServer` + - `forceTimeSynchronization` -> `forceGameTimeSynchronization`,不是一对一 + - `clockManager` - 服务器时钟管理器。 +- `net.minecraft.server.level.ServerLevel` + - `setDayTime`、`getDayCount` 已移除 + - `setEnvironmentAttributes` - 设置要使用的环境属性系统。 +- `net.minecraft.world.attribute.EnvironmentAttributeSystem$Builder#addTimelineLayer` 现在接受 `ClockManager` 而不是 `LongSupplier` +- `net.minecraft.world.clock` + - `ClockManager` - 一个管理器,获取世界时钟已过去的刻数总数。 + - `ClockNetworkState` - 要在网络上同步的时钟的当前状态。 + - `ClockState` - 时钟的当前状态,包括总刻数和时钟是否暂停。 + - `ClockTimeMarker` - 一个跟踪世界时钟某个时间段的标记。 + - `ClockTimeMarkers` - 所有原版时间标记。 + - `PackedClockStates` - 一个时钟到其状态的映射,为存储或其他用途而压缩。 + - `ServerClockManager` - 在服务器端管理世界时钟的滴答。 + - `WorldClock` - 一个表示某个位置计时键的空记录。 + - `WorldClocks` - 所有原版世界时钟。 +- `net.minecraft.world.level` + - `Level` + - `getDayTime` -> `getOverworldClockTime`,不是一对一 + - `clockManager` - 时钟管理器。 + - `getDefaultClockTime` - 获取当前维度的时钟时间。 + - `LevelReader#getEffectiveSkyBrightness` - 使用当前变暗因子获取天空亮度。 +- `net.minecraft.world.level.dimension.DimensionType` 现在接受一个默认的、持有者包装的 `WorldClock` +- `net.minecraft.world.level.storage` + - `LevelData#getDayTime` 已移除 + - `ServerLevelData` + - `setDayTime` 已移除 + - `setClockStates`、`clockStates` - 处理世界时钟的当前状态。 +- `net.minecraft.world.level.storage.loot.predicates.TimeCheck` 现在接受一个持有者包装的 `WorldClock` + - `time` 现在接受一个持有者包装的 `WorldClock` + - `$Builder` 现在接受一个持有者包装的 `WorldClock` +- `net.minecraft.world.timeline` + - `AttributeTrack#bakeSampler` 现在接受一个持有者包装的 `WorldClock` + - `AttributeTrackSampler` 现在接受一个持有者包装的 `WorldClock`,以及一个 `ClockManager` 而不是用于白天时间获取器的 `LongSupplier` + - `Timeline` 现在接受一个持有者包装的 `WorldClock` 以及一个时间标记到其信息的映射 + - `#builder` 现在接受一个持有者包装的 `WorldClock` + - `getPeriodCount` - 获取在给定时间范围内已发生的周期数。 + - `getCurrentTicks` 现在接受 `ClockManager` 而不是 `Level` + - `getTotalTicks` 现在接受 `ClockManager` 而不是 `Level` + - `clock` - 返回持有者包装的 `WorldClock`。 + - `registerTimeMarkers` - 注册此时间线中定义的所有 `ClockTimeMarker`。 + - `createTrackSampler` 现在接受 `ClockManager` 而不是用于白天时间获取器的 `LongSupplier` + - `$Builder#addTimeMarker` - 在给定刻数添加一个时间标记,以及是否可以在命令中建议该标记。 + - `Timelines#DAY` -> `OVERWORLD_DAY` + +## 将主关卡数据拆分为保存数据 + +一些 `WorldData` 设置已移至 `SavedData`,允许等级/维度具有更多可定制性。这一增加还带来了一些关于如何引用和查询 `SavedData` 的变化。 + +### 保存数据变更 + +`SavedDataType` 现在使用 `Identifier` 标识某些 `SavedData`,该标识符针对数据文件夹进行解析。此更改允许数据文件夹内的子目录,因为数据存储将在尝试写入文件之前首先创建所有缺失的父目录。 + +```java +public class ExampleData extends SavedData { + + public static final SavedDataType TYPE = new SavedDataType<>( + // 要解析的保存数据的标识符 + // 数据可以在以下位置找到: + // `/dimensions///data/examplemod/example/data.dat` + Identifier.fromNamespaceAndPath("examplemod", "example/data"), + // 创建新保存数据的构造函数 + ExampleData::new, + // 序列化新保存数据的编解码器 + MapCodec.unitCodec(ExampleData::new), + // 数据修复器类型 + // 要么是一些修补的枚举值,要么根据模组加载器实现为 null。 + null + ); +} +``` + +然后可以通过 `SavedDataStorage`(从 `DimensionDataStorage` 重命名而来)查询 `SavedData`。重命名是因为 `MinecraftServer` 实例现在除了等级之外,还有自己的用于全局实例的数据存储。这意味着任何全局保存的数据都应存储在服务器实例上,而不是主世界。 + +```java +// 给定一个 MinecraftServer server +ExampleData data = server.getDataStorage().computeIfAbsent(ExampleData.TYPE); + + +// 给定一个 ServerLevel level +ExampleData data = level.getDataStorage().computeIfAbsent(ExampleData.TYPE); +``` + +### 额外的保存数据 + +以下信息现在存储为保存数据: + +- 自定义 Boss 事件 +- 末影龙战斗 +- 游戏规则 +- 流浪商人生成 +- 天气 +- 世界生成设置 + +其中,只有末影龙战斗是基于每个等级/维度的。其余仍然通过服务器数据存储存储和访问。另一方面,自定义 Boss 事件仍然是唯一的,因为它由实现者决定哪些玩家参与事件。 + +- `net.minecraft.client.Minecraft#doWorldLoad` 现在接受可选的 `GameRules` +- `net.minecraft.client.gui.screens.worldselection` + - `CreateWorldCallback` 现在接受 `LevelDataAndDimensions$WorldDataAndGenSettings` 和可选的 `GameRules`,而不是 `PrimaryLevelData` + - `EditGameRulesScreen` -> `AbstractGameRulesScreen` + - 实现在 `.screens.options.InWorldGameRulesScreen` 和 `WorldCreationGameRulesScreen` 中 + - `WorldOpenFlows` + - `createLevelFromExistingSettings` 现在接受 `LevelDataAndDimensions$WorldDataAndGenSettings` 和可选的 `GameRules`,而不是 `WorldData` + - `loadWorldStem` 现在接受 `LevelStorageSource$LevelStorageAccess` +- `net.minecraft.client.server.IntegratedServer` 现在接受可选的 `GameRules` +- `net.minecraft.server` + - `MinecraftServer` 现在接受可选的 `GameRules` + - `getGlobalGameRules` - 获取主世界维度的游戏规则。 + - `getWorldGenSettings` - 获取世界的生成设置。 + - `getWeatherData` - 获取服务器的天气数据。 + - `getDataStorage` - 获取服务器的保存数据存储。 + - `getGameRules` - 获取服务器的游戏规则。 + - `WorldStem` 现在接受 `LevelDataAndDimensions$WorldDataAndGenSettings` 而不是 `WorldData` +- `net.minecraft.server.bossevents` + - `CustomBossEvent` 现在接受一个 `UUID` 作为标识符,以及一个 `Runnable` 作为回调 + - `getTextId` -> `customId` + - `addOfflinePlayer` 已移除 + - `getValue` -> `value` + - `getMax` -> `max` + - `load` 现在接受 `UUID` 标识符以及一个 `Runnable` 作为回调 + - `CustomBossEvents` 现在扩展 `SavedData` + - `create` 现在接受一个 `RandomSource` + - `save`、`load` -> `TYPE`,不是一对一 +- `net.minecraft.server.dedicated.DedicatedServer` 现在接受可选的 `GameRules` +- `net.minecraft.server.level` + - `ChunkMap#getChunkDataFixContextTag` 现在接受一个可选的 `Identifier` 而不是 `ResourceKey` + - `ServerBossEvent` 现在接受一个 `UUID` 作为 id + - `setDirty` - 将 boss 事件标记为脏以进行保存。 + - `ServerLevel` 不再接受 `RandomSequences` + - `setWeatherParameters` -> `MinecraftServer#setWeatherParameters` + - `getWeatherData` - 获取服务器的天气数据。 + - `getRandomSequence` -> `MinecraftServer#getRandomSequence` + - `getRandomSequences` -> `MinecraftServer#getRandomSequences` +- `net.minecraft.world.entity.npc.wanderingtrader.WanderingTraderSpawner` 现在接受 `SavedDataStorage` 而不是 `ServerLevelData` + - `MIN_SPAWN_CHANCE` 现在从 `private` 变为 `public` +- `net.minecraft.world.entity.raid.Raids#TYPE_END`、`getType` 已移除 +- `net.minecraft.world.level` + - `Level#prepareWeather` 已移除 + - `LevelSettings` 现在是一个记录 + - 构造函数现在接受 `$DifficultySettings` 而不是仅仅 `Difficulty` + - `withDifficultyLock` - 带有难度是否锁定的设置。 + - `copy` - 复制设置。 + - `$DifficultySettings` - 难度的设置。 +- `net.minecraft.world.level.dimension.DimensionType` 现在接受一个 `boolean`,表示维度是否可以有末影龙战斗 +- `net.minecraft.world.level.dimension.end` + - `DragonRespawnAnimation` -> `DragonRespawnStage`,不是一对一 + - `EndDragonFight` -> `EnderDragonFight`,不是一对一 +- `net.minecraft.world.level.gamerules.GameRuleMap` 现在扩展 `SavedData` + - `TYPE` - 保存数据类型。 + - `reset` - 将规则重置为其默认值。 +- `net.minecraft.world.level.levelgen.WorldGenSettings` 现在是一个 final 类而不是记录,扩展 `SavedData` + - `encode`、`decode` 被 `TYPE` 取代 + - `of` - 构造生成设置。 +- `net.minecraft.world.level.saveddata` + - `SavedDataType` 现在接受 `Identifier` 而不是字符串作为 id + - `WanderingTraderData` - 流浪商人的保存数据。 + - `WeatherData` - 天气的保存数据。 +- `net.minecraft.world.level.storage` + - `DimensionDataStorage` -> `SavedDataStorage`,不是一对一 + - `LevelData#isThundering`、`isRaining`、`setRaining` 现在在 `WeatherData` 中 + - `LevelDataAndDimensions` 现在接受 `$WorldDataAndGenSettings` 而不是 `WorldData` + - `$WorldDataAndGenSettings` - 保存世界数据和生成设置。 + - `LevelResource` 现在是一个记录 + - `PrimaryLevelData` 字段已移至各自的保存数据类 + - `PLAYER` -> `OLD_PLAYER` + - `SINGLEPLAYER_UUID` - 一个表示单机世界中玩家 UUID 的字符串。 + - `WORLD_GEN_SETTINGS` -> `OLD_WORLD_GEN_SETTINGS` + - `writeLastPlayed` - 写入最后玩的玩家。 + - `writeVersionTag` - 写入数据版本标签。 + - `ServerLevelData` + - `setThundering`、`getRainTime`、`setRainTime`、`setThunderTime`、`getThunderTime`、`getClearWeatherTime`、`setClearWeatherTime` 移至 `WeatherData` + - `getWanderingTraderSpawnDelay`、`setWanderingTraderSpawnDelay`、`getWanderingTraderSpawnChance`、`setWanderingTraderSpawnChance`、`getWanderingTraderId`、`setWanderingTraderId` 移至 `WanderingTraderData` + - `getLegacyWorldBorderSettings`、`setLegacyWorldBorderSettings` 被 `WorldBorder` 取代 + - `getScheduledEvents` -> `MinecraftServer#getScheduledEvents` + - `getGameRules` 被 `GameRuleMap` 取代 + - `WorldData` + - `getCustomBossEvents`、`setCustomBossEvents` 被 `CustomBossEvents` 取代 + - `createTag` 不再接受 `RegistryAccess` + - `getGameRules` 被 `GameRuleMap` 取代 + - `getLoadedPlayerTag` -> `getSinglePlayerUUID`,不是一对一 + - `endDragonFightData`、`setEndDragonFightData` 被 `EnderDragonFight` 取代 + - `worldGenOptions` 被 `WorldGenSettings` 保存数据取代 +- `net.minecraft.world.level.timers.TimerQueue` 现在扩展 `SavedData` + - 构造函数现在接受 `$Packed` 事件,而不是 `TimerCallbacks` 和事件数据的 `Stream` + - `store` 被 `CODEC`、`TYPE`、`codec` 取代 + - `loadEvent`、`storeEvent` 被 `$Event$Packed#codec` 取代 + - `$Event` 现在是一个记录 + - `$Packed` - 打包的事件数据。 + - `$Packed` - 打包的时间队列。 + +## 更多渲染变更 + +### 材质与动态层选择 + +方块和物品模型现在不再指定它们属于哪个 `RenderType` 或 `ChunkSectionLayer`。相反,这在加载模型时计算,确定每个四边形的关联层。这意味着 `ItemBlockRenderTypes` 被移除,物品的 `RenderType` 设置也被完全移除。 + +为了确定四边形或面被设置到哪个层,会计算纹理的 `Transparency`。具体来说,对于映射到四边形的 UV 区域,检查是否存在任何透明像素(alpha 为 0)或半透明像素(alpha 不为 0 或 255)。对于 `ChunkSectionLayer`,如果存在半透明像素则使用 `ChunkSectionLayer#TRANSLUCENT`,否则如果存在透明像素则使用 `CUTOUT`,否则使用 `SOLID`。对于物品 `RenderType`,如果存在半透明像素则使用 `Sheets#translucentItemSheet` 和用于方块物品的 `translucentBlockItemSheet`,否则使用 `cutoutItemSheet` 和用于方块物品的 `cutoutBlockItemSheet`。`Transparency` 也影响 `MipmapStrategy#AUTO` 的使用,如果存在透明像素则使用 `CUTOUT` 而不是 `MEAN` 作为默认值。 + +可以通过模型 JSON 定义的 `Material` 纹理来影响四边形的 `Transparency`。一个 `Material` 指定纹理的 `sprite`,它表示纹理的相对路径,以及可选的 `force_translucent`,它强制使用此纹理的任何四边形使用 `Transparency#TRANSLUCENT`(透明为 false,半透明为 true): + +```json5 +// 对于某个模型 `examplemod:example_model` +// 在:`assets/examplemod/models/example_model.json` +{ + "parent": "minecraft:block/template_glass_pane_post", + "textures": { + // Material 可以是一个简单的纹理引用 + // 指向 `assets/minecraft/textures/block/glass_pane_top.png` + "edge": "minecraft:block/glass_pane_top", + // 或者它可以是一个对象 + "pane": { + // 使用此键的面的相对纹理引用 + // 指向 `assets/minecraft/textures/block/glass.png` + "sprite": "minecraft:block/glass", + // 当为 true 时,将此纹理键的所有面设置为 + // 始终具有透明像素。 + "force_translucent": true + } + } + // ... +} +``` + +此更改还定义了渲染顺序,其中所有实体四边形首先渲染,然后是切割四边形,最后是半透明四边形,按距离相机的距离排序。 + +### 材质与精灵 + +您可能已经注意到,`Material` 最初用于定义图集中的某些纹理。纹理 JSON 中材质的添加改变了这些类的命名。`Material` 现在明确指代模型 JSON 中的纹理引用。这意味着,所有对原始纹理位置的引用都已替换为 `Material`(如果未烘焙)和 `Material$Baked`(如果已烘焙)。此外,`SpriteGetter` 现在是 `MaterialBaker`。 + +至于原来的 `Material`,现在称为精灵,其中 `Material` 重命名为 `SpriteId`,`MaterialSet` 重命名为 `SpriteGetter`。 + +### 四边形粒子层 + +`SingleQuadParticle$Layer` 已拆分为 `OPAQUE_*` 和 `TRANSLUCENT_*` 层,具体取决于图集使用的粒子纹理是否包含半透明像素。请注意,这里的“opaque”指的是切割,其中 alpha 小于 0.1 的像素被丢弃。如果不创建新层,可以使用 `$Layer#bySprite` 来确定粒子纹理应使用的层。 + +```java +public class ExampleParticle extends SingleQuadParticle { + + private final SingleQuadParticle.Layer layer; + + public SingleQuadParticle(ClientLevel level, double x, double y, double z, TextureAtlasSprite sprite) { + super(level, x, y, z, sprite); + this.layer = SingleQuadParticle.Layer.bySprite(sprite); + } + + @Override + protected SingleQuadParticle.Layer getLayer() { + return this.layer; + } +} +``` + +### 方块模型 + +在通用世界上下文之外渲染单个方块模型的管道已被重写,类似于 `ItemModel`,其中“方块模型”更新某个渲染状态,然后提交其元素进行渲染。因此,大多数方块模型类已被重写或在一定程度上重新组织。 + +由于方块模型名称通常与通用模型 JSON 同义,许多类被移动和重命名,以将模型 JSON 与方块状态 JSON 以及现在的方块模型分开。因此,原始名称中带有“block”的模型几何体已更改为“cuboid”:(例如,`BlockModel` -> `CuboidModel`,`BlockModelWrapper` -> `CuboidItemModelWrapper`)。此外,引用方块状态 JSON 内定义的渲染过程部分已从“block”更改为“block state”(例如,`BlockModelPart` -> `BlockStateModelPart`,`BlockModelDefinition` -> `BlockStateModelDispatcher`)。您可以认为大多数“block model”类是新的,而那些重命名的类取代了 `SpecialBlockModelRenderer` 系统。 + +方块模型系统从所有模型和定义加载并解析后的 `ModelManager` 开始,准备进行烘焙。方块模型通过 `BuiltInBlockModels#crateBlockModels` 加载,它将某个 `BlockState` 链接到 `BlockModel$Unbaked`。与物品模型类似,未烘焙实例定义了应如何构建方块模型的属性。这些存储在 `LoadedBlockModels` 中,然后在所有 JSON 之后通过 `bake` 烘焙成 `BlockModel`。然后,这个 `BlockState` 到 `BlockModel` 的映射存储在 `BlockModelSet` 中,准备好通过 `get` 查询,或者更常见的是通过 `BlockModelResolver#update` 查询,后者调用模型集。任何未延迟定义的模型都会解析为 `BlockStateModel` 的包装器。 + +原版为常见用途提供了六种 `BlockModel` 实现。有 `EmptyBlockModel`,它不提交任何元素,因此不渲染任何内容;以及 `BlockStateModelWrapper`,它包装并显示关联的 `BlockStateModel`。然后,有根据某些属性开关更改模型的等价物(`SelectBlockModel`)、条件 `boolean`(`ConditionalBlockModel`)以及将多个模型组合在一起的组合(`CompositeBlockModel`)。最后,有 `SpecialBlockModelWrapper`,它通过存储的 `SpecialModelRenderer` 提交其元素,这是物品和方块模型之间的统一提交器。 + +```java +// 由于方块模型系统通过其代码内引导程序进行硬编码, +// 此示例将假设存在某种方法来访问 `BuiltInBlockModels$Builder` 构建器。 + +// 我们还将假设我们有某个 Block EXAMPLE_BLOCK_* 来附加模型。 + +// 常规方块模型 +builder.put( + // 一个工厂,接受 `BlockColors` 和 `BlockState` 并返回 + // 一个 `BlockModel$Unbaked`。 + (colors, state) -> new BlockStateModelWrapper.Unbaked( + // 要获取 `BlockStateModel` 的状态。 + state, + // 模型的色调层。 + colors.getTintSources(state), + // 在提交模型之前应用于 `PoseStack` 的可选变换。 + Optional.empty(new Transformation(new Matrix4f().translation(0.5f, 0.5f, 0.5f))) + ), + // 要使用此模型的方块。将为每个状态循环构造一个。 + EXAMPLE_BLOCK_1 +); + +// 根据某些属性切换的方块模型 +builder.put( + (colors, state) -> new SelectBlockModel.Unbaked( + // 在提交模型之前应用于 `PoseStack` 的可选变换。 + Optional.empty(), + // 一个包含要切换的属性以及当应选择特定方块模型时的值的记录。 + new SelectBlockModel.UnbakedSwitch<>( + // 要切换的 `SelectBlockModelProperty`。属性 + // 值从 `BlockState` 及其 `BlockDisplayContext` 确定。 + (state, displayContext) -> state.getRenderShape(), + // 用于确定要使用哪个 `BlockModel` 的情况列表。 + List.of( + new SelectBlockModel.SwitchCase<>( + // 此模型适用的值列表。 + List.of(RenderShape.INVISIBLE), + // 当满足此属性时要使用的模型。 + new EmptyBlockModel.Unbaked() + ) + ), + // 如果没有开关情况与状态属性匹配,则使用可选的后备。 + Optional.of(new EmptyBlockModel.Unbaked()) + ) + ), + EXAMPLE_BLOCK_2 +); + +// 基于某些条件的方块模型 +builder.put( + (colors, state) -> new ConditionalBlockModel.Unbaked( + // 在提交模型之前应用于 `PoseStack` 的可选变换。 + Optional.empty(), + // 从 `BlockState` 确定 `boolean` 的 `ConditionalBlockModelProperty`。 + BlockState::isSignalSource, + // 当属性返回 `true` 时要显示的模型。 + new EmptyBlockModel.Unbaked(), + // 当属性返回 `false` 时要显示的模型。 + new EmptyBlockModel.Unbaked() + ), + EXAMPLE_BLOCK_3 +); + +// 一个组合方块模型 +builder.put( + (colors, state) -> new CompositeBlockModel.Unbaked( + // 要显示的第一个模型。 + new EmptyBlockModel.Unbaked(), + // 要显示的第二个模型。 + new EmptyBlockModel.Unbaked(), + // 在提交模型之前应用于 `PoseStack` 的可选变换。 + Optional.empty() + ), + EXAMPLE_BLOCK_4 +); + +// 特殊方块模型 +builder.put( + (colors, state) -> new SpecialBlockModelWrapper.Unbaked( + // 用于为模型提交元素的未烘焙 `SpecialModelRenderer`。 + new BellSpecialRenderer.Unbaked(), + // 在提交模型之前应用于 `PoseStack` 的可选变换。 + Optional.empty() + ), + EXAMPLE_BLOCK_5 +); +``` + +在功能提交过程中,方块模型通过 `BlockModelResolver` 和 `BlockModelRenderState` 处理。这类似于其他渲染状态的工作方式。首先,`BlockModelResolver#update` 设置 `BlockModelRenderState`。设置通过基本路径处理——`BlockModelRenderState#setupModel`,将模型部件添加到返回的列表中,然后 `setupTints`,或者通过特殊渲染器的 `setupSpecialModel`。然后,渲染状态通过 `BlockModelRenderState#submit` 提交其元素进行渲染。渲染状态还提供 `submitOnlyOutline`,它使用轮廓渲染类型,以及 `submitWithZOffset`,它使用实体的前向 Z 偏移渲染类型。 + +```java +// BlockEntity 示例 +public class ExampleRenderState extends BlockEntityRenderState { + // 持有渲染状态。 + public final BlockModelRenderState exampleBlock = new BlockModelRenderState(); +} + +public class ExampleRenderer implements BlockEntityRenderer { + + // 用于方块实体渲染器的显示上下文。 + public static final BlockDisplayContext BLOCK_DISPLAY_CONTEXT = BlockDisplayContext.create(); + private final BlockModelResolver blockResolver; + + public ExampleRenderer(BlockEntityRendererProvider.Context ctx) { + super(ctx); + // 获取模型解析器。 + this.blockResolver = ctx.blockModelResolver(); + } + + @Override + public void extractRenderState(ExampleBlockEntity blockEntity, ExampleRenderState state, float partialTick, Vec3 cameraPosition, ModelFeatureRenderer.CrumblingOverlay breakProgress) { + super.extractRenderState(blockEntity, state, partialTick, cameraPosition, breakProgress); + + // 更新模型状态。 + this.blockResolver.update(state.exampleBlock, Blocks.DIRT.defaultBlockState(), BLOCK_DISPLAY_CONTEXT); + } + + @Override + public void submit(ExampleRenderState state, PoseStack pose, SubmitNodeCollector collector, CameraRenderState camera) { + super.submit(state, pose, collector, camera); + + // 提交模型状态进行渲染。 + state.exampleBlock.submit( + // 当前姿势堆栈, + pose, + // 节点收集器。 + collector, + // 光照坐标。 + state.lightCoords, + // 覆盖坐标。 + OverlayTexture.NO_OVERLAY, + // 轮廓颜色。 + 0 + ); + + + } +} + +// Entity 示例 +public class ExampleRenderState extends EntityRenderState { + // 持有渲染状态。 + public final BlockModelRenderState exampleBlock = new BlockModelRenderState(); +} + +public class ExampleRenderer extends EntityRenderer { + + // 用于实体渲染器的显示上下文。 + public static final BlockDisplayContext BLOCK_DISPLAY_CONTEXT = BlockDisplayContext.create(); + private final BlockModelResolver blockResolver; + + public ExampleRenderer(EntityRendererProvider.Context ctx) { + super(ctx); + // 获取模型解析器。 + this.blockResolver = ctx.getBlockModelResolver(); + } + + @Override + public void extractRenderState(ExampleEntity entity, ExampleRenderState state, float partialTick) { + super.extractRenderState(entity, state, partialTick); + + // 更新模型状态。 + this.blockResolver.update(state.exampleBlock, Blocks.DIRT.defaultBlockState(), BLOCK_DISPLAY_CONTEXT); + } + + @Override + public void submit(ExampleRenderState state, PoseStack pose, SubmitNodeCollector collector, CameraRenderState camera) { + super.submit(state, pose, collector, camera); + + // 提交模型状态进行渲染。 + state.exampleBlock.submit( + // 当前姿势堆栈。 + pose, + // 节点收集器。 + collector, + // 光照坐标。 + state.lightCoords, + // 覆盖坐标。 + OverlayTexture.NO_OVERLAY, + // 轮廓颜色。 + state.outlineColor + ); + } +} +``` + +### 方块色调源 + +`BlockColor` 已被完全替换为 `BlockTintSource`,它根据所需的上下文设置特定索引的 ARGB 色调。色调源可以提供三种上下文: + +- `color` 用于通用上下文,由 `BlockModel` 使用 +- `colorInWorld` 用于世界上下文,由 `ModelBlockRenderer#tesselateBlock` 使用 +- `colorAsTerrainParticle` 用于粒子上下文,由下落的灰尘和地形粒子使用 + +此外,如果使用 `BlockState` 的 `Property` 来确定要染色的颜色,`BlockTintSource` 会提供 `relevantProperties`。这被 `LevelRenderer` 用来确定状态更改是否需要重新渲染模型。 + +`BlockTintSource` 仍然通过 `register` 注册到 `BlockColors`,接受一个源列表,后跟可变参数的方块。模型 JSON 中指定的 `tintindex` 用于索引到色调源列表中。 + +```java +// 假设可以访问 BlockColors colors +colors.register( + // 要应用于某个方块模型的色调列表。 + List.of( + // "tintindex": 0 + (state) -> 0xFFFF0000, + // "tintindex": 1 + new BlockTintSource() { + + @Override + public int color(BlockState state) { + return 0xFF00FF00; + } + + @Override + public int colorInWorld(BlockState state, BlockAndTintGetter level, BlockPos pos) { + return 0xFF0000FF; + } + } + ), + // 这些色调源将应用的方块。 + EXAMPLE_BLOCK_1 +); +``` + +### 移除旧的方块和物品渲染器 + +由于 `ItemModel` 和 `BlockModel` 现在完全通过它们自己的功能提交管道处理,`BlockRenderDispatcher` 和 `ItemRenderer` 已被完全移除,被相应的系统取代。 + +### 对象定义变换 + +`ItemModel` 和 `BlockModel` 现在可以接受一个可选的 `Transformation`,它变换模型应如何显示。因此,`$Unbaked#bake` 方法现在接受父级 `Matrix4fc` 变换,变换通过 `Transformation#compose` 乘以此变换。对于物品模型,这被称为独立于模型 JSON 物品变换的局部变换。局部变换总是在物品变换之后应用。 + +请注意,添加对变换的支持应始终通过 `Transformation#compose` 完成,因为传递的矩阵本质上是可变的。假设任何不通过原版接口执行的自定义方法应在执行任何修改之前进行复制。 + +### 四边形实例 + +亮度/色调颜色、光照图和覆盖坐标已被合并到一个单一对象中:`QuadInstance`。这个可变类通过其关联的 `set*` 方法设置值,并可以通过 `get*` 方法为每个四边形顶点提取信息。亮度和色调作为颜色存储在一起,而不是两个单独的值。 + +`QuadInstance` 并不取代所有用例,例如添加单个顶点时。它仅更新 `VertexConsumer` 的方法,将 `putBulkData` 拆分为用于方块模型中四边形的 `putBlockBakedQuad`,以及用于所有其他用途的 `putBakedQuad`。 + +此外,许多用于将 `BakedQuad` 上传到缓冲区的方法现在接受一个 `BlockQuadOutput`。它具有与 `VertexConsumer#putBlockBakedQuad` 相同的参数,并且由于部分渲染器上传到新分配的 `BufferBuilder` 以用于超级缓冲区而添加。 + +### GUI 提取器 + +GUI 类方法经历了一个大规模的重命名方案,表明提交的元素被“提取”到一个通用树中以进行提交然后渲染。因此,以 `draw*` 或 `render*` 开头的方法现在以 `extract*` 为前缀,并可能以 `*RenderState` 为后缀(例如,`Renderable#render` -> `extractRenderState`,`AbstractContainerScreen#renderLabels` -> `extractLabels`,`AbstractWidget#renderWidget` -> `extractWidgetRenderState`)。一些缩写也被扩展,要么通过重命名,要么用另一个方法替换(例如,`AbstractContainerScreen#renderBg` 被 `Screen#extractBackground` 取代)。 + +`GuiGraphics` 也以同样的方式重命名为 `GuiGraphicsExtractor`。这些方法遵循与 GUI 其余部分类似的模式(例如,`hline` -> `horizontalLine`),不同之处在于 `draw*`、`render*` 和 `submit*` 前缀以及 `*RenderState` 后缀被移除(例如,`renderOutline` -> `outline`,`submitEntityRenderState` -> `entity`)。唯一的重命名是 `*String*` 方法名称被替换为 `*Text*`。 + +### 流体模型 + +定义流体的纹理和色调现已从 `FluidRenderer`(以前称为 `LiquidBlockRenderer`)中移出,并移入其自己的 `FluidModel` 记录中。最初,由于流体数量较少,未烘焙变体(`FluidModel$Unbaked`)存储为常量。然后,在 `BlockModel` 烘焙之后,流体模型通过 `FluidStateModelSet#bake` 烘焙,将 `Fluid` 链接到其 `FluidModel`。然后,此映射存储在 `FluidStateModelSet` 中,准备好通过 `get` 查询。 + +一个 `FluidModel$Unbaked` 有四个参数:三个用于静态、流动和可选覆盖纹理的 `Material`;以及一个用于 `BlockTintSource` 的参数。色调通过 `BlockTintSource#colorInWorld` 获得。在烘焙过程中,它将根据提供的材质的透明度确定 `ChunkSectionLayer`。 + +```java +// 由于流体模型系统在其烘焙中被硬编码,此示例 +// 将假设存在某种方法可以修改访问 +// `FluidStateModelSet#bake` 返回的 `Map` fluidModels。 + +// 我们还将假设我们有某个 Fluid EXAMPLE_FLUID* 来附加模型。 + +FluidModel.Unbaked exampleFluidModel = new FluidModel.Unbaked( + // 静态流体的纹理。 + new Material( + // 纹理的相对标识符。 + // 指向 `assets/examplemod/textures/block/example_fluid_still.png` + Identifier.fromNamespaceAndPath("examplemod", "block/example_fluid_still"), + // 当为 true 时,将此纹理键的所有面设置为 + // 始终具有透明像素。 + true + ), + // 流动流体的纹理。 + new Material(Identifier.fromNamespaceAndPath("examplemod", "block/example_fluid_flowing")), + // 如果不为 null,则当流体侧面被 `HalfTransparentBlock` 或 `LeavesBlock` 遮挡时使用的覆盖纹理。 + null, + // 如果不为 null,则在世界中时应用于流体纹理的色调源。 + null +); + +// 假设我们可以访问 `MaterialBaker` materials。 + +FluidModel exampleBakedFluidModel = exampleFluidModel.bake( + // 用于获取材质图集精灵的烘焙器。 + materials, + // 一个提供的调试名称,以正确报告哪些模型 + // 缺少纹理。 + () -> "examplemod:example_fluid_model" +); + +fluidModels.put( + // 模型应使用的流体。 + EXAMPLE_FLUID, + // 烘焙的流体模型。 + exampleBakedFluidModel +); +fluidModels.put(EXAMPLE_FLUID_FLOWING, exampleBakedFluidModel); +``` + +### 名称标签偏移 + +`EntityRenderer#submitNameTag` 已重命名为 `submitNameDisplay`,现在可以选择接受名称标签附件的 y 偏移。 + +### 色调获取器 + +`BlockAndTintGetter` 现在是一个附加到 `ClientLevel` 的客户端专用接口。它以前的用途被 `BlockAndLightGetter` 取代——`BlockAndTintGetter` 现在扩展了它——并剥离了色调和光照方向。 + +### 管道深度和颜色 + +`RenderPipeline` 中定义的深度和颜色方法已被合并到两个状态对象中。 + +`DepthTestFunction`、写入 `boolean` 以及偏置 `float` 现在存储在 `DepthStencilState` 中。`DepthTestFunction` 已被更通用的 `CompareOp` 取代,后者定义了如何比较两个数字。每个函数都有一个简单的等价物,`NO_DEPTH_TEST` 被 `CompareOp#ALWAYS_PASS` 取代。深度信息可以通过 `RenderPipeline$Builder#withDepthStencilState` 添加到管道中。 + +可选的 `BlendFunction` 以及颜色/alpha `boolean` 现在存储在 `ColorTargetState` 中。`boolean` 被合并到一个 `int` 中,使用低四位作为标志:`1` 是红色,`2` 是绿色,`4` 是蓝色,`8` 是 alpha。颜色 `boolean` 使用 `7` 表示红色、绿色和蓝色;alpha `boolean` 使用 `8`;而两者结合使用 `15`。`LopicOp` 已被完全移除。颜色信息可以通过 `RenderPipeline$Builder#withColorTargetState` 添加到管道中。 + +```java +public static final RenderPipeline EXAMPLE_PIPELINE = RenderPipeline.builder() + .withLocation(ResourceLocation.fromNamespaceAndPath("examplemod", "pipeline/example")) + .withVertexShader(ResourceLocation.fromNamespaceAndPath("examplemod", "example")) + .withFragmentShader(ResourceLocation.fromNamespaceAndPath("examplemod", "example")) + .withVertexFormat(DefaultVertexFormat.POSITION_TEX_COLOR, VertexFormat.Mode.QUADS) + .withShaderDefine("ALPHA_CUTOUT", 0.5) + .withSampler("Sampler0") + .withUniform("ModelOffset", UniformType.VEC3) + .withUniform("CustomUniform", UniformType.INT) + .withPolygonMode(PolygonMode.FILL) + .withCull(false) + // 在写入缓冲区数据时设置颜色目标。 + .withColorTargetState(new ColorTargetState( + // 指定将两个带有 alpha 的颜色混合在一起时要使用的函数。 + // 由 `SourceFactor` 和 `DestFactor` 组成。 + // 前两个用于 RGB,后两个用于 alpha。 + // 如果 optional 为空,则禁用混合。 + Optional.of(BlendFunction.TRANSLUCENT), + // 确定将哪些颜色写入缓冲区的掩码。 + // 表示为四位值 + // 0001 - 写入红色通道。 + // 0010 - 写入绿色通道。 + // 0100 - 写入蓝色通道。 + // 1000 - 写入 alpha 通道。 + ColorTargetState.WRITE_RED | ColorTargetState.WRITE_GREEN | ColorTargetState.WRITE_BLUE | ColorTargetState.WRITE_ALPHA + )) + // 在写入缓冲区数据时设置深度模板。 + .withDepthStencilState(new DepthStencilState( + // 设置在离相机不同距离处渲染对象时要使用的深度测试函数。 + // 值: + // - ALWAYS_PASS (GL_ALWAYS) + // - LESS_THAN (GL_LESS) + // - LESS_THAN_OR_EQUAL (GL_LEQUAL) + // - EQUAL (GL_EQUAL) + // - NOT_EQUAL (GL_NOTEQUAL) + // - GREATER_THAN_OR_EQUAL (GL_GEQUAL) + // - GREATER_THAN (GL_GREATER) + // - NEVER_PASS (GL_NEVER) + CompareOp.LESS_THAN_OR_EQUAL, + // 是否屏蔽写入值到深度缓冲区 + false, + // 用于计算多边形深度值的比例因子。 + 0f, + // 用于计算多边形深度值的单位偏移。 + 0f + )) + .build() +; +``` + +### Blaze3d 后端 + +`CommandEncoder`、`GpuDevice` 和 `RenderPassBackend` 已拆分为 `*Backend` 接口,其功能类似于以前的接口,以及包装器类,它持有后端并提供委托调用,执行任何必要的验证。`*Backend` 接口现在显式执行操作,而不检查操作是否有效。 + +### 实体和半透明功能 + +功能渲染已进一步拆分为两个通道:一个用于实体渲染类型,一个用于半透明渲染类型。因此,大多数 `render` 方法现在分别具有 `renderSolid` 和 `renderTranslucent` 方法。那些只渲染实体或半透明数据的方法只具有其中一个方法。 + +### 相机状态 + +`Camera` 已更新,类似于其他渲染状态实现,其中相机在 `GameRenderer#renderLevel` 期间被提取到 `CameraRenderState`,并传递带有提交和渲染元素所需的数据。 + +由于此更改,`FogRenderer#setupFog` 现在返回 `FogData`,其中包含渲染雾所需的所有信息,而不仅仅是其颜色,并将其存储在 `CameraRenderState#fogData` 中。 + +- `assets/minecraft/shaders/core` 中的一些着色器现在使用 `texture` 而不是 `texelFetch` + - `entity.vsh` + - `item.vsh` + - `rendertype_leash.vsh` + - `rendertype_text.vsh` + - `rendertype_text_background.vsh` + - `rendertype_text_intensity.vsh` +- `assets/minecraft/shaders/core` + - `block.vsh#minecraft_sample_lightmap` -> `sample_lightmap.glsl#sample_lightmap` + - `rendertype_crumbling` 不再接受 `texCoord2`(光照图) + - `rendertype_entity_alpha`、`rendertype_entity_decal` 合并到 `entity.fsh` 中,使用 `DissolveMaskSampler` + - `rendertype_item_entity_translucent_cull` -> `item`,不是一对一 + - `rendertype_translucent_moving_block` 已移除 + - 现在使用 `ChunkSectionLayer` 提供的着色器 +- `com.mojang.blaze3d` + - `GLFWErrorCapture` - 在 GL 过程中捕获错误。 + - `GLFWErrorScope` - 一个定义要捕获的 GL 错误范围的可关闭对象。 +- `com.mojang.blaze3d.opengl` + - `GlProgram#BUILT_IN_UNIFORMS`、`INVALID_PROGRAM` 现在是 final + - `GlBackend` - OpenGL 的 GPU 后端。 + - `GlCommandEncoder` 现在实现 `CommandEncoderBackend` 而不是 `CommandEncoder`,该类现在是包私有的 + - `getDevice` 已移除 + - `GlConst#toGl` 现在接受 `CompareOp` 而不是 `DepthTestFunction` + - `GlDevice` 现在实现 `GpuDeviceBackend` 而不是 `GpuDevice`,该类现在是包私有的 + - 构造函数现在接受 `GpuDebugOptions`,其中包含日志级别、是否使用同步日志以及是否使用调试标签,而不是直接传入这些参数 + - `GlRenderPass` 现在实现 `RenderPassBackend` 而不是 `RenderPass`,该类现在是包私有的 + - 构造函数现在接受 `GlDevice` + - `GlStateManager#_colorMask` 现在接受 `int` 作为颜色掩码,而不是四个 `boolean` +- `com.mojang.blaze3d.platform` + - `ClientShutdownWatchdog` 现在接受 `Minecraft` 实例 + - `DebugMemoryUntracker#untrack` 已移除 + - `GLX#make(T, Consumer)` 已移除 + - `NativeImage` + - `computeTransparency` - 返回图像中是否至少有一个透明或半透明像素。 + - 如果图像面积大于 512MiB,或带有颜色数据时大于 2GiB,则会崩溃。 + - `isClosed` - 图像是否已关闭或释放。 + - `Transparency` - 一个关于某个图像是否具有半透明和/或透明像素的对象。 + - `Window` 现在接受 `GpuBackend` 而不是 `ScreenManager` + - `createGlfwWindow` - 直接使用提供的设置创建 GLFW 窗口。 + - `updateDisplay` -> `updateFullscreenIfChanged`,不是一对一 + - `isResized`、`resetIsResized` - 处理窗口是否已调整大小。 + - `backend` - 返回 `GpuBackend`。 + - `$WindowInitFailed` 构造函数现在从 `private` 变为 `public` + - `WindowEventHandler#resizeDisplay` -> `resizeGui` +- `com.mojang.blaze3d.pipeline` + - `ColorTargetState` - 一个包含颜色混合函数和掩码的记录。 + - `DepthStencilState` - 一个包含深度模板数据的记录。 + - `RenderPipeline` 现在接受 `ColorTargetState` 而不是可选的 `BlendFunction`、颜色和 alpha `boolean` 以及 `LogicOp`;以及 `DepthStencilState` 而不是 `DepthTestFunction`、深度 `boolean` 和偏置 `float` + - `getDepthTestFunction`、`isWriteDepth`、`getDepthBiasScaleFactor`、`getDepthBiasConstant` -> `getDepthStencilState`,不是一对一 + - `getColorLogic`、`getBlendFunction`、`isWriteColor`、`isWriteAlpha` -> `getColorTargetState`,不是一对一 + - `$Builder` + - `withDepthTestFunction`、`withDepthWrite`、`withDepthBias` -> `withDepthStencilState`,不是一对一 + - `withBlend`、`withColorWrite`、`withColorLogic` -> `withColorTargetState`,不是一对一 + - `$Snippet` 现在接受 `ColorTargetState` 而不是可选的 `BlendFunction`、颜色和 alpha `boolean` 以及 `LogicOp`;以及 `DepthStencilState` 而不是 `DepthTestFunction` 和深度 `boolean` +- `com.mojang.blaze3d.platform` + - `BackendOptions` - 用于初始化后端的配置。 + - `DepthTestFunction` -> `CompareOp`,不是一对一 + - `NO_DEPTH_TEST` -> `CompareOp#ALWAYS_PASS` + - `EQUAL_DEPTH_TEST` -> `CompareOp#EQUAL` + - `LEQUAL_DEPTH_TEST` -> `CompareOp#LESS_THAN_OR_EQUAL` + - `LESS_DEPTH_TEST` -> `CompareOp#LESS_THAN` + - `GREATER_DEPTH_TEST` -> `CompareOp#GREATER_THAN` + - `GLX` + - `_initGlfw` 现在接受 `BackendOptions` + - `glfwBool` - `true` 为 `1`,`false` 为 `0`。 + - `LogicOp` 枚举已移除 +- `com.mojang.blaze3d.shaders.GpuDebugOptions` - GPU 管线的调试选项。 +- `com.mojang.blaze3d.systems` + - `BackendCreationException` - 当无法创建 GPU 后端时抛出的异常。 + - `CommandEncoder` -> `CommandEncoderBackend` + - 原始接口现在是一个围绕接口的类包装器,在执行验证检查后委托给后端 + - `GpuBackend` - 一个负责创建使用的 GPU 设备和显示窗口的接口。 + - `GpuDevice` -> `GpuDeviceBackend` + - 原始接口现在是一个围绕接口的类包装器,在执行验证检查后委托给后端 + - `setVsync` - 设置是否启用 VSync。 + - `presentFrame` - 交换窗口的前后缓冲区以显示当前帧。 + - `isZZeroToOne` - 是否使用 0 到 1 的 Z 范围而不是 -1 到 1。 + - `RenderPass` -> `RenderPassBackend` + - 原始接口现在是一个围绕接口的类包装器,在执行验证检查后委托给后端 + - `RenderSystem` + - `pollEvents` 现在是公开的 + - `flipFrame` 不再接受 `Window` + - `initRenderer` 现在只接受 `GpuDevice` + - `limitDisplayFPS` -> `FramerateLimiter#limitDisplayFPS` + - `initBackendSystem` 现在接受 `BackendOptions` +- `com.mojang.blaze3d.vertex` + - `DefaultVertexFormat#BLOCK` 不再接受法线向量 + - `PoseStack#mulPose`、`$Pose#mulPose` 现在有一个接受 `Transformation` 的重载 + - `QuadInstance` - 一个包含四边形颜色、光照坐标和覆盖的类。 + - `TlsfAllocator` - 一个用于动态内存分配的两级隔离适配分配器。 + - `UberGpuBuffer` - 一个用于将动态大小的数据上传到 GPU 的缓冲区,用于区块部分层。 + - `VertexConsumer` + - `putBulkData` -> `putBlockBakedQuad`、`putBakedQuad`;不是一对一 + - 亮度 `float` 数组、颜色 `float`、光照图 `int` 数组和覆盖 `int` 被 `QuadInstance` 取代 + - `putBlockBakedQuad` 将 `PoseStack$Pose` 替换为 XYZ `float` 方块位置 + - `addVertex(PoseStack$Pose, Vector3f)` -> `addVertex(PoseStack$Pose, Vector3fc)` + - `setNormal(PoseStack$Pose, Vector3f)` -> `setNormal(PoseStack$Pose, Vector3fc)` +- `com.mojang.math` + - `MatrixUtil` + - `checkPropertyRaw` 现在从 `private` 变为 `public` + - `isOrthonormal` 已移除 + - `Transformation` + - `IDENTITY` 现在从 `private` 变为 `public` + - 取代了 `identity` 方法 + - `getTranslation` -> `translation` + - `getLeftRotation` -> `leftRotation` + - `getScale` -> `scale` + - `getRightRotation` -> `rightRotation` + - `compose` - 如果存在,将变换应用于给定矩阵。 +- `net.minecraft.SharedConstants` + - `DEBUG_DUMP_INTERPOLATED_TEXTURE_FRAMES` 已移除 + - `DEBUG_PREFER_WAYLAND` - 当为 true 时,如果同时支持 Wayland 和 X11,则阻止将平台初始化提示设置为 X11。 +- `net.minecraft.client` + - `Camera` + - `BASE_HUD_FOV` - 基础 HUD 视场角。 + - `setup` -> `update`,不是一对一 + - `extractRenderState` - 提取相机的状态。 + - `getFov` - 获取视场角。 + - `getViewRotationMatrix` - 获取投影视图的矩阵。 + - `setEntity` - 设置相机附加到的实体。 + - `getNearPlane` 现在接受 `float` 视场角 + - `panoramicForwards` - 全景模式下的前向向量。 + - `getPartialTickTime` 已移除 + - `setLevel` - 设置相机所在的等级。 + - `getCameraEntityPartialTicks` - 根据实体的状态获取部分刻。 + - `DeltaTracker#advanceTime` 被 `advanceGameTime`(当 `boolean` 为 `true` 时)和 `advanceRealTime` 取代 + - `advanceGameTime`、`advanceRealTime` 以前是 `private`,现在是 `public` + - `FramerateLimiter` - 一个用于限制客户端帧率的实用程序。 + - `Minecraft` + - `noRender` 已移除 + - `useAmbientOcclusion` 已移除 + - `getBlockRenderer` 已移除 + - `getItemRenderer` 已移除 + - `Options` + - `getCloudsType` -> `getCloudStatus` + - `exclusiveFullscreen` - 当为 `true` 时,全屏模式完全控制显示器。 +- `net.minecraft.client.color.block` + - `BlockColor` 被 `BlockTintSource` 取代,不是一对一 + - `getColor` -> `colorInWorld`, `colorAsTerrainParticle`; 不是一对一 + - `BlockColors` + - `getColor` 被 `getTintSources`、`getTintSource` 取代;不是一对一 + - `register` 现在接受一个 `BlockTintSource` 列表而不是一个 `BlockColor` + - `BlockTintSource` - 一个用于确定如何对 `BlockState` 进行单独或上下文着色的源。 + - `BlockTintSources` - 通用块着色源的实用程序。 +- `net.minecraft.client.data.models` + - `BlockModelGenerators` + - `createSuffixedVariant` 现在接受一个从 `Material` 到 `TextureMapping` 的函数用于纹理,而不仅仅是 `Identifier` + - `createAirLikeBlock` 现在接受一个 `Material` 而不是 `Identifier` 作为粒子纹理 + - `generateSimpleSpecialItemModel` 现在接受一个可选的 `Transformation` + - `createChest` 现在有一个接受 `MutiblockChestResources` 纹理的重载 + - `ItemModelGenerators#generateLayeredItem` 现在接受 `Material` 而不是 `Identifier` 作为纹理 +- `net.minecraft.client.data.models.model` + - `ItemModelUtils` + - `specialModel` 现在有接受 `Transformation` 的重载 + - `conditional` 现在有接受 `Transformation` 的重载 + - `select` 现在有一个接受 `Transformation` 的重载 + - `selectBlockItemProperty` 现在有一个接受 `Transformation` 的重载 + - `TexturedModel#createAllSame` 现在接受一个 `Material` 而不是 `Identifier` 作为纹理 + - `TextureMapping` + - `put`、`putForced` 现在接受一个 `Material` 而不是 `Identifier` 作为纹理 + - `get` 现在返回一个 `Material` 而不是 `Identifier` 作为纹理 + - `copyAndUpdate` 现在接受一个 `Material` 而不是 `Identifier` 作为纹理 + - `updateSlots` - 使用提供的映射函数替换所有槽位。 + - `forceAllTranslucent` - 为所有材质纹理设置强制半透明标志。 + - `defaultTexture`, `cube`, `cross`, `plant`, `rail`, `wool`, `crop`, `singleSlot`, `particle`, `torch`, `cauldron`, `layer0` 现在接受一个 `Material` 而不是 `Identifier` 作为纹理 + - `column`, `door`, `layered` 现在接受 `Material` 而不是 `Identifier` 作为纹理 + - `getBlockTeture`, `getItemTexture` 现在返回一个 `Material` 而不是 `Identifier` 作为纹理 +- `net.minecraft.client.entity.ClientAvatarEntity#belowNameDisplay` -> `Entity#belowNameDisplay` +- `net.minecraft.client.gui` + - `Font` + - `drawInBatch`, `drawInBatch8xOutline` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 作为姿态 + - `$GlyphVisitor#forMultiBufferSource` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 作为姿态 + - `Gui` + - `render*` 方法已重命名为 `extract*` + - `render` -> `extractRenderState` + - `$RenderFunction` 接口已移除 + - `GuiGraphics` -> `GuiGraphicsExtractor` + - `hLine` -> `horizontalLine` + - `vLine` -> `verticalLine` + - `renderOutline` -> `outline` + - `drawCenteredString` -> `centeredText` + - `drawString` -> `text` + - `drawStringWithBackdrop` -> `textWithBackdrop` + - `renderItem` -> `item` + - `renderFakeItem` -> `fakeItem` + - `renderItemDecorations` -> `itemDecorations` + - `submitMapRenderState` -> `map` + - `submitEntityRenderState` -> `entity` + - `submitSkinRenderState` -> `skin` + - `submitBookModelRenderState` -> `book` + - `submitBannerPatternRenderState` -> `bannerPattern` + - `submitSignRenderState` -> `sign` + - `submitProfilerChartRenderState` -> `profilerChart` + - `renderTooltip` -> `tooltip` + - `renderComponentHoverEffect` -> `componentHoverEffect`,现在为 `private` 而不是 `public` +- `net.minecraft.client.gui.components` + - 大多数以 `render*` 或 `draw*` 开头的方法已根据用法重命名为 `extract*` 或 `extract*RenderState` + - `AbstractWidget#renderWidget` -> `extractWidgetRenderState` + - `DebugScreenOverlay#render3dCrosshair` 现在接受 `CameraRenderState` 而不是 `Camera`,以及 GUI 缩放 `int` + - `LogoRenderer#renderLogo` -> `extractRenderState` + - `PlayerFaceRenderer` -> `PlayerFaceExtractor` + - `draw` -> `extractRenderState` + - `Renderable#render` -> `extractRenderState` + - `StringWidget#clipText` -> `ComponentRenderUtils#clipText` + - `TextCursorUtils#draw*` -> `extract*` +- `net.minecraft.client.gui.components.debugchart` + - `AbstractDebugChart` + - `drawChart` -> `extractRenderState` + - `drawDimensions` -> `extractSampleBars` + - `drawMainDimension` -> `extractMainSampleBar` + - `drawAdditionalDimensions` -> `extractAdditionalSampleBars` + - `renderAdditionalLinesAndLabels` -> `extractAdditionalLinesAndLabels` + - `drawStringWithShade` -> `extractStringWithShade` + - `ProfilerPieChart#render` -> `extractRenderState` +- `net.minecraft.client.gui.components.spectator.SpectatorGui#render*` -> `extract*` +- `net.minecraft.client.gui.components.toasts` + - `NowPlayingToast#renderToast` -> `extractToast` + - `Toast#render` -> `extractRenderState` + - `ToastManager`, `$ToastInstance#render` -> `extractRenderState` + - `TutorialToast$Icons#render` -> `extractRenderState` +- `net.minecraft.client.gui.contextualbar.ContextualBarRenderer` + - `renderBackground` -> `extractBackground` + - `render` -> `extractRenderState` + - `renderExperienceLevel` -> `extractExperienceLevel` +- `net.minecraft.client.gui.font` + - `PlainTextRenderable#renderSprite` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 作为姿态 + - `TextRenderable#render` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 作为姿态 +- `net.minecraft.client.gui.render` + - `DynamicAtlasAllocator` - 一个用于处理动态大小纹理图集的分配器。 + - `GuiItemAtlas` - 一个用于用户界面中显示的所有物品的图集。 + - `GuiRenderer#incrementFrameNumber` -> `endFrame`,不是一对一 +- `net.minecraft.client.gui.render.state.*` -> `.client.rendererer.state.gui.*` + - `GuiItemRenderState` 不再接受 `String` 名称 + - `name` 已移除 +- `net.minecraft.client.gui.render.state.pip.*` -> `.client.rendererer.state.gui.pip.*` +- `net.minecraft.client.gui.screens` + - `LevelLoadingScreen#renderChunks` -> `extractChunksForRendering` + - `Screen` + - `renderWithTooltipAndSubtitles` -> `extractRenderStateWithTooltipAndSubtitles` + - `renderBackground` -> `extractBackground` + - `renderBlurredBackground` -> `extractBlurredBackground` + - `renderPanorama` -> `extractPanorama` + - `renderMenuBackground` -> `extractMenuBackground` + - `renderMenuBackgroundTexture` -> `extractMenuBackgroundTexture` + - `renderTransparentBackground` -> `extractTransparentBackground` +- `net.minecraft.client.gui.screens.advancements` + - `AdvancementTab#draw*` -> `extract*` + - `AdvancementTabType` + - `draw` -> `extractRenderState` + - `drawIcon` -> `extractIcon` + - `AdvancementWidget` + - `draw` -> `extractRenderState` + - `draw*` -> `extract*` +- `net.minecraft.client.gui.screens.inventory` + - 大多数以 `render*` 或 `draw*` 开头的方法已根据用法重命名为 `extract*` 或 `extract*RenderState` + - `AbstractContainerScreen` + - `renderContents` -> `extractContents` + - `renderCarriedItem` -> `extractCarriedItem` + - `renderSnapbackItem` -> `extractSnapbackItem` + - `renderSlots` -> `extractSlots` + - `renderTooltip` -> `extractTooltip` + - `renderLabels` -> `extractLabels` + - `renderBg` 被 `Screen#extractBackground` 取代 + - `renderSlot` -> `extractSlot` + - `AbstractMountInventoryScreen#drawSlot` -> `extractSlot` + - `AbstractSignEditScreen#renderSignBackground` -> `extractSignBackground` + - `CyclingSlotBackground#render` -> `extractRenderState` + - `EffectsInInventory#render` -> `extractRenderState` + - `InventoryScreen#renderEntityInInventoryFollowsMouse` -> `extractEntityInInventoryFollowsMouse` + - `ItemCombinerScreen#renderErrorIcon` -> `extractErrorIcon` +- `net.minecraft.client.gui.screens.inventory.tooltip` + - `ClientTooltipComponent` + - `renderText` -> `extractText` + - `renderImage` -> `extractImage` + - `TooltipRenderUtil#renderTooltipBackground` -> `extractTooltipBackground` +- `net.minecraft.client.gui.screens.options` + - `DifficultyButtons` 现在是一个记录,接受 `LayoutElement`、用于难度的 `CycleButton`、`LockIconButton` 和当前 `Level` + - `create` 现在接受 `Level` 并返回 `DifficultyButtons` 而不是 `LayoutElement` + - `refresh` - 设置持有的按钮组件的数据。 + - `HasDifficultyReaction` - 一个响应难度更改的接口。 + - `OptionsScreen` 现在实现 `HasDifficultyReaction` + - `WorldOptionsScreen` 现在实现 `HasDifficultyReaction` + - 构造函数现在接受 `Level` +- `net.minecraft.client.gui.screens.recipebook` + - `GhostSlots` + - `render` -> `extractRenderState` + - `renderTooltip` -> `extractTooltip` + - `RecipeBookComponent#render*` -> `extract*` + - `RecipeBookPage` + - `render` -> `extractRenderState` + - `renderTooltip` -> `extractTooltip` +- `net.minecraft.cilent.gui.screens.reporting.ChatSelectionScreen$ChatSelectionList#renderItem` -> `extractItem` +- `net.minecraft.client.gui.screens.worldselection.AbstractGameRulesScreen$GameRuleEntry#renderLabel` -> `extractLabel` +- `net.minecraft.client.gui.spectator.SpectatorMenuItem#renderIcon` -> `extractIcon` +- `net.minecraft.client.model.Model#renderType` 现在有一个返回传入函数的重载 +- `net.minecraft.client.model.object.book.BookModel$State` 不再接受动画位置,并将开放的 `float` 移到第一个参数 + - `forAnimation` - 根据进度获取书本动画的当前状态。 +- `net.minecraft.client.model.object.statue.CopperGolemStatueModel` 现在使用 `Unit` 作为泛型而不是 `Direction` +- `net.minecraft.client.multiplayer.ClientLevel` 现在实现 `BlockAndTintGetter` + - `update` - 更新关卡的光照。 +- `net.minecraft.client.multiplayer.chat.GuiMessageTag$Icon#draw` -> `extractRenderState` +- `net.minecraft.client.particle` + - `Particle#getLightColor` -> `getLightCoords` + - `SimpleVerticalParticle` - 一个垂直移动的粒子。 + - `SingleQuadParticle$Layer` + - `TERRAIN` -> `OPAQUE_TERRAIN`, `TRANSLUCENT_TERRAIN` + - `ITEMS` -> `OPAQUE_ITEMS`, `TRANSLUCENT_ITEMS` + - `bySprite` - 从图集精灵获取层。 +- `net.minecraft.client.renderer` + - `CachedOrthoProjectionMatrixBuffer`, `CachedPerspectiveProjectionMatrixBuffer`, `PerspectiveProjectionMatrixBuffer` -> `ProjectionMatrixBuffer`,有时带有 `Projection`,不是一对一 + - `CloudRenderer` 现在接受云范围 `int` + - `CubeMap` 不再接受 `Minecraft` 实例 + - `GameRenderer` 现在接受 `ModelManager` 而不是 `BlockRenderDispatcher` + - `PROJECTION_Z_NEAR` -> `Camera#PROJECTION_Z_NEAR` + - `setPanoramicScreenshotParameters`, `getPanoramicScreenshotParameters` -> `Camera#enablePanoramicMode`, `disablePanoramicMode`; 不是一对一 + - `isPanoramicMode` -> `Camera#isPanoramicMode` + - `getProjectionMatrix` -> `Camera#getViewRotationProjectionMatrix`,不是一对一 + - `updateCamera` -> `Camera#update`,不是一对一 + - `getRenderDistance` 已移除 + - `cubeMap` -> `GuiRenderer#cubeMap`,现在为 `private` 而不是 `protected` + - `getDarkenWorldAmount` -> `getBossOverlayWorldDarkening` + - `lightTexture` -> `lightmap`, `levelLightmap`; 不是一对一 + - `getLevelRenderState` 被 `getGameRenderState` 取代,返回 `GameRenderState` 而不是 `LevelRenderState` + - `pick` -> `Minecraft#pick`,现在为 `private` 而不是 `public` + - `render` 拆分为 `update`、`extract` 和 `render`;`boolean` 现在表示是否推进游戏时间而不是渲染关卡 + - `GlobalSettingsUniform` 现在接受 `Vec3` 相机位置而不是主 `Camera` 本身 + - `ItemBlockRenderTypes` 已移除 + - `getChunkRenderType`、`getMovingBlockRenderType` 现在存储在 `BakedQuad$SpriteInfo` 中 + - `getRenderLayer(FluidState)` -> `FluidModel#layer`,不是一对一 + - `setCutoutLeaves` 已移除 + - 这应直接从选项中获取 + - `MultiblockChestResources` - 一个包含基于 `ChestType` 的一些数据的记录。 + - `LevelRenderer` 现在接受 `GameRenderState` 而不是 `LevelRenderState` + - `update` - 更新关卡。 + - `renderLevel` 现在接受 `CameraRenderState` 而不是 `Camera`,一个 `Matrix4fc` 而不是 `Matrix4f` 用于模型视图,以及 `ChunkSectionsToRender`;不再接受用于投影矩阵的 `Matrix3f` + - `extractLevel` - 提取关卡状态。 + - `prepareChunkRenders` 现在是 `public` 而不是 `private` + - `captureFrustum`, `killFrustum`, `getCapturedFrustum` 已移除 + - `getLightColor` -> `getLightCoords`,现在接受 `BlockAndLightGetter` 而不是 `BlockAndTintGetter` + - `$BrightnessGetter#packedBrightness` 现在接受 `BlockAndLightGetter` 而不是 `BlockAndTintGetter` + - `LightTexture` -> `Lightmap` + - `tick` -> `LightmapRenderStateExtractor#tick` + - `updateLightTexture` -> `render` + - `pack` -> `LightCoordsUtil#pack` + - `block` -> `LightCoordsUtil#block` + - `sky` -> `LightCoordsUtil#sky` + - `lightCoordsWithEmission` -> `LightCoordsUtil#lightCoordsWithEmission` + - `MaterialMapper` -> `SpriteMapper` + - `OrderedSubmitNodeCollector` + - `submitBlock` 已移除 + - `submitBlockModel` 现在接受一个 `BlockStateModelPart` 列表而不是 `BlockStateModel`,以及一个 `int` 数组(色调颜色数组)而不是三个 `float` 用于单一颜色 + - `submitItem` 不再接受 `RenderType` + - `submitModel` 现在有接受纹理的 `Identifier` 或带有 `SpriteGetter` 的 `SpriteId` 以及一个 `int` 色调颜色的重载 + - `submitBreakingBlockModel` - 提交方块破坏覆盖层。 + - `PanoramaRenderer` 被 `Panorama` 取代 + - `registerTextures` -> `GuiRenderer#registerPanoramaTextures` + - `render` -> `extractRenderState` + - `PanoramicScreenshotParameters` 记录已移除 + - `PostChain` 现在接受一个 `Projection` 和 `ProjectionMatrixBuffer` 而不是 `CachedOrthoProjectionMatrixBuffer` + - `load` 现在接受一个 `Projection` 和 `ProjectionMatrixBuffer` 而不是 `CachedOrthoProjectionMatrixBuffer` + - `RenderPipelines` + - `ENTITY_CUTOUT_NO_CULL` -> `ENTITY_CUTOUT` + - 原来的带剔除的 cutout 被 `ENTITY_CUTOUT_CULL` 取代 + - `ENTITY_CUTOUT_NO_CULL_Z_OFFSET` -> `ENTITY_CUTOUT_Z_OFFSET` + - `ENTITY_SMOOTH_CUTOUT` -> `END_CRYSTAL_BEAM` + - `ENTITY_NO_OUTLINE` 被 `ENTITY_TRANSLUCENT` 取代,渲染类型构造时 affects outline 为 false + - `ENTITY_DECAL`, `DRAGON_EXPLOSION_ALPHA` -> `ENTITY_CUTOUT_DISSOLVE`,不是一对一 + - `ITEM_ENTITY_TRANSLUCENT_CULL` -> `ENTITY_TRANSLUCENT_CULL`, `ITEM_CUTOUT`, `ITEM_TRANSLUCENT`; 不是一对一 + - `TRANSLUCENT_MOVING_BLOCK` 被 `TRANSLUCENT_BLOCK` 取代 + - `BANNER_PATTERN` - 用于渲染旗帜图案的管线。 + - `ScreenEffectRenderer#renderScreenEffect` 现在接受 `boolean` 表示玩家是否处于第一人称以及是否隐藏 GUI + - `Sheets` + - `*CHEST_*LOCATION*` 已根据资源用途合并到 `CHEST_*` 字段之一 + - `translucentBlockSheet` - 使用方块图集的可剔除实体物品半透明渲染类型。 + - `cutoutBlockItemSheet` - 使用方块图集的物品 cutout 渲染类型。 + - `bannerSheet` -> `RenderTypes#entityTranslucent`,不是一对一 + - `cutoutItemSheet` - 使用物品图集的物品 cutout 渲染类型。 + - `get*Material` -> `get*Sprite` + - `chooseMaterial` -> `chooseSprite` + - `SpecialBlockModelRenderer` 被 `BuiltInBlockModels` 取代,不是一对一 + - `renderByBlock` -> `BlockModelRenderState#submit*`,不是一对一 + - `SubmitNodeCollection` + - `getBlockSubmits` 已移除 + - `getBreakingBlockModelSubmits` - 获取已提交的方块破坏覆盖层。 + - `SubmitNodeCollector$ParticleGroupRenderer` + - `isEmpty` - 此组中是否没有要渲染的粒子。 + - `prepare` 现在接受是否为半透明层准备粒子 + - `render` 不再接受半透明 `boolean` + - `SubmitNodeStorage` + - `$BlockModelSubmit` 现在接受一个 `BlockStateModelPart` 列表而不是 `BlockStateModel`,以及一个 `int` 数组(色调颜色数组)而不是三个 `float` 用于单一颜色 + - `$BlockSubmit` 已移除 + - `$BreakingBlockModelSubmit` - 一个包含渲染方块破坏覆盖层信息的记录。 + - `$ItemSubmit` 不再接受 `RenderType` + - `$MovingBlockSubmit`, `$NameTagSubmit`, `$ShadowSubmit`, `$TextSubmit` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 作为姿态 + - `VirtualScreen` 被 `GpuBackend` 取代 +- `net.minecraft.client.renderer.block` + - `BlockAndTintGetter` - 一个用于位置方块着色(例如生物群系)的获取器。 + - `BlockModelRenderState` - 方块模型的渲染状态。 + - `BlockModelResolver` - 一个用于设置 `BlockState` 渲染状态的辅助类。 + - `BlockModelSet` - 持有与每个 `BlockState` 关联的 `BlockModel`。 + - `BlockModelShaper` -> `BlockStateModelSet`,不是一对一 + - `getParticleIcon` -> `getParticleMaterial`,现在返回 `Material$Baked` 而不是 `TextureAtlasSprite` + - `getBlockModel` -> `get` + - `getModelManager`, `replaceCache` 已移除 + - `BlockQuadOutput` - 一个用于将烘焙四边形信息写入某些输出(如缓冲区)的函数接口。 + - `BlockRenderDispatcher` 类已移除 + - `getBlockModelShaper` -> `getModelSet`,不是一对一 + - `renderBreakingTexture` 被 `SubmitNodeCollector#submitBreakingBlockModel` 取代 + - `renderBatched` 被直接调用 `ModelBlockRenderer#tesselateBlock` 取代 + - `renderLiquid` 被直接调用 `FluidRenderer#tesselate` 取代 + - `renderSingleBlock` 现在内联在 `BlockFeatureRenderer#renderBlockModelSubmits` 中,一个 `private` 方法 + - 使用 `ModelBlockRenderer#tesselateBlock` 作为替代 + - `FluidModel` - 持有渲染器数据的基础流体模型。 + - `FluidStateModelSet` - 持有与每个 `Fluid` 关联的 `FluidModel`。 + - `LoadedBlockModels` - 一个为每个 `BlockState` 烘焙 `BlockModel` 的任务。 + - `LiquidBlockRenderer` -> `FluidRenderer`,不是一对一 + - 构造函数现在接受 `FluidStateModelSet` 而不是 `SpriteGetter` + - `tesselate` 现在接受一个 `FluidRenderer$Output` 而不是 `VertexConsumer` + - `$Output` - 获取用于 `ChunkSectionLayer` 的 `VertexConsumer`。 + - `ModelBlockRenderer` 现在接受 `boolean` 用于环境光遮蔽和剔除 + - `tesselateBlock` 现在接受一个 `BlockQuadOutput` 而不是 `VertexConsumer`,XYZ `float` 而不是 `PoseStack`,`BlockStateModel` 而不是 `BlockModelPart` 列表,不再接受剔除 `boolean` 和 `int` 覆盖,并接受种子 `long` + - `tesselateWithAO` -> `tesselateAmbientOcclusion`,现在是 `private` 而不是 `public` + - `tesselateWithoutAO` -> `tesselateFlat`,现在是 `private` 而不是 `public` + - `renderModel` 现在内联在 `BlockFeatureRenderer#renderBlockModelSubmits` 中,一个 `private` 方法 + - `forceOpaque` - 方块纹理是否应为不透明而不是半透明。 + - `enableCaching` -> `BlockModelLighter$Cache#enable` + - `clearCache` -> `BlockModelLighter$Cache#disable` + - `$AdjacencyInfo` -> `BlockModelLighter$AdjacencyInfo`,现在是 `private` 而不是 `protected` + - `$AmbientOcclusionRenderStorage` 被 `BlockModelLighter` 取代,不是一对一 + - `$AmbientVertexRemap` -> `BlockModelLighter$AmbientVertexRemap` + - `$Cache` -> `BlockModelLighter$Cache` + - `$CommonRenderStorage` 被 `BlockModelLighter` 取代,不是一对一 + - `$SizeInfo` -> `BlockModelLighter$SizeInfo` + - `MovingBlockRenderState#level` -> `cardinalLighting`, `lightEngine`; 不是一对一 + - `SelectBlockModel` - 一个由其解析属性确定或选择的方块模型。 +- `net.minecraft.client.renderer.block.model` + - `BakedQuad` -> `.client.resources.model.geometry.BakedQuad` + - 构造函数现在接受一个 `$MaterialInfo` 而不是 `TextureAtlasSprite`、`int` 色调索引、`int` 光发射和 `boolean` 阴影 + - `FLAG_TRANSLUCENT` - 一个标志,标记烘焙四边形具有半透明性。 + - `FLAG_ANIMATED` - 一个标志,标记烘焙四边形具有动画纹理。 + - `isTinted` -> `$MaterialInfo#isTinted` + - `$MaterialFlags` - 一个注解,标记给定的整数是否定义材质的标志。 + - `$MaterialInfo` - 一个持有如何渲染四边形信息的记录。 + - `BlockDisplayContext` - 一个表示方块显示上下文的对象。 + - `BlockElement` -> `.client.resources.model.cuboid.CuboidModelElement` + - `BlockElementFace` -> `.client.resources.model.cuboid.CuboidFace` + - `BlockElementRotation` -> `.client.resources.model.cuboid.CuboidRotation` + - `BlockModel` - 基础方块模型,用于在世界上下文之外更新渲染状态。 + - 原始实现已移至 `.client.resources.model.cuboid.CuboidModel` + - `BlockModelDefinition` -> `.block.dispatch.BlockStateModelDispatcher` + - `BlockModelPart` -> `.block.dispatch.BlockStateModelPart` + - `particleIcon` -> `particleMaterial`,现在返回 `Material$Baked` 而不是 `TextureAtlasSprite` + - `materialFlags` - 处理模型使用的材质的标志。 + - `BlockStateModel` -> `.block.dispatch.BlockStateModel` + - `particleIcon` -> `particleMaterial`,现在返回 `Material$Baked` 而不是 `TextureAtlasSprite` + - `materialFlags`, `hasMaterialFlag` - 处理模型使用的材质的标志。 + - `BlockStateModelWrapper` - 包含模型、色调和变换的基础方块模型。 + - `CompositeBlockModel` - 将多个方块模型叠加在一起。 + - `ConditionalBlockModel` - 一个根据从某个属性获取的布尔值显示不同模型的方块模型。 + - `EmptyBlockModel` - 不显示任何内容的方块模型。 + - `FaceBakery` -> `.client.resources.model.cuboid.FaceBakery` + - `bakeQuad` 重载现在接受一个 `ModelBaker` 而不是 `ModelBaker$PartCache`,以及一个 `Material$Baked` 而不是 `TextureAtlasSprite` + - 它还有一个接受 `BlockElementFace` 字段而不是对象本身的重载 + - 另一个 `bakedQuad` 重载现在接受 `BakedQuad$MaterialInfo` 而不是 `int` 色调索引和光发射,以及阴影 `boolean` + - `ItemModelGenerator` -> `.client.resources.model.cuboid.ItemModelGenerator` + - `ItemTransform` -> `.client.resources.model.cuboid.ItemTransform` + - `ItemTransforms` -> `.client.resources.model.cuboid.ItemTransforms` + - `SimpleModelWrapper` -> `.client.resources.model.SimpleModelWrapper` + - 构造函数现在接受一个 `Material$Baked` 而不是 `TextureAtlasSprite` 作为粒子 + - `SimpleUnbakedGeometry` -> `.client.resources.model.cuboid.UnbakedCuboidGeometry` + - `SingleVariant` -> `.block.dispatch.SingleVariant` + - `SpecialBlockModelWrapper` - 一个用于通过 `SpecialModelRenderer` 提交其元素的方块模型。 + - `TextureSlots` -> `.client.resources.model.sprite.TextureSlots` + - `Variant` -> `.block.dispatch.Variant` + - `VariantMutator` -> `.block.dispatch.VariantMutator` + - `VariantSelector` -> `.block.dispatch.VariantSelector` +- `net.minecraft.client.renderer.block.model.multipart.*` -> `.block.dispatch.multipart.*` +- `net.minecraft.client.renderer.block.model.properties.conditional` + - `ConditionalBlockModelProperty` - 一个从 `BlockState` 计算某个 `boolean` 的属性。 + - `IsXmas` - 返回当前时间是否在 12 月 24 日至 26 日之间。 +- `net.minecraft.client.renderer.block.model.properties.select` + - `DisplayContext` - 基于当前 `BlockDisplayContext` 的情况。 + - `SelectBlockModelProperty` - 一个从 `BlockState` 计算某个开关状态的属性。 +- `net.minecraft.client.renderer.blockentity` + - `AbstractEndPortalRenderer` + - `renderCube` -> `submitCube`,现在为 `protected` 和 `static` 而不是 `private` + - `submitSpecial` - 提交末地传送门立方体,由特殊渲染器使用。 + - `getExtents` - 获取每个面的顶点。 + - `getOffsetUp`, `getOffsetDown` 已移除 + - `renderType` 已移除 + - `AbstractSignRenderer` 现在接受一个泛型用于 `SignRenderState` + - `getSignModel` 现在接受 `SignRenderState` 泛型而不是 `BlockState` 和 `WoodType` + - `getSignModelRenderScale`, `getSignTextRenderScale`, `getTextOffset`, `translateSign` 被 `SignRenderState#transformations`、`SignRenderState$SignTransformations` 取代 + - `getSignMaterial` -> `getSignSprite` + - `BannerRenderer` + - `TRANSFORMATIONS` - 当在墙上或地上时要应用的变换。 + - `submitPatterns` 不再接受基础 `SpriteId`、图案是否有箔以及轮廓颜色 + - `submitSpecial` 现在接受 `BannerBlock$AttachmentType` + - `BedRenderer` + - `submitSpecial` 已移除 + - 这被调用两次 `submitPiece` 或通过 `BedSpecialRenderer` 为每个床部件制作组合体所取代 + - `submitPiece` 现在是 `public` 而不是 `private`,接受 `BedPart` 而不是 `Model$Simple`、`Direction` 或是否在 Z 方向平移的 `boolean` + - `getExtents` 现在接受 `BedPart` + - `modelTransform` - 获取给定 `Direction` 的变换。 + - `BlockEntityRenderDispatcher` 现在接受 `BlockModelResolver` 而不是 `BlockRenderDispatcher`,并且不再接受 `ItemRenderer` + - `prepare` 现在接受一个 `Vec3` 相机位置而不是 `Camera` 本身 + - `BlockEntityRendererProvider$Context` 现在接受 `BlockModelResolver` 而不是 `BlockRenderDispatcher`,并且不再接受 `ItemRenderer` + - `materials` -> `sprites` + - `ChestRenderer` + - `LAYERS` - 持有箱子的模型层。 + - `modelTransformation` - 获取给定 `Direction` 的变换。 + - `ConduitRenderer#DEFAULT_TRANSFORMATION` - 要应用的默认变换。 + - `CopperGolemStatueBlockRenderer#modelTransformation` - 获取给定 `Direction` 的变换。 + - `DecoratedPotRenderer#modelTransformation` - 获取给定 `Direction` 的变换。 + - `HangingSignRenderer` 现在使用 `HangingSignRenderState` + - `MODEL_RENDER_SCALE` 现在是 `private` 而不是 `public` + - `TRANSFORMATIONS` - 当在墙上或地上时要应用的变换。 + - `translateBase` -> `baseTransformation`,现在是 `private` 而不是 `public` + - `$AttachmentType` -> `HangingSignBlock$Attachment` + - `byBlockState` -> `$Models#get` + - `$ModelKey` 记录已移除 + - `ShulkerBoxRenderer` + - `modelTransform` - 获取给定 `Direction` 的变换。 + - `getExtents` 不再接受 `Direction` + - `SignRenderer` -> `StandingSignRenderer` + - `TRANSFORMATIONS` - 当在墙上或地上时要应用的变换。 + - `createSignModel` 现在接受一个 `PlainSignBlock$Attachment` 而不是一个表示方块是否站立的 `boolean` + - `SkullBlockRenderer` + - `TRANSFORMATIONS` - 当在墙上或地上时要应用的变换。 + - `submitSkull` 不再接受 `Direction` 或 `float` 旋转 + - `WallAndGroundTransformations` - 一个持有 `Direction` 到墙上变换映射的类,以及一个计算地面变换的 `int` 函数,其中 `int` 段通常表示旋转状态的数量。 +- `net.minecraft.client.renderer.blockentity.state` + - `BannerRenderState` + - `angle` -> `transformation`,不是一对一 + - `standing` -> `attachmentType`,不是一对一 + - `BedRenderState#isHead` -> `part`,不是一对一 + - `BlockEntityRenderState#blockState` 现在是 `private` 而不是 `public` + - `ChestRenderState#angle` -> `facing`,不是一对一 + - `CondiutRenderState` -> `ConduitRenderState` + - `CopperGolemStatueRenderState#oxidationState` - 当前的氧化状态。 + - `HangingSignRenderState` - 悬挂标志的渲染状态。 + - `ShelfRenderState#facing` - 架子面向的方向。 + - `SignRenderState#woodType` - 标志的木材类型。 + - `SkullblockRenderState#direction`, `rotationDegrees` -> `transformation`,不是一对一 + - `StandingSignRenderState` - 站立标志的渲染状态。 +- `net.minecraft.client.renderer.chunk` + - `ChunkSectionLayer` 现在接受一个 `boolean` 表示层是否为半透明,而不是仅在上传时排序 + - `byTransparency` - 根据其透明度设置获取层。 + - `sortOnUpload` -> `translucent`,不是一对一 + - `vertexFormat` - 层使用的管线的顶点格式。 + - `ChunkSectionsToRender#drawsPerLayer` -> `drawGroupsPerLayer`,其值为 `int` 到绘图列表的映射 + - `CompiledSectionMesh` + - `uploadMeshLayer` 被 `isVertexBufferUploaded`、`setVertexBufferUploaded` 取代 + - `uploadLayerIndexBuffer` 被 `isIndexBufferUploaded`、`setIndexBufferUploaded` 取代 + - `RenderRegionCache#createRegion` 现在接受 `ClientLevel` 而不是 `Level` + - `SectionBuffers` -> `SectionRenderDispatcher$RenderSectionBufferSlice`,不是一对一 + - `SectionCompiler` 现在接受 `boolean` 用于环境光遮蔽和 cutout 树叶、`BlockStateModelSet`、`FluidStateModelSet` 和 `BlockColors` 而不是 `BlockRenderDispatcher` + - `SectionMesh` + - `getBuffers` -> `getSectionDraw`,不是一对一 + - `$SectionDraw` - 部分的绘图信息。 + - `SectionRenderDispatcher` 现在接受 `SectionCompiler` 而不是 `BlockRenderDispatcher` 和 `BlockEntityRenderDispatcher` + - `getRenderSectionSlice` - 获取要为块层渲染的部分网格的缓冲区切片。 + - `uploadAllPendingUploads` -> `uploadGlobalGeomBuffersToGPU`,不是一对一 + - `lock`, `unlock` - 处理在将数据从一个位置复制到另一个位置时锁定调度程序,通常用于 GPU 分配。 + - `getToUpload` 已移除 + - `setLevel` 现在接受 `SectionCompiler` + - `$RenderSection` + - `upload`, `uploadSectionIndexBuffer` -> `addSectionBuffersToUberBuffer`,现在是 private + - `$CompileTask` + - `doTask` 现在返回一个 `$SectionTaskResult` 而不是 `CompletableFuture` + - `$SectionTaskResult` -> `$RenderSection$CompileTask$SectionTaskResult` +- `net.minecraft.client.renderer.culling.Frustum` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 用于模型视图 + - `set` - 从另一个视景体复制信息。 +- `net.minecraft.client.renderer.entity` + - `AbstractBoatRenderer` 现在接受 `Identifier` 纹理 + - `renderType` 已移除 + - `AbstractMinecartRenderer` + - `BLOCK_DISPLAY_CONTEXT` - 显示矿车内方块的上下文。 + - `submitMinecartContents` 现在接受一个 `BlockModelRenderState` 而不是 `BlockState` + - `CopperGolemRenderer#BLOCK_DISPLAY_CONTEXT` - 显示天线方块的上下文。 + - `DisplayRenderer` + - `BLOCK_DISPLAY_CONTEXT` - 显示所显示方块的上下文。 + - `blockModelResolver` - 方块模型解析器。 + - `EndermanRenderer#BLOCK_DISPLAY_CONTEXT` - 显示手持方块的上下文。 + - `EntityRenderDispatcher` 现在接受 `BlockModelResolver` 而不是 `BlockRenderDispatcher` + - `EntityRenderer#submitNameTag` -> `submitNameDisplay`,现在可选地接受 Y 位置 `int` 作为名称标签附件的偏移量 + - `EntityRendererProvider$Context` 现在接受 `BlockModelResolver` 而不是 `BlockRenderDispatcher` + - `getMaterials` -> `getSprites` + - `getBlockRenderDispatcher` 被 `getBlockModelResolver` 取代 + - `IronGolemRenderer#BLOCK_DISPLAY_CONTEXT` - 显示手持方块的上下文。 + - `ItemFrameRenderer#BLOCK_DISPLAY_CONTEXT` - 显示所显示方块的上下文。 + - `ItemRenderer` 类已移除 + - 使用 `ItemStackRenderState` 将元素提交给功能调度程序 + - `ENCHANTED_GLINT_ARMOR` -> `ItemFeatureRenderer#ENCHANTED_GLINT_ARMOR` + - `ENCHANTED_GLINT_ITEM` -> `ItemFeatureRenderer#ENCHANTED_GLINT_ITEM` + - `NO_TINT` -> `ItemFeatureRenderer#NO_TINT` + - `getFoilBuffer` -> `ItemFeatureRenderer#getFoilBuffer` + - `getFoilRenderType` -> `ItemFeatureRenderer#getFoilRenderType`,现在是 `public` 而不是 `private` + - `MushroomCowRenderer#BLOCK_DISPLAY_CONTEXT` - 显示附加方块的上下文。 + - `SnowGolemRenderer#BLOCK_DISPLAY_CONTEXT` - 显示头部方块的上下文。 + - `TntRenderer#BLOCK_DISPLAY_CONTEXT` - 显示 TNT 方块的上下文。 + - `TntMinecartRenderer#submitWhiteSolidBlock` 现在接受一个 `BlockModelRenderState` 而不是 `BlockState` +- `net.minecraft.client.renderer.entity.layers` + - `BlockDecorationLayer` 现在接受一个返回 `BlockModelRenderState` 的函数而不是可选的 `BlockState` + - `MushroomCowMushroomLayer` 不再接受 `BlockRenderDispatcher` + - `SnowGolemHeadLayer` 不再接受 `BlockRenderDispatcher` +- `net.minecraft.client.renderer.entity.state` + - `AvatarRenderState#scoreText` -> `EntityRenderState#scoreText` + - `BlockDisplayEntityRenderState#blockRenderState` -> `blockModel`,现在是 `BlockModelRenderState` 而不是 `BlockRenderState` + - `CopperGolemRenderState#blockOnAntenna` 现在是 `BlockModelRenderState` 而不是可选的 `BlockState` + - `EndermanRenderState#carriedBlock` 现在是 `BlockModelRenderState` 而不是可空的 `BlockState` + - `IronGolemRenderState#flowerBlock` - 铁傀儡手持的花。 + - `ItemFrameRenderState#frameModel` - 物品框方块的模型。 + - `MinecartRenderState#displayBlockState` -> `displayBlockModel`,现在是 `BlockModelRenderState` 而不是 `BlockState` + - `MushroomCowRenderState#mushroomModel` - 附着在牛上的蘑菇模型。 + - `SnowGolemRenderState#hasPumpkin` -> `headBlock`,现在是 `BlockModelRenderState` 而不是 `boolean` + - `TntRenderState#blockState` 现在是 `BlockModelRenderState` 而不是可空的 `BlockState` +- `net.minecraft.client.renderer.features` + - 功能 `render` 方法已拆分为 `renderSolid`(用于不透明渲染类型)和 `renderTranslucent`(用于半透明渲染类型) + - 一些 `render*` 方法现在接受 `OptionsRenderState` + - `BlockFeatureRenderer` + - `renderSolid` 现在接受 `BlockStateModelSet` 而不是 `BlockRenderDispatcher` + - `renderTranslucent` 现在接受 `BlockStateModelSet` 和 crumbling `MultiBufferSource$BufferSource` 而不是 `BlockRenderDispatcher` + - `FeatureRenderDispatcher` 现在接受 `GameRenderState` 和 `ModelManager` + - `renderAllFeatures` 已拆分为 `renderSolidFeatures` 和 `renderTranslucentFeatures` + - 原始方法现在调用这两个方法,先 solid 后 translucent + - `clearSubmitNodes` - 清除提交节点存储。 + - `renderTranslucentParticles` - 渲染收集的半透明粒子。 + - `FlameFeatureRenderer#render` -> `renderSolid` + - `LeashFeatureRenderer#render` -> `renderSolid` + - `NameTagFeatureRenderer#render` -> `renderTranslucent` + - `ShadowFeatureRenderer#render` -> `renderTranslucent` + - `TextFeatureRenderer#render` -> `renderTranslucent` +- `net.minecraft.client.renderer.fog` + - `FogData#color` - 雾的颜色。 + - `FogRenderer` + - `setupFog` 现在返回一个 `FogData` 而不是 `Vector4f` 雾颜色 + - `updateBuffer` - 用雾数据更新缓冲区。 +- `net.minecraft.client.renderer.gizmos.DrawableGizmoPrimitives#render` 现在接受一个 `Matrix4fc` 而不是 `Matrix4f` 用于模型视图 +- `net.minecraft.client.renderer.item` + - `BlockModelWrapper` -> `CuboidItemModelWrapper` + - 构造函数不再接受 `RenderType` 函数,现在接受 `Matrix4fc` 变换 + - `$Unbaked` 现在接受一个可选的 `Transformation` + - `CompositeModel$Unbaked` 现在接受一个可选的 `Transformation` + - `ConiditionalItemModel$Unbaked` 现在接受一个可选的 `Transformation` + - `ItemModel` + - `$BakingContext` + - `materials` -> `sprites` + - `missingItem` - 获取带有给定 `Matrix4fc` 变换的缺失物品模型。 + - `$Unbaked#bake` 现在接受来自任何父客户端物品的 `Matrix4fc` 变换 + - `ItemStackRenderState` + - `pickParticleIcon` -> `pickParticleMaterial`,现在返回 `Material$Baked` 而不是 `TextureAtlasSprite` + - `$LayerRenderState` + - `EMPTY_TINTS` - 一个表示不应用色调的 `int` 数组。 + - `setRenderType` 已移除 + - `setParticleIcon` -> `setParticleMaterial`,现在接受 `Material$Baked` 而不是 `TextureAtlasSprite` + - `setTransform` -> `setItemTransform` + - `setLocalTransform` - 设置客户端物品变换,该变换在物品显示变换后应用。 + - `prepareTintLayers` -> `tintLayers`,不是一对一 + - `MissingItemModel#withTransform` - 获取带有给定变换的缺失物品模型。 + - `ModelRenderProperties#particleIcon` -> `particleMaterial`,现在接受 `Material$Baked` 而不是 `TextureAtlasSprite` + - `RangeSelectItemModel$Unbaked` 现在接受一个可选的 `Transformation` + - `SelectItemModel` + - `$ModelSelector#get` 不再支持可空的 `ItemModel`。 + - `$Unbaked` 现在接受一个可选的 `Transformation` + - `$UnbakedSwitch#bake` 现在接受 `Matrix4fc` 变换 + - `SpecialModelWrapper` 现在接受 `Matrix4fc` 变换 + - `$Unbaked` 现在接受一个可选的 `Transformation` +- `net.minecraft.client.renderer.rendertype` + - `RenderType#outputTarget` - 获取输出目标。 + - `RenderTypes` + - `MOVING_BLOCK_SAMPLER` 被 `createMovingBlockSetup` 取代,现在是 `private` + - `entityCutoutNoCull` -> `entityCutout` + - 原来的带剔除的 cutout 被 `entityCutoutCull` 取代 + - `entityCutoutNoCullZOffset` -> `entityCutoutZOffset` + - `entitySmoothCutout` -> `endCrystalBeam` + - `entityNoOutline` -> `entityTranslucent`,`affectsOutline` 为 `false` + - `entityDecal`, `dragonExplosionAlpha` -> `entityCutoutDissolve`,不是一对一 + - `itemEntityTranslucentCull` -> `entityTranslucentCullItemTarget`, `itemCutout`, `itemTranslucent`; 不是一对一 + - `bannerPattern` - 用于渲染旗帜图案的渲染类型。 +- `net.minecraft.client.renderer.special` + - `BannerSpecialRenderer`, `$Unbaked` 现在接受 `BannerBlock$AttachmentType` + - `$Unbaked` 现在使用 `BannerPatternLayers` 作为泛型 + - `BedSpecialRenderer`, `$Unbaked` 现在接受 `BedPart` + - `$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - `BellSpecialRenderer` - 钟的特殊渲染器。 + - `BookSpecialRenderer` - 附魔台上书的特殊渲染器。 + - `ChestSpecialRenderer$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - 构造函数现在接受 `ChestType` + - `*_CHEST_TEXTURE` 已从字段名中移除,现在是 `MultiBlockChestResources` + - `ENDER_CHEST_TEXTURE` -> `ENDER_CHEST` + - `ConduitSpecialRenderer$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - `CopperGolemStatueSpecialRenderer$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - `DecoratedPotSpecialRenderer$Unbaked` 现在使用 `PotDecorations` 作为泛型 + - `EndCubeSpecialRenderer` - 末地传送门立方体的特殊渲染器。 + - `HangingSignSpecialRenderer$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - 构造函数现在接受 `HangingSignBlock$Attachment` + - `NoDataSpecialModelRenderer` + - `submit` 不再接受 `ItemDisplayContext` + - `$Unbaked` - 不需要提取数据的特殊模型渲染器的未烘焙渲染器。 + - `PlayerHeadSpecialRenderer$Unbaked` 现在使用 `PlayerSkinRenderCache$RenderInfo` 作为泛型 + - `ShieldSpecialRenderer` + - `DEFAULT_TRANSFORMATION` - 要应用的默认变换。 + - `$Unbaked` 现在使用 `DataComponentMap` 作为泛型 + - `ShulkerBoxSpecialRenderer`, `$Unbaked` 不再接受 `Direction` 方向 + - `$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - `SkullSpecialRenderer$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - `SpecialModelRenderer` + - `submit` 不再接受 `ItemDisplayContext` + - `$BakingContext` + - `materials` -> `sprites` + - `$Simple` 被 `BlockModel$BakingContext`、`ItemModel$BakingContext` 取代 + - `materials` -> `sprites` + - `$Unbaked` 现在具有从表示对象中提取参数的泛型 + - `SpecialModelRenderers#createBlockRenderers` -> `BuiltInBlockModels#createBlockModels`,不是一对一 + - `StandingSignSpecialRenderer$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` + - 构造函数现在接受 `PlainSignBlock$Attachment` + - `TridentSpecialRenderer` + - `DEFAULT_TRANSFORMATION` - 要应用的默认变换。 + - `$Unbaked` 现在实现 `NoDataSpecialModelRenderer$Unbaked` +- `net.minecraft.client.renderer.state.*` -> `.state.level.*` +- `net.minecraft.client.renderer.state` + - `GameRenderState` - 游戏的渲染状态。 + - `OptionsRenderState` - 客户端用户选项的渲染状态。 + - `WindowRenderState` - 游戏窗口的渲染状态。 +- `net.minecraft.client.renderer.state.gui.GuiRenderState#submit*` 方法已重命名为 `add*` +- `net.minecraft.client.renderer.state.level` + - `BlockBreakingRenderState` 现在是一个记录,不再扩展 `MovingBlockRenderState` + - 构造函数接受 `BlockPos`、`BlockState` 和当前进度 `int` + - `CameraEntityRenderState` - 相机实体的渲染状态。 + - `CameraRenderState` + - `xRot`, `yRot` - 相机的旋转。 + - `entityPos` 已移除 + - `isPanoramicMode` - 相机是否处于全景模式。 + - `cullFrustum` - 剔除视景体。 + - `fogType`, `fogData` - 雾元数据。 + - `hudFov` - HUD 视场角。 + - `depthFar` - 深度 Z 远平面。 + - `projectionMatrix`, `viewRotationMatrix` - 用于从世界空间转换到屏幕空间的矩阵。 + - `entityRenderState` - 相机附加到的实体。 + - `LevelRenderState` + - `lastEntityRenderStateCount` - 渲染到屏幕的实体数量。 + - `cloudColor`, `cloudHeight` - 云元数据。 + - `LightmapRenderState` - 光照图的渲染状态。 +- `net.minecraft.client.renderer.texture` + - `MipmapGenerator#generateMipLevels` 现在接受计算出的图像 `Transparency` + - `SpriteContents` + - `transparency` - 获取精灵的透明度。 + - `getUniqueFrames` 现在返回一个 `IntList` 而不是 `IntStream` + - `computeTransparency` - 计算选定 UV 边界的透明度。 + - `$AnimatedTexture#getUniqueFrames` 现在返回一个 `IntList` 而不是 `IntStream` + - `TextureAtlasSprite#transparency` - 获取精灵的透明度。 +- `net.minecraft.client.resources.model` + - `AtlasManager` -> `.model.sprite.AtlasManager` + - `BlockModelRotation` -> `.client.renderer.block.dispatch.BlockModelRotation` + - `Material` -> `.model.sprite.SpriteId`,不是一对一 + - `MaterialSet` -> `.model.sprite.SpriteGetter` + - `MissingBlockModel` -> `.model.cuboid.MissingCuboidModel` + - `ModelBaker` + - `sprites` -> `materials` + - `parts` -> `interner` + - `$PartCache` -> `$Interner` + - `vector(float, float, float)` 已移除 + - `materialInfo` - 获取内部化的材质信息对象。 + - `ModelBakery` + - `BANNER_BASE` -> `Sheets#BANNER_BASE` + - `SHIELD_BASE` -> `Sheets#SHIELD_BASE` + - `NO_PATTERN_SHIELD` -> `Sheets#SHIELD_BASE_NO_PATTERN` + - `LAVA_*` -> `FluidStateModelSet#LAVA_MODEL`,现在是 `private` 而不是 `public` + - `WATER_*` -> `FluidStateModelSet#WATER_MODEL`,现在是 `private` 而不是 `public` + - `$BakingResult#getBlockStateModel` - 从 `BlockState` 获取 `BlockStateModel`。 + - `$MissingModels` 现在接受一个 `MissingItemModel` 而不是 `ItemModel` 用于 `Item`,以及一个 `FluidModel` + - `ModelManager` + - `BLOCK_OR_ITEM` 已移除 + - `getMissingBlockStateModel` -> `BlockStateModelSet#missingModel` + - `getBlockModelShaper` -> `getBlockStateModelSet`,不是一对一 + - `getBlockModelSet` - 获取 `BlockState` 到方块模型的映射。 + - `specialBlockModelRenderer` 已移除 + - `getFluidStateModelSet` - 获取 `Fluid` 到流体模型的映射。 + - `ModelState` -> `.client.renderer.block.dispatch.ModelState` + - `QuadCollection` -> `.model.geometry.QuadCollection` + - `addAll` - 添加另一个四边形集合中的所有元素。 + - `materialFlags`, `hasMaterialFlag` - 处理模型使用的材质的标志。 + - `ResolvedModel#resolveParticleSprite` -> `resolveParticleMaterial`,现在返回 `Material$Baked` 而不是 `TextureAtlasSprite` + - `SpriteGetter` -> `.model.sprite.MaterialBaker` + - `UnbakedGeometry` -> `.model.geometry.UnbakedGeometry` + - `WeightedVariants` -> `.client.renderer.block.dispatch.WeightedVariants` +- `net.minecraft.client.resources.model.sprite.Material` - 对纹理精灵的引用,以及是否强制纹理半透明。 +- `net.minecraft.world.entity.animal.Animal#isBrightEnoughToSpawn` 现在接受一个 `BlockAndLightGetter` 而不是 `BlockAndTintGetter` +- `net.minecraft.world.level` + - `BlockAndTintGetter` -> `BlockAndLightGetter` + - `BlockAndTintGetter` 现在是客户端专用的,实现 `BlockAndLightGetter` + - `getShade` -> `cardinalLighting`;不是一对一 + - `getBlockTint` -> `BlockAndTintGetter#getBlockTint` + - `CardinalLighting` - 持有每个方向应用的光照。 + - `EmptyBlockAndTintGetter` -> `BlockAndTintGetter#EMPTY` + - `LevelReader` 现在实现 `BlockAndLightGetter` 而不是 `BlockAndTintGetter` +- `net.minecraft.world.level.block` + - `BannerBlock$AttachmentType` - 旗帜附着到另一个方块的位置。 + - `CeilingHangingSignBlock` 现在实现 `HangingSignBlock` + - `getAttachmentPoint` - 获取标志附着到另一个方块的位置。 + - `HangingSignBlock` - 一个定义附着到另一个方块的悬挂标志的接口。 + - `PlainSignBlock` - 一个定义附着到另一个方块的普通标志的接口。 + - `StandingSignBlock` 现在实现 `PlainSignBlock` + - `WallingHangingSignBlock` 现在实现 `HangingSignBlock` + - `WallSignBlock` 现在实现 `PlainSignBlock` +- `net.minecraft.world.level.block.state.BlockBehaviour#getLightBlock`, `$BlockStateBase#getLightBlock` -> `getLightDampening` +- `net.minecraft.world.level.block.state.properties` + - `BedPart#CODEC` - 床部分的编解码器。 + - `ChestType#CODEC` - 箱子类型的编解码器。 +- `net.minecraft.world.level.dimension.DimensionType$CardinalLightType` -> `CardinalLighting$Type` + +## 次要迁移 + +以下是有用或有趣的添加、更改和删除列表,这些内容在入门指南中不值得单独列出一个章节。 + +### 可种植标签 + +用于确定可种植植物能否存活或被放置的方块已移至方块和流体标签。每个此类标签以 `support_*` 开头,后跟方块(例如 `bamboo`、`cactus`)或组(例如 `crops`、`dry_vegetation`)。这由相关的方块子类通过重写 `Block#canSurvive` 或对于植被 `VegetationBlock#mayPlaceOn` 来处理。 + +- `net.minecraft.world.level.block` + - `AttachedStemBlock` 现在接受一个 `TagKey` 表示它可以放置在上面的方块 + - `FarmBlock` -> `FarmlandBlock` + - `FungusBlock` -> `NetherFungusBlock`,不是一对一 + - `RootsBlock` -> `NetherRootsBlock`,不是一对一 + - `WaterlilyBlock` -> `LilyPadBlock`,不是一对一 + - `StemBlock` 现在接受一个 `TagKey` 表示它可以放置在上面的方块,以及一个 `TagKey` 表示其果实可以放置在上面的方块 + +### 容器屏幕变更 + +`AbstractContainerScreen` 的使用略有变化,需要一些小的更改。首先,`imageWidth` 和 `imageHeight` 现在是 final 的,可以在构造函数中作为参数设置。如果未指定这两个参数,它们默认为原始的 176 x 166 背景图像。 + +```java +// 假设存在某个 AbstractContainerMenu 子类 +public class ExampleContainerScreen extends AbstractContainerScreen { + + // 构造函数 + public ExampleContainerScreen(ExampleContainerMenu menu, Inventory playerInventory, Component title) { + // 在构造函数的最后两个参数中指定图像宽度和高度 + super(menu, playerInventory, title, 256, 256); + } +} +``` + +此外,`AbstractContainerScreen#render` 的重写现在在调用栈末尾调用 `renderTooltip`。这意味着,在大多数情况下,你不应该在 `AbstractContainerScreen` 的子类型中重写 `render`。所有事情都可以通过类提供的其他方法之一完成。 + +- `net.minecraft.client.gui.screens.inventory.AbstractContainerScreen` 现在可选地接受背景图像宽度和高度 + - `imageWidth`, `imageHeight` 现在是 final 的 + - `DEFAULT_IMAGE_WIDTH`, `DEFAULT_IMAGE_HEIGHT` - 容器背景图像的默认宽度和高度。 + - `slotClicked` 现在接受一个 `ContainerInput` 而不是 `ClickType` + - `render` 重写现在默认调用 `renderTooltip` +- `net.minecraft.world.inventory` + - `AbstractContainerMenu#clicked` 现在接受一个 `ContainerInput` 而不是 `ClickType` + - `ClickType` -> `ContainerInput` + +### 新标签提供者 + +添加了一个新的 `TagsProvider`,它提供了一个处理 `Holder$Reference` 的工具,称为 `HolderTagProvider`。这仅由 `PotionTagsProvider` 使用。 + +此外,`TagBuilder` 现在提供了一种设置标签上 `replace` 字段的方法,该方法在反序列化期间删除所有先前读取的条目。 + +- `net.minecraft.data.tags` + - `FeatureTagsProvider` - 用于 `ConfiguredFeature` 的标签提供器。 + - `HolderTagProvider` - 一个带有通过其引用持有者附加标签的工具的标签提供器。 + - `KeyTagProvider#tag` 现在有一个重载,指示是否替换标签中的条目。 + - `PotionTagsProvider` - 用于药水标签的提供器。 + - `TradeRebalanceTradeTagsProvider` - 用于交易重新平衡的村民交易标签提供器。 + - `VillagerTradesTagsProvider` - 用于村民交易标签的提供器。 +- `net.minecraft.tags` + - `FeatureTags` - 用于 `ConfiguredFeature` 的标签。 + - `TagBuilder#shouldReplace`, `setReplace` - 处理 `replace` 字段,该字段在反序列化期间删除所有先前读取的条目。 + +### 测试环境状态追踪 + +`TestEnvironmentDefinition` 现在可以跟踪创建时世界的原始状态,以便在运行时正确恢复。这是通过一个称为 'SavedDataType' 的泛型完成的。在 `setup` 上,每个环境将返回表示修改内容原始状态的泛型数据。然后,在 `teardown` 上,原始状态将恢复到关卡中,以供下一个测试用例使用。 + +```java +// 泛型应表示存储在关卡上的原始数据 +public record RespawnEnvironment(LevelData.RespawnData respawn) implements TestEnvironmentDefinition { + + @Override + public LevelData.RespawnData setup(ServerLevel level) { + // 修改关卡同时记录原始状态。 + var original = level.getRespawnData(); + level.setRespawnData(this.respawn); + + // 返回原始状态。 + return original; + } + + @Override + public void teardown(ServerLevel level, LevelData.RespawnData original) { + // 将关卡状态重置为原始值。 + level.setRespawnData(original); + } + + @Override + public MapCodec codec() { + // 在此处返回注册的 MapCodec。 + // ... + } +} +``` + +- `net.minecraft.gametest.framework.TestEnvironmentDefinition` 现在有一个泛型表示测试环境执行的给定修改的原始状态 + - `setup` 现在返回表示原始状态的泛型 + - `teardown` 不再是默认的,接受要恢复的原始状态 + - `activate`, `$Activation` - 处理一个活动的测试环境。 + +### 类型化实例 + +`TypedInstance` 是一个附加到某些对象的接口,这些对象表示某个其他“类型”对象的实例。例如,`Entity` 是 `EntityType` 的类型化实例,或者 `BlockState` 是 `Block` 的类型化实例。此接口旨在作为一种标准方法,提供对类型 `Holder` 的访问,并通过 `is` 检查后备类型(因此也是实例)是否等同于某个标识符、标签或原始对象。 + +```java +// 对于某个 Entity entity,检查 EntityType +entity.is(EntityType.PLAYER); + +// 对于某个 ItemStack itemStack,检查 Item +itemStack.is(ItemTags.BUTTONS); + +// 对于某个 BlockEntity blockEntity,检查 BlockEntityType +blockEntity.is(BlockEntityType.CHEST); + +// 对于某个 BlockState blockState,检查 Block +blockState.is(Blocks.DIRT); + +// 对于某个 FluidState fluidState,检查 Fluid +fluidState.is(FluidTags.WATER); +``` + +- `net.minecraft.core.TypedInstance` - 一个表示此对象是某个其他“类型”对象的实例的接口。 +- `net.minecraft.world.entity` + - `Entity` 现在实现 `TypedInstance>` + - `EntityType#is` -> `TypedInstance#is` + - 现在在 `Entity` 实例上 +- `net.minecraft.world.item.ItemStack` 现在实现 `TypedInstance` + - `getItemHolder` -> `typeHolder` + - `getTags` -> `tags` +- `net.minecraft.world.level.block.entity` + - `BlockEntity` 现在实现 `TypedInstance` + - `BlockEntityType#getKey` 已移除 +- `net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase` 现在实现 `TypedInstance` + - `getBlockHolder` -> `typeHolder` + - `getTags` -> `tags` +- `net.minecraft.world.level.material.FluidState` 现在实现 `TypedInstance` + - `holder` -> `typeHolder` + - `getTags` -> `tags` + +### 实体纹理与成年幼年模型 + +`assets/minecraft/textures/entity/*` 中的实体纹理现已分类到子目录中(例如,熊猫纹理的 `entity/panda`,或猪纹理的 `entity/pig`)。大多数纹理已以实体类型开头后跟下划线及其变体命名(例如,喷溅箭的 `arrow_tipped`,冷猪变体的 `pig_cold`,或棕色熊猫变体的 `panda_brown`)。 + +此外,一些动物模型已拆分为幼年和成年变体的单独类。这些模型要么直接扩展抽象模型实现(例如 `AbstractFelineModel`),要么扩展原始模型类(例如 `PigModel`)。 + +- `net.minecraft.client.animation.definitions` + - `BabyArmadilloAnimation` - 幼年犰狳的动画。 + - `BabyAxolotlAnimation` - 幼年美西螈的动画。 + - `BabyRabbitAnimation` - 幼年兔子的动画。 + - `CamelBabyAnimation` - 幼年骆驼的动画。 + - `FoxBabyAnimation` - 幼年狐狸的动画。 + - `RabbitAnimation` - 兔子的动画。 +- `net.minecraft.client.model` + - `HumanoidModel` + - `ADULT_ARMOR_PARTS_PER_SLOT`, `BABY_ARMOR_PARTS_PER_SLOT` - 装备槽位到模型部件键的映射。 + - `createBabyArmorMeshSet` - 为幼年类人生物创建盔甲模型集。 + - `createArmorMeshSet` 现在可以接受一个装备槽位到要保留的模型部件键的映射 + - `setAllVisible` 已移除 + - `QuadrupedModel` 现在有一个接受 `RenderType` 函数的构造函数 +- `net.minecraft.client.model.animal.armadillo` + - `AdultArmadilloModel` - 成年犰狳的实体模型。 + - `ArmadilloModel` 现在是抽象的 + - 构造函数现在接受行走、卷起/展开和窥视动画的定义 + - `BABY_TRANSFORMER` 已直接合并到 `BabyArmadilloModel` 的层定义中 + - `HEAD_CUBE`, `RIGHT_EAR_CUBE`, `LEFT_EAR_CUBE` 现在是 `protected` 而不是 `private` + - `createBodyLayer` -> `AdultArmadilloModel#createBodyLayer`, `BabyArmadilloModel#createBodyLayer` + - `BabyArmadilloModel` - 幼年犰狳的实体模型。 +- `net.minecraft.client.model.animal.axolotl.AxolotlModel` -> `AdultAxolotlModel`, `BabyAxolotlModel` +- `net.minecraft.client.model.animal.bee` + - `AdultBeeModel` - 成年蜜蜂的实体模型。 + - `BabyBeeModel` - 幼年蜜蜂的实体模型。 + - `BeeModel` 现在是抽象的 + - `BABY_TRANSFORMER` 已直接合并到 `BabyBeeModel` 的层定义中 + - `BONE`, `STINGER`, `FRONT_LEGS`, `MIDDLE_LEGS`, `BACK_LEGS` 现在是 `protected` 而不是 `private` + - `bone` 现在是 `protected` 而不是 `private` + - `createBodyLayer` -> `AdultBeeModel#createBodyLayer`, `BabyBeeModel#createBodyLayer` + - `bobUpAndDown` - 以期望的速度上下摆动蜜蜂,取决于其当前年龄。 +- `net.minecraft.client.model.animal.camel` + - `AdultCamelModel` - 成年骆驼的实体模型。 + - `BabyCamelModel` - 幼年骆驼的实体模型。 + - `CamelModel` 现在是抽象的 + - 构造函数现在接受行走、坐下(带/不带姿势)、站立、闲置和冲刺动画的定义 + - `BABY_TRANSFORMER` 已直接合并到 `BabyCamelModel` 的层定义中 + - `createBodyLayer` -> `AdultCamelModel#createBodyLayer`, `BabyCamelModel#createBodyLayer` + - `CameSaddleModel` 现在扩展 `AdultCamelModel` 而不是 `CamelModel` +- `net.minecraft.client.model.animal.chicken` + - `AdultChickenModel` - 成年鸡的实体模型。 + - `BabyChickenModel` - 幼年鸡的实体模型。 + - `ChickenModel` 现在是抽象的 + - `RED_THING` -> `AdultChickenModel#RED_THING` + - `BABY_TRANSFORMER` 已直接合并到 `BabyChickenModel` 的层定义中 + - `createBodyLayer` -> `AdultChickenModel#createBodyLayer` + - `createBaseChickenModel` -> `AdultChickenModel#createBaseChickenModel` + - `ColdChickenModel` 现在扩展 `AdultChickenModel` +- `net.minecraft.client.model.animal.cow.BabyCowModel` - 幼年牛的实体模型。 +- `net.minecraft.client.model.animal.dolphin.BabyDolphinModel` - 幼年海豚的实体模型。 +- `net.minecraft.client.model.animal.equine` + - `AbstractEquineModel` 现在有一个直接指定要使用的 `ModelPart` 的重载 + - `BABY_TRANSFORMER` 已直接合并到 `BabyDonkeyModel` 的层定义中 + - `rightHindLeg`, `leftHindLeg`, `rightFrontLeg`, `leftFrontLeg` 现在是 `protected` 而不是 `private` + - `createBabyMesh` -> `BabyHorseModel#createBabyMesh`,不是一对一 + - `offsetLegPositionWhenStanding` - 当实体站立时偏移腿的位置。 + - `getLegStandAngle`, `getLegStandingYOffset`, `getLegStandingZOffset`, `getLegStandingXRotOffset`, `getTailXRotOffset` - 马模型各部分的偏移和角度。 + - `animateHeadPartsPlacement` - 根据其进食和站立状态动画头部。 + - `BabyDonkeyModel` - 幼年驴的实体模型。 + - `BabyHorseModel` - 幼年马的实体模型。 + - `DonkeyModel` 现在有一个直接指定要使用的 `ModelPart` 的重载 + - `createBabyLayer` -> `BabyDonkeyModel#createBabyLayer` + - `EquineSaddleModel#createFullScaleSaddleLayer` 已移除 + 合并到 `createSaddleLayer` 中,移除了幼年变体 +- `net.minecraft.client.model.animal.feline` + - `CatModel` -> `AdultCatModel`, `BabyCatModel`; 不是一对一 + - `FelineModel` -> `AbstractFelineModel`,不是一对一 + - 实现为 `AdultFelineModel` 和 `BabyFelineModel` + - `OcelotModel` -> `AdultOcelotModel`, `BabyOcelotModel`; 不是一对一 +- `net.minecraft.client.model.animal.fox` + - `AdultFoxModel` - 成年狐狸的实体模型。 + - `BabyFoxModel` - 幼年狐狸的实体模型。 + - `FoxModel` 现在是抽象的 + - `BABY_TRANSFORMER` 已直接合并到 `BabyFoxModel` 的层定义中 + - `body`, `rightHindLeg`, `leftHindLeg`, `rightFontLeg`, `leftFrontLeg`, `tail` 现在是 `protected` 而不是 `private` + - `createBodyLayer` -> `AdultFoxModel#createBodyLayer`, `BabyFoxModel#createBodyLayer` + - `set*Pose` - 设置狐狸当前姿势的方法。 +- `net.minecraft.client.model.animal.goat` + - `BabyGoatModel` - 幼年山羊的实体模型。 + - `GoatModel#BABY_TRANSFORMER` 已直接合并到 `BabyGoatModel` 的层定义中 +- `net.minecraft.client.model.animal.llama` + - `BabyLlamaModel` - 幼年羊驼的实体模型。 + - `LlamaModel#createBodyLayer` 不再接受表示实体是否为幼年的 `boolean` +- `net.minecraft.client.model.animal.panda` + - `BabyPandaModel` - 幼年熊猫的实体模型。 + - `PandaModel` + - `BABY_TRANSFORMER` 已直接合并到 `BabyPandaModel` 的层定义中 + - `animateSitting` - 动画熊猫坐下。 +- `net.minecraft.client.model.animal.pig.BabyPigModel` - 幼年猪的实体模型。 +- `net.minecraft.client.model.animal.polarbear` + - `BabyPolarBearModel` - 幼年北极熊的实体模型。 + - `PolarBearModel#BABY_TRANSFORMER` 已直接合并到 `BabyPolarBearModel` 的层定义中 +- `net.minecraft.client.model.animal.rabbit` + - `AdultRabbitModel` - 成年兔子的实体模型。 + - `BabyRabbitModel` - 幼年兔子的实体模型。 + - `RabbitModel` 现在是抽象的 + - 构造函数现在接受跳跃和空闲头部倾斜的两个动画定义 + - `FRONT_LEGS`, `BACK_LEGS` - 实体腿的子名称。 + - `LEFT_HAUNCH`, `RIGHT_HAUNCH` 现在是 `protected` + - `createBodyLayer` -> `AdultRabbitModel#createBodyLayer`, `BabyRabbitModel#createBodyLayer`; 不是一对一 +- `net.minecraft.client.model.animal.sheep` + - `BabySheepModel` - 幼年羊的实体模型。 + - `SheepModel#BABY_TRANSFORMER` 已直接合并到 `BabySheepModel` 的层定义中 +- `net.minecraft.client.model.animal.sniffer` + - `SnifferModel#BABY_TRANSFORMER` 已直接合并到 `SniffletModel` 的层定义中 + - `SniffletModel` - 幼年嗅探兽的实体模型。 +- `net.minecraft.client.model.animal.squid` + - `BabySquidModel` - 幼年鱿鱼的实体模型。 + - `SquidModel#createTentacleName` 现在是 protected 而不是 private +- `net.minecraft.client.model.animal.turtle` + - `AdultTurtleModel` - 成年海龟的实体模型。 + - `BabyTurtleModel` - 幼年海龟的实体模型。 + - `TurtleModel` 现在是抽象的 + - 构造函数现在可以接受渲染类型函数 + - `BABY_TRANSFORMER` 已直接合并到 `BabyTurtleModel` 的层定义中 + - `createBodyLayer` -> `AdultTurtleModel#createBodyLayer`, `BabyTurtleModel#createBodyLayer` +- `net.minecraft.client.model.animal.wolf` + - `AdultWolfModel` - 成年狼的实体模型。 + - `BabyWolfModel` - 幼年狼的实体模型。 + - `WolfModel` 现在是抽象的 + - `ModelPart` 字段现在都是 protected + - `createMeshDefinition` -> `AdultWolfModel#createBodyLayer`, `BabyWolfModel#createBodyLayer`; 不是一对一 + - `shakeOffWater` - 甩水时设置身体旋转。 + - `setSittingPose` - 设置狼的坐姿。 +- `net.minecraft.client.model.geom` + - `ModelLayers` + - `COLD_CHICKEN_BABY` 已移除 + - `COLD_PIG_BABY` 已移除 + - `PIG_BABY_SADDLE` 已移除 + - `SHEEP_BABY_WOOL_UNDERCOAT` 已移除 + - `WOLF_BABY_ARMOR` 已移除 + - `DONKEY_BABY_SADDLE` 已移除 + - `HORSE_BABY_ARMOR` 已移除 + - `HORSE_BABY_SADDLE` 已移除 + - `MULE_BABY_SADDLE` 已移除 + - `SKELETON_HORSE_BABY_SADDLE` 已移除 + - `UNDEAD_HORSE_BABY_ARMOR` 已移除 + - `ZOMBIE_HORSE_BABY_SADDLE` 已移除 + - `STRIDER_BABY_SADDLE` 已移除 + - `PartNames#WAIST` - 腰部部件。 +- `net.minecraft.client.model.monster.hoglin` + - `BabyHoglinModel` - 幼年疣猪兽的实体模型。 + - `HoglinModel` + - `BABY_TRANSFORMER` 已直接合并到 `BabyHoglinModel` 的层定义中 + - `head` 现在是 `protected` 而不是 `private` + - `createBabyLayer` -> `BabyHoglinModel#createBodyLayer` +- `net.minecraft.client.model.monster.piglin` + - `AbstractPiglinModel` 现在是抽象的 + - `leftSleeve`, `rightSleeve`, `leftPants`, `rightPants`, `jacket` 已移除 + - `ADULT_EAR_ANGLE_IN_DEGREES`, `BABY_EAR_ANGLE_IN_DEGREES` - 猪灵耳朵的角度。 + - `createMesh` 被 `AdultPiglinModel#createBodyLayer`, `AdultZombifiedPiglinModel#createBodyLayer`, `BabyPiglinModel#createBodyLayer`, `BabyZombifiedPiglinModel#createBodyLayer` 取代 + - `createBabyArmorMeshSet` - 为幼年猪灵模型创建盔甲网格。 + - `getDefaultEarAngleInDegrees` - 获取默认耳朵角度。 + - `AdultPiglinModel` - 成年猪灵的实体模型。 + - `AdultZombifiedPiglinModel` - 成年僵尸猪灵的实体模型。 + - `BabyPiglinModel` - 幼年猪灵的实体模型。 + - `BabyZombifiedPiglinModel` - 幼年僵尸猪灵的实体模型。 + - `PiglinModel` 现在是抽象的 + - `ZombifiedPiglinModel` 现在是抽象的 +- `net.minecraft.client.model.monster.strider` + - `AdultStriderModel` - 成年炽足兽的实体模型。 + - `BabyStriderModel` - 幼年炽足兽的实体模型。 + - `StriderModel` 现在是抽象的 + - `BABY_TRANSFORMER` 已直接合并到 `BabyStriderModel` 的层定义中 + - `rightLeg`, `leftLeg`, `body` 现在是 `protected` 而不是 `private` + - `SPEED` - 移动动画的速度标量。 + - `customAnimations` - 额外的动画设置。 + - `animateBristle` - 动画炽足兽的鬃毛。 +- `net.minecraft.client.model.monster.zombie` + - `BabyDrownedModel` - 幼年溺尸的实体模型。 + - `BabyZombieModel` - 幼年僵尸的实体模型。 + - `BabyZombieVillagerModel` - 幼年僵尸村民的实体模型。 +- `net.minecraft.client.model.npc` + - `BabyVillagerModel` - 幼年村民的实体模型。 + - `VillagerModel#BABY_TRANSFORMER` 已直接合并到 `BabyVillagerModel` 的层定义中 +- `net.minecraft.client.renderer.entity` + - `AxolotlRenderer` 现在接受一个 `EntityModel` 作为其泛型 + - `CamelHuskRenderer` 现在扩展 `MobRenderer` 而不是 `CamelRenderer` + - `CamelRenderer#createCamelSaddleLayer` 现在是 `static` + - `CatRenderer` 现在接受一个 `AbstractFelineModel` 作为其泛型 + - `DonkeyRenderer` 现在接受一个 `EquipmentClientInfo$LayerType` 和 `ModelLayerLocation` 用于鞍层和模型,并将 `DonkeyRenderer$Type` 拆分为成年类型和幼年类型 + - `$Type` + - `DONKEY_BABY` - 驴的幼年变体。 + - `MULE_BABY` - 骡的幼年变体。 + - `OcelotRenderer` 现在接受一个 `AbstractFelineModel` 作为其泛型 + - `UndeadHorseRenderer` 现在接受一个 `EquipmentClientInfo$LayerType` 和 `ModelLayerLocation` 用于鞍层和模型,并将 `UndeadHorseRenderer$Type` 拆分为成年类型和幼年类型 + - `$Type` + - `SKELETON_BABY` - 骷髅马的幼年变体。 + - `ZOMBIE_BABY` - 僵尸马的幼年变体。 +- `net.minecraft.client.renderer.entity.layers.CatCollarLayer` 现在接受一个 `AbstractFelineModel` 作为其泛型 +- `net.minecraft.client.renderer.entity.state` + - `AxolotlRenderState` + - `swimAnimation` - 游泳的状态。 + - `walkAnimationState` - 在地面(非水下)行走的状态。 + - `walkUnderWaterAnimationState` - 在水下行走的状态。 + - `idleUnderWaterAnimationState` - 在水下但未在地面闲置的状态。 + - `idleUnderWaterOnGroundAnimationState` - 在水下接触海底时闲置的状态。 + - `idleOnGroundAnimationState` - 在地面(非水下)闲置的状态。 + - `playDeadAnimationState` - 装死的状态。 + - `RabbitRenderState` + - `hopAnimationState` - 实体正在执行的跳跃状态。 + - `idleHeadTiltAnimationState` - 执行空闲动画时头部倾斜的状态。 +- `net.minecraft.client.resources.model.EquipmentClientInfo$LayerType#HUMANOID_BABY` - 幼年类人生物装备层。 +- `net.minecraft.sounds.SoundEvents#PIG_EAT_BABY` - 幼年猪进食时播放的声音。 +- `net.minecraft.world.entity.AgeableMob` + - `canUseGoldenDandelion` - 是否可以使用金蒲公英来锁定实体的年龄。 + - `setAgeLocked` - 将实体设置为年龄锁定。 + - `makeAgeLockedParticle` - 在锁定实体年龄时创建粒子。 + - `AGE_LOCK_DOWNWARDS_MOVING_PARTICLE_Y_OFFSET` - 粒子起始位置的 Y 偏移量。 +- `net.minecraft.world.entity.animal.axolotl.Axolotl` + - `swimAnimation` - 游泳的状态。 + - `walkAnimationState` - 在地面(非水下)行走的状态。 + - `walkUnderWaterAnimationState` - 在水下行走的状态。 + - `idleUnderWaterAnimationState` - 在水下但未在地面闲置的状态。 + - `idleUnderWaterOnGroundAnimationState` - 在水下接触海底时闲置的状态。 + - `idleOnGroundAnimationState` - 在地面(非水下)闲置的状态。 + - `playDeadAnimationState` - 装死的状态。 + - `$AnimationState` -> `$AxolotlAnimationState` +- `net.minecraft.world.entity.animal.chicken.ChickenVariant` 现在接受一个幼年纹理的资源 +- `net.minecraft.world.entity.animal.cow.CowVariant` 现在接受一个幼年纹理的资源 +- `net.minecraft.world.entity.animal.cat.CatVariant` 现在接受一个幼年纹理的资源 + - `CatVariant#assetInfo` - 根据实体是否为幼年获取实体纹理。 +- `net.minecraft.world.entity.animal.equine.AbstractHorse#BABY_SCALE` - 幼年大小与成年相比的比例。 +- `net.minecraft.world.entity.animal.frog.Tadpole` + - `ageLockParticleTimer` - 年龄锁定实体的粒子应生成的计时器。 + - `setAgeLocked`, `isAgeLocked` - 处理蝌蚪的年龄锁定。 +- `net.minecraft.world.entity.animal.goat.Goat` + - `BABY_DEFAULT_X_HEAD_ROT` - 幼年变体的默认头部 X 旋转。 + - `MAX_ADDED_RAMMING_X_HEAD_ROT` - 最大头部 X 旋转。 + - `addHorns`, `removeHorns` 已移除 +- `net.minecraft.world.entity.animal.pig.PigVariant` 现在接受一个幼年纹理的资源 +- `net.minecraft.world.entity.animal.rabbit.Rabbit` + - `hopAnimationState` - 实体正在执行的跳跃状态。 + - `idleHeadTiltAnimationState` - 执行空闲动画时头部倾斜的状态。 +- `net.minecraft.world.entity.animal.wolf` + - `WolfSoundVariant` -> `WolfSoundVariant$WolfSoundSet` + - 该类本身现在持有成年狼和幼年狼的两个声音集。 + - `WolfSoundVariants$SoundSet#getSoundEventSuffix` -> `getSoundEventIdentifier` +- `net.minecraft.world.entity.animal.wolf.WolfVariant` 现在接受一个幼年变体的资源信息 +- `net.minecraft.world.item.equipment.EquipmentAssets#TRADER_LLAMA_BABY` - 幼年流浪商羊驼的装备资源。 + +### interactAt 的移除 + +`Entity#interactAt` 已被移除,所有进一步的调用都已合并到 `Entity#interact` 中。最初,如果命中结果在实体的交互范围内,则调用 `Entity#interactAt`。如果 `interactAt` 没有消耗该操作(即 `InteractionResult#consumesAction` 返回 false),则调用 `Entity#interact`。现在,如果在实体的交互范围内,则调用 `Entity#interact`,并接受交互位置的 `Vec3`。 + +```java +// 在某个 Entity 子类中 + +@Override +public InteractionResult interact(Player player, InteractionHand hand, Vec3 location) { + // 处理交互 + super.interact(player, hand, location); +} +``` + +`interactAt` 检查结果是否未消耗交互,如果没有则调用 `interact` +现在,使用实体命中调用 `interact` + +- `net.minecraft.client.multiplayer.MultiPlayerGameMode` + - `interact` 现在接受 `EntityHitResult` + - `interactAt` 已移除 +- `net.minecraft.world.entity.Entity` + - `interact` 现在接受一个 `Vec3` 表示交互的位置 + - `interactAt` 已移除 +- `net.minecraft.world.entity.player.Player#interactOn` 现在接受一个 `Vec3` 表示交互的位置 + +### ChunkPos,现在是一个记录 + +`ChunkPos` 现在是一个记录。`BlockPos` 构造函数已被 `ChunkPos#containing` 取代,而打包的 `long` 构造函数已被 `ChunkPose#unpack` 取代。 + +- `net.minecraft.world.level.ChunkPos` 现在是一个记录 + - `ChunkPos(BlockPos)` -> `containing` + - `ChunkPos(long)` -> `unpack` + - `toLong`, `asLong` -> `pack` + +### 不再有绊线管线 + +绊线渲染管线已被完全移除。现在,绊线使用带有 `alpha_cutoff_bias` 为 0.1 的 `cutout` 用于纹理。 + +- `net.minecraft.client.renderer.RenderPipelines#TRIPWIRE_BLOCK`, `TRIPWIRE_TERRAIN` 已移除 +- `net.minecraft.client.renderer.chunk` + - `ChunkSectionLayer#TRIPWIRE` 已移除 + - `ChunkSectionLayerGroup#TRIPWIRE` 已移除 +- `net.minecraft.client.renderer.rendertype.RenderTypes#tripwireMovingBlock` 已移除 + +### 活动与大脑 + +活动(Activity)定义了生物在特定阶段的行为方式,现在通过 `ActivityData` 存储和传递。这包含活动类型、要执行的行为及其优先级、活动激活的条件,以及活动停止时要擦除的记忆。`Brain#provider` 现在接受一个 `$ActivitySupplier` 来构造实体执行的一系列 `ActivityData`。 + +大脑也略有变化。首先,`Brain` 本身不能直接序列化。相反,大脑被 `$Packed` 到记录中,持有其当前记忆的映射。要获取使用的记忆类型,通常从 `Sensor#requires` 中提取。此外,`LivingEntity#brainProvider` 不再存在,而是选择将提供器存储在实体本身的静态常量中。这意味着大脑现在完全通过 `makeBrain` 方法构建,接受之前的 `$Packed` 数据: + +```java +// 对于某个扩展 LivingEntity 的 ExampleEntity +// 假设扩展了 Mob 子类 +private static final Brain.Provider BRAIN_PROVIDER = Brain.provider( + // 实体使用的传感器列表。 + ImmutableList.of(), + // 一个接受实体并返回活动列表的函数。 + entity -> List.of( + new ActivityData( + // 活动类型 + Activity.CORE, + // 优先级和行为对的列表 + ImmutableList.of(Pair.of(0, new MoveToTargetSink())), + // 活动运行所需的记忆条件集 + // 例如,此记忆值必须存在 + ImmutableSet.of(Pair.of(MemoryModuleType.ATTACK_TARGET, MemoryStatus.VALUE_PRESENT)), + // 活动停止时要擦除的记忆集 + ImmutableSet.of(MemoryModuleType.ATTACK_TARGET) + ) + ) +); + +@Override +protected Brain.Provider makeBrain(Brain.Packed packedBrain) { + // 构建大脑,填充任何先前的记忆 + return BRAIN_PROVIDER.makeBrain(this, packedBrain); +} +``` + +- `net.minecraft.world.entity.LivingEntity` + - `brainProvider` 已移除 + - `makeBrain` 现在接受一个 `Brain$Packed` 而不是 `Dynamic` +- `net.minecraft.world.entity.ai` + - `ActivityData` - 一个记录,包含正在执行的活动、该活动中要执行的行为、任何记忆条件以及停止后要擦除的记忆。 + - `Brain` 现在是 protected 的,接受一个 `ActivityData` 列表、一个 `MemoryMap` 而不是不可变的记忆列表、一个 `RandomSource`,并且不提供 `Codec` + - 公共构造函数不再接受任何参数 + - `provider` 现在有一个只接受传感器类型的重载,默认记忆类型为空列表 + - 一些 `provider` 方法也接受要执行的 `Brain$ActivitySupplier` + - `codec`, `serializeStart` 被 `pack`, `Brain$Packed` 取代 + - `addActivityAndRemoveMemoryWhenStopped`, `addActivityWithConditions` 合并到 `addActivity` 中 + - 或者使用 `ActivityData#create` + - `copyWithoutBehaviors` 已移除 + - `getMemories` 被 `forEach` 取代 + - `$ActivitySupplier` - 为实体创建活动列表。 + - `$MemoryValue` 已移除 + - `$Packed` - 一个包含序列化大脑到磁盘的数据的记录。 + - `$Provider#makeBrain` 现在接受实体和要反序列化记忆的 `Brain$Packed` + - `$Visitor` - 访问大脑内的记忆,无论是已定义但为空、存在还是带有计时器存在。 +- `net.minecraft.world.entity.ai.behavior.VillagerGoalPackages#get*Package` 不再接受 `VillagerProfession` +- `net.minecraft.world.entity.ai.memory` + - `ExpirableValue` -> `MemorySlot`,不是一对一 + - 原始的 `ExpirableValue` 现在是一个记录,定义记忆何时过期,而不是自己更新 + - `MemoryMap` - 将记忆类型链接到其存储值的映射。 + - `MemoryModuleType` - 记忆是否可以序列化。 +- `net.minecraf.tworld.entity.ai.sensing.Sensor#randomlyDelayStart` - 延迟传感器多长时间。 +- `net.minecraft.world.entity.animal.allay.AllayAi` + - `SENSOR_TYPES`, `MEMORY_TYPES` -> `BRAIN_PROVIDER`,现在是 private;不是一对一 + - `makeBrain` -> `getActivities`,不是一对一 +- `net.minecraft.world.entity.animal.armadillo.ArmadilloAi#makeBrain`, `brainProvider` -> `getActivities`,现在是 `protected`,不是一对一 +- `net.minecraft.world.entity.animal.axolotl` + - `AxolotlSENSOR_TYPES` -> `BRAIN_PROVIDER`,现在是 private;不是一对一 + - `AxolotlAi` + - `makeBrain` -> `getActivities`,不是一对一 + - `initPlayDeadActivity`,现在是 `protected`,不再接受任何参数 + - `initFightActivity`,现在是 `protected`,不再接受任何参数 + - `initCoreActivity`,现在是 `protected`,不再接受任何参数 + - `initIdleActivity`,现在是 `protected`,不再接受任何参数 +- `net.minecraft.world.entity.animal.camel.CamelAi#makeBrain`, `brainProvider` -> `getActivities`,现在是 `protected`,不是一对一 +- `net.minecraft.world.entity.animal.frog` + - `Frog#SENSOR_TYPES` -> `BRAIN_PROVIDER`,现在是 private;不是一对一 + - `FrogAi#makeBrain` -> `getActivities`,不是一对一 + - `Tadpole#SENSOR_TYPES` -> `BRAIN_PROVIDER`,现在是 private;不是一对一 +- `net.minecraft.world.entity.animal.frog.TadpoleAi#makeBrain` -> `getActivities`,现在是 `public`,不是一对一 +- `net.minecraft.world.entity.animal.goat` + - `Goat#SENSOR_TYPES` -> `BRAIN_PROVIDER`,现在是 private;不是一对一 + - `GoatAi#makeBrain` -> `getActivities`,不是一对一 +- `net.minecraft.world.entity.animal.golem.CopperGolemAi#makeBrain`, `brainProvider` -> `getActivities`,现在是 `protected`,不是一对一 +- `net.minecraft.world.entity.animal.happyghast.HappyGhastAi#makeBrain`, `brainProvider` -> `getActivities`,现在是 `protected`,不是一对一 +- `net.minecraft.world.entity.animal.nautilus` + - `NautilusAi` + - `SENSOR_TYPES`,`MEMORY_TYPES` -> `Nautilus#BRAIN_PROVIDER`,现在是 private,不是一对一 + - `makeBrain`, `brainProvider` -> `getActivities`,现在是 `public`,不是一对一 + - `ZombieNautilusAi` + - `SENSOR_TYPES`,`MEMORY_TYPES` -> `ZombieNautilus#BRAIN_PROVIDER`,现在是 private,不是一对一 + - `makeBrain`, `brainProvider` -> `getActivities`,现在是 `public`,不是一对一 +- `net.minecraft.world.entity.animal.sniffer.SnifferAi#makeBrain` -> `getActivities`,现在是 `public`,不是一对一 +- `net.minecraft.world.entity.monster.Zoglin` + - `SENSOR_TYPES` -> `BRAIN_PROVIDER`,现在是 private,不是一对一 + - `getActivities` - 僵尸疣猪兽执行的活动。 +- `net.minecraft.world.entity.monster.breeze.BreezeAi#makeBrain` -> `getActivities`,不是一对一 +- `net.minecraft.world.entity.monster.creaking.CreakingAi#makeBrain`, `brainProvider` -> `getActivities`,现在是 `protected`,不是一对一 +- `net.minecraft.world.entity.monster.hoglin` + - `Hoglin#SENSOR_TYPES` -> `BRAIN_PROVIDER`,现在是 private;不是一对一 + - `HoglinAi#makeBrain` -> `getActivities`,不是一对一 +- `net.minecraft.world.entity.monster.piglin` + - `Piglin#SENSOR_TYPES`,`MEMORY_TYPES` -> `BRAIN_PROVIDER`,现在是 private,不是一对一 + - `PiglinAi#makeBrain` -> `getActivities`,现在是 `public`,不是一对一 + - `PiglinBrute#SENSOR_TYPES`,`MEMORY_TYPES` -> `BRAIN_PROVIDER`,现在是 private,不是一对一 + - `PiglinBruteAi#makeBrain` -> `getActivities`,现在是 `public`,不是一对一 +- `net.minecraft.world.entity.monster.warden.WardenAi#makeBrain` -> `getActivities`,不是一对一 + +### 文件修复器 + +文件修复器是一个新系统,用于帮助在版本之间升级游戏文件,与数据修复器协同工作。类似于文件中的数据可以通过数据修复器在 Minecraft 版本之间修改或“升级”,文件修复器可以修改世界目录中的任何内容,从移动文件和目录到直接删除它们。因此,文件修复器总是在数据修复器之前应用。 + +与数据修复器升级时不同,文件修复器会改变世界文件夹的结构。因此,降级几乎不可能,因为文件名和位置很可能已更改位置。 + +文件修复器通过 `FileFix` 应用,它使用 `makeFixer` 定义要对文件执行的一些操作。通常通过 `addFileContentFix` 完成,通过 `FileAccess` 提供对所需文件的访问,然后像任何其他 `Dynamic` 实例一样修改数据。操作通常定义为 `FileFixOperation`,可以移动文件结构。 + +然后通过 `FileFixerUpper` 应用文件修复,它使用写时复制文件系统对文件进行操作。修复器使用 `$Builder` 构建,使用 `addSchema` 和 `addFixer` 为所需版本应用修复器。在升级过程中,文件在临时文件夹中创建,然后移动到世界文件夹。原始世界文件夹在删除之前被移动到另一个文件夹。 + +- `net.minecraft.client.gui.screens.FileFixerAbortedScreen` - 当文件修复被中止时显示的屏幕。 +- `net.minecraft.client.gui.screens.worldselection.FileFixerProgressScreen` - 尝试显示升级和修复世界文件的进度时显示的屏幕。 +- `net.minecraft.server.packs.linkfs` + - `DummyFileAttributes` -> `.minecraft.util.DummyFileAttributes` + - `LinkFSPath` + - `DIRECTORY_ATTRIBUTES` -> `DummyFileAttributes#DIRECTORY` + - `FILE_ATTRIBUTES` -> `DummyFileAttributes#FILE` +- `net.minecraft.util` + - `ExtraCodecs` + - `pathCodec` - 路径的编解码器,从字符串转换并使用 Unix 分隔符存储。 + - `relaiveNormalizedSubPathCodec` - 路径的编解码器,已标准化并验证以确保其是相对的。 + - `guardedPathCodec` - 路径的编解码器,从某个基础路径解析和相对化。 + - `FileUtil` + - `isPathNormalized`, `createPathToResource` 已移除 + - `isEmptyPath` - 返回路径是否为空。 + - `Util#safeMoveFile` - 使用给定选项安全地将文件从某个源移动到目标。 +- `net.minecraft.util.filefix` + - `AbortedFileFixException` - 当文件修复已中止且无法还原移动时抛出的异常。 + - `AtmoicMoveNotSupportedFileFixException` - 当用户文件系统不支持原子移动时抛出的异常。 + - `CanceledFileFixException` - 当文件修复升级过程被取消时抛出的异常。 + - `FailedCleanupFileFixException` - 当文件修复无法移动或删除文件夹进行清理时抛出的异常。 + - `FileFix` - 对文件执行某些操作的修复器。 + - `FileFixerUpper` - 要执行操作的文件修复器。 + - `FileFixException` - 尝试通过文件修复器升级世界时抛出的异常。 + - `FileFixUtil` - 执行某些文件操作的工具。 + - `FileSystemCapabilities` - 目录中文件系统的能力。 +- `net.minecraft.util.filefix.access` + - `ChunkNbt` - 处理升级区块 nbt。 + - `CompressedNbt` - 处理升级压缩的 nbt 文件。 + - `FileAccess` - 提供对某些文件资源的引用,给定其关系。 + - `FileAccessProvider` - 文件访问的提供器,给定与源的关系。 + - `FileRelation` - 文件与某个源路径的关系的定义。 + - `FileResourceType` - 定义要访问的资源类型。 + - `FileResourceTypes` - 所有定义的文件资源类型。 + - `LevelDat` - 处理升级 level dat。 + - `PlayerData` - 处理升级玩家数据。 + - `SavedDataNbt` - 处理升级保存的数据。 +- `net.minecraft.util.filefix.fixes.*` - 要应用到文件的原版修复。 +- `net.minecraft.util.filefix.operations` + - `ApplyInFolders` - 在相关文件夹内应用给定操作。 + - `DeleteFileOrEmptyDirectory` - 删除目标文件或空目录。 + - `FileFixOperation` - 在某个基础目录内执行的操作。 + - `FileFixOperations` - 所有原版文件修复操作。 + - `GroupMove` - 移动某个目录,对其内容应用任何移动操作。 + - `ModifyContent` - 修改文件的内容。 + - `Move` - 将文件从某个源移动到某个目标。 + - `RegexMove` - 移动所有匹配给定源模式的文件到目标,替换匹配的部分。 +- `net.minecraft.util.filefix.virtualfilesystem` + - `CopyOnWriteFileStore` - 使用写时复制原理的文件存储。 + - `CopyOnWriteFileSystem` - 使用写时复制原理的文件系统。 + - `CopyOnWriteFSPath` - 使用写时复制原理的路径。 + - `CopyOnWriteFSProvider` - 使用写时复制原理的文件系统提供器。 + - `DirectoryNode` - 某个写时复制文件系统路径的目录节点。 + - `FileMove` - 一个记录,包含文件被移动到的路径。 + - `FileNode` - 某个写时复制文件系统路径的文件节点。 + - `Node` - 某个写时复制文件系统路径的节点。 +- `net.minecraft.util.filefix.virtualfilesystem.exception` + - `CowFSCreationException` - 当无法创建文件系统时的 `CowFSFileSystemException`。 + - `CowFSDirectoryNotEmptyException` - 专门针对写时复制系统的 `DirectoryNotEmptyException`。 + - `CowFSFileAlreadyExistsException` - 专门针对写时复制系统的 `FileAlreadyExistsException`。 + - `CowFSFileSystemException` - 专门针对写时复制系统的 `FileSystemException`。 + - `CowFSIllegalArgumentException` - 尝试在写时复制系统上操作时的 `IllegalArgumentException`。 + - `CowFSNoSuchFileException` - 专门针对写时复制系统的 `NoSuchFileException`。 + - `CowFSNotDirectoryException` - 专门针对写时复制系统的 `NotDirectoryException`。 + - `CowFSSymlinkException` - 尝试将写时复制系统与符号链接一起使用时的 `CowFSCreationException`。 +- `net.minecraft.util.worldupdate` + - `UpgradeProgress` + - `getTotalFiles` -> `getTotalFileFixState`,不是一对一 + - `addTotalFiles` -> `addTotalFileFixOperations`,不是一对一 + - `getTypeFileFixStats`, `getRunningFileFixerStats` - 获取特定文件组的修复器统计信息。 + - `incrementFinishedOperations`, `incrementFinishedOperationsBy` - 增加已完成操作的数量。 + - `setType`, `getType` - 获取升级进度的类型。 + - `setApplicableFixerAmount` - 设置运行中文件修复器的已完成操作总数。 + - `incrementRunningFileFixer` - 增加已完成操作的数量。 + - `logProgress` - 每秒记录一次升级进度。 + - `$FileFixStats` - 已执行/已完成操作的计数器。 + - `$Type` - 对世界数据执行的升级类型。 + - `WorldUpgrader` 不再接受 `WorldData` + - `STATUS_*` 消息已合并到 `UpgradeStatusTranslator` 中 + - 这也包含特定的 datafix 类型 + - `running`, `finished`, `progress`, `totalChunks`, `totalFiles`, `converted`, `skipped`, `progressMap`, `status` 都已移至 `UpgradeProgress`,存储在 `upgradeProgress` 中 + - `getProgress` -> `getTotalProgress`,不是一对一 + - `$AbstractUpgrader`, `$SimpleRegionStorageUpgrader` -> `RegionStorageUpgrader`,不再接受提供的 `LegacyTagFixer`,不是一对一 + - `$ChunkUpgrader`, `$EntityUpgrader`, `$PoiUpgrader` 现在只是在 `WorldUpgrader#work` 中构造 + - `$Builder#setLegacyFixer` 已移除 + - `$ChunkUpgrader#tryProcessOnePosition` 已部分抽象为 `getDataFixContentTag`, `verifyChunkPosAndEraseCache`, `verifyChunkPos` + - `$FileToUpgrade` -> `FileToUpgrade` +- `net.minecraft.world.level.ChunkPos#getRegionX`, `getRegionZ` - 获取区块所在的区域。 +- `net.minecraft.world.level.chunk.ChunkGenerator#getTypeNameForDataFixer` 现在返回一个可选的 `Identifier` 而不是 `ResourceKey` +- `net.minecraft.world.level.chunk.storage` + - `LegacyTagFixer` 接口已移除 + - `RecreatingSimpleRegionStorage` 不再接受提供的 `LegacyTagFixer` + - `SimpleRegionStorage` 不再接受提供的 `LegacyTagFixer` + - `markChunkDone` 已移除 +- `net.minecraft.world.level.levelgen.structure` + - `LegacyStructureDataHandler` 类已移除 + - `StructureFeatureIndexSavedData` 类已移除 +- `net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager` + - `STRUCTURE_RESOURCE_DIRECTORY_NAME` -> `STRUCTURE_DIRECTORY_NAME`,不是一对一 + - `WORLD_STRUCTURE_LISTER`, `RESOURCE_TEXT_STRUCTURE_LISTER` - 结构 nbt 的 ID 转换器。 + - `save` 现在有一个接受路径、`StructureTemplate` 和 `boolean`(是否将数据写为文本)的重载 + - `createAndValidatePathToGeneratedStructure` -> `TemplatePathFactory#createAndValidatePathToStructure`,不是一对一 + - `worldTemplates`, `testTemplates` - 模板路径工厂。 +- `net.minecraft.world.level.levelgen.structure.templatesystem.loader` + - `DirectoryTemplateSource` - 某个目录的模板源。 + - `ResourceManagerTemplateSource` - 资源管理器的模板源。 + - `TemplatePathFactory` - 获取某个结构路径的工厂。 + - `TemplateSource` - 结构模板的加载器。 +- `net.minecraft.world.level.storage` + - `LevelStorageSource` + - `getLevelDataAndDimensions` 现在接受 `$LevelStorageAccess` + - `readExistingSavedData` - 读取任何现有的保存数据。 + - `writeGameRules` - 将游戏规则写入保存数据。 + - `$LevelStorageAccess#releaseTemporarilyAndRun` - 释放访问并在重新创建锁之前运行。 + - `collectIssues` - 收集尝试升级数据时的任何问题。 + - `getSummary` -> `fixAndGetSummary`, `fixAndGetSummaryFromTag`;不是一对一 + - `getDataTag` -> `getUnfixedDataTag`,不是一对一 + - `getDataTagFallback` -> `getUnfixedDataTagWithFallback`,不是一对一 + - `saveDataTag` 不再接受 `RegistryAccess` + - `saveLevelData` - 保存 level dat。 + - `LevelSummary` 现在接受是否需要文件修复 + - `UPGRADE_AND_PLAY_WORLD` - 告诉用户升级他们的世界并游玩的组件。 + - `requiresFileFixing` - 关卡是否需要文件修复。 + - `$BackupStatus#FILE_FIXING_REQUIRED` - 是否需要文件修复才能在此版本上玩此关卡。 + +### 聊天权限 + +聊天系统现在有一组相关的权限,指示玩家可以发送或接收哪些消息。`Permissions#CHAT_SEND_MESSAGES` 和 `CHAT_SEND_COMMANDS` 分别确定玩家是否可以发送消息或命令。同样,`CHAT_RECEIVE_PLAYER_MESSAGES` 和 `CHAT_RECEIVE_SYSTEM_MESSAGES` 确定玩家是否可以接收来自其他玩家或系统的消息。请注意,所有这些权限仅在客户端处理,不在服务器端验证。 + +- `net.minecraft.SharedConstants#DEBUG_CHAT_DISABLED` - 禁用聊天框的标志。 +- `net.minecraft.client` + - `GuiMessage` -> `.multiplayer.chat.GuiMessage` + - `GuiMessageTag` -> `.multiplayer.chat.GuiMessageTag` + - `Minecraft` + - `getChatStatus` -> `computeChatAbilities`,不是一对一 + - `$ChatStatus` -> `ChatRestriction`,不是一对一 +- `net.minecraft.client.gui.Gui#setChatDisabledByPlayerShown`, `isShowingChatDisabledByPlayer` - 处理聊天是否被显示的玩家禁用。 +- `net.minecraft.client.gui.components` + - `ChatComponent` + - `GO_TO_RESTRICTIONS_SCREEN` - 重定向到限制屏幕的标识符。 + - `setVisibleMessageFilter` - 设置消息过滤函数。 + - `render`, `captureClickableText` 现在接受 `$DisplayMode` 而不是表示玩家是否在聊天的 `boolean` + - `addMessage` 现在是 private + - 拆分为 `addClientSystemMessage`, `addServerSystemMessage`, `addPlayerMessage` + - `$DisplayMode` - 聊天应如何显示。 + - `CommandSuggestions` + - `USAGE_FORMAT`, `USAGE_OFFSET_FROM_BOTTOM`, `LINE_HEIGHT` - 显示命令建议的常量。 + - `setRestrictions` - 设置可以键入的消息的限制。 + - `hasAllowedInput` - 聊天框是否允许输入。 +- `net.minecraft.client.gui.screens.ChatScreen#USAGE_BACKGROUND_COLOR` - 命令建议框的背景颜色。 +- `net.minecraft.client.gui.screens.multiplayer.RestrictionsScreen` - 用于设置玩家聊天框限制的屏幕。 +- `net.minecraft.client.gui.screens.reporting.ReportPlayerScreen` 现在接受一个 `boolean` 表示聊天是否被禁用或阻止 +- `net.minecraft.client.multiplayer.chat` + - `ChatAbilities` - 玩家在聊天框上聊天的限制集。 + - `ChatListener` + - `handleSystemMessage` 的 `boolean` 现在用于表示系统是否为远程而不是覆盖 + - 覆盖现在通过 `handleOverlay` 处理 + - `GuiMessageSource` - 消息的来源。 +- `net.minecraft.client.player.LocalPlayer` 现在接受 `ChatAbilities` + - `chatAbilities`, `refreshChatAbilities` - 处理聊天能力。 +- `net.minecraft.server.permissions.Permissions` + - `CHAT_SEND_MESSAGES` - 玩家是否可以发送聊天消息。 + - `CHAT_SEND_COMMANDS` - 玩家是否可以发送命令。 + - `CHAT_RECEIVE_PLAYER_MESSAGES` - 玩家是否可以接收其他玩家的消息。 + - `CHAT_RECEIVE_SYSTEM_MESSAGES` - 玩家是否可以接收系统消息。 + - `CHAT_PERMISSIONS` - 一组可用的聊天权限。 +- `net.minecraft.world.entity.player.Player#displayClientMessage` 拆分为当覆盖消息为 `false` 时的 `sendSystemMessage`,以及当覆盖消息为 `true` 时的 `sendOverlayMessage` + +### 更多实体声音变种注册表 + +猫、鸡、牛和猪现在有了声音变体:一个数据包注册表对象,指定实体播放的声音。这不需要直接与实际的实体变体相关联,因为它只定义实体播放的声音,通常在 `Mob#finalizeSpawn` 期间。 + +对于牛: + +```json5 +// 文件位于: +// - `data/examplemod/cow_sound_variant/example_cow_sound.json` +{ + // 空闲时随机播放的声音事件的注册表名称。 + "ambient_sound": "minecraft:entity.cow.ambient", + // 被杀死时播放的声音事件的注册表名称。 + "death_sound": "minecraft:entity.cow.death", + // 受伤时播放的声音事件的注册表名称。 + "hurt_sound": "minecraft:entity.cow.hurt", + // 踩踏时播放的声音事件的注册表名称。 + "step_sound": "minecraft:entity.cow.step" +} +``` + +对于鸡和猪: + +```json5 +// 文件位于: +// - `data/examplemod/chicken_sound_variant/example_chicken_sound.json` +{ + // 实体年龄大于或等于 0(成年)时播放的声音。 + "adult_sounds": { + // 空闲时随机播放的声音事件的注册表名称。 + "ambient_sound": "minecraft:entity.chicken.ambient", + // 被杀死时播放的声音事件的注册表名称。 + "death_sound": "minecraft:entity.chicken.death", + // 受伤时播放的声音事件的注册表名称。 + "hurt_sound": "minecraft:entity.chicken.hurt", + // 踩踏时播放的声音事件的注册表名称。 + "step_sound": "minecraft:entity.chicken.step" + }, + // 实体年龄小于 0(幼年)时播放的声音。 + "baby_sounds": { + "ambient_sound": "minecraft:entity.baby_chicken.ambient", + "death_sound": "minecraft:entity.baby_chicken.death", + "hurt_sound": "minecraft:entity.baby_chicken.hurt", + "step_sound": "minecraft:entity.baby_chicken.step" + } +} +``` + +对于猪: + +```json5 +// 文件位于: +// - `data/examplemod/pig_sound_variant/example_pig_sound.json` +{ + // 实体年龄大于或等于 0(成年)时播放的声音。 + "adult_sounds": { + // 空闲时随机播放的声音事件的注册表名称。 + "ambient_sound": "minecraft:entity.pig.ambient", + // 被杀死时播放的声音事件的注册表名称。 + "death_sound": "minecraft:entity.pig.death", + // 进食时播放的声音事件的注册表名称。 + "eat_sound": "minecraft:entity.pig.eat", + // 受伤时播放的声音事件的注册表名称。 + "hurt_sound": "minecraft:entity.pig.hurt", + // 踩踏时播放的声音事件的注册表名称。 + "step_sound": "minecraft:entity.pig.step" + }, + // 实体年龄小于 0(幼年)时播放的声音。 + "baby_sounds": { + "ambient_sound": "minecraft:entity.baby_pig.ambient", + "death_sound": "minecraft:entity.baby_pig.death", + "eat_sound": "minecraft:entity.baby_pig.eat", + "hurt_sound": "minecraft:entity.baby_pig.hurt", + "step_sound": "minecraft:entity.baby_pig.step" + } +} +``` + +对于猫: + +```json5 +// 文件位于: +// - `data/examplemod/cat_sound_variant/example_cat_sound.json` +{ + // 实体年龄大于或等于 0(成年)时播放的声音。 + "adult_sounds": { + // 驯服后空闲时随机播放的声音事件的注册表名称。 + "ambient_sound": "minecraft:entity.cat.ambient", + // 未驯服且被食物诱惑时播放的声音事件的注册表名称。 + "beg_for_food_sound": "minecraft:entity.cat.beg_for_food", + // 被杀死时播放的声音事件的注册表名称。 + "death_sound": "minecraft:entity.cat.death", + // 被喂食时播放的声音事件的注册表名称。 + "eat_sound": "minecraft:entity.cat.eat", + // 发出嘶嘶声时播放的声音事件的注册表名称,通常针对追逐的幻翼。 + "hiss_sound": "minecraft:entity.cat.hiss", + // 受伤时播放的声音事件的注册表名称。 + "hurt_sound": "minecraft:entity.cat.hurt", + // 发出呼噜声时播放的声音事件的注册表名称,通常在恋爱或躺下时。 + "purr_sound": "minecraft:entity.cat.purr", + // 驯服后 25% 空闲时随机播放的声音事件的注册表名称。 + "purreow_sound": "minecraft:entity.cat.purreow", + // 未驯服时空闲时随机播放的声音事件的注册表名称。 + "stray_ambient_sound": "minecraft:entity.cat.stray_ambient" + }, + // 实体年龄小于 0(幼年)时播放的声音。 + "baby_sounds": { + "ambient_sound": "minecraft:entity.baby_cat.ambient", + "beg_for_food_sound": "minecraft:entity.baby_cat.beg_for_food", + "death_sound": "minecraft:entity.baby_cat.death", + "eat_sound": "minecraft:entity.baby_cat.eat", + "hiss_sound": "minecraft:entity.baby_cat.hiss", + "hurt_sound": "minecraft:entity.baby_cat.hurt", + "purr_sound": "minecraft:entity.baby_cat.purr", + "purreow_sound": "minecraft:entity.baby_cat.purreow", + "stray_ambient_sound": "minecraft:entity.baby_cat.stray_ambient" + } +} +``` + +- `net.minecraft.core.registries.Registries` + - `CAT_SOUND_VARIANT` - 猫应发出的声音的注册表键。 + - `CHICKEN_SOUND_VARIANT` - 鸡应发出的声音的注册表键。 + - `COW_SOUND_VARIANT` - 牛应发出的声音的注册表键。 + - `PIG_SOUND_VARIANT` - 猪应发出的声音的注册表键。 +- `net.minecraft.network.synched.EntityDataSerializers` + - `CAT_SOUND_VARIANT` - 猫应发出的声音的实体序列化器。 + - `CHICKEN_SOUND_VARIANT` - 鸡应发出的声音的实体序列化器。 + - `COW_SOUND_VARIANT` - 牛应发出的声音的实体序列化器。 + - `PIG_SOUND_VARIANT` - 猪应发出的声音的实体序列化器。 +- `net.minecraft.sounds.SoundEvents` + - `CAT_*` 声音现在是 `Holder$Reference` 或存储在 `CAT_SOUNDS` 映射中 + - `CHICKEN_*` 声音现在是 `Holder$Reference` 或存储在 `CHICKEN_SOUNDS` 映射中 + - `COW_*` 声音存储在 `COW_SOUNDS` 映射中 + - `PIG_*` 声音现在是 `Holder$Reference` 或存储在 `PIG_SOUNDS` 映射中 +- `net.minecraft.world.entity.animal.chicken` + - `ChickenSoundVariant` - 鸡变体播放的声音。 + - `ChickenSoundVariants` - 所有原版鸡变体。 +- `net.minecraft.world.entity.animal.cow` + - `AbstractCow#getSoundSet` - 获取牛发出的声音。 + - `CowSoundVariant` - 牛变体播放的声音。 + - `CowSoundVariants` - 所有原版牛变体。 +- `net.minecraft.world.entity.animal.feline` + - `CatSoundVariant` - 猫变体播放的声音。 + - `CatSoundVariants` - 所有原版猫变体。 +- `net.minecraft.world.entity.animal.chicken` + - `ChickenSoundVariant` - 鸡变体播放的声音。 + - `ChickenSoundVariants` - 所有原版鸡变体。 +- `net.minecraft.world.entity.animal.pig` + - `PigSoundVariant` - 猪变体播放的声音。 + - `PigSoundVariants` - 所有原版猪变体。 + +### 音频变更 + +音频设备现在通过 `DeviceTracker` 处理,根据底层机器,允许机器在音频设备更改时发送系统事件,或使用标准轮询间隔重新查询可用设备列表。 + +- `com.mojang.blaze3d.audio` + - `AbstractDeviceTracker` - 用于管理音频设备更改的抽象跟踪器。 + - `CallbackDeviceTracker` - 使用 SOFT 系统事件回调确定更改的设备跟踪器。 + - `DeviceList` - 所有音频设备的列表,包括默认设备(如果可用)。 + - `DeviceTracker` - 用于管理音频设备更改的接口。 + - `Library` + - `NO_DEVICE` 现在是 `public` 而不是 `private` + - `init` 现在接受 `DeviceList` + - `getDefaultDeviceName` 移至 `DeviceList#defaultDevice` + - `getCurrentDeviceName` -> `currentDeviceName` + - `hasDefaultDeviceChanged` 移至 `AbstractDeviceTracker`,不是一对一 + - `getAvailableSoundDevices` 移至 `DeviceList`,不是一对一 + - `createDeviceTracker` - 创建用于管理音频设备更改的跟踪器。 + - `getDebugString` -> `getChannelDebugString` + - `PollingDeviceTracker` - 使用轮询间隔确定更改的设备跟踪器。 + - `SoundBuffer` + - `format` - 声音流的格式。 + - `size` - 声音流中的字节数。 + - `isValid` - 声音流是否处于有效状态。 +- `net.minecraft.client.Options` + - `DEFAULT_SOUND_DEVICE` 现在是 `private` 而不是 `public` + - `isSoundDeviceDefault` - 检查设备是否为默认音频设备。 +- `net.minecraft.client.gui.components.debug` + - `DebugEntrySoundCache` - 显示已加载声音流当前缓存的调试条目。 + - `DebugScreenEntries#SOUND_CACHE` - 声音流缓存调试信息的标识符。 +- `net.minecraft.client.sounds` + - `SoundBufferLibrary` + - `enumerate` - 循环所有缓存的声音,输出其 id、大小和格式。 + - `$DebugOutput` - 接受某些声音元数据的接口。 + - `$Counter` - 跟踪声音流数量和总大小的输出实现。 + - `SoundEngine` + - `getDebugString` -> `getChannelDebugString`, `getSoundCacheDebugStats`; 不是一对一 + - `$DeviceCheckState` 已移除 + - `SoundManager#getDebugString` -> `getChannelDebugString`, `getSoundCacheDebugStats`; 不是一对一 + +### 输入消息编辑器支持 + +Minecraft 现在支持输入消息编辑器(IME),允许输入来自中文或印地语等语言的复杂字符,而不是临时的预编辑文本。预编辑文本在游戏内显示为覆盖层,而主机操作系统提供的任何其他 IME 功能。通过此添加,可以通过 `GuiEventListener#preeditUpdated` 在屏幕中添加支持,或使用实现该方法的现有原版小部件。 + +- `com.mojang.blaze3d.platform` + - `InputConstants#setupKeyboardCallbacks` 现在接受 `GLFWCharCallbackI` 而不是 `GLFWCharModsCallbackI` 用于字符键入回调,`GLFWPreeditCallbackI` 用于通过输入方法编辑器处理复杂字符输入,以及 `GLFWIMEStatusCallbackI` 用于通知编辑器的状态 + - `MessageBox` - 用于创建操作系统原生消息框的工具。 + - `TextInputManager` - 用于处理游戏窗口中文本输入的管理器。 + - `setTextInputArea` - 设置预测文本光标应出现的区域。 + - `startTextInput`, `stopTextInput` - 处理切换输入消息编辑器。 +- `net.minecraft.client` + - `KeyboardHandler` + - `resubmitLastPreeditEvent` - 重复发送的最后一个预编辑事件。 + - `submitPreeditEvent` - 告知监听器预编辑文本已更新。 + - `Minecraft` + - `textInputManager` - 返回输入管理器。 + - `onTextInputFocusChange` - 根据文本输入是否聚焦更改编辑器输入状态。 +- `net.minecraft.client.gui.GuiGraphics` + - `setPreeditOverlay` - 设置绘制预编辑文本的覆盖层。 + - `renderDeferredElements` 现在接受鼠标位置的 `int` 和游戏时间增量刻 `float` +- `net.minecraft.client.gui.components` + - `IMEPreeditOverlay` - 用于显示输入消息编辑器预编辑文本的覆盖层。 + - `TextCursorUtils` - 绘制文本光标的工具。 +- `net.minecraft.client.gui.components.events.GuiEventListener#preeditUpdated` - 监听预编辑文本何时更改。 +- `net.minecraft.client.input` + - `CharacterEvent` 不再接受修饰符 `int` 集 + - `PreeditEvent` - 一个包含预编辑文本以及光标位置的事件。 + +### 炼药锅交互调度器 + +炼药锅的交互已进行了一些重组,所有注册已从 `CauldronInteraction` 移至 `CauldronInteractions`。此外,炼药锅类型的底层 `$InteractionMap` 已被 `$Dispatcher` 取代,它可以为标签和物品注册交互。标签在物品之前检查。 + +```java +CauldronInteractions.EMPTY.put( + // 要使用的物品或 TagKey + ItemTags.WOOL, + // 要应用的炼药锅交互 + (state, level, pos, player, hand, itemInHand) -> InteractionResult.TRY_WITH_EMPTY_HAND +); +``` + +- `net.minecraft.core.cauldron` + - `CauldronInteraction` + - 所有关于将交互注册到映射的字段已移至 `CauldronInteractions` + - `INTERACTIONS` -> `CauldronInteractions#ID_MAPPER`,现在是 `private` + - `DEFAULT` - 与炼药锅的默认交互 + - `$InteractionMap` -> `$Dispatcher`,不是一对一 + - `CauldronInteractions` - 所有原版炼药锅交互。 + +### 基于规则的方块状态提供者 + +`RuleBasedStateProvider` 现在扩展了 `BlockStateProvider`,允许它在需要状态提供器的任何地方使用。其实现没有改变,现在只需要将 `rule_based_state_provider` 指定为 `type`。因此,所有使用 `RuleBasedBlockStateProvider` 的配置都已放宽,以允许任何 `BlockStateProvider`。 + +- `net.minecraft.world.level.levelgen.feature.configurations` + - `DiskConfiguration` 现在接受一个 `BlockStateProvider` 而不是 `RuleBasedBlockStateProvider` + - `TreeConfiguration` 现在接受一个 `BlockStateProvider` 而不是 `RuleBasedBlockStateProvider` + - `belowTrunkProvider` 现在是 `BlockStateProvider` 而不是 `RuleBasedBlockStateProvider` + - `$TreeConfigurationBuilder` 现在接受一个 `BlockStateProvider` 而不是 `RuleBasedBlockStateProvider` +- `net.minecraft.world.level.levelgen.feature.stateproviders` + - `BlockStateProvider#getOptionalState` - 获取方块的状态,否则为 `null`。 + - `BlockStateProviderType#RULE_BASED_STATE_PROVIDER` - 基于规则的状态提供器的类型。 + - `RuleBasedBlockStateProvider` -> `RuleBasedStateProvider`,现在是一个类,实现 `BlockStateProvider` + - 构造函数现在可以接受一个可空的回退 `BlockStateProvider` + - `ifTrueThenProvide` - 如果谓词为真,则提供给定的方块。 + - `simple` -> `always` + - `$Builder` - 用于构建放置方块规则的构建器。 + +### 流体逻辑重组 + +通用实体流体移动已移至单独的 `EntityFluidInteraction` 类中,其中所有跟踪的流体都会更新,然后在层时间可能被水流推动。 + +- `net.minecraft.world.entity` + - `Entity` + - `updateInWaterStateAndDoFluidPushing` -> `updateFluidInteraction`,不是一对一 + - `updateFluidHeightAndDoFluidPushing` -> `EntityFluidInteraction#update` + - `getFluidInteractionBox` - 获取实体在流体交互期间的边界框。 + - `modifyPassengerFluidInteractionBox` - 返回实体骑乘在某些交通工具中时修改后的边界框。 + - `EntityFluidInteraction` - 用于管理实体流体交互和基本移动的处理程序。 +- `net.minecraft.world.level.chunk.LevelChunkSection#hasFluid` - 区块部分是否包含流体方块。 + +### 移除随机斑块特征 + +随机斑块特征已被完全移除,转而使用像大多数其他特征一样的位置放置。要转换,配置中定义为 `ConfiguredFeature` 的部分应该是它自己的文件。然后,位置放置应指定 `CountPlacement`,后跟使用 `TrapezoidInt` 的 `RandomOffsetPlacement`,最后是 `BlockPredicateFilter`: + +```json5 +// 在 `data/examplemod/worldgen/configured_feature/example_configured_feature.json` 中 +{ + // 之前 config.feature.feature + "type": "minecraft:simple_block", + "config": { + "to_place": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:sweet_berry_bush", + "Properties": { + "age": "3" + } + } + } + } +} + +// 在 `data/examplemod/worldgen/placed_feature/example_placed_feature.json` 中 +{ + "feature": "examplemod:example_configured_feature", + "placement": [ + { + // 之前 config.tries + "type": "minecraft:count", + "count": 96 + }, + { + "type": "minecraft:random_offset", + "xz_spread": { + // 之前 config.xz_spread + // min 和 max 是加法逆元 + "type": "minecraft:trapezoid", + "max": 7, + "min": -7, + "plateau": 0 + }, + "y_spread": { + // 之前 config.y_spread + "type": "minecraft:trapezoid", + "max": 3, + "min": -3, + "plateau": 0 + } + }, + { + // 之前 config.feature.placement + // 这包含原始放置特征的位置放置 + "type": "minecraft:block_predicate_filter", + "predicate": { + "type": "minecraft:all_of", + "predicates": [ + { + "type": "minecraft:matching_block_tag", + "tag": "minecraft:air" + }, + { + "type": "minecraft:matching_blocks", + "blocks": "minecraft:grass_block", + "offset": [ 0, -1, 0 ] + } + ] + } + } + ] +} +``` + +- `net.minecraft.data.worldgen.features` + - `FeatureUtils#simpleRandomPatchConfiguration`, `simplePatchConfiguration` 已移除 + - 通常被 `Feature#SIMPLE_BLOCK` 替换,带有随机偏移和过滤器的位置放置 + - `NetherFeatures#PATCH_*` 字段不再有 `PATCH_*` 前缀 + - `VegetationFeatures` + - `PATCH_*` 字段不再有 `PATCH_*` 前缀 + - `WILDFLOWERS_BIRCH_FOREST`, `WILDFLOWERS_MEADOW` -> `WILDFLOWER`,不是一对一 + - `PALE_FOREST_FLOWERS` -> `PALE_FOREST_FLOWER` +- `net.minecraft.world.level.biome.BiomeGenerationSettings#getFlowerFeatures` -> `getBoneMealFeatures` +- `net.minecraft.world.level.levelgen.feature` + - `ConfiguredFeature#getFeatures` -> `getSubFeatures`,现在返回一个持有者包装的 `ConfiguredFeature` 流,不包含此特征 + - `Feature` + - `FLOWER`, `NO_BONEMEAL_FLOWER` 已移除 + - `RANDOM_PATCH` 已移除 + - `RandomPatchFeature` 类已移除 +- `net.minecraft.world.level.levelgen.feature.configurations` + - `FeatureConfiguration#getFeatures` -> `getSubFeatures`,现在返回一个持有者包装的 `ConfiguredFeature` 流 + - `RandomPatchConfiguration` 记录已移除 +- `net.minecraft.world.level.levelgen.placement.PlacedFeature#getFeatures` 现在返回一个持有者包装的 `ConfiguredFeature` 流,并连接了此特征 + +### 特定逻辑变更 + +- 画中画提交调用现在使用 `0xF000F0` 而不是 `0x000000` 作为光照坐标。 +- `net.minecraft.client.multiplayer.RegistryDataCollector#collectGameRegistries` 的 `boolean` 参数现在仅处理从同步注册表以及标签更新组件。 +- `net.minecraft.client.renderer.RenderPipelines#VIGNETTE` 现在将 alpha 与源为零和目标为一混合。 +- `net.minecraft.server.packs.PathPackResources#getResource`, `listPath`, `listResources` 使用标识符的命名空间首先解析路径。 +- `net.minecraft.world.entity.EntitySelector#CAN_BE_PICKED` 现在可以找到旁观者模式下的实体,假设 `Entity#isPickable` 为 true。 +- `net.minecraft.world.entity.ai.sensing.NearestVisibleLivingEntitySensor#requires` 默认不再实现。 +- `net.minecraft.world.level.levelgen.WorldOptions#generate_features` JSON 中的字段已重命名为 `generate_structures` 以匹配其 Java 字段名称。 +- `net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate#ONLY_IN_AIR_PREDICATE` 现在匹配空气标签而不是仅仅空气方块。 +- `net.minecraft.world.level.timers` + - `FunctionCallback`, `FunctionTagCallback` 现在在序列化时使用 `id` 而不是 `Name` + - `TimerCallbacks` 现在在序列化时使用 `type` 而不是 `Type` + +### 数据组件新增 + +- `dye` - 将物品设置为染料材料,在特定情况下使用。 +- `additional_trade_cost` - 按指定金额偏移交易成本的修饰符。 +- `pig/sound_variant` - 猪应发出的声音。 +- `cow/sound_variant` - 牛应发出的声音。 +- `chicken/sound_variant` - 鸡应发出的声音。 +- `cat/sound_variant` - 猫应发出的声音。 + +### 环境属性新增 + +- `visual/block_light_tint` - 着色方块发出的光的颜色。 +- `visual/night_vision_color` - 夜视激活时的颜色。 +- `visual/ambient_light_color` - 环境中环境光的颜色。 + +### 标签更改 + +- `minecraft:block` + - `bamboo_plantable_on` -> `supports_bamboo` + - `mushroom_grow_block` -> `overrides_mushroom_light_requirement` + - `small_dripleaf_placeable` -> `supports_small_dripleaf` + - `big_dripleaf_placeable` -> `supports_big_dripleaf` + - `dry_vegetation_may_place_on` -> `supports_dry_vegetation` + - `snow_layer_cannot_survive_on` -> `cannot_support_snow_layer` + - `snow_layer_can_survive_on` -> `support_override_snow_layer` + - `enables_bubble_column_drag_down` + - `enables_bubble_column_push_up` + - `supports_vegetation` + - `supports_crops` + - `supports_stem_crops` + - `supports_stem_fruit` + - `supports_pumpkin_stem` + - `supports_melon_stem` + - `supports_pumpkin_stem_fruit` + - `supports_melon_stem_fruit` + - `supports_sugar_cane` + - `supports_sugar_cane_adjacently` + - `supports_cactus` + - `supports_chorus_plant` + - `supports_chorus_flower` + - `supports_nether_sprouts` + - `supports_azalea` + - `supports_warped_fungus` + - `supports_crimson_fungus` + - `supports_mangrove_propagule` + - `supports_hanging_mangrove_propagule` + - `supports_nether_wart` + - `supports_crimson_roots` + - `supports_warped_roots` + - `supports_wither_rose` + - `supports_cocoa` + - `supports_lily_pad` + - `supports_frogspawn` + - `support_override_cactus_flower` + - `cannot_support_seagrass` + - `cannot_support_kelp` + - `grows_crops` + - `mud` + - `moss_blocks` + - `grass_blocks` + - `substrate_overworld` + - `beneath_tree_podzol_replaceable` + - `beneath_bamboo_podzol_replaceable` + - `cannot_replace_below_tree_trunk` + - `ice_spike_replaceable` + - `forest_rock_can_place_on` + - `huge_brown_mushroom_can_place_on` + - `huge_red_mushroom_can_place_on` + - `prevents_nearby_leaf_decay` +- `minecraft:enchantment` + - `trades/desert_special` 已移除 + - `trades/jungle_special` 已移除 + - `trades/plains_special` 已移除 + - `trades/savanna_special` 已移除 + - `trades/snow_special` 已移除 + - `trades/swamp_special` 已移除 + - `trades/taiga_special` 已移除 +- `minecraft:entity_type` + - `cannot_be_age_locked` +- `minecraft:fluid` + - `supports_sugar_cane_adjacently` + - `supports_lily_pad` + - `supports_frogspawn` + - `bubble_column_can_occupy` +- `minecraft:item` + - `metal_nuggets` + - `dyeable` 已移除,拆分为: + - `dyes` + - `loom_dyes` + - `loom_patterns` + - `cauldron_can_remove_due` + - `cat_collar_dyes` + - `wolf_collar_dyes` + - `mud` + - `moss_blocks` + - `grass_blocks` +- `minecraft:potion` + - `tradable` +- `minecraft:worldgen/configured_feature` + - `can_spawn_from_bone_meal` + +### 添加列表 + +- `net.minecraft.advancements.criterion` + - `FoodPredicate` - 可以检查食物水平和饱和度的标准谓词。 + - `MinMaxBounds` + - `validateContainedInRange` - 返回一个验证目标范围并将结果作为数据结果返回的函数。 + - `$Bounds#asRange` - 将边界转换为 `Range`。 +- `net.minecraft.client` + - `Minecraft#sendLowDiskSpaceWarning` - 发送系统通知以提示磁盘空间不足。 + - `Options#keyDebugLightmapTexture` - 显示光照图纹理的键映射。 +- `net.minecraft.client.gui.components` + - `AbstractScrollArea` + - `scrollbarWidth` - 滚动条的宽度。 + - `defaultSettings` - 根据滚动速率构建默认滚动条设置。 + - `$ScrollbarSettings` - 包含滚动条元数据的记录。 + - `DebugScreenOverlay` + - `showLightmapTexture` - 是否在覆盖层上渲染光照图纹理。 + - `toggleLightmapTexture` - 切换是否应渲染光照图纹理。 + - `ScrollableLayout` + - `setMinHeight` - 设置容器布局的最小高度。 + - `$ReserveStrategy` - 在布局内保留滚动条宽度时使用的策略。 + - `Tooltip` + - `component` - 要显示的工具提示的组件。 + - `style` - 用于计算工具提示背景和框架的标识符。 +- `net.minecraft.client.gui.components.debug` + - `DebugEntryDetailedMemory` - 显示详细内存使用情况的调试条目。 + - `DebugEntryLookingAt#getHitResult` - 获取相机实体的命中结果。 + - `DebugEntryLookingAtEntityTags` - 用于显示实体标签的调试条目。 + - `DebugScreenEntries` + - `DETAILED_MEMORY` - 详细内存使用调试条目的标识符。 + - `LOOKING_AT_ENTITY_TAGS` - 实体标签调试条目的标识符。 +- `net.minecraft.client.gui.navigation.FocusNavigationEvent$ArrowNavigation#with` - 设置导航的上一个焦点。 +- `net.minecraft.client.gui.screens.GenericWaitingScreene#createWaitingWithoutButton` - 创建一个不显示取消按钮的等待屏幕。 +- `net.minecraft.client.gui.screens.options` + - `DifficultyButtons` - 一个包含创建难度按钮的布局元素的类。 + - `HasGamemasterPermissionReaction` - 标记选项屏幕能够响应游戏管理员权限更改的接口。 + - `OptionsScreen#getLastScreen` - 返回导航到此屏幕的上一个屏幕。 + - `WorldOptionsScreen` - 包含玩家所在当前世界的选项的屏幕。 +- `net.minecraft.client.gui.screens.worldselection.EditWorldScreen#conditionallyMakeBackupAndShowToast` - 仅在传入的 `boolean` 为 true 时制作备份并显示提示;否则返回一个 `false` 的 future。 +- `net.minecraft.client.multiplayer.MultiPlayerGameMode#spectate` - 向服务器发送数据包,表示玩家将旁观给定的实体。 +- `net.minecraft.client.multiplayer.prediction.BlockStatePredictionHandler#onTeleport` - 当玩家在服务器上移动时运行,通常来自某种传送(例如命令、骑乘实体)。 +- `net.minecraft.client.renderer` + - `LightmapRenderStateExtractor` - 提取光照图的渲染状态。 + - `UiLightmap` - 用户界面中的光照图。 + - `RenderPipelines` + - `LINES_DEPTH_BIAS` - 一个将多边形深度偏移因子设置为 -1 并将单位设置为 -1 的渲染管线。 + - `ENTITY_CUTOUT_DISSOLVE` - 一个使用蒙版采样器将实体模型溶解到背景中的渲染管线。 +- `net.minecraft.client.renderer.rendertype.RenderType#hasBlending` - 管线是否定义了混合函数。 +- `net.minecraft.commands.ArgumentVisitor` - 用于访问命令参数的辅助类。 +- `net.minecraft.commands.arguments.selector.EntitySelector#COMPILABLE_CODEC` - 包装在 `EntitySelectorParser` 周围的 `CompilableString` 编解码器。 +- `net.minecraft.core.component.predicates` + - `DataComponentPredicates#VILLAGER_VARIANT` - 检查村民类型的谓词。 + - `VillagerTypePredicate` - 检查村民类型的谓词。 +- `net.minecraft.core.dispenser.SpawnEggItemBehavior` - 刷怪蛋的发射器行为。 +- `net.minecraft.core.registries.ConcurrentHolderGetter` - 从本地缓存读取引用的获取器,必要时同步到原始数据。 +- `net.minecraft.data` + - `BlockFamilies` + - `END_STONE` - 末地石变体的家族。 + - `getFamily` - 获取基础方块的家族(如果存在)。 + - `BlockFamily` + - `$Builder#tiles`, `$Variant#TILES` - 作为某个基础方块的瓷砖变体的方块。 + - `$Builder#bricks`, `$Variant#BRICKS` - 作为某个基础方块的砖块变体的方块。 + - `$Builder#cobbled`, `$Variant#COBBLED` - 作为某个基础方块的圆石变体的方块。 + - `DataGenerator$Uncached` - 不缓存任何关于生成文件信息的数据生成器。 +- `net.minecraft.data.recipes.RecipeProvider` + - `bricksBuilder`, `tilesBuilder` - 用于砖块和瓷砖方块变体的构建器。 + - `generateCraftingRecipe`, `generateStonecutterRecipe` - 为给定的方块家族生成适当的配方。 + - `getBaseBlock` -> `getBaseBlockForCrafting` + - `bredAnimal` - 如果玩家已使两只动物繁殖,则解锁配方。 +- `net.minecraft.gametest.framework` + - `GameTestEvent#createWithMinimumDelay` - 创建一个具有最小延迟的测试事件。 + - `GameTestHelper` + - `getBoundsWithPadding` - 获取带有指定填充的测试区域的边界框。 + - `runBeforeTestEnd` - 在测试结束前一 tick 运行 runnable。 + - `despawnItem` - 清除位置距离内的所有物品实体。 + - `discard` - 丢弃实体。 + - `setTime` - 设置维度默认时钟的时间。 + - `placeBlock` - 在相对位置和方向上放置给定的方块。 + - `GameTestInstance#padding` - 每个游戏测试周围间隔的方块数。 + - `GameTestSequence#thenWaitAtLeast` - 在运行 runnable 之前至少等待指定的 tick 数。 +- `net.minecraft.nbt.TextComponentTagVisitor` + - `$PlainStyling` - 将 nbt 存储在组件字面量映射中的样式。 + - `$RichStyling` - 带有语法高亮和格式化的 nbt 样式。 + - `$Styling` - 定义读取标签应如何样式的接口。 + - `$Token` - 用于更丰富地表示标签数据的令牌。 +- `net.minecraft.network.chat.ResolutionContext` - 组件被解析为字符串的上下文。 +- `net.minecraft.network.chat.contents.NbtContents#NBT_PATH_CODEC` - 包装在 `NbtPathArgument$NbtPath` 周围的 `CompilableString` 编解码器。 +- `net.minecraft.network.chat.contents.data.BlockDataSource#BLOCK_POS_CDEC` - 包装在 `Coordinates` 周围的 `CompilableString` 编解码器。 +- `net.minecraft.network.protocol.game` + - `ClientboundGameRuleValuesPacket` - 以字符串形式向客户端发送游戏规则值的数据包。 + - `ClientboundGamePacketListener#handleGameRuleValues` - 处理从服务器发送的游戏规则值。 + - `ClientboundLowDiskSpaceWarningPacket` - 发送给客户端警告机器磁盘空间不足的数据包。 + - `ClientGamePacketListener#handleLowDiskSpaceWarning` - 处理关于磁盘空间不足的警告数据包。 + - `ServerboundAttackPacket` - 发送给服务器关于玩家攻击了哪个实体的数据包。 + - `ServerboundClientCommandPacket$Action#REQUEST_GAMERULE_VALUES` - 从服务器请求游戏规则值。 + - `ServerboundSetGameRulePacket` - 从客户端发送设置游戏规则条目的数据包。 + - `ServerboundSpectateEntityPacket` - 发送给服务器关于玩家想要旁观哪个实体的数据包。 + - `ServerGamePacketListener` + - `handleAttack` - 处理玩家对实体的攻击。 + - `handleSpectateEntity` - 处理玩家想要旁观实体。 + - `handleSetGameRule` - 处理从客户端设置游戏规则。 +- `net.minecraft.resources` + - `FileToIdConverter#extensionMatches` - 检查标识符是否以指定的扩展名结尾。 + - `Identifier` + - `ALLOWED_NAMESPACE_CHARACTERS` - 标识符命名空间中允许的字符。 + - `resolveAgainst` - 通过检查 `//` 从给定根解析路径。 +- `net.minecraft.server` + - `Bootstrap#shutdownStdout` - 关闭 stdout 流。 + - `MinecraftServer` + - `DEFAULT_GAME_RULES` - 服务器的默认游戏规则。 + - `warnOnLowDiskSpace` - 如果磁盘空间低于 64 MiB,则发送警告。 + - `sendLowDiskSpaceWarning` - 发送磁盘空间不足的警告。 +- `net.minecraft.server.commands.SwingCommand` - 调用所有目标的 `LivingEntity#swing` 的命令。 +- `net.minecraft.server.level.ServerPlayer` + - `sendBuildLimitMessage` - 如果玩家在相应的 Y 方向上无法再建造,则发送覆盖消息。 + - `sendSpawnProtectionMessage` - 如果玩家因生成保护而无法修改地形,则发送覆盖消息。 +- `net.minecraft.server.packs.AbstractPackResources#loadMetadata` - 加载根目录的 `pack.mcmeta`。 +- `net.minecraft.server.packs.resources.ResourceMetadata$MapBased` - 将节存储在映射中的资源元数据。 +- `net.minecraft.tags.TagLoader$ElementLookup#fromGetters` - 根据元素是否需要,从给定的 `HolderGetter` 构造元素查找。 +- `net.minecraft.util` + - `ARGB#gray` - 根据给定的亮度获取灰度颜色。 + - `CompilableString` - 用于获取某些字符串模式并通过某些解析器将其编译为对象的工具。 + - `LightCoordsUtil` - 用于从光照值确定光照坐标的工具。 + - `ProblemReporter$MapEntryPathElement` - 映射中某个条目的路径元素。 + - `Util#allOfEnumExcept` - 获取除提供的值之外的所有枚举的集合。 +- `net.minecraft.util.thread.BlockableEventLoop#hasDelayedCrash` - 是否有排队的崩溃报告。 +- `net.minecraft.util.valueproviders.TrapezoidInt` - 使用梯形分布采样随机值。 +- `net.minecraft.world.InteractionHand#STREAM_CODEC` - 交互手的网络编解码器。 +- `net.minecraft.world.attribute` + - `AttributeType#toFloat` - 将属性值作为 `float` 获取,如果未定义则抛出异常。 + - `AttributeTypes#INTEGER` - 一个整数属性类型。 + - `LerpFunction#ofInteger` - 用于整数值的 lerp 函数。 +- `net.minecraft.world.attribute.modifier` + - `AttributeModifier#INTEGER_LIBRARY` - 用于整数的操作符库。 + - `IntegerModifier` - 将某些参数应用于整数值的修饰符。 +- `net.minecraft.world.entity` + - `AgeableMob` + - `AGE_LOCK_COOLDOWN_TICKS` - 在实体可以年龄锁定/解锁之前等待的 tick 数。 + - `ageLockParticleTimer` - 年龄锁定/解锁时显示粒子的时间。 + - `Entity` + - `applyEffectsFromBlocksForLastMovements` - 对实体应用上一次移动的方块效果,由物品使用。 + - `$Flags` - 一个注解,标记特定值为实体的标志位集。 + - `LivingEntity#getLiquidCollisionShape` - 返回实体尝试与液体碰撞时的边界。 + - `Mob` + - `asValidTarget` - 检查实体是否为该实体的有效目标(可以攻击)。 + - `getTargetUnchecked` - 获取原始目标而不检查其有效性。 + - `canAgeUp` - 实体是否可以成长为更成熟的变体。 + - `setAgeLocked`, `isAgeLocked` - 实体是否不能成长。 + - `NeutralMob#getTargetUnchecked` - 获取原始目标而不检查其有效性。 + - `TamableAnimal#feed` - 玩家将物品作为食物给予,根据存储的组件或某些默认值治愈它们。 +- `net.minecraft.world.entity.ai.behavior` + - `BehaviorControl#getRequiredMemories` - 行为所需的记忆列表。 + - `GoAndGiveItemsToTarget$ItemThrower` - 一个处理如果因该实体的行为而抛出物品时应发生什么的接口。 +- `net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder$TriggerWithResult#memories` - 行为所需的记忆列表。 +- `net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities#nearbyEntities` - 在该实体的跟随范围内的实体列表。 +- `net.minecraft.world.entity.decoration.LeashFenceKnotEntity` + - `getKnot` - 在给定位置找到结,否则返回空的可选值。 + - `createKnot` - 创建一个新结并将其添加到关卡中。 +- `net.minecraft.world.entity.monster.piglin` + - `PiglinAi` + - `MAX_TIME_BETWEEN_HUNTS` - 猪灵再次开始狩猎前的最大秒数。 + - `findNearbyAdultPiglins` - 返回此猪灵记忆中所有成年猪灵的列表。 +- `net.minecraft.world.entity.raid.Raid` + - `getBannerComponentPatch` - 获取旗帜图案的组件。 + - `getOminousBannerTemplate` - 获取不祥旗帜的堆叠模板。 +- `net.minecraft.world.inventory.SlotRanges` + - `MOB_INVENTORY_SLOT_OFFSET` - 生物物品栏的起始索引。 + - `MOB_INVENTORY_SIZE` - 生物物品栏的大小。 +- `net.minecraft.world.item.component.BundleContents#BEEHIVE_WEIGHT` - 蜂箱的重量。 +- `net.minecraft.world.item.enchantment.EnchantmentTarget#NON_DAMAGE_CODEC` - 仅允许攻击者和受害者附魔目标的编解码器。 +- `net.minecraft.world.level.block.BigDripleafBlock#canGrowInto` - 大垂滴叶是否可以生长到指定位置。 +- `net.minecraft.world.level.block.grower.TreeGrower#getMinimumHeight` - 如果存在,获取树干放置器的基本高度。 +- `net.minecraft.world.level.block.state` + - `BlockBehaviour$PostProcess` - 一个获取要标记为后处理的位置的接口。 + - `StateDefinition` + - `propertiesCodec` - 状态属性的映射编解码器。 + - `isSingletonState` - 定义是否只包含一个状态。 + - `StateHolder#isSingletonState` - 持有者是否只包含一个状态。 +- `net.minecraft.world.level.block.state.properties` + - `NoteBlockInstrument` + - `TRUMPET` - 铜块放置在音符盒下时发出的声音。 + - `TRUMPET_EXPOSED` - 斑驳的铜块放置在音符盒下时发出的声音。 + - `TRUMPET_OXIDIZED` - 氧化的铜块放置在音符盒下时发出的声音。 + - `TRUMPET_WEATHERED` - 锈蚀的铜块放置在音符盒下时发出的声音。 + - `Property$Value#valueName` - 获取值的名称。 +- `net.minecraft.world.level.dimension.DimensionDefaults` + - `BLOCK_LIGHT_TINT` - 方块光的默认着色。 + - `NIGHT_VISION_COLOR` - 夜视激活时的默认颜色。 + - `TURTLE_EGG_HATCH_CHANCE` - 海龟蛋在随机 tick 中孵化的几率。 +- `net.minecraft.world.level.gamerules.GameRule#getIdentifierWithFallback` - 获取游戏规则的标识符,否则获取未注册的标识符。 +- `net.minecraft.world.level.levelgen` + - `NoiseBasedChunkGenerator#getInterpolatedNoiseValue` - 获取给定位置的插值密度,如果 Y 超出噪声设置范围,则返回非数字值。 + - `NoiseChunk#getInterpolatedDensity` - 计算完整的噪声密度。 +- `net.minecraft.world.level.levelgen.feature.AbstractHugeMushroomFeature#MIN_MUSHROOM_HEIGHT` - 蘑菇的最小高度。 +- `net.minecraft.world.level.levelgen.feature.configurations.BlockBlobConfiguration` - 块团块特征的配置。 +- `net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer#getBaseHeight` - 返回树干的最小高度。 +- `net.minecraft.world.level.levelgen.placement.RandomOffsetPlacement#ofTriangle` - 创建一个梯形分布,用于选择 XZ 和 Y 范围。 +- `net.minecraft.world.level.material` + - `FluidState#isFull` - 流体量是否为八。 + - `LavaFluid#LIGHT_EMISSION` - 熔岩流体发出的光量。 +- `net.minecraft.world.level.pathfinder.PathType#BIG_MOBS_CLOSE_TO_DANGER` - 对于实体宽度大于一个方块的生物的路径惩罚。 +- `net.minecraft.world.level.storage.LevelStorageSource#writeWorldGenSettings` - 将世界生成设置写入其保存数据位置。 +- `net.minecraft.world.level.storage.loot.functions` + - `EnchantRandomlyFunction$Builder#withOptions` - 指定可用于随机附魔此物品的附魔。 + - `SetRandomDyesFunction` - 一个物品函数,从提供的列表中应用随机染料(如果物品在 `dyeable` 标签中)到 `DYED_COLOR` 组件。 + - `SetRandomPotionFunction` - 一个物品函数,从提供的列表中应用随机药水到 `POTION_CONTENTS` 组件。 +- `net.minecraft.world.level.storage.loot.parameters.LootContextParams#ADDITIONAL_COST_COMPONENT_ALLOWED` - 允许交易在所需堆叠或修饰符需要时产生额外成本。 +- `net.minecraft.world.level.storage.loot.predicates.EnvironmentAttributeCheck` - 检查给定属性是否与提供的值匹配的战利品条件。 +- `net.minecraft.world.level.storage.loot.providers.number` + - `EnvironmentAttributeValue` - 以浮点数形式获取属性值的提供器。 + - `Sum` - 对所有提供的数字提供器的值求和的提供器。 +- `net.minecraft.world.phys.AABB$Builder#isDefined` - 检查是否至少有一个点构成边界框。 + +### 更改列表 + +- `net.minecraft.advancements.criterion` + - `EntityTypePredicate#matches` 现在接受一个持有者包装的 `EntityType` 而不是原始类型本身 + - `KilledTrigger$TriggerInstance#entityPredicate` -> `entity` + - `PlayerPredicate` 现在接受一个 `FoodPredicate` + - 可以使用 `PlayerPredicate$Builder#setFood` 设置 +- `net.minecraft.client.gui` + - `GuiGraphics` + - `blit` 现在有一个接受 `GpuTextureView` 和 `GpuSampler` 的重载 + - `setTooltipForNextFrame` 现在有一个接受工具提示的 `FormattedCharSequence` 列表以及是否替换任何现有工具提示的重载 + - `ItemSlotMouseAction#onSlotClicked` 现在接受一个 `ContainerInput` 而不是 `ClickType` +- `net.minecraft.client.gui.components` + - `AbstractContainerWidget` 现在接受一个 `AbstractScrollArea$ScrollbarSettings` + - `AbstractScrollArea` 现在接受一个 `AbstractScrollArea$ScrollbarSettings` + - `scrollbarVisible` -> `scrollable` + - `scrollBarY` 现在是 public + - `scrollRate` 不再是抽象的 + - `AbstractTextAreaWidget` 现在接受一个 `AbstractScrollArea$ScrollbarSettings` + - `PopupScreen$Builder#setMessage` -> `addMessage`,不是一对一 + - `ScrollableLayout$Container` 现在接受一个 `AbstractScrollArea$ScrollbarSettings` + - `Tooltip#create` 现在有一个接受可选的 `TooltipComponent` 和样式 `Identifier` 的重载 +- `net.minecraft.client.gui.components.debug` + - `DebugEntryLookingAtBlock`, `DebugEntryLookingAtFluid` -> `DebugEntryLookingAt` + - 更具体地说,`$BlockStateInfo`, `$BlockTagInfo`, `$FluidStateInfo`, `$FluidTagInfo` + - `DebugEntryLookingAtEntity#GROUP` 现在是 public + - `DebugScreenEntries` + - `LOOKING_AT_BLOCK` -> `LOOKING_AT_BLOCK_STATE`, `LOOKING_AT_BLOCK_TAGS` + - `LOOKING_AT_FLUID` -> `LOOKING_AT_FLUID_STATE`, `LOOKING_AT_FLUID_TAGS` + - `DebugScreenEntryList` 现在接受一个 `DataFixer` +- `net.minecraft.client.gui.components.tabs.TabNavigationBar#setWidth` -> `updateWidth`,不是一对一 +- `net.minecraft.client.gui.navigation.FocusNavigationEvent$ArrowNavigation` 现在接受一个可空的 `ScreenRectangle` 用于上一个焦点 +- `net.minecraft.client.gui.screens` + - `ConfirmScreen#layout` 现在是 final + - `DemoIntroScreen` 被 `ClientPacketListener#openDemoIntroScreen` 取代,现在是 private + - `GenericWaitingScreen` 现在接受三个 `boolean`,分别表示是否显示加载点、取消按钮以及屏幕是否应在按下 ESC 时关闭 +- `net.minecraft.client.gui.screens.inventory.AbstractMountInventoryScreen#mount` 现在是 final +- `net.minecraft.client.gui.screens.options` + - `OptionsScreen` 现在实现 `HasGamemasterPermissionReaction` + - 构造函数现在接受一个 `boolean` 表示玩家是否当前在世界中 + - `createDifficultyButton` 现在在 `WorldOptionsScreen#createDifficultyButtons` 中处理,不是一对一 + - `WorldOptionsScreen` 现在实现 `HasGamemasterPermissionReaction` + - `createDifficultyButtons` -> `DifficultyButtons#create`,现在是 public +- `net.minecraft.client.multiplayer` + - `ClientLevel#syncBlockState` 现在可以接受一个可空的玩家位置 + - `MultiPlayerGameMode#handleInventoryMouseClick` 现在接受一个 `ContainerInput` 而不是 `ClickType` + - `WeatherEffectRenderer#render` 不再接受 `MultiBufferSource` +- `net.minecraft.client.renderer.block.ModelBlockRenderer$Cache#getLightColor` -> `getLightCoords` +- `net.minecraft.client.renderer.blockentity.state` + - `BrushableBlockRenderState#itemState` 现在是 final + - `EndPortalRenderState` 现在是一个 final 的 `Direction` 集而不是 `EnumSet` + - `ShelfRenderState#items` 现在是 final +- `net.minecraft.client.renderer.entity.state` + - `FallingBlockRenderState#movingBlockRenderState` 现在是 final + - `HumanoidRenderState#attackArm` -> `ArmedEntityRenderState#attackArm` + - `WitherRenderState#xHeadRots`, `yHeadRots` 现在是 final +- `net.minecraft.client.resources.sounds.AbstractSoundInstance#random` 现在是 final +- `net.minecraft.commands.SharedSuggestionProvider#getCustomTabSugggestions` -> `getCustomTabSuggestions` +- `net.minecraft.commands.arguments.ComponentArgument#getResolvedComponent` 现在接受一个非空的 `Entity` +- `net.minecraft.commands.arguments.selector.SelectorPattern` 被 `CompilableString` 取代 +- `net.minecraft.core.HolderGetter$Provider#get`, `getOrThrow` 现在有接受 `TagKey` 的重载 +- `net.minecraft.data` + - `BlockFamily` + - `shouldGenerateRecipe` -> `shouldGenerateCraftingRecipe`, `shouldGenerateStonecutterRecipe` + - `$Builder#dontGenerateRecipe` -> `dontGenerateCraftingRecipe`, `generateStonecutterRecipe` + - `DataGenerator` 现在是抽象的 + - 构造函数现在只接受 `Path` 输出,不接受 `WorldVersion` 或是否总是生成 + - 原始实现可以在 `DataGenerator$Cached` 中找到 + - `run` 现在是抽象的 + - `Main#addServerProviders` -> `addServerDefinitionProviders`,不再接受 dev `boolean`,不是一对一 + - 其余逻辑已放入 `addServerConverters`,接受 dev `boolean` 但不接受报告 `boolean` +- `net.minecraft.data.loot.BlockLootSubProvider` + - `explosionResistant` 现在是 private + - `enabledFeatures` 现在是 private + - `map` 现在是 private +- `net.minecraft.data.recipes` + - `RecipeProvider$FamilyRecipeProvider` -> `$FamilyCraftingRecipeProvider`, `$FamilyStonecutterRecipeProvider` + - `SingleItemRecipeBuilder#stonecutting` 移至 `BlockFamily` 上的参数 +- `net.minecraft.data.structures.SnbtToNbt` 现在有一个接受单个输入文件夹路径的重载 +- `net.minecraft.gametest.framework` + - `GameTestHelper` + - `assertBlockPresent` 现在有一个只接受要检查的块的重载 + - `moveTo` 现在有接受 `BlockPos` 和 `Vec3` 作为位置的重载 + - `assertEntityInstancePresent` 现在有一个通过 `double` 膨胀边界框以检查内部实体的重载 + - `GameTestServer#create` 现在接受一个 `int` 表示运行所有匹配测试的次数 + - `StructureUtils#testStructuresDir` 拆分为 `testStructuresTargetDir`, `testStructuresSourceDir` + - `TestData` 现在接受一个 `int` 表示测试周围填充的方块数 +- `net.minecraft.nbt` + - `NbtUtils` + - `addDataVersion` 现在使用泛型 `Dynamic` 而不是显式的 nbt 标签 + - `getDataVersion` 现在有一个默认值为 -1 的重载(如果未指定版本) + - `TextComponentTagVisitor` 现在可以接受一个 `$Styling` 和一个 `boolean` 表示是否对键进行排序 + - `handleEscapePretty` 现在是一个 `private` 实例方法,而不是 `protected` static +- `net.minecraft.network.FriendlyByteBuf` + - `readVec3`, `writeVec3` 被 `Vec3#STREAM_CODEC` 取代 + - `readLpVec3`, `writeLpVec3` 被 `Vec3#LP_STREAM_CODEC` 取代 +- `net.minecraft.network.chat` + - `Component` + - `nbt` 现在接受一个 `CompilableString` 而不是 `String` + - `object` 现在有一个接受 `Component` 回退的重载 + - `ComponentContents#resolve` 现在接受一个 `ResolutionContext` 而不是 `CommandSourceStack` 和 `Entity` + - `ComponentUtils#updateForEnttiy` -> `resolve`,接受 `ResolutionContext` 而不是 `CommandSourceStack` 和 `Entity` + - `LastSeenMessages#EMPTY` 现在是 final +- `net.minecraft.network.chat.contents` + - `NbtContents` 现在是一个记录,构造函数接受一个 `CompilableString` 而不是 `String` + - `ObjectContents` 现在接受一个可选的 `Component` 作为回退,如果其内容无法验证 +- `net.minecraft.network.chat.contents.data` + - `BlockDataSource` 现在接受一个 `CompilableString` 用于 `Coordinates`,而不是模式和编译后的位置 + - `EntityDataSource` 现在接受一个 `CompilableString` 用于 `EntitySelector`,而不是模式和编译后的选择器 +- `net.minecraft.network.chat.contents.objects.ObjectInfo#description` -> `defaultFallback` +- `net.minecraft.network.protocol.game` + - `ClientboundSetEntityMotionPacket` 现在是一个记录 + - `ServerboundContainerClickPacket` 现在接受一个 `ContainerInput` 而不是 `ClickType` + - `ServerboundInteractPacket` 现在是一个记录,现在接受交互位置的 `Vec3` +- `net.minecraft.references` + - `Blocks` -> `BlockIds` + - `Items` -> `ItemIds` +- `net.minecraft.resources` + - `FileToIdConverter` 现在是一个记录 + - `RegistryDataLoader#load` 现在返回一个 `CompletableFuture` 的冻结注册表访问 +- `net.minecraft.server.MinecraftServer` 现在接受一个 `boolean` 表示是否传播崩溃,通常用于抛出延迟崩溃 + - `throwIfFatalException` -> `BlockableEventLoop#throwDelayedException`,现在是 private,不是一对一 + - `setFatalException` -> `BlockableEventLoop#delayCrash`, `relayDelayCrash`;不是一对一 +- `net.minecraft.server.commands.ChaseCommand#DIMENSION_NAMES` 现在是 final +- `net.minecraft.server.dedicated.DedicatedServerProperties#acceptsTransfers` 现在是 final +- `net.minecraft.server.packs` + - `BuiltInMetadata` 已合并到 `ResourceMetadata` 中 + - `get` -> `ResourceMetadata#getSection` + - `of` -> `ResourceMetadata#of` + - `PathPackResources#getNamespaces` 现在有一个接受根目录 `Path` 的静态重载 + - `VanillaPackResourcesBuilder#setMetadata` 现在接受一个 `ResourceMetadata` 而不是 `BuiltInMetadata` +- `net.minecraft.server.players.OldUsersConverter#serverReadyAfterUserconversion` 被 `areOldUserListsRemoves` 取代,现在是 `public` +- `net.minecraft.tags.TagLoader` + - `loadTagsFromNetwork` 现在接受一个 `Registry` 而不是 `WritableRegistry`,返回标签键到持有者条目列表的映射 + - `loadTagsForRegistry` 现在有一个接受注册表键以及元素查找的重载,返回标签键到持有者条目列表的映射 +- `net.minecraft.util` + - `Brightness` + - `pack` -> `LightCoordsUtil#pack` + - `block` -> `LightCoordsUtil#block` + - `sky` -> `LightCoordsUtil#sky` + - `RandomSource#createNewThreadLocalInstance` -> `createThreadLocalInstance` + - 现在有一个指定种子的重载 + - `Util#copyAndAdd` 现在有一个接受可变参数元素的重载 +- `net.minecraft.util.thread` + - `BlockableEventLoop` 现在接受一个 `boolean` 表示是否传播崩溃,通常用于抛出延迟崩溃 + - `ReentrantBlockableEventLoop` 现在接受一个 `boolean` 表示是否传播崩溃,通常用于抛出延迟崩溃 +- `net.minecraft.world.InteractionResult$ItemContext#NONE`, `DEFAULT` 现在是 final +- `net.minecraft.world.attribute` + - `AttributeType` 现在接受一个 `ToFloatFunction` 用于数字提供器 + - `ofInterpolated` 现在接受一个 `ToFloatFunction` + - `EnvironmentAttributeReader#getValue` 现在有一个接受 `LootContext` 的重载 +- `net.minecraft.world.entity` + - `Avatar` 现在是抽象的 + - `Entity` + - `fluidHeight` 现在是 final + - `getTags` -> `entityTags` + - `getRandomY` 现在有一个指定扩展 `double` 的重载 + - `Leashable$Wrench#ZERO` 现在是 final + - `LivingEntity` + - `interpolation` 现在是 final + - `swingingArm` 现在是可空的 + - `canAttackType` -> `canAttack`,不是一对一,接受 `LivingEntity` 而不是 `EntityType` + - `lungeForwardMaybe` -> `postPiercingAttack` + - `entityAttackRange` -> `getAttackRangeWith`,现在接受用于攻击的 `ItemStack` +- `net.minecraft.world.entity.ai.behavior` + - `GoAndGiveItemsToTarget` 现在接受 `$ItemThrower` + - `throwItem` -> `BehaviorUtils#throwItem`,不是一对一 + - `SpearAttack` 不再接受接近距离 `float` + - `TryLaySpawnOnWaterNearLand` -> `TryLaySpawnOnFluidNearLand`,不是一对一 +- `net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder#sequence` 现在接受一个 `Oneshot` 作为第二个条目,而不是 `Trigger` +- `net.minecraft.world.entity.ai.goal` + - `BoatGoals` -> `FollowPlayerRiddenEntityGoal$FollowEntityGoal` + - `BOAT` 被 `ENTITY` 取代 + - `FollowBoatGoal` -> `FollowPlayerRiddenEntityGoal`,不是一对一 +- `net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal#targetConditions` 现在是 final +- `net.minecraft.world.entity.ai.sensing.NearestVisibleLivingEntitySensor#getMemory` -> `getMemoryToSet` +- `net.minecraft.world.entity.decoration.Mannequin#getProfile` -> `Avatar#getProfile`,现在是 `public` 和 `abstract` + - 仍在 `Mannequin` 中实现 +- `net.minecraft.world.entity.item.ItemEntity#DEFAULT_HEALTH` 现在是 `public` 而不是 `private` +- `net.minecraft.world.entity.monster.breeze.Breeze#idle`, `slide`, `slideBack`, `longJump`, `shoot`, `inhale` 现在是 final +- `net.minecraft.world.entity.monster.piglin.PiglinAi#isZombified` 现在接受 `Entity` 而不是 `EntityType` +- `net.minecraft.world.entity.monster.warden.Warden#roarAnimationState`, `sniffAnimationState`, `emergeAnimationState`, `diggingAnimationState`, `attackAnimationState`, `sonicBoomAnimationState` 现在是 final +- `net.minecraft.world.entity.monster.zombie.Zombie#handleAttributes` 现在接受 `EntitySpawnReason` +- `net.minecraft.world.entity.player` + - `Input#EMPTY` 现在是 final + - `Player` + - `currentImpulseImpactPos` -> `LivingEntity#currentImpulseImpactPos` + - `currentExplosionCause` -> `LivingEntity#currentExplosionCause` + - `setIgnoreFallDamageFromCurrentImpulse` -> `LivingEntity#setIgnoreFallDamageFromCurrentImpulse` + - `applyPostImpulseGraceTime` -> `LivingEntity#applyPostImpulseGraceTime` + - `isIgnoringFallDamageFromCurrentImpulse` -> `LivingEntity#isIgnoringFallDamageFromCurrentImpulse` + - `tryResetCurrentImpulseContext` -> `LivingEntity#tryResetCurrentImpulseContext` + - `resetCurrentImpulseContext` -> `LivingEntity#resetCurrentImpulseContext` + - `isInPostImpulseGraceTime` -> `LivingEntity#isInPostImpulseGraceTime` + - `isWithinAttackRange` 现在接受用于攻击的 `ItemStack` +- `net.minecraft.world.entity.vehicle.minecart.NewMinecartBehavior$MinecartStep#EMPTY` 现在是 final +- `net.minecraft.world.inventory.AbstractContainerMenu#getQuickCraftPlaceCount` 现在接受快速制作槽位的数量而不是槽位集本身 +- `net.minecraft.world.item` + - `BundleItem#getSelectedItem` -> `getSelectedItemIndex` + - `CreativeModeTab$Output` 现在是 `protected` 而不是 `public` + - `EnderpearlItem#PROJECTILE_SHOOT_POWER` 现在是 final + - `Item$Properties#requiredFeatures` 现在有一个接受 `FeatureFlagSet` 的重载 + - `Items#register*` 方法现在是 `private` 而不是 `public` + - `ItemUtils#onContainerDestroyed` 现在接受一个 `Stream` 的 `ItemStack` 而不是 `Iterable` + - `SignApplicator#tryApplyToSign`, `canApplyToSign` 现在接受正在使用的 `ItemStack` + - `SnowballItem#PROJECTILE_SHOOT_POWER` 现在是 final + - `ThrowablePotionItem#PROJECTILE_SHOOT_POWER` 现在是 final + - `WindChargeItem#PROJECTILE_SHOOT_POWER` 现在是 final +- `net.minecraft.world.item.alchemy.Potions` 现在处理 `Holder$Reference` 而不是超类 `Holder` +- `net.minecraft.world.item.component` + - `AttackRange` + - `minRange` -> `minReach` + - `maxRange` -> `maxReach` + - `minCreativeRange` -> `minCreativeReach` + - `maxCreativeRange` -> `maxCreativeReach` + - `BundleContents` + - `weight` 现在返回一个 `DataResult` 包装的 `Fraction` 而不是原始对象 + - `getSelectedItem` -> `getSelectedItemIndex` + - `WrittenBookContent` + - `tryCraftCopy` -> `craftCopy` + - `resolveForItem`, `resolve` 现在接受 `ResolutionContext` 和 `HolderLookup$Provider` 而不是 `CommandSourceStack` 和 `Player` +- `net.minecraft.world.item.enchantment` + - `ConditionalEffect#codec` 不再接受 `ContextKeySet` + - `Enchantment#doLunge` -> `doPostPiercingAttack` + - `EnchantmentHelper#doLungeEffects` -> `doPostPiercingAttackEffects` + - `TargetedConditionalEffect#codec`, `equipmentDropsCodec` 不再接受 `ContextKeySet` +- `net.minecraft.world.item.enchantment.effects.EnchantmentAttributeEffect#CODEC` -> `MAP_CODEC` +- `net.minecraft.world.item.equipment.Equippable#canBeEquippedBy` 现在接受一个持有者包装的 `EntityType` 而不是原始类型本身 +- `net.minecraft.world.item.trading.VillagerTrades#LIBRARIAN_5_EMERALD_NAME_TAG` 被 `LIBRARIAN_5_EMERALD_YELLOW_CANDLE`, `LIBRARIAN_5_EMERALD_RED_CANDLE` 取代,不是一对一 + - 原始交易已移至 `WANDERING_TRADER_EMERALD_NAME_TAG` +- `net.minecraft.world.level` + - `LevelAccessor` 不再实现 `LevelReader` + - `LevelHeightAccessor#isInsideBuildHeight` 现在有一个接受 `BlockPos` 的重载 +- `net.minecraft.world.level.block` + - `BigDripleafBlock#canPlaceAt` 现在接受 `LevelReader` 而不是 `LevelHeightAccessor`,并且不再接受旧状态 + - `BubbleColumnBlock#updateColumn` 现在接受气泡柱 `Block` + - `FireBlock#setFlammable` 现在是 `private` 而不是 `public` + - `MultifaceSpreader$DefaultSpreaderConfig#block` 现在是 final + - `SnowyDirtBlock` -> `SnowyBlock` + - `SpreadingSnowyDirtBlock` -> `SpreadingSnowyBlock` + - 构造函数现在接受 `ResourceKey` 表示“基础”方块,或当雪或任何其他装饰(例如草)被移除时的方块 +- `net.minecraft.world.level.block.entity.TestInstanceBlockEntity` + - `getTestBoundingBox` - 测试的边界框,按其填充膨胀。 + - `getTestBounds` - 测试的轴对齐边界框,按其填充膨胀。 +- `net.minecraft.world.level.block.entity.vault` + - `VaultConfig#DEFAULT`, `CODEC` 现在是 final + - `VaultServerData#CODEC` 现在是 final + - `VaultSharedData#CODEC` 现在是 final +- `net.minecraft.world.level.block.state` + - `BlockBehaviour` + - `$BlockStateBase` 现在接受一个 `Property` 和 `Comparable` 值的数组,而不是一个值映射,并且不再接受 `MapCodec` + - `hasPostProcess` -> `getPostProcessPos`,不是一对一 + - `$Properties#hasPostProcess` -> `getPostProcessPos`,不是一对一 + - `BlockState` 现在接受一个 `Property` 和 `Comparable` 值的数组,而不是一个值映射,并且不再接受 `MapCodec` + - `StateHolder` 现在接受一个 `Property` 和 `Comparable` 值的数组,而不是一个值映射,并且不再接受 `MapCodec` + - `populateNeighbours` -> `initializeNeighbors`,现在是包私有而不是 `public`;不是一对一 + - `getValues` 现在返回一个 `Property$Value` 流 + - `codec` 现在接受从某个对象获取状态定义的函数 +- `net.minecraft.world.level.chunk.ChunkAccess#blendingData` 现在是 final +- `net.minecraft.world.level.chunk.storage.SimpleRegionStorage#upgradeChunkTag` 现在接受一个 `int` 表示目标版本 +- `net.minecraft.world.level.gameevent.vibrations.VibrationSystem$Data#CODEC` 现在是 final +- `net.minecraft.world.level.gamerules.GameRules` 现在有一个接受 `GameRule` 列表的重载 +- `net.minecraft.world.level.levelgen` + - `NoiseRouterData#caves` 不再接受 `NormalNoise$NoiseParameters` 的 `HolderGetter` + - `WorldDimensions#keysInOrder` 现在接受一个 `LevelStem` 键的集合而不是一个流 +- `net.minecraft.world.level.levelgen.blockpredicates` + - `TrueBlockPredicate#INSTANCE` 现在是 final + - `UnobstructedPredicate#INSTANCE` 现在是 final +- `net.minecraft.world.level.levelgen.feature` + - `AbstractHugeMushrromFeature` + - `isValidPosition` 现在接受一个 `WorldGenLevel` 而不是 `LevelAccessor` + - `placeTrunk` 现在接受一个 `WorldGenLevel` 而不是 `LevelAccessor` + - `makeCap` 现在接受一个 `WorldGenLevel` 而不是 `LevelAccessor` + - `BlockBlobFeature` 现在使用 `BlockBlobConfiguration` 泛型 + - `Feature` + - `FOREST_ROCK` 被 `BLOCK_BLOB` 取代 + - `ICE_SPIKE` 被 `SPIKE` 取代 + - `IceSpikeFeature` -> `SpikeFeature`,不是一对一 + - `SpikeFeature` 现在是 `EndSpikeFeature`,不是一对一 + - `NUMBER_OF_SPIKES` -> `EndSpikeFeature#NUMBER_OF_SPIKES` + - `getSpikesForLevel` -> `EndSpikeFeature#getSpikesForLevel` +- `net.minecraft.world.level.levelgen.feature.configurations` + - `HugeMushroomFeatureConfiguration` 现在是一个记录,接受一个可放置的 `BlockPredicate` + - `SpikeConfiguration` -> `EndSpikeConfiguration` 现在是一个记录 + - 原始的 `SpikeConfiguration` 现在用于冰刺,接受要使用的方块,以及放置位置的谓词和是否可以替换存在的方块 + - `TreeConfiguration` + - `dirtProvider`, `forceDirt` 已被 `belowTrunkProvider` 取代 + - `dirtProvider` 通常使用 `CAN_PLACE_BELOW_OVERWORLD_TRUNKS` + - `forceDirt` 通常使用 `PLACE_BELOW_OVERWORLD_TRUNKS` + - `$TreeConfigurationBuilder` + - `dirtProvider`, `dirt`, `forceDirt` 已被 `belowTrunkProvider` 取代 +- `net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer` + - `createFoliage` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `placeLeavesRow`, `placeLeavesRowWithHangingLeavesBelow` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `tryPlaceExtension`, `tryPlaceLeaf` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` +- `net.minecraft.world.level.levelgen.feature.rootplacers.RootPlacer#placeRoots`, `placeRoot` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` +- `net.minecraft.world.level.levelgen.feature.stateproviders` + - `BlockStateProvider#getState` 现在接受 `WorldGenLevel` +- `net.minecraft.world.level.levelgen.feature.treedecorators` + - `TreeDecorator$Context` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `level` 现在返回 `WorldGenLevel` 而不是 `LevelSimulatedReader` +- `net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer` + - `placeTrunk` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `setDirtAt` -> `placeBelowTrunkBlock`,现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `placeLog` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `placeLogIfFree` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `validTreePos` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` + - `isFree` 现在接受一个 `WorldGenLevel` 而不是 `LevelSimulatedReader` +- `net.minecraft.world.level.levelgen.placement.BiomeFilter#CODEC` 现在是 final +- `net.minecraft.world.level.levelgen.structure.TemplateStructurePiece#template`, `placeSettings` 现在是 final +- `net.minecraft.world.level.levelgen.structure.pools.alias` + - `DirectPoolAlias#CODEC` 现在是 final + - `RandomGroupPoolAlias#CODEC` 现在是 final + - `RandomPoolAlias#CODEC` 现在是 final +- `net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings#CODEC` 现在是 final +- `net.minecraft.world.level.levelgen.synth.PerlinNoise#getValue` 不再接受 `boolean` 表示是否展平 Y 值而不是应用频率因子 +- `net.minecraft.world.level.material.FluidState` 现在接受一个 `Property` 和 `Comparable` 值的数组,而不是一个值映射,并且不再接受 `MapCodec` +- `net.minecraft.world.level.pathfinder.PathType` + - `DANGER_POWDER_SNOW` -> `ON_TOP_OF_POWDER_SNOW` + - `DANGER_FIRE` -> `FIRE_IN_NEIGHBOR` + - `DAMAGE_FIRE` -> `FIRE` + - `DANGER_OTHER` -> `DAMAGING_IN_NEIGHBOR` + - `DAMAGE_OTHER` -> `DAMAGING`, + - `DANGER_TRAPDOOR` -> `ON_TOP_OF_TRAPDOOR` +- `net.minecraft.world.level.saveddata.maps.MapItemSavedData#tickCarriedBy` 现在接受一个可空的 `ItemFrame` +- `net.minecraft.world.level.storage.loot.functions` + - `EnchantRandomlyFunction` 现在接受一个 `boolean` 表示是否包含来自被附魔堆叠的额外交易成本组件 + - 通过 `$Builder#includeAdditionalCostComponent` 设置 + - `EnchantWithLevelsFunction` 现在接受一个 `boolean` 表示是否包含来自被附魔堆叠的额外交易成本组件 + - 通过 `$Builder#includeAdditionalCostComponent` 设置 + - `$Builder#fromOptions` -> `withOptions`,现在有一个接受可选的 `HolderSet` 的重载 + +### 移除列表 + +- `net.minecraft.SharedConstants#USE_WORKFLOWS_HOOKS` +- `net.minecraft.client.data.models.BlockModelGenerators#createGenericCube` +- `net.minecraft.client.Minecraft#delayCrashRaw` +- `net.minecraft.client.gui.components.EditBox#setFilter` +- `net.minecraft.client.multiplayer.ClientPacketListener#getId` +- `net.minecraft.client.renderer.Sheets` + - `shieldSheet` + - `bedSheet` + - `shulkerBoxSheet` + - `signSheet` + - `hangingSignSheet` + - `chestSheet` +- `net.minecraft.client.renderer.rendertype.RenderTypes#weather` +- `net.minecraft.data.loot.BlockLootSubProvider(Set, FeatureFlagSet, Map, HolderLookup$Provider)` +- `net.minecraft.gametest.framework.StructureUtils#DEFAULT_TEST_STRUCTURES_DIR` +- `net.minecraft.nbt.NbtUtils` + - `addCurrentDataVersion` + - `prettyPrint(Tag)` +- `net.minecraft.server.packs.AbstractPackResources#getMetadataFromStream` +- `net.minecraft.server.players.PlayerList#getSingleplayerData` +- `net.minecraft.util` + - `Mth#createInsecureUUID` + - `LightCoordsUtil#UI_FULL_BRIGHT` +- `net.minecraft.world` + - `ContainerListener` + - `Difficulty#getKey` + - `SimpleContainer#addListener`, `removeListener` +- `net.minecraft.world.entity.ai.memory.MemoryModuleType#INTERACTABLE_DOORS` +- `net.minecraft.world.entity.monster.Zoglin#MEMORY_TYPES` +- `net.minecraft.world.entity.monster.creaking.Creaking#MEMORY_TYPES` +- `net.minecraft.world.entity.monster.hogling.Hoglin#MEMORY_TYPES` +- `net.minecraft.world.item` + - `BundleItem#hasSelectedItem` + - `Item#getName()` + - `ItemStack` + - `isFramed`, `getFrame` + - `setEntityRepresentation`, `getEntityRepresentation` +- `net.minecraft.world.item.component` + - `BundleContents` + - `getItemUnsafe` + - `hasSelectedItem` + - `WrittenBookContent#MAX_CRAFTABLE_GENERATION` +- `net.minecraft.world.level.block.LiquidBlock#SHAPE_STABLE` +- `net.minecraft.world.level.levelgen.feature` + - `Feature#isStone`, `isDirt`, `isGrassOrDirt` +- `net.minecraft.world.level.levelgen.material.WorldGenMaterialRule` +- `net.minecraft.world.level.storage.loot.functions.SetOminousBottleAmplifierFunction#amplifier` diff --git a/primers-doc/README.md b/primers-doc/README.md new file mode 100644 index 0000000..fb238bb --- /dev/null +++ b/primers-doc/README.md @@ -0,0 +1,135 @@ +# 详细入门文档 + +这些章节按步骤保留了上游入门文档的内容。每个章节标题都直接链接到入门文档页面内的相应章节。 + +## 1.21.1 -> 1.21.2/3 + +[完整入门文档](1.21.2-from-1.21.1.md) + +- [资源包变更](1.21.2-from-1.21.1.md#资源包变更) +- [持有者集过渡](1.21.2-from-1.21.1.md#持有者集-过渡) +- [GUI 渲染类型](1.21.2-from-1.21.1.md#gui-渲染类型) +- [着色器重写](1.21.2-from-1.21.1.md#着色器重写) +- [实体渲染状态](1.21.2-from-1.21.1.md#实体渲染状态) +- [装备与物品、模型等等](1.21.2-from-1.21.1.md#装备与物品-模型等等) +- [盔甲材料、装备和模型(纹理)](1.21.2-from-1.21.1.md#盔甲材料-装备和模型纹理) +- [交互结果](1.21.2-from-1.21.1.md#交互结果) +- [乐器,数据包版](1.21.2-from-1.21.1.md#乐器-数据包版) +- [试炼刷怪笼配置,现在采用数据包形式](1.21.2-from-1.21.1.md#试炼刷怪笼配置-现在采用数据包形式) +- [配方提供者,数据提供者的“并非真正”](1.21.2-from-1.21.1.md#配方提供者-数据提供者的并非真正) +- [原料的转变](1.21.2-from-1.21.1.md#原料的转变) +- [BlockEntityTypes 私有化了!](1.21.2-from-1.21.1.md#blockentitytypes-私有化) +- [消耗品](1.21.2-from-1.21.1.md#消耗品) +- [注册表对象 ID,在属性里?](1.21.2-from-1.21.1.md#注册表对象-id-在属性里) +- [属性变更](1.21.2-from-1.21.1.md#属性变更) +- [配方,现在采用注册表格式](1.21.2-from-1.21.1.md#配方-现在采用注册表格式) +- [小幅迁移](1.21.2-from-1.21.1.md#小幅-迁移) + +## 1.21.2/3 -> 1.21.4 + +[完整入门文档](1.21.4-from-1.21.2-3.md) + +- [资源包变更](1.21.4-from-1.21.2-3.md#资源包变更) +- [客户端物品](1.21.4-from-1.21.2-3.md#客户端-物品) +- [生物替换当前物品](1.21.4-from-1.21.2-3.md#生物-替换当前物品) +- [粒子,通过渲染类型渲染](1.21.4-from-1.21.2-3.md#粒子-通过渲染类型渲染) +- [小幅迁移](1.21.4-from-1.21.2-3.md#小幅-迁移) + +## 1.21.4 -> 1.21.5 + +[完整入门文档](1.21.5-from-1.21.4.md) + +- [资源包变更](1.21.5-from-1.21.4.md#资源包变更) +- [正确处理方块实体的移除](1.21.5-from-1.21.4.md#正确处理方块实体的移除) +- [体素形状辅助类](1.21.5-from-1.21.4.md#体素形状辅助类) +- [武器、工具和盔甲:去除冗余](1.21.5-from-1.21.4.md#武器工具和盔甲-去除冗余) +- [加权列表重做](1.21.5-from-1.21.4.md#加权列表重做) +- [加载票](1.21.5-from-1.21.4.md#加载票) +- [游戏测试大修](1.21.5-from-1.21.4.md#游戏测试-大修) +- [数据组件获取器](1.21.5-from-1.21.4.md#数据组件-获取器) +- [标签与解析](1.21.5-from-1.21.4.md#标签与解析) +- [保存数据,现在带有类型](1.21.5-from-1.21.4.md#保存数据-现在带有类型) +- [渲染管线重做](1.21.5-from-1.21.4.md#渲染管线重做) +- [模型重做](1.21.5-from-1.21.4.md#模型-重做) +- [小幅迁移](1.21.5-from-1.21.4.md#小幅-迁移) + +## 1.21.5 -> 1.21.6 + +[完整入门文档](1.21.6-from-1.21.5.md) + +- [资源包变更](1.21.6-from-1.21.5.md#资源包变更) +- [GUI 变更](1.21.6-from-1.21.5.md#gui-变更) +- [路径点](1.21.6-from-1.21.5.md#路径点) +- [Blaze3d 变更](1.21.6-from-1.21.5.md#blaze3d-变更) +- [标签提供者:追加器重写](1.21.6-from-1.21.5.md#标签提供者-追加器重写) +- [通用编码与解码:替换直接 NBT 访问](1.21.6-from-1.21.5.md#通用编码与解码-替换直接-nbt-访问) +- [服务端玩家变更](1.21.6-from-1.21.5.md#服务端玩家变更) +- [小幅迁移](1.21.6-from-1.21.5.md#小幅-迁移) + +## 1.21.6 -> 1.21.7 + +[完整入门文档](1.21.7-from-1.21.6.md) + +- [资源包变更](1.21.7-from-1.21.6.md#资源包变更) +- [小幅迁移](1.21.7-from-1.21.6.md#小幅-迁移) + +## 1.21.7 -> 1.21.8 + +[完整入门文档](1.21.8-from-1.21.7.md) + +- [资源包变更](1.21.8-from-1.21.7.md#资源包变更) +- [小幅迁移](1.21.8-from-1.21.7.md#小幅-迁移) + +## 1.21.8 -> 1.21.9 + +[完整入门文档](1.21.9-from-1.21.8.md) + +- [资源包变更](1.21.9-from-1.21.8.md#资源包变更) +- [调试大修](1.21.9-from-1.21.8.md#调试-大修) +- [调试屏幕](1.21.9-from-1.21.8.md#调试-屏幕) +- [功能提交:电影版](1.21.9-from-1.21.8.md#功能提交-电影版) +- [字体字形管线](1.21.9-from-1.21.8.md#字体-字形-管线) +- [JSON-RPC 管理服务器](1.21.9-from-1.21.8.md#json-rpc-管理服务器) +- [输入处理整合](1.21.9-from-1.21.8.md#输入处理整合) +- [`Level#isClientSide` 现在为 private](1.21.9-from-1.21.8.md#levelisclientside-现在为-private) +- [小幅迁移](1.21.9-from-1.21.8.md#小幅-迁移) + +## 1.21.9 -> 1.21.10 + +[完整入门文档](1.21.10-from-1.21.9.md) + +- [资源包变更](1.21.10-from-1.21.9.md#资源包变更) +- [小幅迁移](1.21.10-from-1.21.9.md#小幅-迁移) + +## 1.21.10 -> 1.21.11 + +[完整入门文档](1.21.11-from-1.21.10.md) + +- [资源包变更](1.21.11-from-1.21.10.md#资源包变更) +- [重命名混乱](1.21.11-from-1.21.10.md#重命名混乱) +- [哦,又来了,一次渲染重写](1.21.11-from-1.21.10.md#哦又来了-一次渲染重写) +- [Gizmo 控件](1.21.11-from-1.21.10.md#gizmo-控件) +- [权限大修](1.21.11-from-1.21.10.md#权限-大修) +- [新数据组件](1.21.11-from-1.21.10.md#新数据组件) +- [环境属性的时间线](1.21.11-from-1.21.10.md#环境属性的时间线) +- [游戏规则洗牌](1.21.11-from-1.21.10.md#游戏规则洗牌) +- [小幅迁移](1.21.11-from-1.21.10.md#小幅-迁移) + +## 1.21.11 -> 26.1 + +[完整入门文档](26.1-from-1.21.11.md) + +- [资源包变更](26.1-from-1.21.11.md#资源包变更) +- [Java 25 与反混淆](26.1-from-1.21.11.md#java-25-与反混淆) +- [战利品类型展开](26.1-from-1.21.11.md#战利品类型-展开) +- [验证大修](26.1-from-1.21.11.md#验证-大修) +- [数据包村民交易](26.1-from-1.21.11.md#数据包村民交易) +- [`Level#random` 字段现在为 protected](26.1-from-1.21.11.md#levelrandom-字段现在为-protected) +- [数据组件初始化器](26.1-from-1.21.11.md#数据组件-初始化器) +- [物品实例与堆栈模板](26.1-from-1.21.11.md#物品实例与堆栈模板) +- [序列化器记录与配方信息](26.1-from-1.21.11.md#序列化器记录与配方信息) +- [染料组件](26.1-from-1.21.11.md#染料组件) +- [世界时钟与时间标记](26.1-from-1.21.11.md#世界时钟与时间标记) +- [将主关卡数据拆分为保存数据](26.1-from-1.21.11.md#将主关卡数据拆分为保存数据) +- [更多渲染变更](26.1-from-1.21.11.md#更多渲染变更) +- [小幅迁移](26.1-from-1.21.11.md#小幅-迁移)