Skip to content

玩法机制 ​

这一页说明 LyCamp 的阵营战斗、声望、称号、属性、掉落、传送和数据存储规则。配置键的完整说明见 配置说明。

玩家数据 ​

LyCamp 会为玩家记录以下状态:

数据说明
所属阵营对应 camp 下的阵营名称,未加入阵营时为空
当前声望可被死亡或管理操作扣除的声望
累计声望用于匹配称号和称号属性,只会被指定管理操作或退出阵营降低
今日剩余声望当天还可以获得的当前声望数量
上次每日刷新时间用于判断今日剩余额度是否需要刷新
上次击杀时间用于判断当天的首次击杀是否完成

玩家数据未加载完成时,阵营战斗、传送检查和部分死亡结算不会正常进行。

阵营战斗规则 ​

阵营 PVP 只在 pvp-world 中配置的世界生效。普通世界中的玩家伤害不会被 LyCamp 接管。

玩家攻击另一名玩家时,会依次检查:

  1. 攻击者所在世界是否为已配置的 PVP 世界。
  2. 攻击者和目标的玩家数据是否均已加载。
  3. 攻击者是否加入了一个仍然存在于配置中的阵营。
  4. 目标是否加入了一个仍然存在于配置中的阵营。
  5. 目标阵营是否存在于攻击者阵营的 hostiles 列表中。
  6. 当前时间是否处于该世界的 PVP 开放时间内。

任意条件不满足时,本次伤害会被取消。

敌对关系是单向的 ​

hostiles 只读取攻击者阵营的配置,不会自动建立双向敌对关系。

如果阵营 A 可以攻击阵营 B,而阵营 B 也需要攻击阵营 A,两个阵营都必须配置对方:

yaml
camp:
  阵营A:
    hostiles:
      - '阵营B'
  阵营B:
    hostiles:
      - '阵营A'

同阵营玩家以及未被列入 hostiles 的阵营玩家无法互相造成直接玩家伤害。

ProtocolLib 拦截 ​

插件始终注册 Bukkit 玩家伤害监听。如果服务器安装并启用了 ProtocolLib,插件还会注册玩家攻击数据包监听,对 USE_ENTITY 攻击动作执行相同的阵营和时间检查。

ProtocolLib 未启用时不会注册数据包层拦截,但 Bukkit 伤害监听仍然存在。

PVP 开放时间 ​

每个 PVP 世界拥有独立的开始和结束时间:

yaml
pvp-world:
  pvp_world:
    pvp-start-time: '0 0 9 * * ?'
    pvp-end-time: '0 0 18 * * ?'

默认示例表示每天 9 点开始、18 点结束。非开放时间攻击玩家时,伤害会被取消,并向攻击者发送该世界的 not-pvp-message。

时间表达式使用六段格式:

text
秒 分 时 日 月 周

当前时间范围解析支持数字以及以下基础符号:

符号作用
*任意值
/步长
,多个值
-连续范围
?不指定日期或星期,只能用于“日”或“周”字段

“日”和“周”字段不能同时使用 ?。开始时间晚于结束时间时,插件会按跨日时间段处理。

时间判断使用严格边界比较,恰好命中开始时间或结束时间的那一秒不属于开放区间。无法解析表达式时,本次检查会返回非 PVP 时间。

如果开始或结束表达式配置为每秒触发:

text
* * * * * ?

该端时间限制会被视为不限制。

进入 PVP 世界 ​

当玩家通过传送事件从其它世界进入 PVP 世界时,插件会读取 must-have-camp-to-teleport。

设置为 true 时,玩家必须同时满足:

条件说明
玩家数据已加载玩家必须存在于 LyCamp 的在线数据中
已加入有效阵营阵营不能为空,并且应存在于当前阵营配置中
存在阵营位置目标世界的 respawn-location 必须包含该阵营名称

任意条件不满足时,传送会被取消,并发送 message.must-have-camp-to-teleport。

设置为 false 时,进入该世界不会进行上述阵营位置检查。

这里检查的是跨世界传送事件。插件配置注释仍建议阻止玩家通过其它未受控方式进入 PVP 地图。

离开 PVP 世界 ​

非 OP 玩家从任意已配置的 PVP 世界传送到另一个世界时,传送会被直接取消。

此限制不读取 allow-command,也不检查目标世界。即使某条传送命令被允许执行,最终产生的跨世界传送仍会被阻止。

