Merge branch 'develop'
This commit is contained in:
commit
a62db174f4
|
@ -1,90 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
jsonIter "github.com/json-iterator/go"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoadHandle 配置加载处理函数
|
||||
type LoadHandle func(handle func(filename string, config any) error)
|
||||
|
||||
// RefreshHandle 配置刷新处理函数
|
||||
type RefreshHandle func()
|
||||
|
||||
const (
|
||||
tickerLoadRefresh = "_tickerLoadRefresh"
|
||||
)
|
||||
|
||||
var (
|
||||
cLoadDir string
|
||||
cTicker *timer.Ticker
|
||||
cInterval time.Duration
|
||||
cLoadHandle LoadHandle
|
||||
cRefreshHandle RefreshHandle
|
||||
json = jsonIter.ConfigCompatibleWithStandardLibrary
|
||||
mutex sync.Mutex
|
||||
isInit = true
|
||||
)
|
||||
|
||||
// Init 配置初始化
|
||||
func Init(loadDir string, loadHandle LoadHandle, refreshHandle RefreshHandle) {
|
||||
cLoadDir = loadDir
|
||||
cLoadHandle = loadHandle
|
||||
cRefreshHandle = refreshHandle
|
||||
Load()
|
||||
Refresh()
|
||||
}
|
||||
|
||||
// Load 加载配置
|
||||
// - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新
|
||||
func Load() {
|
||||
mutex.Lock()
|
||||
cLoadHandle(func(filename string, config any) error {
|
||||
bytes, err := os.ReadFile(filepath.Join(cLoadDir, filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = json.Unmarshal(bytes, &config); err == nil && isInit {
|
||||
log.Info("Config", zap.String("Name", filename), zap.Bool("LoadSuccess", true))
|
||||
}
|
||||
return err
|
||||
})
|
||||
isInit = false
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// WithTickerLoad 通过定时器加载配置
|
||||
// - 通过定时器加载配置后,会自动刷新线上配置
|
||||
// - 调用该函数后将会立即加载并刷新一次配置,随后每隔 interval 时间加载并刷新一次配置
|
||||
func WithTickerLoad(ticker *timer.Ticker, interval time.Duration) {
|
||||
if ticker != cTicker && cTicker != nil {
|
||||
cTicker.StopTimer(tickerLoadRefresh)
|
||||
}
|
||||
cTicker = ticker
|
||||
cInterval = interval
|
||||
cTicker.Loop(tickerLoadRefresh, timer.Instantly, cInterval, timer.Forever, func() {
|
||||
Load()
|
||||
Refresh()
|
||||
})
|
||||
}
|
||||
|
||||
// StopTickerLoad 停止通过定时器加载配置
|
||||
func StopTickerLoad() {
|
||||
if cTicker != nil {
|
||||
cTicker.StopTimer(tickerLoadRefresh)
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh 刷新配置
|
||||
func Refresh() {
|
||||
mutex.Lock()
|
||||
cRefreshHandle()
|
||||
OnConfigRefreshEvent()
|
||||
mutex.Unlock()
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// Package config 基于配置导表功能实现的配置加载及刷新功能
|
||||
package config
|
|
@ -0,0 +1,73 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
tickerLoadRefresh = "_tickerLoadRefresh"
|
||||
)
|
||||
|
||||
var (
|
||||
cTicker *timer.Ticker
|
||||
cInterval time.Duration
|
||||
cLoader []Loader
|
||||
)
|
||||
|
||||
// Init 配置初始化
|
||||
// - 在初始化后会立即加载配置
|
||||
func Init(loader ...Loader) {
|
||||
cLoader = loader
|
||||
Load()
|
||||
Refresh()
|
||||
}
|
||||
|
||||
// Load 加载配置
|
||||
// - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新
|
||||
func Load() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error("Config", log.String("Action", "Load"), log.Err(err.(error)))
|
||||
}
|
||||
}()
|
||||
for _, loader := range cLoader {
|
||||
loader.Load()
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh 刷新配置
|
||||
func Refresh() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error("Config", log.String("Action", "Refresh"), log.Err(err.(error)))
|
||||
}
|
||||
OnConfigRefreshEvent()
|
||||
}()
|
||||
for _, loader := range cLoader {
|
||||
loader.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// WithTickerLoad 通过定时器加载配置
|
||||
// - 通过定时器加载配置后,会自动刷新线上配置
|
||||
// - 调用该函数后不会立即刷新,而是在 interval 后加载并刷新一次配置,之后每隔 interval 加载并刷新一次配置
|
||||
func WithTickerLoad(ticker *timer.Ticker, interval time.Duration) {
|
||||
if ticker != cTicker && cTicker != nil {
|
||||
cTicker.StopTimer(tickerLoadRefresh)
|
||||
}
|
||||
cTicker = ticker
|
||||
cInterval = interval
|
||||
cTicker.Loop(tickerLoadRefresh, cInterval, cInterval, timer.Forever, func() {
|
||||
Load()
|
||||
Refresh()
|
||||
})
|
||||
}
|
||||
|
||||
// StopTickerLoad 停止通过定时器加载配置
|
||||
func StopTickerLoad() {
|
||||
if cTicker != nil {
|
||||
cTicker.StopTimer(tickerLoadRefresh)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
// Package configuration 基于配置导表功能实现的配置加载及刷新功能
|
||||
package configuration
|
|
@ -1,4 +1,4 @@
|
|||
package config
|
||||
package configuration
|
||||
|
||||
// RefreshEventHandle 配置刷新事件处理函数
|
||||
type RefreshEventHandle func()
|
|
@ -0,0 +1,10 @@
|
|||
package configuration
|
||||
|
||||
// Loader 配置加载器
|
||||
type Loader interface {
|
||||
// Load 加载配置
|
||||
// - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新
|
||||
Load()
|
||||
// Refresh 刷新线上配置
|
||||
Refresh()
|
||||
}
|
|
@ -272,7 +272,7 @@ func (slf *Attrs) GetAttrInt64(id int) int64 {
|
|||
case int32:
|
||||
return int64(value)
|
||||
case int64:
|
||||
return int64(value)
|
||||
return value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"github.com/kercylan98/minotaur/utils/asynchronous"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// NewRoom 创建一个默认的内置游戏房间 Room
|
||||
|
@ -90,7 +89,7 @@ func (slf *Room[PlayerID, Player]) Join(player Player) error {
|
|||
}
|
||||
slf.players.Set(playerId, player)
|
||||
if !exist {
|
||||
log.Debug("Room.Join", zap.Any("guid", slf.GetGuid()), zap.Any("player", playerId))
|
||||
log.Debug("Room.Join", log.Any("guid", slf.GetGuid()), log.Any("player", playerId))
|
||||
if slf.players.Size() == 1 && !slf.noMaster {
|
||||
slf.owner = playerId
|
||||
}
|
||||
|
@ -105,7 +104,7 @@ func (slf *Room[PlayerID, Player]) Leave(id PlayerID) {
|
|||
if !exist {
|
||||
return
|
||||
}
|
||||
log.Debug("Room.Leave", zap.Any("guid", slf.GetGuid()), zap.Any("player", id))
|
||||
log.Debug("Room.Leave", log.Any("guid", slf.GetGuid()), log.Any("player", id))
|
||||
slf.OnPlayerLeaveRoomEvent(player)
|
||||
slf.players.Delete(id)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/synchronization"
|
||||
"go.uber.org/zap"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
|
@ -103,7 +102,7 @@ func (slf *World[PlayerID, Player]) Join(player Player) error {
|
|||
if slf.players.Size() >= slf.playerLimit && slf.playerLimit > 0 {
|
||||
return ErrWorldPlayerLimit
|
||||
}
|
||||
log.Debug("World.Join", zap.Int64("guid", slf.GetGuid()), zap.Any("player", player.GetID()))
|
||||
log.Debug("World.Join", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID()))
|
||||
slf.players.Set(player.GetID(), player)
|
||||
if actors := slf.playerActors.Get(player.GetID()); actors == nil {
|
||||
actors = synchronization.NewMap[int64, game.Actor]()
|
||||
|
@ -118,7 +117,7 @@ func (slf *World[PlayerID, Player]) Leave(id PlayerID) {
|
|||
if !exist {
|
||||
return
|
||||
}
|
||||
log.Debug("World.Leave", zap.Int64("guid", slf.GetGuid()), zap.Any("player", player.GetID()))
|
||||
log.Debug("World.Leave", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID()))
|
||||
slf.OnPlayerLeaveWorldEvent(player)
|
||||
slf.playerActors.Get(player.GetID()).Range(func(guid int64, actor game.Actor) {
|
||||
slf.OnActorAnnihilationEvent(actor)
|
||||
|
@ -170,7 +169,7 @@ func (slf *World[PlayerID, Player]) RemoveActorOwner(guid int64) {
|
|||
}
|
||||
|
||||
func (slf *World[PlayerID, Player]) Reset() {
|
||||
log.Debug("World", zap.Int64("Reset", slf.guid))
|
||||
log.Debug("World", log.Int64("Reset", slf.guid))
|
||||
slf.players.Clear()
|
||||
slf.playerActors.Range(func(id PlayerID, actors hash.Map[int64, game.Actor]) {
|
||||
actors.Clear()
|
||||
|
@ -184,7 +183,7 @@ func (slf *World[PlayerID, Player]) Reset() {
|
|||
|
||||
func (slf *World[PlayerID, Player]) Release() {
|
||||
if !slf.released.Swap(true) {
|
||||
log.Debug("World", zap.Int64("Release", slf.guid))
|
||||
log.Debug("World", log.Int64("Release", slf.guid))
|
||||
slf.OnWorldReleaseEvent()
|
||||
slf.Reset()
|
||||
slf.players = nil
|
||||
|
|
|
@ -2,7 +2,6 @@ package notify
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"go.uber.org/zap"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
@ -20,19 +19,19 @@ func NewManager(senders ...Sender) *Manager {
|
|||
case <-manager.closeChannel:
|
||||
close(manager.closeChannel)
|
||||
close(manager.notifyChannel)
|
||||
log.Info("Manager", zap.String("state", "release"))
|
||||
log.Info("Manager", log.String("state", "release"))
|
||||
return
|
||||
case notify := <-manager.notifyChannel:
|
||||
for _, sender := range manager.senders {
|
||||
if err := sender.Push(notify); err != nil {
|
||||
log.Error("Manager", zap.String("sender", reflect.TypeOf(sender).String()), zap.Error(err))
|
||||
log.Error("Manager", log.String("sender", reflect.TypeOf(sender).String()), log.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log.Info("Manager", zap.String("state", "running"))
|
||||
log.Info("Manager", log.String("state", "running"))
|
||||
return manager
|
||||
}
|
||||
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
package configexport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/planner/configexport/internal"
|
||||
"github.com/kercylan98/minotaur/utils/file"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/tealeg/xlsx"
|
||||
"go.uber.org/zap"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// New 创建一个导表配置
|
||||
func New(xlsxPath string) *ConfigExport {
|
||||
ce := &ConfigExport{xlsxPath: xlsxPath, exist: make(map[string]bool)}
|
||||
xlsxFile, err := xlsx.OpenFile(xlsxPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; i < len(xlsxFile.Sheets); i++ {
|
||||
sheet := xlsxFile.Sheets[i]
|
||||
if config, err := internal.NewConfig(sheet, ce.exist); err != nil {
|
||||
switch err {
|
||||
case internal.ErrReadConfigFailedSame:
|
||||
log.Warn("ConfigExport",
|
||||
zap.String("File", xlsxPath),
|
||||
zap.String("Sheet", sheet.Name),
|
||||
zap.String("Info", "A configuration with the same name exists, skipped"),
|
||||
)
|
||||
case internal.ErrReadConfigFailedIgnore:
|
||||
log.Info("ConfigExport",
|
||||
zap.String("File", xlsxPath),
|
||||
zap.String("Sheet", sheet.Name),
|
||||
zap.String("Info", "Excluded non-configuration table files"),
|
||||
)
|
||||
default:
|
||||
log.ErrorHideStack("ConfigExport",
|
||||
zap.String("File", xlsxPath),
|
||||
zap.String("Sheet", sheet.Name),
|
||||
zap.String("Info", "Excluded non-configuration table files"),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if config == nil {
|
||||
continue
|
||||
}
|
||||
ce.configs = append(ce.configs, config)
|
||||
ce.exist[config.Name] = true
|
||||
|
||||
log.Info("ConfigExport",
|
||||
zap.String("File", xlsxPath),
|
||||
zap.String("Sheet", sheet.Name),
|
||||
zap.String("Info", "Export successfully"),
|
||||
)
|
||||
}
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
type ConfigExport struct {
|
||||
xlsxPath string
|
||||
configs []*internal.Config
|
||||
exist map[string]bool
|
||||
}
|
||||
|
||||
// Merge 合并多个导表配置
|
||||
func Merge(exports ...*ConfigExport) *ConfigExport {
|
||||
if len(exports) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(exports) == 1 {
|
||||
return exports[0]
|
||||
}
|
||||
var export = exports[0]
|
||||
for i := 1; i < len(exports); i++ {
|
||||
ce := exports[i]
|
||||
for _, config := range ce.configs {
|
||||
if _, ok := export.exist[config.Name]; ok {
|
||||
log.Warn("ConfigExport",
|
||||
zap.String("File", ce.xlsxPath),
|
||||
zap.String("Sheet", config.Name),
|
||||
zap.String("Info", "A configuration with the same name exists, skipped"),
|
||||
)
|
||||
continue
|
||||
}
|
||||
export.configs = append(export.configs, config)
|
||||
export.exist[config.Name] = true
|
||||
}
|
||||
}
|
||||
return export
|
||||
}
|
||||
|
||||
func (slf *ConfigExport) ExportClient(prefix, outputDir string) {
|
||||
for _, config := range slf.configs {
|
||||
config := config
|
||||
if len(prefix) > 0 {
|
||||
config.Prefix = fmt.Sprintf("%s.", prefix)
|
||||
}
|
||||
if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s%s.json", config.Prefix, config.Name)), config.JsonClient()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (slf *ConfigExport) ExportServer(prefix, outputDir string) {
|
||||
for _, config := range slf.configs {
|
||||
config := config
|
||||
if len(prefix) > 0 {
|
||||
config.Prefix = fmt.Sprintf("%s.", prefix)
|
||||
}
|
||||
if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s%s.json", config.Prefix, config.Name)), config.JsonServer()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (slf *ConfigExport) ExportGo(prefix, outputDir string) {
|
||||
if len(prefix) > 0 {
|
||||
for _, config := range slf.configs {
|
||||
config.Prefix = fmt.Sprintf("%s.", prefix)
|
||||
}
|
||||
}
|
||||
slf.exportGoConfig(outputDir)
|
||||
slf.exportGoDefine(outputDir)
|
||||
}
|
||||
|
||||
func (slf *ConfigExport) exportGoConfig(outputDir string) {
|
||||
var v struct {
|
||||
Package string
|
||||
Configs []*internal.Config
|
||||
}
|
||||
v.Package = filepath.Base(outputDir)
|
||||
|
||||
for _, config := range slf.configs {
|
||||
v.Configs = append(v.Configs, config)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("struct").Parse(internal.GenerateGoConfigTemplate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, &v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var result string
|
||||
_ = str.RangeLine(buf.String(), func(index int, line string) error {
|
||||
if len(strings.TrimSpace(line)) == 0 {
|
||||
return nil
|
||||
}
|
||||
result += fmt.Sprintf("%s\n", strings.ReplaceAll(line, "\t\t", "\t"))
|
||||
if len(strings.TrimSpace(line)) == 1 {
|
||||
result += "\n"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
filePath := filepath.Join(outputDir, "config.go")
|
||||
if err := file.WriterFile(filePath, []byte(result)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("gofmt", "-w", filePath)
|
||||
_ = cmd.Run()
|
||||
}
|
||||
|
||||
func (slf *ConfigExport) exportGoDefine(outputDir string) {
|
||||
var v struct {
|
||||
Package string
|
||||
Configs []*internal.Config
|
||||
}
|
||||
v.Package = filepath.Base(outputDir)
|
||||
|
||||
for _, config := range slf.configs {
|
||||
v.Configs = append(v.Configs, config)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("struct").Parse(internal.GenerateGoDefineTemplate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, &v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var result string
|
||||
_ = str.RangeLine(buf.String(), func(index int, line string) error {
|
||||
if len(strings.TrimSpace(line)) == 0 {
|
||||
return nil
|
||||
}
|
||||
result += fmt.Sprintf("%s\n", strings.ReplaceAll(line, "\t\t", "\t"))
|
||||
if len(strings.TrimSpace(line)) == 1 {
|
||||
result += "\n"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
filePath := filepath.Join(outputDir, "config.define.go")
|
||||
if err := file.WriterFile(filePath, []byte(result)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("gofmt", "-w", filePath)
|
||||
_ = cmd.Run()
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package configexport_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/config"
|
||||
"github.com/kercylan98/minotaur/planner/configexport"
|
||||
"github.com/kercylan98/minotaur/planner/configexport/example"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
var workdir = "./"
|
||||
files, err := os.ReadDir(workdir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var ces []*configexport.ConfigExport
|
||||
for _, file := range files {
|
||||
if file.IsDir() || !strings.HasSuffix(file.Name(), ".xlsx") || strings.HasPrefix(file.Name(), "~") {
|
||||
continue
|
||||
}
|
||||
|
||||
ces = append(ces, configexport.New(filepath.Join(workdir, file.Name())))
|
||||
}
|
||||
|
||||
c := configexport.Merge(ces...)
|
||||
outDir := filepath.Join(workdir, "example")
|
||||
c.ExportGo("", outDir)
|
||||
c.ExportServer("", outDir)
|
||||
c.ExportClient("", outDir)
|
||||
|
||||
// 下方为配置加载代码
|
||||
// 使用生成的 LoadConfig 函数加载配置
|
||||
config.Init(outDir, example.LoadConfig, example.Refresh)
|
||||
|
||||
fmt.Println("success")
|
||||
|
||||
// Output:
|
||||
// success
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// Package configexport 提供了XLSX配置转换为JSON及Go代码的导表工具实现
|
||||
package configexport
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"Id": 1,
|
||||
"Award": {
|
||||
"0": "asd",
|
||||
"1": "12"
|
||||
},
|
||||
"Other": {
|
||||
"0": {
|
||||
"id": 1,
|
||||
"name": "张飞"
|
||||
},
|
||||
"1": {
|
||||
"id": 2,
|
||||
"name": "刘备"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
{
|
||||
"0": {
|
||||
"": {
|
||||
"Award": null,
|
||||
"Other": null,
|
||||
"Id": 0,
|
||||
"Count": ""
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"b": {
|
||||
"Id": 1,
|
||||
"Count": "b",
|
||||
"Award": {
|
||||
"0": "asd",
|
||||
"1": "12"
|
||||
},
|
||||
"Other": {
|
||||
"0": {
|
||||
"id": 1,
|
||||
"name": "张飞"
|
||||
},
|
||||
"1": {
|
||||
"name": "刘备",
|
||||
"id": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"c": {
|
||||
"Id": 2,
|
||||
"Count": "c",
|
||||
"Award": {
|
||||
"0": "asd",
|
||||
"1": "12"
|
||||
},
|
||||
"Other": {
|
||||
"0": {
|
||||
"id": 1,
|
||||
"name": "张飞"
|
||||
},
|
||||
"1": {
|
||||
"id": 2,
|
||||
"name": "刘备"
|
||||
}
|
||||
}
|
||||
},
|
||||
"d": {
|
||||
"Id": 2,
|
||||
"Count": "d",
|
||||
"Award": {
|
||||
"0": "asd",
|
||||
"1": "12"
|
||||
},
|
||||
"Other": {
|
||||
"0": {
|
||||
"id": 1,
|
||||
"name": "张飞"
|
||||
},
|
||||
"1": {
|
||||
"id": 2,
|
||||
"name": "刘备"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
// Code generated by minotaur-config-export. DO NOT EDIT.
|
||||
package example
|
||||
|
||||
// IndexConfigDefine 有索引
|
||||
type IndexConfigDefine struct {
|
||||
Id int // 任务ID
|
||||
Count string // 次数
|
||||
Info *IndexConfigInfo // 信息
|
||||
Other map[int]*IndexConfigOther // 信息2
|
||||
}
|
||||
|
||||
func (slf *IndexConfigDefine) String() string {
|
||||
if data, err := json.Marshal(slf); err == nil {
|
||||
return string(data)
|
||||
}
|
||||
|
||||
return "{}"
|
||||
}
|
||||
|
||||
type IndexConfigInfo struct {
|
||||
Id int
|
||||
Name string
|
||||
Info *IndexConfigInfoInfo
|
||||
}
|
||||
|
||||
type IndexConfigInfoInfo struct {
|
||||
Lv int
|
||||
Exp *IndexConfigInfoInfoExp
|
||||
}
|
||||
|
||||
type IndexConfigInfoInfoExp struct {
|
||||
Mux int
|
||||
Count int
|
||||
}
|
||||
|
||||
type IndexConfigOther struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
// EasyConfigDefine 无索引
|
||||
type EasyConfigDefine struct {
|
||||
Id int // 任务ID
|
||||
Info *EasyConfigInfo // 信息
|
||||
Other map[int]*EasyConfigOther // 信息2
|
||||
}
|
||||
|
||||
func (slf *EasyConfigDefine) String() string {
|
||||
if data, err := json.Marshal(slf); err == nil {
|
||||
return string(data)
|
||||
}
|
||||
|
||||
return "{}"
|
||||
}
|
||||
|
||||
type EasyConfigInfo struct {
|
||||
Id int
|
||||
Name string
|
||||
Info *EasyConfigInfoInfo
|
||||
}
|
||||
|
||||
type EasyConfigInfoInfo struct {
|
||||
Lv int
|
||||
Exp *EasyConfigInfoInfoExp
|
||||
}
|
||||
|
||||
type EasyConfigInfoInfoExp struct {
|
||||
Mux int
|
||||
Count int
|
||||
}
|
||||
|
||||
type EasyConfigOther struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// Code generated by minotaur-config-export. DO NOT EDIT.
|
||||
package example
|
||||
|
||||
import (
|
||||
jsonIter "github.com/json-iterator/go"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
)
|
||||
|
||||
var json = jsonIter.ConfigCompatibleWithStandardLibrary
|
||||
var full map[string]any
|
||||
var (
|
||||
// IndexConfig 有索引
|
||||
IndexConfigSign = "IndexConfig"
|
||||
IndexConfig map[int]map[string]*IndexConfigDefine
|
||||
_IndexConfig map[int]map[string]*IndexConfigDefine
|
||||
// EasyConfig 无索引
|
||||
EasyConfigSign = "EasyConfig"
|
||||
EasyConfig *EasyConfigDefine
|
||||
_EasyConfig *EasyConfigDefine
|
||||
)
|
||||
|
||||
func LoadConfig(handle func(filename string, config any) error) {
|
||||
var err error
|
||||
_IndexConfig = make(map[int]map[string]*IndexConfigDefine)
|
||||
if err = handle("IndexConfig.json", &_IndexConfig); err != nil {
|
||||
log.Error("Config", zap.String("Name", "IndexConfig"), zap.Bool("Invalid", true), zap.Error(err))
|
||||
}
|
||||
|
||||
_EasyConfig = new(EasyConfigDefine)
|
||||
if err = handle("EasyConfig.json", _EasyConfig); err != nil {
|
||||
log.Error("Config", zap.String("Name", "EasyConfig"), zap.Bool("Invalid", true), zap.Error(err))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Refresh 将加载后的配置刷新到线上
|
||||
func Refresh() {
|
||||
full = make(map[string]any)
|
||||
IndexConfig = _IndexConfig
|
||||
full["IndexConfig"] = IndexConfig
|
||||
EasyConfig = _EasyConfig
|
||||
full["EasyConfig"] = EasyConfig
|
||||
}
|
||||
|
||||
// DefaultLoad 默认提供的配置加载函数
|
||||
func DefaultLoad(filepath string) {
|
||||
LoadConfig(func(filename string, config any) error {
|
||||
bytes, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(bytes, &config)
|
||||
})
|
||||
}
|
||||
|
||||
// GetFull 获取所有配置的 map 集合
|
||||
// - 通常用于前端配置通过后端接口获取的情况
|
||||
func GetFull() map[string]any {
|
||||
return full
|
||||
}
|
|
@ -1,423 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
jsonIter "github.com/json-iterator/go"
|
||||
"github.com/kercylan98/minotaur/utils/geometry/matrix"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/kercylan98/minotaur/utils/xlsxtool"
|
||||
"github.com/tealeg/xlsx"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// NewConfig 定位为空将读取Sheet名称
|
||||
// - 表格中需要严格遵守 描述、名称、类型、导出参数、数据列的顺序
|
||||
func NewConfig(sheet *xlsx.Sheet, exist map[string]bool) (*Config, error) {
|
||||
config := &Config{
|
||||
ignore: "#",
|
||||
excludeFields: map[int]bool{0: true},
|
||||
Exist: exist,
|
||||
}
|
||||
if strings.HasPrefix(sheet.Name, config.ignore) {
|
||||
return nil, ErrReadConfigFailedIgnore
|
||||
}
|
||||
if err := config.initField(sheet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Exist map[string]bool
|
||||
Prefix string
|
||||
DisplayName string
|
||||
Name string
|
||||
Describe string
|
||||
ExportParam string
|
||||
IndexCount int
|
||||
Fields []*Field
|
||||
|
||||
matrix *matrix.Matrix[*xlsx.Cell]
|
||||
excludeFields map[int]bool // 排除的字段
|
||||
ignore string
|
||||
horizontal bool
|
||||
dataStart int
|
||||
|
||||
dataServer map[any]any
|
||||
dataClient map[any]any
|
||||
}
|
||||
|
||||
func (slf *Config) initField(sheet *xlsx.Sheet) error {
|
||||
slf.matrix = xlsxtool.GetSheetMatrix(sheet)
|
||||
var displayName *Position
|
||||
name, indexCount := NewPosition(1, 0), NewPosition(1, 1)
|
||||
if displayName == nil {
|
||||
slf.DisplayName = sheet.Name
|
||||
} else {
|
||||
if value := slf.matrix.Get(displayName.X, displayName.Y); value == nil {
|
||||
return ErrReadConfigFailedWithDisplayName
|
||||
} else {
|
||||
slf.DisplayName = strings.TrimSpace(value.String())
|
||||
}
|
||||
}
|
||||
|
||||
if name == nil {
|
||||
slf.Name = sheet.Name
|
||||
} else {
|
||||
if value := slf.matrix.Get(name.X, name.Y); value == nil {
|
||||
return ErrReadConfigFailedWithName
|
||||
} else {
|
||||
slf.Name = str.FirstUpper(strings.TrimSpace(value.String()))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(sheet.Name, slf.ignore) {
|
||||
return ErrReadConfigFailedIgnore
|
||||
}
|
||||
if hash.Exist(slf.Exist, slf.Name) {
|
||||
return ErrReadConfigFailedSame
|
||||
}
|
||||
|
||||
if indexCount == nil {
|
||||
slf.IndexCount, _ = strconv.Atoi(sheet.Name)
|
||||
} else {
|
||||
if value := slf.matrix.Get(indexCount.X, indexCount.Y); value == nil {
|
||||
return ErrReadConfigFailedWithIndexCount
|
||||
} else {
|
||||
indexCount, err := value.Int()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if indexCount < 0 {
|
||||
return ErrReadConfigFailedWithIndexCountLessThanZero
|
||||
}
|
||||
slf.IndexCount = indexCount
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
describeStart int
|
||||
horizontal bool
|
||||
fields = make(map[string]bool)
|
||||
dx, dy, nx, ny, tx, ty, ex, ey int
|
||||
)
|
||||
horizontal = slf.IndexCount > 0
|
||||
slf.horizontal = horizontal
|
||||
|
||||
if horizontal {
|
||||
describeStart = 3
|
||||
dy = describeStart
|
||||
ny = dy + 1
|
||||
ty = ny + 1
|
||||
ey = ty + 1
|
||||
slf.dataStart = ey + 1
|
||||
} else {
|
||||
delete(slf.excludeFields, 0)
|
||||
describeStart = 4
|
||||
dy = describeStart
|
||||
ny = dy
|
||||
ty = dy
|
||||
ey = dy
|
||||
nx = dx + 1
|
||||
tx = nx + 1
|
||||
ex = tx + 1
|
||||
slf.dataStart = ey + 1
|
||||
}
|
||||
var index = slf.IndexCount
|
||||
for {
|
||||
var (
|
||||
describe, fieldName, fieldType, exportParam string
|
||||
)
|
||||
var skip bool
|
||||
if value, exist := slf.matrix.GetExist(dx, dy); !exist {
|
||||
skip = true
|
||||
} else {
|
||||
describe = value.String()
|
||||
}
|
||||
if value, exist := slf.matrix.GetExist(nx, ny); !exist {
|
||||
skip = true
|
||||
} else {
|
||||
fieldName = str.FirstUpper(strings.TrimSpace(value.String()))
|
||||
}
|
||||
if value, exist := slf.matrix.GetExist(tx, ty); !exist {
|
||||
skip = true
|
||||
} else {
|
||||
fieldType = strings.TrimSpace(value.String())
|
||||
}
|
||||
if value, exist := slf.matrix.GetExist(ex, ey); !exist {
|
||||
skip = true
|
||||
} else {
|
||||
exportParam = strings.ToLower(strings.TrimSpace(value.String()))
|
||||
}
|
||||
if len(strings.TrimSpace(fieldName))+len(strings.TrimSpace(fieldType))+len(strings.TrimSpace(exportParam)) < 3 {
|
||||
skip = true
|
||||
}
|
||||
|
||||
var field = NewField(slf.Name, fieldName, fieldType)
|
||||
field.Describe = strings.ReplaceAll(describe, "\n", ", ")
|
||||
if strings.HasSuffix(field.Describe, ", ") {
|
||||
field.Describe = field.Describe[:len(field.Describe)-2]
|
||||
}
|
||||
field.ExportParam = exportParam
|
||||
switch field.ExportParam {
|
||||
case "s", "sc", "cs":
|
||||
field.Server = true
|
||||
}
|
||||
|
||||
if horizontal {
|
||||
dx++
|
||||
nx++
|
||||
tx++
|
||||
ex++
|
||||
} else {
|
||||
dy++
|
||||
ny++
|
||||
ty++
|
||||
ey++
|
||||
}
|
||||
|
||||
if !skip {
|
||||
|
||||
field.Ignore = slf.excludeFields[len(slf.Fields)]
|
||||
if !field.Ignore {
|
||||
if strings.HasPrefix(field.Describe, slf.ignore) {
|
||||
field.Ignore = true
|
||||
} else if strings.HasPrefix(field.Name, slf.ignore) {
|
||||
field.Ignore = true
|
||||
} else if strings.HasPrefix(field.Type, slf.ignore) {
|
||||
field.Ignore = true
|
||||
} else if strings.HasPrefix(field.ExportParam, slf.ignore) {
|
||||
field.Ignore = true
|
||||
}
|
||||
}
|
||||
if !field.Ignore {
|
||||
switch exportParam {
|
||||
case "s", "c", "sc", "cs":
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if fields[field.Name] && !field.Ignore {
|
||||
return ErrReadConfigFailedWithNameDuplicate
|
||||
}
|
||||
if index > 0 && !field.Ignore {
|
||||
if _, exist := basicTypeName[field.Type]; !exist {
|
||||
return ErrReadConfigFailedWithIndexTypeException
|
||||
}
|
||||
index--
|
||||
}
|
||||
|
||||
fields[field.Name] = true
|
||||
slf.Fields = append(slf.Fields, field)
|
||||
}
|
||||
|
||||
if horizontal {
|
||||
if dx >= slf.matrix.GetWidth() {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if dy >= slf.matrix.GetHeight() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return slf.initData()
|
||||
}
|
||||
|
||||
func (slf *Config) initData() error {
|
||||
var x, y int
|
||||
if slf.horizontal {
|
||||
y = slf.dataStart
|
||||
} else {
|
||||
x = 4
|
||||
y = 4
|
||||
}
|
||||
var dataSourceServer = make(map[any]any)
|
||||
var dataSourceClient = make(map[any]any)
|
||||
for {
|
||||
var dataServer = dataSourceServer
|
||||
var dataClient = dataSourceClient
|
||||
var currentIndex = 0
|
||||
|
||||
var lineServer = map[any]any{}
|
||||
var lineClient = map[any]any{}
|
||||
var skip bool
|
||||
var offset int
|
||||
var stop bool
|
||||
for i := 0; i < len(slf.Fields); i++ {
|
||||
if slf.excludeFields[i] {
|
||||
if c := slf.matrix.Get(x+i, y); c != nil && strings.HasPrefix(c.String(), "#") {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
field := slf.Fields[i]
|
||||
if field.Ignore {
|
||||
continue
|
||||
}
|
||||
var value any
|
||||
var zero bool
|
||||
if slf.horizontal {
|
||||
c := slf.matrix.Get(x+i, y)
|
||||
if c == nil {
|
||||
if currentIndex < slf.IndexCount {
|
||||
stop = true
|
||||
break
|
||||
}
|
||||
value = getValueZero(field.SourceType)
|
||||
zero = true
|
||||
} else if currentIndex < slf.IndexCount && len(strings.TrimSpace(c.String())) == 0 {
|
||||
stop = true
|
||||
break
|
||||
}
|
||||
if !zero {
|
||||
value = getValueWithType(field.SourceType, c.String())
|
||||
}
|
||||
} else {
|
||||
c := slf.matrix.Get(x, y+i+offset)
|
||||
for c == nil {
|
||||
offset++
|
||||
c = slf.matrix.Get(x, y+i+offset)
|
||||
if y+i+offset >= slf.matrix.GetHeight() {
|
||||
break
|
||||
}
|
||||
}
|
||||
value = getValueWithType(field.SourceType, c.String())
|
||||
}
|
||||
switch field.ExportParam {
|
||||
case "s":
|
||||
lineServer[field.Name] = value
|
||||
case "c":
|
||||
lineClient[field.Name] = value
|
||||
case "sc", "cs":
|
||||
lineServer[field.Name] = value
|
||||
lineClient[field.Name] = value
|
||||
default:
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
|
||||
if currentIndex < slf.IndexCount {
|
||||
currentIndex++
|
||||
m, exist := dataServer[value]
|
||||
if !exist {
|
||||
if currentIndex == slf.IndexCount {
|
||||
dataServer[value] = lineServer
|
||||
} else {
|
||||
m = map[any]any{}
|
||||
dataServer[value] = m
|
||||
}
|
||||
}
|
||||
if currentIndex < slf.IndexCount {
|
||||
dataServer = m.(map[any]any)
|
||||
}
|
||||
|
||||
m, exist = dataClient[value]
|
||||
if !exist {
|
||||
if currentIndex == slf.IndexCount {
|
||||
dataClient[value] = lineClient
|
||||
} else {
|
||||
m = map[any]any{}
|
||||
dataClient[value] = m
|
||||
}
|
||||
}
|
||||
if currentIndex < slf.IndexCount {
|
||||
dataClient = m.(map[any]any)
|
||||
}
|
||||
}
|
||||
}
|
||||
if stop {
|
||||
break
|
||||
} else if slf.horizontal {
|
||||
y++
|
||||
if y >= slf.matrix.GetHeight() {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if !skip {
|
||||
slf.dataServer = lineServer
|
||||
slf.dataClient = lineClient
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if slf.horizontal {
|
||||
slf.dataServer = dataSourceServer
|
||||
slf.dataClient = dataSourceClient
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (slf *Config) String() string {
|
||||
tmpl, err := template.New("struct").Parse(generateConfigTemplate)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, slf); err != nil {
|
||||
return ""
|
||||
}
|
||||
var result string
|
||||
_ = str.RangeLine(buf.String(), func(index int, line string) error {
|
||||
if len(strings.TrimSpace(line)) == 0 {
|
||||
return nil
|
||||
}
|
||||
result += fmt.Sprintf("%s\n", strings.ReplaceAll(line, "\t\t", "\t"))
|
||||
if len(strings.TrimSpace(line)) == 1 {
|
||||
result += "\n"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (slf *Config) JsonServer() []byte {
|
||||
buffer := &bytes.Buffer{}
|
||||
encoder := jsonIter.NewEncoder(buffer)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", " ")
|
||||
_ = encoder.Encode(slf.dataServer)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func (slf *Config) JsonClient() []byte {
|
||||
buffer := &bytes.Buffer{}
|
||||
encoder := jsonIter.NewEncoder(buffer)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", " ")
|
||||
_ = encoder.Encode(slf.dataClient)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func (slf *Config) GetVariable() string {
|
||||
var result string
|
||||
var count int
|
||||
if slf.IndexCount > 0 {
|
||||
for _, field := range slf.Fields {
|
||||
if field.Ignore {
|
||||
continue
|
||||
}
|
||||
result += fmt.Sprintf("map[%s]", field.Type)
|
||||
count++
|
||||
if count >= slf.IndexCount {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s*%sDefine", result, slf.Name)
|
||||
}
|
||||
|
||||
func (slf *Config) GetVariableGen() string {
|
||||
if slf.IndexCount == 0 {
|
||||
return fmt.Sprintf("new(%s)", strings.TrimPrefix(slf.GetVariable(), "*"))
|
||||
}
|
||||
return fmt.Sprintf("make(%s)", slf.GetVariable())
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package internal
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrReadConfigFailedIgnore = errors.New("read config skip ignore")
|
||||
ErrReadConfigFailedSame = errors.New("read config skip, same name")
|
||||
ErrReadConfigFailedWithDisplayName = errors.New("read config display name failed, can not found position")
|
||||
ErrReadConfigFailedWithName = errors.New("read config name failed, can not found position")
|
||||
ErrReadConfigFailedWithIndexCount = errors.New("read config index count failed, can not found position")
|
||||
ErrReadConfigFailedWithIndexCountLessThanZero = errors.New("read config index count failed, value less than zero")
|
||||
ErrReadConfigFailedWithFieldPosition = errors.New("read config index count failed, field position exception")
|
||||
ErrReadConfigFailedWithNameDuplicate = errors.New("read config index count failed, duplicate field names")
|
||||
ErrReadConfigFailedWithExportParamException = errors.New("read config index count failed, export param must is s or c or sc or cs")
|
||||
ErrReadConfigFailedWithIndexTypeException = errors.New("read config index count failed, the index type is only allowed to be the basic type")
|
||||
)
|
|
@ -1,146 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"html/template"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewField(configName, fieldName, fieldType string) *Field {
|
||||
sourceType := fieldType
|
||||
var handleStruct = func(configName, fieldType string) []*Field {
|
||||
var fs []*Field
|
||||
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
||||
var fields []string
|
||||
var field string
|
||||
var leftBrackets []int
|
||||
for i, c := range s {
|
||||
switch c {
|
||||
case ',':
|
||||
if len(leftBrackets) == 0 {
|
||||
fields = append(fields, field)
|
||||
field = ""
|
||||
} else {
|
||||
field += string(c)
|
||||
}
|
||||
case '{':
|
||||
leftBrackets = append(leftBrackets, i)
|
||||
field += string(c)
|
||||
case '}':
|
||||
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
||||
field += string(c)
|
||||
if len(leftBrackets) == 0 {
|
||||
fields = append(fields, field)
|
||||
field = ""
|
||||
}
|
||||
default:
|
||||
field += string(c)
|
||||
}
|
||||
}
|
||||
if len(field) > 0 {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
for _, fieldInfo := range fields {
|
||||
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
||||
fs = append(fs, NewField(configName, str.FirstUpper(fieldName), fieldType))
|
||||
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
var fs []*Field
|
||||
var sliceField *Field
|
||||
if t, exist := basicTypeName[fieldType]; exist {
|
||||
fieldType = t
|
||||
goto end
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "[]") {
|
||||
fieldType = strings.TrimPrefix(fieldType, "[]")
|
||||
if t, exist := basicTypeName[fieldType]; exist {
|
||||
fieldType = fmt.Sprintf("map[int]%s", t)
|
||||
} else {
|
||||
sliceField = NewField(configName, fieldName, fieldType)
|
||||
fieldType = fmt.Sprintf("map[int]*%s", configName+fieldName)
|
||||
}
|
||||
goto end
|
||||
}
|
||||
|
||||
fs = handleStruct(configName+fieldName, fieldType)
|
||||
fieldType = fmt.Sprintf("*%s", configName+fieldName)
|
||||
|
||||
end:
|
||||
{
|
||||
return &Field{
|
||||
Name: fieldName,
|
||||
Type: fieldType,
|
||||
SourceType: sourceType,
|
||||
TypeNotStar: strings.TrimPrefix(fieldType, "*"),
|
||||
Fields: fs,
|
||||
SliceField: sliceField,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Type string
|
||||
SourceType string
|
||||
TypeNotStar string
|
||||
Fields []*Field
|
||||
SliceField *Field
|
||||
ExportParam string
|
||||
Server bool
|
||||
Describe string
|
||||
Ignore bool
|
||||
}
|
||||
|
||||
func (slf *Field) Struct() string {
|
||||
if len(slf.Fields) == 0 && slf.SliceField == nil {
|
||||
return ""
|
||||
}
|
||||
tmpl, err := template.New("struct").Parse(generateGoStructTemplate)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if len(slf.Fields) > 0 {
|
||||
if err = tmpl.Execute(&buf, slf); err != nil {
|
||||
return ""
|
||||
}
|
||||
} else if slf.SliceField != nil {
|
||||
if err = tmpl.Execute(&buf, slf.SliceField); err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
s := buf.String()
|
||||
return s
|
||||
}
|
||||
|
||||
func (slf *Field) String() string {
|
||||
if len(slf.Fields) == 0 && slf.SliceField == nil {
|
||||
return ""
|
||||
}
|
||||
tmpl, err := template.New("struct").Parse(generateGoStructTemplate)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if len(slf.Fields) > 0 {
|
||||
if err = tmpl.Execute(&buf, slf); err != nil {
|
||||
return ""
|
||||
}
|
||||
} else if slf.SliceField != nil {
|
||||
if err = tmpl.Execute(&buf, slf.SliceField); err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
s := buf.String()
|
||||
for _, field := range slf.Fields {
|
||||
s += fmt.Sprintf("%s", field.String())
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/tidwall/gjson"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var basicTypeName = map[string]string{
|
||||
"string": "string",
|
||||
"int": "int",
|
||||
"int8": "int8",
|
||||
"int16": "int16",
|
||||
"int32": "int32",
|
||||
"int64": "int64",
|
||||
"uint": "uint",
|
||||
"uint8": "uint8",
|
||||
"uint16": "uint16",
|
||||
"uint32": "uint32",
|
||||
"uint64": "uint64",
|
||||
"float32": "float32",
|
||||
"float64": "float64",
|
||||
"float": "float64",
|
||||
"double": "float64",
|
||||
"number": "float64",
|
||||
"byte": "byte",
|
||||
"rune": "rune",
|
||||
"bool": "bool",
|
||||
"boolean": "bool",
|
||||
}
|
||||
|
||||
var basicType = map[string]func(fieldValue string) any{
|
||||
"string": withStringType,
|
||||
"int": withIntType,
|
||||
"int8": withInt8Type,
|
||||
"int16": withInt16Type,
|
||||
"int32": withInt32Type,
|
||||
"int64": withInt64Type,
|
||||
"uint": withUintType,
|
||||
"uint8": withUint8Type,
|
||||
"uint16": withUint16Type,
|
||||
"uint32": withUint32Type,
|
||||
"uint64": withUint64Type,
|
||||
"float32": withFloat32Type,
|
||||
"float64": withFloat64Type,
|
||||
"float": withFloat64Type,
|
||||
"double": withFloat64Type,
|
||||
"number": withFloat64Type,
|
||||
"byte": withByteType,
|
||||
"rune": withRuneType,
|
||||
"bool": withBoolType,
|
||||
"boolean": withBoolType,
|
||||
}
|
||||
|
||||
func getValueZero(fileType string) any {
|
||||
switch basicTypeName[fileType] {
|
||||
case "string":
|
||||
return getValueWithType(fileType, "")
|
||||
case "int":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "int8":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "int16":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "int32":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "int64":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "uint":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "uint8":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "uint16":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "uint32":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "uint64":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "float32":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "float64":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "byte":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "rune":
|
||||
return getValueWithType(fileType, "0")
|
||||
case "bool":
|
||||
return getValueWithType(fileType, "false")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getValueWithType(fieldType string, fieldValue string) any {
|
||||
fieldType = strings.ToLower(strings.TrimSpace(fieldType))
|
||||
handle, exist := basicType[fieldType]
|
||||
if exist {
|
||||
return handle(fieldValue)
|
||||
} else {
|
||||
return withStructType(fieldType, fieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
func withStructType(fieldType string, fieldValue string) any {
|
||||
// {id:int,name:string,info:{lv:int,exp:int}}
|
||||
if strings.HasPrefix(fieldType, "[]") {
|
||||
return withSliceType(fieldType, fieldValue)
|
||||
} else if !strings.HasPrefix(fieldType, "{") || !strings.HasSuffix(fieldType, "}") {
|
||||
return nil
|
||||
}
|
||||
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
||||
var data = map[any]any{}
|
||||
var fields []string
|
||||
var field string
|
||||
var leftBrackets []int
|
||||
for i, c := range s {
|
||||
switch c {
|
||||
case ',':
|
||||
if len(leftBrackets) == 0 {
|
||||
fields = append(fields, field)
|
||||
field = ""
|
||||
} else {
|
||||
field += string(c)
|
||||
}
|
||||
case '{':
|
||||
leftBrackets = append(leftBrackets, i)
|
||||
field += string(c)
|
||||
case '}':
|
||||
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
||||
field += string(c)
|
||||
if len(leftBrackets) == 0 {
|
||||
fields = append(fields, field)
|
||||
field = ""
|
||||
}
|
||||
default:
|
||||
field += string(c)
|
||||
}
|
||||
}
|
||||
if len(field) > 0 {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
for _, fieldInfo := range fields {
|
||||
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
||||
handle, exist := basicType[fieldType]
|
||||
if exist {
|
||||
data[fieldName] = handle(gjson.Get(fieldValue, fieldName).String())
|
||||
} else {
|
||||
data[fieldName] = withStructType(fieldType, gjson.Get(fieldValue, fieldName).String())
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func withSliceType(fieldType string, fieldValue string) any {
|
||||
if !strings.HasPrefix(fieldType, "[]") {
|
||||
return nil
|
||||
}
|
||||
t := strings.TrimPrefix(fieldType, "[]")
|
||||
var data = map[any]any{}
|
||||
gjson.ForEachLine(fieldValue, func(line gjson.Result) bool {
|
||||
line.ForEach(func(key, value gjson.Result) bool {
|
||||
handle, exist := basicType[t]
|
||||
if exist {
|
||||
data[len(data)] = handle(value.String())
|
||||
} else {
|
||||
data[len(data)] = withStructType(t, value.String())
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
func withStringType(fieldValue string) any {
|
||||
return fieldValue
|
||||
}
|
||||
|
||||
func withIntType(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
return value
|
||||
}
|
||||
|
||||
func withInt8Type(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return int8(0)
|
||||
} else if value > math.MaxInt8 {
|
||||
return int8(math.MaxInt8)
|
||||
}
|
||||
return int8(value)
|
||||
}
|
||||
|
||||
func withInt16Type(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return int16(0)
|
||||
} else if value > math.MaxInt16 {
|
||||
return int16(math.MaxInt16)
|
||||
}
|
||||
return int16(value)
|
||||
}
|
||||
|
||||
func withInt32Type(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return int32(0)
|
||||
} else if value > math.MaxInt32 {
|
||||
return int32(math.MaxInt32)
|
||||
}
|
||||
return int32(value)
|
||||
}
|
||||
|
||||
func withInt64Type(fieldValue string) any {
|
||||
value, _ := strconv.ParseInt(fieldValue, 10, 64)
|
||||
return value
|
||||
}
|
||||
|
||||
func withUintType(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return uint(0)
|
||||
}
|
||||
return uint(value)
|
||||
}
|
||||
|
||||
func withUint8Type(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return uint8(0)
|
||||
} else if value > math.MaxUint8 {
|
||||
return uint8(math.MaxUint8)
|
||||
}
|
||||
return uint8(value)
|
||||
}
|
||||
|
||||
func withUint16Type(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return uint16(0)
|
||||
} else if value > math.MaxUint16 {
|
||||
return uint16(math.MaxUint16)
|
||||
}
|
||||
return uint16(value)
|
||||
}
|
||||
|
||||
func withUint32Type(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return uint32(0)
|
||||
} else if value > math.MaxUint32 {
|
||||
return uint32(math.MaxUint32)
|
||||
}
|
||||
return uint32(value)
|
||||
}
|
||||
|
||||
func withUint64Type(fieldValue string) any {
|
||||
value, _ := strconv.ParseInt(fieldValue, 10, 64)
|
||||
return uint64(value)
|
||||
}
|
||||
|
||||
func withFloat32Type(fieldValue string) any {
|
||||
value, _ := strconv.ParseFloat(fieldValue, 32)
|
||||
return value
|
||||
}
|
||||
|
||||
func withFloat64Type(fieldValue string) any {
|
||||
value, _ := strconv.ParseFloat(fieldValue, 64)
|
||||
return value
|
||||
}
|
||||
|
||||
func withByteType(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < 0 {
|
||||
return byte(0)
|
||||
} else if value > math.MaxUint8 {
|
||||
return byte(math.MaxInt8)
|
||||
}
|
||||
return byte(value)
|
||||
}
|
||||
|
||||
func withRuneType(fieldValue string) any {
|
||||
value, _ := strconv.Atoi(fieldValue)
|
||||
if value < math.MinInt32 {
|
||||
return rune(0)
|
||||
} else if value > math.MaxInt32 {
|
||||
return rune(math.MaxInt32)
|
||||
}
|
||||
return rune(value)
|
||||
}
|
||||
|
||||
func withBoolType(fieldValue string) any {
|
||||
switch fieldValue {
|
||||
case "0", "false", "!":
|
||||
return false
|
||||
case "1", "true", "&":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package internal
|
||||
|
||||
func NewPosition(x, y int) *Position {
|
||||
return &Position{
|
||||
X: x,
|
||||
Y: y,
|
||||
}
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
X int
|
||||
Y int
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package internal
|
||||
|
||||
const (
|
||||
generateConfigTemplate = `
|
||||
|
||||
// {{.Name}}Define {{.DisplayName}}
|
||||
type {{.Name}}Define struct {
|
||||
{{range $index, $value := .Fields}}{{if eq $value.Server true}}{{if eq $value.Ignore false}}{{$value.Name}} {{$value.Type}} // {{$value.Describe}}{{end}}{{end}}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
func (slf *{{.Name}}Define) String() string {
|
||||
if data, err := json.Marshal(slf); err == nil {
|
||||
return string(data)
|
||||
}
|
||||
return "{}"
|
||||
}
|
||||
|
||||
{{range $index, $value := .Fields}}{{$value}}{{end}}
|
||||
`
|
||||
|
||||
generateGoStructTemplate = `
|
||||
{{if eq .Ignore false}}
|
||||
type {{.TypeNotStar}} struct {
|
||||
{{range $index, $value := .Fields}}
|
||||
{{$value.Name}} {{$value.Type}}
|
||||
{{end}}
|
||||
}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
GenerateGoConfigTemplate = `// Code generated by minotaur-config-export. DO NOT EDIT.
|
||||
|
||||
package {{.Package}}
|
||||
|
||||
import (
|
||||
jsonIter "github.com/json-iterator/go"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
)
|
||||
|
||||
var json = jsonIter.ConfigCompatibleWithStandardLibrary
|
||||
var full map[string]any
|
||||
|
||||
var (
|
||||
{{range $index, $config := .Configs}}
|
||||
// {{$config.Name}}Sign {{$config.DisplayName}}签名
|
||||
{{$config.Name}}Sign = "{{$config.Name}}"
|
||||
// {{$config.Name}} {{$config.DisplayName}}
|
||||
{{$config.Name}} {{$config.GetVariable}}
|
||||
_{{$config.Name}} {{$config.GetVariable}}
|
||||
{{end}}
|
||||
)
|
||||
|
||||
func LoadConfig(handle func(filename string, config any) error) {
|
||||
var err error
|
||||
{{range $index, $config := .Configs}}
|
||||
_{{$config.Name}} = {{$config.GetVariableGen}}
|
||||
{{if eq $config.IndexCount 0}}
|
||||
if err = handle("{{$config.Prefix}}{{$config.Name}}.json", _{{$config.Name}}); err != nil {
|
||||
log.Error("Config", zap.String("Name", "{{$config.Name}}"), zap.Bool("Invalid", true), zap.Error(err))
|
||||
}
|
||||
{{else}}
|
||||
if err = handle("{{$config.Prefix}}{{$config.Name}}.json", &_{{$config.Name}}); err != nil {
|
||||
log.Error("Config", zap.String("Name", "{{$config.Name}}"), zap.Bool("Invalid", true), zap.Error(err))
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
// Refresh 将加载后的配置刷新到线上
|
||||
func Refresh() {
|
||||
full = make(map[string]any)
|
||||
{{range $index, $config := .Configs}}
|
||||
{{$config.Name}} = _{{$config.Name}}
|
||||
full["{{$config.Name}}"] = {{$config.Name}}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
// DefaultLoad 默认提供的配置加载函数
|
||||
func DefaultLoad(filepath string) {
|
||||
LoadConfig(func(filename string, config any) error {
|
||||
bytes, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(bytes, &config)
|
||||
})
|
||||
}
|
||||
|
||||
// GetFull 获取所有配置的 map 集合
|
||||
// - 通常用于前端配置通过后端接口获取的情况
|
||||
func GetFull() map[string]any {
|
||||
return full
|
||||
}
|
||||
`
|
||||
|
||||
GenerateGoDefineTemplate = `// Code generated by minotaur-config-export. DO NOT EDIT.
|
||||
|
||||
package {{.Package}}
|
||||
|
||||
{{range $index, $config := .Configs}}
|
||||
{{$config.String}}
|
||||
{{end}}
|
||||
|
||||
`
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
package pce
|
||||
|
||||
// Config 配置解析接口
|
||||
// - 用于将配置文件解析为可供分析的数据结构
|
||||
// - 可以在 cs 包中找到内置提供的实现及其模板,例如 cs.XlsxIndexConfig
|
||||
type Config interface {
|
||||
// GetConfigName 配置名称
|
||||
GetConfigName() string
|
||||
// GetDisplayName 配置显示名称
|
||||
GetDisplayName() string
|
||||
// GetDescription 配置描述
|
||||
GetDescription() string
|
||||
// GetIndexCount 索引数量
|
||||
GetIndexCount() int
|
||||
// GetFields 获取字段
|
||||
GetFields() []DataField
|
||||
// GetData 获取数据
|
||||
GetData() [][]DataInfo
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
package cs
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/planner/pce"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/tealeg/xlsx"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type XlsxExportType int
|
||||
|
||||
const (
|
||||
XlsxExportTypeServer XlsxExportType = iota
|
||||
XlsxExportTypeClient
|
||||
)
|
||||
|
||||
func NewXlsx(sheet *xlsx.Sheet, exportType XlsxExportType) *Xlsx {
|
||||
config := &Xlsx{
|
||||
sheet: sheet,
|
||||
exportType: exportType,
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// Xlsx 内置的 Xlsx 配置
|
||||
type Xlsx struct {
|
||||
sheet *xlsx.Sheet
|
||||
exportType XlsxExportType
|
||||
}
|
||||
|
||||
func (slf *Xlsx) GetConfigName() string {
|
||||
return str.FirstUpper(strings.TrimSpace(slf.sheet.Rows[0].Cells[1].String()))
|
||||
}
|
||||
|
||||
func (slf *Xlsx) GetDisplayName() string {
|
||||
return slf.sheet.Name
|
||||
}
|
||||
|
||||
func (slf *Xlsx) GetDescription() string {
|
||||
return slf.GetDisplayName()
|
||||
}
|
||||
|
||||
func (slf *Xlsx) GetIndexCount() int {
|
||||
index, err := slf.sheet.Rows[1].Cells[1].Int()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
func (slf *Xlsx) GetFields() []pce.DataField {
|
||||
var handle = func(index int, desc, name, fieldType, exportType *xlsx.Cell) (pce.DataField, bool) {
|
||||
var field pce.DataField
|
||||
if desc == nil || name == nil || fieldType == nil || exportType == nil {
|
||||
return field, false
|
||||
}
|
||||
field = pce.DataField{
|
||||
Index: index,
|
||||
Name: strings.ReplaceAll(strings.ReplaceAll(str.FirstUpper(name.String()), "\r", " "), "\n", " "),
|
||||
Type: fieldType.String(),
|
||||
ExportType: exportType.String(),
|
||||
Desc: strings.ReplaceAll(strings.ReplaceAll(desc.String(), "\r", " "), "\n", " "),
|
||||
}
|
||||
if len(field.Name) == 0 || len(field.Type) == 0 || len(field.ExportType) == 0 {
|
||||
return field, false
|
||||
}
|
||||
|
||||
if slf.checkFieldInvalid(field) {
|
||||
return field, false
|
||||
}
|
||||
|
||||
return field, true
|
||||
}
|
||||
var fields []pce.DataField
|
||||
if slf.GetIndexCount() > 0 {
|
||||
for x := 1; x < slf.getWidth(); x++ {
|
||||
if field, match := handle(x, slf.get(x, 3), slf.get(x, 4), slf.get(x, 5), slf.get(x, 6)); match {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for y := 4; y < slf.getHeight(); y++ {
|
||||
if field, match := handle(y, slf.get(0, y), slf.get(1, y), slf.get(2, y), slf.get(3, y)); match {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func (slf *Xlsx) GetData() [][]pce.DataInfo {
|
||||
var data [][]pce.DataInfo
|
||||
var fields = slf.GetFields()
|
||||
if slf.GetIndexCount() > 0 {
|
||||
for y := 7; y < slf.getHeight(); y++ {
|
||||
var line []pce.DataInfo
|
||||
var stop bool
|
||||
|
||||
if prefixCell := slf.get(0, y); prefixCell != nil {
|
||||
prefix := prefixCell.String()
|
||||
if strings.HasPrefix(prefix, "#") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for i, field := range slf.GetFields() {
|
||||
var isIndex = i-1 < slf.GetIndexCount()
|
||||
|
||||
var value string
|
||||
if valueCell := slf.get(field.Index, y); valueCell != nil {
|
||||
value = valueCell.String()
|
||||
} else if isIndex {
|
||||
stop = true
|
||||
break
|
||||
}
|
||||
valueCell := slf.get(field.Index, y)
|
||||
if valueCell == nil {
|
||||
break
|
||||
}
|
||||
if len(fields) > i-1 {
|
||||
line = append(line, pce.DataInfo{
|
||||
DataField: field,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(line) > 0 {
|
||||
data = append(data, line)
|
||||
}
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var line []pce.DataInfo
|
||||
for i, field := range slf.GetFields() {
|
||||
var value string
|
||||
if valueCell := slf.get(4, 4+i); valueCell != nil {
|
||||
value = valueCell.String()
|
||||
}
|
||||
line = append(line, pce.DataInfo{
|
||||
DataField: field,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
data = append(data, line)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// getWidth 获取宽度
|
||||
func (slf *Xlsx) getWidth() int {
|
||||
return slf.sheet.MaxCol
|
||||
}
|
||||
|
||||
// getHeight 获取高度
|
||||
func (slf *Xlsx) getHeight() int {
|
||||
return slf.sheet.MaxRow
|
||||
}
|
||||
|
||||
// get 获取单元格
|
||||
func (slf *Xlsx) get(x, y int) *xlsx.Cell {
|
||||
if x < 0 || y < 0 || y >= len(slf.sheet.Rows) {
|
||||
return nil
|
||||
}
|
||||
row := slf.sheet.Rows[y]
|
||||
if x >= len(row.Cells) {
|
||||
return nil
|
||||
}
|
||||
return row.Cells[x]
|
||||
}
|
||||
|
||||
func (slf *Xlsx) checkFieldInvalid(field pce.DataField) bool {
|
||||
switch strings.ToLower(field.ExportType) {
|
||||
case "s":
|
||||
if slf.exportType != XlsxExportTypeServer {
|
||||
return true
|
||||
}
|
||||
case "c":
|
||||
if slf.exportType != XlsxExportTypeClient {
|
||||
return true
|
||||
}
|
||||
case "sc", "cs":
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
pattern := "^[a-zA-Z][a-zA-Z0-9]*$"
|
||||
reg := regexp.MustCompile(pattern)
|
||||
if !reg.MatchString(field.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(field.Name, "#") || strings.HasPrefix(field.Type, "#") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,63 @@
|
|||
package cs_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/tealeg/xlsx"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewIndexXlsxConfig(t *testing.T) {
|
||||
Convey("TestNewIndexXlsxConfig", t, func() {
|
||||
f, err := xlsx.OpenFile("./xlsx_template.xlsx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config := cs.NewXlsx(f.Sheets[1])
|
||||
So(config, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestXlsxIndexConfig_GetConfigName(t *testing.T) {
|
||||
Convey("TestXlsxIndexConfig_GetConfigName", t, func() {
|
||||
f, err := xlsx.OpenFile("./xlsx_template.xlsx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config := cs.NewXlsx(f.Sheets[1])
|
||||
So(config.GetConfigName(), ShouldEqual, "IndexConfig")
|
||||
})
|
||||
}
|
||||
|
||||
func TestXlsxIndexConfig_GetDisplayName(t *testing.T) {
|
||||
Convey("TestXlsxIndexConfig_GetDisplayName", t, func() {
|
||||
f, err := xlsx.OpenFile("./xlsx_template.xlsx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config := cs.NewXlsx(f.Sheets[1])
|
||||
So(config.GetDisplayName(), ShouldEqual, "有索引")
|
||||
})
|
||||
}
|
||||
|
||||
func TestXlsxIndexConfig_GetDescription(t *testing.T) {
|
||||
Convey("TestXlsxIndexConfig_GetDescription", t, func() {
|
||||
f, err := xlsx.OpenFile("./xlsx_template.xlsx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config := cs.NewXlsx(f.Sheets[1])
|
||||
So(config.GetDescription(), ShouldEqual, "暂无描述")
|
||||
})
|
||||
}
|
||||
|
||||
func TestXlsxIndexConfig_GetIndexCount(t *testing.T) {
|
||||
Convey("TestXlsxIndexConfig_GetIndexCount", t, func() {
|
||||
f, err := xlsx.OpenFile("./xlsx_template.xlsx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config := cs.NewXlsx(f.Sheets[1])
|
||||
So(config.GetIndexCount(), ShouldEqual, 2)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package pce
|
||||
|
||||
// DataTmpl 数据导出模板
|
||||
type DataTmpl interface {
|
||||
// Render 渲染模板
|
||||
Render(data map[any]any) (string, error)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package pce
|
||||
|
||||
// NewExporter 创建导出器
|
||||
func NewExporter() *Exporter {
|
||||
return &Exporter{}
|
||||
}
|
||||
|
||||
// Exporter 导出器
|
||||
type Exporter struct{}
|
||||
|
||||
// ExportStruct 导出结构
|
||||
func (slf *Exporter) ExportStruct(tmpl Tmpl, tmplStruct ...*TmplStruct) ([]byte, error) {
|
||||
raw, err := tmpl.Render(tmplStruct...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(raw), nil
|
||||
}
|
||||
|
||||
// ExportData 导出数据
|
||||
func (slf *Exporter) ExportData(tmpl DataTmpl, data map[any]any) ([]byte, error) {
|
||||
raw, err := tmpl.Render(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(raw), nil
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package pce
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Field 基本字段类型接口
|
||||
type Field interface {
|
||||
// TypeName 字段类型名称
|
||||
TypeName() string
|
||||
// Zero 获取零值
|
||||
Zero() any
|
||||
// Parse 解析
|
||||
Parse(value string) any
|
||||
}
|
||||
|
||||
// GetFieldGolangType 获取字段的 Golang 类型
|
||||
func GetFieldGolangType(field Field) string {
|
||||
typeOf := reflect.TypeOf(field).Elem()
|
||||
kind := strings.ToLower(typeOf.Kind().String())
|
||||
return kind
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package pce_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/planner/pce"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetFieldGolangType(t *testing.T) {
|
||||
fmt.Println(pce.GetFieldGolangType(new(pce.String)))
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
package pce
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var fields = []Field{
|
||||
new(Int),
|
||||
new(Int8),
|
||||
new(Int16),
|
||||
new(Int32),
|
||||
new(Int64),
|
||||
new(Uint),
|
||||
new(Uint8),
|
||||
new(Uint16),
|
||||
new(Uint32),
|
||||
new(Uint64),
|
||||
new(Float32),
|
||||
new(Float64),
|
||||
new(String),
|
||||
new(Bool),
|
||||
new(Byte),
|
||||
new(Rune),
|
||||
new(Complex64),
|
||||
new(Complex128),
|
||||
new(Uintptr),
|
||||
new(Double),
|
||||
new(Float),
|
||||
new(Long),
|
||||
new(Short),
|
||||
new(Char),
|
||||
new(Number),
|
||||
new(Integer),
|
||||
new(Boolean),
|
||||
}
|
||||
|
||||
// GetFields 获取所有内置支持的字段
|
||||
func GetFields() []Field {
|
||||
return fields
|
||||
}
|
||||
|
||||
type Int int
|
||||
|
||||
func (slf Int) TypeName() string {
|
||||
return "int"
|
||||
}
|
||||
|
||||
func (slf Int) Zero() any {
|
||||
return int(0)
|
||||
}
|
||||
|
||||
func (slf Int) Parse(value string) any {
|
||||
return super.StringToInt(value)
|
||||
}
|
||||
|
||||
type Int8 int8
|
||||
|
||||
func (slf Int8) TypeName() string {
|
||||
return "int8"
|
||||
}
|
||||
|
||||
func (slf Int8) Zero() any {
|
||||
return int8(0)
|
||||
}
|
||||
|
||||
func (slf Int8) Parse(value string) any {
|
||||
v := super.StringToInt(value)
|
||||
if v < 0 {
|
||||
return int8(0)
|
||||
} else if v > math.MaxInt8 {
|
||||
return int8(math.MaxInt8)
|
||||
}
|
||||
return int8(v)
|
||||
}
|
||||
|
||||
type Int16 int16
|
||||
|
||||
func (slf Int16) TypeName() string {
|
||||
return "int16"
|
||||
}
|
||||
|
||||
func (slf Int16) Zero() any {
|
||||
return int16(0)
|
||||
}
|
||||
|
||||
func (slf Int16) Parse(value string) any {
|
||||
v := super.StringToInt(value)
|
||||
if v < 0 {
|
||||
return int16(0)
|
||||
} else if v > math.MaxInt16 {
|
||||
return int16(math.MaxInt16)
|
||||
}
|
||||
return int16(v)
|
||||
}
|
||||
|
||||
type Int32 int32
|
||||
|
||||
func (slf Int32) TypeName() string {
|
||||
return "int32"
|
||||
}
|
||||
|
||||
func (slf Int32) Zero() any {
|
||||
return int32(0)
|
||||
}
|
||||
|
||||
func (slf Int32) Parse(value string) any {
|
||||
v := super.StringToInt(value)
|
||||
if v < 0 {
|
||||
return int32(0)
|
||||
} else if v > math.MaxInt32 {
|
||||
return int32(math.MaxInt32)
|
||||
}
|
||||
return int32(v)
|
||||
}
|
||||
|
||||
type Int64 int64
|
||||
|
||||
func (slf Int64) TypeName() string {
|
||||
return "int64"
|
||||
}
|
||||
|
||||
func (slf Int64) Zero() any {
|
||||
return int64(0)
|
||||
}
|
||||
|
||||
func (slf Int64) Parse(value string) any {
|
||||
v, _ := strconv.ParseInt(value, 10, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type Uint uint
|
||||
|
||||
func (slf Uint) TypeName() string {
|
||||
return "uint"
|
||||
}
|
||||
|
||||
func (slf Uint) Zero() any {
|
||||
return uint(0)
|
||||
}
|
||||
|
||||
func (slf Uint) Parse(value string) any {
|
||||
v, _ := strconv.Atoi(value)
|
||||
if v < 0 {
|
||||
return uint(0)
|
||||
}
|
||||
return uint(v)
|
||||
}
|
||||
|
||||
type Uint8 uint8
|
||||
|
||||
func (slf Uint8) TypeName() string {
|
||||
return "uint8"
|
||||
}
|
||||
|
||||
func (slf Uint8) Zero() any {
|
||||
return uint8(0)
|
||||
}
|
||||
|
||||
func (slf Uint8) Parse(value string) any {
|
||||
v, _ := strconv.Atoi(value)
|
||||
if v < 0 {
|
||||
return uint8(0)
|
||||
} else if v > math.MaxUint8 {
|
||||
return uint8(math.MaxUint8)
|
||||
}
|
||||
return uint8(v)
|
||||
}
|
||||
|
||||
type Uint16 uint16
|
||||
|
||||
func (slf Uint16) TypeName() string {
|
||||
return "uint16"
|
||||
}
|
||||
|
||||
func (slf Uint16) Zero() any {
|
||||
return uint16(0)
|
||||
}
|
||||
|
||||
func (slf Uint16) Parse(value string) any {
|
||||
v, _ := strconv.Atoi(value)
|
||||
if v < 0 {
|
||||
return uint16(0)
|
||||
} else if v > math.MaxUint16 {
|
||||
return uint16(math.MaxUint16)
|
||||
}
|
||||
return uint16(v)
|
||||
}
|
||||
|
||||
type Uint32 uint32
|
||||
|
||||
func (slf Uint32) TypeName() string {
|
||||
return "uint32"
|
||||
}
|
||||
|
||||
func (slf Uint32) Zero() any {
|
||||
return uint32(0)
|
||||
}
|
||||
|
||||
func (slf Uint32) Parse(value string) any {
|
||||
v, _ := strconv.Atoi(value)
|
||||
if v < 0 {
|
||||
return uint32(0)
|
||||
} else if v > math.MaxUint32 {
|
||||
return uint32(math.MaxUint32)
|
||||
}
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
type Uint64 uint64
|
||||
|
||||
func (slf Uint64) TypeName() string {
|
||||
return "uint64"
|
||||
}
|
||||
|
||||
func (slf Uint64) Zero() any {
|
||||
return uint64(0)
|
||||
}
|
||||
|
||||
func (slf Uint64) Parse(value string) any {
|
||||
v, _ := strconv.ParseUint(value, 10, 64)
|
||||
if v < 0 {
|
||||
return uint64(0)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
type Float32 float32
|
||||
|
||||
func (slf Float32) TypeName() string {
|
||||
return "float32"
|
||||
}
|
||||
|
||||
func (slf Float32) Zero() any {
|
||||
return float32(0)
|
||||
}
|
||||
|
||||
func (slf Float32) Parse(value string) any {
|
||||
v, _ := strconv.ParseFloat(value, 32)
|
||||
return v
|
||||
}
|
||||
|
||||
type Float64 float64
|
||||
|
||||
func (slf Float64) TypeName() string {
|
||||
return "float64"
|
||||
}
|
||||
|
||||
func (slf Float64) Zero() any {
|
||||
return float64(0)
|
||||
}
|
||||
|
||||
func (slf Float64) Parse(value string) any {
|
||||
v, _ := strconv.ParseFloat(value, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type String string
|
||||
|
||||
func (slf String) TypeName() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func (slf String) Zero() any {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (slf String) Parse(value string) any {
|
||||
return value
|
||||
}
|
||||
|
||||
type Bool bool
|
||||
|
||||
func (slf Bool) TypeName() string {
|
||||
return "bool"
|
||||
}
|
||||
|
||||
func (slf Bool) Zero() any {
|
||||
return false
|
||||
}
|
||||
|
||||
func (slf Bool) Parse(value string) any {
|
||||
v, _ := strconv.ParseBool(value)
|
||||
return v
|
||||
}
|
||||
|
||||
type Byte byte
|
||||
|
||||
func (slf Byte) TypeName() string {
|
||||
return "byte"
|
||||
}
|
||||
|
||||
func (slf Byte) Zero() any {
|
||||
return byte(0)
|
||||
}
|
||||
|
||||
func (slf Byte) Parse(value string) any {
|
||||
v, _ := strconv.Atoi(value)
|
||||
if v < 0 {
|
||||
return byte(0)
|
||||
} else if v > math.MaxUint8 {
|
||||
return byte(math.MaxUint8)
|
||||
}
|
||||
return byte(v)
|
||||
}
|
||||
|
||||
type Rune rune
|
||||
|
||||
func (slf Rune) TypeName() string {
|
||||
return "rune"
|
||||
}
|
||||
|
||||
func (slf Rune) Zero() any {
|
||||
return rune(0)
|
||||
}
|
||||
|
||||
func (slf Rune) Parse(value string) any {
|
||||
v, _ := strconv.Atoi(value)
|
||||
if v < 0 {
|
||||
return rune(0)
|
||||
} else if v > math.MaxInt32 {
|
||||
return rune(math.MaxInt32)
|
||||
}
|
||||
return rune(v)
|
||||
}
|
||||
|
||||
type Complex64 complex64
|
||||
|
||||
func (slf Complex64) TypeName() string {
|
||||
return "complex64"
|
||||
}
|
||||
|
||||
func (slf Complex64) Zero() any {
|
||||
return complex64(0)
|
||||
}
|
||||
|
||||
func (slf Complex64) Parse(value string) any {
|
||||
v, _ := strconv.ParseComplex(value, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type Complex128 complex128
|
||||
|
||||
func (slf Complex128) TypeName() string {
|
||||
return "complex128"
|
||||
}
|
||||
|
||||
func (slf Complex128) Zero() any {
|
||||
return complex128(0)
|
||||
}
|
||||
|
||||
func (slf Complex128) Parse(value string) any {
|
||||
v, _ := strconv.ParseComplex(value, 128)
|
||||
return v
|
||||
}
|
||||
|
||||
type Uintptr uintptr
|
||||
|
||||
func (slf Uintptr) TypeName() string {
|
||||
return "uintptr"
|
||||
}
|
||||
|
||||
func (slf Uintptr) Zero() any {
|
||||
return uintptr(0)
|
||||
}
|
||||
|
||||
func (slf Uintptr) Parse(value string) any {
|
||||
v, _ := strconv.ParseUint(value, 10, 64)
|
||||
return uintptr(v)
|
||||
}
|
||||
|
||||
type Double float64
|
||||
|
||||
func (slf Double) TypeName() string {
|
||||
return "double"
|
||||
}
|
||||
|
||||
func (slf Double) Zero() any {
|
||||
return float64(0)
|
||||
}
|
||||
|
||||
func (slf Double) Parse(value string) any {
|
||||
v, _ := strconv.ParseFloat(value, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type Float float32
|
||||
|
||||
func (slf Float) TypeName() string {
|
||||
return "float"
|
||||
}
|
||||
|
||||
func (slf Float) Zero() any {
|
||||
return float32(0)
|
||||
}
|
||||
|
||||
func (slf Float) Parse(value string) any {
|
||||
v, _ := strconv.ParseFloat(value, 32)
|
||||
return v
|
||||
}
|
||||
|
||||
type Long int64
|
||||
|
||||
func (slf Long) TypeName() string {
|
||||
return "long"
|
||||
}
|
||||
|
||||
func (slf Long) Zero() any {
|
||||
return int64(0)
|
||||
}
|
||||
|
||||
func (slf Long) Parse(value string) any {
|
||||
v, _ := strconv.ParseInt(value, 10, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type Short int16
|
||||
|
||||
func (slf Short) TypeName() string {
|
||||
return "short"
|
||||
}
|
||||
|
||||
func (slf Short) Zero() any {
|
||||
return int16(0)
|
||||
}
|
||||
|
||||
func (slf Short) Parse(value string) any {
|
||||
v, _ := strconv.ParseInt(value, 10, 16)
|
||||
return v
|
||||
}
|
||||
|
||||
type Char int8
|
||||
|
||||
func (slf Char) TypeName() string {
|
||||
return "char"
|
||||
}
|
||||
|
||||
func (slf Char) Zero() any {
|
||||
return int8(0)
|
||||
}
|
||||
|
||||
func (slf Char) Parse(value string) any {
|
||||
v, _ := strconv.ParseInt(value, 10, 8)
|
||||
return v
|
||||
}
|
||||
|
||||
type Number float64
|
||||
|
||||
func (slf Number) TypeName() string {
|
||||
return "number"
|
||||
}
|
||||
|
||||
func (slf Number) Zero() any {
|
||||
return float64(0)
|
||||
}
|
||||
|
||||
func (slf Number) Parse(value string) any {
|
||||
v, _ := strconv.ParseFloat(value, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type Integer int64
|
||||
|
||||
func (slf Integer) TypeName() string {
|
||||
return "integer"
|
||||
}
|
||||
|
||||
func (slf Integer) Zero() any {
|
||||
return int64(0)
|
||||
}
|
||||
|
||||
func (slf Integer) Parse(value string) any {
|
||||
v, _ := strconv.ParseInt(value, 10, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
type Boolean bool
|
||||
|
||||
func (slf Boolean) TypeName() string {
|
||||
return "boolean"
|
||||
}
|
||||
|
||||
func (slf Boolean) Zero() any {
|
||||
return false
|
||||
}
|
||||
|
||||
func (slf Boolean) Parse(value string) any {
|
||||
v, _ := strconv.ParseBool(value)
|
||||
return v
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package pce
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/tidwall/gjson"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewLoader 创建加载器
|
||||
// - 加载器被用于加载配置表的数据和结构信息
|
||||
func NewLoader(fields []Field) *Loader {
|
||||
loader := &Loader{
|
||||
fields: make(map[string]Field),
|
||||
}
|
||||
for _, f := range fields {
|
||||
loader.fields[f.TypeName()] = f
|
||||
}
|
||||
return loader
|
||||
}
|
||||
|
||||
// Loader 配置加载器
|
||||
type Loader struct {
|
||||
fields map[string]Field
|
||||
}
|
||||
|
||||
// LoadStruct 加载结构
|
||||
func (slf *Loader) LoadStruct(config Config) *TmplStruct {
|
||||
var tmpl = &TmplStruct{
|
||||
Name: str.FirstUpper(config.GetConfigName()),
|
||||
Desc: config.GetDescription(),
|
||||
IndexCount: config.GetIndexCount(),
|
||||
}
|
||||
for i, field := range config.GetFields() {
|
||||
f := tmpl.addField(tmpl.Name, str.FirstUpper(field.Name), field.Desc, field.Type, slf.fields)
|
||||
if i < tmpl.IndexCount {
|
||||
f.isIndex = true
|
||||
}
|
||||
}
|
||||
return tmpl
|
||||
}
|
||||
|
||||
// LoadData 加载配置并得到配置数据
|
||||
func (slf *Loader) LoadData(config Config) map[any]any {
|
||||
var source = make(map[any]any)
|
||||
var action = source
|
||||
var indexCount = config.GetIndexCount() - 1
|
||||
for _, row := range config.GetData() {
|
||||
var item = make(map[any]any)
|
||||
for index, field := range row {
|
||||
bind, exist := slf.fields[field.Type]
|
||||
var value any
|
||||
if exist {
|
||||
if len(field.Value) == 0 {
|
||||
value = bind.Zero()
|
||||
} else {
|
||||
value = bind.Parse(field.Value)
|
||||
}
|
||||
} else {
|
||||
value = slf.structInterpreter(field.Type, field.Value)
|
||||
}
|
||||
|
||||
if value != nil {
|
||||
item[field.Name] = value
|
||||
}
|
||||
|
||||
if index < indexCount {
|
||||
m, exist := action[value]
|
||||
if !exist {
|
||||
m = map[any]any{}
|
||||
action[value] = m
|
||||
action = m.(map[any]any)
|
||||
} else {
|
||||
action = m.(map[any]any)
|
||||
}
|
||||
} else if index == indexCount {
|
||||
action[value] = item
|
||||
} else if indexCount == -1 {
|
||||
source = item
|
||||
}
|
||||
}
|
||||
action = source
|
||||
}
|
||||
|
||||
return source
|
||||
}
|
||||
|
||||
// sliceInterpreter 切片解释器
|
||||
func (slf *Loader) sliceInterpreter(fieldType, fieldValue string) any {
|
||||
if !strings.HasPrefix(fieldType, "[]") {
|
||||
return nil
|
||||
}
|
||||
t := strings.TrimPrefix(fieldType, "[]")
|
||||
var data = map[any]any{}
|
||||
gjson.ForEachLine(fieldValue, func(line gjson.Result) bool {
|
||||
line.ForEach(func(key, value gjson.Result) bool {
|
||||
field, exist := slf.fields[t]
|
||||
if exist {
|
||||
data[len(data)] = field.Parse(value.String())
|
||||
} else {
|
||||
data[len(data)] = slf.structInterpreter(t, value.String())
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// structInterpreter 结构体解释器
|
||||
// - {id:int,name:string,info:{lv:int,exp:int}}
|
||||
func (slf *Loader) structInterpreter(fieldType, fieldValue string) any {
|
||||
if strings.HasPrefix(fieldType, "[]") {
|
||||
return slf.sliceInterpreter(fieldType, fieldValue)
|
||||
} else if !strings.HasPrefix(fieldType, "{") || !strings.HasSuffix(fieldType, "}") {
|
||||
return nil
|
||||
}
|
||||
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
||||
var data = map[any]any{}
|
||||
var fields []string
|
||||
var field string
|
||||
var leftBrackets []int
|
||||
for i, c := range s {
|
||||
switch c {
|
||||
case ',':
|
||||
if len(leftBrackets) == 0 {
|
||||
fields = append(fields, field)
|
||||
field = ""
|
||||
} else {
|
||||
field += string(c)
|
||||
}
|
||||
case '{':
|
||||
leftBrackets = append(leftBrackets, i)
|
||||
field += string(c)
|
||||
case '}':
|
||||
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
||||
field += string(c)
|
||||
if len(leftBrackets) == 0 {
|
||||
fields = append(fields, field)
|
||||
field = ""
|
||||
}
|
||||
default:
|
||||
field += string(c)
|
||||
}
|
||||
}
|
||||
if len(field) > 0 {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
for _, fieldInfo := range fields {
|
||||
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
||||
field, exist := slf.fields[fieldType]
|
||||
if exist {
|
||||
data[fieldName] = field.Parse(gjson.Get(fieldValue, fieldName).String())
|
||||
} else {
|
||||
data[fieldName] = slf.structInterpreter(fieldType, gjson.Get(fieldValue, fieldName).String())
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// DataInfo 配置数据
|
||||
type DataInfo struct {
|
||||
DataField // 字段
|
||||
Value string // 字段值
|
||||
}
|
||||
|
||||
// DataField 配置数据字段
|
||||
type DataField struct {
|
||||
Index int // 字段索引
|
||||
Name string // 字段名称
|
||||
Desc string // 字段描述
|
||||
Type string // 字段类型
|
||||
ExportType string // 导出类型
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package pce
|
||||
|
||||
// Tmpl 配置结构模板接口
|
||||
type Tmpl interface {
|
||||
// Render 渲染模板
|
||||
Render(templates ...*TmplStruct) (string, error)
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package pce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TmplField 模板字段
|
||||
type TmplField struct {
|
||||
Name string // 字段名称
|
||||
Desc string // 字段描述
|
||||
Type string // 字段类型
|
||||
Struct *TmplStruct // 结构类型字段结构信息
|
||||
Index int // 字段索引
|
||||
slice bool // 是否是切片类型
|
||||
isIndex bool // 是否是索引字段
|
||||
}
|
||||
|
||||
// IsIndex 是否是索引字段
|
||||
func (slf *TmplField) IsIndex() bool {
|
||||
return slf.isIndex
|
||||
}
|
||||
|
||||
// IsStruct 是否是结构类型
|
||||
func (slf *TmplField) IsStruct() bool {
|
||||
return slf.Struct != nil
|
||||
}
|
||||
|
||||
// IsSlice 是否是切片类型
|
||||
func (slf *TmplField) IsSlice() bool {
|
||||
return slf.slice
|
||||
}
|
||||
|
||||
// setStruct 设置结构类型
|
||||
func (slf *TmplField) setStruct(parent, name, desc, fieldType string, fields map[string]Field) {
|
||||
slf.Struct = &TmplStruct{
|
||||
Name: fmt.Sprintf("%s%s", parent, name),
|
||||
Desc: desc,
|
||||
}
|
||||
slf.handleStruct(slf.Struct.Name, fieldType, fields)
|
||||
}
|
||||
|
||||
// handleStruct 处理结构类型
|
||||
func (slf *TmplField) handleStruct(fieldName, fieldType string, fields map[string]Field) {
|
||||
if strings.HasPrefix(fieldType, "[]") {
|
||||
slf.handleSlice(fieldName, fieldType, fields)
|
||||
return
|
||||
} else if !strings.HasPrefix(fieldType, "{") || !strings.HasSuffix(fieldType, "}") {
|
||||
return
|
||||
}
|
||||
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
||||
var fs []string
|
||||
var field string
|
||||
var leftBrackets []int
|
||||
for i, c := range s {
|
||||
switch c {
|
||||
case ',':
|
||||
if len(leftBrackets) == 0 {
|
||||
fs = append(fs, field)
|
||||
field = ""
|
||||
} else {
|
||||
field += string(c)
|
||||
}
|
||||
case '{':
|
||||
leftBrackets = append(leftBrackets, i)
|
||||
field += string(c)
|
||||
case '}':
|
||||
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
||||
field += string(c)
|
||||
if len(leftBrackets) == 0 {
|
||||
fs = append(fs, field)
|
||||
field = ""
|
||||
}
|
||||
default:
|
||||
field += string(c)
|
||||
}
|
||||
}
|
||||
if len(field) > 0 {
|
||||
fs = append(fs, field)
|
||||
}
|
||||
|
||||
for _, fieldInfo := range fs {
|
||||
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
||||
slf.Struct.addField(slf.Struct.Name, str.FirstUpper(fieldName), fieldName, fieldType, fields)
|
||||
}
|
||||
|
||||
slf.Type = slf.Struct.Name
|
||||
}
|
||||
|
||||
// handleSlice 处理切片类型
|
||||
func (slf *TmplField) handleSlice(fieldName, fieldType string, fields map[string]Field) {
|
||||
if !strings.HasPrefix(fieldType, "[]") {
|
||||
return
|
||||
}
|
||||
slf.slice = true
|
||||
t := strings.TrimPrefix(fieldType, "[]")
|
||||
if hash.Exist(fields, t) {
|
||||
slf.Struct = nil
|
||||
slf.Type = t
|
||||
} else {
|
||||
slf.handleStruct(fieldName, t, fields)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package pce
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
)
|
||||
|
||||
// TmplStruct 模板结构
|
||||
type TmplStruct struct {
|
||||
Name string // 结构名称
|
||||
Desc string // 结构描述
|
||||
Fields []*TmplField // 字段列表
|
||||
IndexCount int // 索引数量
|
||||
}
|
||||
|
||||
// addField 添加字段
|
||||
func (slf *TmplStruct) addField(parent, name, desc, fieldType string, fields map[string]Field) *TmplField {
|
||||
field := &TmplField{
|
||||
Name: name,
|
||||
Desc: desc,
|
||||
Type: fieldType,
|
||||
}
|
||||
if !hash.Exist(fields, fieldType) {
|
||||
field.setStruct(parent, name, desc, fieldType, fields)
|
||||
} else {
|
||||
field.Type = GetFieldGolangType(fields[fieldType])
|
||||
}
|
||||
slf.Fields = append(slf.Fields, field)
|
||||
return field
|
||||
}
|
||||
|
||||
// AllChildren 获取所有子结构
|
||||
func (slf *TmplStruct) AllChildren() []*TmplStruct {
|
||||
var children []*TmplStruct
|
||||
for _, field := range slf.Fields {
|
||||
if field.IsStruct() {
|
||||
children = append(children, field.Struct)
|
||||
children = append(children, field.Struct.AllChildren()...)
|
||||
}
|
||||
}
|
||||
return children
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
package tmpls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/planner/pce"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewGolang 创建一个 Golang 配置导出模板
|
||||
func NewGolang(packageName string) *Golang {
|
||||
return &Golang{
|
||||
Package: packageName,
|
||||
}
|
||||
}
|
||||
|
||||
// Golang 配置导出模板
|
||||
type Golang struct {
|
||||
Package string
|
||||
Templates []*pce.TmplStruct
|
||||
}
|
||||
|
||||
func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||
slf.Templates = templates
|
||||
return render(`// Code generated by minotaur. DO NOT EDIT.
|
||||
package {{.Package}}
|
||||
|
||||
import (
|
||||
jsonIter "github.com/json-iterator/go"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Sign string
|
||||
|
||||
const (
|
||||
{{- range .Templates}}
|
||||
{{.Name}}Sign Sign = "{{.Name}}" // {{.Desc}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
var (
|
||||
json = jsonIter.ConfigCompatibleWithStandardLibrary
|
||||
configs map[Sign]any
|
||||
signs = []Sign{
|
||||
{{- range .Templates}}
|
||||
{{.Name}}Sign,
|
||||
{{- end}}
|
||||
}
|
||||
mutex sync.Mutex
|
||||
)
|
||||
|
||||
var (
|
||||
{{- range .Templates}}
|
||||
{{- if $.HasIndex .}}
|
||||
{{.Name}} {{$.GetVariable .}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
var (
|
||||
{{- range .Templates}}
|
||||
{{- if $.HasIndex .}}{{- else}}
|
||||
{{.Name}} *{{$.GetConfigName .}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
var (
|
||||
{{- range .Templates}}
|
||||
{{- if $.HasIndex .}}
|
||||
_{{.Name}} {{$.GetVariable .}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
var (
|
||||
{{- range .Templates}}
|
||||
{{- if $.HasIndex .}}{{- else}}
|
||||
_{{.Name}} *{{$.GetConfigName .}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
{{- range .Templates}}
|
||||
// {{$.GetConfigName .}} {{.Desc}}
|
||||
type {{$.GetConfigName .}} struct {
|
||||
{{- range .Fields}}
|
||||
{{- if .IsSlice}}
|
||||
{{- if .IsStruct}}
|
||||
{{.Name}} []*{{.Struct.Name}} // {{.Desc}}
|
||||
{{- else}}
|
||||
{{.Name}} []{{.Type}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- else}}
|
||||
{{- if .IsStruct}}
|
||||
{{.Name}} *{{.Struct.Name}} // {{.Desc}}
|
||||
{{- else}}
|
||||
{{.Name}} {{.Type}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
func (slf *{{$.GetConfigName .}}) String() string {
|
||||
if data, err := json.Marshal(slf); err == nil {
|
||||
return string(data)
|
||||
}
|
||||
return "{}"
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
{{- range .Templates}}
|
||||
{{- range .AllChildren}}
|
||||
// {{.Name}} {{.Desc}}
|
||||
type {{.Name}} struct {
|
||||
{{- range .Fields}}
|
||||
{{- if .IsSlice}}
|
||||
{{- if .IsStruct}}
|
||||
{{.Name}} []*{{.Struct.Name}} // {{.Desc}}
|
||||
{{- else}}
|
||||
{{.Name}} []{{.Type}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- else}}
|
||||
{{- if .IsStruct}}
|
||||
{{.Name}} *{{.Struct.Name}} // {{.Desc}}
|
||||
{{- else}}
|
||||
{{.Name}} {{.Type}} // {{.Desc}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
// Load 通过自定义的方式加载配置
|
||||
// - 通常建议使用该方法加载配置,因为仅执行一次加解锁
|
||||
func LoadWithHandle(handle func(sign Sign, config any, json jsonIter.API) error) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
for _, sign := range signs {
|
||||
switch sign {
|
||||
{{- range .Templates}}
|
||||
case {{.Name}}Sign:
|
||||
{{- if $.HasIndex .}}
|
||||
temp := make({{$.GetVariable .}})
|
||||
{{- else}}
|
||||
temp := new({{$.GetConfigName .}})
|
||||
{{- end}}
|
||||
if err := handle(sign, &temp, json);err != nil {
|
||||
log.Error("Config", log.String("Name", "{{.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
||||
}else {
|
||||
_{{.Name}} = temp
|
||||
}
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LoadIndexConfig 通过 JSON 加载配置
|
||||
func LoadWithJSON(sign Sign, data []byte) {
|
||||
switch sign {
|
||||
{{- range .Templates}}
|
||||
case {{.Name}}Sign:
|
||||
{{- if $.HasIndex .}}
|
||||
temp := make({{$.GetVariable .}})
|
||||
{{- else}}
|
||||
temp := new({{$.GetConfigName .}})
|
||||
{{- end}}
|
||||
if err := json.Unmarshal(data, &{{.Name}}); err != nil {
|
||||
log.Error("Config", log.String("Name", "{{.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
||||
return
|
||||
}
|
||||
_{{.Name}} = temp
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh 将加载后的配置刷新到线上
|
||||
func Refresh() {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
cs := make(map[Sign]any)
|
||||
|
||||
{{- range .Templates}}
|
||||
{{.Name}} = _{{.Name}}
|
||||
cs[{{.Name}}Sign] = {{.Name}}
|
||||
{{- end}}
|
||||
|
||||
configs = cs
|
||||
}
|
||||
|
||||
// GetConfigs 获取所有配置
|
||||
func GetConfigs() map[Sign]any {
|
||||
return configs
|
||||
}
|
||||
|
||||
// GetConfigSigns 获取所有配置的标识
|
||||
func GetConfigSigns() []Sign {
|
||||
return signs
|
||||
}
|
||||
`, slf)
|
||||
}
|
||||
|
||||
func (slf *Golang) GetVariable(config *pce.TmplStruct) string {
|
||||
var prefix string
|
||||
for _, field := range config.Fields {
|
||||
if field.IsIndex() {
|
||||
prefix += fmt.Sprintf("map[%s]", field.Type)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s*%s", prefix, slf.GetConfigName(config))
|
||||
}
|
||||
|
||||
func (slf *Golang) GetConfigName(config *pce.TmplStruct) string {
|
||||
return strings.ReplaceAll(config.Name, "Config", "Configuration")
|
||||
}
|
||||
|
||||
func (slf *Golang) HasIndex(config *pce.TmplStruct) bool {
|
||||
return config.IndexCount > 0
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package tmpls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
jsonIter "github.com/json-iterator/go"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
)
|
||||
|
||||
func NewJSON() *JSON {
|
||||
return &JSON{
|
||||
API: jsonIter.ConfigCompatibleWithStandardLibrary,
|
||||
}
|
||||
}
|
||||
|
||||
type JSON struct {
|
||||
jsonIter.API
|
||||
}
|
||||
|
||||
func (slf *JSON) Render(data map[any]any) (string, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
encoder := jsonIter.NewEncoder(buffer)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", " ")
|
||||
err := encoder.Encode(data)
|
||||
if err != nil {
|
||||
return str.None, err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package tmpls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func render(temp string, o any) (string, error) {
|
||||
tmpl, err := template.New(temp).Parse(temp)
|
||||
if err != nil {
|
||||
return str.None, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, o); err != nil {
|
||||
return str.None, err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
|
@ -9,9 +9,9 @@ import (
|
|||
// ReporterStrategy 上报器策略
|
||||
type ReporterStrategy func(reporter *Reporter)
|
||||
|
||||
// ReportStrategyLoop 循环上报
|
||||
// StrategyLoop 循环上报
|
||||
// - 将在创建后上报一次,并且在每隔一段时间后继续上报
|
||||
func ReportStrategyLoop(t time.Duration) ReporterStrategy {
|
||||
func StrategyLoop(t time.Duration) ReporterStrategy {
|
||||
return func(reporter *Reporter) {
|
||||
reporter.ticker.Loop(fmt.Sprintf("ReportStrategyLoop_%d", t.Milliseconds()), timer.Instantly, t, timer.Forever, func() {
|
||||
if err := reporter.Report(); err != nil && reporter.errorHandle != nil {
|
||||
|
@ -21,8 +21,8 @@ func ReportStrategyLoop(t time.Duration) ReporterStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
// ReportStrategyFixedTime 将在每天的固定时间上报
|
||||
func ReportStrategyFixedTime(hour, min, sec int) ReporterStrategy {
|
||||
// StrategyFixedTime 将在每天的固定时间上报
|
||||
func StrategyFixedTime(hour, min, sec int) ReporterStrategy {
|
||||
return func(reporter *Reporter) {
|
||||
now := time.Now()
|
||||
current := now.Unix()
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/synchronization"
|
||||
"github.com/nats-io/nats.go"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -47,10 +46,10 @@ func (slf *Nats) Init(server *server.Server, packetHandle func(serverId int64, p
|
|||
nats.ReconnectWait(time.Second*5),
|
||||
nats.MaxReconnects(-1),
|
||||
nats.DisconnectErrHandler(func(conn *nats.Conn, err error) {
|
||||
log.Error(nasMark, zap.String("info", "disconnect"), zap.Error(err))
|
||||
log.Error(nasMark, log.String("info", "disconnect"), log.Err(err))
|
||||
}),
|
||||
nats.ReconnectHandler(func(conn *nats.Conn) {
|
||||
log.Info(nasMark, zap.String("info", "reconnect"))
|
||||
log.Info(nasMark, log.String("info", "reconnect"))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -63,7 +62,7 @@ func (slf *Nats) Init(server *server.Server, packetHandle func(serverId int64, p
|
|||
message := slf.messagePool.Get()
|
||||
defer slf.messagePool.Release(message)
|
||||
if err := json.Unmarshal(msg.Data, &message); err != nil {
|
||||
log.Error(nasMark, zap.Error(err))
|
||||
log.Error(nasMark, log.Err(err))
|
||||
return
|
||||
}
|
||||
packetHandle(message.ServerId, message.Packet)
|
||||
|
|
114
server/event.go
114
server/event.go
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/runtimes"
|
||||
"go.uber.org/zap"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -41,13 +41,15 @@ type event struct {
|
|||
// RegStopEvent 服务器停止时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegStopEvent(handle StopEventHandle) {
|
||||
slf.stopEventHandles = append(slf.stopEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnStopEvent() {
|
||||
for _, handle := range slf.stopEventHandles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
for _, handle := range slf.stopEventHandles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RegConsoleCommandEvent 控制台收到指令时将立即执行被注册的事件处理函数
|
||||
|
@ -65,33 +67,41 @@ func (slf *event) RegConsoleCommandEvent(command string, handle ConsoleCommandEv
|
|||
}()
|
||||
})
|
||||
slf.consoleCommandEventHandles[command] = append(slf.consoleCommandEventHandles[command], handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnConsoleCommandEvent(command string) {
|
||||
handles, exist := slf.consoleCommandEventHandles[command]
|
||||
if !exist {
|
||||
switch command {
|
||||
case "exit", "quit", "close", "shutdown", "EXIT", "QUIT", "CLOSE", "SHUTDOWN":
|
||||
log.Info("Console", zap.String("Receive", command), zap.String("Action", "Shutdown"))
|
||||
slf.Server.shutdown(nil)
|
||||
return
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
handles, exist := slf.consoleCommandEventHandles[command]
|
||||
if !exist {
|
||||
switch command {
|
||||
case "exit", "quit", "close", "shutdown", "EXIT", "QUIT", "CLOSE", "SHUTDOWN":
|
||||
log.Info("Console", log.String("Receive", command), log.String("Action", "Shutdown"))
|
||||
slf.Server.shutdown(nil)
|
||||
return
|
||||
}
|
||||
log.Warn("Server", log.String("Command", "unregistered"))
|
||||
} else {
|
||||
for _, handle := range handles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
}
|
||||
log.Warn("Server", zap.String("Command", "unregistered"))
|
||||
} else {
|
||||
for _, handle := range handles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RegStartBeforeEvent 在服务器初始化完成启动前立刻执行被注册的事件处理函数
|
||||
func (slf *event) RegStartBeforeEvent(handle StartBeforeEventHandle) {
|
||||
slf.startBeforeEventHandles = append(slf.startBeforeEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnStartBeforeEvent() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error("Server", log.String("OnStartBeforeEvent", fmt.Sprintf("%v", err)))
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
for _, handle := range slf.startBeforeEventHandles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
|
@ -100,13 +110,15 @@ func (slf *event) OnStartBeforeEvent() {
|
|||
// RegStartFinishEvent 在服务器启动完成时将立刻执行被注册的事件处理函数
|
||||
func (slf *event) RegStartFinishEvent(handle StartFinishEventHandle) {
|
||||
slf.startFinishEventHandles = append(slf.startFinishEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnStartFinishEvent() {
|
||||
for _, handle := range slf.startFinishEventHandles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
for _, handle := range slf.startFinishEventHandles {
|
||||
handle(slf.Server)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RegConnectionClosedEvent 在连接关闭后将立刻执行被注册的事件处理函数
|
||||
|
@ -115,15 +127,17 @@ func (slf *event) RegConnectionClosedEvent(handle ConnectionClosedEventHandle) {
|
|||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionClosedEventHandles = append(slf.connectionClosedEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnConnectionClosedEvent(conn *Conn, err any) {
|
||||
for _, handle := range slf.connectionClosedEventHandles {
|
||||
handle(slf.Server, conn, err)
|
||||
}
|
||||
conn.Close()
|
||||
slf.Server.online.Delete(conn.GetID())
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
for _, handle := range slf.connectionClosedEventHandles {
|
||||
handle(slf.Server, conn, err)
|
||||
}
|
||||
conn.Close()
|
||||
slf.Server.online.Delete(conn.GetID())
|
||||
})
|
||||
}
|
||||
|
||||
// RegConnectionOpenedEvent 在连接打开后将立刻执行被注册的事件处理函数
|
||||
|
@ -132,14 +146,16 @@ func (slf *event) RegConnectionOpenedEvent(handle ConnectionOpenedEventHandle) {
|
|||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionOpenedEventHandles = append(slf.connectionOpenedEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnConnectionOpenedEvent(conn *Conn) {
|
||||
slf.Server.online.Set(conn.GetID(), conn)
|
||||
for _, handle := range slf.connectionOpenedEventHandles {
|
||||
handle(slf.Server, conn)
|
||||
}
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
slf.Server.online.Set(conn.GetID(), conn)
|
||||
for _, handle := range slf.connectionOpenedEventHandles {
|
||||
handle(slf.Server, conn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RegConnectionReceivePacketEvent 在接收到数据包时将立刻执行被注册的事件处理函数
|
||||
|
@ -148,7 +164,7 @@ func (slf *event) RegConnectionReceivePacketEvent(handle ConnectionReceivePacket
|
|||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionReceivePacketEventHandles = append(slf.connectionReceivePacketEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnConnectionReceivePacketEvent(conn *Conn, packet Packet) {
|
||||
|
@ -160,7 +176,7 @@ func (slf *event) OnConnectionReceivePacketEvent(conn *Conn, packet Packet) {
|
|||
// RegReceiveCrossPacketEvent 在接收到跨服数据包时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegReceiveCrossPacketEvent(handle ReceiveCrossPacketEventHandle) {
|
||||
slf.receiveCrossPacketEventHandles = append(slf.receiveCrossPacketEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnReceiveCrossPacketEvent(serverId int64, packet []byte) {
|
||||
|
@ -172,25 +188,29 @@ func (slf *event) OnReceiveCrossPacketEvent(serverId int64, packet []byte) {
|
|||
// RegMessageErrorEvent 在处理消息发生错误时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegMessageErrorEvent(handle MessageErrorEventHandle) {
|
||||
slf.messageErrorEventHandles = append(slf.messageErrorEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnMessageErrorEvent(message *Message, err error) {
|
||||
for _, handle := range slf.messageErrorEventHandles {
|
||||
handle(slf.Server, message, err)
|
||||
}
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
for _, handle := range slf.messageErrorEventHandles {
|
||||
handle(slf.Server, message, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RegMessageLowExecEvent 在处理消息缓慢时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegMessageLowExecEvent(handle MessageLowExecEventHandle) {
|
||||
slf.messageLowExecEventHandles = append(slf.messageLowExecEventHandles, handle)
|
||||
log.Info("Server", zap.String("RegEvent", runtimes.CurrentRunningFuncName()), zap.String("handle", reflect.TypeOf(handle).String()))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handle", reflect.TypeOf(handle).String()))
|
||||
}
|
||||
|
||||
func (slf *event) OnMessageLowExecEvent(message *Message, cost time.Duration) {
|
||||
for _, handle := range slf.messageLowExecEventHandles {
|
||||
handle(slf.Server, message, cost)
|
||||
}
|
||||
PushSystemMessage(slf.Server, func() {
|
||||
for _, handle := range slf.messageLowExecEventHandles {
|
||||
handle(slf.Server, message, cost)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (slf *event) check() {
|
||||
|
@ -198,12 +218,12 @@ func (slf *event) check() {
|
|||
case NetworkHttp, NetworkGRPC:
|
||||
default:
|
||||
if len(slf.connectionReceivePacketEventHandles) == 0 {
|
||||
log.Warn("Server", zap.String("ConnectionReceivePacketEvent", "invalid server, no packets processed"))
|
||||
log.Warn("Server", log.String("ConnectionReceivePacketEvent", "invalid server, no packets processed"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(slf.receiveCrossPacketEventHandles) > 0 && slf.cross == nil {
|
||||
log.Warn("Server", zap.String("ReceiveCrossPacketEvent", "invalid server, not register cross server"))
|
||||
log.Warn("Server", log.String("ReceiveCrossPacketEvent", "invalid server, not register cross server"))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/panjf2000/gnet"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -23,7 +22,7 @@ func (slf *gNet) OnShutdown(server gnet.Server) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
if err := gnet.Stop(ctx, fmt.Sprintf("%s://%s", slf.network, slf.addr)); err != nil {
|
||||
log.Error("Server", zap.String("Minotaur GNet Server", "Shutdown"), zap.Error(err))
|
||||
log.Error("Server", log.String("Minotaur GNet Server", "Shutdown"), log.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ const (
|
|||
|
||||
// MessageTypeAsync 异步消息类型
|
||||
MessageTypeAsync
|
||||
|
||||
// MessageTypeSystem 系统消息类型
|
||||
MessageTypeSystem
|
||||
)
|
||||
|
||||
var messageNames = map[MessageType]string{
|
||||
|
@ -29,6 +32,7 @@ var messageNames = map[MessageType]string{
|
|||
MessageTypeCross: "MessageTypeCross",
|
||||
MessageTypeTicker: "MessageTypeTicker",
|
||||
MessageTypeAsync: "MessageTypeAsync",
|
||||
MessageTypeSystem: "MessageTypeSystem",
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -146,6 +150,14 @@ func PushAsyncMessage(srv *Server, caller func() error, callback func(err error)
|
|||
srv.pushMessage(msg)
|
||||
}
|
||||
|
||||
// PushSystemMessage 向特定服务器中推送 MessageTypeSystem 消息
|
||||
func PushSystemMessage(srv *Server, handle func(), mark ...any) {
|
||||
msg := srv.messagePool.Get()
|
||||
msg.t = MessageTypeSystem
|
||||
msg.attrs = append([]any{handle}, mark...)
|
||||
srv.pushMessage(msg)
|
||||
}
|
||||
|
||||
// SetMessagePacketVisualizer 设置消息可视化函数
|
||||
// - 消息可视化将在慢消息等情况用于打印,使用自定消息可视化函数可以便于开发者进行调试
|
||||
// - 默认的消息可视化函数将直接返回消息的字符串表示
|
||||
|
|
|
@ -2,7 +2,6 @@ package server
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
@ -60,14 +59,14 @@ func (slf *MultipleServer) Run() {
|
|||
}
|
||||
wait.Wait()
|
||||
|
||||
log.Info("Server", zap.String(serverMultipleMark, "===================================================================="))
|
||||
log.Info("Server", log.String(serverMultipleMark, "===================================================================="))
|
||||
for _, server := range slf.servers {
|
||||
log.Info("Server", zap.String(serverMultipleMark, "RunningInfo"),
|
||||
zap.Any("network", server.network),
|
||||
zap.String("listen", server.addr),
|
||||
log.Info("Server", log.String(serverMultipleMark, "RunningInfo"),
|
||||
log.Any("network", server.network),
|
||||
log.String("listen", server.addr),
|
||||
)
|
||||
}
|
||||
log.Info("Server", zap.String(serverMultipleMark, "===================================================================="))
|
||||
log.Info("Server", log.String(serverMultipleMark, "===================================================================="))
|
||||
|
||||
systemSignal := make(chan os.Signal, 1)
|
||||
signal.Notify(systemSignal, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"github.com/gin-contrib/pprof"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"reflect"
|
||||
"time"
|
||||
|
@ -87,7 +86,7 @@ func WithDeadlockDetect(t time.Duration) Option {
|
|||
return func(srv *Server) {
|
||||
if t > 0 {
|
||||
srv.deadlockDetect = t
|
||||
log.Info("DeadlockDetect", zap.String("Time", t.String()))
|
||||
log.Info("DeadlockDetect", log.String("Time", t.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,11 +152,11 @@ func WithCross(crossName string, serverId int64, cross Cross) Option {
|
|||
srv.pushMessage(msg)
|
||||
})
|
||||
if err != nil {
|
||||
log.Info("Cross", zap.Int64("ServerID", serverId), zap.String("Cross", reflect.TypeOf(cross).String()), zap.String("State", "WaitNatsRun"))
|
||||
log.Info("Cross", log.Int64("ServerID", serverId), log.String("Cross", reflect.TypeOf(cross).String()), log.String("State", "WaitNatsRun"))
|
||||
time.Sleep(1 * time.Second)
|
||||
goto start
|
||||
}
|
||||
log.Info("Cross", zap.Int64("ServerID", serverId), zap.String("Cross", reflect.TypeOf(cross).String()))
|
||||
log.Info("Cross", log.Int64("ServerID", serverId), log.String("Cross", reflect.TypeOf(cross).String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/panjf2000/gnet"
|
||||
"github.com/panjf2000/gnet/pkg/logging"
|
||||
"github.com/xtaci/kcp-go/v5"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -114,6 +113,7 @@ func (slf *Server) Run(addr string) error {
|
|||
slf.event.check()
|
||||
slf.addr = addr
|
||||
var protoAddr = fmt.Sprintf("%s://%s", slf.network, slf.addr)
|
||||
var messageInitFinish = make(chan struct{}, 1)
|
||||
var connectionInitHandle = func(callback func()) {
|
||||
slf.messagePool = synchronization.NewPool[*Message](slf.messagePoolSize,
|
||||
func() *Message {
|
||||
|
@ -132,6 +132,7 @@ func (slf *Server) Run(addr string) error {
|
|||
go callback()
|
||||
}
|
||||
go func() {
|
||||
messageInitFinish <- struct{}{}
|
||||
for message := range slf.messageChannel {
|
||||
slf.dispatchMessage(message)
|
||||
}
|
||||
|
@ -312,13 +313,16 @@ func (slf *Server) Run(addr string) error {
|
|||
return ErrCanNotSupportNetwork
|
||||
}
|
||||
|
||||
<-messageInitFinish
|
||||
close(messageInitFinish)
|
||||
messageInitFinish = nil
|
||||
if slf.multiple == nil {
|
||||
log.Info("Server", zap.String(serverMark, "===================================================================="))
|
||||
log.Info("Server", zap.String(serverMark, "RunningInfo"),
|
||||
zap.Any("network", slf.network),
|
||||
zap.String("listen", slf.addr),
|
||||
log.Info("Server", log.String(serverMark, "===================================================================="))
|
||||
log.Info("Server", log.String(serverMark, "RunningInfo"),
|
||||
log.Any("network", slf.network),
|
||||
log.String("listen", slf.addr),
|
||||
)
|
||||
log.Info("Server", zap.String(serverMark, "===================================================================="))
|
||||
log.Info("Server", log.String(serverMark, "===================================================================="))
|
||||
slf.OnStartFinishEvent()
|
||||
|
||||
signal.Notify(slf.systemSignal, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
@ -417,15 +421,15 @@ func (slf *Server) shutdown(err error) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
if shutdownErr := slf.httpServer.Shutdown(ctx); shutdownErr != nil {
|
||||
log.Error("Server", zap.Error(shutdownErr))
|
||||
log.Error("Server", log.Err(shutdownErr))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if slf.multiple != nil {
|
||||
slf.multiple.RegExitEvent(func() {
|
||||
log.Panic("Server", zap.Any("network", slf.network), zap.String("listen", slf.addr),
|
||||
zap.String("action", "shutdown"), zap.String("state", "exception"), zap.Error(err))
|
||||
log.Panic("Server", log.Any("network", slf.network), log.String("listen", slf.addr),
|
||||
log.String("action", "shutdown"), log.String("state", "exception"), log.Err(err))
|
||||
})
|
||||
for i, server := range slf.multiple.servers {
|
||||
if server.addr == slf.addr {
|
||||
|
@ -434,12 +438,12 @@ func (slf *Server) shutdown(err error) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
log.Panic("Server", zap.Any("network", slf.network), zap.String("listen", slf.addr),
|
||||
zap.String("action", "shutdown"), zap.String("state", "exception"), zap.Error(err))
|
||||
log.Panic("Server", log.Any("network", slf.network), log.String("listen", slf.addr),
|
||||
log.String("action", "shutdown"), log.String("state", "exception"), log.Err(err))
|
||||
}
|
||||
} else {
|
||||
log.Info("Server", zap.Any("network", slf.network), zap.String("listen", slf.addr),
|
||||
zap.String("action", "shutdown"), zap.String("state", "normal"))
|
||||
log.Info("Server", log.Any("network", slf.network), log.String("listen", slf.addr),
|
||||
log.String("action", "shutdown"), log.String("state", "normal"))
|
||||
}
|
||||
if slf.gServer == nil {
|
||||
slf.closeChannel <- struct{}{}
|
||||
|
@ -474,7 +478,7 @@ func (slf *Server) pushMessage(message *Message) {
|
|||
func (slf *Server) low(message *Message, present time.Time, expect time.Duration) {
|
||||
cost := time.Since(present)
|
||||
if cost > expect {
|
||||
log.Warn("Server", zap.String("type", "low-message"), zap.String("cost", cost.String()), zap.String("message", message.String()), zap.Stack("stack"))
|
||||
log.Warn("Server", log.String("type", "low-message"), log.String("cost", cost.String()), log.String("message", message.String()), log.Stack("stack"))
|
||||
slf.OnMessageLowExecEvent(message, cost)
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +495,7 @@ func (slf *Server) dispatchMessage(msg *Message) {
|
|||
select {
|
||||
case <-ctx.Done():
|
||||
if err := ctx.Err(); err == context.DeadlineExceeded {
|
||||
log.Warn("Server", zap.String("MessageType", messageNames[msg.t]), zap.Any("SuspectedDeadlock", msg.attrs))
|
||||
log.Warn("Server", log.String("MessageType", messageNames[msg.t]), log.Any("SuspectedDeadlock", msg.attrs))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -501,7 +505,7 @@ func (slf *Server) dispatchMessage(msg *Message) {
|
|||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
stack := string(debug.Stack())
|
||||
log.Error("Server", zap.String("MessageType", messageNames[msg.t]), zap.Any("MessageAttrs", msg.attrs), zap.Any("error", err), zap.String("stack", stack))
|
||||
log.Error("Server", log.String("MessageType", messageNames[msg.t]), log.Any("MessageAttrs", msg.attrs), log.Any("error", err), log.String("stack", stack))
|
||||
fmt.Println(stack)
|
||||
if e, ok := err.(error); ok {
|
||||
slf.OnMessageErrorEvent(msg, e)
|
||||
|
@ -530,11 +534,11 @@ func (slf *Server) dispatchMessage(msg *Message) {
|
|||
err, action := attrs[0].(error), attrs[1].(MessageErrorAction)
|
||||
switch action {
|
||||
case MessageErrorActionNone:
|
||||
log.Panic("Server", zap.Error(err))
|
||||
log.Panic("Server", log.Err(err))
|
||||
case MessageErrorActionShutdown:
|
||||
slf.shutdown(err)
|
||||
default:
|
||||
log.Warn("Server", zap.String("not support message error action", action.String()))
|
||||
log.Warn("Server", log.String("not support message error action", action.String()))
|
||||
}
|
||||
case MessageTypeCross:
|
||||
slf.OnReceiveCrossPacketEvent(attrs[0].(int64), attrs[1].([]byte))
|
||||
|
@ -547,7 +551,7 @@ func (slf *Server) dispatchMessage(msg *Message) {
|
|||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
stack := string(debug.Stack())
|
||||
log.Error("Server", zap.String("MessageType", messageNames[msg.t]), zap.Any("error", err), zap.String("stack", stack))
|
||||
log.Error("Server", log.String("MessageType", messageNames[msg.t]), log.Any("error", err), log.String("stack", stack))
|
||||
fmt.Println(stack)
|
||||
if e, ok := err.(error); ok {
|
||||
slf.OnMessageErrorEvent(msg, e)
|
||||
|
@ -560,15 +564,18 @@ func (slf *Server) dispatchMessage(msg *Message) {
|
|||
slf.messagePool.Release(msg)
|
||||
}
|
||||
}()
|
||||
if err := handle(); err != nil {
|
||||
if cb {
|
||||
callback(err)
|
||||
}
|
||||
err := handle()
|
||||
if cb {
|
||||
callback(err)
|
||||
} else {
|
||||
log.Error("Server", log.String("MessageType", messageNames[msg.t]), log.Any("error", err), log.String("stack", string(debug.Stack())))
|
||||
}
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
case MessageTypeSystem:
|
||||
attrs[0].(func())()
|
||||
default:
|
||||
log.Warn("Server", zap.String("not support message type", msg.t.String()))
|
||||
log.Warn("Server", log.String("not support message type", msg.t.String()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ func (slf *Map[Key, Value]) rangeFree(handle func(key Key, value Value, skip fun
|
|||
|
||||
func (slf *Map[Key, Value]) Keys() []Key {
|
||||
var s = make([]Key, 0, len(slf.data))
|
||||
for k, _ := range slf.data {
|
||||
for k := range slf.data {
|
||||
s = append(s, k)
|
||||
}
|
||||
return s
|
||||
|
|
|
@ -102,10 +102,10 @@ func LineCount(filePath string) int {
|
|||
return line
|
||||
}
|
||||
|
||||
// FilePaths 获取指定目录下的所有文件路径
|
||||
// Paths 获取指定目录下的所有文件路径
|
||||
// - 包括了子目录下的文件
|
||||
// - 不包含目录
|
||||
func FilePaths(dir string) []string {
|
||||
func Paths(dir string) []string {
|
||||
var paths []string
|
||||
abs, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
|
@ -119,7 +119,7 @@ func FilePaths(dir string) []string {
|
|||
for _, file := range files {
|
||||
fileAbs := filepath.Join(abs, file.Name())
|
||||
if file.IsDir() {
|
||||
paths = append(paths, FilePaths(fileAbs)...)
|
||||
paths = append(paths, Paths(fileAbs)...)
|
||||
continue
|
||||
}
|
||||
paths = append(paths, fileAbs)
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
func TestFilePaths(t *testing.T) {
|
||||
var line int
|
||||
var fileCount int
|
||||
for _, path := range file.FilePaths(`D:\sources\minotaur`) {
|
||||
for _, path := range file.Paths(`D:\sources\minotaur`) {
|
||||
if !strings.HasSuffix(path, ".go") {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func ExampleNavMesh_FindPath() {
|
|||
|
||||
for x := sx; x <= bx; x++ {
|
||||
for y := sy; y <= by; y++ {
|
||||
fp.Put(geometry.NewPoint[int](int(x), int(y)), '+')
|
||||
fp.Put(geometry.NewPoint[int](x, y), '+')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func (slf *SortMap[K, V]) For(handle func(key K, value V) bool) {
|
|||
|
||||
func (slf *SortMap[K, V]) ForSort(handle func(key K, value V) bool) {
|
||||
var indexes []int
|
||||
for i, _ := range slf.s {
|
||||
for i := range slf.s {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
sort.Ints(indexes)
|
||||
|
@ -85,7 +85,7 @@ func (slf *SortMap[K, V]) ToSlice() []V {
|
|||
|
||||
func (slf *SortMap[K, V]) ToSliceSort() []V {
|
||||
var indexes []int
|
||||
for i, _ := range slf.s {
|
||||
for i := range slf.s {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
sort.Ints(indexes)
|
||||
|
|
|
@ -156,4 +156,7 @@ var (
|
|||
// Any 接受一个键和一个任意值,并选择将它们表示为字段的最佳方式,仅在必要时才回退到基于反射的方法。
|
||||
// 由于 byteuint8 和 runeint32 是别名,Any 无法区分它们。为了尽量减少意外情况,[]byte 值被视为二进制 blob,字节值被视为 uint8,而 runes 始终被视为整数
|
||||
Any = zap.Any
|
||||
|
||||
// Err 是常见习语 NamedError("error", err) 的简写
|
||||
Err = zap.Error
|
||||
)
|
||||
|
|
|
@ -27,7 +27,7 @@ func Pow(a, n int) int {
|
|||
if n == 1 {
|
||||
return a
|
||||
}
|
||||
var result int = 1
|
||||
var result = 1
|
||||
factor := a
|
||||
for n != 0 {
|
||||
if n&1 != 0 {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package super
|
||||
|
||||
import "os/exec"
|
||||
|
||||
// GoFormat go 代码格式化
|
||||
func GoFormat(filePath string) {
|
||||
cmd := exec.Command("gofmt", "-w", filePath)
|
||||
_ = cmd.Run()
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package super
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Matcher 匹配器
|
||||
type Matcher[Value, Result any] struct {
|
||||
value Value
|
||||
r Result
|
||||
d bool
|
||||
}
|
||||
|
||||
// Match 匹配
|
||||
func Match[Value, Result any](value Value) *Matcher[Value, Result] {
|
||||
return &Matcher[Value, Result]{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// Case 匹配
|
||||
func (slf *Matcher[Value, Result]) Case(value Value, result Result) *Matcher[Value, Result] {
|
||||
if !slf.d && reflect.DeepEqual(slf.value, value) {
|
||||
slf.r = result
|
||||
slf.d = true
|
||||
}
|
||||
return slf
|
||||
}
|
||||
|
||||
// Default 默认
|
||||
func (slf *Matcher[Value, Result]) Default(value Result) Result {
|
||||
if slf.d {
|
||||
return slf.r
|
||||
}
|
||||
return value
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package super_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
Convey("TestMatch", t, func() {
|
||||
So(super.Match[int, string](1).
|
||||
Case(1, "a").
|
||||
Case(2, "b").
|
||||
Default("c"), ShouldEqual, "a")
|
||||
So(super.Match[int, string](2).
|
||||
Case(1, "a").
|
||||
Case(2, "b").
|
||||
Default("c"), ShouldEqual, "b")
|
||||
So(super.Match[int, string](3).
|
||||
Case(1, "a").
|
||||
Case(2, "b").
|
||||
Default("c"), ShouldEqual, "c")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package super
|
||||
|
||||
import "strconv"
|
||||
|
||||
// StringToInt 字符串转换为整数
|
||||
func StringToInt(value string) int {
|
||||
i, _ := strconv.Atoi(value)
|
||||
return i
|
||||
}
|
|
@ -216,7 +216,7 @@ func (slf *Map[Key, Value]) Keys() []Key {
|
|||
defer slf.lock.RUnlock()
|
||||
}
|
||||
var s = make([]Key, 0, len(slf.data))
|
||||
for k, _ := range slf.data {
|
||||
for k := range slf.data {
|
||||
s = append(s, k)
|
||||
}
|
||||
return s
|
||||
|
|
|
@ -183,7 +183,7 @@ func (slf *MapSegment[Key, Value]) RangeFree(handle func(key Key, value Value, s
|
|||
func (slf *MapSegment[Key, Value]) Keys() []Key {
|
||||
var s = make([]Key, 0, len(slf.cache))
|
||||
slf.lock.RLock()
|
||||
for k, _ := range slf.cache {
|
||||
for k := range slf.cache {
|
||||
s = append(s, k)
|
||||
}
|
||||
defer slf.lock.RUnlock()
|
||||
|
|
|
@ -2,7 +2,6 @@ package synchronization
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -42,7 +41,7 @@ func (slf *Pool[T]) Get() T {
|
|||
slf.mutex.Unlock()
|
||||
slf.warn++
|
||||
if slf.warn >= 100 {
|
||||
log.Warn("Pool", zap.String("Get", "the number of buffer members is insufficient, consider whether it is due to unreleased or inappropriate buffer size"))
|
||||
log.Warn("Pool", log.String("Get", "the number of buffer members is insufficient, consider whether it is due to unreleased or inappropriate buffer size"))
|
||||
slf.warn = 0
|
||||
}
|
||||
return slf.generator()
|
||||
|
|
Loading…
Reference in New Issue