浏览代码

can now connect to remote host and switch to his scene

/main
David Woodruff 4 年前
当前提交
e9032e85
共有 9 个文件被更改,包括 234 次插入131 次删除
  1. 136
      Assets/Scenes/MainMenu.unity
  2. 2
      Assets/Scripts/Client/Game/CMainMenuBRState.cs
  3. 12
      Assets/Scripts/Client/Net/GNH_Client.cs
  4. 29
      Assets/Scripts/Client/UI/MainMenuUI.cs
  5. 37
      Assets/Scripts/Server/Net/GNH_Server.cs
  6. 19
      Assets/Scripts/Shared/Game/BossRoomStateManager.cs
  7. 5
      Assets/Scripts/Shared/Game/CharSelectBRState.cs
  8. 17
      Assets/Scripts/Shared/Game/GameBRState.cs
  9. 108
      Assets/Scripts/Shared/Net/GameNetHub.cs

136
Assets/Scenes/MainMenu.unity


m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &308991157
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 308991160}
- component: {fileID: 308991159}
- component: {fileID: 308991158}
m_Layer: 0
m_Name: GameHub
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &308991158
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 308991157}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7ba4a1d598313d94a81a08ceae147b57, type: 3}
m_Name:
m_EditorClassIdentifier:
NetworkingManagerGO: {fileID: 1359040525}
--- !u!114 &308991159
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 308991157}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 39332c63269f3f34db37df122adcc57b, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &308991160
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 308991157}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 44.584602, y: -17.096796, z: -32.618843}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &342895129
GameObject:
m_ObjectHideFlags: 0

m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!1 &569965805
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 569965807}
- component: {fileID: 569965806}
- component: {fileID: 569965808}
- component: {fileID: 569965809}
m_Layer: 0
m_Name: GameHub
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &569965806
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 569965805}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7ba4a1d598313d94a81a08ceae147b57, type: 3}
m_Name:
m_EditorClassIdentifier:
NetworkingManagerGO: {fileID: 1359040525}
--- !u!4 &569965807
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 569965805}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 44.584602, y: -17.096796, z: -32.618843}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &569965808
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 569965805}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
NetworkedInstanceId: 0
PrefabHash: 3086995442165573965
PrefabHashGenerator: GameNetHub
AlwaysReplicateAsRoot: 0
DontDestroyWithOwner: 0
--- !u!114 &569965809
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 569965805}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 39332c63269f3f34db37df122adcc57b, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &726602803
GameObject:
m_ObjectHideFlags: 0

m_Script: {fileID: 11500000, guid: f6144e8dd0fc8ec4cb23a1a49059aa08, type: 3}
m_Name:
m_EditorClassIdentifier:
GameHubGO: {fileID: 569965805}
GameHubGO: {fileID: 0}
--- !u!1 &1359040525
GameObject:
m_ObjectHideFlags: 0

EnableNetworkedVar: 0
EnsureNetworkedVarLengthSafety: 0
EnableSceneManagement: 0
ForceSamePrefabs: 1
ForceSamePrefabs: 0
UsePrefabSync: 0
RecycleNetworkIds: 1
NetworkIdRecycleDelay: 120

2
Assets/Scripts/Client/Game/CMainMenuBRState.cs


public void Initialize(BossRoomStateManager manager, Dictionary<string, object> stateParams)
{
m_manager = manager;
UnityEngine.Application.targetFrameRate = 60;
}
public void Update()

12
Assets/Scripts/Client/Net/GNH_Client.cs


