- 首先来解释一下什么是路由数据格式,这个呢,是一个非官方的描述词。就是是我感觉应该叫这个比较合适,但是并不明白各位大佬怎么叫这种数据格式。要用到这种数据格式的起因也比较简单,是我们的的游戏里边要有一个掉落系统。比如说,在某些场景下,某些东西死了或者被破坏掉了会按照某些规则掉落某些东西。当然路由还可以做其他很有意思的事情,我拿它来做的事情包含掉落总共有三个,不过其他两个优点类似一会一块说
掉落的规则,是这样的,我首先需要计算掉率比如说 50%,那么就会按照这个掉率计算一下,看掉还是不掉;如果掉了,那么就计算数量,可能是一个范围,然后随机一下具体掉落几个;确定了个数,然后找到对应条件下的可掉落列表,然后从可掉落列表中筛选出具体都要掉落什么东西。这三个相关数据,都是相同的规则,就是通过其他条件获取的。其他条件的数据中间的可变化数据总共有五个,地图名称,对象类型,对象名称,对象品质,权重值。需要根据这五个具体传入的值来匹配最合适的那条数据。或许你觉得这个可以通过各种 if else 来实现,但是这种实现方式需求一旦添加或者变化的时候,会带来很大的问题,具体的问题我不做赘述能懂的人自然能动是什么问题。
其实已经说得比较明白了,但是没有接触过这个的看到这个东西可能是一脸懵逼,不知道痛点在什么地方。所以我还是再分析一下可能出现的情况就能把握痛点了。
简单点说就是可能会出现各种各样的组合关系,具体的掉落的所需的参数可能跟不同的参数都有关系,也可能都没有关系。比如说,什么都没有匹配到数据的话系统可能会提供一个默认的数据,比如说掉率的 0,掉落数量的 0,空的掉落对象列表。
可能你看到上边的需求并不能直接考虑到怎么处理才能达到这种效果,我们还是先来认识一下路由数据格式(当然这个名字是我随便取的,不要拿来直接跟别人说这个,别人应该听不懂)吧。这个数据格式呢,是我参照了 ASP.NET 中的路由表来做的。ASP 的路由数据的添加是这样色的。
- routes.MapRoute(
- name: "Default",
- url: "{controller}/{action}/{id}",
- defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
- );
这一小段代码的意思其实非常简单,就是注册了一个路由数据,名称是 Default,然后 URL 的格式是 {controller}/{action}/{id},然后还附带了部分的默认数据,如果 Url 格式中需要的数据没有的时候,就会使用默认数据填充起来,当然真正的项目中不会只有一条路由规则,而是会有很多很多的路由规则,然后 URL 会自动进行最匹配的一条。通过一组 Url 然后定位到某个 Controller 中的的某个函数,这个就是这个路由的主要作用。其实这个场景跟我们要解决的问题还挺相似的,都是通过一组数据匹配到一个最合适的结果,然后使用这个结果完成后续的逻辑。所以让我们来整理一下路由数据格式的相关需求,然后来实现一个简单的路由格式的数据吧。
其实上边这两个需求看上去真的很简单。总结起来就是,路由数据就是一个一个带有自己数据格式化规则与数据优先级的大字典。首先通过格式化功能部分讲原本的数据格式化内部可能需要的数据,然后在将格式化后的数据作为 Key 依次向后数据匹配,当匹配到第一条数据之后则作为结果返回。
不废话直接上代码,我的代码中写的已经比较明确了,每一个函数都是用来干什么的。懂 Lua 代码的应该非常容易就能看懂了,不懂 Lua 的仔细看看应该也能看懂毕竟都是 C 语系的都大差不差
- local RouteData = class("RouteData")
- function RouteData:ctor()
- self._provider = {}
- self._route_formats = {}
- end
- -- 插入路由格式
- function RouteData:addRouteFormat(sformat, index)
- table.insert(self._route_formats, sformat, index)
- end
- -- 设置路由格式
- function RouteData:setRouteFormat(sformat)
- self._route_formats = sformat
- end
- -- 获取路由格式
- function RouteData:getRouteFormats()
- return self._route_formats
- end
- -- 根据之前注入的路由规则获取路由名称列表
- function RouteData:getRouteNames(change_list)
- local namelist = {}
- for _,v in ipairs(self._route_formats) do
- local name = v
- for _, change in ipairs(change_list) do
- if change.Befor and change.Change then
- name = string.gsub(name, change.Befor, change.Change)
- end
- end
- table.insert(namelist, name)
- end
- return namelist
- end
- function RouteData:setData(name, value)
- self._provider[name] = value
- end
- -- 查找数据
- function RouteData:findValue(change_list)
- local namelist = self:getRouteNames(change_list)
- local value = nil
- for _, name in ipairs(namelist) do
- local v = self._provider[name]
- if v then
- value = v
- break
- end
- end
- return value
- end
- kunpo.RouteData = kunpo.RouteData or RouteData
- return RouteData
之前已经说过了需求,跟路由,现在需要做的就是怎么样才能结合路由的代码来实现我们的掉落需求了。当然了,我所说的路由格式数据并不是指像是像是 ASP 那种注册和使用一样的数据,而是符合我前边提到的总结的格式化数据模式的数据格式。所以在使用的时候也跟 ASP 的那种数据有很大的不同。
首先是根据业务需求准备格式化数据,下面这些事我们项目中使用的格式化数据。
- -- 注入查找顺序
- local Obstacle_routeformat =
- {
- "MapName::ObjType::ObjName::Default::Default",
- "Default::ObjType::ObjName::Default::TC",
- "MapName::ObjType::Default::Ability::TC",
- "MapName::Default::ObjName::Ability::TC",
- "MapName::ObjType::Default::Default::TC",
- "MapName::Default::ObjName::Default::TC",
- "Default::ObjType::ObjName::Default::Default",
- "MapName::ObjType::Default::Ability::Default",
- "MapName::Default::ObjName::Ability::Default",
- "Default::ObjType::Default::Ability::TC",
- "Default::Default::ObjName::Ability::TC",
- "MapName::Default::Default::Ability::TC",
- "MapName::ObjType::Default::Default::Default",
- "MapName::Default::ObjName::Default::Default",
- "Default::Default::ObjName::Ability::Default",
- "Default::ObjType::Default::Ability::Default",
- "MapName::Default::Default::Ability::Default",
- "Default::Default::ObjName::Default::Default",
- "Default::ObjType::Default::Default::Default",
- "Default::Default::Default::Ability::Default",
- "Default::Default::Default::Default::TC",
- "MapName::Default::Default::Default::Default",
- -- 基础默认值
- "Default::Default::Default::Default::Default",
- }
然后将这些数据注入到某一个路由中。当然也少不了部分代码初始化的数据。因为所有的数据一般都得有个初始化默认值。当然了这些默认数据最后都会被覆盖掉,因为我们真正的数据都是走策划提供的数值表。那个地方跟业务耦合的太紧密了。代码贴上来也没有什么用。
- -- 注入实际数据
- local default_data =
- {
- Bullet =
- {
- Probability =
- {
- ["Default::Enemy::Default::Common::Default"] = 0.2, -- 普通怪物在任何场景下的掉落几率为0.2(从关卡表获取)
- ["Default::Enemy::Default::Boss::Default"] = 1,
- ["Default::Enemy::Default::Npc::Default"] = 1,
- ["Default::Obstacle::ComprehensiveBox::Default::Default"] = 1,
- },
- Count =
- {
- -- ["Default::Enemy::Default::Common::Default"] = 1,
- ["Default::Enemy::xianjing_monster6_1::Common::Default"] = 2,
- ["Default::Enemy::Default::Boss::Default"] = {3, 5},
- ["Default::Obstacle::ComprehensiveBox::Default::Default"] = {4, 6},
- },
- DataPool =
- {
- ["Default::Default::Default::Default::Default"] = default_bullet_pool,
- },
- },
- Weapon =
- {
- Probability =
- {
- ["Default::Enemy::Default::Boss::Default"] = 1,
- ["Default::Obstacle::ComprehensiveBox::Default::Default"] = 1,
- },
- Count =
- {
- ["Default::Enemy::Default::Boss::Default"] = {1, 2},
- ["Default::Obstacle::ComprehensiveBox::Default::Default"] = 1,
- },
- DataPool =
- {
- },
- },
- Skill =
- {
- Probability =
- {
- ["Default::Obstacle::ComprehensiveBox::Default::Default"] = 1,
- },
- Count =
- {
- ["Default::Obstacle::ComprehensiveBox::Default::Default"] = 1,
- },
- DataPool =
- {
- },
- },
- -- Else =
- -- {
- -- Probability =
- -- {
- -- ["Default::Obstacle::Default::Default::Default"] = 1,
- -- },
- -- Count =
- -- {
- -- ["Default::Obstacle::Default::Default::Default"] = 4,
- -- },
- -- DataPool =
- -- {
- -- },
- -- },
- }
然后调用的时候,将条件数据通过前面的格式化数据。真的变成对应的关键 Key,然后通过这些关键 Key 按照顺序依次匹配路由数据就可以了。这样就可以实现掉落数据的提前注入与规则匹配啦。有新的掉落需求也可以直接满足。这个掉落规则自我实现以来,就没有大框架上的修改。当然实现的细节还是修改过两三次的。
因为我们游戏中的另一个组件模式挺有意思的,所以我就试着将这个模式放到了 C#编程中。但是我最初没有想法具体要实现一个什么东西。所以就在考虑原本的 C#实现的 WebAPI 还需要借助 IIS 用起来忒费劲,因为 IIS 有自己的问题,比如说 IIS 的多实例的问题,IIS 自动休眠的问题,所以我就实现了一个不依托与 IIS 的简单的 API 项目取名 TheHost,中间实现了一个 HTTP 的组件,需要做类似 ASP 的那种路由转发功能,所以我也将路由数据在 C# 中实现了一份。其实也是类似的逻辑,我就不多做赘述了。不过这个文件所在的项目我已经将他开源了。顺便贴一下地址
https://git.oschina.net/anxin1225/TheHost
- using System;
- using System.Collections.Generic;
- namespace Host
- {
- public class RouteData<T> where T : class
- {
- public RouteData()
- {
- }
- private List<KeyValuePair<string, T>> _route_list = new List<KeyValuePair<string, T>>();
- private Dictionary<Dictionary<string, string>, T> _route_value_list = new Dictionary<Dictionary<string, string>, T>();
- private List<string> _route_format_list = new List<string>();
- /// <summary>
- /// 从路由数据中查找对应数据
- /// </summary>
- /// <returns>The value.</returns>
- /// <param name="key">Key.</param>
- public T FindValue(string key)
- {
- foreach (var item in _route_list)
- {
- if (item.Key == key)
- {
- return item.Value;
- }
- }
- return null;
- }
- /// <summary>
- /// 从路由数据中查找对应数据
- /// </summary>
- /// <returns>The value.</returns>
- /// <param name="keys">Keys.</param>
- public object FindValue(IEnumerable<string> keys)
- {
- foreach (var item in keys)
- {
- var obj = FindValue(item);
- if (obj != null)
- return obj;
- }
- return null;
- }
- /// <summary>
- /// 注册路由列表
- /// </summary>
- /// <param name="key">Key.</param>
- /// <param name="obj">Object.</param>
- public void RegisterRouteData(string key, T obj)
- {
- _route_list.Add(new KeyValuePair<string, T>(key, obj));
- }
- /// <summary>
- /// 获取当前的路由列表
- /// </summary>
- /// <returns>The route list.</returns>
- public List<KeyValuePair<string, T>> GetRouteList()
- {
- return _route_list;
- }
- /// <summary>
- /// 注册路由格式化数据
- /// </summary>
- /// <param name="route">Route.</param>
- public void RegisterRouteFormat(string route)
- {
- if (!string.IsNullOrEmpty(route))
- {
- _route_format_list.Add(route);
- }
- }
- /// <summary>
- /// 注册路由列表数据
- /// </summary>
- /// <param name="routevalue">Routevalue.</param>
- /// <param name="obj">Object.</param>
- /// <param name="reset_list">If set to <c>true</c> reset list.</param>
- public void RegisterRouteData(Dictionary<string, string> routevalue, T obj, bool reset_list = true)
- {
- _route_value_list.Add(routevalue, obj);
- if (reset_list)
- RestRouteList();
- }
- /// <summary>
- /// 重新计算路由列表
- /// </summary>
- public void RestRouteList()
- {
- _route_list.Clear();
- foreach (var item in _route_format_list)
- {
- foreach (var route in _route_value_list)
- {
- var key = item;
- foreach (var kv in route.Key)
- {
- key = key.Replace("{" + kv.Key + "}", kv.Value);
- }
- if (key.IndexOf('{') == -1 && key.IndexOf('}') == -1)
- {
- RegisterRouteData(key, route.Value);
- }
- }
- }
- }
- /// <summary>
- /// 根据变换列表和注册的格式获取路由名称列表
- /// </summary>
- /// <returns>The route key.</returns>
- /// <param name="routevalue">Routevalue.</param>
- public List<string> GetRouteKey(Dictionary<string, string> routevalue)
- {
- List<string> list = new List<string>();
- foreach (var item in _route_format_list)
- {
- var key = item;
- foreach (var kv in routevalue)
- {
- key = key.Replace("{" + kv.Key + "}", kv.Value);
- }
- if (key.IndexOf('{') == -1 && key.IndexOf('}') == -1)
- {
- list.Add(key);
- }
- }
- return list;
- }
- }
- }
我很久之前写过一个聊天的项目,不过当时因为工作的变动,那个项目就没有继续维护下去了,因为我的离开所以那个项目也死掉了。不过聊天项目还真是有意思,所以我准备再次重新造这个轮子,不为别的,就是造着玩。中间已经不是使用 HTTP 协议了,准备使用 UDP 协议,然后用来转发消息,当然中间也要用到路由转发相关的功能,就直接使用 TheHost 中实现的那个了,毕竟那个路由一进挂在 HTTP 实现的时候验证过了,代码应该还算 OK
贴一下地址: https://git.oschina.net/anxin1225/aximserver
好像也没什么好继续说的了,先这样,好的,我懂我懂又有人要吐槽我了。哈哈哈
来源: http://www.cnblogs.com/anxin1225/p/6801139.html