优化架构

This commit is contained in:
huangxiaolei
2022-11-23 18:05:11 +08:00
parent 3efed3defe
commit 43403202b5
6760 changed files with 33748 additions and 554768 deletions

211
gs/game/aoi/aoi.go Normal file
View File

@@ -0,0 +1,211 @@
package aoi
import (
"fmt"
"hk4e/logger"
)
// aoi管理模块
type AoiManager struct {
// 区域边界坐标
minX int16
maxX int16
minY int16
maxY int16
minZ int16
maxZ int16
numX int16 // x方向格子的数量
numY int16 // y方向的格子数量
numZ int16 // z方向的格子数量
gridMap map[uint32]*Grid // 当前区域中都有哪些格子 key:gid value:格子对象
}
// 初始化aoi区域
func NewAoiManager(minX, maxX, numX, minY, maxY, numY, minZ, maxZ, numZ int16) (r *AoiManager) {
r = new(AoiManager)
r.minX = minX
r.maxX = maxX
r.minY = minY
r.maxY = maxY
r.numX = numX
r.numY = numY
r.minZ = minZ
r.maxZ = maxZ
r.numZ = numZ
r.gridMap = make(map[uint32]*Grid)
logger.LOG.Info("start init aoi area grid, num: %v", uint32(numX)*uint32(numY)*uint32(numZ))
// 初始化aoi区域中所有的格子
for x := int16(0); x < numX; x++ {
for y := int16(0); y < numY; y++ {
for z := int16(0); z < numZ; z++ {
// 利用格子坐标得到格子id gid从0开始按xzy的顺序增长
gid := uint32(y)*(uint32(numX)*uint32(numZ)) + uint32(z)*uint32(numX) + uint32(x)
// 初始化一个格子放在aoi中的map里 key是当前格子的id
grid := NewGrid(
gid,
r.minX+x*r.GridXLen(),
r.minX+(x+1)*r.GridXLen(),
r.minY+y*r.GridYLen(),
r.minY+(y+1)*r.GridYLen(),
r.minZ+z*r.GridZLen(),
r.minZ+(z+1)*r.GridZLen(),
)
r.gridMap[gid] = grid
}
}
}
logger.LOG.Info("init aoi area grid finish")
return r
}
// 每个格子在x轴方向的长度
func (a *AoiManager) GridXLen() int16 {
return (a.maxX - a.minX) / a.numX
}
// 每个格子在y轴方向的长度
func (a *AoiManager) GridYLen() int16 {
return (a.maxY - a.minY) / a.numY
}
// 每个格子在z轴方向的长度
func (a *AoiManager) GridZLen() int16 {
return (a.maxZ - a.minZ) / a.numZ
}
// 通过坐标获取对应的格子id
func (a *AoiManager) GetGidByPos(x, y, z float32) uint32 {
gx := (int16(x) - a.minX) / a.GridXLen()
gy := (int16(y) - a.minY) / a.GridYLen()
gz := (int16(z) - a.minZ) / a.GridZLen()
return uint32(gy)*(uint32(a.numX)*uint32(a.numZ)) + uint32(gz)*uint32(a.numX) + uint32(gx)
}
// 判断坐标是否存在于aoi区域内
func (a *AoiManager) IsValidAoiPos(x, y, z float32) bool {
if (int16(x) > a.minX && int16(x) < a.maxX) &&
(int16(y) > a.minY && int16(y) < a.maxY) &&
(int16(z) > a.minZ && int16(z) < a.maxZ) {
return true
} else {
return false
}
}
// 打印信息方法
func (a *AoiManager) DebugString() string {
s := fmt.Sprintf("AoiMgr: minX: %d, maxX: %d, numX: %d, minY: %d, maxY: %d, numY: %d, minZ: %d, maxZ: %d, numZ: %d\n",
a.minX, a.maxX, a.numX, a.minY, a.maxY, a.numY, a.minZ, a.maxZ, a.numZ)
s += "gridList in AoiMgr:\n"
for _, grid := range a.gridMap {
s += fmt.Sprintln(grid.DebugString())
}
return s
}
// 根据格子的gid得到当前周边的格子信息
func (a *AoiManager) GetSurrGridListByGid(gid uint32) (gridList []*Grid) {
gridList = make([]*Grid, 0)
// 判断grid是否存在
grid, exist := a.gridMap[gid]
if !exist {
return gridList
}
// 添加自己
gridList = append(gridList, grid)
// 根据gid得到当前格子所在的x轴编号
idx := int16(gid % (uint32(a.numX) * uint32(a.numZ)) % uint32(a.numX))
// 判断当前格子左边是否还有格子
if idx > 0 {
gridList = append(gridList, a.gridMap[gid-1])
}
// 判断当前格子右边是否还有格子
if idx < a.numX-1 {
gridList = append(gridList, a.gridMap[gid+1])
}
// 将x轴当前的格子都取出进行遍历 再分别得到每个格子的平面上下是否有格子
// 得到当前x轴的格子id集合
gidListX := make([]uint32, 0)
for _, v := range gridList {
gidListX = append(gidListX, v.gid)
}
// 遍历x轴格子
for _, v := range gidListX {
// 计算该格子的idz
idz := int16(v % (uint32(a.numX) * uint32(a.numZ)) / uint32(a.numX))
// 判断当前格子平面上方是否还有格子
if idz > 0 {
gridList = append(gridList, a.gridMap[v-uint32(a.numX)])
}
// 判断当前格子平面下方是否还有格子
if idz < a.numZ-1 {
gridList = append(gridList, a.gridMap[v+uint32(a.numX)])
}
}
// 将xoz平面当前的格子都取出进行遍历 再分别得到每个格子的空间上下是否有格子
// 得到当前xoz平面的格子id集合
gidListXOZ := make([]uint32, 0)
for _, v := range gridList {
gidListXOZ = append(gidListXOZ, v.gid)
}
// 遍历xoz平面格子
for _, v := range gidListXOZ {
// 计算该格子的idy
idy := int16(v / (uint32(a.numX) * uint32(a.numZ)))
// 判断当前格子空间上方是否还有格子
if idy > 0 {
gridList = append(gridList, a.gridMap[v-uint32(a.numX)*uint32(a.numZ)])
}
// 判断当前格子空间下方是否还有格子
if idy < a.numY-1 {
gridList = append(gridList, a.gridMap[v+uint32(a.numX)*uint32(a.numZ)])
}
}
return gridList
}
// 通过坐标得到周边格子内的全部entityId
func (a *AoiManager) GetEntityIdListByPos(x, y, z float32) (entityIdList []uint32) {
// 根据坐标得到当前坐标属于哪个格子id
gid := a.GetGidByPos(x, y, z)
// 根据格子id得到周边格子的信息
gridList := a.GetSurrGridListByGid(gid)
entityIdList = make([]uint32, 0)
for _, v := range gridList {
tmp := v.GetEntityIdList()
entityIdList = append(entityIdList, tmp...)
//logger.LOG.Debug("Grid: gid: %d, tmp len: %v", v.gid, len(tmp))
}
return entityIdList
}
// 通过gid获取当前格子的全部entityId
func (a *AoiManager) GetEntityIdListByGid(gid uint32) (entityIdList []uint32) {
grid := a.gridMap[gid]
entityIdList = grid.GetEntityIdList()
return entityIdList
}
// 添加一个entityId到一个格子中
func (a *AoiManager) AddEntityIdToGrid(entityId uint32, gid uint32) {
grid := a.gridMap[gid]
grid.AddEntityId(entityId)
}
// 移除一个格子中的entityId
func (a *AoiManager) RemoveEntityIdFromGrid(entityId uint32, gid uint32) {
grid := a.gridMap[gid]
grid.RemoveEntityId(entityId)
}
// 通过坐标添加一个entityId到一个格子中
func (a *AoiManager) AddEntityIdToGridByPos(entityId uint32, x, y, z float32) {
gid := a.GetGidByPos(x, y, z)
a.AddEntityIdToGrid(entityId, gid)
}
// 通过坐标把一个entityId从对应的格子中删除
func (a *AoiManager) RemoveEntityIdFromGridByPos(entityId uint32, x, y, z float32) {
gid := a.GetGidByPos(x, y, z)
a.RemoveEntityIdFromGrid(entityId, gid)
}

30
gs/game/aoi/aoi_test.go Normal file
View File

@@ -0,0 +1,30 @@
package aoi
import (
"hk4e/common/config"
"hk4e/logger"
"testing"
)
func TestAoiManagerGetSurrGridListByGid(t *testing.T) {
filePath := "./application.toml"
config.InitConfig(filePath)
logger.InitLogger("")
aoiManager := NewAoiManager(
-150, 150, 3,
-150, 150, 3,
-150, 150, 3,
)
logger.LOG.Debug("aoiManager: %s", aoiManager.DebugString())
for k := range aoiManager.gridMap {
// 得到当前格子周边的九宫格
gridList := aoiManager.GetSurrGridListByGid(k)
// 得到九宫格所有的id
logger.LOG.Debug("gid: %d gridList len: %d", k, len(gridList))
gidList := make([]uint32, 0, len(gridList))
for _, grid := range gridList {
gidList = append(gidList, grid.gid)
}
logger.LOG.Debug("Grid: gid: %d, surr grid gid list: %v", k, gidList)
}
}

63
gs/game/aoi/grid.go Normal file
View File

@@ -0,0 +1,63 @@
package aoi
import (
"fmt"
"hk4e/logger"
)
// 地图格子
type Grid struct {
gid uint32 // 格子id
// 格子边界坐标
minX int16
maxX int16
minY int16
maxY int16
minZ int16
maxZ int16
entityIdMap map[uint32]bool // k:entityId v:是否存在
}
// 初始化格子
func NewGrid(gid uint32, minX, maxX, minY, maxY, minZ, maxZ int16) (r *Grid) {
r = new(Grid)
r.gid = gid
r.minX = minX
r.maxX = maxX
r.minY = minY
r.maxY = maxY
r.minZ = minZ
r.maxZ = maxZ
r.entityIdMap = make(map[uint32]bool)
return r
}
// 向格子中添加一个实体id
func (g *Grid) AddEntityId(entityId uint32) {
g.entityIdMap[entityId] = true
}
// 从格子中删除一个实体id
func (g *Grid) RemoveEntityId(entityId uint32) {
_, exist := g.entityIdMap[entityId]
if exist {
delete(g.entityIdMap, entityId)
} else {
logger.LOG.Error("remove entity id but it not exist, entityId: %v", entityId)
}
}
// 获取格子中所有实体id
func (g *Grid) GetEntityIdList() (entityIdList []uint32) {
entityIdList = make([]uint32, 0)
for k := range g.entityIdMap {
entityIdList = append(entityIdList, k)
}
return entityIdList
}
// 打印信息方法
func (g *Grid) DebugString() string {
return fmt.Sprintf("Grid: gid: %d, minX: %d, maxX: %d, minY: %d, maxY: %d, minZ: %d, maxZ: %d, entityIdMap: %v",
g.gid, g.minX, g.maxX, g.minY, g.maxY, g.minZ, g.maxZ, g.entityIdMap)
}

122
gs/game/game_manager.go Normal file
View File

@@ -0,0 +1,122 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/alg"
"hk4e/gate/entity/gm"
"hk4e/gate/kcp"
"hk4e/gs/dao"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
)
type GameManager struct {
dao *dao.Dao
netMsgInput chan *cmd.NetMsg
netMsgOutput chan *cmd.NetMsg
snowflake *alg.SnowflakeWorker
// 本地事件队列管理器
localEventManager *LocalEventManager
// 接口路由管理器
routeManager *RouteManager
// 用户管理器
userManager *UserManager
// 世界管理器
worldManager *WorldManager
// 游戏服务器tick
tickManager *TickManager
}
func NewGameManager(dao *dao.Dao, netMsgInput chan *cmd.NetMsg, netMsgOutput chan *cmd.NetMsg) (r *GameManager) {
r = new(GameManager)
r.dao = dao
r.netMsgInput = netMsgInput
r.netMsgOutput = netMsgOutput
r.snowflake = alg.NewSnowflakeWorker(1)
r.localEventManager = NewLocalEventManager(r)
r.routeManager = NewRouteManager(r)
r.userManager = NewUserManager(dao, r.localEventManager.localEventChan)
r.worldManager = NewWorldManager(r.snowflake)
r.tickManager = NewTickManager(r)
//r.worldManager.worldStatic.InitTerrain()
//r.worldManager.worldStatic.Pathfinding()
//r.worldManager.worldStatic.ConvPathVectorListToAiMoveVectorList()
// 大世界的主人
r.OnRegOk(false, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: "大世界的主人"}, 1, 0)
bigWorldOwner := r.userManager.GetOnlineUser(1)
bigWorldOwner.SceneLoadState = model.SceneEnterDone
bigWorldOwner.DbState = model.DbNormal
r.worldManager.InitBigWorld(bigWorldOwner)
return r
}
func (g *GameManager) Start() {
g.routeManager.InitRoute()
g.userManager.StartAutoSaveUser()
go func() {
for {
select {
case netMsg := <-g.netMsgOutput:
// 接收客户端消息
g.routeManager.RouteHandle(netMsg)
case <-g.tickManager.ticker.C:
// 游戏服务器定时帧
g.tickManager.OnGameServerTick()
case localEvent := <-g.localEventManager.localEventChan:
// 处理本地事件
g.localEventManager.LocalEventHandle(localEvent)
}
}
}()
}
func (g *GameManager) Stop() {
g.worldManager.worldStatic.SaveTerrain()
}
// 发送消息给客户端
func (g *GameManager) SendMsg(cmdId uint16, userId uint32, clientSeq uint32, payloadMsg pb.Message) {
if userId < 100000000 {
return
}
netMsg := new(cmd.NetMsg)
netMsg.UserId = userId
netMsg.EventId = cmd.NormalMsg
netMsg.CmdId = cmdId
netMsg.ClientSeq = clientSeq
// 在这里直接序列化成二进制数据 防止发送的消息内包含各种游戏数据指针 而造成并发读写的问题
payloadMessageData, err := pb.Marshal(payloadMsg)
if err != nil {
logger.LOG.Error("parse payload msg to bin error: %v", err)
return
}
netMsg.PayloadMessageData = payloadMessageData
g.netMsgInput <- netMsg
}
func (g *GameManager) ReconnectPlayer(userId uint32) {
g.SendMsg(cmd.ClientReconnectNotify, userId, 0, new(proto.ClientReconnectNotify))
}
func (g *GameManager) DisconnectPlayer(userId uint32) {
g.SendMsg(cmd.ServerDisconnectClientNotify, userId, 0, new(proto.ServerDisconnectClientNotify))
}
func (g *GameManager) KickPlayer(userId uint32) {
info := new(gm.KickPlayerInfo)
info.UserId = userId
// 客户端提示信息为服务器断开连接
info.Reason = uint32(kcp.EnetServerKick)
var result bool
ok := false
//ok := r.hk4eGatewayConsumer.CallFunction("RpcManager", "KickPlayer", &info, &result)
if ok == true && result == true {
return
}
return
}

View File

@@ -0,0 +1,34 @@
package game
const (
LoadLoginUserFromDbFinish = iota
CheckUserExistOnRegFromDbFinish
)
type LocalEvent struct {
EventId int
Msg any
}
type LocalEventManager struct {
localEventChan chan *LocalEvent
gameManager *GameManager
}
func NewLocalEventManager(gameManager *GameManager) (r *LocalEventManager) {
r = new(LocalEventManager)
r.localEventChan = make(chan *LocalEvent, 1000)
r.gameManager = gameManager
return r
}
func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) {
switch localEvent.EventId {
case LoadLoginUserFromDbFinish:
playerLoginInfo := localEvent.Msg.(*PlayerLoginInfo)
l.gameManager.OnLoginOk(playerLoginInfo.UserId, playerLoginInfo.Player, playerLoginInfo.ClientSeq)
case CheckUserExistOnRegFromDbFinish:
playerRegInfo := localEvent.Msg.(*PlayerRegInfo)
l.gameManager.OnRegOk(playerRegInfo.Exist, playerRegInfo.Req, playerRegInfo.UserId, playerRegInfo.ClientSeq)
}
}

View File

@@ -0,0 +1,104 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
func (g *GameManager) PlayerSetPauseReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user pause, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PlayerSetPauseReq)
isPaused := req.IsPaused
player.Pause = isPaused
// PacketPlayerSetPauseRsp
playerSetPauseRsp := new(proto.PlayerSetPauseRsp)
g.SendMsg(cmd.PlayerSetPauseRsp, player.PlayerID, player.ClientSeq, playerSetPauseRsp)
}
func (g *GameManager) TowerAllDataReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get tower all data, uid: %v", player.PlayerID)
// PacketTowerAllDataRsp
towerAllDataRsp := new(proto.TowerAllDataRsp)
towerAllDataRsp.TowerScheduleId = 29
towerAllDataRsp.TowerFloorRecordList = []*proto.TowerFloorRecord{{FloorId: 1001}}
towerAllDataRsp.CurLevelRecord = &proto.TowerCurLevelRecord{IsEmpty: true}
towerAllDataRsp.NextScheduleChangeTime = 4294967295
towerAllDataRsp.FloorOpenTimeMap = make(map[uint32]uint32)
towerAllDataRsp.FloorOpenTimeMap[1024] = 1630486800
towerAllDataRsp.FloorOpenTimeMap[1025] = 1630486800
towerAllDataRsp.FloorOpenTimeMap[1026] = 1630486800
towerAllDataRsp.FloorOpenTimeMap[1027] = 1630486800
towerAllDataRsp.ScheduleStartTime = 1630486800
g.SendMsg(cmd.TowerAllDataRsp, player.PlayerID, player.ClientSeq, towerAllDataRsp)
}
func (g *GameManager) EntityAiSyncNotify(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user entity ai sync, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.EntityAiSyncNotify)
if len(req.LocalAvatarAlertedMonsterList) == 0 {
return
}
// PacketEntityAiSyncNotify
entityAiSyncNotify := new(proto.EntityAiSyncNotify)
entityAiSyncNotify.InfoList = make([]*proto.AiSyncInfo, 0)
for _, monsterId := range req.LocalAvatarAlertedMonsterList {
entityAiSyncNotify.InfoList = append(entityAiSyncNotify.InfoList, &proto.AiSyncInfo{
EntityId: monsterId,
HasPathToTarget: true,
IsSelfKilling: false,
})
}
g.SendMsg(cmd.EntityAiSyncNotify, player.PlayerID, player.ClientSeq, entityAiSyncNotify)
}
func (g *GameManager) ClientTimeNotify(userId uint32, clientTime uint32) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
logger.LOG.Debug("client time notify, uid: %v, time: %v", userId, clientTime)
player.ClientTime = clientTime
}
func (g *GameManager) ClientRttNotify(userId uint32, clientRtt uint32) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
logger.LOG.Debug("client rtt notify, uid: %v, rtt: %v", userId, clientRtt)
player.ClientRTT = clientRtt
}
func (g *GameManager) ServerAnnounceNotify(announceId uint32, announceMsg string) {
for _, onlinePlayer := range g.userManager.GetAllOnlineUserList() {
serverAnnounceNotify := new(proto.ServerAnnounceNotify)
now := uint32(time.Now().Unix())
serverAnnounceNotify.AnnounceDataList = []*proto.AnnounceData{{
ConfigId: announceId,
BeginTime: now + 1,
EndTime: now + 2,
CenterSystemText: announceMsg,
CenterSystemFrequency: 1,
}}
g.SendMsg(cmd.ServerAnnounceNotify, onlinePlayer.PlayerID, 0, serverAnnounceNotify)
}
}
func (g *GameManager) ServerAnnounceRevokeNotify(announceId uint32) {
for _, onlinePlayer := range g.userManager.GetAllOnlineUserList() {
serverAnnounceRevokeNotify := new(proto.ServerAnnounceRevokeNotify)
serverAnnounceRevokeNotify.ConfigIdList = []uint32{announceId}
g.SendMsg(cmd.ServerAnnounceRevokeNotify, onlinePlayer.PlayerID, 0, serverAnnounceRevokeNotify)
}
}

116
gs/game/route_manager.go Normal file
View File

@@ -0,0 +1,116 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
)
type HandlerFunc func(player *model.Player, payloadMsg pb.Message)
type RouteManager struct {
gameManager *GameManager
// k:cmdId v:HandlerFunc
handlerFuncRouteMap map[uint16]HandlerFunc
}
func NewRouteManager(gameManager *GameManager) (r *RouteManager) {
r = new(RouteManager)
r.gameManager = gameManager
r.handlerFuncRouteMap = make(map[uint16]HandlerFunc)
return r
}
func (r *RouteManager) registerRouter(cmdId uint16, handlerFunc HandlerFunc) {
r.handlerFuncRouteMap[cmdId] = handlerFunc
}
func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, payloadMsg pb.Message) {
handlerFunc, ok := r.handlerFuncRouteMap[cmdId]
if !ok {
logger.LOG.Error("no route for msg, cmdId: %v", cmdId)
return
}
player := r.gameManager.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
player.ClientSeq = clientSeq
handlerFunc(player, payloadMsg)
}
func (r *RouteManager) InitRoute() {
r.registerRouter(cmd.PlayerSetPauseReq, r.gameManager.PlayerSetPauseReq)
r.registerRouter(cmd.EnterSceneReadyReq, r.gameManager.EnterSceneReadyReq)
r.registerRouter(cmd.PathfindingEnterSceneReq, r.gameManager.PathfindingEnterSceneReq)
r.registerRouter(cmd.GetScenePointReq, r.gameManager.GetScenePointReq)
r.registerRouter(cmd.GetSceneAreaReq, r.gameManager.GetSceneAreaReq)
r.registerRouter(cmd.SceneInitFinishReq, r.gameManager.SceneInitFinishReq)
r.registerRouter(cmd.EnterSceneDoneReq, r.gameManager.EnterSceneDoneReq)
r.registerRouter(cmd.EnterWorldAreaReq, r.gameManager.EnterWorldAreaReq)
r.registerRouter(cmd.PostEnterSceneReq, r.gameManager.PostEnterSceneReq)
r.registerRouter(cmd.TowerAllDataReq, r.gameManager.TowerAllDataReq)
r.registerRouter(cmd.SceneTransToPointReq, r.gameManager.SceneTransToPointReq)
r.registerRouter(cmd.MarkMapReq, r.gameManager.MarkMapReq)
r.registerRouter(cmd.ChangeAvatarReq, r.gameManager.ChangeAvatarReq)
r.registerRouter(cmd.SetUpAvatarTeamReq, r.gameManager.SetUpAvatarTeamReq)
r.registerRouter(cmd.ChooseCurAvatarTeamReq, r.gameManager.ChooseCurAvatarTeamReq)
r.registerRouter(cmd.GetGachaInfoReq, r.gameManager.GetGachaInfoReq)
r.registerRouter(cmd.DoGachaReq, r.gameManager.DoGachaReq)
r.registerRouter(cmd.QueryPathReq, r.gameManager.QueryPathReq)
r.registerRouter(cmd.CombatInvocationsNotify, r.gameManager.CombatInvocationsNotify)
r.registerRouter(cmd.AbilityInvocationsNotify, r.gameManager.AbilityInvocationsNotify)
r.registerRouter(cmd.ClientAbilityInitFinishNotify, r.gameManager.ClientAbilityInitFinishNotify)
r.registerRouter(cmd.EntityAiSyncNotify, r.gameManager.EntityAiSyncNotify)
r.registerRouter(cmd.WearEquipReq, r.gameManager.WearEquipReq)
r.registerRouter(cmd.ChangeGameTimeReq, r.gameManager.ChangeGameTimeReq)
r.registerRouter(cmd.GetPlayerSocialDetailReq, r.gameManager.GetPlayerSocialDetailReq)
r.registerRouter(cmd.SetPlayerBirthdayReq, r.gameManager.SetPlayerBirthdayReq)
r.registerRouter(cmd.SetNameCardReq, r.gameManager.SetNameCardReq)
r.registerRouter(cmd.SetPlayerSignatureReq, r.gameManager.SetPlayerSignatureReq)
r.registerRouter(cmd.SetPlayerNameReq, r.gameManager.SetPlayerNameReq)
r.registerRouter(cmd.SetPlayerHeadImageReq, r.gameManager.SetPlayerHeadImageReq)
r.registerRouter(cmd.GetAllUnlockNameCardReq, r.gameManager.GetAllUnlockNameCardReq)
r.registerRouter(cmd.GetPlayerFriendListReq, r.gameManager.GetPlayerFriendListReq)
r.registerRouter(cmd.GetPlayerAskFriendListReq, r.gameManager.GetPlayerAskFriendListReq)
r.registerRouter(cmd.AskAddFriendReq, r.gameManager.AskAddFriendReq)
r.registerRouter(cmd.DealAddFriendReq, r.gameManager.DealAddFriendReq)
r.registerRouter(cmd.GetOnlinePlayerListReq, r.gameManager.GetOnlinePlayerListReq)
r.registerRouter(cmd.PlayerApplyEnterMpReq, r.gameManager.PlayerApplyEnterMpReq)
r.registerRouter(cmd.PlayerApplyEnterMpResultReq, r.gameManager.PlayerApplyEnterMpResultReq)
r.registerRouter(cmd.PlayerGetForceQuitBanInfoReq, r.gameManager.PlayerGetForceQuitBanInfoReq)
r.registerRouter(cmd.GetShopmallDataReq, r.gameManager.GetShopmallDataReq)
r.registerRouter(cmd.GetShopReq, r.gameManager.GetShopReq)
r.registerRouter(cmd.BuyGoodsReq, r.gameManager.BuyGoodsReq)
r.registerRouter(cmd.McoinExchangeHcoinReq, r.gameManager.McoinExchangeHcoinReq)
r.registerRouter(cmd.AvatarChangeCostumeReq, r.gameManager.AvatarChangeCostumeReq)
r.registerRouter(cmd.AvatarWearFlycloakReq, r.gameManager.AvatarWearFlycloakReq)
r.registerRouter(cmd.PullRecentChatReq, r.gameManager.PullRecentChatReq)
r.registerRouter(cmd.PullPrivateChatReq, r.gameManager.PullPrivateChatReq)
r.registerRouter(cmd.PrivateChatReq, r.gameManager.PrivateChatReq)
r.registerRouter(cmd.ReadPrivateChatReq, r.gameManager.ReadPrivateChatReq)
r.registerRouter(cmd.PlayerChatReq, r.gameManager.PlayerChatReq)
r.registerRouter(cmd.BackMyWorldReq, r.gameManager.BackMyWorldReq)
r.registerRouter(cmd.ChangeWorldToSingleModeReq, r.gameManager.ChangeWorldToSingleModeReq)
r.registerRouter(cmd.SceneKickPlayerReq, r.gameManager.SceneKickPlayerReq)
r.registerRouter(cmd.ChangeMpTeamAvatarReq, r.gameManager.ChangeMpTeamAvatarReq)
}
func (r *RouteManager) RouteHandle(netMsg *cmd.NetMsg) {
switch netMsg.EventId {
case cmd.NormalMsg:
r.doRoute(netMsg.CmdId, netMsg.UserId, netMsg.ClientSeq, netMsg.PayloadMessage)
case cmd.UserRegNotify:
r.gameManager.OnReg(netMsg.UserId, netMsg.ClientSeq, netMsg.PayloadMessage)
case cmd.UserLoginNotify:
r.gameManager.OnLogin(netMsg.UserId, netMsg.ClientSeq)
case cmd.UserOfflineNotify:
r.gameManager.OnUserOffline(netMsg.UserId)
case cmd.ClientRttNotify:
r.gameManager.ClientRttNotify(netMsg.UserId, netMsg.ClientRtt)
case cmd.ClientTimeNotify:
r.gameManager.ClientTimeNotify(netMsg.UserId, netMsg.ClientTime)
}
}

311
gs/game/tick_manager.go Normal file
View File

