eu.xworlds.util.raknet.RaknetDecoder.java Source code

Java tutorial

Introduction

Here is the source code for eu.xworlds.util.raknet.RaknetDecoder.java

Source

/*
This file is part of "xWorlds utilities".
    
"xWorlds utilities" is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
"xWorlds utilities" is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with "nukkit xWorlds plugin". If not, see <http://www.gnu.org/licenses/>.
    
 */
package eu.xworlds.util.raknet;

import java.lang.reflect.Constructor;
import java.net.InetSocketAddress;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import eu.xworlds.util.raknet.protocol.ConnectedPing;
import eu.xworlds.util.raknet.protocol.ConnectedPong;
import eu.xworlds.util.raknet.protocol.ConnectionRequest;
import eu.xworlds.util.raknet.protocol.DetectLostConnections;
import eu.xworlds.util.raknet.protocol.InvalidRaknetMessage;
import eu.xworlds.util.raknet.protocol.OpenConnectionReply1;
import eu.xworlds.util.raknet.protocol.OpenConnectionReply2;
import eu.xworlds.util.raknet.protocol.OpenConnectionRequest1;
import eu.xworlds.util.raknet.protocol.OpenConnectionRequest2;
import eu.xworlds.util.raknet.protocol.OurSystemRequiresSecurity;
import eu.xworlds.util.raknet.protocol.OutOfBandInternal;
import eu.xworlds.util.raknet.protocol.PublicKeyMismatch;
import eu.xworlds.util.raknet.protocol.RaknetMessage;
import eu.xworlds.util.raknet.protocol.RemoteSystemRequiresPublicKey;
import eu.xworlds.util.raknet.protocol.SndReceiptAcked;
import eu.xworlds.util.raknet.protocol.SndReceiptLoss;
import eu.xworlds.util.raknet.protocol.UnconnectedPing;
import eu.xworlds.util.raknet.protocol.UnconnectedPingOpenConnections;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;

/**
 * Pipeline for incoming server connections.
 * 
 * <p>
 * This handler translates incoming datagram packages to RaknetMessage objects.
 * </p>
 * 
 * @author mepeisen
 */
class RaknetDecoder extends MessageToMessageDecoder<DatagramPacket> {

    /** the well known messages */
    private final Map<Byte, Constructor<? extends RaknetMessage>> messages = new HashMap<>();

    /**
     * The handler for incoming connections.
     * 
     * @param messageFactories
     *            the message factories
     */
    public RaknetDecoder(RaknetMessageFactory[] messageFactories) {
        this.registerMessages(this.getDefaultMessages());
        for (final RaknetMessageFactory factory : messageFactories) {
            final Map<Byte, Class<? extends RaknetMessage>> messageClasses = factory.getMessageClasses();
            this.registerMessages(messageClasses);
        }
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
        final ByteBuf buf = msg.content();
        buf.order(ByteOrder.BIG_ENDIAN);
        final byte id = buf.readByte();
        final Constructor<? extends RaknetMessage> ctor = this.messages.get(Byte.valueOf(id));
        if (ctor == null) {
            final RaknetMessage result = new InvalidRaknetMessage(id, buf, msg.sender(), msg.recipient());
            out.add(result);
        } else {
            try {
                final RaknetMessage result = ctor.newInstance(buf, msg.sender(), msg.recipient());
                out.add(result);
            } catch (Exception ex) {
                final RaknetMessage result = new InvalidRaknetMessage(id, buf, msg.sender(), msg.recipient(), ex);
                out.add(result);
            }
        }
    }

    /**
     * Register message classes.
     * 
     * @param classMap
     *            the message classes.
     */
    private void registerMessages(Map<Byte, Class<? extends RaknetMessage>> classMap) {
        if (classMap != null) {
            for (final Map.Entry<Byte, Class<? extends RaknetMessage>> entry : classMap.entrySet()) {
                try {
                    final Constructor<? extends RaknetMessage> ctor = entry.getValue().getConstructor(ByteBuf.class,
                            InetSocketAddress.class, InetSocketAddress.class);
                    this.messages.put(entry.getKey(), ctor);
                } catch (NoSuchMethodException ex) {
                    RaknetServer.LOGGER.log(Level.WARNING, "Invalid raknet message", ex); //$NON-NLS-1$
                }
            }
        }
    }

    /**
     * Creates the default messages
     * 
     * @return class map for standard protocol
     */
    private Map<Byte, Class<? extends RaknetMessage>> getDefaultMessages() {
        final Map<Byte, Class<? extends RaknetMessage>> result = new HashMap<>();
        result.put(Byte.valueOf(ConnectedPing.ID), ConnectedPing.class);
        result.put(Byte.valueOf(ConnectedPong.ID), ConnectedPong.class);
        result.put(Byte.valueOf(ConnectionRequest.ID), ConnectionRequest.class);
        result.put(Byte.valueOf(DetectLostConnections.ID), DetectLostConnections.class);
        result.put(Byte.valueOf(OpenConnectionReply1.ID), OpenConnectionReply1.class);
        result.put(Byte.valueOf(OpenConnectionReply2.ID), OpenConnectionReply2.class);
        result.put(Byte.valueOf(OpenConnectionRequest1.ID), OpenConnectionRequest1.class);
        result.put(Byte.valueOf(OpenConnectionRequest2.ID), OpenConnectionRequest2.class);
        result.put(Byte.valueOf(OurSystemRequiresSecurity.ID), OurSystemRequiresSecurity.class);
        result.put(Byte.valueOf(OutOfBandInternal.ID), OutOfBandInternal.class);
        result.put(Byte.valueOf(PublicKeyMismatch.ID), PublicKeyMismatch.class);
        result.put(Byte.valueOf(RemoteSystemRequiresPublicKey.ID), RemoteSystemRequiresPublicKey.class);
        result.put(Byte.valueOf(SndReceiptAcked.ID), SndReceiptAcked.class);
        result.put(Byte.valueOf(SndReceiptLoss.ID), SndReceiptLoss.class);
        result.put(Byte.valueOf(UnconnectedPing.ID), SndReceiptAcked.class);
        result.put(Byte.valueOf(UnconnectedPingOpenConnections.ID), UnconnectedPingOpenConnections.class);
        return result;
    }

}