当前位置: 首页 > news >正文

我的世界1.20.1forge模组开发进阶教程——序列化(1)

mc的序列化

在《Minecraft》(MC)中,序列化指将游戏数据(如方块、实体、玩家状态等)转换为可存储或传输的格式。这是游戏运行、存档保存和网络通信的关键技术。以下是Minecraft中常见的序列化方式及其用途:


一、序列化在Minecraft中的作用

  1. 存档数据持久化
    将玩家建筑、地图、物品栏等数据保存到硬盘(如.minecraft/saves中的区域文件)。
  2. 网络传输
    服务器与客户端同步方块更新、实体移动等实时数据。
  3. NBT数据存储
    存储复杂结构数据(如箱子内的物品、附魔属性)。

二、Minecraft中的序列化方式

1. NBT(Named Binary Tag)

特点:Minecraft专用的二进制序列化格式,高效压缩且支持嵌套结构。
用途
◦ 存储物品、实体、方块的附加数据(如箱子内的物品、生物属性)。
◦ 存档文件(如level.dat)和结构文件(.nbt)的核心格式。
示例
通过命令生成一个带NBT数据的钻石剑:
mcfunction give @p diamond_sword{Enchantments:[{id:sharpness,lvl:5}]}

2. Region文件(Anvil格式)

特点:将地图划分为32x32区块的区域文件(.mca),使用压缩算法(如GZip)存储方块数据。
用途:游戏存档(如world/region目录)的核心存储格式。

3. JSON序列化

特点:文本格式,可读性强,但体积较大。
用途
◦ 数据包(datapacks)和资源包的配置(如战利品表、进度条件)。
◦ 部分Mod的配置文件(如模组物品的生成规则)。

4. Protocol Buffers(Protobuf)

特点:二进制协议,高效压缩,用于网络通信。
用途:Minecraft多人服务器的数据包传输(如方块更新、实体移动)。

5. Base64编码

特点:将二进制数据转换为文本,便于嵌入其他格式。
用途
◦ 在命令中存储自定义物品的NBT数据(如告示牌文本)。
◦ 部分Mod的跨存档数据交换。


三、如何操作Minecraft的序列化数据?

  1. 游戏内工具
    • 使用/data命令读取或修改NBT数据。
    • 通过数据包(datapacks)自定义JSON规则。
  2. 外部工具
    NBTExplorer:可视化编辑存档的NBT数据。
    MCEdit:直接修改Region文件中的方块。
    调试屏幕:按F3 + H显示物品的NBT标签。

四、不同场景下的选择建议

存档编辑:优先使用NBTExplorer或命令修改NBT。
模组开发:JSON配置 + NBT数据操作。
服务器优化:Protobuf传输减少带宽占用。

如果需要更具体的操作示例(如自定义NBT结构或数据包配置),可以进一步补充说明! 🎮

Holder接口

代码解释与 Biome 类的联系

1. Holder<T> 接口的核心功能

Holder<T> 是 Minecraft 中用于 管理注册表对象引用 的核心接口,主要用于以下场景:
资源键(ResourceKey)绑定:将对象(如 Biome)与唯一的 ResourceKey 关联,例如 ResourceKey<Biome> FOREST = ResourceKey.create(Registries.BIOME, new ResourceLocation("forest"))
标签(Tag)支持:通过 is(TagKey<T>)tags() 方法,判断对象是否属于某个标签集合(如生物群系标签 is_forest),并遍历所有关联标签。
动态加载与序列化:通过 canSerializeInunwrap 方法,支持跨数据包的资源引用和序列化。

2. DirectReference 的实现差异

Direct 实现
直接包装一个对象实例,不依赖注册表系统。例如:

Holder<Biome> directBiome = Holder.direct(new Biome(...)); // 临时生物群系实例

特点:
• 不可绑定资源键或标签(is(ResourceKey) 始终返回 false)。
• 适用于未注册的临时对象或测试场景。

Reference 实现
通过 ResourceKey 间接引用注册表中的对象,支持动态绑定。例如:

// 创建未绑定的引用
Holder.Reference<Biome> biomeRef = Holder.Reference.createStandAlone(registryOwner, FOREST_KEY);
// 注册时绑定键和值
biomeRef.bindKey(FOREST_KEY);
biomeRef.bindValue(forestBiomeInstance);

特点:
• 绑定后可通过 key() 获取资源键,value() 获取实例。
• 支持标签系统(通过 bindTags 关联标签集合)。

3. Biome 类的直接联系

