优化代码

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