Beispiel #1
0
        /// <summary>
        /// 根据 value 设置到排行榜中
        /// </summary>
        /// <param name="hash"></param>
        /// <param name="rankType"></param>
        /// <param name="roleId"></param>
        /// <param name="value"></param>
        /// <returns>Procudure.Success...</returns>
        protected int UpdateRank(int hash, BConcurrentKey keyHint, long roleId, long value, Zeze.Net.Binary valueEx)
        {
            int concurrentLevel = GetConcurrentLevel(keyHint.RankType);
            int maxCount        = GetRankComputeCount(keyHint.RankType);

            var concurrentKey = new BConcurrentKey(
                keyHint.RankType, hash % concurrentLevel,
                keyHint.TimeType, keyHint.Year, keyHint.Offset);

            var rank = _trank.GetOrAdd(concurrentKey);
            // remove if role exist. 看看有没有更快的算法。
            BRankValue exist = null;

            for (int i = 0; i < rank.RankList.Count; ++i)
            {
                var rankValue = rank.RankList[i];
                if (rankValue.RoleId == roleId)
                {
                    exist = rankValue;
                    rank.RankList.RemoveAt(i);
                    break;
                }
            }
            // insert if in rank. 这里可以用 BinarySearch。
            for (int i = 0; i < rank.RankList.Count; ++i)
            {
                if (rank.RankList[i].Value < value)
                {
                    rank.RankList.Insert(i, new BRankValue()
                    {
                        RoleId     = roleId,
                        Value      = value,
                        ValueEx    = valueEx,
                        AwardTaken = null == exist ? false : exist.AwardTaken
                    });
                    if (rank.RankList.Count > maxCount)
                    {
                        rank.RankList.RemoveAt(rank.RankList.Count - 1);
                    }
                    return(Procedure.Success);
                }
            }
            // A: 如果排行的Value可能减少,那么当它原来存在,但现在处于队尾时,不要再进榜。
            // 因为此时可能存在未进榜但比它大的Value。
            // B: 但是在进榜玩家比榜单数量少的时候,如果不进榜,队尾的玩家更新还在队尾就会消失。
            if (rank.RankList.Count < GetRankCount(keyHint.RankType) || // B:
                (rank.RankList.Count < maxCount && null == exist)    // A:
                )
            {
                rank.RankList.Add(new BRankValue()
                {
                    RoleId  = roleId,
                    Value   = value,
                    ValueEx = valueEx
                });
            }
            return(Procedure.Success);
        }
Beispiel #2
0
        private Rank GetRank(BConcurrentKey keyHint)
        {
            var Rank = Ranks.GetOrAdd(keyHint, (key) => new Rank());

            lock (Rank)
            {
                long now = Zeze.Util.Time.NowUnixMillis;
                if (now - Rank.BuildTime < RebuildTime)
                {
                    return(Rank);
                }
                // rebuild
                List <BRankList> datas = new List <BRankList>();
                int cocurrentLevel     = GetConcurrentLevel(keyHint.RankType);
                for (int i = 0; i < cocurrentLevel; ++i)
                {
                    var concurrentKey = new BConcurrentKey(
                        keyHint.RankType, i,
                        keyHint.TimeType, keyHint.Year, keyHint.Offset);
                    var rank = _trank.GetOrAdd(concurrentKey);
                    datas.Add(rank);
                }
                int countNeed = GetRankCount(keyHint.RankType);
                switch (datas.Count)
                {
                case 0:
                    Rank.TableValue = new BRankList();
                    break;

                case 1:
                    Rank.TableValue = datas[0].Copy();
                    break;

                default:
                    // 合并过程中,结果是新的 BRankList,List中的 BRankValue 引用到表中。
                    // 最后 Copy 一次。
                    BRankList current = datas[0];
                    for (int i = 1; i < datas.Count; ++i)
                    {
                        current = Merge(current, datas[i]);
                        if (current.RankList.Count > countNeed)
                        {
                            // 合并中间结果超过需要的数量可以先删除。
                            // 第一个current直接引用table.data,不能删除。
                            current.RankList.RemoveRange(countNeed, current.RankList.Count - countNeed);
                        }
                    }
                    Rank.TableValue = current.Copy();     // current 可能还直接引用第一个,虽然逻辑上不大可能。先Copy。
                    break;
                }
                Rank.BuildTime = now;
                if (Rank.TableValue.RankList.Count > countNeed) // 再次删除多余的结果。
                {
                    Rank.TableValue.RankList.RemoveRange(countNeed, Rank.TableValue.RankList.Count - countNeed);
                }
            }
            return(Rank);
        }
