版本更新

This commit is contained in:
hahwu 2026-04-22 16:44:37 +08:00
parent 3dc71f22be
commit f0a0c4ac57
27 changed files with 1246 additions and 598 deletions

21
.env Normal file
View File

@ -0,0 +1,21 @@
OSS_ACCESS_KEY_ID=LTAI5t7e94rne7K7JYiJFm1N
OSS_ACCESS_KEY_SECRET=wvZyRmuQ81qwnVv2f3EOAZGgm0Ot2c
OSS_SESSION_TOKEN=
OSS_REGION=cn-hangzhou
OSS_BUCKET=merge-pet-elastic
OSS_OBJECT_KEY=example-object.txt
# Nacos connection
NACOS_SERVER_ADDRS=127.0.0.1:8848
NACOS_USERNAME=nacos
NACOS_PASSWORD=nacos
NACOS_NAMESPACE_ID=public
NACOS_GROUP=server
NACOS_TIMEOUT_MS=5000
NACOS_LOG_DIR=./log/nacos
NACOS_CACHE_DIR=./runtime/nacos
NACOS_LOG_LEVEL=info
# OSS config stored in Nacos JSON
OSS_NACOS_DATA_ID=oss.json
OSS_NACOS_GROUP=server

View File

@ -0,0 +1,42 @@
---
name: 游戏中台 Admin 后端专家
description: "Use when: 开发或维护游戏中台 web admin 服务端;处理 Golang Gin 接口、protobuf/thrift 协议、MySQL 数据访问、Python 配置工具、监控指标、负载均衡、GM 后台、管理后台接口、游戏服管理逻辑、elastic 日志收集整理。"
tools: [read, search, edit, execute, todo]
argument-hint: "描述你的后台服务端任务,例如修改接口、优化权重算法、调整协议字段、排查 MySQL 查询或配置生成流程"
user-invocable: true
---
你是资深的游戏中台 web admin 服务端工程师,技术栈包括 Golang、Gin、protobuf、thrift、Python、MySQL。你的任务是帮助用户在游戏管理后台和相关服务端代码中做出高质量、可验证、可上线的修改。
## 适用范围
- Go + Gin 的后台接口、路由、中间件、控制器、模型和工具函数
- protobuf / thrift 协议字段、序列化结构、兼容性调整
- MySQL 查询、数据模型、后台统计和管理功能
- Python 配置脚本、配置生成链路、后台依赖的静态配置
- 监控、权重分配、负载均衡、游戏服状态聚合
- GM 工具、运营配置、资源发放、账号或服务器管理逻辑
## 约束
- 只聚焦游戏中台 admin 服务端,不主动扩展到无关前端或基础设施改造
- 优先修根因,不做只掩盖问题的表层补丁
- 优先最小改动,保持现有协议、表结构和调用方兼容
- 修改协议、配置或权重逻辑时,必须检查上下游消费方
- 涉及数据库、配置、协议生成时,优先做窄范围验证,避免全量破坏性操作
- 不在未确认需求的情况下重写模块或大范围重构
## 工具偏好
- 先用搜索和局部阅读定位真实控制路径,再动手修改
- 优先做可执行验证:局部编译、针对性测试、协议生成、配置生成
- 需要命令执行时,使用最小范围的构建、测试或生成命令
- 除非任务明确要求,不依赖外网资料来替代仓库事实
## 工作方式
1. 先定位用户请求对应的控制路径、协议定义、表结构或配置链路。
2. 基于真实代码提出一个可证伪的本地假设,并优先做最小修改。
3. 修改后立刻做窄范围验证,例如相关包编译、单测、生成任务或查询校验。
4. 返回结果时明确说明改了什么、为什么这样改、验证是否通过、剩余风险是什么。
## 输出要求
- 先给结论,再补关键依据
- 涉及文件时引用具体文件位置
- 如果存在歧义,明确列出需要用户确认的点
- 如果无法安全落地,给出最小可行替代方案

3
.gitignore vendored
View File

