- 版权类型
- 原创
- 插件中文名称
- 在线时长记录api
- 插件英文名称
- TimeRecord API
- 原帖地址
- #
- 支持的核心(服务端)
- Spigot
- Paper
- 语言支持
- 中文(简体)
- 适配版本(Java)
- 最新版本
- 26.x
- 1.21
[MD]
# TimeRecord
TimeRecord 是一个面向 Spigot 生存服务器的在线时长统计插件。它使用 SQLite 按玩家和日期记录有效在线时间,并通过多维 AFK 检测排除离开游戏或高度机械重复行为产生的时长。
## 功能概览
- 按 UUID 记录玩家,玩家改名后历史数据不会丢失。
- 按服务器时区将有效在线秒数拆分到每日记录。
- 查询今日、本月、指定日期或指定月份的在线时长。
- 使用箱子 GUI 查看整月日历。
- 查看指定月份在线时长 Top 10。
- 默认使用多维 AFK 检测,也可以切回旧版五分钟位置检测。
- AFK 状态日志和限频 Debug 日志可分别开关,并支持按天轮转。
- SQLite 操作异步执行,插件关闭时会写入最后一批时长。
- 通过 Bukkit `ServicesManager` 提供异步只读 API。
## 运行要求
- Java 21。
- Spigot 1.21.11 或兼容的 1.21.x 服务端。
- 插件本身不需要单独安装 SQLite;驱动已包含在构建产物中。
## 安装与升级
1. 停止服务器。
2. 将 `timerecord-1.0-SNAPSHOT.jar` 放入服务器的 `plugins` 目录。
3. 启动服务器。首次启动会生成:
- `plugins/TimeRecord/config.yml`
- `plugins/TimeRecord/timerecord.db`
4. 根据需要修改 `config.yml`,然后完整重启服务器使配置生效。
升级插件时建议先停止服务器并备份整个 `plugins/TimeRecord` 目录。不要在服务器运行期间只复制 `timerecord.db`,因为 SQLite 可能同时使用 `timerecord.db-wal` 和 `timerecord.db-shm`。
## 权限说明
当前版本没有单独的权限节点:
- OP 玩家可以执行全部查询指令。
- 控制台可以执行文本查询和排行榜指令。
- 普通玩家不能执行查询指令。
- `/trg` 会打开箱子 GUI,因此只能由游戏内 OP 玩家执行,控制台无法使用。
## 指令指南
### 查询玩家在线时长
```text
/tr <玩家> today
/tr <玩家> month
/tr <玩家> date <yyyy-MM-dd|MM-dd>
/tr <玩家> month-at <yyyy-MM|MM>
```
示例:
```text
/tr Steve today
/tr Steve month
/tr Steve date 2026-07-25
/tr Steve date 07-25
/tr Steve month-at 2026-07
/tr Steve month-at 07
```
- `today`:查询服务器时区中的今日记录。
- `month`:查询服务器时区中的本月累计记录。
- `date`:查询指定日期。使用 `MM-dd` 时自动采用服务器当前年份。
- `month-at`:查询指定月份。使用 `MM` 时自动采用服务器当前年份。
- 玩家必须至少成功加入过一次服务器,才能通过名称查询。
### 打开月历 GUI
```text
/trg <玩家> <yyyy-MM|MM>
```
示例:
```text
/trg Steve 2026-07
/trg Steve 07
```
GUI 会显示该月每一天的在线时长,可使用界面中的左右按钮切换相邻月份。日历中的物品不可取出。
### 查看月排行榜
```text
/trr <yyyy-MM|MM>
```
示例:
```text
/trr 2026-07
/trr 07
```
结果显示指定月份有效在线时长最高的 10 名玩家。
## AFK 检测说明
### 高级检测
默认配置 `afk-detection.use-advanced: true` 会启用多维检测。插件在内存中分析近期行为,包括:
- 聊天和非重复命令。
- 容器打开、背包整理和物品拖动。
- 方块破坏、放置和物品使用。
- 主动攻击、快捷栏切换。
- 有效区域移动和视角目标变化。
- 行为间隔、位置、角度及动作序列的重复程度。
- 玩家受到实体伤害后是否产生合理反应。
聊天和命令内容只会转换为不可逆指纹用于短期重复判断,不保存原文,也不会写入数据库。
高级检测包含四种内部状态:
| 状态 | 含义 | 是否累计在线时长 |
| --- | --- | --- |
| `ACTIVE` | 正常活跃 | 是 |
| `IDLE` | 活跃度较低,尚未确认 AFK | 是 |
| `AFK` | 高概率真人离开 | 否 |
| `AUTOMATED` | 疑似宏、脚本或机械重复行为 | 否 |
状态带有持续确认和恢复回滞,不会因为单次转头、跳跃或挥手频繁切换。进入 `IDLE`、`AFK`、`AUTOMATED` 或恢复计时时,玩家会收到相应提示。
### 旧版检测
将配置改为:
```yaml
afk-detection:
use-advanced: false
```
重启后会使用旧算法:玩家 XYZ 坐标持续五分钟不变时进入 AFK,默认提前 30 秒提醒;再次发生实际位置变化、传送或世界切换时恢复计时。
## AFK 日志与 Debug 模式
默认会记录影响在线计时的重要 AFK 事件:
- 控制台前缀:`[AFK]`。
- 当前文件:`plugins/TimeRecord/afk.log`。
- 记录全部 `ACTIVE / IDLE / AFK / AUTOMATED` 状态切换,以及旧算法 AFK 预警。
- 每条状态记录包含玩家名、触发时间、世界名、整数 XYZ、前后状态、触发源和原因;UUID 可通过配置选择是否显示。
- 风险评估触发的切换会附带活跃分、空闲秒数、AFK 风险和自动化分;行为恢复会附带行为类型与有效分。
普通状态日志和详细 Debug 可以在 `config.yml` 中独立开关:
```yaml
logging:
enabled: true
include-uuid: false
debug:
enabled: false
retention-days: 7
```
修改后完整重启服务器。启动日志会分别显示重要事件日志和 Debug 日志状态。
四种组合的行为如下:
- `logging: true`、`debug: false`:状态和预警进入 `afk.log`,不产生 Debug 明细。
- `logging: true`、`debug: true`:状态和预警进入 `afk.log`,诊断信息进入 `debug.log`,状态不会重复。
- `logging: false`、`debug: true`:不写 `afk.log`,状态、预警和诊断信息统一进入 `debug.log`。
- 两项均为 `false`:不创建 AFK 日志线程,不写入控制台或 AFK 日志文件。
这些开关只控制 AFK 状态和诊断日志,不影响玩家提示、AFK 判定、在线计时、插件启动信息、配置警告或数据库异常日志。旧版配置中缺少 `logging.enabled` 时按默认值 `true` 处理。
`logging.include-uuid` 控制所有 AFK 日志中的玩家 UUID:
- 默认 `false`:控制台、`afk.log`、`debug.log` 和状态兜底只显示玩家名,完全省略 `uuid` 字段;旧配置缺少该项时同样按 `false` 处理。
- 设置为 `true`:恢复 `player=Steve uuid=...` 格式。
- 此设置只改变日志展示,数据库和玩家追踪始终继续使用 UUID。
### 输出位置
- 重要事件:`logging.enabled: true` 时,控制台使用 `[AFK]`,文件为 `afk.log`。
- 详细诊断:控制台使用 `[AFK-DEBUG]`,文件为 `debug.log`;仅在 `debug.enabled: true` 时产生。
- 普通日志关闭而 Debug 开启时,状态和预警使用 `[AFK-DEBUG]` 并写入 `debug.log`。
- 每日归档分别为 `afk-yyyy-MM-dd.log` 和 `debug-yyyy-MM-dd.log`。
- 归档重名时追加 `-1`、`-2` 等数字后缀,绝不覆盖已有日志。
- `debug.retention-days` 同时控制两类历史归档的保留天数。
- 关闭任一开关不会删除或清空已有日志;重新开启后继续执行轮转和过期清理。
两类文件均使用 UTF-8 和服务器时区,并使用相互独立的有界队列及守护线程写入。Debug 日志爆量不会挤掉重要状态日志;任一文件队列溢出或写入失败时,插件会限频告警并继续控制台输出,AFK 判定和在线计时不受影响。
### 日志内容
Debug 模式经过以下精简,不再逐条输出所有事件:
- 强行为立即记录行为质量、有效分、位置、目标和状态。
- 其他有效行为按“玩家 + 行为类型”每 10 秒汇总,输出次数、总分和最高质量;弱行为不输出。
- 载具、外部速度、下落、水流或冰面等被动位移按原因每 10 秒汇总。
- 传送、世界切换和伤害刺激等低频事件立即记录。
- 完整风险评估每名玩家最多每 30 秒输出一次;指标跨越 AFK 或自动化阈值时立即输出。
- 普通日志开启时状态切换只进入 `afk.log`;普通日志关闭时则回退到 `debug.log`。
示例:
```text
2026-07-26T07:30:00.000+08:00 [STATE] player=Steve from=IDLE to=AFK reason=AFK_RISK_CONFIRMED trigger=EVALUATION world=world x=128 y=64 z=-32 activity=0.120 automation=0.000 idleSeconds=612.0 afkRisk=0.835
2026-07-26T07:31:00.000+08:00 [STATE] player=Steve from=AFK to=ACTIVE reason=STRONG_ACTION trigger=CHAT world=world x=128 y=64 z=-32 score=12.000
2026-07-26T07:31:10.000+08:00 [EVENT] player=Steve type=SUMMARY activityType=BLOCK_BREAK count=8 totalScore=43.500 maxQuality=1.000 ...
2026-07-26T07:31:30.000+08:00 [EVALUATION] player=Steve activity=24.300 activityNorm=0.810 ... afkRisk=0.120 state=ACTIVE
```
### 隐私与性能
- 聊天和命令原文不会进入日志组件;日志只显示 SHA-256 的前 12 位短指纹,用于判断是否重复。
- 日志始终包含玩家名称;UUID 默认隐藏,可通过 `logging.include-uuid` 开启。位置只记录世界名和方块整数坐标,不记录小数坐标或视角。
- 高频微小移动和未达到采样阈值的视角变化不会逐条记录。
- Debug 关闭后不创建新的 `debug.log`;是否写入重要状态由 `logging.enabled` 决定。
- 问题排查完成后建议重新设置 `enabled: false` 并重启服务器。
## 配置与调参
默认配置已经为常规生存服提供了较保守的参数。每个配置项的用途、单位、范围和约束均写在 `config.yml` 的中文注释中。
调参建议:
- 误判正常玩家为 AFK:适当提高 `state.afk-threshold` 或 `idle.afk-confirm-seconds`。
- AFK 判定太慢:适当降低 `idle.full-risk-seconds` 或 `idle.afk-confirm-seconds`。
- 对循环宏不够敏感:适当提高 `automation.interval-cv-threshold`,或降低 `automation.suspicion-threshold`。
- 自动化误判较多:提高 `automation.minimum-actions`、`automation.suspicion-threshold` 或 `automation.confirm-seconds`。
- 不确定参数影响时,优先恢复默认值;非法配置会在启动日志中产生警告并自动回退。
阈值修改幅度建议每次不超过 `0.05`,确认时间建议每次只调整 10~30 秒,并结合实际服务器行为观察。
## 数据与时间计算
- 数据库位置:`plugins/TimeRecord/timerecord.db`。
- 数据按“玩家 UUID + 自然日”保存有效在线秒数。
- 日期边界使用插件启动时服务器操作系统的默认时区。
- 查询结果会将秒数四舍五入为分钟。
- 月总时长先汇总整月秒数再四舍五入;日历每天分别四舍五入,因此每日显示值之和可能与月总值有少量差异。
- 行为历史、AFK 风险和自动化证据仅保存在内存中,玩家离线或服务器重启后清除。
## 构建
项目使用 Maven:
```text
mvn clean package
```
构建产物位于:
```text
target/timerecord-1.0-SNAPSHOT.jar
```
构建过程会运行全部单元测试,并将 SQLite JDBC 驱动打入最终 JAR。
## 公共 API
其他插件可以通过 Bukkit `ServicesManager` 获取 `TimeRecordApi`,异步查询每日、每月、每日明细、排行榜及已记录玩家。
完整接入说明和 Java 示例请查看 [API.md](https://github.com/73410/TimeRecord/blob/main/API.md)。
## 常见问题
### 修改配置后没有生效
当前版本不支持热重载。修改 `config.yml` 后请完整重启插件或服务器,并在日志中确认当前使用的是“高级多维检测”还是“旧版位置检测”。
### 查询时提示未找到玩家
玩家需要至少加入服务器一次,TimeRecord 才会保存 UUID 与最近使用的名称。名称匹配不区分大小写。
### 插件无法启动
确认服务端使用 Java 21,并检查服务端版本是否兼容 Spigot 1.21.x。具体初始化异常会写入服务器日志。
### 数据库查询失败
先查看服务器日志中的 SQLite 异常。备份数据库前应停止服务器,不要手工删除运行中的 `-wal` 或 `-shm` 文件。
[/MD]