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/goserver/srvlib" "mongo.games.com/game/common" "mongo.games.com/game/proto" hall_proto "mongo.games.com/game/protocol/gamehall" "mongo.games.com/game/protocol/server" server_proto "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[int32]string), //按组管理 scenesOfGroup: make(map[int32]map[int32]*CoinScenePool), groupOfScene: make(map[int32]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[int32]string // sceneid:platform; 创建房间后记录房间所在平台 //按组管理 scenesOfGroup map[int32]map[int32]*CoinScenePool // groupid:gamefreeid groupOfScene map[int32]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 } } } var dbGameFree *server_proto.DB_GameFree if groupId != 0 { conf := PlatformGameGroupMgrSington.GetGameGroup(groupId) if conf != nil { dbGameFree = conf.GetDbGameFree() } } if dbGameFree == nil { dbGameFree = gf.GetDbGameFree() } if dbGameFree == nil { return nil } // 创建了一个新的 // 应该是走不到这里,因为模块启动时所有场次都创建了房间池 return NewCoinScenePool(plt, groupId, dbGameFree) } 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 { return nil, 0, nil } groupId := gf.GetGroupId() var ss map[int32]*CoinScenePool var ok bool if groupId != 0 { pgg := PlatformGameGroupMgrSington.GetGameGroup(groupId) if pgg != nil { ss, ok = csm.scenesOfGroup[groupId] if !ok { ss = make(map[int32]*CoinScenePool) csm.scenesOfGroup[groupId] = ss } return ss, groupId, pgg.GetDbGameFree() } } if ss == nil { ss, ok = csm.scenesOfPlatform[platform] if !ok { ss = make(map[int32]*CoinScenePool) csm.scenesOfPlatform[platform] = ss } return ss, 0, gf.GetDbGameFree() } return nil, 0, nil } // 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 } pools, groupID, free := csm.findCoinScenePool(platform.IdStr, id) if pools == nil { return hall_proto.OpResultCode_OPRC_RoomHadClosed } csp, ok := pools[id] if !ok || csp == nil { csp = NewCoinScenePool(platform.IdStr, groupID, free) if csp == nil { logger.Logger.Warnf("(csm *CoinSceneMgr) PlayerEnter snid:%v find in id:%v exclude:%v NewCoinScenePool failed", p.SnId, id, exclude) return hall_proto.OpResultCode_OPRC_Error } pools[id] = csp } 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[int32(sceneId)]; ok { if ss, ok := csm.scenesOfPlatform[platformName]; ok { for _, csp := range ss { csp.OnDestroyScene(sceneId) } } delete(csm.platformOfScene, int32(sceneId)) } if groupId, ok := csm.groupOfScene[int32(sceneId)]; ok { if ss, ok := csm.scenesOfGroup[groupId]; ok { for _, csp := range ss { csp.OnDestroyScene(sceneId) } } delete(csm.groupOfScene, int32(sceneId)) } } // GetPlatformBySceneId 获取房间所在平台 func (csm *CoinSceneMgr) GetPlatformBySceneId(sceneId int) string { if platformName, ok := csm.platformOfScene[int32(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.DataMgr.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 } // ListRooms 给客户端发送场次房间列表 // id 场次id func (csm *CoinSceneMgr) ListRooms(p *Player, id int32) bool { //多平台支持 platform := p.GetPlatform() if platform == nil { return false } pools, groupID, free := csm.findCoinScenePool(platform.IdStr, id) if pools == nil { return false } if csp, ok := pools[id]; ok && csp != nil { return csp.ListRoom(p) } csp := NewCoinScenePool(platform.IdStr, groupID, free) if csp == nil { return false } pools[id] = csp return csp.ListRoom(p) } // CreateRoomByCache 创建预创建房间 // 每2秒创建一次 func (csm *CoinSceneMgr) CreateRoomByCache() { cnt := len(csm.delayCache) if cnt > 0 { data := csm.delayCache[cnt-1] csm.delayCache = csm.delayCache[:cnt-1] pdd := PlatformMgrSingleton.GetGameFree(data.platformName, data.gameFreeId) //对战场和捕鱼场 预创建 if pdd != nil && pdd.DbGameFree != nil { var ss map[int32]*CoinScenePool var ok bool //分组模式 if pdd.GroupId != 0 { pgg := PlatformGameGroupMgrSington.GetGameGroup(pdd.GroupId) if pgg != nil { ss, ok = csm.scenesOfGroup[pdd.GroupId] if !ok { ss = make(map[int32]*CoinScenePool) csm.scenesOfGroup[pdd.GroupId] = ss } } } //独立模式 if ss == nil { ss, ok = csm.scenesOfPlatform[data.platformName] if !ok { ss = make(map[int32]*CoinScenePool) csm.scenesOfPlatform[data.platformName] = ss } } if ss == nil { return } if csp, ok := ss[pdd.DbGameFree.Id]; ok && csp != nil { csp.PreCreateRoom() return } csp := NewCoinScenePool(data.platformName, 0, pdd.DbGameFree) if csp != nil { ss[pdd.DbGameFree.Id] = csp csp.PreCreateRoom() return } } } } func (csm *CoinSceneMgr) ModuleName() string { return "CoinSceneMgr" } func (csm *CoinSceneMgr) Init() { // 场次池预创建 for _, platform := range PlatformMgrSingleton.GetPlatforms() { if platform.Isolated || platform.IdStr == "" { arr := srvdata.PBDB_GameFreeMgr.Datas.GetArr() for _, dbGame := range arr { gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, dbGame.GetId()) if gps != nil { // dbGameFree dbGameFree := gps.DbGameFree if gps.GroupId != 0 { pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) if pgg != nil { dbGameFree = pgg.DbGameFree } } // CoinScenePool csp := NewCoinScenePool(platform.IdStr, gps.GroupId, dbGameFree) if csp != nil { if gps.GroupId != 0 { if ss, exist := csm.scenesOfGroup[gps.GroupId]; exist { ss[dbGame.GetId()] = csp } else { ss = make(map[int32]*CoinScenePool) ss[dbGame.GetId()] = csp csm.scenesOfGroup[gps.GroupId] = ss } } else { if ss, exist := csm.scenesOfPlatform[platform.IdStr]; exist { ss[dbGame.GetId()] = csp } else { ss = make(map[int32]*CoinScenePool) ss[dbGame.GetId()] = csp csm.scenesOfPlatform[platform.IdStr] = ss } } } } } } } } func (csm *CoinSceneMgr) Update() { // 预创建房间 csm.CreateRoomByCache() } func (csm *CoinSceneMgr) Shutdown() { module.UnregisteModule(csm) } //=====================PlatformObserver====================== func (this *CoinSceneMgr) OnPlatformCreate(p *Platform) { if p.IdStr == DefaultPlatform { return } //获取配置 gps := PlatformMgrSingleton.GetGameFrees(p.IdStr) for _, v := range gps { if v.Status && v.DbGameFree.GetCreateRoomNum() > 0 { this.delayCache = append(this.delayCache, CreateRoomCache{gameFreeId: v.DbGameFree.Id, platformName: p.IdStr}) } } } func (this *CoinSceneMgr) OnPlatformDestroy(p *Platform) { if p == nil { return } if csps, ok := this.scenesOfPlatform[p.IdStr]; ok { for _, csp := range csps { pack := &server_proto.WGGraceDestroyScene{} for _, scene := range csp.scenes { pack.Ids = append(pack.Ids, int32(scene.sceneId)) } // 房间中记录的有游服连接,广播的方式也可以 srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) } } } func (this *CoinSceneMgr) OnPlatformChangeIsolated(p *Platform, isolated bool) { if !isolated { this.OnPlatformDestroy(p) } } 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 pack := &server_proto.WGGraceDestroyScene{} for _, scene := range cps.scenes { pack.Ids = append(pack.Ids, int32(scene.sceneId)) } srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) //预创建房间配置更新 if newCfg.DbGameFree.GetCreateRoomNum() != 0 && p.IdStr != DefaultPlatform && newCfg.Status { logger.Logger.Tracef(">>>预创建房间 platform:%v %v_%v gamefreeid:%v CreateRoomNum:%v", p.Name, newCfg.DbGameFree.GetName(), newCfg.DbGameFree.GetTitle(), newCfg.DbGameFree.GetId(), newCfg.DbGameFree.GetCreateRoomNum()) this.delayCache = append(this.delayCache, CreateRoomCache{gameFreeId: newCfg.DbGameFree.GetId(), platformName: p.IdStr}) } } } func (this *CoinSceneMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { if p == nil { return } if csps, ok := this.scenesOfPlatform[p.IdStr]; ok { for _, csp := range csps { pack := &server_proto.WGGraceDestroyScene{} for _, scene := range csp.scenes { if scene.dbGameFree.Id == gameFreeId { pack.Ids = append(pack.Ids, int32(scene.sceneId)) } } srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) } } } //=========================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 { pack := &server_proto.WGGraceDestroyScene{} for _, scene := range cps.scenes { pack.Ids = append(pack.Ids, int32(scene.sceneId)) } srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) } } } } //=========================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.Status && v.DbGameFree.GetCreateRoomNum() > 0 && (wildGs || common.InSliceInt32(gs.gameIds, v.DbGameFree.GetGameId())) { csm.delayCache = append(csm.delayCache, CreateRoomCache{gameFreeId: v.DbGameFree.Id, platformName: platform.IdStr}) } } } } func (this *CoinSceneMgr) OnGameSessionUnregiste(gs *GameSession) { //todo 游戏服务断开,是否解散房间? } func init() { module.RegisteModule(CoinSceneMgrSingleton, time.Second*2, 0) PlatformMgrSingleton.RegisterObserver(CoinSceneMgrSingleton) PlatformGameGroupMgrSington.RegisteObserver(CoinSceneMgrSingleton) RegisteGameSessionListener(CoinSceneMgrSingleton) }