com.nus.mazegame.client.GameClientHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.nus.mazegame.client.GameClientHandler.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.nus.mazegame.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.nus.mazegame.client.ui.CommandUI;
import com.nus.mazegame.client.ui.UIInterface;
import com.nus.mazegame.domain.Command;
import com.nus.mazegame.domain.GeneralResponseType;
import com.nus.mazegame.domain.MNodeType;
import com.nus.mazegame.io.MDataInputStream;
import com.nus.mazegame.io.MSimpleSerializable;
import com.nus.mazegame.p2p.MazeNode;
import com.nus.mazegame.pojo.GeneralResponsePacket;
import com.nus.mazegame.pojo.InitPacket;
import com.nus.mazegame.pojo.JoinResponsePacket;
import com.nus.mazegame.pojo.MoveRequestPacket;
import com.nus.mazegame.pojo.MoveResponsePacket;
import com.nus.mazegame.pojo.MsgHeader;
import com.nus.mazegame.server.GameServerHandler;
import com.nus.mazegame.server.GameService;
import com.nus.mazegame.server.User;
import com.nus.mazegame.util.RetrySender;
import com.nus.mazegame.util.SendPacketTask;
import com.nus.mazegame.util.SenderInterface;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

/**
 *
 * @author gejun
 */
@Sharable
public class GameClientHandler extends SimpleChannelInboundHandler<Object> implements SenderInterface {

    public static final GameClientHandler instance = new GameClientHandler();
    private ChannelHandlerContext ctx;
    UIInterface ui = new CommandUI();
    public static RetrySender rsender;

