| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- package model
- import (
- "container/heap"
- "math/rand"
- "rocommon/service"
- "rocommon/util"
- "roserver/baseserver/model"
- "roserver/baseserver/set"
- "roserver/serverproto"
- "sort"
- "strconv"
- )
- // 远航试炼
- const (
- YuanHangTrialViewMaxNum = 200 //最大客户端可视列表数量
- //1 你被xxx挑战,失去xxx奖励
- //2 你对xx发起了抢夺,获得了xxx奖励
- //3 你对xx发起了抢夺,但是自己无法获取到奖励
- TrialLogDataType_1 = 1
- TrialLogDataType_2 = 2
- TrialLogDataType_3 = 3
- )
- type yuanHangTrialList struct {
- isDirty bool
- itemList []*serverproto.YuanHangTrialData
- trailItemList map[uint64]*serverproto.YuanHangTrialData
- }
- func (this *yuanHangTrialList) addItem(item *serverproto.YuanHangTrialData) {
- this.itemList = append(this.itemList, item)
- this.isDirty = true
- this.trailItemList[item.Uid] = item
- }
- func (this *yuanHangTrialList) delItem(uid uint64) {
- //后续使用二分查找实现
- for idx := 0; idx < len(this.itemList); idx++ {
- if this.itemList[idx].Uid == uid {
- this.itemList = append(this.itemList[idx:], this.itemList[:idx+1]...)
- this.isDirty = true
- break
- }
- }
- delete(this.trailItemList, uid)
- }
- func (this *yuanHangTrialList) binarySearch(leftIndex, rightIndex int, findVal uint64) int {
- if leftIndex > rightIndex {
- return 0
- }
- middle := (leftIndex + rightIndex) / 2
- if this.itemList[middle].DirtyStamp > findVal {
- this.binarySearch(middle+1, rightIndex, findVal)
- } else if this.itemList[middle].DirtyStamp < findVal {
- this.binarySearch(leftIndex, middle-1, findVal)
- }
- return middle
- }
- // minheap
- type TrailItemMinHeap struct {
- ItemList []*serverproto.YuanHangTrialData
- }
- func (h *TrailItemMinHeap) Len() int {
- return len(h.ItemList)
- }
- func (h *TrailItemMinHeap) Less(i, j int) bool {
- return h.ItemList[i].EndTimeStamp < h.ItemList[j].EndTimeStamp
- }
- func (h *TrailItemMinHeap) Swap(i, j int) {
- h.ItemList[i], h.ItemList[j] = h.ItemList[j], h.ItemList[i]
- }
- func (h *TrailItemMinHeap) Push(item interface{}) {
- h.ItemList = append(h.ItemList, item.(*serverproto.YuanHangTrialData))
- }
- func (h *TrailItemMinHeap) Pop() (ret interface{}) {
- l := len(h.ItemList)
- h.ItemList, ret = h.ItemList[:l-1], h.ItemList[l-1]
- return
- }
- // //
- type YuanHangTrialManager struct {
- trailItemMinHeap *TrailItemMinHeap
- tailItemLeftList *TrailItemMinHeap //超过200后的存储列表(排序)
- trialItemMapList map[uint64]*serverproto.YuanHangTrialData //total
- //cache fightInfo
- cacheFightRoleInfoList map[uint64]*serverproto.FightRoleInfo
- dbChangeTrialItemList set.Interface
- lineNum int32
- latestTrialTime uint64 //玩家最近一次的远航操作时间戳
- notifyList []*serverproto.YuanHangTrialData
- bInit bool
- updateTimer util.ServerTimer //主循环定时器
- }
- func newYuanHangTrialManager() *YuanHangTrialManager {
- mag := &YuanHangTrialManager{
- trialItemMapList: map[uint64]*serverproto.YuanHangTrialData{},
- trailItemMinHeap: &TrailItemMinHeap{},
- tailItemLeftList: &TrailItemMinHeap{},
- cacheFightRoleInfoList: map[uint64]*serverproto.FightRoleInfo{},
- dbChangeTrialItemList: set.New(set.NonThreadSafe),
- }
- mag.updateTimer = util.NewDurationTimer(util.GetCurrentTime(), 3000)
- heap.Init(mag.trailItemMinHeap)
- heap.Init(mag.tailItemLeftList)
- return mag
- }
- func (this *YuanHangTrialManager) Update(ms uint64) {
- //if !this.bInit {
- // if this.initTrialDataFromDB() {
- // this.bInit = true
- // }
- // return
- //}
- //util.InfoF("notifyList Trial5:%v", this.notifyList)
- if !this.updateTimer.IsStart() || !this.updateTimer.IsExpired(ms) {
- return
- }
- this.initTrialDataFromDB()
- //util.InfoF("notifyList Trial1:%v", this.notifyList)
- this.refreshViewList(ms)
- //util.InfoF("notifyList Trial2:%v", this.notifyList)
- //save change uid
- this.saveItemData()
- //util.InfoF("notifyList Trial3:%v", this.notifyList)
- //notify list
- if len(this.notifyList) > 0 {
- util.InfoF("notifyList Trial4:%v", this.notifyList)
- ssNotifyMsg := &serverproto.SSCrossYuanHangTrialViewNtf{}
- ssNotifyMsg.TrialViewList = append(ssNotifyMsg.TrialViewList, this.notifyList...)
- SendAllZoneSocial(ssNotifyMsg)
- this.notifyList = this.notifyList[:0]
- }
- }
- // 服务器启动时数据初始化
- func (this *YuanHangTrialManager) initTrialDataFromDB() bool {
- tmpItemList, bOk := GetTrialItemFromRedis()
- if !bOk {
- return false
- }
- sort.Slice(tmpItemList, func(i, j int) bool {
- return tmpItemList[i].EndTimeStamp < tmpItemList[j].EndTimeStamp
- })
- for idx := 0; idx < len(tmpItemList); idx++ {
- if _, ok := this.trialItemMapList[tmpItemList[idx].Uid]; ok {
- continue
- }
- //if idx < YuanHangTrialViewMaxNum {
- // heap.Push(this.trailItemMinHeap, tmpItemList[idx])
- //} else {
- heap.Push(this.tailItemLeftList, tmpItemList[idx])
- //}
- util.InfoF("addtrailItem info=%v", tmpItemList[idx])
- this.trialItemMapList[tmpItemList[idx].Uid] = tmpItemList[idx]
- }
- list, _ := GetBeChallengeFromRedis()
- if list == nil || len(list) == 0 {
- return true
- }
- //通知被挑战者
- util.InfoF("addBeChallengeLog list=%v", list)
- for _, v := range list {
- SendZoneSocial(v, 0)
- }
- return true
- }
- func (this *YuanHangTrialManager) refreshViewList(ms uint64) {
- //更新可视列表中是否有玩家远航结束
- if this.trailItemMinHeap.Len() <= 0 && this.tailItemLeftList.Len() <= 0 {
- return
- }
- //当前可视列表中过期的玩家移除
- for {
- if this.trailItemMinHeap.Len() <= 0 {
- break
- }
- itemInfo := heap.Pop(this.trailItemMinHeap).(*serverproto.YuanHangTrialData)
- if itemInfo == nil {
- break
- }
- //玩家远航结束,需要添加新玩家到可视列表中
- if ms >= itemInfo.EndTimeStamp {
- if this.tailItemLeftList.Len() > 0 {
- addItemInfo := heap.Pop(this.tailItemLeftList).(*serverproto.YuanHangTrialData)
- if addItemInfo != nil {
- heap.Push(this.trailItemMinHeap, addItemInfo)
- this.dbChangeTrialItemList.Add(addItemInfo.Uid)
- //添加到通知列表中
- this.addNotifyList(addItemInfo)
- }
- }
- this.dbChangeTrialItemList.Add(itemInfo.Uid)
- delete(this.trialItemMapList, itemInfo.Uid)
- } else {
- heap.Push(this.trailItemMinHeap, itemInfo)
- break
- }
- }
- //添加新玩家到可视列表中(填充可视列表最大数量)
- needAddNum := YuanHangTrialViewMaxNum - this.trailItemMinHeap.Len()
- if needAddNum > 0 {
- for idx := 0; idx < needAddNum; idx++ {
- if this.tailItemLeftList.Len() <= 0 {
- break
- }
- addItemInfo := heap.Pop(this.tailItemLeftList).(*serverproto.YuanHangTrialData)
- if addItemInfo != nil {
- heap.Push(this.trailItemMinHeap, addItemInfo)
- //添加到通知列表中
- this.addNotifyList(addItemInfo)
- }
- }
- }
- }
- func (this *YuanHangTrialManager) saveItemData() {
- if this.dbChangeTrialItemList.Size() <= 0 {
- return
- }
- saveList := this.dbChangeTrialItemList.List()
- for idx := 0; idx < len(saveList); idx++ {
- saveUid := saveList[idx].(uint64)
- if item, ok := this.trialItemMapList[saveUid]; ok {
- SetTrialItemToRedis(item, saveUid)
- } else {
- //数据库中需要在获取奖励之后才删除
- //SetTrialItemToRedis(nil, saveUid)
- }
- }
- this.dbChangeTrialItemList.Clear()
- }
- func (this *YuanHangTrialManager) addTrialItem(uid uint64, trialType int32, endTimeStamp uint64, durationTime int32,
- zone, fromZone int32) *serverproto.YuanHangTrialData {
- nowTime := util.GetTimeMilliseconds()
- this.lineNum = (this.lineNum+1)%10 + 1
- if this.latestTrialTime <= 0 {
- this.latestTrialTime = nowTime
- } else if this.latestTrialTime+30000 < nowTime {
- this.latestTrialTime = nowTime
- this.lineNum = rand.Int31n(10) + 1
- }
- item := &serverproto.YuanHangTrialData{
- Uid: uid,
- TrialType: trialType,
- DirtyStamp: nowTime,
- EndTimeStamp: endTimeStamp,
- Zone: zone,
- FromRealZone: fromZone,
- DurationTime: durationTime,
- LineNum: this.lineNum,
- }
- this.trialItemMapList[item.Uid] = item
- //添加到可见列表中
- if this.trailItemMinHeap.Len() < YuanHangTrialViewMaxNum {
- heap.Push(this.trailItemMinHeap, item)
- this.addNotifyList(item)
- } else {
- heap.Push(this.tailItemLeftList, item)
- }
- this.dbChangeTrialItemList.Add(uid)
- return item
- }
- func (this *YuanHangTrialManager) addNotifyList(notifyItem *serverproto.YuanHangTrialData) {
- for idx := 0; idx < len(this.notifyList); idx++ {
- if this.notifyList[idx].Uid == notifyItem.Uid {
- this.notifyList[idx] = notifyItem
- return
- }
- }
- this.notifyList = append(this.notifyList, notifyItem)
- }
- func (this *YuanHangTrialManager) getFightRoleInfo(uid uint64) *serverproto.FightRoleInfo {
- if fightInfo, ok := this.cacheFightRoleInfoList[uid]; ok {
- return fightInfo
- }
- err, fightInfo := GetFightInfoFromRedis(uid)
- if err != nil {
- util.InfoF("getFightRoleInfo uid=%v err=%v", uid, err)
- return nil
- }
- this.cacheFightRoleInfoList[uid] = fightInfo
- return fightInfo
- }
- // 获取远航可视列表
- func (this *YuanHangTrialManager) GetTrialViewList(bViewList bool, uidTrialInfo uint64) ([]*serverproto.YuanHangTrialData, *serverproto.YuanHangTrialData) {
- if uidTrialInfo > 0 {
- if item, ok := this.trialItemMapList[uidTrialInfo]; ok {
- if bViewList {
- return this.trailItemMinHeap.ItemList, item
- } else {
- return nil, item
- }
- }
- }
- if bViewList {
- return this.trailItemMinHeap.ItemList, nil
- }
- return nil, nil
- }
- // 发起远航试炼
- // @trialType 远航类型 (1,2,3,4,5...)
- func (this *YuanHangTrialManager) YuanHangTrial(fromZone int32, uid uint64, trialType int32, endTimeStamp uint64,
- fightInfo *serverproto.FightRoleInfo, durationTime int32) *serverproto.YuanHangTrialData {
- zone := fightInfo.BriefInfo.SelectZone
- if zone <= 0 {
- zone = fromZone
- }
- addTrialItem := this.addTrialItem(uid, trialType, endTimeStamp, durationTime, zone, fromZone)
- err := SetFightInfoToRedis(uid, fightInfo)
- util.InfoF("uid=%v YuanHangTrial err=%v addTrialItem=%v", uid, err, addTrialItem)
- this.cacheFightRoleInfoList[uid] = fightInfo
- //notify other
- return addTrialItem
- }
- // 非正常情况下不调用该接口
- func (this *YuanHangTrialManager) YuanHangTrialForceSelf(ssMsg *serverproto.SSCrossYuanHangTrialSelfReq, fromZone int32) {
- _, ok := this.trialItemMapList[ssMsg.SelfUid]
- if !ok {
- nowTime := util.GetTimeMilliseconds()
- if ssMsg.EndTimeStamp > nowTime {
- zone := ssMsg.SelfFightInfo.BriefInfo.SelectZone
- if zone <= 0 {
- zone = fromZone
- }
- addTrialItem := this.addTrialItem(ssMsg.SelfUid, ssMsg.TrialType, ssMsg.EndTimeStamp, ssMsg.DurationTime, zone, fromZone)
- //err := SetFightInfoToRedis(ssMsg.SelfUid, ssMsg.SelfFightInfo)
- util.InfoF("uid=%v YuanHangTrialForceSelf addTrialItem=%v", ssMsg.SelfUid, addTrialItem)
- this.cacheFightRoleInfoList[ssMsg.SelfUid] = ssMsg.SelfFightInfo
- }
- }
- SetFightInfoToRedis(ssMsg.SelfUid, ssMsg.SelfFightInfo)
- }
- // 飞艇信息
- // trialUid 飞艇对应玩家
- // trialUidEndTime 当前玩家飞艇对应的结束时间
- func (this *YuanHangTrialManager) GetTrialInfo(trialUid, trialUidEndTime uint64) (serverproto.ErrorCode, *serverproto.YuanHangTrialData) {
- trialItem, ok := this.trialItemMapList[trialUid]
- if !ok {
- //必须是该玩家的本次远航试炼
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN, nil
- }
- nowTime := util.GetTimeMilliseconds()
- if trialItem.EndTimeStamp <= nowTime {
- //本次查询的玩家对应的飞艇已经结束
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN, nil
- }
- return serverproto.ErrorCode_ERROR_OK, trialItem
- }
- // @param selfUid 发起挑战玩家
- // @param challengeUid 被挑战玩家
- // @param beChallengeMaxNum 最大被挑战次数
- func (this *YuanHangTrialManager) GetChallengeTrialItem(selfUid, challengeUid, challengeUidEndTime uint64,
- beChallengeMaxNum int32, ssAckMsg *serverproto.SSCrossYuanHangTrialChallengeAck) serverproto.ErrorCode {
- trialItem, ok := this.trialItemMapList[challengeUid]
- if !ok || trialItem.EndTimeStamp < challengeUidEndTime {
- //必须是该玩家的本次远航试炼
- util.InfoF("uid=%v GetChallengeTrialItem challenge item not exist existUidAndTime[%v,%v] not[%v,%v]", selfUid,
- trialItem.Uid, trialItem.EndTimeStamp, challengeUid, challengeUidEndTime)
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN
- }
- nowTime := util.GetTimeMilliseconds()
- if trialItem.EndTimeStamp <= nowTime {
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN
- }
- //是否已经抢夺过一次该玩家
- for idx := 0; idx < len(trialItem.BeChallengeUidList); idx++ {
- if trialItem.BeChallengeUidList[idx] == selfUid {
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_CHALLENGED
- }
- }
- //抢夺次数上限判断
- if beChallengeMaxNum <= 0 {
- convertData, ok := model.ConvertYuanHangTrail[trialItem.TrialType]
- if ok {
- beChallengeMaxNum = convertData.BeAttackNum
- }
- }
- if trialItem.BeChallengeNum >= beChallengeMaxNum {
- return serverproto.ErrorCode_ERROR_CORSS_YUANHANGTRIAL_BE_CHALLENGE_LIMIT
- }
- //获取挑战玩家信息
- fightInfo := this.getFightRoleInfo(challengeUid)
- if fightInfo == nil {
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN
- }
- ssAckMsg.FightInfo = fightInfo
- ssAckMsg.ChallengeUid = challengeUid
- ssAckMsg.ChallengeUidEndTime = challengeUidEndTime
- return serverproto.ErrorCode_ERROR_OK
- }
- // 挑战结果通知
- func (this *YuanHangTrialManager) ChallengeTrialItemResult(selfUid, challengeUid, challengeUidEndTime uint64,
- selfNickName string, selfZone int32, bWin bool, fightInfo *serverproto.FightRoleInfo, ssAckMsg *serverproto.SSCrossYuanHangTrialChallengeResultAck) serverproto.ErrorCode {
- trialItem, ok := this.trialItemMapList[challengeUid]
- if !ok || trialItem.EndTimeStamp < challengeUidEndTime {
- //必须是该玩家的本次远航试炼
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN
- }
- nowTime := util.GetTimeMilliseconds()
- if trialItem.EndTimeStamp <= nowTime {
- return serverproto.ErrorCode_ERROR_CROSS_YUANHANGTRIAL_NOT_JOIN
- }
- convertData, ok := model.ConvertYuanHangTrail[trialItem.TrialType]
- if ok {
- if trialItem.BeChallengeNum >= convertData.BeAttackNum {
- return serverproto.ErrorCode_ERROR_CORSS_YUANHANGTRIAL_BE_CHALLENGE_LIMIT
- }
- }
- SetFightInfoToRedis(selfUid, fightInfo)
- this.cacheFightRoleInfoList[selfUid] = fightInfo
- //挑战日志记录
- beChallengePlayer := this.getFightRoleInfo(trialItem.Uid)
- if beChallengePlayer != nil {
- ssAckMsg.LogData = &serverproto.YuanHangTrialLogData{
- Type: TrialLogDataType_2,
- RecordTime: nowTime,
- State: true,
- TargetPlayerUid: trialItem.Uid,
- TargetPlayerZone: trialItem.Zone,
- TargetPlayerName: beChallengePlayer.BriefInfo.NickName,
- TrialType: trialItem.TrialType,
- }
- }
- ssAckMsg.ShipTrialLevel = trialItem.TrialType
- trialItem.BeChallengeNum++
- trialItem.BeChallengeUidList = append(trialItem.BeChallengeUidList, selfUid)
- trialItem.DirtyStamp = nowTime
- this.dbChangeTrialItemList.Add(challengeUid)
- this.addNotifyList(trialItem)
- //被挑战日志记录
- //通知被挑战玩家
- this.addBeChallengeLog(trialItem, nowTime, selfUid, selfZone, selfNickName)
- return serverproto.ErrorCode_ERROR_OK
- }
- func (this *YuanHangTrialManager) UpdateRankScore(uid, rankScore uint64) {
- UpdateTrialRankScore(uid, rankScore)
- }
- // 远航试炼排行榜
- func (this *YuanHangTrialManager) GetTrialRankList(uid uint64, startIdx int32,
- ssAckMsg *serverproto.SSCrossYuanHangTrialRankListAck) {
- selfRank, selfScore, totalRank, rankList := GetTrialRankList(uid, startIdx)
- ssAckMsg.SelfRank = selfRank
- ssAckMsg.SelfScore = selfScore
- ssAckMsg.TotalRank = totalRank
- ssAckMsg.RankList = rankList
- }
- func (this *YuanHangTrialManager) GetTrialRankReward(uid uint64, score int32, seasonId int32) int32 {
- //uid
- keyStr := strconv.FormatUint(uid, 10)
- //获取自身排名
- selfRank, err := service.GetRedis().ZRevRank(YuanHangTrialRankPrefix, keyStr).Result()
- if err != nil {
- util.ErrorF("[GetTrialRankReward][%v] selfRank err:%v", uid, err)
- return 0
- }
- util.InfoF("[GetTrialRankReward][%v] selfRank:%v", uid, selfRank+1)
- return int32(selfRank + 1)
- }
- var crossTopRankReward = map[uint64]int{}
- // 远航试炼排行榜刷新
- func (this *YuanHangTrialManager) RefreshTrialRank(lastSeasonId int32, rewardPlayerList *[]uint64, outList *[]*serverproto.KeyValueType64) serverproto.ErrorCode {
- if len(crossTopRankReward) <= 0 {
- //获得前100排行玩家数据
- for idx := 0; idx < 5; idx++ {
- startIdx := int64(idx * 20)
- rankNumList, err2 := service.GetRedis().ZRevRange(YuanHangTrialRankPrefix, startIdx, startIdx+19).Result()
- if err2 != nil {
- break
- }
- if len(rankNumList) <= 0 {
- break
- }
- for i := 0; i < len(rankNumList); i++ {
- rankUid, _ := model.Str2NumU64(rankNumList[i])
- crossTopRankReward[rankUid] = int(startIdx) + i + 1
- }
- }
- }
- for i := 0; i < len(*rewardPlayerList); i++ {
- uid := (*rewardPlayerList)[i]
- *outList = append(*outList, &serverproto.KeyValueType64{
- Key: uid,
- Value: int32(crossTopRankReward[uid]),
- })
- }
- return serverproto.ErrorCode_ERROR_OK
- }
- func (this *YuanHangTrialManager) addBeChallengeLog(trialItem *serverproto.YuanHangTrialData,
- nowTime uint64, targetPlayerUid uint64, targetPlayerZone int32, targetPlayerNickName string) {
- //通知被挑战玩家
- logNtf := &serverproto.SSCrossYuanHangTrialLogNtf{
- NtfUid: trialItem.Uid,
- }
- logNtf.LogData = &serverproto.YuanHangTrialLogData{
- Type: TrialLogDataType_1,
- RecordTime: nowTime,
- State: true,
- TargetPlayerUid: targetPlayerUid,
- TargetPlayerZone: targetPlayerZone,
- TargetPlayerName: targetPlayerNickName,
- TrialType: trialItem.TrialType,
- }
- //失去的奖励
- convertData, ok := model.ConvertYuanHangTrail[trialItem.TrialType]
- if ok {
- logNtf.LogData.ItemList = append(logNtf.LogData.ItemList, convertData.BeAttackLoseItemListSlice...)
- }
- util.InfoF("uid=%v addBeChallengeLog trialType=%v beAttackedUid=%v realZone=%v", targetPlayerUid, trialItem.TrialType, trialItem.Uid, trialItem.FromRealZone)
- //SendZoneSocial(logNtf, trialItem.FromRealZone)
- err := SetBeChallengeToRedis(trialItem.Uid, logNtf.LogData, trialItem.FromRealZone)
- if err != nil {
- util.ErrorF("uid=%v addBeChallengeLog error:%v", err)
- }
- }
|