Doc ID: SIRC-061

HuskSync API v3 知识文档

HuskSync API v3 提供了获取和更新数据快照的方法、一系列API事件用于跟踪用户数据何时被同步和保存,以及注册序列化器以同步自定义数据类型的基础设施。

Overview

HuskSync API v3 文档

概述

HuskSync API v3 提供了获取和更新数据快照的方法、一系列API事件用于跟踪用户数据何时被同步和保存,以及注册序列化器以同步自定义数据类型的基础设施。

兼容性

  • API版本与插件版本号一致
  • v3.x:支持 v3.0 至今版本
  • v2.x:不支持
  • v1.x:不支持

平台支持

  • bukkit:Bukkit、Spigot、Paper等,提供Bukkit API事件监听器和适配器
  • fabric:Fabric API for Minecraft
  • common:所有平台的通用API

核心包名和类

主要包结构

  • net.william278.husksync.api.HuskSyncAPI – 主API类
  • net.william278.husksync.api.BukkitHuskSyncAPI – Bukkit平台扩展API
  • net.william278.husksync.user.User – 用户对象
  • net.william278.husksync.user.OnlineUser – 在线用户对象
  • net.william278.husksync.data.DataSnapshot – 数据快照类
  • net.william278.husksync.data.Data – 数据接口

数据快照类型

  • DataSnapshot.Packed – 已打包的快照(序列化为字节映射)
  • DataSnapshot.Unpacked – 已解包的快照(可操作数据)

API 使用方法

1. 获取API实例

// 获取通用API实例
HuskSyncAPI huskSyncAPI = HuskSyncAPI.getInstance();

// 获取Bukkit平台API实例(推荐)
BukkitHuskSyncAPI bukkitAPI = BukkitHuskSyncAPI.getBukkitInstance();

2. 获取用户对象

// 通过UUID获取用户
huskSyncAPI.getUser(uuid).thenAccept(optionalUser -> {
    if (optionalUser.isEmpty()) {
        System.out.println("User does not exist!");
        return;
    }
    User user = optionalUser.get();
    System.out.println("Found " + user.getUsername());
});

// 获取在线用户
OnlineUser onlineUser = huskSyncAPI.getUser(player);

3. 数据快照操作

获取当前数据

huskSyncAPI.getCurrentData(user).thenAccept(optionalSnapshot -> {
    if (optionalSnapshot.isEmpty()) {
        System.out.println("Couldn't get data for " + user.getUsername());
        return;
    }
    DataSnapshot.Unpacked snapshot = optionalSnapshot.get();
    // 操作快照数据
});

获取最后保存的快照

huskSyncAPI.getLatestSnapshot(user).thenAccept(optionalSnapshot -> {
    // 处理快照
});

获取所有保存的快照

huskSyncAPI.getSnapshots(user).thenAccept(optionalSnapshots -> {
    if (optionalSnapshots.isEmpty()) {
        System.out.println("No saved snapshots!");
        return;
    }
    List<DataSnapshot.Unpacked> snapshots = optionalSnapshots.get();
});

4. 数据快照打包与解包

// 打包快照
DataSnapshot.Packed packed = huskSyncAPI.packSnapshot(unpackedSnapshot);

// 解包快照
DataSnapshot.Unpacked unpacked = huskSyncAPI.unpackSnapshot(packedSnapshot);

// 编辑打包的快照
DataSnapshot.Packed edited = huskSyncAPI.editPackedSnapshot(packed, unpacked -> {
    // 编辑解包后的快照
    unpacked.setHealth(BukkitData.Health.from(20, 20));
});

5. 数据类型和操作方法

支持的数据类型

标识符键 描述 获取方法 设置方法
husksync:inventory 用户背包和手持物品槽 #getInventory() #setInventory()
husksync:ender_chest 用户末影箱 #getEnderChest() #setEnderChest()
husksync:potion_effects 用户药水效果 #getPotionEffects() #setPotionEffects()
husksync:advancements 用户成就 #getAdvancements() #setAdvancements()
husksync:location 用户位置 #getLocation() #setLocation()
husksync:statistics 用户统计数据 #getStatistics() #setStatistics()
husksync:health 用户生命值 #getHealth() #setHealth()
husksync:hunger 用户饥饿度、饱和度和消耗度 #getHunger() #setHunger()
husksync:attributes 用户属性 #getAttributes() #setAttributes()
husksync:experience 用户等级、经验和分数 #getExperience() #setExperience()
husksync:game_mode 用户游戏模式 #getGameMode() #setGameMode()
husksync:flight_status 用户飞行能力/状态 #getFlightStatus() #setFlightStatus()
husksync:persistent_data 用户持久化数据容器 #getPersistentData() #setPersistentData()
自定义类型:plugin:foo 任何自定义数据 #getData(Identifier) #setData(Identifier)

