Canales de Mensajes

En Sponge, todos los objetos a los que se envían mensajes implementan la interfaz MessageReceiver. Un MessageChannel es un dispositivo que puede enviar mensajes a un número arbitrario de MessageReceivers e incluso realizar acciones como destinatario específico del mensaje.

Selección de Destinatario de Mensajes

Dentro de la interfaz de MessageChannel hay constantes y métodos estáticos que pueden ser utilizados para obtener o crear los canales utilizados comúnmente. Hay también otras clases e interfaces que pueden utilizarse para crear un MessageChannel, como por ejemplo AbstractMutableMessageChannel y MutableMessageChannel. Una lista completa de esto puede encontrarse en org.spongepowered.api.text.channel y sus subpaquetes.

Transmisión

El canal más común será el canal de transmisión. Esto puede ser obtenido desde Server a través del método Server#getBroadcastChannel() o desde la constante MessageChannel#TO_PLAYERS o MessageChannel#TO_ALL. La única diferencia es que TO_ALL incluye no solo a todos los jugadores conectados al servidor sino también a al servidor Consola.

El canal devuelto por getBroadcastChannel() generalmente será el canal MessageChannel.TO_ALL, sin embargo un canal diferente puede establecerse utilizando el método Server#setBroadcastChannel(MessageChannel).

Envío a una Lista Fija de MessageReceivers

También es posible enviar un mensaje no a todos los jugadores conectados, sino a un número de destinatarios seleccionados a mano. Esto puede ser hecho pasando la lista de los destinatarios al método MessageChannel#fixed(MessageReceiver…). A diferencia de la mayoría de los otros canales, la lista de destinatarios no será generada dinámicamente cada vez que algo se envíe a través del canal sino que permanece estática por el resto de su existencia. Sin embargo, las referencias que se mantienen son débiles. Esto significa que si por ejemplo un jugador se desconecta y el correspondiente objeto Player es removido por el basurero de Java, ese jugador también desaparecerá de la lista de de destinatarios del canal.

Selección basada en Permisos

También es posible crear un envío de canal a todos los destinatarios que tienen un permiso específico. El canal es obtenido desde el método MessageChannel#permission(String) con el permiso para verificar como un argumento. Cuando un mensaje es enviado a través de este canal, se obtendrán todos los temas desde el PermissionService que tienen el permiso dado.

Combinación de Canales