@@ -0,0 +1,311 @@
package game
import (
"hk4e/common/utils/random"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
type TickManager struct {
ticker *time.Ticker
tickCount uint64
gameManager *GameManager
}
func NewTickManager(gameManager *GameManager) (r *TickManager) {
r = new(TickManager)
r.ticker = time.NewTicker(time.Millisecond * 100)
logger.LOG.Info("game server tick start at: %v", time.Now().UnixMilli())
r.gameManager = gameManager
return r
}
func (t *TickManager) OnGameServerTick() {
t.tickCount++
now := time.Now().UnixMilli()
t.onTick100MilliSecond(now)
if t.tickCount%(10*1) == 0 {
t.onTickSecond(now)
}
if t.tickCount%(10*5) == 0 {
t.onTick5Second(now)
}
if t.tickCount%(10*10) == 0 {
t.onTick10Second(now)
}
if t.tickCount%(10*60) == 0 {
t.onTickMinute(now)
}
if t.tickCount%(10*60*10) == 0 {
t.onTick10Minute(now)
}
if t.tickCount%(10*3600) == 0 {
t.onTickHour(now)
}
if t.tickCount%(10*3600*24) == 0 {
t.onTickDay(now)
}
if t.tickCount%(10*3600*24*7) == 0 {
t.onTickWeek(now)
}
}
func (t *TickManager) onTickWeek(now int64) {
logger.LOG.Info("on tick week, time: %v", now)
}
func (t *TickManager) onTickDay(now int64) {
logger.LOG.Info("on tick day, time: %v", now)
}
func (t *TickManager) onTickHour(now int64) {
logger.LOG.Info("on tick hour, time: %v", now)
}
func (t *TickManager) onTick10Minute(now int64) {
for _, world := range t.gameManager.worldManager.worldMap {
for _, player := range world.playerMap {
// 蓝球粉球
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: 223, ChangeCount: 1}}, true, 0)
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: 224, ChangeCount: 1}}, true, 0)
}
}
}
func (t *TickManager) onTickMinute(now int64) {
t.gameManager.ServerAnnounceNotify(100, "test123")
for _, world := range t.gameManager.worldManager.worldMap {
for _, player := range world.playerMap {
// 随机物品
allItemDataConfig := t.gameManager.GetAllItemDataConfig()
count := random.GetRandomInt32(0, 4)
i := int32(0)
for itemId := range allItemDataConfig {
itemDataConfig := allItemDataConfig[itemId]
// TODO 3.0.0REL版本中 发送某些无效家具 可能会导致客户端背包家具界面卡死
if itemDataConfig.ItemEnumType == constant.ItemTypeConst.ITEM_FURNITURE {
continue
}
num := random.GetRandomInt32(1, 9)
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: uint32(itemId), ChangeCount: uint32(num)}}, true, 0)
i++
if i > count {
break
}
}
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: 102, ChangeCount: 30}}, true, 0)
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: 201, ChangeCount: 10}}, true, 0)
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: 202, ChangeCount: 100}}, true, 0)
t.gameManager.AddUserItem(player.PlayerID, []*UserItem{{ItemId: 203, ChangeCount: 10}}, true, 0)
}
}
}
func (t *TickManager) onTick10Second(now int64) {
for _, world := range t.gameManager.worldManager.worldMap {
if !world.IsBigWorld() && (world.multiplayer || !world.owner.Pause) {
// 刷怪
scene := world.GetSceneById(3)
monsterEntityCount := 0
for _, entity := range scene.entityMap {
if entity.entityType == uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_MONSTER) {
monsterEntityCount++
}
}
if monsterEntityCount < 30 {
monsterEntityId := t.createMonster(scene)
bigWorldOwner := t.gameManager.userManager.GetOnlineUser(1)
t.gameManager.AddSceneEntityNotify(bigWorldOwner, proto.VisionType_VISION_TYPE_BORN, []uint32{monsterEntityId}, true)
}
}
for _, player := range world.playerMap {
if world.multiplayer || !world.owner.Pause {
// 改面板
team := player.TeamConfig.GetActiveTeam()
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
avatar := player.AvatarMap[avatarId]
avatar.FightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_ATTACK)] = 1000000
avatar.FightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CRITICAL)] = 1.0
t.gameManager.UpdateUserAvatarFightProp(player.PlayerID, avatarId)
}
}
}
}
}
func (t *TickManager) onTick5Second(now int64) {
for _, world := range t.gameManager.worldManager.worldMap {
if world.IsBigWorld() {
for applyUid := range world.owner.CoopApplyMap {
t.gameManager.UserDealEnterWorld(world.owner, applyUid, true)
}
}
for _, player := range world.playerMap {
if world.multiplayer {
// PacketWorldPlayerLocationNotify
worldPlayerLocationNotify := new(proto.WorldPlayerLocationNotify)
for _, worldPlayer := range world.playerMap {
playerWorldLocationInfo := &proto.PlayerWorldLocationInfo{
SceneId: worldPlayer.SceneId,
PlayerLoc: &proto.PlayerLocationInfo{
Uid: worldPlayer.PlayerID,
Pos: &proto.Vector{
X: float32(worldPlayer.Pos.X),
Y: float32(worldPlayer.Pos.Y),
Z: float32(worldPlayer.Pos.Z),
},
Rot: &proto.Vector{
X: float32(worldPlayer.Rot.X),
Y: float32(worldPlayer.Rot.Y),
Z: float32(worldPlayer.Rot.Z),
},
},
}
worldPlayerLocationNotify.PlayerWorldLocList = append(worldPlayerLocationNotify.PlayerWorldLocList, playerWorldLocationInfo)
}
t.gameManager.SendMsg(cmd.WorldPlayerLocationNotify, player.PlayerID, 0, worldPlayerLocationNotify)
// PacketScenePlayerLocationNotify
scene := world.GetSceneById(player.SceneId)
scenePlayerLocationNotify := new(proto.ScenePlayerLocationNotify)
scenePlayerLocationNotify.SceneId = player.SceneId
for _, scenePlayer := range scene.playerMap {
playerLocationInfo := &proto.PlayerLocationInfo{
Uid: scenePlayer.PlayerID,
Pos: &proto.Vector{
X: float32(scenePlayer.Pos.X),
Y: float32(scenePlayer.Pos.Y),
Z: float32(scenePlayer.Pos.Z),
},
Rot: &proto.Vector{
X: float32(scenePlayer.Rot.X),
Y: float32(scenePlayer.Rot.Y),
Z: float32(scenePlayer.Rot.Z),
},
}
scenePlayerLocationNotify.PlayerLocList = append(scenePlayerLocationNotify.PlayerLocList, playerLocationInfo)
}
t.gameManager.SendMsg(cmd.ScenePlayerLocationNotify, player.PlayerID, 0, scenePlayerLocationNotify)
}
}
}
}
func (t *TickManager) onTickSecond(now int64) {
for _, world := range t.gameManager.worldManager.worldMap {
for _, player := range world.playerMap {
// PacketWorldPlayerRTTNotify
worldPlayerRTTNotify := new(proto.WorldPlayerRTTNotify)
worldPlayerRTTNotify.PlayerRttList = make([]*proto.PlayerRTTInfo, 0)
for _, worldPlayer := range world.playerMap {
playerRTTInfo := &proto.PlayerRTTInfo{Uid: worldPlayer.PlayerID, Rtt: worldPlayer.ClientRTT}
worldPlayerRTTNotify.PlayerRttList = append(worldPlayerRTTNotify.PlayerRttList, playerRTTInfo)
}
t.gameManager.SendMsg(cmd.WorldPlayerRTTNotify, player.PlayerID, 0, worldPlayerRTTNotify)
}
}
}
func (t *TickManager) onTick100MilliSecond(now int64) {
// AttackHandler
for _, world := range t.gameManager.worldManager.worldMap {
for _, scene := range world.sceneMap {
scene.AttackHandler(t.gameManager)
}
}
//bigWorldOwner := t.gameManager.userManager.GetOnlineUser(1)
//bigWorld := t.gameManager.worldManager.GetBigWorld()
//bigWorldScene := bigWorld.GetSceneById(3)
//
//if len(bigWorldScene.playerMap) < 2 {
// return
//}
//if t.gameManager.worldManager.worldStatic.aiMoveCurrIndex >= len(t.gameManager.worldManager.worldStatic.aiMoveVectorList)-1 {
// return
//}
//t.gameManager.worldManager.worldStatic.aiMoveCurrIndex++
//
//entityMoveInfo := new(proto.EntityMoveInfo)
//activeAvatarId := bigWorldOwner.TeamConfig.GetActiveAvatarId()
//playerTeamEntity := bigWorldScene.GetPlayerTeamEntity(bigWorldOwner.PlayerID)
//entityMoveInfo.EntityId = playerTeamEntity.avatarEntityMap[activeAvatarId]
//entityMoveInfo.SceneTime = uint32(bigWorldScene.GetSceneTime())
//entityMoveInfo.ReliableSeq = uint32(bigWorldScene.GetSceneTime() / 100 * 100)
//entityMoveInfo.IsReliable = true
//oldPos := model.Vector{
// X: bigWorldOwner.Pos.X,
// Y: bigWorldOwner.Pos.Y,
// Z: bigWorldOwner.Pos.Z,
//}
//newPos := t.gameManager.worldManager.worldStatic.aiMoveVectorList[t.gameManager.worldManager.worldStatic.aiMoveCurrIndex]
//rotY := math.Atan2(newPos.X-oldPos.X, newPos.Z-oldPos.Z) / math.Pi * 180.0
//if rotY < 0.0 {
// rotY += 360.0
//}
//entityMoveInfo.MotionInfo = &proto.MotionInfo{
// Pos: &proto.Vector{
// X: float32(newPos.X),
// Y: float32(newPos.Y),
// Z: float32(newPos.Z),
// },
// Rot: &proto.Vector{
// X: 0.0,
// Y: float32(rotY),
// Z: 0.0,
// },
// Speed: &proto.Vector{
// X: float32((newPos.X - oldPos.X) * 10.0),
// Y: float32((newPos.Y - oldPos.Y) * 10.0),
// Z: float32((newPos.Z - oldPos.Z) * 10.0),
// },
// State: proto.MotionState_MOTION_STATE_RUN,
// RefPos: new(proto.Vector),
//}
//data, err := pb.Marshal(entityMoveInfo)
//if err != nil {
// logger.LOG.Error("build combat invocations entity move info error: %v", err)
// return
//}
//combatInvocationsNotify := new(proto.CombatInvocationsNotify)
//combatInvocationsNotify.InvokeList = []*proto.CombatInvokeEntry{{
// CombatData: data,
// ForwardType: proto.ForwardType_FORWARD_TYPE_TO_ALL_EXCEPT_CUR,
// ArgumentType: proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ENTITY_MOVE,
//}}
//t.gameManager.CombatInvocationsNotify(bigWorldOwner.PlayerID, bigWorldOwner, 0, combatInvocationsNotify)
}
func (t *TickManager) createMonster(scene *Scene) uint32 {
pos := &model.Vector{
X: 2747,
Y: 194,
Z: -1719,
}
fpm := map[uint32]float32{
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): float32(72.91699),
uint32(constant.FightPropertyConst.FIGHT_PROP_PHYSICAL_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_DEFENSE): float32(505.0),
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_ATTACK): float32(45.679916),
uint32(constant.FightPropertyConst.FIGHT_PROP_ICE_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_ATTACK): float32(45.679916),
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): float32(72.91699),
uint32(constant.FightPropertyConst.FIGHT_PROP_FIRE_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_ELEC_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_WIND_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_ROCK_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_GRASS_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_WATER_SUB_HURT): float32(0.1),
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(72.91699),
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_DEFENSE): float32(505.0),
}
entityId := scene.CreateEntityMonster(pos, 1, fpm)
return entityId
}

323
gs/game/user_avatar.go Normal file
View File

@@ -0,0 +1,323 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/object"
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
)
func (g *GameManager) GetAllAvatarDataConfig() map[int32]*gdc.AvatarData {
allAvatarDataConfig := make(map[int32]*gdc.AvatarData)
for avatarId, avatarData := range gdc.CONF.AvatarDataMap {
if avatarId < 10000002 || avatarId >= 11000000 {
// 跳过无效角色
continue
}
if avatarId == 10000005 || avatarId == 10000007 {
// 跳过主角
continue
}
allAvatarDataConfig[avatarId] = avatarData
}
return allAvatarDataConfig
}
func (g *GameManager) AddUserAvatar(userId uint32, avatarId uint32) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
player.AddAvatar(avatarId)
// 添加初始武器
avatarDataConfig := gdc.CONF.AvatarDataMap[int32(avatarId)]
weaponId := g.AddUserWeapon(player.PlayerID, uint32(avatarDataConfig.InitialWeapon))
// 角色装上初始武器
g.WearUserAvatarEquip(player.PlayerID, avatarId, weaponId)
// TODO 真的有必要存在吗
g.UpdateUserAvatarFightProp(player.PlayerID, avatarId)
// PacketAvatarAddNotify
avatar := player.AvatarMap[avatarId]
avatarAddNotify := new(proto.AvatarAddNotify)
avatarAddNotify.Avatar = g.PacketAvatarInfo(avatar)
avatarAddNotify.IsInTeam = false
g.SendMsg(cmd.AvatarAddNotify, userId, player.ClientSeq, avatarAddNotify)
}
func (g *GameManager) WearEquipReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user wear equip, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.WearEquipReq)
avatarGuid := req.AvatarGuid
equipGuid := req.EquipGuid
avatar := player.GameObjectGuidMap[avatarGuid].(*model.Avatar)
weapon := player.GameObjectGuidMap[equipGuid].(*model.Weapon)
g.WearUserAvatarEquip(player.PlayerID, avatar.AvatarId, weapon.WeaponId)
// PacketWearEquipRsp
wearEquipRsp := new(proto.WearEquipRsp)
wearEquipRsp.AvatarGuid = avatarGuid
wearEquipRsp.EquipGuid = equipGuid
g.SendMsg(cmd.WearEquipRsp, player.PlayerID, player.ClientSeq, wearEquipRsp)
}
func (g *GameManager) WearUserAvatarEquip(userId uint32, avatarId uint32, weaponId uint64) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
avatar := player.AvatarMap[avatarId]
weapon := player.WeaponMap[weaponId]
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
team := player.TeamConfig.GetActiveTeam()
if weapon.AvatarId != 0 {
// 武器在别的角色身上
weakAvatarId := weapon.AvatarId
weakWeaponId := weaponId
strongAvatarId := avatarId
strongWeaponId := avatar.EquipWeapon.WeaponId
player.TakeOffWeapon(weakAvatarId, weakWeaponId)
player.TakeOffWeapon(strongAvatarId, strongWeaponId)
player.WearWeapon(weakAvatarId, strongWeaponId)
player.WearWeapon(strongAvatarId, weakWeaponId)
weakAvatar := player.AvatarMap[weakAvatarId]
weakWeapon := player.WeaponMap[weakAvatar.EquipWeapon.WeaponId]
for _, aid := range team.AvatarIdList {
if aid == 0 {
break
}
if aid == weakAvatar.AvatarId {
playerTeamEntity.weaponEntityMap[weakWeapon.WeaponId] = scene.CreateEntityWeapon()
}
}
// PacketAvatarEquipChangeNotify
avatarEquipChangeNotify := g.PacketAvatarEquipChangeNotify(weakAvatar, weakWeapon, playerTeamEntity.weaponEntityMap[weakWeapon.WeaponId])
g.SendMsg(cmd.AvatarEquipChangeNotify, userId, player.ClientSeq, avatarEquipChangeNotify)
} else if avatar.EquipWeapon != nil {
// 角色当前有武器
player.TakeOffWeapon(avatarId, avatar.EquipWeapon.WeaponId)
player.WearWeapon(avatarId, weaponId)
} else {
// 是新角色还没有武器
player.WearWeapon(avatarId, weaponId)
}
for _, aid := range team.AvatarIdList {
if aid == 0 {
break
}
if aid == avatarId {
playerTeamEntity.weaponEntityMap[weaponId] = scene.CreateEntityWeapon()
}
}
// PacketAvatarEquipChangeNotify
avatarEquipChangeNotify := g.PacketAvatarEquipChangeNotify(avatar, weapon, playerTeamEntity.weaponEntityMap[weaponId])
g.SendMsg(cmd.AvatarEquipChangeNotify, userId, player.ClientSeq, avatarEquipChangeNotify)
}
func (g *GameManager) AvatarChangeCostumeReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change avatar costume, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.AvatarChangeCostumeReq)
avatarGuid := req.AvatarGuid
costumeId := req.CostumeId
exist := false
for _, v := range player.CostumeList {
if v == costumeId {
exist = true
}
}
if costumeId == 0 {
exist = true
}
if !exist {
return
}
avatar := player.GameObjectGuidMap[avatarGuid].(*model.Avatar)
avatar.Costume = req.CostumeId
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
// PacketAvatarChangeCostumeNotify
avatarChangeCostumeNotify := new(proto.AvatarChangeCostumeNotify)
avatarChangeCostumeNotify.EntityInfo = g.PacketSceneEntityInfoAvatar(scene, player, avatar.AvatarId)
for _, scenePlayer := range scene.playerMap {
g.SendMsg(cmd.AvatarChangeCostumeNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, avatarChangeCostumeNotify)
}
// PacketAvatarChangeCostumeRsp
avatarChangeCostumeRsp := new(proto.AvatarChangeCostumeRsp)
avatarChangeCostumeRsp.AvatarGuid = req.AvatarGuid
avatarChangeCostumeRsp.CostumeId = req.CostumeId
g.SendMsg(cmd.AvatarChangeCostumeRsp, player.PlayerID, player.ClientSeq, avatarChangeCostumeRsp)
}
func (g *GameManager) AvatarWearFlycloakReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change avatar fly cloak, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.AvatarWearFlycloakReq)
avatarGuid := req.AvatarGuid
flycloakId := req.FlycloakId
exist := false
for _, v := range player.FlyCloakList {
if v == flycloakId {
exist = true
}
}
if !exist {
return
}
avatar := player.GameObjectGuidMap[avatarGuid].(*model.Avatar)
avatar.FlyCloak = req.FlycloakId
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
// PacketAvatarFlycloakChangeNotify
avatarFlycloakChangeNotify := new(proto.AvatarFlycloakChangeNotify)
avatarFlycloakChangeNotify.AvatarGuid = avatarGuid
avatarFlycloakChangeNotify.FlycloakId = flycloakId
for _, scenePlayer := range scene.playerMap {
g.SendMsg(cmd.AvatarFlycloakChangeNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, avatarFlycloakChangeNotify)
}
// PacketAvatarWearFlycloakRsp
avatarWearFlycloakRsp := new(proto.AvatarWearFlycloakRsp)
avatarWearFlycloakRsp.AvatarGuid = req.AvatarGuid
avatarWearFlycloakRsp.FlycloakId = req.FlycloakId
g.SendMsg(cmd.AvatarWearFlycloakRsp, player.PlayerID, player.ClientSeq, avatarWearFlycloakRsp)
}
func (g *GameManager) PacketAvatarEquipChangeNotify(avatar *model.Avatar, weapon *model.Weapon, entityId uint32) *proto.AvatarEquipChangeNotify {
itemDataConfig := gdc.CONF.ItemDataMap[int32(weapon.ItemId)]
avatarEquipChangeNotify := new(proto.AvatarEquipChangeNotify)
avatarEquipChangeNotify.AvatarGuid = avatar.Guid
avatarEquipChangeNotify.EquipType = uint32(itemDataConfig.EquipEnumType)
avatarEquipChangeNotify.ItemId = weapon.ItemId
avatarEquipChangeNotify.EquipGuid = weapon.Guid
avatarEquipChangeNotify.Weapon = &proto.SceneWeaponInfo{
EntityId: entityId,
GadgetId: uint32(gdc.CONF.ItemDataMap[int32(weapon.ItemId)].GadgetId),
ItemId: weapon.ItemId,
Guid: weapon.Guid,
Level: uint32(weapon.Level),
AbilityInfo: new(proto.AbilitySyncStateInfo),
}
return avatarEquipChangeNotify
}
func (g *GameManager) PacketAvatarEquipTakeOffNotify(avatar *model.Avatar, weapon *model.Weapon) *proto.AvatarEquipChangeNotify {
itemDataConfig := gdc.CONF.ItemDataMap[int32(weapon.ItemId)]
avatarEquipChangeNotify := new(proto.AvatarEquipChangeNotify)
avatarEquipChangeNotify.AvatarGuid = avatar.Guid
avatarEquipChangeNotify.EquipType = uint32(itemDataConfig.EquipEnumType)
return avatarEquipChangeNotify
}
func (g *GameManager) UpdateUserAvatarFightProp(userId uint32, avatarId uint32) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
avatarFightPropNotify := new(proto.AvatarFightPropNotify)
avatar := player.AvatarMap[avatarId]
avatarFightPropNotify.AvatarGuid = avatar.Guid
avatarFightPropNotify.FightPropMap = avatar.FightPropMap
g.SendMsg(cmd.AvatarFightPropNotify, userId, player.ClientSeq, avatarFightPropNotify)
}
func (g *GameManager) PacketAvatarInfo(avatar *model.Avatar) *proto.AvatarInfo {
isFocus := false
//if avatar.AvatarId == 10000005 || avatar.AvatarId == 10000007 {
// isFocus = true
//}
pbAvatar := &proto.AvatarInfo{
IsFocus: isFocus,
AvatarId: avatar.AvatarId,
Guid: avatar.Guid,
PropMap: map[uint32]*proto.PropValue{
uint32(constant.PlayerPropertyConst.PROP_LEVEL): {
Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL),
Val: int64(avatar.Level),
Value: &proto.PropValue_Ival{Ival: int64(avatar.Level)},
},
uint32(constant.PlayerPropertyConst.PROP_EXP): {
Type: uint32(constant.PlayerPropertyConst.PROP_EXP),
Val: int64(avatar.Exp),
Value: &proto.PropValue_Ival{Ival: int64(avatar.Exp)},
},
uint32(constant.PlayerPropertyConst.PROP_BREAK_LEVEL): {
Type: uint32(constant.PlayerPropertyConst.PROP_BREAK_LEVEL),
Val: int64(avatar.Promote),
Value: &proto.PropValue_Ival{Ival: int64(avatar.Promote)},
},
uint32(constant.PlayerPropertyConst.PROP_SATIATION_VAL): {
Type: uint32(constant.PlayerPropertyConst.PROP_SATIATION_VAL),
Val: 0,
Value: &proto.PropValue_Ival{Ival: 0},
},
uint32(constant.PlayerPropertyConst.PROP_SATIATION_PENALTY_TIME): {
Type: uint32(constant.PlayerPropertyConst.PROP_SATIATION_PENALTY_TIME),
Val: 0,
Value: &proto.PropValue_Ival{Ival: 0},
},
},
LifeState: 1,
EquipGuidList: object.ConvMapToList(avatar.EquipGuidList),
FightPropMap: nil,
SkillDepotId: avatar.SkillDepotId,
FetterInfo: &proto.AvatarFetterInfo{
ExpLevel: uint32(avatar.FetterLevel),
ExpNumber: avatar.FetterExp,
// FetterList 不知道是啥 该角色在配置表里的所有FetterId
// TODO 资料解锁条目
FetterList: nil,
RewardedFetterLevelList: []uint32{10},
},
SkillLevelMap: nil,
AvatarType: 1,
WearingFlycloakId: avatar.FlyCloak,
CostumeId: avatar.Costume,
BornTime: uint32(avatar.BornTime),
}
pbAvatar.FightPropMap = avatar.FightPropMap
for _, v := range avatar.FetterList {
pbAvatar.FetterInfo.FetterList = append(pbAvatar.FetterInfo.FetterList, &proto.FetterData{
FetterId: v,
FetterState: uint32(constant.FetterStateConst.FINISH),
})
}
// 解锁全部资料
for _, v := range gdc.CONF.AvatarFetterDataMap[int32(avatar.AvatarId)] {
pbAvatar.FetterInfo.FetterList = append(pbAvatar.FetterInfo.FetterList, &proto.FetterData{
FetterId: uint32(v),
FetterState: uint32(constant.FetterStateConst.FINISH),
})
}
pbAvatar.SkillLevelMap = make(map[uint32]uint32)
for k, v := range avatar.SkillLevelMap {
pbAvatar.SkillLevelMap[k] = v
}
return pbAvatar
}

255
gs/game/user_chat.go Normal file
View File

@@ -0,0 +1,255 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
func (g *GameManager) PullRecentChatReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user pull recent chat, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PullRecentChatReq)
// 经研究发现 原神现网环境 客户端仅拉取最新的5条未读聊天消息 所以人太多的话小姐姐不回你消息是有原因的
// 因此 阿米你这样做真的合适吗 不过现在代码到了我手上我想怎么写就怎么写 我才不会重蹈覆辙
_ = req.PullNum
retMsgList := make([]*proto.ChatInfo, 0)
for _, msgList := range player.ChatMsgMap {
for _, chatMsg := range msgList {
// 反手就是一个遍历
if chatMsg.IsRead {
continue
}
retMsgList = append(retMsgList, g.ConvChatMsgToChatInfo(chatMsg))
}
}
world := g.worldManager.GetWorldByID(player.WorldId)
if world.multiplayer {
chatList := world.GetChatList()
count := len(chatList)
if count > 10 {
count = 10
}
for i := len(chatList) - count; i < len(chatList); i++ {
// PacketPlayerChatNotify
playerChatNotify := new(proto.PlayerChatNotify)
playerChatNotify.ChannelId = 0
playerChatNotify.ChatInfo = chatList[i]
g.SendMsg(cmd.PlayerChatNotify, player.PlayerID, 0, playerChatNotify)
}
}
// PacketPullRecentChatRsp
pullRecentChatRsp := new(proto.PullRecentChatRsp)
pullRecentChatRsp.ChatInfo = retMsgList
g.SendMsg(cmd.PullRecentChatRsp, player.PlayerID, 0, pullRecentChatRsp)
}
func (g *GameManager) PullPrivateChatReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user pull private chat, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PullPrivateChatReq)
targetUid := req.TargetUid
pullNum := req.PullNum
fromSequence := req.FromSequence
msgList, exist := player.ChatMsgMap[targetUid]
if !exist {
return
}
if pullNum+fromSequence > uint32(len(msgList)) {
pullNum = uint32(len(msgList)) - fromSequence
}
recentMsgList := msgList[fromSequence : fromSequence+pullNum]
retMsgList := make([]*proto.ChatInfo, 0)
for _, chatMsg := range recentMsgList {
retMsgList = append(retMsgList, g.ConvChatMsgToChatInfo(chatMsg))
}
// PacketPullPrivateChatRsp
pullPrivateChatRsp := new(proto.PullPrivateChatRsp)
pullPrivateChatRsp.ChatInfo = retMsgList
g.SendMsg(cmd.PullPrivateChatRsp, player.PlayerID, 0, pullPrivateChatRsp)
}
func (g *GameManager) PrivateChatReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user send private chat, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PrivateChatReq)
targetUid := req.TargetUid
content := req.Content
// TODO 同步阻塞待优化
targetPlayer := g.userManager.LoadTempOfflineUserSync(targetUid)
if targetPlayer == nil {
return
}
chatInfo := &proto.ChatInfo{
Time: uint32(time.Now().Unix()),
Sequence: 101,
ToUid: targetPlayer.PlayerID,
Uid: player.PlayerID,
IsRead: false,
Content: nil,
}
switch content.(type) {
case *proto.PrivateChatReq_Text:
text := content.(*proto.PrivateChatReq_Text).Text
if len(text) == 0 {
return
}
chatInfo.Content = &proto.ChatInfo_Text{
Text: text,
}
case *proto.PrivateChatReq_Icon:
icon := content.(*proto.PrivateChatReq_Icon).Icon
chatInfo.Content = &proto.ChatInfo_Icon{
Icon: icon,
}
default:
return
}
// 消息加入自己的队列
msgList, exist := player.ChatMsgMap[targetPlayer.PlayerID]
if !exist {
msgList = make([]*model.ChatMsg, 0)
}
msgList = append(msgList, g.ConvChatInfoToChatMsg(chatInfo))
player.ChatMsgMap[targetPlayer.PlayerID] = msgList
// 消息加入目标玩家的队列
msgList, exist = targetPlayer.ChatMsgMap[player.PlayerID]
if !exist {
msgList = make([]*model.ChatMsg, 0)
}
msgList = append(msgList, g.ConvChatInfoToChatMsg(chatInfo))
targetPlayer.ChatMsgMap[player.PlayerID] = msgList
if targetPlayer.Online {
// PacketPrivateChatNotify
privateChatNotify := new(proto.PrivateChatNotify)
privateChatNotify.ChatInfo = chatInfo
g.SendMsg(cmd.PrivateChatNotify, targetPlayer.PlayerID, 0, privateChatNotify)
}
// PacketPrivateChatNotify
privateChatNotify := new(proto.PrivateChatNotify)
privateChatNotify.ChatInfo = chatInfo
g.SendMsg(cmd.PrivateChatNotify, player.PlayerID, 0, privateChatNotify)
// PacketPrivateChatRsp
privateChatRsp := new(proto.PrivateChatRsp)
g.SendMsg(cmd.PrivateChatRsp, player.PlayerID, 0, privateChatRsp)
}
func (g *GameManager) ReadPrivateChatReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user read private chat, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.ReadPrivateChatReq)
targetUid := req.TargetUid
msgList, exist := player.ChatMsgMap[targetUid]
if !exist {
return
}
for index, chatMsg := range msgList {
chatMsg.IsRead = true
msgList[index] = chatMsg
}
player.ChatMsgMap[targetUid] = msgList
// PacketReadPrivateChatRsp
readPrivateChatRsp := new(proto.ReadPrivateChatRsp)
g.SendMsg(cmd.ReadPrivateChatRsp, player.PlayerID, 0, readPrivateChatRsp)
}
func (g *GameManager) PlayerChatReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user multiplayer chat, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PlayerChatReq)
channelId := req.ChannelId
chatInfo := req.ChatInfo
sendChatInfo := &proto.ChatInfo{
Time: uint32(time.Now().Unix()),
Uid: player.PlayerID,
Content: nil,
}
switch chatInfo.Content.(type) {
case *proto.ChatInfo_Text:
text := chatInfo.Content.(*proto.ChatInfo_Text).Text
if len(text) == 0 {
return
}
sendChatInfo.Content = &proto.ChatInfo_Text{
Text: text,
}
case *proto.ChatInfo_Icon:
icon := chatInfo.Content.(*proto.ChatInfo_Icon).Icon
sendChatInfo.Content = &proto.ChatInfo_Icon{
Icon: icon,
}
default:
return
}
world := g.worldManager.GetWorldByID(player.WorldId)
world.AddChat(sendChatInfo)
// PacketPlayerChatNotify
playerChatNotify := new(proto.PlayerChatNotify)
playerChatNotify.ChannelId = channelId
playerChatNotify.ChatInfo = sendChatInfo
for _, worldPlayer := range world.playerMap {
g.SendMsg(cmd.PlayerChatNotify, worldPlayer.PlayerID, 0, playerChatNotify)
}
// PacketPlayerChatRsp
playerChatRsp := new(proto.PlayerChatRsp)
g.SendMsg(cmd.PlayerChatRsp, player.PlayerID, 0, playerChatRsp)
}
func (g *GameManager) ConvChatInfoToChatMsg(chatInfo *proto.ChatInfo) (chatMsg *model.ChatMsg) {
chatMsg = &model.ChatMsg{
Time: chatInfo.Time,
ToUid: chatInfo.ToUid,
Uid: chatInfo.Uid,
IsRead: chatInfo.IsRead,
MsgType: 0,
Text: "",
Icon: 0,
}
switch chatInfo.Content.(type) {
case *proto.ChatInfo_Text:
chatMsg.MsgType = model.ChatMsgTypeText
chatMsg.Text = chatInfo.Content.(*proto.ChatInfo_Text).Text
case *proto.ChatInfo_Icon:
chatMsg.MsgType = model.ChatMsgTypeIcon
chatMsg.Icon = chatInfo.Content.(*proto.ChatInfo_Icon).Icon
default:
}
return chatMsg
}
func (g *GameManager) ConvChatMsgToChatInfo(chatMsg *model.ChatMsg) (chatInfo *proto.ChatInfo) {
chatInfo = &proto.ChatInfo{
Time: chatMsg.Time,
Sequence: 0,
ToUid: chatMsg.ToUid,
Uid: chatMsg.Uid,
IsRead: chatMsg.IsRead,
Content: nil,
}
switch chatMsg.MsgType {
case model.ChatMsgTypeText:
chatInfo.Content = &proto.ChatInfo_Text{
Text: chatMsg.Text,
}
case model.ChatMsgTypeIcon:
chatInfo.Content = &proto.ChatInfo_Icon{
Icon: chatMsg.Icon,
}
default:
}
return chatInfo
}

403
gs/game/user_combat.go Normal file
View File

