game_sync/worldsrv/scenemgr.go

509 lines
14 KiB
Go

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"
"mongo.games.com/game/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{}),
}
// SceneMgr 房间管理器
type SceneMgr struct {
common.BaseClockSinker // 驱动时间事件
scenes map[int]*Scene // 房间id: Scene
privateAutoId int // 私人房房间号
matchAutoId int // 比赛场房间号
coinSceneAutoId int // 金币场房间号
hundredSceneAutoId int // 百人场房间号
password map[string]struct{} // 密码
}
// 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) GenPasswordInt32() int32 {
for i := 0; i < 100; i++ {
s := strconv.Itoa(common.RandInt(10000, 100000))
if _, ok := m.password[s]; !ok {
m.password[s] = struct{}{}
n, _ := strconv.Atoi(s)
return int32(n)
}
}
return 0
}
func (m *SceneMgr) GetPlatformBySceneId(sceneId int) string {
s := m.GetScene(sceneId)
if s != nil && s.limitPlatform != nil {
return s.limitPlatform.IdStr
}
return ""
}
// GetScene 获取房间对象
func (m *SceneMgr) GetScene(sceneId int) *Scene {
if s, exist := m.scenes[sceneId]; exist && !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.limitPlatform != nil && s.limitPlatform.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)) || isNeedFindAll &&
((s.IsCustom() && isCustom) || !isCustom) &&
(s.GetRoomConfigId() == roomConfigId || roomConfigId == 0) {
var platformName string
if s.limitPlatform != nil {
platformName = s.limitPlatform.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,
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,
GameFreeId: s.dbGameFree.GetId(),
MaxRound: s.totalRound,
Password: s.GetPassword(),
CostType: s.GetCostType(),
Voice: s.GetVoice(),
CurrRound: s.currRound,
}
if s.starting {
si.Start = 1
} else {
si.Start = 0
}
if s.IsHundredScene() {
si.Start = 1
}
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.limitPlatform.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
}
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 // 比赛场参数
}
// CreateScene 创建房间
func (m *SceneMgr) CreateScene(args *CreateSceneParam) *Scene {
logger.Logger.Tracef("SceneMgr NewScene %v", args)
if args.GF == nil {
return nil
}
if args.Platform == nil {
return nil
}
if args.GS == nil {
args.GS = GameSessMgrSington.GetMinLoadSess(int(args.GF.GetGameId()))
}
if args.GS == nil {
return nil
}
// 创建房间
s := NewScene(args)
if s == nil {
return nil
}
m.scenes[args.RoomId] = s
// 添加到游戏服记录中
args.GS.AddScene(&AddSceneParam{
S: s,
})
logger.Logger.Infof("SceneMgr NewScene Platform:%v %+v", args.Platform.IdStr, args)
// 创建水池
if !s.IsMatchScene() && s.dbGameFree != nil && s.limitPlatform != nil {
//平台水池设置
args.GS.DetectCoinPoolSetting(s.limitPlatform.IdStr, s.dbGameFree.GetId(), s.groupId)
}
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
}
switch {
case s.IsCoinScene():
CoinSceneMgrSingleton.OnDestroyScene(s.sceneId)
case s.IsHundredScene():
HundredSceneMgrSingleton.OnDestroyScene(s.sceneId)
case s.IsMatchScene():
MatchSceneMgrSingleton.OnDestroyScene(s.sceneId)
}
s.gameSess.DelScene(s)
s.OnClose()
delete(m.scenes, s.sceneId)
delete(m.password, s.GetPassword())
logger.Logger.Infof("(this *SceneMgr) DestroyScene, SceneId=%v", sceneId)
}
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)
}
//=========================ClockSinker===============================
// InterestClockEvent 接收所有时间事件
func (m *SceneMgr) InterestClockEvent() int {
return 1 << common.ClockEventMinute
}
func (m *SceneMgr) OnMiniTimer() {
// 解散空闲房间
for _, s := range m.scenes {
if webapi.ThridPlatformMgrSington.FindPlatformByPlatformBaseGameId(s.gameId) != nil {
continue
}
switch {
case s.IsCoinScene():
if s.IsLongTimeInactive() {
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 != nil && s.csp.GetRoomNum(common.SceneMode_Public) > int(s.dbGameFree.GetCreateRoomNum()) {
logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive CoinScene SendGameDestroy scene:%v IsLongTimeInactive", s.sceneId)
s.SendGameDestroy(false)
}
}
case s.IsPrivateScene():
if s.IsLongTimeInactive() {
logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive PrivateScene SendGameDestroy scene:%v IsLongTimeInactive", s.sceneId)
s.SendGameDestroy(false)
}
}
}
}