admin_backend/util/login.go
2026-02-25 10:16:35 +08:00

285 lines
7.0 KiB
Go

package util
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/gin-gonic/gin"
)
type ServerConfig struct {
ServerId int `db:"ServerId"`
Host string `db:"Host"`
Port int `db:"Port"`
MaxOnline int `db:"MaxOnline"`
Online int `db:"Online"`
Version string `db:"version"`
Status int `db:"Status"`
Weight int `db:"Weight"`
WorkDir string `db:"work_dir"`
Ecs int `db:"ecs"`
Name string `db:"ServerName"`
AppId int `db:"AppId"`
NodeType int `db:"node_type"`
}
var serverList map[int][]ServerConfig
func LoginResponse(c *gin.Context, AppId, AreaCode int, Version string) {
Uid := c.Query("uid")
Port, Host, ServerId := GetUserInfo(AppId, AreaCode, Uid, Version)
LoginSuccess(c, "login success", Port, Host, ServerId)
}
func GetUserInfo(AppId, AreaCode int, Uid, Version string) (int, string, int) {
// now := time.Now().UnixMilli()
lockUid(Uid)
defer unlockUid(Uid)
// Db := MPool.GetGameDB()
// defer Db.Close()
var ServerId int
var Host string
var Port int
ServerId = 0
ServerList := GetServerInfo(AppId, AreaCode)
if len(ServerList) == 0 || Uid == "" || Version == "" {
return 0, "", 0
}
node, err := GetUserConnectNode(Uid)
if err != nil {
node = 0
}
if node > 0 {
for _, server := range ServerList {
if server.ServerId == node && server.Status == 1 && Version == server.Version {
ServerId = server.ServerId
Host = server.Host
Port = server.Port
break
}
}
}
if ServerId == 0 {
new_server_list := make([]ServerConfig, 0)
// 先尝试找版本号完全匹配且未满的服务器
for _, server := range ServerList {
if server.Status != 1 {
continue
}
if Version != "" && server.Version == Version {
new_server_list = append(new_server_list, server)
}
}
// 过滤出未满员且状态正常的候选服务器
// 加权随机选择
if len(new_server_list) > 0 {
totalWeight := 0
for _, s := range new_server_list {
w := s.Weight
if w <= 0 {
w = 1
}
totalWeight += w
}
if totalWeight == 0 {
// 兜底,选择第一个
ServerId = new_server_list[0].ServerId
Host = new_server_list[0].Host
Port = new_server_list[0].Port
} else {
r := rand.Intn(totalWeight)
acc := 0
for _, s := range new_server_list {
w := s.Weight
if w <= 0 {
w = 1
}
acc += w
if r < acc {
ServerId = s.ServerId
Host = s.Host
Port = s.Port
break
}
}
}
}
}
if ServerId == 0 {
ServerId = ServerList[0].ServerId
Host = ServerList[0].Host
Port = ServerList[0].Port
}
// 更新玩家所在节点
if err == nil {
SaveUserConnectNode(Uid, ServerId)
}
//log.Printf("GetUserInfo selected server %d for uid %s, cost time %dms\n", ServerId, Uid, time.Now().UnixMilli()-now)
return Port, Host, ServerId
}
// per-UID lock to serialize concurrent requests for the same uid
type uidLockRef struct {
mu sync.Mutex
refs int
}
var (
uidLocks = make(map[string]*uidLockRef)
uidLocksMu sync.Mutex
)
func lockUid(uid string) {
if uid == "" {
return
}
uidLocksMu.Lock()
ref, ok := uidLocks[uid]
if !ok {
ref = &uidLockRef{refs: 1}
uidLocks[uid] = ref
uidLocksMu.Unlock()
ref.mu.Lock()
return
}
ref.refs++
uidLocksMu.Unlock()
ref.mu.Lock()
}
func unlockUid(uid string) {
if uid == "" {
return
}
uidLocksMu.Lock()
ref, ok := uidLocks[uid]
if !ok {
uidLocksMu.Unlock()
return
}
ref.refs--
if ref.refs <= 0 {
delete(uidLocks, uid)
uidLocksMu.Unlock()
ref.mu.Unlock()
return
}
uidLocksMu.Unlock()
ref.mu.Unlock()
}
// func GetServerInfo(AppId, AreaCode int) []ServerConfig {
// Db := MPool.GetGameDB()
// defer Db.Close()
// rows, err := Db.Query("SELECT ServerId, Status, Host, Port, MaxOnline, Online, version, weight FROM server WHERE AppId = ? AND Status = 1 AND node_type = 1 ORDER BY ServerId", AppId)
// if err != nil {
// return nil
// }
// defer rows.Close()
// var servers []ServerConfig
// for rows.Next() {
// var server ServerConfig
// if err := rows.Scan(&server.ServerId, &server.Status, &server.Host, &server.Port, &server.MaxOnline, &server.Online, &server.Version, &server.Weight); err != nil {
// continue
// }
// servers = append(servers, server)
// }
// return servers
// }
func GetServer(AppId, ServerId int) ServerConfig {
Db := MPool.GetGameDB()
defer Db.Close()
var server ServerConfig
err := Db.Get(&server, "SELECT ServerId, ServerName, Status, Host, Port, MaxOnline, Online, version, ecs, work_dir, node_type FROM server WHERE AppId = ? AND ServerId = ?", AppId, ServerId)
if err != nil {
return ServerConfig{}
}
return server
}
// VersionDistance 计算两个版本号之间的跨度
// 版本号格式为 major.minor.patch (例如: 1.0.0)
// 返回值越大表示版本差异越大
func VersionDistance(v1, v2 string) int {
parse := func(version string) (int, int, int) {
var major, minor, patch int
fmt.Sscanf(version, "%d.%d.%d", &major, &minor, &patch)
return major, minor, patch
}
major1, minor1, patch1 := parse(v1)
major2, minor2, patch2 := parse(v2)
majorDiff := abs(major1 - major2)
minorDiff := abs(minor1 - minor2)
patchDiff := abs(patch1 - patch2)
// 主版本号差异权重最高,次版本号次之,补丁版本号最低
return majorDiff*10000 + minorDiff*100 + patchDiff
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func GetServerInfo(AppId, AreaCode int) []ServerConfig {
if serverList != nil {
return serverList[AppId]
}
return []ServerConfig{}
}
func MonitorServerList() {
_initServerList()
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
_initServerList()
}
}
func _initServerList() {
Db := MPool.GetGameDB()
defer Db.Close()
rows, err := Db.Query("SELECT AppId, ServerId, Status, Host, Port, MaxOnline, Online, version, weight FROM server WHERE Status = 1 AND node_type = 1 ORDER BY ServerId")
if err != nil {
return
}
defer rows.Close()
tempServerList := make(map[int][]ServerConfig)
for rows.Next() {
var server ServerConfig
if err := rows.Scan(&server.AppId, &server.ServerId, &server.Status, &server.Host, &server.Port, &server.MaxOnline, &server.Online, &server.Version, &server.Weight); err != nil {
continue
}
tempServerList[server.AppId] = append(tempServerList[server.AppId], server)
}
serverList = tempServerList
}
func GetAllServersByAppId(appid int) []ServerConfig {
Db := MPool.GetGameDB()
defer Db.Close()
rows, err := Db.Query("SELECT ServerId, ServerName, Status, Host, Port, MaxOnline, Online, version, ecs, work_dir, node_type FROM server WHERE AppId = ? ORDER BY ServerId", appid)
if err != nil {
return nil
}
defer rows.Close()
var servers []ServerConfig
for rows.Next() {
var server ServerConfig
if err := rows.Scan(&server.ServerId, &server.Name, &server.Status, &server.Host, &server.Port, &server.MaxOnline, &server.Online, &server.Version, &server.Ecs, &server.WorkDir, &server.NodeType); err != nil {
continue
}
servers = append(servers, server)
}
return servers
}