OP 玩家不受离开限制。管理指令 /lyzy tp 由具有 OP 状态的命令发送者执行,用于把指定玩家传送到目标 PVP 世界的阵营位置。

阵营重生点 ​

玩家在 PVP 世界死亡并触发重生时,插件会根据玩家当前阵营查找该世界的 respawn-location。

状态重生结果
找到玩家阵营对应的位置将重生点设置为该位置
玩家数据未加载不修改重生点
玩家没有阵营不修改重生点
该世界没有对应阵营位置不修改重生点

位置格式为:

text
x,y,z,yaw,pitch

世界必须在插件加载 PVP 世界配置时已经存在,否则该世界的阵营位置不会载入。

声望体系 ​

LyCamp 分别记录“当前声望”和“累计声望”。两者用途不同,不能互相替代。

类型主要用途是否会因死亡扣除
当前声望记录玩家当前可用的阵营声望会
累计声望匹配称号、称号属性和声望上限不会

阵营的声望上限取该阵营 prestige 配置中最大的区间上限。当前声望和累计声望都不会高于这个值,也不会低于 0。

如果阵营没有有效的 prestige 配置,其最大声望为 0,玩家无法正常增加当前声望或累计声望。

声望变化 ​

场景当前声望累计声望今日剩余额度
在开放时间击杀玩家增加增加扣除实际增加量
在开放时间被玩家击杀扣除不变不变
在开放时间非玩家原因死亡扣除不变不变
PVP 世界内退出服务器不变不变不变
/lyzy given增加增加扣除实际增加量
/lyzy taken扣除不变不变
/lyzy setn 向上设置增加增加扣除实际增加量
/lyzy setn 向下设置扣除不变不变
/lyzy givec不变增加不变
/lyzy takec不变扣除不变
/lyzy setc不变设置为目标累计声望不变
/lyzy setcamp 退出阵营清零清零清零

击杀奖励读取当前 PVP 世界的 kill-give-prestige,死亡扣除读取 dead-take-prestige。

退出阵营时,还会把上次每日刷新时间清零。上次击杀时间不会在退出阵营时清零。

增加当前声望 ​

增加当前声望时,会同时受到三项限制:

  1. 本次增加量不能超过今日剩余额度。
  2. 当前声望不能超过阵营最大声望。
  3. 累计声望不能超过阵营最大声望。

最终实际增加量可能小于配置值或管理指令输入值。只有实际增加的部分会从今日剩余额度中扣除,并同步加入累计声望。

今日额度耗尽时,会发送 message.today-max-prestige。累计声望达到上限、无法继续增加时,会发送 message.cumulative-max-prestige。

扣除当前声望 ​

死亡和 /lyzy taken 只扣除当前声望,不减少累计声望,也不会返还今日剩余额度。

实际扣除量不会超过玩家已有的当前声望,因此当前声望最低为 0。

管理操作的声望限制 ​

管理指令不会绕过声望上下限。

  • /lyzy given 和向上调整的 /lyzy setn 仍受今日额度、当前声望上限和累计声望上限限制。
  • /lyzy givec 和 /lyzy setc 不消耗今日额度,但累计声望仍会被限制在 0 至阵营最大声望之间。
  • /lyzy takec 只能把累计声望扣到 0。
  • 累计声望降低时,不会执行称号区间中的奖励指令。

每日声望额度 ​

every-day-max-get-prestage-value 用于设置玩家每天最多还能获得多少当前声望。

插件计算额度时以 1000 为基础值,然后检查:

  • 名为 default 的配置。
  • 玩家拥有的权限配置。
  • 所有满足条件的配置中数值最高的一项。

只有高于当前结果的数值才会覆盖。因此,将 default 配置为低于 1000 不会降低基础额度。

权限键中的下划线会在加载时替换为点:

yaml
camp:
  潜伏者:
    every-day-max-get-prestage-value:
      default: 1000
      vip_1: 2000

其中 vip_1 实际检查的权限为 vip.1。如果玩家同时拥有多个已配置权限,会使用数值最高的一项。

每日刷新时机 ​

插件使用服务器默认时区,以当天 00:00:00 作为跨日边界。

每日额度通过两种方式刷新:

来源说明
玩家数据加载玩家数据读取完成后立即检查是否已经跨日
在线定时检查异步任务每秒检查一次;只有服务器当前秒数为 0 时才遍历在线玩家并判断是否跨日

满足跨日条件后,今日剩余额度会设置为玩家当前阵营计算出的每日上限,并把上次刷新时间更新为当天零点。