/// <summary>
/// Wraps the invocation of NetworkingManager.StartClient, including our GUID as the payload.
/// </summary>
/// <param name="ipaddress">the IP address of the host to connect to.</param>
/// <remarks>
/// This method must be static because, when it is invoked, the client still doesn't know it's a client yet, and in particular, GameNetHub hasn't
/// yet initialized its GNH_Client and GNH_Server objects yet (which it does in NetworkStart, based on the role that the current player is performing).
/// </remarks>
/// <param name="ipaddress">the IP address of the host to connect to. (currently IPV4 only)</param>
/// <param name="port">The port of the host to connect to. </param>
public static void StartClient(GameNetHub hub, string ipaddress, int port)
{

Debug.Log("client send payload: " + payload); //dmw_debug: remove.
//fixme: this code is not portable, and will break depending on the transport used. Unfortunately different transports call these
//fields different things, so we might need a big switch-case to handle more than one transport. Or we can update this once
//if we have decisively normalized on UTP transport.
//DMW_NOTE: non-portable. We need to be updated when moving to UTP transport.
var transport = hub.NetworkingManagerGO.GetComponent<MLAPI.Transports.UNET.UnetTransport>();
transport.ConnectAddress = ipaddress;
transport.ConnectPort = port;

29
Assets/Scripts/Client/UI/MainMenuUI.cs


namespace BossRoomViz
{
/// <summary>
/// Provides backing logic for all of the UI that runs in the MainMenu stage.
/// </summary>
private string m_defaultIPText;
//track what this is on startup. We're not opinionated; we will try whatever the user puts in, as long as they've changed the text.
m_defaultIPText = GameObject.Find("IPInputBox").GetComponent<UnityEngine.UI.Text>().text;
}
// Update is called once per frame

}
/// <summary>
/// Gets the IP Address the user set in the UI, or returns 127.0.0.1 if IP is not present.
/// </summary>
/// <returns>IP address entered by user, in string form. </returns>
private string GetIPAddress()
{
string iptext = GameObject.Find("IPInputText").GetComponent<UnityEngine.UI.Text>().text;
if( iptext == m_defaultIPText )
{
return "127.0.0.1";
}
return iptext;
}
//TODO: bring up transition screen.
GetIPAddress();
m_netHub.NetManager.StartHost();
m_netHub.StartHost(GetIPAddress(), 9998);
m_netHub.StartClient("127.0.0.1", 7777);
m_netHub.StartClient(GetIPAddress(), 9998);
}
}
}

37
Assets/Scripts/Server/Net/GNH_Server.cs


namespace BossRoomServer
{
/// <summary>
/// Server logic plugin for the GameNetHub. Contains implementations for all GameNetHub's C2S RPCs.
/// </summary>
public class GNH_Server
{
private GameNetHub m_hub;

}
/// <summary>
/// Initializes host mode on this client. Call this and then other clients should connect to us!
/// </summary>
/// <remarks>
/// See notes in GNH_Client.StartClient about why this must be static.
/// </remarks>
/// <param name="hub">The GameNetHub that is invoking us. </param>
/// <param name="ipaddress">The IP address to connect to (currently IPV4 only).</param>
/// <param name="port">The port to connect to. </param>
public static void StartHost(GameNetHub hub, string ipaddress, int port )
{
//DMW_NOTE: non-portable. We need to be updated when moving to UTP transport.
var transport = hub.NetworkingManagerGO.GetComponent<MLAPI.Transports.UNET.UnetTransport>();
transport.ConnectAddress = ipaddress;
transport.ServerListenPort = port;
hub.NetManager.StartHost();
}
/// <summary>
/// This logic plugs into the "ConnectionApprovalCallback" exposed by MLAPI.NetworkingManager, and is run every time a client connects to us.
/// See GNH_Client.StartClient for the complementary logic that runs when the client starts its connection.
/// </summary>

}
}
//TODO: save off the player's Guid.
//TODO_DMW: save off the player's Guid.
//TODO: handle different cases based on gamestate (e.g. GameState.PLAYING would cause a reject if this isn't a reconnect case).
//TODO_DMW: handle different cases based on gamestate (e.g. GameState.PLAYING would cause a reject if this isn't a reconnect case).
//TODO: add corresponding NetworkHide, just so that we don't endlessly leak clientIds into our observer's list. The
//GameNetHub should be observable by everybody all the time.
m_hub.GetComponent<MLAPI.NetworkedObject>().NetworkShow(clientId);
callback(false, 0, true, null, null);
m_hub.S2C_ConnectResult(clientId, ConnectStatus.SUCCESS, BossRoomState.CHARSELECT );
callback(false, 0, true, null, null);
//FIXME_DMW: it is weird to do this after the callback, but the custom message won't be delivered if we call it beforehand.
//This creates an "honor system" scenario where it is up to the client to politely leave on failure. Probably
//we should add a NetManager.DisconnectClient call directly below this line, when we are rejecting the connection.
m_hub.S2C_ConnectResult(clientId, ConnectStatus.SUCCESS, BossRoomState.CHARSELECT);
}
}

19
Assets/Scripts/Shared/Game/BossRoomStateManager.cs


