diff --git a/gs/game/command_controller.go b/gs/game/command_controller.go index bab561cb..def22f00 100644 --- a/gs/game/command_controller.go +++ b/gs/game/command_controller.go @@ -1,6 +1,11 @@ package game -import "fmt" +import ( + "fmt" + "hk4e/gs/model" + "strconv" + "strings" +) // HelpCommand 帮助命令 func (c *CommandManager) HelpCommand(cmd *Command) { @@ -10,8 +15,99 @@ func (c *CommandManager) HelpCommand(cmd *Command) { ) } -// OpCommand 帮助命令 +// OpCommand 给予权限命令 func (c *CommandManager) OpCommand(cmd *Command) { cmd.Executor.IsGM = 1 c.gameManager.SendPrivateChat(c.system, cmd.Executor, fmt.Sprintf("权限修改完毕, 现在你是GM啦 %v", cmd.Args)) } + +// TeleportCommand 传送玩家命令 +// tp {-u [userId]} {-s [sceneId]} -x [posX] -y [posY] -z [posZ] +func (c *CommandManager) TeleportCommand(cmd *Command) { + game := c.gameManager + player := cmd.Executor + + // 初始值 + target := player + sceneId := target.SceneId + pos := &model.Vector{} + + // 判断是否填写必备参数 + if cmd.Args["x"] == "" || cmd.Args["y"] == "" || cmd.Args["z"] == "" { + game.SendPrivateChat(c.system, player, "参数不足, 正确用法 /tp {-u [userId]} {-s [sceneId]} -x [posX] -y [posY] -z [posZ]。") + return + } + + // 选择每个参数 + for k, v := range cmd.Args { + var err error + + switch k { + case "u": + var t uint64 + if t, err = strconv.ParseUint(v, 10, 32); err != nil { + // 判断目标用户是否在线 + if target = game.userManager.GetOnlineUser(uint32(t)); target == nil { + game.SendPrivateChat(c.system, player, fmt.Sprintf("目标玩家不在线, UID: %v。", v)) + return + } + } + case "s": + var s uint64 + if s, err = strconv.ParseUint(v, 10, 32); err == nil { + sceneId = uint32(s) + } + case "x": + // 玩家此时的位置X + var nowX float64 + // 如果以 ~ 开头则 此时位置加 ~ 后的数 + if strings.HasPrefix(v, "~") { + v = v[1:] // 去除 ~ + nowX = player.Pos.X // 先记录 + } + var x float64 + if x, err = strconv.ParseFloat(v, 64); err == nil { + pos.X = x + nowX // 如果不以 ~ 开头则加 0 + } + case "y": + // 玩家此时的位置Z + var nowY float64 + // 如果以 ~ 开头则 此时位置加 ~ 后的数 + if strings.HasPrefix(v, "~") { + v = v[1:] // 去除 ~ + nowY = player.Pos.Y // 先记录 + } + var y float64 + if y, err = strconv.ParseFloat(v, 64); v != "~" && err == nil { + pos.Y = y + nowY + } + case "z": + // 玩家此时的位置Z + var nowZ float64 + // 如果以 ~ 开头则 此时位置加 ~ 后的数 + if strings.HasPrefix(v, "~") { + v = v[1:] // 去除 ~ + nowZ = player.Pos.Z // 先记录 + } + var z float64 + if z, err = strconv.ParseFloat(v, 64); v != "~" && err == nil { + pos.Z = z + nowZ + } + default: + game.SendPrivateChat(c.system, player, fmt.Sprintf("参数 -%v 冗余。", k)) + return + } + + // 解析错误的话应该是参数类型问题 + if err != nil { + game.SendPrivateChat(c.system, player, fmt.Sprintf("参数 -%v 有误, 类型错误。", k)) + return + } + } + + // 传送玩家 + game.TeleportPlayer(target, sceneId, pos) + + // 发送消息给执行者 + game.SendPrivateChat(c.system, player, fmt.Sprintf("已将玩家 UID: %v 传送至 区域: %v X: %v Y: %v Z:%v。", target.PlayerID, sceneId, pos.X, pos.Y, pos.Z)) +} diff --git a/gs/game/command_manager.go b/gs/game/command_manager.go index d7ce943f..0596273c 100644 --- a/gs/game/command_manager.go +++ b/gs/game/command_manager.go @@ -13,16 +13,21 @@ import ( // 0 为普通玩家 数越大权限越大 type CommandPerm uint8 +const ( + CommandPermNormal = CommandPerm(iota) // 普通玩家 + CommandPermGM // 管理员 +) + // CommandFunc 命令执行函数 type CommandFunc func(*Command) // Command 命令结构体 // 给下层执行命令时提供数据 type Command struct { - Executor *model.Player // 执行者 - Text string // 命令原始文本 - Name string // 命令前缀 - Args []string // 命令参数 + Executor *model.Player // 执行者 + Text string // 命令原始文本 + Name string // 命令前缀 + Args map[string]string // 命令参数 } // CommandManager 命令管理器 @@ -61,23 +66,42 @@ func (c *CommandManager) InitRouter() { c.commandPermMap = make(map[string]CommandPerm) { // 权限等级 0: 普通玩家 - c.RegisterRouter("help", 0, c.HelpCommand) - c.RegisterRouter("op", 0, c.OpCommand) + c.RegisterRouter(CommandPermNormal, c.HelpCommand, "help") + c.RegisterRouter(CommandPermNormal, c.OpCommand, "op") + c.RegisterRouter(CommandPermNormal, c.TeleportCommand, "teleport", "tp") } // GM命令 { // 权限等级 1: GM 1级 - c.RegisterRouter("nmsl", 1, c.HelpCommand) + c.RegisterRouter(CommandPermGM, c.HelpCommand, "nmsl") } } // RegisterRouter 注册命令路由 -func (c *CommandManager) RegisterRouter(cmdName string, cmdPerm CommandPerm, cmdFunc CommandFunc) { - // 命令名统一转为小写 - cmdName = strings.ToLower(cmdName) - // 记录命令 - c.commandFuncRouter[cmdName] = cmdFunc - c.commandPermMap[cmdName] = cmdPerm +func (c *CommandManager) RegisterRouter(cmdPerm CommandPerm, cmdFunc CommandFunc, cmdName ...string) { + // 支持一个命令拥有多个别名 + for _, s := range cmdName { + // 命令名统一转为小写 + s = strings.ToLower(s) + // 如果命令已注册则报错 后者覆盖前者 + if c.HasCommand(s) { + logger.LOG.Error("register command repeat, name: %v", s) + } + // 记录命令 + c.commandFuncRouter[s] = cmdFunc + c.commandPermMap[s] = cmdPerm + } +} + +// HasCommand 命令是否已被注册 +func (c *CommandManager) HasCommand(cmdName string) bool { + _, cmdFuncOK := c.commandFuncRouter[cmdName] + _, cmdPermOK := c.commandPermMap[cmdName] + // 判断命令函数和命令权限是否已注册 + if cmdFuncOK && cmdPermOK { + return true + } + return false } // InputCommand 输入要处理的命令 @@ -114,10 +138,10 @@ func (c *CommandManager) GetFriendList(friendList map[uint32]bool) map[uint32]bo func (c *CommandManager) NewCommand(executor *model.Player, text string) *Command { // 将开头的 / 去掉 并 分割出命令的每个参数 // 不区分命令的大小写 统一转为小写 - cmdSplit := strings.Split(strings.ToLower(text[1:]), " ") + cmdSplit := strings.Split(strings.ToLower(text[1:]), " -") - var cmdName string // 命令名 - var cmdArgs []string // 命令参数 + cmdName := "" // 命令名 + cmdArgs := make(map[string]string, len(cmdSplit)-1) // 命令参数 // 分割出来啥也没有可能是个空的字符串 // 此时将会返回的命令名和命令参数都为空 @@ -125,7 +149,24 @@ func (c *CommandManager) NewCommand(executor *model.Player, text string) *Comman // 首个参数必是命令名 cmdName = cmdSplit[0] // 命令名后当然是命令的参数喽 - cmdArgs = cmdSplit[1:] + argSplit := cmdSplit[1:] + + // 我们要将命令的参数转换为键值对 + // 每个参数之间会有个空格分割 + for _, s := range argSplit { + cmdArg := strings.Split(s, " ") + + // 分割出来的参数只有一个那肯定不是键值对 + if len(cmdArg) >= 2 { + argKey := cmdArg[0] // 参数的键 + argValue := cmdArg[1] // 参数的值 + + // 记录命令的参数 + cmdArgs[argKey] = argValue + } else { + logger.LOG.Debug("command arg error, arg: %v", cmdArg) + } + } } return &Command{executor, text, cmdName, cmdArgs} diff --git a/gs/game/user_map.go b/gs/game/user_map.go index df1e8712..da0722d6 100644 --- a/gs/game/user_map.go +++ b/gs/game/user_map.go @@ -28,7 +28,50 @@ func (g *GameManager) SceneTransToPointReq(player *model.Player, payloadMsg pb.M } // 传送玩家 - newSceneId := req.SceneId + sceneId := uint32(transPointConfig.PointData.TranSceneId) + transPos := transPointConfig.PointData.TranPos + pos := &model.Vector{ + X: transPos.X, + Y: transPos.Y, + Z: transPos.Z, + } + g.TeleportPlayer(player, sceneId, pos) + + // 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 = 300 + } + // 传送玩家 + pos := &model.Vector{ + X: float64(req.Mark.Pos.X), + Y: float64(posYInt), + Z: float64(req.Mark.Pos.Z), + } + g.TeleportPlayer(player, req.Mark.SceneId, pos) + } + } +} + +// TeleportPlayer 传送玩家至地图上的某个位置 +func (g *GameManager) TeleportPlayer(player *model.Player, sceneId uint32, pos *model.Vector) { + // 传送玩家 + newSceneId := sceneId oldSceneId := player.SceneId oldPos := &model.Vector{ X: player.Pos.X, @@ -55,9 +98,9 @@ func (g *GameManager) SceneTransToPointReq(player *model.Player, payloadMsg pb.M } 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.Pos.X = pos.X + player.Pos.Y = pos.Y + player.Pos.Z = pos.Z player.SceneId = newSceneId player.SceneLoadState = model.SceneNone @@ -72,75 +115,6 @@ func (g *GameManager) SceneTransToPointReq(player *model.Player, payloadMsg pb.M } 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 = 300 - } - - // 传送玩家 - 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) {