消息通道
在 Sponge 中,可以发送消息的每个对象,都实现了 MessageReceiver 接口。 MessageChannel 是一种设备,用于发送消息到任意数量的 MessageReceiver
,也可以按照消息的收件人进行特定的格式化等操作。
选定消息接受者
在 MessageChannel
接口中,一些常量和静态方法可以用来获取或创建常用的信道。当然,创建一个 MessageChannel
并不仅限于使用这些类和接口,例如 AbstractMutableMessageChannel, 和 MutableMessageChannel。可以在包 org.spongepowered.api.text.channel 及其子包中找到一个完整的列表。
广播
最常见的信道是广播信道。我们既可以通过 Server 类中的 Server#getBroadcastChannel() 方法获取,也可以从 MessageChannel#TO_PLAYERS 或者 MessageChannel#TO_ALL 两个常量中获取到广播信道。唯一的区别是 TO_ALL
不仅包括连接到服务器的每个玩家,还包括服务器的控制台输出(Console)。
getBroadcastChannel()
方法的返回值一般情况下和常量 MessageChannel.TO_ALL
相同。不过,我们也可以通过 Server#setBroadcastChannel(MessageChannel) 方法设置不同的信道。
指定发送列表的消息通道
另外,也可以不向所有已连接的玩家发送消息,而是手动选择接收消息的目标。这可以通过手动将接收消息的目标列表传入 MessageChannel#fixed(MessageReceiver…) 方法来完成。与大多数其他信道不同,收件人列表不会在每次发送消息时动态生成,而是静态地保持仍然保持连接的其余部分的引用。不过,这样的引用是 弱 引用。这意味着如果一个玩家断开连接并且相应的 Player 对象被 Java 的 垃圾收集器 回收,该玩家也将从该列表中消失。
基于权限的选择列表的消息通道
你也可以创建一个信道,用于发送给拥有特定权限的所有接收者。可以通过 MessageChannel#permission(String) 方法生成这样一个贪信道,传入的参数将作为检查的权限。当通过这个信道发送消息时,它将向所有拥有给定权限的 PermissionService 发送消息。
合并消息通道
你也可以将多个信道组合成一个。这可以通过将所有通道传入 MessageChannel#combined(MessageChannel…) 方法来完成。在发送消息时,消息就会传递到出现在任一个信道中的消息接收者中。
发送消息
一旦你得到了一个 MessageChannel
,你就可以通过 MessageChannel#send(Object, Text) 方法发送消息。和 MessageChannel#send(Text) 相比,我们更建议使用前者,因为 Object
可以用于识别或执行其他各种操作。 你也可以使用 ChatType ,并使用 MessageChannel#send(Object, Text, ChatType) 方法,来指定消息将发送到哪里。然后,信道将为每个接收者变换消息并发送变换后的消息。
扩展应用:聊天专用消息通道
消息信道具有一个非常有用的应用,因为它们可以被用来建立聊天信道。例如,您可以为您希望拥有的每个聊天频道建立一个消息信道。然后,当一个 MessageReceiver
连入一个频道,比如 /join <channel name>
时,只需通过 MessageReceiver#setMessageChannel(MessageChannel) 方法把 MessageReceiver
的 MessageChannel
设置为合适的信道。这将导致 从 MessageReceiver
发送的每个消息被传递给指定的 MessageChannel
而非默认的 MessageChannel
。你也可以监听 MessageChannelEvent ,并使用 MessageChannelEvent#setChannel(MessageChannel) 方法设置合适的 MessageChannel
。将 null
传递给该方法将取消对于任何自定义信道的设置,并使消息发送至原始的 MessageChannel
中。
变换消息
你可以为所有经过 MessageChannel
的 Text
,按照你喜欢的方式,变换其中的内容。你可以继承 MessageChannel
,并且覆写 MessageChannel#transformMessage(Object, MessageReceiver, Text, ChatType) 方法,就像下面的示例一样。
示例:变换消息
下面的代码摘录定义了一个 AdminMessageChannel
类,它覆写了默认的 transformMessage
方法。新的 transformMessage
方法将获取原始消息,并在消息的前面附加上一个红色的 [Admin]
标签。
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageChannel;
import org.spongepowered.api.text.channel.MessageReceiver;
import org.spongepowered.api.text.format.TextColors;
public class AdminMessageChannel implements MessageChannel {
@Override
public Optional<Text> transformMessage(Object sender, MessageReceiver recipient,
Text original) {
Text text = original;
text = Text.of(TextColors.RED, "[Admin]", TextColors.RESET, text);
return Optional.of(text);
}
@Override
public Collection<MessageReceiver> getMembers() {
return Collections.emptyList();
}
}
请注意,我们不希望定义任何其他收件人,因此我们使 MessageChannel#getMembers() 方法返回一个空集合。
由于我们可以将 MessageChannel
进行组合,因此我们可以将我们新创建的 AdminMessageChannel
与任何其他的 MessageChannel
组合。现在如果一个新加入的玩家拥有 myplugin.admin
权限,那么我们将获得用于发送其消息的 MessageChannel
,并与一个 AdminMessageChannel
组合,并将组合结果设置回玩家。这样,所有的消息都被发送到原先的信道指定的每个人,但是由于添加了 AdminMessageChannel
,部分信息将附加一个红色的 [Admin]
前缀。
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.network.ClientConnectionEvent;
private AdminMessageChannel adminChannel = new AdminMessageChannel();
@Listener
public void onClientConnectionJoin(ClientConnectionEvent.Join event) {
Player player = event.getTargetEntity();
if(player.hasPermission("myplugin.admin")) {
MessageChannel originalChannel = event.getOriginalChannel();
MessageChannel newChannel = MessageChannel.combined(originalChannel,
adminChannel);
player.setMessageChannel(newChannel);
}
}
注意所有满足条件的玩家发送的 所有 消息都将会加上一个前缀。这包括了玩家离开的消息、死亡的消息等。如果你只想要给 聊天 消息加上前缀的话,那么你需要监听 MessageChannelEvent.Chat 事件,并且设置事件而非玩家的信道。你可以通过 MessageChannelEvent.Chat
的 event.setChannel(newChannel)
方法来完成这件事。如果想要获取玩家并且检查其权限,你需要从事件的 Cause
中获取一个 Player
,下面就是代码示例:
Optional<Player> playerOptional = event.getCause().<Player>first(Player.class);
更多和 Cause
有关的东西可以在 causes page 中找到。
注解
当把多个 MessageChannel
进行组合以进行消息转换时, Text
的转换结果取决于传入 MessageChannel#combined(MessageChannel... channels)
方法的 MessageChannel
的顺序。请注意所有返回一个空的 Optional
的 MessageChannel
将被忽略,除非它是消息转换链上的最后一个 MessageChannel
。
可变消息通道
一个 MutableMessageChannel 包含了一些方法用于更改通过我们的信道发送的消息的接收者。因此,我们必须实现执行修改的操作的方法。为此,我们将创建一个名为 MutableAdminMessageChannel
的类,并实现一个 MutableMessageChannel
。
import java.util.Set;
import java.util.WeakHashMap;
import org.spongepowered.api.text.channel.MutableMessageChannel;
public class MutableAdminMessageChannel implements MutableMessageChannel {
private Set<MessageReceiver> members;
public MutableAdminMessageChannel() {
this(Collections.emptySet());
}
public MutableAdminMessageChannel(Collection<MessageReceiver> members) {
this.members = Collections.newSetFromMap(new WeakHashMap<>());
this.members.addAll(members);
}
@Override
public Collection<MessageReceiver> getMembers() {
return Collections.unmodifiableSet(this.members);
}
@Override
public boolean addMember(MessageReceiver member) {
return this.members.add(member);
}
@Override
public boolean removeMember(MessageReceiver member) {
return this.members.remove(member);
}
@Override
public void clearMembers() {
this.members.clear();
}
@Override
public Optional<Text> transformMessage(Object sender, MessageReceiver recipient,
Text original) {
Text text = original;
if(this.members.contains(recipient)) {
text = Text.of(TextColors.RED, "[Admin]", TextColors.RESET, text);
}
return Optional.of(text);
}
}
我们的 AdminMessageChannel
和我们的新的 MutableAdminMessageChannel
之间的主要区别是在于我们转换消息之前是否检查接收者在接收者列表中。如果是,那么我们可以改变发送的消息,也就是附加红色的 [Admin]
前缀。在我们的 getMembers()
方法中,我们返回一个不可变的集合,而这一集合只能通过我们的 MutableAdminMessageChannel
中的适当的方法来修改。
请注意,在 Sponge API 中 MutableMessageChannel
的抽象实现为 AbstractMutableMessageChannel
。还要注意,我们的成员不保证是否被回收。 如果玩家离开服务器,他们将从集合中删除。
修改消息接收者
为了充分利用我们的 MutableAdminMessageChannel
,我们需要能够添加和删除接收者。为此,当我们需要从成员集中添加或删除一个接收者时,我们可以简单地调用我们的 MutableMessageChannel#addMember(MessageReceiver) 和 MutableMessageChannel#addMember(MessageReceiver) 两个方法。