package client.protocol;
import client.bean.Dir;
import client.bean.Push;
import client.bean.Tee;
import client.client.TeeClient;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**
* Created by lichunxuan on 2021/6/19.
*/
public class TeeThrewMsg implements Msg {
private int msgType = Msg.TEE_MOVE_MSG;
private int teeId;
private TeeClient tc;
private Push p;
private int x, y;
private Dir dir;
private Dir ptDir;
public TeeThrewMsg(int teeId,int x,int y,Dir dir, Dir ptDir,Push p){
this.x=x;
this.y=y;
this.dir=dir;
this.ptDir=ptDir;
this.teeId = teeId;
this.p = p;
}
public TeeThrewMsg(TeeClient tc){
this.tc = tc;
}
@Override
public void send(DatagramSocket ds, String IP, int UDP_Port) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(30);//指定大小, 免得字节数组扩容占用时间
DataOutputStream dos = new DataOutputStream(baos);
try {
dos.writeInt(msgType);
dos.writeInt(teeId);
dos.writeInt(p.getX() - 20);
dos.writeInt(p.getY() - 20);
dos.writeInt(dir.ordinal());
dos.writeInt(ptDir.ordinal());
dos.writeInt(x);
dos.writeInt(y);
} catch (IOException e) {
e.printStackTrace();
}
byte[] buf = baos.toByteArray();
try{
DatagramPacket dp = new DatagramPacket(buf, buf.length, new InetSocketAddress(IP, UDP_Port));
ds.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void parse(DataInputStream dis) {
try{
int id = dis.readInt();
if(id == this.tc.getMyTee().getId()){
return;
}
Dir dir = Dir.values()[dis.readInt()];
Dir ptDir = Dir.values()[dis.readInt()];
int x = dis.readInt();
int y = dis.readInt();
for(Tee t : tc.getTees()){
if(t.getId() == id){
t.setDir(dir);
t.setPtDir(ptDir);
t.setX(x);
t.setY(y);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个协议运行之前,坦克仍能正常移动,但是一旦进行了这个操作,那么被push的坦克会被强制移动,这个在其他玩家界面能看到,但是在发出push命令的玩家界面看不到,并且一旦坦克被push之后所进行的所有操作都不会再被上传,无法在其他玩家的界面显示出操作。
下面补充一下这个方法
这个是在坦克类里对push行为进行的反应
@Override
public void actionToTeePushEvent(TeePushEvent teePushEvent) {
this.tc.getExplodes().add(new Explode(teePushEvent.getSource().getX() - 20,
teePushEvent.getSource().getY() - 20, this.tc));//tee自身产生一个爆炸
this.dir=teePushEvent.getSource().getDir();
this.ptDir=teePushEvent.getSource().getDir();
this.threw();
this.dir= Dir.STOP;
this.dir= Dir.STOP;
TeeThrewMsg msg = new TeeThrewMsg(this.id,this.x,this.y,this.dir,this.ptDir,teePushEvent.getSource());
this.tc.getNc().send(msg);
}
这个是用户个人界面的paint方法
public void paint(Graphics g) {
g.drawString("missiles count:" + missiles.size(), 10, 50);
g.drawString("explodes count:" + explodes.size(), 10, 70);
g.drawString("tees count:" + tees.size(), 10, 90);
for(int i = 0; i < missiles.size(); i++) {
Missile m = missiles.get(i);
if (m.hitTee(myTee)) {
/*TeeDeadMsg msg = new TeeDeadMsg(myTee.getId());
nc.send(msg);*/
MissileDeadMsg mmsg = new MissileDeadMsg(m.getTeeId(), m.getId());
nc.send(mmsg);
/* nc.sendClientDisconnectMsg();
gameOverDialog.setVisible(true);*/
}
m.draw(g);
}
for(int i = 0; i < pushes.size(); i++) {
Push p = pushes.get(i);
if(p.hitTee(myTee)){
PushDeadMsg mmsg = new PushDeadMsg(p.getTeeId(), p.getId());
nc.send(mmsg);
}
p.draw(g);
}
for(int i = 0; i < explodes.size(); i++) {
Explode e = explodes.get(i);
e.draw(g);
}
for(int i = 0; i < tees.size(); i++) {
Tee t = tees.get(i);
t.draw(g);
}
if(null != myTee){
myTee.draw(g);
}
}
这个是反应
package client.strategy;
import client.bean.Push;
import client.bean.Tee;
import client.protocol.PushNewMsg;
/**
* Created by lichunxuan on 2021/6/18.
*/
public class NormalPushAction implements PushAction {
@Override
public void pushAction(Tee tee) {
if(!tee.isLive()) return;
int x = tee.getX() + 15 - 5;//确定推动的坐标
int y = tee.getY() + 15 - 5;
Push p = new Push(tee.getId(), x, y, tee.isGood(), tee.getPtDir(), tee.getTc());//产生一个推动
tee.getTc().getPushes().add(p);
PushNewMsg msg = new PushNewMsg(p);
tee.getTc().getNc().send(msg);
}
}
补充一下,在进行发出push类子弹打到人之前,所有用户正常,一旦打到人,被打到的坦克界面:坦克会被推动,但是之后所有操作不能再被其他坦克界面收到,而发出push类子弹的用户做出的操作,能被 被打到的坦克收到。
最后补充可能有用的代码段
package client.event;
import client.bean.Push;
/**
* Created by lichunxuan on 2021/6/18.
*/
public class TeePushEvent {
private Push source;
public TeePushEvent(Push source){
this.source = source;
}
public Push getSource() {
return source;
}
}
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
/**
* Created by lichunxuan on 2021/6/19.
*/
public class Push {
public static final int XSPEED = 20;
public static final int YSPEED = 20;
private static int ID = 10;
private int id;
private TeeClient tc;
private int teeId;
private int x, y;
private Dir dir = Dir.R;
private boolean live = true;
private boolean good;
private static Toolkit tk = Toolkit.getDefaultToolkit();
private static Image[] imgs = null;
private static Map<String, Image> map = new HashMap<>();
static{
imgs = new Image[]{
tk.getImage(client.bean.Push.class.getClassLoader().getResource("client/images/push/m.png")),
tk.getImage(client.bean.Push.class.getClassLoader().getResource("client/images/push/n.png"))
};
map.put("n", imgs[0]);
map.put("m", imgs[1]);
}
public static final int WIDTH = imgs[0].getWidth(null);
public static final int HEIGHT = imgs[0].getHeight(null);
public Push(int teeId, int x, int y, boolean good, Dir dir) {
this.teeId = teeId;
this.x = x;
this.y = y;
this.good = good;
this.dir = dir;
this.id = ID++;
}
public Push(int teeId, int x, int y, boolean good, Dir dir, TeeClient tc) {
this(teeId, x, y, good, dir);
this.tc = tc;
}
public void draw(Graphics g) {
if(!live) {
tc.getPushes().remove(this);
return;
}
g.drawImage(good ? map.get("n") : map.get("m"), x, y, null);
move();
}
private void move() {//每画一次, 子弹的坐标移动一次
switch(dir) {
case L:
x -= XSPEED;
break;
case LU:
x -= XSPEED;
y -= YSPEED;
break;
case U:
y -= YSPEED;
break;
case RU:
x += XSPEED;
y -= YSPEED;
break;
case R:
x += XSPEED;
break;
case RD:
x += XSPEED;
y += YSPEED;
break;
case D:
y += YSPEED;
break;
case LD:
x -= XSPEED;
y += YSPEED;
break;
case STOP:
break;
}
if(x < 0 || y < 0 || x > TeeClient.GAME_WIDTH || y > TeeClient.GAME_HEIGHT) {
live = false;
}
}
public Rectangle getRect() {
return new Rectangle(x, y, imgs[0].getWidth(null), imgs[0].getHeight(null));
}
public boolean hitTee(Tee t) {//子弹击中坦克的方法
if(this.live && t.isLive() && this.good != t.isGood() && this.getRect().intersects(t.getRect())) {
this.live = false;//子弹死亡
t.actionToTeePushEvent(new TeePushEvent(this));//告知观察的坦克被打中了
return true;
}
return false;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setLive(boolean live) {
this.live = live;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Dir getDir() {
return dir;
}
public boolean isGood() {
return good;
}
public int getTeeId() {
return teeId;
}
}
package client.client;
import client.bean.*;
import client.protocol.MissileDeadMsg;
import client.protocol.PushDeadMsg;
import client.protocol.TeeDeadMsg;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
public class TeeClient extends Frame {
public static final int GAME_WIDTH = 800;
public static final int GAME_HEIGHT = 600;
private Image offScreenImage = null;
private Monitor monitor = new Monitor(this);
public Tee myTee;//客户端的坦克
private NetClient nc = new NetClient(this);
private ConDialog dialog = new ConDialog();
private GameOverDialog gameOverDialog = new GameOverDialog();
private UDPPortWrongDialog udpPortWrongDialog = new UDPPortWrongDialog();
private ServerNotStartDialog serverNotStartDialog = new ServerNotStartDialog();
private List<Missile> missiles = new ArrayList<>();//存储游戏中的子弹集合
private List<Push> pushes=new ArrayList<>();//存储推动的集合
private List<Explode> explodes = new ArrayList<>();//爆炸集合
private List<Tee> tees = new ArrayList<>();//坦克集合
@Override
public void paint(Graphics g) {
g.drawString("missiles count:" + missiles.size(), 10, 50);
g.drawString("explodes count:" + explodes.size(), 10, 70);
g.drawString("tees count:" + tees.size(), 10, 90);
for(int i = 0; i < missiles.size(); i++) {
Missile m = missiles.get(i);
if (m.hitTee(myTee)) {
/*TeeDeadMsg msg = new TeeDeadMsg(myTee.getId());
nc.send(msg);*/
MissileDeadMsg mmsg = new MissileDeadMsg(m.getTeeId(), m.getId());
nc.send(mmsg);
/* nc.sendClientDisconnectMsg();
gameOverDialog.setVisible(true);*/
}
m.draw(g);
}
for(int i = 0; i < pushes.size(); i++) {
Push p = pushes.get(i);
if(p.hitTee(myTee)){
PushDeadMsg mmsg = new PushDeadMsg(p.getTeeId(), p.getId());
nc.send(mmsg);
}
p.draw(g);
}
for(int i = 0; i < explodes.size(); i++) {
Explode e = explodes.get(i);
e.draw(g);
}
for(int i = 0; i < tees.size(); i++) {
Tee t = tees.get(i);
t.draw(g);
}
if(null != myTee){
myTee.draw(g);
}
}
@Override
public void update(Graphics g) {
if(offScreenImage == null) {
offScreenImage = this.createImage(800, 600);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.LIGHT_GRAY);
gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
public void launchFrame() {
this.setLocation(400, 300);
this.setSize(GAME_WIDTH, GAME_HEIGHT);
this.setTitle("TeeClient");
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
nc.sendClientDisconnectMsg();//关闭窗口前要向服务器发出注销消息.
System.exit(0);
}
});
this.setResizable(false);
this.setBackground(Color.LIGHT_GRAY);
this.addKeyListener(new Monitor(this));
this.setVisible(true);
new Thread(new PaintThread()).start();
dialog.setVisible(true);
}
public static void main(String[] args) {
TeeClient tc = new TeeClient();
tc.launchFrame();
}
/**
* 重画线程
*/
class PaintThread implements Runnable {
public void run() {
while(true) {
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 游戏开始前连接到服务器的对话框
*/
class ConDialog extends Dialog{
Button b = new Button("connect to server");
TextField tfIP = new TextField("127.0.0.1", 15);//服务器的IP地址
TextField tfTeeName = new TextField("myTee", 8);
public ConDialog() {
super(TeeClient.this, true);
this.setLayout(new FlowLayout());
this.add(new Label("server IP:"));
this.add(tfIP);
this.add(new Label("tee name:"));
this.add(tfTeeName);
this.add(b);
this.setLocation(500, 400);
this.pack();
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String IP = tfIP.getText().trim();
String teeName = tfTeeName.getText().trim();
myTee = new Tee(teeName, 50 + (int)(Math.random() * (GAME_WIDTH - 100)),
50 + (int)(Math.random() * (GAME_HEIGHT - 100)), true, Dir.STOP, TeeClient.this);
nc.connect(IP);
setVisible(false);
}
});
}
}
/**
* 坦克死亡后退出的对话框
*/
class GameOverDialog extends Dialog{
Button b = new Button("exit");
public GameOverDialog() {
super(TeeClient.this, true);
this.setLayout(new FlowLayout());
this.add(new Label("Game Over~"));
this.add(b);
this.setLocation(500, 400);
this.pack();
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
}
}
/**
* UDP端口分配失败后的对话框
*/
class UDPPortWrongDialog extends Dialog{
Button b = new Button("ok");
public UDPPortWrongDialog() {
super(TeeClient.this, true);
this.setLayout(new FlowLayout());
this.add(new Label("something wrong, please connect again"));
this.add(b);
this.setLocation(500, 400);
this.pack();
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
}
}
/**
* 连接服务器失败后的对话框
*/
class ServerNotStartDialog extends Dialog{
Button b = new Button("ok");
public ServerNotStartDialog() {
super(TeeClient.this, true);
this.setLayout(new FlowLayout());
this.add(new Label("The server has not been opened yet..."));
this.add(b);
this.setLocation(500, 400);
this.pack();
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
}
}
public void gameOver(){
this.gameOverDialog.setVisible(true);
}
public List<Missile> getMissiles() {
return missiles;
}
public List<Explode> getExplodes() {
return explodes;
}
public List<Tee> getTees() {
return tees;
}
public Tee getMyTee() {
return myTee;
}
public NetClient getNc() {
return nc;
}
public UDPPortWrongDialog getUdpPortWrongDialog() {
return udpPortWrongDialog;
}
public ServerNotStartDialog getServerNotStartDialog() {
return serverNotStartDialog;
}
public List<Push> getPushes() {
return pushes;
}
}
package client.bean;
import client.client.TeeClient;
import client.event.TeeHitEvent;
import client.event.TeeHitListener;
import client.event.TeePushEvent;
import client.event.TeePushListener;
import client.protocol.TeeDeadMsg;
import client.protocol.TeeMoveMsg;
import client.protocol.TeeReduceBloodMsg;
import client.protocol.TeeThrewMsg;
import client.strategy.*;
import client.strategy.Push;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.nio.channels.NonWritableChannelException;
import java.util.HashMap;
import java.util.Map;
/**
* Created by lichunxuan on 2021/5/11.
*/
public class Tee implements TeeHitListener,TeePushListener,Fire,Push {
int id;
public static final int XSPEED = 5;
public static final int YSPEED = 5;
private String name;
private boolean good;
int x;
int y;
private boolean live = true;
private TeeClient tc;
Dir dir = Dir.STOP;
Dir ptDir = Dir.D;
private int blood = 100;
private FireAction fireAction = new NormalFireAction();//可以开火
private PushAction pushAction = new NormalPushAction();//可以推动
private static Toolkit tk = Toolkit.getDefaultToolkit();
private static Image[] imgs = null;
private static Map<String, Image> map = new HashMap<>();
static{
imgs = new Image[]{//加载两方阵营的图片
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tD.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tL.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tLD.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tLU.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tR.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tRD.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tRU.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/tU.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eD.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eL.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eLD.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eLU.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eR.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eRD.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eRU.png")),
tk.getImage(Tee.class.getClassLoader().getResource("client/images/tee/eU.png")),
};
map.put("tD", imgs[0]);
map.put("tL", imgs[1]);
map.put("tLD", imgs[2]);
map.put("tLU", imgs[3]);
map.put("tR", imgs[4]);
map.put("tRD", imgs[5]);
map.put("tRU", imgs[6]);
map.put("tU", imgs[7]);
map.put("eD", imgs[8]);
map.put("eL", imgs[9]);
map.put("eLD", imgs[10]);
map.put("eLU", imgs[11]);
map.put("eR", imgs[12]);
map.put("eRD", imgs[13]);
map.put("eRU", imgs[14]);
map.put("eU", imgs[15]);
}
public static final int WIDTH = imgs[0].getWidth(null);
public static final int HEIGHT = imgs[0].getHeight(null);
public Tee(int x, int y, boolean good, String name) {
this.x = x;
this.y = y;
this.good = good;
this.name = name;
}
public Tee(String name, int x, int y, boolean good, Dir dir, TeeClient tc) {
this(x, y, good, name);
this.dir = dir;
this.tc = tc;
}
/**
* 根据tee阵营画出图片
* @param g
*/
public void draw(Graphics g) {
if(!live) {
if(!good) {
tc.getTees().remove(this);
}
return;
}
switch(ptDir) {
case L:
g.drawImage(good ? map.get("tL") : map.get("eL"), x, y, null);
break;
case LU:
g.drawImage(good ? map.get("tLU") : map.get("eLU"), x, y, null);
break;
case U:
g.drawImage(good ? map.get("tU") : map.get("eU"), x, y, null);
break;
case RU:
g.drawImage(good ? map.get("tRU") : map.get("eRU"), x, y, null);
break;
case R:
g.drawImage(good ? map.get("tR") : map.get("eR"), x, y, null);
break;
case RD:
g.drawImage(good ? map.get("tRD") : map.get("eRD"), x, y, null);
break;
case D:
g.drawImage(good ? map.get("tD") : map.get("eD"), x, y, null);
break;
case LD:
g.drawImage(good ? map.get("tLD") : map.get("eLD"), x, y, null);
break;
}
move();
}
/**
* 根据teept的方向进行移动
*/
private void move() {
switch(dir) {
case L:
x -= XSPEED;
break;
case LU:
x -= XSPEED;
y -= YSPEED;
break;
case U:
y -= YSPEED;
break;
case RU:
x += XSPEED;
y -= YSPEED;
break;
case R:
x += XSPEED;
break;
case RD:
x += XSPEED;
y += YSPEED;
break;
case D:
y += YSPEED;
break;
case LD:
x -= XSPEED;
y += YSPEED;
break;
case STOP:
break;
default:
}
if(dir != Dir.STOP) {
ptDir = dir;
}
if(x < 0) live=false;
if(y < 30) live=false;
if(x + WIDTH > TeeClient.GAME_WIDTH) live=false;
if(y + HEIGHT > TeeClient.GAME_HEIGHT) live=false;
if(!live){
TeeDeadMsg msg = new TeeDeadMsg(this.id);//向其他客户端转发坦克死亡的消息
this.tc.getNc().send(msg);
this.tc.getNc().sendClientDisconnectMsg();//和服务器断开连接
this.tc.gameOver();
}
}
public void threw() {
switch(dir) {
case L:
x -= XSPEED*15;
break;
case LU:
x -= XSPEED*15;
y -= YSPEED*15;
break;
case U:
y -= YSPEED*15;
break;
case RU:
x += XSPEED*15;
y -= YSPEED*15;
break;
case R:
x += XSPEED*15;
break;
case RD:
x += XSPEED*15;
y += YSPEED*15;
break;
case D:
y += YSPEED*15;
break;
case LD:
x -= XSPEED*15;
y += YSPEED*15;
break;
default:
}
ptDir = dir;
}
@Override
public void fire() {//发出一颗炮弹的方法
fireAction.fireAction(this);
}
@Override
public void push(){pushAction.pushAction(this);}
@Override
public void actionToTeeHitEvent(TeeHitEvent teeHitEvent) {
this.tc.getExplodes().add(new Explode(teeHitEvent.getSource().getX() - 20,
teeHitEvent.getSource().getY() - 20, this.tc));//tee自身产生一个爆炸
if(this.blood == 1){//坦克每次扣1滴血, 如果只剩下1滴了, 那么就标记为死亡.这个血量设定是为了不让他死
this.live = false;
TeeDeadMsg msg = new TeeDeadMsg(this.id);//向其他客户端转发坦克死亡的消息
this.tc.getNc().send(msg);
this.tc.getNc().sendClientDisconnectMsg();//和服务器断开连接
this.tc.gameOver();
return;
}
this.blood -= 1;//血量减少1并通知其他客户端本坦克血量减少1.
TeeReduceBloodMsg msg = new TeeReduceBloodMsg(this.id, teeHitEvent.getSource());
this.tc.getNc().send(msg);
}
// public boolean threwTee(Tee t) {//两个tee碰撞的方法
// if(t.dir!=Dir.STOP && this.live && t.isLive() && this.good != t.isGood() && this.getRect().intersects(t.getRect())) {
// t.actionToTeePushEvent(new TeePushEvent(this));//告知观察的tee相撞了
// return true;
// }
// return false;
// }
@Override
public void actionToTeePushEvent(TeePushEvent teePushEvent) {
this.tc.getExplodes().add(new Explode(teePushEvent.getSource().getX() - 20,
teePushEvent.getSource().getY() - 20, this.tc));//tee自身产生一个爆炸
this.dir=teePushEvent.getSource().getDir();
this.ptDir=teePushEvent.getSource().getDir();
this.threw();
this.dir= Dir.STOP;
this.dir= Dir.STOP;
TeeThrewMsg msg = new TeeThrewMsg(this.id,this.x,this.y,this.dir,this.ptDir,teePushEvent.getSource());
this.tc.getNc().send(msg);
}
public Rectangle getRect() {
return new Rectangle(x, y, imgs[0].getWidth(null), imgs[0].getHeight(null));
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public boolean isGood() {
return good;
}
public void setGood(boolean good) {
this.good = good;
}
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;
}
public Dir getPtDir() {
return ptDir;
}
public void setPtDir(Dir ptDir) {
this.ptDir = ptDir;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public String getName() {
return name;
}
public TeeClient getTc() {
return tc;
}
public void setTc(TeeClient tc) {
this.tc = tc;
}
}
package client.client;
import client.protocol.*;
import server.TeeServer;
import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* 网络方法接口
*/
public class NetClient {
private TeeClient tc;
private int UDP_PORT;//客户端的UDP端口号
private String serverIP;//服务器IP地址
private int serverUDPPort;//服务器转发客户但UDP包的UDP端口
private int TEE_DEAD_UDP_PORT;//服务器监听坦克死亡的UDP端口
private DatagramSocket ds = null;//客户端的UDP套接字
public void setUDP_PORT(int UDP_PORT) {
this.UDP_PORT = UDP_PORT;
}
public NetClient(TeeClient tc){
this.tc = tc;
try {
this.UDP_PORT = getRandomUDPPort();
}catch (Exception e){
tc.getUdpPortWrongDialog().setVisible(true);//弹窗提示
System.exit(0);//如果选择到了重复的UDP端口号就退出客户端重新选择.
}
}
/**
* 与服务器进行TCP连接
* @param ip server IP
*/
public void connect(String ip){
serverIP = ip;
Socket s = null;
try {
ds = new DatagramSocket(UDP_PORT);//创建UDP套接字
try {
s = new Socket(ip, TeeServer.TCP_PORT);//创建TCP套接字
}catch (Exception e1){
tc.getServerNotStartDialog().setVisible(true);
}
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeInt(UDP_PORT);//向服务器发送自己的UDP端口号
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();//获得自己的id号
this.serverUDPPort = dis.readInt();//获得服务器转发客户端消息的UDP端口号
this.TEE_DEAD_UDP_PORT = dis.readInt();//获得服务器监听坦克死亡的UDP端口
tc.getMyTee().setId(id);//设置坦克的id号
tc.getMyTee().setGood((id & 1) == 0 ? true : false);//根据坦克的id号分配阵营
System.out.println("connect to server successfully...");
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if(s != null) s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
new Thread(new UDPThread()).start();//开启客户端UDP线程, 向服务器发送或接收游戏数据
TeeNewMsg msg = new TeeNewMsg(tc.getMyTee());//创建坦克出生的消息
send(msg);
}
/**
* 客户端随机获取UDP端口号
* @return
*/
private int getRandomUDPPort(){
return 55558 + (int)(Math.random() * 9000);
}
public void send(Msg msg){
msg.send(ds, serverIP, serverUDPPort);
}
public class UDPThread implements Runnable{
byte[] buf = new byte[1024];
@Override
public void run() {
while(null != ds){
DatagramPacket dp = new DatagramPacket(buf, buf.length);
try{
ds.receive(dp);
parse(dp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void parse(DatagramPacket dp) {
ByteArrayInputStream bais = new ByteArrayInputStream(buf, 0, dp.getLength());
DataInputStream dis = new DataInputStream(bais);
int msgType = 0;
try {
msgType = dis.readInt();//获得消息类型
} catch (IOException e) {
e.printStackTrace();
}
Msg msg = null;
switch (msgType){//根据消息的类型调用对应消息的解析方法
case Msg.TEE_NEW_MSG :
msg = new TeeNewMsg(tc);
msg.parse(dis);
break;
case Msg.TEE_MOVE_MSG :
msg = new TeeMoveMsg(tc);
msg.parse(dis);
break;
case Msg.MISSILE_NEW_MSG:
msg = new MissileNewMsg(tc);
msg.parse(dis);
break;
case Msg.TEE_DEAD_MSG:
msg = new TeeDeadMsg(tc);
msg.parse(dis);
break;
case Msg.MISSILE_DEAD_MSG:
msg = new MissileDeadMsg(tc);
msg.parse(dis);
break;
case Msg.TEE_ALREADY_EXIST_MSG:
msg = new TeeAlreadyExistMsg(tc);
msg.parse(dis);
break;
case Msg.TEE_REDUCE_BLOOD_MSG:
msg = new TeeReduceBloodMsg(tc);
msg.parse(dis);
break;
case Msg.PUSH_NEW_MSG:
msg = new PushNewMsg(tc);
msg.parse(dis);
break;
case Msg.PUSH_DEAD_MSG:
msg = new PushDeadMsg(tc);
msg.parse(dis);
break;
case Msg.TEE_THREW_MSG:
msg = new TeeThrewMsg(tc);
msg.parse(dis);
break;
}
}
}
public void sendClientDisconnectMsg(){
ByteArrayOutputStream baos = new ByteArrayOutputStream(88);
DataOutputStream dos = new DataOutputStream(baos);
try {
dos.writeInt(UDP_PORT);//发送客户端的UDP端口号, 从服务器Client集合中注销
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != dos){
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != baos){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
byte[] buf = baos.toByteArray();
try{
DatagramPacket dp = new DatagramPacket(buf, buf.length, new InetSocketAddress(serverIP, TEE_DEAD_UDP_PORT));
ds.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
}
}