mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-07 05:52:30 +08:00
优化关闭服务器以及用户重连
添加命令管理器
This commit is contained in:
153
gs/game/command_manager.go
Normal file
153
gs/game/command_manager.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"hk4e/gs/model"
|
||||
"hk4e/logger"
|
||||
"hk4e/protocol/proto"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CommandFunc func(string, []string)
|
||||
|
||||
type Command struct {
|
||||
Name string // 命令前缀
|
||||
Args []string // 命令参数
|
||||
}
|
||||
|
||||
type CommandManager struct {
|
||||
system *model.Player // 机器人 目前负责收发命令 以及 大世界
|
||||
commandFuncRouter map[string]CommandFunc // 记录命令处理函数
|
||||
commandTextInput chan string // 传输要处理的命令文本
|
||||
|
||||
gameManager *GameManager
|
||||
}
|
||||
|
||||
// NewCommandManager 新建命令管理器
|
||||
func NewCommandManager(g *GameManager) *CommandManager {
|
||||
r := new(CommandManager)
|
||||
|
||||
// 创建一个公共的开放世界的AI
|
||||
g.OnRegOk(false, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: "System"}, 1, 0)
|
||||
r.system = g.userManager.GetOnlineUser(1)
|
||||
// 开放大世界
|
||||
r.system.SceneLoadState = model.SceneEnterDone
|
||||
r.system.DbState = model.DbNormal
|
||||
g.worldManager.InitBigWorld(r.system)
|
||||
|
||||
// 初始化
|
||||
r.commandTextInput = make(chan string, 1000)
|
||||
r.InitRouter() // 初始化路由
|
||||
|
||||
// 处理传入的命令
|
||||
go r.HandleCommand()
|
||||
|
||||
r.gameManager = g
|
||||
return r
|
||||
}
|
||||
|
||||
// InitRouter 初始化命令路由
|
||||
func (c *CommandManager) InitRouter() {
|
||||
c.commandFuncRouter = make(map[string]CommandFunc)
|
||||
c.RegisterRouter("awa", c.FuncAwA)
|
||||
}
|
||||
|
||||
// RegisterRouter 注册命令路由
|
||||
func (c *CommandManager) RegisterRouter(cmdName string, cmdFunc CommandFunc) {
|
||||
c.commandFuncRouter[cmdName] = cmdFunc
|
||||
}
|
||||
|
||||
func (c *CommandManager) FuncAwA(cmd string, args []string) {
|
||||
logger.LOG.Info("awa命令执行啦, name: %v, args: %v", cmd, args)
|
||||
}
|
||||
|
||||
// InputCommand 输入要处理的命令
|
||||
func (c *CommandManager) InputCommand(text string) {
|
||||
// 机器人不会读命令所以写到了 PrivateChatReq
|
||||
|
||||
// 输入的命令将在其他协程中处理
|
||||
c.commandTextInput <- text
|
||||
}
|
||||
|
||||
// GetFriendList 获取包含系统的玩家好友列表
|
||||
func (c *CommandManager) GetFriendList(friendList map[uint32]bool) map[uint32]bool {
|
||||
// 可能还有更好的方法实现这功能
|
||||
// 但我想不出来awa
|
||||
|
||||
// 临时好友列表
|
||||
tempFriendList := make(map[uint32]bool, len(friendList))
|
||||
// 复制玩家的好友列表
|
||||
for userId, b := range friendList {
|
||||
tempFriendList[userId] = b
|
||||
}
|
||||
// 添加系统
|
||||
tempFriendList[c.system.PlayerID] = true
|
||||
|
||||
return tempFriendList
|
||||
}
|
||||
|
||||
// HandleCommand 处理命令
|
||||
func (c *CommandManager) HandleCommand() {
|
||||
// 处理传入 commandTextInput 的所有命令文本
|
||||
// 为了避免主协程阻塞搞了个channel
|
||||
|
||||
for {
|
||||
// 取出要处理的命令文本
|
||||
text := <-c.commandTextInput
|
||||
|
||||
// 读取并创建命令
|
||||
cmd := c.NewCommand(text)
|
||||
if cmd == nil {
|
||||
logger.LOG.Error("handle command is nil, text: %v", text)
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
c.ExecCommand(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// NewCommand 创建命令结构
|
||||
func (c *CommandManager) NewCommand(text string) *Command {
|
||||
// 命令必须以 / 为开头
|
||||
if !strings.HasPrefix(text, "/") {
|
||||
return nil
|
||||
}
|
||||
// 将开头的 / 去掉
|
||||
text = text[1:]
|
||||
|
||||
// 分割出命令的每个参数
|
||||
cmdSplit := strings.Split(text, " ")
|
||||
|
||||
// 分割出来啥也没有可能是个空的字符串
|
||||
// 处理个寂寞直接return
|
||||
if len(cmdSplit) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 首个参数必是命令名
|
||||
cmdName := cmdSplit[0]
|
||||
// 命令名后当然是命令的参数喽
|
||||
cmdArgs := cmdSplit[1:]
|
||||
|
||||
return &Command{cmdName, cmdArgs}
|
||||
}
|
||||
|
||||
// ExecCommand 执行命令
|
||||
func (c *CommandManager) ExecCommand(cmd *Command) {
|
||||
// 理论上执行前已经校验过不会出现nil 但以免万一
|
||||
if cmd == nil {
|
||||
logger.LOG.Error("exec command is nil")
|
||||
return
|
||||
}
|
||||
|
||||
// 判断命令名是否注册
|
||||
cmdFunc, ok := c.commandFuncRouter[cmd.Name]
|
||||
if !ok {
|
||||
logger.LOG.Error("exec command not exist, name: %v", cmd.Name)
|
||||
return
|
||||
}
|
||||
|
||||
logger.LOG.Debug("command start, name: %v args: %v", cmd.Name, cmd.Args)
|
||||
cmdFunc(cmd.Name, cmd.Args) // 执行命令
|
||||
logger.LOG.Debug("command done, name: %v args: %v", cmd.Name, cmd.Args)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"hk4e/gate/entity/gm"
|
||||
"hk4e/gate/kcp"
|
||||
"hk4e/gs/dao"
|
||||
"hk4e/gs/model"
|
||||
"hk4e/logger"
|
||||
"hk4e/protocol/cmd"
|
||||
"hk4e/protocol/proto"
|
||||
@@ -27,6 +26,8 @@ type GameManager struct {
|
||||
worldManager *WorldManager
|
||||
// 游戏服务器定时帧管理器
|
||||
tickManager *TickManager
|
||||
// 命令管理器
|
||||
commandManager *CommandManager
|
||||
}
|
||||
|
||||
func NewGameManager(dao *dao.Dao, netMsgInput chan *cmd.NetMsg, netMsgOutput chan *cmd.NetMsg) (r *GameManager) {
|
||||
@@ -40,13 +41,7 @@ func NewGameManager(dao *dao.Dao, netMsgInput chan *cmd.NetMsg, netMsgOutput cha
|
||||
r.userManager = NewUserManager(dao, r.localEventManager.localEventChan)
|
||||
r.worldManager = NewWorldManager(r.snowflake)
|
||||
r.tickManager = NewTickManager(r)
|
||||
|
||||
// 创建一个公共的开放世界的AI
|
||||
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)
|
||||
r.commandManager = NewCommandManager(r)
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -72,7 +67,14 @@ func (g *GameManager) Start() {
|
||||
}
|
||||
|
||||
func (g *GameManager) Stop() {
|
||||
g.worldManager.worldStatic.SaveTerrain()
|
||||
// 踢出所有在线玩家
|
||||
for userId := range g.userManager.GetAllOnlineUserList() {
|
||||
g.DisconnectPlayer(userId)
|
||||
}
|
||||
// 保存玩家数据
|
||||
g.userManager.SaveUser()
|
||||
|
||||
//g.worldManager.worldStatic.SaveTerrain()
|
||||
}
|
||||
|
||||
// 发送消息给客户端
|
||||
|
||||
@@ -103,6 +103,8 @@ func (g *GameManager) PrivateChatReq(player *model.Player, payloadMsg pb.Message
|
||||
chatInfo.Content = &proto.ChatInfo_Text{
|
||||
Text: text,
|
||||
}
|
||||
// 输入命令 会检测是否为命令的
|
||||
g.commandManager.InputCommand(text)
|
||||
case *proto.PrivateChatReq_Icon:
|
||||
icon := content.(*proto.PrivateChatReq_Icon).Icon
|
||||
chatInfo.Content = &proto.ChatInfo_Icon{
|
||||
|
||||
@@ -241,60 +241,64 @@ 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")
|
||||
u.SaveUser()
|
||||
<-ticker.C
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *UserManager) SaveUser() {
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -143,7 +143,12 @@ func (g *GameManager) GetPlayerFriendListReq(player *model.Player, payloadMsg pb
|
||||
// PacketGetPlayerFriendListRsp
|
||||
getPlayerFriendListRsp := new(proto.GetPlayerFriendListRsp)
|
||||
getPlayerFriendListRsp.FriendList = make([]*proto.FriendBrief, 0)
|
||||
for uid := range player.FriendList {
|
||||
|
||||
// 获取包含系统的临时好友列表
|
||||
// 用于实现好友列表内的系统且不更改原先的内容
|
||||
tempFriendList := g.commandManager.GetFriendList(player.FriendList)
|
||||
|
||||
for uid := range tempFriendList {
|
||||
// TODO 同步阻塞待优化
|
||||
var onlineState proto.FriendOnlineState
|
||||
online := g.userManager.GetUserOnlineState(uid)
|
||||
@@ -265,6 +270,17 @@ func (g *GameManager) AskAddFriendReq(player *model.Player, payloadMsg pb.Messag
|
||||
g.SendMsg(cmd.AskAddFriendRsp, player.PlayerID, player.ClientSeq, askAddFriendRsp)
|
||||
}
|
||||
|
||||
func (g *GameManager) AddFriend(player *model.Player, targetUid uint32) {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -272,14 +288,7 @@ func (g *GameManager) DealAddFriendReq(player *model.Player, payloadMsg pb.Messa
|
||||
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
|
||||
g.AddFriend(player, targetUid)
|
||||
}
|
||||
delete(player.FriendApplyList, targetUid)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user