561 lines
14 KiB
Go
561 lines
14 KiB
Go
package sociaty
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"go_dreamfactory/comm"
|
|
"go_dreamfactory/lego/core"
|
|
"go_dreamfactory/lego/sys/log"
|
|
rn "go_dreamfactory/lego/sys/redis"
|
|
"go_dreamfactory/lego/sys/redis/pipe"
|
|
"go_dreamfactory/modules"
|
|
"go_dreamfactory/pb"
|
|
"go_dreamfactory/sys/configure"
|
|
"go_dreamfactory/utils"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/go-redis/redis/v8"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
)
|
|
|
|
var (
|
|
BOSS_SPORTS = "sports"
|
|
BOSS_PERSONAL_RANK = "personalrank" //个人排行key
|
|
BOSS_SOCIATY_RANK = "sociatyrank" //公会排名
|
|
SPORTS_HOUR = 3 * 24 //赛季周期
|
|
)
|
|
|
|
type ModelSociatyBoss struct {
|
|
modules.MCompModel
|
|
moduleSociaty *Sociaty
|
|
service core.IService
|
|
}
|
|
|
|
func (this *ModelSociatyBoss) Init(service core.IService, module core.IModule, comp core.IModuleComp, options core.IModuleOptions) (err error) {
|
|
this.TableName = comm.TableSociatyBoss
|
|
err = this.MCompModel.Init(service, module, comp, options)
|
|
this.moduleSociaty = module.(*Sociaty)
|
|
this.service = service
|
|
return
|
|
}
|
|
|
|
// 初始化赛季
|
|
func (s *ModelSociatyBoss) initSports() error {
|
|
if !s.moduleSociaty.IsCross() {
|
|
return nil
|
|
}
|
|
ticker := time.NewTicker(time.Second)
|
|
sports := &pb.DBSociatyBossSports{}
|
|
if err := s.Get(BOSS_SPORTS, sports); err != nil {
|
|
if err == rn.RedisNil || err == mongo.ErrNoDocuments {
|
|
sports.EndTime, sports.SettlementTime = s.sportsTime()
|
|
if err2 := s.Add(BOSS_SPORTS, sports); err2 != nil {
|
|
return err2
|
|
}
|
|
} else {
|
|
s.moduleSociaty.Errorln(err)
|
|
return err
|
|
}
|
|
}
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
now := configure.Now()
|
|
//触发结算
|
|
if now.Unix() >= sports.SettlementTime {
|
|
_, sports.SettlementTime = s.sportsTime()
|
|
s.settlement()
|
|
s.moduleSociaty.Debugln("更新赛季结算时间:" + utils.FormatStr(sports.SettlementTime, ""))
|
|
}
|
|
if now.Unix() >= sports.EndTime {
|
|
sports.EndTime, sports.SettlementTime = s.sportsTime()
|
|
//归档前赛季数据
|
|
s.resetSportsData()
|
|
// 更新下一赛季周期结束时间
|
|
update := map[string]interface{}{
|
|
"endTime": sports.EndTime,
|
|
"settlementTime": sports.SettlementTime,
|
|
"uids": []string{},
|
|
}
|
|
if err := s.Change(BOSS_SPORTS, update); err != nil {
|
|
s.moduleSociaty.Errorln("Failed to change")
|
|
return
|
|
}
|
|
s.moduleSociaty.Debugln("更新赛季周期时间:" + utils.FormatStr(sports.EndTime, ""))
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
// 获取赛季数据
|
|
func (s *ModelSociatyBoss) getSociatyBossSports() *pb.DBSociatyBossSports {
|
|
sports := &pb.DBSociatyBossSports{}
|
|
if err := s.Get(BOSS_SPORTS, sports); err != nil {
|
|
s.moduleSociaty.Errorln(err)
|
|
return nil
|
|
}
|
|
return sports
|
|
}
|
|
|
|
// 更新赛季数据
|
|
func (s *ModelSociatyBoss) updateSociatyBossSports(data map[string]interface{}) {
|
|
if err := s.Change(BOSS_SPORTS, data); err != nil {
|
|
s.moduleSociaty.Error("更新赛季数据", log.Field{Key: "err", Value: err.Error()})
|
|
}
|
|
}
|
|
|
|
// 赛季结算和结束时间
|
|
func (s *ModelSociatyBoss) sportsTime() (t1, t2 int64) {
|
|
now := configure.Now()
|
|
end1 := now.Add(time.Duration(SPORTS_HOUR) * time.Hour) //测试时单位分钟 else Hour
|
|
h, _ := time.ParseDuration("-1h") //测试时-2m else -1h
|
|
end2 := end1.Add(h)
|
|
return end1.Unix(), end2.Unix()
|
|
}
|
|
|
|
// 公会BOSS赛季是否开始
|
|
func (s *ModelSociatyBoss) sportsIsStarted() bool {
|
|
sports := s.getSociatyBossSports()
|
|
if sports == nil {
|
|
return false
|
|
}
|
|
if sports.EndTime == 0 {
|
|
return false
|
|
} else {
|
|
now := configure.Now().Unix()
|
|
end := sports.SettlementTime
|
|
if now < end {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 公会BOSS赛季是否结束
|
|
func (s *ModelSociatyBoss) sportsIsFinished() bool {
|
|
sports := s.getSociatyBossSports()
|
|
if sports == nil {
|
|
return false
|
|
}
|
|
if sports.EndTime == 0 {
|
|
return true
|
|
} else {
|
|
now := configure.Now().Unix()
|
|
end := sports.SettlementTime
|
|
if now > end && now < sports.EndTime {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 设置阵容
|
|
func (s *ModelSociatyBoss) setFormation(sociaty *pb.DBSociaty, uid string, teams map[int32]*pb.ChallengeTeam) error {
|
|
if !s.moduleSociaty.modelSociaty.isMember(uid, sociaty) {
|
|
return comm.NewCustomError(pb.ErrorCode_SociatyBelongTo)
|
|
}
|
|
for _, m := range sociaty.Members {
|
|
if m.Uid == uid {
|
|
m.Teams = teams
|
|
}
|
|
}
|
|
|
|
update := map[string]interface{}{
|
|
"members": sociaty.Members,
|
|
}
|
|
return s.moduleSociaty.modelSociaty.updateSociaty(sociaty.Id, update)
|
|
}
|
|
|
|
//挑战开始
|
|
func (s *ModelSociatyBoss) challengestart(session comm.IUserSession, sociaty *pb.DBSociaty) error {
|
|
//组装阵容数据
|
|
var formations []*pb.BattleFormation
|
|
m := s.moduleSociaty.modelSociaty.getMemberInfo(sociaty, session.GetUserId())
|
|
for _, v := range m.Teams {
|
|
formations = append(formations, v.Formation)
|
|
}
|
|
|
|
if len(formations) == 0 {
|
|
return comm.NewCustomError(pb.ErrorCode_SociatyNoFormation)
|
|
}
|
|
|
|
iBattle, err := s.service.GetModule(comm.ModuleBattle)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if b, y := iBattle.(comm.IBattle); y {
|
|
if code, _ := b.CreatePvbBattle(session, &pb.BattlePVBReq{
|
|
Ptype: pb.PlayType_sociaty,
|
|
Title: "公会BOSS",
|
|
Format: formations,
|
|
}); code != pb.ErrorCode_Success {
|
|
return comm.NewCustomError(code)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 挑战结束
|
|
func (s *ModelSociatyBoss) challengefinish(sociaty *pb.DBSociaty, uid string, report *pb.BattleReport) error {
|
|
// BOSS无伤害
|
|
if report.Harm == 0 {
|
|
return nil
|
|
}
|
|
|
|
sm := s.moduleSociaty.modelSociaty.getMemberInfo(sociaty, uid)
|
|
if sm == nil {
|
|
s.moduleSociaty.Error("没有该成员", log.Field{Key: "uid", Value: uid}, log.Field{Key: "sociatyId", Value: sociaty.Id})
|
|
return comm.NewCustomError(pb.ErrorCode_SociatyNoMember)
|
|
}
|
|
|
|
//保存挑战记录
|
|
record := &pb.ChallengeRecord{
|
|
Teams: sm.Teams,
|
|
Integral: s.transIntegral(report.Harm),
|
|
Duration: report.Costtime,
|
|
Rtime: configure.Now().Unix(),
|
|
}
|
|
if err := s.updateChallengeRecord(uid, sociaty.Id, record); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.addSociatyBossUser(uid)
|
|
return nil
|
|
}
|
|
|
|
// 添加参赛玩家
|
|
func (s *ModelSociatyBoss) addSociatyBossUser(uid string) {
|
|
sports := s.getSociatyBossSports()
|
|
if sports == nil {
|
|
return
|
|
}
|
|
|
|
if len(sports.Uids) == 0 {
|
|
sports.Uids = append(sports.Uids, uid)
|
|
} else {
|
|
if _, ok := utils.Findx(sports.Uids, uid); !ok {
|
|
sports.Uids = append(sports.Uids, uid)
|
|
}
|
|
}
|
|
|
|
update := map[string]interface{}{
|
|
"uids": sports.Uids,
|
|
}
|
|
if err := s.Change(BOSS_SPORTS, update); err != nil {
|
|
s.moduleSociaty.Error("添加参赛玩家", log.Field{Key: "err", Value: err.Error()})
|
|
}
|
|
}
|
|
|
|
// 获取玩家赛季挑战记录
|
|
func (s *ModelSociatyBoss) getChallengeRecord(uid string) *pb.DBSociatyBossRecord {
|
|
his := &pb.DBSociatyBossRecord{}
|
|
if err := s.Get(uid, his); err != nil {
|
|
return nil
|
|
}
|
|
if his.Uid == "" {
|
|
return nil
|
|
}
|
|
return his
|
|
}
|
|
|
|
// 挑战记录 作为阵容推荐的依据;
|
|
func (s *ModelSociatyBoss) updateChallengeRecord(uid, sociatyId string, record *pb.ChallengeRecord) error {
|
|
his := &pb.DBSociatyBossRecord{}
|
|
if err := s.Get(uid, his); err != nil {
|
|
if err == rn.RedisNil || err == mongo.ErrNoDocuments {
|
|
if err := s.Add(uid, &pb.DBSociatyBossRecord{
|
|
Uid: uid,
|
|
SociatyId: sociatyId,
|
|
Record: []*pb.ChallengeRecord{record},
|
|
}); err != nil {
|
|
return comm.NewCustomError(pb.ErrorCode_DBError)
|
|
}
|
|
} else {
|
|
return comm.NewCustomError(pb.ErrorCode_DBError)
|
|
}
|
|
}
|
|
|
|
//更新挑战记录
|
|
his.Record = append(his.Record, record)
|
|
update := map[string]interface{}{
|
|
"record": his.Record,
|
|
}
|
|
if err := s.Change(uid, update); err != nil {
|
|
return comm.NewCustomError(pb.ErrorCode_DBError)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 记入个人排行
|
|
func (s *ModelSociatyBoss) addPersonalRank(uid string, integral int64) error {
|
|
var (
|
|
pipe *pipe.RedisPipe = s.Redis.RedisPipe(context.TODO())
|
|
m *redis.Z
|
|
)
|
|
m = &redis.Z{Score: float64(integral), Member: uid}
|
|
if cmd := pipe.ZAdd(fmt.Sprintf("%s:%s", s.TableName, BOSS_PERSONAL_RANK), m); cmd != nil {
|
|
_, err := cmd.Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err := pipe.Exec(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 记入公会排行
|
|
func (s *ModelSociatyBoss) addSociatyRank(uid string, integral int64) error {
|
|
dbr := s.getChallengeRecord(uid)
|
|
if dbr == nil {
|
|
return errors.New("没有挑战记录")
|
|
}
|
|
|
|
if dbr.SociatyId != "" {
|
|
var pipe *pipe.RedisPipe = s.Redis.RedisPipe(context.TODO())
|
|
if cmd := pipe.ZIncrBy(fmt.Sprintf("%s:%s", s.TableName, BOSS_SOCIATY_RANK), float64(integral), dbr.SociatyId); cmd != nil {
|
|
_, err := cmd.Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err := pipe.Exec(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 伤害转换积分
|
|
func (s *ModelSociatyBoss) transIntegral(harm int32) int64 {
|
|
return int64(harm)
|
|
}
|
|
|
|
// 查询排行榜
|
|
func (s *ModelSociatyBoss) queryRankUid(count int64) (ranks []string, err error) {
|
|
var (
|
|
result []string
|
|
)
|
|
if result, err = s.DBModel.Redis.ZRevRange(s.TableName, 0, count).Result(); err != nil {
|
|
return
|
|
}
|
|
ranks = make([]string, 0)
|
|
for i := 0; i < len(result); i += 1 {
|
|
ranks = append(ranks, result[i])
|
|
}
|
|
return
|
|
}
|
|
|
|
// 取积分
|
|
func (s *ModelSociatyBoss) getScoreByUid(key, member string) int64 {
|
|
result, err := s.DBModel.Redis.ZScore(key, member)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return int64(result)
|
|
}
|
|
|
|
// 取排名
|
|
func (s *ModelSociatyBoss) getRankingByMember(key, member string) int64 {
|
|
result, err := s.DBModel.Redis.ZRevRank(key, member)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return (result + 1)
|
|
}
|
|
|
|
// 排行榜
|
|
func (s *ModelSociatyBoss) bossRank(sociaty *pb.DBSociaty, rankType int32) (res []*pb.SociatyRankInfo) {
|
|
var (
|
|
rankCount int64
|
|
key string
|
|
)
|
|
|
|
// 所有排行记录(个人、公会)
|
|
rank := func() []*pb.SociatyRankInfo {
|
|
rankUids, err := s.queryRankUid(rankCount)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
for _, uid := range rankUids {
|
|
imodule, err := s.service.GetModule(comm.ModuleUser)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if iuser, ok := imodule.(comm.IUser); ok {
|
|
if user := iuser.GetUser(uid); user != nil && user.Uid != "" {
|
|
res = append(res, &pb.SociatyRankInfo{
|
|
SociatyName: sociaty.Name,
|
|
Name: user.Name,
|
|
Head: user.Avatar,
|
|
Lv: user.Lv,
|
|
Ranking: s.getRankingByMember(key, user.Uid),
|
|
Integral: s.getScoreByUid(key, user.Uid),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
switch rankType {
|
|
case 1: //个人
|
|
rankCount = 1000 //数据数量
|
|
key = fmt.Sprintf("%s:%s", s.TableName, BOSS_PERSONAL_RANK)
|
|
rank()
|
|
case 2: //公会
|
|
rankCount = 50 //数据数量
|
|
key = fmt.Sprintf("%s:%s", s.TableName, BOSS_SOCIATY_RANK)
|
|
rank()
|
|
case 3: //公会成员排行
|
|
for _, m := range sociaty.Members {
|
|
imodule, err := s.service.GetModule(comm.ModuleUser)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if iuser, ok := imodule.(comm.IUser); ok {
|
|
if user := iuser.GetUser(m.Uid); user != nil && user.Uid != "" {
|
|
res = append(res, &pb.SociatyRankInfo{
|
|
SociatyName: sociaty.Name,
|
|
Name: user.Name,
|
|
Head: user.Avatar,
|
|
Lv: user.Lv,
|
|
Ranking: s.getRankingByMember(key, user.Uid),
|
|
Integral: s.getScoreByUid(key, user.Uid),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
sort.SliceStable(res, func(i, j int) bool {
|
|
return res[i].Integral > res[j].Integral
|
|
})
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 公会BOSS重置数据(每个周期)
|
|
func (s *ModelSociatyBoss) resetSportsData() error {
|
|
sports := s.getSociatyBossSports()
|
|
if sports == nil {
|
|
return errors.New("sociatyboss sports is nil")
|
|
}
|
|
|
|
// 归档前赛季数据
|
|
for _, uid := range sports.Uids {
|
|
var record []*pb.DBSociatyBossRecord
|
|
_ = s.GetList(uid, &record)
|
|
for _, v := range record {
|
|
if err := s.DelByUId(uid); err != nil {
|
|
s.moduleSociaty.Error("清理玩家赛季记录", log.Field{Key: "uid", Value: v.Uid}, log.Field{Key: "err", Value: err.Error()})
|
|
}
|
|
}
|
|
}
|
|
// 清理排行榜
|
|
// if err := s.Del(BOSS_PERSONAL_RANK); err != nil {
|
|
// s.moduleSociaty.Error("清理排行榜", log.Field{Key: "err", Value: err.Error()})
|
|
// }
|
|
return nil
|
|
}
|
|
|
|
// 是否参赛
|
|
func (s *ModelSociatyBoss) IsInSports(uid string) bool {
|
|
sports := s.getSociatyBossSports()
|
|
if sports == nil {
|
|
return false
|
|
}
|
|
for _, v := range sports.Uids {
|
|
if v == uid {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 赛季结算
|
|
func (s *ModelSociatyBoss) settlement() error {
|
|
sports := s.getSociatyBossSports()
|
|
if sports == nil {
|
|
return errors.New("sociatyboss sports is nil")
|
|
}
|
|
// 计算玩家的赛季积分
|
|
for _, uid := range sports.Uids {
|
|
cr := s.getChallengeRecord(uid)
|
|
//排序-倒叙
|
|
sort.SliceStable(cr.Record, func(i, j int) bool {
|
|
return cr.Record[i].Integral > cr.Record[j].Integral
|
|
})
|
|
|
|
var total int64 //当前赛季总积分
|
|
var highScore []int64 //暂存玩家积分
|
|
for i, v := range cr.Record {
|
|
highScore = append(highScore, v.Integral)
|
|
total += v.Integral
|
|
if i > 2 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// 更新玩家赛季历史记录
|
|
isExist := func(tasks []*pb.ChallengeTask, taskId int32) bool {
|
|
for _, task := range tasks {
|
|
if task.TaskId == taskId {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
// 判断积分任务是否达标
|
|
var tasks []*pb.ChallengeTask
|
|
var taskFlag bool //任务更新状态
|
|
taskList := s.moduleSociaty.configure.getBossTaskList()
|
|
for _, task := range taskList {
|
|
if total >= int64(task.Score) && !isExist(cr.Tasks, task.Id) {
|
|
tasks = append(tasks, &pb.ChallengeTask{
|
|
TaskId: task.Id,
|
|
Status: 1, //可领取
|
|
})
|
|
taskFlag = true
|
|
}
|
|
}
|
|
|
|
cr.Total = total
|
|
cr.Integrals = highScore
|
|
update := map[string]interface{}{
|
|
"total": total,
|
|
"integrals": highScore,
|
|
}
|
|
|
|
// 任务状态有变化时更新
|
|
if taskFlag {
|
|
update["tasks"] = tasks
|
|
cr.Tasks = tasks
|
|
}
|
|
if err := s.Change(uid, update); err != nil {
|
|
s.moduleSociaty.Error("更新玩家赛事信息", log.Field{Key: "uid", Value: uid}, log.Field{Key: "err", Value: err.Error()})
|
|
continue
|
|
}
|
|
|
|
// 将玩家积分加入排行榜
|
|
s.addPersonalRank(uid, total)
|
|
// 将玩家积分加入公会排行榜
|
|
s.addSociatyRank(uid, total)
|
|
|
|
s.moduleSociaty.Debug("结算:", log.Field{Key: "uid", Value: uid})
|
|
|
|
}
|
|
|
|
//归档前10的玩家记录
|
|
|
|
return nil
|
|
}
|