自定义玩家数据

自定义玩家数据

一个适用于Minecraft服务器或模组开发者的实用模组框架。

自定义玩家数据

版本徽章 加载器徽章 状态徽章

Fabric Framework 1.20.6,用于管理持久化且已同步的玩家数据。

此模组允许您定义附加到每个玩家的自定义变量,这些变量会保存在 Minecraft 原生的 player.dat 文件中,并在服务器和客户端之间自动同步。


功能特性

  • JSON 架构:在 player_data_schema.json 文件中定义您的变量,该文件会在地图文件夹中自动生成。
  • 原生持久化:数据通过 Mixin 直接存储在 player.dat 中 — 即使崩溃也不会丢失数据。
  • 自动同步:所有变量在连接时以及每次更改时都会发送到客户端(非常适合 HUD 显示)。
  • 开发者 API:其他模组可将其作为依赖项,以编程方式注入和操作变量。
  • 自定义类型:创建您自己的复杂数据类型(例如 Power、Skill、Quest…)。
  • 管理员命令:/playerdata get 和 /playerdata set 供管理员使用。
  • 验证:支持 int 类型的最小/最大值、空值检查、运行时类型验证。

对于服务器管理员

安装

  1. 将 .jar 文件放入 Fabric 1.20.6 服务器的 mods/ 文件夹中。
  2. 首次启动服务器 — player_data_schema.json 文件将在您的世界文件夹中生成(位于 level.dat 旁边)。
  3. 根据需要修改 JSON,然后重新启动。

JSON 架构格式

{
  "note": {
    "type": "int",
    "defaultValue": 0,
    "maxValue": 5,
    "minValue": 0,
    "null": false
  },
  "hudColor": {
    "type": "string",
    "defaultValue": "light_blue",
    "null": false
  },
  "powers": {
    "type": "Power",
    "array": true,
    "defaultValue": null,
    "null": true
  }
}
参数 描述 是否必填
type int、string、boolean 或已注册的自定义类型 是
defaultValue 新玩家的默认值 否
maxValue 最大值(仅限 int 类型) 否
minValue 最小值(仅限 int 类型) 否
null 是否允许 null(默认为 false) 否
array 该变量是否为数组(默认为 false) 否

重要:如果某个变量设置了 "null": false 且没有 defaultValue,启动时将会记录错误。

命令

命令 描述 权限
/playerdata get <变量> <玩家> 显示当前值 管理员等级 2+
/playerdata set <变量> <玩家> <值> 修改值(仅限基本类型) 管理员等级 2+

示例:

/playerdata get note Steve
/playerdata set hudColor Steve red
/playerdata set note Steve 3

开发者指南 — 将自定义玩家数据用作 API

1. 添加依赖项

在您的 build.gradle 文件中,将 JAR 添加为本地依赖项(如果已发布,也可通过 Maven 仓库添加):

dependencies {
    // 添加这一行
    modImplementation files("libs/custom-player-data-1.0.0.jar")
}

在您的 fabric.mod.json 文件中,添加以下依赖项:

{
  "depends": {
    "custom-player-data": ">=1.0.0"
  }
}

2. 创建自定义类型(可选)

如果您需要存储复杂数据,请实现 ICustomDataType:

package com.myMod.data;

import fr.hdi.api.ICustomDataType;
import net.minecraft.nbt.NbtCompound;

public class Skill implements ICustomDataType {

    private String name = "";
    private int xp = 0;
    private boolean unlocked = false;

    public Skill() {}

    public Skill(String name, int xp, boolean unlocked) {
        this.name = name;
        this.xp = xp;
        this.unlocked = unlocked;
    }

    @Override
    public void writeNbt(NbtCompound nbt) {
        nbt.putString("name", name);
        nbt.putInt("xp", xp);
        nbt.putBoolean("unlocked", unlocked);
    }

    @Override
    public void readNbt(NbtCompound nbt) {
        this.name = nbt.getString("name");
        this.xp = nbt.getInt("xp");
        this.unlocked = nbt.getBoolean("unlocked");
    }

    public String getName() { return name; }
    public int getXp() { return xp; }
    public boolean isUnlocked() { return unlocked; }
    public void setXp(int xp) { this.xp = xp; }
    public void setUnlocked(boolean unlocked) { this.unlocked = unlocked; }

    @Override
    public String toString() {
        return "Skill{" + name + ", xp=" + xp + ", unlocked=" + unlocked + "}";
    }
}

3. 注册您的类型和变量

在您的模组的 onInitialize() 方法中:

package com.myMod;

import com.myMod.data.Skill;
import fr.hdi.api.TypeRegistry;
import fr.hdi.schema.SchemaManager;
import fr.hdi.schema.VariableDefinition;
import net.fabricmc.api.ModInitializer;

public class myMod implements ModInitializer {

    @Override
    public void onInitialize() {
        // 1. 注册自定义类型
        TypeRegistry.register("Skill", Skill::new);

        // 2. 注册所有可能的变量架构
        SchemaManager.registerVariable(
            VariableDefinition.builder("level", "int")
                .defaultValue(1)
                .minValue(1)
                .maxValue(100)
                .build()
        );

        SchemaManager.registerVariable(
            VariableDefinition.builder("clan", "string")
                .defaultValue("none")
                .build()
        );

        SchemaManager.registerVariable(
            VariableDefinition.builder("hasCompletedTutorial", "boolean")
                .defaultValue(false)
                .build()
        );

        SchemaManager.registerVariable(
            VariableDefinition.builder("skills", "Skill")
                .array(true)
                .nullable(true)
                .defaultNull()
                .build()
        );
    }
}

4. 读取和写入玩家数据

使用 PlayerDataStore 从服务器代码的任何位置访问数据:

import fr.hdi.api.PlayerDataStore;
import net.minecraft.server.network.ServerPlayerEntity;

// 读取基本类型
int level = PlayerDataStore.getInt(player, "level");
String clan = PlayerDataStore.getString(player, "clan");
boolean done = PlayerDataStore.getBoolean(player, "hasCompletedTutorial");

// 使用默认值读取
String color = PlayerDataStore.getString(player, "color", "red");

// 读取自定义类型
Skill skill = PlayerDataStore.getCustom(player, "mainSkill", Skill.class);

// 读取列表
List<Skill> skills = PlayerDataStore.getList(player, "skills", Skill.class);

// 通用读取
Object raw = PlayerDataStore.getData(player, "level");

// 设置基本变量
PlayerDataStore.setData(player, "level", 42);
PlayerDataStore.setData(player, "clan", "Dragons");
PlayerDataStore.setData(player, "hasCompletedTutorial", true);

// 写入自定义类型数组
Skill newSkill = new Skill("Archery", 150, true);
List<Skill> skillList = new ArrayList<>();
skillList.add(newSkill);
PlayerDataStore.setData(player, "skills", skillList);

5. 在客户端读取数据

数据会自动同步到客户端。使用 CustomPlayerDataClient:

import fr.hdi.CustomPlayerDataClient;

int level = CustomPlayerDataClient.getClientInt("level");
String color = CustomPlayerDataClient.getClientString("hudColor");
boolean done = CustomPlayerDataClient.getClientBoolean("hasCompletedTutorial");