正在载入

等待时间过长时请刷新页面

编写于

最近更新

消息服务

消息服务 LinMessenger 用于向玩家或控制台投递一次性的聊天、动作栏与标题文本。它负责连接语言引用、高级字符串、全局前缀与 Bukkit 发送接口。

通过 Linlang Facade 取得消息服务:

LinMessenger messenger = lin.linMessenger();

发送聊天消息

直接发送普通文本:

messenger.send(player, "操作成功");

直接发送语言字段引用:

messenger.send(player, lang.command.success);

带有命名变量时,将变量名称和值交替传入:

messenger.send(
        player,
        lang.command.success,
        "player", player.getName(),
        "item", itemName
);

参数必须按「名称、值」成对出现,参数数量为奇数时会抛出 IllegalArgumentException。已经组织好的变量也可以通过 Map<String, ?> 重载传入。

字符串参数会作为普通文本安全插入。LinTextLangTextTextSource 参数会作为独立的高级文本片段插入,具体规则请见:高级字符串 ⇱

聊天消息默认使用当前 Facade 的全局前缀。

动作栏与标题

发送动作栏:

messenger.actionBar(
        player,
        lang.status,
        "progress", progress
);

使用默认时长发送标题:

messenger.title(player, lang.title, lang.subtitle);

需要指定淡入、停留和淡出时长时,使用 TitleTimes。时长单位为游戏刻:

messenger.title(
        player,
        lang.title,
        lang.subtitle,
        TitleTimes.of(10, 60, 20)
);

动作栏与标题默认不使用全局前缀。接收者不支持动作栏或标题时,新接口默认报告错误,不会自动改发聊天消息。

高级投递设置

通常直接调用发送方法即可。需要进一步设置前缀、降级策略或标题时长时,可以手动创建 LinMessage

messenger.send(
        sender,
        LinMessage.actionBar(lang.status)
                .args("progress", progress)
                .fallback(FallbackPolicy.CHAT)
);

前缀策略 PrefixMode 包含:

  1. AUTO:聊天使用前缀,动作栏和标题不使用。
  2. INCLUDE:明确使用前缀。
  3. OMIT:明确省略前缀。

下面的消息即使通过聊天发送,也不会带有前缀:

messenger.send(
        player,
        LinMessage.chat(lang.message).withoutPrefix()
);

通道不受支持时可以选择:

  1. REJECT:报告调用错误,是新消息的默认值。
  2. CHAT:明确降级为聊天消息。
  3. IGNORE:忽略本次投递。

LinMessage 是不可变对象。调用 args()prefix()fallback()times() 会返回新的消息对象,因此预先保存的消息可以安全复用。

语言重载

LangText 直接传给 Messenger,发送时会读取当前文本,不必先转换为 String

LangText message = lang.command.success;

messenger.send(player, message);
langService.reload();
messenger.send(player, message);

第二次发送会使用重载后的语言文本。lin.settings().apply() 切换语言时同样保留引用,不需要重新绑定语言对象。

sendKey()sendText()sendTitleKey()sendActionBarKey() 等旧方法仍然保留,以便已有插件逐步迁移,但已经标记为弃用。新代码不应再把语言路径字符串交给 Messenger,应直接使用 LangText 字段。

消息传输层

Messenger 不直接限定消息只能发送到当前 Bukkit 服务器。实际投递由 MessageTransport 完成,Bukkit Runtime 默认注册了本地传输层。

传输层接口包含四项职责:

  1. 提供稳定的传输层标识。
  2. 声明路由优先级。
  3. 判断是否支持某种接收者。
  4. 投递规范化后的 TransportMessage

未来网络模块可以定义远程接收者并注册对应传输层:

public final class NetworkMessageTransport implements MessageTransport {

    @Override
    public String id() {
        return "linlang-network";
    }

    @Override
    public int priority() {
        return 100;
    }

    @Override
    public boolean supports(Object recipient) {
        return recipient instanceof RemoteAudience;
    }

    @Override
    public void send(Object recipient, TransportMessage message) {
        RemoteAudience remote = (RemoteAudience) recipient;
        network.send(remote.server(), remote.playerId(), message);
    }
}

注册传输层:

lin.linMessenger().registerTransport(new NetworkMessageTransport());

相同标识的新传输层会替换旧实现。多个传输层同时支持同一接收者时,优先级较高的实现先接收消息;没有传输层支持接收者时,Messenger 会抛出 IllegalArgumentException

普通重载、语言切换、前缀更新和 settings().apply() 均保留当前 Messenger,已注册的传输层可以继续使用。显式重建或重新初始化 Facade 后,需要为新消息服务注册传输层。

TransportMessage 中的语言引用、变量和前缀已经在发送端解析,内容只包含固定的 LinText 源码、消息通道、标题时长与降级策略,不包含 Bukkit 类型。网络模块可以据此建立稳定的序列化协议,接收端再使用对应平台的高级字符串渲染器完成显示。

投递流程

flowchart LR
    Call["send、actionBar 或 title"] --> Model["建立内部 LinMessage"]
    Model --> Resolve["解析 TextSource"]
    Resolve --> Args["绑定并转义变量"]
    Args --> Prefix["应用前缀策略"]
    Prefix --> Route["选择 MessageTransport"]
    Route --> Local["Bukkit 本地投递"]
    Route --> Network["未来网络投递"]

BossBar、声音、粒子和 GUI 具有不同生命周期,不属于一次性文本消息,不会并入 LinMessenger。GUI 名称与 Lore 可以复用高级字符串解析能力,但由 LinView 自己负责渲染和更新。

讨论

请登录账号