问题

你想搜索你的机器、网络或Live服务找到活动会话,你想从检测到的会话列表中选择一个会话并加入其中。

解决方案

你可以使用NetworkSessions.Find方法搜索可用的会话,这会返回一个包含所有可用会话的AvailableNetworkSessionCollection对象。你可以使用 NetworkSession.Join方法加入其中的一个会话,将指定的会话作为一个参数。

当连接到一个会话时,如前一个教程所述,你应确保监听由这个会话引发的事件。

工作原理

在这个教程中,你将在前一个教程定义的GameState枚举中添加一个SearchSession状态:

public enum GameState ...{ SignIn, SearchSession, CreateSession, InSession}

程序从SignIn状态开始,允许用户选择一个账号。然后,不是立即创建一个新的网络会话,而是首先找到并加入一个已存在的会话并进入InSession状态。如果没有找到可用的会话,你将进入CreateSession状态,这个已经在前一个教程中解释过了。

搜索可用会话

你可以使用NetworkSession.Find方法搜索所有可用的会话,这个方法需要三个参数,让你可以过滤结果。这个方法只返回与第一个参数同种类型并且与一台机器上允许玩家数量相同的会话。你可以在本教程最后找到最后一个参数的例子。

AvailableNetworkSessionCollection activeSessions = NetworkSession.Find(NetworkSessionType.SystemLink, 4, null); 

注意:和NetworkSession.Create方法一样,如果机器没有连接到网络,NetworkSession. Find方法会报错。所以,你需要将它封装在一个try-catch结构中。

所有检测到的会话存储在一个AvailableNetworkSessionsCollection中,如果没有找到会话,这个集合为空。

加入一个活动的会话

如果检测到至少一个会话,你可以使用NetworkSession. Join方法加入它:

AvailableNetworkSession networkToJoin = activeSessions[0]; 
networkSession = NetworkSession.Join(networkToJoin); 

这会将程序连接到第一个参数指定的会话,本例中,你加入的是activeSessions中的第一个会话。在进行这个操作之前,你可能还想检查是否至少有一个玩家slot,这是由networkToJoin.OpenPublicPlayerSlots表示的。

注意:一个AvailableNetworkSession包含许多有用的信息,其中QualityOfService属性可以用来判断你的机器和会话主机间的连接速度。

最后的SearchSession 状态如下所示:

case GameState.SearchSession:
...{
    AvailableNetworkSessionCollection activeSessions = NetworkSession.Find(NetworkSessionType.SystemLink, 4, null); 
    if (activeSessions.Count == 0)
    ...{
        currentGameState = GameState.CreateSession;
        log.Add("No active sessions found - proceed to CreateSession");
        AvailableNetworkSession networkToJoin = activeSessions[0]; 
        networkSession = NetworkSession.Join(networkToJoin);

        string myString = "Joined session hosted by " + networkToJoin.HostGamertag; 
        myString += " with " + networkToJoin.CurrentGamerCount.ToString() +  " players";
        myString += " and " + networkToJoin.OpenPublicGamerSlots.ToString() + " open player slots.";
        log.Add(myString);
        HookSessionEvents();
        currentGameState = GameState.InSession; 
    }
}
break;

首先找到指定三个过滤参数的所有活动会话,如果没有检测到会话,则进入CreateSession状态创建你自己的会话,可参见前一个教程。如果找到至少一个会话,加入找到的第一个会话。更高级的例子会将所有会话显示在屏幕上让用户可以加以选择。

然后,将连接的会话属性显示在屏幕上,诸如主机名称,加入这个会话的玩家数量,打开的玩家slots的数量。

最后在进入InSession状态前开始监听由会话引发的任何事件(可参见教程8-2) 。

使用NetworkSessionProperties过滤检测到的会话

当使用NetworkSession.Find方法搜索活动会话时,你可以指定三个参数过滤检测到的会话。

前面已经提及,前两个参数让你可以指定会话的种类和会话允许的最多玩家数量。

但是,当游戏变得非常流行时,Find方法会返回很大数量的活动会话。所以,当你创建一个会话时,指定一个NetworkSessionProperties对象是很有用的,这个对象最多可以包含八个整数值。这八个整数代表什么完全取决于你。例如,你可以让第一个整数表示游戏的难度设置,第二个代表bots的数量,第三个代表游戏正在处于的地图,本例中设置为null,因为你还没有设置这个对象。

