You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
5.5 KiB
297 lines
5.5 KiB
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/aceld/zinx/zinx_app_demo/mmo_game/pb"
|
|
"github.com/golang/protobuf/proto"
|
|
)
|
|
|
|
type Message struct {
|
|
Len uint32
|
|
MsgID uint32
|
|
Data []byte
|
|
}
|
|
|
|
type TcpClient struct {
|
|
conn net.Conn
|
|
X float32
|
|
Y float32
|
|
Z float32
|
|
V float32
|
|
PID int32
|
|
isOnline chan bool
|
|
}
|
|
|
|
func (this *TcpClient) Unpack(headdata []byte) (head *Message, err error) {
|
|
headBuf := bytes.NewReader(headdata)
|
|
|
|
head = &Message{}
|
|
|
|
// 读取Len
|
|
if err = binary.Read(headBuf, binary.LittleEndian, &head.Len); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 读取MsgID
|
|
if err = binary.Read(headBuf, binary.LittleEndian, &head.MsgID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 封包太大
|
|
//if head.Len > MaxPacketSize {
|
|
// return nil, packageTooBig
|
|
//}
|
|
|
|
return head, nil
|
|
}
|
|
|
|
func (this *TcpClient) Pack(msgID uint32, dataBytes []byte) (out []byte, err error) {
|
|
outbuff := bytes.NewBuffer([]byte{})
|
|
// 写Len
|
|
if err = binary.Write(outbuff, binary.LittleEndian, uint32(len(dataBytes))); err != nil {
|
|
return
|
|
}
|
|
// 写MsgID
|
|
if err = binary.Write(outbuff, binary.LittleEndian, msgID); err != nil {
|
|
return
|
|
}
|
|
|
|
//all pkg data
|
|
if err = binary.Write(outbuff, binary.LittleEndian, dataBytes); err != nil {
|
|
return
|
|
}
|
|
|
|
out = outbuff.Bytes()
|
|
|
|
return
|
|
}
|
|
|
|
func (this *TcpClient) SendMsg(msgID uint32, data proto.Message) {
|
|
|
|
// 进行编码
|
|
binaryData, err := proto.Marshal(data)
|
|
if err != nil {
|
|
fmt.Println(fmt.Sprintf("marshaling error: %s", err))
|
|
return
|
|
}
|
|
|
|
sendData, err := this.Pack(msgID, binaryData)
|
|
if err == nil {
|
|
_, _ = this.conn.Write(sendData)
|
|
} else {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (this *TcpClient) AIRobotAction() {
|
|
//聊天或者移动
|
|
|
|
//随机获得动作
|
|
tp := rand.Intn(2)
|
|
if tp == 0 {
|
|
content := fmt.Sprintf("hello 我是player %d, 你是谁?", this.PID)
|
|
msg := &pb.Talk{
|
|
Content: content,
|
|
}
|
|
this.SendMsg(2, msg)
|
|
} else {
|
|
//移动
|
|
x := this.X
|
|
z := this.Z
|
|
|
|
randPos := rand.Intn(2)
|
|
if randPos == 0 {
|
|
x -= float32(rand.Intn(10))
|
|
z -= float32(rand.Intn(10))
|
|
} else {
|
|
x += float32(rand.Intn(10))
|
|
z += float32(rand.Intn(10))
|
|
}
|
|
|
|
//纠正坐标位置
|
|
if x > 410 {
|
|
x = 410
|
|
} else if x < 85 {
|
|
x = 85
|
|
}
|
|
|
|
if z > 400 {
|
|
z = 400
|
|
} else if z < 75 {
|
|
z = 75
|
|
}
|
|
|
|
//移动方向角度
|
|
randV := rand.Intn(2)
|
|
v := this.V
|
|
if randV == 0 {
|
|
v = 25
|
|
} else {
|
|
v = 335
|
|
}
|
|
//封装Position消息
|
|
msg := &pb.Position{
|
|
X: x,
|
|
Y: this.Y,
|
|
Z: z,
|
|
V: v,
|
|
}
|
|
|
|
fmt.Println(fmt.Sprintf("player ID: %d. Walking...", this.PID))
|
|
//发送移动MsgID:3的指令
|
|
this.SendMsg(3, msg)
|
|
}
|
|
}
|
|
|
|
/*
|
|
处理一个回执业务
|
|
*/
|
|
func (this *TcpClient) DoMsg(msg *Message) {
|
|
//处理消息
|
|
//fmt.Println(fmt.Sprintf("msg ID :%d, data len: %d", msg.MsgID, msg.Len))
|
|
if msg.MsgID == 1 {
|
|
//服务器回执给客户端 分配ID
|
|
|
|
//解析proto
|
|
syncpID := &pb.SyncPID{}
|
|
_ = proto.Unmarshal(msg.Data, syncpID)
|
|
|
|
//给当前客户端ID进行赋值
|
|
this.PID = syncpID.PID
|
|
} else if msg.MsgID == 200 {
|
|
//服务器回执客户端广播数据
|
|
|
|
//解析proto
|
|
bdata := &pb.BroadCast{}
|
|
_ = proto.Unmarshal(msg.Data, bdata)
|
|
|
|
//初次玩家上线 广播位置消息
|
|
if bdata.Tp == 2 && bdata.PID == this.PID {
|
|
//本人
|
|
//更新客户端坐标
|
|
this.X = bdata.GetP().X
|
|
this.Y = bdata.GetP().Y
|
|
this.Z = bdata.GetP().Z
|
|
this.V = bdata.GetP().V
|
|
fmt.Println(fmt.Sprintf("player ID: %d online.. at(%f,%f,%f,%f)", bdata.PID, this.X, this.Y, this.Z, this.V))
|
|
|
|
//玩家已经成功上线
|
|
this.isOnline <- true
|
|
|
|
} else if bdata.Tp == 1 {
|
|
fmt.Println(fmt.Sprintf("世界聊天,玩家%d说的话是: %s", bdata.PID, bdata.GetContent()))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (this *TcpClient) Start() {
|
|
go func() {
|
|
for {
|
|
//读取服务端发来的数据 ==》 SyncPID
|
|
//1.读取8字节
|
|
//第一次读取,读取数据头
|
|
headData := make([]byte, 8)
|
|
|
|
if _, err := io.ReadFull(this.conn, headData); err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
pkgHead, err := this.Unpack(headData)
|
|
if err != nil {
|
|
return
|
|
}
|
|
//data
|
|
if pkgHead.Len > 0 {
|
|
pkgHead.Data = make([]byte, pkgHead.Len)
|
|
if _, err := io.ReadFull(this.conn, pkgHead.Data); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
//处理服务器回执业务
|
|
this.DoMsg(pkgHead)
|
|
}
|
|
}()
|
|
|
|
// 10s后,断开连接
|
|
for {
|
|
select {
|
|
case <-this.isOnline:
|
|
go func() {
|
|
for {
|
|
this.AIRobotAction()
|
|
time.Sleep(time.Second)
|
|
}
|
|
}()
|
|
case <-time.After(time.Second * 10):
|
|
_ = this.conn.Close()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func NewTcpClient(ip string, port int) *TcpClient {
|
|
addrStr := fmt.Sprintf("%s:%d", ip, port)
|
|
conn, err := net.Dial("tcp", addrStr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
client := &TcpClient{
|
|
conn: conn,
|
|
PID: 0,
|
|
X: 0,
|
|
Y: 0,
|
|
Z: 0,
|
|
V: 0,
|
|
isOnline: make(chan bool),
|
|
}
|
|
return client
|
|
}
|
|
|
|
func main() {
|
|
// 开启一个waitgroup,同时运行3个goroutine
|
|
|
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
var wg sync.WaitGroup
|
|
wg.Add(3)
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
for i := 0; i < 10; i++ {
|
|
client := NewTcpClient("127.0.0.1", 8999)
|
|
client.Start()
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
for i := 0; i < 10; i++ {
|
|
client := NewTcpClient("127.0.0.1", 8999)
|
|
client.Start()
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
for i := 0; i < 10; i++ {
|
|
client := NewTcpClient("127.0.0.1", 8999)
|
|
client.Start()
|
|
}
|
|
}()
|
|
|
|
fmt.Println("AI robot start")
|
|
wg.Wait()
|
|
fmt.Println("AI robot exit")
|
|
}
|