starbounddata.packets.Packet.java Source code

Java tutorial

Introduction

Here is the source code for starbounddata.packets.Packet.java

Source

/*
* Copyright (C) 2014 www.StarNub.org - Underbalanced
*
* This utilities.file is part of org.starnub a Java Wrapper for Starbound.
*
* This above mentioned StarNub software 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
* any later version. This above mentioned CodeHome software
* 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 in
* this StarNub Software.  If not, see <http://www.gnu.org/licenses/>.
*/

package starbounddata.packets;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import starbounddata.types.variants.VLQ;
import utilities.compression.Zlib;

import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.UUID;

import static starbounddata.types.variants.VLQ.writeUnsignedVLQNoObject;

/**
 * Represents a basic packet that all packets should inherit.
 * <p>
 * Notes:
 * - Packet ID is a single Byte
 * - {@link io.netty.channel.ChannelHandlerContext} are the decoders on both the starnubclient and starnubserver side of the socket, this is used to write starnubdata.network data to the session
 * - recycle represents if this packet should be recycled when handled by StarNub
 * <p>
 *
 * @author Daniel (Underbalanced) (www.StarNub.org)
 * @since 1.0 Beta
 */
public abstract class Packet {

    private final Direction DIRECTION;
    private final byte PACKET_ID;
    private final ChannelHandlerContext SENDER_CTX;
    private final ChannelHandlerContext DESTINATION_CTX;
    private boolean recycle = false;

    /**
     * Recommended: For connections StarNub usage.
     * <p>
     * Uses: This constructor is used in construction of packets to be cached on each side of the players connection (Client, Server) sides of the proxy
     *
     * @param DIRECTION       Direction representing the direction the packet flows to
     * @param PACKET_ID       byte representing the Starbound packet id for this type of packet
     * @param SENDER_CTX      ChannelHandlerContext which represents the sender of this packets context (Context can be written to)
     * @param DESTINATION_CTX ChannelHandlerContext which represents the destination of this packets context (Context can be written to)
     */
    public Packet(Direction DIRECTION, byte PACKET_ID, ChannelHandlerContext SENDER_CTX,
            ChannelHandlerContext DESTINATION_CTX) {
        this.DIRECTION = DIRECTION;
        this.PACKET_ID = PACKET_ID;
        this.SENDER_CTX = SENDER_CTX;
        this.DESTINATION_CTX = DESTINATION_CTX;
    }

    /**
     * Recommended: For internal StarNub use with copying
     * <p>
     * Uses: This will construct a new packet from a packet
        
     * @param packet Packet representing the packet to construct from
     */
    public Packet(Packet packet) {
        this.DIRECTION = packet.getDIRECTION();
        this.PACKET_ID = packet.getPACKET_ID();
        this.SENDER_CTX = packet.getSENDER_CTX();
        this.DESTINATION_CTX = packet.getDESTINATION_CTX();
    }

    public Direction getDIRECTION() {
        return DIRECTION;
    }

    public byte getPACKET_ID() {
        return PACKET_ID;
    }

    public ChannelHandlerContext getSENDER_CTX() {
        return SENDER_CTX;
    }

    public ChannelHandlerContext getDESTINATION_CTX() {
        return DESTINATION_CTX;
    }

