ecs/servers/gate/logic/manager.go
2025-07-17 16:21:08 +08:00

346 lines
8.2 KiB
Go

package logic
import (
"ecs/proto"
"github.com/oylshe1314/framework/client/db"
"github.com/oylshe1314/framework/client/rpc"
"github.com/oylshe1314/framework/errors"
"github.com/oylshe1314/framework/log"
"github.com/oylshe1314/framework/server"
"github.com/oylshe1314/framework/util"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"sort"
"sync"
"time"
)
type manager struct {
server server.Server
}
func (this *manager) Logger() log.Logger {
return this.server.Logger()
}
const tableSetting = "setting"
type Setting struct {
Id uint32 `bson:"_id"`
Version string `bson:"version"`
LoginClosed bool `bson:"login_closed"`
TimedClose bool `bson:"timed_closed"`
CloseTime int64 `bson:"close_time"`
ClosedList []uint32 `bson:"closed_list"`
WhiteList []uint64 `bson:"white_list"`
}
const tableNotice = "notice"
type Notice struct {
Id uint64 `bson:"_id"`
Type string `json:"type"`
Title string `bson:"title"`
Content string `bson:"content"`
BeginTime int64 `bson:"begin_time"`
EndTime int64 `bson:"end_time"`
}
type GateManager struct {
manager
setting *Setting
closeTime int64
closeMutex sync.Mutex
closeTimer *time.Timer
mongoClient db.MongoClient
httpRpcClient rpc.HttpRpcClient
}
func NewGateManager(svr server.Server, mongoClient db.MongoClient, httpRpcClient rpc.HttpRpcClient) *GateManager {
return &GateManager{
manager: manager{
server: svr,
},
mongoClient: mongoClient,
httpRpcClient: httpRpcClient,
}
}
func (this *GateManager) Init() error {
setting, err := this.querySetting()
if err != nil {
return err
}
if setting != nil {
this.setSetting(setting)
}
return nil
}
func (this *GateManager) Close() error {
return nil
}
func (this *GateManager) querySetting() (*Setting, error) {
var setting = &Setting{Id: this.server.AppId()}
var err = this.mongoClient.Collection(tableSetting).FindOne(this.mongoClient.Context(), bson.M{"_id": setting.Id}).Decode(setting)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, nil
}
return nil, err
}
return setting, nil
}
func (this *GateManager) GetSetting() *Setting {
return this.setting
}
func (this *GateManager) SetSetting(setting *Setting) error {
var err = this.saveSetting(setting)
if err != nil {
this.Logger().Error(err)
return proto.ErrInternalError
}
this.setSetting(setting)
return nil
}
func (this *GateManager) saveSetting(setting *Setting) error {
var updateOption = options.Update().SetUpsert(true)
_, err := this.mongoClient.Collection(tableSetting).UpdateByID(this.mongoClient.Context(), setting.Id, bson.M{"$set": setting}, updateOption)
return err
}
func (this *GateManager) setSetting(setting *Setting) {
this.setting = setting
if setting.LoginClosed {
var now = util.NowUnix()
this.closeMutex.Lock()
if this.closeTimer != nil {
if this.closeTime == setting.CloseTime || now >= setting.CloseTime {
this.closeMutex.Unlock()
return
}
this.closeTimer.Stop()
this.closeTime = 0
this.closeTimer = nil
}
this.closeMutex.Unlock()
var f = func() {
this.closeMutex.Lock()
this.closeTime = 0
this.closeTimer = nil
this.closeMutex.Unlock()
ar, err := this.httpRpcClient.AllPost("game", "/server/close", nil, nil, nil)
if err != nil {
this.Logger().Errorf("Http get error, error: %v", err)
} else {
for appId, result := range ar {
if result.Err != nil {
this.Logger().Errorf("[game_1:%d] <- Server offline error, %v", appId, result.Err)
continue
}
if result.Res.Status != errors.StatusSuccessful {
this.Logger().Errorf("[game_1:%d] <- Server offline failed, %s", appId, result.Res.Message)
continue
}
}
}
}
if !setting.TimedClose || util.NowUnix() >= setting.CloseTime {
f()
} else {
this.closeMutex.Lock()
this.closeTime = setting.CloseTime
this.closeTimer = time.AfterFunc(time.Second*time.Duration(setting.CloseTime-now), f)
this.closeMutex.Unlock()
}
} else {
this.closeMutex.Lock()
if this.closeTimer != nil {
this.closeTimer.Stop()
this.closeTime = 0
this.closeTimer = nil
}
this.closeMutex.Unlock()
}
}
type gameServerInfo struct {
appId uint32
address string
online uint32
}
type gameServerList struct {
id uint32
platform uint32
channel uint32
area string
name string
online uint32
roleLv uint32
serverList []*gameServerInfo
}
func (this *GateManager) GetServerList(platform uint32, channel uint32, userId uint64, recentServer uint32) ([]*proto.GameServer, error) {
var setting = this.GetSetting()
var whiteList = map[uint64]struct{}{}
var closedList = map[uint32]struct{}{}
if setting != nil && setting.LoginClosed {
if !setting.TimedClose || util.NowUnix() >= setting.CloseTime {
for _, whiteId := range setting.WhiteList {
whiteList[whiteId] = struct{}{}
}
if len(setting.ClosedList) == 0 {
if _, ok := whiteList[userId]; !ok {
return nil, proto.ErrServerMaintain
}
}
for _, closedId := range setting.ClosedList {
closedList[closedId] = struct{}{}
}
}
}
ar, err := this.httpRpcClient.AllPost("game", "/server/info", nil, &proto.MsgServerInfoReq{UserId: userId}, &proto.MsgServerInfoAck{})
if err != nil {
return nil, proto.ErrServerMaintain
}
var listMap = map[uint32]*gameServerList{}
for appId, reply := range ar {
if reply.Err != nil {
this.Logger().Error("Get server online failed, ", reply.Err)
continue
}
if reply.Res.Error() != nil {
this.Logger().Errorf("Get server online failed, status: %d, message: %s", reply.Res.Status, reply.Res.Message)
continue
}
var node = this.httpRpcClient.Node("game", appId)
if node == nil {
continue
}
var infoAck = reply.Res.Data.(*proto.MsgServerInfoAck)
if platform != 0 {
if infoAck.Platform != platform {
continue
}
}
if channel != 0 {
if infoAck.Channel != channel {
continue
}
}
if _, closed := closedList[infoAck.Id]; closed {
if _, white := whiteList[userId]; !white {
continue
}
}
var gsl = listMap[infoAck.Id]
if gsl == nil {
gsl = &gameServerList{
id: infoAck.Id,
platform: infoAck.Platform,
channel: infoAck.Channel,
area: infoAck.Area,
name: infoAck.Name,
roleLv: infoAck.RoleLv,
}
listMap[infoAck.Id] = gsl
}
gsl.online += infoAck.Online
gsl.serverList = append(gsl.serverList, &gameServerInfo{appId: appId, address: node.Exter.Address, online: infoAck.Online})
}
var serverList []*proto.GameServer
for _, gsl := range listMap {
if len(gsl.serverList) == 0 {
continue
}
sort.Slice(gsl.serverList, func(i, j int) bool {
if gsl.serverList[i].online == gsl.serverList[j].online {
return gsl.serverList[i].appId < gsl.serverList[j].appId
} else {
return gsl.serverList[i].online < gsl.serverList[j].online
}
})
serverList = append(serverList, &proto.GameServer{
Id: gsl.id,
Area: gsl.area,
Name: gsl.name,
Address: gsl.serverList[0].address,
Online: gsl.online,
Recent: gsl.id == recentServer,
RoleLv: gsl.roleLv,
})
}
sort.Slice(serverList, func(i, j int) bool {
return serverList[i].Id < serverList[j].Id
})
return serverList, nil
}
func (this *GateManager) GetNotice() ([]*Notice, error) {
var now = util.NowUnix()
cur, err := this.mongoClient.Collection(tableNotice).Find(this.mongoClient.Context(), bson.M{"begin_time": bson.M{"$lt": now}, "end_time": bson.M{"$gt": now}})
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, nil
}
return nil, err
}
defer cur.Close(this.mongoClient.Context())
var notices []*Notice
for cur.Next(this.mongoClient.Context()) {
var notice = new(Notice)
err = cur.Decode(notice)
if err != nil {
return nil, err
}
notices = append(notices, notice)
}
return notices, nil
}
func (this *GateManager) AddNotice(notice *Notice) error {
_, err := this.mongoClient.Collection(tableNotice).InsertOne(this.mongoClient.Context(), notice)
return err
}
func (this *GateManager) DeleteNotice(id uint64) error {
_, err := this.mongoClient.Collection(tableNotice).DeleteOne(this.mongoClient.Context(), bson.M{"_id": id})
return err
}