LocationComponent
请大家关注我的微博:@NormanLin_BadPixel坏像素
作为程序员,我们对数据,对逻辑是敏感的。看到一段代码,我们很快就能看懂里面的逻辑,数据是怎么变化的。但是,这段代码在整个工作环境中起到了什么作用,我们不能一眼看出来。
就像这个组件,里面的数据变化很容易看懂,但是我们不知道这些数据代表了什么。作者也没有注释,我们只能猜测,之后再来验证。
我们看变量名,locations,储存地址的一个字典。我们顺藤摸瓜,找到在哪里储存了这个地址。我们在ActorComponent的扩展方法里找到了
public static async Task AddLocation(this ActorComponent self)
{
await Game.Scene.GetComponent<LocationProxyComponent>().Add(self.actorId);
}
在LocationProxyComponent里面
public async Task Add(long key)
{
Session session = Game.Scene.GetComponent<NetInnerComponent>().Get(this.LocationAddress);
await session.Call(new ObjectAddRequest() { Key = key, AppId = this.AppId });
}
从而我们得知,locations字典里面,键是ActorComponent的actorId,值则是LocationProxyComponent的AppId。而我们知道,这个AppId是启动服务时候从启动设置里面获取的。也就是说,这里登记的地址,是添加了ActorComponent组件的实体对象所在服务器的地址。为什么要这么做呢?
我们知道,ET是分布式的服务器架构,不同的服务会存在在不同的服务器上。那不同服务器之间实体对象的互相访问就要获取对方所在的服务器,才能精准的访问。
举一个例子。我们的游戏有两张地图,地图的数据由两个不同的服务器Map1、Map2管理。
我们有两个玩家Player1、Player2分别在Map1、Map2上。
在进入地图的时候,会记录Player1所在服务器的地址是Map1,Player2所在服务器的地址是Map2。这些数据会存在另外一个服务器location服务器上。
这个时候,如果Player1要给Player2发送消息,则要经过一下的步骤。
- 获取Player2的Id。(不管你用什么方法获取)
- 根据Id向location服务器发送内部请求,获取Player2所在服务器的地址Map2。
- 根据Map2的地址(AppId)从配置信息里面获取到Map2服务器的IP地址。
- 有了这两个信息,Player2的ID跟Map2的IP地址。我们就可以创建跟Map2的会话Session,并且发送针对Player2的消息了。
有的同学可能会问,我们既然在第一步能获取到Player2的Id,为什么不能获取到它所在服务器的地址,而要从location获取?
这里我的理解是,Player的Id,也就是实体对象的Id在其生命周期内是固定不变且唯一的。所以我们在传输保存Player的信息的时候,可以保存Id来标识它。如果我们保存服务器地址的话会出现什么情况呢?
假如Player1跟其他1000万个人是好友,那它每次更换服务器的时候,它都得通知那1000万个人更换地址,这是极其愚蠢的。这也体现了location服务器的作用,每个实体对象更换服务器的时候,只要通知location服务器更新地址,其他对象就可以获得正确的地址了。
我们看到,这个组件里除了添加和获取的方法外,还有Lock与UnLock方法。顾名思义,是锁住地址的意思。为什么要锁住地址呢?我的理解是,有得实体对象可能会转换服务器,比如玩家进入另一张地图。而这个转换过程是有时间的,在这个时间内如果想对玩家发送消息,则容易出错,我们可以想象成玩家既不在地图1也不在地图2上。这个时候,我们就锁住玩家的地址,所有获取地址的请求全部都搁置,等待玩家转换完成后再处理。这样,获取的地址才是正确的。而搁置的请求,则会以LocationTask储存在taskQueues字典里。
private readonly Dictionary<long, Queue<LocationTask>> taskQueues = new Dictionary<long, Queue<LocationTask>>();