当另外一个玩家搜索活动会话时,他们可以搜索对应的游戏,例如,在一张指定的地图上进行游戏。

根据NetworkSessionProperties创建一个会话

要指定一个NetworkSessionProperties,你需要使用NetworkSession.Create的第二个重载方法,它接受另外两个参数,让你可以将属性作为最后一个参数。创建了一个新NetworkSessionProperties对象后,你可以设定8个整数值。在下面的例子中,只设置两个值。之后,将属性作为最后一个参数传递到NetworkSession.Create方法中:

NetworkSessionProperties createProperties = new NetworkSessionProperties(); 
createProperties[0] = 3; 
createProperties[1] = 4096; 

networkSession = NetworkSession.Create(NetworkSessionType.SystemLink, 4, 16, 0, createProperties); 
使用指定的NetworkSessionProperties搜索会话

你可以使用指定的属性搜索会话。只需简单地创建一个NetworkSessionProperties对象,指定你要查找的属性,将它传递到NetworkSession.Find方法中:

NetworkSessionProperties findProperties = new NetworkSessionProperties(); 
findProperties[1] = 4096; 

AvailableNetworkSessionCollection activeSessions = NetworkSession.Find(NetworkSessionType.SystemLink, 4, findProperties); 

本例中,只使用一个值而不是两个。Find方法会返回所有拥有第二个属性中4096的方法,而不管其他七个属性是什么。

代码

下面的Update方法显示了基本的会话管理。

当程序开始时,用户要求使用一个账号登录,然后,程序搜索活动会话,如果找到至少一个,则加入这个会话。

如果没有找到活动会话,程序会创建自己的会话。无论通过何种方式,你都会连接到一个会话,程序需要监听会话引发的事件。你结束于InSession状态,这个状态中会以一定的时间间隔更新会话。

同样的代码可以运行在多台机器上,如果这些机器连接到同一个网络,第一个机器会创建会话,其他机器会检测并自动加入这个会话。

protected override void Update(GameTime gameTime)
...{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) 
        this.Exit();

    if (this.IsActive)
...    {
        switch (currentGameState)
...        {
            case GameState.SignIn:
...            {
                if (Gamer.SignedInGamers.Count < 1)
...                {
                    Guide.ShowSignIn(1, false);
                    log.Add("Opened User SignIn Interface");
                }
                else
...                {
                    currentGameState = GameState.SearchSession; 
                    log.Add(Gamer.SignedInGamers[0].Gamertag + " logged in - proceed to SearchSession");
                }
            }
            break;
            case GameState.SearchSession:
            ...{
                NetworkSessionProperties findProperties = new NetworkSessionProperties();
                findProperties[0] = 3;
                findProperties[1] = 4096;
                AvailableNetworkSessionCollection activeSessions NetworkSession.Find(NetworkSessionType.SystemLink,4, findProperties);
                if (activeSessions.Count == 0)
...                {
                    currentGameState = GameState.CreateSession;
                    log.Add("No active sessions found - proceed to CreateSession");
                }
                else
...                {
                    AvailableNetworkSession networkToJoin = activeSessions[0];
                    networkSession = NetworkSession.Join(networkToJoin);
                    string myString = "Joined session hosted by " + networkToJoin.HostGamertag; 
                    myString += " with " + networkToJoin.CurrentGamerCount.ToString() + " players"; 
                    myString += " and " + networkToJoin.OpenPublicGamerSlots.ToString() + " open player slots.";
                    log.Add(myString);
                    HookSessionEvents();
                    currentGameState = GameState.InSession;
                }
            }
            break;
            case GameState.CreateSession: 
            ...{
                NetworkSessionProperties createProperties = new NetworkSessionProperties(); 
                createProperties[0] = 3;
                createProperties[1] = 4096;

                networkSession = NetworkSession.Create(NetworkSessionType.SystemLink, 4, 16, 0, createProperties);
                networkSession.AllowHostMigration = true; 
                networkSession.AllowJoinInProgress = false;
                log.Add("New session created");
                HookSessionEvents();
            currentGameState = GameState.InSession;
            }
            break;
            case GameState.InSession:
...            {
                networkSession.Update();
            }
            break;
        }
    }
    base.Update(gameTime); 
}