diff --git a/gs/game/command_controller.go b/gs/game/command_controller.go index def22f00..50051856 100644 --- a/gs/game/command_controller.go +++ b/gs/game/command_controller.go @@ -1,43 +1,56 @@ package game import ( - "fmt" "hk4e/gs/model" "strconv" "strings" ) // HelpCommand 帮助命令 -func (c *CommandManager) HelpCommand(cmd *Command) { - c.gameManager.SendPrivateChat(c.system, cmd.Executor, +func (c *CommandManager) HelpCommand(cmd *CommandMessage) { + executor := cmd.Executor + + c.SendMessage(executor, "===== 帮助 / Help =====\n"+ - "以后再写awa\n", + "传送: /tp {-u [UID]} {-s [场景ID]} -x [坐标X] -y [坐标Y] -z [坐标Z]\n", ) } // OpCommand 给予权限命令 -func (c *CommandManager) OpCommand(cmd *Command) { - cmd.Executor.IsGM = 1 - c.gameManager.SendPrivateChat(c.system, cmd.Executor, fmt.Sprintf("权限修改完毕, 现在你是GM啦 %v", cmd.Args)) +func (c *CommandManager) OpCommand(cmd *CommandMessage) { + player, ok := cmd.Executor.(*model.Player) + if !ok { + c.SendMessage(cmd.Executor, "只有玩家才能执行此命令。") + return + } + + player.IsGM = 1 + c.SendMessage(player, "权限修改完毕, 现在你是GM啦 %v", cmd.Args) } // TeleportCommand 传送玩家命令 // tp {-u [userId]} {-s [sceneId]} -x [posX] -y [posY] -z [posZ] -func (c *CommandManager) TeleportCommand(cmd *Command) { +func (c *CommandManager) TeleportCommand(cmd *CommandMessage) { game := c.gameManager - player := cmd.Executor + + // 执行者如果不是玩家则必须输入目标UID + player, ok := cmd.Executor.(*model.Player) + if !ok && cmd.Args["u"] == "" { + c.SendMessage(cmd.Executor, "你不是玩家请指定目标UID。") + return + } + + // 判断是否填写必备参数 + if cmd.Args["x"] == "" || cmd.Args["y"] == "" || cmd.Args["z"] == "" { + c.SendMessage(player, "参数不足, 正确用法: /%v {-u [UID]} {-s [场景ID]} -x [坐标X] -y [坐标Y] -z [坐标Z]。", cmd.Name) + return + } // 初始值 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 @@ -47,8 +60,11 @@ func (c *CommandManager) TeleportCommand(cmd *Command) { 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)) + if user := game.userManager.GetOnlineUser(uint32(t)); user != nil { + target = user + sceneId = target.SceneId + } else { + c.SendMessage(player, "目标玩家不在线, UID: %v。", v) return } } @@ -94,13 +110,13 @@ func (c *CommandManager) TeleportCommand(cmd *Command) { pos.Z = z + nowZ } default: - game.SendPrivateChat(c.system, player, fmt.Sprintf("参数 -%v 冗余。", k)) + c.SendMessage(player, "参数 -%v 冗余。", k) return } // 解析错误的话应该是参数类型问题 if err != nil { - game.SendPrivateChat(c.system, player, fmt.Sprintf("参数 -%v 有误, 类型错误。", k)) + c.SendMessage("参数 -%v 有误, 类型错误。", k) return } } @@ -109,5 +125,5 @@ func (c *CommandManager) TeleportCommand(cmd *Command) { 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)) + c.SendMessage(player, "已将玩家 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 0596273c..09f802b6 100644 --- a/gs/game/command_manager.go +++ b/gs/game/command_manager.go @@ -19,12 +19,14 @@ const ( ) // CommandFunc 命令执行函数 -type CommandFunc func(*Command) +type CommandFunc func(*CommandMessage) -// Command 命令结构体 +// CommandMessage 命令消息 // 给下层执行命令时提供数据 -type Command struct { - Executor *model.Player // 执行者 +type CommandMessage struct { + // executor 玩家为 model.Player 类型 + // GM等为 string 类型 + Executor any // 执行者 Text string // 命令原始文本 Name string // 命令前缀 Args map[string]string // 命令参数 @@ -35,7 +37,7 @@ type CommandManager struct { system *model.Player // 机器人 目前负责收发消息 以及 大世界 commandFuncRouter map[string]CommandFunc // 记录命令处理函数 commandPermMap map[string]CommandPerm // 记录命令对应的权限 - commandTextInput chan *Command // 传输要处理的命令文本 + commandTextInput chan *CommandMessage // 传输要处理的命令文本 gameManager *GameManager } @@ -53,7 +55,7 @@ func NewCommandManager(g *GameManager) *CommandManager { g.worldManager.InitBigWorld(r.system) // 初始化 - r.commandTextInput = make(chan *Command, 1000) + r.commandTextInput = make(chan *CommandMessage, 1000) r.InitRouter() // 初始化路由 r.gameManager = g @@ -105,7 +107,7 @@ func (c *CommandManager) HasCommand(cmdName string) bool { } // InputCommand 输入要处理的命令 -func (c *CommandManager) InputCommand(executor *model.Player, text string) { +func (c *CommandManager) InputCommand(executor any, text string) { // 机器人不会读命令所以写到了 PrivateChatReq // 确保消息文本为 / 开头 @@ -113,10 +115,52 @@ func (c *CommandManager) InputCommand(executor *model.Player, text string) { if strings.HasPrefix(text, "/") { // 输入的命令将在其他协程中处理 - c.commandTextInput <- c.NewCommand(executor, text) + c.commandTextInput <- &CommandMessage{Executor: executor, Text: text} } } +// HandleCommand 处理命令 +// 主协程接收到命令消息后执行 +func (c *CommandManager) HandleCommand(cmd *CommandMessage) { + // 将开头的 / 去掉 并 分割出命令的每个参数 + // 不区分命令的大小写 统一转为小写 + cmdSplit := strings.Split(strings.ToLower(cmd.Text[1:]), " -") + + // 分割出来啥也没有可能是个空的字符串 + // 此时将会返回的命令名和命令参数都为空 + if len(cmdSplit) == 0 { + return + } + + // 命令参数 初始化 + cmd.Args = make(map[string]string, len(cmdSplit)-1) + + // 首个参数必是命令名 + cmd.Name = cmdSplit[0] + // 命令名后当然是命令的参数喽 + argSplit := cmdSplit[1:] + + // 我们要将命令的参数转换为键值对 + // 每个参数之间会有个空格分割 + for _, s := range argSplit { + cmdArg := strings.Split(s, " ") + + // 分割出来的参数只有一个那肯定不是键值对 + if len(cmdArg) < 2 { + logger.LOG.Debug("command arg error, arg: %v", cmdArg) + } + + argKey := cmdArg[0] // 参数的键 + argValue := cmdArg[1] // 参数的值 + + // 记录命令的参数 + cmd.Args[argKey] = argValue + } + + // 执行命令 + c.ExecCommand(cmd) +} + // GetFriendList 获取包含系统的玩家好友列表 func (c *CommandManager) GetFriendList(friendList map[uint32]bool) map[uint32]bool { // 可能还有更好的方法实现这功能 @@ -134,54 +178,16 @@ func (c *CommandManager) GetFriendList(friendList map[uint32]bool) map[uint32]bo return tempFriendList } -// NewCommand 创建命令结构 -func (c *CommandManager) NewCommand(executor *model.Player, text string) *Command { - // 将开头的 / 去掉 并 分割出命令的每个参数 - // 不区分命令的大小写 统一转为小写 - cmdSplit := strings.Split(strings.ToLower(text[1:]), " -") - - cmdName := "" // 命令名 - cmdArgs := make(map[string]string, len(cmdSplit)-1) // 命令参数 - - // 分割出来啥也没有可能是个空的字符串 - // 此时将会返回的命令名和命令参数都为空 - if len(cmdSplit) != 0 { - // 首个参数必是命令名 - cmdName = cmdSplit[0] - // 命令名后当然是命令的参数喽 - 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} -} - // ExecCommand 执行命令 -func (c *CommandManager) ExecCommand(cmd *Command) { - player := cmd.Executor +func (c *CommandManager) ExecCommand(cmd *CommandMessage) { + executor := cmd.Executor // 判断命令是否注册 cmdFunc, ok := c.commandFuncRouter[cmd.Name] if !ok { // 玩家可能会执行一些没有的命令仅做调试输出 logger.LOG.Debug("exec command not exist, name: %v", cmd.Name) - c.gameManager.SendPrivateChat(c.system, player, "命令不存在,输入 /help 查看帮助。") + c.SendMessage(executor, "命令不存在,输入 /help 查看帮助。") return } // 判断命令权限是否注册 @@ -192,14 +198,35 @@ func (c *CommandManager) ExecCommand(cmd *Command) { return } + // 执行者是否为玩家 + player, ok := executor.(*model.Player) // 判断玩家的权限是否符合要求 - if player.IsGM < uint8(cmdPerm) { + if ok && player.IsGM < uint8(cmdPerm) { logger.LOG.Debug("exec command permission denied, uid: %v, isGM: %v", player.PlayerID, player.IsGM) - c.gameManager.SendPrivateChat(c.system, player, fmt.Sprintf("权限不足,该命令需要%v级权限。\n你目前的权限等级:%v", cmdPerm, player.IsGM)) + c.SendMessage(player, "权限不足,该命令需要%v级权限。\n你目前的权限等级:%v", cmdPerm, player.IsGM) return } - logger.LOG.Debug("command start, uid: %v, text: %v, name: %v, args: %v", cmd.Executor.PlayerID, cmd.Text, cmd.Name, cmd.Args) + logger.LOG.Debug("command start, name: %v, args: $v", cmd.Name, cmd.Args) cmdFunc(cmd) // 执行命令 - logger.LOG.Debug("command done, uid: %v, text: %v, name: %v, args: %v", cmd.Executor.PlayerID, cmd.Text, cmd.Name, cmd.Args) + logger.LOG.Debug("command done, name: %v, args: $v", cmd.Name, cmd.Args) +} + +// SendMessage 发送消息 +func (c *CommandManager) SendMessage(executor any, msg string, param ...any) { + game := c.gameManager + + // 根据相应的类型发送消息 + switch executor.(type) { + case *model.Player: + // 玩家类型 + player := executor.(*model.Player) + game.SendPrivateChat(c.system, player, fmt.Sprintf(msg, param...)) + case string: + // GM接口等 + //str := executor.(string) + default: + // 无效的类型报错 + logger.LOG.Error("command executor type error, type: %T", executor) + } } diff --git a/gs/game/game_manager.go b/gs/game/game_manager.go index 9b2b7554..0631dc2b 100644 --- a/gs/game/game_manager.go +++ b/gs/game/game_manager.go @@ -64,7 +64,7 @@ func (g *GameManager) Start() { g.localEventManager.LocalEventHandle(localEvent) case command := <-g.commandManager.commandTextInput: // 处理传入的命令 (普通玩家 GM命令) - g.commandManager.ExecCommand(command) + g.commandManager.HandleCommand(command) } } }()