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匹配包含边界的 11000

玩家累计声望位于区间内时,该区间的 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 世界非开放时间死亡或退出不处理不处理

插件只扫描玩家背包索引 035,即背包和快捷栏的前 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.enableattribute-plugin 后应重启服务器。