Beispiel #3
0
        // 使用异步方案构建rank。
        private void GetRankAsync(BConcurrentKey keyHint, System.Action <Rank> callback)
        {
            if (Ranks.TryGetValue(keyHint, out var rank))
            {
                long now = Zeze.Util.Time.NowUnixMillis;
                if (now - rank.BuildTime < RebuildTime)
                {
                    callback(rank);
                    return;
                }
            }
            // 异步方式没法锁住Rank,所以并发的情况下,可能多次去获取数据,多次构建,多次覆盖Ranks的cache。
            int countNeed       = GetRankCount(keyHint.RankType);
            int concurrentLevel = GetConcurrentLevel(keyHint.RankType);

            RunGetRank(keyHint,
                       // Action OnHashResult
                       (sessionId, hash, returnCode, BRankList) =>
            {
                App.Server.TryGetManualContext <ModuleRedirectAllContext>(sessionId)
                ?.ProcessHash(hash, () => new Rank(), (rank) =>
                {
                    if (returnCode != Procedure.Success)         // 只有处理成功的结果才是有效的。
                    {
                        return(returnCode);
                    }
                    if (rank.TableValue == null)
                    {
                        rank.TableValue = BRankList.CopyIfManaged();         // 本地实现的时候可能返回受管理的数据Bean,此时需要拷贝。
                    }
                    else
                    {
                        rank.TableValue = Merge(rank.TableValue, BRankList);
                    }
                    if (rank.TableValue.RankList.Count > countNeed)         // 合并中间结果超过需要的数量可以先删除。
                    {
                        rank.TableValue.RankList.RemoveRange(countNeed, rank.TableValue.RankList.Count - countNeed);
                    }
                    return(Procedure.Success);
                });
            },
                       // Action OnHashEnd
                       (context) =>
            {
                if (context.HashCodes.Count > 0)
                {
                    // 一般是超时发生时还有未返回结果的hash分组。
                    logger.Warn($"OnHashEnd: timeout with hashs: {context.HashCodes}");
                }

                var rank       = context.UserState as Rank;
                rank.BuildTime = Zeze.Util.Time.NowUnixMillis;
                Ranks[keyHint] = rank;     // 覆盖最新的数据到缓存里面。
                callback(rank);
            }
                       );
        }
Beispiel #4
0
 public virtual void RunUpdateRankWithHash(
     int hash, BConcurrentKey keyHint,
     long roleId, long value, Zeze.Net.Binary valueEx)
 {
     App.Zeze.Run(() => UpdateRankWithHash(
                      hash, keyHint, roleId, value, valueEx),
                  nameof(RunUpdateRankWithHash),
                  Zeze.TransactionModes.ExecuteInAnotherThread, hash);
 }
Beispiel #5
0
        public virtual void RunUpdateRank(
            BConcurrentKey keyHint,
            long roleId, long value, Zeze.Net.Binary valueEx)
        {
            int hash = Game.ModuleRedirect.GetChoiceHashCode();

            App.Zeze.Run(() => UpdateRank(hash, keyHint, roleId, value, valueEx),
                         nameof(RunUpdateRank), Zeze.TransactionModes.ExecuteInAnotherThread, hash);
        }
Beispiel #6
0
        /// <summary>
        /// ModuleRedirectAll 实现要求:
        /// 1)第一个参数是调用会话id;
        /// 2)第二个参数是hash-index;
        /// 3)然后是实现自定义输入参数;
        /// 4)最后是结果回调,
        ///    a) 第一参数是会话id,
        ///    b) 第二参数hash-index,
        ///    c) 第三个参数是returnCode,
        ///    d) 剩下的是自定义参数。
        /// </summary>
        protected int GetRank(long sessionId, int hash, BConcurrentKey keyHint,
                              System.Action <long, int, int, BRankList> onHashResult)
        {
            // 根据hash获取分组rank。
            int concurrentLevel = GetConcurrentLevel(keyHint.RankType);
            var concurrentKey   = new BConcurrentKey(
                keyHint.RankType, hash % concurrentLevel,
                keyHint.TimeType, keyHint.Year, keyHint.Offset);

            onHashResult(sessionId, hash, Procedure.Success, _trank.GetOrAdd(concurrentKey));
            return(Procedure.Success);
        }
Beispiel #7
0
        public virtual void RunGetRank(BConcurrentKey keyHint,
                                       System.Action <long, int, int, BRankList> onHashResult,
                                       Action <ModuleRedirectAllContext> onHashEnd
                                       )
        {
            // 默认实现是本地遍历调用,这里不使用App.Zeze.Run启动任务(这样无法等待),直接调用实现。
            int concurrentLevel = GetConcurrentLevel(keyHint.RankType);
            var ctx             = new ModuleRedirectAllContext(concurrentLevel, $"{FullName}:{nameof(RunGetRank)}")
            {
                OnHashEnd = onHashEnd,
            };
            long sessionId = App.Server.AddManualContextWithTimeout(ctx); // 处理hash分组结果需要一个上下文保存收集的结果。

            for (int i = 0; i < concurrentLevel; ++i)
            {
                GetRank(sessionId, i, keyHint, onHashResult);
            }
        }
Beispiel #8
0
 // 名字必须和RunUpdateRankWithHash匹配,内部使用一样的实现。
 protected int UpdateRankWithHash(
     int hash, BConcurrentKey keyHint,
     long roleId, long value, Zeze.Net.Binary valueEx)
 {
     return(UpdateRank(hash, keyHint, roleId, value, valueEx));
 }