表情数据
表情目录、纹理序列帧、动作参数与客户端资源校验。
一个表情定义把图片、翻译键和动作组合成贴纸。服务端决定目录与播放请求;客户端用自己的资源包解析图片,因此数据包和图片资源需要配套提供。
文件放在哪里
定义放在data/<namespace>/emojis/<path>.json,表情ID为<namespace>:<path>。图片放在assets/<namespace>/textures/...;定义中的纹理ID保留textures/前缀和扩展名。同ID资源由数据包优先级选择,不合并字段。
最小示例
下面保留内置isekaiexpansion:evelina/smile的必填字段,并使用其已有纹理:
{
"texture": "isekaiexpansion:textures/gui/emoji/evelina/evelinasmile.png",
"title_key": "emoji.isekaiexpansion.smile.title",
"caption_key": "emoji.isekaiexpansion.smile.caption",
"category_key": "emoji.isekaiexpansion.category.positive"
}
字段
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
texture | identifier | 必填 | 包含textures/和.png的资源包纹理ID。ID语法错误使定义失败,图片缺失由客户端处理。 |
title_key | string | 必填 | 非空白标题翻译键;数据加载器不检查译文是否存在。 |
caption_key | string | 必填 | 非空白说明翻译键。 |
category_key | string | 必填 | 非空白分类翻译键。 |
order | integer | 0 | 显示顺序由小到大,同值按表情ID字符串排序,没有显式范围限制。 |
duration_ticks | integer | 70 | 选择器播放时长,70刻(3.5秒);限制在20–200刻(1–10秒)。 |
frame_width | integer | 0 | 每帧像素宽度。负数变为0,0采用整图宽度,正数最多取整图宽度。 |
frame_height | integer | 0 | 每帧像素高度。负数变为0,0采用整图高度,正数最多取整图高度。 |
frame_count | integer | 1 | 至少1帧;客户端再限制到纹理中完整网格单元的数量。 |
frame_time_ms | integer | 80 | 每帧毫秒数,至少1。 |
loop | boolean | true | 循环序列帧;false时停在最后一帧。 |
animation | object | sway_wiggle | 对象或类型名称字符串。缺失或null使用默认摇摆,字符串使用该类型默认参数。 |
animation.type | enum(none, sway_wiggle, float_bob, pulse, tremble, bounce, elastic_pop, squash_stretch) | sway_wiggle | 类型名称去除两端空白且不区分大小写,未知值拒绝目录重载。 |
animation.period_ticks | number | 按类型决定 | 周期,以刻为单位,至少1刻(0.05秒);下表同时列出秒数。 |
animation.offset_pixels | number | 按类型决定 | 移动幅度,单位像素,最小0。 |
animation.max_scale | number | 按类型决定 | 较大缩放参数,最小0.05;不强制要求大于min_scale。 |
animation.min_scale | number | 按类型决定 | 较小缩放参数,最小0.05。 |
animation.rotation_degrees | number | 按类型决定 | 非负旋转幅度,单位度;颤动实际使用的幅度还会限制到4度。 |
animation.squash | number | 按类型决定 | 挤压拉伸参数,限制在0–0.45。 |
animation.loop | boolean | true | 独立于序列帧循环,控制图标动作循环;false在周期结束后恢复原始姿态。 |
动作默认值
所有类型的动作循环默认为true。各列对应上面的动作参数;周期列是刻数及秒数。
| 类型 | 周期 | 位移 | 最大缩放 | 最小缩放 | 旋转 | 挤压 |
|---|---|---|---|---|---|---|
none | 30 (1.5秒) | 0 | 1 | 1 | 0 | 0 |
sway_wiggle | 30 (1.5秒) | 0 | 1 | 1 | 15 | 0 |
float_bob | 60 (3秒) | 4 | 1 | 1 | 0 | 0 |
pulse | 28 (1.4秒) | 0 | 1.2 | 1 | 0 | 0 |
tremble | 8 (0.4秒) | 2 | 1 | 1 | 3 | 0 |
bounce | 24 (1.2秒) | 8 | 1 | 1 | 0 | 0 |
elastic_pop | 26 (1.3秒) | 0 | 1.3 | 0.82 | 0 | 0 |
squash_stretch | 24 (1.2秒) | 9 | 1 | 1 | 0 | 0.18 |
对话播放选项
isekaiexpansion:emoji 对话动作在动作值内接受以下字段。这些属于播放选项,不是表情贴纸定义中的字段。
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
emoji | identifier | required | 目录 ID。语法错误会使动作解码失败;未知 ID 不产生播放。 |
duration_ticks | integer | 70 | 规范化后至少 1 刻(0.05 秒);默认 70 刻(3.5 秒),独立于定义时长。 |
priority | integer | 50 | 构建播放选项时限制在 0–1000。 |
policy | string | replace | replace、queue 或 force;忽略首尾空白及大小写。未知值采用 replace。 |
target | string | dialogue_entity | dialogue_entity、player 或 both。未知值采用 dialogue_entity。对话实体缺失或不是存活实体类型时跳过该目标;both 仍作用于玩家。 |
interruptible | boolean | true | 允许足够优先级的普通替换;force 不受此标记限制。 |
interrupt_on_damage | boolean | true | 允许服务端在受伤时中断最新请求。 |
可选字段缺失时使用所列默认值;缺少 emoji 或字段类型错误会使动作解码失败。额外字段被忽略。排队与受伤中断约定见表情贴纸播放 API。
完整示例
内置微笑表情把播放时长改为74刻(3.7秒),摇摆周期改为48刻(2.4秒),旋转幅度改为2.5度:
{
"texture": "isekaiexpansion:textures/gui/emoji/evelina/evelinasmile.png",
"title_key": "emoji.isekaiexpansion.smile.title",
"caption_key": "emoji.isekaiexpansion.smile.caption",
"category_key": "emoji.isekaiexpansion.category.positive",
"order": 20,
"duration_ticks": 74,
"animation": {
"type": "sway_wiggle",
"period_ticks": 48,
"rotation_degrees": 2.5
}
}
序列帧按从左到右、再从上到下排列。未填帧尺寸时整张图只有一个网格单元;要使用多帧,必须按图片布局填写尺寸。边缘不足一帧的像素不会计入可用帧数。
校验
缺少必填键、空白翻译键、非法纹理ID、错误的已读取字段类型或未知动作类型,会汇总为目录重载错误。目录替换前抛出异常,因此这个定义加载阶段失败时保留原目录。未知字段被忽略,不能用额外字段改变播放优先级或队列策略。空对象缺少必填字段,空白文本不是合法JSON。
时长、帧尺寸、帧数、帧间隔和动作参数按字段表修正,不因有限数值超范围就拒绝目录。数值读取没有精确整数或统一有限数校验;应使用正确类型的有限JSON数值,不依赖小数截断或极大指数的转换。
服务端不验证图片是否存在、尺寸是否正确,也不验证翻译是否存在。客户端找不到或无法解码图片时记录警告,不建立该表情的可用图片定义;其他表情不受影响。
重载
/reload重载服务端目录;玩家加入和数据包重载成功后发送目录给客户端。客户端收到目录后重建图片定义,客户端资源重载也会重建。图片不会随目录数据包传输,需要另行提供资源包。
选择器发送的表情使用定义时长,服务端限制两次发送至少间隔30刻(1.5秒)。其他播放入口可以另给时长,不能把duration_ticks视为所有播放请求的强制寿命。