@@ -0,0 +1,403 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
)
func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg pb.Message) {
//logger.LOG.Debug("user combat invocations, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.CombatInvocationsNotify)
world := g.worldManager.GetWorldByID(player.WorldId)
if world == nil {
return
}
scene := world.GetSceneById(player.SceneId)
invokeHandler := NewInvokeHandler[proto.CombatInvokeEntry]()
for _, entry := range req.InvokeList {
//logger.LOG.Debug("AT: %v, FT: %v, UID: %v", entry.ArgumentType, entry.ForwardType, player.PlayerID)
switch entry.ArgumentType {
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT:
scene.AddAttack(&Attack{
combatInvokeEntry: entry,
uid: player.PlayerID,
})
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ENTITY_MOVE:
entityMoveInfo := new(proto.EntityMoveInfo)
err := pb.Unmarshal(entry.CombatData, entityMoveInfo)
if err != nil {
logger.LOG.Error("parse combat invocations entity move info error: %v", err)
continue
}
motionInfo := entityMoveInfo.MotionInfo
if motionInfo.Pos == nil || motionInfo.Rot == nil {
continue
}
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
playerActiveAvatarEntityId := playerTeamEntity.avatarEntityMap[activeAvatarId]
if entityMoveInfo.EntityId == playerActiveAvatarEntityId {
// 玩家在移动
ok := world.aoiManager.IsValidAoiPos(motionInfo.Pos.X, motionInfo.Pos.Y, motionInfo.Pos.Z)
if !ok {
continue
}
// aoi
oldGid := world.aoiManager.GetGidByPos(float32(player.Pos.X), float32(player.Pos.Y), float32(player.Pos.Z))
newGid := world.aoiManager.GetGidByPos(motionInfo.Pos.X, motionInfo.Pos.Y, motionInfo.Pos.Z)
if oldGid != newGid {
// 跨越了格子
oldGridList := world.aoiManager.GetSurrGridListByGid(oldGid)
oldEntityIdMap := make(map[uint32]bool)
for _, grid := range oldGridList {
tmp := grid.GetEntityIdList()
for _, v := range tmp {
oldEntityIdMap[v] = true
}
}
newGridList := world.aoiManager.GetSurrGridListByGid(newGid)
newEntityIdMap := make(map[uint32]bool)
for _, grid := range newGridList {
tmp := grid.GetEntityIdList()
for _, v := range tmp {
newEntityIdMap[v] = true
}
}
delEntityIdList := make([]uint32, 0)
delUidList := make([]uint32, 0)
for oldEntityId := range oldEntityIdMap {
_, exist := newEntityIdMap[oldEntityId]
if exist {
continue
}
delEntityIdList = append(delEntityIdList, oldEntityId)
entity := scene.GetEntity(oldEntityId)
if entity == nil {
continue
}
if entity.avatarEntity != nil {
delUidList = append(delUidList, entity.avatarEntity.uid)
}
}
addEntityIdList := make([]uint32, 0)
addUidList := make([]uint32, 0)
for newEntityId := range newEntityIdMap {
_, exist := oldEntityIdMap[newEntityId]
if exist {
continue
}
addEntityIdList = append(addEntityIdList, newEntityId)
entity := scene.GetEntity(newEntityId)
if entity == nil {
continue
}
if entity.avatarEntity != nil {
addUidList = append(addUidList, entity.avatarEntity.uid)
}
}
// 发送已消失格子里的实体消失通知
g.RemoveSceneEntityNotifyToPlayer(player, delEntityIdList)
// 发送新出现格子里的实体出现通知
g.AddSceneEntityNotify(player, proto.VisionType_VISION_TYPE_BORN, addEntityIdList, false)
// 更新玩家的位置信息
player.Pos.X = float64(motionInfo.Pos.X)
player.Pos.Y = float64(motionInfo.Pos.Y)
player.Pos.Z = float64(motionInfo.Pos.Z)
// 更新玩家所在格子
world.aoiManager.RemoveEntityIdFromGrid(playerActiveAvatarEntityId, oldGid)
world.aoiManager.AddEntityIdToGrid(playerActiveAvatarEntityId, newGid)
// 其他玩家
for _, uid := range delUidList {
otherPlayer := g.userManager.GetOnlineUser(uid)
g.RemoveSceneEntityNotifyToPlayer(otherPlayer, []uint32{playerActiveAvatarEntityId})
}
for _, uid := range addUidList {
otherPlayer := g.userManager.GetOnlineUser(uid)
g.AddSceneEntityNotify(otherPlayer, proto.VisionType_VISION_TYPE_BORN, []uint32{playerActiveAvatarEntityId}, false)
}
}
// 把队伍中的其他非活跃角色也同步进行移动
team := player.TeamConfig.GetActiveTeam()
for _, avatarId := range team.AvatarIdList {
// 跳过当前的活跃角色
if avatarId == activeAvatarId {
continue
}
entityId := playerTeamEntity.avatarEntityMap[avatarId]
entity := scene.GetEntity(entityId)
if entity == nil {
continue
}
entity.pos.X = float64(motionInfo.Pos.X)
entity.pos.Y = float64(motionInfo.Pos.Y)
entity.pos.Z = float64(motionInfo.Pos.Z)
entity.rot.X = float64(motionInfo.Rot.X)
entity.rot.Y = float64(motionInfo.Rot.Y)
entity.rot.Z = float64(motionInfo.Rot.Z)
}
// 更新玩家的位置信息
player.Pos.X = float64(motionInfo.Pos.X)
player.Pos.Y = float64(motionInfo.Pos.Y)
player.Pos.Z = float64(motionInfo.Pos.Z)
player.Rot.X = float64(motionInfo.Rot.X)
player.Rot.Y = float64(motionInfo.Rot.Y)
player.Rot.Z = float64(motionInfo.Rot.Z)
// TODO 采集大地图地形数据
if world.IsBigWorld() && scene.id == 3 && player.PlayerID != 1 {
if motionInfo.State == proto.MotionState_MOTION_STATE_WALK ||
motionInfo.State == proto.MotionState_MOTION_STATE_RUN ||
motionInfo.State == proto.MotionState_MOTION_STATE_DASH ||
motionInfo.State == proto.MotionState_MOTION_STATE_CLIMB {
logger.LOG.Debug("set terr motionInfo: %v", motionInfo)
exist := g.worldManager.worldStatic.GetTerrain(int16(motionInfo.Pos.X), int16(motionInfo.Pos.Y), int16(motionInfo.Pos.Z))
g.worldManager.worldStatic.SetTerrain(int16(motionInfo.Pos.X), int16(motionInfo.Pos.Y), int16(motionInfo.Pos.Z))
if !exist {
// TODO 薄荷标记
// 只给附近aoi区域的玩家广播消息
surrPlayerList := make([]*model.Player, 0)
entityIdList := world.aoiManager.GetEntityIdListByPos(float32(player.Pos.X), float32(player.Pos.Y), float32(player.Pos.Z))
for _, entityId := range entityIdList {
entity := scene.GetEntity(entityId)
if entity == nil {
continue
}
if entity.avatarEntity != nil {
otherPlayer := g.userManager.GetOnlineUser(entity.avatarEntity.uid)
surrPlayerList = append(surrPlayerList, otherPlayer)
}
}
pos := &model.Vector{
X: float64(int16(motionInfo.Pos.X)),
Y: float64(int16(motionInfo.Pos.Y)),
Z: float64(int16(motionInfo.Pos.Z)),
}
gadgetEntityId := scene.CreateEntityGadget(pos, 3003009)
for _, otherPlayer := range surrPlayerList {
g.AddSceneEntityNotify(otherPlayer, proto.VisionType_VISION_TYPE_BORN, []uint32{gadgetEntityId}, false)
}
}
}
}
}
// 更新场景实体的位置信息
sceneEntity := scene.GetEntity(entityMoveInfo.EntityId)
if sceneEntity != nil {
sceneEntity.pos = &model.Vector{
X: float64(motionInfo.Pos.X),
Y: float64(motionInfo.Pos.Y),
Z: float64(motionInfo.Pos.Z),
}
sceneEntity.rot = &model.Vector{
X: float64(motionInfo.Rot.X),
Y: float64(motionInfo.Rot.Y),
Z: float64(motionInfo.Rot.Z),
}
sceneEntity.moveState = uint16(motionInfo.State)
sceneEntity.lastMoveSceneTimeMs = entityMoveInfo.SceneTime
sceneEntity.lastMoveReliableSeq = entityMoveInfo.ReliableSeq
//logger.LOG.Debug("entity move, id: %v, pos: %v, uid: %v", sceneEntity.id, sceneEntity.pos, player.PlayerID)
}
invokeHandler.addEntry(entry.ForwardType, entry)
default:
invokeHandler.addEntry(entry.ForwardType, entry)
}
}
// 只给附近aoi区域的玩家广播消息
surrPlayerList := make([]*model.Player, 0)
entityIdList := world.aoiManager.GetEntityIdListByPos(float32(player.Pos.X), float32(player.Pos.Y), float32(player.Pos.Z))
for _, entityId := range entityIdList {
entity := scene.GetEntity(entityId)
if entity == nil {
continue
}
if entity.avatarEntity != nil {
otherPlayer := g.userManager.GetOnlineUser(entity.avatarEntity.uid)
surrPlayerList = append(surrPlayerList, otherPlayer)
}
}
// PacketCombatInvocationsNotify
if invokeHandler.AllLen() > 0 {
combatInvocationsNotify := new(proto.CombatInvocationsNotify)
combatInvocationsNotify.InvokeList = invokeHandler.entryListForwardAll
for _, v := range surrPlayerList {
g.SendMsg(cmd.CombatInvocationsNotify, v.PlayerID, v.ClientSeq, combatInvocationsNotify)
}
}
if invokeHandler.AllExceptCurLen() > 0 {
combatInvocationsNotify := new(proto.CombatInvocationsNotify)
combatInvocationsNotify.InvokeList = invokeHandler.entryListForwardAllExceptCur
for _, v := range surrPlayerList {
if player.PlayerID == v.PlayerID {
continue
}
g.SendMsg(cmd.CombatInvocationsNotify, v.PlayerID, v.ClientSeq, combatInvocationsNotify)
}
}
if invokeHandler.HostLen() > 0 {
combatInvocationsNotify := new(proto.CombatInvocationsNotify)
combatInvocationsNotify.InvokeList = invokeHandler.entryListForwardHost
g.SendMsg(cmd.CombatInvocationsNotify, world.owner.PlayerID, world.owner.ClientSeq, combatInvocationsNotify)
}
}
func (g *GameManager) AbilityInvocationsNotify(player *model.Player, payloadMsg pb.Message) {
//logger.LOG.Debug("user ability invocations, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.AbilityInvocationsNotify)
world := g.worldManager.GetWorldByID(player.WorldId)
if world == nil {
return
}
scene := world.GetSceneById(player.SceneId)
invokeHandler := NewInvokeHandler[proto.AbilityInvokeEntry]()
for _, entry := range req.Invokes {
//logger.LOG.Debug("AT: %v, FT: %v, UID: %v", entry.ArgumentType, entry.ForwardType, player.PlayerID)
invokeHandler.addEntry(entry.ForwardType, entry)
}
// 只给附近aoi区域的玩家广播消息
surrPlayerList := make([]*model.Player, 0)
entityIdList := world.aoiManager.GetEntityIdListByPos(float32(player.Pos.X), float32(player.Pos.Y), float32(player.Pos.Z))
for _, entityId := range entityIdList {
entity := scene.GetEntity(entityId)
if entity == nil {
continue
}
if entity.avatarEntity != nil {
otherPlayer := g.userManager.GetOnlineUser(entity.avatarEntity.uid)
surrPlayerList = append(surrPlayerList, otherPlayer)
}
}
// PacketAbilityInvocationsNotify
if invokeHandler.AllLen() > 0 {
abilityInvocationsNotify := new(proto.AbilityInvocationsNotify)
abilityInvocationsNotify.Invokes = invokeHandler.entryListForwardAll
for _, v := range surrPlayerList {
g.SendMsg(cmd.AbilityInvocationsNotify, v.PlayerID, v.ClientSeq, abilityInvocationsNotify)
}
}
if invokeHandler.AllExceptCurLen() > 0 {
abilityInvocationsNotify := new(proto.AbilityInvocationsNotify)
abilityInvocationsNotify.Invokes = invokeHandler.entryListForwardAllExceptCur
for _, v := range surrPlayerList {
if player.PlayerID == v.PlayerID {
continue
}
g.SendMsg(cmd.AbilityInvocationsNotify, v.PlayerID, v.ClientSeq, abilityInvocationsNotify)
}
}
if invokeHandler.HostLen() > 0 {
abilityInvocationsNotify := new(proto.AbilityInvocationsNotify)
abilityInvocationsNotify.Invokes = invokeHandler.entryListForwardHost
g.SendMsg(cmd.AbilityInvocationsNotify, world.owner.PlayerID, world.owner.ClientSeq, abilityInvocationsNotify)
}
}
func (g *GameManager) ClientAbilityInitFinishNotify(player *model.Player, payloadMsg pb.Message) {
//logger.LOG.Debug("user client ability ok, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.ClientAbilityInitFinishNotify)
world := g.worldManager.GetWorldByID(player.WorldId)
if world == nil {
return
}
scene := world.GetSceneById(player.SceneId)
invokeHandler := NewInvokeHandler[proto.AbilityInvokeEntry]()
for _, entry := range req.Invokes {
//logger.LOG.Debug("AT: %v, FT: %v, UID: %v", entry.ArgumentType, entry.ForwardType, player.PlayerID)
invokeHandler.addEntry(entry.ForwardType, entry)
}
// 只给附近aoi区域的玩家广播消息
surrPlayerList := make([]*model.Player, 0)
entityIdList := world.aoiManager.GetEntityIdListByPos(float32(player.Pos.X), float32(player.Pos.Y), float32(player.Pos.Z))
for _, entityId := range entityIdList {
entity := scene.GetEntity(entityId)
if entity == nil {
continue
}
if entity.avatarEntity != nil {
otherPlayer := g.userManager.GetOnlineUser(entity.avatarEntity.uid)
surrPlayerList = append(surrPlayerList, otherPlayer)
}
}
// PacketClientAbilityInitFinishNotify
if invokeHandler.AllLen() > 0 {
clientAbilityInitFinishNotify := new(proto.ClientAbilityInitFinishNotify)
clientAbilityInitFinishNotify.Invokes = invokeHandler.entryListForwardAll
for _, v := range surrPlayerList {
g.SendMsg(cmd.ClientAbilityInitFinishNotify, v.PlayerID, v.ClientSeq, clientAbilityInitFinishNotify)
}
}
if invokeHandler.AllExceptCurLen() > 0 {
clientAbilityInitFinishNotify := new(proto.ClientAbilityInitFinishNotify)
clientAbilityInitFinishNotify.Invokes = invokeHandler.entryListForwardAllExceptCur
for _, v := range surrPlayerList {
if player.PlayerID == v.PlayerID {
continue
}
g.SendMsg(cmd.ClientAbilityInitFinishNotify, v.PlayerID, v.ClientSeq, clientAbilityInitFinishNotify)
}
}
if invokeHandler.HostLen() > 0 {
clientAbilityInitFinishNotify := new(proto.ClientAbilityInitFinishNotify)
clientAbilityInitFinishNotify.Invokes = invokeHandler.entryListForwardHost
g.SendMsg(cmd.ClientAbilityInitFinishNotify, world.owner.PlayerID, world.owner.ClientSeq, clientAbilityInitFinishNotify)
}
}
type InvokeType interface {
proto.AbilityInvokeEntry | proto.CombatInvokeEntry
}
type InvokeHandler[T InvokeType] struct {
entryListForwardAll []*T
entryListForwardAllExceptCur []*T
entryListForwardHost []*T
}
func NewInvokeHandler[T InvokeType]() (r *InvokeHandler[T]) {
r = new(InvokeHandler[T])
r.InitInvokeHandler()
return r
}
func (i *InvokeHandler[T]) InitInvokeHandler() {
i.entryListForwardAll = make([]*T, 0)
i.entryListForwardAllExceptCur = make([]*T, 0)
i.entryListForwardHost = make([]*T, 0)
}
func (i *InvokeHandler[T]) addEntry(forward proto.ForwardType, entry *T) {
switch forward {
case proto.ForwardType_FORWARD_TYPE_TO_ALL:
i.entryListForwardAll = append(i.entryListForwardAll, entry)
case proto.ForwardType_FORWARD_TYPE_TO_ALL_EXCEPT_CUR:
fallthrough
case proto.ForwardType_FORWARD_TYPE_TO_ALL_EXIST_EXCEPT_CUR:
i.entryListForwardAllExceptCur = append(i.entryListForwardAllExceptCur, entry)
case proto.ForwardType_FORWARD_TYPE_TO_HOST:
i.entryListForwardHost = append(i.entryListForwardHost, entry)
default:
if forward != proto.ForwardType_FORWARD_TYPE_ONLY_SERVER {
logger.LOG.Error("forward: %v, entry: %v", forward, entry)
}
}
}
func (i *InvokeHandler[T]) AllLen() int {
return len(i.entryListForwardAll)
}
func (i *InvokeHandler[T]) AllExceptCurLen() int {
return len(i.entryListForwardAllExceptCur)
}
func (i *InvokeHandler[T]) HostLen() int {
return len(i.entryListForwardHost)
}

611
gs/game/user_gacha.go Normal file
View File

@@ -0,0 +1,611 @@
package game
import (
"github.com/golang-jwt/jwt/v4"
pb "google.golang.org/protobuf/proto"
"hk4e/common/config"
"hk4e/common/utils/random"
gdc "hk4e/gs/config"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
type UserInfo struct {
UserId uint32 `json:"userId"`
jwt.RegisteredClaims
}
// 获取卡池信息
func (g *GameManager) GetGachaInfoReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get gacha info, uid: %v", player.PlayerID)
serverAddr := config.CONF.Hk4e.GachaHistoryServer
getGachaInfoRsp := new(proto.GetGachaInfoRsp)
getGachaInfoRsp.GachaRandom = 12345
userInfo := &UserInfo{
UserId: player.PlayerID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour * time.Duration(1))),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS512, userInfo)
jwtStr, err := token.SignedString([]byte("flswld"))
if err != nil {
logger.LOG.Error("generate jwt error: %v", err)
jwtStr = "default.jwt.token"
}
getGachaInfoRsp.GachaInfoList = []*proto.GachaInfo{
// 温迪
{
GachaType: 300,
ScheduleId: 823,
BeginTime: 0,
EndTime: 2051193600,
GachaSortId: 9998,
GachaPrefabPath: "GachaShowPanel_A019",
GachaPreviewPrefabPath: "UI_Tab_GachaShowPanel_A019",
TitleTextmap: "UI_GACHA_SHOW_PANEL_A019_TITLE",
LeftGachaTimes: 2147483647,
GachaTimesLimit: 2147483647,
CostItemId: 223,
CostItemNum: 1,
TenCostItemId: 223,
TenCostItemNum: 10,
GachaRecordUrl: serverAddr + "/gm/gacha?gachaType=300&jwt=" + jwtStr,
GachaRecordUrlOversea: serverAddr + "/gm/gacha?gachaType=300&jwt=" + jwtStr,
GachaProbUrl: serverAddr + "/gm/gacha/details?scheduleId=823&jwt=" + jwtStr,
GachaProbUrlOversea: serverAddr + "/gm/gacha/details?scheduleId=823&jwt=" + jwtStr,
GachaUpInfoList: []*proto.GachaUpInfo{
{
ItemParentType: 1,
ItemIdList: []uint32{1022},
},
{
ItemParentType: 2,
ItemIdList: []uint32{1023, 1031, 1014},
},
},
DisplayUp4ItemList: []uint32{1023},
DisplayUp5ItemList: []uint32{1022},
WishItemId: 0,
WishProgress: 0,
WishMaxProgress: 0,
IsNewWish: false,
},
// 可莉
{
GachaType: 400,
ScheduleId: 833,
BeginTime: 0,
EndTime: 2051193600,
GachaSortId: 9998,
GachaPrefabPath: "GachaShowPanel_A018",
GachaPreviewPrefabPath: "UI_Tab_GachaShowPanel_A018",
TitleTextmap: "UI_GACHA_SHOW_PANEL_A018_TITLE",
LeftGachaTimes: 2147483647,
GachaTimesLimit: 2147483647,
CostItemId: 223,
CostItemNum: 1,
TenCostItemId: 223,
TenCostItemNum: 10,
GachaRecordUrl: serverAddr + "/gm/gacha?gachaType=400&jwt=" + jwtStr,
GachaRecordUrlOversea: serverAddr + "/gm/gacha?gachaType=400&jwt=" + jwtStr,
GachaProbUrl: serverAddr + "/gm/gacha/details?scheduleId=833&jwt=" + jwtStr,
GachaProbUrlOversea: serverAddr + "/gm/gacha/details?scheduleId=833&jwt=" + jwtStr,
GachaUpInfoList: []*proto.GachaUpInfo{
{
ItemParentType: 1,
ItemIdList: []uint32{1029},
},
{
ItemParentType: 2,
ItemIdList: []uint32{1025, 1034, 1043},
},
},
DisplayUp4ItemList: []uint32{1025},
DisplayUp5ItemList: []uint32{1029},
WishItemId: 0,
WishProgress: 0,
WishMaxProgress: 0,
IsNewWish: false,
},
// 阿莫斯之弓&天空之傲
{
GachaType: 431,
ScheduleId: 1143,
BeginTime: 0,
EndTime: 2051193600,
GachaSortId: 9997,
GachaPrefabPath: "GachaShowPanel_A030",
GachaPreviewPrefabPath: "UI_Tab_GachaShowPanel_A030",
TitleTextmap: "UI_GACHA_SHOW_PANEL_A030_TITLE",
LeftGachaTimes: 2147483647,
GachaTimesLimit: 2147483647,
CostItemId: 223,
CostItemNum: 1,
TenCostItemId: 223,
TenCostItemNum: 10,
GachaRecordUrl: serverAddr + "/gm/gacha?gachaType=431&jwt=" + jwtStr,
GachaRecordUrlOversea: serverAddr + "/gm/gacha?gachaType=431&jwt=" + jwtStr,
GachaProbUrl: serverAddr + "/gm/gacha/details?scheduleId=1143&jwt=" + jwtStr,
GachaProbUrlOversea: serverAddr + "/gm/gacha/details?scheduleId=1143&jwt=" + jwtStr,
GachaUpInfoList: []*proto.GachaUpInfo{
{
ItemParentType: 1,
ItemIdList: []uint32{15502, 12501},
},
{
ItemParentType: 2,
ItemIdList: []uint32{11403, 12402, 13401, 14409, 15401},
},
},
DisplayUp4ItemList: []uint32{11403},
DisplayUp5ItemList: []uint32{15502, 12501},
WishItemId: 0,
WishProgress: 0,
WishMaxProgress: 0,
IsNewWish: false,
},
// 常驻
{
GachaType: 201,
ScheduleId: 813,
BeginTime: 0,
EndTime: 2051193600,
GachaSortId: 1000,
GachaPrefabPath: "GachaShowPanel_A017",
GachaPreviewPrefabPath: "UI_Tab_GachaShowPanel_A017",
TitleTextmap: "UI_GACHA_SHOW_PANEL_A017_TITLE",
LeftGachaTimes: 2147483647,
GachaTimesLimit: 2147483647,
CostItemId: 224,
CostItemNum: 1,
TenCostItemId: 224,
TenCostItemNum: 10,
GachaRecordUrl: serverAddr + "/gm/gacha?gachaType=201&jwt=" + jwtStr,
GachaRecordUrlOversea: serverAddr + "/gm/gacha?gachaType=201&jwt=" + jwtStr,
GachaProbUrl: serverAddr + "/gm/gacha/details?scheduleId=813&jwt=" + jwtStr,
GachaProbUrlOversea: serverAddr + "/gm/gacha/details?scheduleId=813&jwt=" + jwtStr,
GachaUpInfoList: []*proto.GachaUpInfo{
{
ItemParentType: 1,
ItemIdList: []uint32{1003, 1016},
},
{
ItemParentType: 2,
ItemIdList: []uint32{1021, 1006, 1015},
},
},
DisplayUp4ItemList: []uint32{1021},
DisplayUp5ItemList: []uint32{1003, 1016},
WishItemId: 0,
WishProgress: 0,
WishMaxProgress: 0,
IsNewWish: false,
},
}
g.SendMsg(cmd.GetGachaInfoRsp, player.PlayerID, player.ClientSeq, getGachaInfoRsp)
}
func (g *GameManager) DoGachaReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user do gacha, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.DoGachaReq)
gachaScheduleId := req.GachaScheduleId
gachaTimes := req.GachaTimes
gachaType := uint32(0)
costItemId := uint32(0)
switch gachaScheduleId {
case 823:
// 温迪
gachaType = 300
costItemId = 223
case 833:
// 可莉
gachaType = 400
costItemId = 223
case 1143:
// 阿莫斯之弓&天空之傲
gachaType = 431
costItemId = 223
case 813:
// 常驻
gachaType = 201
costItemId = 224
}
// PacketDoGachaRsp
doGachaRsp := new(proto.DoGachaRsp)
doGachaRsp.GachaType = gachaType
doGachaRsp.GachaScheduleId = gachaScheduleId
doGachaRsp.GachaTimes = gachaTimes
doGachaRsp.NewGachaRandom = 12345
doGachaRsp.LeftGachaTimes = 2147483647
doGachaRsp.GachaTimesLimit = 2147483647
doGachaRsp.CostItemId = costItemId
doGachaRsp.CostItemNum = 1
doGachaRsp.TenCostItemId = costItemId
doGachaRsp.TenCostItemNum = 10
// 先扣掉粉球或蓝球再进行抽卡
g.CostUserItem(player.PlayerID, []*UserItem{
{
ItemId: costItemId,
ChangeCount: gachaTimes,
},
})
doGachaRsp.GachaItemList = make([]*proto.GachaItem, 0)
for i := uint32(0); i < gachaTimes; i++ {
var ok bool
var itemId uint32
if gachaType == 400 {
// 可莉
ok, itemId = g.doGachaKlee()
} else if gachaType == 300 {
// 角色UP池
ok, itemId = g.doGachaOnce(player.PlayerID, gachaType, true, false)
} else if gachaType == 431 {
// 武器UP池
ok, itemId = g.doGachaOnce(player.PlayerID, gachaType, true, true)
} else if gachaType == 201 {
// 常驻
ok, itemId = g.doGachaOnce(player.PlayerID, gachaType, false, false)
} else {
ok, itemId = false, 0
}
if !ok {
itemId = 11301
}
// 添加抽卡获得的道具
if itemId > 1000 && itemId < 2000 {
avatarId := (itemId % 1000) + 10000000
_, exist := player.AvatarMap[avatarId]
if !exist {
g.AddUserAvatar(player.PlayerID, avatarId)
} else {
constellationItemId := itemId + 100
if player.GetItemCount(constellationItemId) < 6 {
g.AddUserItem(player.PlayerID, []*UserItem{{ItemId: constellationItemId, ChangeCount: 1}}, false, 0)
}
}
} else if itemId > 10000 && itemId < 20000 {
g.AddUserWeapon(player.PlayerID, itemId)
} else {
g.AddUserItem(player.PlayerID, []*UserItem{{ItemId: itemId, ChangeCount: 1}}, false, 0)
}
// 计算星尘星辉
xc := uint32(random.GetRandomInt32(0, 10))
xh := uint32(random.GetRandomInt32(0, 10))
gachaItem := new(proto.GachaItem)
gachaItem.GachaItem_ = &proto.ItemParam{
ItemId: itemId,
Count: 1,
}
// 星尘
if xc != 0 {
g.AddUserItem(player.PlayerID, []*UserItem{{
ItemId: 222,
ChangeCount: xc,
}}, false, 0)
gachaItem.TokenItemList = []*proto.ItemParam{{
ItemId: 222,
Count: xc,
}}
}
// 星辉
if xh != 0 {
g.AddUserItem(player.PlayerID, []*UserItem{{
ItemId: 221,
ChangeCount: xh,
}}, false, 0)
gachaItem.TransferItems = []*proto.GachaTransferItem{{
Item: &proto.ItemParam{
ItemId: 221,
Count: xh,
},
}}
}
doGachaRsp.GachaItemList = append(doGachaRsp.GachaItemList, gachaItem)
}
//logger.LOG.Debug("doGachaRsp: %v", doGachaRsp.String())
g.SendMsg(cmd.DoGachaRsp, player.PlayerID, player.ClientSeq, doGachaRsp)
}
// 扣1给可莉刷烧烤酱
func (g *GameManager) doGachaKlee() (bool, uint32) {
allAvatarList := make([]uint32, 0)
allAvatarDataConfig := g.GetAllAvatarDataConfig()
for k, v := range allAvatarDataConfig {
if v.QualityType == "QUALITY_ORANGE" || v.QualityType == "QUALITY_PURPLE" {
allAvatarList = append(allAvatarList, uint32(k))
}
}
allWeaponList := make([]uint32, 0)
allWeaponDataConfig := g.GetAllWeaponDataConfig()
for k, v := range allWeaponDataConfig {
if v.RankLevel == 5 {
allWeaponList = append(allWeaponList, uint32(k))
}
}
allGoodList := make([]uint32, 0)
// 全部角色
allGoodList = append(allGoodList, allAvatarList...)
// 全部5星武器
allGoodList = append(allGoodList, allWeaponList...)
// 原石 摩拉 粉球 蓝球
allGoodList = append(allGoodList, 201, 202, 223, 224)
// 苟利国家生死以
allGoodList = append(allGoodList, 100081)
rn := random.GetRandomInt32(0, int32(len(allGoodList)-1))
itemId := allGoodList[rn]
if itemId > 10000000 {
itemId %= 1000
itemId += 1000
}
return true, itemId
}
const (
Orange = iota
Purple
Blue
Avatar
Weapon
)
const (
StandardOrangeTimesFixThreshold uint32 = 74 // 标准池触发5星概率修正阈值的抽卡次数
StandardOrangeTimesFixValue int32 = 600 // 标准池5星概率修正因子
StandardPurpleTimesFixThreshold uint32 = 9 // 标准池触发4星概率修正阈值的抽卡次数
StandardPurpleTimesFixValue int32 = 5100 // 标准池4星概率修正因子
WeaponOrangeTimesFixThreshold uint32 = 63 // 武器池触发5星概率修正阈值的抽卡次数
WeaponOrangeTimesFixValue int32 = 700 // 武器池5星概率修正因子
WeaponPurpleTimesFixThreshold uint32 = 8 // 武器池触发4星概率修正阈值的抽卡次数
WeaponPurpleTimesFixValue int32 = 6000 // 武器池4星概率修正因子
)
// 单抽一次
func (g *GameManager) doGachaOnce(userId uint32, gachaType uint32, mustGetUpEnable bool, weaponFix bool) (bool, uint32) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return false, 0
}
// 找到卡池对应的掉落组
dropGroupDataConfig := gdc.CONF.DropGroupDataMap[int32(gachaType)]
if dropGroupDataConfig == nil {
logger.LOG.Error("drop group not found, drop id: %v", gachaType)
return false, 0
}
// 获取用户的卡池保底信息
gachaPoolInfo := player.DropInfo.GachaPoolInfo[gachaType]
if gachaPoolInfo == nil {
logger.LOG.Error("user gacha pool info not found, gacha type: %v", gachaType)
return false, 0
}
// 保底计数+1
gachaPoolInfo.OrangeTimes++
gachaPoolInfo.PurpleTimes++
// 4星和5星概率修正
OrangeTimesFixThreshold := uint32(0)
OrangeTimesFixValue := int32(0)
PurpleTimesFixThreshold := uint32(0)
PurpleTimesFixValue := int32(0)
if !weaponFix {
OrangeTimesFixThreshold = StandardOrangeTimesFixThreshold
OrangeTimesFixValue = StandardOrangeTimesFixValue
PurpleTimesFixThreshold = StandardPurpleTimesFixThreshold
PurpleTimesFixValue = StandardPurpleTimesFixValue
} else {
OrangeTimesFixThreshold = WeaponOrangeTimesFixThreshold
OrangeTimesFixValue = WeaponOrangeTimesFixValue
PurpleTimesFixThreshold = WeaponPurpleTimesFixThreshold
PurpleTimesFixValue = WeaponPurpleTimesFixValue
}
if gachaPoolInfo.OrangeTimes >= OrangeTimesFixThreshold || gachaPoolInfo.PurpleTimes >= PurpleTimesFixThreshold {
fixDropGroupDataConfig := new(gdc.DropGroupData)
fixDropGroupDataConfig.DropId = dropGroupDataConfig.DropId
fixDropGroupDataConfig.WeightAll = dropGroupDataConfig.WeightAll
// 计算4星和5星权重修正值
addOrangeWeight := int32(gachaPoolInfo.OrangeTimes-OrangeTimesFixThreshold+1) * OrangeTimesFixValue
if addOrangeWeight < 0 {
addOrangeWeight = 0
}
addPurpleWeight := int32(gachaPoolInfo.PurpleTimes-PurpleTimesFixThreshold+1) * PurpleTimesFixValue
if addPurpleWeight < 0 {
addPurpleWeight = 0
}
for _, drop := range dropGroupDataConfig.DropConfig {
fixDrop := new(gdc.Drop)
fixDrop.Result = drop.Result
fixDrop.DropId = drop.DropId
fixDrop.IsEnd = drop.IsEnd
// 找到5/4/3星掉落组id 要求配置表的5/4/3星掉落组id规则固定为(卡池类型*10+1/2/3)
orangeDropId := int32(gachaType*10 + 1)
purpleDropId := int32(gachaType*10 + 2)
blueDropId := int32(gachaType*10 + 3)
// 权重修正
if drop.Result == orangeDropId {
fixDrop.Weight = drop.Weight + addOrangeWeight
} else if drop.Result == purpleDropId {
fixDrop.Weight = drop.Weight + addPurpleWeight
} else if drop.Result == blueDropId {
fixDrop.Weight = drop.Weight - addOrangeWeight - addPurpleWeight
} else {
logger.LOG.Error("invalid drop group id, does not match any case of orange/purple/blue, result group id: %v", drop.Result)
fixDrop.Weight = drop.Weight
}
fixDropGroupDataConfig.DropConfig = append(fixDropGroupDataConfig.DropConfig, fixDrop)
}
dropGroupDataConfig = fixDropGroupDataConfig
}
// 掉落
ok, drop := g.doFullRandDrop(dropGroupDataConfig)
if !ok {
return false, 0
}
// 分析本次掉落结果的星级和类型
itemColor := 0
itemType := 0
_ = itemType
gachaItemId := uint32(drop.Result)
if gachaItemId < 2000 {
// 抽到角色
itemType = Avatar
avatarId := (gachaItemId % 1000) + 10000000
allAvatarDataConfig := g.GetAllAvatarDataConfig()
avatarDataConfig := allAvatarDataConfig[int32(avatarId)]
if avatarDataConfig == nil {
logger.LOG.Error("avatar data config not found, avatar id: %v", avatarId)
return false, 0
}
if avatarDataConfig.QualityType == "QUALITY_ORANGE" {
itemColor = Orange
logger.LOG.Debug("[orange avatar], times: %v, gachaItemId: %v", gachaPoolInfo.OrangeTimes, gachaItemId)
if gachaPoolInfo.OrangeTimes > 90 {
logger.LOG.Error("[abnormal orange avatar], times: %v, gachaItemId: %v", gachaPoolInfo.OrangeTimes, gachaItemId)
}
} else if avatarDataConfig.QualityType == "QUALITY_PURPLE" {
itemColor = Purple
logger.LOG.Debug("[purple avatar], times: %v, gachaItemId: %v", gachaPoolInfo.PurpleTimes, gachaItemId)
if gachaPoolInfo.PurpleTimes > 10 {
logger.LOG.Error("[abnormal purple avatar], times: %v, gachaItemId: %v", gachaPoolInfo.PurpleTimes, gachaItemId)
}
} else {
itemColor = Blue
}
} else {
// 抽到武器
itemType = Weapon
allWeaponDataConfig := g.GetAllWeaponDataConfig()
weaponDataConfig := allWeaponDataConfig[int32(gachaItemId)]
if weaponDataConfig == nil {
logger.LOG.Error("weapon item data config not found, item id: %v", gachaItemId)
return false, 0
}
if weaponDataConfig.RankLevel == 5 {
itemColor = Orange
logger.LOG.Debug("[orange weapon], times: %v, gachaItemId: %v", gachaPoolInfo.OrangeTimes, gachaItemId)
if gachaPoolInfo.OrangeTimes > 90 {
logger.LOG.Error("[abnormal orange weapon], times: %v, gachaItemId: %v", gachaPoolInfo.OrangeTimes, gachaItemId)
}
} else if weaponDataConfig.RankLevel == 4 {
itemColor = Purple
logger.LOG.Debug("[purple weapon], times: %v, gachaItemId: %v", gachaPoolInfo.PurpleTimes, gachaItemId)
if gachaPoolInfo.PurpleTimes > 10 {
logger.LOG.Error("[abnormal purple weapon], times: %v, gachaItemId: %v", gachaPoolInfo.PurpleTimes, gachaItemId)
}
} else {
itemColor = Blue
}
}
// 后处理
switch itemColor {
case Orange:
// 重置5星保底计数
gachaPoolInfo.OrangeTimes = 0
if mustGetUpEnable {
// 找到UP的5星对应的掉落组id 要求配置表的UP的5星掉落组id规则固定为(卡池类型*100+12)
upOrangeDropId := int32(gachaType*100 + 12)
// 替换本次结果为5星大保底
if gachaPoolInfo.MustGetUpOrange {
logger.LOG.Debug("trigger must get up orange, uid: %v", userId)
upOrangeDropGroupDataConfig := gdc.CONF.DropGroupDataMap[upOrangeDropId]
if upOrangeDropGroupDataConfig == nil {
logger.LOG.Error("drop group not found, drop id: %v", upOrangeDropId)
return false, 0
}
upOrangeOk, upOrangeDrop := g.doFullRandDrop(upOrangeDropGroupDataConfig)
if !upOrangeOk {
return false, 0
}
gachaPoolInfo.MustGetUpOrange = false
upOrangeGachaItemId := uint32(upOrangeDrop.Result)
return upOrangeOk, upOrangeGachaItemId
}
// 触发5星大保底
if drop.DropId != upOrangeDropId {
gachaPoolInfo.MustGetUpOrange = true
}
}
case Purple:
// 重置4星保底计数
gachaPoolInfo.PurpleTimes = 0
if mustGetUpEnable {
// 找到UP的4星对应的掉落组id 要求配置表的UP的4星掉落组id规则固定为(卡池类型*100+22)
upPurpleDropId := int32(gachaType*100 + 22)
// 替换本次结果为4星大保底
if gachaPoolInfo.MustGetUpPurple {
logger.LOG.Debug("trigger must get up purple, uid: %v", userId)
upPurpleDropGroupDataConfig := gdc.CONF.DropGroupDataMap[upPurpleDropId]
if upPurpleDropGroupDataConfig == nil {
logger.LOG.Error("drop group not found, drop id: %v", upPurpleDropId)
return false, 0
}
upPurpleOk, upPurpleDrop := g.doFullRandDrop(upPurpleDropGroupDataConfig)
if !upPurpleOk {
return false, 0
}
gachaPoolInfo.MustGetUpPurple = false
upPurpleGachaItemId := uint32(upPurpleDrop.Result)
return upPurpleOk, upPurpleGachaItemId
}
// 触发4星大保底
if drop.DropId != upPurpleDropId {
gachaPoolInfo.MustGetUpPurple = true
}
}
default:
}
return ok, gachaItemId
}
// 走一次完整流程的掉落组
func (g *GameManager) doFullRandDrop(dropGroupDataConfig *gdc.DropGroupData) (bool, *gdc.Drop) {
for {
drop := g.doRandDropOnce(dropGroupDataConfig)
if drop == nil {
logger.LOG.Error("weight error, drop group config: %v", dropGroupDataConfig)
return false, nil
}
if drop.IsEnd {
// 成功抽到物品
return true, drop
}
// 进行下一步掉落流程
dropGroupDataConfig = gdc.CONF.DropGroupDataMap[drop.Result]
if dropGroupDataConfig == nil {
logger.LOG.Error("drop config tab exist error, invalid drop id: %v", drop.Result)
return false, nil
}
}
}
// 进行单次随机掉落
func (g *GameManager) doRandDropOnce(dropGroupDataConfig *gdc.DropGroupData) *gdc.Drop {
randNum := random.GetRandomInt32(0, dropGroupDataConfig.WeightAll-1)
sumWeight := int32(0)
// 轮盘选择法
for _, drop := range dropGroupDataConfig.DropConfig {
sumWeight += drop.Weight
if sumWeight > randNum {
return drop
}
}
return nil
}