每日首杀 ​

玩家在 PVP 世界的开放时间内完成玩家击杀后,插件会比较上次击杀时间和服务器当天零点。

如果上次击杀发生在今天之前,会执行玩家阵营的 every-day-first-kill-run-command。结算完成后,无论是否属于今日首杀,都会把上次击杀时间更新为当前时间。

首杀指令支持:

写法执行方式
[console]指令由控制台执行
[op]指令临时以 OP 身份让击杀玩家执行,完成后恢复原状态
无前缀指令由击杀玩家直接执行

执行前会解析 PlaceholderAPI 变量。PlaceholderAPI 未安装时,涉及变量解析的功能无法按预期工作。

%lyzy_first_kill% 的判断与该时间记录一致:当天尚未完成首杀时返回 placeholder.has-first-kill,完成后返回 placeholder.not-first-kill。

死亡结算范围

死亡结算监听不会再次检查双方是否为敌对阵营。正常直接战斗会先经过伤害监听,因此通常只有敌对阵营能够完成击杀;由其它插件制造的伤害归属或特殊死亡流程仍可能直接进入结算。

称号区间 ​

阵营称号由累计声望匹配 camp.<阵营>.prestige 下的数值或范围键。

支持两种键格式:

格式说明
100只匹配累计声望 100
1-1000匹配包含边界的 1 至 1000

玩家累计声望位于区间内时,该区间的 tag 会作为当前称号。

建议保证区间连续且互不重叠。区间重叠时,属性系统会加入所有命中区间的属性,而称号变量只会返回遍历过程中第一个命中的称号。

首次达到区间 ​

累计声望从低值上升并越过某个区间的最小值时,会执行该区间的 command。

如果一次增加跨过多个区间的最小值,所有被跨过区间的指令都会执行。累计声望降低后再次上升并重新越过最小值时,对应指令可以再次执行。

区间指令执行前会:

  1. 将 {tag} 替换为该区间的称号。
  2. 解析 PlaceholderAPI 变量。
  3. 按 [console]、[op] 或玩家身份执行。

仅在区间内部变动、没有从下方向上越过最小值时,不会执行该区间指令。

阵营属性 ​

玩家加入有效阵营后,最终提交给属性插件的属性由以下内容组成:

  1. camp.<阵营>.attribute 中的阵营基础属性。
  2. 当前累计声望命中的所有 prestige.<区间>.attribute 称号属性。

当前代码支持以下 attribute-plugin 值:

配置值对接目标
AttributePlusAttributePlus
SX-Attribute2SX-Attribute 2
SX-Attribute3SX-Attribute 3
AttributeSystemAttributeSystem
ItemLoreOriginItemLoreOrigin
空字符串不向属性插件提交属性

属性会在以下时机刷新:

时机说明
玩家数据加载后数据读取流程会请求更新属性
玩家关闭背包界面后每次触发 InventoryCloseEvent 都会重新计算

属性计算和提交在异步任务中执行。当前声望不会参与称号属性判断,只有累计声望参与。

attribute-plugin 的对接初始化发生在插件启动阶段。默认配置也明确要求修改该项后重启服务器。

死亡与离线掉落 ​

特殊掉落只在已配置的 PVP 世界中,并且当前处于该世界 PVP 开放时间时处理。

场景声望扣除特殊掉落
被玩家击杀,双方数据均已加载扣除当前声望处理
非玩家原因死亡,玩家数据已加载扣除当前声望处理
PVP 世界开放时间内退出服务器不扣除声望处理
非 PVP 世界死亡或退出不处理不处理
PVP 世界非开放时间死亡或退出不处理不处理

插件只扫描玩家背包索引 0 至 35,即背包和快捷栏的前 36 个格子。装备栏、副手等其它位置不在扫描范围内。

Lore 判断采用“包含”匹配。配置中的 & 颜色代码会先转换为 Minecraft 颜色代码,再与物品 Lore 的每一行比较。

随机掉落 ​

随机掉落使用全局 force-drop-item-lore 和当前世界的 force-drop-item-amount。

处理流程:

  1. 扫描前 36 格。
  2. 将 Lore 包含任意 force-drop-item-lore 文本的格子加入候选池。
  3. 从候选格子中随机选择指定数量。
  4. 将选中格子的整组物品从背包移除。
  5. 在玩家当前位置自然生成掉落物。

