VIP计时插件技术文档

来自Oasis Wiki

[Also available in English]

VIPTimer 是开发用来对权限进行计时,并在适当的时机执行适当的指令的插件。除此之外,插件支持利用 MySQL 数据库在不同的服务器之间同步数据(暂未考虑容错性,谢绝自行野蛮更改程序所操作的文件)。

指令

玩家默认只能使用 /viptimer menu 来查看属于自己的 VIP 菜单,没有其它可用的指令。

指令均以 /viptimer 开头,可简写为 /vipt。以下指令均只有管理员可以使用。

指令 解释
/vipt give <playername> <type> <duration> 给予指定玩家一定时长的指定类型的 VIP。如果指定类型的 VIP 已存在,则在原日期的基础上进行加减。duration 的格式见下文「格式约定」
/vipt set <playername> <type> <until> [new?] 给予/设置指定玩家一定时长的指定类型的 VIP。until 的格式见下文「格式约定」当玩家没有此 VIP 的记录时,需要在末尾加上 new。
/vipt take <playername> <type> 取消指定玩家的指定类型的 VIP。
/vipt transown <from> <from-type> <to> 将 from 的 from-type 类型的 VIP 转让给 to,到期时间不变。
/vipt chtype <playername> <from> <to> 将指定玩家的一种类型(from)的 VIP 改为另一种类型(to)的 VIP,到期时间不变。
/vipt reload 刷新内存中的文件。

配置文件

config.yml - 用于配置插件。

# 数据库相关配置
database:
  username: ""
  password: ""
  host: "localhost"
  port: 3306
  # 自行创建
  db: "viptimer"
# 可从 distributor 和 receiver 中选择
# 不填则为 none
# 具体解释请见 https://wiki.oases.red/VIP%E8%AE%A1%E6%97%B6%E6%8F%92%E4%BB%B6%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3
role: distributor
# 在获得/失去会员时显示的信息,可以使用占位符。
# 留空则不发送
thank-you-text: ~

types.yml - 用于定义 VIP,请自行编写。此文件详细用法见后文。

types:
    vip1:
        displayname: ~
        give:
            - lp user $playername permission set ... true
        take:
            - lp user $playername permission set ... false
inst_id: ~

tasks.yml - 程序内部操作的文件,用于存储当操作对象玩家不在线时需要执行的操作或者需要发送的信息,然后在玩家上线以后立即执行。此机制使得一部分较为依赖玩家在线的操作推迟至玩家下一次上线后执行。

文件同步功能(Beta)

VIPTimer 支持从一个集中的分发者(distributor)将文件的内容同步到所有接收者(receiver)处。

简单地说,就是可以让一个地方的文件按照一定的刷新时间同步至所有其它需要的地方。在本插件中,同步的对象是 types.yml,此文件用于描述各个 VIP 的具体内容。

设置文件同步

首先需要确定一个主服务器,此主服务器充当 distributor 的角色。在主服务器中的 config.yml 中,将 role 字段设定为 distributor 即可定义该服务器所用的插件角色为 distributor。

更换角色以后,必须重启服务器(不是 reload)才能生效。

然后,在其余的服务器都安装本插件,并在 config.yml 中,将 role 字段设定为 receiver 即可定义该服务器所用的插件角色为 receiver,将实时接收来自 distributor 的文件改动。

注:不同服务器的插件之间的通讯依赖于 MySQL,因此这些服务器的插件都必须连接到同一个 MySQL Server 的同一个数据库中

取消文件同步/不需要文件同步

取消文件同步或者不需要文件同步,将 config.yml 中的 role 改为代表 null 的 ~ ,重启服务器即可。

数据库设置

插件的正常运行需要事先设置好数据库,下面给出参考设置的 SQL 语句。最新语句见 https://github.com/oasis-mc/viptimer/blob/main/init.sql

执行下面的语句之前,请先 CREATE DATABASE 并 USE。

由于 JDBC 驱动限制,暂时不支持连接 MariaDB(因为即使是最新版的 MariaDB 的 tx_isolation 字段也没有如 MySQL 8 一样更新为 transaction_isolation,这将导致驱动的兼容性问题)。如果你愿意解决此问题,欢迎来提交 PR。

CREATE TABLE `records` (
	`id` INT unsigned NOT NULL AUTO_INCREMENT,
	`playername` VARCHAR(20) NOT NULL,
	`type` VARCHAR(10) NOT NULL,
	`until` BIGINT unsigned NOT NULL,
	`created_by` VARCHAR(20) NOT NULL,
	`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
	`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (`id`)
);

CREATE TABLE `distribution` (
    `id` INT unsigned NOT NULL AUTO_INCREMENT,
    `dist_by` VARCHAR(36) NOT NULL,
    `dist_content` TEXT NOT NULL,
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
);

