package main import ( "math/rand" "time" "mongo.games.com/goserver/core" "mongo.games.com/goserver/core/logger" "mongo.games.com/goserver/core/module" "mongo.games.com/goserver/core/transact" "mongo.games.com/game/common" "mongo.games.com/game/model" "mongo.games.com/game/proto" hallproto "mongo.games.com/game/protocol/gamehall" "mongo.games.com/game/protocol/server" "mongo.games.com/game/protocol/webapi" "mongo.games.com/game/srvdata" ) var CoinSceneMgrSingleton = &CoinSceneMgr{ playerChanging: make(map[int32]int32), scenesOfPlatform: make(map[string]map[int32]*CoinScenePool), } type CreateRoomCache struct { gameFreeId int32 platformName string } type CoinSceneMgr struct { scenesOfPlatform map[string]map[int32]*CoinScenePool // 场次房间池 platform:gamefreeid playerChanging map[int32]int32 // 换桌中的玩家 snid:gamefreeid //延迟创建房间列表 delayCache []*CreateRoomCache // 待预创建房间 } // GetCoinScenePool 获取一个场景池 // plt 平台id // id 场次id func (m *CoinSceneMgr) GetCoinScenePool(plt string, id int32) *CoinScenePool { gf := PlatformMgrSingleton.GetGameFree(plt, id) if gf == nil || gf.DbGameFree == nil { return nil } if ss, ok := m.scenesOfPlatform[plt]; ok { if csp, ok := ss[id]; ok && csp != nil { return csp } } // 创建了一个新的 // 模块启动时所有场次都创建了房间池 pool := newCoinScenePool(plt, gf.GetDbGameFree()) v, ok := m.scenesOfPlatform[plt] if !ok || v == nil { m.scenesOfPlatform[plt] = make(map[int32]*CoinScenePool) } m.scenesOfPlatform[plt][id] = pool return pool } // findCoinScenePool 查找指定平台和ID的场景池 // platform 平台ID // id 场次ID // 返回场景池map和对应的游戏场次配置 func (m *CoinSceneMgr) findCoinScenePool(platform string, id int32) (pools map[int32]*CoinScenePool, dbGameFree *server.DB_GameFree) { gf := PlatformMgrSingleton.GetGameFree(platform, id) if gf == nil || gf.GetDbGameFree() == nil { return nil, nil } ss, ok := m.scenesOfPlatform[platform] if !ok || ss == nil { ss = make(map[int32]*CoinScenePool) m.scenesOfPlatform[platform] = ss } return ss, gf.GetDbGameFree() } // PlayerEnter 玩家进入房间池 // 参数: // - p: 玩家对象 // - id: 场次ID // - roomId: 房间ID,为0时随机分配房间 // - exclude: 排除的房间ID列表 // - isChangeRoom: 是否为换房操作 // // 返回值: // - hallproto.OpResultCode: 操作结果代码 func (m *CoinSceneMgr) PlayerEnter(p *Player, id int32, roomId int32, exclude []int32, isChangeRoom bool) hallproto.OpResultCode { logger.Logger.Tracef("CoinSceneMgr PlayerEnter snid:%v id:%v roomid:%v exclude:%v", p.SnId, id, roomId, exclude) //删档用户不让进游戏 if p.isDelete { return hallproto.OpResultCode_OPRC_RoomHadClosed } // 玩家已经在房间里了 if m.InCoinScene(p) { logger.Logger.Warnf("CoinSceneMgr PlayerEnter snid:%v find in gamefreeid:%v roomId:%v", p.SnId, p.scene.dbGameFree.Id, p.scene.sceneId) return hallproto.OpResultCode_OPRC_Error } //多平台支持 platform := p.GetPlatform() if platform == nil { return hallproto.OpResultCode_OPRC_RoomHadClosed } csp := m.GetCoinScenePool(platform.IdStr, id) ret := csp.playerEnter(p, roomId, exclude, isChangeRoom) logger.Logger.Tracef("CoinSceneMgr PlayerEnter snid:%v id:%v ret:%v", p.SnId, id, ret) return ret } // AudienceEnter 观众进入房间 // 参数: // - p: 玩家对象 // - id: 场次ID // - roomId: 房间ID,为0时随机分配房间 // - exclude: 排除的房间ID列表 // - ischangeroom: 是否为换房操作 // // 返回值: // - hallproto.OpResultCode: 操作结果代码 func (m *CoinSceneMgr) AudienceEnter(p *Player, id int32, roomId int32, exclude []int32, ischangeroom bool) hallproto.OpResultCode { logger.Logger.Tracef("CoinSceneMgr AudienceEnter snid:%v id:%v roomid:%v exclude:%v", p.SnId, id, roomId, exclude) //删档用户不让进游戏 if p.isDelete { return hallproto.OpResultCode_OPRC_RoomHadClosed } if m.InCoinScene(p) { logger.Logger.Warnf("CoinSceneMgr AudienceEnter snid:%v find in gamefreeid:%v roomId:%v", p.SnId, p.scene.dbGameFree.Id, p.scene.sceneId) return hallproto.OpResultCode_OPRC_Error } //多平台支持 platform := p.GetPlatform() if platform == nil { return hallproto.OpResultCode_OPRC_RoomHadClosed } pools, _ := m.findCoinScenePool(platform.IdStr, id) if pools == nil { return hallproto.OpResultCode_OPRC_RoomHadClosed } if len(pools) == 0 { return hallproto.OpResultCode_OPRC_NoFindDownTiceRoom } csp, ok := pools[id] if !ok || csp == nil { return hallproto.OpResultCode_OPRC_RoomHadClosed } ret := csp.audienceEnter(p, roomId, exclude, ischangeroom) logger.Logger.Tracef("CoinSceneMgr AudienceEnter snid:%v id:%v ret:%v", p.SnId, id, ret) return ret } // PlayerLeave 玩家离开 // 游戏服玩家离开房间消息触发 func (m *CoinSceneMgr) PlayerLeave(p *Player, reason int) bool { logger.Logger.Tracef("玩家离开: snid %v, reason %v", p.SnId, reason) if p.scene == nil || p.scene.csp == nil { return false } if p.scene.csp.playerLeave(p, reason) { return true } if p.scene.csp.audienceLeave(p, reason) { return true } return false } // OnDestroyScene 解散房间 // 游戏服解散房间消息触发 // sceneId 房间id func (m *CoinSceneMgr) OnDestroyScene(sceneId int) { if s := SceneMgrSingleton.GetScene(sceneId, true); s != nil && s.csp != nil { s.csp.onDestroyScene(sceneId) } } // GetPlayerNums 获取场次人数 func (m *CoinSceneMgr) GetPlayerNums(p *Player, gameId, gameMode int32) []int32 { //多平台支持 platform := p.GetPlatform() var nums [10]int32 wantNum := [10]int32{80, 50, 30, 20, 10, 10, 10, 10, 10, 10} for i := 0; i < 10; i++ { if wantNum[i]/2 > 0 { nums[i] = rand.Int31n(wantNum[i]/2) + wantNum[i] } } if platform == nil { return nums[:] } ids, _ := srvdata.GameFreeMgr.GetGameFreeIds(gameId, gameMode) for _, id := range ids { gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, id) if gps != nil { if ss, ok := m.scenesOfPlatform[platform.IdStr]; ok { if csp, exist := ss[id]; exist { sceneType := csp.GetSceneType() - 1 if sceneType >= 0 && sceneType < len(nums) { nums[sceneType] += csp.GetPlayerNum() + csp.GetFakePlayerNum() } } } } } if len(ids) <= 10 { return nums[:len(ids)] } return nums[:] } // InCoinScene 检查玩家是否在场次中 func (m *CoinSceneMgr) InCoinScene(p *Player) bool { if p == nil { logger.Logger.Tracef("(m *CoinSceneMgr) InCoinScene p == nil") return false } if p.scene == nil { return false } if p.scene.csp == nil { return false } return true } // PlayerTryLeave 通知游戏服务玩家要离开房间 func (m *CoinSceneMgr) PlayerTryLeave(p *Player, isAudience bool) hallproto.OpResultCode { if !m.InCoinScene(p) { logger.Logger.Tracef("(m *CoinSceneMgr) PlayerTryLeave !m.InCoinScene(p) snid:%v ", p.SnId) return hallproto.OpResultCode_OPRC_Sucess } // 通知gamesrv op := common.CoinSceneOp_Leave if isAudience { op = common.CoinSceneOp_AudienceLeave } pack := &hallproto.CSCoinSceneOp{ Id: proto.Int32(p.scene.csp.ID()), OpType: proto.Int32(op), } common.TransmitToServer(p.sid, int(hallproto.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), pack, p.scene.gameSess.Session) logger.Logger.Tracef("(m *CoinSceneMgr) PlayerTryLeave snid:%v id:%v", p.SnId, pack.GetId()) return hallproto.OpResultCode_OPRC_OpYield } // PlayerInChanging 检查玩家是否正在换房中 func (m *CoinSceneMgr) PlayerInChanging(p *Player) bool { _, exist := m.playerChanging[p.SnId] return exist } // ClearPlayerChanging 清除玩家换房状态 func (m *CoinSceneMgr) ClearPlayerChanging(p *Player) { delete(m.playerChanging, p.SnId) } // PlayerTryChange 玩家尝试换房 // id 场次id // excludeRoomId 排除的房间id // isAudience 是否是观众 func (m *CoinSceneMgr) PlayerTryChange(p *Player, id int32, excludeRoomId []int32, isAudience bool) hallproto.OpResultCode { if m.InCoinScene(p) { return m.StartChangeCoinSceneTransact(p, id, excludeRoomId, isAudience) } // 不在场次中,进入房间观战 if isAudience { return m.AudienceEnter(p, id, 0, excludeRoomId, true) } // 不在场次中,进入房间 return m.PlayerEnter(p, id, 0, excludeRoomId, true) } // StartChangeCoinSceneTransact 开始换房事务 func (m *CoinSceneMgr) StartChangeCoinSceneTransact(p *Player, id int32, exclude []int32, isAudience bool) hallproto.OpResultCode { if p == nil || p.scene == nil { logger.Logger.Warnf("(m *CoinSceneMgr) StartChangeCoinSceneTransact p == nil || p.scene == nil snid:%v id:%v", p.SnId, id) return hallproto.OpResultCode_OPRC_Error } tNow := time.Now() if !p.lastChangeScene.IsZero() && tNow.Sub(p.lastChangeScene) < time.Second { logger.Logger.Warnf("(m *CoinSceneMgr) StartChangeCoinSceneTransact !p.lastChangeScene.IsZero() && tNow.Sub(p.lastChangeScene) < time.Second snid:%v id:%v", p.SnId, id) return hallproto.OpResultCode_OPRC_ChangeRoomTooOften } tnp := &transact.TransNodeParam{ Tt: common.TransTypeCoinSceneChange, Ot: transact.TransOwnerType(common.GetSelfSrvType()), Oid: common.GetSelfSrvId(), AreaID: common.GetSelfAreaId(), } ctx := &CoinSceneChangeCtx{ id: id, isClub: false, snid: p.SnId, sceneId: int32(p.scene.sceneId), exclude: exclude, isAudience: isAudience, } tNode := transact.DTCModule.StartTrans(tnp, ctx, CoinSceneChangeTimeOut) if tNode != nil { tNode.Go(core.CoreObject()) m.playerChanging[p.SnId] = id p.lastChangeScene = tNow return hallproto.OpResultCode_OPRC_Sucess } logger.Logger.Warnf("(m *CoinSceneMgr) StartChangeCoinSceneTransact tNode == nil snid:%v id:%v", p.SnId, id) return hallproto.OpResultCode_OPRC_Error } // TouchCreateRoom 触发预创建房间 // 1.模块启动后触发 // 2.游戏服建立连接后触发 // 3.房间解散后触发 // 4.场次配置更新后 func (m *CoinSceneMgr) TouchCreateRoom(platform string, gameFreeId int32) { if model.GameParamData.ClosePreCreateRoom { return } gf := PlatformMgrSingleton.GetGameFree(platform, gameFreeId) if gf.Status && gf.DbGameFree.GetCreateRoomNum() > 0 { logger.Logger.Tracef("TouchCreateRoom platform:%v gameFreeId:%v", platform, gameFreeId) m.delayCache = append(m.delayCache, &CreateRoomCache{ platformName: platform, gameFreeId: gameFreeId, }) } } func (m *CoinSceneMgr) ModuleName() string { return "CoinSceneMgr" } func (m *CoinSceneMgr) Init() { // 房间池初始化 for _, platform := range PlatformMgrSingleton.GetPlatforms() { if platform.Isolated || platform.IdStr == "" { for _, v := range srvdata.PBDB_GameFreeMgr.Datas.GetArr() { gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, v.GetId()) if gps != nil { m.GetCoinScenePool(platform.IdStr, v.GetId()) m.TouchCreateRoom(platform.IdStr, v.GetId()) } } } } } func (m *CoinSceneMgr) Update() { cnt := len(m.delayCache) if cnt > 0 { data := m.delayCache[cnt-1] m.delayCache = m.delayCache[:cnt-1] gf := PlatformMgrSingleton.GetGameFree(data.platformName, data.gameFreeId) if gf != nil && gf.DbGameFree != nil { csp := m.GetCoinScenePool(data.platformName, data.gameFreeId) if csp != nil { csp.PreCreateRoom() } } } } func (m *CoinSceneMgr) Shutdown() { module.UnregisteModule(m) } //=====================PlatformObserver====================== func (m *CoinSceneMgr) OnPlatformCreate(p *Platform) { } func (m *CoinSceneMgr) OnPlatformDestroy(p *Platform) { if p == nil { return } var ids []int if v, ok := m.scenesOfPlatform[p.IdStr]; ok { for _, csp := range v { for _, scene := range csp.scenes { ids = append(ids, scene.sceneId) } } } SceneMgrSingleton.SendGameDestroy(ids, true) } func (m *CoinSceneMgr) OnPlatformChangeDisabled(p *Platform, disabled bool) { if disabled { m.OnPlatformDestroy(p) } } func (m *CoinSceneMgr) OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapi.GameFree) { if p == nil || newCfg == nil { return } ss, ok := m.scenesOfPlatform[p.IdStr] if !ok || ss == nil { return } if cps, ok := ss[newCfg.DbGameFree.Id]; ok { cps.dbGameFree = newCfg.DbGameFree var ids []int for _, scene := range cps.scenes { ids = append(ids, scene.sceneId) } SceneMgrSingleton.SendGameDestroy(ids, true) m.TouchCreateRoom(p.IdStr, newCfg.DbGameFree.Id) } } func (m *CoinSceneMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { if p == nil { return } var ids []int if v, ok := m.scenesOfPlatform[p.IdStr]; ok { for _, csp := range v { for _, scene := range csp.scenes { if scene.dbGameFree.Id == gameFreeId { ids = append(ids, scene.sceneId) } } } } SceneMgrSingleton.SendGameDestroy(ids, true) } //=========================PlatformGameGroupObserver============================== func (m *CoinSceneMgr) OnGameGroupUpdate(oldCfg, newCfg *webapi.GameConfigGroup) { } //=========================GameSessionListener====================================== func (m *CoinSceneMgr) OnGameSessionRegiste(gs *GameSession) { wildGs := len(gs.gameIds) == 0 || common.InSliceInt32(gs.gameIds, 0) // 是否所有游戏都支持 for _, platform := range PlatformMgrSingleton.GetPlatforms() { if platform.IdStr == DefaultPlatform { continue } //获取配置 gps := PlatformMgrSingleton.GetGameFrees(platform.IdStr) for _, v := range gps { if v != nil && (wildGs || common.InSliceInt32(gs.gameIds, v.DbGameFree.GetGameId())) { m.TouchCreateRoom(platform.IdStr, v.DbGameFree.Id) } } } } func (m *CoinSceneMgr) OnGameSessionUnregiste(gs *GameSession) { //todo 游戏服务断开,是否解散房间? } func init() { module.RegisteModule(CoinSceneMgrSingleton, time.Second*2, 0) PlatformMgrSingleton.RegisterObserver(CoinSceneMgrSingleton) PlatformGameGroupMgrSington.RegisteObserver(CoinSceneMgrSingleton) RegisteGameSessionListener(CoinSceneMgrSingleton) }