package main import ( "mongo.games.com/goserver/core/logger" "mongo.games.com/game/common" "mongo.games.com/game/model" gamehallproto "mongo.games.com/game/protocol/gamehall" serverproto "mongo.games.com/game/protocol/server" "mongo.games.com/game/srvdata" ) // CoinScenePool 房间池 type CoinScenePool struct { platform string // 平台id dbGameFree *serverproto.DB_GameFree // 场次配置 dbGameRule *serverproto.DB_GameRule // 场次配置 scenes map[int]*Scene // 所有房间,房间id players map[int32]struct{} // 玩家id // 扩展数据 extraData interface{} // 房间池规则 policy ICoinScenePool } func newCoinScenePool(platform string, dbGameFree *serverproto.DB_GameFree) *CoinScenePool { if dbGameFree == nil { return nil } if platform == "" { platform = DefaultPlatform } dbGameRule := srvdata.PBDB_GameRuleMgr.GetData(dbGameFree.GetGameRule()) if dbGameRule == nil { logger.Logger.Errorf("Coin scene pool init failed,%v game rule data not find.", dbGameFree.GetGameRule()) return nil } csp := &CoinScenePool{ platform: platform, dbGameFree: dbGameFree, dbGameRule: dbGameRule, scenes: make(map[int]*Scene), players: make(map[int32]struct{}), policy: new(BaseCoinScenePool), // 默认方式 } policy := GetCoinScenePool(dbGameFree.GetGameId()) if policy != nil { csp.policy = policy csp.extraData = policy.New() } return csp } func (csp *CoinScenePool) ID() int32 { return csp.dbGameFree.Id } func (csp *CoinScenePool) GetPlayerNum() int32 { return int32(len(csp.players)) } func (csp *CoinScenePool) GetFakePlayerNum() int32 { //if csp.dbGameFree != nil { // correctNum := csp.dbGameFree.GetCorrectNum() // correctRate := csp.dbGameFree.GetCorrectRate() // count := csp.GetPlayerNum() // return correctNum + count*correctRate/100 + csp.numDeviation //} return 0 } // GetSceneType 获取场次类型 // 新手场,中级场 ... func (csp *CoinScenePool) GetSceneType() int { if csp.dbGameFree != nil { return int(csp.dbGameFree.GetSceneType()) } return 0 } // CanInviteRob 能否邀请机器人 func (csp *CoinScenePool) CanInviteRob() bool { if csp.dbGameFree != nil { return csp.dbGameFree.GetBot() != 0 } return false } // PreCreateRoom 预创建房间 func (csp *CoinScenePool) PreCreateRoom() { if csp.platform == DefaultPlatform || model.GameParamData.ClosePreCreateRoom { return } if p := PlatformMgrSingleton.GetPlatform(csp.platform); p == nil || p.Disable { return } preCreateNum := int(csp.dbGameFree.GetCreateRoomNum()) if preCreateNum <= 0 { return } num := preCreateNum - csp.GetRoomNum(common.SceneModePublic) if num > 0 { logger.Logger.Tracef("预创建房间 [inc:%v] platform:%v gameFreeId:%v", num, csp.platform, csp.dbGameFree.Id) for i := 0; i < num; i++ { scene := csp.policy.NewPreCreateScene(csp) if scene != nil { csp.AddScene(scene) } } } } func (csp *CoinScenePool) GetRoomNum(mode ...int) int { tp := 0 if len(mode) > 0 { tp = mode[0] } var num int for _, scene := range csp.scenes { if tp > 0 { if scene.IsSceneMode(tp) { num++ } } else { num++ } } return num } // GetHasTruePlayerSceneCnt 有真人的房间数量 func (csp *CoinScenePool) GetHasTruePlayerSceneCnt() int { cnt := 0 for _, s := range csp.scenes { if s.GetTruePlayerCnt() != 0 { cnt++ } } return cnt } // AddScene 添加房间 // 自定义房间参数的时候创建房间后添加到房间池 // 创建房间后,调用AddScene添加到房间池,在玩家进入房间之前调用 func (csp *CoinScenePool) AddScene(s *Scene) { if s == nil { return } csp.scenes[s.sceneId] = s s.csp = csp } // canEnter 检查入场条件 func (csp *CoinScenePool) canEnter(p *Player) gamehallproto.OpResultCode { if csp.dbGameFree == nil || p == nil { return gamehallproto.OpResultCode_OPRC_Error } //检测房间状态是否开启 gps := PlatformMgrSingleton.GetGameFree(p.Platform, csp.ID()) if gps == nil || !gps.Status { return gamehallproto.OpResultCode_OPRC_RoomHadClosed } dbGameFree := csp.dbGameFree if dbGameFree == nil { return gamehallproto.OpResultCode_OPRC_RoomHadClosed } // 检查游戏次数限制 if !p.IsRob { todayData, _ := p.GetDaliyGameData(int(dbGameFree.GetId())) if dbGameFree.GetPlayNumLimit() != 0 && todayData != nil && todayData.GameTimes >= int64(dbGameFree.GetPlayNumLimit()) { return gamehallproto.OpResultCode_OPRC_RoomGameTimes } } // 具体场次的入场规则 canEnterFunc := GetCoinSceneCanEnterFunc(int(dbGameFree.GetGameId())) if canEnterFunc != nil { ret := canEnterFunc(csp, p) if ret != gamehallproto.OpResultCode_OPRC_Sucess { return ret } } // 通用入场检测 if dbGameFree.GetLimitCoin() != 0 && dbGameFree.GetLimitCoin() > p.Coin && !p.IsRob { return gamehallproto.OpResultCode_OPRC_CoinNotEnough } if dbGameFree.GetMaxCoinLimit() != 0 && dbGameFree.GetMaxCoinLimit() < p.Coin && !p.IsRob { return gamehallproto.OpResultCode_OPRC_CoinTooMore } return csp.policy.CanEnter(csp, p) } // canAudienceEnter 检查观众入场条件 func (csp *CoinScenePool) canAudienceEnter(p *Player) gamehallproto.OpResultCode { if csp.dbGameFree == nil || p == nil { return gamehallproto.OpResultCode_OPRC_Error } //检测房间状态是否开启 gps := PlatformMgrSingleton.GetGameFree(p.Platform, csp.ID()) if gps == nil { return gamehallproto.OpResultCode_OPRC_RoomHadClosed } dbGameFree := csp.dbGameFree if dbGameFree == nil { return gamehallproto.OpResultCode_OPRC_RoomHadClosed } return csp.policy.CanAudienceEnter(csp, p) } // playerEnter 玩家进入房间池 // exclude 排除的房间id // isChangeRoom 是否换房 func (csp *CoinScenePool) playerEnter(p *Player, roomId int32, exclude []int32, isChangeRoom bool) gamehallproto.OpResultCode { if ret := csp.canEnter(p); ret != gamehallproto.OpResultCode_OPRC_Sucess { logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter find snid:%v csp.canEnter coin:%v ret:%v id:%v", p.SnId, p.Coin, ret, csp.dbGameFree.GetId()) return ret } var scene *Scene // 进入房间 // 指定房间id进入,忽略排除exclude,只有机器人和进入预创建房间才允许 if roomId != 0 && (p.IsRob || csp.dbGameFree.GetCreateRoomNum() != 0) { if s, ok := csp.scenes[int(roomId)]; ok { if s != nil && !s.deleting { //指定房间id进入,那么忽略掉排除id if s.IsFull() { return gamehallproto.OpResultCode_OPRC_RoomIsFull } if s.sp.CanEnter(s, p.SnId) == 0 { scene = s } else { logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter[!s.starting || sp.EnterAfterStart] snid:%v sceneid:%v starting:%v EnterAfterStart:%v", p.SnId, s.sceneId, s.starting, s.sp.CanEnter(s, p.SnId)) return gamehallproto.OpResultCode_OPRC_Error } } } else { logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter(robot:%v,roomid:%v, exclude:%v, isChangeRoom:%v) no found scene", p.SnId, roomId, exclude, isChangeRoom) return gamehallproto.OpResultCode_OPRC_Error } } if scene == nil { var ret gamehallproto.OpResultCode ret, scene = csp.policy.PlayerEnter(csp, p, exclude, isChangeRoom) if ret != gamehallproto.OpResultCode_OPRC_Sucess { return ret } } // 没有找到房间,创建新房间 if scene == nil { scene = csp.policy.NewScene(csp, p) if scene != nil { logger.Logger.Infof("(csp *CoinScenePool) PlayerEnter create new scene:%v snid:%v gamefreeid:%v", scene.sceneId, p.SnId, csp.ID()) csp.AddScene(scene) } else { logger.Logger.Errorf("Create %v scene failed.", csp.ID()) } } if scene != nil { if scene.PlayerEnter(p, -1, isChangeRoom) { logger.Logger.Infof("(csp *CoinScenePool) PlayerEnter snid:%v sceneid:%v gamefreeid:%v success", p.SnId, scene.sceneId, csp.ID()) csp.onPlayerEnter(p, scene) return gamehallproto.OpResultCode_OPRC_Sucess } } logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter snid:%v not found scene", p.SnId) return gamehallproto.OpResultCode_OPRC_SceneServerMaintain } // audienceEnter 观众入场 func (csp *CoinScenePool) audienceEnter(p *Player, roomId int32, exclude []int32, isChangeRoom bool) gamehallproto.OpResultCode { if ret := csp.canAudienceEnter(p); ret != gamehallproto.OpResultCode_OPRC_Sucess { logger.Logger.Warnf("(csp *CoinScenePool) AudienceEnter find snid:%v csp.canEnter coin:%v ret:%v id:%v", p.SnId, p.Coin, ret, csp.dbGameFree.GetId()) return ret } var scene *Scene if roomId != 0 { if s, ok := csp.scenes[int(roomId)]; ok { if s != nil && !s.deleting { scene = s } else { logger.Logger.Warnf("(csp *CoinScenePool) AudienceEnter[!s.starting || sp.EnterAfterStart] snid:%v sceneid:%v starting:%v EnterAfterStart:%v", p.SnId, s.sceneId, s.starting) } } } if scene == nil { var ret gamehallproto.OpResultCode ret, scene = csp.policy.AudienceEnter(csp, p, exclude, isChangeRoom) if ret != gamehallproto.OpResultCode_OPRC_Sucess { return ret } } if scene == nil { return gamehallproto.OpResultCode_OPRC_NoFindDownTiceRoom } // 预创建房间检查观众数量 if scene.IsPreCreateScene() && scene.GetAudienceCnt() >= model.GameParamData.MaxAudienceNum { return gamehallproto.OpResultCode_OPRC_RoomIsFull } if scene.AudienceEnter(p, isChangeRoom) { logger.Logger.Infof("(csp *CoinScenePool) AudienceEnter snid:%v sceneid:%v gamefreeid:%v success", p.SnId, scene.sceneId, csp.ID()) csp.onPlayerEnter(p, scene) return gamehallproto.OpResultCode_OPRC_Sucess } logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter snid:%v not found scene", p.SnId) return gamehallproto.OpResultCode_OPRC_NoFindDownTiceRoom } // onPlayerEnter 玩家进入房间完成 func (csp *CoinScenePool) onPlayerEnter(p *Player, scene *Scene) { csp.players[p.SnId] = struct{}{} csp.policy.OnPlayerEnter(csp, p, scene) } // OnPlayerLeave 离开房间完成 func (csp *CoinScenePool) onPlayerLeave(s *Scene, p *Player) { if s == nil || p == nil { return } delete(csp.players, p.SnId) // 玩家离开结算空房间的私人房 if s.IsPrivateScene() { if s.IsEmpty() { s.SendGameDestroy(false) } return } // 解散空房间并且房间数量大于预创建房间数量 if s.IsPreCreateScene() { if s.IsEmpty() { var hasCnt int for _, scene := range csp.scenes { if s.platform.IdStr == scene.platform.IdStr { hasCnt++ } } if hasCnt > int(csp.dbGameFree.GetCreateRoomNum()) { s.SendGameDestroy(false) } } } } // playerLeave 玩家离开房间 func (csp *CoinScenePool) playerLeave(p *Player, reason int) bool { if p.scene != csp.scenes[p.scene.sceneId] { logger.Logger.Error("bug") } s, ok := csp.scenes[p.scene.sceneId] if !ok || s == nil { return false } if !s.HasPlayer(p) { return false } if !csp.policy.PlayerLeave(csp, p, reason) { return false } s.PlayerLeave(p, reason) logger.Logger.Tracef("(csp *CoinScenePool) PlayerLeave snid:%v in scene:%v", p.SnId, s.sceneId) csp.policy.OnPlayerLeave(csp, s, p) csp.onPlayerLeave(s, p) return true } // audienceLeave 观众离开房间 func (csp *CoinScenePool) audienceLeave(p *Player, reason int) bool { s, ok := csp.scenes[p.scene.sceneId] if !ok || s == nil { return false } if !s.HasAudience(p) { return false } if !csp.policy.AudienceLeave(csp, p, reason) { return false } s.AudienceLeave(p, reason) logger.Logger.Tracef("(csp *CoinScenePool) AudienceLeave snid:%v in scene:%v", p.SnId, s.sceneId) csp.policy.OnPlayerLeave(csp, s, p) csp.onPlayerLeave(s, p) return true } // onDestroyScene 解散房间 // 房间解散一定是游戏服确认的,worldsrv收到游戏房间解散消息后解散房间,此时房间内玩家应该都离开了 // 或者游戏服异常断开触发房间解散,此时房间中还有人 func (csp *CoinScenePool) onDestroyScene(sceneId int) { scene, ok := csp.scenes[sceneId] if !ok || scene == nil { return } logger.Logger.Tracef("(csp *CoinScenePool) OnDestroyScene scene:%v", sceneId) // todo 是否需要优化 // 游戏服异常断开,同步一次金币 for id := range scene.players { player := PlayerMgrSington.GetPlayerBySnId(id) if player != nil { if !player.IsRob { ctx := scene.GetPlayerGameCtx(player.SnId) if ctx != nil { //发送一个探针,等待ack后同步金币 player.TryRetrieveLostGameCoin(sceneId) } } } } for id := range scene.audiences { player := PlayerMgrSington.GetPlayerBySnId(id) if player != nil { if !player.IsRob { ctx := scene.GetPlayerGameCtx(player.SnId) if ctx != nil { //发送一个探针,等待ack后同步金币 player.TryRetrieveLostGameCoin(sceneId) } } } } csp.policy.OnDestroyScene(csp, sceneId) for k := range scene.players { delete(csp.players, k) } for k := range scene.audiences { delete(csp.players, k) } scene.csp = nil // 解除关联 delete(csp.scenes, sceneId) CoinSceneMgrSingleton.TouchCreateRoom(csp.platform, csp.dbGameFree.Id) }