namespace BossRoom
{
/// <summary>
/// enum for the top-level game FSM.
/// </summary>
NONE,
MAINMENU,
CHARSELECT,
GAME
NONE, // no state is actively running. Currently this only happens prior to BossRoomStateManager.Start having run.
MAINMENU, // main menu logic is running.
CHARSELECT, // character select logic is running.
GAME, // core game logic is running.
/// The BossRoomStateManager manages the top-level logic for each gamestate
/// The BossRoomStateManager manages the top-level logic for each gamestate.
/// <remarks>
/// This class is intended as the top-level FSM for the game. Because of that, it runs both before and after
/// the time when the client has an active connection to the host.
/// On the Host, server and client logic run concurrently (with server logic preceding client on every update tic).
/// On the Client, only client logic runs.
/// </remarks>
public class BossRoomStateManager : MonoBehaviour
{
public GameNetHub NetHub { get; private set; }

5
Assets/Scripts/Shared/Game/CharSelectBRState.cs


namespace BossRoom
{
/// <summary>
/// The BossRoomState that runs during Character Select.
/// </summary>
class CharSelectBRState : IBossRoomState
{
protected BossRoomStateManager m_manager;

public virtual void Update()
{
//FIXME (temp): as we don't have a CharacterSelect scene yet, we just advance directly to the Game state.
//FIXME_DMW (temp): as we don't have a CharacterSelect scene yet, we just advance directly to the Game state.
m_manager.ChangeState(BossRoomState.GAME, null);
}
}

17
Assets/Scripts/Shared/Game/GameBRState.cs


namespace BossRoom
{
/// <summary>
/// The BossRoomState that runs during primary gameplay.
/// </summary>
class GameBRState : IBossRoomState
{
protected BossRoomStateManager m_manager;

{
m_manager = manager;
Debug.Log("Entering GameBRState, advancing to SampleScene");
// FIXME_DMW: we probably need to rely on MLAPI's scene management on the client to change scenes, rather than doing it here.
// I haven't yet been able to get SceneManagement to work though--on login, the client just stays at the MainMenu scene, even though
// the server is in the SampleScene.
//
// problems with doing LoadScene here:
// 1.Runs twice on the host. This is harmless, but not ideal.
// 2.This may happen AFTER the server has sent us all dynamic object spawns in ITS scene (because we can't send the event that
// triggers this code until after we have returned ConnectionApproval==true to MLAPI).
//this is an example of the slight weirdnesses of having "server" and "client" logic running in parallel on the host.
//This will get invoked twice; once as part of the SGameBRState, once as part of the CGameBRState. It might be tempting
//to move it to the CGameBRState exclusively, but that's not right--it really should happen on a dedicated server too.
//The only alternative I can think of is to add an "if(!IsHost)" check around this logic. There will always be some logic
//which it is not OK to do twice that will need this kind of special handling, although it's not clear to me yet how much.
UnityEngine.SceneManagement.SceneManager.LoadScene("SampleScene");
}

108
Assets/Scripts/Shared/Net/GameNetHub.cs


using System.Collections.Generic;
using UnityEngine;
using MLAPI.Serialization.Pooled;
namespace BossRoom
{
public enum ConnectStatus

EMATCHSTARTED, //can't join, match is already in progress.
EUNKNOWN //can't join, reason unknown.
EUNKNOWN, //can't join, reason unknown.
}
/// <summary>

/// </summary>
///
/// <remarks
/// Why is there a RequestConnect call-and-response here? How is that different from the "ApprovalCheck" logic that MLAPI optionally runs
/// Why is there a C2S_ConnectFinished event here? How is that different from the "ApprovalCheck" logic that MLAPI optionally runs
/// In short, the connection flow in this class happens second after the logic embodied by StartClient runs (and after the client has seen
/// NetworkStart fire). We need to provide an initial dump of info when a client logs in, specifically what scene to transition to. We would need
/// this message even if we used ConnectionData to send up our client GUID--because of that, it makes more sense to keep the two RPCs here, as
/// a 2nd step in the login flow.
/// MLAPI's ApprovalCheck logic doesn't offer a way to return rich data. We need to know certain things directly upon logging in, such as
/// whether the game-layer even wants us to join (we could fail because the server is full, or some other non network related reason), and also
/// what BossRoomState to transition to. We do this with a Custom Named Message, which fires on the server immediately after the approval check delegate
/// has run.
///
/// Why do we need to send a client GUID? What is it? Don't we already have a clientID?
/// ClientIDs are assigned on login. If you connect to a server, then your connection drops, and you reconnect, you get a new ClientID. This

/// </remarks>
///
public class GameNetHub : MLAPI.NetworkedBehaviour
public class GameNetHub : MonoBehaviour
{
public GameObject NetworkingManagerGO;

void Start()
{
Object.DontDestroyOnLoad(this.gameObject);
Object.DontDestroyOnLoad(NetworkingManagerGO);
//FIXME_DMW: would like to remove this. I have added it because at the moment I can't stop MLAPI from destroying the
//GameHub object I created (that I intended to live for the entire lifetime of the game), and replacing it with its
//own copy (which naturally doesn't have this editor-configured value set). I have tried setting "UsePrefabSync" to false,
//but didn't stop this behavior.
if (!NetworkingManagerGO)
{
NetworkingManagerGO = GameObject.Find("NetworkingManager");
}
//because we are not a true NetworkedBehavior, we don't get NetworkStart messages. But we still need to run at that point
//where we know if we're a host or client. So we fake a "NetworkingManager.OnNetworkStarted" event out of the existing OnServerStarted
//and OnClientConnectedCallback events.
NetManager.OnServerStarted += () =>
{
NetworkStart();
};
NetManager.OnClientConnectedCallback += (clientId) =>
{
if (clientId == NetManager.LocalClientId)
{
NetworkStart();
}
};
public override void NetworkStart()
private void RegisterClientMessageHandlers()
{
MLAPI.Messaging.CustomMessagingManager.RegisterNamedMessageHandler("S2C_ConnectResult", (senderClientId, stream) =>
{
using (PooledBitReader reader = PooledBitReader.Get(stream))
{
ConnectStatus status = (ConnectStatus)reader.ReadInt32();
BossRoomState state = (BossRoomState)reader.ReadInt32();
m_clientLogic.RecvConnectFinished(status, state);
}
});
}
private void RegisterServerMessageHandlers()
{
//TODO: plug in any C->S message handlers here.
}
/// <summary>
/// This method runs when NetworkingManager has started up (following a succesful connect on the client, or directly after StartHost is invoked
/// on the host). It is named to match NetworkedBehaviour.NetworkStart, and serves the same role, even though GameNetHub itself isn't a NetworkedBehaviour.
/// </summary>
public void NetworkStart()
RegisterClientMessageHandlers();
RegisterServerMessageHandlers();
}
if( NetManager.IsHost )
{
RecvConnectFinished(ConnectStatus.SUCCESS, BossRoomState.CHARSELECT);
m_clientLogic.RecvConnectFinished(ConnectStatus.SUCCESS, BossRoomState.CHARSELECT);
}
}

/// <param name="ipaddress">the IP address of the host to connect to.</param>
/// <param name="ipaddress">the IP address of the host to connect to. (IPV4 only)</param>
/// <param name="port">The port of the host to connect to. </param>
public void StartClient(string ipaddress, int port)
{

/// <summary>
/// Wraps the invocation of NetworkingManager.StartHost.
/// </summary>
/// <param name="ipaddress">The IP address of the network interface we should listen to connections on. (IPV4 only)</param>
/// <param name="port">The port we should listen on. </param>
public void StartHost(string ipaddress, int port )
{
BossRoomServer.GNH_Server.StartHost(this, ipaddress, port);
}
InvokeClientRpcOnClient("RecvConnectResult",
netId,
"MLAPI_INTERNAL", // channelID. Must be MLAPI_INTERNAL Because it is called as part of the StartClient flow (before possible reject comes back).
MLAPI.Security.SecuritySendFlags.None,
MLAPI.Security.SecuritySendFlags.None,
status, // this is the actual payload
targetState); // ""
using (PooledBitStream stream = PooledBitStream.Get())
{
using (PooledBitWriter writer = PooledBitWriter.Get(stream))
{
writer.WriteInt32((int)status);
writer.WriteInt32((int)targetState);
MLAPI.Messaging.CustomMessagingManager.SendNamedMessage("S2C_ConnectResult", netId, stream, "MLAPI_INTERNAL");
}
}
[MLAPI.Messaging.ClientRPC()]
private void RecvConnectFinished( ConnectStatus status, BossRoomState targetState )
{
m_clientLogic.RecvConnectFinished( status, targetState );
}
}
}
正在加载...
取消
保存