Nachrichtenkanäle

In Sponge implementiert jedes Objekt, das Nachrichten empfangen kann, das MessageReceiver Interface. Ein MessageChannel ist ein Gerät, dass Nachrichten an eine beliebige Anzahl von MessageReceivern senden und sogar Aktionen wie Empfänger-spezifische Formatierungen durchführen kann.

Auswählen der Empfänger

Im MessageChannel Interface gibt es Konstanten und statische Methoden, die dafür verwendet werden können, häufig verwendete Kanäle zu erstellen. Außerdem gibt es noch andere Klassen und Interfaces, die verwendet werden können um MessageChannel zu erstellen wie beispielsweise AbstractMutableMessageChannel und MutableMessageChannel. Eine komplette Liste dieser kann im org.spongepowered.api.text.channel Package und seinen Unter-Packages gefunden werden.

Rundfunk

Der meistverwendetste Kanal ist der Rundfunk-Kanal. Dieser kann entweder vom Server mit der Server#getBroadcastChannel() Methode oder der MessageChannel#TO_PLAYERS bzw. MessageChannel#TO_ALL Konstante abgerufen werden. Der unterschied von TO_ALL ist der, dass dieser nicht nur alle mit dem Server verbunden Spieler enthält sondern auch die Server Console.

Der Kanal, der von getBroadcastChannel() zurückgegeben wird, ist meistens der MessageChannel.TO_ALL Kanal, aber es kann auch ein anderer Kanal mit Hilfe vor Server#setBroadcastChannel(MessageChannel) Methode gesetzt werden.

Senden an eine feststehende Liste von Empfängern

Es ist auch möglich eine Nachricht nicht an alle verbundenen Spieler zu senden, sondern nur an eine ausgewählte Menge von Empfängern. Dies kann erreicht werden, indem die gewünschte Liste von Empfängern an die MessageChannel#fixed(MessageReceiver…) Methode übergeben wird. Im Gegensatz zu den meisten anderen Kanälen ist die Liste hierfür nicht dynamisch generiert, sondern bleibt statisch hinterlegt solange dieser existiert. Aber die Referenzen auf die Objekte sind weak (schwach). Das bedeutet, dass wenn zum Beispiel ein Spieler das Spiel verlässt und das dazugehörige Player Objekt von Javas garbage collector (Müllabfuhr) entfernt wurde, dieser Spieler auch von der Liste der Empfänger verschwindet.

Rechte-basiertes Auswählen

Es ist auch möglich einen Kanal zu erstellen, der an alle Empfänger sendet, die eine bestimmte Berechtigung haben. Dieser Kanal wird von der MessageChannel#permission(String) Methode, unter Angabe der zu verwendenen Berechtigung, erstellt. Wenn eine Nachricht über diesen Kanal gesendet wird, wird diese an alle Subjects von PermissionService weitergeleitet, die die entsprechende Berechtigung haben.

Kanäle kombinieren

It is also possible to combine multiple channels into one. This can be done by passing all channels into the MessageChannel#combined(MessageChannel…) method. The resulting channel will relay messages to every recipient that is targeted by at least one of the combined channels.

Nachrichten senden

Once you have obtained a MessageChannel you can send a message through it via the MessageChannel#send(Object, Text) method. This method is preferred over the MessageChannel#send(Text) method, as the Object can be used for identification or for performing other various actions. Alternatively, you can use a ChatType to specify where the message will be sent to. Using the MessageChannel#send(Object, Text, ChatType) method will allow you to accomplish this. The channel will then transform the message for every recipient and send the transformed message.

Erweiterte Einsatzmöglichkeit: Chaträume

Message channels have a very useful application as they can be used to establish chat channels. For example, you could establish a message channel for every chat channel you wish to have. Then, when a MessageReceiver joins a channel, such as with /join <channel name>, simply set the MessageReceiver’s MessageChannel to the appropriate channel using MessageReceiver#setMessageChannel(MessageChannel). This will cause every message sent from the MessageReceiver to be passed to the given MessageChannel instead of the default one. Alternatively, you could listen to MessageChannelEvent, and set the appropriate MessageChannel using MessageChannelEvent#setChannel(MessageChannel). Passing null to that method will unset any custom channel, causing the message to be sent to the original MessageChannel instead.

