package baseroom import ( "fmt" "game/common/proto/pb" "game/common/utils" "github.com/fox/fox/ipb" "github.com/fox/fox/log" "github.com/fox/fox/processor" "github.com/fox/fox/service" "github.com/fox/fox/timer" "github.com/golang/protobuf/proto" "time" ) type PlayerType int const ( PT_User PlayerType = 0 PT_Robot PlayerType = 1 PT_All PlayerType = 2 ) // 房间基类,不要直接用于业务 type BaseRoom[Seat ISeat] struct { id int roomType int // 房间配置id 初级,中级,高级 gameId int // 玩法配置id color玩法id gameNo string seats []Seat processor *processor.Processor timeProcessor *processor.TimerProcessor timeTypes map[timer.ITimeType]uint32 srv service.IService } func NewBaseRoom[Seat ISeat](id, roomType, gameId, seatNum int, srv service.IService) (*BaseRoom[Seat], pb.ErrCode) { room := &BaseRoom[Seat]{ id: id, roomType: roomType, gameId: gameId, gameNo: "", seats: make([]Seat, seatNum), timeTypes: make(map[timer.ITimeType]uint32), srv: srv, processor: processor.NewProcessor(), timeProcessor: processor.NewTimerProcessor(), } return room, pb.ErrCode_OK } func (r *BaseRoom[Seat]) Id() int { return r.id } func (r *BaseRoom[Seat]) RoomType() int { return r.roomType } func (r *BaseRoom[Seat]) GameId() int { return r.gameId } func (r *BaseRoom[Seat]) GetService() service.IService { return r.srv } // 入座玩家数量 func (r *BaseRoom[Seat]) SeatPlayerNum(playerType PlayerType) int { num := 0 for _, seat := range r.seats { if !seat.Empty() { switch playerType { case PT_All: num++ case PT_User: if !seat.Player().IsRobot() { num++ } case PT_Robot: if seat.Player().IsRobot() { num++ } } } } return num } // 返回是否有空座位及空座位编号 func (r *BaseRoom[Seat]) FindEmptySeat() (bool, Seat) { for _, seat := range r.seats { if seat.Empty() { return true, seat } } return false, utils.TValue[Seat]{}.V } // 查找玩家所在的座位 func (r *BaseRoom[Seat]) FindSeat(uid int64) (bool, Seat) { for _, seat := range r.seats { if !seat.Empty() && seat.Player().Id() == uid { return true, seat } } return false, utils.TValue[Seat]{}.V } // 查找玩家所在的座位 func (r *BaseRoom[Seat]) GetSeat(seatNo int) (bool, Seat) { if seatNo < 0 || seatNo >= len(r.seats) { return false, utils.TValue[Seat]{}.V } return true, r.seats[seatNo] } // 站起时player为nil,坐下时player非nil func (r *BaseRoom[Seat]) SitDownOrStandup(player IPlayer, seat int) bool { if seat < 0 || seat >= len(r.seats) { log.Error(r.SeatLog(r.seats[seat], "out of range")) return false } if player != nil && r.seats[seat].Player() != nil { log.Error(r.SeatLog(r.seats[seat], "is already here")) return false } r.seats[seat].SetPlayer(player) return true } func (r *BaseRoom[Seat]) RangeSeat(proc func(Seat) bool) { for _, seat := range r.seats { if !seat.Empty() && !proc(seat) { return } } } func (r *BaseRoom[Seat]) FilterSeat(proc func(Seat) bool) []Seat { seats := make([]Seat, 0) for _, seat := range r.seats { if !seat.Empty() && proc(seat) { seats = append(seats, seat) } } return seats } func (r *BaseRoom[Seat]) DebugSendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) { log.Debug(r.UserLog(user.Id(), "send msg:%v %v", msgId, msg.String())) } func (r *BaseRoom[Seat]) SendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) { r.DebugSendMsg(user, msgId, msg) if user.Robot() != nil { user.Robot().OnMessage(msgId, msg) } else { iMsg := ipb.MakeMsgEx(r.srv.Name(), 0, user.Id(), int32(msgId), msg) _ = r.srv.Send(user.GateTopicName(), iMsg) } } func (r *BaseRoom[Seat]) Broadcast(msgId pb.MsgId, msg proto.Message, exclude ...IPlayer) { for _, seat := range r.seats { if !seat.Empty() { exist := false for _, excludePlayer := range exclude { if excludePlayer.Id() == seat.Player().Id() { exist = true break } } if !exist { r.SendMsg(seat.Player(), msgId, msg) } } } } func (r *BaseRoom[Seat]) NewTimer(timerType timer.ITimeType, duration time.Duration, args ...any) { if _, ok := r.timeTypes[timerType]; ok { log.Error(r.Log("timer type:%v is exist.can not new timer", timerType.String())) // if timerType == TtPlayerAct { // log.Debug(r.Log(log.StackTrace())) // } return } tid := r.srv.NewTimer(duration+time.Duration(10)*time.Millisecond, func() { if err := r.timeProcessor.Dispatch(timerType, args...); err != nil { log.ErrorF(r.Log("timer dispatch err:%v", err)) } }, true, r.Log("start type:%v timer", timerType.String())) r.timeTypes[timerType] = tid // log.Debug(r.Log("start type:%v timer", timerType.String())) } func (r *BaseRoom[Seat]) CancelTimer(timerType timer.ITimeType) { if tid, ok := r.timeTypes[timerType]; ok { r.srv.CancelTimer(tid) delete(r.timeTypes, timerType) // log.Debug(r.Log("stop type:%v timer, rest timer:%+v", timerType.String(), r.timeTypes)) } } func (r *BaseRoom[Seat]) CancelAllTimer() { for _, tid := range r.timeTypes { r.srv.CancelTimer(tid) // log.Debug(r.Log("start type:%v timer", timerType.String())) } r.timeTypes = make(map[timer.ITimeType]uint32) } func (r *BaseRoom[Seat]) Log(format string, a ...any) string { head := fmt.Sprintf("room:%v type:%v ", r.id, r.roomType) return head + fmt.Sprintf(format, a...) } func (r *BaseRoom[Seat]) SeatLog(seat Seat, format string, a ...any) string { head := "" if seat.Player() != nil { head = fmt.Sprintf("room:%v type:%v seat:%v user:%v robot:%v ", r.id, r.roomType, seat.No(), seat.Player().Id(), seat.Player().Robot() != nil) } else { head = fmt.Sprintf("room:%v type:%v seat:%v ", r.id, r.roomType, seat.No()) } return head + fmt.Sprintf(format, a...) } func (r *BaseRoom[Seat]) UserLog(uid int64, format string, a ...any) string { var seat Seat exist := false for _, st := range r.seats { if !st.Empty() && st.Player().Id() == uid { seat = st exist = true break } } if exist { return r.SeatLog(seat, format, a...) } else { return r.Log(format, a...) } } func (r *BaseRoom[Seat]) Unmarshal(cmd int32, data []byte) (any, error) { return r.processor.Unmarshal(cmd, data) } func (r *BaseRoom[Seat]) Dispatch(user IPlayer, cmd int32, params ...any) error { inp := make([]any, len(params)+1) inp = append(inp, user) inp = append(inp, params...) return r.processor.Dispatch(cmd, inp...) } func (r *BaseRoom[Seat]) RegisterMessages(metas processor.RegisterMetas) { r.processor.RegisterMessages(metas) } // 注册时间事件及处理 func (r *BaseRoom[Seat]) RegisterTimerMessages(metas processor.RegisterTimerMetas) { r.timeProcessor.RegisterMessages(metas) } // 初始化房间 func (r *BaseRoom[Seat]) OnInit() { }