Java tutorial
/* * 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(); } }; }