Nachrichten transformieren

You can apply a filter to all Texts that pass through a MessageChannel to change the message however you like. This is possible by extending MessageChannel and overriding the MessageChannel#transformMessage(Object, MessageReceiver, Text, ChatType) method as demonstrated below.

Beispiel: Nachrichten transformieren

The following code excerpt defines an AdminMessageChannel class which overrides the default transformMessage method. The new transformMessage method will take the original message and append a red [Admin] tag to the front of the message.

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.chat.ChatType;
import org.spongepowered.api.text.format.TextColors;

public class AdminMessageChannel implements MessageChannel {

    @Override
    public Optional<Text> transformMessage(Object sender, MessageReceiver recipient,
                Text original, ChatType type) {
        Text text = original;
        text = Text.of(TextColors.RED, "[Admin]", TextColors.RESET, text);
        return Optional.of(text);
    }

    @Override
    public Collection<MessageReceiver> getMembers() {
        return Collections.emptyList();
    }
}

Note that we do not wish to define any additional recipients, so we return an empty collection in the MessageChannel#getMembers() method.

Thanks to the capabilities of combined MessageChannels, we can combine our newly created AdminMessageChannel with any other MessageChannel. Now if a player joins with the myplugin.admin permission, we will obtain the MessageChannel his messages are sent to, combine it with an AdminMessageChannel and set the combined channel back onto the player. That way all his messages are sent to everyone specified in the original channel, but due to the addition of the AdminMessageChannel, a red [Admin] tag will be prefixed.

import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.network.ClientConnectionEvent;
import org.spongepowered.api.text.channel.MessageChannel;

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);
    }
}

Note that this will prefix all messages pertaining to a player. This includes death messages, leave messages, etc. If you only want to prefix all chat messages, you would need to listen to MessageChannelEvent.Chat and set the channel onto the event rather than the player. This would be done using event.setChannel(newChannel) onto the MessageChannelEvent.Chat event. To get the player from the event to check for permissions, you would need to get a Player from the Cause of the event. This is demonstrated below:

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

More on causes can be found on the causes page.

Bemerkung

When combining multiple MessageChannels defining different message transformations, the Text will be transformed in the order that the MessageChannels are passed in to the MessageChannel#combined(MessageChannel... channels) method. Note that any transformations resulting in an empty Optional will be ignored unless performed by the last channel in the chain.

Veränderbare Nachrichten-Kanäle

A MutableMessageChannel contains methods for changing who may receive the messages sent through our channel. As such, we must implement methods for performing actions that modify our members. To do this, we simply will create a class named MutableAdminMessageChannel that will implement a MutableMessageChannel.

import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;

import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageReceiver;
import org.spongepowered.api.text.channel.MutableMessageChannel;
import org.spongepowered.api.text.chat.ChatType;
import org.spongepowered.api.text.format.TextColors;

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, ChatType type) {
        Text text = original;
        if(this.members.contains(recipient)) {
            text = Text.of(TextColors.RED, "[Admin]", TextColors.RESET, text);
        }
        return Optional.of(text);
    }
}

The main difference between our AdminMessageChannel and our new MutableAdminMessageChannel is that we check if the recipient is in the member list before transforming the message. If it is, then we may alter the message that is sent, appending the red [Admin] prefix. In our getMembers() method we return an immutable set, so that the set can only be modified by the appropriate methods in our MutableAdminMessageChannel.

Note that an abstract implementation for MutableMessageChannels exists in SpongeAPI as AbstractMutableMessageChannel. Also note that our members do not persist. If a player were to leave the server, they would be removed from the set.

Verändern der Teilnehmer

To make full use of our MutableAdminMessageChannel, we need to be able to add and remove members from the channel. To do this, we can simply call our MutableMessageChannel#addMember(MessageReceiver) and MutableMessageChannel#removeMember(MessageReceiver) methods we implemented previously whenever we need to add or remove a member from the member set.