refactor(socket): 重构 ClientData 结构体并优化相关逻辑

- 简化 ClientData 结构体,移除不必要的方法
- 优化 Player 结构体,调整 Conn 类型
- 更新 wscodec.go 中的 Conn 结构体
- 删除未使用的 XML 相关文件和代码
- 调整 ServerEvent 和 controller 中的相关逻辑
This commit is contained in:
2025-08-30 00:36:08 +08:00
parent 1f835c1197
commit 7b5ec208fc
31 changed files with 472 additions and 602 deletions

7
.vscode/launch.json vendored
View File

@@ -13,7 +13,7 @@
"mode": "auto",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/login",
"console": "integratedTerminal"
},
{
@@ -24,7 +24,7 @@
"cwd": "${workspaceFolder}",
"args": ["-port=1"],
"program": "${workspaceFolder}/logic",
"console": "integratedTerminal"
}, {
"name": "Launch logic2",
"type": "go",
@@ -32,6 +32,7 @@
"mode": "auto",
"cwd": "${workspaceFolder}",
"args": ["-port=2"],
"program": "${workspaceFolder}/logic", "console": "integratedTerminal"
},
{
@@ -41,7 +42,7 @@
"mode": "auto",
"cwd": "${workspaceFolder}",
"args": ["-port=0"],
"program": "${workspaceFolder}/logic", "console": "integratedTerminal"
"program": "${workspaceFolder}/logic",
}
]

View File

