自定义条件读取组件
概述
条件读取组件用于从物品 Lore 中解析条件,并判断该条件是否满足。例如,插件自带的「等级需求」「权限需求」「装备类型」都属于条件读取。
核心任务: 判断「这一行的条件是否满足」。
基础结构
自定义条件读取组件需要:
- 继承
GroovyConditionReader - 在构造函数中设置
name、priority、key - 定义
readFormat(正则匹配模式) - 重写
read()方法
构造函数参数
GroovyConditionReader(String name, int priority, String key)| 参数 | 说明 | 示例 |
|---|---|---|
name | 读取器名称,会作为 read.yml 中的配置键 | "health" |
priority | 优先级,数字越小越先执行 | 10 |
key | 条件标识——Lore 中匹配此 key 的行才会触发此读取器 | "血量需求" |
有了 key = "血量需求",那么当解析到 血量需求: 100 这样的 Lore 行时,就会调用这个读取器。
readFormat 正则
List<String> readFormat = Arrays.asList("{key}.*?@value")格式中的特殊标记:
{key}— 自动替换为构造函数中传入的key值@value— 自动替换为数值匹配正则(可以匹配100、50-100等)
你也可以写更复杂的正则,比如:
// 匹配 "血量需求: 100" 或 "血量需求:100" 格式
List<String> readFormat = Arrays.asList("{key}\\s*:\\s*@value")
// 匹配 "血量需求 >= 100" 格式
List<String> readFormat = Arrays.asList("{key}\\s*>=\\s*@value")完整示例
下面实现一个「血量需求」条件读取器——Lore 中写 血量需求: 100 表示需要 100 血以上才能使用:
package scripts.reads
import cn.org.bukkit.craneattribute.api.attribute.data.AttributeData
import cn.org.bukkit.craneattribute.api.attribute.source.AttributeSource
import cn.org.bukkit.craneattribute.api.attribute.source.Conditional
import cn.org.bukkit.craneattribute.api.attribute.source.ItemStackBased
import cn.org.bukkit.craneattribute.api.read.ReadableLine
import cn.org.bukkit.craneattribute.api.utils.StringUtilsKt
import cn.org.bukkit.craneattribute.core.attribute.data.PlayerAttributeData
import cn.org.bukkit.craneattribute.core.manager.CacheManager
import cn.org.bukkit.craneattribute.core.utils.BukkitUtils
import groovy.transform.CompileStatic
import scripts.libs.GroovyConditionReader
@CompileStatic
class HealthConditionReader extends GroovyConditionReader {
// 正则匹配模式
List<String> readFormat = Arrays.asList("{key}.*?@value")
HealthConditionReader() {
super("health", 10, "血量需求")
}
@Override
protected boolean read(ReadableLine readableLine, AttributeData data, AttributeSource attributeSource) {
// 第一步:只给玩家判断,非玩家直接放行
if (data instanceof PlayerAttributeData) {
// 第二步:使用缓存管理器获取正则解析结果
List<String> list = CacheManager.INSTANCE.getStringList(readableLine) {
// 缓存未命中时,调用 extractValues 进行正则解析
extractValues(readableLine, readFormat)
}
// 第三步:解析结果可能为 null(该行不匹配此读取器)
if (list == null) return true
// 第四步:将字符串转为数值数组 [最小值, 最大值]
double[] numbers = StringUtilsKt.toDoubleArray(list.first())
double first = numbers[0] // 最小值
double last = numbers[1] // 最大值
// 第五步:获取玩家的实际血量
double health = data.player.getHealth()
// 第六步:判断是否满足条件
if (first == last && health >= first) {
// 单一值,如"血量需求: 100",玩家血量>=100即可
return true
} else if (first != last && health in first..last) {
// 范围值,如"血量需求: 50-100",玩家血量在范围内即可
return true
}
// 第七步:不满足时,可以选择发送提示信息
if (attributeSource instanceof Conditional && attributeSource instanceof ItemStackBased) {
def itemName = BukkitUtils.getItemName(attributeSource.itemStack, data.player)
if (first == last) {
data.player.sendMessage("§b[CraneAttribute]§e §a${itemName}§e 要求玩家血量大于等于 §a${first}§e")
} else {
data.player.sendMessage("§b[CraneAttribute]§e §a${itemName}§e 要求玩家血量为 §a${first}§e ~ §a${last}§e")
}
}
// 不满足条件
return false
}
// 非玩家实体 → 无条件放行
return true
}
}代码逐步解析
1. if (data instanceof PlayerAttributeData)
属性数据有 PlayerAttributeData(玩家)和 AttributeData(非玩家)两种。条件判断通常只对玩家有意义。
2. CacheManager.INSTANCE.getStringList(...)
同一行 Lore 可能被多个读取器检查。缓存管理器确保了正则解析只执行一次,后续访问直接返回缓存结果。{ it -> extractValues(readableLine, readFormat) } 是缓存未命中时的回调。
3. if (list == null) return true
extractValues 返回 null 表示「这行文本不匹配我的正则模式」——不是我的事,直接返回 true 让后面的逻辑继续。
4. StringUtilsKt.toDoubleArray(list.first())
将 "100" 转为 [100.0, 100.0],将 "50-100" 转为 [50.0, 100.0]。这是插件提供的工具方法。
5. 判断逻辑
first == last:单一值 → 要求 ≥ 该值first != last:范围值 → 要求在范围内
6. 发送提示
条件不满足时,可以给玩家发送一条消息说明原因。判断 Conditional 和 ItemStackBased 是为了确保只在「物品上的条件」场景下发消息(而不是属性源更新等场景)。
更简单的示例:公会等级需求
如果你的需求很简单,代码可以更短:
@CompileStatic
class GuildLevelCondition extends GroovyConditionReader {
List<String> readFormat = Arrays.asList("{key}.*?@value")
GuildLevelCondition() {
super("guild_level", 15, "公会等级需求")
}
@Override
protected boolean read(ReadableLine readableLine, AttributeData data, AttributeSource attributeSource) {
if (data instanceof PlayerAttributeData) {
List<String> list = CacheManager.INSTANCE.getStringList(readableLine) {
extractValues(readableLine, readFormat)
}
if (list == null) return true
double[] numbers = StringUtilsKt.toDoubleArray(list.first())
// 假设你有一个方法来获取玩家公会等级
int guildLevel = getPlayerGuildLevel(data.player)
if (guildLevel >= numbers[0]) {
return true
}
data.player.sendMessage("§c需要公会等级 ${numbers[0].toInteger()} 才能使用此装备")
return false
}
return true
}
}注册与配置
保存到 scripts/reads/ 后,使用 /ca reload 重载。插件会自动在 read.yml 的 read condition 下生成配置:
read condition:
health:
priority: 10
key: 血量需求
format:
- "{key}.*?@value"你可以在这里直接修改 priority、key 和 format,下次重载时会覆盖代码中的设置(但代码中的 key 是初始值)。
注意事项
- 非玩家实体通常返回
true放行 - 正则解析失败时应返回
true(容错优先) data.player只在PlayerAttributeData类型下可用,使用前记得instanceof检查- 条件读取组件决定了整行属性是否生效,而不仅仅是单个属性
下一步
了解条件读取后,继续学习 自定义数值读取组件 —— 决定属性值如何从文本中提取。
