From 1d0ce09952a8df244c5ea28559b92a1ee72b22c0 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Fri, 27 Dec 2024 11:00:54 +0800
Subject: [PATCH 01/74] update ci
---
.gitlab-ci.yml | 51 ++++++++++++++++++++------------------------------
1 file changed, 20 insertions(+), 31 deletions(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2fb8ee8..8e2e145 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,15 +1,14 @@
stages:
- lock
- build
- - save
- - sync
- unlock
variables:
- ProjectPath: "mongo.games.com/game" # 项目相对于GOPATH的路径
+ GOPROXY: "https://goproxy.cn,direct"
+ GOPATH: "/home/gopath"
+ GoServerSrcPath: "mongo.games.com/goserver" # 项目相对于GOPATH/src的路径
BetaBinPath: "/home/game/" # beta环境部署路径
DevelopBinPath: "/home/game/" # develop环境部署路径
- GOPROXY: "https://goproxy.cn,direct"
default:
tags:
@@ -26,12 +25,12 @@ lock_job:
stage: lock
script:
- |
- if [ -f /tmp/ci_lock ]; then
- echo "流水线已在运行,等待..."
+ if [ -f /tmp/ci_lock_$CI_COMMIT_REF_NAME ]; then
+ echo "流水线($CI_COMMIT_REF_NAME)已在运行,等待..."
exit 1
else
- touch /tmp/ci_lock
- echo "获得锁定,开始流水线。"
+ touch /tmp/ci_lock_$CI_COMMIT_REF_NAME
+ echo "获得锁定,开始流水线($CI_COMMIT_REF_NAME)。"
fi
build-job:
@@ -40,20 +39,21 @@ build-job:
- develop
- release
script:
+ # 拉取代码
- git checkout $CI_COMMIT_REF_NAME
- git pull origin $CI_COMMIT_REF_NAME
+
+ # 替换 go.mod 中的 ../goserver
+ - sed -i "s|mongo.games.com/goserver => .*|mongo.games.com/goserver => $GOPATH/src/$GoServerSrcPath|" go.mod
+
- if [ ! -z "$(git status --porcelain go.mod go.sum)" ]; then
GOMODTIDY=1;
fi
- # 拷贝到GOPATH
- - echo '拷贝到GOPATH'
- - rsync -rvc --no-perms --delete ./* $GOPATH/src/$ProjectPath
- # 进入项目目录
- - cd $GOPATH/src/$ProjectPath
+
# 编译
- echo '编译'
- if [ "$GOMODTIDY" == 1 ]; then
- go mod tidy;
+ go mod tidy;
fi
- |
while IFS= read -r line || [[ -n $line ]]
@@ -64,19 +64,13 @@ build-job:
cd ..
done < ./programs.txt
-save-job:
- stage: save
- only:
- - develop
- - release
- script:
- - cd $GOPATH/src/$ProjectPath
# 拷贝文件
- echo '拷贝文件'
- rm -rf ./temp
- mkdir ./temp
- mkdir ./temp/data
- cp -rfp ./data/* ./temp/data
+
# 删除自定义配置
- echo '删除自定义配置'
- |
@@ -85,6 +79,7 @@ save-job:
echo "删除 $line 配置"
rm ./temp/data/$line
done < ./exclude.txt
+
# 拷贝可执行程序
- echo '拷贝可执行程序'
- |
@@ -94,13 +89,7 @@ save-job:
mv ./$line/$line ./temp/$line
done < ./programs.txt
-sync_job:
- stage: sync
- only:
- - develop
- - release
- script:
- - cd $GOPATH/src/$ProjectPath
+ # 获取部署环境信息
- if [ "$CI_COMMIT_BRANCH" == "develop" ]; then
SSH_PRIVATE_KEY="$SSH_PRIVATE_KEY_DEVELOP";
REMOTE_HOST="$REMOTE_HOST_DEVELOP";
@@ -129,7 +118,7 @@ sync_job:
- echo "同步到服务器"
- echo "Deploying to remote server using rsync... $BinPath"
- rsync -rvz --delete ./temp/ $REMOTE_USER@$REMOTE_HOST:$BinPath
-
+
# 触发部署
- "curl -X POST --fail -F token=$SERVER_CI_TOKEN -F ref=release -F variables[ServerName]=$ServerName https://git.pogorockgames.com/api/v4/projects/31/trigger/pipeline"
@@ -137,6 +126,6 @@ sync_job:
unlock_job:
stage: unlock
script:
- - rm -f /tmp/ci_lock
- - echo "释放锁定,流水线结束。"
+ - rm -f /tmp/ci_lock_$CI_COMMIT_REF_NAME
+ - echo "释放锁定,流水线结束(/$CI_COMMIT_REF_NAME)。"
when: always
\ No newline at end of file
From d8bee4bd7318ec6279ceb97f0bc46d9d9233f2c7 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Fri, 27 Dec 2024 10:35:07 +0800
Subject: [PATCH 02/74] update excel
---
data/DB_PropExchange.dat | Bin 384 -> 384 bytes
data/DB_Task.dat | Bin 5519 -> 5519 bytes
srvdata/db_game_bankruptcy.go | 77 ----------------------------------
3 files changed, 77 deletions(-)
delete mode 100644 srvdata/db_game_bankruptcy.go
diff --git a/data/DB_PropExchange.dat b/data/DB_PropExchange.dat
index 4cba38b2ee99301479f655deafb1443fb5300d51..109f948e7fca12cd4f604e1b15095711569b7f8e 100644
GIT binary patch
literal 384
zcmd-w<6snElw#w!+{O&1uR!UmQ2H8_z7C?5I5-yevI!h$vE|a^U;?YZ1(m-IrEfs#
zn`r98IUwf4?17mNHWz3=iasD8-CUTv!0u7v1iPbQ?IRYhSPmAjLtqZL19bpQ1I!{^
W7Qh^Y>QQ!%ML>^!Vdi3>Ed&5&Dm*m+
literal 384
zcmd-w<6snElw#w!-o^~3FGJ}oQ2HvAz6PR|I5-yevI!h$vE|a^U;?YZ0hPZArEfv$
z+i2>;IUwf4>_JzL)jpuRVCI9(1M
qhakM7Oq$h78Hkr9g5^3bOY`{9f)or
Tii4EcITisu`h}T`fwm9;Why*1
diff --git a/data/DB_Task.dat b/data/DB_Task.dat
index 1ed281a3fdd7f732002366f1766bd22f975fc9cc..d5ee83faae86d0c2d63ff030e04c3aaf63e20efc 100644
GIT binary patch
delta 256
zcmeCz?$@4BF385Qq?b)V#EXMtA&@>Wi)UkpI3t?qg*N8N8yJnDqH-{`Ew-D#Fh(;?
zZs8G`oXoNdD1EVwS%3*DJz*jLWLu$N8Xz$_jwHjX7gmdM5qaYm@
Date: Tue, 24 Dec 2024 17:12:14 +0800
Subject: [PATCH 03/74] =?UTF-8?q?add=20rpc=E7=9B=91=E6=8E=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
dbproxy/config.json | 3 +
dbproxy/monitormgr.go | 85 +++++++++++++++++++
dbproxy/rpcstate.go | 75 ++++++++++++++++
dbproxy/svc/m_monitor.go | 4 +
gamesrv/base/monitormgr.go | 133 -----------------------------
gob_register.go | 47 +++++++++++
mgrsrv/monitormgr.go | 147 --------------------------------
model/monitor.go | 58 +++++++++++++
robot/base/monitormgr.go | 115 -------------------------
rpc/rpclient.go | 39 ++++++++-
rpc/rpcstate.go | 29 +++++++
worldsrv/main.go | 1 +
worldsrv/monitor.go | 51 +++++++++++
worldsrv/monitormgr.go | 169 -------------------------------------
14 files changed, 389 insertions(+), 567 deletions(-)
create mode 100644 dbproxy/monitormgr.go
create mode 100644 dbproxy/rpcstate.go
delete mode 100644 gamesrv/base/monitormgr.go
create mode 100644 gob_register.go
delete mode 100644 mgrsrv/monitormgr.go
delete mode 100644 robot/base/monitormgr.go
create mode 100644 rpc/rpcstate.go
create mode 100644 worldsrv/monitor.go
delete mode 100644 worldsrv/monitormgr.go
diff --git a/dbproxy/config.json b/dbproxy/config.json
index e97d4bb..f0eccd8 100644
--- a/dbproxy/config.json
+++ b/dbproxy/config.json
@@ -47,6 +47,9 @@
"data":{
"RootPath":"../data"
},
+ "mongox": {
+ "Path": "./etc/mgo.json"
+ },
"etcd": {
"Url": ["127.0.0.1:2379"],
"UserName": "",
diff --git a/dbproxy/monitormgr.go b/dbproxy/monitormgr.go
new file mode 100644
index 0000000..9be8493
--- /dev/null
+++ b/dbproxy/monitormgr.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "go.mongodb.org/mongo-driver/bson"
+ "go.mongodb.org/mongo-driver/mongo"
+ "go.mongodb.org/mongo-driver/mongo/options"
+
+ "mongo.games.com/goserver/core/basic"
+ "mongo.games.com/goserver/core/logger"
+ "mongo.games.com/goserver/core/module"
+ "mongo.games.com/goserver/core/mongox"
+ "mongo.games.com/goserver/core/task"
+
+ "mongo.games.com/game/common"
+ "mongo.games.com/game/model"
+)
+
+func init() {
+ //module.RegisteModule(new(MonitorMgr), time.Second*30, 0)
+}
+
+type MonitorData struct {
+ SrvId int32 //服务器id
+ SrvType int32 //服务器类型
+ Key string //自定义key
+ Time time.Time //时间戳
+ Data interface{} //数据体
+}
+
+type MonitorMgr struct {
+}
+
+func (m *MonitorMgr) ModuleName() string {
+ return "MonitorMgr"
+}
+
+func (m *MonitorMgr) Init() {
+
+}
+
+func (m *MonitorMgr) Update() {
+ task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
+ var updates []mongo.WriteModel
+ for k, v := range GetPPCState() {
+ d := &MonitorData{
+ SrvId: int32(common.GetSelfSrvId()),
+ SrvType: int32(common.GetSelfSrvType()),
+ Key: k,
+ Time: time.Now(),
+ Data: v,
+ }
+ updates = append(updates,
+ mongo.NewUpdateOneModel().
+ SetFilter(bson.M{"srvid": d.SrvId, "srvtype": d.SrvType, "key": d.Key}).
+ SetUpdate(bson.M{"$set": d}).SetUpsert(true))
+ }
+
+ if len(updates) == 0 {
+ return nil
+ }
+
+ c, err := mongox.GetGlobalMonitorCollection(fmt.Sprintf("%s_rpc", model.MonitorPrefixName))
+ if err != nil {
+ logger.Logger.Errorf("MonitorMgr Update get collection failed")
+ return nil
+ }
+
+ opts := options.BulkWrite().SetOrdered(false) // SetOrdered(false) 表示并行执行
+ _, err = c.BulkWrite(context.Background(), updates, opts)
+ if err != nil {
+ logger.Logger.Errorf("MonitorMgr Update bulk write failed: %v", err)
+ return nil
+ }
+
+ return nil
+ }), nil, "MonitorMgr_Update").StartByFixExecutor("MonitorMgr")
+}
+
+func (m *MonitorMgr) Shutdown() {
+ module.UnregisteModule(m)
+}
diff --git a/dbproxy/rpcstate.go b/dbproxy/rpcstate.go
new file mode 100644
index 0000000..4f94637
--- /dev/null
+++ b/dbproxy/rpcstate.go
@@ -0,0 +1,75 @@
+package main
+
+import (
+ "sync"
+ "time"
+)
+
+const Timeout = 30 * time.Second
+
+type RPCState struct {
+ RunTimes int64 //执行次数
+ TotalRuningTime int64 //总执行时间
+ MaxRuningTime int64 //最长执行时间
+ TimeoutTimes int64 //执行超时次数 大于30秒的次数
+ FailTimes int64 //执行失败次数
+ SuccessTimes int64 //执行成功次数
+}
+
+var RPCStateMgr = make(map[string]*RPCState)
+var RPCStateMgrLock = sync.RWMutex{}
+
+func GetPPCState() map[string]*RPCState {
+ ret := make(map[string]*RPCState)
+ RPCStateMgrLock.RLock()
+ defer RPCStateMgrLock.RUnlock()
+ for k, v := range RPCStateMgr {
+ e := *v // 复制一份
+ ret[k] = &e
+ }
+ return ret
+}
+
+//func loggingMiddleware(h http.Handler) http.Handler {
+// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+// RPCStateMgrLock.Lock()
+// state, ok := RPCStateMgr[r.URL.Path]
+// if !ok {
+// state = &RPCState{}
+// RPCStateMgr[r.URL.Path] = state
+// }
+// RPCStateMgrLock.Unlock()
+//
+// // 记录请求的时间戳
+// start := time.Now()
+//
+// var buf bytes.Buffer
+// if r.Body != nil {
+// tee := io.TeeReader(r.Body, &buf)
+// r.Body = io.NopCloser(tee)
+// }
+//
+// logger.Logger.Infof("==>RPC %s %s %s", r.Method, r.URL.Path, buf.String())
+// // 包装响应写入器以捕获响应内容
+// rw := &responseWriter{w, http.StatusOK}
+// h.ServeHTTP(rw, r)
+// // 记录请求完成时间和响应状态码
+// duration := time.Since(start).Milliseconds()
+//
+// RPCStateMgrLock.Lock()
+// state.RunTimes++
+// state.TotalRuningTime += duration
+// if duration > state.MaxRuningTime {
+// state.MaxRuningTime = duration
+// }
+// if duration > Timeout.Milliseconds() {
+// state.TimeoutTimes++
+// }
+// if rw.statusCode != http.StatusOK {
+// state.FailTimes++
+// } else {
+// state.SuccessTimes++
+// }
+// RPCStateMgrLock.Unlock()
+// })
+//}
diff --git a/dbproxy/svc/m_monitor.go b/dbproxy/svc/m_monitor.go
index f3ffef5..1c84975 100644
--- a/dbproxy/svc/m_monitor.go
+++ b/dbproxy/svc/m_monitor.go
@@ -13,6 +13,7 @@ import (
"mongo.games.com/game/dbproxy/mongo"
"mongo.games.com/game/mgrsrv/api"
"mongo.games.com/game/model"
+ rpcx "mongo.games.com/game/rpc"
"mongo.games.com/game/webapi"
"mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/logger"
@@ -136,6 +137,9 @@ func init() {
gob.Register(basic.CmdStats{})
gob.Register(map[string]basic.CmdStats{})
gob.Register(utils.RuntimeStats{})
+ gob.Register(rpcx.State{})
+ gob.Register(map[string]rpcx.State{})
+
//gob registe
rpc.Register(new(MonitorDataSvc))
diff --git a/gamesrv/base/monitormgr.go b/gamesrv/base/monitormgr.go
deleted file mode 100644
index b2e3e39..0000000
--- a/gamesrv/base/monitormgr.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package base
-
-import (
- "encoding/gob"
- "mongo.games.com/game/common"
- "mongo.games.com/game/model"
- "mongo.games.com/goserver/core"
- "mongo.games.com/goserver/core/basic"
- "mongo.games.com/goserver/core/module"
- "mongo.games.com/goserver/core/netlib"
- "mongo.games.com/goserver/core/profile"
- "mongo.games.com/goserver/core/schedule"
- "mongo.games.com/goserver/core/task"
- "mongo.games.com/goserver/core/transact"
- "mongo.games.com/goserver/core/utils"
- "time"
-)
-
-var MonitorMgrSington = &MonitorMgr{}
-
-type MonitorMgr struct {
-}
-
-func (this *MonitorMgr) ModuleName() string {
- return "MonitorMgr"
-}
-
-func (this *MonitorMgr) Init() {
-
-}
-
-func (this *MonitorMgr) Update() {
- //logic stats
- logicStats := profile.GetStats()
- if len(logicStats) > 0 {
- logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats)
- if logLogic != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("logic", logLogic)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //net session stats
- netStats := netlib.Stats()
- if len(netStats) > 0 {
- logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats)
- if logNet != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("net", logNet)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //schedule stats
- jobStats := schedule.Stats()
- if len(jobStats) > 0 {
- logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats)
- if logJob != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("job", logJob)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //trans stats
- transStats := transact.Stats()
- if len(transStats) > 0 {
- logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats)
- if logTrans != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("transact", logTrans)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //panic stats
- panicStats := utils.GetPanicStats()
- if len(panicStats) > 0 {
- for key, stats := range panicStats {
- logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats)
- if logPanic != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("panic", logPanic)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
- }
-
- //object command quene stats
- objStats := core.AppCtx.GetStats()
- if len(objStats) > 0 {
- logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats)
- if logCmd != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("cmdque", logCmd)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //gorouting count, eg. system info
- runtimeStats := utils.StatsRuntime()
- logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats)
- if logRuntime != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.InsertMonitorData("runtime", logRuntime)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
-}
-
-func (this *MonitorMgr) Shutdown() {
- module.UnregisteModule(this)
-}
-
-func init() {
- //gob registe
- gob.Register(profile.TimeElement{})
- gob.Register(map[string]profile.TimeElement{})
- gob.Register(netlib.ServiceStats{})
- gob.Register(map[int]netlib.ServiceStats{})
- gob.Register(schedule.TaskStats{})
- gob.Register(map[string]schedule.TaskStats{})
- gob.Register(transact.TransStats{})
- gob.Register(map[int]transact.TransStats{})
- gob.Register(utils.PanicStackInfo{})
- gob.Register(map[string]utils.PanicStackInfo{})
- gob.Register(basic.CmdStats{})
- gob.Register(map[string]basic.CmdStats{})
- gob.Register(utils.RuntimeStats{})
- //gob registe
-
- module.RegisteModule(MonitorMgrSington, time.Minute*5, 0)
-}
diff --git a/gob_register.go b/gob_register.go
new file mode 100644
index 0000000..04eabc0
--- /dev/null
+++ b/gob_register.go
@@ -0,0 +1,47 @@
+package win88
+
+import (
+ "encoding/gob"
+
+ "mongo.games.com/goserver/core/basic"
+ "mongo.games.com/goserver/core/netlib"
+ "mongo.games.com/goserver/core/profile"
+ "mongo.games.com/goserver/core/schedule"
+ "mongo.games.com/goserver/core/transact"
+ "mongo.games.com/goserver/core/utils"
+
+ "mongo.games.com/game/mgrsrv/api"
+ "mongo.games.com/game/model"
+ "mongo.games.com/game/rpc"
+)
+
+func init() {
+ gob.Register(utils.RuntimeStats{})
+
+ gob.Register(model.PlayerOLStats{})
+ gob.Register(map[string]*model.APITransactStats{})
+
+ gob.Register(rpc.State{})
+ gob.Register(map[string]rpc.State{})
+
+ gob.Register(api.ApiStats{})
+ gob.Register(map[string]api.ApiStats{})
+
+ gob.Register(profile.TimeElement{})
+ gob.Register(map[string]profile.TimeElement{})
+
+ gob.Register(netlib.ServiceStats{})
+ gob.Register(map[int]netlib.ServiceStats{})
+
+ gob.Register(schedule.TaskStats{})
+ gob.Register(map[string]schedule.TaskStats{})
+
+ gob.Register(transact.TransStats{})
+ gob.Register(map[int]transact.TransStats{})
+
+ gob.Register(utils.PanicStackInfo{})
+ gob.Register(map[string]utils.PanicStackInfo{})
+
+ gob.Register(basic.CmdStats{})
+ gob.Register(map[string]basic.CmdStats{})
+}
diff --git a/mgrsrv/monitormgr.go b/mgrsrv/monitormgr.go
deleted file mode 100644
index c24567b..0000000
--- a/mgrsrv/monitormgr.go
+++ /dev/null
@@ -1,147 +0,0 @@
-package main
-
-import (
- "encoding/gob"
- "mongo.games.com/game/common"
- "mongo.games.com/game/mgrsrv/api"
- "mongo.games.com/game/model"
- "mongo.games.com/goserver/core"
- "mongo.games.com/goserver/core/basic"
- "mongo.games.com/goserver/core/module"
- "mongo.games.com/goserver/core/netlib"
- "mongo.games.com/goserver/core/profile"
- "mongo.games.com/goserver/core/schedule"
- "mongo.games.com/goserver/core/task"
- "mongo.games.com/goserver/core/transact"
- "mongo.games.com/goserver/core/utils"
- "time"
-)
-
-var MonitorMgrSington = &MonitorMgr{}
-
-type MonitorMgr struct {
-}
-
-func (this *MonitorMgr) ModuleName() string {
- return "MonitorMgr"
-}
-
-func (this *MonitorMgr) Init() {
-
-}
-
-func (this *MonitorMgr) Update() {
- //webapi stats
- apiStats := api.Stats()
- if len(apiStats) > 0 {
- log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", apiStats)
- if log != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("webapi", log)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //logic stats
- logicStats := profile.GetStats()
- if len(logicStats) > 0 {
- logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats)
- if logLogic != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("logic", logLogic)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //net session stats
- netStats := netlib.Stats()
- if len(netStats) > 0 {
- logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats)
- if logNet != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("net", logNet)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //schedule stats
- jobStats := schedule.Stats()
- if len(jobStats) > 0 {
- logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats)
- if logJob != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("job", logJob)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //trans stats
- transStats := transact.Stats()
- if len(transStats) > 0 {
- logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats)
- if logTrans != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("transact", logTrans)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //panic stats
- panicStats := utils.GetPanicStats()
- if len(panicStats) > 0 {
- for key, stats := range panicStats {
- logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats)
- if logPanic != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("panic", logPanic)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
- }
-
- //object command quene stats
- objStats := core.AppCtx.GetStats()
- if len(objStats) > 0 {
- logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats)
- if logCmd != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("cmdque", logCmd)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //gorouting count, eg. system info
- runtimeStats := utils.StatsRuntime()
- logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats)
- if logRuntime != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.InsertMonitorData("runtime", logRuntime)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
-}
-
-func (this *MonitorMgr) Shutdown() {
- module.UnregisteModule(this)
-}
-
-func init() {
- //gob registe
- gob.Register(api.ApiStats{})
- gob.Register(map[string]api.ApiStats{})
- gob.Register(profile.TimeElement{})
- gob.Register(map[string]profile.TimeElement{})
- gob.Register(netlib.ServiceStats{})
- gob.Register(map[int]netlib.ServiceStats{})
- gob.Register(schedule.TaskStats{})
- gob.Register(map[string]schedule.TaskStats{})
- gob.Register(transact.TransStats{})
- gob.Register(map[int]transact.TransStats{})
- gob.Register(utils.PanicStackInfo{})
- gob.Register(map[string]utils.PanicStackInfo{})
- gob.Register(basic.CmdStats{})
- gob.Register(map[string]basic.CmdStats{})
- gob.Register(utils.RuntimeStats{})
- //gob registe
-
- module.RegisteModule(MonitorMgrSington, time.Minute*5, 0)
-}
diff --git a/model/monitor.go b/model/monitor.go
index 1085f9c..db1215a 100644
--- a/model/monitor.go
+++ b/model/monitor.go
@@ -5,7 +5,11 @@ import (
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
+ "mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/logger"
+ "mongo.games.com/goserver/core/task"
+
+ "mongo.games.com/game/common"
)
var (
@@ -85,6 +89,60 @@ func RemoveMonitorData(t time.Time) (chged []*mgo.ChangeInfo, err error) {
return
}
+type MonitorTool[T any] struct {
+ CollectionName string
+}
+
+func NewMonitorTool[T any](collectionName string) *MonitorTool[T] {
+ return &MonitorTool[T]{
+ CollectionName: collectionName,
+ }
+}
+
+func (m *MonitorTool[T]) Insert(key string, state *T) {
+ d := NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, state)
+ task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
+ return InsertMonitorData(m.CollectionName, d)
+ }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
+
+}
+
+func (m *MonitorTool[T]) InsertKV(kv map[string]*T) {
+ // todo 批量插入
+ for k, v := range kv {
+ m.Insert(k, v)
+ }
+}
+
+func (m *MonitorTool[T]) Upsert(key string, state *T) {
+ d := NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, state)
+ task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
+ return UpsertMonitorData(m.CollectionName, d)
+ }), nil, "UpsertMonitorData").StartByFixExecutor("monitor")
+}
+
+func (m *MonitorTool[T]) UpsertKV(kv map[string]*T) {
+ //todo 批量更新
+ for k, v := range kv {
+ m.Upsert(k, v)
+ }
+}
+
+// Remove 清理监控日志
+func Remove(t time.Time) {
+ if t.IsZero() {
+ return
+ }
+
+ task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
+ _, err := RemoveMonitorData(t)
+ if err != nil {
+ logger.Logger.Error("RemoveMonitorData error:", err)
+ }
+ return nil
+ }), nil, "RemoveMonitorData").StartByFixExecutor("monitor")
+}
+
type PlayerOLStats struct {
PlatformStats map[string]*PlayerStats
RobotStats PlayerStats
diff --git a/robot/base/monitormgr.go b/robot/base/monitormgr.go
deleted file mode 100644
index 6e96738..0000000
--- a/robot/base/monitormgr.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package base
-
-import (
- "mongo.games.com/goserver/core/module"
-)
-
-var MonitorMgrSington = &MonitorMgr{}
-
-type MonitorMgr struct {
-}
-
-func (this *MonitorMgr) ModuleName() string {
- return "MonitorMgr"
-}
-
-func (this *MonitorMgr) Init() {
-
-}
-
-func (this *MonitorMgr) Update() {
- //mongodb stats
- //mgo.SetStats(true)
- //mgoStats := mgo.GetStats()
- //logMgo := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", mgoStats)
- //if logMgo != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.InsertMonitorData("mgo", logMgo)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- //}
- //
- ////logic stats
- //logicStats := profile.GetStats()
- //if len(logicStats) > 0 {
- // logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats)
- // if logLogic != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.UpsertMonitorData("logic", logLogic)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- // }
- //}
- //
- ////net session stats
- //netStats := netlib.Stats()
- //if len(netStats) > 0 {
- // logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats)
- // if logNet != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.UpsertMonitorData("net", logNet)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- // }
- //}
- //
- ////schedule stats
- //jobStats := schedule.Stats()
- //if len(jobStats) > 0 {
- // logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats)
- // if logJob != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.UpsertMonitorData("job", logJob)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- // }
- //}
- //
- ////trans stats
- //transStats := transact.Stats()
- //if len(transStats) > 0 {
- // logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats)
- // if logTrans != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.UpsertMonitorData("transact", logTrans)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- // }
- //}
- //
- ////panic stats
- //panicStats := utils.GetPanicStats()
- //if len(panicStats) > 0 {
- // for key, stats := range panicStats {
- // logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats)
- // if logPanic != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.UpsertMonitorData("panic", logPanic)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- // }
- // }
- //}
- //
- ////object command quene stats
- //objStats := core.AppCtx.GetStats()
- //if len(objStats) > 0 {
- // logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats)
- // if logCmd != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.UpsertMonitorData("cmdque", logCmd)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- // }
- //}
- //
- ////gorouting count, eg. system info
- //runtimeStats := utils.StatsRuntime()
- //logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats)
- //if logRuntime != nil {
- // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- // return model.InsertMonitorData("runtime", logRuntime)
- // }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- //}
-}
-
-func (this *MonitorMgr) Shutdown() {
- module.UnregisteModule(this)
-}
-
-func init() {
- //module.RegisteModule(MonitorMgrSington, time.Minute*5, 0)
-}
diff --git a/rpc/rpclient.go b/rpc/rpclient.go
index 3b810a2..86ece61 100644
--- a/rpc/rpclient.go
+++ b/rpc/rpclient.go
@@ -79,6 +79,41 @@ func (this *RPClient) Call(serviceMethod string, args interface{}, reply interfa
}
func (this *RPClient) CallWithTimeout(serviceMethod string, args interface{}, reply interface{}, d time.Duration) error {
+ var err error
+ start := time.Now()
+
+ defer func() {
+ // 记录请求完成时间和响应状态码
+ duration := time.Since(start).Milliseconds()
+ StateMgrLock.Lock()
+ state, ok := StateMgr[serviceMethod]
+ if !ok {
+ state = new(State)
+ StateMgr[serviceMethod] = state
+ }
+ // 执行次数
+ state.RunTimes++
+ // 执行总时长
+ state.TotalRuningTime += duration
+ // 最长执行时长
+ if duration > state.MaxRuningTime {
+ state.MaxRuningTime = duration
+ }
+ switch {
+ case errors.Is(err, ErrRPClientNoConn):
+ state.NoConnTimes++
+ case errors.Is(err, ErrRPClientCallTimeout):
+ state.TimeoutTimes++
+ default:
+ if err != nil {
+ state.FailTimes++
+ } else {
+ state.SuccessTimes++
+ }
+ }
+ StateMgrLock.Unlock()
+ }()
+
if this.client == nil {
return ErrRPClientNoConn
}
@@ -87,8 +122,6 @@ func (this *RPClient) CallWithTimeout(serviceMethod string, args interface{}, re
d = time.Second
}
- start := time.Now()
- var err error
call := this.client.Go(serviceMethod, args, reply, make(chan *rpc.Call, 1))
select {
case <-time.After(d):
@@ -96,7 +129,7 @@ func (this *RPClient) CallWithTimeout(serviceMethod string, args interface{}, re
case call = <-call.Done:
err = call.Error
}
- if err != nil && (err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF) {
+ if err != nil && (errors.Is(err, rpc.ErrShutdown) || errors.Is(err, io.ErrUnexpectedEOF)) {
var dailed chan struct{}
if atomic.CompareAndSwapInt32(&this.connecting, 0, 1) {
dailed = make(chan struct{})
diff --git a/rpc/rpcstate.go b/rpc/rpcstate.go
new file mode 100644
index 0000000..f63ef5e
--- /dev/null
+++ b/rpc/rpcstate.go
@@ -0,0 +1,29 @@
+package rpc
+
+import (
+ "sync"
+)
+
+type State struct {
+ RunTimes int64 //执行次数
+ TotalRuningTime int64 //总执行时间
+ MaxRuningTime int64 //最长执行时间
+ TimeoutTimes int64 //执行超时次数 大于30秒的次数
+ FailTimes int64 //执行失败次数
+ SuccessTimes int64 //执行成功次数
+ NoConnTimes int64 //无连接次数
+}
+
+var StateMgr = make(map[string]*State)
+var StateMgrLock = sync.RWMutex{}
+
+func GetState() map[string]*State {
+ ret := make(map[string]*State)
+ StateMgrLock.RLock()
+ defer StateMgrLock.RUnlock()
+ for k, v := range StateMgr {
+ e := *v // 复制一份
+ ret[k] = &e
+ }
+ return ret
+}
diff --git a/worldsrv/main.go b/worldsrv/main.go
index 6170ab1..366b8e2 100644
--- a/worldsrv/main.go
+++ b/worldsrv/main.go
@@ -54,5 +54,6 @@ func main() {
schedule.StartTask()
//启动业务模块
waiter := module.Start()
+ StartMonitor()
waiter.Wait("main()")
}
diff --git a/worldsrv/monitor.go b/worldsrv/monitor.go
new file mode 100644
index 0000000..ef25742
--- /dev/null
+++ b/worldsrv/monitor.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "time"
+
+ "mongo.games.com/goserver/core/logger"
+ "mongo.games.com/goserver/core/timer"
+
+ "mongo.games.com/game/model"
+ "mongo.games.com/game/rpc"
+)
+
+var (
+ monitorRpc = model.NewMonitorTool[rpc.State]("rpc")
+)
+
+func StartMonitor() {
+ timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
+ //monitorRpc.InsertKV(rpc.GetState())
+
+ //保存到本地文件
+ go func() {
+ err := saveToFile("rpc.json", rpc.GetState())
+ if err != nil {
+ logger.Logger.Errorf("rpc.json failed to save to file: %v", err)
+ }
+ }()
+
+ return true
+ }), nil, time.Second*10, -1)
+}
+
+// 保存变量到文件(覆盖方式)
+func saveToFile(filename string, data interface{}) error {
+ // 将数据转换为JSON字节
+ content, err := json.MarshalIndent(data, "", " ")
+ if err != nil {
+ return fmt.Errorf("failed to serialize data: %w", err)
+ }
+
+ // 写入文件,覆盖模式
+ err = os.WriteFile(filename, content, 0644)
+ if err != nil {
+ return fmt.Errorf("failed to write to file: %w", err)
+ }
+
+ return nil
+}
diff --git a/worldsrv/monitormgr.go b/worldsrv/monitormgr.go
deleted file mode 100644
index aa5e845..0000000
--- a/worldsrv/monitormgr.go
+++ /dev/null
@@ -1,169 +0,0 @@
-package main
-
-import (
- "encoding/gob"
- "time"
-
- "mongo.games.com/goserver/core"
- "mongo.games.com/goserver/core/basic"
- "mongo.games.com/goserver/core/module"
- "mongo.games.com/goserver/core/netlib"
- "mongo.games.com/goserver/core/profile"
- "mongo.games.com/goserver/core/schedule"
- "mongo.games.com/goserver/core/task"
- "mongo.games.com/goserver/core/transact"
- "mongo.games.com/goserver/core/utils"
-
- "mongo.games.com/game/common"
- "mongo.games.com/game/model"
- "mongo.games.com/game/webapi"
-)
-
-var MonitorMgrSington = &MonitorMgr{}
-
-type MonitorMgr struct {
-}
-
-func (this *MonitorMgr) ModuleName() string {
- return "MonitorMgr"
-}
-
-func (this *MonitorMgr) Init() {
-
-}
-
-func (this *MonitorMgr) Update() {
- //player online stats
- olStats := PlayerMgrSington.StatsOnline()
- log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", olStats)
- if log != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.InsertMonitorData("online", log)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
-
- //api stats
- if len(WebAPIStats) > 0 {
- log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", WebAPIStats)
- if log != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("webapi", log)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
- apiStats := webapi.Stats()
- if len(apiStats) > 0 {
- log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "api", apiStats)
- if log != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("webapi", log)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //logic stats
- logicStats := profile.GetStats()
- if len(logicStats) > 0 {
- logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats)
- if logLogic != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("logic", logLogic)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //net session stats
- netStats := netlib.Stats()
- if len(netStats) > 0 {
- logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats)
- if logNet != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("net", logNet)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //schedule stats
- jobStats := schedule.Stats()
- if len(jobStats) > 0 {
- logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats)
- if logJob != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("job", logJob)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //trans stats
- transStats := transact.Stats()
- if len(transStats) > 0 {
- logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats)
- if logTrans != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("transact", logTrans)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //panic stats
- panicStats := utils.GetPanicStats()
- if len(panicStats) > 0 {
- for key, stats := range panicStats {
- logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats)
- if logPanic != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("panic", logPanic)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
- }
-
- //object command quene stats
- objStats := core.AppCtx.GetStats()
- if len(objStats) > 0 {
- logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats)
- if logCmd != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.UpsertMonitorData("cmdque", logCmd)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
- }
-
- //gorouting count, eg. system info
- runtimeStats := utils.StatsRuntime()
- logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats)
- if logRuntime != nil {
- task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
- return model.InsertMonitorData("runtime", logRuntime)
- }), nil, "InsertMonitorData").StartByFixExecutor("monitor")
- }
-}
-
-func (this *MonitorMgr) Shutdown() {
- module.UnregisteModule(this)
-}
-
-func init() {
-
- //gob registe
- gob.Register(model.PlayerOLStats{})
- gob.Register(map[string]*model.APITransactStats{})
- gob.Register(webapi.ApiStats{})
- gob.Register(map[string]webapi.ApiStats{})
- gob.Register(profile.TimeElement{})
- gob.Register(map[string]profile.TimeElement{})
- gob.Register(netlib.ServiceStats{})
- gob.Register(map[int]netlib.ServiceStats{})
- gob.Register(schedule.TaskStats{})
- gob.Register(map[string]schedule.TaskStats{})
- gob.Register(transact.TransStats{})
- gob.Register(map[int]transact.TransStats{})
- gob.Register(utils.PanicStackInfo{})
- gob.Register(map[string]utils.PanicStackInfo{})
- gob.Register(basic.CmdStats{})
- gob.Register(map[string]basic.CmdStats{})
- gob.Register(utils.RuntimeStats{})
- //gob registe
-
- module.RegisteModule(MonitorMgrSington, time.Minute*5, 0)
-}
From e0629e341927cc045143ccd387a2bafd67cb9bef Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Fri, 27 Dec 2024 18:00:27 +0800
Subject: [PATCH 04/74] no message
---
worldsrv/monitor.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/worldsrv/monitor.go b/worldsrv/monitor.go
index ef25742..00bb027 100644
--- a/worldsrv/monitor.go
+++ b/worldsrv/monitor.go
@@ -30,7 +30,7 @@ func StartMonitor() {
}()
return true
- }), nil, time.Second*10, -1)
+ }), nil, time.Minute*5, -1)
}
// 保存变量到文件(覆盖方式)
From 47be044d4a2b76e1919d578a34385de5f698d63e Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 30 Dec 2024 09:23:25 +0800
Subject: [PATCH 05/74] no message
---
gob_register.go | 47 -----------------------------------------------
1 file changed, 47 deletions(-)
delete mode 100644 gob_register.go
diff --git a/gob_register.go b/gob_register.go
deleted file mode 100644
index 04eabc0..0000000
--- a/gob_register.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package win88
-
-import (
- "encoding/gob"
-
- "mongo.games.com/goserver/core/basic"
- "mongo.games.com/goserver/core/netlib"
- "mongo.games.com/goserver/core/profile"
- "mongo.games.com/goserver/core/schedule"
- "mongo.games.com/goserver/core/transact"
- "mongo.games.com/goserver/core/utils"
-
- "mongo.games.com/game/mgrsrv/api"
- "mongo.games.com/game/model"
- "mongo.games.com/game/rpc"
-)
-
-func init() {
- gob.Register(utils.RuntimeStats{})
-
- gob.Register(model.PlayerOLStats{})
- gob.Register(map[string]*model.APITransactStats{})
-
- gob.Register(rpc.State{})
- gob.Register(map[string]rpc.State{})
-
- gob.Register(api.ApiStats{})
- gob.Register(map[string]api.ApiStats{})
-
- gob.Register(profile.TimeElement{})
- gob.Register(map[string]profile.TimeElement{})
-
- gob.Register(netlib.ServiceStats{})
- gob.Register(map[int]netlib.ServiceStats{})
-
- gob.Register(schedule.TaskStats{})
- gob.Register(map[string]schedule.TaskStats{})
-
- gob.Register(transact.TransStats{})
- gob.Register(map[int]transact.TransStats{})
-
- gob.Register(utils.PanicStackInfo{})
- gob.Register(map[string]utils.PanicStackInfo{})
-
- gob.Register(basic.CmdStats{})
- gob.Register(map[string]basic.CmdStats{})
-}
From b308f93cdf75639c51bf35914499b3b117068d21 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 30 Dec 2024 09:33:27 +0800
Subject: [PATCH 06/74] =?UTF-8?q?modify=20=E4=BF=AE=E6=94=B9=E5=8D=81?=
=?UTF-8?q?=E4=B8=89=E5=BC=A0=E9=80=89=E7=89=8C=E6=97=B6=E9=95=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gamerule/thirteen/constants.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gamerule/thirteen/constants.go b/gamerule/thirteen/constants.go
index aeeded7..c18cf4d 100644
--- a/gamerule/thirteen/constants.go
+++ b/gamerule/thirteen/constants.go
@@ -41,7 +41,7 @@ const (
ThirteenWaterSceneWaitTimeout = time.Second * 2 //等待倒计时
ThirteenWaterStartTimeout = time.Second * 3 //开始倒计时
ThirteenWaterSendCardsTimeout = time.Second * 3 //开始发牌
- ThirteenWaterOptCardTimeout = time.Second * 30 //30选牌换牌
+ ThirteenWaterOptCardTimeout = time.Second * 45 //45选牌换牌
ThirteenWaterShowCardsTimeout = time.Second * 1 //轮流看牌时间
ThirteenWaterHitTimeout = time.Second * 2 //打枪
ThirteenWaterBilledTimeout = time.Second * 5 //结算
From 309fe3b9441de024baa6bc3fa1adef40f82893cb Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 30 Dec 2024 11:04:37 +0800
Subject: [PATCH 07/74] =?UTF-8?q?modify=20=E5=8D=81=E4=B8=89=E5=BC=A0?=
=?UTF-8?q?=E9=80=89=E7=89=8C=E6=97=B6=E9=95=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gamerule/thirteen/constants.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gamerule/thirteen/constants.go b/gamerule/thirteen/constants.go
index c18cf4d..0c234f5 100644
--- a/gamerule/thirteen/constants.go
+++ b/gamerule/thirteen/constants.go
@@ -41,7 +41,7 @@ const (
ThirteenWaterSceneWaitTimeout = time.Second * 2 //等待倒计时
ThirteenWaterStartTimeout = time.Second * 3 //开始倒计时
ThirteenWaterSendCardsTimeout = time.Second * 3 //开始发牌
- ThirteenWaterOptCardTimeout = time.Second * 45 //45选牌换牌
+ ThirteenWaterOptCardTimeout = time.Second * 48 //48选牌换牌
ThirteenWaterShowCardsTimeout = time.Second * 1 //轮流看牌时间
ThirteenWaterHitTimeout = time.Second * 2 //打枪
ThirteenWaterBilledTimeout = time.Second * 5 //结算
From d3d722c8975fcba139e9fe78068c7717a44695b3 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 30 Dec 2024 11:53:38 +0800
Subject: [PATCH 08/74] no message
---
gamerule/thirteen/constants.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gamerule/thirteen/constants.go b/gamerule/thirteen/constants.go
index 0c234f5..be20e9c 100644
--- a/gamerule/thirteen/constants.go
+++ b/gamerule/thirteen/constants.go
@@ -41,7 +41,7 @@ const (
ThirteenWaterSceneWaitTimeout = time.Second * 2 //等待倒计时
ThirteenWaterStartTimeout = time.Second * 3 //开始倒计时
ThirteenWaterSendCardsTimeout = time.Second * 3 //开始发牌
- ThirteenWaterOptCardTimeout = time.Second * 48 //48选牌换牌
+ ThirteenWaterOptCardTimeout = time.Second * 47 //47选牌换牌
ThirteenWaterShowCardsTimeout = time.Second * 1 //轮流看牌时间
ThirteenWaterHitTimeout = time.Second * 2 //打枪
ThirteenWaterBilledTimeout = time.Second * 5 //结算
From 7066d7c441f8ba008523b8f00b604d44b8b63679 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 30 Dec 2024 13:55:32 +0800
Subject: [PATCH 09/74] =?UTF-8?q?fix=20=E6=AF=94=E8=B5=9B=E9=85=8D?=
=?UTF-8?q?=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
worldsrv/tournament.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/worldsrv/tournament.go b/worldsrv/tournament.go
index fc6fc2e..0dd17c9 100644
--- a/worldsrv/tournament.go
+++ b/worldsrv/tournament.go
@@ -1803,6 +1803,9 @@ func (this *Tournament) GetSCTMInfosPack(platform, channelName string) *tourname
}
tMInfo.SignupCostItem = signupCost
}
+ if info.MatchTimeType == 0 {
+ tMInfo.MatchTimeStamp = []int64{}
+ }
pack.TMInfo = append(pack.TMInfo, tMInfo)
}
}
From 44acf8b5e7b93d2ed051e248b0c0b196bb3be244 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 30 Dec 2024 18:17:11 +0800
Subject: [PATCH 10/74] =?UTF-8?q?fix=20=E8=83=8C=E5=8C=85=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E4=B8=A2=E5=A4=B1=E6=81=A2=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
dbproxy/svc/l_itemlog.go | 77 +++++++++++---------
model/baginfo.go | 2 +-
model/itemdatalog.go | 15 ++--
worldsrv/bagmgr.go | 147 ++++++++++++++++++++-------------------
4 files changed, 131 insertions(+), 110 deletions(-)
diff --git a/dbproxy/svc/l_itemlog.go b/dbproxy/svc/l_itemlog.go
index 3606e7e..ed8eebc 100644
--- a/dbproxy/svc/l_itemlog.go
+++ b/dbproxy/svc/l_itemlog.go
@@ -30,6 +30,7 @@ func ItemLogsCollection(plt string) *mongo.Collection {
c_itemlog.EnsureIndex(mgo.Index{Key: []string{"typeid", "roomconfigid"}, Background: true, Sparse: true})
c_itemlog.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true})
c_itemlog.EnsureIndex(mgo.Index{Key: []string{"-ts"}, Background: true, Sparse: true})
+ c_itemlog.EnsureIndex(mgo.Index{Key: []string{"seq"}, Background: true, Sparse: true})
c_itemlog.EnsureIndex(mgo.Index{Key: []string{"gamedif"}, Background: true, Sparse: true})
}
return c_itemlog
@@ -43,6 +44,7 @@ type ItemLogSvc struct {
func (svc *ItemLogSvc) InsertItemLog(log *model.ItemLog, ret *bool) (err error) {
clog := ItemLogsCollection(log.Platform)
if clog == nil {
+ logger.Logger.Errorf("ItemLogSvc.InsertItemLog get collection fail platform:%v", log.Platform)
return
}
err = clog.Insert(log)
@@ -54,10 +56,46 @@ func (svc *ItemLogSvc) InsertItemLog(log *model.ItemLog, ret *bool) (err error)
return
}
+func (svc *ItemLogSvc) Insert(req *model.InsertItemLogReq, res *bool) error {
+ if len(req.Logs) == 0 {
+ return nil
+ }
+
+ clog := ItemLogsCollection(req.Logs[0].Platform)
+ if clog == nil {
+ logger.Logger.Errorf("ItemLogSvc.Insert get collection fail platform:%v", req.Logs[0].Platform)
+ return nil
+ }
+ var docs []interface{}
+ for _, v := range req.Logs {
+ docs = append(docs, v)
+ }
+ if err := clog.Insert(docs...); err != nil {
+ logger.Logger.Errorf("ItemLogSvc.Insert error: %v", err)
+ return err
+ }
+ *res = true
+ return nil
+}
+
+func (svc *ItemLogSvc) UpdateState(req *model.UpdateParam, res *model.UpdateRes) error {
+ c := ItemLogsCollection(req.Platform)
+ if c == nil {
+ logger.Logger.Errorf("ItemLogSvc.UpdateState get collection fail platform:%v", req.Platform)
+ return nil
+ }
+ err := c.UpdateId(req.LogId, bson.M{"$set": bson.M{"status": req.State}})
+ if err != nil {
+ logger.Logger.Errorf("ItemLogSvc.UpdateState error: %v", err)
+ }
+ return err
+}
+
// GetItemCount 获取v卡兑换消耗数量
func GetItemCount(platform string, snid, id int32, tp int) (count int64) {
c := ItemLogsCollection(platform)
if c == nil {
+ logger.Logger.Errorf("ItemLogSvc.GetItemCount get collection fail platform:%v", platform)
return 0
}
var err error
@@ -73,7 +111,7 @@ func GetItemCount(platform string, snid, id int32, tp int) (count int64) {
{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": "$count"}}},
}).AllowDiskUse().One(tc)
if err != nil && !errors.Is(err, mgo.ErrNotFound) {
- logger.Logger.Warn("GetItemCount swapN error:", err)
+ logger.Logger.Error("GetItemCount swapN error:", err)
return 0
}
swapN = tc.Count
@@ -85,7 +123,7 @@ func GetItemCount(platform string, snid, id int32, tp int) (count int64) {
{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": "$count"}}},
}).AllowDiskUse().One(tc)
if err != nil && !errors.Is(err, mgo.ErrNotFound) {
- logger.Logger.Warn("GetItemCount costN error:", err)
+ logger.Logger.Error("GetItemCount costN error:", err)
return 0
}
costN = tc.Count
@@ -107,15 +145,6 @@ func (svc *ItemLogSvc) GetItemCount(req *model.ItemCountParam, count *int64) err
return nil
}
-func (svc *ItemLogSvc) UpdateState(req *model.UpdateParam, res *model.UpdateRes) error {
- c := ItemLogsCollection(req.Platform)
- if c == nil {
- return nil
- }
- err := c.UpdateId(req.LogId, bson.M{"$set": bson.M{"status": req.State}})
- return err
-}
-
func (svc *ItemLogSvc) GetClawdollItemLog(args *model.ClawdollItemLogReq, ret *model.GetClawdollItemLogRet) (err error) {
limitDataNum := 200
@@ -229,38 +258,18 @@ func (svc *ItemLogSvc) GetClawdollSuccessItemLog(args *model.ClawdollSuccessItem
func (svc *ItemLogSvc) GetItemLog(req *model.GetItemLogParam, res *model.GetItemLogRes) error {
c := ItemLogsCollection(req.Plt)
if c == nil {
+ logger.Logger.Errorf("ItemLogSvc.GetItemLog get collection fail platform:%v", req.Plt)
return nil
}
- err := c.Find(bson.M{"snid": req.SnId, "ts": bson.M{"$gt": req.Ts}}).All(&res.Logs)
+ err := c.Find(bson.M{"snid": req.SnId, "ts": bson.M{"$gt": req.Ts}}).Sort("ts", "seq").All(&res.Logs)
if err != nil && !errors.Is(err, mgo.ErrNotFound) {
+ logger.Logger.Errorf("ItemLogSvc.GetItemLog error: %v", err)
return err
}
return nil
}
-func (svc *ItemLogSvc) Insert(req *model.InsertItemLogReq, res *bool) error {
- if len(req.Logs) == 0 {
- return nil
- }
-
- clog := ItemLogsCollection(req.Logs[0].Platform)
- if clog == nil {
- logger.Logger.Errorf("ItemLogSvc.Insert collection not found Platform:%v", req.Logs[0].Platform)
- return nil
- }
- var docs []interface{}
- for _, v := range req.Logs {
- docs = append(docs, v)
- }
- if err := clog.Insert(docs...); err != nil {
- logger.Logger.Warn("ItemLogSvc.Insert error:", err)
- return err
- }
- *res = true
- return nil
-}
-
func init() {
rpc.Register(new(ItemLogSvc))
}
diff --git a/model/baginfo.go b/model/baginfo.go
index a44cf26..5df4ec5 100644
--- a/model/baginfo.go
+++ b/model/baginfo.go
@@ -55,7 +55,7 @@ func UpBagItem(args *BagInfo) error {
ret := false
err := rpcCli.CallWithTimeout("BagSvc.UpgradeBag", args, &ret, time.Second*30)
if err != nil {
- return fmt.Errorf("UpgradeBag err:%v SnId:%v BagId:%v", err, args.SnId, args.BagId)
+ return fmt.Errorf("UpgradeBag err:%v SnId:%v BagId:%v Ts:%v", err, args.SnId, args.BagId, args.Ts)
}
return nil
diff --git a/model/itemdatalog.go b/model/itemdatalog.go
index 16415d4..eca24ca 100644
--- a/model/itemdatalog.go
+++ b/model/itemdatalog.go
@@ -2,16 +2,19 @@ package model
import (
"errors"
- "github.com/globalsign/mgo/bson"
- "mongo.games.com/game/protocol/server"
- "mongo.games.com/goserver/core/logger"
+ "sync/atomic"
"time"
+
+ "github.com/globalsign/mgo/bson"
+ "mongo.games.com/goserver/core/logger"
+
+ "mongo.games.com/game/protocol/server"
)
var (
ItemLogDBName = "log"
ItemLogCollName = "log_itemlog"
- ClawDollItemIds = []int32{40003, 40004, 80001, 80002}
+ ItemSeq = int64(0)
)
type ItemLog struct {
@@ -24,6 +27,7 @@ type ItemLog struct {
Count int64 //个数
CreateTs int64 //记录时间
Ts int64 // 纳秒时间戳
+ Seq int64 // 序号
Remark string //备注
TypeId int32 // 变化类型
GameId int64 // 游戏id,游戏中获得时有值
@@ -55,7 +59,7 @@ type ItemParam struct {
Cost []*Item // 消耗的道具
LogId string // 撤销的id,兑换失败
RoomConfigId int32 // 房间配置id
- Offline int32 // 离线记录
+ Offline int32 // 离线记录 1是 0否
}
func NewItemLogEx(param ItemParam) *ItemLog {
@@ -69,6 +73,7 @@ func NewItemLogEx(param ItemParam) *ItemLog {
itemLog.Count = param.Count
itemLog.CreateTs = now.Unix()
itemLog.Ts = now.UnixNano()
+ itemLog.Seq = atomic.AddInt64(&ItemSeq, 1)
itemLog.Remark = param.Remark
itemLog.TypeId = param.TypeId
itemLog.GameId = param.GameId
diff --git a/worldsrv/bagmgr.go b/worldsrv/bagmgr.go
index eb2ad7e..d936ec5 100644
--- a/worldsrv/bagmgr.go
+++ b/worldsrv/bagmgr.go
@@ -6,7 +6,6 @@ import (
"time"
"github.com/globalsign/mgo/bson"
- "golang.org/x/exp/maps"
"mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/i18n"
"mongo.games.com/goserver/core/logger"
@@ -172,8 +171,9 @@ type BagInfo struct {
Ts int64 //更新时间戳
// 临时携带参数
- dirty bool `bson:"-"` //是否需要更新数据库
- LogId string `bson:"-"` //最后一次保存的日志id
+ ItemLogsOffline []*model.ItemLog
+ dirty bool `bson:"-"` //是否需要更新数据库
+ LogId string `bson:"-"` //最后一次保存的日志id
}
func NewBagInfo(platform string, snid int32) *BagInfo {
@@ -205,7 +205,9 @@ var BagMgrSingleton = &BagMgr{
//=============================================================================
type LoadData struct {
- BagInfo *model.BagInfo
+ BagInfo *model.BagInfo
+ ItemLogsOnline []*model.ItemLog
+ ItemLogsOffline []*model.ItemLog
}
func (this *BagMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay {
@@ -225,12 +227,61 @@ func (this *BagMgr) Load(platform string, snid int32, player any) *internal.Play
bagInfo.Ts = time.Now().UnixNano()
}
+ // 根据时间戳对账
+ itemLogs, err := model.GetItemLog(platform, snid, bagInfo.Ts)
+ if err != nil {
+ logger.Logger.Errorf("Load GetItemLog err: %v", err)
+ return &internal.PlayerLoadReplay{
+ Platform: platform,
+ Snid: snid,
+ Err: err,
+ Data: nil,
+ }
+ }
+
+ // 恢复道具
+ endTs := time.Now().UnixNano()
+ var itemLogsOnline, itemLogsOffline []*model.ItemLog
+ for _, v := range itemLogs {
+ if v == nil {
+ continue
+ }
+ if v.Ts >= bagInfo.Ts && v.Ts < endTs {
+ num := v.Count
+ if v.LogType == 1 {
+ num = -num
+ }
+ if v.Ts > bagInfo.Ts {
+ bagInfo.Ts = v.Ts
+ }
+ if v.Offline == 0 {
+ // 在线数据恢复
+ logger.Logger.Tracef("道具恢复 SnId:%v Item:%+v", snid, *v)
+ if _, ok := bagInfo.BagItem[v.ItemId]; !ok {
+ bagInfo.BagItem[v.ItemId] = &model.Item{
+ ItemId: v.ItemId,
+ ItemNum: 0,
+ ObtainTime: v.CreateTs,
+ }
+ }
+ bagInfo.BagItem[v.ItemId].ItemNum += num
+ itemLogsOnline = append(itemLogsOnline, v)
+ } else {
+ // 离线时的变更
+ logger.Logger.Tracef("处理离线道具变化 SnId:%v Item:%v", snid, *v)
+ itemLogsOffline = append(itemLogsOffline, v)
+ }
+ }
+ }
+
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: err,
Data: &LoadData{
- BagInfo: bagInfo,
+ BagInfo: bagInfo,
+ ItemLogsOnline: itemLogsOnline,
+ ItemLogsOffline: itemLogsOffline,
},
}
}
@@ -262,43 +313,25 @@ func (this *BagMgr) Callback(player any, ret *internal.PlayerLoadReplay) {
logger.Logger.Error("InitBagInfo err: item is nil. ItemId:", bi.ItemId)
}
}
+
+ newBagInfo.ItemLogsOffline = data.ItemLogsOffline
this.PlayerBag[ret.Snid] = newBagInfo
}
type LoadAfterData struct {
- GameID []int32
- ItemLogs []*model.ItemLog
- StarTs, EndTs int64
+ GameID []int32
}
func (this *BagMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay {
- var err error
// 查询最近游戏
gameID := model.GetRecentGame(platform, snid)
- // 道具变更记录
- endTs := time.Now().UnixNano()
- var itemLogs []*model.ItemLog
- itemLogs, err = model.GetItemLog(platform, snid, this.PlayerBag[snid].Ts)
- if err != nil {
- logger.Logger.Errorf("LoadAfter GetItemLog err: %v", err)
- return &internal.PlayerLoadReplay{
- Platform: platform,
- Snid: snid,
- Err: err,
- Data: nil,
- }
- }
-
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: nil,
Data: &LoadAfterData{
- GameID: gameID,
- ItemLogs: itemLogs,
- StarTs: this.PlayerBag[snid].Ts,
- EndTs: endTs,
+ GameID: gameID,
},
}
}
@@ -330,50 +363,21 @@ func (this *BagMgr) CallbackAfter(ret *internal.PlayerLoadReplay) {
// 道具变更记录
bagInfo := this.PlayerBag[p.SnId]
if bagInfo != nil {
- changeItems := make(map[int32]struct{})
- for _, v := range param.ItemLogs {
- if v == nil {
- continue
- }
- if v.Ts > param.StarTs && v.Ts <= param.EndTs {
- bagInfo.dirty = true
- num := v.Count
- if v.LogType == 1 {
- num = -num
- }
- if v.Ts > bagInfo.Ts {
- bagInfo.Ts = v.Ts
- }
- if v.Offline == 0 {
- // 在线数据恢复
- logger.Logger.Tracef("道具恢复 SnId:%v Item:%+v", p.SnId, *v)
- if _, ok := bagInfo.BagItem[v.ItemId]; !ok {
- bagInfo.BagItem[v.ItemId] = &Item{
- ItemId: v.ItemId,
- ItemNum: 0,
- ObtainTime: v.CreateTs,
- }
- }
- bagInfo.BagItem[v.ItemId].ItemNum += num
- changeItems[v.ItemId] = struct{}{}
- } else {
- // 离线时的变更
- logger.Logger.Tracef("处理离线道具变化 SnId:%v Item:%v", p.SnId, *v)
- this.OnChangeFuncs(&model.ChangeItemParam{
- SnId: p.SnId,
- ItemId: v.ItemId,
- ItemNum: v.Count,
- GainWay: v.TypeId,
- RoomConfigId: v.RoomConfigId,
- GameId: v.GameId,
- GameFreeId: v.GameFreeId,
- Cost: v.Cost,
- })
- }
- }
+ // 离线时的变更
+ for _, v := range bagInfo.ItemLogsOffline {
+ logger.Logger.Tracef("处理离线道具变化 SnId:%v Item:%v", p.SnId, *v)
+ this.OnChangeFuncs(&model.ChangeItemParam{
+ SnId: p.SnId,
+ ItemId: v.ItemId,
+ ItemNum: v.Count,
+ GainWay: v.TypeId,
+ RoomConfigId: v.RoomConfigId,
+ GameId: v.GameId,
+ GameFreeId: v.GameFreeId,
+ Cost: v.Cost,
+ })
}
-
- this.SyncBagData(p.SnId, maps.Keys(changeItems)...)
+ bagInfo.ItemLogsOffline = nil
}
}
@@ -399,6 +403,9 @@ func (this *BagMgr) Save(platform string, snid int32, isSync, force bool) {
newBagInfo.BagItem[v.ItemId] = &model.Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime}
}
err = model.UpBagItem(newBagInfo)
+ if err != nil {
+ logger.Logger.Errorf("SaveBagData err: %v", err)
+ }
}
cf := func() {
From 3e93085537eeed7976b770b4555bc9c70558c8b1 Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Tue, 31 Dec 2024 09:42:38 +0800
Subject: [PATCH 11/74] no message
---
etcd/keyconf.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/etcd/keyconf.go b/etcd/keyconf.go
index f3dc6a5..4960b7c 100644
--- a/etcd/keyconf.go
+++ b/etcd/keyconf.go
@@ -15,7 +15,7 @@ const (
ETCDKEY_ACT_GIVE_PREFIX = "/game/plt/actgive/"
ETCDKEY_SHOP_EXCHANGE = "/game/exchange_shop"
ETCDKEY_GAME_NOTICE = "/game/common_notice"
- ETCDKEY_SHOP_ITEM = "/game/item_shop"
+ ETCDKEY_SHOP_ITEM = "/game/item_shop/"
ETCDKEY_GAME_MATCH = "/game/game_match"
ETCDKEY_GAME_MATCH_TYPE = "/game/match_type"
ETCDKEY_ACT_TURNPLATE = "/game/act_turnplate"
@@ -34,7 +34,7 @@ const (
ETCDKEY_ACT_Invite = "/game/act_invite" // 邀请活动配置
ETCDKEY_ACT_Permit = "/game/act_permit" // 赛季通行证配置
ETCDKEY_DIAMOND_LOTTERY = "/game/diamond_lottery" // 钻石抽奖配置
- ETCDKEY_Item = "/game/item" // 道具列表
+ ETCDKEY_Item = "/game/item/" // 道具列表
ETCDKEY_SKin = "/game/skin_config" // 皮肤配置
ETCDKEY_RANK_TYPE = "/game/RankType" // 排行榜奖励配置
ETCDKEY_AWARD_CONFIG = "/game/awardlog_config" //获奖记录
From cd1b4978dec2f9a696dc641ff65f56ba277266da Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Fri, 3 Jan 2025 08:43:19 +0800
Subject: [PATCH 12/74] fix go mod
---
go.sum | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/go.sum b/go.sum
index 50b60dc..ff0a805 100644
--- a/go.sum
+++ b/go.sum
@@ -386,8 +386,6 @@ github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk=
-github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY=
-github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 h1:8m6DWBG+dlFNbx5ynvrE7NgI+Y7OlZVMVTpayoW+rCc=
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE=
@@ -441,8 +439,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
-golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -479,8 +475,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -493,8 +487,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -523,8 +516,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
-golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -537,8 +528,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
-golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
From 756f52dd63bf3671f2446a8ec892f70c7d40dd5f Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Fri, 3 Jan 2025 09:31:34 +0800
Subject: [PATCH 13/74] =?UTF-8?q?modify=20=E7=BA=A2=E5=8C=85=E6=B4=BB?=
=?UTF-8?q?=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
worldsrv/etcd.go | 40 ++++++++++++++++++++--------------------
worldsrv/welfmgr.go | 2 +-
2 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/worldsrv/etcd.go b/worldsrv/etcd.go
index a9bcd29..4f7b904 100644
--- a/worldsrv/etcd.go
+++ b/worldsrv/etcd.go
@@ -133,19 +133,19 @@ func init() {
RedList: []*webapi.RedInfo{
{
Num: 1,
- Rate: 50,
+ Rate: 5000,
},
{
Num: 10,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 20,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 30,
- Rate: 10,
+ Rate: 1000,
},
},
},
@@ -162,19 +162,19 @@ func init() {
RedList: []*webapi.RedInfo{
{
Num: 1,
- Rate: 50,
+ Rate: 5000,
},
{
Num: 10,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 20,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 30,
- Rate: 10,
+ Rate: 1000,
},
},
},
@@ -191,19 +191,19 @@ func init() {
RedList: []*webapi.RedInfo{
{
Num: 1,
- Rate: 50,
+ Rate: 5000,
},
{
Num: 10,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 20,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 30,
- Rate: 10,
+ Rate: 1000,
},
},
},
@@ -220,19 +220,19 @@ func init() {
RedList: []*webapi.RedInfo{
{
Num: 1,
- Rate: 50,
+ Rate: 5000,
},
{
Num: 10,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 20,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 30,
- Rate: 10,
+ Rate: 1000,
},
},
},
@@ -249,19 +249,19 @@ func init() {
RedList: []*webapi.RedInfo{
{
Num: 1,
- Rate: 50,
+ Rate: 5000,
},
{
Num: 10,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 20,
- Rate: 10,
+ Rate: 1000,
},
{
Num: 30,
- Rate: 10,
+ Rate: 1000,
},
},
},
diff --git a/worldsrv/welfmgr.go b/worldsrv/welfmgr.go
index 198d2c3..eb48281 100644
--- a/worldsrv/welfmgr.go
+++ b/worldsrv/welfmgr.go
@@ -2384,7 +2384,7 @@ func (this *WelfareMgr) GetRedPacket(p *Player, id int64) *welfare.SCRedPacketDr
f := func() {
// 概率抽奖
rate := 0
- n := rand.Int63n(100)
+ n := rand.Int63n(10000)
for _, v := range cfg.GetRedList() {
rate += int(v.GetRate())
if n < int64(rate) {
From 66e6e20972c48f47b77a074798ce1fa03d70ad5e Mon Sep 17 00:00:00 2001
From: sk <123456@qq.com>
Date: Mon, 6 Jan 2025 16:13:57 +0800
Subject: [PATCH 14/74] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../excel/Base/Slots/FortuneDragon/Bet.xlsx | Bin 13010 -> 13015 bytes
.../excel/Base/Slots/FortuneMouse/Bet.xlsx | Bin 13009 -> 13010 bytes
.../excel/Base/Slots/FortuneOx/Bet.xlsx | Bin 13059 -> 13056 bytes
.../excel/Base/Slots/FortuneRabbit/Bet.xlsx | Bin 13033 -> 13054 bytes
.../excel/Base/Slots/FortuneTiger/Bet.xlsx | Bin 13038 -> 13043 bytes
.../exported/excel2go/base/cash_mania.go | 744 +++++++++---------
.../exported/excel2go/base/fortune_dragon.go | 386 ++++-----
.../exported/excel2go/base/fortune_mouse.go | 284 +++----
.../exported/excel2go/base/fortune_ox.go | 310 ++++----
.../exported/excel2go/base/fortune_rabbit.go | 378 ++++-----
.../exported/excel2go/base/fortune_tiger.go | 296 +++----
.../internal/exported/excel2go/base/matrix.go | 342 ++++----
12 files changed, 1370 insertions(+), 1370 deletions(-)
diff --git a/gamesrv/slotspkg/external/excel/Base/Slots/FortuneDragon/Bet.xlsx b/gamesrv/slotspkg/external/excel/Base/Slots/FortuneDragon/Bet.xlsx
index 229e2df9b4eb574723f8942cd50f7737707e5628..d4bc52e27c6a66c0ba3dc9f0d070e146267f7478 100644
GIT binary patch
delta 5516
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
delta 5572
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
j_d6n0m39D`|pq_A3iV^2vo%X_v!xu-kJ;s
diff --git a/gamesrv/slotspkg/external/excel/Base/Slots/FortuneMouse/Bet.xlsx b/gamesrv/slotspkg/external/excel/Base/Slots/FortuneMouse/Bet.xlsx
index 4502c038c8febb92ca40c96e4cd1ccaa9382e275..da3334f99fdfbf505fac0cc00d3519c0f5c5b477 100644
GIT binary patch
delta 3140
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
delta 5537
zcmZ9QWmFr?wuTcRxI=*A1b26Lx3;(z+G0ft#REZ#I{`{@f)|J2QYcQ*Leb(-ptuz%
zcKN<@?>*;y^JCVUJ+s%EAJ3k7_j~_p_f-uNh;@Fric|pt0G99$ASFy+vxko~Yz6e7
zIOLy+^psFs3?9y*#xx{dQE;CHX9_V*Rz55vmmDJ?o6MJ4(90j1b>HPhy2&ZbnmAP7
zD(L~Dn-GQu@s`7$%}T+ZT39nkZ1?G4{7Y$VSHLOc&fin8GL&PfFcn*fZX1%ptmV
zOEPgpWjVjS**Wgz7|cpLOn8H?0ws&kJ&sw7CEd0eBis*yV0?OvRVJ$~2tXT{2twQ9
ziZ7se@S&k_c!2V;36@k?l-y|zGVUF}$l1
z%Y%?)xucFW6z^=$F69p$SIqI&yqIymt!fgEY^!qf1hMpdHU<$;)g>qq2J0#g?6YnO
zcfijo49_bE(8O!+bIRHT5>442ml
zY@I+oO;j{e004jqh|pP=W9cgQfeWH?!Uj9EGm0!E7UJ)X<)zrfym=j3!3%{(;IzDLY^vu^>a`P$Vlu~F$1>6wh2c`ws`UYB59|=3ljqdeCfI}n^pIt&l&
zf{YNK)3EIPI@ZnX`~I`)#~np3uRSg8WOEf()lPCMmtDFGHsxeH;sw$D>(H^1Rw;oS
zKq4hP1x+4Q)Sj;fhcBQp!-iZI`3O76cVr3MRb!f_!?)=536XMUXxbkH8RNJhDTB$#
z)bl;bLN4XHIC_b>O}C#NqBt#`epP7mp(5rpSaG3|#HE5C&-^kctYnXPWb|#g$~{fWTY)2{f!m9K>SmkoKii33v!6zlL
zD1@>xzvUF!i=vfKhL{jLH_mzsP=m9)5aw(B&+IJ%Gz%X0JniQ7=pF~>xJ=89tkRZJ
zl9NU!TAGYFR4?nW%^XThczU+j#yKQ`63;54cT2Kc<*dVoq}{s@Vsx8G(0+qU95V9s
zSq6WFM#~4Ue_=1mOr3&|
z+qD*MlGeiPgNQdWs4Hn?+waIm*pehUeekSa5TFo{_`Ot2d=Jy1_GJMHtnrpLQ+a4`
z{N8`aOk>VLKkIJFV>$y%Mn)3QJ6~-Q$`lpiSfQO9OOn$zLZDB~8AY79sX`Q^9b;_{
zvHQx|(}Xtsw!@ir>1_Ww;M3f-??OB5Q5fQ{yH@+o{LDh*s`T(_~
zhKih@MfOx}1$Ii2WD6WmENEcOrAhkyHcbLqTjNV7jp*awb=c@!wLfqm-$vaI?V8mY
z4M>QWaPydnZgToDZfN8$x{$B;24^LG`E-F02-1iS(PWxA*leQWOdUVhH*KE@G+Cd*
z78BDC8VgHlTwJ}99KooOR3?3vM|UL)9Xhd5A3Y(u;BmE6NS!tmbXh8Gvi7F!TUxrW
zUAe{h@7m68*DhH?0RU7n;45fPU;;co&TryU&0H7hNe5O~4G8##l+#ojsQQn|^nPV>
z;+y5kv5a)O>^obZG=Vbf7U@A|%{CMQ0dsD_cmp9#U8YT}jwtdb
z!DuhZ4bcddi=3#}nW}8`-xFVy>)DuEGxowiUV*ifUMymT4?Z0waKlhen(&nB75U?o
zoo@Gz$dB%Bdu*k{ITJ%&U~s;3!W(hbBiNz2ljYS&hqy+8woOrtz}P7CA-`K!!O&1t
z(kuS_sb^FQT0eHAz#zXbR!`lhey61D&u?wE_v-WgBG7nh)-4i=zrGo282=ClC+L^|
zL3cDNf{-*jriw@09LO>>mqoVo$yOc>~{k>wduG(u^^Eao=UWTy-
zonkelxZfw#kgNX7C9H?TG#tac9(m{GL~6Kgr*#)gSVkLn(GsLmPLAI((b^##{p|d0
zKh^?r)_2V3*jY>Aa9Y6XbcdA<4XFV5~AgOk1?wH$af$Tp0Mk1u|nm3VS*)-MPa6A@~F
zH#%m%bKn}(*?|m#OO3I(KPG};-;X)G^FJ{al!3v%V$lc_V?9e7RpX&Y`qNI{FGX$Y
z)jXAqd@25&FR)=d)zN`>H^Qf^PFsW?=LG@okWn(M>b
z>B}5tcl6VA1HEf#X%)b{do#yp?4mr|MUuYQU&mrr-lB}2JyRbS7jgzm++7U)hG;Ix
zSRY+@y(%A`={el34EJL)A={v;TS&$VM_=3$wQHc1VuYWuGURCY%nJ1<1=nUV_XjAg
zdA)4n8nioc}>26E;f^PC;+R6tpHx?7B3m
zrz0t$82H5UXm}uw`Y;Yk#1&4ib(CG*!*sB^wu~Q085{9ImsSF&VvS`Zvw>7Nv(pSpY%KZb&S#WypNy@;_dX9
z_}jb|a-pU?%XK*(ziyZMm0Vrt7xEpw!RE-OhhB3wX99bNznA9*$N5DFs~))Q247n?iqrQOg_<7S_IZR=g*ugQ<0af$Kr
z4E?_=rz?e-@&2)L*zx}UJ%s-HJyMMd
zJ$xR5LJSlCsA(8ZQQYNm|EYP{SgNwUZRybCK#?n5q_B8zPg_t58|6hpoChgQdxoP<
zqA{B{w@aPHm{q~gQzxM!W~yLz$3i07>uT$osj@+b{!=Gqr+j%a?kK?BL6QA}uJXL@uMA#@61*{ZiTGw+{(klM}7L{Hb`af#g&R1IN3HVCP>60T$8k>oGm6J5Fu)+
zAQx+x%x1@&_XVa0t}D>jOCtB&7lP!3eLW!nwP?gM^r9<8jBATgYhOXZ>nBH}$6;6z
z8`%MORBJWr%(U^B#|F(5577N2onVdTimSi@6Y{PlK#M&UjCDA<~jQLY1?IgCHl%)4E5hTs7~XZJE*C@l~lz3)Ep%1
zSWqYKa}#LNJ)bNfsOab{(#{#gR9&g@ZU~#+SjUD=^*zOm`^IU02}XCSc5~*G8pJHq
zTSjb{R;Z{cR#&uJASz=%-p(JFiPC#ilASGl@DkEK2riBRvmnXEft;M#5^-MJ<2E
z*?0}7+3(E^RD3XhP2O*lHs$VTC&Tu-go@a?FM6IqeKf=3y_Ok5|us
zVf*QUX*C|xr#3nL^3G?&EBk@P0%lu=&RDWL-j&cJOQ
z^;i>_>oSAJC}Jx2BR_#J@_K9YF$!$(uweh_O|VuBnVqn94r
zY1ABSbN}HVDwBl*0y2G6nPH&6-wENrRpxVT*R03>$mkcW21>B{o@vx^o{b^Sv@8|z
ziOy~SHwH}gY_W~$q0^Rx?agmOeCu(abpYYjKvZQ?~pEjCS)E7lPLC*itNL^pIMSkc^=tN-%F5gE-Pbgd$JSG$t;xoH6Mi=NJUe)1sp881
zQYU5#N3WfTP4DeTp1DCQu`@tidh`k>SOep<8hW4da-WPP-B8K*P%=zmd34gA9Brga
z89$XD^&bA9a+j3qitVSYx=1hu1GYFzjXC6Orle;Z)rGm%V1$jqLUOpBF`rIM$??Q0
zus}(;d(xjFu03vlqvCu`hTiKzZ2BzW*81H+#GdapTxMp@(MoXF%m;dCtjh%}qA>fZ
zkT%8mkKItC&Lx{!`yUrYrPxfJ8mMn!Rz&^T&_~F&64Wn+qLO5Po|FRKibtfm9!j!x
zvIF8yyNL6_~Y+Mw3lhZ7?1Ua|Y_TAliB5Su*BhLz$X)E^gPQ{k<>GlCIin07M
znw2l!E=_fwO7d|Tb(_h=l3JGL{!_|(;>-#C8-0}Cc=s!S%v@%+5|RD{s;usAZ@RcO
z87SLfZ);5@Oi`Q?dtAi&==H19tu<0Cq01XS(O
z6T5$^g{Ia%OPK
z87VCl1%sy8Q#3fCOI{p`7B|>R5w)?V?QqdoS~i7pmj_q{I}5})UqCUaG6l-czwM|}
z*VRF1xx4mGyku}49!j7u+<^!FAC4o4R|M^EcTAR-3`hkx^OSOf3n69LY`Z}?cK
z{t4t5tieDq>Vx6b(U}qSaJYV-%$}Fz8HjC&)tyWcz?
zn1iRcs9&(RlGY8>GY4R9=a|>CX!_ewXy}Gv;z--e-_1e1vfG`BFMWQJKCMH;u*~sse;X+?#P#b6thn|#hyF!8*Va!{&EbAWi$X&s`O+H;%&2+HiEK9+onglkBtgyQ@_{A#(&
zw_)K1CEeMs^fW@C6yrQ1d`)qq!Ow|B!^M8I>ca~SkfeXJ+Fy-^LRFcy)ga|8T?Fn)
zOw|Art{KFO=6OSx!ONMHk`Oyk;C_i6p8aeq5lp}6k
zW*3sWo}rs8#$>w+C);@ETGzz;FvK~@I(e(#Bj>M^=p}voyz2lF8)w}pjcVl)dFw?8
z9PxEIRxb*nQDB)>7XB{vo%Y2N=V?Rz<)rk6xY%Pl!;%^yZC^XNEH_}bhS;Ej$kSWh
zYUf<-bo6t3(Sik=WDJ@tq>-%iwz+)S
z&t0vpG=wr`Xq8p!ATqCi`{a=sXqVc&$io4Gi}}H5zINN_95=wynlMl4+Q%H%dYH_H
zRP?HZ%PalHovg0l_WfsMWbMzbGBs5hx@eR1SW+mJkyI@q`=c+hA6mY!KnLV
ziJw+wqT_H|iCN3oVDYmHl0>W=4Rxkr;wS>@wk_4b@*rGl2qKTJFyUdeeLlJOw&vZF
zRpzJY7Lyaz%>#H^aa`&BST3}Lt7@f)-ZyNI<((YK$OxVRH?syz6G_!p-eRHGy*~%d
z@iLPo1ir%|^X}n;<7ykN*GCo?%qF(;BF8#5$sI1*ci!UVC-=0LXAf3q`^Q7Gf?c{I
zoGhJq;Ti2AqNrs`dW*yNxFuG|C&}9Ek4ls1Y@UHDW$5v^H&Y2d=^xxZVAMO+e&lGz5G}eh$W$C@*^gE2I^BOxDuWaurH?2xdzRDdBe43B<;zbPr
zZ2Sw@ZthAW#^v|s|
zt)l68I)A`CbA6DpHs%KuiSJHv|q^0N@(yKRa>t@101y?O6Ioh2zm(uQSkA1dzHt
zFHMk!TgU|Gde@V+j-qi$KIk>m+SSrun+C#4<3;`#X}}RZuQ!tzDjuEPN^42Oq;IOh
zRgx@pmhpVWTH*`8&}{1$&HIdQ@eyXP-Rv_P%h|8jJ|l%VXw-NZxdB*$-(*+3>~`U<
zLLa`iA+Uz&`dmJf$oT2=(B8sja7y2KlyQZp4#g@W%)cC`Mm$dy!S4MCPQXo
zpxmU-py3Je@V)Jjruhv`>d6WAEPHu2@f=gQSjMfju*0`JTX!^BK<|!`F%;2vi*c@j*5-g+I?r2xjwh^tqz5{!;&5eSY0v(H@JOc%U70v+
zUeSl`wvU#5oJ!QI{lApEsl(tB<=vq(=v`z`?POQRz0zX;NvpfILA7?u;}?y;Mh^k{dP+?
z%As;wi7v^^WD^n@gqacHwp^za_Cm88<7Gx#W6I4h7WzfLVAj$R3I>BdYgYq{#Waj9^`
zq@cI*H!*bdp|Y=&wWnyX;;c2oT{dpCgy=1HekthF=~R&*GJ~w+naNx2Qex;%8A(5F
zQfJkqDPbUwJTk{}?>s5o(MD)gu-(TGc0aGGep}YC^
zg4KQZbO2I?IQ|?&9-5f<%hVGonh!ck1HSLN^Ts=(aSlzPMm;R{e
z=%1^>{>NUReC0y}9{4y11o-?Ts8)_CV^EIB(!n#1L&z480^l^IO$g`0o9fiTK
zc%PmdfcV|7H&87E&M#SY{qAIr$j_JUP|nS2K#310qv{Z{=?C)T_xq{k;NdFK#Pm5)
z+0Sc_OpEc-nm1p?i3!V&V;~I-kV7o(FJqE!^*Aq08OJehOH>T@9g_%Qz!>EV_*|@oQ#d3#Li9S*)U$%gXVK-jMg^
zkL#`rA`9+tV=$&atl4gY&1TZ$KwRjB(E>j9K>Ub~58M@%V|7DbN)Z)v96jUQX-Y`H
zd=P!LN9tEII_YB(6WOC{kD-fyCWD9Qe$n7|)KHc*WKS$F>{v{;!s;}lWM-FzW-mPN
zVU?YI2hEoHPW}6Vr7B>tM
zk)(fbgl}U>uHLcU88l@7%*<7#VCc7m03;{h$&P_dKj=gfj)uW(*jCPf83%enwVdEj
z0kp|N)S^QyY4qXlj4F{wBQqY?zk1N`4Ex=b8Ks{0S=+Y&tN1M=2;cDNScdhCrUE8Z
z&IaVf&C2S~-n|R};F{!laawW$z!;pZ9PU4Uf?YXf(BIBgUOoX>gsYWT0{+CU%Nqb6
z~Q*l>dx##}h0^&wtw}H=aFa;U#
nzaHjlkVTER+D+_1jC^t#;A)2!hA^F+%@
z6aWAK2mpsp;y}7Hky$Z<{a_fZr$WKOn!CORgjeshU>R
zCT&u+?(*!wA+ZXW*-mKp>vwE}6B=5T2W*agzH`N1T|bwFcLX6BuM!gZA@QI}dB&<;
zLVo?czlw<`6|FK_@Cp+00+L*>KYv;^Ts+A=fbx(~$%O2cs+U0^(>;__`n-mJ3RiM2
zN~-Wv>;hQ}pqWuBi(nLnv!JA`B9^ceA7Su3XDQtA^iV>jEJQ#-75Xpttd^Ir|VV
z6STotJAckxyt6Bg7fmkwD_JVI?HmGi;xtX;H?{QBs`cW3EJ4vc0CjtiikEj(QL$9UZ5p>dnMCtPP~#no3XK0qR^jAqFMJKC^Z>to#-G|eblPs*s71FeiX)I_q?k<
zZq;#Y)nh+ioQ%)jw?1jraqOI*`p&YR3|S9s3QQ4n8?0A?Hy-{Xm4IyB~I0F6?oIChH0>_%(!(Wf0fG{!i9
zMyW$jUZJ%{A04|LJv8Rkp(B?NlsfcmSW&n9WhSSCIkeJo*gnI+*&uLcfRR(NnQNsT
z@Km#2Y}i=?F3t^c^onSu&c!!~`g*twIyS^p?W~Vhx_HY+dqUV>=B()szEiPdmGlb8
zG4-K8#{>)e|Jx76Yg5rJS9rLY=^if_zy=QW`H*w2x(_@AH1iSEq4sK8gLq{B@IJ%i
zg$ssPiMc@%QgfjMWlEnK7Itqa`UG=V-ugdKO9KRx5i%OHrwT{|3ZEFp?ivID09y@{
zp$Zv)G%k2;Y?W7AkE1peey`O3fc(CIbDfNmtQJt(O0Bf&^z!T>PGSvUYMe0JU*F>k
z5F0Yu<{`l6bIx~eKA7$2_p%6XR5XlNJA}g+1+>cfkyYm%`ty&&rzaFNl2k`h@QUux
zH`<`j`yYSUwp_e57fNLS7OG~4E>hOdQPkvr7g~}gdq+W9h_&S5`3jO^4e#MCD+A
zr)}G?t(!2ftQU6)!t032^x8;XW<(OS-|7l{714fsWROKw`asar4t>GT2fRkGphf;-
zv~BM014&+AXhCyHkI*Y9P(AR<`I{2_1)x|N*8<8U$tAndlorJf{WJ&h<{!L<;CCZC@(vIv_T#AJ8yrc?0k{n%sgD8fL~?7D*7wE(E=29
zXsL*DUVv5j4@#zb90etLH~t%oI1U#Jyy|{y6y!QxmQZ`!tIw=DTw9|)TZTA(n)kmC
z)#p|nu2vnd{)hU)s>8KYUxq!iy7`cOW!2$o)i>dMt+N!FH0h$KKG|;tZv)tWL#l8!
zQ9B$zFQCA=0>1zXX>Z{9wvV^b4QiO%wM1!Xy%zB9kTT^KXbnG-