Files
service-scene/internal/instance/instance.go

132 lines
3.6 KiB
Go

package instance
import (
"context"
"fmt"
"git.hlsq.asia/mmorpg/service-common/discover"
"git.hlsq.asia/mmorpg/service-common/log"
"git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb"
"git.hlsq.asia/mmorpg/service-common/proto/ss/ss_pb"
"git.hlsq.asia/mmorpg/service-common/utils"
"git.hlsq.asia/mmorpg/service-scene/internal/grpc_server/stream_client"
"git.hlsq.asia/mmorpg/service-scene/internal/npc"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"runtime/debug"
"sync"
"time"
)
// Instance 场景类
type Instance struct {
wg sync.WaitGroup
players map[string]*npc.PlayerNode // 存储所有玩家节点 [usn]
ctx context.Context // 停止指令
cancel context.CancelFunc // 停止函数
logger *zap.SugaredLogger // 日志
lastLogicTime int64 // 上次逻辑帧执行时间(毫秒时间戳)
SID string // 服务ID
InstanceID int32 // 副本ID
UniqueNo string // 唯一编号
EventIn chan proto.Message // 消息入口
}
// NewScene 初始化场景
func NewScene(sid string, instanceID int32) *Instance {
s := &Instance{
players: make(map[string]*npc.PlayerNode),
SID: sid,
InstanceID: instanceID,
UniqueNo: utils.SnowflakeInstance().Generate().String(),
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([]*ss_pb.PositionInfo, 0)
sid := ""
// 处理玩家指令
for _, node := range i.players {
if node.LogicAction(delta) {
positionUpdate = append(positionUpdate, &ss_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(&ss_pb.S2C_Position{
Info: positionUpdate,
})
if err := stream_client.SendMessageToGateway(sid, stream_client.FunToClient, &grpc_pb.ToClientReq{
USN: "",
MessageID: int32(ss_pb.MessageID_MESSAGE_ID_POSITION),
Payload: payload,
}); err != nil {
i.logger.Errorf("send action err: %v", err)
}
}
}