package main import ( "math" "slices" "sort" "strconv" "mongo.games.com/goserver/core/logger" "mongo.games.com/goserver/srvlib" "mongo.games.com/game/common" "mongo.games.com/game/model" serverproto "mongo.games.com/game/protocol/server" webapiproto "mongo.games.com/game/protocol/webapi" ) func init() { common.ClockMgrSingleton.RegisterSinker(SceneMgrSingleton) } var SceneMgrSingleton = &SceneMgr{ scenes: make(map[int]*Scene), privateAutoId: common.PrivateSceneStartId, matchAutoId: common.MatchSceneStartId, coinSceneAutoId: common.CoinSceneStartId, hundredSceneAutoId: common.HundredSceneStartId, password: make(map[string]struct{}), pushList: make(map[int]struct{}), } // SceneMgr 房间管理器 type SceneMgr struct { common.BaseClockSinker // 驱动时间事件 scenes map[int]*Scene // 房间id: Scene privateAutoId int // 私人房房间号 matchAutoId int // 比赛场房间号 coinSceneAutoId int // 金币场房间号 hundredSceneAutoId int // 百人场房间号 password map[string]struct{} // 密码 pushList map[int]struct{} // 已经推荐过的房间列表 lastPushSceneId int // 最后推荐的房间id } // AllocReplayCode 获取回访码 func (m *SceneMgr) AllocReplayCode() string { code, _ := model.GetOneReplayId() return code } // GenOnePrivateSceneId 生产一个私人房间id func (m *SceneMgr) GenOnePrivateSceneId() int { m.privateAutoId++ if m.privateAutoId > common.PrivateSceneMaxId { m.privateAutoId = common.PrivateSceneStartId } return m.privateAutoId } // GenOneCoinSceneId 生产一个金币场房间id func (m *SceneMgr) GenOneCoinSceneId() int { m.coinSceneAutoId++ if m.coinSceneAutoId > common.CoinSceneMaxId { m.coinSceneAutoId = common.CoinSceneStartId } return m.coinSceneAutoId } // GenOneHundredSceneId 生产一个百人场房间id func (m *SceneMgr) GenOneHundredSceneId() int { m.hundredSceneAutoId++ if m.hundredSceneAutoId > common.HundredSceneMaxId { m.hundredSceneAutoId = common.HundredSceneStartId } return m.hundredSceneAutoId } // GenOneMatchSceneId 生产一个比赛场房间id func (m *SceneMgr) GenOneMatchSceneId() int { m.matchAutoId++ if m.matchAutoId > common.MatchSceneMaxId { m.matchAutoId = common.MatchSceneStartId } return m.matchAutoId } func (m *SceneMgr) GenPassword() string { for i := 0; i < 100; i++ { s := strconv.Itoa(common.RandInt(100000, 1000000)) if _, ok := m.password[s]; !ok { m.password[s] = struct{}{} return s } } return "" } func (m *SceneMgr) GetPlatformBySceneId(sceneId int) string { s := m.GetScene(sceneId, true) if s != nil && s.platform != nil { return s.platform.IdStr } return "" } // GetScene 获取房间对象 // 默认是不包含删除中的房间 // hasDeleting true 包含删除中的房间 func (m *SceneMgr) GetScene(sceneId int, hasDeleting ...bool) *Scene { has := false if len(hasDeleting) > 0 { has = hasDeleting[0] } if s, exist := m.scenes[sceneId]; exist && (has || !s.deleting) { return s } return nil } // GetScenesByGame 根据游戏id查询房间 func (m *SceneMgr) GetScenesByGame(gameId int) []*Scene { var scenes []*Scene for _, value := range m.scenes { if value.gameId == gameId { s := m.GetScene(value.sceneId) if s != nil { scenes = append(scenes, value) } } } return scenes } // GetScenesByGameFreeId 根据场次id查询房间 func (m *SceneMgr) GetScenesByGameFreeId(gameFreeId int32) []*Scene { var scenes []*Scene for _, value := range m.scenes { if value.dbGameFree.GetId() == gameFreeId { s := m.GetScene(value.sceneId) if s != nil { scenes = append(scenes, value) } } } return scenes } // GetMatchRoom 获取比赛房间 // sortId 比赛id func (m *SceneMgr) GetMatchRoom(sortId int64) []*Scene { var scenes []*Scene for _, value := range m.scenes { if value.GetMatchSortId() == sortId { s := m.GetScene(value.sceneId) if s != nil { scenes = append(scenes, value) } } } return scenes } // MarshalAllRoom 获取房间列表 // 返回 房间列表,总页数,总条数 func (m *SceneMgr) MarshalAllRoom(platform string, groupId, gameId int, gameMode, clubId, sceneMode, sceneId int, gameFreeId, snId int32, isCustom bool, roomConfigId int32, start, end, pageSize int32) ([]*webapiproto.RoomInfo, int32, int32) { roomInfo := make([]*webapiproto.RoomInfo, 0, len(m.scenes)) var isNeedFindAll = false if model.GameParamData.IsFindRoomByGroup && platform != "" && snId != 0 && gameId == 0 && gameMode == 0 && sceneId == -1 && groupId == 0 && sceneMode == 0 { p := PlayerMgrSington.GetPlayerBySnId(snId) if p != nil && p.Platform == platform { isNeedFindAll = true } } for _, s := range m.scenes { if (((s.platform != nil && s.platform.IdStr == platform) || platform == "") && ((s.gameId == gameId && s.gameMode == gameMode) || gameId == 0) && (s.sceneId == sceneId || sceneId == 0) && (s.groupId == int32(groupId) || groupId == 0) && (s.dbGameFree.GetId() == gameFreeId || gameFreeId == 0) && (s.sceneMode == sceneMode || sceneMode == -1) && ((s.IsCustom() && isCustom) || !isCustom) && (s.CustomParam.GetRoomConfigId() == roomConfigId || roomConfigId == 0)) || isNeedFindAll { var platformName string if s.platform != nil { platformName = s.platform.IdStr } si := &webapiproto.RoomInfo{ Platform: platformName, SceneId: int32(s.sceneId), GameId: int32(s.gameId), GameMode: int32(s.gameMode), SceneMode: int32(s.sceneMode), GroupId: s.groupId, GameFreeId: s.dbGameFree.GetId(), Creator: s.creator, ReplayCode: s.replayCode, Params: common.CopySliceInt64ToInt32(s.params), PlayerCnt: int32(len(s.players) - s.robotNum), RobotCnt: int32(s.robotNum), CreateTime: s.createTime.Unix(), BaseScore: s.dbGameFree.BaseScore, RoomConfigId: s.CustomParam.GetRoomConfigId(), CurrRound: s.currRound, MaxRound: s.totalRound, Password: s.GetPassword(), CostType: s.CustomParam.GetCostType(), Voice: s.CustomParam.GetVoice(), PlayerNum: int32(s.playerNum), } if s.starting { si.Start = 1 } else { si.Start = 0 } if s.IsHundredScene() { si.Start = 1 } if s.RoomConfigSystem.GetId() > 0 { si.Creator = s.RoomConfigSystem.GetId() } if s.gameSess != nil { si.SrvId = s.gameSess.GetSrvId() } cnt := 0 total := len(s.players) robots := []int32{} isContinue := false if snId != 0 { for _, p := range s.players { if p.SnId == snId { isContinue = true break } } } else { isContinue = true } if !isContinue { continue } //优先显示玩家 for id, p := range s.players { if !p.IsRob || total < 10 { si.PlayerIds = append(si.PlayerIds, id) cnt++ } else { robots = append(robots, id) } if cnt > 10 { break } } //不够再显示机器人 if total > cnt && cnt < 10 && len(robots) != 0 { for i := 0; cnt < 10 && i < len(robots); i++ { si.PlayerIds = append(si.PlayerIds, robots[i]) cnt++ if cnt > 10 { break } } } roomInfo = append(roomInfo, si) } } sort.Slice(roomInfo, func(i, j int) bool { if roomInfo[i].CreateTime < roomInfo[j].CreateTime || (roomInfo[i].CreateTime == roomInfo[j].CreateTime && roomInfo[i].SceneId < roomInfo[j].SceneId) { return true } return false }) //分页处理 roomSum := float64(len(roomInfo)) //房间总数 pageCount := int32(math.Ceil(roomSum / float64(pageSize))) //总页数 if roomSum <= float64(start) { start = 0 } if roomSum < float64(end) { end = int32(roomSum) } needList := roomInfo[start:end] //需要的房间列表 if len(needList) > 0 { return needList, pageCount, int32(roomSum) } return nil, 0, int32(roomSum) } type FindRoomParam struct { Platform string GameId []int GameMode []int SceneMode []int RoomId int32 IsCustom int32 // 房卡场 IsFree int32 // 自由桌 GameFreeId []int32 // 场次id SnId int32 // 玩家id IsMatch bool // 比赛场 IsRankMatch bool // 排位赛 Channel []string // 渠道 } func (m *SceneMgr) FindRoomList(args *FindRoomParam) []*Scene { ret := make([]*Scene, 0) for _, v := range m.scenes { if args.Platform != "" && v.platform.IdStr != args.Platform { continue } if len(args.GameId) > 0 { if !slices.Contains(args.GameId, int(v.dbGameFree.GetGameId())) { continue } } if len(args.GameMode) > 0 { if !slices.Contains(args.GameMode, int(v.dbGameFree.GetGameMode())) { continue } } if len(args.SceneMode) > 0 { if !slices.Contains(args.SceneMode, v.sceneMode) { continue } } if args.RoomId > 0 && v.sceneId != int(args.RoomId) { continue } if args.IsCustom == 1 && v.dbGameFree.GetIsCustom() != args.IsCustom { continue } if args.IsFree == 1 && v.dbGameFree.GetFreeMode() != args.IsFree { continue } if len(args.GameFreeId) > 0 { if !slices.Contains(args.GameFreeId, v.dbGameFree.GetId()) { continue } } if args.SnId > 0 && v.GetPlayer(args.SnId) == nil { continue } if args.IsMatch && !v.IsMatchScene() { continue } if args.IsRankMatch && !v.IsRankMatch() { continue } if len(args.Channel) > 0 { has := false for _, vv := range args.Channel { if slices.Contains(v.Channel, vv) { has = true break } } if !has { continue } } if v.deleting || v.closed || v.force { continue } ret = append(ret, v) } return ret } // FindCustomInviteRoom 竞技馆房间推荐 func (m *SceneMgr) FindCustomInviteRoom(p *Player) *Scene { // 无密码,未满人,未开始 // 玩家房间 > 系统房间 // 人数 > 招募次数 > 创建时间 var ret []*Scene for _, v := range m.scenes { if v.deleting || v.force || v.closed { continue } if !v.IsCustom() { continue } if v.GetPassword() != "" { continue } if v.IsFull() { continue } if !v.IsRecruit { continue } if len(v.Channel) > 0 && !slices.Contains(v.Channel, p.AppChannel) { continue } if !PlatformMgrSingleton.CustomIsOn(p.Platform, v.CustomParam.GetRoomConfigId()) { continue } // 系统房只剩一人才推荐 if v.creator == 0 && v.RoomConfigSystem != nil { if int(v.RoomConfigSystem.GetPlayerNum())-v.GetPlayerCnt() != 1 { continue } } ret = append(ret, v) } sort.Slice(ret, func(i, j int) bool { if ret[i].creator > 0 && ret[j].creator == 0 { return true } if ret[i].creator == 0 && ret[j].creator > 0 { return false } iN, jN := ret[i].GetMaxPlayerNum()-ret[i].GetPlayerCnt(), ret[j].GetMaxPlayerNum()-ret[j].GetPlayerCnt() if iN < jN { return true } if iN > jN { return false } if ret[i].RecruitTimes > ret[j].RecruitTimes { return true } if ret[i].RecruitTimes < ret[j].RecruitTimes { return false } return ret[i].createTime.Unix() < ret[j].createTime.Unix() }) // 删除没有的房间 var list []*Scene var pushList = map[int]struct{}{} for k := range m.pushList { var has bool for _, v := range ret { if v.sceneId == k { has = true break } } if has { pushList[k] = struct{}{} } } m.pushList = pushList // 删除推荐过的房间 for _, v := range ret { if _, ok := m.pushList[v.sceneId]; !ok { list = append(list, v) } } if len(list) > 0 { m.pushList[list[0].sceneId] = struct{}{} return list[0] } if len(ret) > 0 { // 全都推荐过了,循环推荐房间 var b bool for _, v := range ret { if b { m.lastPushSceneId = v.sceneId return v } if v.sceneId == m.lastPushSceneId { b = true } } // 没找到,从头开始 m.lastPushSceneId = ret[0].sceneId return ret[0] } return nil } type CreateSceneParam struct { CreateId int32 // 创建者id RoomId int // 房间id SceneMode int // 公共,私人,赛事 CycleTimes int // 循环次数 TotalRound int // 总轮数 Params []int64 // 房间参数 GS *GameSession // 游戏服务 Platform *Platform // 所在平台 GF *serverproto.DB_GameFree // 场次配置 PlayerNum int32 // 玩家最大数量 BaseScore int32 // 底分 Channel []string // 客户端类型,允许查看的客户端类型 *serverproto.CustomParam // 房卡场参数 *serverproto.MatchParam // 比赛场参数 *webapiproto.RoomConfigSystem // 竞技管系统房参数 IsRecruit bool // 是否招募 } // CreateScene 创建房间 func (m *SceneMgr) CreateScene(args *CreateSceneParam) *Scene { logger.Logger.Tracef("SceneMgr NewScene %v", args) if args.GF == nil { logger.Logger.Errorf("SceneMgr NewScene GameFree is nil") return nil } if args.Platform == nil { logger.Logger.Errorf("SceneMgr NewScene Platform is nil") return nil } if args.GS == nil { args.GS = GameSessMgrSington.GetMinLoadSess(int(args.GF.GetGameId())) } if args.GS == nil { logger.Logger.Errorf("SceneMgr NewScene GameServer is nil") return nil } // 创建房间 s := NewScene(args) if s == nil { logger.Logger.Errorf("SceneMgr NewScene Scene is nil") return nil } m.scenes[args.RoomId] = s // 添加到游戏服记录中 args.GS.AddScene(&AddSceneParam{ S: s, }) s.sp.OnStart(s) logger.Logger.Infof("SceneMgr NewScene Platform:%v %+v", args.Platform.IdStr, args) return s } // DestroyScene 解散房间 // 房间销毁,游戏服务断开 func (m *SceneMgr) DestroyScene(sceneId int, isCompleted bool) { logger.Logger.Trace("(this *SceneMgr) DestroyScene ") s, ok := m.scenes[sceneId] if !ok || s == nil { return } s.sp.OnStop(s) s.gameSess.DelScene(s) switch { case s.IsCoinScene(): CoinSceneMgrSingleton.OnDestroyScene(s.sceneId) case s.IsHundredScene(): HundredSceneMgrSingleton.OnDestroyScene(s.sceneId) } s.OnClose() delete(m.scenes, s.sceneId) delete(m.password, s.GetPassword()) logger.Logger.Infof("(this *SceneMgr) DestroyScene, SceneId=%v", sceneId) } // SendGameDestroy 发送游戏服销毁房间 func (m *SceneMgr) SendGameDestroy(sceneId []int, isGrace bool) { if len(sceneId) == 0 { return } var ids []int64 for _, v := range sceneId { ids = append(ids, int64(v)) s, ok := m.scenes[v] if !isGrace && ok && s != nil { s.deleting = true s.force = true } } pack := &serverproto.WGDestroyScene{ Ids: ids, IsGrace: isGrace, } srvlib.ServerSessionMgrSington.Broadcast(int(serverproto.SSPacketID_PACKET_WG_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) } // CheckDestroyEmptyRoom 尝试解散空闲房间 // 非必须,防止内存泄露 func (m *SceneMgr) CheckDestroyEmptyRoom() { for _, s := range m.scenes { switch { case s.IsCoinScene(): if !s.IsLongTimeInactive() { continue } if s.dbGameFree == nil { continue } if s.dbGameFree.GetCreateRoomNum() == 0 { logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive CoinScene SendGameDestroy scene:%v IsLongTimeInactive", s.sceneId) s.SendGameDestroy(false) } if s.dbGameFree.GetCreateRoomNum() > 0 && s.csp.GetRoomNum(common.SceneModePublic) > int(s.dbGameFree.GetCreateRoomNum()) { logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive CoinScene SendGameDestroy scene:%v IsLongTimeInactive", s.sceneId) s.SendGameDestroy(false) } } } } //=========================ClockSinker=============================== // InterestClockEvent 接收所有时间事件 func (m *SceneMgr) InterestClockEvent() int { return 1 << common.ClockEventMinute } func (m *SceneMgr) OnMiniTimer() { m.CheckDestroyEmptyRoom() }