All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat(common/cool): 更新GetClient函数支持端口参数 更新GetClient函数签名以接收端口参数,并修改客户端映射键的计算方式, 添加GetClientOnly函数用于仅通过uid获取客户端。 fix(common/rpc): 修复RPC调用中的客户端获取方法 将GetClient调用替换为GetClientOnly,确保正确的客户端获取逻辑。 refactor(logic/controller): 重命名Port字段为UID并优化道具列表处理 将Controller结构体中的Port字段重命名为UID以更好地反映其用途, 优化GetUserItemList函数中道具列表的初始化和填充逻辑。 perf(logic): 调整性能分析web服务启动位置 将PprofWeb服务从全局启动移至调试模式下启动,优化服务配置。 refactor(logic/server): 更新服务器UID生成逻辑 修改Maincontroller的UID字段设置方式,使用服务器ID和端口组合生成唯一标识。 refactor(logic/service/player): 移除未使用的导入并优化怪物生成 移除未使用的service导入,优化怪物生成逻辑中的地图数据访问。 feat(logic/service/space): 添加PitS缓存映射并重构空间初始化 添加新的PitS字段
259 lines
6.5 KiB
Go
259 lines
6.5 KiB
Go
package service
|
||
|
||
import (
|
||
"blazing/common/data/share"
|
||
"blazing/cool"
|
||
"blazing/modules/config/service"
|
||
"blazing/modules/player/model"
|
||
"context"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/os/glog"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
"github.com/google/uuid"
|
||
csmap "github.com/mhmtszr/concurrent-swiss-map"
|
||
)
|
||
|
||
// 是否注册,如果注册过,那么就会产生用户player信息
|
||
|
||
// 实现注册,id+昵称+颜色
|
||
func (s *InfoService) Reg(nick string, color uint32) *model.PlayerInfo {
|
||
|
||
var tt *model.Player
|
||
s.dbm_fix(s.Model).Scan(&tt)
|
||
if tt == nil {
|
||
t := model.NewPlayer()
|
||
t.PlayerID = uint64(s.userid)
|
||
//设置用户信息
|
||
t.Data = model.NewPlayerInfo()
|
||
t.Data.Nick = nick
|
||
t.Data.UserID = s.userid
|
||
t.Data.Color = color
|
||
t.Data.RegisterTime = uint32(time.Now().Unix()) //写入注册时间
|
||
|
||
_, err := cool.DBM(s.Model).Data(t).FieldsEx("id").Insert()
|
||
if err != nil {
|
||
glog.Error(context.Background(), err)
|
||
|
||
}
|
||
return &t.Data
|
||
} else {
|
||
return &tt.Data
|
||
}
|
||
|
||
}
|
||
|
||
func (s *InfoService) Person(userid uint32) (out *model.Player) {
|
||
cool.DBM(s.Model).Where("player_id", userid).Scan(&out)
|
||
|
||
return
|
||
|
||
}
|
||
|
||
func (s *InfoService) SetLogin() *model.PlayerInfo {
|
||
|
||
var tt *model.Player
|
||
s.dbm_fix(s.Model).Scan(&tt)
|
||
if tt == nil {
|
||
return nil
|
||
}
|
||
tt.Data.AllPetNumber = uint32(NewPetService(s.userid).PetCount(0))
|
||
|
||
if tt.Data.MapID > 300 || tt.Data.MapID == 0 { //如果位于基地,就重置到传送仓
|
||
tt.Data.MapID = 1
|
||
|
||
}
|
||
if tt.Data.IsNewPlayer() { //重置新手地图,放到机械仓
|
||
tt.Data.SetTask(4, model.Completed) //设置新手任务默认完成
|
||
tt.Data.MapID = 8
|
||
if len(tt.Data.PetList) == 0 {
|
||
//这个是添加后防止卡死
|
||
rr := NewPetService(s.userid).PetInfo(0)
|
||
if len(rr) > 0 {
|
||
tt.Data.PetList = append(tt.Data.PetList, rr[0].Data)
|
||
}
|
||
|
||
}
|
||
}
|
||
if tt.Data.MaxPuniLv < 9 {
|
||
|
||
for i := 291; i < 299; i++ {
|
||
|
||
if tt.Data.GetTask(i) == model.Completed {
|
||
tt.Data.MaxPuniLv = uint32(i) - 290
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if !IsToday(tt.LastResetTime) { //判断是否是今天
|
||
|
||
//每天login时候检查重置时间,然后把电池,任务,挖矿重置
|
||
//挖矿需要单独存,因为防止多开挖矿
|
||
tt.LastResetTime = gtime.Now()
|
||
//每天login时候检查重置时间,然后把电池,任务,挖矿重置
|
||
//挖矿需要单独存,因为防止多开挖矿
|
||
tt.Data.TimeToday = 0 //重置电池
|
||
//tt.Data.FightTime = 60 * 60 * 2 //重置战斗次数
|
||
|
||
for _, v := range service.NewTaskService().GetDaily() {
|
||
|
||
tt.Data.SetTask(int(v.TaskId), model.Unaccepted)
|
||
|
||
}
|
||
|
||
for i := 0; i < 50; i++ { //每日任务区段
|
||
tt.Data.DailyResArr[i] = 0 //重置每日任务
|
||
|
||
}
|
||
//defer t.Service.Talk_Reset()
|
||
_, err := s.dbm_fix(s.Model).Save(tt)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
}
|
||
ret := tt.Data
|
||
|
||
return &ret
|
||
|
||
}
|
||
|
||
var User = csmap.New(
|
||
// set the number of map shards. the default value is 32.
|
||
csmap.WithShardCount[string, uint32](32),
|
||
|
||
// set the total capacity, every shard map has total capacity/shard count capacity. the default value is 0.
|
||
// csmap.WithSize[string, int](1000),
|
||
)
|
||
|
||
// 生成session
|
||
// GetSessionId 生成并返回会话ID、UUID字符串及可能的错误
|
||
// 会话ID由accountID(4字节) + UUID(16字节) + 随机数(4字节)组成,最终编码为十六进制字符串
|
||
func (s *InfoService) Gensession() string {
|
||
uuidV7, _ := uuid.NewV7()
|
||
uuidBytes := uuidV7[:] // UUID 类型底层是 [16]byte,直接切片获取
|
||
// 移除UUID中的连字符,便于后续处理
|
||
|
||
// 3. 计算 CRC32-IEEE 校验码(最通用的CRC32标准)
|
||
|
||
sessionID := hex.EncodeToString(uuidBytes)
|
||
|
||
cool.CacheManager.Set(context.Background(), fmt.Sprintf("session:%d", uint32(s.userid)), sessionID, 10*time.Minute)
|
||
// ///User.Store(string(uuidStr), uint32(s.userid))
|
||
// //share.ShareManager.SaveSession(string(uuidStr), uint32(s.userid))
|
||
return sessionID
|
||
}
|
||
|
||
func (s *InfoService) Kick(id uint32) error {
|
||
|
||
useid1, err := share.ShareManager.GetUserOnline(id)
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
cl, ok := cool.GetClientOnly(useid1)
|
||
if ok {
|
||
err := cl.KickPerson(id) //实现指定服务器踢人
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// saveToLocalFile 兜底保存:将数据写入本地lose文件夹
|
||
func (s *InfoService) saveToLocalFile(player *model.Player, err error) {
|
||
// 1. 创建lose文件夹(如果不存在)
|
||
loseDir := "./lose"
|
||
if err := os.MkdirAll(loseDir, 0755); err != nil {
|
||
fmt.Printf("[ERROR] 创建lose文件夹失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
// 2. 构造保存的数据结构,包含错误信息和时间戳
|
||
type FallbackData struct {
|
||
PlayerData *model.Player `json:"player_data"`
|
||
ErrorMsg string `json:"error_msg"`
|
||
SaveTime string `json:"save_time"`
|
||
ServerInfo string `json:"server_info"`
|
||
}
|
||
|
||
fallbackData := FallbackData{
|
||
PlayerData: player,
|
||
ErrorMsg: err.Error(),
|
||
SaveTime: time.Now().Format("20060102150405.000"), // 精确到毫秒的时间戳
|
||
ServerInfo: fmt.Sprintf("server_vip:%d", cool.Config.ServerInfo.IsVip),
|
||
}
|
||
|
||
// 3. 生成唯一的文件名(避免覆盖)
|
||
playerID := fmt.Sprintf("%d", player.PlayerID) // 假设Player有PlayerID字段,根据实际调整
|
||
filename := fmt.Sprintf("player_%s_%s.json", playerID, fallbackData.SaveTime)
|
||
filePath := filepath.Join(loseDir, filename)
|
||
|
||
// 4. 将数据序列化为JSON并写入文件
|
||
file, err := os.Create(filePath)
|
||
if err != nil {
|
||
fmt.Printf("[ERROR] 创建兜底文件失败 %s: %v\n", filePath, err)
|
||
return
|
||
}
|
||
defer file.Close()
|
||
|
||
encoder := json.NewEncoder(file)
|
||
encoder.SetIndent("", " ") // 格式化JSON,方便查看
|
||
if err := encoder.Encode(fallbackData); err != nil {
|
||
fmt.Printf("[ERROR] 写入兜底文件失败 %s: %v\n", filePath, err)
|
||
return
|
||
}
|
||
|
||
// 5. 记录日志,方便排查
|
||
fmt.Printf("[INFO] 数据库保存失败,已将玩家[%s]数据兜底保存到: %s\n", playerID, filePath)
|
||
}
|
||
func (s *InfoService) Save(data model.PlayerInfo) {
|
||
if cool.Config.ServerInfo.IsVip != 0 {
|
||
|
||
return
|
||
}
|
||
m := s.dbm_fix(s.Model)
|
||
var tt *model.Player
|
||
m.Scan(&tt)
|
||
if tt == nil {
|
||
return
|
||
}
|
||
tt.Data = data
|
||
_, err := m.Save(tt)
|
||
if err != nil {
|
||
//todo 待实现兜底保存,现在有可能出错
|
||
s.saveToLocalFile(tt, err)
|
||
panic(err)
|
||
}
|
||
|
||
}
|
||
|
||
type InfoService struct {
|
||
BaseService
|
||
}
|
||
|
||
func NewInfoService(id uint32) *InfoService {
|
||
return &InfoService{
|
||
|
||
BaseService: BaseService{userid: id,
|
||
|
||
Service: &cool.Service{Model: model.NewPlayer(), UniqueKey: map[string]string{
|
||
"player_id": "角色名称不能重复",
|
||
}, PageQueryOp: &cool.QueryOp{
|
||
FieldEQ: []string{"player_id"},
|
||
}},
|
||
},
|
||
}
|
||
|
||
}
|