优化代码

This commit is contained in:
flswld
2023-03-12 02:08:15 +08:00
parent 3bcdf75448
commit 8c1dedc472
20 changed files with 407 additions and 296 deletions

View File

@@ -1,5 +1,8 @@
http_port = 8080
[hk4e]
dispatch_url = "https://hk4e.flswld.com/query_cur_region" # 二级dispatch地址 将域名改为dispatch的外网地址
[logger]
level = "DEBUG"
mode = "CONSOLE"

View File

@@ -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 消息队列

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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]

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

1
go.mod
View File

@@ -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

2
go.sum
View File

@@ -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=

View File

@@ -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
}
}
}

View File

@@ -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++ {

View File

@@ -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,
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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() {
// 随机物品

View File

@@ -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,
}
}

View File

@@ -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 {