/// <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); }
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); }
// 使用异步方案构建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); } ); }
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); }
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); }
/// <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); }
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); } }
// 名字必须和RunUpdateRankWithHash匹配,内部使用一样的实现。 protected int UpdateRankWithHash( int hash, BConcurrentKey keyHint, long roleId, long value, Zeze.Net.Binary valueEx) { return(UpdateRank(hash, keyHint, roleId, value, valueEx)); }