实际随机掉落格数为 force-drop-item-amount 和候选格子数量中的较小值。数量按格子计算,不按单个物品计算,一个格子中的整组物品会一起掉落。

force-drop-item-amount 小于或等于 0 时,不执行随机掉落。

必定掉落 ​

随机掉落完成后,插件会再次扫描前 36 格。Lore 包含任意 independent-of-the-world-force-drop-item-lore 文本的物品会全部掉落。

必定掉落具有以下特点:

  • 不受 force-drop-item-amount 限制。
  • 每个匹配格子的整组物品都会掉落。
  • 如果某件物品已在随机阶段掉落并从背包移除,不会再次掉落。
  • 掉落记录后会附带“必定掉落”标记。

掉落提示 ​

只有实际掉落了至少一个匹配物品时,插件才会发送掉落信息。

场景使用的消息
被玩家击杀drop-item-message
非玩家死亡或退出服务器abnormal-death-drop-item-message

被玩家击杀时支持:

占位文本内容
{killer}击杀者名称
{camp}击杀者阵营名称

消息之后会逐行列出掉落物名称和数量。存在自定义显示名称时使用显示名称,否则使用 Bukkit 材质名称。

PVP 世界命令限制 ​

非 OP 玩家在 PVP 世界内执行命令时,插件会将完整命令文本与该世界的 allow-command 逐项比较。

匹配方式为区分大小写的字符串前缀匹配:

yaml
pvp-world:
  pvp_world:
    allow-command:
      - '/say'

配置 /say 后,以下文本都会匹配:

text
/say
/say 内容
/saytest

因此,白名单应尽量写成不会误放行其它命令的前缀。插件不会自动检查命令名称后的空格边界。

没有任何前缀匹配时,命令事件会被取消。OP 玩家不受该限制。

消息键差异

当前命令拦截代码读取的是 message.allow-message,默认配置提供的却是 message.allow-command。未额外添加 message.allow-message 时,会使用代码内置的“PVP地图内禁止使用该指令”提示;修改默认的 message.allow-command 不会改变此处提示。

数据加载与保存 ​

插件支持本地 YAML 和 MySQL 两种玩家数据存储方式。mysql.enable 的修改需要重启服务器,因为玩家加载、退出保存监听器会在插件启动时按该开关选择注册。

本地 YAML ​

MySQL 未建立连接时,玩家数据保存到:

text
plugins/LyCamp/data/玩家UUID.yml

本地数据使用 UTF-8 无 BOM 读写,并采用以下保护流程:

  1. 同一玩家文件的读取和写入使用文件锁串行处理。
  2. 保存时先写入唯一的临时文件。
  3. 已存在的主文件会移动到同名 .bak 备份。
  4. 临时文件再替换为主文件。
  5. 文件系统不支持原子移动时,会回退为普通覆盖移动。
  6. 读取到损坏主文件时,插件会尝试从 .bak 恢复。
  7. 主文件和备份均不可用时,返回空玩家数据。
  8. 数据目录中的历史 .tmp 文件会被清理。

MySQL ​

启用 MySQL 时,必须安装 LyMySQLCore,并确保 LyMySQLCore 已成功连接数据库,相关玩家加载与保存功能才会生效。

LyCamp 自身还必须使用 mysql 下的连接信息成功建立连接池。连接未完成时,启用了 MySQL 的服务器会拒绝玩家登录,并提示服务器尚未加载完成。

数据库表名使用插件名称,玩家名称存入 name 字段,序列化后的玩家数据存入 data 字段。

保存时机 ​

时机行为
玩家数据发生需要保存的声望变化标记数据为已修改
自动保存任务每 50 tick 检查在线玩家,只保存已标记修改的数据
玩家正常退出保存数据并移除内存数据
玩家被踢出保存数据并移除内存数据
插件关闭保存所有在线玩家数据

MySQL 模式通过 LyMySQLCore 提供的安全加载、保存事件处理正常登录和退出数据;玩家被踢出时也会直接执行保存。

重载行为 ​

执行 /lyzy reload 后,插件会重新读取配置并更新:

  • 阵营数据和声望区间。
  • PVP 世界规则和阵营位置。
  • PlaceholderAPI 默认文本。
  • 自动保存任务。
  • 每日额度检查任务。
  • 已启用的 MySQL 连接。

重载不会重新选择或注册 YAML、MySQL 玩家事件监听器,也不会重新执行属性插件和 ProtocolLib 的启动初始化。因此,修改 mysql.enable 或 attribute-plugin 后应重启服务器。