132 lines
3.4 KiB
Go
132 lines
3.4 KiB
Go
package instance
|
|
|
|
import (
|
|
"common/discover"
|
|
"common/log"
|
|
"common/proto/sc/sc_pb"
|
|
"common/proto/ss/grpc_pb"
|
|
"common/utils"
|
|
"context"
|
|
"fmt"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/protobuf/proto"
|
|
"runtime/debug"
|
|
"scene/internal/grpc_server/stream_client"
|
|
"scene/internal/npc"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Instance 场景类
|
|
type Instance struct {
|
|
wg sync.WaitGroup
|
|
players map[int64]*npc.PlayerNode // 存储所有玩家节点 [usn]
|
|
ctx context.Context // 停止指令
|
|
cancel context.CancelFunc // 停止函数
|
|
logger *zap.SugaredLogger // 日志
|
|
lastLogicTime int64 // 上次逻辑帧执行时间(毫秒时间戳)
|
|
|
|
SID int64 // 服务ID
|
|
InstanceID int32 // 副本ID
|
|
UniqueNo int64 // 唯一编号
|
|
EventIn chan proto.Message // 消息入口
|
|
}
|
|
|
|
// NewScene 初始化场景
|
|
func NewScene(sid int64, instanceID int32) *Instance {
|
|
s := &Instance{
|
|
players: make(map[int64]*npc.PlayerNode),
|
|
SID: sid,
|
|
InstanceID: instanceID,
|
|
UniqueNo: utils.SnowflakeInstance().Generate().Int64(),
|
|
EventIn: make(chan proto.Message, 1024),
|
|
}
|
|
s.logger = log.GetLogger().Named(fmt.Sprintf("instance %v:%v", s.InstanceID, s.UniqueNo))
|
|
s.ctx, s.cancel = context.WithCancel(context.Background())
|
|
return s
|
|
}
|
|
|
|
func (i *Instance) Start(ttl int64) {
|
|
i.wg.Add(1)
|
|
go func() {
|
|
defer i.wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
i.logger.Infof("instance err: %v", err)
|
|
debug.PrintStack()
|
|
}
|
|
}()
|
|
Mgr.Add(i.UniqueNo, i)
|
|
defer func() {
|
|
i.logger.Infof("instance destroy")
|
|
discover.UnRegisterInstance(i.UniqueNo)
|
|
close(i.EventIn)
|
|
Mgr.Delete(i.UniqueNo)
|
|
}()
|
|
|
|
if err := discover.RegisterInstance(i.SID, i.InstanceID, i.UniqueNo, ttl); err != nil {
|
|
i.logger.Errorf("register discover error")
|
|
return
|
|
}
|
|
// 场景心跳
|
|
tick := time.NewTicker(50 * time.Millisecond)
|
|
i.lastLogicTime = time.Now().UnixMilli()
|
|
for {
|
|
select {
|
|
case <-i.ctx.Done():
|
|
return
|
|
case <-tick.C:
|
|
now := time.Now().UnixMilli()
|
|
i.onLogic(now - i.lastLogicTime)
|
|
i.lastLogicTime = now
|
|
case e := <-i.EventIn:
|
|
i.onEvent(e)
|
|
}
|
|
}
|
|
}()
|
|
i.logger.Infof("new scene start")
|
|
}
|
|
|
|
// 网络帧,将玩家的操作储存起来,到逻辑帧再处理玩家操作
|
|
func (i *Instance) onEvent(e proto.Message) {
|
|
switch v := e.(type) {
|
|
case *grpc_pb.EnterReq:
|
|
i.players[v.USN] = npc.NewPlayerNode(v.GatewaySID, v.USN)
|
|
case *grpc_pb.ActionReq:
|
|
if node, ok := i.players[v.USN]; ok {
|
|
node.AddAction(v)
|
|
}
|
|
case *grpc_pb.LeaveReq:
|
|
delete(i.players, v.USN)
|
|
}
|
|
}
|
|
|
|
// 逻辑帧
|
|
func (i *Instance) onLogic(delta int64) {
|
|
positionUpdate := make([]*sc_pb.PositionInfo, 0)
|
|
sid := int64(0)
|
|
// 处理玩家指令
|
|
for _, node := range i.players {
|
|
if node.LogicAction(delta) {
|
|
positionUpdate = append(positionUpdate, &sc_pb.PositionInfo{
|
|
USN: node.USN,
|
|
X: int32(node.Position[0] * 100),
|
|
Y: int32(node.Position[1] * 100),
|
|
})
|
|
sid = node.GatewaySID
|
|
}
|
|
}
|
|
if len(positionUpdate) > 0 {
|
|
payload, _ := proto.Marshal(&sc_pb.S2C_Position{
|
|
Info: positionUpdate,
|
|
})
|
|
if err := stream_client.SendMessageToGateway(sid, stream_client.FunToClient, &grpc_pb.ToClientReq{
|
|
USN: -1,
|
|
MessageID: int32(sc_pb.MessageID_MESSAGE_ID_POSITION),
|
|
Payload: payload,
|
|
}); err != nil {
|
|
i.logger.Errorf("send action err: %v", err)
|
|
}
|
|
}
|
|
}
|