============================排行榜===========================
实时排行榜就直接游戏里内存实时排序:
用于排名的分数区间不大,也就是 0 分到 5000 分。而参与排名的人数众多,数以百万计。对百万用户做插入排序,每个插入即使是 O(N) 的也不可接受。可事实是大量玩家的分数相同,都是并列排名的。所以我们只需要做 5000 个桶,每个桶里仅记录这个分数有多少个人就可以了。
当玩家分数变迁,把原来的桶减一,新的桶加一。这个操作就是 O(1) 的。
而排行榜的查询仅需要把当前分数靠前的桶累加,就能获知查询者的名次。对于上百万玩家,看到哪些人和你并列的人的名字是没有意义的。这个查询虽然是 O(n) 复杂度,但 n 只有区区 5000 ,还可以做 cache 以应对查询频率远高于更新频率的情况。
真正需要精确知道人名的是榜单的前 200 个人,而对前 200 个人做插入排序也很快,所以并不会造成性能问题。
我们在系统的单点做排行榜的维持,完全没有外部数据库操作,它只是一小段操作普通内存结构的 c 代码。而这个单点远远成为不了整个系统的热点。
我们在系统临时退出时,把已经排好的榜单落地,下次启动的时候恢复。但也不必完全信任落地的数据,可以用离线脚本检索整个数据库重新生成一份正确的榜单。所以数据库中的榜单只是被 cache 起来而已,系统运行期间是不需要写入数据库的,也不用担心数据丢失。
非实时排行榜可以依赖于数据库的order by排序,虽然会造成数据库io操作,但是影响不大。
=======================事件分发=========================
NewEvent 产生事件,创建协程分发(在RegEvent注册过的事件中通过事件名字找到对应的信息,然后执行对应模块的OnEvent函数),多个地方都注册了同一个事件的话,则所有监视该事件的模块都会收到
--Condition:事件条件,满足条件时事件才分发
--Args:附加参数
function clsCoreObj:RegEvent(Obj, Name, Condition, Args)
if not Name then return end
local List
--print("注册事件", Obj, Name, Condition)
--table.print(Condition)
if self.__EventLock == Name then
self.__EventLockAdd = self.__EventLockAdd or {}
List = self.__EventLockAdd
else
if not self.__Event then
self.__Event = {[Name] = {}}
elseif not self.__Event[Name] then
self.__Event[Name] = {}
end
List = self.__Event[Name]
end
if not List then return end
for _, v in ipairs(List) do
if v and v.Obj == Obj and
EventConditionCmp(v.Condition, Condition)