    public boolean isRecycle() {
        return recycle;
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: Setting this will cause the packet to stop being routed to events handlers and placed back into the packet pool with out
     * being routed to the destination
     */
    public void recycle() {
        this.recycle = true;
    }

    /**
     * Recommended: For connections StarNub usage.
     * <p>
     * Uses: This is used for StarNub to reset the packets routing
     */
    public void resetRecycle() {
        this.recycle = false;
    }

    /**
     * This will provide a new object while copying all of the internal data as well into this
     * new Object
     *
     * @return Packet new copied Object
     */
    public abstract Packet copy();

    /**
     * Recommended: For connections StarNub usage.
     * <p>
     * Uses: This method will read in a {@link io.netty.buffer.ByteBuf} into this packets fields
     * <p>
     *
     * @param in ByteBuf representing the reason to be read into the packet
     */
    public abstract void read(ByteBuf in);

    /**
     * Recommended: For connections StarNub usage.
     * <p>
     * Uses: This method will write to a {@link io.netty.buffer.ByteBuf} using this packets fields
     * <p>
     *
     * @param out ByteBuf representing the space to write out the packet reason
     */
    public abstract void write(ByteBuf out);

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This method will write to a {@link io.netty.buffer.ByteBuf} using this packets fields
     */
    public void routeToDestination() {
        DESTINATION_CTX.writeAndFlush(packetToMessageEncoder(), DESTINATION_CTX.voidPromise());
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This method will write to a {@link io.netty.buffer.ByteBuf} using this packets fields
     */
    public void routeToDestinationNoFlush() {
        DESTINATION_CTX.write(packetToMessageEncoder(), DESTINATION_CTX.voidPromise());
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will send this packet to multiple people
     *
     * @param sendList    HashSet of ChannelHandlerContext to this packet to
     */
    public void routeToGroup(HashSet<ChannelHandlerContext> sendList) {
        for (ChannelHandlerContext ctx : sendList) {
            ctx.writeAndFlush(packetToMessageEncoder(), ctx.voidPromise());
        }
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will send this packet to multiple people
     *
     * @param sendList    HashSet of ChannelHandlerContext to this packet to
     * @param ignoredList HashSet of ChannelHandlerContext to not send the message too
     */
    public void routeToGroup(HashSet<ChannelHandlerContext> sendList, HashSet<ChannelHandlerContext> ignoredList) {
        if (ignoredList != null) {
            sendList.stream().filter(ctx -> !ignoredList.contains(ctx))
                    .forEach(ctx -> ctx.writeAndFlush(packetToMessageEncoder(), ctx.voidPromise()));
        } else {
            for (ChannelHandlerContext ctx : sendList) {
                ctx.writeAndFlush(packetToMessageEncoder(), ctx.voidPromise());
            }
        }
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will send this packet to multiple people
     *
     * @param sendList    HashSet of ChannelHandlerContext to this packet to
     */
    public void routeToGroupNoFlush(HashSet<ChannelHandlerContext> sendList) {
        for (ChannelHandlerContext ctx : sendList) {
            ctx.write(packetToMessageEncoder(), ctx.voidPromise());
        }
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will send this packet to multiple people
     *
     * @param sendList    HashSet of ChannelHandlerContext to this packet to
     * @param ignoredList HashSet of ChannelHandlerContext to not send the message too
     */
    public void routeToGroupNoFlush(HashSet<ChannelHandlerContext> sendList,
            HashSet<ChannelHandlerContext> ignoredList) {
        if (ignoredList != null) {
            sendList.stream().filter(ctx -> !ignoredList.contains(ctx))
                    .forEach(ctx -> ctx.write(packetToMessageEncoder(), ctx.voidPromise()));
        } else {
            for (ChannelHandlerContext ctx : sendList) {
                ctx.write(packetToMessageEncoder(), ctx.voidPromise());
            }
        }
    }

    /**
     * Recommended: For connections StarNub usage.
     * <p>
     * Uses: This method will write to a {@link io.netty.buffer.ByteBuf} using this packets fields
     * <p>
     *
     * @return ByteBuf representing the ByteBuf to write to socket
     */
    protected ByteBuf packetToMessageEncoder() {
        ByteBuf msgOut = PooledByteBufAllocator.DEFAULT.directBuffer();
        this.write(msgOut);
        int payloadLengthOut = msgOut.readableBytes();
        byte[] dataOut;
        if (payloadLengthOut > 100) {
            dataOut = Zlib.compress(msgOut.readBytes(payloadLengthOut).array());
            payloadLengthOut = -dataOut.length;
        } else {
            dataOut = msgOut.readBytes(payloadLengthOut).array();
        }
        msgOut.clear();
        msgOut.writeByte(PACKET_ID);
        writeSVLQPacketEncoder(msgOut, payloadLengthOut);
        msgOut.writeBytes(dataOut);
        return msgOut;
    }

    /**
     * Recommended: For internal use with StarNub packet decoding
     * <p>
     * Uses: This will write a s{@link starbounddata.types.variants.VLQ} to a {@link io.netty.buffer.ByteBuf}
     * <p>
     * Notes: This will not create a VLQ object and should be used
     * <p>
     *
     * @param out   ByteBuf in which is to be read
     * @param value long representing the VLQ value to be written out
     */
    public static void writeSVLQPacketEncoder(ByteBuf out, long value) {
        if (value < 0) {
            value = ((-(value + 1)) << 1) | 1;
        } else {
            value = value << 1;
        }
        int numRelevantBits = 64 - Long.numberOfLeadingZeros(value);
        int numBytes = (numRelevantBits + 6) / 7;
        if (numBytes == 0) {
            numBytes = 1;
        }
        out.writerIndex(numBytes + 1); /* Sets the write index at the number of bytes + 1 byte for packet id */
        for (int i = numBytes - 1; i >= 0; i--) {
            int curByte = (int) (value & 0x7F);
            if (i != (numBytes - 1)) {
                curByte |= 0x80;
            }
            out.setByte(i + 1, curByte); /* Sets the byte at index + 1 byte for packet id */
            value >>>= 7;
        }
    }

    /**
     * This represents the direction the packet travels to.
     */
    public enum Direction {
        TO_STARBOUND_SERVER, TO_STARBOUND_CLIENT, BIDIRECTIONAL, NOT_USED
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will read a uuid value from a {@link io.netty.buffer.ByteBuf} and advanced the buffer reader index 16 bytes
     * <p>
     *
     * @param in ByteBuf representing the data to be read
     * @return uuid the uuid that was read
     */
    public static UUID readUUID(ByteBuf in) {
        long mostSignificantBits = in.readLong();
        long leastSignificantBits = in.readLong();
        return new UUID(mostSignificantBits, leastSignificantBits);
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will read a VLQ and then a byte array to form a String value from a {@link io.netty.buffer.ByteBuf} and advanced the buffer reader index by the length of the VLQ and read bytes
     * <p>
     *
     * @param in ByteBuf representing the data to be read
     * @return String the String that was read
     */
    public static String readVLQString(ByteBuf in) {
        byte[] bytes = readVLQArray(in);
        return new String(bytes, Charset.forName("UTF-8"));
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will read a VLQ and then a variant length of bytes from a {@link io.netty.buffer.ByteBuf} and advanced the buffer reader index by the length of the VLQ and read bytes
     * <p>
     *
     * @param in ByteBuf representing the data to be read
     * @return byte[] the byte[] that was read
     */
    public static byte[] readVLQArray(ByteBuf in) {
        long len = VLQ.readUnsignedFromBufferNoObject(in);
        return in.readBytes((int) len).array();
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will write a uuid value to a {@link io.netty.buffer.ByteBuf} and advanced the buffer writer index by 16 bytes
     * <p>
     *
     * @param out  ByteBuf representing the buffer to be written to
     * @param uuid uuid value to be written to the buffer
     */
    public static void writeUUID(ByteBuf out, UUID uuid) {
        out.writeLong(uuid.getMostSignificantBits());
        out.writeLong(uuid.getLeastSignificantBits());
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will write a String value to a {@link io.netty.buffer.ByteBuf} and advanced the buffer writer index by a variant length and the number of bytes
     * <p>
     *
     * @param out   ByteBuf representing the buffer to be written to
     * @param value String value to be written to the buffer
     */
    public static void writeStringVLQ(ByteBuf out, String value) {
        writeVLQArray(out, value.getBytes(Charset.forName("UTF-8")));
    }

    /**
     * Recommended: For Plugin Developers & Anyone else.
     * <p>
     * Uses: This will write a variant length byte[] value to a {@link io.netty.buffer.ByteBuf} and advanced the buffer writer index by a variant length and the number of bytes
     * <p>
     *
     * @param out   ByteBuf representing the buffer to be written to
     * @param bytes bytes[] value to be written to the buffer
     */
    public static void writeVLQArray(ByteBuf out, byte[] bytes) {
        byte[] vlqBytes = writeUnsignedVLQNoObject(bytes.length);
        out.writeBytes(vlqBytes);
        out.writeBytes(bytes);
    }

    @Override
    public String toString() {
        return "Packet{" + "DIRECTION=" + DIRECTION + ", PACKET_ID=" + PACKET_ID + ", SENDER_CTX=" + SENDER_CTX
                + ", DESTINATION_CTX=" + DESTINATION_CTX + ", recycle=" + recycle + '}';
    }
}