mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-17 02:42:28 +08:00
优化代码
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++ {
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
// 随机物品
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user