ET框架---MatchComponent学习笔记

MatchComponent

请大家关注我的微博:@NormanLin_BadPixel坏像素


作者注释到,此为匹配组件

/// <summary>
/// 匹配组件,匹配逻辑在MatchComponentSystem扩展
/// </summary>
public class MatchComponent : Component
{
    //游戏中匹配对象列表
    public readonly Dictionary<long, long> Playing = new Dictionary<long, long>();

    //匹配成功队列
    public readonly Queue<Matcher> MatchSuccessQueue = new Queue<Matcher>();

    //创建房间消息加锁,避免因为延迟重复发多次创建房间消息
    public bool CreateRoomLock { get; set; }
}

我们快速的看一下Matcher

/// <summary>
/// 匹配对象
/// </summary>
public sealed class Matcher : Entity
{
    //用户ID(唯一)
    public long UserID { get; private set; }
    //玩家GateActorID
    public long PlayerID { get; set; }
    //客户端与网关服务器的SessionID
    public long GateSessionID { get; set; }
    ...
}

很简单的一个类。我们来看看MatchComponent的扩展。

我们看到,它为MatchComponent组件订阅了Update事件。Update事件会在每帧都调用。

Update里面有一个While循环。我们来看一下这个循环会在什么情况下结束。

  1. 当没有匹配玩家时直接结束
  2. 当没有空房间的时候会尝试创建一个新的房间并结束

注意这个循环结束只能说明这一帧的匹配逻辑结束,并不是不再匹配了。因为Update每帧都调用嘛。

好的,我们再来看看它是怎么进行匹配的。

MatcherComponent matcherComponent = Game.Scene.GetComponent<MatcherComponent>();

MatcherComponent学习笔记

MatchRoomComponent学习笔记

...
Room room = roomManager.GetReadyRoom();
...
if (room == null && matchers.Count >= 3)
{
    //当还有一桌匹配玩家且没有可加入房间时使用空房间
    room = roomManager.GetIdleRoom();
}
...

通过这段代码我们能知道什么呢?这个匹配机制会优先填充未满的房间,只有一个房间填满了才会往下一个房间填。也是一个很简单的机制。

在看接下的代码前,我们先去看看JoinRoomCreateRoom方法吧。

if (room != null)
{
    //当有准备状态房间且房间还有空位时匹配玩家直接加入填补空位
    while (matchers.Count > 0 && room.Count < 3)
    {
        self.JoinRoom(room, matcherComponent.Remove(matchers.Dequeue().UserID));
    }
}
else if (matchers.Count >= 3)
{
    //当还有一桌匹配玩家且没有空房间时创建新房间
    self.CreateRoom();
    break;
}
else
{
    break;
}

//移除匹配成功玩家
while (self.MatchSuccessQueue.Count > 0)
{
    matcherComponent.Remove(self.MatchSuccessQueue.Dequeue().UserID);
}

这些代码,作者注释的都非常详细。不过这里大家要注意,在每次匹配循环逻辑执行的最后一步,会遍历在这一次循环匹配成功的玩家列表,并把他们移除出matcherComponent组件。不过我们知道,当玩家加入房间的时候,用的是matcherComponent.Remove方法,已经移除了,这会不会是重复操作呢?在现在看来,我觉得是的。

public static async void JoinRoom(this MatchComponent self, Room room, Matcher matcher)
{
    //玩家加入房间,移除匹配队列
    self.Playing[matcher.UserID] = room.Id;
    self.MatchSuccessQueue.Enqueue(matcher);

    //向房间服务器发送玩家进入请求
    ActorProxy actorProxy = Game.Scene.GetComponent<ActorProxyComponent>().Get(room.Id);
    Actor_PlayerEnterRoom_Ack actor_PlayerEnterRoom_Ack = await actorProxy.Call(new Actor_PlayerEnterRoom_Req()
    {
        PlayerID = matcher.PlayerID,
        UserID = matcher.UserID,
        SessionID = matcher.GateSessionID
    }) as Actor_PlayerEnterRoom_Ack;

    Gamer gamer = GamerFactory.Create(matcher.PlayerID, matcher.UserID, actor_PlayerEnterRoom_Ack.GamerID);
    room.Add(gamer);

    ActorProxyComponent actorProxyComponent = Game.Scene.GetComponent<ActorProxyComponent>();

    //向玩家发送匹配成功消息
    ActorProxy gamerActorProxy = actorProxyComponent.Get(gamer.PlayerID);
    gamerActorProxy.Send(new Actor_MatchSucess_Ntt() { GamerID = gamer.Id });
}

首先对自身的属性数据进行了修改。因为房间服务器是分布式的,所以我们要用ActorProxy把请求传过去。具体服务器会对这个消息做怎样的处理,我们之后讲整个游戏流程的时候再说。

而创建一个房间也是一样,会发送创建房间的请求,得到回复后再用回复的Id创建一个Room管理起来。不过这里需要注意的是,这里用到了CreateRoomLock这个“锁”。为什么要用到这个呢?我们之前讲过,每一帧的匹配逻辑都是一个无限循环,而且每帧都会启动一个新的循环。而我们创建房间的操作是异步的,需要网络传输。而在我们得到远端答复之前,匹配的逻辑还在跑,当它发现自己还是没有新的房间时,会不断发送创建房间的请求,所以,在这里我们在第一次发送创建请求时,会用这个锁标记,我已经发送创建请求了,还在等待,你不要再发送了。直到我们异步获取到了远端的回复,并且创建了一个新的房间后,才会给这个锁解锁,开始接收新的创建房间请求。

public static async void CreateRoom(this MatchComponent self)
{
    if (self.CreateRoomLock)
    {
        return;
    }

    //消息加锁,避免因为延迟重复发多次创建消息
    self.CreateRoomLock = true;

    //发送创建房间消息
    IPEndPoint mapIPEndPoint = Game.Scene.GetComponent<AllotMapComponent>().GetAddress().GetComponent<InnerConfig>().IPEndPoint;
    Session mapSession = Game.Scene.GetComponent<NetInnerComponent>().Get(mapIPEndPoint);
    MP2MH_CreateRoom_Ack createRoomRE = await mapSession.Call(new MH2MP_CreateRoom_Req()) as MP2MH_CreateRoom_Ack;

    Room room = ComponentFactory.CreateWithId<Room>(createRoomRE.RoomID);
    Game.Scene.GetComponent<MatchRoomComponent>().Add(room);

    //解锁
    self.CreateRoomLock = false;
}

对这些消息的具体处理,我们之后再讲。

猜你喜欢

转载自blog.csdn.net/norman_lin/article/details/79966503