Skip to content

Commit fd60daa

Browse files
refactor: replacing GameNetPortal with state machine [MTT-1742] [MTT-3501] [MTT-3502] (#666)
* Removing GameNetPortal * Adding ConnectionManager and ConnectionStates * Moving connection management to new assembly * Simplifying initial player spawn flow in ServerBossRoomScene Co-authored-by: Sam Bellomo <71790295+SamuelBellomo@users.noreply.github.com>
1 parent fc7436b commit fd60daa

59 files changed

Lines changed: 1079 additions & 1189 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Assets/Prefabs/GameNetPortal.prefab

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ GameObject:
99
serializedVersion: 6
1010
m_Component:
1111
- component: {fileID: 5062837533140207352}
12-
- component: {fileID: 5062837533140207350}
13-
- component: {fileID: 5062837533140207353}
14-
- component: {fileID: 5062837533140207351}
12+
- component: {fileID: 1984353324962460586}
1513
m_Layer: 0
1614
m_Name: GameNetPortal
1715
m_TagString: GameNetPortal
@@ -29,11 +27,12 @@ Transform:
2927
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
3028
m_LocalPosition: {x: 0, y: 0, z: 0}
3129
m_LocalScale: {x: 1, y: 1, z: 1}
30+
m_ConstrainProportionsScale: 0
3231
m_Children: []
3332
m_Father: {fileID: 0}
3433
m_RootOrder: 0
3534
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
36-
--- !u!114 &5062837533140207350
35+
--- !u!114 &1984353324962460586
3736
MonoBehaviour:
3837
m_ObjectHideFlags: 0
3938
m_CorrespondingSourceObject: {fileID: 0}
@@ -42,34 +41,9 @@ MonoBehaviour:
4241
m_GameObject: {fileID: 5062837533140207349}
4342
m_Enabled: 1
4443
m_EditorHideFlags: 0
45-
m_Script: {fileID: 11500000, guid: 7ba4a1d598313d94a81a08ceae147b57, type: 3}
44+
m_Script: {fileID: 11500000, guid: bf51ea31bbc84cc3aa883cc5f1caae06, type: 3}
4645
m_Name:
4746
m_EditorClassIdentifier:
4847
m_NetworkManager: {fileID: 0}
4948
m_AvatarRegistry: {fileID: 11400000, guid: 48d17d764bff6c643a3dc035fb71c979, type: 2}
50-
PlayerName:
51-
--- !u!114 &5062837533140207353
52-
MonoBehaviour:
53-
m_ObjectHideFlags: 0
54-
m_CorrespondingSourceObject: {fileID: 0}
55-
m_PrefabInstance: {fileID: 0}
56-
m_PrefabAsset: {fileID: 0}
57-
m_GameObject: {fileID: 5062837533140207349}
58-
m_Enabled: 1
59-
m_EditorHideFlags: 0
60-
m_Script: {fileID: 11500000, guid: 5bd31edc1b1fd1342b3bf8193a5a03b2, type: 3}
61-
m_Name:
62-
m_EditorClassIdentifier:
63-
--- !u!114 &5062837533140207351
64-
MonoBehaviour:
65-
m_ObjectHideFlags: 0
66-
m_CorrespondingSourceObject: {fileID: 0}
67-
m_PrefabInstance: {fileID: 0}
68-
m_PrefabAsset: {fileID: 0}
69-
m_GameObject: {fileID: 5062837533140207349}
70-
m_Enabled: 1
71-
m_EditorHideFlags: 0
72-
m_Script: {fileID: 11500000, guid: 54b0859a1da930541adf0b2d1bb8d977, type: 3}
73-
m_Name:
74-
m_EditorClassIdentifier:
7549
m_GameState: {fileID: 7151198093957655792, guid: 3af96a32a84bcf74d9538fa7af973c97, type: 3}

Assets/Scenes/Startup.unity

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:8e7de9f7adc74855f71578459a9045e7b306964aa10509cfe451e18afd08129c
3-
size 46516
2+
oid sha256:48a24b280794a53ab92b88c560dba8dddc996e910e534a952e60fa0bad85a1cc
3+
size 45753

Assets/Scripts/ApplicationLifecycle/ApplicationController.cs

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
using System.Collections;
33
using BossRoom.Scripts.Shared.Net.UnityServices.Auth;
44
using Unity.Multiplayer.Samples.BossRoom.ApplicationLifecycle.Messages;
5-
using Unity.Multiplayer.Samples.BossRoom.Client;
6-
using Unity.Multiplayer.Samples.BossRoom.Server;
75
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
86
using Unity.Multiplayer.Samples.BossRoom.Shared.Net.UnityServices.Infrastructure;
97
using Unity.Multiplayer.Samples.BossRoom.Shared.Net.UnityServices.Lobbies;
10-
using Unity.Multiplayer.Samples.Utilities;
118
using UnityEngine;
129
using UnityEngine.SceneManagement;
1310
using VContainer;
@@ -22,9 +19,7 @@ namespace Unity.Multiplayer.Samples.BossRoom.Shared
2219
public class ApplicationController : LifetimeScope
2320
{
2421
[SerializeField] UpdateRunner m_UpdateRunner;
25-
[SerializeField] GameNetPortal m_GameNetPortal;
26-
[SerializeField] ClientGameNetPortal m_ClientNetPortal;
27-
[SerializeField] ServerGameNetPortal m_ServerGameNetPortal;
22+
[SerializeField] ConnectionManager m_ConnectionManager;
2823

2924
LocalLobby m_LocalLobby;
3025
LobbyServiceFacade m_LobbyServiceFacade;
@@ -35,9 +30,7 @@ protected override void Configure(IContainerBuilder builder)
3530
{
3631
base.Configure(builder);
3732
builder.RegisterComponent(m_UpdateRunner);
38-
builder.RegisterComponent(m_GameNetPortal);
39-
builder.RegisterComponent(m_ClientNetPortal);
40-
builder.RegisterComponent(m_ServerGameNetPortal);
33+
builder.RegisterComponent(m_ConnectionManager);
4134

4235
//the following singletons represent the local representations of the lobby that we're in and the user that we are
4336
//they can persist longer than the lifetime of the UI in MainMenu where we set up the lobby that we create or join
@@ -48,7 +41,6 @@ protected override void Configure(IContainerBuilder builder)
4841

4942
//these message channels are essential and persist for the lifetime of the lobby and relay services
5043
// Registering as instance to prevent code stripping on iOS
51-
builder.RegisterInstance(new MessageChannel<QuitGameSessionMessage>()).AsImplementedInterfaces();
5244
builder.RegisterInstance(new MessageChannel<QuitApplicationMessage>()).AsImplementedInterfaces();
5345
builder.RegisterInstance(new MessageChannel<UnityServiceErrorMessage>()).AsImplementedInterfaces();
5446
builder.RegisterInstance(new MessageChannel<ConnectStatus>()).AsImplementedInterfaces();
@@ -80,11 +72,9 @@ private void Start()
8072
m_LocalLobby = Container.Resolve<LocalLobby>();
8173
m_LobbyServiceFacade = Container.Resolve<LobbyServiceFacade>();
8274

83-
var quitGameSessionSub = Container.Resolve<ISubscriber<QuitGameSessionMessage>>();
8475
var quitApplicationSub = Container.Resolve<ISubscriber<QuitApplicationMessage>>();
8576

8677
var subHandles = new DisposableGroup();
87-
subHandles.Add(quitGameSessionSub.Subscribe(LeaveSession));
8878
subHandles.Add(quitApplicationSub.Subscribe(QuitGame));
8979
m_Subscriptions = subHandles;
9080

@@ -131,22 +121,6 @@ private bool OnWantToQuit()
131121
return canQuit;
132122
}
133123

134-
private void LeaveSession(QuitGameSessionMessage msg)
135-
{
136-
m_LobbyServiceFacade.EndTracking();
137-
138-
if (msg.UserRequested)
139-
{
140-
// first disconnect then return to menu
141-
var gameNetPortal = GameNetPortal.Instance;
142-
if (gameNetPortal != null)
143-
{
144-
gameNetPortal.RequestDisconnect();
145-
}
146-
}
147-
SceneLoaderWrapper.Instance.LoadScene("MainMenu", useNetworkSceneManager: false);
148-
}
149-
150124
private void QuitGame(QuitApplicationMessage msg)
151125
{
152126
#if UNITY_EDITOR

Assets/Scripts/ApplicationLifecycle/Messages/QuitGameSessionMessage.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

Assets/Scripts/ApplicationLifecycle/Messages/QuitGameSessionMessage.cs.meta

Lines changed: 0 additions & 11 deletions
This file was deleted.

Assets/Scripts/ApplicationLifecycle/Unity.BossRoom.ApplicationLifecycle.asmdef

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"Unity.Collections",
1111
"Unity.Multiplayer.Samples.Utilities",
1212
"Unity.BossRoom.Utils",
13+
"Unity.BossRoom.ConnectionManagement",
1314
"VContainer",
1415
"VContainer.EnableCodeGen"
1516
],

Assets/Scripts/Gameplay/ConnectionManagement.meta renamed to Assets/Scripts/ConnectionManagement.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Unity.Collections;
4+
using Unity.Netcode;
5+
using UnityEngine;
6+
using VContainer;
7+
8+
namespace Unity.Multiplayer.Samples.BossRoom
9+
{
10+
public enum ConnectStatus
11+
{
12+
Undefined,
13+
Success, //client successfully connected. This may also be a successful reconnect.
14+
ServerFull, //can't join, server is already at capacity.
15+
LoggedInAgain, //logged in on a separate client, causing this one to be kicked out.
16+
UserRequestedDisconnect, //Intentional Disconnect triggered by the user.
17+
GenericDisconnect, //server disconnected, but no specific reason given.
18+
Reconnecting, //client lost connection and is attempting to reconnect.
19+
IncompatibleBuildType, //client build type is incompatible with server.
20+
HostEndedSession, //host intentionally ended the session.
21+
StartHostFailed, // server failed to bind
22+
StartClientFailed // failed to connect to server and/or invalid network endpoint
23+
}
24+
25+
public struct ReconnectMessage
26+
{
27+
public int CurrentAttempt;
28+
public int MaxAttempt;
29+
30+
public ReconnectMessage(int currentAttempt, int maxAttempt)
31+
{
32+
CurrentAttempt = currentAttempt;
33+
MaxAttempt = maxAttempt;
34+
}
35+
}
36+
37+
public struct ConnectionEventMessage : INetworkSerializeByMemcpy
38+
{
39+
public ConnectStatus ConnectStatus;
40+
public FixedPlayerName PlayerName;
41+
}
42+
43+
[Serializable]
44+
public class ConnectionPayload
45+
{
46+
public string playerId;
47+
public string playerName;
48+
public bool isDebug;
49+
}
50+
51+
/// <summary>
52+
/// This state machine handles connection through the NetworkManager. It is responsible for listening to
53+
/// NetworkManger callbacks and other outside calls and redirecting them to the current ConnectionState object.
54+
/// </summary>
55+
public class ConnectionManager : MonoBehaviour
56+
{
57+
ConnectionState m_CurrentState;
58+
59+
[SerializeField]
60+
NetworkManager m_NetworkManager;
61+
public NetworkManager NetworkManager => m_NetworkManager;
62+
63+
[SerializeField]
64+
NetworkObject m_GameState;
65+
public NetworkObject GameState => m_GameState;
66+
67+
[Inject]
68+
IObjectResolver m_Resolver;
69+
70+
public int MaxConnectedPlayers = 8;
71+
72+
internal readonly OfflineState m_Offline = new OfflineState();
73+
internal readonly ClientConnectingState m_ClientConnecting = new ClientConnectingState();
74+
internal readonly ClientConnectedState m_ClientConnected = new ClientConnectedState();
75+
internal readonly ClientReconnectingState m_ClientReconnecting = new ClientReconnectingState();
76+
internal readonly DisconnectingWithReasonState m_DisconnectingWithReason = new DisconnectingWithReasonState();
77+
internal readonly StartingHostState m_StartingHost = new StartingHostState();
78+
internal readonly HostingState m_Hosting = new HostingState();
79+
80+
void Awake()
81+
{
82+
DontDestroyOnLoad(gameObject);
83+
}
84+
85+
void Start()
86+
{
87+
List<ConnectionState> states = new() { m_Offline, m_ClientConnecting, m_ClientConnected, m_ClientReconnecting, m_DisconnectingWithReason, m_StartingHost, m_Hosting };
88+
foreach (var connectionState in states)
89+
{
90+
m_Resolver.Inject(connectionState);
91+
}
92+
93+
m_CurrentState = m_Offline;
94+
95+
NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback;
96+
NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
97+
NetworkManager.OnServerStarted += OnServerStarted;
98+
NetworkManager.ConnectionApprovalCallback += ApprovalCheck;
99+
}
100+
101+
void OnDestroy()
102+
{
103+
NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback;
104+
NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback;
105+
NetworkManager.OnServerStarted -= OnServerStarted;
106+
NetworkManager.ConnectionApprovalCallback -= ApprovalCheck;
107+
108+
}
109+
110+
internal void ChangeState(ConnectionState nextState)
111+
{
112+
Debug.Log($"Changed connection state from {m_CurrentState.GetType().Name} to {nextState.GetType().Name}.");
113+
114+
if (m_CurrentState != null)
115+
{
116+
m_CurrentState.Exit();
117+
}
118+
m_CurrentState = nextState;
119+
m_CurrentState.Enter();
120+
}
121+
122+
void OnClientDisconnectCallback(ulong clientId)
123+
{
124+
m_CurrentState.OnClientDisconnect(clientId);
125+
}
126+
127+
void OnClientConnectedCallback(ulong clientId)
128+
{
129+
m_CurrentState.OnClientConnected(clientId);
130+
}
131+
132+
void OnServerStarted()
133+
{
134+
m_CurrentState.OnServerStarted();
135+
}
136+
137+
void ApprovalCheck(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
138+
{
139+
m_CurrentState.ApprovalCheck(request, response);
140+
}
141+
142+
public void StartClientLobby(string playerName)
143+
{
144+
m_CurrentState.StartClientLobby(playerName);
145+
}
146+
147+
public void StartClientIp(string playerName, string ipaddress, int port)
148+
{
149+
m_CurrentState.StartClientIP(playerName, ipaddress, port);
150+
}
151+
152+
public void StartHostLobby(string playerName)
153+
{
154+
m_CurrentState.StartHostLobby(playerName);
155+
}
156+
157+
public void StartHostIp(string playerName, string ipaddress, int port)
158+
{
159+
m_CurrentState.StartHostIP(playerName, ipaddress, port);
160+
}
161+
162+
public void RequestShutdown()
163+
{
164+
m_CurrentState.OnUserRequestedShutdown();
165+
}
166+
167+
/// <summary>
168+
/// Registers the message handler for custom named messages. This should only be done once StartClient has been
169+
/// called (start client will initialize NetworkSceneManager and CustomMessagingManager)
170+
/// </summary>
171+
public void RegisterCustomMessages()
172+
{
173+
NetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), ReceiveServerToClientSetDisconnectReason_CustomMessage);
174+
}
175+
176+
void ReceiveServerToClientSetDisconnectReason_CustomMessage(ulong clientID, FastBufferReader reader)
177+
{
178+
reader.ReadValueSafe(out ConnectStatus status);
179+
m_CurrentState.OnDisconnectReasonReceived(status);
180+
}
181+
182+
/// <summary>
183+
/// Sends a DisconnectReason to all connected clients. This should only be done on the server, prior to disconnecting the clients.
184+
/// </summary>
185+
/// <param name="status"> The reason for the upcoming disconnect.</param>
186+
public static void SendServerToAllClientsSetDisconnectReason(ConnectStatus status)
187+
{
188+
var writer = new FastBufferWriter(sizeof(ConnectStatus), Allocator.Temp);
189+
writer.WriteValueSafe(status);
190+
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessageToAll(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), writer);
191+
}
192+
193+
/// <summary>
194+
/// Sends a DisconnectReason to the indicated client. This should only be done on the server, prior to disconnecting the client.
195+
/// </summary>
196+
/// <param name="clientID"> id of the client to send to </param>
197+
/// <param name="status"> The reason for the upcoming disconnect.</param>
198+
public static void SendServerToClientSetDisconnectReason(ulong clientID, ConnectStatus status)
199+
{
200+
var writer = new FastBufferWriter(sizeof(ConnectStatus), Allocator.Temp);
201+
writer.WriteValueSafe(status);
202+
NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), clientID, writer);
203+
}
204+
}
205+
}

Assets/Scripts/ConnectionManagement/ConnectionManager.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/ConnectionManagement/ConnectionState.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)