```
refactor(logic): 重构服务器启动逻辑与任务状态管理 - 移除了 `gcmd` 包在 controller 中的直接使用,改为通过参数传递端口和服务器类型 - 统一使用 `GetTask` 和 `SetTask` 方法替代直接访问 `TaskList` 数组,提升代码可维护性 - 修改了战斗逻辑中部分调试打印语句,并优化战斗循环结束日志输出 - 调整了新手玩家初始化流程,默认完成新手任务4 - 更新了数据库模型字段及结构定义,如增加 `max_ts` 字段、扩展 `TaskList` 长度等 - 改进了宠物添加逻辑,采用 SQL 方式确保捕捉时间唯一递增 - 清理了无用或注释掉的旧代码块
This commit is contained in:
90
.vscode/launch.json
vendored
90
.vscode/launch.json
vendored
@@ -1,49 +1,45 @@
|
|||||||
{
|
{
|
||||||
// 使用 IntelliSense 了解相关属性。
|
// 使用 IntelliSense 了解相关属性。
|
||||||
// 悬停以查看现有属性的描述。
|
// 悬停以查看现有属性的描述。
|
||||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch login-http",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"program": "${workspaceFolder}/login"
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// "name": "Launch logic1",
|
||||||
|
// "type": "go",
|
||||||
|
// "request": "launch",
|
||||||
|
// "mode": "auto",
|
||||||
|
// "cwd": "${workspaceFolder}",
|
||||||
|
// "args": ["-port=1", " -race"],
|
||||||
|
// "program": "${workspaceFolder}/logic",
|
||||||
|
// "console": "integratedTerminal"
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
"name": "Launch test server",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"args": ["-port=2"],
|
||||||
|
|
||||||
{
|
"program": "${workspaceFolder}/logic"
|
||||||
"name": "Launch login-http",
|
}
|
||||||
"type": "go",
|
// {
|
||||||
"request": "launch",
|
// "name": "Launch login-tcp",
|
||||||
"mode": "auto",
|
// "type": "go",
|
||||||
"cwd": "${workspaceFolder}",
|
// "request": "launch",
|
||||||
"program": "${workspaceFolder}/login",
|
// "mode": "auto",
|
||||||
|
// "cwd": "${workspaceFolder}",
|
||||||
|
// "args": ["-port=0"],
|
||||||
},
|
// "program": "${workspaceFolder}/logic",
|
||||||
{
|
// }
|
||||||
"name": "Launch logic1",
|
]
|
||||||
"type": "go",
|
}
|
||||||
"request": "launch",
|
|
||||||
"mode": "auto",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"args": ["-port=1"," -race"],
|
|
||||||
"program": "${workspaceFolder}/logic", "console": "integratedTerminal"
|
|
||||||
|
|
||||||
}, {
|
|
||||||
"name": "Launch logic2",
|
|
||||||
"type": "go",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "auto",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"args": ["-port=2"],
|
|
||||||
|
|
||||||
"program": "${workspaceFolder}/logic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Launch login-tcp",
|
|
||||||
"type": "go",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "auto",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"args": ["-port=0"],
|
|
||||||
"program": "${workspaceFolder}/logic",
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/os/gcmd"
|
|
||||||
"github.com/gogf/gf/v2/os/glog"
|
"github.com/gogf/gf/v2/os/glog"
|
||||||
"github.com/lunixbochs/struc"
|
"github.com/lunixbochs/struc"
|
||||||
)
|
)
|
||||||
@@ -38,9 +37,8 @@ func ParseCmd[T any](a T, data []byte) T {
|
|||||||
//fmt.Println(data)
|
//fmt.Println(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { //默认初始化扫描
|
func Init(isgame bool) { //默认初始化扫描
|
||||||
// 解析命令行参数
|
|
||||||
cool.Config.PortBL = gcmd.GetOpt("port", "1").Uint16()
|
|
||||||
// 获取对象的反射值和类型
|
// 获取对象的反射值和类型
|
||||||
value := reflect.ValueOf(Maincontroller)
|
value := reflect.ValueOf(Maincontroller)
|
||||||
|
|
||||||
@@ -60,12 +58,12 @@ func init() { //默认初始化扫描
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if cool.Config.PortBL == 0 && func_cmd > 1000 { //判断login服务器
|
if !isgame && func_cmd > 1000 { //判断login服务器
|
||||||
continue
|
continue
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cool.Config.PortBL != 0 && func_cmd < 1000 { //判断login服务器
|
if isgame && func_cmd < 1000 { //判断login服务器
|
||||||
continue
|
continue
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ func (h Controller) PlayerFightBoss(data *fight.ChallengeBossInboundInfo, c *pla
|
|||||||
fight.NewFight(c, ai, func(foi *info.FightOverInfo) {
|
fight.NewFight(c, ai, func(foi *info.FightOverInfo) {
|
||||||
if taskid != 0 {
|
if taskid != 0 {
|
||||||
if foi.Reason == 0 && foi.WinnerId == c.Info.UserID {
|
if foi.Reason == 0 && foi.WinnerId == c.Info.UserID {
|
||||||
if c.Info.TaskList[taskid-1] != 3 {
|
if c.GetTask(taskid) == player.Unaccepted {
|
||||||
|
c.SetTask(taskid, player.Completed) //设置完成任务
|
||||||
c.Info.TaskList[taskid-1] = 3
|
c.Info.TaskList[taskid-1] = 3
|
||||||
|
|
||||||
moinfo.PetList[0].Downgrade(1)
|
moinfo.PetList[0].Downgrade(1)
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *player.Playe
|
|||||||
// //isdaliy = true
|
// //isdaliy = true
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if c.Info.TaskList[data.TaskId-1] == 0 {
|
if c.GetTask(int(data.TaskId)) == player.Unaccepted {
|
||||||
c.Info.TaskList[data.TaskId-1] = 1
|
c.SetTask(int(data.TaskId), player.Accepted)
|
||||||
|
|
||||||
}
|
}
|
||||||
c.Service.Task.Exec(uint32(data.TaskId), func(t *model.TaskEX) bool {
|
c.Service.Task.Exec(uint32(data.TaskId), func(t *model.TaskEX) bool {
|
||||||
t.Data = []uint32{}
|
t.Data = []uint32{}
|
||||||
@@ -54,13 +55,13 @@ func (h Controller) AddTaskBuf(data *task.AddTaskBufInboundInfo, c *player.Playe
|
|||||||
* 完成任务
|
* 完成任务
|
||||||
*/
|
*/
|
||||||
func (h Controller) Complete_Task(data *task.CompleteTaskInboundInfo, c *player.Player) (result *task.CompleteTaskOutboundInfo, err errorcode.ErrorCode) {
|
func (h Controller) Complete_Task(data *task.CompleteTaskInboundInfo, c *player.Player) (result *task.CompleteTaskOutboundInfo, err errorcode.ErrorCode) {
|
||||||
|
if c.GetTask(int(data.TaskId)) != player.Accepted { //如果任务没有接受或者已经完成Complete_Task
|
||||||
if c.Info.TaskList[data.TaskId-1] != 1 { //如果任务没有接受或者已经完成Complete_Task
|
|
||||||
|
|
||||||
return result, 0
|
return result, 0
|
||||||
|
|
||||||
}
|
}
|
||||||
c.Info.TaskList[data.TaskId-1] = 3
|
|
||||||
|
c.SetTask(int(data.TaskId), player.Completed)
|
||||||
|
|
||||||
result = &task.CompleteTaskOutboundInfo{
|
result = &task.CompleteTaskOutboundInfo{
|
||||||
TaskId: data.TaskId,
|
TaskId: data.TaskId,
|
||||||
ItemList: make([]model.ItemInfo, 0),
|
ItemList: make([]model.ItemInfo, 0),
|
||||||
@@ -111,10 +112,9 @@ func (h Controller) Get_Task_Buf(data *task.GetTaskBufInboundInfo, c *player.Pla
|
|||||||
*/
|
*/
|
||||||
func (h Controller) Delete_Task(data *task.DeleteTaskInboundInfo, c *player.Player) (result *task.DeleteTaskOutboundInfo, err errorcode.ErrorCode) {
|
func (h Controller) Delete_Task(data *task.DeleteTaskInboundInfo, c *player.Player) (result *task.DeleteTaskOutboundInfo, err errorcode.ErrorCode) {
|
||||||
|
|
||||||
if c.Info.TaskList[data.TaskId-1] == 1 {
|
if c.GetTask(int(data.TaskId)) == player.Accepted {
|
||||||
c.Info.TaskList[data.TaskId-1] = 0
|
c.SetTask(int(data.TaskId), player.Unaccepted)
|
||||||
return &task.DeleteTaskOutboundInfo{TaskId: data.TaskId}, 0
|
return &task.DeleteTaskOutboundInfo{TaskId: data.TaskId}, 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &task.DeleteTaskOutboundInfo{}, 0
|
return &task.DeleteTaskOutboundInfo{}, 0
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||||
|
"github.com/gogf/gf/v2/os/gcmd"
|
||||||
"github.com/gogf/gf/v2/os/gproc"
|
"github.com/gogf/gf/v2/os/gproc"
|
||||||
|
|
||||||
"blazing/logic/service/fight"
|
"blazing/logic/service/fight"
|
||||||
@@ -19,8 +20,6 @@ import (
|
|||||||
blservice "blazing/modules/blazing/service"
|
blservice "blazing/modules/blazing/service"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/os/gctx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func PprofWeb() {
|
func PprofWeb() {
|
||||||
@@ -44,10 +43,11 @@ func signalHandlerForMain(sig os.Signal) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//loadAccounts()
|
//loadAccounts()
|
||||||
if cool.IsRedisMode {
|
// if cool.IsRedisMode {
|
||||||
go cool.ListenFunc(gctx.New())
|
// go cool.ListenFunc(gctx.New())
|
||||||
}
|
// }
|
||||||
|
// 解析命令行参数
|
||||||
|
cool.Config.PortBL = gcmd.GetOpt("port", "1").Uint16()
|
||||||
go Start(cool.Config.PortBL) //注入service
|
go Start(cool.Config.PortBL) //注入service
|
||||||
if cool.Config.PortBL == 1 || cool.Config.PortBL == 2 { //只分析1服务器的
|
if cool.Config.PortBL == 1 || cool.Config.PortBL == 2 { //只分析1服务器的
|
||||||
go PprofWeb()
|
go PprofWeb()
|
||||||
|
|||||||
@@ -23,15 +23,10 @@ const (
|
|||||||
maxPortRetryCount = 5
|
maxPortRetryCount = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultPort = gconv.Int(cool.Config.Port) //读入默认的端口
|
|
||||||
var candidatePorts = cool.Config.GamePort
|
var candidatePorts = cool.Config.GamePort
|
||||||
|
|
||||||
// determinePort 确定服务器使用的端口
|
// determinePort 确定服务器使用的端口
|
||||||
func determinePort(serverid uint16) (int, error) {
|
func determinePort() (int, error) {
|
||||||
// 服务器ID为0时使用默认端口
|
|
||||||
if serverid == 0 {
|
|
||||||
return defaultPort, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试从指定端口列表中找可用端口,最多尝试maxPortRetryCount轮
|
// 尝试从指定端口列表中找可用端口,最多尝试maxPortRetryCount轮
|
||||||
for i := 0; i < maxPortRetryCount; i++ {
|
for i := 0; i < maxPortRetryCount; i++ {
|
||||||
@@ -61,35 +56,22 @@ func isPortAvailable(port uint64) bool {
|
|||||||
|
|
||||||
// 如果id是0,那就是login server
|
// 如果id是0,那就是login server
|
||||||
func Start(serverid uint16) {
|
func Start(serverid uint16) {
|
||||||
|
// 确定端口
|
||||||
if serverid != 0 { //logic服务器
|
port, err := determinePort()
|
||||||
// 确定端口
|
if err != nil {
|
||||||
port, err := determinePort(serverid)
|
log.Fatalf("Failed to determine port: %v", err)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to determine port: %v", err)
|
|
||||||
}
|
|
||||||
ser := socket.NewServer(
|
|
||||||
socket.WithCORS(),
|
|
||||||
socket.WithPort(port),
|
|
||||||
)
|
|
||||||
// go func() {
|
|
||||||
t := rpc.StartClient(serverid, uint16(port), ser)
|
|
||||||
|
|
||||||
controller.Maincontroller.RPCClient = *t //将RPC赋值Start
|
|
||||||
controller.Maincontroller.Port = uint16(port) //赋值服务器ID
|
|
||||||
xmlres.Initfile()
|
|
||||||
blservice.NewLoginServiceService().SetServerID(serverid, gconv.Uint16(port))
|
|
||||||
ser.Boot()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
go rpc.StartServer()
|
|
||||||
|
|
||||||
//go rpcserver() //对login tcp启动
|
|
||||||
//ants.Submit(rpcserver)
|
|
||||||
socket.NewServer(
|
|
||||||
socket.WithCORS(),
|
|
||||||
socket.WithPort(defaultPort),
|
|
||||||
).Boot()
|
|
||||||
}
|
}
|
||||||
|
ser := socket.NewServer(
|
||||||
|
socket.WithCORS(),
|
||||||
|
socket.WithPort(port),
|
||||||
|
)
|
||||||
|
// go func() {
|
||||||
|
t := rpc.StartClient(serverid, uint16(port), ser)
|
||||||
|
|
||||||
|
controller.Maincontroller.RPCClient = *t //将RPC赋值Start
|
||||||
|
controller.Maincontroller.Port = uint16(port) //赋值服务器ID
|
||||||
|
controller.Init(true)
|
||||||
|
xmlres.Initfile()
|
||||||
|
blservice.NewLoginServiceService().SetServerID(serverid, gconv.Uint16(port))
|
||||||
|
ser.Boot()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
|
|||||||
e.Alive(true) //添加后默认激活
|
e.Alive(true) //添加后默认激活
|
||||||
//todo 免疫
|
//todo 免疫
|
||||||
//TODO 先激活
|
//TODO 先激活
|
||||||
fmt.Println("产生回合数", e.ID(), e.Duration())
|
//fmt.Println("产生回合数", e.ID(), e.Duration())
|
||||||
// 如果已有同 ID 的效果,尝试叠加
|
// 如果已有同 ID 的效果,尝试叠加
|
||||||
for _, v := range our.Effects {
|
for _, v := range our.Effects {
|
||||||
if v == e {
|
if v == e {
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ func (f *FightC) battleLoop() {
|
|||||||
//<-time.After(1000)
|
//<-time.After(1000)
|
||||||
f.Broadcast(func(ff *input.Input) {
|
f.Broadcast(func(ff *input.Input) {
|
||||||
|
|
||||||
fmt.Println("战斗结束信息", ff.UserID)
|
|
||||||
ff.Player.SendPackCmd(2506, &f.FightOverInfo)
|
ff.Player.SendPackCmd(2506, &f.FightOverInfo)
|
||||||
|
|
||||||
ff.Player.QuitFight()
|
ff.Player.QuitFight()
|
||||||
@@ -102,7 +101,7 @@ func (f *FightC) battleLoop() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// close(f.actionChan)
|
// close(f.actionChan)
|
||||||
fmt.Println("战斗循环结束")
|
fmt.Println(f.ownerID, "战斗循环结束")
|
||||||
close(f.over)
|
close(f.over)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ func (lw *Player) CompleteLogin() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if lw.IsNewPlayer() { //重置新手地图,放到机械仓
|
if lw.IsNewPlayer() { //重置新手地图,放到机械仓
|
||||||
|
lw.SetTask(4, Completed) //设置新手任务默认完成
|
||||||
lw.Info.MapID = 8
|
lw.Info.MapID = 8
|
||||||
if len(lw.Info.PetList) == 0 {
|
if len(lw.Info.PetList) == 0 {
|
||||||
rr := lw.Service.Pet.PetInfo(0)
|
rr := lw.Service.Pet.PetInfo(0)
|
||||||
@@ -87,7 +88,7 @@ func (lw *Player) CompleteLogin() {
|
|||||||
func (lw *Player) IsNewPlayer() bool {
|
func (lw *Player) IsNewPlayer() bool {
|
||||||
// 遍历84到87的索引
|
// 遍历84到87的索引
|
||||||
for i := 84; i <= 87; i++ {
|
for i := 84; i <= 87; i++ {
|
||||||
if lw.Info.TaskList[i] != 3 {
|
if lw.GetTask(i) != Completed {
|
||||||
return true // 只要有一个不等于3,就返回true
|
return true // 只要有一个不等于3,就返回true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
logic/service/player/task.go
Normal file
45
logic/service/player/task.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// TaskStatus 任务状态(与 AS3 对应)
|
||||||
|
type TaskStatus uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Unaccepted TaskStatus = 0 // 未接受(AS3 中 0 或 2 映射至此)
|
||||||
|
Accepted TaskStatus = 1 // 已接受
|
||||||
|
Completed TaskStatus = 3 // 已完成
|
||||||
|
Reserved TaskStatus = 2 // 预留(AS3 中映射为未接受)
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetTask 设置第 i 个任务的状态(0 ≤ i < 2000)
|
||||||
|
func (m *Player) SetTask(i int, status TaskStatus) error {
|
||||||
|
i-- //下标减1
|
||||||
|
if i < 0 || i >= 4000 {
|
||||||
|
return fmt.Errorf("index out of range: %d (must be 0-1999)", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
byteIdx := i / 4
|
||||||
|
bitOffset := (i % 4) * 2
|
||||||
|
|
||||||
|
// 清除原有 2 位
|
||||||
|
m.Info.TaskList[byteIdx] &^= 0x3 << bitOffset
|
||||||
|
|
||||||
|
// 设置新状态(确保只取低 2 位)
|
||||||
|
m.Info.TaskList[byteIdx] |= byte(status&0x3) << bitOffset
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTask 获取第 i 个任务的状态
|
||||||
|
func (m *Player) GetTask(i int) TaskStatus {
|
||||||
|
i-- //下标减1
|
||||||
|
// if i < 0 || i >= 2000 {
|
||||||
|
// return 0, fmt.Errorf("index out of range: %d", i)
|
||||||
|
// }
|
||||||
|
|
||||||
|
byteIdx := i / 4
|
||||||
|
bitOffset := (i % 4) * 2
|
||||||
|
|
||||||
|
return TaskStatus((m.Info.TaskList[byteIdx] >> bitOffset) & 0x3)
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
|
||||||
@@ -26,14 +25,15 @@ var (
|
|||||||
go cool.ListenFunc(ctx)
|
go cool.ListenFunc(ctx)
|
||||||
}
|
}
|
||||||
//go robot()
|
//go robot()
|
||||||
|
go reg()
|
||||||
s := g.Server()
|
s := g.Server()
|
||||||
s.Use(Limiter, ghttp.MiddlewareHandlerResponse)
|
s.Use(Limiter, ghttp.MiddlewareHandlerResponse)
|
||||||
s.EnableAdmin()
|
s.EnableAdmin()
|
||||||
s.SetServerAgent(cool.Config.Name)
|
s.SetServerAgent(cool.Config.Name)
|
||||||
s.BindHookHandler("/*", ghttp.HookBeforeServe, beforeServeHook)
|
s.BindHookHandler("/*", ghttp.HookBeforeServe, beforeServeHook)
|
||||||
runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪
|
// runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪
|
||||||
runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪
|
// runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪
|
||||||
s.EnablePProf()
|
// s.EnablePProf()
|
||||||
// 如果存在 data/cool-admin-vue/dist 目录,则设置为主目录
|
// 如果存在 data/cool-admin-vue/dist 目录,则设置为主目录
|
||||||
if gfile.IsDir("public") {
|
if gfile.IsDir("public") {
|
||||||
s.SetServerRoot("public")
|
s.SetServerRoot("public")
|
||||||
|
|||||||
23
login/internal/cmd/reg.go
Normal file
23
login/internal/cmd/reg.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/common/rpc"
|
||||||
|
"blazing/common/socket"
|
||||||
|
"blazing/cool"
|
||||||
|
"blazing/logic/controller"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultPort = gconv.Int(cool.Config.Port) //读入默认的端口
|
||||||
|
func reg() {
|
||||||
|
go rpc.StartServer()
|
||||||
|
controller.Init(false)
|
||||||
|
//go rpcserver() //对login tcp启动
|
||||||
|
//ants.Submit(rpcserver)
|
||||||
|
socket.NewServer(
|
||||||
|
socket.WithCORS(),
|
||||||
|
socket.WithPort(defaultPort),
|
||||||
|
).Boot()
|
||||||
|
|
||||||
|
}
|
||||||
@@ -58,7 +58,10 @@ func main() {
|
|||||||
//service.TestSendVerificationCode()
|
//service.TestSendVerificationCode()
|
||||||
|
|
||||||
// t := model.GenPetInfo(1, 31, 1, 1, 1, 1)
|
// t := model.GenPetInfo(1, 31, 1, 1, 1, 1)
|
||||||
// service.NewUserService(10001).Pet.PetAdd(*t)
|
|
||||||
|
// for i := 0; i < 1000; i++ {
|
||||||
|
// service.NewUserService(10001).Pet.PetAdd(t)
|
||||||
|
// }
|
||||||
// service.NewUserService(10001).Pet.PetInfo_One_exec(t.CatchTime, func(pe *model.PetEX) {
|
// service.NewUserService(10001).Pet.PetInfo_One_exec(t.CatchTime, func(pe *model.PetEX) {
|
||||||
// fmt.Println(pe.CreateTime)
|
// fmt.Println(pe.CreateTime)
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ type BaseSysUser struct {
|
|||||||
Email *string `gorm:"column:email;type:varchar(255)" json:"email"` // 邮箱
|
Email *string `gorm:"column:email;type:varchar(255)" json:"email"` // 邮箱
|
||||||
Status *int32 `gorm:"column:status;not null;default:1" json:"status"` // 状态 0:禁用 1:启用
|
Status *int32 `gorm:"column:status;not null;default:1" json:"status"` // 状态 0:禁用 1:启用
|
||||||
GoldBean float64 `gorm:"column:goldBean;type:decimal;not null;default:0" json:"goldBean"`
|
GoldBean float64 `gorm:"column:goldBean;type:decimal;not null;default:0" json:"goldBean"`
|
||||||
Remark *string `gorm:"column:remark;type:varchar(255)" json:"remark"` // 备注
|
Remark *string `gorm:"column:remark;type:varchar(255)" json:"remark"` // 备注
|
||||||
Debug int32 `gorm:"column:debug;type:int;not null;default:0" json:"debug"` // 是否可以进入2服
|
Debug int32 `gorm:"column:debug;type:int;not null;default:0" json:"debug"` // 是否可以进入2服 测试服
|
||||||
|
Maxts uint32 `gorm:"column:max_ts;type:int;not null;default:0" json:"max_ts"` //最后生成的时间记录表
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName BaseSysUser's table name
|
// TableName BaseSysUser's table name
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (c *BaseSysPermsService) RefreshPerms(ctx context.Context, userId uint) (er
|
|||||||
)
|
)
|
||||||
cool.CacheManager.Set(ctx, "admin:perms:"+gconv.String(userId), perms, 0)
|
cool.CacheManager.Set(ctx, "admin:perms:"+gconv.String(userId), perms, 0)
|
||||||
// 更新部门权限
|
// 更新部门权限
|
||||||
departments := baseSysDepartmentService.GetByRoleIds(roleIds, userId == 1)
|
departments := baseSysDepartmentService.GetByRoleIds(roleIds, userId == 10001)
|
||||||
cool.CacheManager.Set(ctx, "admin:department:"+gconv.String(userId), departments, 0)
|
cool.CacheManager.Set(ctx, "admin:department:"+gconv.String(userId), departments, 0)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
22
modules/blazing/controller/admin/talkconfig.go
Normal file
22
modules/blazing/controller/admin/talkconfig.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/cool"
|
||||||
|
"blazing/modules/blazing/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TalkConfigController struct {
|
||||||
|
*cool.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var task_info_controller = &TalkConfigController{
|
||||||
|
&cool.Controller{
|
||||||
|
Prefix: "/admin/sun/talkconfig",
|
||||||
|
Api: []string{"Add", "Delete", "Update", "Info", "List", "Page"},
|
||||||
|
Service: service.NewTalkConfigService(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// 注册路由
|
||||||
|
cool.RegisterController(task_info_controller)
|
||||||
|
}
|
||||||
@@ -72,38 +72,38 @@ type PlayerInfo struct {
|
|||||||
//LastResetTime time.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
|
//LastResetTime time.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
|
||||||
OnlineTime uint32 `struc:"skip" json:"online_time"` //在线分钟数
|
OnlineTime uint32 `struc:"skip" json:"online_time"` //在线分钟数
|
||||||
// OutInfo 字段
|
// OutInfo 字段
|
||||||
UserID uint32 `struc:"uint32" json:"user_id"` // 米米号 通过sid拿到
|
UserID uint32 `struc:"uint32" json:"user_id"` // 米米号 通过sid拿到
|
||||||
RegisterTime uint32 `struc:"uint32" json:"register_time"` // 注册时间(秒时间戳)
|
RegisterTime uint32 `struc:"uint32" json:"register_time"` // 注册时间(秒时间戳)
|
||||||
Nick string `struc:"[16]byte" default:"seer" json:"nick"` // 16字节昵称
|
Nick string `struc:"[16]byte" default:"seer" json:"nick"` // 16字节昵称
|
||||||
Vip uint16 `struc:"uint16" json:"vip"` // 固定0
|
Vip uint16 `struc:"uint16" json:"vip"` // 固定0
|
||||||
Viped uint16 `struc:"uint16" default:"15" json:"viped"` // 固定15
|
Viped uint16 `struc:"uint16" default:"15" json:"viped"` // 固定15
|
||||||
DSFlag uint32 `struc:"uint32" json:"ds_flag"` // 固定0
|
DSFlag uint32 `struc:"uint32" json:"ds_flag"` // 固定0
|
||||||
Color uint32 `struc:"uint32" json:"color"` // 机器人颜色RGB颜色值(uint32,实际为3个uint8)
|
Color uint32 `struc:"uint32" json:"color"` // 机器人颜色RGB颜色值(uint32,实际为3个uint8)
|
||||||
Texture uint32 `struc:"uint32" json:"texture"` // 固定0
|
Texture uint32 `struc:"uint32" json:"texture"` // 固定0
|
||||||
Energy uint32 `struc:"uint32" default:"3000" json:"energy"` // 固定3000
|
Energy uint32 `struc:"uint32" default:"3000" json:"energy"` // 固定3000
|
||||||
Coins uint32 `struc:"uint32" json:"coins"` // 赛尔豆
|
Coins uint32 `struc:"uint32" json:"coins"` // 赛尔豆
|
||||||
EVPool uint32 `struc:"uint32" json:"ev_pool"` //累计学习力
|
EVPool uint32 `struc:"uint32" json:"ev_pool"` //累计学习力
|
||||||
FightBadge uint32 `struc:"uint32" json:"fight_badge"` // 固定0
|
FightBadge uint32 `struc:"uint32" json:"fight_badge"` // 固定0
|
||||||
MapID uint32 `struc:"uint32" default:"1" json:"map_id"` // 上线地图ID
|
MapID uint32 `struc:"uint32" default:"1" json:"map_id"` // 上线地图ID
|
||||||
Pos Pos `json:"pos"` // 坐标
|
Pos Pos `json:"pos"` // 坐标
|
||||||
TimeToday uint32 `struc:"uint32" default:"0" json:"time_today"` // 已消耗时间(秒)
|
TimeToday uint32 `struc:"uint32" default:"0" json:"time_today"` // 已消耗时间(秒)
|
||||||
TimeLimit uint32 `struc:"uint32" default:"43200" json:"time_limit"` // 总电池限制(秒)
|
TimeLimit uint32 `struc:"uint32" default:"43200" json:"time_limit"` // 总电池限制(秒)
|
||||||
IsClothHalfDay byte `struc:"byte" json:"is_cloth_half_day"` // 活动标志0/1
|
IsClothHalfDay byte `struc:"byte" json:"is_cloth_half_day"` // 活动标志0/1
|
||||||
IsRoomHalfDay byte `struc:"byte" json:"is_room_half_day"` // 活动标志0/1
|
IsRoomHalfDay byte `struc:"byte" json:"is_room_half_day"` // 活动标志0/1
|
||||||
IFortressHalfDay byte `struc:"byte" json:"i_fortress_half_day"` // 活动标志0/1
|
IFortressHalfDay byte `struc:"byte" json:"i_fortress_half_day"` // 活动标志0/1
|
||||||
IsHQHalfDay byte `struc:"byte" json:"is_hq_half_day"` // 活动标志0/1
|
IsHQHalfDay byte `struc:"byte" json:"is_hq_half_day"` // 活动标志0/1
|
||||||
LoginCount uint32 `struc:"uint32" json:"login_count"` // 固定0
|
LoginCount uint32 `struc:"uint32" json:"login_count"` // 固定0
|
||||||
Inviter uint32 `struc:"uint32" json:"inviter"` // 固定0
|
Inviter uint32 `struc:"uint32" json:"inviter"` // 固定0
|
||||||
NewInviteeCount uint32 `struc:"uint32" json:"new_invitee_count"` // 固定0
|
NewInviteeCount uint32 `struc:"uint32" json:"new_invitee_count"` // 固定0
|
||||||
VipLevel uint32 `struc:"uint32" default:"8" json:"vip_level"` // 固定8
|
VipLevel uint32 `struc:"uint32" default:"8" json:"vip_level"` // 固定8
|
||||||
VipValue uint32 `struc:"uint32" default:"80000" json:"vip_value"` // 固定80000
|
VipValue uint32 `struc:"uint32" default:"80000" json:"vip_value"` // 固定80000
|
||||||
VipStage uint32 `struc:"uint32" default:"1" json:"vip_stage"` // 超no的外形等级建议固定1
|
VipStage uint32 `struc:"uint32" default:"1" json:"vip_stage"` // 超no的外形等级建议固定1
|
||||||
AutoCharge uint32 `struc:"uint32" default:"1" json:"auto_charge"` // nono是否自动充电
|
AutoCharge uint32 `struc:"uint32" default:"1" json:"auto_charge"` // nono是否自动充电
|
||||||
VipEndTime uint32 `struc:"uint32" default:"4294967295" json:"vip_end_time"` // 超no的结束时间建议尽可能大
|
VipEndTime uint32 `struc:"uint32" default:"4294967295" json:"vip_end_time"` // 超no的结束时间建议尽可能大
|
||||||
FreshManBonus uint32 `struc:"uint32" json:"fresh_man_bonus"` // 邀请活动建议先给固定值0
|
FreshManBonus uint32 `struc:"uint32" json:"fresh_man_bonus"` // 邀请活动建议先给固定值0
|
||||||
NonoChipList [80]byte `struc:"[80]byte" json:"-"` // 超no芯片列表
|
//NonoChipList [80]byte `struc:"[80]byte" json:"-"` // 超no芯片列表
|
||||||
DailyResArr [50]byte `struc:"[50]byte" default:"0" json:"daily_res_arr"` // 每日任务状态 40+是谱尼的
|
DailyResArr [50]byte `struc:"[50]byte" default:"0" json:"daily_res_arr"` // 每日任务状态 40+是谱尼的
|
||||||
Study struct {
|
Study struct {
|
||||||
TeacherID uint32 `struc:"uint32" json:"teacher_id"` // 教官id
|
TeacherID uint32 `struc:"uint32" json:"teacher_id"` // 教官id
|
||||||
|
|
||||||
StudentID uint32 `struc:"uint32" json:"student_id"` // 学员id
|
StudentID uint32 `struc:"uint32" json:"student_id"` // 学员id
|
||||||
@@ -144,16 +144,16 @@ type PlayerInfo struct {
|
|||||||
Nick string `struc:"[16]byte" default:"nono" json:"nono_nick"` // nono名字(16字节)
|
Nick string `struc:"[16]byte" default:"nono" json:"nono_nick"` // nono名字(16字节)
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamInfo TeamInfo `struc:"struct" json:"team_info"` // 战队信息24字节
|
TeamInfo TeamInfo `struc:"struct" json:"team_info"` // 战队信息24字节
|
||||||
TeamPkInfo TeamPKInfo `struc:"struct" json:"team_pk_info"` // 8字节
|
TeamPkInfo TeamPKInfo `struc:"struct" json:"team_pk_info"` // 8字节
|
||||||
Reserved byte `struc:"byte" json:"reserved"` // 1字节无内容
|
Reserved byte `struc:"byte" json:"reserved"` // 1字节无内容
|
||||||
Badge uint32 `struc:"uint32" default:"0" json:"badge"` // 默认0
|
Badge uint32 `struc:"uint32" default:"0" json:"badge"` // 默认0
|
||||||
Reserved1 [27]byte `struc:"[27]byte" default:"3" json:"reserved1"` // 27字节默认3
|
Reserved1 [27]byte `struc:"[27]byte" default:"3" json:"reserved1"` // 27字节默认3
|
||||||
TaskList [500]byte `struc:"[500]byte" default:"0" json:"task_list"` // 任务状态数组500字节,默认3
|
TaskList [1000]byte `struc:"[1000]byte" default:"0" json:"task_list"` // 任务状态数组500字节,默认3
|
||||||
PetListCount uint32 `struc:"sizeof=PetList" json:"pet_list_count"` // 精灵列表长度
|
PetListCount uint32 `struc:"sizeof=PetList" json:"pet_list_count"` // 精灵列表长度
|
||||||
PetList []PetInfo ` json:"pet_list"` // 精灵背包内信息
|
PetList []PetInfo ` json:"pet_list"` // 精灵背包内信息
|
||||||
ClothesCount uint32 `struc:"sizeof=Clothes" json:"clothes_count"` // 穿戴装备数量
|
ClothesCount uint32 `struc:"sizeof=Clothes" json:"clothes_count"` // 穿戴装备数量
|
||||||
Clothes []PeopleItemInfo ` json:"clothes"` // 穿戴装备
|
Clothes []PeopleItemInfo ` json:"clothes"` // 穿戴装备
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName PlayerInfo's table name
|
// TableName PlayerInfo's table name
|
||||||
|
|||||||
55
modules/blazing/model/talkconfig.go
Normal file
55
modules/blazing/model/talkconfig.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/cool" // 沿用你项目中已有的基础Model包
|
||||||
|
)
|
||||||
|
|
||||||
|
// 表名常量(遵循项目现有命名规范)
|
||||||
|
const TableNameMineralCollectionConfig = "mineral_collection_config"
|
||||||
|
|
||||||
|
// MineralCollectionConfig 挖矿/采集/采摘矿产配置表Model定义
|
||||||
|
// 字段完全匹配数据表结构,包含最小/最大产出核心字段
|
||||||
|
type MineralCollectionConfig struct {
|
||||||
|
*cool.Model // 嵌入基础Model,包含id(主键)、createTime、updateTime等通用字段
|
||||||
|
|
||||||
|
// MapID 矿产所在地图ID
|
||||||
|
MapID uint32 `gorm:"column:map_id;not null;index:idx_mineral_collection_config_map_id;comment:矿产所在地图ID" json:"map_id"`
|
||||||
|
Type uint32 `gorm:"column:type;not null;index:idx_mineral_collection_config_type;comment:类型" json:"type"`
|
||||||
|
|
||||||
|
// DailyCollectCount 每日可采集次数
|
||||||
|
DailyCollectCount uint32 `gorm:"column:daily_collect_count;not null;comment:每日可采集次数" json:"daily_collect_count"`
|
||||||
|
|
||||||
|
// ItemID 物品编号(对应道具系统ID)
|
||||||
|
ItemID string `gorm:"column:item_id;type:varchar(16);not null;index:idx_mineral_collection_config_item_id;comment:物品编号(对应道具系统ID)" json:"item_id"`
|
||||||
|
// ItemMinCount 单次采集最小产出数量
|
||||||
|
ItemMinCount uint32 `gorm:"column:item_min_count;not null;comment:单次采集最小产出数量" json:"item_min_count"`
|
||||||
|
// ItemMaxCount 单次采集最大产出数量
|
||||||
|
ItemMaxCount uint32 `gorm:"column:item_max_count;not null;comment:单次采集最大产出数量" json:"item_max_count"`
|
||||||
|
// Description 矿产描述
|
||||||
|
Description string `gorm:"column:description;type:varchar(128); comment:矿产描述" json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName 指定数据表名(必须匹配数据库表名,遵循项目规范)
|
||||||
|
func (*MineralCollectionConfig) TableName() string {
|
||||||
|
return TableNameMineralCollectionConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupName 指定表分组(与项目现有表保持一致,默认分组)
|
||||||
|
func (*MineralCollectionConfig) GroupName() string {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMineralCollectionConfig 创建挖矿配置表实例(初始化基础Model)
|
||||||
|
// 保证通用字段(createTime/updateTime)被正确初始化
|
||||||
|
func NewMineralCollectionConfig() *MineralCollectionConfig {
|
||||||
|
return &MineralCollectionConfig{
|
||||||
|
Model: cool.NewModel(), // 调用基础Model的初始化方法
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init 程序启动时自动创建数据表(与项目现有表初始化逻辑一致)
|
||||||
|
// 若项目有统一的表初始化入口,可将此逻辑迁移至对应位置
|
||||||
|
func init() {
|
||||||
|
// 自动创建表(不存在则创建,已存在则不操作)
|
||||||
|
cool.CreateTable(&MineralCollectionConfig{})
|
||||||
|
}
|
||||||
@@ -37,11 +37,11 @@ func (s *InfoService) Reg(nick string, color uint32) {
|
|||||||
//设置用户信息
|
//设置用户信息
|
||||||
t.Data = model.NewPlayerInfo()
|
t.Data = model.NewPlayerInfo()
|
||||||
t.Data.Nick = nick
|
t.Data.Nick = nick
|
||||||
t.Data.TaskList[3] = 3 //新手任务
|
|
||||||
for i := 0; i < 80; i++ { //超NO芯片填充
|
|
||||||
|
|
||||||
t.Data.NonoChipList[i] = 255
|
// for i := 0; i < 80; i++ { //超NO芯片填充
|
||||||
}
|
|
||||||
|
// t.Data.NonoChipList[i] = 255
|
||||||
|
// }
|
||||||
|
|
||||||
t.Data.Color = color
|
t.Data.Color = color
|
||||||
t.Data.RegisterTime = uint32(time.Now().Unix()) //写入注册时间
|
t.Data.RegisterTime = uint32(time.Now().Unix()) //写入注册时间
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
"blazing/cool"
|
||||||
|
"blazing/modules/base/service"
|
||||||
"blazing/modules/blazing/model"
|
"blazing/modules/blazing/model"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -64,24 +65,50 @@ func (s *PetService) Pet_del(cachetime uint32) {
|
|||||||
cool.DBM(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime).Delete()
|
cool.DBM(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime).Delete()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 精灵真正添加后的捕捉时间才是真正的时间
|
||||||
func (s *PetService) PetAdd(y *model.PetInfo) {
|
func (s *PetService) PetAdd(y *model.PetInfo) {
|
||||||
|
sql := fmt.Sprintf(`
|
||||||
|
UPDATE %s
|
||||||
|
SET max_ts = CASE
|
||||||
|
WHEN max_ts < EXTRACT(EPOCH FROM NOW())::INT THEN EXTRACT(EPOCH FROM NOW())::INT
|
||||||
|
ELSE max_ts + 1
|
||||||
|
END
|
||||||
|
WHERE id = ? AND deleted_at IS NULL
|
||||||
|
RETURNING max_ts;
|
||||||
|
`, service.NewBaseSysUserService().Model.TableName())
|
||||||
|
|
||||||
for {
|
// 执行 Raw SQL 并扫描返回值
|
||||||
m1 := cool.DBM(s.Model).Where("player_id", s.userid)
|
ret, err := cool.DBM(service.NewBaseSysUserService().Model).Raw(sql, s.userid).All()
|
||||||
var player model.PetEX
|
//fmt.Println(ret, err)
|
||||||
player.PlayerID = s.userid
|
y.CatchTime = ret.Array()[0].Uint32()
|
||||||
player.Data = *y
|
m1 := cool.DBM(s.Model).Where("player_id", s.userid)
|
||||||
player.CatchTime = y.CatchTime
|
var player model.PetEX
|
||||||
player.Free = 0
|
player.PlayerID = s.userid
|
||||||
|
player.Data = *y
|
||||||
|
player.CatchTime = y.CatchTime
|
||||||
|
player.Free = 0
|
||||||
|
|
||||||
_, err := m1.Insert(player)
|
_, err = m1.Insert(player)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("添加失败id自增1继续添加")
|
panic(err)
|
||||||
y.CatchTime += 1 //自增保持时间排序
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
// for {
|
||||||
|
// m1 := cool.DBM(s.Model).Where("player_id", s.userid)
|
||||||
|
// var player model.PetEX
|
||||||
|
// player.PlayerID = s.userid
|
||||||
|
// player.Data = *y
|
||||||
|
// player.CatchTime = y.CatchTime
|
||||||
|
// player.Free = 0
|
||||||
|
|
||||||
|
// _, err := m1.Insert(player)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println("添加失败id自增1继续添加")
|
||||||
|
// y.CatchTime += 1 //自增保持时间排序
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
func (s *PetService) ModifyBefore(ctx context.Context, method string, param map[string]interface{}) (err error) {
|
func (s *PetService) ModifyBefore(ctx context.Context, method string, param map[string]interface{}) (err error) {
|
||||||
|
|||||||
@@ -5,6 +5,18 @@ import (
|
|||||||
"blazing/modules/blazing/model"
|
"blazing/modules/blazing/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TalkConfigService struct {
|
||||||
|
*cool.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTalkConfigService() *TalkConfigService {
|
||||||
|
return &TalkConfigService{
|
||||||
|
|
||||||
|
Service: &cool.Service{Model: model.NewMineralCollectionConfig()},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
type TalkService struct {
|
type TalkService struct {
|
||||||
BaseService
|
BaseService
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user