编辑生命值、饥饿度、经验值和游戏模式

huskSyncAPI.editCurrentData(user, snapshot -> {
    // 获取生命值
    Optional<Data.Health> healthOptional = snapshot.getHealth();
    if (healthOptional.isPresent()) {
        Data.Health health = healthOptional.get();
        double currentHealth = health.getCurrentHealth();
        double maxHealth = health.getMaxHealth();
        
        // 设置生命值
        snapshot.setHealth(BukkitData.Health.from(20, 20, 20));
    }
    
    // 获取游戏模式
    Optional<Data.GameMode> gameModeOptional = snapshot.getGameMode();
    if (gameModeOptional.isPresent()) {
        snapshot.setGameMode(BukkitData.GameMode.from("SURVIVAL"));
    }
});

编辑背包和末影箱数据

// 编辑当前背包
huskSyncAPI.editCurrentInventory(user, inventory -> {
    ItemStack[] contents = ((BukkitData.Items.Inventory) inventory).getContents();
    contents[0] = new ItemStack(Material.DIAMOND_SWORD);
    contents[1] = null; // 空槽位
    ((BukkitData.Items.Inventory) inventory).setContents(contents);
});

// 编辑当前末影箱
huskSyncAPI.editCurrentEnderChest(user, enderChest -> {
    // 类似操作
});

编辑位置数据

huskSyncAPI.editCurrentData(user, snapshot -> {
    Optional<Data.Location> locationOptional = snapshot.getLocation();
    if (locationOptional.isPresent()) {
        Data.Location location = locationOptional.get();
        org.bukkit.Location bukkitLocation = ((BukkitData.Location) location).getLocation();
        // 修改位置
        bukkitLocation.setX(100);
        bukkitLocation.setY(64);
        bukkitLocation.setZ(200);
        ((BukkitData.Location) location).setLocation(bukkitLocation);
    }
});

编辑成就数据

huskSyncAPI.editCurrentData(user, snapshot -> {
    Optional<Data.Advancements> advancementsOptional = snapshot.getAdvancements();
    if (advancementsOptional.isPresent()) {
        Data.Advancements advancements = advancementsOptional.get();
        List<Data.Advancements.Advancement> playerAdvancements = 
            new ArrayList<>(advancements.getAdvancements());
        
        // 添加成就
        Map<String, Date> criteria = Map.of("criteria_item_1", new Date());
        playerAdvancements.add(Data.Advancements.Advancement.adapt("foo:bar/baz", criteria));
        
        // 移除所有配方成就
        playerAdvancements.removeIf(advancement -> 
            advancement.getIdentifier().startsWith("minecraft:recipes/"));
        
        advancements.setAdvancements(playerAdvancements);
    }
});

6. 创建新的数据快照

从玩家当前数据创建

DataSnapshot.Packed data = huskSyncAPI.createSnapshot(user);
DataSnapshot.Packed edited = huskSyncAPI.editPackedSnapshot(data, unpacked -> {
    unpacked.setHealth(BukkitData.Health.from(10, 20, 20));
});
huskSyncAPI.addSnapshot(edited);

从零创建

DataSnapshot.Builder builder = huskSyncAPI.snapshotBuilder();
BukkitData.Items.Inventory inventory = BukkitData.Items.Inventory.empty();
inventory.setContents(new ItemStack[] { new ItemStack(Material.DIAMOND_SWORD) });
inventory.setHeldItemSlot(0);

DataSnapshot.Packed packed = builder
    .saveCause(SaveCause.API)
    .setTimestamp(OffsetDateTime.now().minusDays(3))
    .setInventory(inventory)
    .setHealth(BukkitData.Health.from(10, 20, 20))
    .buildAndPack();

huskSyncAPI.addSnapshot(user, packed);

7. 删除数据快照

huskSyncAPI.deleteSnapshot(user, uuid).thenAccept(success -> {
    if (success) {
        System.out.println("Deleted snapshot with UUID " + uuid);
    } else {
        System.out.println("No snapshot with UUID " + uuid + " to delete");
    }
});

自定义数据 API

1. 创建自定义数据类

public class LoginParticleData extends BukkitData implements Adaptable {
    private String particleId;
    private int numberOfParticles;
    
    public LoginParticleData(String particleId, int numberOfParticles) {
        this.particleId = particleId;
        this.numberOfParticles = numberOfParticles;
    }
    
