org.obsidianbox.obsidian.message.CommonMessagePipeline.java Source code

Java tutorial

Introduction

Here is the source code for org.obsidianbox.obsidian.message.CommonMessagePipeline.java

Source

/**
 * This file is part of Obsidian, licensed under the MIT License (MIT).
 *
 * Copyright (c) 2013-2014 ObsidianBox <http://obsidianbox.org/>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.obsidianbox.obsidian.message;

import java.lang.reflect.Field;
import java.util.EnumMap;

import com.google.common.collect.Maps;
import cpw.mods.fml.common.network.FMLEmbeddedChannel;
import cpw.mods.fml.common.network.FMLIndexedMessageToMessageCodec;
import cpw.mods.fml.common.network.FMLOutboundHandler;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import gnu.trove.map.hash.TByteObjectHashMap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.INetHandler;
import net.minecraft.network.NetHandlerPlayServer;
import org.obsidianbox.magma.message.Message;
import org.obsidianbox.magma.message.MessagePipeline;
import org.obsidianbox.magma.Game;
import org.obsidianbox.magma.addon.Addon;

@ChannelHandler.Sharable
public class CommonMessagePipeline extends FMLIndexedMessageToMessageCodec<Message> implements MessagePipeline {
    private final Game game;
    private final EnumMap<Side, FMLEmbeddedChannel> channels = Maps.newEnumMap(Side.class);
    private transient boolean locked = false;

    public CommonMessagePipeline(Game game) {
        this.game = game;
    }

    @Override
    public void encodeInto(ChannelHandlerContext ctx, Message message, ByteBuf target) throws Exception {
        // Allow addon API to write data
        message.encode(game, target);
    }

    @Override
    public void decodeInto(ChannelHandlerContext ctx, ByteBuf source, Message message) {
        // Allow the addon API to decode it into the message's fields
        try {
            message.decode(game, source);
        } catch (Exception e) {
            game.getLogger().fatal("Exception caught decoding message!", e);
            return;
        }
        // Handle the message
        switch (game.getSide()) {
        case CLIENT:
            message.handle(game, getClientPlayer());
            break;
        case SERVER:
            INetHandler net = ctx.channel().attr(NetworkRegistry.NET_HANDLER).get();
            message.handle(game, ((NetHandlerPlayServer) net).playerEntity);
            break;
        default:
        }
    }

    @SideOnly(Side.CLIENT)
    private EntityPlayer getClientPlayer() {
        return Minecraft.getMinecraft().thePlayer;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        game.getLogger().fatal("Exception caught in pipeline!", cause);
        ctx.fireExceptionCaught(cause);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void register(Addon addon, Class<? extends Message> clazz) {
        if (locked) {
            throw new IllegalStateException(addon.getDescription().getName()
                    + " attempted to register a message after INITIALIZE phase! This is NOT ALLOWED.");
        }
        final Field discriminatorsField;
        final TByteObjectHashMap<Class<? extends Message>> discriminators;
        try {
            discriminatorsField = getClass().getSuperclass().getDeclaredField("discriminators");
            discriminatorsField.setAccessible(true);
            discriminators = (TByteObjectHashMap<Class<? extends Message>>) discriminatorsField.get(this);
            discriminatorsField.setAccessible(false);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            game.getLogger().info("Encountered fatal exception when " + addon.getDescription().getName()
                    + " attempted to register [" + clazz.getSimpleName() + "]", e);
            return;
        }

        if (discriminators.containsValue(clazz)) {
            game.getLogger()
                    .warn(addon.getDescription().getName() + " attempted to register [" + clazz + "] twice!");
            return;
        }

        addDiscriminator(discriminators.size() == 0 ? 0 : discriminators.size() + 1, clazz);

        game.getLogger().info(addon.getDescription().getName() + " has registered message [" + clazz.getSimpleName()
                + "] in the pipeline");
    }

    @Override
    public void sendToAll(Message message) {
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET)
                .set(FMLOutboundHandler.OutboundTarget.ALL);
        this.channels.get(Side.SERVER).writeAndFlush(message);
    }

    @Override
    public void sendTo(Message message, EntityPlayer player) {
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET)
                .set(FMLOutboundHandler.OutboundTarget.PLAYER);
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(player);
        this.channels.get(Side.SERVER).writeAndFlush(message);
    }

    @Override
    public void sendToAllAround(Message message, NetworkRegistry.TargetPoint point) {
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET)
                .set(FMLOutboundHandler.OutboundTarget.ALLAROUNDPOINT);
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(point);
        this.channels.get(Side.SERVER).writeAndFlush(message);
    }

    @Override
    public void sendToDimension(Message message, int dimensionId) {
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET)
                .set(FMLOutboundHandler.OutboundTarget.DIMENSION);
        this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(dimensionId);
        this.channels.get(Side.SERVER).writeAndFlush(message);
    }

    @Override
    public void sendToServer(Message message) {
        this.channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET)
                .set(FMLOutboundHandler.OutboundTarget.TOSERVER);
        this.channels.get(Side.CLIENT).writeAndFlush(message);
    }

    public void lockPipeline() {
        channels.putAll(NetworkRegistry.INSTANCE.newChannel(Game.MOD_ID.toUpperCase(), this));
        locked = true;
    }
}