在 Minecraft 生物群系系统中,Holder<Biome> 用于:
注册表管理
每个生物群系实例(如 Biome.FOREST)在注册时会被包装为 Holder.Reference<Biome>,绑定到唯一的 ResourceKey

// 伪代码示例:生物群系注册流程
Registry<Biome> biomeRegistry = ...;
Holder.Reference<Biome> holder = biomeRegistry.register(FOREST_KEY, forestBiome);
holder.bindTags(Set.of(BiomeTags.IS_FOREST)); // 关联标签

标签查询
游戏逻辑通过 holder.is(BiomeTags.IS_FOREST) 判断生物群系属性,影响生成规则(如树木生成)。
数据包支持
Holder 的序列化能力(canSerializeIn)允许生物群系配置通过数据包动态修改,无需重启游戏。

4. 关键方法在 Biome 中的应用场景

value()
获取实际的 Biome 实例,用于访问气候、生成设置等属性:

Biome biome = holder.value();
float temperature = biome.getBaseTemperature();

tags()
遍历生物群系的所有标签,用于世界生成时的条件判断:

holder.tags().forEach(tag -> {
   
    if (tag.equals(BiomeTags.IS_OVERWORLD)) {
   
        // 生成主世界特有结构
    }
});

unwrapKey()
在保存世界数据时,通过资源键(而非实例)持久化生物群系引用,避免序列化整个对象:

Optional<ResourceKey<Biome>> key = holder.unwrapKey();
key.ifPresent(k -> nbt.putString("biome", k.location().toString()));
5. 设计意义与性能优化

懒加载与动态绑定
Reference 允许生物群系实例在注册表加载完成前暂不绑定,减少启动时的内存压力。
标签缓存
bindTags 将标签集合存储为不可变 Set,加速 is(TagKey) 的查询速度。
安全性
IllegalStateException 确保未绑定的 Holder 不会被误用(如尝试访问未初始化的 value())。


总结

Holder<T> 是 Minecraft 注册表系统的核心抽象,为 Biome 等注册对象提供 资源键管理、标签系统集成、跨数据包序列化 等功能。通过 DirectReference 两种实现,既支持临时对象的快速访问,又满足正式注册资源的动态加载与高效查询需求。在生物群系生成、世界保存、数据包编辑等场景中,Holder<Biome> 是连接代码逻辑与配置数据的关键桥梁。

Codec

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.mojang.serialization;

import com.mojang.datafixers.DataFixUtils;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import com.mojang.datafixers.util.Unit;
import com.mojang.serialization.codecs.CompoundListCodec;
import com.mojang.serialization.codecs.EitherCodec;
import com.mojang.serialization.codecs.EitherMapCodec;
import com.mojang.serialization.codecs.KeyDispatchCodec;
import com.mojang.serialization.codecs.ListCodec;
import com.mojang.serialization.codecs.OptionalFieldCodec;
import com.mojang.serialization.codecs.PairCodec;
import com.mojang.serialization.codecs.PairMapCodec;
import com.mojang.serialization.codecs.PrimitiveCodec;
import com.mojang.serialization.codecs.SimpleMapCodec;
import com.mojang.serialization.codecs.UnboundedMapCodec;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

public interface Codec<A> extends Encoder<A>, Decoder<A> {
   