182
gs/game/user_item.go Normal file
View File

@@ -0,0 +1,182 @@
package game
import (
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
)
type UserItem struct {
ItemId uint32
ChangeCount uint32
}
func (g *GameManager) GetAllItemDataConfig() map[int32]*gdc.ItemData {
allItemDataConfig := make(map[int32]*gdc.ItemData)
for itemId, itemData := range gdc.CONF.ItemDataMap {
if itemData.ItemEnumType == constant.ItemTypeConst.ITEM_WEAPON {
// 排除武器
continue
}
if itemData.ItemEnumType == constant.ItemTypeConst.ITEM_RELIQUARY {
// 排除圣遗物
continue
}
if itemId == 100086 ||
itemId == 100087 ||
(itemId >= 100100 && itemId <= 101000) ||
(itemId >= 101106 && itemId <= 101110) ||
itemId == 101306 ||
(itemId >= 101500 && itemId <= 104000) ||
itemId == 105001 ||
itemId == 105004 ||
(itemId >= 106000 && itemId <= 107000) ||
itemId == 107011 ||
itemId == 108000 ||
(itemId >= 109000 && itemId <= 110000) ||
(itemId >= 115000 && itemId <= 130000) ||
(itemId >= 200200 && itemId <= 200899) ||
itemId == 220050 ||
itemId == 220054 {
// 排除无效道具
continue
}
allItemDataConfig[itemId] = itemData
}
return allItemDataConfig
}
func (g *GameManager) AddUserItem(userId uint32, itemList []*UserItem, isHint bool, hintReason uint16) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
for _, userItem := range itemList {
player.AddItem(userItem.ItemId, userItem.ChangeCount)
}
// PacketStoreItemChangeNotify
storeItemChangeNotify := new(proto.StoreItemChangeNotify)
storeItemChangeNotify.StoreType = proto.StoreType_STORE_TYPE_PACK
for _, userItem := range itemList {
pbItem := &proto.Item{
ItemId: userItem.ItemId,
Guid: player.GetItemGuid(userItem.ItemId),
Detail: &proto.Item_Material{
Material: &proto.Material{
Count: player.GetItemCount(userItem.ItemId),
},
},
}
storeItemChangeNotify.ItemList = append(storeItemChangeNotify.ItemList, pbItem)
}
g.SendMsg(cmd.StoreItemChangeNotify, userId, player.ClientSeq, storeItemChangeNotify)
if isHint {
if hintReason == 0 {
hintReason = constant.ActionReasonConst.SubfieldDrop
}
// PacketItemAddHintNotify
itemAddHintNotify := new(proto.ItemAddHintNotify)
itemAddHintNotify.Reason = uint32(hintReason)
for _, userItem := range itemList {
itemAddHintNotify.ItemList = append(itemAddHintNotify.ItemList, &proto.ItemHint{
ItemId: userItem.ItemId,
Count: userItem.ChangeCount,
IsNew: false,
})
}
g.SendMsg(cmd.ItemAddHintNotify, userId, player.ClientSeq, itemAddHintNotify)
}
// PacketPlayerPropNotify
playerPropNotify := new(proto.PlayerPropNotify)
playerPropNotify.PropMap = make(map[uint32]*proto.PropValue)
for _, userItem := range itemList {
isVirtualItem, prop := player.GetVirtualItemProp(userItem.ItemId)
if !isVirtualItem {
continue
}
playerPropNotify.PropMap[uint32(prop)] = &proto.PropValue{
Type: uint32(prop),
Val: int64(player.PropertiesMap[prop]),
Value: &proto.PropValue_Ival{
Ival: int64(player.PropertiesMap[prop]),
},
}
}
if len(playerPropNotify.PropMap) > 0 {
g.SendMsg(cmd.PlayerPropNotify, userId, player.ClientSeq, playerPropNotify)
}
}
func (g *GameManager) CostUserItem(userId uint32, itemList []*UserItem) {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return
}
for _, userItem := range itemList {
player.CostItem(userItem.ItemId, userItem.ChangeCount)
}
// PacketStoreItemChangeNotify
storeItemChangeNotify := new(proto.StoreItemChangeNotify)
storeItemChangeNotify.StoreType = proto.StoreType_STORE_TYPE_PACK
for _, userItem := range itemList {
count := player.GetItemCount(userItem.ItemId)
if count == 0 {
continue
}
pbItem := &proto.Item{
ItemId: userItem.ItemId,
Guid: player.GetItemGuid(userItem.ItemId),
Detail: &proto.Item_Material{
Material: &proto.Material{
Count: count,
},
},
}
storeItemChangeNotify.ItemList = append(storeItemChangeNotify.ItemList, pbItem)
}
if len(storeItemChangeNotify.ItemList) > 0 {
g.SendMsg(cmd.StoreItemChangeNotify, userId, player.ClientSeq, storeItemChangeNotify)
}
// PacketStoreItemDelNotify
storeItemDelNotify := new(proto.StoreItemDelNotify)
storeItemDelNotify.StoreType = proto.StoreType_STORE_TYPE_PACK
for _, userItem := range itemList {
count := player.GetItemCount(userItem.ItemId)
if count > 0 {
continue
}
storeItemDelNotify.GuidList = append(storeItemDelNotify.GuidList, player.GetItemGuid(userItem.ItemId))
}
if len(storeItemDelNotify.GuidList) > 0 {
g.SendMsg(cmd.StoreItemDelNotify, userId, player.ClientSeq, storeItemDelNotify)
}
// PacketPlayerPropNotify
playerPropNotify := new(proto.PlayerPropNotify)
playerPropNotify.PropMap = make(map[uint32]*proto.PropValue)
for _, userItem := range itemList {
isVirtualItem, prop := player.GetVirtualItemProp(userItem.ItemId)
if !isVirtualItem {
continue
}
playerPropNotify.PropMap[uint32(prop)] = &proto.PropValue{
Type: uint32(prop),
Val: int64(player.PropertiesMap[prop]),
Value: &proto.PropValue_Ival{
Ival: int64(player.PropertiesMap[prop]),
},
}
}
if len(playerPropNotify.PropMap) > 0 {
g.SendMsg(cmd.PlayerPropNotify, userId, player.ClientSeq, playerPropNotify)
}
}

376
gs/game/user_login.go Normal file
View File

@@ -0,0 +1,376 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/reflection"
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
func (g *GameManager) OnLogin(userId uint32, clientSeq uint32) {
logger.LOG.Info("user login, uid: %v", userId)
player, asyncWait := g.userManager.OnlineUser(userId, clientSeq)
if !asyncWait {
g.OnLoginOk(userId, player, clientSeq)
}
}
func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq uint32) {
if player == nil {
g.SendMsg(cmd.DoSetPlayerBornDataNotify, userId, clientSeq, new(proto.DoSetPlayerBornDataNotify))
return
}
player.OnlineTime = uint32(time.Now().UnixMilli())
player.Online = true
// TODO 3.0.0REL版本 目前存在当前队伍活跃角色非主角时 登录进不去场景的情况 所以暂时先把四号队伍作为仅存在主角的保留队伍
team := player.TeamConfig.GetTeamByIndex(3)
team.AvatarIdList = []uint32{player.MainCharAvatarId, 0, 0, 0}
player.TeamConfig.CurrTeamIndex = 3
player.TeamConfig.CurrAvatarIndex = 0
// 初始化
player.InitAll()
player.TeamConfig.UpdateTeam()
// 创建世界
world := g.worldManager.CreateWorld(player, false)
world.AddPlayer(player, player.SceneId)
player.WorldId = world.id
// TODO 薄荷标记
if world.IsBigWorld() {
bigWorld := world.GetSceneById(3)
for pos := range g.worldManager.worldStatic.terrain {
bigWorld.CreateEntityGadget(&model.Vector{
X: float64(pos.X),
Y: float64(pos.Y),
Z: float64(pos.Z),
}, 3003009)
}
}
// PacketPlayerDataNotify
playerDataNotify := new(proto.PlayerDataNotify)
playerDataNotify.NickName = player.NickName
playerDataNotify.ServerTime = uint64(time.Now().UnixMilli())
playerDataNotify.IsFirstLoginToday = true
playerDataNotify.RegionId = player.RegionId
playerDataNotify.PropMap = make(map[uint32]*proto.PropValue)
for k, v := range player.PropertiesMap {
propValue := new(proto.PropValue)
propValue.Type = uint32(k)
propValue.Value = &proto.PropValue_Ival{Ival: int64(v)}
propValue.Val = int64(v)
playerDataNotify.PropMap[uint32(k)] = propValue
}
g.SendMsg(cmd.PlayerDataNotify, userId, clientSeq, playerDataNotify)
// PacketStoreWeightLimitNotify
storeWeightLimitNotify := new(proto.StoreWeightLimitNotify)
storeWeightLimitNotify.StoreType = proto.StoreType_STORE_TYPE_PACK
// TODO 原神背包容量限制 写到配置文件
storeWeightLimitNotify.WeightLimit = 30000
storeWeightLimitNotify.WeaponCountLimit = 2000
storeWeightLimitNotify.ReliquaryCountLimit = 1500
storeWeightLimitNotify.MaterialCountLimit = 2000
storeWeightLimitNotify.FurnitureCountLimit = 2000
g.SendMsg(cmd.StoreWeightLimitNotify, userId, clientSeq, storeWeightLimitNotify)
// PacketPlayerStoreNotify
playerStoreNotify := new(proto.PlayerStoreNotify)
playerStoreNotify.StoreType = proto.StoreType_STORE_TYPE_PACK
playerStoreNotify.WeightLimit = 30000
itemDataMapConfig := gdc.CONF.ItemDataMap
for _, weapon := range player.WeaponMap {
pbItem := &proto.Item{
ItemId: weapon.ItemId,
Guid: weapon.Guid,
Detail: nil,
}
if itemDataMapConfig[int32(weapon.ItemId)].ItemEnumType != constant.ItemTypeConst.ITEM_WEAPON {
continue
}
affixMap := make(map[uint32]uint32)
for _, affixId := range weapon.AffixIdList {
affixMap[affixId] = uint32(weapon.Refinement)
}
pbItem.Detail = &proto.Item_Equip{
Equip: &proto.Equip{
Detail: &proto.Equip_Weapon{
Weapon: &proto.Weapon{
Level: uint32(weapon.Level),
Exp: weapon.Exp,
PromoteLevel: uint32(weapon.Promote),
AffixMap: affixMap,
},
},
IsLocked: weapon.Lock,
},
}
playerStoreNotify.ItemList = append(playerStoreNotify.ItemList, pbItem)
}
for _, reliquary := range player.ReliquaryMap {
pbItem := &proto.Item{
ItemId: reliquary.ItemId,
Guid: reliquary.Guid,
Detail: nil,
}
if itemDataMapConfig[int32(reliquary.ItemId)].ItemEnumType != constant.ItemTypeConst.ITEM_RELIQUARY {
continue
}
pbItem.Detail = &proto.Item_Equip{
Equip: &proto.Equip{
Detail: &proto.Equip_Reliquary{
Reliquary: &proto.Reliquary{
Level: uint32(reliquary.Level),
Exp: reliquary.Exp,
PromoteLevel: uint32(reliquary.Promote),
MainPropId: reliquary.MainPropId,
// TODO 圣遗物副词条
AppendPropIdList: nil,
},
},
IsLocked: reliquary.Lock,
},
}
playerStoreNotify.ItemList = append(playerStoreNotify.ItemList, pbItem)
}
for _, item := range player.ItemMap {
pbItem := &proto.Item{
ItemId: item.ItemId,
Guid: item.Guid,
Detail: nil,
}
itemDataConfig := itemDataMapConfig[int32(item.ItemId)]
if itemDataConfig != nil && itemDataConfig.ItemEnumType == constant.ItemTypeConst.ITEM_FURNITURE {
pbItem.Detail = &proto.Item_Furniture{
Furniture: &proto.Furniture{
Count: item.Count,
},
}
} else {
pbItem.Detail = &proto.Item_Material{
Material: &proto.Material{
Count: item.Count,
DeleteInfo: nil,
},
}
}
playerStoreNotify.ItemList = append(playerStoreNotify.ItemList, pbItem)
}
g.SendMsg(cmd.PlayerStoreNotify, userId, clientSeq, playerStoreNotify)
// PacketAvatarDataNotify
avatarDataNotify := new(proto.AvatarDataNotify)
chooseAvatarId := player.TeamConfig.GetActiveAvatarId()
avatarDataNotify.CurAvatarTeamId = uint32(player.TeamConfig.GetActiveTeamId())
avatarDataNotify.ChooseAvatarGuid = player.AvatarMap[chooseAvatarId].Guid
avatarDataNotify.OwnedFlycloakList = player.FlyCloakList
// 角色衣装
avatarDataNotify.OwnedCostumeList = player.CostumeList
for _, avatar := range player.AvatarMap {
pbAvatar := g.PacketAvatarInfo(avatar)
avatarDataNotify.AvatarList = append(avatarDataNotify.AvatarList, pbAvatar)
}
avatarDataNotify.AvatarTeamMap = make(map[uint32]*proto.AvatarTeam)
for teamIndex, team := range player.TeamConfig.TeamList {
var teamAvatarGuidList []uint64 = nil
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
teamAvatarGuidList = append(teamAvatarGuidList, player.AvatarMap[avatarId].Guid)
}
avatarDataNotify.AvatarTeamMap[uint32(teamIndex)+1] = &proto.AvatarTeam{
AvatarGuidList: teamAvatarGuidList,
TeamName: team.Name,
}
}
g.SendMsg(cmd.AvatarDataNotify, userId, clientSeq, avatarDataNotify)
player.SceneLoadState = model.SceneNone
// PacketPlayerEnterSceneNotify
playerEnterSceneNotify := g.PacketPlayerEnterSceneNotify(player)
g.SendMsg(cmd.PlayerEnterSceneNotify, userId, clientSeq, playerEnterSceneNotify)
// PacketOpenStateUpdateNotify
openStateUpdateNotify := new(proto.OpenStateUpdateNotify)
openStateConstMap := reflection.ConvStructToMap(constant.OpenStateConst)
openStateUpdateNotify.OpenStateMap = make(map[uint32]uint32)
for _, v := range openStateConstMap {
openStateUpdateNotify.OpenStateMap[uint32(v.(uint16))] = 1
}
g.SendMsg(cmd.OpenStateUpdateNotify, userId, clientSeq, openStateUpdateNotify)
}
func (g *GameManager) OnReg(userId uint32, clientSeq uint32, payloadMsg pb.Message) {
logger.LOG.Debug("user reg, uid: %v", userId)
req := payloadMsg.(*proto.SetPlayerBornDataReq)
logger.LOG.Debug("avatar id: %v, nickname: %v", req.AvatarId, req.NickName)
exist, asyncWait := g.userManager.CheckUserExistOnReg(userId, req, clientSeq)
if !asyncWait {
g.OnRegOk(exist, req, userId, clientSeq)
}
}
func (g *GameManager) OnRegOk(exist bool, req *proto.SetPlayerBornDataReq, userId uint32, clientSeq uint32) {
if exist {
logger.LOG.Error("recv reg req, but user is already exist, userId: %v", userId)
return
}
nickName := req.NickName
mainCharAvatarId := req.GetAvatarId()
if mainCharAvatarId != 10000005 && mainCharAvatarId != 10000007 {
logger.LOG.Error("invalid main char avatar id: %v", mainCharAvatarId)
return
}
player := g.CreatePlayer(userId, nickName, mainCharAvatarId)
g.userManager.AddUser(player)
g.SendMsg(cmd.SetPlayerBornDataRsp, userId, clientSeq, new(proto.SetPlayerBornDataRsp))
g.OnLogin(userId, clientSeq)
}
func (g *GameManager) OnUserOffline(userId uint32) {
logger.LOG.Info("user offline, uid: %v", userId)
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, userId: %v", userId)
return
}
world := g.worldManager.GetWorldByID(player.WorldId)
if world != nil {
g.UserWorldRemovePlayer(world, player)
}
player.OfflineTime = uint32(time.Now().Unix())
player.Online = false
player.TotalOnlineTime += uint32(time.Now().UnixMilli()) - player.OnlineTime
g.userManager.OfflineUser(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.MainCharAvatarId = mainCharAvatarId
player.HeadImage = mainCharAvatarId
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.RegionId = 1
player.SceneId = 3
player.PropertiesMap = make(map[uint16]uint32)
// 初始化所有属性
propList := reflection.ConvStructToMap(constant.PlayerPropertyConst)
for fieldName, fieldValue := range propList {
if fieldName == "PROP_EXP" ||
fieldName == "PROP_BREAK_LEVEL" ||
fieldName == "PROP_SATIATION_VAL" ||
fieldName == "PROP_SATIATION_PENALTY_TIME" ||
fieldName == "PROP_LEVEL" {
continue
}
value := fieldValue.(uint16)
player.PropertiesMap[value] = 0
}
player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL] = 1
player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL] = 0
player.PropertiesMap[constant.PlayerPropertyConst.PROP_IS_SPRING_AUTO_USE] = 1
player.PropertiesMap[constant.PlayerPropertyConst.PROP_SPRING_AUTO_USE_PERCENT] = 100
player.PropertiesMap[constant.PlayerPropertyConst.PROP_IS_FLYABLE] = 1
player.PropertiesMap[constant.PlayerPropertyConst.PROP_IS_TRANSFERABLE] = 1
player.PropertiesMap[constant.PlayerPropertyConst.PROP_MAX_STAMINA] = 24000
player.PropertiesMap[constant.PlayerPropertyConst.PROP_CUR_PERSIST_STAMINA] = 24000
player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_RESIN] = 160
player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE] = 2
player.PropertiesMap[constant.PlayerPropertyConst.PROP_IS_MP_MODE_AVAILABLE] = 1
player.FlyCloakList = make([]uint32, 0)
player.FlyCloakList = append(player.FlyCloakList, 140001)
player.FlyCloakList = append(player.FlyCloakList, 140002)
player.FlyCloakList = append(player.FlyCloakList, 140003)
player.FlyCloakList = append(player.FlyCloakList, 140004)
player.FlyCloakList = append(player.FlyCloakList, 140005)
player.FlyCloakList = append(player.FlyCloakList, 140006)
player.FlyCloakList = append(player.FlyCloakList, 140007)
player.FlyCloakList = append(player.FlyCloakList, 140008)
player.FlyCloakList = append(player.FlyCloakList, 140009)
player.FlyCloakList = append(player.FlyCloakList, 140010)
player.CostumeList = make([]uint32, 0)
player.CostumeList = append(player.CostumeList, 200301)
player.CostumeList = append(player.CostumeList, 201401)
player.CostumeList = append(player.CostumeList, 202701)
player.CostumeList = append(player.CostumeList, 204201)
player.CostumeList = append(player.CostumeList, 200302)
player.CostumeList = append(player.CostumeList, 202101)
player.CostumeList = append(player.CostumeList, 204101)
player.CostumeList = append(player.CostumeList, 204501)
player.CostumeList = append(player.CostumeList, 201601)
player.CostumeList = append(player.CostumeList, 203101)
player.Pos = &model.Vector{X: 2747, Y: 194, Z: -1719}
player.Rot = &model.Vector{X: 0, Y: 307, Z: 0}
player.ItemMap = make(map[uint32]*model.Item)
player.WeaponMap = make(map[uint64]*model.Weapon)
player.ReliquaryMap = make(map[uint64]*model.Reliquary)
player.AvatarMap = make(map[uint32]*model.Avatar)
player.GameObjectGuidMap = make(map[uint64]model.GameObject)
player.DropInfo = model.NewDropInfo()
player.ChatMsgMap = make(map[uint32][]*model.ChatMsg)
// 选哥哥的福报
if mainCharAvatarId == 10000005 {
// 添加所有角色
allAvatarDataConfig := g.GetAllAvatarDataConfig()
for avatarId, avatarDataConfig := range allAvatarDataConfig {
player.AddAvatar(uint32(avatarId))
// 添加初始武器
weaponId := uint64(g.snowflake.GenId())
player.AddWeapon(uint32(avatarDataConfig.InitialWeapon), weaponId)
// 角色装上初始武器
player.WearWeapon(uint32(avatarId), weaponId)
}
// 添加所有武器
allWeaponDataConfig := g.GetAllWeaponDataConfig()
for itemId := range allWeaponDataConfig {
weaponId := uint64(g.snowflake.GenId())
player.AddWeapon(uint32(itemId), weaponId)
}
// 添加所有道具
allItemDataConfig := g.GetAllItemDataConfig()
for itemId := range allItemDataConfig {
player.AddItem(uint32(itemId), 1)
}
}
// 添加选定的主角
player.AddAvatar(mainCharAvatarId)
// 添加初始武器
avatarDataConfig := gdc.CONF.AvatarDataMap[int32(mainCharAvatarId)]
weaponId := uint64(g.snowflake.GenId())
player.AddWeapon(uint32(avatarDataConfig.InitialWeapon), weaponId)
// 角色装上初始武器
player.WearWeapon(mainCharAvatarId, weaponId)
player.TeamConfig = model.NewTeamInfo()
player.TeamConfig.AddAvatarToTeam(mainCharAvatarId, 0)
return player
}

300
gs/game/user_manager.go Normal file
View File