@@ -1,9 +1,5 @@
package socket
import (
"sync"
)
func ConutPlayer() int {
count := 0
@@ -15,46 +11,19 @@ func ConutPlayer() int {
}
type ClientData struct {
isCrossDomain bool //是否跨域过
player *Player //客户实体
IsCrossDomain bool //是否跨域过
Player *Player //客户实体
//UserID uint32
m sync.Mutex
wsmsg WsCodec
}
func (cd *ClientData) SetPlayer(player *Player) {
cd.m.Lock()
defer cd.m.Unlock()
cd.player = player
}
func (cd *ClientData) GetPlayer() *Player {
cd.m.Lock()
defer cd.m.Unlock()
return cd.player
}
func (cd *ClientData) Getwsmsg() *WsCodec {
cd.m.Lock()
defer cd.m.Unlock()
return &cd.wsmsg
}
func (cd *ClientData) SetCrossDomain(isCrossDomain bool) {
cd.m.Lock()
defer cd.m.Unlock()
cd.isCrossDomain = isCrossDomain
}
func (cd *ClientData) GetIsCrossDomain() bool {
cd.m.Lock()
defer cd.m.Unlock()
return cd.isCrossDomain
Wsmsg *WsCodec
}
func NewClientData() *ClientData {
cd := ClientData{
isCrossDomain: false,
player: nil,
m: sync.Mutex{},
wsmsg: WsCodec{},
IsCrossDomain: false,
Player: nil,
Wsmsg: &WsCodec{},
}
return &cd

View File

@@ -20,7 +20,7 @@ var Mainplayer = &utils.SyncMap[uint32, *Player]{} //玩家数据
func (c *Conn) SendPack(bytes []byte) error {
if t, ok := c.MainConn.Context().(*ClientData); ok {
if t.Getwsmsg().Upgraded {
if t.Wsmsg.Upgraded {
// This is the echo server
err := wsutil.WriteServerMessage(c.MainConn, ws.OpBinary, bytes)
if err != nil {
@@ -41,7 +41,7 @@ func (c *Conn) SendPack(bytes []byte) error {
}
type Player struct {
MainConn Conn
MainConn *Conn
IsLogin bool //是否登录
mu sync.Mutex
@@ -58,7 +58,7 @@ type Player struct {
// PlayerOption 定义配置 Player 的函数类型
type PlayerOption func(*Player)
func WithConn(c Conn) PlayerOption {
func WithConn(c *Conn) PlayerOption {
return func(p *Player) {
p.MainConn = c
}
@@ -76,7 +76,7 @@ func NewPlayer(opts ...PlayerOption) *Player {
}
func (p *Player) SendPack(b []byte) error {
fmt.Println("发送数据包", len(b))
// fmt.Println("发送数据包", len(b))
err := p.MainConn.SendPack(b)
return err
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"io"
"sync"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
@@ -13,6 +14,7 @@ import (
type Conn struct {
MainConn gnet.Conn `struc:"[0]pad"` //TODO 不序列化,,序列化下面的作为blob存数据库
Mu sync.Mutex
}
func NewConn(c gnet.Conn) *Conn {

View File

@@ -1,20 +0,0 @@
package xml
import (
"fmt"
"testing"
"github.com/ECUST-XX/xml"
)
func Test_main(t *testing.T) {
// 解析XML到结构体
var maps Items
t1, _ := getItemsXML()
xml.Unmarshal(t1, &maps)
//tf, _ := xml.MarshalIndentShortForm(tt, " ", " ")
fmt.Println(maps)
}

View File

@@ -1,106 +0,0 @@
package xml
import (
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/ECUST-XX/xml" // 保持与之前一致的XML库
)
// Items 根节点,对应<Items>标签
type Items struct {
XMLName xml.Name `xml:"Items"`
Items []Item `xml:"Item"` // 包含所有物品配置
}
// Item 单个物品配置,对应<Item>标签
type Item struct {
ID int `xml:"ID,attr"` // 物品ID与items.xml一致
Name string `xml:"Name,attr"` // 物品名称
Rarity int `xml:"Rarity,attr,omitempty"` // 稀有度(可选)
Price int `xml:"Price,attr"` // 价格
Tradability int `xml:"Tradability,attr"` // 可交易性0/1
VipTradability int `xml:"VipTradability,attr"` // VIP可交易性0/1
DailyKey int `xml:"DailyKey,attr,omitempty"` // 每日限制键值(可选)
DailyOutMax int `xml:"DailyOutMax,attr,omitempty"` // 每日最大产出(可选)
Wd int `xml:"wd,attr"` // 未知属性根据原XML保留
UseMax int `xml:"UseMax,attr"` // 最大使用次数
LifeTime int `xml:"LifeTime,attr"` // 生命周期0为永久
Purpose int `xml:"purpose,attr"` // 用途标识
Bean int `xml:"Bean,attr,omitempty"` // 豆子数量(可选)
Hide int `xml:"Hide,attr"` // 是否隐藏0/1
Sort int `xml:"Sort,attr,omitempty"` // 排序序号(可选)
Des string `xml:"des,attr"` // 物品用途(根据说明补充)
Color string `xml:"color,attr,omitempty"` // 装备名字颜色(可选)
Level int `xml:"level,attr,omitempty"` // 装备等级(星星,可选)
Pet *Pet `xml:"pet,omitempty"` // 精灵属性子节点(可选)
TeamPK *TeamPK `xml:"teamPK,omitempty"` // 要塞保卫战子节点(可选)
}
// Pet 精灵属性子节点,对应<pet>标签
// 注:根据实际需求补充字段,这里以常见属性为例
type Pet struct {
Attack int `xml:"attack,attr,omitempty"` // 攻击加成
Defense int `xml:"defense,attr,omitempty"` // 防御加成
HP int `xml:"hp,attr,omitempty"` // 生命值加成
Speed int `xml:"speed,attr,omitempty"` // 速度加成
}
// TeamPK 要塞保卫战子节点,对应<teamPK>标签
// 注:根据实际需求补充字段,这里以常见属性为例
type TeamPK struct {
FortBonus int `xml:"fortBonus,attr,omitempty"` // 要塞加成
DefenseBonus int `xml:"defenseBonus,attr,omitempty"` // 防御加成
AttackBonus int `xml:"attackBonus,attr,omitempty"` // 攻击加成
}
// 复用HTTP文件读取函数
func ReadHTTPFile(url string) ([]byte, error) {
client := &http.Client{
Timeout: 30 * time.Second,
}
resp, err := client.Get(url)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("请求返回非成功状态码: %d", resp.StatusCode)
}
content, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取内容失败: %w", err)
}
return content, nil
}
// 获取物品XML内容
func getItemsXML() ([]byte, error) {
// 替换为实际的Items XML文件URL
content, err := ReadHTTPFile("http://127.0.0.1:8080/assets/43.xml")
if err != nil {
log.Fatalf("无法读取物品文件: %v", err)
}
return content, nil
}
// 解析XML到结构体
func getItems() Items {
var items Items
content, _ := getItemsXML()
if err := xml.Unmarshal(content, &items); err != nil {
log.Fatalf("物品XML解析失败: %v", err)
}
return items
}
// 全局物品配置变量
var ItemsConfig = getItems()

View File

@@ -1,20 +0,0 @@
package skill
import (
"fmt"
"testing"
"github.com/ECUST-XX/xml"
)
func Test_main(t *testing.T) {
// 解析XML到结构体
var maps MovesTbl
t1, _ := getxml()
xml.Unmarshal(t1, &maps)
//tf, _ := xml.MarshalIndentShortForm(tt, " ", " ")
fmt.Println(maps.Moves[4].SideEffect)
fmt.Println(maps.Moves[4].SideEffectArg)
}

View File

@@ -1,20 +0,0 @@
package xml
import (
"fmt"
"testing"
"github.com/ECUST-XX/xml"
)
func Test_main(t *testing.T) {
// 解析XML到结构体
var maps TalkCount
t1, _ := getxml()
xml.Unmarshal(t1, &maps)
//tf, _ := xml.MarshalIndentShortForm(tt, " ", " ")
fmt.Println(maps)
}

View File

@@ -1,89 +0,0 @@
package xml
import (
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/ECUST-XX/xml" // 注意需确保该xml库与示例中使用的一致
)
func getxml() ([]byte, error) {
// 读取整个文件内容,返回字节切片和错误
content, err := ReadHTTPFile("http://127.0.0.1:8080/assets/talk_count.xml")
if err != nil {
// 处理错误(文件不存在、权限问题等)
log.Fatalf("无法读取文件: %v", err)
}
return content, nil
}
// TalkCount 根节点,对应<talk_count>标签
// 注意xml.Name需指定命名空间与XML中的xmlns保持一致
type TalkCount struct {
XMLName xml.Name `xml:"nieo.seer.org.talk-count talk_count"`
Entries []Entry `xml:"entry"` // 包含所有entry节点
}
// Entry 单个条目配置,对应<entry>标签
type Entry struct {
ID int `xml:"id,attr"` // 条目ID
ItemID int `xml:"item_id,attr"` // 物品ID
ItemMinCount int `xml:"item_min_count,attr"` // 物品最小数量
ItemMaxCount int `xml:"item_max_count,attr"` // 物品最大数量
Desc string `xml:"desc,attr"` // 描述信息
Count int `xml:"count,attr"` // 计数
Policy string `xml:"policy,attr,omitempty"` // 策略可选如week
}
// 复用示例中的ReadHTTPFile函数读取远程文件
func ReadHTTPFile(url string) ([]byte, error) {
client := &http.Client{
Timeout: 30 * time.Second,
}
resp, err := client.Get(url)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("请求返回非成功状态码: %d", resp.StatusCode)
}
content, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取内容失败: %w", err)
}
return content, nil
}
// 获取XML内容可根据实际URL修改
func getTalkCountXML() ([]byte, error) {
// 注意此处URL需替换为实际的talk_count.xml文件地址
content, err := ReadHTTPFile("http://127.0.0.1:8080/assets/talk_count.xml")
if err != nil {
log.Fatalf("无法读取文件: %v", err)
}
return content, nil
}
// 解析XML到结构体
func getTalkCount() TalkCount {
var talkCount TalkCount
content, _ := getTalkCountXML()
// 解析XML时需注意命名空间匹配
if err := xml.Unmarshal(content, &talkCount); err != nil {
log.Fatalf("XML解析失败: %v", err)
}
return talkCount
}
// 全局配置变量,用于存储解析后的结果
var TalkCountConfig = getTalkCount()

View File

@@ -0,0 +1,84 @@
package xmlres
import (
"os"
"github.com/ECUST-XX/xml"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gfsnotify"
"github.com/gogf/gf/v2/os/glog"
)
var path string
func getXml[T any](path string) T {
// 解析XML到结构体
var xmls T
t1 := gfile.GetBytes(path)
xml.Unmarshal(t1, &xmls)
return xmls
}
var (
MapConfig Maps //地图配置
ItemsConfig Items //物品配置
TalkConfig TalkCount //任务配置
Monster MonsterRoot //野怪配置
Skill MovesTbl //技能配置
)
func initfile() {
path1, _ := os.Getwd()
path = path1 + "/public/assets/"
MapConfig = getXml[Maps](path + "210.xml")
ItemsConfig = getXml[Items](path + "43.xml")
TalkConfig = getXml[TalkCount](path + "talk.xml")
Monster = getXml[MonsterRoot](path + "地图配置野怪.xml")
Skill = getXml[MovesTbl](path + "227.xml")
}
func init() {
initfile() //先初始化一次
go func() {
if !gfile.Exists(path) {
fileHandle, _ := gfile.Create(path)
fileHandle.Close()
}
//updatersares()
ctx := gctx.New()
_, err := gfsnotify.Add(path, func(event *gfsnotify.Event) {
if event.IsCreate() {
glog.Debug(ctx, "创建文件 : ", event.Path)
}
if event.IsWrite() {
glog.Debug(ctx, "写入文件 : ", event.Path)
initfile() //先初始化一次
}
if event.IsRemove() {
glog.Debug(ctx, "删除文件 : ", event.Path)
}
if event.IsRename() {
glog.Debug(ctx, "重命名文件 : ", event.Path)
}
if event.IsChmod() {
glog.Debug(ctx, "修改权限 : ", event.Path)
}
glog.Debug(ctx, event)
}, true)
if err != nil {
glog.Fatal(ctx, err)
} else {
select {}
}
}()
}

View File

@@ -0,0 +1,53 @@
package xmlres
import (
"github.com/ECUST-XX/xml" // 保持与之前一致的XML库
)
// Items 根节点,对应<Items>标签
type Items struct {
XMLName xml.Name `xml:"Items"`
Items []Item `xml:"Item"` // 包含所有物品配置
}
// Item 单个物品配置,对应<Item>标签
type Item struct {
ID int `xml:"ID,attr"` // 物品ID与items.xml一致
Name string `xml:"Name,attr"` // 物品名称
Rarity int `xml:"Rarity,attr,omitempty"` // 稀有度(可选)
Price int `xml:"Price,attr"` // 价格
Tradability int `xml:"Tradability,attr"` // 可交易性0/1
VipTradability int `xml:"VipTradability,attr"` // VIP可交易性0/1
DailyKey int `xml:"DailyKey,attr,omitempty"` // 每日限制键值(可选)
DailyOutMax int `xml:"DailyOutMax,attr,omitempty"` // 每日最大产出(可选)
Wd int `xml:"wd,attr"` // 未知属性根据原XML保留
UseMax int `xml:"UseMax,attr"` // 最大使用次数
LifeTime int `xml:"LifeTime,attr"` // 生命周期0为永久
Purpose int `xml:"purpose,attr"` // 用途标识
Bean int `xml:"Bean,attr,omitempty"` // 豆子数量(可选)
Hide int `xml:"Hide,attr"` // 是否隐藏0/1
Sort int `xml:"Sort,attr,omitempty"` // 排序序号(可选)
Des string `xml:"des,attr"` // 物品用途(根据说明补充)
Color string `xml:"color,attr,omitempty"` // 装备名字颜色(可选)
Level int `xml:"level,attr,omitempty"` // 装备等级(星星,可选)
Pet *Pet `xml:"pet,omitempty"` // 精灵属性子节点(可选)
TeamPK *TeamPK `xml:"teamPK,omitempty"` // 要塞保卫战子节点(可选)
}
// Pet 精灵属性子节点,对应<pet>标签
// 注:根据实际需求补充字段,这里以常见属性为例
type Pet struct {
Attack int `xml:"attack,attr,omitempty"` // 攻击加成
Defense int `xml:"defense,attr,omitempty"` // 防御加成
HP int `xml:"hp,attr,omitempty"` // 生命值加成
Speed int `xml:"speed,attr,omitempty"` // 速度加成
}
// TeamPK 要塞保卫战子节点,对应<teamPK>标签
// 注:根据实际需求补充字段,这里以常见属性为例
type TeamPK struct {
FortBonus int `xml:"fortBonus,attr,omitempty"` // 要塞加成
DefenseBonus int `xml:"defenseBonus,attr,omitempty"` // 防御加成
AttackBonus int `xml:"attackBonus,attr,omitempty"` // 攻击加成
}

View File

@@ -1,12 +1,6 @@
package xml
package xmlres
import (
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/ECUST-XX/xml"
)
@@ -68,56 +62,3 @@ type Component struct {
Des string `xml:"des,attr,omitempty"` // 鼠标提示文本(可选)
IsStop int `xml:"isStop,attr,omitempty"` // 鼠标悬停是否跳帧0/1可选
}
// ReadHTTPFile 通过HTTP GET请求获取远程文件内容
// url: 远程文件的URL地址
// 返回文件内容字节流和可能的错误
func ReadHTTPFile(url string) ([]byte, error) {
// 创建HTTP客户端并设置超时时间避免无限等待
client := &http.Client{
Timeout: 30 * time.Second, // 30秒超时
}
// 发送GET请求
resp, err := client.Get(url)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close() // 确保响应体被关闭
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("请求返回非成功状态码: %d", resp.StatusCode)
}
// 读取响应体内容
content, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取内容失败: %w", err)
}
return content, nil
}
func getxml() ([]byte, error) {
// 读取整个文件内容,返回字节切片和错误
content, err := ReadHTTPFile("http://127.0.0.1:8080/assets/210.xml")
if err != nil {
// 处理错误(文件不存在、权限问题等)
log.Fatalf("无法读取文件: %v", err)
}
return content, nil
}
func getMaps() Maps {
// 解析XML到结构体
var maps Maps
t1, _ := getxml()
xml.Unmarshal(t1, &maps)
return maps
}
// 全局函数配置
var MapConfig = getMaps()

122
common/data/xmlres/mon.go Normal file
View File

@@ -0,0 +1,122 @@
package xmlres
import (
"encoding/xml"
)
// 根结构体:对应 <Maps> 节点
type MonsterRoot struct {
XMLName xml.Name `xml:"Maps"`
WorldWildMons WorldWildMons `xml:"WorldWildMons"` // 世界野怪配置
Maps []TMapConfig `xml:"Map"` // 所有地图配置(多个<Map>
DefaultMaps DefaultMaps `xml:"DefaultMaps"` // 默认登录场景
VersionNumber VersionNumber `xml:"VersionNumber"` // 版本信息
}
// 世界野怪容器:对应 <WorldWildMons>
type WorldWildMons struct {
WorldWildMonsters []WorldWildMonster `xml:"WorldWildMonster"` // 多个<WorldWildMonster>
}
// 世界野怪配置:对应 <WorldWildMonster>
type WorldWildMonster struct {
WorldWildProb int `xml:"WorldWildProb,attr"` // 变身概率(分子)
WorldWildMonId int `xml:"WorldWildMonId,attr"` // 野怪ID
WorldWildMonLv int `xml:"WorldWildMonLv,attr"` // 野怪等级
WorldWildStart int `xml:"WorldWildStart,attr"` // 出现起始时间0-23
WorldWildEnd int `xml:"WorldWildEnd,attr"` // 出现结束时间0-23
NewSeIdxs string `xml:"NewSeIdxs,attr"` // 特效ID可能多个空格分隔
}
// 单张地图配置:对应 <Map>
type TMapConfig struct {
ID int `xml:"ID,attr"` // 地图ID
Name string `xml:"Name,attr"` // 地图名称
InitX int `xml:"InitX,attr"` // 玩家初始X坐标
InitY int `xml:"InitY,attr"` // 玩家初始Y坐标
GameTriggerGrps []GameTriggerGrp `xml:"GameTriggerGrp"` // 触发点组(多个)
Monsters *MonstersC `xml:"Monsters"` // 新增:野怪配置(可选,用指针处理“无该节点”的情况)
Bosses []BossConfig `xml:"Bosses>Boss"` // BOSS配置<Bosses>下的<Boss>
}
// ########################### 关键新增MonstersConfig对应 <Monsters> ###########################
// 野怪配置容器:包含野怪奖励概率和多只野怪
type MonstersC struct {
WildBonusProb int `xml:"WildBonusProb,attr"` // 打赢野怪给奖励的概率分子如300
WildBonusTotalProb int `xml:"WildBonusTotalProb,attr"` // 打赢野怪给奖励的概率分母如1000
BonusID int `xml:"BonusID,attr"` // 奖励ID如5239
ItemBonusID int `xml:"ItemBonusID,attr"` // 物品奖励ID如1
Monsters []Monster1 `xml:"Monster"` // 多只野怪(<Monster>子节点)
}
// ########################### 关键新增Monster对应 <Monster> ###########################
// 单只野怪配置ID可能是多个值空格分隔Lv是等级范围空格分隔
type Monster1 struct {
ID string `xml:"ID,attr"` // 野怪ID如"164 0 0..."或"10"
Lv string `xml:"Lv,attr"` // 等级范围(如"1 2"表示1-2级
}
// 游戏触发点组:对应 <GameTriggerGrp>
type GameTriggerGrp struct {
GameID string `xml:"GameID,attr"` // 游戏ID
TriggerPts []TriggerPt `xml:"TriggerPt"` // 触发点(多个)
}
// 触发点:对应 <TriggerPt>
type TriggerPt struct {
ID int `xml:"ID,attr"` // 触发点ID
Name string `xml:"Name,attr"` // 触发点名称(可选)
}
// BOSS配置对应 <Boss>
type BossConfig struct {
Id *int `xml:"Id,attr"` // BOSSID可选用指针处理空值
BossCatchable int `xml:"BossCatchable,attr"` // 是否可捕捉0/1默认0
AppearTime string `xml:"AppearTime,attr"` // 出现时间(如"0 23"
BossVisible int `xml:"BossVisible,attr"` // 是否可见0/1默认0
Name string `xml:"Name,attr"` // BOSS名称可选
DailyKey *string `xml:"DailyKey,attr"` // 每日挑战次数Key可选
MaxTimes *int `xml:"MaxTimes,attr"` // 非VIP每日挑战上限可选
VipMaxTimes *int `xml:"VipMaxTimes,attr"` // VIP每日挑战上限可选
WinBonusId *string `xml:"WinBonusId,attr"` // 胜利奖励ID可选
WinOutId *int `xml:"WinOutId,attr"` // 胜利输出ID可选
FailBonusId *string `xml:"FailBonusId,attr"` // 失败奖励ID可选
FailOutId *int `xml:"FailOutId,attr"` // 失败输出ID可选
BossMon []BossMon `xml:"BossMon"` // BOSS对应的精灵多个
}
// BOSS精灵配置对应 <BossMon>
type BossMon struct {
MonID string `xml:"MonID,attr"` // 精灵ID可能多个空格分隔
Hp int `xml:"Hp,attr"` // 生命值
Lv int `xml:"Lv,attr"` // 等级
NewSeIdxs string `xml:"NewSeIdxs,attr"` // 特效ID可选空格分隔
Atk *int `xml:"Atk,attr"` // 攻击(可选)
Def *int `xml:"Def,attr"` // 防御(可选)
Spatk *int `xml:"Spatk,attr"` // 特攻(可选)
Spdef *int `xml:"Spdef,attr"` // 特防(可选)
Spd *int `xml:"Spd,attr"` // 速度(可选)
}
// 默认登录场景容器:对应 <DefaultMaps>
type DefaultMaps struct {
DefaultMaps []DefaultMap `xml:"DefaultMap"` // 多个默认场景
}
// 默认登录场景:对应 <DefaultMap>
type DefaultMap struct {
ID int `xml:"ID,attr"` // 场景ID
RandMaps string `xml:"RandMaps,attr"` // 随机场景列表(逗号分隔)
Desc string `xml:"Desc,attr"` // 场景描述
}
// 版本信息容器:对应 <VersionNumber>
type VersionNumber struct {
Version Version `xml:"Version"` // 版本详情
}
// 版本详情:对应 <Version>
type Version struct {
ID string `xml:"ID,attr"` // 版本号(如"20190614"
Desc string `xml:"Desc,attr"` // 版本描述
}

View File

@@ -1,4 +1,4 @@
package skill
package xmlres
import (
"encoding/xml"

View File

@@ -0,0 +1,23 @@
package xmlres
import (
"github.com/ECUST-XX/xml" // 注意需确保该xml库与示例中使用的一致
)
// TalkCount 根节点,对应<talk_count>标签
// 注意xml.Name需指定命名空间与XML中的xmlns保持一致
type TalkCount struct {
XMLName xml.Name `xml:"nieo.seer.org.talk-count talk_count"`
Entries []TalkEntry `xml:"entry"` // 包含所有entry节点
}
// Entry 单个条目配置,对应<entry>标签
type TalkEntry struct {
ID int `xml:"id,attr"` // 条目ID
ItemID int `xml:"item_id,attr"` // 物品ID
ItemMinCount int `xml:"item_min_count,attr"` // 物品最小数量
ItemMaxCount int `xml:"item_max_count,attr"` // 物品最大数量
Desc string `xml:"desc,attr"` // 描述信息
Count int `xml:"count,attr"` // 计数
Policy string `xml:"policy,attr,omitempty"` // 策略可选如week
}

View File

@@ -1,4 +1,4 @@
package xml
package xmlres
import (
"fmt"
@@ -49,13 +49,7 @@ var s = `
func Test_main(t *testing.T) {
// 解析XML到结构体
var maps Maps
t1, _ := getxml()
xml.Unmarshal(t1, &maps)
//tf, _ := xml.MarshalIndentShortForm(tt, " ", " ")
fmt.Println(maps)
initfile()
}

View File

@@ -46,13 +46,13 @@ func (s *Server) OnClose(c gnet.Conn, _ error) (action gnet.Action) {
return
}
t := v.GetPlayer()
if v != nil {
glog.Debug(context.Background(), t, "断开连接")
t.IsLogin = false
socket.Mainplayer.Delete(t.Info.UserID)
share.ShareManager.DeleteUserOnline(t.Info.UserID) //设置用户登录服务器
t.Save() //保存玩家数据
if v.Player != nil {
glog.Debug(context.Background(), v.Player.Info.UserID, "断开连接")
v.Player.IsLogin = false
socket.Mainplayer.Delete(v.Player.Info.UserID)
share.ShareManager.DeleteUserOnline(v.Player.Info.UserID) //设置用户登录服务器
v.Player.Save() //保存玩家数据
}
//}
@@ -86,7 +86,7 @@ func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
return gnet.Close
}
ws := c.Context().(*socket.ClientData).Getwsmsg()
ws := c.Context().(*socket.ClientData).Wsmsg
tt, len1 := ws.ReadBufferBytes(c)
if tt == gnet.Close {
@@ -166,7 +166,7 @@ const TEXT = "<policy-file-request/>\x00"
func handle(c gnet.Conn) {
clientdata := c.Context().(*socket.ClientData)
if clientdata.GetIsCrossDomain() {
if clientdata.IsCrossDomain {
return
}
@@ -183,7 +183,7 @@ func handle(c gnet.Conn) {
c.Write([]byte(CROSS_DOMAIN))
c.Discard(len(TEXT))
clientdata.SetCrossDomain(true) //= true //TODO 待修复未成功切换bug
clientdata.IsCrossDomain = true
return
}

View File

@@ -88,7 +88,7 @@ func init() { //默认初始化扫描
method := typ.Method(i)
methodValue := value.MethodByName(method.Name)
fmt.Println("找到注册方法", method.Name)
//fmt.Println("找到注册方法", method.Name)
methodValue.Type().NumIn()
for _, func_cmd := range getcmd(methodValue.Type().In(0)) {
@@ -190,13 +190,14 @@ func Recv(c *socket.Conn, data handler.TomeeHeader) {
nameField.Set(reflect.ValueOf(data))
}
if cmdlister.Type().In(1) == reflect.TypeOf(&socket.Player{}) {
c1 := service.GetPlayer(c, data.UserID)
err := c1.WaitForLoginWithCtx(context.Background())
t := service.GetPlayer(c, data.UserID)
// fmt.Println(data.CMD, "接收 变量的地址 ", &t.Info, t.Info.UserID)
err := t.WaitForLoginWithCtx(context.Background())
if err != nil {
fmt.Println("登录失败")
}
params = append(params, ptrValue1, reflect.ValueOf(c1))
params = append(params, ptrValue1, reflect.ValueOf(t))
} else {
params = append(params, ptrValue1, reflect.ValueOf(c))
@@ -225,7 +226,7 @@ func Recv(c *socket.Conn, data handler.TomeeHeader) {
}
data.Version = "7"
glog.Debug(context.Background(), data.CMD, "回复数据")
//glog.Debug(context.Background(), data.CMD, "回复数据")
c.SendPack(data.Pack(ret[0].Interface()))
}

View File

@@ -14,7 +14,6 @@ import (
"time"
"github.com/gogf/gf/v2/os/glog"
"github.com/jinzhu/copier"
)
// 处理命令: 1001
@@ -22,22 +21,23 @@ func (h *Controller) Login(data *login.InInfo, c *socket.Conn) (result *login.Ou
if tt := data.CheakSession(); tt { //说明sid正确
h.RPCClient.Kick(data.Head.UserID) //先踢人
playerinfo := blservice.NewUserService(data.Head.UserID).Person()
t := service.SetPlayer(c, playerinfo)
t := service.GetPlayer(c, data.Head.UserID)
t.Info = blservice.NewUserService(data.Head.UserID).Person()
t.Info.UserID = data.Head.UserID
t.Onlinetime = uint32(time.Now().Unix()) //保存时间戳
copier.Copy(playerinfo, t) //先复制给内存信息
share.ShareManager.SetUserOnline(data.Head.UserID, h.Port) //设置用户登录服务器
t.CompleteLogin() //通知客户端登录成功
glog.Debug(context.Background(), "登录成功,初始地图 人数:", space.GetSpace(t.Info.MapID).Len())
result = login.NewOutInfo() //设置登录消息
copier.Copy(playerinfo, result)
defer space.GetSpace(t.Info.MapID).Set(t.Info.UserID, t).Range(func(playerID uint32, player *socket.Player) bool {
result.PlayerInfo = *t.Info
tt := maps.NewOutInfo()
copier.Copy(playerinfo, tt)
//copier.Copy(t.Info, tt)
t1 := handler.NewTomeeHeader(2001, t.Info.UserID)
defer space.GetSpace(t.Info.MapID).Set(t.Info.UserID, t).Range(func(playerID uint32, player *socket.Player) bool {
player.SendPack(t1.Pack(&tt))
return true
})

View File

@@ -6,7 +6,6 @@ import (
"blazing/logic/service/maphot"
"blazing/logic/service/maps"
"blazing/logic/service/space"
mservice "blazing/modules/blazing/service"
"time"
"github.com/jinzhu/copier"
@@ -18,15 +17,11 @@ func (h *Controller) MapEnter(data *maps.InInfo, c *socket.Player) (result *maps
space.GetSpace(c.Info.MapID).Set(c.Info.UserID, c) //添加玩家
result = maps.NewOutInfo()
c.Info.Pos = data.Point
copier.Copy(c.Info, result)
copier.Copy(result, c.Info)
data.Broadcast(c.Info.MapID, *result) //同步广播
// 如果是无怪地图,直接返回
if mservice.NewMonsterService().GetId(c.Info.MapID) == nil {
return nil, -1
}
// 创建新的停止通道
c.StopChan = make(chan struct{})
@@ -70,6 +65,7 @@ func (h Controller) MapHot(data *maphot.InInfo, c *socket.Player) (result *mapho
}
func (h *Controller) MapLeave(data *maps.LeaveMapInboundInfo, c *socket.Player) (result *maps.LeaveMapOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
//result = &maps.LeaveMapOutboundInfo{UserID: c.GetUserID()}
data.Broadcast(c.Info.MapID, maps.LeaveMapOutboundInfo{UserID: c.Info.UserID}) //同步广播
space.GetSpace(c.Info.MapID).Delete(c.Info.UserID)
// 如果有正在运行的刷怪协程,发送停止信号
@@ -87,7 +83,7 @@ func (h *Controller) MapList(data *maps.ListMapPlayerInboundInfo, c *socket.Play
space.GetSpace(c.Info.MapID).Range(func(userID uint32, player *socket.Player) bool {
result1 := maps.NewOutInfo()
copier.Copy(player.Info, result1)
copier.Copy(result1, player.Info)
result.Player = append(result.Player, *result1)
return true
})

View File

@@ -12,8 +12,10 @@ import (
* 接受任务
*/
func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *socket.Player) (result *task.AcceptTaskOutboundInfo, err errorcode.ErrorCode) {
if data.Head.CMD == 2201 { //判断是每日任务
isdaliy := false
if data.Head.CMD != 2201 { //判断是每日任务
isdaliy = true
}
service.NewUserService(c.Info.UserID).TaskExec(func(ttt map[uint32]model.TaskInfo) bool {
ft, ok := ttt[data.TaskId]
if ok { //如果找到任务
@@ -31,28 +33,7 @@ func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *socket.Playe
}
return false
})
} else {
service.NewUserService(c.Info.UserID).DailyTaskExec(func(ttt map[uint32]model.DailyTaskInfo) bool {
ft, ok := ttt[data.TaskId]
if ok { //如果找到任务
if ft.Status == 0 { //可以接受
ft.Status = 1 //接受
return true
} else {
return false
}
} else {
ttt[data.TaskId] = model.DailyTaskInfo{
Status: 1,
}
}
return false
})
}
}, isdaliy)
result = &task.AcceptTaskOutboundInfo{}
result.TaskId = data.TaskId
return result, 0
@@ -62,8 +43,10 @@ func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *socket.Playe
* 更新任务步骤
*/
func (h Controller) AddTaskBuf(data *task.AddTaskBufInboundInfo, c *socket.Player) (result *task.AddTaskBufOutboundInfo, err errorcode.ErrorCode) {
if data.Head.CMD == 2204 { //判断是每日任务
isdaliy := false
if data.Head.CMD != 2204 { //判断是每日任务
isdaliy = true
}
service.NewUserService(c.Info.UserID).TaskExec(func(ttt map[uint32]model.TaskInfo) bool {
if conditions, ok := ttt[data.TaskId]; ok {
conditions.TaskInfo = data.TaskList
@@ -72,19 +55,7 @@ func (h Controller) AddTaskBuf(data *task.AddTaskBufInboundInfo, c *socket.Playe
}
return false
})
} else {
service.NewUserService(c.Info.UserID).DailyTaskExec(func(ttt map[uint32]model.DailyTaskInfo) bool {
if conditions, ok := ttt[data.TaskId]; ok {
conditions.DailyTaskInfo = data.TaskList
ttt[data.TaskId] = conditions
return true
}
return false
})
}
}, isdaliy)
return &task.AddTaskBufOutboundInfo{}, 0
}
@@ -107,12 +78,10 @@ func (h Controller) Complete_Task(data *task.CompleteTaskInboundInfo, c *socket.
* 获取任务状态
*/
func (h Controller) Get_Task_Buf(data *task.GetTaskBufInboundInfo, c *socket.Player) (result *task.GetTaskBufOutboundInfo, err errorcode.ErrorCode) {
if data.Head.CMD == 2203 { //判断是每日任务
} else {
}
// isdaliy := false
// if data.Head.CMD != 2203 { //判断是每日任务
// isdaliy = true
// }
return &task.GetTaskBufOutboundInfo{}, 0
}

View File

@@ -4,14 +4,18 @@ import (
"blazing/common/data/socket"
"blazing/common/socket/errorcode"
"blazing/logic/service/space"
"context"
"github.com/gogf/gf/v2/os/glog"
"github.com/jinzhu/copier"
)
func (h Controller) Walk(data *space.InInfo, c *socket.Player) (result *space.OutInfo, err errorcode.ErrorCode) {
data.Broadcast(c.Info.UserID, space.OutInfo{Flag: data.Flag,
UserID: c.Info.UserID,
Reserve2: data.Reverse2,
Point: data.Point}) //走路的广播
result = &space.OutInfo{}
err1 := copier.Copy(result, data)
result.UserID = data.Head.UserID
glog.Debug(context.Background(), err1)
data.Broadcast(c.Info.MapID, *result) //走路的广播
return nil, -1
}

View File

@@ -2,7 +2,7 @@ package info
import (
element "blazing/common/data/Element"
"blazing/common/data/xml/skill"
"blazing/common/data/xmlres"
"blazing/common/utils/random"
"context"
@@ -35,7 +35,7 @@ var Category = enum.New[struct {
// 实现了战斗中技能的所有属性和行为包括PP管理、技能使用、属性获取等
// 战斗中可以修改技能实体值,比如是否暴击,是否必中等
type BattleSkillEntity struct {
skill.Move
xmlres.Move
ctx context.Context
SideEffects []int
SideEffectArgs []int
@@ -61,7 +61,7 @@ func CreateBattleSkillWithInfinity(id int, pp int) *BattleSkillEntity {
}
// 从资源仓库获取技能数据
move, ok := skill.MovesConfig.Moves[id]
move, ok := xmlres.MovesConfig.Moves[id]
if !ok {
glog.Error(context.Background(), "技能ID无效", "id", id)
}

View File

@@ -7,5 +7,5 @@ type OgreInfo struct {
type OgrePetInfo struct {
Id uint32
Shiny uint32
Lv uint32 `struc:"skip"` //等级
}

View File

@@ -2,13 +2,16 @@ package maps
import (
"blazing/common/data/socket"
"blazing/common/data/xmlres"
"blazing/common/socket/handler"
"blazing/logic/service/space"
"blazing/modules/blazing/model"
"math/rand"
"strings"
"time"
"github.com/creasty/defaults"
"github.com/gogf/gf/v2/util/gconv"
)
type InInfo struct {
@@ -38,9 +41,9 @@ func (t *InInfo) Broadcast(mapid uint32, o OutInfo) {
func (t *InInfo) SpawnMonsters(c *socket.Player, isfrist bool) {
// 获取当前地图的怪物配置
// if c == nil || mservice.NewMonsterService().GetId(c.MapId) == 0 { //用户离线
// return
// }
if c == nil || c.Info.MapID == 0 { //用户离线
return
}
if !c.IsLogin {
defer func() {
@@ -60,27 +63,54 @@ func (t *InInfo) SpawnMonsters(c *socket.Player, isfrist bool) {
t.monsters, _, _ = replaceOneNumber(t.monsters)
}
t1 := t.genMonster(c.Info.UserID)
t1 := t.genMonster(c.Info.MapID)
if t1 != nil {
t1 := tt.Pack(t1)
c.SendPack(t1)
}
c.SendPack(tt.Pack(&t1))
}
// 2. 从 string 类型 slice 随机选一个元素
func RandomStringFromSlice(s []string) string {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
randomIdx := r.Intn(len(s))
return s[randomIdx]
}
// 应该根据怪物信息决定后端生成
func (t *InInfo) genMonster(mapid uint32) OgreInfo {
func (t *InInfo) genMonster(mapid uint32) *OgreInfo {
// 设置怪物信息
t1 := OgreInfo{}
for _, tc := range xmlres.Monster.Maps {
for i := 0; i < 3; i++ {
if tc.ID == gconv.Int(mapid) && tc.Monsters != nil {
ttt := OgrePetInfo{}
// ttt.Id = mservice.NewMonsterService().GetId(mapid) //待修改成xml获取
for i, m := range tc.Monsters.Monsters { //这里是9个
id := strings.Split(m.ID, " ")
lv := strings.Split(m.Lv, " ")
ttt := OgrePetInfo{
Id: gconv.Uint32(RandomStringFromSlice(id)),
}
if ttt.Id != 0 {
ttt.Shiny = 0 //待确认是否刷新异色
ttt.Lv = gconv.Uint32(RandomStringFromSlice(lv))
}
t1.Data[i] = ttt
ttt.Shiny = uint32(i + 1) //异色概率,待实现自定义
//t1.Data[i] = mservice.NewMonsterService().GetId(c.MapId)
t1.Data[t.monsters[i]] = ttt
}
break
}
return t1
}
t2 := OgreInfo{}
for i := 0; i < 3; i++ {
t2.Data[t.monsters[i]] = t1.Data[t.monsters[i]]
}
return &t2
}
// 计算整数的二进制1的个数Integer.bitCount
@@ -101,7 +131,7 @@ func generateThreeUniqueNumbers() [3]int {
index := 0
for index < 3 {
num := rand.Intn(10)
num := rand.Intn(9)
if !selected[num] {
selected[num] = true
result[index] = num
@@ -124,7 +154,7 @@ func replaceOneNumber(original [3]int) ([3]int, int, int) {
originalMap[num] = true
}
for i := 0; i < 10; i++ {
for i := 0; i < 8; i++ {
if !originalMap[i] {
candidates = append(candidates, i)
}

View File

@@ -4,24 +4,30 @@ import (
"blazing/common/data/socket"
"blazing/common/socket/errorcode"
"blazing/common/socket/handler"
"blazing/modules/blazing/model"
)
func GetPlayer(c *socket.Conn, userid uint32) *socket.Player { //TODO 这里待优化,可能存在内存泄漏问题
c.Mu.Lock()
defer c.Mu.Unlock()
//检查player初始化是否为conn初始后取map防止二次连接后存在两个player
clientdata := c.MainConn.Context().(*socket.ClientData)
if clientdata.GetPlayer() != nil {
return clientdata.GetPlayer()
}
var player *socket.Player
if player1, ok := socket.Mainplayer.Load((userid)); ok {
clientdata.SetPlayer(player1)
if clientdata.Player != nil {
return clientdata.Player
}
return player
clientdata.Player = socket.NewPlayer(
socket.WithConn(c), //注入conn
)
// gff := socket.NewClientData()
// gff.Player = clientdata.Player
// c.MainConn.SetContext(gff)
socket.Mainplayer.Store(userid, clientdata.Player)
return clientdata.Player
// return nil
}
func KickPlayer(userid uint32) { //踢出玩家
@@ -40,19 +46,3 @@ func KickPlayer(userid uint32) { //踢出玩家
//return player
// return nil
}
func SetPlayer(c *socket.Conn, user *model.PlayerInfo) *socket.Player { //TODO 这里待优化,
clientdata := c.MainConn.Context().(*socket.ClientData)
player := socket.NewPlayer(
socket.WithConn(*c), //注入conn
)
socket.Mainplayer.Store(user.UserID, player)
clientdata.SetPlayer(player) //= player
return player
// return nil
}

View File

@@ -1,7 +1,7 @@
package space
import (
xml "blazing/common/data/xml/map"
"blazing/common/data/xmlres"
"golang.org/x/sync/singleflight"
)
@@ -19,7 +19,7 @@ func GetMapHot() []MapHotInfo {
tt := make(map[uint32]uint32)
for _, v := range xml.MapConfig.Maps {
for _, v := range xmlres.MapConfig.Maps {
t1, ok := tt[uint32(v.Super)]
if ok {

View File

@@ -2,7 +2,7 @@ package space
import (
"blazing/common/data/socket"
xml "blazing/common/data/xml/map"
"blazing/common/data/xmlres"
"blazing/common/utils"
"blazing/modules/blazing/model"
"sync"
@@ -90,7 +90,7 @@ func GetSpace(id uint32) *Space {
}
//如果不ok,说明星球未创建,那就新建星球
for _, v := range xml.MapConfig.Maps {
for _, v := range xmlres.MapConfig.Maps {
if v.ID == int(id) { //找到这个地图
t := NewSpace()
t.DefaultPos = model.Pos{X: uint32(v.X), Y: uint32(v.Y)}

View File

@@ -1,6 +1,9 @@
package model
import "blazing/cool"
import (
"blazing/cool"
"time"
)
const TableNameTask = "task"
@@ -15,7 +18,9 @@ type Task struct {
type TaskInfo struct {
// TaskInfo 任务步骤信息对应Java的@ArraySerialize(FIXED_LENGTH=20)注解
// struc:"[20]byte" 确保二进制序列化时固定20字节长度json标签指定JSON字段名
TaskType uint32 `json:"task_type"` //区分是每日任务还是常规任务,常规为0,每日为1
TaskInfo []uint32 `struc:"[20]byte" json:"task_info"`
LastResetTime time.Time `gorm:"not null;comment:'上次重置时间UTC'" json:"last_reset_time"` //这里是每天重置
// Status 任务整体状态0-未接受1-已接受2-已完成未领取3-已完成已领取
// json标签指定JSON字段名与业务状态说明保持一致
Status byte `json:"status"`

View File

@@ -1,48 +0,0 @@
package model
import (
"blazing/cool"
"time"
)
const TableNameDailyTask = "DailyTask"
// DailyTask mapped from table <DailyTask>
type DailyTask struct {
*cool.Model
PlayerID uint64 `gorm:"not null;index:idx_DailyTask_by_player_id;comment:'所属玩家ID'" json:"player_id"`
Data string `gorm:"type:text;not null;comment:'全部数据'" json:"data"`
}
// DailyTaskInfo 单个任务的详细信息,包含任务步骤状态和整体状态
type DailyTaskInfo struct {
// DailyTaskInfo 任务步骤信息对应Java的@ArraySerialize(FIXED_LENGTH=20)注解
// struc:"[20]byte" 确保二进制序列化时固定20字节长度json标签指定JSON字段名
DailyTaskInfo []uint32 `struc:"[20]byte" json:"DailyTask_info"`
LastResetTime time.Time `gorm:"not null;comment:'上次重置时间UTC'" json:"last_reset_time"` //这里是每天重置
// Status 任务整体状态0-未接受1-已接受2-已完成未领取3-已完成已领取
// json标签指定JSON字段名与业务状态说明保持一致
Status byte `json:"status"`
}
// TableName PlayerInfo's table name
func (*DailyTask) TableName() string {
return TableNamePlayerInfo
}
// GroupName PlayerInfo's table group
func (*DailyTask) GroupName() string {
return "default"
}
// NewPlayerInfo create a new PlayerInfo
func NewDailyTask() *DailyTask {
return &DailyTask{
Model: cool.NewModel(),
}
}
// init 创建表
func init() {
cool.CreateTable(&DailyTask{})
}

View File

@@ -33,7 +33,24 @@ func Exec[T, F any](userid uint32, s *cool.Service, processFunc func(F) bool) bo
m1.Save(player)
return false
}
func (s *UserService) TaskExec(t func(map[uint32]model.TaskInfo) bool) (ret bool) {
func (s *UserService) TaskExec(t func(map[uint32]model.TaskInfo) bool, isdaliy bool) (ret bool) {
if isdaliy {
Exec[model.Task](s.userid, s.task, func(tt map[uint32]model.TaskInfo) bool {
//先重置每日
for _, v := range tt {
if v.TaskType == 1 && !IsToday(v.LastResetTime) {
v.Status = 0 //重置+自动接受每日任务
v.LastResetTime = time.Now().UTC()
}
}
return true
})
}
return Exec[model.Task](s.userid, s.task, t)
// m := cool.DBM(s.task.Model).Where("player_id", s.userid)
// var tt model.Task
@@ -57,34 +74,6 @@ func IsToday(t time.Time) bool {
t.Month() == now.Month() &&
t.Day() == now.Day()
}
func (s *UserService) DailyTaskExec(t func(map[uint32]model.DailyTaskInfo) bool) (ret bool) {
Exec[model.DailyTask](s.userid, s.task, func(tt map[uint32]model.DailyTaskInfo) bool {
//先重置每日
for _, v := range tt {
if !IsToday(v.LastResetTime) {
v.Status = 0 //重置+自动接受每日任务
v.LastResetTime = time.Now().UTC()
}
}
return true
})
return Exec[model.DailyTask](s.userid, s.task, t)
// m := cool.DBM(s.task.Model).Where("player_id", s.userid)
// var tt model.Task
// m.Scan(&tt)
// var ttt map[uint32]model.TaskInfo
// json.Unmarshal([]byte(tt.Data), &ttt)
// ret = t(ttt)
// t1, _ := json.Marshal(&ttt)
// tt.Data = string(t1)
// m.Save(&tt) //退出时保存
// return
}
// /**
// * 完成任务