Каналы сообщений

В 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).

Отправка в фиксированный список MessageReceivers

Также возможно отправить сообщение не всем подключенным игрокам, а нескольким выбранным вручную получателям. Это можно сделать, передав список предполагаемых получателей методу MessageChannel#fixed(MessageReceiver…). В отличие от большинства других каналов, список получателей не будет генерироваться динамически каждый раз, когда что-то отправляется через канал, а остается статичным до конца его существования. Тем не менее, сохраненные ссылки слабы. Это означает, что если, например, игрок отключается и соответствующий объект Player удаляется сборщиком мусора Java, этот игрок также исчезает из списка получателей каналов.

Выбор на основе Permissions

Также возможно создать канал для отправки всем получателям, у которых есть специальное разрешение. Канал получается из метода 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>, просто устанавливайте MessageChannel MessageReceiver-а на соответствующий канал, используя MessageReceiver#setMessageChannel(MessageChannel). Это приведет к тому, что каждое сообщение, отправленное из MessageReceiver, будет передано данному MessageChannel вместо значения по умолчанию. В качестве альтернативы, вы можете прослушивать MessageChannelEvent и устанавливать соответствующий MessageChannel, используя MessageChannelEvent#setChannel(MessageChannel). Передача null этому методу отключит любой пользовательский канал, в результате чего сообщение будет отправлено исходному MessageChannel.

Преобразование сообщений

Вы можете применить фильтр ко всем Text-ам, которые проходят через MessageChannel, чтобы изменить сообщение, как вам нравится. Это возможно за счёт расширения 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 и установить канал на событие, а не на игрока. Это можно сделать при помощи event.setChannel(newChannel) для события MessageChannelEvent.Chat. Чтобы получить игрока из события для проверки разрешений, вам нужно будет получить Player из Cause события. Это показано ниже:

Optional<Player> playerOptional = event.getCause().<Player>first(Player.class);

Подробнее о причинах смотрите здесь.

Примечание

При объединении нескольких MessageChannel, определяющих различные преобразования сообщений, Text будет преобразован в том порядке, в котором MessageChannel передается в метод MessageChannel#combined(MessageChannel... channels). Обратите внимание, что любые преобразования, приводящие к пустым Optional, будут игнорироваться, если не будут выполнены последним каналом в цепочке.

Изменяемые Каналы Сообщений

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.

Обратите внимание, что абстрактная реализация для MutableMessageChannel существует в API Sponge как AbstractMutableMessageChannel. Также обратите внимание, что наши участники не сохраняются. Если игрок покидает сервер, то он будет удален из набора.

Изменение членов

Чтобы полностью использовать наш MutableAdminMessageChannel, мы должны иметь возможность добавлять и удалять участников из канала. Для этого мы можем просто вызвать наш MutableMessageChannel#addMember(MessageReceiver) и MutableMessageChannel#removeMember(MessageReceiver) методы, которые мы реализовали ранее, когда нам нужно добавить или удалить пользователя из набора элементов.