@@ -0,0 +1,300 @@
package game
import (
"encoding/json"
"hk4e/gs/dao"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/proto"
"sync"
"time"
)
type UserManager struct {
dao *dao.Dao
playerMap map[uint32]*model.Player
playerMapLock sync.RWMutex
localEventChan chan *LocalEvent
}
func NewUserManager(dao *dao.Dao, localEventChan chan *LocalEvent) (r *UserManager) {
r = new(UserManager)
r.dao = dao
r.playerMap = make(map[uint32]*model.Player)
r.localEventChan = localEventChan
return r
}
func (u *UserManager) GetUserOnlineState(userId uint32) bool {
u.playerMapLock.RLock()
player, exist := u.playerMap[userId]
u.playerMapLock.RUnlock()
if !exist {
return false
} else {
return player.Online
}
}
func (u *UserManager) GetOnlineUser(userId uint32) *model.Player {
u.playerMapLock.RLock()
player, exist := u.playerMap[userId]
u.playerMapLock.RUnlock()
if !exist {
return nil
} else {
if player.Online {
return player
} else {
return nil
}
}
}
func (u *UserManager) GetAllOnlineUserList() map[uint32]*model.Player {
onlinePlayerMap := make(map[uint32]*model.Player)
u.playerMapLock.RLock()
for userId, player := range u.playerMap {
if player.Online == false {
continue
}
onlinePlayerMap[userId] = player
}
u.playerMapLock.RUnlock()
return onlinePlayerMap
}
type PlayerRegInfo struct {
Exist bool
Req *proto.SetPlayerBornDataReq
UserId uint32
ClientSeq uint32
}
func (u *UserManager) CheckUserExistOnReg(userId uint32, req *proto.SetPlayerBornDataReq, clientSeq uint32) (exist bool, asyncWait bool) {
u.playerMapLock.RLock()
_, exist = u.playerMap[userId]
u.playerMapLock.RUnlock()
if exist {
return true, false
} else {
go func() {
player := u.loadUserFromDb(userId)
exist = false
if player != nil {
exist = true
}
u.localEventChan <- &LocalEvent{
EventId: CheckUserExistOnRegFromDbFinish,
Msg: &PlayerRegInfo{
Exist: exist,
Req: req,
UserId: userId,
ClientSeq: clientSeq,
},
}
}()
return false, true
}
}
func (u *UserManager) LoadTempOfflineUserSync(userId uint32) *model.Player {
u.playerMapLock.RLock()
player, exist := u.playerMap[userId]
u.playerMapLock.RUnlock()
if exist {
return player
} else {
player = u.loadUserFromDb(userId)
if player == nil {
return nil
}
player.DbState = model.DbOffline
u.playerMapLock.Lock()
u.playerMap[player.PlayerID] = player
u.playerMapLock.Unlock()
return player
}
}
func (u *UserManager) loadUserFromDb(userId uint32) *model.Player {
player, err := u.dao.QueryPlayerByID(userId)
if err != nil {
logger.LOG.Error("query player error: %v", err)
return nil
}
return player
}
func (u *UserManager) AddUser(player *model.Player) {
if player == nil {
return
}
u.ChangeUserDbState(player, model.DbInsert)
u.playerMapLock.Lock()
u.playerMap[player.PlayerID] = player
u.playerMapLock.Unlock()
}
func (u *UserManager) DeleteUser(player *model.Player) {
if player == nil {
return
}
u.ChangeUserDbState(player, model.DbDelete)
u.playerMapLock.Lock()
u.playerMap[player.PlayerID] = player
u.playerMapLock.Unlock()
}
func (u *UserManager) UpdateUser(player *model.Player) {
if player == nil {
return
}
u.ChangeUserDbState(player, model.DbUpdate)
u.playerMapLock.Lock()
u.playerMap[player.PlayerID] = player
u.playerMapLock.Unlock()
}
type PlayerLoginInfo struct {
UserId uint32
Player *model.Player
ClientSeq uint32
}
func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32) (*model.Player, bool) {
u.playerMapLock.RLock()
player, exist := u.playerMap[userId]
u.playerMapLock.RUnlock()
if exist {
u.ChangeUserDbState(player, model.DbNormal)
return player, false
} else {
go func() {
player = u.loadUserFromDb(userId)
if player != nil {
player.DbState = model.DbNormal
u.playerMapLock.Lock()
u.playerMap[player.PlayerID] = player
u.playerMapLock.Unlock()
}
u.localEventChan <- &LocalEvent{
EventId: LoadLoginUserFromDbFinish,
Msg: &PlayerLoginInfo{
UserId: userId,
Player: player,
ClientSeq: clientSeq,
},
}
}()
return nil, true
}
}
func (u *UserManager) OfflineUser(player *model.Player) {
if player == nil {
return
}
u.ChangeUserDbState(player, model.DbOffline)
u.playerMapLock.Lock()
u.playerMap[player.PlayerID] = player
u.playerMapLock.Unlock()
}
func (u *UserManager) ChangeUserDbState(player *model.Player, state int) {
if player == nil {
return
}
switch player.DbState {
case model.DbInsert:
if state == model.DbDelete {
player.DbState = model.DbDelete
}
case model.DbDelete:
case model.DbUpdate:
if state == model.DbDelete {
player.DbState = model.DbDelete
} else if state == model.DbOffline {
player.DbState = model.DbOffline
}
case model.DbNormal:
if state == model.DbDelete {
player.DbState = model.DbDelete
} else if state == model.DbUpdate {
player.DbState = model.DbUpdate
} else if state == model.DbOffline {
player.DbState = model.DbOffline
}
case model.DbOffline:
if state == model.DbDelete {
player.DbState = model.DbDelete
} else if state == model.DbUpdate {
player.DbState = model.DbUpdate
} else if state == model.DbNormal {
player.DbState = model.DbNormal
}
}
}
func (u *UserManager) StartAutoSaveUser() {
// 用户数据库定时同步协程
go func() {
ticker := time.NewTicker(time.Minute * 5)
for {
logger.LOG.Info("auto save user start")
playerMapTemp := make(map[uint32]*model.Player)
u.playerMapLock.RLock()
for k, v := range u.playerMap {
playerMapTemp[k] = v
}
u.playerMapLock.RUnlock()
logger.LOG.Info("copy user map finish")
insertList := make([]*model.Player, 0)
deleteList := make([]uint32, 0)
updateList := make([]*model.Player, 0)
for k, v := range playerMapTemp {
switch v.DbState {
case model.DbInsert:
insertList = append(insertList, v)
playerMapTemp[k].DbState = model.DbNormal
case model.DbDelete:
deleteList = append(deleteList, v.PlayerID)
delete(playerMapTemp, k)
case model.DbUpdate:
updateList = append(updateList, v)
playerMapTemp[k].DbState = model.DbNormal
case model.DbNormal:
continue
case model.DbOffline:
updateList = append(updateList, v)
delete(playerMapTemp, k)
}
}
insertListJson, err := json.Marshal(insertList)
logger.LOG.Debug("insertList: %v", string(insertListJson))
deleteListJson, err := json.Marshal(deleteList)
logger.LOG.Debug("deleteList: %v", string(deleteListJson))
updateListJson, err := json.Marshal(updateList)
logger.LOG.Debug("updateList: %v", string(updateListJson))
logger.LOG.Info("db state init finish")
err = u.dao.InsertPlayerList(insertList)
if err != nil {
logger.LOG.Error("insert player list error: %v", err)
}
err = u.dao.DeletePlayerList(deleteList)
if err != nil {
logger.LOG.Error("delete player error: %v", err)
}
err = u.dao.UpdatePlayerList(updateList)
if err != nil {
logger.LOG.Error("update player error: %v", err)
}
logger.LOG.Info("db write finish")
u.playerMapLock.Lock()
u.playerMap = playerMapTemp
u.playerMapLock.Unlock()
logger.LOG.Info("auto save user finish")
<-ticker.C
}
}()
}

192
gs/game/user_map.go Normal file
View File

@@ -0,0 +1,192 @@
package game
import (
pb "google.golang.org/protobuf/proto"
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"strconv"
)
func (g *GameManager) SceneTransToPointReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get scene trans to point, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SceneTransToPointReq)
transPointId := strconv.Itoa(int(req.SceneId)) + "_" + strconv.Itoa(int(req.PointId))
transPointConfig, exist := gdc.CONF.ScenePointEntries[transPointId]
if !exist {
// PacketSceneTransToPointRsp
sceneTransToPointRsp := new(proto.SceneTransToPointRsp)
sceneTransToPointRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SVR_ERROR)
g.SendMsg(cmd.SceneTransToPointRsp, player.PlayerID, player.ClientSeq, sceneTransToPointRsp)
return
}
// 传送玩家
newSceneId := req.SceneId
oldSceneId := player.SceneId
oldPos := &model.Vector{
X: player.Pos.X,
Y: player.Pos.Y,
Z: player.Pos.Z,
}
jumpScene := false
if newSceneId != oldSceneId {
jumpScene = true
}
world := g.worldManager.GetWorldByID(player.WorldId)
oldScene := world.GetSceneById(oldSceneId)
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
playerTeamEntity := oldScene.GetPlayerTeamEntity(player.PlayerID)
g.RemoveSceneEntityNotifyBroadcast(oldScene, []uint32{playerTeamEntity.avatarEntityMap[activeAvatarId]})
if jumpScene {
// PacketDelTeamEntityNotify
delTeamEntityNotify := g.PacketDelTeamEntityNotify(oldScene, player)
g.SendMsg(cmd.DelTeamEntityNotify, player.PlayerID, player.ClientSeq, delTeamEntityNotify)
oldScene.RemovePlayer(player)
newScene := world.GetSceneById(newSceneId)
newScene.AddPlayer(player)
} else {
oldScene.UpdatePlayerTeamEntity(player)
}
player.Pos.X = transPointConfig.PointData.TranPos.X
player.Pos.Y = transPointConfig.PointData.TranPos.Y
player.Pos.Z = transPointConfig.PointData.TranPos.Z
player.SceneId = newSceneId
player.SceneLoadState = model.SceneNone
// PacketPlayerEnterSceneNotify
var enterType proto.EnterType
if jumpScene {
logger.LOG.Debug("player jump scene, scene: %v, pos: %v", player.SceneId, player.Pos)
enterType = proto.EnterType_ENTER_TYPE_JUMP
} else {
logger.LOG.Debug("player goto scene, scene: %v, pos: %v", player.SceneId, player.Pos)
enterType = proto.EnterType_ENTER_TYPE_GOTO
}
playerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyTp(player, enterType, uint32(constant.EnterReasonConst.TransPoint), oldSceneId, oldPos)
g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, playerEnterSceneNotify)
// PacketSceneTransToPointRsp
sceneTransToPointRsp := new(proto.SceneTransToPointRsp)
sceneTransToPointRsp.Retcode = 0
sceneTransToPointRsp.PointId = req.PointId
sceneTransToPointRsp.SceneId = req.SceneId
g.SendMsg(cmd.SceneTransToPointRsp, player.PlayerID, player.ClientSeq, sceneTransToPointRsp)
}
func (g *GameManager) MarkMapReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user mark map, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.MarkMapReq)
operation := req.Op
if operation == proto.MarkMapReq_OPERATION_ADD {
logger.LOG.Debug("user mark type: %v", req.Mark.PointType)
if req.Mark.PointType == proto.MapMarkPointType_MAP_MARK_POINT_TYPE_NPC {
posYInt, err := strconv.ParseInt(req.Mark.Name, 10, 64)
if err != nil {
logger.LOG.Error("parse pos y error: %v", err)
posYInt = 0
}
// 传送玩家
newSceneId := req.Mark.SceneId
oldSceneId := player.SceneId
oldPos := &model.Vector{
X: player.Pos.X,
Y: player.Pos.Y,
Z: player.Pos.Z,
}
jumpScene := false
if newSceneId != oldSceneId {
jumpScene = true
}
world := g.worldManager.GetWorldByID(player.WorldId)
oldScene := world.GetSceneById(oldSceneId)
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
playerTeamEntity := oldScene.GetPlayerTeamEntity(player.PlayerID)
g.RemoveSceneEntityNotifyBroadcast(oldScene, []uint32{playerTeamEntity.avatarEntityMap[activeAvatarId]})
if jumpScene {
// PacketDelTeamEntityNotify
delTeamEntityNotify := g.PacketDelTeamEntityNotify(oldScene, player)
g.SendMsg(cmd.DelTeamEntityNotify, player.PlayerID, player.ClientSeq, delTeamEntityNotify)
oldScene.RemovePlayer(player)
newScene := world.GetSceneById(newSceneId)
newScene.AddPlayer(player)
} else {
oldScene.UpdatePlayerTeamEntity(player)
}
player.Pos.X = float64(req.Mark.Pos.X)
player.Pos.Y = float64(posYInt)
player.Pos.Z = float64(req.Mark.Pos.Z)
player.SceneId = newSceneId
player.SceneLoadState = model.SceneNone
// PacketPlayerEnterSceneNotify
var enterType proto.EnterType
if jumpScene {
logger.LOG.Debug("player jump scene, scene: %v, pos: %v", player.SceneId, player.Pos)
enterType = proto.EnterType_ENTER_TYPE_JUMP
} else {
logger.LOG.Debug("player goto scene, scene: %v, pos: %v", player.SceneId, player.Pos)
enterType = proto.EnterType_ENTER_TYPE_GOTO
}
playerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyTp(player, enterType, uint32(constant.EnterReasonConst.TransPoint), oldSceneId, oldPos)
g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, playerEnterSceneNotify)
}
}
}
func (g *GameManager) PathfindingEnterSceneReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user pathfinding enter scene, uid: %v", player.PlayerID)
g.SendMsg(cmd.PathfindingEnterSceneRsp, player.PlayerID, player.ClientSeq, new(proto.PathfindingEnterSceneRsp))
}
func (g *GameManager) QueryPathReq(player *model.Player, payloadMsg pb.Message) {
//logger.LOG.Debug("user query path, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.QueryPathReq)
// PacketQueryPathRsp
queryPathRsp := new(proto.QueryPathRsp)
queryPathRsp.Corners = []*proto.Vector{req.DestinationPos[0]}
queryPathRsp.QueryId = req.QueryId
queryPathRsp.QueryStatus = proto.QueryPathRsp_PATH_STATUS_TYPE_SUCC
g.SendMsg(cmd.QueryPathRsp, player.PlayerID, player.ClientSeq, queryPathRsp)
}
func (g *GameManager) GetScenePointReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get scene point, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.GetScenePointReq)
// PacketGetScenePointRsp
getScenePointRsp := new(proto.GetScenePointRsp)
getScenePointRsp.SceneId = req.SceneId
getScenePointRsp.UnlockedPointList = make([]uint32, 0)
for i := uint32(1); i < 1000; i++ {
getScenePointRsp.UnlockedPointList = append(getScenePointRsp.UnlockedPointList, i)
}
getScenePointRsp.UnlockAreaList = make([]uint32, 0)
for i := uint32(1); i < 9; i++ {
getScenePointRsp.UnlockAreaList = append(getScenePointRsp.UnlockAreaList, i)
}
g.SendMsg(cmd.GetScenePointRsp, player.PlayerID, player.ClientSeq, getScenePointRsp)
}
func (g *GameManager) GetSceneAreaReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get scene area, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.GetSceneAreaReq)
// PacketGetSceneAreaRsp
getSceneAreaRsp := new(proto.GetSceneAreaRsp)
getSceneAreaRsp.SceneId = req.SceneId
getSceneAreaRsp.AreaIdList = []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 100, 101, 102, 103, 200, 210, 300, 400, 401, 402, 403}
getSceneAreaRsp.CityInfoList = make([]*proto.CityInfo, 0)
getSceneAreaRsp.CityInfoList = append(getSceneAreaRsp.CityInfoList, &proto.CityInfo{CityId: 1, Level: 1})
getSceneAreaRsp.CityInfoList = append(getSceneAreaRsp.CityInfoList, &proto.CityInfo{CityId: 2, Level: 1})
getSceneAreaRsp.CityInfoList = append(getSceneAreaRsp.CityInfoList, &proto.CityInfo{CityId: 3, Level: 1})
g.SendMsg(cmd.GetSceneAreaRsp, player.PlayerID, player.ClientSeq, getSceneAreaRsp)
}

417
gs/game/user_multiplayer.go Normal file
View File

@@ -0,0 +1,417 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
func (g *GameManager) PlayerApplyEnterMpReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user apply enter world, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PlayerApplyEnterMpReq)
targetUid := req.TargetUid
// PacketPlayerApplyEnterMpRsp
playerApplyEnterMpRsp := new(proto.PlayerApplyEnterMpRsp)
playerApplyEnterMpRsp.TargetUid = targetUid
g.SendMsg(cmd.PlayerApplyEnterMpRsp, player.PlayerID, player.ClientSeq, playerApplyEnterMpRsp)
ok := g.UserApplyEnterWorld(player, targetUid)
if !ok {
// PacketPlayerApplyEnterMpResultNotify
playerApplyEnterMpResultNotify := new(proto.PlayerApplyEnterMpResultNotify)
playerApplyEnterMpResultNotify.TargetUid = targetUid
playerApplyEnterMpResultNotify.TargetNickname = ""
playerApplyEnterMpResultNotify.IsAgreed = false
playerApplyEnterMpResultNotify.Reason = proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_CANNOT_ENTER_MP
g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, player.PlayerID, player.ClientSeq, playerApplyEnterMpResultNotify)
}
}
func (g *GameManager) PlayerApplyEnterMpResultReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user deal world enter apply, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.PlayerApplyEnterMpResultReq)
applyUid := req.ApplyUid
isAgreed := req.IsAgreed
g.UserDealEnterWorld(player, applyUid, isAgreed)
// PacketPlayerApplyEnterMpResultRsp
playerApplyEnterMpResultRsp := new(proto.PlayerApplyEnterMpResultRsp)
playerApplyEnterMpResultRsp.ApplyUid = applyUid
playerApplyEnterMpResultRsp.IsAgreed = isAgreed
g.SendMsg(cmd.PlayerApplyEnterMpResultRsp, player.PlayerID, player.ClientSeq, playerApplyEnterMpResultRsp)
}
func (g *GameManager) PlayerGetForceQuitBanInfoReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get world exit ban info, uid: %v", player.PlayerID)
result := true
world := g.worldManager.GetWorldByID(player.WorldId)
for _, worldPlayer := range world.playerMap {
if worldPlayer.SceneLoadState != model.SceneEnterDone {
result = false
}
}
// PacketPlayerGetForceQuitBanInfoRsp
playerGetForceQuitBanInfoRsp := new(proto.PlayerGetForceQuitBanInfoRsp)
if result {
playerGetForceQuitBanInfoRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SUCC)
} else {
playerGetForceQuitBanInfoRsp.Retcode = int32(proto.Retcode_RETCODE_RET_MP_TARGET_PLAYER_IN_TRANSFER)
}
g.SendMsg(cmd.PlayerGetForceQuitBanInfoRsp, player.PlayerID, player.ClientSeq, playerGetForceQuitBanInfoRsp)
}
func (g *GameManager) BackMyWorldReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user back world, uid: %v", player.PlayerID)
// 其他玩家
ok := g.UserLeaveWorld(player)
// PacketBackMyWorldRsp
backMyWorldRsp := new(proto.BackMyWorldRsp)
if ok {
backMyWorldRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SUCC)
} else {
backMyWorldRsp.Retcode = int32(proto.Retcode_RETCODE_RET_MP_TARGET_PLAYER_IN_TRANSFER)
}
g.SendMsg(cmd.BackMyWorldRsp, player.PlayerID, player.ClientSeq, backMyWorldRsp)
}
func (g *GameManager) ChangeWorldToSingleModeReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change world to single, uid: %v", player.PlayerID)
// 房主
ok := g.UserLeaveWorld(player)
// PacketChangeWorldToSingleModeRsp
changeWorldToSingleModeRsp := new(proto.ChangeWorldToSingleModeRsp)
if ok {
changeWorldToSingleModeRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SUCC)
} else {
changeWorldToSingleModeRsp.Retcode = int32(proto.Retcode_RETCODE_RET_MP_TARGET_PLAYER_IN_TRANSFER)
}
g.SendMsg(cmd.ChangeWorldToSingleModeRsp, player.PlayerID, player.ClientSeq, changeWorldToSingleModeRsp)
}
func (g *GameManager) SceneKickPlayerReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user kick player, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SceneKickPlayerReq)
targetUid := req.TargetUid
targetPlayer := g.userManager.GetOnlineUser(targetUid)
ok := g.UserLeaveWorld(targetPlayer)
if ok {
// PacketSceneKickPlayerNotify
sceneKickPlayerNotify := new(proto.SceneKickPlayerNotify)
sceneKickPlayerNotify.TargetUid = targetUid
sceneKickPlayerNotify.KickerUid = player.PlayerID
world := g.worldManager.GetWorldByID(player.WorldId)
for _, worldPlayer := range world.playerMap {
g.SendMsg(cmd.SceneKickPlayerNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, sceneKickPlayerNotify)
}
}
// PacketSceneKickPlayerRsp
sceneKickPlayerRsp := new(proto.SceneKickPlayerRsp)
if ok {
sceneKickPlayerRsp.TargetUid = targetUid
} else {
sceneKickPlayerRsp.Retcode = int32(proto.Retcode_RETCODE_RET_MP_TARGET_PLAYER_IN_TRANSFER)
}
g.SendMsg(cmd.SceneKickPlayerRsp, player.PlayerID, player.ClientSeq, sceneKickPlayerRsp)
}
func (g *GameManager) UserApplyEnterWorld(player *model.Player, targetUid uint32) bool {
targetPlayer := g.userManager.GetOnlineUser(targetUid)
if targetPlayer == nil {
return false
}
world := g.worldManager.GetWorldByID(player.WorldId)
if world.multiplayer {
return false
}
applyTime, exist := targetPlayer.CoopApplyMap[player.PlayerID]
if exist && time.Now().UnixNano() < applyTime+int64(10*time.Second) {
return false
}
targetPlayer.CoopApplyMap[player.PlayerID] = time.Now().UnixNano()
targetWorld := g.worldManager.GetWorldByID(targetPlayer.WorldId)
if targetWorld.multiplayer && targetWorld.owner.PlayerID != targetPlayer.PlayerID {
return false
}
// PacketPlayerApplyEnterMpNotify
playerApplyEnterMpNotify := new(proto.PlayerApplyEnterMpNotify)
playerApplyEnterMpNotify.SrcPlayerInfo = g.PacketOnlinePlayerInfo(player)
g.SendMsg(cmd.PlayerApplyEnterMpNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, playerApplyEnterMpNotify)
return true
}
func (g *GameManager) UserDealEnterWorld(hostPlayer *model.Player, otherUid uint32, agree bool) {
otherPlayer := g.userManager.GetOnlineUser(otherUid)
if otherPlayer == nil {
return
}
applyTime, exist := hostPlayer.CoopApplyMap[otherUid]
if !exist || time.Now().UnixNano() > applyTime+int64(10*time.Second) {
return
}
delete(hostPlayer.CoopApplyMap, otherUid)
otherPlayerWorld := g.worldManager.GetWorldByID(otherPlayer.WorldId)
if otherPlayerWorld.multiplayer {
// PacketPlayerApplyEnterMpResultNotify
playerApplyEnterMpResultNotify := new(proto.PlayerApplyEnterMpResultNotify)
playerApplyEnterMpResultNotify.TargetUid = hostPlayer.PlayerID
playerApplyEnterMpResultNotify.TargetNickname = hostPlayer.NickName
playerApplyEnterMpResultNotify.IsAgreed = false
playerApplyEnterMpResultNotify.Reason = proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_CANNOT_ENTER_MP
g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, otherPlayer.PlayerID, otherPlayer.ClientSeq, playerApplyEnterMpResultNotify)
return
}
// PacketPlayerApplyEnterMpResultNotify
playerApplyEnterMpResultNotify := new(proto.PlayerApplyEnterMpResultNotify)
playerApplyEnterMpResultNotify.TargetUid = hostPlayer.PlayerID
playerApplyEnterMpResultNotify.TargetNickname = hostPlayer.NickName
playerApplyEnterMpResultNotify.IsAgreed = agree
playerApplyEnterMpResultNotify.Reason = proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_JUDGE
g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, otherPlayer.PlayerID, otherPlayer.ClientSeq, playerApplyEnterMpResultNotify)
if !agree {
return
}
hostWorld := g.worldManager.GetWorldByID(hostPlayer.WorldId)
if hostWorld.multiplayer == false {
g.UserWorldRemovePlayer(hostWorld, hostPlayer)
hostPlayer.TeamConfig.CurrTeamIndex = 3
hostPlayer.TeamConfig.CurrAvatarIndex = 0
// PacketPlayerEnterSceneNotify
hostPlayerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyMp(
hostPlayer,
hostPlayer,
proto.EnterType_ENTER_TYPE_SELF,
uint32(constant.EnterReasonConst.HostFromSingleToMp),
hostPlayer.SceneId,
hostPlayer.Pos,
)
g.SendMsg(cmd.PlayerEnterSceneNotify, hostPlayer.PlayerID, hostPlayer.ClientSeq, hostPlayerEnterSceneNotify)
hostWorld = g.worldManager.CreateWorld(hostPlayer, true)
g.UserWorldAddPlayer(hostWorld, hostPlayer)
hostPlayer.SceneLoadState = model.SceneNone
}
otherWorld := g.worldManager.GetWorldByID(otherPlayer.WorldId)
g.UserWorldRemovePlayer(otherWorld, otherPlayer)
otherPlayerOldSceneId := otherPlayer.SceneId
otherPlayerOldPos := &model.Vector{
X: otherPlayer.Pos.X,
Y: otherPlayer.Pos.Y,
Z: otherPlayer.Pos.Z,
}
otherPlayer.Pos = &model.Vector{
X: hostPlayer.Pos.X,
Y: hostPlayer.Pos.Y + 1,
Z: hostPlayer.Pos.Z,
}
otherPlayer.Rot = &model.Vector{
X: hostPlayer.Rot.X,
Y: hostPlayer.Rot.Y,
Z: hostPlayer.Rot.Z,
}
otherPlayer.SceneId = hostPlayer.SceneId
otherPlayer.TeamConfig.CurrTeamIndex = 3
otherPlayer.TeamConfig.CurrAvatarIndex = 0
// PacketPlayerEnterSceneNotify
playerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyMp(
otherPlayer,
hostPlayer,
proto.EnterType_ENTER_TYPE_OTHER,
uint32(constant.EnterReasonConst.TeamJoin),
otherPlayerOldSceneId,
otherPlayerOldPos,
)
g.SendMsg(cmd.PlayerEnterSceneNotify, otherPlayer.PlayerID, otherPlayer.ClientSeq, playerEnterSceneNotify)
g.UserWorldAddPlayer(hostWorld, otherPlayer)
otherPlayer.SceneLoadState = model.SceneNone
}
func (g *GameManager) UserLeaveWorld(player *model.Player) bool {
oldWorld := g.worldManager.GetWorldByID(player.WorldId)
if !oldWorld.multiplayer {
return false
}
for _, worldPlayer := range oldWorld.playerMap {
if worldPlayer.SceneLoadState != model.SceneEnterDone {
return false
}
}
g.UserWorldRemovePlayer(oldWorld, player)
//{
// newWorld := g.worldManager.CreateWorld(player, false)
// g.UserWorldAddPlayer(newWorld, player)
// player.SceneLoadState = model.SceneNone
//
// // PacketPlayerEnterSceneNotify
// enterReasonConst := constant.GetEnterReasonConst()
// playerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyMp(
// player,
// player,
// proto.EnterType_ENTER_TYPE_SELF,
// uint32(enterReasonConst.TeamBack),
// player.SceneId,
// player.Pos,
// )
// g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, playerEnterSceneNotify)
//}
{
// PacketClientReconnectNotify
g.SendMsg(cmd.ClientReconnectNotify, player.PlayerID, 0, new(proto.ClientReconnectNotify))
}
return true
}
func (g *GameManager) UserWorldAddPlayer(world *World, player *model.Player) {
_, exist := world.playerMap[player.PlayerID]
if exist {
return
}
world.AddPlayer(player, player.SceneId)
player.WorldId = world.id
if len(world.playerMap) > 1 {
g.UpdateWorldPlayerInfo(world, player)
}
}
func (g *GameManager) UserWorldRemovePlayer(world *World, player *model.Player) {
if world.multiplayer && player.PlayerID == world.owner.PlayerID {
// 多人世界房主离开剔除所有其他玩家
for _, worldPlayer := range world.playerMap {
if worldPlayer.PlayerID == world.owner.PlayerID {
continue
}
if ok := g.UserLeaveWorld(worldPlayer); !ok {
return
}
}
}
// PacketDelTeamEntityNotify
scene := world.GetSceneById(player.SceneId)
delTeamEntityNotify := g.PacketDelTeamEntityNotify(scene, player)
g.SendMsg(cmd.DelTeamEntityNotify, player.PlayerID, player.ClientSeq, delTeamEntityNotify)
if world.multiplayer {
// PlayerQuitFromMpNotify
playerQuitFromMpNotify := new(proto.PlayerQuitFromMpNotify)
playerQuitFromMpNotify.Reason = proto.PlayerQuitFromMpNotify_QUIT_REASON_BACK_TO_MY_WORLD
g.SendMsg(cmd.PlayerQuitFromMpNotify, player.PlayerID, player.ClientSeq, playerQuitFromMpNotify)
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
g.RemoveSceneEntityNotifyBroadcast(scene, []uint32{playerTeamEntity.avatarEntityMap[activeAvatarId]})
}
world.RemovePlayer(player)
player.WorldId = 0
if world.multiplayer && len(world.playerMap) > 0 {
g.UpdateWorldPlayerInfo(world, player)
}
if world.owner.PlayerID == player.PlayerID {
// 房主离开销毁世界
g.worldManager.DestroyWorld(world.id)
}
}
func (g *GameManager) UpdateWorldPlayerInfo(hostWorld *World, excludePlayer *model.Player) {
for _, worldPlayer := range hostWorld.playerMap {
if worldPlayer.PlayerID == excludePlayer.PlayerID || worldPlayer.SceneLoadState == model.SceneNone {
continue
}
// PacketSceneTeamUpdateNotify
sceneTeamUpdateNotify := g.PacketSceneTeamUpdateNotify(hostWorld)
g.SendMsg(cmd.SceneTeamUpdateNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, sceneTeamUpdateNotify)
// PacketWorldPlayerInfoNotify
worldPlayerInfoNotify := new(proto.WorldPlayerInfoNotify)
for _, subWorldPlayer := range hostWorld.playerMap {
onlinePlayerInfo := new(proto.OnlinePlayerInfo)
onlinePlayerInfo.Uid = subWorldPlayer.PlayerID
onlinePlayerInfo.Nickname = subWorldPlayer.NickName
onlinePlayerInfo.PlayerLevel = subWorldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL]
onlinePlayerInfo.MpSettingType = proto.MpSettingType(subWorldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE])
onlinePlayerInfo.NameCardId = subWorldPlayer.NameCard
onlinePlayerInfo.Signature = subWorldPlayer.Signature
onlinePlayerInfo.ProfilePicture = &proto.ProfilePicture{AvatarId: subWorldPlayer.HeadImage}
onlinePlayerInfo.CurPlayerNumInWorld = uint32(len(hostWorld.playerMap))
worldPlayerInfoNotify.PlayerInfoList = append(worldPlayerInfoNotify.PlayerInfoList, onlinePlayerInfo)
worldPlayerInfoNotify.PlayerUidList = append(worldPlayerInfoNotify.PlayerUidList, subWorldPlayer.PlayerID)
}
g.SendMsg(cmd.WorldPlayerInfoNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, worldPlayerInfoNotify)
// PacketScenePlayerInfoNotify
scenePlayerInfoNotify := new(proto.ScenePlayerInfoNotify)
for _, subWorldPlayer := range hostWorld.playerMap {
onlinePlayerInfo := new(proto.OnlinePlayerInfo)
onlinePlayerInfo.Uid = subWorldPlayer.PlayerID
onlinePlayerInfo.Nickname = subWorldPlayer.NickName
onlinePlayerInfo.PlayerLevel = subWorldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL]
onlinePlayerInfo.MpSettingType = proto.MpSettingType(subWorldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE])
onlinePlayerInfo.NameCardId = subWorldPlayer.NameCard
onlinePlayerInfo.Signature = subWorldPlayer.Signature
onlinePlayerInfo.ProfilePicture = &proto.ProfilePicture{AvatarId: subWorldPlayer.HeadImage}
onlinePlayerInfo.CurPlayerNumInWorld = uint32(len(hostWorld.playerMap))
scenePlayerInfoNotify.PlayerInfoList = append(scenePlayerInfoNotify.PlayerInfoList, &proto.ScenePlayerInfo{
Uid: subWorldPlayer.PlayerID,
PeerId: subWorldPlayer.PeerId,
Name: subWorldPlayer.NickName,
SceneId: subWorldPlayer.SceneId,
OnlinePlayerInfo: onlinePlayerInfo,
})
}
g.SendMsg(cmd.ScenePlayerInfoNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, scenePlayerInfoNotify)
// PacketSyncTeamEntityNotify
syncTeamEntityNotify := new(proto.SyncTeamEntityNotify)
syncTeamEntityNotify.SceneId = worldPlayer.SceneId
syncTeamEntityNotify.TeamEntityInfoList = make([]*proto.TeamEntityInfo, 0)
if hostWorld.multiplayer {
for _, subWorldPlayer := range hostWorld.playerMap {
if subWorldPlayer.PlayerID == worldPlayer.PlayerID {
continue
}
subWorldPlayerScene := hostWorld.GetSceneById(subWorldPlayer.SceneId)
subWorldPlayerTeamEntity := subWorldPlayerScene.GetPlayerTeamEntity(subWorldPlayer.PlayerID)
teamEntityInfo := &proto.TeamEntityInfo{
TeamEntityId: subWorldPlayerTeamEntity.teamEntityId,
AuthorityPeerId: subWorldPlayer.PeerId,
TeamAbilityInfo: new(proto.AbilitySyncStateInfo),
}
syncTeamEntityNotify.TeamEntityInfoList = append(syncTeamEntityNotify.TeamEntityInfoList, teamEntityInfo)
}
}
g.SendMsg(cmd.SyncTeamEntityNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, syncTeamEntityNotify)
// PacketSyncScenePlayTeamEntityNotify
syncScenePlayTeamEntityNotify := new(proto.SyncScenePlayTeamEntityNotify)
syncScenePlayTeamEntityNotify.SceneId = worldPlayer.SceneId
g.SendMsg(cmd.SyncScenePlayTeamEntityNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, syncScenePlayTeamEntityNotify)
}
}

