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/grpc_server/stream_client" "scene/npc" "sync" "time" ) // Instance 场景类 type Instance struct { wg sync.WaitGroup players map[int]*npc.PlayerNode // 存储所有玩家节点 [uid] ctx context.Context // 停止指令 cancel context.CancelFunc // 停止函数 logger *zap.SugaredLogger // 日志 SID int64 // 服务ID InstanceID int // 副本ID UniqueNo int64 // 唯一编号 EventIn chan proto.Message // 消息入口 } // NewScene 初始化场景 func NewScene(sid int64, instanceID int) *Instance { s := &Instance{ players: make(map[int]*npc.PlayerNode), SID: sid, InstanceID: instanceID, UniqueNo: utils.SnowflakeInstance().Generate().Int64(), EventIn: make(chan proto.Message), } 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) for { select { case <-i.ctx.Done(): return case <-tick.C: i.onLogic() 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[int(v.UID)] = npc.NewPlayerNode(v.GatewaySID, int(v.UID)) case *grpc_pb.ActionReq: if node, ok := i.players[int(v.UID)]; ok { node.AddAction(v) } } } // 逻辑帧 func (i *Instance) onLogic() { positionUpdate := make([]*sc_pb.PositionInfo, 0) sid := int64(0) // 优先处理移动指令 for _, node := range i.players { if node.LogicMove() { positionUpdate = append(positionUpdate, &sc_pb.PositionInfo{ UID: int32(node.UID), X: node.Position[0], Y: node.Position[1], }) 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{ UID: -1, MessageID: int32(sc_pb.MessageID_MESSAGE_ID_POSITION), Payload: payload, }); err != nil { i.logger.Errorf("send action err: %v", err) } } }