@ -12,4 +12,5 @@ log/*.log
*.log
runtime/*.db
config/*
unit/config/*
unit/config/*
runtime/*

View File

@ -206,3 +206,8 @@ type EventData struct {
Param string `json:"Param"`
Timestamp int `json:"Timestamp"`
}
type LoginDailyCount struct {
Date string `json:"date"`
Count int64 `json:"count"`
}

View File

@ -98,3 +98,28 @@ func GetUserInfo(AppId, ServerId int, req *msg.UserDetailParam) (*msg.ResUserDet
}
return resp.GetInfo(), nil
}
func GetServerInfo(AppId, ServerId int, req *msg.ReqServerInfo) (*msg.ResServerInfo, error) {
ServerConfig, err := util.GetServerConfig(AppId, ServerId)
if err != nil {
return nil, fmt.Errorf("failed to get server config: %v", err)
}
if ServerConfig == nil {
return nil, fmt.Errorf("server config not found for AppId %d and ServerId %d", AppId, ServerId)
}
conn, err := grpc.NewClient(fmt.Sprintf("%s:%d", ServerConfig.Host, ServerConfig.GrpcPort), grpc.WithTransportCredentials(insecure.NewCredentials()))
// TODO : 目前先连接本地,后续改为连接对应服务器
//conn, err := grpc.NewClient("127.0.0.1:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, fmt.Errorf("failed to connect: %v", err)
}
defer conn.Close()
client := msg.NewBackendClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
resp, err := client.ServerInfo(ctx, req)
if err != nil {
return nil, fmt.Errorf("ServerInfo failed: %v", err)
}
return resp, nil
}

View File

@ -5,6 +5,8 @@ import (
"log"
"os"
"gitea.bywaystudios.com/pet_home/nacos"
"gopkg.in/yaml.v2"
)
@ -65,11 +67,12 @@ type LanguageLimit struct {
var config *Config
var tranlaterConfig *TranlaterConfig
func Init() {
func Init() error {
err := loadConfig()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
return fmt.Errorf("failed to load config: %w", err)
}
return nil
}
func InitTranlater() error {
@ -85,15 +88,23 @@ func InitTranlater() error {
}
func loadConfig() error {
data, err := os.ReadFile("conf/server.yml")
nacosClient, err := nacos.GetNacosClient()
if err != nil {
return err
return fmt.Errorf("get nacos client: %w", err)
}
err = yaml.Unmarshal(data, &config)
err = nacosClient.GetYAML("web-backend-env-conf", "web", &config)
if err != nil {
return err
return fmt.Errorf("get nacos config: %w", err)
}
nacosClient.Listen("web-backend-env-conf", "web", func(event nacos.ChangeEvent) {
var newConfig Config
if err := yaml.Unmarshal([]byte(event.Content), &newConfig); err != nil {
log.Printf("解析 Nacos 配置变更失败: %v", err)
return
}
config = &newConfig
log.Printf("Nacos 配置已更新: %v", config)
})
return nil
}

View File

@ -1,5 +1,4 @@
system:
nmap: false
feishu_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/70e24a79-b019-434a-b4d1-4592bbf7c311'
notice_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/64bad1f3-3a41-4dca-9037-399067ffb252'
operation_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/70e24a79-b019-434a-b4d1-4592bbf7c311'
@ -7,25 +6,25 @@ system:
operation_chat_id: 'oc_f6e10a55f28f31e2a5677bdcb6aed599'
us_operation_chat_id: 'oc_a3d1268f63573563fdaea3dedb3bf78d'
client_chat_id: 'oc_f6e10a55f28f31e2a5677bdcb6aed599'
ssh: true
ssh: false
env: 'dev'
rpc_switch: true
mysqls:
- host: '127.0.0.1'
- host: '112.124.54.243'
name: 'merge_pet_test'
port: 3306
user: 'root'
password: 'GWFj1cHaqjpzvcsHBWTFtLWtm8MUZKROx_wvbV6jPg=='
database: 'pet_home_%d'
idleTimeout: Infinity
- host: '127.0.0.1'
- host: '1.15.182.107'
name: 'merge_pet_audit'
port: 3306
user: 'root'
password: 'GWFj1cHaqjpzvcsHBWTFtLWtm8MUZKROx_wvbV6jPg=='
database: 'merge_pet_audit_%d'
idleTimeout: Infinity
- host: '127.0.0.1'
- host: '112.124.54.243'
name: 'merge_pet_sdk'
port: 3306
user: 'root'
@ -53,25 +52,3 @@ mysqls:
password: 'GWFj1cHaqjpzvcsHBWTFtLWtm8MUZKROx_wvbV6jPg=='
database: 'game'
idleTimeout: Infinity
servers:
- name: 'server1'
host: '112.124.54.243'
port: 22
username: 'root'
password: 'H3ukUvaLVLvWRXn9zGeAM9fspkGy6VUpMn7bEtWaTg=='
- name: 'server2'
host: '47.254.83.25'
port: 22
username: 'root'
password: 'ynAMWcS4YpHozPHuudywcIZuBnTiSSaWF2iLoSlRVA=='
- name: 'london'
host: '8.208.47.208'
port: 22
username: 'root'
password: 'dhzyiIMG0LkzSmY0lVEukdbM1uIt8OY8qljhgUJnBT8='
- name: 'log'
host: '8.155.14.94'
port: 22
username: 'root'
password: 'gDcfdjgyq60A-8q_cZ_CbSQnTlY0YakpiYVyTmFTEkk='

View File

@ -36,6 +36,21 @@ func Event(c *gin.Context) {
success(c, res)
}
func LoginCountByMonth(c *gin.Context) {
log := model.Log{}
err := c.BindJSON(&log)
if err != nil {
failed(c, err.Error())
return
}
res, err := log.LoginCountByMonth()
if err != nil {
failed(c, err.Error())
return
}
success(c, res)
}
func Order(c *gin.Context) {
log := model.Log{}
err := c.BindJSON(&log)

View File

@ -375,3 +375,19 @@ func CopyOnlineStep4(c *gin.Context) {
"code": 0,
})
}
func ClientImageGitPull(c *gin.Context) {
nodeInfo := util.GetNodeByName("devops")
SshClient, err := util.NewSshClient(nodeInfo)
if err != nil {
scritp_fail(c, "连接devops节点失败: "+err.Error())
return
}
defer SshClient.Close()
cmd := "ansible-playbook /data/devops/playbook/web-client.yml -i /data/devops/playbook/hosts"
output, err := SshClient.RunCommand(cmd)
if err != nil {
scritp_fail(c, "执行更新翻译图标失败: "+err.Error()+": "+output)
return
}
}

View File

@ -28,10 +28,6 @@ func AppList(c *gin.Context) {
}
}
}
if err != nil {
failed(c, err.Error())
return
}
success(c, AppList)
}

View File

@ -1,13 +1,10 @@
package controller
import (
"backend/Type"
"backend/common"
"backend/model"
"backend/store"
"backend/util"
"fmt"
"strings"
"github.com/gin-gonic/gin"
)
@ -77,67 +74,24 @@ func UserDetail(c *gin.Context) {
failed(c, err.Error())
return
}
if common.GetEnv() == "dev" {
user, err := model.UserDetail2(request.AppId, request.Uid, request.Node)
if err != nil {
fmt.Print(err)
failed(c, err.Error())
return
}
for _, v := range user.Order {
for _, chessInfo := range v.Chess {
chess_url := util.GetChessURL(util.String(chessInfo.GetId()))
chessInfo.Icon = chess_url
}
v.ChessId = v.Chess
}
for _, v := range user.FriendList {
face_url := util.GetFaceURL(util.Int(v.Avatar))
v.AvatarUrl = face_url
v.OnlineStatus = util.Int(v.LoginTime) > util.Int(v.LogoutTime)
}
success(c, user)
return
}
user, err := model.UserDetail(request.AppId, request.Uid, request.Node)
if user["Order"] != nil {
order := user["Order"].(map[string]interface{})
for k, v := range order {
info := v.(map[string]interface{})
chess_id := info["ChessId"].(string)
chess_list := strings.Split(chess_id, " ")
chess_arr := []Type.ChessData{}
for _, cid := range chess_list {
if cid == "" {
continue
}
chess_url := util.GetChessURL(cid)
chess_arr = append(chess_arr, Type.ChessData{
Id: cid,
Icon: chess_url,
})
}
info["ChessId"] = chess_arr
order[k] = info
}
}
if user["FriendList"] != nil {
friendList := user["FriendList"].([]interface{})
for k, v := range friendList {
info := v.(map[string]interface{})
face_url := util.GetFaceURL(util.Int(info["Avatar"]))
info["AvatarUrl"] = face_url
friendList[k] = info
info["OnlineStatus"] = util.Int(info["LoginTime"]) > util.Int(info["LogoutTime"])
}
user["FriendList"] = friendList
}
user, err := model.UserDetail2(request.AppId, request.Uid, request.Node)
if err != nil {
fmt.Print(err)
failed(c, err.Error())
return
}
for _, v := range user.Order {
for _, chessInfo := range v.Chess {
chess_url := util.GetChessURL(util.String(chessInfo.GetId()))
chessInfo.Icon = chess_url
}
v.ChessId = v.Chess
}
for _, v := range user.FriendList {
face_url := util.GetFaceURL(util.Int(v.Avatar))
v.AvatarUrl = face_url
v.OnlineStatus = util.Int(v.LoginTime) > util.Int(v.LogoutTime)
}
success(c, user)
}

7
go.mod
View File

@ -1,6 +1,6 @@
module backend
go 1.25.0
go 1.25.5
require (
github.com/Ullaakut/nmap v2.0.2+incompatible
@ -36,6 +36,7 @@ require github.com/nacos-group/nacos-sdk-go/v2 v2.3.5
require (
filippo.io/edwards25519 v1.1.0 // indirect
gitea.bywaystudios.com/pet_home/nacos v0.0.0-20260422063809-d6268b6aa6e3 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-array v0.1.0 // indirect
@ -57,6 +58,7 @@ require (
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1800 // indirect
github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.5.1 // indirect
github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.8 // indirect
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.4.1 // indirect
github.com/aliyun/aliyun-secretsmanager-client-go v1.1.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
@ -80,6 +82,7 @@ require (
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
@ -117,7 +120,7 @@ require (
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/time v0.4.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

8
go.sum
View File

@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gitea.bywaystudios.com/pet_home/nacos v0.0.0-20260422063809-d6268b6aa6e3 h1:uqq/4VOcpAxjpYH2DwdTc4+VZPGi1Nlavdkcu0mr0V4=
gitea.bywaystudios.com/pet_home/nacos v0.0.0-20260422063809-d6268b6aa6e3/go.mod h1:0VmTyAIjgzP6OWN4IlomxfcGIBsslBdGb1wW+DgojcY=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@ -126,6 +128,8 @@ github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.5.1 h1:nJYyoFP+aqGKgPs9JeZgS1r
github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.5.1/go.mod h1:WzGOmFFTlUzXM03CJnHWMQ85UN6QGpOXZocCjwkiyOg=
github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.8 h1:QeUdR7JF7iNCvO/81EhxEr3wDwxk4YBoYZOq6E0AjHI=
github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.8/go.mod h1:xP0KIZry6i7oGPF24vhAPr1Q8vLZRcMcxtft5xDKwCU=
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.4.1 h1:wF5rZUhhahzJiRSeLSCQhAkaDBXLa/R893C/ZmEpGcE=
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.4.1/go.mod h1:FTzydeQVmR24FI0D6XWUOMKckjXehM/jgMn1xC+DA9M=
github.com/aliyun/aliyun-secretsmanager-client-go v1.1.5 h1:8S0mtD101RDYa0LXwdoqgN0RxdMmmJYjq8g2mk7/lQ4=
github.com/aliyun/aliyun-secretsmanager-client-go v1.1.5/go.mod h1:M19fxYz3gpm0ETnoKweYyYtqrtnVtrpKFpwsghbw+cQ=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
@ -285,6 +289,8 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@ -689,6 +695,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

View File

@ -119,6 +119,7 @@ func main() {
api.POST("/log/user", controller.UserDetail)
api.POST("/log/asset", controller.Asset)
api.POST("/log/event", controller.Event)
api.POST("/log/loginCountByMonth", controller.LoginCountByMonth)
api.POST("/log/order", controller.Order)
// 玩家数据
@ -170,8 +171,9 @@ func main() {
// 自动化脚本
scripts := r.Group("/api/scripts", middleware.ValidateToken())
{
scripts.POST("/copywriting", controller.Copywriting) // 下载文案文件
scripts.POST("/copyonline", controller.CopyOnline) // 复制线上环境到QA
scripts.POST("/copywriting", controller.Copywriting) // 下载文案文件
scripts.POST("/copyonline", controller.CopyOnline) // 复制线上环境到QA
scripts.POST("/clientImageGitPull", controller.ClientImageGitPull) // 复制线下环境到QA
}
config.InitConfig()
//go util.ScheduleDailyTask()

View File

@ -692,11 +692,14 @@ func SendOrderMsg(data *Type.OrderData) (_err error) {
SearchSupport: imGroupOpenSpaceModelSearchSupport,
}
var isTest string
var color string
chargeUid := global_config.GetTestChargeUidList()
if slices.Contains(chargeUid, data.UID) {
isTest = "[测试]"
isTest = "[测试账号]"
color = "blue"
} else {
isTest = ""
color = "green"
}
contentStr := `
# **充值发货通知**
@ -716,7 +719,7 @@ func SendOrderMsg(data *Type.OrderData) (_err error) {
cardDataCardParamMap := map[string]any{
"title": "[充值]充值发货通知",
"markdown": content,
"color": "green",
"color": color,
"config": map[string]any{
"autoLayout": true,
},

299
middleware/alibaba/oss.go Normal file
View File

@ -0,0 +1,299 @@
package alibaba
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"strconv"
"strings"
"sync"
"gitea.bywaystudios.com/pet_home/nacos"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/joho/godotenv"
)
const (
defaultOSSRegion = "cn-hangzhou"
defaultOSSBucket = "merge-pet-elastic"
defaultOSSObjectKey = "example-object.txt"
defaultNacosDataID = "oss.json"
defaultNacosGroup = "server"
defaultNacosLogDir = "./log/nacos"
defaultNacosCacheDir = "./runtime/nacos"
)
type ossConfig struct {
Region string `json:"region" yaml:"region"`
Bucket string `json:"bucket" yaml:"bucket"`
ObjectKey string `json:"objectKey" yaml:"objectKey"`
AccessKeyID string `json:"accessKeyId" yaml:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret" yaml:"accessKeySecret"`
SessionToken string `json:"sessionToken" yaml:"sessionToken"`
}
type ossRuntime struct {
mu sync.RWMutex
config ossConfig
}
var (
globalOSSRuntime ossRuntime
ossInitOnce sync.Once
dotenvLoadOnce sync.Once
)
func Osscreate() {
config, err := initOSSConfig()
if err != nil {
log.Fatalf("加载 OSS 配置失败: %v", err)
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewStaticCredentialsProvider(config.AccessKeyID, config.AccessKeySecret, config.SessionToken)).
WithRegion(config.Region)
client := oss.NewClient(cfg)
body := strings.NewReader("Hello OSS from Go SDK V2!")
request := &oss.PutObjectRequest{
Bucket: oss.Ptr(config.Bucket),
Key: oss.Ptr(config.ObjectKey),
Body: body,
}
result, err := client.PutObject(context.TODO(), request)
if err != nil {
log.Fatalf("上传失败: %v", err)
}
log.Printf("上传成功! ETag: %s\n", *result.ETag)
}
func initOSSConfig() (ossConfig, error) {
loadDotEnv()
var initErr error
ossInitOnce.Do(func() {
config, err := loadOSSConfig()
if err != nil {
initErr = err
return
}
globalOSSRuntime.set(config)
if err := watchOSSConfigChanges(); err != nil {
log.Printf("监听 Nacos OSS 配置失败,继续使用当前配置: %v", err)
}
})
if initErr != nil {
return ossConfig{}, initErr
}
config, ok := globalOSSRuntime.get()
if !ok {
return ossConfig{}, fmt.Errorf("oss config is not initialized")
}
return config, nil
}
func loadDotEnv() {
dotenvLoadOnce.Do(func() {
if err := godotenv.Load(); err != nil {
if !os.IsNotExist(err) {
log.Printf("加载 .env 失败,继续使用现有环境变量: %v", err)
}
}
})
}
func loadOSSConfig() (ossConfig, error) {
baseConfig := loadOSSConfigFromEnv()
config := baseConfig
nacosConfig, err := loadOSSConfigFromNacos()
if err != nil {
log.Printf("从 Nacos 读取 OSS 配置失败,回退环境变量: %v", err)
} else {
config = mergeOSSConfig(baseConfig, nacosConfig)
}
return normalizeOSSConfig(config)
}
func loadOSSConfigFromEnv() ossConfig {
return ossConfig{
Region: strings.TrimSpace(os.Getenv("OSS_REGION")),
Bucket: strings.TrimSpace(os.Getenv("OSS_BUCKET")),
ObjectKey: strings.TrimSpace(os.Getenv("OSS_OBJECT_KEY")),
AccessKeyID: strings.TrimSpace(os.Getenv("OSS_ACCESS_KEY_ID")),
AccessKeySecret: strings.TrimSpace(os.Getenv("OSS_ACCESS_KEY_SECRET")),
SessionToken: strings.TrimSpace(os.Getenv("OSS_SESSION_TOKEN")),
}
}
func loadOSSConfigFromNacos() (ossConfig, error) {
client, err := newNacosClient()
if err != nil {
return ossConfig{}, err
}
var config ossConfig
dataID := getEnvOrDefault("OSS_NACOS_DATA_ID", defaultNacosDataID)
group := getEnvOrDefault("OSS_NACOS_GROUP", getEnvOrDefault("NACOS_GROUP", defaultNacosGroup))
if err := client.GetJSON(dataID, group, &config); err != nil {
return ossConfig{}, err
}
return config, nil
}
func watchOSSConfigChanges() error {
client, err := newNacosClient()
if err != nil {
return err
}
dataID := getEnvOrDefault("OSS_NACOS_DATA_ID", defaultNacosDataID)
group := getEnvOrDefault("OSS_NACOS_GROUP", getEnvOrDefault("NACOS_GROUP", defaultNacosGroup))
return client.Listen(dataID, group, func(event nacos.ChangeEvent) {
var nacosConfig ossConfig
if err := json.Unmarshal([]byte(event.Content), &nacosConfig); err != nil {
log.Printf("解析 Nacos OSS 配置变更失败: %v", err)
return
}
mergedConfig, err := normalizeOSSConfig(mergeOSSConfig(loadOSSConfigFromEnv(), nacosConfig))
if err != nil {
log.Printf("Nacos OSS 配置变更无效,保留旧配置: %v", err)
return
}
globalOSSRuntime.set(mergedConfig)
log.Printf("OSS 配置已从 Nacos 热更新: group=%s dataId=%s", event.Group, event.DataID)
})
}
func newNacosClient() (*nacos.Client, error) {
serverAddrs := strings.TrimSpace(os.Getenv("NACOS_SERVER_ADDRS"))
if serverAddrs == "" {
return nil, fmt.Errorf("NACOS_SERVER_ADDRS is empty")
}
return nacos.NewClient(nacos.Options{
Servers: parseNacosServers(serverAddrs),
Username: strings.TrimSpace(os.Getenv("NACOS_USERNAME")),
Password: strings.TrimSpace(os.Getenv("NACOS_PASSWORD")),
NamespaceID: strings.TrimSpace(os.Getenv("NACOS_NAMESPACE_ID")),
TimeoutMs: parseUintEnv("NACOS_TIMEOUT_MS", 5000),
LogDir: getEnvOrDefault("NACOS_LOG_DIR", defaultNacosLogDir),
CacheDir: getEnvOrDefault("NACOS_CACHE_DIR", defaultNacosCacheDir),
LogLevel: getEnvOrDefault("NACOS_LOG_LEVEL", "info"),
NotLoadCacheAtStart: true,
DefaultGroup: getEnvOrDefault("NACOS_GROUP", defaultNacosGroup),
})
}
func normalizeOSSConfig(config ossConfig) (ossConfig, error) {
if strings.TrimSpace(config.Region) == "" {
config.Region = defaultOSSRegion
}
if strings.TrimSpace(config.Bucket) == "" {
config.Bucket = defaultOSSBucket
}
if strings.TrimSpace(config.ObjectKey) == "" {
config.ObjectKey = defaultOSSObjectKey
}
if strings.TrimSpace(config.AccessKeyID) == "" || strings.TrimSpace(config.AccessKeySecret) == "" {
return ossConfig{}, fmt.Errorf("oss access key is empty after nacos and environment fallback")
}
return config, nil
}
func mergeOSSConfig(base ossConfig, override ossConfig) ossConfig {
if strings.TrimSpace(override.Region) != "" {
base.Region = strings.TrimSpace(override.Region)
}
if strings.TrimSpace(override.Bucket) != "" {
base.Bucket = strings.TrimSpace(override.Bucket)
}
if strings.TrimSpace(override.ObjectKey) != "" {
base.ObjectKey = strings.TrimSpace(override.ObjectKey)
}
if strings.TrimSpace(override.AccessKeyID) != "" {
base.AccessKeyID = strings.TrimSpace(override.AccessKeyID)
}
if strings.TrimSpace(override.AccessKeySecret) != "" {
base.AccessKeySecret = strings.TrimSpace(override.AccessKeySecret)
}
if strings.TrimSpace(override.SessionToken) != "" {
base.SessionToken = strings.TrimSpace(override.SessionToken)
}
return base
}
func parseNacosServers(raw string) []nacos.Server {
parts := strings.Split(raw, ",")
servers := make([]nacos.Server, 0, len(parts))
for _, part := range parts {
item := strings.TrimSpace(part)
if item == "" {
continue
}
server := nacos.Server{IPAddr: item}
if strings.Contains(item, ":") {
host, port, found := strings.Cut(item, ":")
if found {
server.IPAddr = strings.TrimSpace(host)
if parsedPort, convErr := strconv.ParseUint(strings.TrimSpace(port), 10, 64); convErr == nil {
server.Port = parsedPort
}
} else {
server.IPAddr = item
}
}
servers = append(servers, server)
}
return servers
}
func (r *ossRuntime) set(config ossConfig) {
r.mu.Lock()
defer r.mu.Unlock()
r.config = config
}
func (r *ossRuntime) get() (ossConfig, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
if strings.TrimSpace(r.config.AccessKeyID) == "" || strings.TrimSpace(r.config.AccessKeySecret) == "" {
return ossConfig{}, false
}
return r.config, true
}
func parseUintEnv(key string, defaultValue uint64) uint64 {
raw := strings.TrimSpace(os.Getenv(key))
if raw == "" {
return defaultValue
}
value, err := strconv.ParseUint(raw, 10, 64)
if err != nil {
return defaultValue
}
return value
}
func getEnvOrDefault(key string, defaultValue string) string {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return defaultValue
}
return value
}

View File

@ -1,323 +0,0 @@
package nacos
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"gopkg.in/yaml.v2"
)
const (
DefaultGroup = "DEFAULT_GROUP"
ContentTypeText = "text"
ContentTypeJSON = "json"
ContentTypeYAML = "yaml"
defaultTimeoutMs = 5000
defaultNacosPort = 8848
defaultNacosScheme = "http"
)
var (
ErrEmptyDataID = errors.New("nacos data id is required")
ErrNoServers = errors.New("at least one nacos server is required")
)
type Reader interface {
Get(dataID, group string) (string, error)
GetJSON(dataID, group string, out interface{}) error
GetYAML(dataID, group string, out interface{}) error
}
type Writer interface {
Publish(dataID, group, content string, contentType string) error
PublishJSON(dataID, group string, value interface{}) error
PublishYAML(dataID, group string, value interface{}) error
Delete(dataID, group string) error
}
type Watcher interface {
Listen(dataID, group string, handler ChangeHandler) error
CancelListen(dataID, group string) error
}
type ConfigStore interface {
Reader
Writer
Watcher
}
type Server struct {
IPAddr string
Port uint64
Scheme string
ContextPath string
}
type Options struct {
NamespaceID string
Username string
Password string
TimeoutMs uint64
LogDir string
CacheDir string
LogLevel string
NotLoadCacheAtStart bool
DefaultGroup string
Servers []Server
}
type ChangeEvent struct {
Namespace string
Group string
DataID string
Content string
}
type ChangeHandler func(event ChangeEvent)
type Client struct {
client config_client.IConfigClient
defaultGroup string
}
func NewClient(options Options) (*Client, error) {
normalized, err := normalizeOptions(options)
if err != nil {
return nil, err
}
serverConfigs := make([]constant.ServerConfig, 0, len(normalized.Servers))
for _, server := range normalized.Servers {
serverConfigs = append(serverConfigs, constant.ServerConfig{
IpAddr: server.IPAddr,
Port: server.Port,
Scheme: server.Scheme,
ContextPath: server.ContextPath,
})
}
clientConfig := constant.ClientConfig{
NamespaceId: normalized.NamespaceID,
TimeoutMs: normalized.TimeoutMs,
NotLoadCacheAtStart: normalized.NotLoadCacheAtStart,
LogDir: normalized.LogDir,
CacheDir: normalized.CacheDir,
LogLevel: normalized.LogLevel,
Username: normalized.Username,
Password: normalized.Password,
}
configClient, err := clients.NewConfigClient(vo.NacosClientParam{
ClientConfig: &clientConfig,
ServerConfigs: serverConfigs,
})
if err != nil {
return nil, fmt.Errorf("create nacos config client: %w", err)
}
return &Client{
client: configClient,
defaultGroup: normalized.DefaultGroup,
}, nil
}
func (c *Client) Get(dataID, group string) (string, error) {
param, err := c.configParam(dataID, group)
if err != nil {
return "", err
}
content, err := c.client.GetConfig(param)
if err != nil {
return "", fmt.Errorf("get nacos config %s/%s: %w", param.Group, param.DataId, err)
}
return content, nil
}
func (c *Client) GetJSON(dataID, group string, out interface{}) error {
content, err := c.Get(dataID, group)
if err != nil {
return err
}
if err := json.Unmarshal([]byte(content), out); err != nil {
return fmt.Errorf("unmarshal nacos json %s/%s: %w", c.groupOrDefault(group), dataID, err)
}
return nil
}
func (c *Client) GetYAML(dataID, group string, out interface{}) error {
content, err := c.Get(dataID, group)
if err != nil {
return err
}
if err := yaml.Unmarshal([]byte(content), out); err != nil {
return fmt.Errorf("unmarshal nacos yaml %s/%s: %w", c.groupOrDefault(group), dataID, err)
}
return nil
}
func (c *Client) Publish(dataID, group, content string, contentType string) error {
param, err := c.configParam(dataID, group)
if err != nil {
return err
}
param.Content = content
param.Type = normalizeContentType(contentType)
ok, err := c.client.PublishConfig(param)
if err != nil {
return fmt.Errorf("publish nacos config %s/%s: %w", param.Group, param.DataId, err)
}
if !ok {
return fmt.Errorf("publish nacos config %s/%s failed", param.Group, param.DataId)
}
return nil
}
func (c *Client) PublishJSON(dataID, group string, value interface{}) error {
payload, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("marshal nacos json %s/%s: %w", c.groupOrDefault(group), dataID, err)
}
return c.Publish(dataID, group, string(payload), ContentTypeJSON)
}
func (c *Client) PublishYAML(dataID, group string, value interface{}) error {
payload, err := yaml.Marshal(value)
if err != nil {
return fmt.Errorf("marshal nacos yaml %s/%s: %w", c.groupOrDefault(group), dataID, err)
}
return c.Publish(dataID, group, string(payload), ContentTypeYAML)
}
func (c *Client) Delete(dataID, group string) error {
param, err := c.configParam(dataID, group)
if err != nil {
return err
}
ok, err := c.client.DeleteConfig(param)
if err != nil {
return fmt.Errorf("delete nacos config %s/%s: %w", param.Group, param.DataId, err)
}
if !ok {
return fmt.Errorf("delete nacos config %s/%s failed", param.Group, param.DataId)
}
return nil
}
func (c *Client) Listen(dataID, group string, handler ChangeHandler) error {
param, err := c.configParam(dataID, group)
if err != nil {
return err
}
param.OnChange = func(namespace, listenGroup, listenDataID, data string) {
if handler == nil {
return
}
handler(ChangeEvent{
Namespace: namespace,
Group: listenGroup,
DataID: listenDataID,
Content: data,
})
}
if err := c.client.ListenConfig(param); err != nil {
return fmt.Errorf("listen nacos config %s/%s: %w", param.Group, param.DataId, err)
}
return nil
}
func (c *Client) CancelListen(dataID, group string) error {
param, err := c.configParam(dataID, group)
if err != nil {
return err
}
if err := c.client.CancelListenConfig(param); err != nil {
return fmt.Errorf("cancel nacos listen %s/%s: %w", param.Group, param.DataId, err)
}
return nil
}
func (c *Client) configParam(dataID, group string) (vo.ConfigParam, error) {
trimmedDataID := strings.TrimSpace(dataID)
if trimmedDataID == "" {
return vo.ConfigParam{}, ErrEmptyDataID
}
return vo.ConfigParam{
DataId: trimmedDataID,
Group: c.groupOrDefault(group),
}, nil
}
func (c *Client) groupOrDefault(group string) string {
trimmedGroup := strings.TrimSpace(group)
if trimmedGroup != "" {
return trimmedGroup
}
if c.defaultGroup != "" {
return c.defaultGroup
}
return DefaultGroup
}
func (c *Client) ListConfigs(param vo.SearchConfigParam) (*model.ConfigPage, error) {
return c.client.SearchConfig(param)
}
func normalizeOptions(options Options) (Options, error) {
if len(options.Servers) == 0 {
return Options{}, ErrNoServers
}
if options.TimeoutMs == 0 {
options.TimeoutMs = defaultTimeoutMs
}
if strings.TrimSpace(options.DefaultGroup) == "" {
options.DefaultGroup = DefaultGroup
}
normalizedServers := make([]Server, 0, len(options.Servers))
for _, server := range options.Servers {
ipAddr := strings.TrimSpace(server.IPAddr)
if ipAddr == "" {
return Options{}, errors.New("nacos server ip is required")
}
if server.Port == 0 {
server.Port = defaultNacosPort
}
if strings.TrimSpace(server.Scheme) == "" {
server.Scheme = defaultNacosScheme
}
server.IPAddr = ipAddr
normalizedServers = append(normalizedServers, server)
}
options.Servers = normalizedServers
return options, nil
}
func normalizeContentType(contentType string) string {
switch strings.ToLower(strings.TrimSpace(contentType)) {
case "", ContentTypeText:
return ContentTypeText
case "yml", ContentTypeYAML:
return ContentTypeYAML
case ContentTypeJSON:
return ContentTypeJSON
default:
return strings.ToLower(strings.TrimSpace(contentType))
}
}

View File

@ -11,6 +11,7 @@ type Log struct {
PageSize int `json:"PageSize"`
CurrentPage int `json:"CurrentPage"`
AppId int `json:"AppId"`
Month string `json:"Month"`
EventParam string `json:"Event"`
StartTime int64 `json:"StartTime"`
EndTime int64 `json:"EndTime"`
@ -30,6 +31,12 @@ type ResEvent struct {
Data []*Type.EventData `json:"data"`
}
type ResLoginCount struct {
Month string `json:"month"`
Total int `json:"total"`
Data []*Type.LoginDailyCount `json:"data"`
}
type ResOrder struct {
Total int `json:"total"`
Data []*Order `json:"data"`
@ -111,6 +118,18 @@ func (m *Log) Event() (*ResEvent, error) {
}, nil
}
func (m *Log) LoginCountByMonth() (*ResLoginCount, error) {
data, total, err := util.SearchLoginCountByUidMonth(m.AppId, m.Uid, m.Month)
if err != nil {
return nil, err
}
return &ResLoginCount{
Month: m.Month,
Total: int(total),
Data: data,
}, nil
}
func (m *Log) Order() (*ResOrder, error) {
m.AppId = m.Uid / 100000000
AppConfig, err := util.GetAppConfig(m.AppId)

View File

@ -38,6 +38,7 @@ type Mail struct {
SendType int `json:"send_type" db:"send_type"`
ToUids string `json:"to_uids" db:"to_uids"`
CreateTime int64 `json:"create_time" db:"create_time"`
MinLevel int `json:"min_level" db:"level"`
}
type Result struct {
@ -87,7 +88,8 @@ func (m *Mail) SendMail() error {
return fmt.Errorf("failed to get mysql db")
}
defer Db.Close()
_, err = Db.Exec("INSERT INTO system_mail_info (`title`, `content`, `title_en`, `content_en`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`, `send_type`, `to_uids`, `create_time`, `subTitle`, `subTitle_en`, `title_ptbr`,`content_ptbr`,`subTitle_ptbr`, `title_es_latam`, `subTitle_es_latam`, `content_es_latam`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.Title, m.Content, m.TitleEn, m.ContentEn, m.StartTime, m.EndTime, m.Items, m.RegisterTime, m.MailType, m.SendType, m.ToUids, m.CreateTime, m.SubTitle, m.SubTitleEn, m.TitlePtBr, m.ContentPtBr, m.SubTitlePtBr, m.TitleEsLatam, m.SubTitleEsLatam, m.ContentEsLatam)
m.CreateTime = util.Now()
_, err = Db.Exec("INSERT INTO system_mail_info (`title`, `content`, `title_en`, `content_en`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`, `send_type`, `to_uids`, `create_time`, `subTitle`, `subTitle_en`, `title_ptbr`,`content_ptbr`,`subTitle_ptbr`, `title_es_latam`, `subTitle_es_latam`, `content_es_latam`, `level`, `create_time`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.Title, m.Content, m.TitleEn, m.ContentEn, m.StartTime, m.EndTime, m.Items, m.RegisterTime, m.MailType, m.SendType, m.ToUids, m.CreateTime, m.SubTitle, m.SubTitleEn, m.TitlePtBr, m.ContentPtBr, m.SubTitlePtBr, m.TitleEsLatam, m.SubTitleEsLatam, m.ContentEsLatam, m.MinLevel, m.CreateTime)
if err != nil {
return fmt.Errorf("failed to insert mail: %v", err)
}

View File

@ -2,12 +2,16 @@ package monitor
import (
"backend/Type"
"backend/client"
"backend/middleware/alibaba"
"backend/model"
"backend/msg"
"backend/util"
"fmt"
"net"
"time"
"google.golang.org/protobuf/encoding/protojson"
)
func UserAliveMonitor(AppId int) {
@ -32,8 +36,8 @@ func UserAliveMonitor(AppId int) {
`
if drop >= 0.3 && (yCount-curCount) >= int64(10) {
alibaba.SendAliveMsg("服务器报警", fmt.Sprintf(str,
time.Now().Add(-time.Hour).Format("2006-01-02 15:04:05"),
time.Now().Format("2006-01-02 15:04:05"),
time.Now().Add(time.Hour).Format("2006-01-02 15:04:05"),
yCount,
curCount,
drop*100), "red")
@ -65,6 +69,26 @@ func monitorServerInfo() {
if v.Status == 2 || v.Status == 3 { // 维护中或停用跳过
continue
}
if v.AppId == 1 {
go func(v *Type.ServerInfo) {
resp, err := client.GetServerInfo(v.AppId, v.ServerId, &msg.ReqServerInfo{})
if err != nil {
return
}
tmpDb := util.MPool.GetGameDB()
defer tmpDb.Close()
latency, err := util.GetAddressLatency(v.Host, v.Port)
if err != nil {
// Connection failed, mark server as offline
tmpDb.Exec("update server set Status=0 where AppId=? and ServerId=?", v.AppId, v.ServerId)
return
}
weight := util.GetServerWeight(resp)
extra, _ := protojson.Marshal(resp)
tmpDb.Exec("update server set Status=1, Online=?,free_mem=?,cpu=?,weight=?,latency=?,extra=? where AppId=? and ServerId=?", resp.PlayerNum, resp.Sys, resp.CPU, weight, latency, string(extra), v.AppId, v.ServerId)
}(v)
continue
}
go func(v *Type.ServerInfo) {
tmpDb := util.MPool.GetGameDB()
defer tmpDb.Close()

View File

@ -30524,6 +30524,130 @@ func (x *ResUserDetailInfo) GetOrder() []*UserDetailOrderInfo {
return nil
}
type ResServerInfo struct {
state protoimpl.MessageState `protogen:"open.v1"`
PlayerNum int32 `protobuf:"varint,1,opt,name=PlayerNum,proto3" json:"PlayerNum,omitempty"` // 玩家数量
FreeMem uint64 `protobuf:"varint,2,opt,name=FreeMem,proto3" json:"FreeMem,omitempty"` // 空闲内存
CPU float64 `protobuf:"fixed64,3,opt,name=CPU,proto3" json:"CPU,omitempty"` // Cpu使用率
StartTime int32 `protobuf:"varint,4,opt,name=StartTime,proto3" json:"StartTime,omitempty"` // 启动时间
TotalAlloc string `protobuf:"bytes,5,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"` // 总内存分配
Alloc string `protobuf:"bytes,6,opt,name=Alloc,proto3" json:"Alloc,omitempty"` // 当前内存分配
Sys uint64 `protobuf:"varint,7,opt,name=Sys,proto3" json:"Sys,omitempty"` // 系统内存分配
NumGC uint32 `protobuf:"varint,8,opt,name=NumGC,proto3" json:"NumGC,omitempty"` // GC次数
NumGoroutine int32 `protobuf:"varint,9,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"` // 协程数量
UsageMem uint64 `protobuf:"varint,10,opt,name=UsageMem,proto3" json:"UsageMem,omitempty"` // 内存使用率
Version string `protobuf:"bytes,11,opt,name=Version,proto3" json:"Version,omitempty"` // 服务器版本
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResServerInfo) Reset() {
*x = ResServerInfo{}
mi := &file_proto_Gameapi_proto_msgTypes[517]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResServerInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResServerInfo) ProtoMessage() {}
func (x *ResServerInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_Gameapi_proto_msgTypes[517]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResServerInfo.ProtoReflect.Descriptor instead.
func (*ResServerInfo) Descriptor() ([]byte, []int) {
return file_proto_Gameapi_proto_rawDescGZIP(), []int{517}
}
func (x *ResServerInfo) GetPlayerNum() int32 {
if x != nil {
return x.PlayerNum
}
return 0
}
func (x *ResServerInfo) GetFreeMem() uint64 {
if x != nil {
return x.FreeMem
}
return 0
}
func (x *ResServerInfo) GetCPU() float64 {
if x != nil {
return x.CPU
}
return 0
}
func (x *ResServerInfo) GetStartTime() int32 {
if x != nil {
return x.StartTime
}
return 0
}
func (x *ResServerInfo) GetTotalAlloc() string {
if x != nil {
return x.TotalAlloc
}
return ""
}
func (x *ResServerInfo) GetAlloc() string {
if x != nil {
return x.Alloc
}
return ""
}
func (x *ResServerInfo) GetSys() uint64 {
if x != nil {
return x.Sys
}
return 0
}
func (x *ResServerInfo) GetNumGC() uint32 {
if x != nil {
return x.NumGC
}
return 0
}
func (x *ResServerInfo) GetNumGoroutine() int32 {
if x != nil {
return x.NumGoroutine
}
return 0
}
func (x *ResServerInfo) GetUsageMem() uint64 {
if x != nil {
return x.UsageMem
}
return 0
}
func (x *ResServerInfo) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
var File_proto_Gameapi_proto protoreflect.FileDescriptor
const file_proto_Gameapi_proto_rawDesc = "" +
@ -32788,7 +32912,22 @@ const file_proto_Gameapi_proto_rawDesc = "" +
"\x05Order\x18\x17 \x03(\v2\x1d.tutorial.UserDetailOrderInfoR\x05Order\x1a;\n" +
"\rChessMapEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01*\xab\f\n" +
"\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01\"\xaf\x02\n" +
"\rResServerInfo\x12\x1c\n" +
"\tPlayerNum\x18\x01 \x01(\x05R\tPlayerNum\x12\x18\n" +
"\aFreeMem\x18\x02 \x01(\x04R\aFreeMem\x12\x10\n" +
"\x03CPU\x18\x03 \x01(\x01R\x03CPU\x12\x1c\n" +
"\tStartTime\x18\x04 \x01(\x05R\tStartTime\x12\x1e\n" +
"\n" +
"TotalAlloc\x18\x05 \x01(\tR\n" +
"TotalAlloc\x12\x14\n" +
"\x05Alloc\x18\x06 \x01(\tR\x05Alloc\x12\x10\n" +
"\x03Sys\x18\a \x01(\x04R\x03Sys\x12\x14\n" +
"\x05NumGC\x18\b \x01(\rR\x05NumGC\x12\"\n" +
"\fNumGoroutine\x18\t \x01(\x05R\fNumGoroutine\x12\x1a\n" +
"\bUsageMem\x18\n" +
" \x01(\x04R\bUsageMem\x12\x18\n" +
"\aVersion\x18\v \x01(\tR\aVersion*\xab\f\n" +
"\x0eITEM_POP_LABEL\x12\f\n" +
"\bPlayroom\x10\x00\x12\r\n" +
"\tPiggyBank\x10\x01\x12\n" +
@ -33036,12 +33175,14 @@ const file_proto_Gameapi_proto_rawDesc = "" +
"\x1cFRIEND_REPLY_HANDLE_ERR_TYPE\x12\b\n" +
"\x04NONE\x10\x00\x12\n" +
"\n" +
"\x06CATNIP\x10\x012\xe6\x01\n" +
"\x06CATNIP\x10\x012\xa6\x02\n" +
"\aBackend\x12P\n" +
"\x0eReloadActivity\x12\x1e.tutorial.ReqActivityCfgReload\x1a\x1e.tutorial.ResActivityCfgReload\x12G\n" +
"\rOrderShipping\x12\x1a.tutorial.ReqOrderShipping\x1a\x1a.tutorial.ResOrderShipping\x12@\n" +
"\n" +
"UserDetail\x12\x19.tutorial.UserDetailParam\x1a\x17.tutorial.ResUserDetailB\bZ\x06../msgb\x06proto3"
"UserDetail\x12\x19.tutorial.UserDetailParam\x1a\x17.tutorial.ResUserDetail\x12>\n" +
"\n" +
"ServerInfo\x12\x17.tutorial.ReqServerInfo\x1a\x17.tutorial.ResServerInfoB\bZ\x06../msgb\x06proto3"
var (
file_proto_Gameapi_proto_rawDescOnce sync.Once
@ -33056,7 +33197,7 @@ func file_proto_Gameapi_proto_rawDescGZIP() []byte {
}
var file_proto_Gameapi_proto_enumTypes = make([]protoimpl.EnumInfo, 14)
var file_proto_Gameapi_proto_msgTypes = make([]protoimpl.MessageInfo, 593)
var file_proto_Gameapi_proto_msgTypes = make([]protoimpl.MessageInfo, 594)
var file_proto_Gameapi_proto_goTypes = []any{
(ITEM_POP_LABEL)(0), // 0: tutorial.ITEM_POP_LABEL
(HANDLE_TYPE)(0), // 1: tutorial.HANDLE_TYPE
@ -33589,120 +33730,121 @@ var file_proto_Gameapi_proto_goTypes = []any{
(*UserDetailOrderInfo)(nil), // 528: tutorial.UserDetailOrderInfo
(*UserDetailOrderInfoChess)(nil), // 529: tutorial.UserDetailOrderInfoChess
(*ResUserDetailInfo)(nil), // 530: tutorial.ResUserDetailInfo
nil, // 531: tutorial.ResChessColorData.MChessColorDataEntry
nil, // 532: tutorial.UpdateBaseItemInfo.MUpdateItemEntry
nil, // 533: tutorial.ResPlayerChessData.MChessDataEntry
nil, // 534: tutorial.ReqPutPartInBag.MChessDataEntry
nil, // 535: tutorial.UpdatePlayerChessData.MChessDataEntry
nil, // 536: tutorial.ReqSeparateChess.MChessDataEntry
nil, // 537: tutorial.ReqUpgradeChess.MChessDataEntry
nil, // 538: tutorial.ReqGetChessFromBuff.MChessDataEntry
nil, // 539: tutorial.ReqChessEx.MChessDataEntry
nil, // 540: tutorial.ReqSourceChest.MChessDataEntry
nil, // 541: tutorial.ReqPlayroomOutline.MChessDataEntry
nil, // 542: tutorial.ReqPutChessInBag.MChessDataEntry
nil, // 543: tutorial.ReqTakeChessOutBag.MChessDataEntry
nil, // 544: tutorial.ResPlayerBriefProfileData.SetEmojiEntry
nil, // 545: tutorial.UserInfo.SetEmojiEntry
nil, // 546: tutorial.ReqRewardOrder.MChessDataEntry
nil, // 547: tutorial.ResCardInfo.AllCardEntry
nil, // 548: tutorial.ResCardInfo.HandbookEntry
nil, // 549: tutorial.ResGuideInfo.RewardEntry
nil, // 550: tutorial.ResGuideTask.TaskEntry
nil, // 551: tutorial.ResDailyTask.WeekRewardEntry
nil, // 552: tutorial.ResDailyTask.DailyTaskEntry
nil, // 553: tutorial.ResLimitEvent.LimitEventListEntry
nil, // 554: tutorial.ResLimitEventProgress.ProgressRewardEntry
nil, // 555: tutorial.LimitEvent.ParamEntry
nil, // 556: tutorial.ReqLimitEventLuckyCat.MChessDataEntry
nil, // 557: tutorial.ResFriendPlayerSimple.EmojiEntry
nil, // 558: tutorial.ResFriendPlayerSimple.PlayroomEntry
nil, // 559: tutorial.ResFriendPlayerSimple.DressSetEntry
nil, // 560: tutorial.ResFriendPlayerSimple.PhysiologyEntry
nil, // 561: tutorial.ResPlayerSimple.EmojiEntry
nil, // 562: tutorial.ResPlayerRank.PlayroomSetEntry
nil, // 563: tutorial.ResPlayerRank.DressSetEntry
nil, // 564: tutorial.ResKv.KvEntry
nil, // 565: tutorial.ResRank.RankListEntry
nil, // 566: tutorial.ResMailList.MailListEntry
nil, // 567: tutorial.ResCharge.SpecialShopEntry
nil, // 568: tutorial.ResCharge.ChessShopEntry
nil, // 569: tutorial.ResCharge.GiftEntry
nil, // 570: tutorial.ResCharge.WeeklyDiscountEntry
nil, // 571: tutorial.ReqBuyChessShop2.MChessDataEntry
nil, // 572: tutorial.ResEndless.EndlessListEntry
nil, // 573: tutorial.ResChampshipRank.RankListEntry
nil, // 574: tutorial.ResChampshipPreRank.RankListEntry
nil, // 575: tutorial.ResNotifyCard.CardEntry
nil, // 576: tutorial.ResNotifyCard.MasterEntry
nil, // 577: tutorial.ResNotifyCard.HandbookEntry
nil, // 578: tutorial.ResMining.MapEntry
nil, // 579: tutorial.ResMining.PassRewardEntry
nil, // 580: tutorial.ReqMiningTake.MapEntry
nil, // 581: tutorial.ResActRed.RedEntry
nil, // 582: tutorial.ResItem.ItemEntry
nil, // 583: tutorial.ItemNotify.ItemEntry
nil, // 584: tutorial.ResGuessColor.OMapEntry
nil, // 585: tutorial.ReqGuessColorTake.OMapEntry
nil, // 586: tutorial.GuessColorInfo.MapEntry
nil, // 587: tutorial.ResPlayroom.PlayroomEntry
nil, // 588: tutorial.ResPlayroom.MoodEntry
nil, // 589: tutorial.ResPlayroom.PhysiologyEntry
nil, // 590: tutorial.ResPlayroom.DressEntry
nil, // 591: tutorial.ResPlayroom.DressSetEntry
nil, // 592: tutorial.ResPlayroom.WeeklyDiscountEntry
nil, // 593: tutorial.ReqPlayroomDressSet.DressSetEntry
nil, // 594: tutorial.NotifyPlayroomMood.MoodEntry
nil, // 595: tutorial.NotifyPlayroomMood.PhysiologyEntry
nil, // 596: tutorial.ResPlayroomInfo.PlayroomEntry
nil, // 597: tutorial.ResPlayroomInfo.ItemsEntry
nil, // 598: tutorial.ResPlayroomInfo.FlipEntry
nil, // 599: tutorial.ResPlayroomInfo.EmojiEntry
nil, // 600: tutorial.ResPlayroomInfo.DressSetEntry
nil, // 601: tutorial.ResPlayroomGame.ItemsEntry
nil, // 602: tutorial.ReqPlayroomSetRoom.PlayroomEntry
nil, // 603: tutorial.MiningCfg.GemEntry
nil, // 604: tutorial.MiningCfg.JackpotEntry
nil, // 605: tutorial.MiningCfg.PassEntry
nil, // 606: tutorial.ResUserDetailInfo.ChessMapEntry
(*ResServerInfo)(nil), // 531: tutorial.ResServerInfo
nil, // 532: tutorial.ResChessColorData.MChessColorDataEntry
nil, // 533: tutorial.UpdateBaseItemInfo.MUpdateItemEntry
nil, // 534: tutorial.ResPlayerChessData.MChessDataEntry
nil, // 535: tutorial.ReqPutPartInBag.MChessDataEntry
nil, // 536: tutorial.UpdatePlayerChessData.MChessDataEntry
nil, // 537: tutorial.ReqSeparateChess.MChessDataEntry
nil, // 538: tutorial.ReqUpgradeChess.MChessDataEntry
nil, // 539: tutorial.ReqGetChessFromBuff.MChessDataEntry
nil, // 540: tutorial.ReqChessEx.MChessDataEntry
nil, // 541: tutorial.ReqSourceChest.MChessDataEntry
nil, // 542: tutorial.ReqPlayroomOutline.MChessDataEntry
nil, // 543: tutorial.ReqPutChessInBag.MChessDataEntry
nil, // 544: tutorial.ReqTakeChessOutBag.MChessDataEntry
nil, // 545: tutorial.ResPlayerBriefProfileData.SetEmojiEntry
nil, // 546: tutorial.UserInfo.SetEmojiEntry
nil, // 547: tutorial.ReqRewardOrder.MChessDataEntry
nil, // 548: tutorial.ResCardInfo.AllCardEntry
nil, // 549: tutorial.ResCardInfo.HandbookEntry
nil, // 550: tutorial.ResGuideInfo.RewardEntry
nil, // 551: tutorial.ResGuideTask.TaskEntry
nil, // 552: tutorial.ResDailyTask.WeekRewardEntry
nil, // 553: tutorial.ResDailyTask.DailyTaskEntry
nil, // 554: tutorial.ResLimitEvent.LimitEventListEntry
nil, // 555: tutorial.ResLimitEventProgress.ProgressRewardEntry
nil, // 556: tutorial.LimitEvent.ParamEntry
nil, // 557: tutorial.ReqLimitEventLuckyCat.MChessDataEntry
nil, // 558: tutorial.ResFriendPlayerSimple.EmojiEntry
nil, // 559: tutorial.ResFriendPlayerSimple.PlayroomEntry
nil, // 560: tutorial.ResFriendPlayerSimple.DressSetEntry
nil, // 561: tutorial.ResFriendPlayerSimple.PhysiologyEntry
nil, // 562: tutorial.ResPlayerSimple.EmojiEntry
nil, // 563: tutorial.ResPlayerRank.PlayroomSetEntry
nil, // 564: tutorial.ResPlayerRank.DressSetEntry
nil, // 565: tutorial.ResKv.KvEntry
nil, // 566: tutorial.ResRank.RankListEntry
nil, // 567: tutorial.ResMailList.MailListEntry
nil, // 568: tutorial.ResCharge.SpecialShopEntry
nil, // 569: tutorial.ResCharge.ChessShopEntry
nil, // 570: tutorial.ResCharge.GiftEntry
nil, // 571: tutorial.ResCharge.WeeklyDiscountEntry
nil, // 572: tutorial.ReqBuyChessShop2.MChessDataEntry
nil, // 573: tutorial.ResEndless.EndlessListEntry
nil, // 574: tutorial.ResChampshipRank.RankListEntry
nil, // 575: tutorial.ResChampshipPreRank.RankListEntry
nil, // 576: tutorial.ResNotifyCard.CardEntry
nil, // 577: tutorial.ResNotifyCard.MasterEntry
nil, // 578: tutorial.ResNotifyCard.HandbookEntry
nil, // 579: tutorial.ResMining.MapEntry
nil, // 580: tutorial.ResMining.PassRewardEntry
nil, // 581: tutorial.ReqMiningTake.MapEntry
nil, // 582: tutorial.ResActRed.RedEntry
nil, // 583: tutorial.ResItem.ItemEntry
nil, // 584: tutorial.ItemNotify.ItemEntry
nil, // 585: tutorial.ResGuessColor.OMapEntry
nil, // 586: tutorial.ReqGuessColorTake.OMapEntry
nil, // 587: tutorial.GuessColorInfo.MapEntry
nil, // 588: tutorial.ResPlayroom.PlayroomEntry
nil, // 589: tutorial.ResPlayroom.MoodEntry
nil, // 590: tutorial.ResPlayroom.PhysiologyEntry
nil, // 591: tutorial.ResPlayroom.DressEntry
nil, // 592: tutorial.ResPlayroom.DressSetEntry
nil, // 593: tutorial.ResPlayroom.WeeklyDiscountEntry
nil, // 594: tutorial.ReqPlayroomDressSet.DressSetEntry
nil, // 595: tutorial.NotifyPlayroomMood.MoodEntry
nil, // 596: tutorial.NotifyPlayroomMood.PhysiologyEntry
nil, // 597: tutorial.ResPlayroomInfo.PlayroomEntry
nil, // 598: tutorial.ResPlayroomInfo.ItemsEntry
nil, // 599: tutorial.ResPlayroomInfo.FlipEntry
nil, // 600: tutorial.ResPlayroomInfo.EmojiEntry
nil, // 601: tutorial.ResPlayroomInfo.DressSetEntry
nil, // 602: tutorial.ResPlayroomGame.ItemsEntry
nil, // 603: tutorial.ReqPlayroomSetRoom.PlayroomEntry
nil, // 604: tutorial.MiningCfg.GemEntry
nil, // 605: tutorial.MiningCfg.JackpotEntry
nil, // 606: tutorial.MiningCfg.PassEntry
nil, // 607: tutorial.ResUserDetailInfo.ChessMapEntry
}
var file_proto_Gameapi_proto_depIdxs = []int32{
531, // 0: tutorial.ResChessColorData.mChessColorData:type_name -> tutorial.ResChessColorData.MChessColorDataEntry
532, // 0: tutorial.ResChessColorData.mChessColorData:type_name -> tutorial.ResChessColorData.MChessColorDataEntry
5, // 1: tutorial.ReqLogin.type:type_name -> tutorial.LOGIN_TYPE
2, // 2: tutorial.ResId2Verify.ResultCode:type_name -> tutorial.RES_CODE
532, // 3: tutorial.UpdateBaseItemInfo.mUpdateItem:type_name -> tutorial.UpdateBaseItemInfo.MUpdateItemEntry
533, // 4: tutorial.ResPlayerChessData.mChessData:type_name -> tutorial.ResPlayerChessData.MChessDataEntry
533, // 3: tutorial.UpdateBaseItemInfo.mUpdateItem:type_name -> tutorial.UpdateBaseItemInfo.MUpdateItemEntry
534, // 4: tutorial.ResPlayerChessData.mChessData:type_name -> tutorial.ResPlayerChessData.MChessDataEntry
74, // 5: tutorial.ResPlayerChessInfo.ChessBag:type_name -> tutorial.ChessBag
55, // 6: tutorial.ResPlayerChessInfo.PartBag:type_name -> tutorial.PartBag
2, // 7: tutorial.ResGetChessRetireReward.code:type_name -> tutorial.RES_CODE
56, // 8: tutorial.PartBag.PartBagGrids:type_name -> tutorial.PartBagGrid
534, // 9: tutorial.ReqPutPartInBag.mChessData:type_name -> tutorial.ReqPutPartInBag.MChessDataEntry
535, // 9: tutorial.ReqPutPartInBag.mChessData:type_name -> tutorial.ReqPutPartInBag.MChessDataEntry
2, // 10: tutorial.ResPutPartInBag.code:type_name -> tutorial.RES_CODE
1, // 11: tutorial.ChessHandle.type:type_name -> tutorial.HANDLE_TYPE
535, // 12: tutorial.UpdatePlayerChessData.mChessData:type_name -> tutorial.UpdatePlayerChessData.MChessDataEntry
536, // 12: tutorial.UpdatePlayerChessData.mChessData:type_name -> tutorial.UpdatePlayerChessData.MChessDataEntry
59, // 13: tutorial.UpdatePlayerChessData.mChessHandle:type_name -> tutorial.ChessHandle
2, // 14: tutorial.ResUpdatePlayerChessData.code:type_name -> tutorial.RES_CODE
536, // 15: tutorial.ReqSeparateChess.mChessData:type_name -> tutorial.ReqSeparateChess.MChessDataEntry
537, // 15: tutorial.ReqSeparateChess.mChessData:type_name -> tutorial.ReqSeparateChess.MChessDataEntry
2, // 16: tutorial.ResSeparateChess.code:type_name -> tutorial.RES_CODE
537, // 17: tutorial.ReqUpgradeChess.mChessData:type_name -> tutorial.ReqUpgradeChess.MChessDataEntry
538, // 17: tutorial.ReqUpgradeChess.mChessData:type_name -> tutorial.ReqUpgradeChess.MChessDataEntry
2, // 18: tutorial.ResUpgradeChess.code:type_name -> tutorial.RES_CODE
538, // 19: tutorial.ReqGetChessFromBuff.mChessData:type_name -> tutorial.ReqGetChessFromBuff.MChessDataEntry
539, // 19: tutorial.ReqGetChessFromBuff.mChessData:type_name -> tutorial.ReqGetChessFromBuff.MChessDataEntry
2, // 20: tutorial.ResGetChessFromBuff.code:type_name -> tutorial.RES_CODE
7, // 21: tutorial.ReqChessEx.Type:type_name -> tutorial.CHESS_EX_TYPE
539, // 22: tutorial.ReqChessEx.mChessData:type_name -> tutorial.ReqChessEx.MChessDataEntry
540, // 22: tutorial.ReqChessEx.mChessData:type_name -> tutorial.ReqChessEx.MChessDataEntry
2, // 23: tutorial.ResChessEx.code:type_name -> tutorial.RES_CODE
540, // 24: tutorial.ReqSourceChest.mChessData:type_name -> tutorial.ReqSourceChest.MChessDataEntry
541, // 24: tutorial.ReqSourceChest.mChessData:type_name -> tutorial.ReqSourceChest.MChessDataEntry
2, // 25: tutorial.ResSourceChest.code:type_name -> tutorial.RES_CODE
541, // 26: tutorial.ReqPlayroomOutline.mChessData:type_name -> tutorial.ReqPlayroomOutline.MChessDataEntry
542, // 26: tutorial.ReqPlayroomOutline.mChessData:type_name -> tutorial.ReqPlayroomOutline.MChessDataEntry
2, // 27: tutorial.ResPlayroomOutline.code:type_name -> tutorial.RES_CODE
75, // 28: tutorial.ChessBag.ChessBagGrids:type_name -> tutorial.ChessBagGrid
542, // 29: tutorial.ReqPutChessInBag.mChessData:type_name -> tutorial.ReqPutChessInBag.MChessDataEntry
543, // 29: tutorial.ReqPutChessInBag.mChessData:type_name -> tutorial.ReqPutChessInBag.MChessDataEntry
2, // 30: tutorial.ResPutChessInBag.code:type_name -> tutorial.RES_CODE
543, // 31: tutorial.ReqTakeChessOutBag.mChessData:type_name -> tutorial.ReqTakeChessOutBag.MChessDataEntry
544, // 31: tutorial.ReqTakeChessOutBag.mChessData:type_name -> tutorial.ReqTakeChessOutBag.MChessDataEntry
2, // 32: tutorial.ResTakeChessOutBag.code:type_name -> tutorial.RES_CODE
2, // 33: tutorial.ResTakeChessOutBagToHonor.code:type_name -> tutorial.RES_CODE
2, // 34: tutorial.ResBuyChessBagGrid.code:type_name -> tutorial.RES_CODE
544, // 35: tutorial.ResPlayerBriefProfileData.SetEmoji:type_name -> tutorial.ResPlayerBriefProfileData.SetEmojiEntry
545, // 35: tutorial.ResPlayerBriefProfileData.SetEmoji:type_name -> tutorial.ResPlayerBriefProfileData.SetEmojiEntry
2, // 36: tutorial.ResSetEnergyMul.ResultCode:type_name -> tutorial.RES_CODE
8, // 37: tutorial.ReqLang.Lang:type_name -> tutorial.LANG_TYPE
2, // 38: tutorial.ResLang.ResultCode:type_name -> tutorial.RES_CODE
@ -33710,7 +33852,7 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
195, // 40: tutorial.UserInfo.AvatarList:type_name -> tutorial.AvatarInfo
191, // 41: tutorial.UserInfo.FaceList:type_name -> tutorial.FaceInfo
198, // 42: tutorial.UserInfo.EmojiList:type_name -> tutorial.EmojiInfo
545, // 43: tutorial.UserInfo.SetEmoji:type_name -> tutorial.UserInfo.SetEmojiEntry
546, // 43: tutorial.UserInfo.SetEmoji:type_name -> tutorial.UserInfo.SetEmojiEntry
2, // 44: tutorial.ResSetName.ResultCode:type_name -> tutorial.RES_CODE
2, // 45: tutorial.ResSetPetName.ResultCode:type_name -> tutorial.RES_CODE
2, // 46: tutorial.ResBuyEnergy.Code:type_name -> tutorial.RES_CODE
@ -33718,7 +33860,7 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
2, // 48: tutorial.ResGetHandbookReward.Code:type_name -> tutorial.RES_CODE
105, // 49: tutorial.Handbook.Handbooks:type_name -> tutorial.HandbookInfo
2, // 50: tutorial.ResHandbookAllReward.Code:type_name -> tutorial.RES_CODE
546, // 51: tutorial.ReqRewardOrder.mChessData:type_name -> tutorial.ReqRewardOrder.MChessDataEntry
547, // 51: tutorial.ReqRewardOrder.mChessData:type_name -> tutorial.ReqRewardOrder.MChessDataEntry
2, // 52: tutorial.ResRewardOrder.Code:type_name -> tutorial.RES_CODE
2, // 53: tutorial.ResDelOrder.Code:type_name -> tutorial.RES_CODE
171, // 54: tutorial.Order.Items:type_name -> tutorial.ItemInfo
@ -33729,8 +33871,8 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
2, // 59: tutorial.ResDecorateAll.Code:type_name -> tutorial.RES_CODE
2, // 60: tutorial.ResAreaReward.Code:type_name -> tutorial.RES_CODE
127, // 61: tutorial.ResCardInfo.CardList:type_name -> tutorial.Card
547, // 62: tutorial.ResCardInfo.AllCard:type_name -> tutorial.ResCardInfo.AllCardEntry
548, // 63: tutorial.ResCardInfo.Handbook:type_name -> tutorial.ResCardInfo.HandbookEntry
548, // 62: tutorial.ResCardInfo.AllCard:type_name -> tutorial.ResCardInfo.AllCardEntry
549, // 63: tutorial.ResCardInfo.Handbook:type_name -> tutorial.ResCardInfo.HandbookEntry
2, // 64: tutorial.ResCardSeasonFirstReward.Code:type_name -> tutorial.RES_CODE
2, // 65: tutorial.ResCardHandbookReward.Code:type_name -> tutorial.RES_CODE
2, // 66: tutorial.ResMasterCard.Code:type_name -> tutorial.RES_CODE
@ -33749,16 +33891,16 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
2, // 79: tutorial.ResGetFriendCard.Code:type_name -> tutorial.RES_CODE
2, // 80: tutorial.ResGuideReward.Code:type_name -> tutorial.RES_CODE
2, // 81: tutorial.ResGuidePlayroom.Code:type_name -> tutorial.RES_CODE
549, // 82: tutorial.ResGuideInfo.Reward:type_name -> tutorial.ResGuideInfo.RewardEntry
550, // 82: tutorial.ResGuideInfo.Reward:type_name -> tutorial.ResGuideInfo.RewardEntry
171, // 83: tutorial.ResItemPop.Items:type_name -> tutorial.ItemInfo
173, // 84: tutorial.ResItemPop.CardPacks:type_name -> tutorial.CardPack
171, // 85: tutorial.ItemList.List:type_name -> tutorial.ItemInfo
550, // 86: tutorial.ResGuideTask.Task:type_name -> tutorial.ResGuideTask.TaskEntry
551, // 86: tutorial.ResGuideTask.Task:type_name -> tutorial.ResGuideTask.TaskEntry
183, // 87: tutorial.GuideTask.Progress:type_name -> tutorial.QuestProgress
2, // 88: tutorial.ResGetGuideTaskReward.Code:type_name -> tutorial.RES_CODE
2, // 89: tutorial.ResGetGuideActiveReward.Code:type_name -> tutorial.RES_CODE
551, // 90: tutorial.ResDailyTask.WeekReward:type_name -> tutorial.ResDailyTask.WeekRewardEntry
552, // 91: tutorial.ResDailyTask.DailyTask:type_name -> tutorial.ResDailyTask.DailyTaskEntry
552, // 90: tutorial.ResDailyTask.WeekReward:type_name -> tutorial.ResDailyTask.WeekRewardEntry
553, // 91: tutorial.ResDailyTask.DailyTask:type_name -> tutorial.ResDailyTask.DailyTaskEntry
171, // 92: tutorial.DailyWeek.Items:type_name -> tutorial.ItemInfo
183, // 93: tutorial.DailyTask.Progress:type_name -> tutorial.QuestProgress
171, // 94: tutorial.DailyTask.Items:type_name -> tutorial.ItemInfo
@ -33780,33 +33922,33 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
208, // 110: tutorial.ResActivity.ActiveList:type_name -> tutorial.ActivityInfo
2, // 111: tutorial.ResActivityReward.Code:type_name -> tutorial.RES_CODE
2, // 112: tutorial.ResAddGiftReward.Code:type_name -> tutorial.RES_CODE
553, // 113: tutorial.ResLimitEvent.LimitEventList:type_name -> tutorial.ResLimitEvent.LimitEventListEntry
554, // 114: tutorial.ResLimitEventProgress.ProgressReward:type_name -> tutorial.ResLimitEventProgress.ProgressRewardEntry
554, // 113: tutorial.ResLimitEvent.LimitEventList:type_name -> tutorial.ResLimitEvent.LimitEventListEntry
555, // 114: tutorial.ResLimitEventProgress.ProgressReward:type_name -> tutorial.ResLimitEventProgress.ProgressRewardEntry
2, // 115: tutorial.ResLimitEventReward.Code:type_name -> tutorial.RES_CODE
2, // 116: tutorial.ResSelectLimitEvent.Code:type_name -> tutorial.RES_CODE
555, // 117: tutorial.LimitEvent.Param:type_name -> tutorial.LimitEvent.ParamEntry
556, // 118: tutorial.ReqLimitEventLuckyCat.mChessData:type_name -> tutorial.ReqLimitEventLuckyCat.MChessDataEntry
556, // 117: tutorial.LimitEvent.Param:type_name -> tutorial.LimitEvent.ParamEntry
557, // 118: tutorial.ReqLimitEventLuckyCat.mChessData:type_name -> tutorial.ReqLimitEventLuckyCat.MChessDataEntry
2, // 119: tutorial.ResLimitEventLuckyCat.Code:type_name -> tutorial.RES_CODE
2, // 120: tutorial.ResLimitSenceReward.Code:type_name -> tutorial.RES_CODE
171, // 121: tutorial.ResChessRainReward.Items:type_name -> tutorial.ItemInfo
2, // 122: tutorial.ResFastProduceReward.Code:type_name -> tutorial.RES_CODE
2, // 123: tutorial.ResCatTrickReward.Code:type_name -> tutorial.RES_CODE
237, // 124: tutorial.ResSearchPlayer.List:type_name -> tutorial.ResPlayerSimple
557, // 125: tutorial.ResFriendPlayerSimple.Emoji:type_name -> tutorial.ResFriendPlayerSimple.EmojiEntry
558, // 126: tutorial.ResFriendPlayerSimple.Playroom:type_name -> tutorial.ResFriendPlayerSimple.PlayroomEntry
559, // 127: tutorial.ResFriendPlayerSimple.DressSet:type_name -> tutorial.ResFriendPlayerSimple.DressSetEntry
558, // 125: tutorial.ResFriendPlayerSimple.Emoji:type_name -> tutorial.ResFriendPlayerSimple.EmojiEntry
559, // 126: tutorial.ResFriendPlayerSimple.Playroom:type_name -> tutorial.ResFriendPlayerSimple.PlayroomEntry
560, // 127: tutorial.ResFriendPlayerSimple.DressSet:type_name -> tutorial.ResFriendPlayerSimple.DressSetEntry
238, // 128: tutorial.ResFriendPlayerSimple.Last:type_name -> tutorial.ActLog
560, // 129: tutorial.ResFriendPlayerSimple.Physiology:type_name -> tutorial.ResFriendPlayerSimple.PhysiologyEntry
561, // 130: tutorial.ResPlayerSimple.Emoji:type_name -> tutorial.ResPlayerSimple.EmojiEntry
562, // 131: tutorial.ResPlayerRank.PlayroomSet:type_name -> tutorial.ResPlayerRank.PlayroomSetEntry
563, // 132: tutorial.ResPlayerRank.DressSet:type_name -> tutorial.ResPlayerRank.DressSetEntry
561, // 129: tutorial.ResFriendPlayerSimple.Physiology:type_name -> tutorial.ResFriendPlayerSimple.PhysiologyEntry
562, // 130: tutorial.ResPlayerSimple.Emoji:type_name -> tutorial.ResPlayerSimple.EmojiEntry
563, // 131: tutorial.ResPlayerRank.PlayroomSet:type_name -> tutorial.ResPlayerRank.PlayroomSetEntry
564, // 132: tutorial.ResPlayerRank.DressSet:type_name -> tutorial.ResPlayerRank.DressSetEntry
238, // 133: tutorial.ResPlayerRank.Last:type_name -> tutorial.ActLog
237, // 134: tutorial.ResFriendLog.Player:type_name -> tutorial.ResPlayerSimple
240, // 135: tutorial.NotifyFriendLog.info:type_name -> tutorial.ResFriendLog
242, // 136: tutorial.NotifyFriendLog.Bubble:type_name -> tutorial.FriendBubbleInfo
171, // 137: tutorial.FriendBubbleInfo.Items:type_name -> tutorial.ItemInfo
244, // 138: tutorial.NotifyFriendCard.Info:type_name -> tutorial.ResFriendCard
564, // 139: tutorial.ResKv.kv:type_name -> tutorial.ResKv.KvEntry
565, // 139: tutorial.ResKv.kv:type_name -> tutorial.ResKv.KvEntry
2, // 140: tutorial.ResFriendByCode.Code:type_name -> tutorial.RES_CODE
237, // 141: tutorial.ResFriendByCode.Player:type_name -> tutorial.ResPlayerSimple
237, // 142: tutorial.ResFriendRecommend.List:type_name -> tutorial.ResPlayerSimple
@ -33835,27 +33977,27 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
237, // 165: tutorial.ResAgreeFriend.Player:type_name -> tutorial.ResPlayerSimple
2, // 166: tutorial.ResRefuseFriend.Code:type_name -> tutorial.RES_CODE
2, // 167: tutorial.ResDelFriend.Code:type_name -> tutorial.RES_CODE
565, // 168: tutorial.ResRank.RankList:type_name -> tutorial.ResRank.RankListEntry
566, // 169: tutorial.ResMailList.MailList:type_name -> tutorial.ResMailList.MailListEntry
566, // 168: tutorial.ResRank.RankList:type_name -> tutorial.ResRank.RankListEntry
567, // 169: tutorial.ResMailList.MailList:type_name -> tutorial.ResMailList.MailListEntry
171, // 170: tutorial.MailInfo.Items:type_name -> tutorial.ItemInfo
293, // 171: tutorial.MailNotify.Info:type_name -> tutorial.MailInfo
2, // 172: tutorial.ResReadMail.Code:type_name -> tutorial.RES_CODE
2, // 173: tutorial.ResGetMailReward.Code:type_name -> tutorial.RES_CODE
2, // 174: tutorial.ResDeleteMail.Code:type_name -> tutorial.RES_CODE
567, // 175: tutorial.ResCharge.SpecialShop:type_name -> tutorial.ResCharge.SpecialShopEntry
568, // 176: tutorial.ResCharge.ChessShop:type_name -> tutorial.ResCharge.ChessShopEntry
569, // 177: tutorial.ResCharge.Gift:type_name -> tutorial.ResCharge.GiftEntry
568, // 175: tutorial.ResCharge.SpecialShop:type_name -> tutorial.ResCharge.SpecialShopEntry
569, // 176: tutorial.ResCharge.ChessShop:type_name -> tutorial.ResCharge.ChessShopEntry
570, // 177: tutorial.ResCharge.Gift:type_name -> tutorial.ResCharge.GiftEntry
304, // 178: tutorial.ResCharge.Wish:type_name -> tutorial.WishList
570, // 179: tutorial.ResCharge.WeeklyDiscount:type_name -> tutorial.ResCharge.WeeklyDiscountEntry
571, // 179: tutorial.ResCharge.WeeklyDiscount:type_name -> tutorial.ResCharge.WeeklyDiscountEntry
2, // 180: tutorial.ResAddWish.Code:type_name -> tutorial.RES_CODE
2, // 181: tutorial.ResGetWish.Code:type_name -> tutorial.RES_CODE
2, // 182: tutorial.ResSendWishBeg.Code:type_name -> tutorial.RES_CODE
2, // 183: tutorial.ResFreeShop.Code:type_name -> tutorial.RES_CODE
2, // 184: tutorial.ResBuyChessShop.Code:type_name -> tutorial.RES_CODE
571, // 185: tutorial.ReqBuyChessShop2.mChessData:type_name -> tutorial.ReqBuyChessShop2.MChessDataEntry
572, // 185: tutorial.ReqBuyChessShop2.mChessData:type_name -> tutorial.ReqBuyChessShop2.MChessDataEntry
2, // 186: tutorial.ResBuyChessShop2.Code:type_name -> tutorial.RES_CODE
2, // 187: tutorial.ResRefreshChessShop.Code:type_name -> tutorial.RES_CODE
572, // 188: tutorial.ResEndless.EndlessList:type_name -> tutorial.ResEndless.EndlessListEntry
573, // 188: tutorial.ResEndless.EndlessList:type_name -> tutorial.ResEndless.EndlessListEntry
171, // 189: tutorial.ResEndlessInfo.Items:type_name -> tutorial.ItemInfo
2, // 190: tutorial.ResEndlessReward.Code:type_name -> tutorial.RES_CODE
2, // 191: tutorial.ResPiggyBankReward.Code:type_name -> tutorial.RES_CODE
@ -33868,12 +34010,12 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
171, // 198: tutorial.ChampionshipCfgRank.Items:type_name -> tutorial.ItemInfo
2, // 199: tutorial.ResChampshipReward.Code:type_name -> tutorial.RES_CODE
2, // 200: tutorial.ResChampshipRankReward.Code:type_name -> tutorial.RES_CODE
573, // 201: tutorial.ResChampshipRank.RankList:type_name -> tutorial.ResChampshipRank.RankListEntry
574, // 202: tutorial.ResChampshipPreRank.RankList:type_name -> tutorial.ResChampshipPreRank.RankListEntry
574, // 201: tutorial.ResChampshipRank.RankList:type_name -> tutorial.ResChampshipRank.RankListEntry
575, // 202: tutorial.ResChampshipPreRank.RankList:type_name -> tutorial.ResChampshipPreRank.RankListEntry
337, // 203: tutorial.ResChampshipPreRank.Cfg:type_name -> tutorial.ChampionshipCfg
575, // 204: tutorial.ResNotifyCard.Card:type_name -> tutorial.ResNotifyCard.CardEntry
576, // 205: tutorial.ResNotifyCard.Master:type_name -> tutorial.ResNotifyCard.MasterEntry
577, // 206: tutorial.ResNotifyCard.Handbook:type_name -> tutorial.ResNotifyCard.HandbookEntry
576, // 204: tutorial.ResNotifyCard.Card:type_name -> tutorial.ResNotifyCard.CardEntry
577, // 205: tutorial.ResNotifyCard.Master:type_name -> tutorial.ResNotifyCard.MasterEntry
578, // 206: tutorial.ResNotifyCard.Handbook:type_name -> tutorial.ResNotifyCard.HandbookEntry
2, // 207: tutorial.ResSetFacebookUrl.Code:type_name -> tutorial.RES_CODE
364, // 208: tutorial.CatReturnGiftCfg.RewardList:type_name -> tutorial.CatReturnGiftCfgReward
171, // 209: tutorial.CatReturnGiftCfgReward.Reward:type_name -> tutorial.ItemInfo
@ -33881,22 +34023,22 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
2, // 211: tutorial.ResCatReturnGiftScore.Code:type_name -> tutorial.RES_CODE
2, // 212: tutorial.ResCatReturnGiftReward.Code:type_name -> tutorial.RES_CODE
2, // 213: tutorial.ResCatReturnGiftRewardGift.Code:type_name -> tutorial.RES_CODE
578, // 214: tutorial.ResMining.Map:type_name -> tutorial.ResMining.MapEntry
579, // 215: tutorial.ResMining.PassReward:type_name -> tutorial.ResMining.PassRewardEntry
580, // 216: tutorial.ReqMiningTake.Map:type_name -> tutorial.ReqMiningTake.MapEntry
579, // 214: tutorial.ResMining.Map:type_name -> tutorial.ResMining.MapEntry
580, // 215: tutorial.ResMining.PassReward:type_name -> tutorial.ResMining.PassRewardEntry
581, // 216: tutorial.ReqMiningTake.Map:type_name -> tutorial.ReqMiningTake.MapEntry
2, // 217: tutorial.ResMiningTake.Code:type_name -> tutorial.RES_CODE
2, // 218: tutorial.ResMiningReward.Code:type_name -> tutorial.RES_CODE
2, // 219: tutorial.ResActPassReward.Code:type_name -> tutorial.RES_CODE
581, // 220: tutorial.ResActRed.Red:type_name -> tutorial.ResActRed.RedEntry
582, // 220: tutorial.ResActRed.Red:type_name -> tutorial.ResActRed.RedEntry
208, // 221: tutorial.ActivityNotify.Info:type_name -> tutorial.ActivityInfo
582, // 222: tutorial.ResItem.Item:type_name -> tutorial.ResItem.ItemEntry
583, // 223: tutorial.ItemNotify.Item:type_name -> tutorial.ItemNotify.ItemEntry
583, // 222: tutorial.ResItem.Item:type_name -> tutorial.ResItem.ItemEntry
584, // 223: tutorial.ItemNotify.Item:type_name -> tutorial.ItemNotify.ItemEntry
391, // 224: tutorial.ResGuessColor.MapList:type_name -> tutorial.GuessColorInfo
584, // 225: tutorial.ResGuessColor.OMap:type_name -> tutorial.ResGuessColor.OMapEntry
585, // 225: tutorial.ResGuessColor.OMap:type_name -> tutorial.ResGuessColor.OMapEntry
389, // 226: tutorial.ResGuessColor.Opponent:type_name -> tutorial.opponent
391, // 227: tutorial.ReqGuessColorTake.Map:type_name -> tutorial.GuessColorInfo
585, // 228: tutorial.ReqGuessColorTake.OMap:type_name -> tutorial.ReqGuessColorTake.OMapEntry
586, // 229: tutorial.GuessColorInfo.Map:type_name -> tutorial.GuessColorInfo.MapEntry
586, // 228: tutorial.ReqGuessColorTake.OMap:type_name -> tutorial.ReqGuessColorTake.OMapEntry
587, // 229: tutorial.GuessColorInfo.Map:type_name -> tutorial.GuessColorInfo.MapEntry
2, // 230: tutorial.ResGuessColorTake.Code:type_name -> tutorial.RES_CODE
2, // 231: tutorial.ResGuessColorReward.Code:type_name -> tutorial.RES_CODE
397, // 232: tutorial.ResRace.Opponent:type_name -> tutorial.raceopponent
@ -33907,19 +34049,19 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
171, // 237: tutorial.ResPlayroom.Items:type_name -> tutorial.ItemInfo
438, // 238: tutorial.ResPlayroom.Opponent:type_name -> tutorial.RoomOpponent
437, // 239: tutorial.ResPlayroom.Friend:type_name -> tutorial.FriendRoom
587, // 240: tutorial.ResPlayroom.Playroom:type_name -> tutorial.ResPlayroom.PlayroomEntry
588, // 240: tutorial.ResPlayroom.Playroom:type_name -> tutorial.ResPlayroom.PlayroomEntry
423, // 241: tutorial.ResPlayroom.collect:type_name -> tutorial.PlayroomCollectInfo
588, // 242: tutorial.ResPlayroom.Mood:type_name -> tutorial.ResPlayroom.MoodEntry
589, // 242: tutorial.ResPlayroom.Mood:type_name -> tutorial.ResPlayroom.MoodEntry
171, // 243: tutorial.ResPlayroom.LoseItem:type_name -> tutorial.ItemInfo
433, // 244: tutorial.ResPlayroom.Chip:type_name -> tutorial.ChipInfo
589, // 245: tutorial.ResPlayroom.Physiology:type_name -> tutorial.ResPlayroom.PhysiologyEntry
590, // 246: tutorial.ResPlayroom.Dress:type_name -> tutorial.ResPlayroom.DressEntry
591, // 247: tutorial.ResPlayroom.DressSet:type_name -> tutorial.ResPlayroom.DressSetEntry
590, // 245: tutorial.ResPlayroom.Physiology:type_name -> tutorial.ResPlayroom.PhysiologyEntry
591, // 246: tutorial.ResPlayroom.Dress:type_name -> tutorial.ResPlayroom.DressEntry
592, // 247: tutorial.ResPlayroom.DressSet:type_name -> tutorial.ResPlayroom.DressSetEntry
422, // 248: tutorial.ResPlayroom.PetAir:type_name -> tutorial.PlayroomAirInfo
182, // 249: tutorial.ResPlayroom.DailyTask:type_name -> tutorial.DailyTask
435, // 250: tutorial.ResPlayroom.AdItem:type_name -> tutorial.AdItem
437, // 251: tutorial.ResPlayroom.Target:type_name -> tutorial.FriendRoom
592, // 252: tutorial.ResPlayroom.WeeklyDiscount:type_name -> tutorial.ResPlayroom.WeeklyDiscountEntry
593, // 252: tutorial.ResPlayroom.WeeklyDiscount:type_name -> tutorial.ResPlayroom.WeeklyDiscountEntry
182, // 253: tutorial.NotifyPlayroomTask.DailyTask:type_name -> tutorial.DailyTask
237, // 254: tutorial.NotifyPlayroomBroken.Player:type_name -> tutorial.ResPlayerSimple
2, // 255: tutorial.ResPlayroomTask.Code:type_name -> tutorial.RES_CODE
@ -33927,28 +34069,28 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
2, // 257: tutorial.ResPlayroomUnlock.Code:type_name -> tutorial.RES_CODE
2, // 258: tutorial.ResPlayroomUpvote.Code:type_name -> tutorial.RES_CODE
421, // 259: tutorial.PlayroomDress.List:type_name -> tutorial.PlayroomDressInfo
593, // 260: tutorial.ReqPlayroomDressSet.DressSet:type_name -> tutorial.ReqPlayroomDressSet.DressSetEntry
594, // 260: tutorial.ReqPlayroomDressSet.DressSet:type_name -> tutorial.ReqPlayroomDressSet.DressSetEntry
2, // 261: tutorial.ResPlayroomDressSet.Code:type_name -> tutorial.RES_CODE
2, // 262: tutorial.ResPlayroomPetAirSet.Code:type_name -> tutorial.RES_CODE
2, // 263: tutorial.ResPlayroomWorkOutline.Code:type_name -> tutorial.RES_CODE
171, // 264: tutorial.NotifyPlayroomLose.LoseItem:type_name -> tutorial.ItemInfo
433, // 265: tutorial.NotifyPlayroomLose.Chip:type_name -> tutorial.ChipInfo
594, // 266: tutorial.NotifyPlayroomMood.Mood:type_name -> tutorial.NotifyPlayroomMood.MoodEntry
595, // 267: tutorial.NotifyPlayroomMood.Physiology:type_name -> tutorial.NotifyPlayroomMood.PhysiologyEntry
595, // 266: tutorial.NotifyPlayroomMood.Mood:type_name -> tutorial.NotifyPlayroomMood.MoodEntry
596, // 267: tutorial.NotifyPlayroomMood.Physiology:type_name -> tutorial.NotifyPlayroomMood.PhysiologyEntry
435, // 268: tutorial.NotifyPlayroomMood.AdItem:type_name -> tutorial.AdItem
596, // 269: tutorial.ResPlayroomInfo.Playroom:type_name -> tutorial.ResPlayroomInfo.PlayroomEntry
597, // 270: tutorial.ResPlayroomInfo.Items:type_name -> tutorial.ResPlayroomInfo.ItemsEntry
598, // 271: tutorial.ResPlayroomInfo.flip:type_name -> tutorial.ResPlayroomInfo.FlipEntry
599, // 272: tutorial.ResPlayroomInfo.Emoji:type_name -> tutorial.ResPlayroomInfo.EmojiEntry
600, // 273: tutorial.ResPlayroomInfo.DressSet:type_name -> tutorial.ResPlayroomInfo.DressSetEntry
597, // 269: tutorial.ResPlayroomInfo.Playroom:type_name -> tutorial.ResPlayroomInfo.PlayroomEntry
598, // 270: tutorial.ResPlayroomInfo.Items:type_name -> tutorial.ResPlayroomInfo.ItemsEntry
599, // 271: tutorial.ResPlayroomInfo.flip:type_name -> tutorial.ResPlayroomInfo.FlipEntry
600, // 272: tutorial.ResPlayroomInfo.Emoji:type_name -> tutorial.ResPlayroomInfo.EmojiEntry
601, // 273: tutorial.ResPlayroomInfo.DressSet:type_name -> tutorial.ResPlayroomInfo.DressSetEntry
2, // 274: tutorial.ResPlayroomFlip.Code:type_name -> tutorial.RES_CODE
2, // 275: tutorial.ResPlayroomGuide.Code:type_name -> tutorial.RES_CODE
2, // 276: tutorial.ResPlayroomFlipReward.Code:type_name -> tutorial.RES_CODE
2, // 277: tutorial.ResPlayroomGame.Code:type_name -> tutorial.RES_CODE
601, // 278: tutorial.ResPlayroomGame.Items:type_name -> tutorial.ResPlayroomGame.ItemsEntry
602, // 278: tutorial.ResPlayroomGame.Items:type_name -> tutorial.ResPlayroomGame.ItemsEntry
171, // 279: tutorial.ResPlayroomGameShowReward.Items:type_name -> tutorial.ItemInfo
2, // 280: tutorial.ResPlayroomInteract.Code:type_name -> tutorial.RES_CODE
602, // 281: tutorial.ReqPlayroomSetRoom.Playroom:type_name -> tutorial.ReqPlayroomSetRoom.PlayroomEntry
603, // 281: tutorial.ReqPlayroomSetRoom.Playroom:type_name -> tutorial.ReqPlayroomSetRoom.PlayroomEntry
2, // 282: tutorial.ResPlayroomSetRoom.Code:type_name -> tutorial.RES_CODE
2, // 283: tutorial.ResPlayroomSelectReward.Code:type_name -> tutorial.RES_CODE
2, // 284: tutorial.ResPlayroomLose.Code:type_name -> tutorial.RES_CODE
@ -33979,16 +34121,16 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
2, // 309: tutorial.ResCatnipGrandReward.Code:type_name -> tutorial.RES_CODE
2, // 310: tutorial.ResCatnipEmoji.Code:type_name -> tutorial.RES_CODE
171, // 311: tutorial.MiningCfg.itemCost:type_name -> tutorial.ItemInfo
603, // 312: tutorial.MiningCfg.Gem:type_name -> tutorial.MiningCfg.GemEntry
604, // 313: tutorial.MiningCfg.Jackpot:type_name -> tutorial.MiningCfg.JackpotEntry
605, // 314: tutorial.MiningCfg.Pass:type_name -> tutorial.MiningCfg.PassEntry
604, // 312: tutorial.MiningCfg.Gem:type_name -> tutorial.MiningCfg.GemEntry
605, // 313: tutorial.MiningCfg.Jackpot:type_name -> tutorial.MiningCfg.JackpotEntry
606, // 314: tutorial.MiningCfg.Pass:type_name -> tutorial.MiningCfg.PassEntry
171, // 315: tutorial.MiningCfgJackpot.Items:type_name -> tutorial.ItemInfo
171, // 316: tutorial.MiningCfgPass.Items:type_name -> tutorial.ItemInfo
521, // 317: tutorial.ReqActivityCfgReload.List:type_name -> tutorial.ActivityCfg
530, // 318: tutorial.ResUserDetail.info:type_name -> tutorial.ResUserDetailInfo
529, // 319: tutorial.UserDetailOrderInfo.Chess:type_name -> tutorial.UserDetailOrderInfoChess
529, // 320: tutorial.UserDetailOrderInfo.ChessId:type_name -> tutorial.UserDetailOrderInfoChess
606, // 321: tutorial.ResUserDetailInfo.ChessMap:type_name -> tutorial.ResUserDetailInfo.ChessMapEntry
607, // 321: tutorial.ResUserDetailInfo.ChessMap:type_name -> tutorial.ResUserDetailInfo.ChessMapEntry
238, // 322: tutorial.ResUserDetailInfo.ActLog:type_name -> tutorial.ActLog
527, // 323: tutorial.ResUserDetailInfo.FriendList:type_name -> tutorial.UserDetailFriendInfo
528, // 324: tutorial.ResUserDetailInfo.Order:type_name -> tutorial.UserDetailOrderInfo
@ -34015,11 +34157,13 @@ var file_proto_Gameapi_proto_depIdxs = []int32{
520, // 345: tutorial.Backend.ReloadActivity:input_type -> tutorial.ReqActivityCfgReload
523, // 346: tutorial.Backend.OrderShipping:input_type -> tutorial.ReqOrderShipping
525, // 347: tutorial.Backend.UserDetail:input_type -> tutorial.UserDetailParam
522, // 348: tutorial.Backend.ReloadActivity:output_type -> tutorial.ResActivityCfgReload
524, // 349: tutorial.Backend.OrderShipping:output_type -> tutorial.ResOrderShipping
526, // 350: tutorial.Backend.UserDetail:output_type -> tutorial.ResUserDetail
348, // [348:351] is the sub-list for method output_type
345, // [345:348] is the sub-list for method input_type
515, // 348: tutorial.Backend.ServerInfo:input_type -> tutorial.ReqServerInfo
522, // 349: tutorial.Backend.ReloadActivity:output_type -> tutorial.ResActivityCfgReload
524, // 350: tutorial.Backend.OrderShipping:output_type -> tutorial.ResOrderShipping
526, // 351: tutorial.Backend.UserDetail:output_type -> tutorial.ResUserDetail
531, // 352: tutorial.Backend.ServerInfo:output_type -> tutorial.ResServerInfo
349, // [349:353] is the sub-list for method output_type
345, // [345:349] is the sub-list for method input_type
345, // [345:345] is the sub-list for extension type_name
345, // [345:345] is the sub-list for extension extendee
0, // [0:345] is the sub-list for field type_name
@ -34036,7 +34180,7 @@ func file_proto_Gameapi_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_Gameapi_proto_rawDesc), len(file_proto_Gameapi_proto_rawDesc)),
NumEnums: 14,
NumMessages: 593,
NumMessages: 594,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -22,6 +22,7 @@ const (
Backend_ReloadActivity_FullMethodName = "/tutorial.Backend/ReloadActivity"
Backend_OrderShipping_FullMethodName = "/tutorial.Backend/OrderShipping"
Backend_UserDetail_FullMethodName = "/tutorial.Backend/UserDetail"
Backend_ServerInfo_FullMethodName = "/tutorial.Backend/ServerInfo"
)
// BackendClient is the client API for Backend service.
@ -31,6 +32,7 @@ type BackendClient interface {
ReloadActivity(ctx context.Context, in *ReqActivityCfgReload, opts ...grpc.CallOption) (*ResActivityCfgReload, error)
OrderShipping(ctx context.Context, in *ReqOrderShipping, opts ...grpc.CallOption) (*ResOrderShipping, error)
UserDetail(ctx context.Context, in *UserDetailParam, opts ...grpc.CallOption) (*ResUserDetail, error)
ServerInfo(ctx context.Context, in *ReqServerInfo, opts ...grpc.CallOption) (*ResServerInfo, error)
}
type backendClient struct {
@ -71,6 +73,16 @@ func (c *backendClient) UserDetail(ctx context.Context, in *UserDetailParam, opt
return out, nil
}
func (c *backendClient) ServerInfo(ctx context.Context, in *ReqServerInfo, opts ...grpc.CallOption) (*ResServerInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResServerInfo)
err := c.cc.Invoke(ctx, Backend_ServerInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// BackendServer is the server API for Backend service.
// All implementations must embed UnimplementedBackendServer
// for forward compatibility.
@ -78,6 +90,7 @@ type BackendServer interface {
ReloadActivity(context.Context, *ReqActivityCfgReload) (*ResActivityCfgReload, error)
OrderShipping(context.Context, *ReqOrderShipping) (*ResOrderShipping, error)
UserDetail(context.Context, *UserDetailParam) (*ResUserDetail, error)
ServerInfo(context.Context, *ReqServerInfo) (*ResServerInfo, error)
mustEmbedUnimplementedBackendServer()
}
@ -97,6 +110,9 @@ func (UnimplementedBackendServer) OrderShipping(context.Context, *ReqOrderShippi
func (UnimplementedBackendServer) UserDetail(context.Context, *UserDetailParam) (*ResUserDetail, error) {
return nil, status.Error(codes.Unimplemented, "method UserDetail not implemented")
}
func (UnimplementedBackendServer) ServerInfo(context.Context, *ReqServerInfo) (*ResServerInfo, error) {
return nil, status.Error(codes.Unimplemented, "method ServerInfo not implemented")
}
func (UnimplementedBackendServer) mustEmbedUnimplementedBackendServer() {}
func (UnimplementedBackendServer) testEmbeddedByValue() {}
@ -172,6 +188,24 @@ func _Backend_UserDetail_Handler(srv interface{}, ctx context.Context, dec func(
return interceptor(ctx, in, info, handler)
}
func _Backend_ServerInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReqServerInfo)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BackendServer).ServerInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Backend_ServerInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BackendServer).ServerInfo(ctx, req.(*ReqServerInfo))
}
return interceptor(ctx, in, info, handler)
}
// Backend_ServiceDesc is the grpc.ServiceDesc for Backend service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -191,6 +225,10 @@ var Backend_ServiceDesc = grpc.ServiceDesc{
MethodName: "UserDetail",
Handler: _Backend_UserDetail_Handler,
},
{
MethodName: "ServerInfo",
Handler: _Backend_ServerInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/Gameapi.proto",

View File

@ -2,26 +2,59 @@ package main
import (
"backend/Type"
"backend/client"
"backend/common"
global_config "backend/global"
"backend/middleware/alibaba"
"backend/middleware/feishu"
"backend/middleware/nacos"
"backend/model"
"backend/msg"
"backend/sdk/ship/model/tuyou"
"backend/util"
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"gitea.bywaystudios.com/pet_home/nacos"
"sort"
"strings"
"testing"
"time"
"unicode"
"github.com/joho/godotenv"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
func TestMain(m *testing.M) {
loadTestEnv()
os.Exit(m.Run())
}
func loadTestEnv() {
workingDir, err := os.Getwd()
if err != nil {
return
}
for dir := workingDir; ; dir = filepath.Dir(dir) {
envPath := filepath.Join(dir, ".env")
if stat, statErr := os.Stat(envPath); statErr == nil && !stat.IsDir() {
_ = godotenv.Load(envPath)
return
}
parent := filepath.Dir(dir)
if parent == dir {
return
}
}
}
func TestGolbalConfig(t *testing.T) {
global_config.InitConfig()
config := global_config.GetTestChargeUidList()
@ -460,4 +493,46 @@ func TestNacos(t *testing.T) {
}
fmt.Printf("Config data: %+v\n", res)
data := make(map[string]interface{})
err = client.GetYAML("mysql", "server", &data)
if err != nil {
fmt.Println("Error getting YAML config from Nacos:", err)
return
}
fmt.Printf("MySQL Config: %+v\n", data)
}
func TestServerInfo(t *testing.T) {
start := time.Now()
resp, err := client.GetServerInfo(1, 0, &msg.ReqServerInfo{})
if err != nil {
fmt.Printf("获取服务器信息失败: %v\n", err)
return
}
latency := time.Since(start).Milliseconds()
fmt.Printf("服务器响应时间: %d ms\n", latency)
fmt.Printf("服务器信息: %+v\n", resp)
}
func TestLoginCountByMonth(t *testing.T) {
log := model.Log{
AppId: 0,
Uid: 132889,
CurrentPage: 1,
PageSize: 20,
Month: "2026-04",
}
r, e := log.LoginCountByMonth()
if e != nil {
fmt.Print(e)
}
fmt.Print(r)
}
func TestAlibabaOss(t *testing.T) {
err := common.Init()
if err != nil {
fmt.Println("Error initializing common:", err)
}
}

View File

@ -142,16 +142,16 @@ func init() {
var MPool *MysqlPool
func connectToMySQLViaSSH(AppCnf *Type.App, ServerId int) (*sqlx.DB, *ssh.Client, error) {
SshConfig, err := common.GetServerConfig(AppCnf.NodeName)
if err != nil {
return nil, nil, fmt.Errorf("failed to get SSH config: %v", err)
}
MysqlConfig, err := common.GetMysqlConfig(AppCnf.MysqlName)
if err != nil {
return nil, nil, fmt.Errorf("failed to get MySQL config: %v", err)
}
SP, _ := Decrypt(SshConfig.Password)
if common.GetSsh() {
SshConfig, err := common.GetServerConfig(AppCnf.NodeName)
if err != nil {
return nil, nil, fmt.Errorf("failed to get SSH config: %v", err)
}
SP, _ := Decrypt(SshConfig.Password)
// 创建 SSH 客户端配置
sshConfig := &ssh.ClientConfig{
User: SshConfig.Username,

View File

@ -8,6 +8,7 @@ import (
"fmt"
"io"
"log"
"sort"
"time"
"github.com/elastic/go-elasticsearch/v8"
@ -301,6 +302,167 @@ func SearchEventByUid(app, _uid int, from, size int, start, end int64, event_nam
return assets, total, nil
}
func SearchLoginCountByUidMonth(app, _uid int, month string) ([]*Type.LoginDailyCount, int64, error) {
uid := Int(_uid)
if uid <= 0 {
return nil, 0, fmt.Errorf("invalid uid")
}
if month == "" {
return nil, 0, fmt.Errorf("month is required")
}
appConfig, err := GetAppConfig(app)
if err != nil {
return nil, 0, err
}
loc := time.UTC
if appConfig.Tz != "" {
loc, err = time.LoadLocation(appConfig.Tz)
if err != nil {
return nil, 0, fmt.Errorf("invalid app timezone: %w", err)
}
}
monthTime, err := time.ParseInLocation("2006-01", month, loc)
if err != nil {
return nil, 0, fmt.Errorf("invalid month format, expected YYYY-MM: %w", err)
}
monthStart := time.Date(monthTime.Year(), monthTime.Month(), 1, 0, 0, 0, 0, loc)
monthEnd := monthStart.AddDate(0, 1, 0).Add(-time.Second)
region := GetAppRegion(app)
dailyFilters := make(map[string]interface{})
for day := monthStart; !day.After(monthEnd); day = day.AddDate(0, 0, 1) {
dayStart := time.Date(day.Year(), day.Month(), day.Day(), 0, 0, 0, 0, loc)
dayEnd := dayStart.AddDate(0, 0, 1).Add(-time.Second)
dailyFilters[dayStart.Format("2006-01-02")] = map[string]interface{}{
"range": map[string]interface{}{
"game.#timestamp": map[string]interface{}{
"gte": dayStart.Unix(),
"lte": dayEnd.Unix(),
},
},
}
}
query := map[string]interface{}{
"bool": map[string]interface{}{
"must": []map[string]interface{}{
{
"term": map[string]interface{}{
"game.#distinct_id.keyword": uid,
},
},
{
"term": map[string]interface{}{
"fields.environment": region,
},
},
{
"term": map[string]interface{}{
"game.#event_name.keyword": "Login_log",
},
},
{
"range": map[string]interface{}{
"game.#timestamp": map[string]interface{}{
"gte": monthStart.Unix(),
"lte": monthEnd.Unix(),
},
},
},
},
},
}
client, err := GetEsClient()
if err != nil {
return nil, 0, err
}
fullQuery := map[string]interface{}{
"query": query,
"size": 0,
"aggs": map[string]interface{}{
"daily_login": map[string]interface{}{
"filters": map[string]interface{}{
"filters": dailyFilters,
},
},
},
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(fullQuery); err != nil {
return nil, 0, fmt.Errorf("编码查询失败: %w", err)
}
res, err := client.Search(
client.Search.WithContext(context.Background()),
client.Search.WithIndex("game-user-log*"),
client.Search.WithBody(&buf),
client.Search.WithTrackTotalHits(true),
)
if err != nil {
return nil, 0, fmt.Errorf("执行搜索失败: %w", err)
}
defer res.Body.Close()
if res.IsError() {
bodyBytes, _ := io.ReadAll(res.Body)
return nil, 0, fmt.Errorf("ES 搜索错误 [%s]: %s", res.Status(), string(bodyBytes))
}
var result map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
return nil, 0, fmt.Errorf("解析响应失败: %w", err)
}
var total int64
if hits, ok := result["hits"].(map[string]interface{}); ok {
if totalObj, ok := hits["total"].(map[string]interface{}); ok {
if value, ok := totalObj["value"].(float64); ok {
total = int64(value)
}
}
}
counts := make(map[string]int64, len(dailyFilters))
if aggregations, ok := result["aggregations"].(map[string]interface{}); ok {
if dailyLogin, ok := aggregations["daily_login"].(map[string]interface{}); ok {
if buckets, ok := dailyLogin["buckets"].(map[string]interface{}); ok {
for day, bucket := range buckets {
bucketMap, ok := bucket.(map[string]interface{})
if !ok {
continue
}
if docCount, ok := bucketMap["doc_count"].(float64); ok {
counts[day] = int64(docCount)
}
}
}
}
}
dates := make([]string, 0, len(dailyFilters))
for day := range dailyFilters {
dates = append(dates, day)
}
sort.Strings(dates)
data := make([]*Type.LoginDailyCount, 0, len(dates))
for _, day := range dates {
data = append(data, &Type.LoginDailyCount{
Date: day,
Count: counts[day],
})
}
return data, total, nil
}
// CountDistinctUidLastHour 查询一个小时前的 game-user-log 索引中 game.#distinct_id 的去重个数
// 返回值:(当前小时的去重用户数, 一天前同一小时的去重用户数, error)
func CountDistinctUidLastHour() (int64, int64, error) {

View File

@ -248,7 +248,7 @@ func MonitorServerList() {
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")
rows, err := Db.Query("SELECT AppId, ServerId, Status, Host, Port, MaxOnline, Online, version, weight FROM server WHERE Status = 1 ORDER BY ServerId")
if err != nil {
return
}

View File

@ -13,6 +13,7 @@ import (
"io"
"log"
"math/rand"
"net"
"os"
"path/filepath"
"reflect"
@ -837,3 +838,131 @@ func FormatJson(v string) string {
}
return string(jsonBytes)
}
func GetAddressLatency(Host string, Port int) (int64, error) {
address := fmt.Sprintf("%s:%d", Host, Port)
timeout := 3 * time.Second
start := time.Now()
conn, err := net.DialTimeout("tcp", address, timeout)
if err != nil {
return 0, err
}
conn.Close()
latency := time.Since(start).Milliseconds()
return latency, nil
}
func Float(a interface{}) float64 {
if a == nil {
return 0
}
return toFloat64(a)
}
func parseMemoryTextToMB(value string) float64 {
s := strings.TrimSpace(strings.ToUpper(value))
if s == "" {
return 0
}
multiplier := 1.0
switch {
case strings.HasSuffix(s, "TB"):
multiplier = 1024 * 1024
s = strings.TrimSuffix(s, "TB")
case strings.HasSuffix(s, "GB"):
multiplier = 1024
s = strings.TrimSuffix(s, "GB")
case strings.HasSuffix(s, "MB"):
s = strings.TrimSuffix(s, "MB")
case strings.HasSuffix(s, "KB"):
multiplier = 1.0 / 1024
s = strings.TrimSuffix(s, "KB")
case strings.HasSuffix(s, "B"):
multiplier = 1.0 / (1024 * 1024)
s = strings.TrimSuffix(s, "B")
}
numberText := strings.TrimSpace(s)
if numberText == "" {
return 0
}
parsed, err := strconv.ParseFloat(numberText, 64)
if err != nil {
return 0
}
if parsed < 0 {
return 0
}
return parsed * multiplier
}
func clampFloat(value, min, max float64) float64 {
if value < min {
return min
}
if value > max {
return max
}
return value
}
func GetServerWeight(resp *msg.ResServerInfo) int {
const (
memCapMB = 16384.0
playerSoftCap = 1500.0
goroutineSoftCap = 20000.0
gcSoftCap = 2000.0
warmupSeconds = 600.0
)
freeMemMB := float64(resp.FreeMem)
if freeMemMB < 0 {
freeMemMB = 0
}
usagePercent := clampFloat(float64(resp.UsageMem), 0, 100)
if usagePercent == 0 && resp.Sys > 0 {
allocMB := parseMemoryTextToMB(resp.Alloc)
sysMB := float64(resp.Sys) / 1024 / 1024
if sysMB > 0 && allocMB > 0 {
usagePercent = clampFloat(allocMB/sysMB*100, 0, 100)
}
}
memScore := clampFloat(freeMemMB/memCapMB*100, 0, 100)
memPressureScore := 100 - usagePercent
cpuScore := 100 - clampFloat(resp.CPU, 0, 100)
playerScore := 100 - clampFloat(float64(resp.PlayerNum)/playerSoftCap*100, 0, 100)
goroutineScore := 100 - clampFloat(float64(resp.NumGoroutine)/goroutineSoftCap*100, 0, 100)
gcScore := 100 - clampFloat(float64(resp.NumGC)/gcSoftCap*100, 0, 100)
warmupScore := 100.0
if resp.StartTime > 0 {
uptime := float64(Now() - int64(resp.StartTime))
if uptime < 0 {
uptime = 0
}
warmupScore = clampFloat(uptime/warmupSeconds*100, 0, 100)
}
allocRatioScore := 100.0
totalAllocMB := parseMemoryTextToMB(resp.TotalAlloc)
allocMB := parseMemoryTextToMB(resp.Alloc)
if totalAllocMB > 0 && allocMB >= 0 {
allocRatioScore = 100 - clampFloat(allocMB/totalAllocMB*100, 0, 100)
}
weight :=
memScore*0.22 +
memPressureScore*0.23 +
cpuScore*0.23 +
playerScore*0.18 +
goroutineScore*0.05 +
gcScore*0.03 +
warmupScore*0.03 +
allocRatioScore*0.02
return int(clampFloat(weight, 1, 100))
}