Files
bl/logic/service/space/space.go
xinian c021b40fbe
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat: 增强踢人逻辑与BOSS脚本支持
优化踢人超时处理和僵尸连接清理,支持BOSS动作脚本并增加测试,修复事件匹配与战斗循环中的并发问题。
2026-04-05 21:59:22 +08:00

322 lines
6.2 KiB
Go

package space
import (
"blazing/common/data/xmlres"
"blazing/common/utils"
"blazing/cool"
"strconv"
"strings"
"sync/atomic"
"time"
"blazing/logic/service/common"
"blazing/logic/service/space/info"
"blazing/modules/config/model"
"blazing/modules/config/service"
infomodel "blazing/modules/player/model"
"github.com/gogf/gf/v2/util/grand"
csmap "github.com/mhmtszr/concurrent-swiss-map"
"github.com/samber/lo"
"github.com/tnnmigga/enum"
)
var WeatherStatus = enum.New[struct {
Normal uint32 `enum:"0"`
Rain uint32 `enum:"1"`
Snow uint32 `enum:"2"`
}]()
type Space struct {
User *csmap.CsMap[uint32, common.PlayerI]
UserInfo *csmap.CsMap[uint32, info.SimpleInfo]
CanRefresh bool
Super uint32
ID uint32
Name string
Owner ARENA
MapBossSInfo info.MapModelBroadcastInfo
WeatherType []uint32
TimeBoss info.S2C_2022
IsTime bool
DropItemIds []uint32
PitS *csmap.CsMap[int, []model.MapPit]
}
func NewSpace() *Space {
ret := &Space{
User: csmap.New[uint32, common.PlayerI](),
UserInfo: csmap.New[uint32, info.SimpleInfo](),
}
return ret
}
func GetSpace(id uint32) *Space {
planet, ok := planetmap.Load(id)
if ok {
return planet
}
ret := NewSpace()
ret.ID = id
defer ret.init()
planetmap.Store(id, ret)
return ret
}
var planetmap = csmap.New[uint32, *Space]()
func ParseCoordinateString(s string) []infomodel.Pos {
var points []infomodel.Pos
if strings.TrimSpace(s) == "" {
return points
}
coordStrs := strings.Split(s, "|")
for _, coordStr := range coordStrs {
coordStr = strings.TrimSpace(coordStr)
if coordStr == "" {
return nil
}
xy := strings.Split(coordStr, ",")
if len(xy) != 2 {
return nil
}
xStr := strings.TrimSpace(xy[0])
yStr := strings.TrimSpace(xy[1])
x, err := strconv.Atoi(xStr)
if err != nil {
return nil
}
y, err := strconv.Atoi(yStr)
if err != nil {
return nil
}
points = append(points, infomodel.Pos{X: uint32(x), Y: uint32(y)})
}
return points
}
func (t *Space) Next(time.Time) time.Time {
return time.Now().Add(grand.D(10*time.Minute, 30*time.Minute))
}
func (ret *Space) init() {
if ret.ID < 10000 {
for _, v := range xmlres.MapConfig.Maps {
if v.ID == int(ret.ID) {
ret.Super = uint32(v.Super)
if ret.Super == 0 {
ret.Super = uint32(v.ID)
}
ret.ID = uint32(v.ID)
_, ok := maphot[ret.Super]
if !ok {
maphot[ret.Super] = &MapTip{}
maphot[ret.Super].TipInfoS = make(map[uint32]*TipInfo, 0)
}
ret.Name = v.Name
break
}
}
tips := &TipInfo{}
r := service.NewMapService().GetData(ret.ID)
if r != nil {
tips.Diao = service.NewMapService().GetData(ret.ID).DropItemIds
}
pits := service.NewMapPitService().GetDataALL(ret.ID)
ret.PitS = csmap.New[int, []model.MapPit]()
for _, v := range pits {
tips.Pet = append(tips.Pet, v.RefreshID...)
for _, vp := range v.Pos {
t, ok := ret.PitS.Load(vp)
if ok {
t = append(t, v)
ret.PitS.Store(vp, t)
} else {
ret.PitS.Store(vp, []model.MapPit{v})
}
}
}
if ret.Super != 0 {
tips.Pet = lo.Union(tips.Pet)
tips.Talk = service.NewTalkConfigService().GetTip(ret.ID)
tips.Boss = service.NewMapNodeService().GetTip(ret.ID)
maphot[ret.Super].TipInfoS[ret.ID] = tips
}
}
r := service.NewMapService().GetData(ret.ID)
if r != nil {
ret.DropItemIds = r.DropItemIds
if r.IsTimeSpace != 0 {
ret.IsTime = true
}
ret.MapBossSInfo = info.MapModelBroadcastInfo{}
ret.MapBossSInfo.INFO = make([]info.MapModelBroadcastEntry, 0)
if len(r.WeatherType) > 1 {
ret.WeatherType = r.WeatherType
cool.Cron.CustomFunc(ret, ret.GenWer)
}
for _, v := range service.NewMapNodeService().GetDataB(ret.ID) {
r := service.NewMapmodelService().GetDataByModelId(v.IsBroadcast)
if r == nil {
continue
}
info := info.MapModelBroadcastEntry{
ModelID: uint32(r.ID),
Region: v.NodeID,
Hp: r.HP,
PosInfo: ParseCoordinateString(r.Pos),
Config: v,
Model: *r,
}
ret.MapBossSInfo.INFO = append(ret.MapBossSInfo.INFO, info)
}
if len(ret.MapBossSInfo.INFO) > 0 {
cool.Cron.ScheduleFunc(10*time.Second, func() {
ret.Broadcast(nil, 2022, ret.GenBoss(false))
})
cool.Cron.ScheduleFunc(300*time.Second, ret.HealHP)
}
}
}
func (p *Space) IsMatch(t model.Event) bool {
if len(t.Weather) > 0 {
_, ok := lo.Find(t.Weather, func(item int32) bool {
return item == int32(p.MapBossSInfo.Wer)
})
if !ok {
return false
}
}
if t.StartTime != "" && t.EndTime != "" {
ok, _ := utils.IsCurrentTimeInRange(t.StartTime, t.EndTime)
if !ok {
return false
}
}
if len(t.Week) > 0 {
week := int32(time.Now().Weekday())
if week == 0 {
week = 7
}
_, ok := lo.Find(t.Week, func(item int32) bool {
return item == week
})
if !ok {
return false
}
}
return true
}
func (ret *Space) GenBoss(isfrist bool) *info.MapModelBroadcastInfo {
var res info.MapModelBroadcastInfo
res.Wer = ret.MapBossSInfo.Wer
for i := 0; i < len(ret.MapBossSInfo.INFO); i++ {
if !ret.IsMatch(*ret.MapBossSInfo.INFO[i].Config.Event) {
if ret.MapBossSInfo.INFO[i].IsShow != 0 {
ret.MapBossSInfo.INFO[i].IsShow = 0
res.INFO = append(res.INFO, ret.MapBossSInfo.INFO[i])
}
continue
}
s := len(ret.MapBossSInfo.INFO[i].PosInfo)
if s != 0 {
ret.MapBossSInfo.INFO[i].PosIndex = uint32(grand.Intn(s))
}
ret.MapBossSInfo.INFO[i].IsShow = 1
if isfrist {
ret.MapBossSInfo.INFO[i].IsShow = 2
}
res.INFO = append(res.INFO, ret.MapBossSInfo.INFO[i])
}
return &res
}
func (ret *Space) HealHP() {
for _, v := range ret.MapBossSInfo.INFO {
atomic.StoreInt32(&v.Hp, int32(v.Model.HP))
}
}
func (ret *Space) GenWer() {
var neww uint32 = 0
if len(ret.WeatherType) == 2 {
neww, _ = utils.RandomByWeight(ret.WeatherType, []uint32{9, 1})
} else {
neww, _ = utils.RandomByWeight(ret.WeatherType, []uint32{8, 1, 1})
}
if neww != uint32(ret.MapBossSInfo.Wer) {
ret.MapBossSInfo.Wer = int32(neww)
ret.Broadcast(nil, 2022, ret.GenBoss(true))
println(ret.Name, "change weather", neww)
}
}
func (ret *Space) GetDrop() int64 {
if len(ret.DropItemIds) > 0 {
item := int64(ret.DropItemIds[grand.Intn(len(ret.DropItemIds))])
return int64(item)
}
return 0
}