Doc ID:
SIRC-063
LuckPerms API 知识文档
LuckPerms 是一个完整的开发者 API,允许服务器上的其他插件读取和修改 LuckPerms 数据,并轻松地将 LuckPerms 深度集成到现有插件和系统中。
Overview
LuckPerms API 文档
概述
LuckPerms 是一个完整的开发者 API,允许服务器上的其他插件读取和修改 LuckPerms 数据,并轻松地将 LuckPerms 深度集成到现有插件和系统中。
包名
- 主包:
net.luckperms.api - 事件包:
net.luckperms.api.event - 节点类型:
net.luckperms.api.node
API 版本
- 当前 API 版本: 5.4
- 使用语义化版本控制
- API 包:
net.luckperms.api
核心接口和类
1. LuckPerms (主接口)
API 的根接口,需要先获取此实例才能进行任何操作。
获取实例的方法:
// 通过 Bukkit ServicesManager
RegisteredServiceProvider<LuckPerms> provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class);
if (provider != null) {
LuckPerms api = provider.getProvider();
}
// 通过 Sponge ServicesManager
Optional<ProviderRegistration<LuckPerms>> provider = Sponge.getServiceManager().getRegistration(LuckPerms.class);
if (provider.isPresent()) {
LuckPerms api = provider.get().getProvider();
}
// 通过单例(全平台通用)
LuckPerms api = LuckPermsProvider.get(); // 注意:如果 API 未加载会抛出 IllegalStateException
2. User (用户对象)
代表服务器上的玩家及其关联的权限数据。
获取 User 实例:
// 玩家在线时
Player player = ...;
User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);
// 通过 UUID
User user = luckPerms.getUserManager().getUser(uuid);
// 玩家可能离线时(异步加载)
UserManager userManager = luckPerms.getUserManager();
CompletableFuture<User> userFuture = userManager.loadUser(uniqueId);
userFuture.thenAcceptAsync(user -> {
// 处理用户数据
});
3. Group (权限组对象)
代表权限组。
获取 Group 实例:
Group group = luckPerms.getGroupManager().getGroup(groupName);
if (group == null) {
// 权限组不存在
return;
}
4. Track (路线对象)
代表权限组晋升路线。
获取 Track 实例:
Track track = luckPerms.getTrackManager().getTrack(trackName);
Node (节点对象)
Node 接口是 LuckPerms 的核心数据类,表示"权限节点",但实际上封装了更多内容。
节点属性
key– 节点的键value– 节点的值(false 表示否定状态)context– 节点生效的情境expiry– 节点的过期时间
节点类型
PermissionNode– 分配的权限RegexPermissionNode– 分配的正则权限InheritanceNode– 标记持有者从其他组继承数据PrefixNode– 分配的前缀SuffixNode– 分配的后缀MetaNode– 分配的元数据WeightNode– 持有节点的对象权重DisplayNameNode– 持有节点的对象显示名称
创建节点实例
// 创建任何类型的节点
Node node = Node.builder("some.node.key").build();
// 带额外属性
Node node = Node.builder("some.node.key")
.value(false)
.expiry(Duration.ofHours(1))
.withContext(DefaultContextKeys.SERVER_KEY, "survival")
.build();
// 创建特定类型节点
PermissionNode permNode = PermissionNode.builder("my.permission").build();
InheritanceNode inheritNode = InheritanceNode.builder(group).build();
PrefixNode prefixNode = PrefixNode.builder("[Some Prefix]", 100).build();
MetaNode metaNode = MetaNode.builder("some-key", "some-value").build();
修改现有节点
// 节点是不可变的,需要创建新节点
Node negated = node.toBuilder().value(false).build();
数据操作
读取用户/权限组数据
// 获取未扁平化的节点集合(不包含继承数据)
Collection<Node> nodes = user.getNodes();
// 按类型筛选节点
Set<String> groups = user.getNodes(NodeType.INHERITANCE).stream()
.map(InheritanceNode::getGroupName)
.collect(Collectors.toSet());
// 获取排序后的节点集合
SortedSet<Node> distinctNodes = user.getDistinctNodes();
// 获取包含继承数据的节点
Collection<Node> inheritedNodes = user.resolveInheritedNodes(queryOptions);
修改用户/权限组数据
// 添加权限
DataMutateResult result = user.data().add(Node.builder("your.node.here").build());
// 保存更改
luckPerms.getUserManager().saveUser(user);
// 使用 modify* 方法(自动处理加载和保存)
luckPerms.getUserManager().modifyUser(userUuid, user -> {
user.data().add(Node.builder(permission).build());
});
Context (情境)
重要类
ContextSet– 情境组接口ImmutableContextSet– 不可变情境组实现MutableContextSet– 可变情境组实现
创建情境组
// 不可变情境组
ImmutableContextSet set1 = ImmutableContextSet.empty();
ImmutableContextSet set2 = ImmutableContextSet.of("world", "world_nether");
ImmutableContextSet set3 = ImmutableContextSet.builder()
.add("world", "world_nether")
.add("server", "survival")
.build();
// 可变情境组
MutableContextSet mutableSet = MutableContextSet.create();
mutableSet.add("world", "text");
注册 ContextCalculator
public class CustomCalculator implements ContextCalculator<Player> {
@Override
public void calculate(Player target, ContextConsumer contextConsumer) {
contextConsumer.accept("gamemode", target.getGameMode().name());
}
@Override
public ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = ImmutableContextSet.builder();
for (GameMode gameMode : GameMode.values()) {
builder.add("gamemode", gameMode.name().toLowerCase());
}
return builder.build();
}
}
// 注册计算器
luckPerms.getContextManager().registerCalculator(new CustomCalculator());
查询活跃情境
Player player = ...;
ImmutableContextSet contextSet = luckPerms.getContextManager().getContext(player);
QueryOptions queryOptions = luckPerms.getContextManager().getQueryOptions(player);
CachedData (缓存数据)
获取缓存数据
// 通过 PlayerAdapter
Player player = ...;
PlayerAdapter<Player> adapter = luckperms.getPlayerAdapter(Player.class);
CachedPermissionData permissionData = adapter.getPermissionData(player);
CachedMetaData metaData = adapter.getMetaData(player);
// 通过 User/Group
CachedPermissionData permissionData = user.getCachedData().getPermissionData();
CachedMetaData metaData = user.getCachedData().getMetaData();
// 指定查询选项
CachedPermissionData permissionData = user.getCachedData().getPermissionData(queryOptions);
权限检查
// 运行权限检查
Tristate checkResult = permissionData.checkPermission("some.permission.node");
boolean checkResultAsBoolean = checkResult.asBoolean();
// 完整方法
public boolean hasPermission(User user, String permission) {
return user.getCachedData().getPermissionData().checkPermission(permission).asBoolean();
}
获取前缀/后缀
String prefix = user.getCachedData().getMetaData().getPrefix();
String suffix = user.getCachedData().getMetaData().getSuffix();
获取元数据
String metaValue = user.getCachedData().getMetaData().getMetaValue("some-key");
自定义元数据存储和查询
设置元数据
public void setLevel(Player player, int level) {
User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);
MetaNode node = MetaNode.builder("level", Integer.toString(level)).build();
// 清除现有的相同键的元数据节点
user.data().clear(NodeType.META.predicate(mn -> mn.getMetaKey().equals("level")));
// 添加新节点
user.data().add(node);
// 保存
luckPerms.getUserManager().saveUser(user);
}
查询元数据
public int getLevel(Player player) {
CachedMetaData metaData = luckPerms.getPlayerAdapter(Player.class).getMetaData(player);
return metaData.getMetaValue("level", Integer::parseInt).orElse(0);
}
事件系统
事件监听器
import net.luckperms.api.event.EventBus;
import net.luckperms.api.event.log.LogPublishEvent;
import net.luckperms.api.event.user.UserLoadEvent;
public class MyListener {
private final MyPlugin plugin;
public MyListener(MyPlugin plugin, LuckPerms luckPerms) {
this.plugin = plugin;
EventBus eventBus = luckPerms.getEventBus();
// 使用表达式 lambda
eventBus.subscribe(this.plugin, LogPublishEvent.class, e -> /* ... */);
// 使用语句 lambda
eventBus.subscribe(this.plugin, UserLoadEvent.class, e -> {
// ...
});
// 使用方法引用
eventBus.subscribe(this.plugin, UserPromoteEvent.class, this::onUserPromote);
}
private void onUserPromote(UserPromoteEvent event) {
// ...
}
}
重要事件
UserDataRecalculateEvent– 用户缓存数据刷新时调用NodeAddEvent– 节点添加到用户/权限组时调用NodeRemoveEvent– 节点从用户/权限组移除时调用NodeClearEvent– 用户/权限组的所有/部分节点被移除时调用NodeMutateEvent– 上述事件的基类
实用信息
线程安全
- 所有 LuckPerms 内部都是线程安全的
- 可以从异步调度任务安全地与 API 交互
不可变性
- 返回的集合默认是不可变的
- 不能对返回的集合进行修改
阻塞操作
- 某些方法不是"主线程友好"的
- 大多数阻塞方法返回
CompletableFuture - 建议在异步任务中与 API 交互
使用 CompletableFutures
// 阻塞方式(仅在异步线程中使用)
CompletableFuture<User> userFuture = userManager.loadUser(uniqueId);
User user = userFuture.join();
// 回调方式
userFuture.thenAcceptAsync(user -> {
// 处理用户数据
}, executor);
常用工具方法
检查玩家是否在权限组中
public static boolean isPlayerInGroup(Player player, String group) {
return player.hasPermission("group." + group);
}
查找玩家所在权限组
public static String getPlayerGroup(Player player, Collection<String> possibleGroups) {
for (String group : possibleGroups) {
if (player.hasPermission("group." + group)) {
return group;
}
}
return null;
}
平台支持的操作对象类型
| 平台 | 操作对象类型 |
|---|---|
| Bukkit | org.bukkit.entity.Player |
| BungeeCord | net.md_5.bungee.api.connection.ProxiedPlayer |
| Sponge | org.spongepowered.api.service.permission.Subject |
| Fabric | net.minecraft.server.network.ServerPlayerEntity |
| Forge | net.minecraft.server.level.ServerPlayer |
| Nukkit | cn.nukkit.Player |
| Velocity | com.velocitypowered.api.proxy.Player |
重要注意事项
- 在线与离线玩家: 在线玩家保证有已加载的 User 对象,离线玩家可能没有
- 数据保存: 修改用户/权限组数据后必须调用保存方法
- 异步操作: 建议在异步任务中进行数据操作
- 缓存数据: 频繁查询时使用 CachedData 而不是直接查询 User/Group
- 事件监听: LuckPerms 使用独立的事件系统,需要直接注册到 LuckPerms
内容来自 SnowCutieOwO – Wiki