package base import ( "fmt" rawproto "google.golang.org/protobuf/proto" "math" "mongo.games.com/game/common" "mongo.games.com/game/model" "mongo.games.com/game/proto" "mongo.games.com/game/protocol/player" "mongo.games.com/game/protocol/server" "mongo.games.com/game/srvdata" "mongo.games.com/goserver/core/logger" "mongo.games.com/goserver/core/netlib" "mongo.games.com/goserver/core/timer" //rawproto "github.com/golang/protobuf/proto" "math/rand" "strconv" "time" ) // 对应到客户端的一个玩家对象. const ( PlayerState_Online int = 1 << iota //在线标记 1 PlayerState_Ready //准备标记 2 PlayerState_SceneOwner //房主标记 3 PlayerState_Choke //呛标记 被复用于金花,是否被动弃牌 4 PlayerState_Ting //听牌标记 5 金花复用,标记最后押注时,是否看牌 PlayerState_NoisyBanker //闹庄标记 6 金花复用,标记allin时,是否看牌 PlayerState_WaitOp //等待操作标记 7 PlayerState_Auto //托管状态 8 PlayerState_Check //已看牌状态 9 PlayerState_Fold //弃牌状态 10 PlayerState_Lose //输状态 11 PlayerState_Win //赢状态 12 PlayerState_WaitNext //等待下一局游戏 13 PlayerState_GameBreak //不能继续游戏 14 PlayerState_Leave //暂离状态 15 PlayerState_Audience //观众标记 16 PlayerState_AllIn //allin标记 17 PlayerState_FinalAllIn //最后一圈,最后一个人allin标记 18 PlayerState_Show //亮牌标记 19 PlayerState_EnterSceneFailed //进场失败 20 PlayerState_PKLost //发起Pk,失败 21 PlayerState_IsChangeCard //牛牛标识是否换牌 22 PlayerState_IsPayChangeCard //牛牛标识是否充值换牌 23 PlayerState_Bankruptcy //玩家破产 24 PlayerState_MatchQuit //退赛标记 25 PlayerState_AllFollow //跟到底状态 26 PlayerState_SAdjust //单控状态 27 PlayerState_Max ) // 玩家事件 const ( PlayerEventEnter int = iota //进入事件 PlayerEventLeave //离开事件 PlayerEventDropLine //掉线 PlayerEventRehold //重连 PlayerEventReturn //返回房间 gs 添加 PlayerEventRecharge //冲值事件 PlayerEventAddCoin //其他加减币事件(例如:小游戏) AudienceEventEnter //观众进入事件 AudienceEventLeave //观众离开事件 AudienceEventDropLine //观众掉线 AudienceEventRehold //观众重连 ) type Player struct { model.PlayerData //po 持久化对象 ExtraData interface{} //扩展接口 gateSess *netlib.Session //所在GateServer的session worldSess *netlib.Session //所在WorldServer的session scene *Scene //当前所在个Scene ai AI //ai接口 sid int64 //对应客户端的sessionId gateSid int64 //对应网关的sessionId Longitude int32 //经纬度 Latitude int32 //经纬度 city string //城市 flag int //状态标记 Pos int //当前位置 dirty bool //脏标记 Billed bool //是否已经结算过了 AgentCode string //代理商编号 Coin int64 //金币 serviceFee int64 //服务费|税收 TotalBet int64 //总下注额(从进房间开始,包含多局游戏的下注) disbandGen int //第几次解散申请 hAuto timer.TimerHandle //托管handle GameTimes int32 //游戏次数 winTimes int //胜利次数 lostTimes int //失败次数 ActiveLeave bool //主动暂离 OpCode player.OpResultCode //错误码 takeCoin int64 //携带金币 ExpectLeaveCoin int64 //期望离场时的金币[机器人用] ExpectGameTime int32 //期望进行的局数[机器人用] CurIsWin int64 //当局输赢 负数:输 0:平局 正数:赢 currentCoin int64 //本局结束后剩余 CurrentBet int64 //本局下注额 CurrentTax int64 //本局税收 StartCoin int64 //本局开始金币 LastSyncCoin int64 // IsQM bool //是否是全民推广用户 LastOPTimer time.Time //玩家最后一次操作时间 Trusteeship int32 //玩家托管了几局 ValidCacheBetTotal int64 //有效下注缓存 isFightRobot bool //测试机器人,这种机器人可以用作记录水池数据,方便模拟用户输赢 DropTime time.Time //掉线时间 cparams map[string]string //平台登陆数据 Iparams map[int]int64 //整形参数 sparams map[int]string //字符参数 WhiteLevel int32 //todo 使用WBLevel BlackLevel int32 //todo 使用WBLevel SingleAdjust *model.PlayerSingleAdjust IsLocal bool //是否本地player Items map[int32]int64 //背包数据 MatchParams []int32 //比赛参数 排名、段位、假snid、假角色 MatchRobotGrades []MatchRobotGrade TestLog []string // 调试日志 RankScore map[int32]int64 // 段位积分 } type MatchRobotGrade struct { CopySnid int32 Grade int32 } func NewPlayer(sid int64, data []byte, ws, gs *netlib.Session) *Player { p := &Player{ sid: sid, worldSess: ws, gateSess: gs, flag: PlayerState_Online, Pos: -1, Longitude: -1, Latitude: -1, cparams: make(map[string]string), //平台登陆数据 Iparams: make(map[int]int64), //整形参数 sparams: make(map[int]string), //字符参数 Items: make(map[int32]int64), RankScore: make(map[int32]int64), } // 需要make的,统一在这里初始化默认值,别的地方就不用再初始化了 p.PlayerData = model.PlayerData{ TotalGameData: make(map[int][]*model.PlayerGameTotal), GDatas: make(map[string]*model.PlayerGameInfo), ShopTotal: make(map[int32]*model.ShopTotal), ShopLastLookTime: make(map[int32]int64), IsFoolPlayer: make(map[string]bool), } if p.init(data) { return p } return nil } func (this *Player) init(data []byte) bool { if !this.UnmarshalData(data) { return false } if this.GMLevel > 2 { this.Longitude = rand.Int31n(114103930-113216260) + 113216260 this.Latitude = rand.Int31n(34963671-34592702) + 34592702 } this.city = this.City this.LastOPTimer = time.Now() if this.GDatas == nil { this.GDatas = make(map[string]*model.PlayerGameInfo) } if this.WBLevel > 0 { this.WhiteLevel = this.WBLevel } else if this.WBLevel < 0 { this.BlackLevel = -this.WBLevel } return true } func (this *Player) MarkFlag(flag int) { this.flag |= flag switch flag { case PlayerState_Online, PlayerState_Ready, PlayerState_Leave: this.SyncFlagToWorld() } } func (this *Player) UnmarkFlag(flag int) { this.flag &= ^flag switch flag { case PlayerState_Online, PlayerState_Ready, PlayerState_Leave: this.SyncFlagToWorld() } } func (this *Player) IsMarkFlag(flag int) bool { if (this.flag & flag) != 0 { return true } return false } func (this *Player) IsOnLine() bool { return this.IsMarkFlag(PlayerState_Online) } func (this *Player) IsReady() bool { return this.IsMarkFlag(PlayerState_Ready) } func (this *Player) IsSceneOwner() bool { return this.IsMarkFlag(PlayerState_SceneOwner) } func (this *Player) IsAuto() bool { return this.IsMarkFlag(PlayerState_Auto) } func (this *Player) IsGameing() bool { return !this.IsMarkFlag(PlayerState_WaitNext) && !this.IsMarkFlag(PlayerState_GameBreak) && !this.IsMarkFlag(PlayerState_Bankruptcy) && !this.IsMarkFlag(PlayerState_Audience) } func (this *Player) IsAllFollow() bool { return this.IsMarkFlag(PlayerState_AllFollow) } func (this *Player) SyncFlag(onlyMyself ...bool) { if this.IsLocal { return } pack := &player.SCPlayerFlag{ PlayerId: proto.Int32(this.SnId), Flag: proto.Int(this.flag), } proto.SetDefaults(pack) if len(onlyMyself) != 0 && onlyMyself[0] { this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERFLAG), pack) } else { this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERFLAG), pack, 0) } //logger.Logger.Trace("SyncFlag:", pack) } func (this *Player) SyncFlagToWorld() { if this.IsLocal { return } if this.scene == nil || this.scene.IsCoinScene() || this.scene.IsMatchScene() || this.scene.IsHundredScene() { return } pack := &server.GWPlayerFlag{ SnId: proto.Int32(this.SnId), RoomId: proto.Int(this.scene.SceneId), Flag: proto.Int(this.flag), } proto.SetDefaults(pack) this.SendToWorld(int(server.SSPacketID_PACKET_GW_PLAYERSTATE), pack) logger.Logger.Trace("SyncFlag to world:", pack) } func (this *Player) SendToClient(packetid int, rawpack interface{}, forceIgnore ...bool) bool { if this.IsLocal { return true } if !this.scene.Testing && this.scene.Gaming && this.scene.rr != nil && this.Pos != -1 && len(forceIgnore) == 0 && !this.scene.IsHundredScene() { this.scene.rr.Record(this.Pos, -1, packetid, rawpack) } if this.gateSess == nil { logger.Logger.Warnf("(this *Player) SendToClient [snid:%v packetid:%v] gatesess == nil ", this.SnId, packetid) return false } if rawpack == nil { logger.Logger.Tracef("(this *Player) SendToClient [snid:%v packetid:%v] rawpack == nil ", this.SnId, packetid) return false } if !this.IsOnLine() { logger.Logger.Warnf("(this *Player) SendToClient [snid:%v packetid:%v] Player if offline.", this.SnId, packetid) return false } if this.IsMarkFlag(PlayerState_Leave) { logger.Logger.Warnf("(this *Player) SendToClient [snid:%v packetid:%v] Player if leave.", this.SnId, packetid) return false } //logger.Logger.Trace("Send to player's packet:", packetid) return common.SendToGate(this.sid, packetid, rawpack, this.gateSess) } func (this *Player) Broadcast(packetid int, rawpack interface{}, excludeSid int64) bool { if this.scene != nil { this.scene.Broadcast(packetid, rawpack.(rawproto.Message), excludeSid) return true } return false } func (this *Player) SendToWorld(packetid int, rawpack interface{}) bool { if this.IsLocal { return true } if this.worldSess == nil { logger.Logger.Tracef("(this *Player) SendToWorld [%v] worldsess == nil ", this.Name) return false } if rawpack == nil { logger.Logger.Trace("(this *Player) SendToWorld rawpack == nil ") return false } this.scene.SendToWorld(packetid, rawpack) return true } func (this *Player) OnEnter(s *Scene) { this.scene = s this.TestLog = this.TestLog[:0] //标记房主 if this.SnId == s.Creator { this.MarkFlag(PlayerState_SceneOwner) } } func (this *Player) OnAudienceEnter(s *Scene) { this.scene = s } func (this *Player) OnRehold(newSid int64, newSess *netlib.Session) { this.sid = newSid this.gateSess = newSess this.MarkFlag(PlayerState_Online) // 2018-4-25 // 这里先注释掉,暂离的状态在LeaveRoom和ReturnRoom的消息中设置 // 如果这里清除暂离,那么在棋牌馆中,离开房间,用的状态更新为暂离状态,断线重连以后的话,在棋牌馆大厅界面看到的用户为非暂离状态, // 所以这里就先注释掉,让暂离的状态在离开和返回房间的消息中成对出现 this.UnmarkFlag(PlayerState_Leave) this.SyncFlag() } func (this *Player) OnDropLine() { this.UnmarkFlag(PlayerState_Online) if !this.scene.Gaming && this.IsReady() && !this.scene.IsMatchScene() { this.UnmarkFlag(PlayerState_Ready) } this.SyncFlag() //存在假掉线的可能吗? //this.gateSess = nil //this.sid = 0 } func (this *Player) OnAudienceDropLine() { this.gateSess = nil } func (this *Player) OnLeave(reason int) { unbindGateSess := true PlayerMgrSington.DelPlayerBySnId(this.SnId) //解绑gamesession if unbindGateSess && this.gateSess != nil { pack := &server.GGPlayerSessionUnBind{ Sid: proto.Int64(this.sid), } proto.SetDefaults(pack) this.gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONUNBIND), pack) } } func (this *Player) OnAudienceLeave(reason int) { PlayerMgrSington.DelPlayerBySnId(this.SnId) //解绑gamesession if this.gateSess != nil { pack := &server.GGPlayerSessionUnBind{ Sid: proto.Int64(this.sid), } proto.SetDefaults(pack) this.gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONUNBIND), pack) } } func (this *Player) MarshalData(gameid int) (d []byte, e error) { d, e = netlib.Gob.Marshal(&this.PlayerData) logger.Logger.Trace("(this *Player) MarshalData(gameid int)") return } func (this *Player) UnmarshalData(data []byte) bool { if len(data) == 0 { return true } err := netlib.Gob.Unmarshal(data, &this.PlayerData) if err == nil { this.dirty = true return true } else { logger.Logger.Warn("Player.SyncData err:", err) } return false } func (this *Player) OnSecTimer() { } func (this *Player) OnMiniTimer() { } func (this *Player) OnHourTimer() { } func (this *Player) OnDayTimer() { //在线跨天 数据给昨天,今天置为空 this.YesterdayGameData = this.TodayGameData this.TodayGameData = model.NewPlayerGameCtrlData() /* for k, v := range this.YesterdayGameData.CtrlData { t := &model.PlayerGameStatics{} t.AvgBetCoin = v.AvgBetCoin this.TodayGameData.CtrlData[k] = t } */ } func (this *Player) OnMonthTimer() { } func (this *Player) OnWeekTimer() { } func (this *Player) GetName() string { return this.Name } func (this *Player) MarkDirty() { this.dirty = true } const ( SyncFlag_ToClient = 1 << iota //同步给客户端 SyncFlag_ToWorld //同步给服务端 SyncFlag_Broadcast //广播给房间内的用户 ) func (this *Player) AddCoin(num int64, gainWay int32, syncFlag int, oper, remark string) { if num == 0 { return } this.Coin += num this.Items[common.ItemIDCoin] = this.Coin if this.scene != nil { if !this.IsRob && !this.scene.Testing { //机器人log排除掉 log := model.NewCoinLogEx(&model.CoinLogParam{ Platform: this.Platform, SnID: this.SnId, ChangeType: common.BillTypeCoin, ChangeNum: num, RemainNum: this.Coin, Add: 0, LogType: gainWay, GameID: int64(int32(this.scene.GetGameId())), GameFreeID: int64(this.scene.GetGameFreeId()), BaseCoin: int64(this.scene.GetBaseScore()), Operator: oper, Remark: remark, }) if log != nil { this.GameCoinTs = log.Time.UnixNano() this.dirty = true LogChannelSingleton.WriteLog(log) } } //确保金币场金币数量不小于0 if this.Coin < 0 { this.Coin = 0 } if this.scene.IsHundredScene() { this.scene.NewBigCoinNotice(this, int64(num), 5) } } //增加玩家经验 if num > 0 { exp := num / 100 this.AddPlayerExp(exp) logger.Logger.Trace("玩家获取金币 增加玩家经验值:", exp) } if (syncFlag & SyncFlag_ToClient) != 0 { pack := &player.SCPlayerCoinChange{ SnId: proto.Int32(this.SnId), AddCoin: proto.Int64(num), RestCoin: proto.Int64(this.Coin), } proto.SetDefaults(pack) if (syncFlag & SyncFlag_Broadcast) != 0 { this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack, 0) } else { this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) } logger.Logger.Trace("(this *Player) AddCoin SCPlayerCoinChange:", pack) } } func (this *Player) AddCoinNoLog(num int64, syncFlag int) { if num == 0 { return } this.Coin += num if this.scene != nil { if !this.IsRob && !this.scene.Testing { //机器人log排除掉 this.dirty = true } } if (syncFlag & SyncFlag_ToClient) != 0 { pack := &player.SCPlayerCoinChange{ SnId: proto.Int32(this.SnId), AddCoin: proto.Int64(num), RestCoin: proto.Int64(this.Coin), } proto.SetDefaults(pack) if (syncFlag & SyncFlag_Broadcast) != 0 { this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack, 0) } else { this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) } logger.Logger.Trace("(this *Player) AddCoinNoLog SCPlayerCoinChange:", pack) } } func (this *Player) AddCoinAsync(num int64, gainWay int32, notifyC, broadcast bool, oper, remark string, writeLog bool) { if num == 0 { return } this.Coin += num this.Items[common.ItemIDCoin] = this.Coin if this.scene != nil { if !this.IsRob && !this.scene.Testing && writeLog { //机器人log排除掉 log := model.NewCoinLogEx(&model.CoinLogParam{ Platform: this.Platform, SnID: this.SnId, ChangeType: common.BillTypeCoin, ChangeNum: num, RemainNum: this.Coin, Add: 0, LogType: gainWay, GameID: int64(this.scene.GetGameId()), GameFreeID: int64(this.scene.GetGameFreeId()), BaseCoin: int64(this.scene.GetBaseScore()), Operator: oper, Remark: remark, }) if log != nil { this.GameCoinTs = log.Time.UnixNano() this.dirty = true LogChannelSingleton.WriteLog(log) } } //确保金币场金币数量不小于0 if this.Coin < 0 { this.Coin = 0 } } if notifyC { pack := &player.SCPlayerCoinChange{ SnId: proto.Int32(this.SnId), AddCoin: proto.Int64(num), RestCoin: proto.Int64(this.Coin), } proto.SetDefaults(pack) if broadcast { this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack, 0) } else { this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) } } } func (this *Player) AddChessGrade(num int64) { if num == 0 { return } this.ChessGrade += num if this.ChessGrade < 0 { this.ChessGrade = 0 } } func (this *Player) AddRankScore(rankType int32, num int64) { if num == 0 { return } oldScore := this.RankScore[rankType] var lessScore int64 switch rankType { case 1: lessScore = 10001 default: lessScore = 10001 } if oldScore < lessScore && num < 0 { return } this.RankScore[rankType] += num if this.RankScore[rankType] < 0 { this.RankScore[rankType] = 0 } if oldScore >= lessScore && this.RankScore[rankType] < lessScore { this.RankScore[rankType] = lessScore } } // 保存金币变动日志 // 数据用途: 个人房间内牌局账变记录,后台部分报表使用,确保数据计算无误,否则可能影响月底对账 // takeCoin: 牌局结算前玩家身上的金币 // changecoin: 本局玩家输赢的钱,注意是税后 // coin: 结算后玩家当前身上的金币余额 // totalbet: 总下注额 // taxcoin: 本局该玩家产生的税收,这里要包含俱乐部的税 // wincoin: 本局赢取的金币,含税 wincoin==changecoin+taxcoin // jackpotWinCoin: 从奖池中赢取的金币(拉霸类游戏) // smallGameWinCoin: 小游戏赢取的金币(拉霸类游戏) func (this *Player) SaveSceneCoinLog(takeCoin, changecoin, coin, totalbet, taxcoin, wincoin int64, jackpotWinCoin int64, smallGameWinCoin int64) { if this.scene != nil { if !this.IsRob && !this.scene.Testing && !this.scene.IsMatchScene() { //机器人log排除掉 var eventType int64 //输赢事件值 默认值为0 if coin-takeCoin > 0 { eventType = 1 } else if coin-takeCoin < 0 { eventType = -1 } log := model.NewSceneCoinLogEx(this.SnId, changecoin, takeCoin, coin, eventType, int64(this.scene.DbGameFree.GetBaseScore()), totalbet, int32(this.scene.GameId), this.PlayerData.Ip, this.scene.paramsEx[0], this.Pos, this.Platform, this.Channel, this.BeUnderAgentCode, int32(this.scene.SceneId), this.scene.DbGameFree.GetGameMode(), this.scene.GetGameFreeId(), taxcoin, wincoin, jackpotWinCoin, smallGameWinCoin, this.PackageID) if log != nil { LogChannelSingleton.WriteLog(log) } } } } // 需要关照 func (this *Player) IsNeedCare() bool { return false } // 需要削弱 func (this *Player) IsNeedWeaken() bool { return false } func (this *Player) GetCoinOverPercent() int32 { return 0 } func (this *Player) SyncCoin() { pack := &player.SCPlayerCoinChange{ SnId: proto.Int32(this.SnId), AddCoin: proto.Int64(0), RestCoin: proto.Int64(this.Coin), } proto.SetDefaults(pack) this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) logger.Logger.Trace("(this *Player) SyncCoin SCPlayerCoinChange:", pack) } func (this *Player) ReportGameEvent(tax, taxex, changeCoin, validbet, validFlow, in, out int64) { // 记录玩家 首次参与该场次的游戏时间 游戏次数 var gameFirstTime, gameFreeFirstTime time.Time var gameTimes, gameFreeTimes int64 data, ok := this.GDatas[this.scene.KeyGamefreeId] if ok { gameFirstTime = data.FirstTime gameTimes = data.Statics.GameTimes } // 记录玩家 首次参与该游戏时间 游戏次数(不区分场次) dataGame, ok := this.GDatas[this.scene.KeyGameId] if ok { gameFreeFirstTime = dataGame.FirstTime gameFreeTimes = dataGame.Statics.GameTimes } gamingTime := int32(time.Now().Sub(this.scene.GameNowTime).Seconds()) LogChannelSingleton.WriteMQData(model.GenerateGameEvent(model.CreatePlayerGameRecEvent(this.SnId, tax, taxex, changeCoin, validbet, validFlow, in, out, int32(this.scene.GameId), this.scene.DbGameFree.GetId(), int32(this.scene.GameMode), this.scene.GetRecordId(), this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.DeviceOS, this.CreateTime, gamingTime, gameFirstTime, gameFreeFirstTime, gameTimes, gameFreeTimes, this.LastLoginTime, this.TelephonePromoter, this.DeviceId))) } // 破产事件 func (this *Player) ReportBankRuptcy(gameId, gameMode, gameFreeId int32) { //if !this.IsRob { // d, e := model.MarshalBankruptcyEvent(2, this.SnId, this.TelephonePromoter, this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.CreateTime, gameId, gameMode, gameFreeId) // if e == nil { // rmd := model.NewInfluxDBData("hj.player_bankruptcy", d) // if rmd != nil { // InfluxDBDataChannelSington.Write(rmd) // } // } //} } // 汇总玩家该次游戏总产生的税收 // 数据用途: 平台和推广间分账用,确保数据计算无误, // 注意:该税收不包含俱乐部的抽水 // tax:游戏税收 func (this *Player) AddServiceFee(tax int64) { if this.scene == nil || this.scene.Testing || this.scene.IsMatchScene() { //测试场不统计 return } if tax > 0 && !this.IsRob { this.serviceFee += tax } } func (this *Player) GetStaticsData(gameDiff string) (winCoin int64, lostCoin int64) { if this.PlayerData.GDatas != nil { if data, ok := this.PlayerData.GDatas[gameDiff]; ok { winCoin, lostCoin = data.Statics.TotalOut, data.Statics.TotalIn return } } return } func (this *Player) SaveReportForm(showId, sceneMode int, keyGameId string, profitCoin, flow int64, validBet int64) { //个人报表统计 if this.TotalGameData == nil { this.TotalGameData = make(map[int][]*model.PlayerGameTotal) } if this.TotalGameData[showId] == nil { this.TotalGameData[showId] = []*model.PlayerGameTotal{new(model.PlayerGameTotal)} } td := this.TotalGameData[showId][len(this.TotalGameData[showId])-1] td.ProfitCoin += profitCoin td.BetCoin += validBet td.FlowCoin += flow ///////////////最多盈利 if pgs, exist := this.GDatas[keyGameId]; exist { if pgs.Statics.MaxSysOut < profitCoin { pgs.Statics.MaxSysOut = profitCoin } } else { gs := model.NewPlayerGameStatics() gs.MaxSysOut = profitCoin this.GDatas[keyGameId] = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: *gs} } } // Statics 弃用,使用 Scene.Statistics 方法 // 个人投入产出汇总,以游戏id为key存储 // 数据用途:计算玩家赔率用,数据确保计算无误,否则可能影响玩家手牌的调控 // key: 游戏ID对应的字符串,牛牛目前用的是同一个ID,这块有待优化 // gain:输赢额,注意如果是[正值]这里一定要用税前数据,否则玩家会有数值调控优势 // 如果需要汇总gameid today的数据,可以使用game scene的GetTotalTodayDaliyGameData // Deprecated: Use Scene.Statistics instead. func (this *Player) Statics(keyGameId string, keyGameFreeId string, gain int64, isAddNum bool) { //if this.scene == nil || this.scene.Testing { //测试场|自建房和机器人不统计 // return //} // //if this.IsRob && !this.scene.IsRobFightGame() { // return //} // //if this.TodayGameData == nil { // this.TodayGameData = &model.PlayerGameCtrlData{} //} //if this.TodayGameData.CtrlData == nil { // this.TodayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) //} // //var totalIn int64 //var totalOut int64 //if gain > 0 { // totalOut = gain //} else { // totalIn = -gain //} // //statics := make([]*model.PlayerGameStatics, 0, 4) ////当天数据统计 ////按场次分 //if data, ok := this.TodayGameData.CtrlData[keyGameFreeId]; ok { // statics = append(statics, data) //} else { // gs := &model.PlayerGameStatics{} // this.TodayGameData.CtrlData[keyGameFreeId] = gs // statics = append(statics, gs) //} ////按游戏分 //if data, ok := this.TodayGameData.CtrlData[keyGameId]; ok { // statics = append(statics, data) //} else { // data = &model.PlayerGameStatics{} // this.TodayGameData.CtrlData[keyGameId] = data // statics = append(statics, data) //} // ////按游戏场次进行的统计 //if data, ok := this.GDatas[keyGameFreeId]; ok { // statics = append(statics, &data.Statics) //} else { // data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: model.PlayerGameStatics{}} // this.GDatas[keyGameFreeId] = data // statics = append(statics, &data.Statics) //} //if data, ok := this.GDatas[keyGameId]; ok { // statics = append(statics, &data.Statics) //} else { // data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: model.PlayerGameStatics{}} // this.GDatas[keyGameId] = data // statics = append(statics, &data.Statics) //} // ////if !this.scene.IsPrivateScene() { //// //增加黑白名单、GM过滤,因为黑白名单过后,会导致玩家体验急剧变化 //// needStatic := this.WhiteLevel == 0 && this.WhiteFlag == 0 && this.BlackLevel == 0 && this.GMLevel == 0 //// //增加黑白名单过滤,因为黑白名单后,会导致数据出现补偿 //// if needStatic { //for _, data := range statics { // if data != nil { // data.TotalIn += totalIn // data.TotalOut += totalOut // if isAddNum { // data.GameTimes++ // if gain > 0 { // data.WinGameTimes++ // data.WinGameTimesNum++ // data.LoseGameTimesNum = 0 // } else if gain < 0 { // data.LoseGameTimes++ // data.LoseGameTimesNum++ // data.WinGameTimesNum = 0 // } else { // data.DrawGameTimes++ // data.WinGameTimesNum = 0 // data.LoseGameTimesNum = 0 // } // } // } //} // //// 黑白名单,新手,不统计到个人赔率 // ////玩家身上元数据 //this.GameTimes++ //if gain > 0 { // this.winTimes++ // this.WinTimes++ // this.WinCoin += totalOut //} else if gain < 0 { // this.lostTimes++ // this.FailTimes++ // this.FailCoin += totalIn //} else { // this.DrawTimes++ //} //// } ////} } //func (this *Player) CheckType(gamefreeId, gameId int32) *server.DB_PlayerType { // types := srvdata.PlayerTypeMgrSington.GetPlayerType(gamefreeId) // cnt := len(types) // if cnt > 0 { // var pgs *model.PlayerGameStatics // if this.GDatas != nil { // if d, exist := this.GDatas[strconv.Itoa(int(gameId))]; exist { // pgs = &d.Statics // } // } // // //赔率 产出/投入 万分比 // odds := int64(float64(float64(pgs.TotalOut+1)/float64(pgs.TotalIn+1)) * 10000) // if odds > 10000000 { // odds = 10000000 // } // for i := 0; i < cnt; i++ { // t := types[i] // if t != nil { // if this.CoinPayTotal >= int64(t.GetPayLowerLimit()) && this.CoinPayTotal <= int64(t.GetPayUpperLimit()) && // pgs.GameTimes >= int64(t.GetGameTimeLowerLimit()) && pgs.GameTimes <= int64(t.GetGameTimeUpperLimit()) && // pgs.TotalIn >= int64(t.GetTotalInLowerLimit()) && pgs.TotalIn <= int64(t.GetTotalInUpperLimit()) && // odds >= int64(t.GetOddsLowerLimit()) && odds <= int64(t.GetOddsUpperLimit()) { // return t // } // } // } // } // return nil //} // 计算玩家赔率 产出/投入 func (this *Player) LoseRate(gamefreeId, gameId int32) (rate float64) { rate = -1 if this.GDatas != nil { if d, exist := this.GDatas[strconv.Itoa(int(gameId))]; exist { rate = float64(float64(d.Statics.TotalOut+1) / float64(d.Statics.TotalIn+1)) return rate } } return rate } // 计算玩家赔率 产出/投入 func (this *Player) LoseRateKeyGameid(gameKeyId string) (rate float64) { rate = -1 var pgs *model.PlayerGameStatics if this.GDatas != nil { if d, exist := this.GDatas[gameKeyId]; exist { pgs = &d.Statics } } rate = float64(float64(pgs.TotalOut+1) / float64(pgs.TotalIn+1)) return } // 是否是新手判定 // // func (this *Player) IsFoolPlayerBy(gameId string) { // if this.IsRob || this.GDatas == nil { // return // } // if this.GDatas[gameId] == nil { // return // } // if this.IsFoolPlayer == nil { // this.IsFoolPlayer = make(map[string]bool) // } // if model.GameParamData.BirdPlayerFlag == false { // this.IsFoolPlayer[gameId] = false // return // } // playerDate := this.GDatas[gameId] // //金花游戏局数小于10局并且总产出<100000并且总产出/(总投入+10000)<=2的玩家定义为新手玩家 // if playerDate.Statics.GameTimes < 10 && playerDate.Statics.TotalOut < 100000 && // playerDate.Statics.TotalOut/(playerDate.Statics.TotalIn+10000) <= 2 { // this.IsFoolPlayer[gameId] = true // } else { // this.IsFoolPlayer[gameId] = false // } // } //func (this *Player) PlayerGameNewCheck(gameDiff string) bool { // if this.IsRob { // return false // } // if this.GDatas == nil { // return true // } // gameId := gameDiff // if this.GDatas[gameId] == nil { // return true // } // playerDate := this.GDatas[gameId] // if playerDate.Statics.GameTimes > int64(model.GameParamData.GamePlayerCheckNum) { // return true // } else { // return false // } //} func (this *Player) SendTrusteeshipTips() { pack := &player.SCTrusteeshipTips{ Trusteeship: proto.Int32(this.Trusteeship), TotalNum: proto.Int32(model.GameParamData.PlayerWatchNum), } proto.SetDefaults(pack) logger.Logger.Trace("SCTrusteeshipTips: ", pack) this.SendToClient(int(player.PlayerPacketID_PACKET_SC_TRUSTEESHIPTIPS), pack) } func (this *Player) MarshalIParam() []*server.PlayerIParam { var params []*server.PlayerIParam for i, v := range this.Iparams { params = append(params, &server.PlayerIParam{ ParamId: proto.Int(i), IntVal: proto.Int64(v), }) } return params } func (this *Player) UnmarshalIParam(params []*server.PlayerIParam) { for _, p := range params { this.Iparams[int(p.GetParamId())] = p.GetIntVal() } } func (this *Player) MarshalSParam() []*server.PlayerSParam { var params []*server.PlayerSParam for i, v := range this.sparams { params = append(params, &server.PlayerSParam{ ParamId: proto.Int(i), StrVal: proto.String(v), }) } return params } func (this *Player) UnmarshalSParam(params []*server.PlayerSParam) { for _, p := range params { this.sparams[int(p.GetParamId())] = p.GetStrVal() } } func (this *Player) MarshalCParam() []*server.PlayerCParam { var params []*server.PlayerCParam for k, v := range this.cparams { params = append(params, &server.PlayerCParam{ StrKey: proto.String(k), StrVal: proto.String(v), }) } return params } func (this *Player) UnmarshalCParam(params []*server.PlayerCParam) { for _, p := range params { this.cparams[p.GetStrKey()] = p.GetStrVal() } logger.Logger.Trace("(this *Player) UnmarshalCParam ", this.cparams) } func (this *Player) GetIParam(k int) int64 { if v, exist := this.Iparams[k]; exist { return v } return 0 } func (this *Player) SetIParam(k int, v int64) { this.Iparams[k] = v } func (this *Player) GetSParam(k int) string { if v, exist := this.sparams[k]; exist { return v } return "" } func (this *Player) SetSParam(k int, v string) { this.sparams[k] = v } func (this *Player) GetCoin() int64 { return this.Coin } func (this *Player) SetCoin(coin int64) { this.Coin = coin } func (this *Player) GetRankScore(rankType int32) int64 { return this.RankScore[rankType] } func (this *Player) SetRankScore(rankType int32, score int64) { this.RankScore[rankType] = score } // func (this *Player) GetStartCoin() int64 { // return this.StartCoin // } // // func (this *Player) SetStartCoin(startCoin int64) { // this.StartCoin = startCoin // } func (this *Player) GetExtraData() interface{} { return this.ExtraData } func (this *Player) SetExtraData(data interface{}) { this.ExtraData = data } func (this *Player) GetLastOPTimer() time.Time { return this.LastOPTimer } func (this *Player) SetLastOPTimer(lastOPTimer time.Time) { this.LastOPTimer = lastOPTimer } func (this *Player) GetPos() int { return this.Pos } func (this *Player) SetPos(pos int) { this.Pos = pos } func (this *Player) GetGameTimes() int32 { return this.GameTimes } func (this *Player) SetGameTimes(gameTimes int32) { this.GameTimes = gameTimes } func (this *Player) GetWinTimes() int { return this.winTimes } func (this *Player) SetWinTimes(winTimes int) { this.winTimes = winTimes } func (this *Player) GetLostTimes() int { return this.lostTimes } func (this *Player) SetLostTimes(lostTimes int) { this.lostTimes = lostTimes } func (this *Player) GetTotalBet() int64 { return this.TotalBet } func (this *Player) SetTotalBet(totalBet int64) { this.TotalBet = totalBet } func (this *Player) GetCurrentBet() int64 { return this.CurrentBet } func (this *Player) SetCurrentBet(currentBet int64) { this.CurrentBet = currentBet } func (this *Player) GetCurrentTax() int64 { return this.CurrentTax } func (this *Player) SetCurrentTax(currentTax int64) { this.CurrentTax = currentTax } func (this *Player) GetSid() int64 { return this.sid } func (this *Player) SetSid(sid int64) { this.sid = sid } func (this *Player) GetScene() *Scene { return this.scene } func (this *Player) GetGateSess() *netlib.Session { return this.gateSess } func (this *Player) SetGateSess(gateSess *netlib.Session) { this.gateSess = gateSess } func (this *Player) GetWorldSess() *netlib.Session { return this.worldSess } func (this *Player) SetWorldSess(worldSess *netlib.Session) { this.worldSess = worldSess } func (this *Player) GetCurrentCoin() int64 { return this.currentCoin } func (this *Player) SetCurrentCoin(currentCoin int64) { this.currentCoin = currentCoin } func (this *Player) GetTakeCoin() int64 { return this.takeCoin } func (this *Player) SetTakeCoin(takeCoin int64) { this.takeCoin = takeCoin } func (this *Player) GetFlag() int { return this.flag } func (this *Player) SetFlag(flag int) { this.flag = flag } func (this *Player) GetCity() string { return this.city } func (this *Player) SetCity(city string) { this.city = city } // 附加ai func (this *Player) AttachAI(ai AI) { this.ai = ai ai.SetOwner(this) ai.OnStart() } // 解除ai func (this *Player) UnattachAI() { ai := this.ai this.ai = nil ai.SetOwner(nil) ai.OnStop() } func (this *Player) RobotRandName() { if this.IsRob { //if rand.Int31n(100) < 60 { //随机昵称库里的名字 pool := srvdata.PBDB_NameMgr.Datas.GetArr() cnt := int32(len(pool)) if cnt > 0 { this.Name = pool[rand.Int31n(cnt)].GetName() } //} else { // this.Name = "Guest" //} } return } func (this *Player) RobRandVip() { if this.IsRob { dbvip := srvdata.PBDB_VIPMgr.GetData(this.VIP) if dbvip != nil { outlines := dbvip.GetRewardOutlineID() n := len(outlines) this.HeadOutLine = outlines[rand.Intn(n)] logger.Logger.Tracef("(this *Player) RobRandVip() %d HeadOutLine=%d", this.SnId, this.HeadOutLine) this.dirty = true } this.Head = rand.Int31n(common.HeadRange) //0:男 1:女 this.Sex = (this.Head%2 + 1) % 2 } } // BlackWhiteOdds 黑白名单调控概率 func (this *Player) BlackWhiteOdds(gameId int) (int32, bool) { if this.WBLevel == 0 { return 0, false } data := srvdata.PBDB_BlackWhiteMgr.GetData(int32(gameId)) if data == nil { return 0, false } var odds int32 var b bool if this.WBLevel > 0 && len(data.GetWhiteOdds()) > 0 { if int(this.WBLevel) > len(data.GetWhiteOdds()) { odds = data.GetWhiteOdds()[len(data.GetWhiteOdds())-1] b = true } else if this.WBLevel-1 >= 0 { odds = data.GetWhiteOdds()[this.WBLevel-1] b = true } } if this.WBLevel < 0 && len(data.GetBlackOdds()) > 0 { if int(-this.WBLevel) > len(data.GetBlackOdds()) { odds = data.GetBlackOdds()[len(data.GetBlackOdds())-1] b = true } else if -this.WBLevel-1 >= 0 { odds = data.GetBlackOdds()[-this.WBLevel-1] b = true } } //logger.Logger.Tracef("TienLenSceneData snid(%v) 黑白名单调控 是否启用:%v WBLevel:%v config:%v 概率:%v", this.SnId, b, this.WBLevel, data, odds) this.TestLog = append(this.TestLog, fmt.Sprintf("黑白名单调控 是否启用:%v WBTotalOut:%v WBTotalIn:%v WBCoinLimit:%v WBLevel:%v config:%v 概率:%v", b, this.WBCoinTotalOut, this.WBCoinTotalIn, this.WBCoinLimit, this.WBLevel, data, odds)) return odds, b } // NoviceOdds 新手补偿概率 func (this *Player) NoviceOdds(gameId int) (int32, bool) { data := srvdata.PBDB_NewPlayerMgr.GetData(int32(gameId)) if data == nil { return 0, false } if model.GameParamData.CloseNovice || common.InSliceInt(model.GameParamData.CloseNoviceGame, gameId) { return 0, false } keyNoviceGameId := common.GetKeyNoviceGameId(gameId) var b1, b2 bool var times, targetTimes int64 var winCoin, targetWinCoin int64 switch data.GetCondition1() { case 1: // 游戏次数 m := this.GDatas[keyNoviceGameId] if m == nil { b1 = true } else { b1 = m.Statics.GameTimes < data.GetConditionValue1() times = m.Statics.GameTimes } targetTimes = data.GetConditionValue1() case 2: // gameId输赢金额 m := this.GDatas[keyNoviceGameId] if m == nil { b1 = true } else { b1 = m.Statics.TotalOut-m.Statics.Tax-m.Statics.TotalIn < data.GetConditionValue1() winCoin = m.Statics.TotalOut - m.Statics.TotalIn } targetWinCoin = data.GetConditionValue1() } switch data.GetCondition2() { case 1: // 游戏次数 m := this.GDatas[keyNoviceGameId] if m == nil { b2 = true } else { b2 = m.Statics.GameTimes < data.GetConditionValue2() times = m.Statics.GameTimes } targetTimes = data.GetConditionValue2() case 2: // gameId输赢金额 m := this.GDatas[keyNoviceGameId] if m == nil { b2 = true } else { b2 = m.Statics.TotalOut-m.Statics.Tax-m.Statics.TotalIn < data.GetConditionValue2() winCoin = m.Statics.TotalOut - m.Statics.TotalIn } targetWinCoin = data.GetConditionValue2() } switch data.GetBond() { case 1: // or b1 = b1 || b2 case 2: // and b1 = b1 && b2 } var odds int if b1 { switch data.GetAddType() { case 1: // 游戏次数 odds = common.SliceMaxValue([]int{int(data.GetAddMin()), int(data.GetAddMax() - int64(float64(times)/float64(targetTimes)*float64(data.GetAddMax()-data.GetAddMin())))}) case 2: // gameId输赢金额 odds = common.SliceMaxValue([]int{int(data.GetAddMin()), int(data.GetAddMax() - int64(float64(winCoin)/float64(targetWinCoin)*float64(data.GetAddMax()-data.GetAddMin())))}) } odds = common.SliceMinValue([]int{int(data.GetAddMax()), odds}) } //logger.Logger.Tracef("TienLenSceneData snid(%v) 新手调控 是否启用:%v times:%v winCoin:%v config:%v 概率:%v", this.SnId, b1, times, winCoin, data, odds) this.TestLog = append(this.TestLog, fmt.Sprintf("新手调控 是否启用:%v times:%v winCoin:%v config:%v 概率:%v", b1, times, winCoin, data, odds)) return int32(odds), b1 } /*// 设置玩家捕鱼等级 func (this *Player) SetFishLevel(level int64) { data := srvdata.PBDB_PlayerExpMgr.GetData(int32(level)) if data == nil { logger.Logger.Errorf("设置玩家等级错误!snid = %v, lvel = %v", this.SnId, level) return } this.FishLevel = level this.FishExp = int64(data.Exp) }*/ // 增加玩家经验 func (this *Player) AddPlayerExp(exp int64) bool { this.Exp += exp if this.Exp >= int64(math.MaxInt64) { this.Exp = int64(math.MaxInt64) } maxExp := srvdata.PBDB_PlayerExpMgr.Datas.Arr[len(srvdata.PBDB_PlayerExpMgr.Datas.Arr)-1].Exp if this.Exp > int64(maxExp) { this.Exp = int64(maxExp) } //oldLevel := this.Level //获取等级 for _, playerExp := range srvdata.PBDB_PlayerExpMgr.Datas.Arr { if this.Exp >= int64(playerExp.Exp) { this.Level = int64(playerExp.Id) } else { break } } pack := &player.SCPlayerUpLevel{ Level: this.Level, Exp: this.Exp, } //通知客户端玩家提升等级 this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PlayerUpLevel), pack) return true } // 解锁炮倍 func (this *Player) UnPlayerPowerEx(power int64) { logger.Logger.Tracef("解锁炮倍 当前最大解锁炮倍:%v,要解锁的炮倍:%v", this.UnMaxPower, power) if this.UnPlayerPower(power) { pack := &player.SCPlayerUnPower{ UnMaxpower: power, } this.UnMaxPower = power this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PlayerUnPower), pack) logger.Logger.Tracef("通知客户端解锁最大炮倍,snid = %v,power = %v", this.SnId, power) } } // 解锁炮台 func (this *Player) UnPlayerPowerListEx(powerId int32) { data := srvdata.PBDB_ArtillerySkinMgr.GetData(powerId) if data == nil { return } if this.UnPlayerPowerList(powerId) { //通知客户端解锁炮台 pack := &player.SCPlayerUnPowerList{ UnPowerList: powerId, } this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PlayerUnPowerList), pack) logger.Logger.Trace("通知客户端解锁炮台 snid = %v,解锁的炮台:%v,当前已有的炮台 = %v", this.SnId, powerId, this.PowerList) } return } // 获取周卡权益 // typeId : 1-破产救济金领取翻倍 2-排位赛积分提升5% func (this *Player) GetWeekCardPrivilege(typeId int32) bool { logger.Logger.Trace("玩家请求获取周卡权益!") now := time.Now().Unix() for id, endTime := range this.WeekCardTime { if endTime > now { data := srvdata.PBDB_GiftCardMgr.GetData(id) if data.Equity == typeId { return true } } } return false }