    @Override
    public void channelActive(final ChannelHandlerContext ctx) {
        this.ctx = ctx;
        if (rsender == null) {
            rsender = new RetrySender(instance);
            ui.init();
        }

        ChannelFuture closeFuture = ctx.channel().closeFuture();
        closeFuture.addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                System.out.println("Master lost, connect to slave...");
                GameClient.init(GameInfo.instance.getSlaveAddr(), GameInfo.instance.getHostPort() + 1,
                        MNodeType.CLIENT, ctx.channel().localAddress());
            }

        });
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        MDataInputStream din = new MDataInputStream(new ByteArrayInputStream((byte[]) msg));
        MsgHeader header = new MsgHeader();
        header.fromStream(din);
        if (header.getMsgId().length() > 0 && header.getAction() > 50 && header.getAction() < 100
                && !rsender.isMsgValid(header.getMsgId())) {
            return;
        }
        if (header.getAction() > 50) {
            Logger.getLogger(GameServerHandler.class.getName()).log(Level.INFO, "msg recieved, action:{0} msgId{1}",
                    new Object[] { header.getAction(), header.getMsgId() });
        }
        // cancel a recived msg retry
        String msgId = header.getMsgId();

        if (msgId.length() > 0 && header.getAction() < 50) {
            rsender.cancelMsg(msgId);
        }
        if (header.getAction() == Command.END) {
            GeneralResponsePacket packet = new GeneralResponsePacket();
            packet.fromStream(din);
            switch (packet.getType()) {
            case GeneralResponseType.END:
                ui.onEndResponse(packet);
                break;
            case GeneralResponseType.ERROR:
                ui.onErrorResponse(packet);
                break;
            }
        } else if (header.getAction() == Command.JOIN) {
            JoinResponsePacket packet = new JoinResponsePacket();
            packet.fromStream(din);
            if (packet.isIsSlave()) {
                // always start port+1
                MazeNode.startServer(GameInfo.instance.getHostPort() + 1, 1, 1, 0, true);
                GameInfo.instance.setType(MNodeType.SLAVE);
            }
            ui.onJoinResponse(packet);
        } else if (header.getAction() == Command.MOVE) {
            MoveResponsePacket packet = new MoveResponsePacket();
            packet.fromStream(din);
            ui.onMoveResponse(packet);
        } else if (header.getAction() == Command.START) {
            final InitPacket packet = new InitPacket();
            packet.fromStream(din);
            GameInfo.instance.setUserId(packet.getUserId());
            GameInfo.instance.setMasterId(packet.getMasterId());
            GameInfo.instance.setSlaveId(packet.getSlaveId());
            GameInfo.instance.setSlaveAddr(packet.getSlaveAddr());
            if (GameInfo.instance.getType().equals(MNodeType.SLAVE)) {
                initSlaveServer(packet);
            }
            ui.onStartResponse(packet);
        } else if (header.getAction() == Command.UPDATE) {
            //happens only if master or slave is lost, slave node will always be changed
            final InitPacket packet = new InitPacket();
            packet.fromStream(din);
            GameInfo.instance.setUserId(packet.getUserId());
            GameInfo.instance.setMasterId(packet.getMasterId());
            GameInfo.instance.setSlaveId(packet.getSlaveId());
            GameInfo.instance.setSlaveAddr(packet.getSlaveAddr());
            GameInfo.instance.setHostPort(GameInfo.instance.getHostPort() + 1);

            //update server
            if (GameInfo.instance.getUserId() == packet.getSlaveId()) {
                MazeNode.startServer(GameInfo.instance.getHostPort() + 1, 1, 1, 0, true);
                GameInfo.instance.setType(MNodeType.SLAVE);
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            //wait until slave server starts
                            Thread.sleep(500);
                            initSlaveServer(packet);
                        } catch (Exception ex) {
                            Logger.getLogger(GameClientHandler.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }.start();
            }
        } else if (header.getAction() == Command.UPDATE_MOVE) {
            // for slave only
            final MoveResponsePacket packet = new MoveResponsePacket();
            packet.fromStream(din);
            final int updateUId = header.getUserId();
            new Thread() {
                @Override
                public void run() {
                    try {
                        GameService.gameService.setMap(packet.getMap());
                        GameService.gameService.getUsers().get(updateUId).setPosX(packet.getCurrentX());
                        GameService.gameService.getUsers().get(updateUId).setPosY(packet.getCurrentY());
                        GameService.gameService.getUsers().get(updateUId)
                                .setBag(packet.getUserBags().get(updateUId));
                        GameService.gameService.dumpMap();
                    } catch (Exception ex) {
                        Logger.getLogger(GameClientHandler.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }.start();

            // send ACK
            header.setAction(Command.ACK);
            sendMsg(header, null, false);
        } else if (header.getAction() == Command.UPDATE_END) {
            // for slave only
            final int updateUId = header.getUserId();
            new Thread() {
                @Override
                public void run() {
                    try {
                        User u = GameService.gameService.getUsers().remove(updateUId);
                        if (u != null) {
                            GameService.gameService.getMap()[u.getPosX()][u.getPosY()] = 0;
                        }
                        GameService.gameService.dumpMap();
                    } catch (Exception ex) {
                        Logger.getLogger(GameClientHandler.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }.start();

            // send ACK
            header.setAction(Command.ACK);
            sendMsg(header, null, false);
        }
    }

    private void initSlaveServer(final InitPacket packet) {
        new Thread() {
            @Override
            public void run() {
                try {
                    // update slave game info to date
                    GameService.gameService.setIsSlave(true);
                    GameService.gameService.setMasterId(packet.getMasterId());
                    GameService.gameService.setSlaveId(packet.getSlaveId());
                    GameService.gameService.setMap(packet.getMap());
                    GameService.gameService.setStatus(GameService.GAME_STATUS_STARTED);
                    Map<Integer, User> users = new HashMap<>();
                    for (int uid : packet.getUserBags().keySet()) {
                        User u = new User();
                        u.setUserId(uid);
                        u.setBag(packet.getUserBags().get(uid));
                        u.setClientName(packet.getUserCNames().get(uid));
                        users.put(uid, u);
                    }
                    int[][] map = packet.getMap();
                    for (int i = 0; i < map.length; i++) {
                        for (int j = 0; j < map[0].length; j++) {
                            if (map[i][j] < 0) {
                                int uid = -map[i][j];
                                users.get(uid).setPosX(i);
                                users.get(uid).setPosY(j);
                            }
                        }
                    }
                    GameService.gameService.setUsers(users);

                    GameService.gameService.dumpMap();
                } catch (Exception ex) {
                    Logger.getLogger(GameClientHandler.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }.start();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }

    public void move(int userId, int direction) throws IOException {
        MoveRequestPacket packet = new MoveRequestPacket();
        packet.setDirection(direction);
        String msgId = RetrySender.genMsgKey(userId);
        MsgHeader header = new MsgHeader();
        header.setAction(Command.MOVE);
        header.setUserId(userId);
        header.setMsgId(msgId);
        SendPacketTask task = new SendPacketTask(msgId, header, packet);
        task.setCloseCtx(false);
        rsender.send(task);
    }

    public void end(int userId) throws IOException {
        String msgId = RetrySender.genMsgKey(userId);
        MsgHeader header = new MsgHeader();
        header.setAction(Command.END);
        header.setUserId(userId);
        header.setMsgId(msgId);
        SendPacketTask task = new SendPacketTask(msgId, header, null);
        task.setCloseCtx(true);
        rsender.send(task);
    }

    public void join() throws IOException {
        MsgHeader header = new MsgHeader();
        header.setUserId(-1);
        header.setAction(Command.JOIN);
        sendMsg(header, null, false);
    }

    @Override
    public void sendMsg(MsgHeader header, MSimpleSerializable packet, boolean closeCtx) throws IOException {
        Logger.getLogger(GameServerHandler.class.getName()).log(Level.FINE, "send msg...{0} action {1}",
                new Object[] { header.getMsgId(), header.getAction() });
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        header.toStream(bout);
        if (packet != null) {
            packet.toStream(bout);
        }
        write(bout.toByteArray(), false);
    }

    public void write(byte[] buff, boolean closeCtx) {
        final ChannelFuture f = ctx.writeAndFlush(buff);
        if (closeCtx) {
            f.addListener(closeFutureListener);
        }
    }

    private final ChannelFutureListener closeFutureListener = new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) {
            future.channel().close();
        }
    };
}