    PrimitiveCodec<Boolean> BOOL = new PrimitiveCodec<Boolean>() {
   
        public <T> DataResult<Boolean> read(DynamicOps<T> ops, T input) {
   
            return ops.getBooleanValue(input);
        }

        public <T> T write(DynamicOps<T> ops, Boolean value) {
   
            return (T)ops.createBoolean(value);
        }

        public String toString() {
   
            return "Bool";
        }
    };
    PrimitiveCodec<Byte> BYTE = new PrimitiveCodec<Byte>() {
   
        public <T> DataResult<Byte> read(DynamicOps<T> ops, T input) {
   
            return ops.getNumberValue(input).map(Number::byteValue);
        }

        public <T> T write(DynamicOps<T> ops, Byte value) {
   
            return (T)ops.createByte(value);
        }

        public String toString() {
   
            return "Byte";
        }
    };
    PrimitiveCodec<Short> SHORT = new PrimitiveCodec<Short>() {
   
        public <T> DataResult<Short> read(DynamicOps<T> ops, T input) {
   
            return ops.getNumberValue(input).map(Number::shortValue);
        }

        public <T> T write(DynamicOps<T> ops, Short value) {
   
            return (T)ops.createShort(value);
        }

        public String toString() {
   
            return "Short";
        }
    };
    PrimitiveCodec<Integer> INT = new PrimitiveCodec<Integer>() {
   
        public <T> DataResult<Integer> read(DynamicOps<T> ops, T input) {
   
            return ops.getNumberValue(input).map(Number::intValue);
        }

        public <T> T write(DynamicOps<T> ops, Integer value) {
   
            return (T)ops.createInt(value);
        }

        public String toString() {
   
            return "Int";
        }
    };
    PrimitiveCodec<Long> LONG = new PrimitiveCodec<Long>() {
   
        public <T> DataResult<Long> read(DynamicOps<T> ops, T input) {
   
            return ops.getNumberValue(input).map(Number::longValue);
        }

        public <T> T write(DynamicOps<T> ops, Long value) {
   
            return (T)ops.createLong(value);
        }

        public String toString() {
   
            return "Long";
        }
    };
    PrimitiveCodec<Float> FLOAT = new PrimitiveCodec<Float>() {
   
        public <T> DataResult<Float> read(DynamicOps<T> ops, T input) {
   
            return ops.getNumberValue(input).map(Number::floatValue);
        }

        public <T> T write(DynamicOps<T> ops, Float value) {
   
            return (T)ops.createFloat(value);
        }

        public String toString() {
   
            return "Float";
        }
    };
    PrimitiveCodec<Double> DOUBLE = new PrimitiveCodec<Double>() {
   
        public <T> DataResult<Double> read(DynamicOps<T> ops, T input) {
   
            return ops.getNumberValue(input).map(Number::doubleValue);
        }

        public <T> T write(DynamicOps<T> ops, Double value) {
   
            return (T)ops.createDouble(value);
        }

        public String toString() {
   
            return "Double";
        }
    };
    PrimitiveCodec<String> STRING = new PrimitiveCodec<String>() {
   
        public <T> DataResult<String> read(DynamicOps<T> ops, T input) {
   
            return ops.getStringValue(input);
        }

        public <T> T write(DynamicOps<T> ops, String value) {
   
            return (T)ops.createString(value);
        }

        public String toString() {
   
            return "String";
        }
    };
    PrimitiveCodec<ByteBuffer> BYTE_BUFFER = new PrimitiveCodec<ByteBuffer>() {
   
        public <T> DataResult<ByteBuffer> read(DynamicOps<T> ops, T input) {
   
            return ops.getByteBuffer(input);
        }

        public <T> T write(DynamicOps<T> ops, ByteBuffer value) {
   
            return (T)ops.createByteList(value);
        }

        public String toString() {
   
            return "ByteBuffer";
        }
    };
    PrimitiveCodec<IntStream> INT_STREAM = new PrimitiveCodec<IntStream>() {
   
        public <T> DataResult<IntStream> read(

相关文章:

  • 蓝桥杯嵌入式十六届模拟三
  • gradle eclipse
  • ROS--IMU数据包
  • 【计算机操作系统】第二章、进程的描述与控制
  • 深入理解K8s与Docker的关系:容器化技术的双雄
  • 内存检查之Valgrind工具
  • 使用vue cli 5.0 在vscode中运行vue命令报错
  • GBase 8s的TRANSLATE() 函数使用说明
  • 端游熊猫脚本游戏精灵助手2025游戏办公脚本工具!游戏脚本软件免费使用
  • MyBatis中mapper.xml 的sql映射规则
  • Linux系统初始化脚本
  • FastAPI 全面指南:功能解析与应用场景实践
  • 基于RK3588平台的OpenCV 4.11快速部署与使用指南
  • Linux 控制台【Console】类型分类
  • AWS Lambda 深度解析:构建高效无服务器应用的实战指南
  • 基于大数据的各品牌手机销量数据可视化分析系统(源码+lw+部署文档+讲解),源码可白嫖!
  • 使用yaml管理api接口之OpenAPI规范
  • 3.Chatbox + DeepSeek部署Ai
  • 读书笔记-如何有效求助-2/2--帮助的情境解读
  • 嵌入式八股RTOS与Linux---网络系统篇
  • 九江银行落地首单畜牧业转型金融业务,助推传统农业绿色智能
  • 看展 | 黄永玉新作展,感受赤子般的生命力
  • 乌称苏梅州一公共汽车遭俄军袭击,致9死4伤
  • 新华时评:博物馆正以可亲可近替代“高冷范儿”
  • 以军证实空袭也门多个港口
  • 习近平就乌拉圭前总统穆希卡逝世向乌拉圭总统奥尔西致唁电