package game import ( "fmt" "server/call" "server/common" "server/config" "server/db" "server/natsClient" "server/pb" "server/util" "sync" "time" "github.com/gogo/protobuf/proto" "github.com/liangdas/mqant-modules/room" "github.com/liangdas/mqant/gate" "github.com/liangdas/mqant/log" timewheel "github.com/liangdas/mqant/module/modules/timer" "gorm.io/gorm" ) var ( // MatchingPlayers sync.Map AllPlayers sync.Map AllRobots sync.Map PlayerCount *int64 = new(int64) ) type SubPlayer interface { Enter() OnEnter() Leave() bool Reset() Settle() TableInfo() } type Player struct { SubPlayer SubPlayer T *Table *room.BasePlayerImp UID int SeatID int Timer string Role int *common.PlayerDBInfo // Currency *common.PlayerCurrency Balance int64 Status pb.PlayerStatus IsLeave bool StartTime int64 GameCurrencyType common.CurrencyType // 本局参与游戏的货币类型 FinalSettle int64 TotalBet int64 UUID string *MillionSubPlayer // *RoomSubPlayer // *SlotPlayer } func GetPlayer(uid int) *Player { p, ok := AllPlayers.Load(uid) if !ok { return nil } return p.(*Player) } func NewPlayer(uid int, t *Table) *Player { p := &Player{UID: uid, T: t, BasePlayerImp: &room.BasePlayerImp{}, SeatID: -1} p.Status = pb.PlayerStatus_PlayerStatusNormal return p } func (p *Player) Refresh(session gate.Session) { p.IsLeave = false if p.Session().GetSessionID() != session.GetSessionID() { delete(p.T.Players, p.Session().GetSessionID()) p.T.Players[session.GetSessionID()] = p p.Bind(session) } } func (p *Player) Enter() { uid := p.UID AllPlayers.Store(uid, p) util.Go(func() { p.PlayerDBInfo, _ = call.GetUserXInfo(uid, "role", "nick", "avatar", "channel_id", "birth", "mobile") // p.Currency = &common.PlayerCurrency{UID: uid} // db.Mysql().Get(p.Currency) p.Balance = call.GetUserCurrencyTotal(p.UID, p.GameCurrencyType) p.UpdatePlayerStatus() p.T.PutQueue("nf", func() { p.Send(int(pb.GameProtocol_EnterGameResp), &pb.GameCommonResp{}) // 如果是房间模式,玩家进入后自动找位置坐下 // if p.RoomSubPlayer != nil { // p.RoomSubPlayer.Enter() // } p.SubPlayer.Enter() }) }) } // Send 发送消息 func (p *Player) Send(pid int, send proto.Message) { if p.IsLeave || p.IsRobot() { return } p.OnResponse(p.Session()) topic := fmt.Sprintf("%v:%v", call.GetTopicName(), pid) body, _ := proto.Marshal(send) if err := p.T.SendCallBackMsgNR([]string{p.Session().GetSessionID()}, topic, body); err != nil { p.Error("err:%v", err) } } // SendWithModule 发送消息 func (p *Player) SendWithModule(module string, pid int, send proto.Message) { if p.IsLeave || p.IsRobot() { return } p.OnResponse(p.Session()) topic := fmt.Sprintf("%v:%v", module, pid) body, _ := proto.Marshal(send) if err := p.T.SendCallBackMsgNR([]string{p.Session().GetSessionID()}, topic, body); err != nil { p.Error("err:%v", err) } } // StopTimer 开始准备倒计时 func (p *Player) StopTimer() { if p.Timer != "" { timewheel.GetTimeWheel().RemoveTimer(p.Timer) p.Timer = "" } } func (p *Player) Reset() { p.StopTimer() p.Status = pb.PlayerStatus_PlayerStatusNormal p.FinalSettle = 0 p.TotalBet = 0 p.UUID = "" // if p.RoomSubPlayer != nil { // p.RoomSubPlayer.Reset() // } if p.MillionSubPlayer != nil { p.MillionSubPlayer.Reset() } // if p.SlotPlayer != nil { // p.SlotPlayer.Reset() // } p.SubPlayer.Reset() } func (p *Player) DelAllPlayers() { player, ok := AllPlayers.Load(p.UID) if !ok { return } ap := player.(*Player) if p == ap { AllPlayers.Delete(p.UID) } } func (p *Player) IsRobot() bool { return p.Role == common.PlayerRoleRobot } func (p *Player) Leave() { t := p.T if p.Status == pb.PlayerStatus_PlayerStatusLeave { return } p.Debug("leave table") // p.SettleShouldSend = true if !p.SubPlayer.Leave() { return } p.DelAllPlayers() if p.SeatID >= 0 { t.SeatPlayers[p.SeatID] = nil } p.Status = pb.PlayerStatus_PlayerStatusLeave p.StopTimer() t.RemoveOnePlayer(p) if len(t.Room.TableList) > 1 { // 处理一下list t.Room.MineOne(t) } p.Debug("finish leave table") // if t.Status == TableStatusPlaying { // return // } // if !p.IsRobot() && t.IsSingle && t.CountReal() == 0 { // if ThisGameType == GameTypeMillion { // t.Finish() // return // } // t.RobotLeave() // t.StopRobotTimer() // t.Reset() // t.Debug("push back to single queue") // // 该房间空置后,重新丢回队列使用 // t.Room.Queue.AddQueue(func() { // t.Ele = t.Room.TableListSingle.PushBack(t) // t.List = t.Room.TableListSingle // }) // } } func (p *Player) SendReal(pid int, resp proto.Message) { if p.IsRobot() || p.IsLeave { return } p.OnResponse(p.Session()) body, _ := proto.Marshal(resp) call.SendSSBytes(p.Session(), pid, body) } func (p *Player) Disconnect() { p.Debug("disconnect") // p.IsLeave = true // if p.MillionSubPlayer != nil { // p.IsAuto = false // } // // slot游戏重连直接退出房间 // if p.SlotPlayer != nil { p.Leave() // } } // PackBalance 统一封装流水记录 func (p *Player) PackUpdate(data *common.UpdateCurrency) { t := p.T uuid := t.UUID if uuid == "" { uuid = p.UUID } data.UID = p.UID data.Exi1 = ThisGameID data.Exi2 = t.Room.RoomId() data.Exs1 = uuid data.ChannelID = p.ChannelID } // PackBalance 统一封装流水记录 func (p *Player) PackBalance(data *common.CurrencyBalance) { t := p.T data.UID = p.UID data.ChannelID = p.ChannelID data.Exs1 = p.UUID if data.Exs1 == "" { data.Exs1 = t.UUID } data.Exi1 = ThisGameID data.Exi2 = t.Room.RoomId() data.Time = time.Now().Unix() } // Settle 统一结算 func (p *Player) Settle() { t := p.T if p.Status != pb.PlayerStatus_PlayerStatusPlaying { return } p.Debug("settle") p.StopTimer() p.SubPlayer.Settle() p.Status = pb.PlayerStatus_PlayerStatusSettle if t.Room.RoomType == RoomTypePractice { return } // slot玩法直接reset // if p.SlotPlayer != nil { // if p.DBFreeCount == 0 { // p.AfterSettle() // } // p.Reset() // } else { p.AfterSettle() // } } // AfterSettle 结算后检查一些数据 func (p *Player) AfterSettle() { if p.IsRobot() { return } // 写入统计 // p.WriteStats() // 检查房间水位 p.CalWater() // 结算后,处理活动等逻辑 if p.UUID == "" { p.UUID = p.T.UUID } now := time.Now().Unix() if p.StartTime == 0 { p.StartTime = p.T.StartTime } data := &pb.InnerAfterSettle{ UID: int64(p.UID), ProviderID: common.ProviderInhouse, GameID: int64(ThisGameID), TotalBet: p.TotalBet, OriginSettle: p.FinalSettle, FinalSettle: p.FinalSettle, Phone: p.Mobile, UUID: p.UUID, Nick: p.Nick, MyUUID: p.UUID, CurrencyType: int64(p.GameCurrencyType), IsNew: util.IsSameDayTimeStamp(now, p.Birth), SubID: int64(p.T.Room.RoomId()), PlayTime: now - p.StartTime, } util.IndexTryS(func() error { return call.Publish(natsClient.TopicInnerAfterSettle, data) }) } // CalWater 计算水位 func (p *Player) CalWater() { gid := ThisGameID rid := p.T.Room.RoomId() conf := call.GetConfigWaterReal(gid, rid) // 未发生控制,则更新房间水位 if conf != nil { add := p.FinalSettle // if conf.RebatePer != 100 { // add = add * conf.RebatePer / 100 // } // field := "brl" // if p.GameCurrencyType == common.CurrencyUSDT { // field = "usdt" // } db.Mysql().Update(&common.ConfigWater{GameID: gid, RoomID: rid}, map[string]interface{}{"value": gorm.Expr(fmt.Sprintf("value + %d", add))}) } } // Debug 封装玩家打印日志方法(机器人也会打印) func (p *Player) DebugA(fomat string, a ...interface{}) { t := p.T log.Debug("uuid:%v,roomID:%v,tableID:%v,player:%v,"+fomat, append([]interface{}{t.UUID, t.Room.RoomId(), t.TableId(), p.UID}, a...)...) } // Debug 封装玩家打印日志方法 func (p *Player) Debug(fomat string, a ...interface{}) { if p.IsRobot() { return } t := p.T log.Debug("uuid:%v,roomID:%v,tableID:%v,player:%v,"+fomat, append([]interface{}{t.UUID, t.Room.RoomId(), t.TableId(), p.UID}, a...)...) } // Error 封装玩家打印日志方法 func (p *Player) Error(fomat string, a ...interface{}) { if p.IsRobot() { return } t := p.T log.Error("uuid:%v,roomID:%v,tableID:%v,player:%v,"+fomat, append([]interface{}{t.UUID, t.Room.RoomId(), t.TableId(), p.UID}, a...)...) } // WriteStats 写入新的统计记录 func (p *Player) WriteStats() { t := p.T now := time.Now().Unix() if p.StartTime == 0 { p.StartTime = t.StartTime } if p.UUID == "" { p.UUID = t.UUID } pt := now - p.StartTime st := &common.ESGameData{PlayTime: pt} st.UID = p.UID st.Channel = p.ChannelID st.Provider = common.ProviderInhouse st.GameID = ThisGameID st.UUID = p.UUID st.Type = p.GameCurrencyType st.BetAmount = p.TotalBet st.SettleAmount = p.FinalSettle st.MyUUID = p.UUID st.Time = now st.Birth = p.Birth st.SubID = t.Room.RoomId() st.IsNew = util.IsSameDayTimeStamp(now, p.Birth) call.UpdatePlayerProfile(st) } func (p *Player) DeleteSeats() { t := p.T if _, ok := t.GetSeats()[p.Session().GetSessionID()]; ok { delete(t.GetSeats(), p.Session().GetSessionID()) } else { for k, v := range t.GetSeats() { if v.(*Player).UID == p.UID { delete(t.GetSeats(), k) break } } } } // 房间控制 // func (p *Player) CheckWaterControl() (is bool) { // conf := call.GetConfigWaterReal(ThisGameID, p.T.Room.RoomId()) // if conf == nil { // return false // } // // 根据触发概率判断是否触发房间控制 // if conf.RebatePer <= 0 || p.T.Ran.Intn(100)+1 > conf.ControlPer { // return // } // // 当前水位 // water := conf.Value // up := conf.WaterUp * 100 // down := conf.WaterLower * 100 // // 水位大于上限 放 // if water > up { // is = true // } // // 水位小于下限 杀 // if water <= down { // is = true // } // return // } func (p *Player) UpdatePlayerStatus() { one := common.PlayerStatus{ GameID: ThisGameID, ModuleName: call.GetCaller().GetType(), SubID: p.T.Room.RoomId(), WorkID: config.GetConfig().WorkID, TableID: p.T.TableId(), } if err := db.Redis().HSet(common.GetRedisKeyPlayerStatus(p.UID), one); err != nil { p.Error("err:%v", err) } } func (p *Player) DelPlayerStatus() { one := new(common.PlayerStatus) if err := db.Redis().HGetAll(common.GetRedisKeyPlayerStatus(p.UID), one); err != nil { return } if one.TableID == p.T.TableId() && one.GameID == ThisGameID { db.Redis().Delkey(common.GetRedisKeyPlayerStatus(p.UID)) } } func (p *Player) PackTableInfo(tableInfo *pb.GameCommonTableInfo) { tableInfo.User = &pb.GameUser{ UID: int64(p.UID), Nick: p.Nick, Avatar: p.Avatar, CurrencyType: int64(p.GameCurrencyType), Balance: p.Balance, Bet: p.TotalBet, Status: p.Status, } tableInfo.List = p.T.BetPlayers tableInfo.Status = p.T.Status tableInfo.TimeLeft = p.T.OperateTimeout - time.Now().UnixMilli() if tableInfo.TimeLeft < 0 { tableInfo.TimeLeft = 0 } tableInfo.BetLimit = p.T.Config.BetLimit tableInfo.History = p.T.History } func (p *Player) GetPlayerBetList() { t := p.T resp := &pb.GameMsgBetListResp{ List: t.BetPlayers, } p.Send(int(pb.GameProtocol_BetListResp), resp) } func (p *Player) UpdateBetList() { t := p.T bet := p.TotalBet for i := range t.BetPlayers { if t.BetPlayers[i].UID == int64(p.UID) { t.BetPlayers[i].BetAmount = bet return } } t.BetPlayers = append(t.BetPlayers, &pb.BetPlayers{ UID: int64(p.UID), Avatar: p.Avatar, Nick: p.Nick, CurrencyType: int64(p.GameCurrencyType), BetAmount: bet, }) }