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/proto" hall_proto "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), platformOfScene: make(map[int]string), //按组管理 scenesOfGroup: make(map[int32]map[int32]*CoinScenePool), groupOfScene: make(map[int]int32), } type CreateRoomCache struct { gameFreeId int32 platformName string } type CoinSceneMgr struct { playerChanging map[int32]int32 // snid:gamefreeid 换桌中的玩家 //按平台管理 scenesOfPlatform map[string]map[int32]*CoinScenePool // platform:gamefreeid platformOfScene map[int]string // sceneid:platform; 创建房间后记录房间所在平台 //按组管理 scenesOfGroup map[int32]map[int32]*CoinScenePool // groupid:gamefreeid groupOfScene map[int]int32 // sceneid:groupid; //延迟创建房间列表 delayCache []*CreateRoomCache // 待预创建房间 } // GetCoinScenePool 获取一个场景池 // plt 平台id // id 场次id func (csm *CoinSceneMgr) GetCoinScenePool(plt string, id int32) *CoinScenePool { gf := PlatformMgrSingleton.GetGameFree(plt, id) if gf == nil { return nil } groupId := gf.GetGroupId() if groupId != 0 { if _, ok := csm.scenesOfGroup[groupId]; ok { if csp, ok := csm.scenesOfGroup[groupId][id]; ok { return csp } } } else { if ss, ok := csm.scenesOfPlatform[plt]; ok { if csp, ok := ss[id]; ok && csp != nil { return csp } } } if gf.GetDbGameFree() == nil { return nil } // 创建了一个新的 // 应该是走不到这里,因为模块启动时所有场次都创建了房间池 pool := newCoinScenePool(plt, groupId, gf.GetDbGameFree()) if groupId != 0 { v, ok := csm.scenesOfGroup[groupId] if !ok || v == nil { csm.scenesOfGroup[groupId] = make(map[int32]*CoinScenePool) } csm.scenesOfGroup[groupId][id] = pool } else { v, ok := csm.scenesOfPlatform[plt] if !ok || v == nil { csm.scenesOfPlatform[plt] = make(map[int32]*CoinScenePool) } csm.scenesOfPlatform[plt][id] = pool } return pool } func (csm *CoinSceneMgr) findCoinScenePool(platform string, id int32) (pools map[int32]*CoinScenePool, groupID int32, dbGameFree *server.DB_GameFree) { gf := PlatformMgrSingleton.GetGameFree(platform, id) if gf == nil || gf.GetDbGameFree() == nil { return nil, 0, nil } groupId := gf.GetGroupId() var ss map[int32]*CoinScenePool var ok bool if groupId != 0 { ss, ok = csm.scenesOfGroup[groupId] if !ok { ss = make(map[int32]*CoinScenePool) csm.scenesOfGroup[groupId] = ss } return ss, groupId, gf.GetDbGameFree() } ss, ok = csm.scenesOfPlatform[platform] if !ok { ss = make(map[int32]*CoinScenePool) csm.scenesOfPlatform[platform] = ss } return ss, 0, gf.GetDbGameFree() } // PlayerEnter 玩家进入房间池 func (csm *CoinSceneMgr) PlayerEnter(p *Player, id int32, roomId int32, exclude []int32, isChangeRoom bool) hall_proto.OpResultCode { logger.Logger.Tracef("(csm *CoinSceneMgr) PlayerEnter snid:%v id:%v roomid:%v exclude:%v", p.SnId, id, roomId, exclude) if p.isDelete { //删档用户不让进游戏 return hall_proto.OpResultCode_OPRC_RoomHadClosed } //多平台支持 platform := p.GetPlatform() if platform == nil { return hall_proto.OpResultCode_OPRC_RoomHadClosed } // 玩家已经在房间里了 if p.scene != nil { logger.Logger.Warnf("(csm *CoinSceneMgr) PlayerEnter snid:%v find in gameId:%v gamefreeid:%v", p.SnId, p.scene.gameId, id) return hall_proto.OpResultCode_OPRC_Error } csp := csm.GetCoinScenePool(platform.IdStr, id) ret := csp.PlayerEnter(p, roomId, exclude, isChangeRoom) logger.Logger.Warnf("(csm *CoinSceneMgr) PlayerEnter snid:%v find in id:%v exclude:%v return false", p.SnId, id, exclude) return ret } // AudienceEnter 观众进入房间 func (csm *CoinSceneMgr) AudienceEnter(p *Player, id int32, roomId int32, exclude []int32, ischangeroom bool) hall_proto.OpResultCode { //多平台支持 platform := p.GetPlatform() if platform == nil { return hall_proto.OpResultCode_OPRC_RoomHadClosed } pools, _, _ := csm.findCoinScenePool(platform.IdStr, id) if pools == nil { return hall_proto.OpResultCode_OPRC_RoomHadClosed } if len(pools) == 0 { return hall_proto.OpResultCode_OPRC_NoFindDownTiceRoom } if csp, ok := pools[id]; ok && csp != nil { ret := csp.AudienceEnter(p, roomId, exclude, ischangeroom) logger.Logger.Warnf("(csm *CoinSceneMgr) AudienceEnter snid:%v find in id:%v exclude:%v return false", p.SnId, id, exclude) return ret } logger.Logger.Warnf("(csm *CoinSceneMgr) AudienceEnter snid:%v find in id:%v exclude:%v csp.AudienceEnter return false", p.SnId, id, exclude) return hall_proto.OpResultCode_OPRC_Error } func (csm *CoinSceneMgr) playerLeave(p *Player, reason int) bool { if p == nil || p.scene == nil { return true } s := p.scene if s.groupId != 0 { if ss, ok := csm.scenesOfGroup[s.groupId]; ok && ss != nil { if csp, ok := ss[s.dbGameFree.GetId()]; ok && csp != nil { if !csp.PlayerLeave(p, reason) { csp.AudienceLeave(p, reason) } } return true } } // 玩家身上平台 if platform := p.GetPlatform(); platform != nil { if ss, ok := csm.scenesOfPlatform[platform.IdStr]; ok && ss != nil { if csp, ok := ss[s.dbGameFree.GetId()]; ok && csp != nil { if !csp.PlayerLeave(p, reason) { csp.AudienceLeave(p, reason) } } return true } } // 房间所在平台 if s.limitPlatform != nil { if ss, ok := csm.scenesOfPlatform[s.limitPlatform.IdStr]; ok && ss != nil { if csp, ok := ss[s.dbGameFree.GetId()]; ok && csp != nil { if !csp.PlayerLeave(p, reason) { csp.AudienceLeave(p, reason) } } return true } } return true } // PlayerLeave 玩家离开 func (csm *CoinSceneMgr) PlayerLeave(p *Player, reason int) bool { logger.Logger.Tracef("玩家离开: snid %v, reason %v isAudience %v", p.SnId, reason) return csm.playerLeave(p, reason) } // OnDestroyScene 解散房间 // sceneId 房间id func (csm *CoinSceneMgr) OnDestroyScene(sceneId int) { if platformName, ok := csm.platformOfScene[sceneId]; ok { if ss, ok := csm.scenesOfPlatform[platformName]; ok { for _, csp := range ss { csp.OnDestroyScene(sceneId) } } delete(csm.platformOfScene, sceneId) } if groupId, ok := csm.groupOfScene[sceneId]; ok { if ss, ok := csm.scenesOfGroup[groupId]; ok { for _, csp := range ss { csp.OnDestroyScene(sceneId) } } delete(csm.groupOfScene, sceneId) } } // GetPlatformBySceneId 获取房间所在平台 func (csm *CoinSceneMgr) GetPlatformBySceneId(sceneId int) string { if platformName, ok := csm.platformOfScene[sceneId]; ok { return platformName } s := SceneMgrSingleton.GetScene(sceneId) if s != nil && s.limitPlatform != nil { return s.limitPlatform.IdStr } return DefaultPlatform } // GetPlayerNums 获取场次人数 func (csm *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 gps.GroupId != 0 { if ss, exist := csm.scenesOfGroup[gps.GroupId]; exist { if csp, exist := ss[id]; exist { sceneType := csp.GetSceneType() - 1 if sceneType >= 0 && sceneType < len(nums) { nums[sceneType] += csp.GetPlayerNum() + csp.GetFakePlayerNum() } } } } else { if ss, ok := csm.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 (csm *CoinSceneMgr) InCoinScene(p *Player) bool { if p == nil { logger.Logger.Tracef("(csm *CoinSceneMgr) InCoinScene p == nil snid:%v ", p.SnId) return false } if p.scene == nil { return false } if p.scene.csp == nil { return false } return true } // PlayerTryLeave 通知游戏服务玩家要离开房间 func (csm *CoinSceneMgr) PlayerTryLeave(p *Player, isAudience bool) hall_proto.OpResultCode { if !csm.InCoinScene(p) { logger.Logger.Tracef("(csm *CoinSceneMgr) PlayerTryLeave !csm.InCoinScene(p) snid:%v ", p.SnId) return hall_proto.OpResultCode_OPRC_Sucess } // 通知gamesrv op := common.CoinSceneOp_Leave if isAudience { op = common.CoinSceneOp_AudienceLeave } pack := &hall_proto.CSCoinSceneOp{ Id: proto.Int32(p.scene.csp.id), OpType: proto.Int32(op), } proto.SetDefaults(pack) common.TransmitToServer(p.sid, int(hall_proto.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), pack, p.scene.gameSess.Session) logger.Logger.Tracef("(csm *CoinSceneMgr) PlayerTryLeave snid:%v id:%v", p.SnId, pack.GetId()) return hall_proto.OpResultCode_OPRC_OpYield } func (csm *CoinSceneMgr) PlayerInChanging(p *Player) bool { _, exist := csm.playerChanging[p.SnId] return exist } func (csm *CoinSceneMgr) ClearPlayerChanging(p *Player) { delete(csm.playerChanging, p.SnId) } func (csm *CoinSceneMgr) PlayerTryChange(p *Player, id int32, exclude []int32, isAudience bool) hall_proto.OpResultCode { if csm.InCoinScene(p) { return csm.StartChangeCoinSceneTransact(p, id, exclude, isAudience) } if isAudience { return csm.AudienceEnter(p, id, 0, exclude, true) } return csm.PlayerEnter(p, id, 0, exclude, true) } func (csm *CoinSceneMgr) StartChangeCoinSceneTransact(p *Player, id int32, exclude []int32, isAudience bool) hall_proto.OpResultCode { if p == nil || p.scene == nil { logger.Logger.Warnf("(csm *CoinSceneMgr) StartChangeCoinSceneTransact p == nil || p.scene == nil snid:%v id:%v", p.SnId, id) return hall_proto.OpResultCode_OPRC_Error } tNow := time.Now() if !p.lastChangeScene.IsZero() && tNow.Sub(p.lastChangeScene) < time.Second { logger.Logger.Warnf("(csm *CoinSceneMgr) StartChangeCoinSceneTransact !p.lastChangeScene.IsZero() && tNow.Sub(p.lastChangeScene) < time.Second snid:%v id:%v", p.SnId, id) return hall_proto.OpResultCode_OPRC_ChangeRoomTooOften } tnp := &transact.TransNodeParam{ Tt: common.TransType_CoinSceneChange, 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()) csm.playerChanging[p.SnId] = id p.lastChangeScene = tNow return hall_proto.OpResultCode_OPRC_Sucess } logger.Logger.Warnf("(csm *CoinSceneMgr) StartChangeCoinSceneTransact tNode == nil snid:%v id:%v", p.SnId, id) return hall_proto.OpResultCode_OPRC_Error } // TouchCreateRoom 触发预创建房间 // 1.模块启动后触发 // 2.游戏服建立连接后触发 // 3.房间解散后触发 // 4.场次配置更新后 func (csm *CoinSceneMgr) TouchCreateRoom(platform string, gameFreeId int32) { gf := PlatformMgrSingleton.GetGameFree(platform, gameFreeId) if gf.Status && gf.DbGameFree.GetCreateRoomNum() > 0 { logger.Logger.Tracef("TouchCreateRoom platform:%v gameFreeId:%v", platform, gameFreeId) csm.delayCache = append(csm.delayCache, &CreateRoomCache{ platformName: platform, gameFreeId: gameFreeId, }) } } func (csm *CoinSceneMgr) ModuleName() string { return "CoinSceneMgr" } func (csm *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 { csm.GetCoinScenePool(platform.IdStr, v.GetId()) csm.TouchCreateRoom(platform.IdStr, v.GetId()) } } } } } func (csm *CoinSceneMgr) Update() { cnt := len(csm.delayCache) if cnt > 0 { data := csm.delayCache[cnt-1] csm.delayCache = csm.delayCache[:cnt-1] gf := PlatformMgrSingleton.GetGameFree(data.platformName, data.gameFreeId) if gf != nil && gf.DbGameFree != nil { csp := csm.GetCoinScenePool(data.platformName, data.gameFreeId) if csp != nil { csp.PreCreateRoom() } } } } func (csm *CoinSceneMgr) Shutdown() { module.UnregisteModule(csm) } //=====================PlatformObserver====================== func (this *CoinSceneMgr) OnPlatformCreate(p *Platform) { } func (this *CoinSceneMgr) OnPlatformDestroy(p *Platform) { if p == nil { return } var ids []int if v, ok := this.scenesOfPlatform[p.IdStr]; ok { for _, csp := range v { for _, scene := range csp.scenes { ids = append(ids, scene.sceneId) } } } SceneMgrSingleton.DoDelete(ids, true) } func (this *CoinSceneMgr) OnPlatformChangeDisabled(p *Platform, disabled bool) { if disabled { this.OnPlatformDestroy(p) } } func (this *CoinSceneMgr) OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapi.GameFree) { if p == nil || newCfg == nil { return } var ss map[int32]*CoinScenePool var ok bool if oldCfg.GroupId != newCfg.GroupId || oldCfg.GroupId != 0 { ss, ok = this.scenesOfGroup[oldCfg.GroupId] } else { ss, ok = this.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.DoDelete(ids, true) this.TouchCreateRoom(p.IdStr, newCfg.DbGameFree.Id) } } func (this *CoinSceneMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { if p == nil { return } if csps, ok := this.scenesOfPlatform[p.IdStr]; ok { var ids []int for _, csp := range csps { for _, scene := range csp.scenes { if scene.dbGameFree.Id == gameFreeId { ids = append(ids, scene.sceneId) } } } SceneMgrSingleton.DoDelete(ids, true) } } //=========================PlatformGameGroupObserver============================== func (this *CoinSceneMgr) OnGameGroupUpdate(oldCfg, newCfg *webapi.GameConfigGroup) { if newCfg == nil { return } if scenes, exist := this.scenesOfGroup[newCfg.Id]; exist { if cps, ok := scenes[newCfg.DbGameFree.Id]; ok { needDestroy := false if cps.dbGameFree.GetBot() != newCfg.DbGameFree.GetBot() || cps.dbGameFree.GetBaseScore() != newCfg.DbGameFree.GetBaseScore() || cps.dbGameFree.GetLimitCoin() != newCfg.DbGameFree.GetLimitCoin() || cps.dbGameFree.GetMaxCoinLimit() != newCfg.DbGameFree.GetMaxCoinLimit() || cps.dbGameFree.GetTaxRate() != newCfg.DbGameFree.GetTaxRate() || !common.SliceInt64Equal(cps.dbGameFree.GetOtherIntParams(), newCfg.DbGameFree.GetOtherIntParams()) || !common.SliceInt64Equal(cps.dbGameFree.GetRobotTakeCoin(), newCfg.DbGameFree.GetRobotTakeCoin()) || !common.SliceInt64Equal(cps.dbGameFree.GetRobotLimitCoin(), newCfg.DbGameFree.GetRobotLimitCoin()) { needDestroy = true } //TODO 预创建房间配置更新,unsupport group model cps.dbGameFree = newCfg.DbGameFree if needDestroy { var ids []int for _, scene := range cps.scenes { ids = append(ids, scene.sceneId) } SceneMgrSingleton.DoDelete(ids, true) } } } } //=========================GameSessionListener====================================== func (csm *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())) { csm.TouchCreateRoom(platform.IdStr, v.DbGameFree.Id) } } } } func (this *CoinSceneMgr) OnGameSessionUnregiste(gs *GameSession) { //todo 游戏服务断开,是否解散房间? } func init() { module.RegisteModule(CoinSceneMgrSingleton, time.Second*2, 0) PlatformMgrSingleton.RegisterObserver(CoinSceneMgrSingleton) PlatformGameGroupMgrSington.RegisteObserver(CoinSceneMgrSingleton) RegisteGameSessionListener(CoinSceneMgrSingleton) }