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平台扩展APInet.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 提供了强大而灵活的数据同步功能,包括:
- 完整的数据快照管理
- 多种数据类型的支持
- 自定义数据扩展能力
- 异步操作和线程安全
- 事件驱动的架构
通过合理使用API,开发者可以轻松实现跨服务器的玩家数据同步,创建自定义的数据类型,并集成到现有的插件生态系统中。
内容来自 SnowCutieOwO – Wiki