CREATE TABLE `receipt` (
    `id` INT unsigned NOT NULL AUTO_INCREMENT,
    `dist_by` VARCHAR(36) NOT NULL,
    `recv_by` VARCHAR(36) NOT NULL,
    `recv_count` INT unsigned DEFAULT 0,
    `recv_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
);

CREATE TABLE `delivery` (
    `id` INT unsigned NOT NULL AUTO_INCREMENT,
    `inst_id` VARCHAR(36) NOT NULL,
    `playername` VARCHAR(20) NOT NULL,
    `type` VARCHAR(10) NOT NULL,
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
);

请不要使用 root 账户。如有需要,请自行按照下面的方式创建新的用户。

CREATE USER viptimer IDENTIFIED BY password;
GRANT ALL PRIVILEGES ON <数据库名>.*;
FLUSH PRIVILEGES;

格式约定

/vipt give ... <duration>

duration 内需要符合正则表达式 -?\d+[h|d|m],即为一个整数+h 或 m 或 d 的格式,其中

  • h 表示小时
  • d 表示天
  • m 表示月,不是分钟

如需设置分钟,请使用 set 来精确设置。

  • /vipt give Subilan oasis_life 20d - 给予 Subilan 离执行时间(按服务器)整 20 天(1.278e9 毫秒)的 Oasis Life 权限。
  • /vipt give Subilan oasis_life -20d - 将 Subilan 已存在的 Oasis Life 期限缩短 20 天。负数只能在记录已经存在的情况下出现,若结果小于当前时间则执行失败。

/vipt set ... <until> [new?]

until 可以是两种格式,一是 Unix 时间戳,二是标准的日期字符串,精确到秒。如果先前不存在记录,在末尾需要加上 new。

  • Unix 时间戳理论上可填入任意整数,不保证能正确转换。
  • 标准日期字符串须符合 yyyy-MM-dd_HH:mm:ss,例子:2023-08-31_19:00:32,下划线不可省略,符号不可变更

试试输入 /vipt set ... 2023-13-21_27:89:102 看会发生什么!

  • /vipt set Subilan oasis_life 2023-08-31_21:30:00 - 将 Subilan 的 Oasis Life 有效期设置为服务器时间 2023 年 8 月 31 日 21:30:00。
  • /vipt set Subilan oasis_new 2023-09-21_23:59:59 new - 给予 Subilan 有效期至服务器时间 2023 年 9 月 21 日 23:59:59 的 Oasis New

VIP 配置文件

请注意配置文件缩进,并确保所有 VIP 配置文件都放在 types 键下。

注意:只有 distributor 所在的服务器需要设置 VIP 配置文件。

设置为 receiver 的服务器的配置文件将会被来自 distributor 的内容直接覆盖!

types:
    oasis_life: # <-- [1]
        displayname: Oasis Life # <-- [2]
        give:
            - lp user $playername parent add oasislife
        take:
            - lp user $playername parent remove oasislife

displayname

如上 YML,标注为 1 的地方,称为该 VIP 类型的内部名称(internal name),内部名称会在程序内部使用,并对 OP 显示。标注为 2 的地方,称为该 VIP 类型的显示名称(display name),显示名称会显示给玩家。

内部名称需要遵循一般的 YML 键的格式,不能出现非法字符。

显示名称中可以使用 MiniMessage 语法来实现字体的颜色、RGB 渐变等效果。如果不需要,直接不加修饰填写即可。

givetake

give 和 take 下列出在玩家获得/失去该类型的 VIP 时将会执行的指令,按照次序执行。开发者建议保持 give 和 take 的指令数量相同,且作用相反(一个是给,一个是取)

不需要手动添加告诉玩家已经获得/失去 VIP 消息的指令,插件已自带。

占位符

占位符可以使用在 give 或者 take 的指令里,会被替换成对应的内容。

  • $playername - 被执行玩家的游戏名称
  • $displayname - 会员的显示名称,可带 MiniMessage 语法(若找不到则返回内部名称)
  • $raw_displayname - 会员的显示名称纯文本

插件机制

当执行 give、set、take、transown、chtype 时,数据库内的记录会立即变化,其具体变化如下:

  • give - 向插入一条带有玩家名称、VIP 类型、截止时间的记录
  • set - 若为新增操作,则与 give 相同。若不是则直接修改对应玩家名称和 VIP 类型的截止时间。
  • take - 删除含对应玩家名称和 VIP 类型的记录。
  • transown - 将对应玩家名称和 VIP 类型的记录的玩家名称修改为新的玩家名称。
  • chtype - 将对应玩家名称和 VIP 类型的记录的 VIP 类型修改为新的 VIP 类型。

当执行 give、set、take、transown、chtype 时,玩家的具体变化如下:

  • give - 当玩家在线时,按照次序执行描述在 give 内的各个指令。
  • set - 不体现在玩家上。
  • take - 当玩家在线时,按照次序执行描述在 take 内的各个指令。
  • transown - 当玩家在线时,对先前玩家执行 take 内的各个指令,对之后玩家执行 give 内的各个指令。
  • chtype - 当玩家在线时,按照次序执行前一个类型的 take 再执行后一个类型的 give。

当玩家不在线时,上述操作(包含发送消息)将会推迟到玩家下次上线时自动执行,这一机制借助 tasks.yml 进行。

本插件机制的相关参考图片

参考图片可帮助更深入地理解本插件的实现逻辑。下列参考图片引用时请注明出处。

types.yml 相关解释 - 1

types.yml 相关解释 - 2

玩家权限授予和解除逻辑解释