711
gs/game/user_scene.go Normal file
View File

@@ -0,0 +1,711 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/object"
"hk4e/common/utils/random"
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"strconv"
"time"
)
func (g *GameManager) EnterSceneReadyReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user enter scene ready, uid: %v", player.PlayerID)
// PacketEnterScenePeerNotify
enterScenePeerNotify := new(proto.EnterScenePeerNotify)
enterScenePeerNotify.DestSceneId = player.SceneId
world := g.worldManager.GetWorldByID(player.WorldId)
enterScenePeerNotify.PeerId = player.PeerId
enterScenePeerNotify.HostPeerId = world.owner.PeerId
enterScenePeerNotify.EnterSceneToken = player.EnterSceneToken
g.SendMsg(cmd.EnterScenePeerNotify, player.PlayerID, player.ClientSeq, enterScenePeerNotify)
// PacketEnterSceneReadyRsp
enterSceneReadyRsp := new(proto.EnterSceneReadyRsp)
enterSceneReadyRsp.EnterSceneToken = player.EnterSceneToken
g.SendMsg(cmd.EnterSceneReadyRsp, player.PlayerID, player.ClientSeq, enterSceneReadyRsp)
}
func (g *GameManager) SceneInitFinishReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user scene init finish, uid: %v", player.PlayerID)
// PacketServerTimeNotify
serverTimeNotify := new(proto.ServerTimeNotify)
serverTimeNotify.ServerTime = uint64(time.Now().UnixMilli())
g.SendMsg(cmd.ServerTimeNotify, player.PlayerID, player.ClientSeq, serverTimeNotify)
// PacketWorldPlayerInfoNotify
worldPlayerInfoNotify := new(proto.WorldPlayerInfoNotify)
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
for _, worldPlayer := range world.playerMap {
onlinePlayerInfo := new(proto.OnlinePlayerInfo)
onlinePlayerInfo.Uid = worldPlayer.PlayerID
onlinePlayerInfo.Nickname = worldPlayer.NickName
onlinePlayerInfo.PlayerLevel = worldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL]
onlinePlayerInfo.MpSettingType = proto.MpSettingType(worldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE])
onlinePlayerInfo.NameCardId = worldPlayer.NameCard
onlinePlayerInfo.Signature = worldPlayer.Signature
onlinePlayerInfo.ProfilePicture = &proto.ProfilePicture{AvatarId: worldPlayer.HeadImage}
onlinePlayerInfo.CurPlayerNumInWorld = uint32(len(world.playerMap))
worldPlayerInfoNotify.PlayerInfoList = append(worldPlayerInfoNotify.PlayerInfoList, onlinePlayerInfo)
worldPlayerInfoNotify.PlayerUidList = append(worldPlayerInfoNotify.PlayerUidList, worldPlayer.PlayerID)
}
g.SendMsg(cmd.WorldPlayerInfoNotify, player.PlayerID, player.ClientSeq, worldPlayerInfoNotify)
// PacketWorldDataNotify
worldDataNotify := new(proto.WorldDataNotify)
worldDataNotify.WorldPropMap = make(map[uint32]*proto.PropValue)
// 世界等级
worldDataNotify.WorldPropMap[1] = &proto.PropValue{
Type: 1,
Val: int64(world.worldLevel),
Value: &proto.PropValue_Ival{Ival: int64(world.worldLevel)},
}
// 是否多人游戏
worldDataNotify.WorldPropMap[2] = &proto.PropValue{
Type: 2,
Val: object.ConvBoolToInt64(world.multiplayer),
Value: &proto.PropValue_Ival{Ival: object.ConvBoolToInt64(world.multiplayer)},
}
g.SendMsg(cmd.WorldDataNotify, player.PlayerID, player.ClientSeq, worldDataNotify)
// PacketPlayerWorldSceneInfoListNotify
playerWorldSceneInfoListNotify := new(proto.PlayerWorldSceneInfoListNotify)
playerWorldSceneInfoListNotify.InfoList = []*proto.PlayerWorldSceneInfo{
{SceneId: 1, IsLocked: false, SceneTagIdList: []uint32{}},
{SceneId: 3, IsLocked: false, SceneTagIdList: []uint32{102, 113, 117}},
{SceneId: 4, IsLocked: false, SceneTagIdList: []uint32{106, 109, 117}},
{SceneId: 5, IsLocked: false, SceneTagIdList: []uint32{}},
{SceneId: 6, IsLocked: false, SceneTagIdList: []uint32{}},
{SceneId: 7, IsLocked: false, SceneTagIdList: []uint32{}},
}
xumi := &proto.PlayerWorldSceneInfo{
SceneId: 9,
IsLocked: false,
SceneTagIdList: []uint32{},
}
for i := 0; i < 3000; i++ {
xumi.SceneTagIdList = append(xumi.SceneTagIdList, uint32(i))
}
playerWorldSceneInfoListNotify.InfoList = append(playerWorldSceneInfoListNotify.InfoList, xumi)
g.SendMsg(cmd.PlayerWorldSceneInfoListNotify, player.PlayerID, player.ClientSeq, playerWorldSceneInfoListNotify)
// SceneForceUnlockNotify
g.SendMsg(cmd.SceneForceUnlockNotify, player.PlayerID, player.ClientSeq, new(proto.SceneForceUnlockNotify))
// PacketHostPlayerNotify
hostPlayerNotify := new(proto.HostPlayerNotify)
hostPlayerNotify.HostUid = world.owner.PlayerID
hostPlayerNotify.HostPeerId = world.owner.PeerId
g.SendMsg(cmd.HostPlayerNotify, player.PlayerID, player.ClientSeq, hostPlayerNotify)
// PacketSceneTimeNotify
sceneTimeNotify := new(proto.SceneTimeNotify)
sceneTimeNotify.SceneId = player.SceneId
sceneTimeNotify.SceneTime = uint64(scene.GetSceneTime())
g.SendMsg(cmd.SceneTimeNotify, player.PlayerID, player.ClientSeq, sceneTimeNotify)
// PacketPlayerGameTimeNotify
playerGameTimeNotify := new(proto.PlayerGameTimeNotify)
playerGameTimeNotify.GameTime = scene.gameTime
playerGameTimeNotify.Uid = player.PlayerID
g.SendMsg(cmd.PlayerGameTimeNotify, player.PlayerID, player.ClientSeq, playerGameTimeNotify)
// PacketPlayerEnterSceneInfoNotify
empty := new(proto.AbilitySyncStateInfo)
playerEnterSceneInfoNotify := new(proto.PlayerEnterSceneInfoNotify)
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
playerEnterSceneInfoNotify.CurAvatarEntityId = playerTeamEntity.avatarEntityMap[activeAvatarId]
playerEnterSceneInfoNotify.EnterSceneToken = player.EnterSceneToken
playerEnterSceneInfoNotify.TeamEnterInfo = &proto.TeamEnterSceneInfo{
TeamEntityId: playerTeamEntity.teamEntityId,
TeamAbilityInfo: empty,
AbilityControlBlock: new(proto.AbilityControlBlock),
}
playerEnterSceneInfoNotify.MpLevelEntityInfo = &proto.MPLevelEntityInfo{
EntityId: g.worldManager.GetWorldByID(player.WorldId).mpLevelEntityId,
AuthorityPeerId: g.worldManager.GetWorldByID(player.WorldId).owner.PeerId,
AbilityInfo: empty,
}
activeTeam := player.TeamConfig.GetActiveTeam()
for _, avatarId := range activeTeam.AvatarIdList {
if avatarId == 0 {
break
}
avatar := player.AvatarMap[avatarId]
avatarEnterSceneInfo := new(proto.AvatarEnterSceneInfo)
avatarEnterSceneInfo.AvatarGuid = avatar.Guid
avatarEnterSceneInfo.AvatarEntityId = playerTeamEntity.avatarEntityMap[avatarId]
avatarEnterSceneInfo.WeaponGuid = avatar.EquipWeapon.Guid
avatarEnterSceneInfo.WeaponEntityId = playerTeamEntity.weaponEntityMap[avatar.EquipWeapon.WeaponId]
avatarEnterSceneInfo.AvatarAbilityInfo = empty
avatarEnterSceneInfo.WeaponAbilityInfo = empty
playerEnterSceneInfoNotify.AvatarEnterInfo = append(playerEnterSceneInfoNotify.AvatarEnterInfo, avatarEnterSceneInfo)
}
g.SendMsg(cmd.PlayerEnterSceneInfoNotify, player.PlayerID, player.ClientSeq, playerEnterSceneInfoNotify)
// PacketSceneAreaWeatherNotify
sceneAreaWeatherNotify := new(proto.SceneAreaWeatherNotify)
sceneAreaWeatherNotify.WeatherAreaId = 0
sceneAreaWeatherNotify.ClimateType = uint32(constant.ClimateTypeConst.CLIMATE_SUNNY)
g.SendMsg(cmd.SceneAreaWeatherNotify, player.PlayerID, player.ClientSeq, sceneAreaWeatherNotify)
// PacketScenePlayerInfoNotify
scenePlayerInfoNotify := new(proto.ScenePlayerInfoNotify)
for _, worldPlayer := range world.playerMap {
onlinePlayerInfo := new(proto.OnlinePlayerInfo)
onlinePlayerInfo.Uid = worldPlayer.PlayerID
onlinePlayerInfo.Nickname = worldPlayer.NickName
onlinePlayerInfo.PlayerLevel = worldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL]
onlinePlayerInfo.MpSettingType = proto.MpSettingType(worldPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE])
onlinePlayerInfo.NameCardId = worldPlayer.NameCard
onlinePlayerInfo.Signature = worldPlayer.Signature
onlinePlayerInfo.ProfilePicture = &proto.ProfilePicture{AvatarId: worldPlayer.HeadImage}
onlinePlayerInfo.CurPlayerNumInWorld = uint32(len(world.playerMap))
scenePlayerInfoNotify.PlayerInfoList = append(scenePlayerInfoNotify.PlayerInfoList, &proto.ScenePlayerInfo{
Uid: worldPlayer.PlayerID,
PeerId: worldPlayer.PeerId,
Name: worldPlayer.NickName,
SceneId: worldPlayer.SceneId,
OnlinePlayerInfo: onlinePlayerInfo,
})
}
g.SendMsg(cmd.ScenePlayerInfoNotify, player.PlayerID, player.ClientSeq, scenePlayerInfoNotify)
// PacketSceneTeamUpdateNotify
sceneTeamUpdateNotify := g.PacketSceneTeamUpdateNotify(world)
g.SendMsg(cmd.SceneTeamUpdateNotify, player.PlayerID, player.ClientSeq, sceneTeamUpdateNotify)
// PacketSyncTeamEntityNotify
syncTeamEntityNotify := new(proto.SyncTeamEntityNotify)
syncTeamEntityNotify.SceneId = player.SceneId
syncTeamEntityNotify.TeamEntityInfoList = make([]*proto.TeamEntityInfo, 0)
if world.multiplayer {
for _, worldPlayer := range world.playerMap {
if worldPlayer.PlayerID == player.PlayerID {
continue
}
worldPlayerScene := world.GetSceneById(worldPlayer.SceneId)
worldPlayerTeamEntity := worldPlayerScene.GetPlayerTeamEntity(worldPlayer.PlayerID)
teamEntityInfo := &proto.TeamEntityInfo{
TeamEntityId: worldPlayerTeamEntity.teamEntityId,
AuthorityPeerId: worldPlayer.PeerId,
TeamAbilityInfo: new(proto.AbilitySyncStateInfo),
}
syncTeamEntityNotify.TeamEntityInfoList = append(syncTeamEntityNotify.TeamEntityInfoList, teamEntityInfo)
}
}
g.SendMsg(cmd.SyncTeamEntityNotify, player.PlayerID, player.ClientSeq, syncTeamEntityNotify)
// PacketSyncScenePlayTeamEntityNotify
syncScenePlayTeamEntityNotify := new(proto.SyncScenePlayTeamEntityNotify)
syncScenePlayTeamEntityNotify.SceneId = player.SceneId
g.SendMsg(cmd.SyncScenePlayTeamEntityNotify, player.PlayerID, player.ClientSeq, syncScenePlayTeamEntityNotify)
// PacketSceneInitFinishRsp
SceneInitFinishRsp := new(proto.SceneInitFinishRsp)
SceneInitFinishRsp.EnterSceneToken = player.EnterSceneToken
g.SendMsg(cmd.SceneInitFinishRsp, player.PlayerID, player.ClientSeq, SceneInitFinishRsp)
player.SceneLoadState = model.SceneInitFinish
}
func (g *GameManager) EnterSceneDoneReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user enter scene done, uid: %v", player.PlayerID)
// PacketEnterSceneDoneRsp
enterSceneDoneRsp := new(proto.EnterSceneDoneRsp)
enterSceneDoneRsp.EnterSceneToken = player.EnterSceneToken
g.SendMsg(cmd.EnterSceneDoneRsp, player.PlayerID, player.ClientSeq, enterSceneDoneRsp)
// PacketPlayerTimeNotify
playerTimeNotify := new(proto.PlayerTimeNotify)
playerTimeNotify.IsPaused = player.Pause
playerTimeNotify.PlayerTime = uint64(player.TotalOnlineTime)
playerTimeNotify.ServerTime = uint64(time.Now().UnixMilli())
g.SendMsg(cmd.PlayerTimeNotify, player.PlayerID, player.ClientSeq, playerTimeNotify)
player.SceneLoadState = model.SceneEnterDone
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
g.AddSceneEntityNotify(player, proto.VisionType_VISION_TYPE_BORN, []uint32{playerTeamEntity.avatarEntityMap[activeAvatarId]}, true)
// 通过aoi获取场景中在自己周围格子里的全部实体id
entityIdList := world.aoiManager.GetEntityIdListByPos(float32(player.Pos.X), float32(player.Pos.Y), float32(player.Pos.Z))
g.AddSceneEntityNotify(player, proto.VisionType_VISION_TYPE_MEET, entityIdList, false)
}
func (g *GameManager) PostEnterSceneReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user post enter scene, uid: %v", player.PlayerID)
// PacketPostEnterSceneRsp
postEnterSceneRsp := new(proto.PostEnterSceneRsp)
postEnterSceneRsp.EnterSceneToken = player.EnterSceneToken
g.SendMsg(cmd.PostEnterSceneRsp, player.PlayerID, player.ClientSeq, postEnterSceneRsp)
}
func (g *GameManager) EnterWorldAreaReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user enter world area, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.EnterWorldAreaReq)
// PacketEnterWorldAreaRsp
enterWorldAreaRsp := new(proto.EnterWorldAreaRsp)
enterWorldAreaRsp.AreaType = req.AreaType
enterWorldAreaRsp.AreaId = req.AreaId
g.SendMsg(cmd.EnterWorldAreaRsp, player.PlayerID, player.ClientSeq, enterWorldAreaRsp)
}
func (g *GameManager) ChangeGameTimeReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change game time, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.ChangeGameTimeReq)
gameTime := req.GameTime
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
scene.ChangeGameTime(gameTime)
for _, scenePlayer := range scene.playerMap {
// PacketPlayerGameTimeNotify
playerGameTimeNotify := new(proto.PlayerGameTimeNotify)
playerGameTimeNotify.GameTime = scene.gameTime
playerGameTimeNotify.Uid = scenePlayer.PlayerID
g.SendMsg(cmd.PlayerGameTimeNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, playerGameTimeNotify)
}
// PacketChangeGameTimeRsp
changeGameTimeRsp := new(proto.ChangeGameTimeRsp)
changeGameTimeRsp.CurGameTime = scene.gameTime
g.SendMsg(cmd.ChangeGameTimeRsp, player.PlayerID, player.ClientSeq, changeGameTimeRsp)
}
func (g *GameManager) PacketPlayerEnterSceneNotify(player *model.Player) *proto.PlayerEnterSceneNotify {
player.EnterSceneToken = uint32(random.GetRandomInt32(1000, 99999))
playerEnterSceneNotify := new(proto.PlayerEnterSceneNotify)
playerEnterSceneNotify.SceneId = player.SceneId
playerEnterSceneNotify.Pos = &proto.Vector{X: float32(player.Pos.X), Y: float32(player.Pos.Y), Z: float32(player.Pos.Z)}
playerEnterSceneNotify.SceneBeginTime = uint64(time.Now().UnixMilli())
playerEnterSceneNotify.Type = proto.EnterType_ENTER_TYPE_SELF
playerEnterSceneNotify.TargetUid = player.PlayerID
playerEnterSceneNotify.EnterSceneToken = player.EnterSceneToken
playerEnterSceneNotify.WorldLevel = player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL]
playerEnterSceneNotify.EnterReason = uint32(constant.EnterReasonConst.Login)
// 刚登录进入场景的时候才为true
playerEnterSceneNotify.IsFirstLoginEnterScene = true
playerEnterSceneNotify.WorldType = 1
playerEnterSceneNotify.SceneTransaction = strconv.Itoa(int(player.SceneId)) + "-" +
strconv.Itoa(int(player.PlayerID)) + "-" +
strconv.Itoa(int(time.Now().Unix())) + "-" +
"18402"
return playerEnterSceneNotify
}
func (g *GameManager) PacketPlayerEnterSceneNotifyTp(
player *model.Player,
enterType proto.EnterType,
enterReason uint32,
prevSceneId uint32,
prevPos *model.Vector,
) *proto.PlayerEnterSceneNotify {
return g.PacketPlayerEnterSceneNotifyMp(player, player, enterType, enterReason, prevSceneId, prevPos)
}
func (g *GameManager) PacketPlayerEnterSceneNotifyMp(
player *model.Player,
targetPlayer *model.Player,
enterType proto.EnterType,
enterReason uint32,
prevSceneId uint32,
prevPos *model.Vector,
) *proto.PlayerEnterSceneNotify {
player.EnterSceneToken = uint32(random.GetRandomInt32(1000, 99999))
playerEnterSceneNotify := new(proto.PlayerEnterSceneNotify)
playerEnterSceneNotify.PrevSceneId = prevSceneId
playerEnterSceneNotify.PrevPos = &proto.Vector{X: float32(prevPos.X), Y: float32(prevPos.Y), Z: float32(prevPos.Z)}
playerEnterSceneNotify.SceneId = player.SceneId
playerEnterSceneNotify.Pos = &proto.Vector{X: float32(player.Pos.X), Y: float32(player.Pos.Y), Z: float32(player.Pos.Z)}
playerEnterSceneNotify.SceneBeginTime = uint64(time.Now().UnixMilli())
playerEnterSceneNotify.Type = enterType
playerEnterSceneNotify.TargetUid = targetPlayer.PlayerID
playerEnterSceneNotify.EnterSceneToken = player.EnterSceneToken
playerEnterSceneNotify.WorldLevel = targetPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL]
playerEnterSceneNotify.EnterReason = enterReason
playerEnterSceneNotify.WorldType = 1
playerEnterSceneNotify.SceneTransaction = strconv.Itoa(int(player.SceneId)) + "-" +
strconv.Itoa(int(targetPlayer.PlayerID)) + "-" +
strconv.Itoa(int(time.Now().Unix())) + "-" +
"18402"
//playerEnterSceneNotify.SceneTagIdList = []uint32{102, 107, 109, 113, 117}
playerEnterSceneNotify.SceneTagIdList = make([]uint32, 0)
for sceneTagId := uint32(0); sceneTagId < 3000; sceneTagId++ {
playerEnterSceneNotify.SceneTagIdList = append(playerEnterSceneNotify.SceneTagIdList, sceneTagId)
}
return playerEnterSceneNotify
}
func (g *GameManager) AddSceneEntityNotifyToPlayer(player *model.Player, visionType proto.VisionType, entityList []*proto.SceneEntityInfo) {
// PacketSceneEntityAppearNotify
sceneEntityAppearNotify := new(proto.SceneEntityAppearNotify)
sceneEntityAppearNotify.AppearType = visionType
sceneEntityAppearNotify.EntityList = entityList
g.SendMsg(cmd.SceneEntityAppearNotify, player.PlayerID, player.ClientSeq, sceneEntityAppearNotify)
logger.LOG.Debug("SceneEntityAppearNotify, uid: %v, type: %v, len: %v",
player.PlayerID, sceneEntityAppearNotify.AppearType, len(sceneEntityAppearNotify.EntityList))
}
func (g *GameManager) AddSceneEntityNotifyBroadcast(scene *Scene, visionType proto.VisionType, entityList []*proto.SceneEntityInfo) {
// PacketSceneEntityAppearNotify
sceneEntityAppearNotify := new(proto.SceneEntityAppearNotify)
sceneEntityAppearNotify.AppearType = visionType
sceneEntityAppearNotify.EntityList = entityList
for _, scenePlayer := range scene.playerMap {
g.SendMsg(cmd.SceneEntityAppearNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, sceneEntityAppearNotify)
logger.LOG.Debug("SceneEntityAppearNotify, uid: %v, type: %v, len: %v",
scenePlayer.PlayerID, sceneEntityAppearNotify.AppearType, len(sceneEntityAppearNotify.EntityList))
}
}
func (g *GameManager) RemoveSceneEntityNotifyToPlayer(player *model.Player, entityIdList []uint32) {
// PacketSceneEntityDisappearNotify
sceneEntityDisappearNotify := new(proto.SceneEntityDisappearNotify)
sceneEntityDisappearNotify.EntityList = entityIdList
sceneEntityDisappearNotify.DisappearType = proto.VisionType_VISION_TYPE_REMOVE
g.SendMsg(cmd.SceneEntityDisappearNotify, player.PlayerID, player.ClientSeq, sceneEntityDisappearNotify)
logger.LOG.Debug("SceneEntityDisappearNotify, uid: %v, type: %v, len: %v",
player.PlayerID, sceneEntityDisappearNotify.DisappearType, len(sceneEntityDisappearNotify.EntityList))
}
func (g *GameManager) RemoveSceneEntityNotifyBroadcast(scene *Scene, entityIdList []uint32) {
// PacketSceneEntityDisappearNotify
sceneEntityDisappearNotify := new(proto.SceneEntityDisappearNotify)
sceneEntityDisappearNotify.EntityList = entityIdList
sceneEntityDisappearNotify.DisappearType = proto.VisionType_VISION_TYPE_REMOVE
for _, scenePlayer := range scene.playerMap {
g.SendMsg(cmd.SceneEntityDisappearNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, sceneEntityDisappearNotify)
logger.LOG.Debug("SceneEntityDisappearNotify, uid: %v, type: %v, len: %v",
scenePlayer.PlayerID, sceneEntityDisappearNotify.DisappearType, len(sceneEntityDisappearNotify.EntityList))
}
}
func (g *GameManager) AddSceneEntityNotify(player *model.Player, visionType proto.VisionType, entityIdList []uint32, broadcast bool) {
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
entityList := make([]*proto.SceneEntityInfo, 0)
for _, entityId := range entityIdList {
entity := scene.entityMap[entityId]
if entity == nil {
logger.LOG.Error("get entity is nil, entityId: %v", entityId)
continue
}
switch entity.entityType {
case uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_AVATAR):
if visionType == proto.VisionType_VISION_TYPE_MEET && entity.avatarEntity.uid == player.PlayerID {
continue
}
scenePlayer := g.userManager.GetOnlineUser(entity.avatarEntity.uid)
if scenePlayer == nil {
logger.LOG.Error("get scene player is nil, world id: %v, scene id: %v", world.id, scene.id)
continue
}
if scenePlayer.SceneLoadState != model.SceneEnterDone {
continue
}
if entity.avatarEntity.avatarId != scenePlayer.TeamConfig.GetActiveAvatarId() {
continue
}
sceneEntityInfoAvatar := g.PacketSceneEntityInfoAvatar(scene, scenePlayer, scenePlayer.TeamConfig.GetActiveAvatarId())
entityList = append(entityList, sceneEntityInfoAvatar)
case uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_WEAPON):
case uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_MONSTER):
sceneEntityInfoMonster := g.PacketSceneEntityInfoMonster(scene, entity.id)
entityList = append(entityList, sceneEntityInfoMonster)
case uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET):
sceneEntityInfoGadget := g.PacketSceneEntityInfoGadget(scene, entity.id)
entityList = append(entityList, sceneEntityInfoGadget)
}
}
if broadcast {
g.AddSceneEntityNotifyBroadcast(scene, visionType, entityList)
} else {
g.AddSceneEntityNotifyToPlayer(player, visionType, entityList)
}
}
func (g *GameManager) PacketFightPropMapToPbFightPropList(fightPropMap map[uint32]float32) []*proto.FightPropPair {
fightPropList := []*proto.FightPropPair{
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_ATTACK),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_ATTACK)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_DEFENSE),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_DEFENSE)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_CRITICAL),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CRITICAL)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_CRITICAL_HURT),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CRITICAL_HURT)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_CHARGE_EFFICIENCY),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CHARGE_EFFICIENCY)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_ATTACK),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_ATTACK)],
},
{
PropType: uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_DEFENSE),
PropValue: fightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_DEFENSE)],
},
}
return fightPropList
}
func (g *GameManager) PacketSceneEntityInfoAvatar(scene *Scene, player *model.Player, avatarId uint32) *proto.SceneEntityInfo {
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
entity := scene.GetEntity(playerTeamEntity.avatarEntityMap[avatarId])
if entity == nil {
return new(proto.SceneEntityInfo)
}
sceneEntityInfo := &proto.SceneEntityInfo{
EntityType: proto.ProtEntityType_PROT_ENTITY_TYPE_AVATAR,
EntityId: entity.id,
MotionInfo: &proto.MotionInfo{
Pos: &proto.Vector{
X: float32(entity.pos.X),
Y: float32(entity.pos.Y),
Z: float32(entity.pos.Z),
},
Rot: &proto.Vector{
X: float32(entity.rot.X),
Y: float32(entity.rot.Y),
Z: float32(entity.rot.Z),
},
Speed: &proto.Vector{},
State: proto.MotionState(entity.moveState),
},
PropList: []*proto.PropPair{{Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL), PropValue: &proto.PropValue{
Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL),
Value: &proto.PropValue_Ival{Ival: int64(entity.level)},
Val: int64(entity.level),
}}},
FightPropList: g.PacketFightPropMapToPbFightPropList(entity.fightProp),
LifeState: 1,
AnimatorParaList: make([]*proto.AnimatorParameterValueInfoPair, 0),
Entity: &proto.SceneEntityInfo_Avatar{
Avatar: g.PacketSceneAvatarInfo(scene, player, avatarId),
},
EntityClientData: new(proto.EntityClientData),
EntityAuthorityInfo: &proto.EntityAuthorityInfo{
AbilityInfo: new(proto.AbilitySyncStateInfo),
RendererChangedInfo: new(proto.EntityRendererChangedInfo),
AiInfo: &proto.SceneEntityAiInfo{
IsAiOpen: true,
BornPos: new(proto.Vector),
},
BornPos: new(proto.Vector),
},
LastMoveSceneTimeMs: entity.lastMoveSceneTimeMs,
LastMoveReliableSeq: entity.lastMoveReliableSeq,
}
return sceneEntityInfo
}
func (g *GameManager) PacketSceneEntityInfoMonster(scene *Scene, entityId uint32) *proto.SceneEntityInfo {
entity := scene.GetEntity(entityId)
if entity == nil {
return new(proto.SceneEntityInfo)
}
pos := &proto.Vector{
X: float32(entity.pos.X),
Y: float32(entity.pos.Y),
Z: float32(entity.pos.Z),
}
sceneEntityInfo := &proto.SceneEntityInfo{
EntityType: proto.ProtEntityType_PROT_ENTITY_TYPE_MONSTER,
EntityId: entity.id,
MotionInfo: &proto.MotionInfo{
Pos: pos,
Rot: &proto.Vector{
X: float32(entity.rot.X),
Y: float32(entity.rot.Y),
Z: float32(entity.rot.Z),
},
Speed: &proto.Vector{},
State: proto.MotionState(entity.moveState),
},
PropList: []*proto.PropPair{{Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL), PropValue: &proto.PropValue{
Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL),
Value: &proto.PropValue_Ival{Ival: int64(entity.level)},
Val: int64(entity.level),
}}},
FightPropList: g.PacketFightPropMapToPbFightPropList(entity.fightProp),
LifeState: 1,
AnimatorParaList: make([]*proto.AnimatorParameterValueInfoPair, 0),
Entity: &proto.SceneEntityInfo_Monster{
Monster: g.PacketSceneMonsterInfo(),
},
EntityClientData: new(proto.EntityClientData),
EntityAuthorityInfo: &proto.EntityAuthorityInfo{
AbilityInfo: new(proto.AbilitySyncStateInfo),
RendererChangedInfo: new(proto.EntityRendererChangedInfo),
AiInfo: &proto.SceneEntityAiInfo{
IsAiOpen: true,
BornPos: pos,
},
BornPos: pos,
},
}
return sceneEntityInfo
}
func (g *GameManager) PacketSceneEntityInfoGadget(scene *Scene, entityId uint32) *proto.SceneEntityInfo {
entity := scene.GetEntity(entityId)
if entity == nil {
return new(proto.SceneEntityInfo)
}
sceneEntityInfo := &proto.SceneEntityInfo{
EntityType: proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET,
EntityId: entity.id,
MotionInfo: &proto.MotionInfo{
Pos: &proto.Vector{
X: float32(entity.pos.X),
Y: float32(entity.pos.Y),
Z: float32(entity.pos.Z),
},
Rot: &proto.Vector{
X: float32(entity.rot.X),
Y: float32(entity.rot.Y),
Z: float32(entity.rot.Z),
},
Speed: &proto.Vector{},
State: proto.MotionState(entity.moveState),
},
PropList: []*proto.PropPair{{Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL), PropValue: &proto.PropValue{
Type: uint32(constant.PlayerPropertyConst.PROP_LEVEL),
Value: &proto.PropValue_Ival{Ival: int64(1)},
Val: int64(1),
}}},
FightPropList: g.PacketFightPropMapToPbFightPropList(entity.fightProp),
LifeState: 1,
AnimatorParaList: make([]*proto.AnimatorParameterValueInfoPair, 0),
Entity: &proto.SceneEntityInfo_Gadget{
Gadget: g.PacketSceneGadgetInfo(entity.gadgetEntity.gatherId),
},
EntityClientData: new(proto.EntityClientData),
EntityAuthorityInfo: &proto.EntityAuthorityInfo{
AbilityInfo: new(proto.AbilitySyncStateInfo),
RendererChangedInfo: new(proto.EntityRendererChangedInfo),
AiInfo: &proto.SceneEntityAiInfo{
IsAiOpen: true,
BornPos: new(proto.Vector),
},
BornPos: new(proto.Vector),
},
}
return sceneEntityInfo
}
func (g *GameManager) PacketSceneAvatarInfo(scene *Scene, player *model.Player, avatarId uint32) *proto.SceneAvatarInfo {
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
activeAvatar := player.AvatarMap[activeAvatarId]
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
equipIdList := make([]uint32, 0)
weapon := player.AvatarMap[avatarId].EquipWeapon
equipIdList = append(equipIdList, weapon.ItemId)
for _, reliquary := range player.AvatarMap[avatarId].EquipReliquaryList {
equipIdList = append(equipIdList, reliquary.ItemId)
}
sceneAvatarInfo := &proto.SceneAvatarInfo{
Uid: player.PlayerID,
AvatarId: avatarId,
Guid: player.AvatarMap[avatarId].Guid,
PeerId: player.PeerId,
EquipIdList: equipIdList,
SkillDepotId: player.AvatarMap[avatarId].SkillDepotId,
Weapon: &proto.SceneWeaponInfo{
EntityId: playerTeamEntity.weaponEntityMap[activeAvatar.EquipWeapon.WeaponId],
GadgetId: uint32(gdc.CONF.ItemDataMap[int32(weapon.ItemId)].GadgetId),
ItemId: weapon.ItemId,
Guid: weapon.Guid,
Level: uint32(weapon.Level),
AbilityInfo: new(proto.AbilitySyncStateInfo),
},
ReliquaryList: nil,
SkillLevelMap: player.AvatarMap[avatarId].SkillLevelMap,
WearingFlycloakId: player.AvatarMap[avatarId].FlyCloak,
CostumeId: player.AvatarMap[avatarId].Costume,
BornTime: uint32(player.AvatarMap[avatarId].BornTime),
TeamResonanceList: make([]uint32, 0),
}
for id := range player.TeamConfig.TeamResonances {
sceneAvatarInfo.TeamResonanceList = append(sceneAvatarInfo.TeamResonanceList, uint32(id))
}
return sceneAvatarInfo
}
func (g *GameManager) PacketSceneMonsterInfo() *proto.SceneMonsterInfo {
sceneMonsterInfo := &proto.SceneMonsterInfo{
MonsterId: 20011301,
AuthorityPeerId: 1,
BornType: proto.MonsterBornType_MONSTER_BORN_TYPE_DEFAULT,
BlockId: 3001,
TitleId: 3001,
SpecialNameId: 40,
}
return sceneMonsterInfo
}
func (g *GameManager) PacketSceneGadgetInfo(gatherId uint32) *proto.SceneGadgetInfo {
gather := gdc.CONF.GatherDataMap[int32(gatherId)]
sceneGadgetInfo := &proto.SceneGadgetInfo{
GadgetId: uint32(gather.GadgetId),
//GroupId: 133003011,
//ConfigId: 11001,
GadgetState: 0,
IsEnableInteract: false,
AuthorityPeerId: 1,
Content: &proto.SceneGadgetInfo_GatherGadget{
GatherGadget: &proto.GatherGadgetInfo{
ItemId: uint32(gather.ItemId),
IsForbidGuest: false,
},
},
}
return sceneGadgetInfo
}
func (g *GameManager) PacketDelTeamEntityNotify(scene *Scene, player *model.Player) *proto.DelTeamEntityNotify {
delTeamEntityNotify := new(proto.DelTeamEntityNotify)
delTeamEntityNotify.SceneId = player.SceneId
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
delTeamEntityNotify.DelEntityIdList = []uint32{playerTeamEntity.teamEntityId}
return delTeamEntityNotify
}

