From 8c1dedc472ca515dc3d5d7119ec6ec131727511e Mon Sep 17 00:00:00 2001 From: flswld Date: Sun, 12 Mar 2023 02:08:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/dispatch/application.toml | 3 + common/config/config.go | 1 + common/region/region.go | 3 +- dispatch/controller/dispatch_controller.go | 3 +- gate/net/session.go | 1 - gdconf/game_data_config.go | 5 + gdconf/reliquary_affix_data.go | 30 ---- gdconf/reliquary_main_data.go | 19 --- gdconf/scene_lua_config.go | 78 +++++++--- go.mod | 1 - go.sum | 2 - gs/game/audio_player.go | 91 ----------- gs/game/{video_player.go => audio_video.go} | 105 +++++++++++-- gs/game/command_controller.go | 2 +- gs/game/local_event_manager.go | 97 ++++++++---- gs/game/player_login.go | 160 +++++++++++--------- gs/game/player_reliquary.go | 62 +++++++- gs/game/tick_manager.go | 2 + gs/game/user_manager.go | 37 ++++- gs/game/world_manager.go | 1 - 20 files changed, 407 insertions(+), 296 deletions(-) delete mode 100644 gs/game/audio_player.go rename gs/game/{video_player.go => audio_video.go} (71%) diff --git a/cmd/dispatch/application.toml b/cmd/dispatch/application.toml index dd84dc9c..1fb77be8 100644 --- a/cmd/dispatch/application.toml +++ b/cmd/dispatch/application.toml @@ -1,5 +1,8 @@ http_port = 8080 +[hk4e] +dispatch_url = "https://hk4e.flswld.com/query_cur_region" # 二级dispatch地址 将域名改为dispatch的外网地址 + [logger] level = "DEBUG" mode = "CONSOLE" diff --git a/common/config/config.go b/common/config/config.go index 7e783bf6..9ed1ea1d 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -48,6 +48,7 @@ type Hk4e struct { GateTcpMqPort int32 `toml:"gate_tcp_mq_port"` LoginSdkUrl string `toml:"login_sdk_url"` // 网关登录验证token的sdk服务器地址 目前填dispatch的内网地址 LoadSceneLuaConfig bool `toml:"load_scene_lua_config"` // 是否加载场景详情LUA配置数据 + DispatchUrl string `toml:"dispatch_url"` // 二级dispatch地址 将域名改为dispatch的外网地址 } // MQ 消息队列 diff --git a/common/region/region.go b/common/region/region.go index 8b69298d..69a0bb02 100644 --- a/common/region/region.go +++ b/common/region/region.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "os" + "hk4e/common/config" "hk4e/pkg/endec" "hk4e/pkg/logger" "hk4e/pkg/random" @@ -61,7 +62,7 @@ func GetRegionList(ec2b *random.Ec2b) *proto.QueryRegionListHttpRsp { Name: "os_usa", Title: "America", Type: "DEV_PUBLIC", - DispatchUrl: "https://osusadispatch.yuanshen.com/query_cur_region", + DispatchUrl: config.GetConfig().Hk4e.DispatchUrl, } serverList = append(serverList, server) regionList := new(proto.QueryRegionListHttpRsp) diff --git a/dispatch/controller/dispatch_controller.go b/dispatch/controller/dispatch_controller.go index b9a58203..65f20647 100644 --- a/dispatch/controller/dispatch_controller.go +++ b/dispatch/controller/dispatch_controller.go @@ -69,7 +69,7 @@ func (c *Controller) getClientVersionByName(versionName string) (int, string) { func (c *Controller) queryCurRegion(context *gin.Context) { rspError := func() { rspContentError := "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==" - rspSignError := "TW9yZSBsb3ZlIGZvciBVQSBQYXRjaCBwbGF5ZXJz" + rspSignError := "" rsp := &httpapi.QueryCurRegionRspJson{ Content: rspContentError, Sign: rspSignError, @@ -100,7 +100,6 @@ func (c *Controller) queryCurRegion(context *gin.Context) { _, _ = context.Writer.WriteString(regionCurrBase64) return } - logger.Debug("do hk4e 2.8 rsa logic") keyId := context.Query("key_id") encPubPrivKey, exist := c.encRsaKeyMap[keyId] if !exist { diff --git a/gate/net/session.go b/gate/net/session.go index a732bd9a..255d37d0 100644 --- a/gate/net/session.go +++ b/gate/net/session.go @@ -457,7 +457,6 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session serverSeedUint64 := timeRand.Uint64() session.seed = serverSeedUint64 if req.KeyId != 0 { - logger.Debug("do hk4e 2.8 rsa logic, uid: %v", uid) session.useMagicSeed = true keyId := strconv.Itoa(int(req.KeyId)) encPubPrivKey, exist := k.encRsaKeyMap[keyId] diff --git a/gdconf/game_data_config.go b/gdconf/game_data_config.go index 37fa6a40..a0533974 100644 --- a/gdconf/game_data_config.go +++ b/gdconf/game_data_config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "runtime" "strings" "time" @@ -35,6 +36,8 @@ type GameDataConfig struct { ScenePointMap map[int32]*ScenePoint // 场景传送点 SceneTagDataMap map[int32]*SceneTagData // 场景标签 SceneLuaConfigMap map[int32]*SceneLuaConfig // 场景LUA配置 + GroupMap map[int32]*Group // 场景LUA区块group索引 + LuaStateLruMap map[int32]*LuaStateLru // 场景LUA虚拟机LRU内存淘汰 WorldAreaDataMap map[int32]*WorldAreaData // 世界区域 GatherDataMap map[int32]*GatherData // 采集物 GatherDataPointTypeMap map[int32]*GatherData // 采集物场景节点索引 @@ -60,6 +63,7 @@ func InitGameDataConfig() { startTime := time.Now().Unix() CONF.loadAll() endTime := time.Now().Unix() + runtime.GC() logger.Info("load all game data config finish, cost: %v(s)", endTime-startTime) } @@ -68,6 +72,7 @@ func ReloadGameDataConfig() { startTime := time.Now().Unix() CONF_RELOAD.loadAll() endTime := time.Now().Unix() + runtime.GC() logger.Info("reload all game data config finish, cost: %v(s)", endTime-startTime) } diff --git a/gdconf/reliquary_affix_data.go b/gdconf/reliquary_affix_data.go index d139825e..6f9ca8a0 100644 --- a/gdconf/reliquary_affix_data.go +++ b/gdconf/reliquary_affix_data.go @@ -6,7 +6,6 @@ import ( "hk4e/pkg/logger" "github.com/jszwec/csvutil" - "github.com/mroth/weightedrand" ) // ReliquaryAffixData 圣遗物追加属性配置表 @@ -46,35 +45,6 @@ func GetReliquaryAffixDataByDepotIdAndPropId(appendPropDepotId int32, appendProp return value[appendPropId] } -func GetReliquaryAffixDataRandomByDepotId(appendPropDepotId int32, excludeTypeList ...uint32) *ReliquaryAffixData { - appendPropMap, exist := CONF.ReliquaryAffixDataMap[appendPropDepotId] - if !exist { - return nil - } - choices := make([]weightedrand.Choice, 0, len(appendPropMap)) - for _, data := range appendPropMap { - isBoth := false - // 排除列表中的属性类型是否相同 - for _, propType := range excludeTypeList { - if propType == uint32(data.PropType) { - isBoth = true - break - } - } - if isBoth { - continue - } - choices = append(choices, weightedrand.NewChoice(data, uint(data.RandomWeight))) - } - chooser, err := weightedrand.NewChooser(choices...) - if err != nil { - logger.Error("reliquary append random error: %v", err) - return nil - } - result := chooser.Pick() - return result.(*ReliquaryAffixData) -} - func GetReliquaryAffixDataMap() map[int32]map[int32]*ReliquaryAffixData { return CONF.ReliquaryAffixDataMap } diff --git a/gdconf/reliquary_main_data.go b/gdconf/reliquary_main_data.go index 4d68ccfe..9ff56ed5 100644 --- a/gdconf/reliquary_main_data.go +++ b/gdconf/reliquary_main_data.go @@ -6,7 +6,6 @@ import ( "hk4e/pkg/logger" "github.com/jszwec/csvutil" - "github.com/mroth/weightedrand" ) // ReliquaryMainData 圣遗物主属性配置表 @@ -46,24 +45,6 @@ func GetReliquaryMainDataByDepotIdAndPropId(mainPropDepotId int32, mainPropId in return value[mainPropId] } -func GetReliquaryMainDataRandomByDepotId(mainPropDepotId int32) *ReliquaryMainData { - mainPropMap, exist := CONF.ReliquaryMainDataMap[mainPropDepotId] - if !exist { - return nil - } - choices := make([]weightedrand.Choice, 0, len(mainPropMap)) - for _, data := range mainPropMap { - choices = append(choices, weightedrand.NewChoice(data, uint(data.RandomWeight))) - } - chooser, err := weightedrand.NewChooser(choices...) - if err != nil { - logger.Error("reliquary main random error: %v", err) - return nil - } - result := chooser.Pick() - return result.(*ReliquaryMainData) -} - func GetReliquaryMainDataMap() map[int32]map[int32]*ReliquaryMainData { return CONF.ReliquaryMainDataMap } diff --git a/gdconf/scene_lua_config.go b/gdconf/scene_lua_config.go index 3ef96f9e..52731d41 100644 --- a/gdconf/scene_lua_config.go +++ b/gdconf/scene_lua_config.go @@ -2,9 +2,11 @@ package gdconf import ( "os" + "sort" "strconv" "sync" "sync/atomic" + "time" "hk4e/pkg/logger" @@ -68,7 +70,53 @@ type Group struct { LuaState *lua.LState `json:"-"` } +const ( + LuaStateLruKeepNum = 1000 +) + +type LuaStateLru struct { + GroupId int32 + AccessTime int64 +} + +type LuaStateLruList []*LuaStateLru + +func (l LuaStateLruList) Len() int { + return len(l) +} + +func (l LuaStateLruList) Less(i, j int) bool { + return l[i].AccessTime < l[j].AccessTime +} + +func (l LuaStateLruList) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func LuaStateLruRemove() { + removeNum := len(CONF.LuaStateLruMap) - LuaStateLruKeepNum + if removeNum <= 0 { + return + } + luaStateLruList := make(LuaStateLruList, 0) + for _, luaStateLru := range CONF.LuaStateLruMap { + luaStateLruList = append(luaStateLruList, luaStateLru) + } + sort.Stable(luaStateLruList) + for i := 0; i < removeNum; i++ { + luaStateLru := luaStateLruList[i] + group := GetSceneGroup(luaStateLru.GroupId) + group.LuaState = nil + delete(CONF.LuaStateLruMap, luaStateLru.GroupId) + } + logger.Info("lua state lru remove finish, remove num: %v", removeNum) +} + func (g *Group) GetLuaState() *lua.LState { + CONF.LuaStateLruMap[g.Id] = &LuaStateLru{ + GroupId: g.Id, + AccessTime: time.Now().UnixMilli(), + } if g.LuaState == nil { g.LuaState = newLuaState(g.LuaStr) scriptLib := g.LuaState.NewTable() @@ -207,6 +255,8 @@ func (g *GameDataConfig) loadGroup(group *Group, block *Block, sceneId int32, bl func (g *GameDataConfig) loadSceneLuaConfig() { OBJECT_ID_COUNTER = 0 g.SceneLuaConfigMap = make(map[int32]*SceneLuaConfig) + g.GroupMap = make(map[int32]*Group) + g.LuaStateLruMap = make(map[int32]*LuaStateLru) sceneLuaPrefix := g.luaPrefix + "scene/" for _, sceneData := range g.SceneDataMap { sceneId := sceneData.SceneId @@ -279,6 +329,7 @@ func (g *GameDataConfig) loadSceneLuaConfig() { <-wc wg.Done() }() + g.GroupMap[group.Id] = group } wg.Wait() sceneLuaConfig.BlockMap[block.Id] = block @@ -315,29 +366,10 @@ func GetSceneLuaConfigMap() map[int32]*SceneLuaConfig { return CONF.SceneLuaConfigMap } -func GetSceneBlockConfig(sceneId int32, blockId int32) ([]*Monster, []*Npc, []*Gadget, bool) { - monsterList := make([]*Monster, 0) - npcList := make([]*Npc, 0) - gadgetList := make([]*Gadget, 0) - sceneConfig, exist := CONF.SceneLuaConfigMap[sceneId] +func GetSceneGroup(groupId int32) *Group { + groupConfig, exist := CONF.GroupMap[groupId] if !exist { - return nil, nil, nil, false + return nil } - blockConfig, exist := sceneConfig.BlockMap[blockId] - if !exist { - return nil, nil, nil, false - } - for _, groupConfig := range blockConfig.GroupMap { - for _, monsterConfig := range groupConfig.MonsterList { - monsterList = append(monsterList, monsterConfig) - } - for _, npcConfig := range groupConfig.NpcList { - npcList = append(npcList, npcConfig) - } - - for _, gadgetConfig := range groupConfig.GadgetList { - gadgetList = append(gadgetList, gadgetConfig) - } - } - return monsterList, npcList, gadgetList, true + return groupConfig } diff --git a/go.mod b/go.mod index ead95797..0ed6b171 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,6 @@ require github.com/pierrec/lz4/v4 v4.1.17 require github.com/FlourishingWorld/dpdk-go v0.0.0-20230213165129-6c5bc55b1f63 require ( - github.com/mroth/weightedrand v1.0.0 github.com/pkg/errors v0.9.1 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec diff --git a/go.sum b/go.sum index 0753d742..ea0d6579 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/mroth/weightedrand v1.0.0 h1:V8JeHChvl2MP1sAoXq4brElOcza+jxLkRuwvtQu8L3E= -github.com/mroth/weightedrand v1.0.0/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE= github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= github.com/nats-io/nats-server/v2 v2.9.7 h1:VBlfq7xvv/72v0mzGZ2rgsDzUoVyX2Xhssl9XpKDue0= github.com/nats-io/nats-server/v2 v2.9.7/go.mod h1:AB6hAnGZDlYfqb7CTAm66ZKMZy9DpfierY1/PbpvI2g= diff --git a/gs/game/audio_player.go b/gs/game/audio_player.go deleted file mode 100644 index efae252f..00000000 --- a/gs/game/audio_player.go +++ /dev/null @@ -1,91 +0,0 @@ -package game - -import ( - "time" - - "hk4e/pkg/logger" - - "gitlab.com/gomidi/midi/v2" - "gitlab.com/gomidi/midi/v2/smf" -) - -const ( - KeyOffset = -12 * 1 // 八度修正偏移 -) - -var AUDIO_CHAN chan uint32 - -func init() { - AUDIO_CHAN = make(chan uint32, 1000) -} - -func RunPlayAudio() { - audio, err := smf.ReadFile("./in.mid") - if err != nil { - logger.Error("read midi file error: %v", err) - return - } - tempoChangeList := audio.TempoChanges() - if len(tempoChangeList) != 1 { - logger.Error("midi file format not support") - return - } - tempoChange := tempoChangeList[0] - metricTicks := audio.TimeFormat.(smf.MetricTicks) - tickTime := ((60000000.0 / tempoChange.BPM) / float64(metricTicks.Resolution())) / 1000.0 - for { - // 洗脑循环 - logger.Debug("start play audio") - for _, track := range audio.Tracks { - // 全部轨道 - totalTick := uint64(0) - for _, event := range track { - // 单个轨道 - delay := uint32(float64(event.Delta) * tickTime) - // busyPollWaitMilliSecond(delay) - interruptWaitMilliSecond(delay) - totalTick += uint64(delay) - - msg := event.Message - if msg.Type() != midi.NoteOnMsg { - continue - } - midiMsg := midi.Message(msg) - var channel, key, velocity uint8 - midiMsg.GetNoteOn(&channel, &key, &velocity) - // TODO 测试一下客户端是否支持更宽的音域 - // 60 -> 中央C C4 - // if key < 36 || key > 71 { - // continue - // } - note := int32(key) + int32(KeyOffset) - if note < 21 || note > 108 { - // 非88键钢琴音域 - continue - } - if velocity == 0 { - // 可能是NoteOffMsg - continue - } - - AUDIO_CHAN <- uint32(note) - // logger.Debug("send midi note: %v, delay: %v, totalTick: %v", note, delay, totalTick) - } - } - } -} - -func interruptWaitMilliSecond(delay uint32) { - time.Sleep(time.Millisecond * time.Duration(delay)) -} - -func busyPollWaitMilliSecond(delay uint32) { - start := time.Now() - end := start.Add(time.Millisecond * time.Duration(delay)) - for { - now := time.Now() - if now.After(end) { - break - } - } -} diff --git a/gs/game/video_player.go b/gs/game/audio_video.go similarity index 71% rename from gs/game/video_player.go rename to gs/game/audio_video.go index 2918ef56..37307408 100644 --- a/gs/game/video_player.go +++ b/gs/game/audio_video.go @@ -8,6 +8,7 @@ import ( "os" "sort" "strconv" + "time" "hk4e/pkg/logger" @@ -15,8 +16,88 @@ import ( "hk4e/protocol/proto" "github.com/pkg/errors" + "gitlab.com/gomidi/midi/v2" + "gitlab.com/gomidi/midi/v2/smf" ) +const ( + KeyOffset = -12 * 1 // 八度修正偏移 +) + +var AUDIO_CHAN chan uint32 + +func init() { + AUDIO_CHAN = make(chan uint32, 1000) +} + +func PlayAudio() { + audio, err := smf.ReadFile("./audio.mid") + if err != nil { + logger.Error("read midi file error: %v", err) + return + } + tempoChangeList := audio.TempoChanges() + if len(tempoChangeList) != 1 { + logger.Error("midi file format not support") + return + } + tempoChange := tempoChangeList[0] + metricTicks := audio.TimeFormat.(smf.MetricTicks) + tickTime := ((60000000.0 / tempoChange.BPM) / float64(metricTicks.Resolution())) / 1000.0 + logger.Debug("start play audio") + for _, track := range audio.Tracks { + // 全部轨道 + totalTick := uint64(0) + for _, event := range track { + // 单个轨道 + delay := uint32(float64(event.Delta) * tickTime) + // busyPollWaitMilliSecond(delay) + interruptWaitMilliSecond(delay) + totalTick += uint64(delay) + + msg := event.Message + if msg.Type() != midi.NoteOnMsg { + continue + } + midiMsg := midi.Message(msg) + var channel, key, velocity uint8 + midiMsg.GetNoteOn(&channel, &key, &velocity) + // TODO 测试一下客户端是否支持更宽的音域 + // 60 -> 中央C C4 + // if key < 36 || key > 71 { + // continue + // } + note := int32(key) + int32(KeyOffset) + if note < 21 || note > 108 { + // 非88键钢琴音域 + continue + } + if velocity == 0 { + // 可能是NoteOffMsg + continue + } + + AUDIO_CHAN <- uint32(note) + // logger.Debug("send midi note: %v, delay: %v, totalTick: %v", note, delay, totalTick) + } + } +} + +func interruptWaitMilliSecond(delay uint32) { + time.Sleep(time.Millisecond * time.Duration(delay)) +} + +func busyPollWaitMilliSecond(delay uint32) { + start := time.Now() + end := start.Add(time.Millisecond * time.Duration(delay)) + for { + now := time.Now() + if now.After(end) { + break + } + } +} + const ( SCREEN_WIDTH = 80 SCREEN_HEIGHT = 80 @@ -159,9 +240,9 @@ func WriteJpgFile(fileName string, jpg image.Image) { } } -func LoadVideoPlayerFile() error { - inImg := ReadJpgFile("./in.jpg") - if inImg == nil { +func LoadFrameFile() error { + frameImg := ReadJpgFile("./frame.jpg") + if frameImg == nil { return errors.New("file not exist") } FRAME = make([][]bool, SCREEN_WIDTH) @@ -176,20 +257,20 @@ func LoadVideoPlayerFile() error { grayImg := image.NewRGBA(image.Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) for w := 0; w < SCREEN_WIDTH; w++ { for h := 0; h < SCREEN_HEIGHT; h++ { - pix := inImg.At(w, h) + pix := frameImg.At(w, h) r, g, b, _ := pix.RGBA() gray := float32(r>>8)*0.299 + float32(g>>8)*0.587 + float32(b>>8)*0.114 grayImg.SetRGBA(w, h, color.RGBA{R: uint8(gray), G: uint8(gray), B: uint8(gray), A: 255}) grayAvg += uint64(gray) } } - WriteJpgFile("./gray.jpg", grayImg) + WriteJpgFile("./frame_gray.jpg", grayImg) grayAvg /= SCREEN_WIDTH * SCREEN_HEIGHT rgbImg := image.NewRGBA(image.Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) binImg := image.NewRGBA(image.Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) for w := 0; w < SCREEN_WIDTH; w++ { for h := 0; h < SCREEN_HEIGHT; h++ { - pix := inImg.At(w, h) + pix := frameImg.At(w, h) r, g, b, _ := pix.RGBA() gray := float32(r>>8)*0.299 + float32(g>>8)*0.587 + float32(b>>8)*0.114 c := "" @@ -211,15 +292,15 @@ func LoadVideoPlayerFile() error { } } } - WriteJpgFile("./rgb.jpg", rgbImg) - WriteJpgFile("./bin.jpg", binImg) + WriteJpgFile("./frame_rgb.jpg", rgbImg) + WriteJpgFile("./frame_bin.jpg", binImg) return nil } var OBJECT_ID_COUNTER uint64 = math.MaxUint64 -func (g *GameManager) VideoPlayerUpdate(rgb bool) { - err := LoadVideoPlayerFile() +func UpdateFrame(rgb bool) { + err := LoadFrameFile() if err != nil { return } @@ -235,8 +316,8 @@ func (g *GameManager) VideoPlayerUpdate(rgb bool) { GAME_MANAGER.RemoveSceneEntityNotifyBroadcast(scene, proto.VisionType_VISION_REMOVE, SCREEN_ENTITY_ID_LIST) SCREEN_ENTITY_ID_LIST = make([]uint32, 0) leftTopPos := &model.Vector{ - X: BASE_POS.X + float64(float64(SCREEN_WIDTH)*SCREEN_DPI/2), - Y: BASE_POS.Y + float64(float64(SCREEN_HEIGHT)*SCREEN_DPI), + X: BASE_POS.X + float64(SCREEN_WIDTH)*SCREEN_DPI/2, + Y: BASE_POS.Y + float64(SCREEN_HEIGHT)*SCREEN_DPI, Z: BASE_POS.Z, } for w := 0; w < SCREEN_WIDTH; w++ { diff --git a/gs/game/command_controller.go b/gs/game/command_controller.go index b7e0b3d5..5284dd4c 100644 --- a/gs/game/command_controller.go +++ b/gs/game/command_controller.go @@ -338,7 +338,7 @@ func (c *CommandManager) GiveCommand(cmd *CommandMessage) { // ReloadConfigCommand 帮助命令 func (c *CommandManager) ReloadConfigCommand(cmd *CommandMessage) { - LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ + LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{ EventId: ReloadGameDataConfig, } diff --git a/gs/game/local_event_manager.go b/gs/game/local_event_manager.go index 9e6d83a8..a4dea9cd 100644 --- a/gs/game/local_event_manager.go +++ b/gs/game/local_event_manager.go @@ -2,6 +2,7 @@ package game import ( "sort" + "sync" "time" "hk4e/common/mq" @@ -24,6 +25,10 @@ const ( ReloadGameDataConfigFinish // 热更表完成 ) +const ( + UserCopyGoroutineLimit = 4 +) + type LocalEvent struct { EventId int Msg any @@ -62,9 +67,9 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) { case LoadLoginUserFromDbFinish: playerLoginInfo := localEvent.Msg.(*PlayerLoginInfo) if playerLoginInfo.Player != nil { - USER_MANAGER.playerMap[playerLoginInfo.Player.PlayerID] = playerLoginInfo.Player + USER_MANAGER.AddUser(playerLoginInfo.Player) } - GAME_MANAGER.OnLoginOk(playerLoginInfo.UserId, playerLoginInfo.Player, playerLoginInfo.ClientSeq, playerLoginInfo.GateAppId) + GAME_MANAGER.OnLoginOk(playerLoginInfo.UserId, playerLoginInfo.ClientSeq, playerLoginInfo.GateAppId, false, playerLoginInfo.Player) case CheckUserExistOnRegFromDbFinish: playerRegInfo := localEvent.Msg.(*PlayerRegInfo) GAME_MANAGER.OnRegOk(playerRegInfo.Exist, playerRegInfo.Req, playerRegInfo.UserId, playerRegInfo.ClientSeq, playerRegInfo.GateAppId) @@ -73,7 +78,7 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) { case RunUserCopyAndSave: startTime := time.Now().UnixNano() playerList := make(PlayerLastSaveTimeSortList, 0) - for _, player := range USER_MANAGER.playerMap { + for _, player := range USER_MANAGER.GetAllOnlineUserList() { if player.PlayerID < PlayerBaseUid { continue } @@ -84,32 +89,65 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) { insertPlayerList := make([][]byte, 0) updatePlayerList := make([][]byte, 0) saveCount := 0 - for _, player := range playerList { + times := len(playerList) / UserCopyGoroutineLimit + if times == 0 && len(playerList) > 0 { + times = 1 + } + for index := 0; index < times; index++ { totalCostTime := time.Now().UnixNano() - startTime - if totalCostTime > time.Millisecond.Nanoseconds()*50 { - // 总耗时超过50ms就中止本轮保存 - logger.Debug("user copy loop overtime exit, total cost time: %v ns", totalCostTime) + if totalCostTime > time.Millisecond.Nanoseconds()*10 { + // 总耗时超过10ms就中止本轮保存 + logger.Info("user copy loop overtime exit, total cost time: %v ns", totalCostTime) break } - playerData, err := msgpack.Marshal(player) - if err != nil { - logger.Error("marshal player data error: %v", err) - continue + // 分批次并发序列化玩家数据 + oncePlayerListEndIndex := 0 + if index < times-1 { + oncePlayerListEndIndex = (index + 1) * UserCopyGoroutineLimit + } else { + oncePlayerListEndIndex = len(playerList) } - switch player.DbState { - case model.DbNone: - break - case model.DbInsert: - insertPlayerList = append(insertPlayerList, playerData) - USER_MANAGER.playerMap[player.PlayerID].DbState = model.DbNormal - player.LastSaveTime = uint32(time.Now().UnixMilli()) - saveCount++ - case model.DbDelete: - delete(USER_MANAGER.playerMap, player.PlayerID) - case model.DbNormal: - updatePlayerList = append(updatePlayerList, playerData) - player.LastSaveTime = uint32(time.Now().UnixMilli()) - saveCount++ + oncePlayerList := playerList[index*UserCopyGoroutineLimit : oncePlayerListEndIndex] + var playerDataMapLock sync.Mutex + playerDataMap := make(map[uint32][]byte) + var wg sync.WaitGroup + for _, player := range oncePlayerList { + wg.Add(1) + go func(player *model.Player) { + defer func() { + wg.Done() + }() + playerData, err := msgpack.Marshal(player) + if err != nil { + logger.Error("marshal player data error: %v", err) + return + } + playerDataMapLock.Lock() + playerDataMap[player.PlayerID] = playerData + playerDataMapLock.Unlock() + }(player) + } + wg.Wait() + for _, player := range oncePlayerList { + playerData, exist := playerDataMap[player.PlayerID] + if !exist { + continue + } + switch player.DbState { + case model.DbNone: + break + case model.DbInsert: + insertPlayerList = append(insertPlayerList, playerData) + player.DbState = model.DbNormal + player.LastSaveTime = uint32(time.Now().UnixMilli()) + saveCount++ + case model.DbDelete: + USER_MANAGER.DeleteUser(player.PlayerID) + case model.DbNormal: + updatePlayerList = append(updatePlayerList, playerData) + player.LastSaveTime = uint32(time.Now().UnixMilli()) + saveCount++ + } } } saveUserData := &SaveUserData{ @@ -120,10 +158,10 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) { if localEvent.EventId == ExitRunUserCopyAndSave { saveUserData.exitSave = true } - USER_MANAGER.saveUserChan <- saveUserData + USER_MANAGER.GetSaveUserChan() <- saveUserData endTime := time.Now().UnixNano() costTime := endTime - startTime - logger.Debug("run save user copy cost time: %v ns, save user count: %v", costTime, saveCount) + logger.Info("run save user copy cost time: %v ns, save user count: %v", costTime, saveCount) if localEvent.EventId == ExitRunUserCopyAndSave { // 在此阻塞掉主协程 不再进行任何消息和任务的处理 select {} @@ -167,7 +205,10 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) { }() case ReloadGameDataConfigFinish: gdconf.ReplaceGameDataConfig() - // TODO 参考更表一样改成异步加载 + startTime := time.Now().UnixNano() WORLD_MANAGER.LoadSceneBlockAoiMap() + endTime := time.Now().UnixNano() + costTime := endTime - startTime + logger.Info("run [LoadSceneBlockAoiMap], cost time: %v ns", costTime) } } diff --git a/gs/game/player_login.go b/gs/game/player_login.go index 3812c73d..95e37c0a 100644 --- a/gs/game/player_login.go +++ b/gs/game/player_login.go @@ -36,16 +36,16 @@ func (g *GameManager) SetPlayerBornDataReq(userId uint32, clientSeq uint32, gate func (g *GameManager) OnLogin(userId uint32, clientSeq uint32, gateAppId string, isReg bool, regPlayer *model.Player) { logger.Info("user login, uid: %v", userId) if isReg { - g.OnLoginOk(userId, regPlayer, clientSeq, gateAppId) + g.OnLoginOk(userId, clientSeq, gateAppId, true, regPlayer) return } player, isRobot := USER_MANAGER.OnlineUser(userId, clientSeq, gateAppId) if isRobot { - g.OnLoginOk(userId, player, clientSeq, gateAppId) + g.OnLoginOk(userId, clientSeq, gateAppId, false, player) } } -func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq uint32, gateAppId string) { +func (g *GameManager) OnLoginOk(userId uint32, clientSeq uint32, gateAppId string, isReg bool, player *model.Player) { if player == nil { g.SendMsgToGate(cmd.DoSetPlayerBornDataNotify, userId, clientSeq, gateAppId, new(proto.DoSetPlayerBornDataNotify)) return @@ -67,6 +67,27 @@ func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq u dbItem := player.GetDbItem() dbItem.InitAllItem(player) + if isReg { + // 添加选定的主角 + dbAvatar.AddAvatar(player, dbAvatar.MainCharAvatarId) + // 添加主角初始武器 + avatarDataConfig := gdconf.GetAvatarDataById(int32(dbAvatar.MainCharAvatarId)) + if avatarDataConfig == nil { + logger.Error("get avatar data config is nil, avatarId: %v", dbAvatar.MainCharAvatarId) + return + } + weaponId := uint64(g.snowflake.GenId()) + dbWeapon := player.GetDbWeapon() + dbWeapon.AddWeapon(player, uint32(avatarDataConfig.InitialWeapon), weaponId) + weapon := dbWeapon.WeaponMap[weaponId] + dbAvatar.WearWeapon(dbAvatar.MainCharAvatarId, weapon) + + dbTeam := player.GetDbTeam() + dbTeam.GetActiveTeam().SetAvatarIdList([]uint32{dbAvatar.MainCharAvatarId}) + + g.AcceptQuest(player, false) + } + // 确保玩家位置安全 player.Pos.X = player.SafePos.X player.Pos.Y = player.SafePos.Y @@ -81,9 +102,6 @@ func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq u return } - // TODO DEBUG DEL - g.AcceptQuest(player, false) - g.LoginNotify(userId, player, clientSeq) MESSAGE_QUEUE.SendToAll(&mq.NetMsg{ @@ -129,11 +147,71 @@ func (g *GameManager) OnRegOk(exist bool, req *proto.SetPlayerBornDataReq, userI logger.Error("player is nil, uid: %v", userId) return } + USER_MANAGER.ChangeUserDbState(player, model.DbInsert) USER_MANAGER.AddUser(player) g.SendMsgToGate(cmd.SetPlayerBornDataRsp, userId, clientSeq, gateAppId, new(proto.SetPlayerBornDataRsp)) g.OnLogin(userId, clientSeq, gateAppId, true, player) } +func (g *GameManager) CreatePlayer(userId uint32, nickName string, mainCharAvatarId uint32) *model.Player { + player := new(model.Player) + player.PlayerID = userId + player.NickName = nickName + player.Signature = "" + player.HeadImage = mainCharAvatarId + player.Birthday = []uint8{0, 0} + player.NameCard = 210001 + player.NameCardList = make([]uint32, 0) + player.FriendList = make(map[uint32]bool) + player.FriendApplyList = make(map[uint32]bool) + player.PropertiesMap = make(map[uint16]uint32) + player.FlyCloakList = make([]uint32, 0) + player.CostumeList = make([]uint32, 0) + player.ChatMsgMap = make(map[uint32][]*model.ChatMsg) + + player.SceneId = 3 + + player.NameCardList = append(player.NameCardList, 210001, 210042) + + player.PropertiesMap[constant.PLAYER_PROP_PLAYER_LEVEL] = 1 + player.PropertiesMap[constant.PLAYER_PROP_PLAYER_WORLD_LEVEL] = 0 + player.PropertiesMap[constant.PLAYER_PROP_IS_SPRING_AUTO_USE] = 1 + player.PropertiesMap[constant.PLAYER_PROP_SPRING_AUTO_USE_PERCENT] = 100 + player.PropertiesMap[constant.PLAYER_PROP_IS_FLYABLE] = 1 + player.PropertiesMap[constant.PLAYER_PROP_IS_TRANSFERABLE] = 1 + player.PropertiesMap[constant.PLAYER_PROP_MAX_STAMINA] = 24000 + player.PropertiesMap[constant.PLAYER_PROP_CUR_PERSIST_STAMINA] = 24000 + player.PropertiesMap[constant.PLAYER_PROP_PLAYER_RESIN] = 160 + player.PropertiesMap[constant.PLAYER_PROP_PLAYER_MP_SETTING_TYPE] = 2 + player.PropertiesMap[constant.PLAYER_PROP_IS_MP_MODE_AVAILABLE] = 1 + + sceneLuaConfig := gdconf.GetSceneLuaConfigById(int32(player.SceneId)) + if sceneLuaConfig == nil { + logger.Error("get scene lua config is nil, sceneId: %v", player.SceneId) + return nil + } + player.SafePos = &model.Vector{ + X: float64(sceneLuaConfig.SceneConfig.BornPos.X), + Y: float64(sceneLuaConfig.SceneConfig.BornPos.Y), + Z: float64(sceneLuaConfig.SceneConfig.BornPos.Z), + } + player.Pos = &model.Vector{ + X: float64(sceneLuaConfig.SceneConfig.BornPos.X), + Y: float64(sceneLuaConfig.SceneConfig.BornPos.Y), + Z: float64(sceneLuaConfig.SceneConfig.BornPos.Z), + } + player.Rot = &model.Vector{ + X: float64(sceneLuaConfig.SceneConfig.BornRot.X), + Y: float64(sceneLuaConfig.SceneConfig.BornRot.Y), + Z: float64(sceneLuaConfig.SceneConfig.BornRot.Z), + } + + dbAvatar := player.GetDbAvatar() + dbAvatar.MainCharAvatarId = mainCharAvatarId + + return player +} + func (g *GameManager) OnUserOffline(userId uint32, changeGsInfo *ChangeGsInfo) { logger.Info("user offline, uid: %v", userId) player := USER_MANAGER.GetOnlineUser(userId) @@ -164,9 +242,9 @@ func (g *GameManager) LoginNotify(userId uint32, player *model.Player, clientSeq playerLoginRsp := &proto.PlayerLoginRsp{ IsUseAbilityHash: true, AbilityHashCode: 0, - GameBiz: "hk4e_cn", + GameBiz: "hk4e_global", IsScOpen: false, - RegisterCps: "taptap", + RegisterCps: "mihoyo", CountryCode: "CN", Birthday: "2000-01-01", TotalTickTime: 0.0, @@ -345,69 +423,3 @@ func (g *GameManager) PacketOpenStateUpdateNotify() *proto.OpenStateUpdateNotify } return openStateUpdateNotify } - -func (g *GameManager) CreatePlayer(userId uint32, nickName string, mainCharAvatarId uint32) *model.Player { - player := new(model.Player) - player.PlayerID = userId - player.NickName = nickName - player.Signature = "" - player.HeadImage = mainCharAvatarId - player.Birthday = []uint8{0, 0} - player.NameCard = 210001 - player.NameCardList = make([]uint32, 0) - player.NameCardList = append(player.NameCardList, 210001, 210042) - - player.FriendList = make(map[uint32]bool) - player.FriendApplyList = make(map[uint32]bool) - - player.SceneId = 3 - - player.PropertiesMap = make(map[uint16]uint32) - player.PropertiesMap[constant.PLAYER_PROP_PLAYER_LEVEL] = 1 - player.PropertiesMap[constant.PLAYER_PROP_PLAYER_WORLD_LEVEL] = 0 - player.PropertiesMap[constant.PLAYER_PROP_IS_SPRING_AUTO_USE] = 1 - player.PropertiesMap[constant.PLAYER_PROP_SPRING_AUTO_USE_PERCENT] = 100 - player.PropertiesMap[constant.PLAYER_PROP_IS_FLYABLE] = 1 - player.PropertiesMap[constant.PLAYER_PROP_IS_TRANSFERABLE] = 1 - player.PropertiesMap[constant.PLAYER_PROP_MAX_STAMINA] = 24000 - player.PropertiesMap[constant.PLAYER_PROP_CUR_PERSIST_STAMINA] = 24000 - player.PropertiesMap[constant.PLAYER_PROP_PLAYER_RESIN] = 160 - player.PropertiesMap[constant.PLAYER_PROP_PLAYER_MP_SETTING_TYPE] = 2 - player.PropertiesMap[constant.PLAYER_PROP_IS_MP_MODE_AVAILABLE] = 1 - - player.FlyCloakList = make([]uint32, 0) - player.CostumeList = make([]uint32, 0) - - player.SafePos = &model.Vector{X: 2747, Y: 194, Z: -1719} - player.Pos = &model.Vector{X: 2747, Y: 194, Z: -1719} - player.Rot = &model.Vector{X: 0, Y: 307, Z: 0} - - dbAvatar := player.GetDbAvatar() - dbAvatar.MainCharAvatarId = mainCharAvatarId - - player.GameObjectGuidMap = make(map[uint64]model.GameObject) - player.GCGInfo = model.NewGCGInfo() - - // 添加选定的主角 - dbAvatar.AddAvatar(player, mainCharAvatarId) - // 添加主角初始武器 - avatarDataConfig := gdconf.GetAvatarDataById(int32(mainCharAvatarId)) - if avatarDataConfig == nil { - logger.Error("config is nil, mainCharAvatarId: %v", mainCharAvatarId) - return nil - } - weaponId := uint64(g.snowflake.GenId()) - dbWeapon := player.GetDbWeapon() - dbWeapon.AddWeapon(player, uint32(avatarDataConfig.InitialWeapon), weaponId) - weapon := dbWeapon.WeaponMap[weaponId] - dbAvatar.WearWeapon(mainCharAvatarId, weapon) - - dbTeam := player.GetDbTeam() - dbTeam.GetActiveTeam().SetAvatarIdList([]uint32{mainCharAvatarId}) - - player.ChatMsgMap = make(map[uint32][]*model.ChatMsg) - - g.AcceptQuest(player, false) - - return player -} diff --git a/gs/game/player_reliquary.go b/gs/game/player_reliquary.go index 8bbe8560..7f121828 100644 --- a/gs/game/player_reliquary.go +++ b/gs/game/player_reliquary.go @@ -5,6 +5,7 @@ import ( "hk4e/gdconf" "hk4e/gs/model" "hk4e/pkg/logger" + "hk4e/pkg/random" "hk4e/protocol/cmd" "hk4e/protocol/proto" ) @@ -20,6 +21,29 @@ func (g *GameManager) GetAllReliquaryDataConfig() map[int32]*gdconf.ItemData { return allReliquaryDataConfig } +func (g *GameManager) GetReliquaryMainDataRandomByDepotId(mainPropDepotId int32) *gdconf.ReliquaryMainData { + mainPropMap, exist := gdconf.GetReliquaryMainDataMap()[mainPropDepotId] + if !exist { + return nil + } + weightAll := int32(0) + mainPropList := make([]*gdconf.ReliquaryMainData, 0) + for _, data := range mainPropMap { + weightAll += data.RandomWeight + mainPropList = append(mainPropList, data) + } + randNum := random.GetRandomInt32(0, weightAll-1) + sumWeight := int32(0) + // 轮盘选择法 + for _, data := range mainPropList { + sumWeight += data.RandomWeight + if sumWeight > randNum { + return data + } + } + return nil +} + func (g *GameManager) AddUserReliquary(userId uint32, itemId uint32) uint64 { player := USER_MANAGER.GetOnlineUser(userId) if player == nil { @@ -31,7 +55,7 @@ func (g *GameManager) AddUserReliquary(userId uint32, itemId uint32) uint64 { logger.Error("reliquary config error, itemId: %v", itemId) return 0 } - reliquaryMainConfig := gdconf.GetReliquaryMainDataRandomByDepotId(reliquaryConfig.MainPropDepotId) + reliquaryMainConfig := g.GetReliquaryMainDataRandomByDepotId(reliquaryConfig.MainPropDepotId) if reliquaryMainConfig == nil { logger.Error("reliquary main config error, mainPropDepotId: %v", reliquaryConfig.MainPropDepotId) return 0 @@ -53,6 +77,40 @@ func (g *GameManager) AddUserReliquary(userId uint32, itemId uint32) uint64 { return reliquaryId } +func (g *GameManager) GetReliquaryAffixDataRandomByDepotId(appendPropDepotId int32, excludeTypeList ...uint32) *gdconf.ReliquaryAffixData { + appendPropMap, exist := gdconf.GetReliquaryAffixDataMap()[appendPropDepotId] + if !exist { + return nil + } + weightAll := int32(0) + appendPropList := make([]*gdconf.ReliquaryAffixData, 0) + for _, data := range appendPropMap { + isBoth := false + // 排除列表中的属性类型是否相同 + for _, propType := range excludeTypeList { + if propType == uint32(data.PropType) { + isBoth = true + break + } + } + if isBoth { + continue + } + weightAll += data.RandomWeight + appendPropList = append(appendPropList, data) + } + randNum := random.GetRandomInt32(0, weightAll-1) + sumWeight := int32(0) + // 轮盘选择法 + for _, data := range appendPropList { + sumWeight += data.RandomWeight + if sumWeight > randNum { + return data + } + } + return nil +} + // AppendReliquaryProp 圣遗物追加属性 func (g *GameManager) AppendReliquaryProp(reliquary *model.Reliquary, count int32) { // 获取圣遗物配置表 @@ -83,7 +141,7 @@ func (g *GameManager) AppendReliquaryProp(reliquary *model.Reliquary, count int3 excludeTypeList = append(excludeTypeList, uint32(targetAffixConfig.PropType)) } // 将要添加的属性 - appendAffixConfig := gdconf.GetReliquaryAffixDataRandomByDepotId(reliquaryConfig.AppendPropDepotId, excludeTypeList...) + appendAffixConfig := g.GetReliquaryAffixDataRandomByDepotId(reliquaryConfig.AppendPropDepotId, excludeTypeList...) if appendAffixConfig == nil { logger.Error("append affix config error, appendPropDepotId: %v", reliquaryConfig.AppendPropDepotId) return diff --git a/gs/game/tick_manager.go b/gs/game/tick_manager.go index 025acf1d..1bf51a45 100644 --- a/gs/game/tick_manager.go +++ b/gs/game/tick_manager.go @@ -4,6 +4,7 @@ import ( "time" "hk4e/common/constant" + "hk4e/gdconf" "hk4e/pkg/logger" "hk4e/pkg/random" "hk4e/protocol/cmd" @@ -186,6 +187,7 @@ func (t *TickManager) onTickHour(now int64) { func (t *TickManager) onTickMinute(now int64) { // GAME_MANAGER.ServerAnnounceNotify(100, "test123") + gdconf.LuaStateLruRemove() for _, world := range WORLD_MANAGER.GetAllWorld() { for _, player := range world.GetAllPlayer() { // 随机物品 diff --git a/gs/game/user_manager.go b/gs/game/user_manager.go index 01eb19ec..0a874fec 100644 --- a/gs/game/user_manager.go +++ b/gs/game/user_manager.go @@ -93,7 +93,7 @@ func (u *UserManager) CheckUserExistOnReg(userId uint32, req *proto.SetPlayerBor if player != nil { exist = true } - LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ + LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{ EventId: CheckUserExistOnRegFromDbFinish, Msg: &PlayerRegInfo{ Exist: exist, @@ -113,7 +113,6 @@ func (u *UserManager) AddUser(player *model.Player) { if player == nil { return } - u.ChangeUserDbState(player, model.DbInsert) u.playerMap[player.PlayerID] = player } @@ -139,15 +138,25 @@ func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32, gateAppId stri u.DeleteUser(userId) } go func() { + // 加离线玩家数据分布式锁 + ok := u.dao.DistLockSync(userId) + if !ok { + logger.Error("lock redis offline player data error, uid: %v", userId) + return + } player := u.LoadUserFromDbSync(userId) if player != nil { u.SaveUserToRedisSync(player) + } + // 解离线玩家数据分布式锁 + u.dao.DistUnlock(player.PlayerID) + if player != nil { u.ChangeUserDbState(player, model.DbNormal) player.ChatMsgMap = u.LoadUserChatMsgFromDbSync(userId) } else { logger.Error("can not find user from db, uid: %v", userId) } - LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ + LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{ EventId: LoadLoginUserFromDbFinish, Msg: &PlayerLoginInfo{ UserId: userId, @@ -195,7 +204,7 @@ func (u *UserManager) OfflineUser(player *model.Player, changeGsInfo *ChangeGsIn playerCopy.DbState = player.DbState u.SaveUserToDbSync(playerCopy) u.SaveUserToRedisSync(playerCopy) - LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ + LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{ EventId: UserOfflineSaveToDbFinish, Msg: &PlayerOfflineInfo{ Player: player, @@ -322,7 +331,7 @@ func (u *UserManager) LoadGlobalPlayer(userId uint32) (player *model.Player, onl // 正常情况速度较快可以同步阻塞调用 func (u *UserManager) LoadTempOfflineUser(userId uint32, lock bool) *model.Player { player := u.GetOnlineUser(userId) - if player != nil && player.Online { + if player != nil { logger.Error("not allow get a online player as offline player, uid: %v", userId) return nil } @@ -343,7 +352,11 @@ func (u *UserManager) LoadTempOfflineUser(userId uint32, lock bool) *model.Playe logger.Error("try to load a not exist uid, uid: %v", userId) return nil } + startTime := time.Now().UnixNano() player = u.LoadUserFromDbSync(userId) + endTime := time.Now().UnixNano() + costTime := endTime - startTime + logger.Info("try to load player from db sync in game main loop, cost time: %v ns", costTime) if player == nil { // 玩家根本就不存在 logger.Error("try to load a not exist player from db, uid: %v", userId) @@ -361,15 +374,19 @@ func (u *UserManager) LoadTempOfflineUser(userId uint32, lock bool) *model.Playe func (u *UserManager) SaveTempOfflineUser(player *model.Player) { // 主协程同步写入redis u.SaveUserToRedisSync(player) - // 解离线玩家数据分布式锁 - u.dao.DistUnlock(player.PlayerID) // 另一个协程异步的写回db playerData, err := msgpack.Marshal(player) if err != nil { logger.Error("marshal player data error: %v", err) + // 解离线玩家数据分布式锁 + u.dao.DistUnlock(player.PlayerID) return } go func() { + defer func() { + // 解离线玩家数据分布式锁 + u.dao.DistUnlock(player.PlayerID) + }() playerCopy := new(model.Player) err := msgpack.Unmarshal(playerData, playerCopy) if err != nil { @@ -383,6 +400,10 @@ func (u *UserManager) SaveTempOfflineUser(player *model.Player) { // db和redis相关操作 +func (u *UserManager) GetSaveUserChan() chan *SaveUserData { + return u.saveUserChan +} + type SaveUserData struct { insertPlayerList [][]byte updatePlayerList [][]byte @@ -395,7 +416,7 @@ func (u *UserManager) saveUserHandle() { for { <-ticker.C // 保存玩家数据 - LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ + LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{ EventId: RunUserCopyAndSave, } } diff --git a/gs/game/world_manager.go b/gs/game/world_manager.go index 8bb5345c..cd73184f 100644 --- a/gs/game/world_manager.go +++ b/gs/game/world_manager.go @@ -87,7 +87,6 @@ func (w *WorldManager) GetAiWorld() *World { func (w *WorldManager) InitAiWorld(owner *model.Player) { w.aiWorld = w.GetWorldByID(owner.WorldId) w.aiWorld.ChangeToMultiplayer() - go RunPlayAudio() } func (w *WorldManager) IsAiWorld(world *World) bool {