feat: 添加ValueIO和适配性调整迁移了相关的方法
This commit is contained in:
parent
fe186cd36b
commit
15ab233b04
18
AITask.md
Normal file
18
AITask.md
Normal file
|
|
@ -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 对应的版本
|
||||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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<ValueInput> lib39DataOpt = input.child("Lib39Data");
|
||||
if (lib39DataOpt.isPresent()) {
|
||||
ValueInput valueInput = lib39DataOpt.get();
|
||||
Optional<CompoundTag> 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<CompoundTag> compound = lib39$getSyncData().getCompound(syncData.id.toDebugFileName());
|
||||
if (compound.isPresent()) {
|
||||
syncData.deserializeNBT(compound.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ItemStack> 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<Component> 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<Component> 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- 同步建议"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<ItemStack> 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<Component> 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<Component> 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- 玩家专属数据变换"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ItemStack> 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<GameProfile>();
|
||||
Optional<CompoundTag> compound = carry.getNbt().getCompound("tile");
|
||||
AtomicReference<ResolvableProfile> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<CompoundTag> original) {
|
||||
private void wrapSave(ValueOutput output, @NonNull Operation<Void> 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<Void> original) {
|
||||
original.call(compound);
|
||||
lib39$injectLoadSyncData(compound);
|
||||
private void warpLoad(ValueInput input, @NonNull Operation<Void> original) {
|
||||
original.call(input);
|
||||
lib39$injectLoadSyncData(input);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <T> CompletableFuture<Optional<GameProfile>> fetchGameProfile(T identifier) {
|
||||
public static <T> 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<Optional<GameProfile>> 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<Property> textures = profile.getProperties().get("textures");
|
||||
Collection<Property> textures = profile.properties().get("textures");
|
||||
if (textures.isEmpty()) {
|
||||
return false; // 没有皮肤数据,使用默认
|
||||
}
|
||||
|
|
@ -304,7 +300,7 @@ public class GameProfileHelper {
|
|||
return "default";
|
||||
}
|
||||
|
||||
Collection<Property> textures = profile.getProperties().get("textures");
|
||||
Collection<Property> textures = profile.properties().get("textures");
|
||||
if (textures.isEmpty()) {
|
||||
return "default";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import top.r3944realms.lib39.Lib39;
|
|||
/**
|
||||
* The type Plant helper.
|
||||
*/
|
||||
//todo: 需添加26.1.2的新的Plant,部分条目可能需要重命名
|
||||
public class PlantHelper {
|
||||
/**
|
||||
* The enum Plant.
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ResolvableProfile> setter) {
|
||||
Optional<ResolvableProfile> 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<ResolvableProfile> setter, ResolvableProfile defaultValue) {
|
||||
Optional<ResolvableProfile> 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.
|
||||
*
|
||||
|
|
@ -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<ResolvableProfile> profile) {
|
||||
if (profile.isPresent()) {
|
||||
root.store(key, ResolvableProfile.CODEC, profile.get());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -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<String> 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<String> 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<Boolean> 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<Boolean> 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<Byte> 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<Byte> 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<Short> 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<Short> 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<Integer> 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<Integer> 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<Long> 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<Long> 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<Float> 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<Float> 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<Double> 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<Double> 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<int[]> 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<int[]> setter, int[] defaultValue) {
|
||||
setter.accept(valueInput.getIntArray(key).orElse(defaultValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codec value value input reader.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param codec the codec
|
||||
* @param setter the setter
|
||||
* @return the value input reader
|
||||
*/
|
||||
public <T> ValueInputWriter codecValue(String key, Codec<T> codec, Consumer<T> setter) {
|
||||
valueInput.read(key, codec).ifPresent(setter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codec value value input reader.
|
||||
*
|
||||
* @param <T> 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 <T> ValueInputWriter codecValue(String key, Codec<T> codec, @NotNull Consumer<T> 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<Vec3> 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<Vec3> setter, Vec3 defaultValue) {
|
||||
Optional<ValueInput> 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<ResolvableProfile> setter) {
|
||||
Optional<ResolvableProfile> 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<ResolvableProfile> setter, ResolvableProfile defaultValue) {
|
||||
Optional<ResolvableProfile> 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 <T> the type parameter
|
||||
* @param key the key
|
||||
* @param enumClass the enum class
|
||||
* @param setter the setter
|
||||
* @return the value input reader
|
||||
*/
|
||||
public <T extends Enum<T>> ValueInputWriter enumValue(String key, Class<T> enumClass, Consumer<T> 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 <T> 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 <T extends Enum<T>> ValueInputWriter enumValue(String key, Class<T> enumClass, Consumer<T> setter, T defaultValue) {
|
||||
Optional<String> 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<ValueInputWriter> 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<ValueInputWriter> consumer, Runnable orElse) {
|
||||
Optional<ValueInput> child = valueInput.child(key);
|
||||
if (child.isPresent()) {
|
||||
consumer.accept(new ValueInputWriter(child.get()));
|
||||
} else {
|
||||
orElse.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* List value input reader.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param codec the codec
|
||||
* @param consumer the consumer
|
||||
* @return the value input reader
|
||||
*/
|
||||
public <T> ValueInputWriter list(String key, Codec<T> codec, Consumer<java.util.stream.Stream<T>> consumer) {
|
||||
valueInput.list(key, codec).ifPresent(list -> {
|
||||
if (!list.isEmpty()) {
|
||||
consumer.accept(list.stream());
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* List value input reader.
|
||||
*
|
||||
* @param <T> 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 <T> ValueInputWriter list(String key, Codec<T> codec, @NotNull Consumer<java.util.stream.Stream<T>> consumer, java.util.stream.Stream<T> defaultValue) {
|
||||
Optional<ValueInput.TypedInputList<T>> 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<java.util.stream.Stream<ValueInputWriter>> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String> 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<Boolean> 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<Byte> 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<Short> 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<Integer> 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<Long> 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<Float> 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<Double> 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<int[]> value) {
|
||||
value.ifPresent(v -> {
|
||||
if (v.length > 0) {
|
||||
valueOutput.putIntArray(key, v);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codec value value output writer.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param codec the codec
|
||||
* @param value the value
|
||||
* @return the value output writer
|
||||
*/
|
||||
public <T> ValueOutputReader codecValue(String key, Codec<T> codec, T value) {
|
||||
if (value != null) {
|
||||
valueOutput.store(key, codec, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codec value value output writer.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param codec the codec
|
||||
* @param value the value
|
||||
* @return the value output writer
|
||||
*/
|
||||
public <T> ValueOutputReader codecValue(String key, Codec<T> codec, @NonNull Optional<T> value) {
|
||||
value.ifPresent(v -> valueOutput.store(key, codec, v));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codec nullable value value output writer.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param codec the codec
|
||||
* @param value the value
|
||||
* @return the value output writer
|
||||
*/
|
||||
public <T> ValueOutputReader codecNullable(String key, Codec<T> 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<ResolvableProfile> 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<Vec3> vec) {
|
||||
vec.ifPresent(v -> vec3(key, v));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum value value output writer.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return the value output writer
|
||||
*/
|
||||
public <T extends Enum<T>> ValueOutputReader enumValue(String key, T value) {
|
||||
if (value != null) {
|
||||
valueOutput.putString(key, value.name().toLowerCase());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum value value output writer.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return the value output writer
|
||||
*/
|
||||
public <T extends Enum<T>> ValueOutputReader enumValue(String key, @NonNull Optional<T> 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<ValueOutputReader> 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<Boolean> supplier, Consumer<ValueOutputReader> consumer) {
|
||||
if (supplier.get()) {
|
||||
ValueOutput child = valueOutput.child(key);
|
||||
consumer.accept(new ValueOutputReader(child));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* List value output writer.
|
||||
*
|
||||
* @param <T> 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 <T> ValueOutputReader list(String key, Codec<T> elementCodec, Iterable<T> elements, Consumer<TypedListWriter<T>> consumer) {
|
||||
ValueOutput.TypedOutputList<T> list = valueOutput.list(key, elementCodec);
|
||||
if (!list.isEmpty()) {
|
||||
TypedListWriter<T> writer = new TypedListWriter<>(list);
|
||||
consumer.accept(writer);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* List value output writer.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param elementCodec the element codec
|
||||
* @param elements the elements
|
||||
* @return the value output writer
|
||||
*/
|
||||
public <T> ValueOutputReader list(String key, Codec<T> elementCodec, @NonNull Iterable<T> elements) {
|
||||
ValueOutput.TypedOutputList<T> 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<ChildrenListWriter> 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<ValueOutputReader> 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<Boolean> condition, Consumer<ValueOutputReader> 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 <T> the type parameter
|
||||
*/
|
||||
public static class TypedListWriter<T> {
|
||||
private final ValueOutput.TypedOutputList<T> list;
|
||||
|
||||
private TypedListWriter(ValueOutput.TypedOutputList<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add typed list writer.
|
||||
*
|
||||
* @param element the element
|
||||
* @return the typed list writer
|
||||
*/
|
||||
public TypedListWriter<T> 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<T> addAll(@NonNull Iterable<T> 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<ValueOutputReader> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
27
primers-doc/1.21.10-from-1.21.9.md
Normal file
27
primers-doc/1.21.10-from-1.21.9.md
Normal file
|
|
@ -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` 参数,指示实体是与方块相交还是在方块内部。
|
||||
3146
primers-doc/1.21.11-from-1.21.10.md
Normal file
3146
primers-doc/1.21.11-from-1.21.10.md
Normal file
File diff suppressed because it is too large
Load Diff
3195
primers-doc/1.21.2-from-1.21.1.md
Normal file
3195
primers-doc/1.21.2-from-1.21.1.md
Normal file
File diff suppressed because it is too large
Load Diff
1448
primers-doc/1.21.4-from-1.21.2-3.md
Normal file
1448
primers-doc/1.21.4-from-1.21.2-3.md
Normal file
File diff suppressed because it is too large
Load Diff
3058
primers-doc/1.21.5-from-1.21.4.md
Normal file
3058
primers-doc/1.21.5-from-1.21.4.md
Normal file
File diff suppressed because it is too large
Load Diff
2203
primers-doc/1.21.6-from-1.21.5.md
Normal file
2203
primers-doc/1.21.6-from-1.21.5.md
Normal file
File diff suppressed because it is too large
Load Diff
38
primers-doc/1.21.7-from-1.21.6.md
Normal file
38
primers-doc/1.21.7-from-1.21.6.md
Normal file
|
|
@ -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`
|
||||
19
primers-doc/1.21.8-from-1.21.7.md
Normal file
19
primers-doc/1.21.8-from-1.21.7.md
Normal file
|
|
@ -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` - 一个用于解决特定图形硬件问题的辅助工具。
|
||||
2532
primers-doc/1.21.9-from-1.21.8.md
Normal file
2532
primers-doc/1.21.9-from-1.21.8.md
Normal file
File diff suppressed because it is too large
Load Diff
4957
primers-doc/26.1-from-1.21.11.md
Normal file
4957
primers-doc/26.1-from-1.21.11.md
Normal file
File diff suppressed because it is too large
Load Diff
135
primers-doc/README.md
Normal file
135
primers-doc/README.md
Normal file
|
|
@ -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#小幅-迁移)
|
||||
Loading…
Reference in New Issue
Block a user