129
gs/game/user_shop.go Normal file
View File

@@ -0,0 +1,129 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"time"
)
func (g *GameManager) GetShopmallDataReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get shop mall, uid: %v", player.PlayerID)
// PacketGetShopmallDataRsp
getShopmallDataRsp := new(proto.GetShopmallDataRsp)
getShopmallDataRsp.ShopTypeList = []uint32{900, 1052, 902, 1001, 903}
g.SendMsg(cmd.GetShopmallDataRsp, player.PlayerID, player.ClientSeq, getShopmallDataRsp)
}
func (g *GameManager) GetShopReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get shop, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.GetShopReq)
shopType := req.ShopType
if shopType != 1001 {
return
}
nextRefreshTime := uint32(time.Now().Add(time.Hour * 24 * 30).Unix())
// PacketGetShopRsp
getShopRsp := new(proto.GetShopRsp)
getShopRsp.Shop = &proto.Shop{
GoodsList: []*proto.ShopGoods{
{
MinLevel: 1,
EndTime: 2051193600,
Hcoin: 160,
GoodsId: 102001,
NextRefreshTime: nextRefreshTime,
MaxLevel: 99,
BeginTime: 1575129600,
GoodsItem: &proto.ItemParam{
ItemId: 223,
Count: 1,
},
},
{
MinLevel: 1,
EndTime: 2051193600,
Hcoin: 160,
GoodsId: 102002,
NextRefreshTime: nextRefreshTime,
MaxLevel: 99,
BeginTime: 1575129600,
GoodsItem: &proto.ItemParam{
ItemId: 224,
Count: 1,
},
},
},
NextRefreshTime: nextRefreshTime,
ShopType: 1001,
}
g.SendMsg(cmd.GetShopRsp, player.PlayerID, player.ClientSeq, getShopRsp)
}
func (g *GameManager) BuyGoodsReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user buy goods, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.BuyGoodsReq)
buyItemId := req.Goods.GoodsItem.ItemId
buyItemCount := req.BuyCount
costHcoinCount := req.Goods.Hcoin * buyItemCount
if buyItemId != 223 && buyItemId != 224 {
return
}
if player.GetItemCount(201) < costHcoinCount {
return
}
g.CostUserItem(player.PlayerID, []*UserItem{{
ItemId: 201,
ChangeCount: costHcoinCount,
}})
g.AddUserItem(player.PlayerID, []*UserItem{{
ItemId: buyItemId,
ChangeCount: buyItemCount,
}}, true, constant.ActionReasonConst.Shop)
req.Goods.BoughtNum = player.GetItemCount(buyItemId)
// PacketBuyGoodsRsp
buyGoodsRsp := new(proto.BuyGoodsRsp)
buyGoodsRsp.ShopType = req.ShopType
buyGoodsRsp.BuyCount = req.BuyCount
buyGoodsRsp.GoodsList = []*proto.ShopGoods{req.Goods}
g.SendMsg(cmd.BuyGoodsRsp, player.PlayerID, player.ClientSeq, buyGoodsRsp)
}
func (g *GameManager) McoinExchangeHcoinReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user mcoin exchange hcoin, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.McoinExchangeHcoinReq)
if req.Hcoin != req.McoinCost {
return
}
count := req.Hcoin
if player.GetItemCount(203) < count {
return
}
g.CostUserItem(player.PlayerID, []*UserItem{{
ItemId: 203,
ChangeCount: count,
}})
g.AddUserItem(player.PlayerID, []*UserItem{{
ItemId: 201,
ChangeCount: count,
}}, false, 0)
// PacketMcoinExchangeHcoinRsp
mcoinExchangeHcoinRsp := new(proto.McoinExchangeHcoinRsp)
mcoinExchangeHcoinRsp.Hcoin = req.Hcoin
mcoinExchangeHcoinRsp.McoinCost = req.McoinCost
g.SendMsg(cmd.McoinExchangeHcoinRsp, player.PlayerID, player.ClientSeq, mcoinExchangeHcoinRsp)
}

335
gs/game/user_social.go Normal file
View File

@@ -0,0 +1,335 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/object"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"regexp"
"time"
"unicode/utf8"
)
func (g *GameManager) GetPlayerSocialDetailReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get player social detail, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.GetPlayerSocialDetailReq)
targetUid := req.Uid
// PacketGetPlayerSocialDetailRsp
getPlayerSocialDetailRsp := new(proto.GetPlayerSocialDetailRsp)
// TODO 同步阻塞待优化
targetPlayer := g.userManager.LoadTempOfflineUserSync(targetUid)
if targetPlayer != nil {
socialDetail := new(proto.SocialDetail)
socialDetail.Uid = targetPlayer.PlayerID
socialDetail.ProfilePicture = &proto.ProfilePicture{AvatarId: targetPlayer.HeadImage}
socialDetail.Nickname = targetPlayer.NickName
socialDetail.Signature = targetPlayer.Signature
socialDetail.Level = targetPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL]
socialDetail.Birthday = &proto.Birthday{Month: 2, Day: 13}
socialDetail.WorldLevel = targetPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL]
socialDetail.NameCardId = targetPlayer.NameCard
socialDetail.IsShowAvatar = false
socialDetail.FinishAchievementNum = 0
_, exist := player.FriendList[targetPlayer.PlayerID]
socialDetail.IsFriend = exist
getPlayerSocialDetailRsp.DetailData = socialDetail
} else {
getPlayerSocialDetailRsp.Retcode = int32(proto.Retcode_RETCODE_RET_PLAYER_NOT_EXIST)
}
g.SendMsg(cmd.GetPlayerSocialDetailRsp, player.PlayerID, player.ClientSeq, getPlayerSocialDetailRsp)
}
func (g *GameManager) SetPlayerBirthdayReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user set birthday, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SetPlayerBirthdayReq)
_ = req
}
func (g *GameManager) SetNameCardReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change name card, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SetNameCardReq)
nameCardId := req.NameCardId
exist := false
for _, nameCard := range player.NameCardList {
if nameCard == nameCardId {
exist = true
}
}
if !exist {
logger.LOG.Error("name card not exist, uid: %v", player.PlayerID)
return
}
player.NameCard = nameCardId
// PacketSetNameCardRsp
setNameCardRsp := new(proto.SetNameCardRsp)
setNameCardRsp.NameCardId = nameCardId
g.SendMsg(cmd.SetNameCardRsp, player.PlayerID, player.ClientSeq, setNameCardRsp)
}
func (g *GameManager) SetPlayerSignatureReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change signature, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SetPlayerSignatureReq)
signature := req.Signature
// PacketSetPlayerSignatureRsp
setPlayerSignatureRsp := new(proto.SetPlayerSignatureRsp)
if !object.IsUtf8String(signature) {
setPlayerSignatureRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SIGNATURE_ILLEGAL)
} else if utf8.RuneCountInString(signature) > 50 {
setPlayerSignatureRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SIGNATURE_ILLEGAL)
} else {
player.Signature = signature
setPlayerSignatureRsp.Signature = player.Signature
}
g.SendMsg(cmd.SetPlayerSignatureRsp, player.PlayerID, player.ClientSeq, setPlayerSignatureRsp)
}
func (g *GameManager) SetPlayerNameReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change nickname, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SetPlayerNameReq)
nickName := req.NickName
// PacketSetPlayerNameRsp
setPlayerNameRsp := new(proto.SetPlayerNameRsp)
if len(nickName) == 0 {
setPlayerNameRsp.Retcode = int32(proto.Retcode_RETCODE_RET_NICKNAME_IS_EMPTY)
} else if !object.IsUtf8String(nickName) {
setPlayerNameRsp.Retcode = int32(proto.Retcode_RETCODE_RET_NICKNAME_UTF8_ERROR)
} else if utf8.RuneCountInString(nickName) > 14 {
setPlayerNameRsp.Retcode = int32(proto.Retcode_RETCODE_RET_NICKNAME_TOO_LONG)
} else if len(regexp.MustCompile(`\d`).FindAllString(nickName, -1)) > 6 {
setPlayerNameRsp.Retcode = int32(proto.Retcode_RETCODE_RET_NICKNAME_TOO_MANY_DIGITS)
} else {
player.NickName = nickName
setPlayerNameRsp.NickName = player.NickName
}
g.SendMsg(cmd.SetPlayerNameRsp, player.PlayerID, player.ClientSeq, setPlayerNameRsp)
}
func (g *GameManager) SetPlayerHeadImageReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change head image, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SetPlayerHeadImageReq)
avatarId := req.AvatarId
_, exist := player.AvatarMap[avatarId]
if !exist {
logger.LOG.Error("the head img of the avatar not exist, uid: %v", player.PlayerID)
return
}
player.HeadImage = avatarId
// PacketSetPlayerHeadImageRsp
setPlayerHeadImageRsp := new(proto.SetPlayerHeadImageRsp)
setPlayerHeadImageRsp.ProfilePicture = &proto.ProfilePicture{AvatarId: player.HeadImage}
g.SendMsg(cmd.SetPlayerHeadImageRsp, player.PlayerID, player.ClientSeq, setPlayerHeadImageRsp)
}
func (g *GameManager) GetAllUnlockNameCardReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get all unlock name card, uid: %v", player.PlayerID)
// PacketGetAllUnlockNameCardRsp
getAllUnlockNameCardRsp := new(proto.GetAllUnlockNameCardRsp)
getAllUnlockNameCardRsp.NameCardList = player.NameCardList
g.SendMsg(cmd.GetAllUnlockNameCardRsp, player.PlayerID, player.ClientSeq, getAllUnlockNameCardRsp)
}
func (g *GameManager) GetPlayerFriendListReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get friend list, uid: %v", player.PlayerID)
// PacketGetPlayerFriendListRsp
getPlayerFriendListRsp := new(proto.GetPlayerFriendListRsp)
getPlayerFriendListRsp.FriendList = make([]*proto.FriendBrief, 0)
for uid := range player.FriendList {
// TODO 同步阻塞待优化
var onlineState proto.FriendOnlineState
online := g.userManager.GetUserOnlineState(uid)
if online {
onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE
} else {
onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_FREIEND_DISCONNECT
}
friendPlayer := g.userManager.LoadTempOfflineUserSync(uid)
if friendPlayer == nil {
logger.LOG.Error("target player is nil, uid: %v", player.PlayerID)
continue
}
friendBrief := &proto.FriendBrief{
Uid: friendPlayer.PlayerID,
Nickname: friendPlayer.NickName,
Level: friendPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL],
ProfilePicture: &proto.ProfilePicture{AvatarId: friendPlayer.HeadImage},
WorldLevel: friendPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL],
Signature: friendPlayer.Signature,
OnlineState: onlineState,
IsMpModeAvailable: true,
LastActiveTime: player.OfflineTime,
NameCardId: friendPlayer.NameCard,
Param: (uint32(time.Now().Unix()) - player.OfflineTime) / 3600 / 24,
IsGameSource: true,
PlatformType: proto.PlatformType_PLATFORM_TYPE_PC,
}
getPlayerFriendListRsp.FriendList = append(getPlayerFriendListRsp.FriendList, friendBrief)
}
g.SendMsg(cmd.GetPlayerFriendListRsp, player.PlayerID, player.ClientSeq, getPlayerFriendListRsp)
}
func (g *GameManager) GetPlayerAskFriendListReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get friend apply list, uid: %v", player.PlayerID)
// PacketGetPlayerAskFriendListRsp
getPlayerAskFriendListRsp := new(proto.GetPlayerAskFriendListRsp)
getPlayerAskFriendListRsp.AskFriendList = make([]*proto.FriendBrief, 0)
for uid := range player.FriendApplyList {
// TODO 同步阻塞待优化
var onlineState proto.FriendOnlineState
online := g.userManager.GetUserOnlineState(uid)
if online {
onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE
} else {
onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_FREIEND_DISCONNECT
}
friendPlayer := g.userManager.LoadTempOfflineUserSync(uid)
if friendPlayer == nil {
logger.LOG.Error("target player is nil, uid: %v", player.PlayerID)
continue
}
friendBrief := &proto.FriendBrief{
Uid: friendPlayer.PlayerID,
Nickname: friendPlayer.NickName,
Level: friendPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL],
ProfilePicture: &proto.ProfilePicture{AvatarId: friendPlayer.HeadImage},
WorldLevel: friendPlayer.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL],
Signature: friendPlayer.Signature,
OnlineState: onlineState,
IsMpModeAvailable: true,
LastActiveTime: player.OfflineTime,
NameCardId: friendPlayer.NameCard,
Param: (uint32(time.Now().Unix()) - player.OfflineTime) / 3600 / 24,
IsGameSource: true,
PlatformType: proto.PlatformType_PLATFORM_TYPE_PC,
}
getPlayerAskFriendListRsp.AskFriendList = append(getPlayerAskFriendListRsp.AskFriendList, friendBrief)
}
g.SendMsg(cmd.GetPlayerAskFriendListRsp, player.PlayerID, player.ClientSeq, getPlayerAskFriendListRsp)
}
func (g *GameManager) AskAddFriendReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user apply add friend, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.AskAddFriendReq)
targetUid := req.TargetUid
// TODO 同步阻塞待优化
targetPlayerOnline := g.userManager.GetUserOnlineState(targetUid)
targetPlayer := g.userManager.LoadTempOfflineUserSync(targetUid)
if targetPlayer == nil {
logger.LOG.Error("apply add friend target player is nil, uid: %v", player.PlayerID)
return
}
_, applyExist := targetPlayer.FriendApplyList[player.PlayerID]
_, friendExist := targetPlayer.FriendList[player.PlayerID]
if applyExist || friendExist {
logger.LOG.Error("friend or apply already exist, uid: %v", player.PlayerID)
return
}
targetPlayer.FriendApplyList[player.PlayerID] = true
if targetPlayerOnline {
// PacketAskAddFriendNotify
askAddFriendNotify := new(proto.AskAddFriendNotify)
askAddFriendNotify.TargetUid = player.PlayerID
askAddFriendNotify.TargetFriendBrief = &proto.FriendBrief{
Uid: player.PlayerID,
Nickname: player.NickName,
Level: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL],
ProfilePicture: &proto.ProfilePicture{AvatarId: player.HeadImage},
WorldLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL],
Signature: player.Signature,
OnlineState: proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE,
IsMpModeAvailable: true,
LastActiveTime: player.OfflineTime,
NameCardId: player.NameCard,
Param: (uint32(time.Now().Unix()) - player.OfflineTime) / 3600 / 24,
IsGameSource: true,
PlatformType: proto.PlatformType_PLATFORM_TYPE_PC,
}
g.SendMsg(cmd.AskAddFriendNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, askAddFriendNotify)
}
// PacketAskAddFriendRsp
askAddFriendRsp := new(proto.AskAddFriendRsp)
askAddFriendRsp.TargetUid = targetUid
g.SendMsg(cmd.AskAddFriendRsp, player.PlayerID, player.ClientSeq, askAddFriendRsp)
}
func (g *GameManager) DealAddFriendReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user deal friend apply, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.DealAddFriendReq)
targetUid := req.TargetUid
result := req.DealAddFriendResult
if result == proto.DealAddFriendResultType_DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT {
player.FriendList[targetUid] = true
// TODO 同步阻塞待优化
targetPlayer := g.userManager.LoadTempOfflineUserSync(targetUid)
if targetPlayer == nil {
logger.LOG.Error("agree friend apply target player is nil, uid: %v", player.PlayerID)
return
}
targetPlayer.FriendList[player.PlayerID] = true
}
delete(player.FriendApplyList, targetUid)
// PacketDealAddFriendRsp
dealAddFriendRsp := new(proto.DealAddFriendRsp)
dealAddFriendRsp.TargetUid = targetUid
dealAddFriendRsp.DealAddFriendResult = result
g.SendMsg(cmd.DealAddFriendRsp, player.PlayerID, player.ClientSeq, dealAddFriendRsp)
}
func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user get online player list, uid: %v", player.PlayerID)
count := 0
onlinePlayerList := make([]*model.Player, 0)
for _, onlinePlayer := range g.userManager.GetAllOnlineUserList() {
if onlinePlayer.PlayerID == player.PlayerID {
continue
}
onlinePlayerList = append(onlinePlayerList, onlinePlayer)
count++
if count >= 50 {
break
}
}
// PacketGetOnlinePlayerListRsp
getOnlinePlayerListRsp := new(proto.GetOnlinePlayerListRsp)
getOnlinePlayerListRsp.PlayerInfoList = make([]*proto.OnlinePlayerInfo, 0)
for _, onlinePlayer := range onlinePlayerList {
onlinePlayerInfo := g.PacketOnlinePlayerInfo(onlinePlayer)
getOnlinePlayerListRsp.PlayerInfoList = append(getOnlinePlayerListRsp.PlayerInfoList, onlinePlayerInfo)
}
g.SendMsg(cmd.GetOnlinePlayerListRsp, player.PlayerID, player.ClientSeq, getOnlinePlayerListRsp)
}
func (g *GameManager) PacketOnlinePlayerInfo(player *model.Player) *proto.OnlinePlayerInfo {
onlinePlayerInfo := &proto.OnlinePlayerInfo{
Uid: player.PlayerID,
Nickname: player.NickName,
PlayerLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL],
MpSettingType: proto.MpSettingType(player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE]),
NameCardId: player.NameCard,
Signature: player.Signature,
ProfilePicture: &proto.ProfilePicture{AvatarId: player.HeadImage},
CurPlayerNumInWorld: 1,
}
world := g.worldManager.GetWorldByID(player.WorldId)
if world != nil && world.playerMap != nil {
onlinePlayerInfo.CurPlayerNumInWorld = uint32(len(world.playerMap))
}
return onlinePlayerInfo
}

348
gs/game/user_team.go Normal file
View File

