diff --git a/src/main/java/com/example/tankbattle/Bullet.java b/src/main/java/com/example/tankbattle/Bullet.java index 71065c2..22c8dfb 100644 --- a/src/main/java/com/example/tankbattle/Bullet.java +++ b/src/main/java/com/example/tankbattle/Bullet.java @@ -1,7 +1,11 @@ package com.example.tankbattle; +import com.example.tankbattle.net.Client; +import com.example.tankbattle.net.TankDieMessage; + import java.awt.Graphics; import java.awt.Rectangle; +import java.util.UUID; public class Bullet { private static final int SPEED = 6; @@ -10,14 +14,53 @@ public class Bullet { private Rectangle rect = new Rectangle(); + private UUID id = UUID.randomUUID(); + + private UUID playerId; + + public UUID getPlayerId() { + return playerId; + } + + public void setPlayerId(UUID playerId) { + this.playerId = playerId; + } + private int x, y; + private Dir dir; private boolean living = true; + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public Dir getDir() { + return dir; + } + + public void setDir(Dir dir) { + this.dir = dir; + } + TankFrame tf = null; private Group group = Group.BAD; - public Bullet(int x, int y, Dir dir, Group group, TankFrame tf) { + public Bullet(UUID playerId,int x, int y, Dir dir, Group group, TankFrame tf) { + this.playerId = playerId; this.x = x; this.y = y; this.dir = dir; @@ -87,18 +130,26 @@ public class Bullet { if (x < 0 || y < 0 || x > TankFrame.GAME_WIDTH || y > TankFrame.GAME_HEIGHT) living = false; } + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + public void collideWith(Tank tank){ - if (this.group == tank.getGroup()) return; - if (this.rect.intersects(tank.rect)) { + if (this.playerId.equals(tank.getId())) { + return; + } + if (this.living && tank.isLiving() && this.rect.intersects(tank.rect)) { tank.die(); this.die(); - int eX = tank.getX() + Tank.WIDTH / 2 - Explode.WIDTH / 2; - int eY = tank.getY() + Tank.HEIGHT / 2 - Explode.HEIGHT / 2; - tf.explodes.add(new Explode(eX, eY, tf)); + Client.INSTANCE.send(new TankDieMessage(this.id, tank.getId())); } } - private void die() { + public void die() { this.living = false; } } diff --git a/src/main/java/com/example/tankbattle/Explode.java b/src/main/java/com/example/tankbattle/Explode.java index ce0c8cc..8e0b5d8 100644 --- a/src/main/java/com/example/tankbattle/Explode.java +++ b/src/main/java/com/example/tankbattle/Explode.java @@ -8,14 +8,11 @@ public class Explode { private int x, y; - TankFrame tf = null; - private int step = 0; - public Explode(int x, int y, TankFrame tf) { + public Explode(int x, int y) { this.x = x; this.y = y; - this.tf = tf; } public void paint(Graphics g) { @@ -23,7 +20,7 @@ public class Explode { g.drawImage(ResourceMgr.explodes[step++], x, y, null); if (step >= ResourceMgr.explodes.length) - tf.explodes.remove(this); + TankFrame.INSTANCE.explodes.remove(this); } } diff --git a/src/main/java/com/example/tankbattle/Main.java b/src/main/java/com/example/tankbattle/Main.java index 2f7d7b3..9e8044a 100644 --- a/src/main/java/com/example/tankbattle/Main.java +++ b/src/main/java/com/example/tankbattle/Main.java @@ -1,8 +1,11 @@ package com.example.tankbattle; +import com.example.tankbattle.net.Client; + public class Main { public static void main(String[] args) throws InterruptedException { - TankFrame tf = new TankFrame(); + TankFrame tf = TankFrame.INSTANCE; + tf.setVisible(true); // int initTankCount = Integer.valueOf(PropertyMgr.get("initTankCount")); // @@ -11,9 +14,19 @@ public class Main { // tf.tanks.add(new Tank(50 + i * 80, 200, Dir.DOWN, Group.BAD, tf)); // } - while (true) { - Thread.sleep(50); - tf.repaint(); - } + new Thread(() -> new Audio("audio/war1.wav").loop()).start(); + new Thread(() -> { + while (true) { + try { + Thread.sleep(25); + } catch (InterruptedException e) { + e.printStackTrace(); + } + tf.repaint(); + } + }).start(); + + Client client = Client.INSTANCE; + client.connect(); } } diff --git a/src/main/java/com/example/tankbattle/Tank.java b/src/main/java/com/example/tankbattle/Tank.java index 6d83e50..a1102b0 100644 --- a/src/main/java/com/example/tankbattle/Tank.java +++ b/src/main/java/com/example/tankbattle/Tank.java @@ -1,7 +1,12 @@ package com.example.tankbattle; +import com.example.tankbattle.net.BulletNewMessage; +import com.example.tankbattle.net.Client; +import com.example.tankbattle.net.TankJoinMessage; + import java.awt.*; import java.util.Random; +import java.util.UUID; public class Tank { private static final int SPEED = 1; @@ -9,6 +14,7 @@ public class Tank { public static int WIDTH = ResourceMgr.GoodTankD.getWidth(); public static int HEIGHT = ResourceMgr.GoodTankD.getHeight(); + private UUID id = UUID.randomUUID(); Rectangle rect = new Rectangle(); private Random random = new Random(); @@ -17,7 +23,7 @@ public class Tank { private Dir dir = Dir.DOWN; - private boolean moving = true; + private boolean moving = false; private TankFrame tf = null; @@ -25,6 +31,28 @@ public class Tank { private Group group = Group.BAD; + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public Tank(TankJoinMessage message) { + this.x = message.x; + this.y = message.y; + this.dir = message.dir; + this.moving = message.moving; + this.group = message.group; + this.id = message.id; + + rect.x = this.x; + rect.y = this.y; + rect.width = WIDTH; + rect.height = HEIGHT; + } + public Tank(int x, int y, Dir dir, Group group, TankFrame tf) { super(); this.x = x; @@ -42,7 +70,9 @@ public class Tank { public void fire(){ int bX = this.x + Tank.WIDTH / 2 - Bullet.WIDTH / 2; int bY = this.y + Tank.HEIGHT / 2 - Bullet.HEIGHT / 2; - tf.bullets.add(new Bullet(bX, bY, this.dir, this.group, this.tf)); + Bullet bullet = new Bullet(this.id, bX, bY, this.dir, this.group, this.tf); + tf.bullets.add(bullet); + Client.INSTANCE.send(new BulletNewMessage(bullet)); } public Dir getDir() { @@ -70,6 +100,7 @@ public class Tank { } private void move() { + if (!living) return; if (!moving) return; switch (dir) { case LEFT: @@ -121,6 +152,20 @@ public class Tank { public void paint(Graphics g) { if (!living) tf.tanks.remove(this); + Color color = g.getColor(); + g.setColor(Color.YELLOW); + g.drawString(id.toString(), this.x, this.y - 10); + g.drawString("live=" + living, x, y - 10); + g.setColor(color); + + if (!living) { + moving = false; + Color colorc = g.getColor(); + g.setColor(Color.WHITE); + g.drawRect(x, y, WIDTH, HEIGHT); + g.setColor(colorc); + return; + } switch (dir) { case LEFT: g.drawImage(this.group == Group.GOOD ? ResourceMgr.GoodTankL : ResourceMgr.BadTankL, x, y, null); @@ -152,11 +197,24 @@ public class Tank { this.x = x; } + public boolean isLiving() { + return living; + } + + public void setLiving(boolean living) { + this.living = living; + } + public void setY(int y) { this.y = y; } public void die() { this.living = false; + int eX = this.getX() + Tank.WIDTH / 2 - Explode.WIDTH / 2; + int eY = this.getY() + Tank.HEIGHT / 2 - Explode.HEIGHT / 2; + TankFrame.INSTANCE.explodes.add(new Explode(eX, eY)); } + + } diff --git a/src/main/java/com/example/tankbattle/TankFrame.java b/src/main/java/com/example/tankbattle/TankFrame.java index abbc4ab..7257817 100644 --- a/src/main/java/com/example/tankbattle/TankFrame.java +++ b/src/main/java/com/example/tankbattle/TankFrame.java @@ -1,5 +1,10 @@ package com.example.tankbattle; +import com.example.tankbattle.net.Client; +import com.example.tankbattle.net.TankDirChangedMessage; +import com.example.tankbattle.net.TankStartMovingMessage; +import com.example.tankbattle.net.TankStopMessage; + import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; @@ -9,25 +14,54 @@ import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; public class TankFrame extends Frame { - Tank myTank = new Tank(200, 400, Dir.DOWN, Group.GOOD, this); + public static final TankFrame INSTANCE = new TankFrame(); + + Random random = new Random(); + + Tank myTank = new Tank(random.nextInt(GAME_WIDTH), random.nextInt(GAME_HEIGHT), Dir.DOWN, Group.GOOD, this); List bullets = new ArrayList<>(); - List tanks = new ArrayList<>(); + Map tanks = new HashMap<>(); List explodes = new ArrayList<>(); static final int GAME_WIDTH = Integer.valueOf(PropertyMgr.get("gameWidth")); static final int GAME_HEIGHT = Integer.valueOf(PropertyMgr.get("gameHeight")); - public TankFrame() { + public void addBullet(Bullet bullet) { + bullets.add(bullet); + } + + public void addTank(Tank tank) { + tanks.put(tank.getId(), tank); + } + + public Tank findTankByUUID(UUID id){ + return tanks.get(id); + } + + public Bullet findBulletByUUID(UUID id) { + for (int i = 0; i < bullets.size(); i++) { + if (bullets.get(i).getId().equals(id)) { + return bullets.get(i); + } + } + return null; + } + + private TankFrame() { setSize(GAME_WIDTH, GAME_HEIGHT); setResizable(false); setTitle("tank battle"); - setVisible(true); this.addKeyListener(new MyKeyListener()); addWindowListener(new WindowAdapter() { @Override @@ -66,19 +100,17 @@ public class TankFrame extends Frame { bullets.get(i).paint(g); } - for (int i = 0; i < tanks.size(); i++) { - tanks.get(i).paint(g); - } + tanks.values().forEach((e) -> e.paint(g)); for (int i = 0; i < explodes.size(); i++) { explodes.get(i).paint(g); } - + Collection values = tanks.values(); for (int i = 0; i < bullets.size(); i++) { - for (int j = 0; j < tanks.size(); j++) - bullets.get(i).collideWith(tanks.get(j)); + for (Tank value : values) { + bullets.get(i).collideWith(value); + } } - // for (Iterator it = bullets.iterator(); it.hasNext()) { // Bullet b = it.next(); // if (!b.live) it.remove(); @@ -144,17 +176,31 @@ public class TankFrame extends Frame { private void setMainTankDir() { + Dir dir = myTank.getDir(); + if (!bL && !bU && !bR && !bD) { myTank.setMoving(false); + Client.INSTANCE.send(new TankStopMessage(getMainTank())); } else { myTank.setMoving(true); if (bL) myTank.setDir(Dir.LEFT); if (bU) myTank.setDir(Dir.UP); if (bR) myTank.setDir(Dir.RIGHT); if (bD) myTank.setDir(Dir.DOWN); + Client.INSTANCE.send(new TankStartMovingMessage(getMainTank())); + + myTank.setMoving(true); + if (dir != myTank.getDir()) { + Client.INSTANCE.send(new TankDirChangedMessage(myTank)); + } } } } + + public Tank getMainTank() { + return this.myTank; + } + } diff --git a/src/main/java/com/example/tankbattle/net/BulletNewMessage.java b/src/main/java/com/example/tankbattle/net/BulletNewMessage.java new file mode 100644 index 0000000..8c11fdb --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/BulletNewMessage.java @@ -0,0 +1,96 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Bullet; +import com.example.tankbattle.Dir; +import com.example.tankbattle.Group; +import com.example.tankbattle.TankFrame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class BulletNewMessage extends Message { + UUID playerID; + UUID id; + int x, y; + Dir dir; + Group group; + + public BulletNewMessage() { + } + + public BulletNewMessage(Bullet bullet) { + this.playerID = bullet.getPlayerId(); + this.id = bullet.getId(); + this.x = bullet.getX(); + this.y = bullet.getY(); + this.dir = bullet.getDir(); + this.group = bullet.getGroup(); + } + + @Override + public void handle() { + if (this.id.equals(TankFrame.INSTANCE.getMainTank().getId())) { + return; + } + Bullet bullet = new Bullet(this.playerID, x, y, dir, group, TankFrame.INSTANCE); + bullet.setId(this.id); + TankFrame.INSTANCE.addBullet(bullet); + } + + @Override + public byte[] toBytes() { + byte[] bytes = null; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeLong(this.playerID.getMostSignificantBits()); + dos.writeLong(this.playerID.getLeastSignificantBits()); + dos.writeLong(id.getMostSignificantBits()); + dos.writeLong(id.getLeastSignificantBits()); + dos.writeInt(x); + dos.writeInt(y); + dos.writeInt(dir.ordinal()); + dos.writeInt(group.ordinal()); + dos.flush(); + bytes = baos.toByteArray(); + } catch (Exception exception) { + exception.printStackTrace(); + } + return bytes; + } + + @Override + public void parse(byte[] bytes) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) { + this.playerID = new UUID(dis.readLong(), dis.readLong()); + this.id = new UUID(dis.readLong(), dis.readLong()); + + this.x = dis.readInt(); + this.y = dis.readInt(); + this.dir = Dir.values()[dis.readInt()]; + this.group = Group.values()[dis.readInt()]; + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + @Override + public MessageType getMessageType() { + return MessageType.BulletNew; + } + + @Override + public String toString() { + return "BulletNewMessage{" + + "playerID=" + playerID + + ", id=" + id + + ", x=" + x + + ", y=" + y + + ", dir=" + dir + + ", group=" + group + + '}'; + } +} diff --git a/src/main/java/com/example/tankbattle/net/Client.java b/src/main/java/com/example/tankbattle/net/Client.java index 2f382d2..20296b8 100644 --- a/src/main/java/com/example/tankbattle/net/Client.java +++ b/src/main/java/com/example/tankbattle/net/Client.java @@ -1,8 +1,6 @@ package com.example.tankbattle.net; import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -11,9 +9,14 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class Client { + public static final Client INSTANCE = new Client(); + + private Client() { + } + private Channel channel; public void connect() { - EventLoopGroup group = new NioEventLoopGroup(); + EventLoopGroup group = new NioEventLoopGroup(1); Bootstrap bootstrap = new Bootstrap(); try { @@ -32,7 +35,7 @@ public class Client { }); channelFuture.sync(); channelFuture.channel().closeFuture().sync(); - System.out.println("channel closed"); + System.out.println("connection closed"); } catch (InterruptedException e) { e.printStackTrace(); } finally { @@ -40,22 +43,11 @@ public class Client { } } - public void send(String message) { - ByteBuf buf = Unpooled.copiedBuffer(message.getBytes()); - channel.writeAndFlush(buf); - } - - public static void main(String[] args) { - Client client = new Client(); - client.connect(); - client.send("hello"); - client.send("world"); - client.send("hello world"); - client.send("hello world hello"); - client.send("hello world hello world"); + public void send(Message message) { + System.out.println("send " + message.toString()); + channel.writeAndFlush(message); } public void closeConnect(){ - this.send("_bye_"); } } diff --git a/src/main/java/com/example/tankbattle/net/ClientChannelInitializer.java b/src/main/java/com/example/tankbattle/net/ClientChannelInitializer.java index 30881d1..ab65128 100644 --- a/src/main/java/com/example/tankbattle/net/ClientChannelInitializer.java +++ b/src/main/java/com/example/tankbattle/net/ClientChannelInitializer.java @@ -7,6 +7,9 @@ public class ClientChannelInitializer extends ChannelInitializer @Override protected void initChannel(SocketChannel socketChannel) throws Exception { - socketChannel.pipeline().addLast(new TankMessageEncoder()).addLast(new ClientHandler()); + socketChannel.pipeline() + .addLast(new MessageEncoder()) + .addLast(new MessageDecoder()) + .addLast(new ClientHandler()); } } diff --git a/src/main/java/com/example/tankbattle/net/ClientHandler.java b/src/main/java/com/example/tankbattle/net/ClientHandler.java index e56ff80..e8d9b80 100644 --- a/src/main/java/com/example/tankbattle/net/ClientHandler.java +++ b/src/main/java/com/example/tankbattle/net/ClientHandler.java @@ -1,28 +1,18 @@ package com.example.tankbattle.net; -import io.netty.buffer.ByteBuf; +import com.example.tankbattle.TankFrame; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.util.ReferenceCountUtil; +import io.netty.channel.SimpleChannelInboundHandler; -public class ClientHandler extends ChannelInboundHandlerAdapter { +public class ClientHandler extends SimpleChannelInboundHandler { @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - ByteBuf buf = null; - try { - buf = (ByteBuf) msg; - byte[] bytes = new byte[buf.readableBytes()]; -// buf.getByte(buf.readerIndex(), bytes); - String message = new String(bytes); - } finally { - if (buf!= null) { - ReferenceCountUtil.release(buf); - } - } + public void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception { + System.out.println(msg); + msg.handle(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.writeAndFlush(new TankMessage(5, 8)); + ctx.writeAndFlush(new TankJoinMessage(TankFrame.INSTANCE.getMainTank())); } } diff --git a/src/main/java/com/example/tankbattle/net/Message.java b/src/main/java/com/example/tankbattle/net/Message.java new file mode 100644 index 0000000..967dbd8 --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/Message.java @@ -0,0 +1,8 @@ +package com.example.tankbattle.net; + +public abstract class Message { + public abstract void handle(); + public abstract byte[] toBytes(); + public abstract void parse(byte[] bytes); + public abstract MessageType getMessageType(); +} diff --git a/src/main/java/com/example/tankbattle/net/MessageDecoder.java b/src/main/java/com/example/tankbattle/net/MessageDecoder.java new file mode 100644 index 0000000..52b34af --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/MessageDecoder.java @@ -0,0 +1,30 @@ +package com.example.tankbattle.net; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; + +public class MessageDecoder extends ByteToMessageDecoder { + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + if (in.readableBytes() < 8) { + return; + } + in.markReaderIndex(); + MessageType messageType = MessageType.values()[in.readInt()]; + int length = in.readInt(); + if (in.readableBytes() < length) { + in.resetReaderIndex(); + return; + } + + byte[] bytes = new byte[length]; + in.readBytes(bytes); + + Message message = (Message) Class.forName("com.example.tankbattle.net." + messageType.toString() + "Message").getDeclaredConstructor().newInstance(); + message.parse(bytes); + out.add(message); + } +} diff --git a/src/main/java/com/example/tankbattle/net/MessageEncoder.java b/src/main/java/com/example/tankbattle/net/MessageEncoder.java new file mode 100644 index 0000000..2bc462c --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/MessageEncoder.java @@ -0,0 +1,15 @@ +package com.example.tankbattle.net; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class MessageEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception { + out.writeInt(msg.getMessageType().ordinal()); + byte[] data = msg.toBytes(); + out.writeInt(data.length); + out.writeBytes(data);} +} diff --git a/src/main/java/com/example/tankbattle/net/MessageType.java b/src/main/java/com/example/tankbattle/net/MessageType.java new file mode 100644 index 0000000..61685dc --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/MessageType.java @@ -0,0 +1,5 @@ +package com.example.tankbattle.net; + +public enum MessageType { + TankJoin, TankDirChanged, TankStop, TankStartMoving, BulletNew, TankDie, +} diff --git a/src/main/java/com/example/tankbattle/net/Server.java b/src/main/java/com/example/tankbattle/net/Server.java index 0217fea..c2709c5 100644 --- a/src/main/java/com/example/tankbattle/net/Server.java +++ b/src/main/java/com/example/tankbattle/net/Server.java @@ -26,10 +26,13 @@ public class Server { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline channelPipeline = ch.pipeline(); - channelPipeline.addLast(new TankMessageDecoder()).addLast(new ServerChildHandler()); + channelPipeline.addLast(new MessageEncoder()) + .addLast(new MessageDecoder()) + .addLast(new ServerChildHandler()); } }).bind(8888).sync(); + ServerFrame.INSTANCE.updateServerMessage("server started!"); channelFuture.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/example/tankbattle/net/ServerChildHandler.java b/src/main/java/com/example/tankbattle/net/ServerChildHandler.java index 602a8b0..8d5357c 100644 --- a/src/main/java/com/example/tankbattle/net/ServerChildHandler.java +++ b/src/main/java/com/example/tankbattle/net/ServerChildHandler.java @@ -13,14 +13,8 @@ public class ServerChildHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - System.out.println("channelRead"); - try { - System.out.println(msg.toString()); - TankMessage tankMessage = (TankMessage) msg; - System.out.println(tankMessage); - } finally { - ReferenceCountUtil.release(msg); - } + ServerFrame.INSTANCE.updateClientMessage(msg.toString()); + Server.clients.writeAndFlush(msg); } @Override diff --git a/src/main/java/com/example/tankbattle/net/TankDieMessage.java b/src/main/java/com/example/tankbattle/net/TankDieMessage.java new file mode 100644 index 0000000..27ca128 --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/TankDieMessage.java @@ -0,0 +1,96 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Bullet; +import com.example.tankbattle.Tank; +import com.example.tankbattle.TankFrame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class TankDieMessage extends Message{ + + UUID bulletId; + UUID id; + + public TankDieMessage() { + } + + public TankDieMessage(UUID bulletId, UUID id) { + this.bulletId = bulletId; + this.id = id; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + @Override + public void handle() { + System.out.println("we got a tank die: " + id); + System.out.println("and my tank is: " + TankFrame.INSTANCE.getMainTank().getId()); + Tank tank = TankFrame.INSTANCE.findTankByUUID(id); + System.out.println("i found a tank with this id: " + tank); + + Bullet bullet = TankFrame.INSTANCE.findBulletByUUID(bulletId); + if (bullet != null) { + bullet.die(); + } + if (this.id.equals(TankFrame.INSTANCE.getMainTank().getId())) { + TankFrame.INSTANCE.getMainTank().die(); + } else { + Tank tanka = TankFrame.INSTANCE.findTankByUUID(id); + if (tanka != null) { + tanka.die(); + } + } + } + + @Override + public byte[] toBytes() { + byte[] bytes = null; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeLong(bulletId.getMostSignificantBits()); + dos.writeLong(bulletId.getLeastSignificantBits()); + dos.writeLong(id.getMostSignificantBits()); + dos.writeLong(id.getLeastSignificantBits()); + dos.flush(); + bytes = baos.toByteArray(); + + } catch (Exception exception) { + exception.printStackTrace(); + } + return bytes; + } + + @Override + public void parse(byte[] bytes) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) { + this.bulletId = new UUID(dis.readLong(), dis.readLong()); + this.id = new UUID(dis.readLong(), dis.readLong()); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + @Override + public MessageType getMessageType() { + return MessageType.TankDie; + } + + @Override + public String toString() { + return "TankDieMessage{" + + "bulletId=" + bulletId + + ", id=" + id + + '}'; + } +} diff --git a/src/main/java/com/example/tankbattle/net/TankDirChangedMessage.java b/src/main/java/com/example/tankbattle/net/TankDirChangedMessage.java new file mode 100644 index 0000000..5e0fc00 --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/TankDirChangedMessage.java @@ -0,0 +1,131 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Dir; +import com.example.tankbattle.Tank; +import com.example.tankbattle.TankFrame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class TankDirChangedMessage extends Message{ + + UUID id; + + Dir dir; + + int x, y; + + public TankDirChangedMessage() { + } + + public TankDirChangedMessage(UUID id, int x, int y, Dir dir) { + this.id = id; + this.x = x; + this.y = y; + this.dir = dir; + } + + public TankDirChangedMessage(Tank tank) { + this.id = tank.getId(); + this.dir = tank.getDir(); + this.x = tank.getX(); + this.y = tank.getY(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public Dir getDir() { + return dir; + } + + public void setDir(Dir dir) { + this.dir = dir; + } + + @Override + public void handle() { + if (this.id.equals(TankFrame.INSTANCE.getMainTank().getId())) { + return; + } + Tank tank = TankFrame.INSTANCE.findTankByUUID(this.id); + if (tank != null) { + tank.setMoving(false); + tank.setX(this.x); + tank.setY(this.y); + tank.setDir(this.dir); + } + } + + @Override + public byte[] toBytes() { + byte[] bytes = null; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeLong(id.getMostSignificantBits()); + dos.writeLong(id.getLeastSignificantBits()); + dos.writeInt(x); + dos.writeInt(y); + dos.writeInt(dir.ordinal()); + dos.flush(); + bytes = baos.toByteArray(); + + } catch (Exception exception) { + exception.printStackTrace(); + } + return bytes; + } + + @Override + public void parse(byte[] bytes) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) { + this.id = new UUID(dis.readLong(), dis.readLong()); + + this.x = dis.readInt(); + this.y = dis.readInt(); + this.dir = Dir.values()[dis.read()]; + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + @Override + public MessageType getMessageType() { + return MessageType.TankDirChanged; + } + + @Override + public String toString() { + return "TankDirChangedMessage{" + + "id=" + id + + ", dir=" + dir + + ", x=" + x + + ", y=" + y + + '}'; + } +} diff --git a/src/main/java/com/example/tankbattle/net/TankJoinMessage.java b/src/main/java/com/example/tankbattle/net/TankJoinMessage.java new file mode 100644 index 0000000..a6d788b --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/TankJoinMessage.java @@ -0,0 +1,117 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Dir; +import com.example.tankbattle.Group; +import com.example.tankbattle.Tank; +import com.example.tankbattle.TankFrame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class TankJoinMessage extends Message{ + public int x; + public int y; + public Dir dir; + public boolean moving; + public Group group; + public UUID id; + + public TankJoinMessage() { + } + + public TankJoinMessage(int x, int y, Dir dir, boolean moving, Group group, UUID id) { + super(); + this.x = x; + this.y = y; + this.dir = dir; + this.moving = moving; + this.group = group; + this.id = id; + } + + + public TankJoinMessage(Tank tank) { + super(); + this.x = tank.getX(); + this.y = tank.getY(); + this.dir = tank.getDir(); + this.group = tank.getGroup(); + this.moving = tank.isMoving(); + this.id = tank.getId(); + } + + @Override + public void parse(byte[] bytes) { + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); + try { + this.x = dis.readInt(); + this.y = dis.readInt(); + this.dir = Dir.values()[dis.readInt()]; + this.moving = dis.readBoolean(); + this.group = Group.values()[dis.readInt()]; + this.id = new UUID(dis.readLong(), dis.readLong()); + } catch (IOException exception) { + exception.printStackTrace(); + } finally { + try { + dis.close(); + }catch (IOException ex){ + ex.printStackTrace(); + } + } + } + + @Override + public byte[] toBytes(){ + byte[] bytes = null; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeInt(x); + dos.writeInt(y); + dos.writeInt(dir.ordinal()); + dos.writeBoolean(moving); + dos.writeInt(group.ordinal()); + dos.writeLong(id.getMostSignificantBits()); + dos.writeLong(id.getLeastSignificantBits()); + dos.flush(); + bytes = baos.toByteArray(); + + } catch (Exception exception) { + exception.printStackTrace(); + } finally { + } + return bytes; + } + + @Override + public void handle(){ + if (this.id.equals(TankFrame.INSTANCE.getMainTank().getId()) || + TankFrame.INSTANCE.findTankByUUID(this.id) != null) { + return; + } + Tank tank = new Tank(this); + TankFrame.INSTANCE.addTank(tank); + Client.INSTANCE.send(new TankJoinMessage(TankFrame.INSTANCE.getMainTank())); + } + + @Override + public MessageType getMessageType() { + return MessageType.TankJoin; + } + + @Override + public String toString() { + return "TankJoinMessage{" + + "x=" + x + + ", y=" + y + + ", dir=" + dir + + ", moving=" + moving + + ", group=" + group + + ", id=" + id + + '}'; + } +} diff --git a/src/main/java/com/example/tankbattle/net/TankMessage.java b/src/main/java/com/example/tankbattle/net/TankMessage.java deleted file mode 100644 index eb87a6b..0000000 --- a/src/main/java/com/example/tankbattle/net/TankMessage.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.tankbattle.net; - -public class TankMessage { - public int x; - public int y; - - public TankMessage(int x, int y) { - this.x = x; - this.y = y; - } -} diff --git a/src/main/java/com/example/tankbattle/net/TankMessageDecoder.java b/src/main/java/com/example/tankbattle/net/TankMessageDecoder.java deleted file mode 100644 index ad11e3d..0000000 --- a/src/main/java/com/example/tankbattle/net/TankMessageDecoder.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.example.tankbattle.net; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import java.util.List; - -public class TankMessageDecoder extends ByteToMessageDecoder { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - if (in.readableBytes() < 8) { - return; - } - int x = in.readInt(); - int y = in.readInt(); - out.add(new TankMessage(x, y)); - } -} diff --git a/src/main/java/com/example/tankbattle/net/TankMessageEncoder.java b/src/main/java/com/example/tankbattle/net/TankMessageEncoder.java deleted file mode 100644 index cb35540..0000000 --- a/src/main/java/com/example/tankbattle/net/TankMessageEncoder.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example.tankbattle.net; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -public class TankMessageEncoder extends MessageToByteEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, TankMessage msg, ByteBuf out) throws Exception { - out.writeInt(msg.x); - out.writeInt(msg.y); - } -} diff --git a/src/main/java/com/example/tankbattle/net/TankStartMovingMessage.java b/src/main/java/com/example/tankbattle/net/TankStartMovingMessage.java new file mode 100644 index 0000000..57c30db --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/TankStartMovingMessage.java @@ -0,0 +1,131 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Dir; +import com.example.tankbattle.Tank; +import com.example.tankbattle.TankFrame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class TankStartMovingMessage extends Message{ + + UUID id; + + int x,y; + + Dir dir; + + public TankStartMovingMessage() { + } + + public TankStartMovingMessage(UUID id, int x, int y, Dir dir) { + this.id = id; + this.x = x; + this.y = y; + this.dir = dir; + } + + public TankStartMovingMessage(Tank tank) { + this.id = tank.getId(); + this.x = tank.getX(); + this.y = tank.getY(); + this.dir = tank.getDir(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public Dir getDir() { + return dir; + } + + public void setDir(Dir dir) { + this.dir = dir; + } + + @Override + public void handle() { + if (this.id.equals(TankFrame.INSTANCE.getMainTank().getId())) { + return; + } + Tank tank = TankFrame.INSTANCE.findTankByUUID(this.id); + if (tank != null) { + tank.setMoving(true); + tank.setX(this.x); + tank.setY(this.y); + tank.setDir(this.dir); + } + } + + @Override + public byte[] toBytes() { + byte[] bytes = null; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeLong(id.getMostSignificantBits()); + dos.writeLong(id.getLeastSignificantBits()); + dos.writeInt(x); + dos.writeInt(y); + dos.writeInt(dir.ordinal()); + dos.flush(); + bytes = baos.toByteArray(); + + } catch (Exception exception) { + exception.printStackTrace(); + } + return bytes; + } + + @Override + public void parse(byte[] bytes) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) { + this.id = new UUID(dis.readLong(), dis.readLong()); + + this.x = dis.readInt(); + this.y = dis.readInt(); + this.dir = Dir.values()[dis.readInt()]; + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + @Override + public MessageType getMessageType() { + return MessageType.TankStartMoving; + } + + @Override + public String toString() { + return "TankStartMovingMessage{" + + "id=" + id + + ", x=" + x + + ", y=" + y + + ", dir=" + dir + + '}'; + } +} diff --git a/src/main/java/com/example/tankbattle/net/TankStopMessage.java b/src/main/java/com/example/tankbattle/net/TankStopMessage.java new file mode 100644 index 0000000..26e8066 --- /dev/null +++ b/src/main/java/com/example/tankbattle/net/TankStopMessage.java @@ -0,0 +1,114 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Tank; +import com.example.tankbattle.TankFrame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class TankStopMessage extends Message{ + + UUID id; + + int x,y; + + public TankStopMessage() { + } + + public TankStopMessage(UUID id, int x, int y) { + this.id = id; + this.x = x; + this.y = y; + } + + public TankStopMessage(Tank tank) { + this.id = tank.getId(); + this.x = tank.getX(); + this.y = tank.getY(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + @Override + public void handle() { + if (this.id.equals(TankFrame.INSTANCE.getMainTank().getId())) { + return; + } + Tank tank = TankFrame.INSTANCE.findTankByUUID(this.id); + if (tank != null) { + tank.setMoving(false); + tank.setX(this.x); + tank.setY(this.y); + } + } + + @Override + public byte[] toBytes() { + byte[] bytes = null; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeLong(id.getMostSignificantBits()); + dos.writeLong(id.getLeastSignificantBits()); + dos.writeInt(x); + dos.writeInt(y); + dos.flush(); + bytes = baos.toByteArray(); + + } catch (Exception exception) { + exception.printStackTrace(); + } + return bytes; + } + + @Override + public void parse(byte[] bytes) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) { + this.id = new UUID(dis.readLong(), dis.readLong()); + + this.x = dis.readInt(); + this.y = dis.readInt(); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + @Override + public MessageType getMessageType() { + return MessageType.TankStop; + } + + @Override + public String toString() { + return "TankStopMessage{" + + "id=" + id + + ", x=" + x + + ", y=" + y + + '}'; + } +} diff --git a/src/test/java/com/example/tankbattle/net/TankDirChangedMessageCoderTest.java b/src/test/java/com/example/tankbattle/net/TankDirChangedMessageCoderTest.java new file mode 100644 index 0000000..98748d0 --- /dev/null +++ b/src/test/java/com/example/tankbattle/net/TankDirChangedMessageCoderTest.java @@ -0,0 +1,67 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Dir; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TankDirChangedMessageCoderTest { + + @Test + void encode() { + EmbeddedChannel embeddedChannel = new EmbeddedChannel(); + UUID id = UUID.randomUUID(); + + TankDirChangedMessage message = new TankDirChangedMessage(id, 5, 10, Dir.DOWN); + embeddedChannel.pipeline().addLast(new MessageEncoder()); + embeddedChannel.writeOutbound(message); + + ByteBuf buf = embeddedChannel.readOutbound(); + MessageType messageType = MessageType.values()[buf.readInt()]; + assertEquals(MessageType.TankJoin, messageType); + + int length = buf.readInt(); + assertEquals(33, length); + + int x = buf.readInt(); + int y = buf.readInt(); + Dir dir = Dir.values()[buf.readInt()]; + UUID uuid = new UUID(buf.readLong(), buf.readLong()); + + assertEquals(5, x); + assertEquals(10, y); + assertEquals(Dir.DOWN, dir); + assertEquals(id, uuid); + + } + + @Test + void decode() { + EmbeddedChannel embeddedChannel = new EmbeddedChannel(); + + UUID id = UUID.randomUUID(); + TankDirChangedMessage message = new TankDirChangedMessage(id, 5, 10, Dir.DOWN); + embeddedChannel.pipeline().addLast(new MessageDecoder()); + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(MessageType.TankJoin.ordinal()); + byte[] data = message.toBytes(); + buf.writeInt(data.length); + buf.writeBytes(data); + + + embeddedChannel.writeInbound(buf.duplicate()); + + TankDirChangedMessage msg = embeddedChannel.readInbound(); + + assertEquals(5, msg.x); + assertEquals(10, msg.y); + assertEquals(Dir.DOWN, msg.dir); + assertEquals(id, msg.id); + } +} \ No newline at end of file diff --git a/src/test/java/com/example/tankbattle/net/TankJoinMessageCoderTest.java b/src/test/java/com/example/tankbattle/net/TankJoinMessageCoderTest.java new file mode 100644 index 0000000..d941f95 --- /dev/null +++ b/src/test/java/com/example/tankbattle/net/TankJoinMessageCoderTest.java @@ -0,0 +1,74 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Dir; +import com.example.tankbattle.Group; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class TankJoinMessageCoderTest { + + @Test + void encode() { + EmbeddedChannel embeddedChannel = new EmbeddedChannel(); + UUID id = UUID.randomUUID(); + + TankJoinMessage message = new TankJoinMessage(5, 10, Dir.DOWN, true, Group.BAD, id); + embeddedChannel.pipeline().addLast(new MessageEncoder()); + embeddedChannel.writeOutbound(message); + + ByteBuf buf = embeddedChannel.readOutbound(); + MessageType messageType = MessageType.values()[buf.readInt()]; + assertEquals(MessageType.TankJoin, messageType); + + int length = buf.readInt(); + assertEquals(33, length); + + int x = buf.readInt(); + int y = buf.readInt(); + Dir dir = Dir.values()[buf.readInt()]; + boolean moving = buf.readBoolean(); + Group group = Group.values()[buf.readInt()]; + UUID uuid = new UUID(buf.readLong(), buf.readLong()); + + assertEquals(5, x); + assertEquals(10, y); + assertEquals(Dir.DOWN, dir); + assertEquals(true, moving); + assertEquals(Group.BAD, group); + assertEquals(id, uuid); + + } + + @Test + void decode() { + EmbeddedChannel embeddedChannel = new EmbeddedChannel(); + + UUID id = UUID.randomUUID(); + TankJoinMessage message = new TankJoinMessage(5, 10, Dir.DOWN, true, Group.BAD, id); + embeddedChannel.pipeline().addLast(new MessageDecoder()); + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(MessageType.TankJoin.ordinal()); + byte[] data = message.toBytes(); + buf.writeInt(data.length); + buf.writeBytes(data); + + + embeddedChannel.writeInbound(buf.duplicate()); + + TankJoinMessage msg = embeddedChannel.readInbound(); + + assertEquals(5, msg.x); + assertEquals(10, msg.y); + assertEquals(Dir.DOWN, msg.dir); + assertEquals(true, msg.moving); + assertEquals(Group.BAD, msg.group); + assertEquals(id, msg.id); + } +} \ No newline at end of file diff --git a/src/test/java/com/example/tankbattle/net/TankStartMovingMessageTest.java b/src/test/java/com/example/tankbattle/net/TankStartMovingMessageTest.java new file mode 100644 index 0000000..b085219 --- /dev/null +++ b/src/test/java/com/example/tankbattle/net/TankStartMovingMessageTest.java @@ -0,0 +1,68 @@ +package com.example.tankbattle.net; + +import com.example.tankbattle.Dir; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class TankStartMovingMessageTest { + + @Test + void encode() { + EmbeddedChannel embeddedChannel = new EmbeddedChannel(); + UUID id = UUID.randomUUID(); + + TankStartMovingMessage message = new TankStartMovingMessage(id, 5, 10, Dir.LEFT); + embeddedChannel.pipeline().addLast(new MessageEncoder()); + embeddedChannel.writeOutbound(message); + + ByteBuf buf = embeddedChannel.readOutbound(); + MessageType messageType = MessageType.values()[buf.readInt()]; + assertEquals(MessageType.TankStartMoving, messageType); + + int length = buf.readInt(); + assertEquals(28, length); + + UUID uuid = new UUID(buf.readLong(), buf.readLong()); + int x = buf.readInt(); + int y = buf.readInt(); + Dir dir = Dir.values()[buf.readInt()]; + + + assertEquals(5, x); + assertEquals(10, y); + assertEquals(Dir.LEFT, dir); + assertEquals(id, uuid); + + } + + @Test + void decode() { + EmbeddedChannel embeddedChannel = new EmbeddedChannel(); + + UUID id = UUID.randomUUID(); + TankStartMovingMessage message = new TankStartMovingMessage(id, 5, 10, Dir.LEFT); + embeddedChannel.pipeline().addLast(new MessageDecoder()); + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(MessageType.TankStartMoving.ordinal()); + byte[] data = message.toBytes(); + buf.writeInt(data.length); + buf.writeBytes(data); + + + embeddedChannel.writeInbound(buf.duplicate()); + + TankStartMovingMessage msg = (TankStartMovingMessage)embeddedChannel.readInbound(); + + assertEquals(5, msg.x); + assertEquals(10, msg.y); + assertEquals(Dir.LEFT, msg.dir); + assertEquals(id, msg.id); + } +} \ No newline at end of file