mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 15:32:26 +08:00
溺水传送至安全位置初步
This commit is contained in:
@@ -38,7 +38,7 @@ func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, pa
|
|||||||
if player == nil {
|
if player == nil {
|
||||||
logger.Error("player is nil, uid: %v", userId)
|
logger.Error("player is nil, uid: %v", userId)
|
||||||
// 临时为了调试便捷搞的重连 生产环境请务必去除 不然新用户会一直重连不能进入
|
// 临时为了调试便捷搞的重连 生产环境请务必去除 不然新用户会一直重连不能进入
|
||||||
GAME_MANAGER.ReconnectPlayer(userId)
|
// GAME_MANAGER.ReconnectPlayer(userId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
player.ClientSeq = clientSeq
|
player.ClientSeq = clientSeq
|
||||||
@@ -115,6 +115,7 @@ func (r *RouteManager) InitRoute() {
|
|||||||
r.registerRouter(cmd.EvtDestroyGadgetNotify, GAME_MANAGER.EvtDestroyGadgetNotify)
|
r.registerRouter(cmd.EvtDestroyGadgetNotify, GAME_MANAGER.EvtDestroyGadgetNotify)
|
||||||
r.registerRouter(cmd.CreateVehicleReq, GAME_MANAGER.CreateVehicleReq)
|
r.registerRouter(cmd.CreateVehicleReq, GAME_MANAGER.CreateVehicleReq)
|
||||||
r.registerRouter(cmd.VehicleInteractReq, GAME_MANAGER.VehicleInteractReq)
|
r.registerRouter(cmd.VehicleInteractReq, GAME_MANAGER.VehicleInteractReq)
|
||||||
|
r.registerRouter(cmd.SceneEntityDrownReq, GAME_MANAGER.SceneEntityDrownReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) {
|
func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) {
|
||||||
|
|||||||
@@ -113,27 +113,35 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
|
|||||||
if sceneEntity.avatarEntity != nil {
|
if sceneEntity.avatarEntity != nil {
|
||||||
// 玩家实体在移动
|
// 玩家实体在移动
|
||||||
// 更新玩家的位置信息
|
// 更新玩家的位置信息
|
||||||
player.Pos.X = float64(motionInfo.Pos.X)
|
switch player.StaminaInfo.State {
|
||||||
player.Pos.Y = float64(motionInfo.Pos.Y)
|
case proto.MotionState_MOTION_STATE_DANGER_RUN, proto.MotionState_MOTION_STATE_RUN,
|
||||||
player.Pos.Z = float64(motionInfo.Pos.Z)
|
proto.MotionState_MOTION_STATE_DANGER_STANDBY_MOVE, proto.MotionState_MOTION_STATE_DANGER_STANDBY, proto.MotionState_MOTION_STATE_LADDER_TO_STANDBY, proto.MotionState_MOTION_STATE_STANDBY_MOVE, proto.MotionState_MOTION_STATE_STANDBY,
|
||||||
player.Rot.X = float64(motionInfo.Rot.X)
|
proto.MotionState_MOTION_STATE_DANGER_WALK, proto.MotionState_MOTION_STATE_WALK,
|
||||||
player.Rot.Y = float64(motionInfo.Rot.Y)
|
proto.MotionState_MOTION_STATE_DASH:
|
||||||
player.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)
|
||||||
|
default:
|
||||||
|
player.StaminaInfo.ActiveAvatarPos.X = float64(motionInfo.Pos.X)
|
||||||
|
player.StaminaInfo.ActiveAvatarPos.Y = float64(motionInfo.Pos.Y)
|
||||||
|
player.StaminaInfo.ActiveAvatarPos.Z = float64(motionInfo.Pos.Z)
|
||||||
|
}
|
||||||
|
|
||||||
// 处理耐力消耗
|
// 处理耐力消耗
|
||||||
g.ImmediateStamina(player, motionInfo.State)
|
g.ImmediateStamina(player, motionInfo.State)
|
||||||
} else {
|
} else {
|
||||||
// 非玩家实体在移动 更新场景实体的位置信息
|
// 非玩家实体在移动
|
||||||
sceneEntity.pos = &model.Vector{
|
// 更新场景实体的位置信息
|
||||||
X: float64(motionInfo.Pos.X),
|
sceneEntity.pos.X = float64(motionInfo.Pos.X)
|
||||||
Y: float64(motionInfo.Pos.Y),
|
sceneEntity.pos.Y = float64(motionInfo.Pos.Y)
|
||||||
Z: float64(motionInfo.Pos.Z),
|
sceneEntity.pos.Z = float64(motionInfo.Pos.Z)
|
||||||
}
|
sceneEntity.rot.X = float64(motionInfo.Rot.X)
|
||||||
sceneEntity.rot = &model.Vector{
|
sceneEntity.rot.Y = float64(motionInfo.Rot.Y)
|
||||||
X: float64(motionInfo.Rot.X),
|
sceneEntity.rot.Z = float64(motionInfo.Rot.Z)
|
||||||
Y: float64(motionInfo.Rot.Y),
|
|
||||||
Z: float64(motionInfo.Rot.Z),
|
|
||||||
}
|
|
||||||
// 载具耐力消耗
|
// 载具耐力消耗
|
||||||
if sceneEntity.gadgetEntity != nil && sceneEntity.gadgetEntity.gadgetVehicleEntity != nil {
|
if sceneEntity.gadgetEntity != nil && sceneEntity.gadgetEntity.gadgetVehicleEntity != nil {
|
||||||
// 处理耐力消耗
|
// 处理耐力消耗
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ func (g *GameManager) SceneTransToPointReq(player *model.Player, payloadMsg pb.M
|
|||||||
g.TeleportPlayer(player, uint32(constant.EnterReasonConst.TransPoint), sceneId, pos)
|
g.TeleportPlayer(player, uint32(constant.EnterReasonConst.TransPoint), sceneId, pos)
|
||||||
|
|
||||||
sceneTransToPointRsp := &proto.SceneTransToPointRsp{
|
sceneTransToPointRsp := &proto.SceneTransToPointRsp{
|
||||||
Retcode: 0,
|
|
||||||
PointId: req.PointId,
|
PointId: req.PointId,
|
||||||
SceneId: req.SceneId,
|
SceneId: req.SceneId,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -533,6 +533,18 @@ func (g *GameManager) PacketFightPropMapToPbFightPropList(fightPropMap map[uint3
|
|||||||
return fightPropList
|
return fightPropList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SceneEntityDrownReq 实体溺水请求
|
||||||
|
func (g *GameManager) SceneEntityDrownReq(player *model.Player, payloadMsg pb.Message) {
|
||||||
|
req := payloadMsg.(*proto.SceneEntityDrownReq)
|
||||||
|
|
||||||
|
logger.Error("entity drown, entityId: %v", req.EntityId)
|
||||||
|
|
||||||
|
// PacketSceneEntityDrownRsp
|
||||||
|
sceneEntityDrownRsp := new(proto.SceneEntityDrownRsp)
|
||||||
|
sceneEntityDrownRsp.EntityId = req.EntityId
|
||||||
|
g.SendMsg(cmd.SceneEntityDrownRsp, player.PlayerID, player.ClientSeq, sceneEntityDrownRsp)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GameManager) PacketSceneEntityInfoAvatar(scene *Scene, player *model.Player, avatarId uint32) *proto.SceneEntityInfo {
|
func (g *GameManager) PacketSceneEntityInfoAvatar(scene *Scene, player *model.Player, avatarId uint32) *proto.SceneEntityInfo {
|
||||||
entity := scene.GetEntity(scene.world.GetPlayerWorldAvatarEntityId(player, avatarId))
|
entity := scene.GetEntity(scene.world.GetPlayerWorldAvatarEntityId(player, avatarId))
|
||||||
if entity == nil {
|
if entity == nil {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
gdc "hk4e/gs/config"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -418,10 +419,15 @@ func (g *GameManager) UpdatePlayerStamina(player *model.Player, staminaCost int3
|
|||||||
|
|
||||||
// DrownBackHandler 玩家溺水返回安全点
|
// DrownBackHandler 玩家溺水返回安全点
|
||||||
func (g *GameManager) DrownBackHandler(player *model.Player) {
|
func (g *GameManager) DrownBackHandler(player *model.Player) {
|
||||||
// 溺水返回时间为0代表不进行返回
|
// 玩家暂停跳过
|
||||||
if player.StaminaInfo.DrownBackTime == 0 {
|
if player.Pause {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 溺水返回时间为0代表不进行返回
|
||||||
|
if player.StaminaInfo.DrownBackDelay == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||||||
scene := world.GetSceneById(player.SceneId)
|
scene := world.GetSceneById(player.SceneId)
|
||||||
activeAvatar := world.GetPlayerWorldAvatar(player, world.GetPlayerActiveAvatarId(player))
|
activeAvatar := world.GetPlayerWorldAvatar(player, world.GetPlayerActiveAvatarId(player))
|
||||||
@@ -431,54 +437,58 @@ func (g *GameManager) DrownBackHandler(player *model.Player) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录溺水安全点
|
// TODO
|
||||||
// 溺水安全点可能需要读取附近锚点坐标
|
// 溺水安全点可能需要读取附近锚点坐标
|
||||||
// 多次溺水后提示溺水的死亡信息
|
// 多次溺水后提示溺水的死亡信息
|
||||||
// 官服 游戏离线也会回到安全位置
|
// 官服 游戏离线也会回到安全位置
|
||||||
// 很显然目前这个很不完善
|
// 很显然目前这个很不完善
|
||||||
switch player.StaminaInfo.State {
|
|
||||||
case proto.MotionState_MOTION_STATE_DANGER_RUN, proto.MotionState_MOTION_STATE_RUN,
|
|
||||||
proto.MotionState_MOTION_STATE_DANGER_STANDBY_MOVE, proto.MotionState_MOTION_STATE_DANGER_STANDBY, proto.MotionState_MOTION_STATE_LADDER_TO_STANDBY, proto.MotionState_MOTION_STATE_STANDBY_MOVE, proto.MotionState_MOTION_STATE_STANDBY,
|
|
||||||
proto.MotionState_MOTION_STATE_DANGER_WALK, proto.MotionState_MOTION_STATE_WALK,
|
|
||||||
proto.MotionState_MOTION_STATE_DASH:
|
|
||||||
player.StaminaInfo.DrownBackPos.X = player.Pos.X
|
|
||||||
player.StaminaInfo.DrownBackPos.Y = player.Pos.Y
|
|
||||||
player.StaminaInfo.DrownBackPos.Z = player.Pos.Z
|
|
||||||
}
|
|
||||||
|
|
||||||
// 临时
|
// 先传送玩家再设置角色存活否则同时设置会传送前显示角色实体
|
||||||
if player.StaminaInfo.DrownBackPos.X == 0 && player.StaminaInfo.DrownBackPos.Y == 0 && player.StaminaInfo.DrownBackPos.Z == 0 {
|
if player.StaminaInfo.DrownBackDelay > 20 && player.SceneLoadState == model.SceneEnterDone {
|
||||||
player.StaminaInfo.DrownBackPos.X = player.Pos.X
|
// 设置角色存活
|
||||||
player.StaminaInfo.DrownBackPos.Y = player.Pos.Y
|
scene.SetEntityLifeState(avatarEntity, constant.LifeStateConst.LIFE_REVIVE, proto.PlayerDieType_PLAYER_DIE_TYPE_NONE)
|
||||||
player.StaminaInfo.DrownBackPos.Z = player.Pos.Z
|
// 重置溺水返回时间
|
||||||
}
|
player.StaminaInfo.DrownBackDelay = 0
|
||||||
|
} else if player.StaminaInfo.DrownBackDelay == 20 {
|
||||||
if player.StaminaInfo.DrownBackTime == 1 {
|
// TODO 队伍扣血
|
||||||
// 确保玩家已经完成加载场景
|
maxStamina := player.PropertiesMap[constant.PlayerPropertyConst.PROP_MAX_STAMINA]
|
||||||
if player.SceneLoadState == model.SceneEnterDone {
|
// 设置玩家耐力为一半
|
||||||
// 设置角色存活
|
g.SetPlayerStamina(player, maxStamina/2)
|
||||||
scene.SetEntityLifeState(avatarEntity, constant.LifeStateConst.LIFE_REVIVE, proto.PlayerDieType_PLAYER_DIE_TYPE_NONE)
|
// 如果玩家的位置比锚点距离近则优先使用玩家位置
|
||||||
// 重置溺水返回时间
|
pos := &model.Vector{
|
||||||
player.StaminaInfo.DrownBackTime = 0
|
X: player.Pos.X,
|
||||||
|
Y: player.Pos.Y,
|
||||||
|
Z: player.Pos.Z,
|
||||||
|
}
|
||||||
|
// 获取最近角色实体的锚点
|
||||||
|
// TODO 阻塞优化 16ms我感觉有点慢
|
||||||
|
for _, entry := range gdc.CONF.ScenePointEntries {
|
||||||
|
if entry.PointData == nil || entry.PointData.TranPos == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pointPos := &model.Vector{
|
||||||
|
X: entry.PointData.TranPos.X,
|
||||||
|
Y: entry.PointData.TranPos.Y,
|
||||||
|
Z: entry.PointData.TranPos.Z,
|
||||||
|
}
|
||||||
|
// 该坐标距离小于之前的则赋值
|
||||||
|
if player.StaminaInfo.ActiveAvatarPos.Distance(pointPos) < player.StaminaInfo.ActiveAvatarPos.Distance(pos) {
|
||||||
|
pos = pointPos
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if time.Now().UnixMilli() >= player.StaminaInfo.DrownBackTime {
|
|
||||||
// 临时 防止一直淹死 尚未完善
|
|
||||||
g.SetPlayerStamina(player, 10000)
|
|
||||||
// 传送玩家至安全位置
|
// 传送玩家至安全位置
|
||||||
g.TeleportPlayer(player, uint32(constant.EnterReasonConst.Revival), player.SceneId, player.StaminaInfo.DrownBackPos)
|
g.TeleportPlayer(player, uint32(constant.EnterReasonConst.Revival), player.SceneId, pos)
|
||||||
|
}
|
||||||
// 设置溺水返回时间为1
|
// 防止重置后又被修改
|
||||||
// 1代表加载完场景后再复活玩家
|
if player.StaminaInfo.DrownBackDelay != 0 {
|
||||||
player.StaminaInfo.DrownBackTime = 1
|
player.StaminaInfo.DrownBackDelay++
|
||||||
// 重置动作状态否则可能会导致多次溺水
|
|
||||||
player.StaminaInfo.State = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleDrown 处理玩家溺水
|
// HandleDrown 处理玩家溺水
|
||||||
func (g *GameManager) HandleDrown(player *model.Player, stamina uint32) {
|
func (g *GameManager) HandleDrown(player *model.Player, stamina uint32) {
|
||||||
// 溺水需要耐力等于0 返回时间不等于0代表已处理过溺水正在等待返回
|
// 溺水需要耐力等于0 返回延时不等于0代表已处理过溺水正在等待返回
|
||||||
if stamina != 0 || player.StaminaInfo.DrownBackTime != 0 {
|
if stamina != 0 || player.StaminaInfo.DrownBackDelay != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,8 +506,8 @@ func (g *GameManager) HandleDrown(player *model.Player, stamina uint32) {
|
|||||||
logger.Debug("player drown, curStamina: %v, state: %v", stamina, player.StaminaInfo.State)
|
logger.Debug("player drown, curStamina: %v, state: %v", stamina, player.StaminaInfo.State)
|
||||||
// 设置角色为死亡
|
// 设置角色为死亡
|
||||||
scene.SetEntityLifeState(avatarEntity, constant.LifeStateConst.LIFE_DEAD, proto.PlayerDieType_PLAYER_DIE_TYPE_DRAWN)
|
scene.SetEntityLifeState(avatarEntity, constant.LifeStateConst.LIFE_DEAD, proto.PlayerDieType_PLAYER_DIE_TYPE_DRAWN)
|
||||||
// 溺水返回安全点的时间
|
// 溺水返回安全点 计时开始
|
||||||
player.StaminaInfo.DrownBackTime = time.Now().Add(time.Second * 4).UnixMilli()
|
player.StaminaInfo.DrownBackDelay = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (p *Player) InitAll() {
|
|||||||
p.GameObjectGuidMap = make(map[uint64]GameObject)
|
p.GameObjectGuidMap = make(map[uint64]GameObject)
|
||||||
p.CoopApplyMap = make(map[uint32]int64)
|
p.CoopApplyMap = make(map[uint32]int64)
|
||||||
p.StaminaInfo = new(StaminaInfo)
|
p.StaminaInfo = new(StaminaInfo)
|
||||||
p.StaminaInfo.DrownBackPos = new(Vector)
|
p.StaminaInfo.ActiveAvatarPos = new(Vector)
|
||||||
p.VehicleInfo = new(VehicleInfo)
|
p.VehicleInfo = new(VehicleInfo)
|
||||||
p.VehicleInfo.LastCreateEntityIdMap = make(map[uint32]uint32)
|
p.VehicleInfo.LastCreateEntityIdMap = make(map[uint32]uint32)
|
||||||
p.InitAllAvatar()
|
p.InitAllAvatar()
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ type StaminaInfo struct {
|
|||||||
LastSkillId uint32 // 最后释放的技能Id
|
LastSkillId uint32 // 最后释放的技能Id
|
||||||
LastSkillTime int64 // 最后释放技能的时间
|
LastSkillTime int64 // 最后释放技能的时间
|
||||||
LastSkillStartTime int64 // 最后执行开始技能耐力消耗的时间
|
LastSkillStartTime int64 // 最后执行开始技能耐力消耗的时间
|
||||||
DrownBackTime int64 // 溺水返回安全点的时间
|
DrownBackDelay int64 // 溺水返回安全点延时
|
||||||
DrownBackPos *Vector // 溺水返回的安全点坐标
|
ActiveAvatarPos *Vector // 当前角色位置
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStaminaCost 设置动作需要消耗的耐力
|
// SetStaminaCost 设置动作需要消耗的耐力
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
type Vector struct {
|
type Vector struct {
|
||||||
X float64 `bson:"x"`
|
X float64 `bson:"x"`
|
||||||
Y float64 `bson:"y"`
|
Y float64 `bson:"y"`
|
||||||
Z float64 `bson:"z"`
|
Z float64 `bson:"z"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distance 两坐标之间的距离
|
||||||
|
func (v *Vector) Distance(vector *Vector) float64 {
|
||||||
|
return math.Sqrt(math.Pow(v.X-vector.X, 2) + math.Pow(v.Y-vector.Y, 2) + math.Pow(v.Z-vector.Z, 2))
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ func (c *CmdProtoMap) registerAllMessage() {
|
|||||||
c.registerMessage(LeaveWorldNotify, &proto.LeaveWorldNotify{}) // 删除客户端世界通知
|
c.registerMessage(LeaveWorldNotify, &proto.LeaveWorldNotify{}) // 删除客户端世界通知
|
||||||
c.registerMessage(SceneAvatarStaminaStepReq, &proto.SceneAvatarStaminaStepReq{}) // 缓慢游泳或缓慢攀爬时消耗耐力请求
|
c.registerMessage(SceneAvatarStaminaStepReq, &proto.SceneAvatarStaminaStepReq{}) // 缓慢游泳或缓慢攀爬时消耗耐力请求
|
||||||
c.registerMessage(SceneAvatarStaminaStepRsp, &proto.SceneAvatarStaminaStepRsp{}) // 缓慢游泳或缓慢攀爬时消耗耐力响应
|
c.registerMessage(SceneAvatarStaminaStepRsp, &proto.SceneAvatarStaminaStepRsp{}) // 缓慢游泳或缓慢攀爬时消耗耐力响应
|
||||||
|
c.registerMessage(LifeStateChangeNotify, &proto.LifeStateChangeNotify{}) // 实体存活状态改变通知
|
||||||
|
c.registerMessage(SceneEntityDrownReq, &proto.SceneEntityDrownReq{}) // 场景实体溺水请求
|
||||||
|
c.registerMessage(SceneEntityDrownRsp, &proto.SceneEntityDrownRsp{}) // 场景实体溺水响应
|
||||||
|
|
||||||
// 战斗与同步
|
// 战斗与同步
|
||||||
c.registerMessage(AvatarFightPropNotify, &proto.AvatarFightPropNotify{}) // 角色战斗属性通知
|
c.registerMessage(AvatarFightPropNotify, &proto.AvatarFightPropNotify{}) // 角色战斗属性通知
|
||||||
@@ -113,7 +116,6 @@ func (c *CmdProtoMap) registerAllMessage() {
|
|||||||
c.registerMessage(EvtEntityRenderersChangedNotify, &proto.EvtEntityRenderersChangedNotify{}) // 实体可视状态改变通知
|
c.registerMessage(EvtEntityRenderersChangedNotify, &proto.EvtEntityRenderersChangedNotify{}) // 实体可视状态改变通知
|
||||||
c.registerMessage(EvtCreateGadgetNotify, &proto.EvtCreateGadgetNotify{}) // 创建实体通知
|
c.registerMessage(EvtCreateGadgetNotify, &proto.EvtCreateGadgetNotify{}) // 创建实体通知
|
||||||
c.registerMessage(EvtDestroyGadgetNotify, &proto.EvtDestroyGadgetNotify{}) // 销毁实体通知
|
c.registerMessage(EvtDestroyGadgetNotify, &proto.EvtDestroyGadgetNotify{}) // 销毁实体通知
|
||||||
c.registerMessage(LifeStateChangeNotify, &proto.LifeStateChangeNotify{}) // 实体存活状态改变通知
|
|
||||||
|
|
||||||
// 队伍
|
// 队伍
|
||||||
c.registerMessage(ChangeAvatarReq, &proto.ChangeAvatarReq{}) // 更换角色请求 切人
|
c.registerMessage(ChangeAvatarReq, &proto.ChangeAvatarReq{}) // 更换角色请求 切人
|
||||||
|
|||||||
Reference in New Issue
Block a user