También se pueden combinar múltiples canales en uno. Esto puede hacerse pasando todos los canales al método :javadoc:`MessageChannel#combined(MessageChannel…). El canal resultante retransmitirá los mensajes a cada destinatario que sea el objetivo de al menos uno de los canales combinados.

Envío de Mensajes

Una vez que ha obtenido un MessageChannel puede enviar un mensaje a través del método MessageChannel#send(Object, Text). Este método es preferido sobre el método MessageChannel#send(Text), ya que el Objecto puede ser utilizado para la identificación o para realizar otras acciones diferentes. Alternativamente, puede utilizar un ChatType para especificar a donde los mensajes serán enviados. Utilizando el método MessageChannel#send(Object, Text, ChatType) le permitirá lograr esto. El canal después transformará el mensaje para cada destinatarios y enviará el mensaje transformado.

Aplicación Extendida: Canales de Chat

Los canales de mensaje tienen una aplicación muy útil ya que pueden ser utilizados para establecer canales de chat. Por ejemplo, puede establecer un mensaje de chat para cada canal de chat que desee tener. Entonces, cuando un MessageReceiver se une a un canal, como un /join <channel name>, simplemente configure el MessageChannel del MessageReceiver en el canal apropiado utilizando MessageReceiver#setMessageChannel(MessageChannel). Esto causará que cada mensaje enviado desde el MessageReceiver para ser pasado al MessageChannel dado en lugar del predeterminado. Alternativamente, puede escuchar MessageChannelEvent, y configurar el apropiado MessageChannel utilizando MessageChannelEvent#setChannel(MessageChannel). Pasarle nulo a ese método desconfigurará cualquier canal personalizado, causando que el mensaje sea enviado al MessageChannel original en su lugar.

Transformación de Mensajes

Puede aplicar un filtro a todos los Textos que pasen a través de un MessageChannel para cambiar el mensaje como prefiera. Esto es posible extendiendo el MessageChannel y remplazando el método MessageChannel#transformMessage(Object, MessageReceiver, Text, ChatType) como se demostrará a continuación.

Ejemplo: Transformación de Mensajes

El siguiente fragmento de código define una clase de AdminMessageChannel que reemplaza el método predeterminado transformMessage. El nuevo método transformMessage tomará el mensaje original y anexará una etiqueta roja [Admin] al frente del mensaje.

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

Tenga en cuenta que no se desea definir ningún destinatario adicional, por lo que devolveremos una colección vacía en el método MessageChannel#getMembers().

Gracias a las capacidades de combinar MessageChannels, podemos combinar nuestro recién creado AdminMessageChannel con cualquier otro MessageChannel. Ahora si un jugador se une con el permiso myplugin.admin, obtendremos el MessageChannel al que sus mensajes son enviados, lo combinaremos con un AdminMessageChannel y estbleceremos el canal combinado de vuelta al jugador. De esa manera, todos sus mensajes son enviados a todas las personas especificadas en el canal original, pero debido a la adición del AdminMessageChannel, una etiqueta roja [Admin] será prefijada.

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

Tenga en cuenta que serán prefijados los mensajes todos pertenecientes a un jugador. Esto incluye mensajes muertos, mensajes abandonados, etc. Si solo quiere prefijar todos los mensajes de chat, necesitará escuchar el MessageChannelEvent.Chat y establecer el canal en el evento en lugar del jugador. Esto se haría utilizando event.setChannel(newChannel) en el evento MessageChannelEvent.Chat. Para que el jugador del evento verifique los permisos, necesitará obtener un Jugador de la Causa del evento. Esto es demostrado a continuación:

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

Más sobre causas puede encontrarlas en causes page.

Nota

Cuando se combinan múltiples MessageChannels definiendo diferentes transformaciones de mensajes, el Texto será transformado en el orden en que los MessageChannels son transferidos al método MessageChannel#combined(MessageChannel... channels). Tenga en cuenta que cualquier transformación que resulte en un vacío Opcional será ignorada a menos que sea realizada por el último canal en la cadena.

Canales de Mensajes Mutables

Un MutableMessageChannel contiene métodos para cambiar quien puede recibir mensajes enviados a través de nuestro canal. Como tal, debemos implementar métodos para la realización de acciones que modifiquen nuestros miembros. Para hacer esto, simplemente crearemos una clase llamada MutableAdminMessageChannel que implementará un 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);
    }
}

La principal diferencia entre nuestro AdminMessageChannel y nuestro nuevo MutableAdminMessageChannel es que verificaremos si el destinatario está en la lista de miembros antes de transformar el mensaje. Si es así, podemos alterar el mensaje que es enviado, anexando el prefijo en rojo [Admin]. En nuestro método getMembers() devolvemos un conjunto inmutable, de modo que el conjunto puede ser modificado solo por el método apropiado en nuestro MutableAdminMessageChannel.

Tenga en cuenta que una implementación abstracta para MutableMessageChannels existe en Sponge API como AbstractMutableMessageChannel. También tenga en cuenta que nuestros miembros no persisten. Si un jugador abandona el servidor, sería removido del conjunto.

Modificación de los Miembros

Para hacer completo uso de nuestro MutableAdminMessageChannel, debemos poder agregar y eliminar miembros del canal. Para hacer esto, podemos simplemente llamar a nuestros métodos MutableMessageChannel#addMember(MessageReceiver) y MutableMessageChannel#removeMember(MessageReceiver) que implementamos previamente cada vez que necesitamos agregar o eliminar un miembro del conjunto.