@@ -0,0 +1,348 @@
package game
import (
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/endec"
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
)
func (g *GameManager) ChangeAvatarReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change avatar, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.ChangeAvatarReq)
targetAvatarGuid := req.Guid
world := g.worldManager.GetWorldByID(player.WorldId)
scene := world.GetSceneById(player.SceneId)
playerTeamEntity := scene.GetPlayerTeamEntity(player.PlayerID)
oldAvatarId := player.TeamConfig.GetActiveAvatarId()
oldAvatar := player.AvatarMap[oldAvatarId]
if oldAvatar.Guid == targetAvatarGuid {
logger.LOG.Error("can not change to the same avatar, uid: %v, oldAvatarId: %v, oldAvatarGuid: %v", player.PlayerID, oldAvatarId, oldAvatar.Guid)
return
}
activeTeam := player.TeamConfig.GetActiveTeam()
index := -1
for avatarIndex, avatarId := range activeTeam.AvatarIdList {
if avatarId == 0 {
break
}
if targetAvatarGuid == player.AvatarMap[avatarId].Guid {
index = avatarIndex
}
}
if index == -1 {
logger.LOG.Error("can not find the target avatar in team, uid: %v, target avatar guid: %v", player.PlayerID, targetAvatarGuid)
return
}
player.TeamConfig.CurrAvatarIndex = uint8(index)
entity := scene.GetEntity(playerTeamEntity.avatarEntityMap[oldAvatarId])
if entity == nil {
return
}
entity.moveState = uint16(proto.MotionState_MOTION_STATE_STANDBY)
// PacketSceneEntityDisappearNotify
sceneEntityDisappearNotify := new(proto.SceneEntityDisappearNotify)
sceneEntityDisappearNotify.DisappearType = proto.VisionType_VISION_TYPE_REPLACE
sceneEntityDisappearNotify.EntityList = []uint32{playerTeamEntity.avatarEntityMap[oldAvatarId]}
for _, scenePlayer := range scene.playerMap {
g.SendMsg(cmd.SceneEntityDisappearNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, sceneEntityDisappearNotify)
}
// PacketSceneEntityAppearNotify
sceneEntityAppearNotify := new(proto.SceneEntityAppearNotify)
sceneEntityAppearNotify.AppearType = proto.VisionType_VISION_TYPE_REPLACE
sceneEntityAppearNotify.Param = playerTeamEntity.avatarEntityMap[oldAvatarId]
sceneEntityAppearNotify.EntityList = []*proto.SceneEntityInfo{g.PacketSceneEntityInfoAvatar(scene, player, player.TeamConfig.GetActiveAvatarId())}
for _, scenePlayer := range scene.playerMap {
g.SendMsg(cmd.SceneEntityAppearNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, sceneEntityAppearNotify)
}
// PacketChangeAvatarRsp
changeAvatarRsp := new(proto.ChangeAvatarRsp)
changeAvatarRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SUCC)
changeAvatarRsp.CurGuid = targetAvatarGuid
g.SendMsg(cmd.ChangeAvatarRsp, player.PlayerID, player.ClientSeq, changeAvatarRsp)
}
func (g *GameManager) SetUpAvatarTeamReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change team, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.SetUpAvatarTeamReq)
teamId := req.TeamId
if teamId <= 0 || teamId >= 5 {
// PacketSetUpAvatarTeamRsp
setUpAvatarTeamRsp := new(proto.SetUpAvatarTeamRsp)
setUpAvatarTeamRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SVR_ERROR)
g.SendMsg(cmd.SetUpAvatarTeamRsp, player.PlayerID, player.ClientSeq, setUpAvatarTeamRsp)
return
}
avatarGuidList := req.AvatarTeamGuidList
world := g.worldManager.GetWorldByID(player.WorldId)
multiTeam := teamId == 4
selfTeam := teamId == uint32(player.TeamConfig.GetActiveTeamId())
if (multiTeam && len(avatarGuidList) == 0) || (selfTeam && len(avatarGuidList) == 0) || len(avatarGuidList) > 4 || world.multiplayer {
// PacketSetUpAvatarTeamRsp
setUpAvatarTeamRsp := new(proto.SetUpAvatarTeamRsp)
setUpAvatarTeamRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SVR_ERROR)
g.SendMsg(cmd.SetUpAvatarTeamRsp, player.PlayerID, player.ClientSeq, setUpAvatarTeamRsp)
return
}
avatarIdList := make([]uint32, 0)
for _, avatarGuid := range avatarGuidList {
for avatarId, avatar := range player.AvatarMap {
if avatarGuid == avatar.Guid {
avatarIdList = append(avatarIdList, avatarId)
}
}
}
player.TeamConfig.ClearTeamAvatar(uint8(teamId - 1))
for _, avatarId := range avatarIdList {
player.TeamConfig.AddAvatarToTeam(avatarId, uint8(teamId-1))
}
if world.multiplayer {
// PacketSetUpAvatarTeamRsp
setUpAvatarTeamRsp := new(proto.SetUpAvatarTeamRsp)
setUpAvatarTeamRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SVR_ERROR)
g.SendMsg(cmd.SetUpAvatarTeamRsp, player.PlayerID, player.ClientSeq, setUpAvatarTeamRsp)
return
}
// PacketAvatarTeamUpdateNotify
avatarTeamUpdateNotify := new(proto.AvatarTeamUpdateNotify)
avatarTeamUpdateNotify.AvatarTeamMap = make(map[uint32]*proto.AvatarTeam)
for teamIndex, team := range player.TeamConfig.TeamList {
avatarTeam := new(proto.AvatarTeam)
avatarTeam.TeamName = team.Name
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
avatarTeam.AvatarGuidList = append(avatarTeam.AvatarGuidList, player.AvatarMap[avatarId].Guid)
}
avatarTeamUpdateNotify.AvatarTeamMap[uint32(teamIndex)+1] = avatarTeam
}
g.SendMsg(cmd.AvatarTeamUpdateNotify, player.PlayerID, player.ClientSeq, avatarTeamUpdateNotify)
if selfTeam {
player.TeamConfig.CurrAvatarIndex = 0
player.TeamConfig.UpdateTeam()
scene := world.GetSceneById(player.SceneId)
scene.UpdatePlayerTeamEntity(player)
// PacketSceneTeamUpdateNotify
sceneTeamUpdateNotify := g.PacketSceneTeamUpdateNotify(world)
g.SendMsg(cmd.SceneTeamUpdateNotify, player.PlayerID, player.ClientSeq, sceneTeamUpdateNotify)
// PacketSetUpAvatarTeamRsp
setUpAvatarTeamRsp := new(proto.SetUpAvatarTeamRsp)
setUpAvatarTeamRsp.TeamId = teamId
setUpAvatarTeamRsp.CurAvatarGuid = player.AvatarMap[player.TeamConfig.GetActiveAvatarId()].Guid
team := player.TeamConfig.GetTeamByIndex(uint8(teamId - 1))
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
setUpAvatarTeamRsp.AvatarTeamGuidList = append(setUpAvatarTeamRsp.AvatarTeamGuidList, player.AvatarMap[avatarId].Guid)
}
g.SendMsg(cmd.SetUpAvatarTeamRsp, player.PlayerID, player.ClientSeq, setUpAvatarTeamRsp)
} else {
// PacketSetUpAvatarTeamRsp
setUpAvatarTeamRsp := new(proto.SetUpAvatarTeamRsp)
setUpAvatarTeamRsp.TeamId = teamId
setUpAvatarTeamRsp.CurAvatarGuid = player.AvatarMap[player.TeamConfig.GetActiveAvatarId()].Guid
team := player.TeamConfig.GetTeamByIndex(uint8(teamId - 1))
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
setUpAvatarTeamRsp.AvatarTeamGuidList = append(setUpAvatarTeamRsp.AvatarTeamGuidList, player.AvatarMap[avatarId].Guid)
}
g.SendMsg(cmd.SetUpAvatarTeamRsp, player.PlayerID, player.ClientSeq, setUpAvatarTeamRsp)
}
}
func (g *GameManager) ChooseCurAvatarTeamReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user switch team, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.ChooseCurAvatarTeamReq)
teamId := req.TeamId
world := g.worldManager.GetWorldByID(player.WorldId)
if world.multiplayer {
return
}
team := player.TeamConfig.GetTeamByIndex(uint8(teamId) - 1)
if team == nil || len(team.AvatarIdList) == 0 {
return
}
player.TeamConfig.CurrTeamIndex = uint8(teamId) - 1
player.TeamConfig.CurrAvatarIndex = 0
player.TeamConfig.UpdateTeam()
scene := world.GetSceneById(player.SceneId)
scene.UpdatePlayerTeamEntity(player)
// PacketSceneTeamUpdateNotify
sceneTeamUpdateNotify := g.PacketSceneTeamUpdateNotify(world)
g.SendMsg(cmd.SceneTeamUpdateNotify, player.PlayerID, player.ClientSeq, sceneTeamUpdateNotify)
// PacketChooseCurAvatarTeamRsp
chooseCurAvatarTeamRsp := new(proto.ChooseCurAvatarTeamRsp)
chooseCurAvatarTeamRsp.CurTeamId = teamId
g.SendMsg(cmd.ChooseCurAvatarTeamRsp, player.PlayerID, player.ClientSeq, chooseCurAvatarTeamRsp)
}
func (g *GameManager) ChangeMpTeamAvatarReq(player *model.Player, payloadMsg pb.Message) {
logger.LOG.Debug("user change mp team, uid: %v", player.PlayerID)
req := payloadMsg.(*proto.ChangeMpTeamAvatarReq)
avatarGuidList := req.AvatarGuidList
world := g.worldManager.GetWorldByID(player.WorldId)
if len(avatarGuidList) == 0 || len(avatarGuidList) > 4 || !world.multiplayer {
// PacketChangeMpTeamAvatarRsp
changeMpTeamAvatarRsp := new(proto.ChangeMpTeamAvatarRsp)
changeMpTeamAvatarRsp.Retcode = int32(proto.Retcode_RETCODE_RET_SVR_ERROR)
g.SendMsg(cmd.ChangeMpTeamAvatarRsp, player.PlayerID, player.ClientSeq, changeMpTeamAvatarRsp)
return
}
avatarIdList := make([]uint32, 0)
for _, avatarGuid := range avatarGuidList {
for avatarId, avatar := range player.AvatarMap {
if avatarGuid == avatar.Guid {
avatarIdList = append(avatarIdList, avatarId)
}
}
}
player.TeamConfig.ClearTeamAvatar(3)
for _, avatarId := range avatarIdList {
player.TeamConfig.AddAvatarToTeam(avatarId, 3)
}
player.TeamConfig.CurrAvatarIndex = 0
player.TeamConfig.UpdateTeam()
scene := world.GetSceneById(player.SceneId)
scene.UpdatePlayerTeamEntity(player)
for _, worldPlayer := range world.playerMap {
// PacketSceneTeamUpdateNotify
sceneTeamUpdateNotify := g.PacketSceneTeamUpdateNotify(world)
g.SendMsg(cmd.SceneTeamUpdateNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, sceneTeamUpdateNotify)
}
// PacketChangeMpTeamAvatarRsp
changeMpTeamAvatarRsp := new(proto.ChangeMpTeamAvatarRsp)
changeMpTeamAvatarRsp.CurAvatarGuid = player.AvatarMap[player.TeamConfig.GetActiveAvatarId()].Guid
team := player.TeamConfig.GetTeamByIndex(3)
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
changeMpTeamAvatarRsp.AvatarGuidList = append(changeMpTeamAvatarRsp.AvatarGuidList, player.AvatarMap[avatarId].Guid)
}
g.SendMsg(cmd.ChangeMpTeamAvatarRsp, player.PlayerID, player.ClientSeq, changeMpTeamAvatarRsp)
}
func (g *GameManager) PacketSceneTeamUpdateNotify(world *World) *proto.SceneTeamUpdateNotify {
sceneTeamUpdateNotify := new(proto.SceneTeamUpdateNotify)
sceneTeamUpdateNotify.IsInMp = world.multiplayer
empty := new(proto.AbilitySyncStateInfo)
for _, worldPlayer := range world.playerMap {
worldPlayerScene := world.GetSceneById(worldPlayer.SceneId)
worldPlayerTeamEntity := worldPlayerScene.GetPlayerTeamEntity(worldPlayer.PlayerID)
team := worldPlayer.TeamConfig.GetActiveTeam()
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
worldPlayerAvatar := worldPlayer.AvatarMap[avatarId]
equipIdList := make([]uint32, 0)
weapon := worldPlayerAvatar.EquipWeapon
equipIdList = append(equipIdList, weapon.ItemId)
for _, reliquary := range worldPlayerAvatar.EquipReliquaryList {
equipIdList = append(equipIdList, reliquary.ItemId)
}
sceneTeamAvatar := &proto.SceneTeamAvatar{
PlayerUid: worldPlayer.PlayerID,
AvatarGuid: worldPlayerAvatar.Guid,
SceneId: worldPlayer.SceneId,
EntityId: worldPlayerTeamEntity.avatarEntityMap[avatarId],
SceneEntityInfo: g.PacketSceneEntityInfoAvatar(worldPlayerScene, worldPlayer, avatarId),
WeaponGuid: worldPlayerAvatar.EquipWeapon.Guid,
WeaponEntityId: worldPlayerTeamEntity.weaponEntityMap[worldPlayerAvatar.EquipWeapon.WeaponId],
IsPlayerCurAvatar: worldPlayer.TeamConfig.GetActiveAvatarId() == avatarId,
IsOnScene: worldPlayer.TeamConfig.GetActiveAvatarId() == avatarId,
AvatarAbilityInfo: empty,
WeaponAbilityInfo: empty,
AbilityControlBlock: new(proto.AbilityControlBlock),
}
if world.multiplayer {
sceneTeamAvatar.AvatarInfo = g.PacketAvatarInfo(worldPlayerAvatar)
sceneTeamAvatar.SceneAvatarInfo = g.PacketSceneAvatarInfo(worldPlayerScene, worldPlayer, avatarId)
}
// add AbilityControlBlock
avatarDataConfig := gdc.CONF.AvatarDataMap[int32(avatarId)]
acb := sceneTeamAvatar.AbilityControlBlock
embryoId := 0
// add avatar abilities
for _, abilityId := range avatarDataConfig.Abilities {
embryoId++
emb := &proto.AbilityEmbryo{
AbilityId: uint32(embryoId),
AbilityNameHash: uint32(abilityId),
AbilityOverrideNameHash: uint32(constant.GameConstantConst.DEFAULT_ABILITY_NAME),
}
acb.AbilityEmbryoList = append(acb.AbilityEmbryoList, emb)
}
// add default abilities
for _, abilityId := range constant.GameConstantConst.DEFAULT_ABILITY_HASHES {
embryoId++
emb := &proto.AbilityEmbryo{
AbilityId: uint32(embryoId),
AbilityNameHash: uint32(abilityId),
AbilityOverrideNameHash: uint32(constant.GameConstantConst.DEFAULT_ABILITY_NAME),
}
acb.AbilityEmbryoList = append(acb.AbilityEmbryoList, emb)
}
// add team resonances
for id := range worldPlayer.TeamConfig.TeamResonancesConfig {
embryoId++
emb := &proto.AbilityEmbryo{
AbilityId: uint32(embryoId),
AbilityNameHash: uint32(id),
AbilityOverrideNameHash: uint32(constant.GameConstantConst.DEFAULT_ABILITY_NAME),
}
acb.AbilityEmbryoList = append(acb.AbilityEmbryoList, emb)
}
// add skill depot abilities
skillDepot := gdc.CONF.AvatarSkillDepotDataMap[int32(worldPlayerAvatar.SkillDepotId)]
if skillDepot != nil && len(skillDepot.Abilities) != 0 {
for _, id := range skillDepot.Abilities {
embryoId++
emb := &proto.AbilityEmbryo{
AbilityId: uint32(embryoId),
AbilityNameHash: uint32(id),
AbilityOverrideNameHash: uint32(constant.GameConstantConst.DEFAULT_ABILITY_NAME),
}
acb.AbilityEmbryoList = append(acb.AbilityEmbryoList, emb)
}
}
// add equip abilities
for skill := range worldPlayerAvatar.ExtraAbilityEmbryos {
embryoId++
emb := &proto.AbilityEmbryo{
AbilityId: uint32(embryoId),
AbilityNameHash: uint32(endec.Hk4eAbilityHashCode(skill)),
AbilityOverrideNameHash: uint32(constant.GameConstantConst.DEFAULT_ABILITY_NAME),
}
acb.AbilityEmbryoList = append(acb.AbilityEmbryoList, emb)
}
sceneTeamUpdateNotify.SceneTeamAvatarList = append(sceneTeamUpdateNotify.SceneTeamAvatarList, sceneTeamAvatar)
}
}
return sceneTeamUpdateNotify
}

78
gs/game/user_weapon.go Normal file
View File

@@ -0,0 +1,78 @@
package game
import (
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
)
func (g *GameManager) GetAllWeaponDataConfig() map[int32]*gdc.ItemData {
allWeaponDataConfig := make(map[int32]*gdc.ItemData)
for itemId, itemData := range gdc.CONF.ItemDataMap {
if itemData.EquipEnumType != constant.EquipTypeConst.EQUIP_WEAPON {
continue
}
if (itemId >= 10000 && itemId <= 10008) ||
itemId == 11411 ||
(itemId >= 11506 && itemId <= 11508) ||
itemId == 12505 ||
itemId == 12506 ||
itemId == 12508 ||
itemId == 12509 ||
itemId == 13503 ||
itemId == 13506 ||
itemId == 14411 ||
itemId == 14503 ||
itemId == 14505 ||
itemId == 14508 ||
(itemId >= 15504 && itemId <= 15506) ||
itemId == 20001 || itemId == 15306 || itemId == 14306 || itemId == 13304 || itemId == 12304 {
// 跳过无效武器
continue
}
allWeaponDataConfig[itemId] = itemData
}
return allWeaponDataConfig
}
func (g *GameManager) AddUserWeapon(userId uint32, itemId uint32) uint64 {
player := g.userManager.GetOnlineUser(userId)
if player == nil {
logger.LOG.Error("player is nil, uid: %v", userId)
return 0
}
weaponId := uint64(g.snowflake.GenId())
player.AddWeapon(itemId, weaponId)
weapon := player.GetWeapon(weaponId)
// PacketStoreItemChangeNotify
storeItemChangeNotify := new(proto.StoreItemChangeNotify)
storeItemChangeNotify.StoreType = proto.StoreType_STORE_TYPE_PACK
affixMap := make(map[uint32]uint32)
for _, affixId := range weapon.AffixIdList {
affixMap[affixId] = uint32(weapon.Refinement)
}
pbItem := &proto.Item{
ItemId: itemId,
Guid: player.GetWeaponGuid(weaponId),
Detail: &proto.Item_Equip{
Equip: &proto.Equip{
Detail: &proto.Equip_Weapon{
Weapon: &proto.Weapon{
Level: uint32(weapon.Level),
Exp: weapon.Exp,
PromoteLevel: uint32(weapon.Promote),
// key:武器效果id value:精炼等阶
AffixMap: affixMap,
},
},
IsLocked: weapon.Lock,
},
},
}
storeItemChangeNotify.ItemList = append(storeItemChangeNotify.ItemList, pbItem)
g.SendMsg(cmd.StoreItemChangeNotify, userId, player.ClientSeq, storeItemChangeNotify)
return weaponId
}

622
gs/game/world_manager.go Normal file
View File

@@ -0,0 +1,622 @@
package game
import (
"bytes"
"encoding/gob"
pb "google.golang.org/protobuf/proto"
"hk4e/common/utils/alg"
gdc "hk4e/gs/config"
"hk4e/gs/constant"
"hk4e/gs/game/aoi"
"hk4e/gs/model"
"hk4e/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
"math"
"time"
"unsafe"
)
// 世界管理器
type MeshMapPos struct {
X int16
Y int16
Z int16
}
type WorldStatic struct {
// x y z -> if terrain exist
terrain map[MeshMapPos]bool
// x y z -> gather id
gather map[MeshMapPos]uint32
pathfindingStartPos MeshMapPos
pathfindingEndPos MeshMapPos
pathVectorList []MeshMapPos
aiMoveMeshSpeedParam int
aiMoveVectorList []*model.Vector
aiMoveCurrIndex int
}
func NewWorldStatic() (r *WorldStatic) {
r = new(WorldStatic)
r.terrain = make(map[MeshMapPos]bool)
r.gather = make(map[MeshMapPos]uint32)
r.InitGather()
r.pathfindingStartPos = MeshMapPos{
X: 2747,
Y: 194,
Z: -1719,
}
r.pathfindingEndPos = MeshMapPos{
X: 2588,
Y: 211,
Z: -1349,
}
r.pathVectorList = make([]MeshMapPos, 0)
r.aiMoveMeshSpeedParam = 3
r.aiMoveVectorList = make([]*model.Vector, 0)
r.aiMoveCurrIndex = 0
return r
}
func (w *WorldStatic) ConvWSTMapToPFMap() map[alg.MeshMapPos]bool {
return *(*map[alg.MeshMapPos]bool)(unsafe.Pointer(&w.terrain))
}
func (w *WorldStatic) ConvWSPosToPFPos(v MeshMapPos) alg.MeshMapPos {
return alg.MeshMapPos(v)
}
func (w *WorldStatic) ConvPFPVLToWSPVL(v []alg.MeshMapPos) []MeshMapPos {
return *(*[]MeshMapPos)(unsafe.Pointer(&v))
}
func (w *WorldStatic) Pathfinding() {
bfs := alg.NewBFS()
bfs.InitMap(
w.ConvWSTMapToPFMap(),
w.ConvWSPosToPFPos(w.pathfindingStartPos),
w.ConvWSPosToPFPos(w.pathfindingEndPos),
100,
)
pathVectorList := bfs.Pathfinding()
if pathVectorList == nil {
logger.LOG.Error("could not find path")
return
}
logger.LOG.Debug("find path success, path: %v", pathVectorList)
w.pathVectorList = w.ConvPFPVLToWSPVL(pathVectorList)
}
func (w *WorldStatic) ConvPathVectorListToAiMoveVectorList() {
for index, currPathVector := range w.pathVectorList {
if index > 0 {
lastPathVector := w.pathVectorList[index-1]
for i := 0; i < w.aiMoveMeshSpeedParam; i++ {
w.aiMoveVectorList = append(w.aiMoveVectorList, &model.Vector{
X: float64(lastPathVector.X) + float64(currPathVector.X-lastPathVector.X)/float64(w.aiMoveMeshSpeedParam)*float64(i),
Y: float64(lastPathVector.Y) + float64(currPathVector.Y-lastPathVector.Y)/float64(w.aiMoveMeshSpeedParam)*float64(i),
Z: float64(lastPathVector.Z) + float64(currPathVector.Z-lastPathVector.Z)/float64(w.aiMoveMeshSpeedParam)*float64(i),
})
}
}
}
}
func (w *WorldStatic) InitTerrain() bool {
data := gdc.CONF.ReadWorldTerrain()
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&w.terrain)
if err != nil {
logger.LOG.Error("unmarshal world terrain data error: %v", err)
return false
}
return true
}
func (w *WorldStatic) SaveTerrain() bool {
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(w.terrain)
if err != nil {
logger.LOG.Error("marshal world terrain data error: %v", err)
return false
}
gdc.CONF.WriteWorldTerrain(buffer.Bytes())
return true
}
func (w *WorldStatic) GetTerrain(x int16, y int16, z int16) (exist bool) {
pos := MeshMapPos{
X: x,
Y: y,
Z: z,
}
exist = w.terrain[pos]
return exist
}
func (w *WorldStatic) SetTerrain(x int16, y int16, z int16) {
pos := MeshMapPos{
X: x,
Y: y,
Z: z,
}
w.terrain[pos] = true
}
func (w *WorldStatic) InitGather() {
}
func (w *WorldStatic) GetGather(x int16, y int16, z int16) (gatherId uint32, exist bool) {
pos := MeshMapPos{
X: x,
Y: y,
Z: z,
}
gatherId, exist = w.gather[pos]
return gatherId, exist
}
func (w *WorldStatic) SetGather(x int16, y int16, z int16, gatherId uint32) {
pos := MeshMapPos{
X: x,
Y: y,
Z: z,
}
w.gather[pos] = gatherId
}
type WorldManager struct {
worldMap map[uint32]*World
snowflake *alg.SnowflakeWorker
worldStatic *WorldStatic
bigWorld *World
}
func NewWorldManager(snowflake *alg.SnowflakeWorker) (r *WorldManager) {
r = new(WorldManager)
r.worldMap = make(map[uint32]*World)
r.snowflake = snowflake
r.worldStatic = NewWorldStatic()
return r
}
func (w *WorldManager) GetWorldByID(worldId uint32) *World {
return w.worldMap[worldId]
}
func (w *WorldManager) GetWorldMap() map[uint32]*World {
return w.worldMap
}
func (w *WorldManager) CreateWorld(owner *model.Player, multiplayer bool) *World {
worldId := uint32(w.snowflake.GenId())
world := &World{
id: worldId,
owner: owner,
playerMap: make(map[uint32]*model.Player),
sceneMap: make(map[uint32]*Scene),
entityIdCounter: 0,
worldLevel: 0,
multiplayer: multiplayer,
mpLevelEntityId: 0,
chatMsgList: make([]*proto.ChatInfo, 0),
// aoi划分
// TODO 为减少内存占用暂时去掉Y轴AOI格子划分 原来的Y轴格子数量为80
aoiManager: aoi.NewAoiManager(
-8000, 4000, 120,
-2000, 2000, 1,
-5500, 6500, 120,
),
}
if world.IsBigWorld() {
world.aoiManager = aoi.NewAoiManager(
-8000, 4000, 800,
-2000, 2000, 1,
-5500, 6500, 800,
)
}
world.mpLevelEntityId = world.GetNextWorldEntityId(constant.EntityIdTypeConst.MPLEVEL)
w.worldMap[worldId] = world
return world
}
func (w *WorldManager) DestroyWorld(worldId uint32) {
world := w.GetWorldByID(worldId)
for _, player := range world.playerMap {
world.RemovePlayer(player)
player.WorldId = 0
}
delete(w.worldMap, worldId)
}
func (w *WorldManager) GetBigWorld() *World {
return w.bigWorld
}
func (w *WorldManager) InitBigWorld(owner *model.Player) {
w.bigWorld = w.GetWorldByID(owner.WorldId)
w.bigWorld.multiplayer = true
}
type World struct {
id uint32
owner *model.Player
playerMap map[uint32]*model.Player
sceneMap map[uint32]*Scene
entityIdCounter uint32
worldLevel uint8
multiplayer bool
mpLevelEntityId uint32
chatMsgList []*proto.ChatInfo
aoiManager *aoi.AoiManager // 当前世界地图的aoi管理器
}
func (w *World) GetNextWorldEntityId(entityType uint16) uint32 {
w.entityIdCounter++
ret := (uint32(entityType) << 24) + w.entityIdCounter
return ret
}
func (w *World) AddPlayer(player *model.Player, sceneId uint32) {
player.PeerId = uint32(len(w.playerMap) + 1)
w.playerMap[player.PlayerID] = player
scene := w.GetSceneById(sceneId)
scene.AddPlayer(player)
}
func (w *World) RemovePlayer(player *model.Player) {
scene := w.sceneMap[player.SceneId]
scene.RemovePlayer(player)
delete(w.playerMap, player.PlayerID)
}
func (w *World) CreateScene(sceneId uint32) *Scene {
scene := &Scene{
id: sceneId,
world: w,
playerMap: make(map[uint32]*model.Player),
entityMap: make(map[uint32]*Entity),
playerTeamEntityMap: make(map[uint32]*PlayerTeamEntity),
gameTime: 18 * 60,
attackQueue: alg.NewRAQueue[*Attack](1000),
createTime: time.Now().UnixMilli(),
}
w.sceneMap[sceneId] = scene
return scene
}
func (w *World) GetSceneById(sceneId uint32) *Scene {
scene, exist := w.sceneMap[sceneId]
if !exist {
scene = w.CreateScene(sceneId)
}
return scene
}
func (w *World) AddChat(chatInfo *proto.ChatInfo) {
w.chatMsgList = append(w.chatMsgList, chatInfo)
}
func (w *World) GetChatList() []*proto.ChatInfo {
return w.chatMsgList
}
func (w *World) IsBigWorld() bool {
return w.owner.PlayerID == 1
}
type Scene struct {
id uint32
world *World
playerMap map[uint32]*model.Player
entityMap map[uint32]*Entity
playerTeamEntityMap map[uint32]*PlayerTeamEntity
gameTime uint32
attackQueue *alg.RAQueue[*Attack]
createTime int64
}
type AvatarEntity struct {
uid uint32
avatarId uint32
}
type MonsterEntity struct {
}
type GadgetEntity struct {
gatherId uint32
}
type Entity struct {
id uint32
scene *Scene
pos *model.Vector
rot *model.Vector
moveState uint16
lastMoveSceneTimeMs uint32
lastMoveReliableSeq uint32
fightProp map[uint32]float32
entityType uint32
level uint8
avatarEntity *AvatarEntity
monsterEntity *MonsterEntity
gadgetEntity *GadgetEntity
}
type PlayerTeamEntity struct {
teamEntityId uint32
avatarEntityMap map[uint32]uint32
weaponEntityMap map[uint64]uint32
}
type Attack struct {
combatInvokeEntry *proto.CombatInvokeEntry
uid uint32
}
func (s *Scene) ChangeGameTime(time uint32) {
s.gameTime = time % 1440
}
func (s *Scene) GetSceneTime() int64 {
now := time.Now().UnixMilli()
return now - s.createTime
}
func (s *Scene) GetPlayerTeamEntity(userId uint32) *PlayerTeamEntity {
return s.playerTeamEntityMap[userId]
}
func (s *Scene) CreatePlayerTeamEntity(player *model.Player) {
playerTeamEntity := &PlayerTeamEntity{
teamEntityId: s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.TEAM),
avatarEntityMap: make(map[uint32]uint32),
weaponEntityMap: make(map[uint64]uint32),
}
s.playerTeamEntityMap[player.PlayerID] = playerTeamEntity
}
func (s *Scene) UpdatePlayerTeamEntity(player *model.Player) {
team := player.TeamConfig.GetActiveTeam()
playerTeamEntity := s.playerTeamEntityMap[player.PlayerID]
for _, avatarId := range team.AvatarIdList {
if avatarId == 0 {
break
}
avatar := player.AvatarMap[avatarId]
avatarEntityId, exist := playerTeamEntity.avatarEntityMap[avatarId]
if exist {
s.DestroyEntity(avatarEntityId)
}
playerTeamEntity.avatarEntityMap[avatarId] = s.CreateEntityAvatar(player, avatarId)
weaponEntityId, exist := playerTeamEntity.weaponEntityMap[avatar.EquipWeapon.WeaponId]
if exist {
s.DestroyEntity(weaponEntityId)
}
playerTeamEntity.weaponEntityMap[avatar.EquipWeapon.WeaponId] = s.CreateEntityWeapon()
}
}
func (s *Scene) AddPlayer(player *model.Player) {
s.playerMap[player.PlayerID] = player
s.CreatePlayerTeamEntity(player)
s.UpdatePlayerTeamEntity(player)
}
func (s *Scene) RemovePlayer(player *model.Player) {
playerTeamEntity := s.GetPlayerTeamEntity(player.PlayerID)
for _, avatarEntityId := range playerTeamEntity.avatarEntityMap {
s.DestroyEntity(avatarEntityId)
}
for _, weaponEntityId := range playerTeamEntity.weaponEntityMap {
s.DestroyEntity(weaponEntityId)
}
delete(s.playerTeamEntityMap, player.PlayerID)
delete(s.playerMap, player.PlayerID)
}
func (s *Scene) CreateEntityAvatar(player *model.Player, avatarId uint32) uint32 {
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.AVATAR)
entity := &Entity{
id: entityId,
scene: s,
pos: player.Pos,
rot: player.Rot,
moveState: uint16(proto.MotionState_MOTION_STATE_NONE),
lastMoveSceneTimeMs: 0,
lastMoveReliableSeq: 0,
fightProp: player.AvatarMap[avatarId].FightPropMap,
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_AVATAR),
level: player.AvatarMap[avatarId].Level,
avatarEntity: &AvatarEntity{
uid: player.PlayerID,
avatarId: avatarId,
},
}
s.entityMap[entity.id] = entity
if avatarId == player.TeamConfig.GetActiveAvatarId() {
s.world.aoiManager.AddEntityIdToGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z))
}
return entity.id
}
func (s *Scene) CreateEntityWeapon() uint32 {
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.WEAPON)
entity := &Entity{
id: entityId,
scene: s,
pos: new(model.Vector),
rot: new(model.Vector),
moveState: uint16(proto.MotionState_MOTION_STATE_NONE),
lastMoveSceneTimeMs: 0,
lastMoveReliableSeq: 0,
fightProp: nil,
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_WEAPON),
level: 0,
}
s.entityMap[entity.id] = entity
return entity.id
}
func (s *Scene) CreateEntityMonster(pos *model.Vector, level uint8, fightProp map[uint32]float32) uint32 {
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.MONSTER)
entity := &Entity{
id: entityId,
scene: s,
pos: pos,
rot: new(model.Vector),
moveState: uint16(proto.MotionState_MOTION_STATE_NONE),
lastMoveSceneTimeMs: 0,
lastMoveReliableSeq: 0,
fightProp: fightProp,
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_MONSTER),
level: level,
}
s.entityMap[entity.id] = entity
s.world.aoiManager.AddEntityIdToGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z))
return entity.id
}
func (s *Scene) CreateEntityGadget(pos *model.Vector, gatherId uint32) uint32 {
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.GADGET)
entity := &Entity{
id: entityId,
scene: s,
pos: pos,
rot: new(model.Vector),
moveState: uint16(proto.MotionState_MOTION_STATE_NONE),
lastMoveSceneTimeMs: 0,
lastMoveReliableSeq: 0,
fightProp: map[uint32]float32{
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): math.MaxFloat32,
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): math.MaxFloat32,
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(1),
},
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET),
level: 0,
gadgetEntity: &GadgetEntity{
gatherId: gatherId,
},
}
s.entityMap[entity.id] = entity
s.world.aoiManager.AddEntityIdToGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z))
return entity.id
}
func (s *Scene) DestroyEntity(entityId uint32) {
entity := s.GetEntity(entityId)
if entity == nil {
return
}
s.world.aoiManager.RemoveEntityIdFromGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z))
delete(s.entityMap, entityId)
}
func (s *Scene) GetEntity(entityId uint32) *Entity {
return s.entityMap[entityId]
}
func (s *Scene) AddAttack(attack *Attack) {
s.attackQueue.EnQueue(attack)
}
func (s *Scene) AttackHandler(gameManager *GameManager) {
combatInvokeEntryListAll := make([]*proto.CombatInvokeEntry, 0)
combatInvokeEntryListOther := make(map[uint32][]*proto.CombatInvokeEntry)
combatInvokeEntryListHost := make([]*proto.CombatInvokeEntry, 0)
for s.attackQueue.Len() != 0 {
attack := s.attackQueue.DeQueue()
if attack.combatInvokeEntry == nil {
logger.LOG.Error("error attack data, attack value: %v", attack)
continue
}
hitInfo := new(proto.EvtBeingHitInfo)
err := pb.Unmarshal(attack.combatInvokeEntry.CombatData, hitInfo)
if err != nil {
logger.LOG.Error("parse combat invocations entity hit info error: %v", err)
continue
}
attackResult := hitInfo.AttackResult
//logger.LOG.Debug("run attack handler, attackResult: %v", attackResult)
target := s.entityMap[attackResult.DefenseId]
if target == nil {
logger.LOG.Error("could not found target, defense id: %v", attackResult.DefenseId)
continue
}
attackResult.Damage *= 100
damage := attackResult.Damage
attackerId := attackResult.AttackerId
_ = attackerId
currHp := float32(0)
if target.fightProp != nil {
currHp = target.fightProp[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP)]
currHp -= damage
if currHp < 0 {
currHp = 0
}
target.fightProp[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP)] = currHp
}
// PacketEntityFightPropUpdateNotify
entityFightPropUpdateNotify := new(proto.EntityFightPropUpdateNotify)
entityFightPropUpdateNotify.EntityId = target.id
entityFightPropUpdateNotify.FightPropMap = make(map[uint32]float32)
entityFightPropUpdateNotify.FightPropMap[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP)] = currHp
for _, player := range s.playerMap {
gameManager.SendMsg(cmd.EntityFightPropUpdateNotify, player.PlayerID, player.ClientSeq, entityFightPropUpdateNotify)
}
combatData, err := pb.Marshal(hitInfo)
if err != nil {
logger.LOG.Error("create combat invocations entity hit info error: %v", err)
}
attack.combatInvokeEntry.CombatData = combatData
switch attack.combatInvokeEntry.ForwardType {
case proto.ForwardType_FORWARD_TYPE_TO_ALL:
combatInvokeEntryListAll = append(combatInvokeEntryListAll, attack.combatInvokeEntry)
case proto.ForwardType_FORWARD_TYPE_TO_ALL_EXCEPT_CUR:
fallthrough
case proto.ForwardType_FORWARD_TYPE_TO_ALL_EXIST_EXCEPT_CUR:
if combatInvokeEntryListOther[attack.uid] == nil {
combatInvokeEntryListOther[attack.uid] = make([]*proto.CombatInvokeEntry, 0)
}
combatInvokeEntryListOther[attack.uid] = append(combatInvokeEntryListOther[attack.uid], attack.combatInvokeEntry)
case proto.ForwardType_FORWARD_TYPE_TO_HOST:
combatInvokeEntryListHost = append(combatInvokeEntryListHost, attack.combatInvokeEntry)
default:
}
}
// PacketCombatInvocationsNotify
if len(combatInvokeEntryListAll) > 0 {
combatInvocationsNotifyAll := new(proto.CombatInvocationsNotify)
combatInvocationsNotifyAll.InvokeList = combatInvokeEntryListAll
for _, player := range s.playerMap {
gameManager.SendMsg(cmd.CombatInvocationsNotify, player.PlayerID, player.ClientSeq, combatInvocationsNotifyAll)
}
}
if len(combatInvokeEntryListOther) > 0 {
for uid, list := range combatInvokeEntryListOther {
combatInvocationsNotifyOther := new(proto.CombatInvocationsNotify)
combatInvocationsNotifyOther.InvokeList = list
for _, player := range s.playerMap {
if player.PlayerID == uid {
continue
}
gameManager.SendMsg(cmd.CombatInvocationsNotify, player.PlayerID, player.ClientSeq, combatInvocationsNotifyOther)
}
}
}
if len(combatInvokeEntryListHost) > 0 {
combatInvocationsNotifyHost := new(proto.CombatInvocationsNotify)
combatInvocationsNotifyHost.InvokeList = combatInvokeEntryListHost
gameManager.SendMsg(cmd.CombatInvocationsNotify, s.world.owner.PlayerID, s.world.owner.ClientSeq, combatInvocationsNotifyHost)
}
}