    @Override
    public void apply(BukkitUser user, BukkitHuskSync plugin) {
        Player player = user.getPlayer();
        player.spawnParticle(Particle.valueOf(particleId), player.getLocation(), numberOfParticles);
    }
    
    // Adaptable 接口需要的无参构造函数
    private LoginParticleData() {}
}

2. 创建序列化器

public class LoginParticleSerializer extends BukkitSerializer.Json<LoginParticleData> 
    implements Serializer<LoginParticleData> {
    
    public LoginParticleSerializer(@NotNull HuskSyncAPI api) {
        super(api, LoginParticleData.class);
    }
}

3. 注册序列化器

// 创建标识符
public static Identifier LOGIN_PARTICLES_ID = Identifier.from("myplugin", "login_particles");

// 注册序列化器
huskSyncAPI.registerSerializer(LOGIN_PARTICLES_ID, 
    new LoginParticleSerializer(HuskSyncAPI.getInstance()));

4. 设置和获取自定义数据

// 设置数据到玩家
LoginParticleData loginParticleData = new LoginParticleData("FIREWORKS_SPARK", 10);
huskSyncAPI.getUser(player).setData(LOGIN_PARTICLES_ID, loginParticleData);

// 从玩家获取数据
LoginParticleData data = (LoginParticleData) huskSyncAPI.getUser(player)
    .getData(LOGIN_PARTICLES_ID);

5. 在DataSaveEvent中持久化自定义数据

@EventHandler
public void onDataSave(BukkitDataSaveEvent event) {
    event.editData(unpacked -> 
        unpacked.setData(LOGIN_PARTICLES_ID, 
            new LoginParticleData("FIREWORKS_SPARK", 10)));
}

API 事件

可用事件

  • DataSaveEvent – 数据保存时触发
  • DataSyncEvent – 数据同步时触发
  • 其他相关事件

事件监听示例

@EventHandler
public void onDataSave(BukkitDataSaveEvent event) {
    // 修改保存的数据
    event.editData(unpacked -> {
        // 编辑快照数据
        unpacked.setHealth(BukkitData.Health.from(20, 20));
    });
    
    // 取消事件
    // event.setCancelled(true);
}

重要注意事项

CompletableFuture 和 Optional 使用

  • 所有异步方法返回 CompletableFuture
  • 使用 thenAccept() 处理结果,避免阻塞主线程
  • 不要调用 #join() 方法,可能导致线程死锁
  • 使用 Optional 安全处理可能为空的数据

线程安全

  • API调用是线程安全的
  • 异步操作在服务器异步任务中处理
  • 避免在主线程中执行长时间操作

错误处理

  • 检查 Optional 是否为空
  • 处理异步异常
  • 验证数据类型是否支持

最佳实践

1. 创建API钩子类

public class HuskSyncAPIHook {
    private final HuskSyncAPI huskSyncAPI;
    
    public HuskSyncAPIHook() {
        this.huskSyncAPI = HuskSyncAPI.getInstance();
    }
    
    // API操作方法
}

2. 检查插件存在

public class MyPlugin extends JavaPlugin {
    public HuskSyncAPIHook huskSyncAPIHook;
    
    @Override
    public void onEnable() {
        if (Bukkit.getPluginManager().getPlugin("HuskSync") != null) {
            this.huskSyncAPIHook = new HuskSyncAPIHook();
        }
    }
}

3. 使用编辑方法简化代码

// 使用 editCurrentData 简化操作
huskSyncAPI.editCurrentData(user, snapshot -> {
    // 所有编辑操作在这里完成
    snapshot.setHealth(BukkitData.Health.from(20, 20));
    snapshot.setGameMode(BukkitData.GameMode.from("SURVIVAL"));
});

变量和常量

重要变量

  • SaveCause.API – API操作保存原因
  • Identifier – 数据类型标识符
  • User – 用户对象
  • OnlineUser – 在线用户对象

配置相关

  • 需要在配置文件中启用相应的同步功能
  • 自定义数据类型需要在所有服务器上注册
  • 位置数据默认禁用,需要在配置中启用

总结

HuskSync API v3 提供了强大而灵活的数据同步功能,包括:

  1. 完整的数据快照管理
  2. 多种数据类型的支持
  3. 自定义数据扩展能力
  4. 异步操作和线程安全
  5. 事件驱动的架构

通过合理使用API,开发者可以轻松实现跨服务器的玩家数据同步,创建自定义的数据类型,并集成到现有的插件生态系统中。


内容来自 SnowCutieOwO – Wiki