Skip to content

Commit 98f243a

Browse files
authored
Merge pull request #133 from MidLevel/feature/scene-object-spawning
Feature/scene object spawning
2 parents 3e838e2 + 269739f commit 98f243a

10 files changed

Lines changed: 647 additions & 127 deletions

File tree

MLAPI/Data/MLAPIConstants.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ public static class MLAPIConstants
88
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
99
public const string MLAPI_PROTOCOL_VERSION = "3.0.0";
1010

11-
1211
public const byte MLAPI_CERTIFICATE_HAIL = 0;
1312
public const byte MLAPI_CERTIFICATE_HAIL_RESPONSE = 1;
1413
public const byte MLAPI_GREETINGS = 2;
@@ -18,16 +17,17 @@ public static class MLAPIConstants
1817
public const byte MLAPI_CLIENT_DISCONNECT = 6;
1918
public const byte MLAPI_DESTROY_OBJECT = 7;
2019
public const byte MLAPI_SWITCH_SCENE = 8;
21-
public const byte MLAPI_SPAWN_POOL_OBJECT = 9;
22-
public const byte MLAPI_DESTROY_POOL_OBJECT = 10;
23-
public const byte MLAPI_CHANGE_OWNER = 11;
24-
public const byte MLAPI_ADD_OBJECTS = 12;
25-
public const byte MLAPI_TIME_SYNC = 13;
26-
public const byte MLAPI_NETWORKED_VAR_DELTA = 14;
27-
public const byte MLAPI_NETWORKED_VAR_UPDATE = 15;
28-
public const byte MLAPI_SERVER_RPC = 16;
29-
public const byte MLAPI_CLIENT_RPC = 17;
30-
public const byte MLAPI_CUSTOM_MESSAGE = 18;
20+
public const byte MLAPI_CLIENT_SWITCH_SCENE_COMPLETED = 9;
21+
public const byte MLAPI_SPAWN_POOL_OBJECT = 10;
22+
public const byte MLAPI_DESTROY_POOL_OBJECT = 11;
23+
public const byte MLAPI_CHANGE_OWNER = 12;
24+
public const byte MLAPI_ADD_OBJECTS = 13;
25+
public const byte MLAPI_TIME_SYNC = 14;
26+
public const byte MLAPI_NETWORKED_VAR_DELTA = 15;
27+
public const byte MLAPI_NETWORKED_VAR_UPDATE = 16;
28+
public const byte MLAPI_SERVER_RPC = 17;
29+
public const byte MLAPI_CLIENT_RPC = 18;
30+
public const byte MLAPI_CUSTOM_MESSAGE = 19;
3131
public const byte INVALID = 32;
3232

3333
public static readonly string[] MESSAGE_NAMES = {
@@ -40,6 +40,7 @@ public static class MLAPIConstants
4040
"MLAPI_CLIENT_DISCONNECT",
4141
"MLAPI_DESTROY_OBJECT",
4242
"MLAPI_SWITCH_SCENE",
43+
"MLAPI_CLIENT_SWITCH_SCENE_COMPLETED",
4344
"MLAPI_SPAWN_POOL_OBJECT",
4445
"MLAPI_DESTROY_POOL_OBJECT",
4546
"MLAPI_CHANGE_OWNER",

MLAPI/Data/NetworkConfig.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public class NetworkConfig
119119
/// </summary>
120120
public bool EnableSceneSwitching = true;
121121
/// <summary>
122-
/// If your logic uses the NetwokrTime, this should probably be turned off. If however it's needed to maximize accuracy, this is recommended to be turned on
122+
/// If your logic uses the NetworkedTime, this should probably be turned off. If however it's needed to maximize accuracy, this is recommended to be turned on
123123
/// </summary>
124124
public bool EnableTimeResync = false;
125125
/// <summary>
@@ -137,6 +137,11 @@ public class NetworkConfig
137137
public HashSize PrefabHashSize = HashSize.VarIntTwoBytes;
138138
/// <summary>
139139
/// Wheter or not to enable encryption
140+
/// The amount of seconds to wait on all clients to load requested scene before the SwitchSceneProgress onComplete callback, that waits for all clients to complete loading, is called anyway.
141+
/// </summary>
142+
public int LoadSceneTimeOut = 120;
143+
/// <summary>
144+
/// Wheter or not to enable the ECDHE key exchange to allow for encryption and authentication of messages
140145
/// </summary>
141146
[Header("Cryptography")]
142147
public bool EnableEncryption = false;
@@ -225,6 +230,7 @@ public string ToBase64()
225230
writer.WriteBool(config.EnableEncryption);
226231
writer.WriteBool(config.SignKeyExchange);
227232
writer.WriteBool(config.EnableSceneSwitching);
233+
writer.WriteInt32Packed(config.LoadSceneTimeOut);
228234
writer.WriteBool(config.EnableTimeResync);
229235
writer.WriteBits((byte)config.RpcHashSize, 3);
230236
stream.PadStream();
@@ -306,6 +312,7 @@ public void FromBase64(string base64, bool createDummyObject = false)
306312
config.EnableEncryption = reader.ReadBool();
307313
config.SignKeyExchange = reader.ReadBool();
308314
config.EnableSceneSwitching = reader.ReadBool();
315+
config.LoadSceneTimeOut = reader.ReadInt32Packed();
309316
config.EnableTimeResync = reader.ReadBool();
310317
config.RpcHashSize = (HashSize)reader.ReadBits(3);
311318
}

MLAPI/Data/SceneSwitchProgress.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace MLAPI.Components
6+
{
7+
/// <summary>
8+
/// Class for tracking scene switching progress by server and clients.
9+
/// </summary>
10+
public class SceneSwitchProgress
11+
{
12+
/// <summary>
13+
/// List of clientIds of those clients that is done loading the scene.
14+
/// </summary>
15+
public List<uint> DoneClients { get; } = new List<uint>();
16+
/// <summary>
17+
/// The NetworkTime time at the moment the scene switch was initiated by the server.
18+
/// </summary>
19+
public float TimeAtInitiation { get; } = NetworkingManager.singleton.NetworkTime;
20+
/// <summary>
21+
/// Delegate type for when the switch scene progress is completed. Either by all clients done loading the scene or by time out.
22+
/// </summary>
23+
public delegate void OnCompletedDelegate(bool timedOut);
24+
/// <summary>
25+
/// The callback invoked when the switch scene progress is completed. Either by all clients done loading the scene or by time out.
26+
/// </summary>
27+
public event OnCompletedDelegate OnComplete;
28+
/// <summary>
29+
/// Is this scene switch progresses completed, all clients are done loading the scene or a timeout has occured.
30+
/// </summary>
31+
public bool isCompleted { get; private set; }
32+
/// <summary>
33+
/// If all clients are done loading the scene, at the moment of completed.
34+
/// </summary>
35+
public bool isAllClientsDoneLoading { get; private set; }
36+
/// <summary>
37+
/// Delegate type for when a client is done loading the scene.
38+
/// </summary>
39+
public delegate void OnClientLoadedSceneDelegate(uint clientId);
40+
/// <summary>
41+
/// The callback invoked when a client is done loading the scene.
42+
/// </summary>
43+
public event OnClientLoadedSceneDelegate OnClientLoadedScene;
44+
45+
internal Guid guid { get; } = Guid.NewGuid();
46+
47+
private Coroutine timeOutCoroutine;
48+
private AsyncOperation sceneLoadOperation;
49+
50+
internal SceneSwitchProgress()
51+
{
52+
timeOutCoroutine = NetworkingManager.singleton.StartCoroutine(NetworkingManager.singleton.TimeOutSwitchSceneProgress(this));
53+
}
54+
55+
internal void AddClientAsDone(uint clientId)
56+
{
57+
DoneClients.Add(clientId);
58+
if (OnClientLoadedScene != null)
59+
OnClientLoadedScene.Invoke(clientId);
60+
CheckCompletion();
61+
}
62+
63+
internal void RemoveClientAsDone(uint clientId)
64+
{
65+
DoneClients.Remove(clientId);
66+
CheckCompletion();
67+
}
68+
69+
internal void SetSceneLoadOperation(AsyncOperation sceneLoadOperation)
70+
{
71+
this.sceneLoadOperation = sceneLoadOperation;
72+
this.sceneLoadOperation.completed += (AsyncOperation operation) => { CheckCompletion(); };
73+
}
74+
75+
internal void CheckCompletion()
76+
{
77+
if (!isCompleted && DoneClients.Count == NetworkingManager.singleton.ConnectedClientsList.Count && sceneLoadOperation.isDone)
78+
{
79+
isCompleted = true;
80+
isAllClientsDoneLoading = true;
81+
NetworkSceneManager.sceneSwitchProgresses.Remove(guid);
82+
if (OnComplete != null)
83+
OnComplete.Invoke(false);
84+
85+
NetworkingManager.singleton.StopCoroutine(timeOutCoroutine);
86+
}
87+
}
88+
89+
internal void SetTimedOut()
90+
{
91+
if (!isCompleted)
92+
{
93+
isCompleted = true;
94+
NetworkSceneManager.sceneSwitchProgresses.Remove(guid);
95+
if (OnComplete != null)
96+
OnComplete.Invoke(true);
97+
}
98+
}
99+
100+
}
101+
}

MLAPI/MLAPI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
<Compile Include="Data\NetworkedVar.cs" />
9292
<Compile Include="Data\NetworkedVarMeta.cs" />
9393
<Compile Include="Data\PendingClient.cs" />
94+
<Compile Include="Data\SceneSwitchProgress.cs" />
9495
<Compile Include="Data\SecuritySendFlags.cs" />
9596
<Compile Include="Data\Transports\ChannelType.cs" />
9697
<Compile Include="Data\FixedQueue.cs" />

MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,32 @@ private static FieldInfo[] GetFieldInfoForType(Type type)
216216
return fieldTypes[type];
217217
}
218218

219+
internal List<INetworkedVar> GetDummyNetworkedVars()
220+
{
221+
List<INetworkedVar> networkedVars = new List<INetworkedVar>();
222+
FieldInfo[] sortedFields = GetFieldInfoForType(GetType());
223+
for (int i = 0; i < sortedFields.Length; i++)
224+
{
225+
Type fieldType = sortedFields[i].FieldType;
226+
if (fieldType.HasInterface(typeof(INetworkedVar)))
227+
{
228+
INetworkedVar instance = null;
229+
if (fieldType.IsGenericTypeDefinition)
230+
{
231+
Type genericType = fieldType.MakeGenericType(fieldType.GetGenericArguments());
232+
instance = (INetworkedVar)Activator.CreateInstance(genericType, true);
233+
}
234+
else
235+
{
236+
instance = (INetworkedVar)Activator.CreateInstance(fieldType, true);
237+
}
238+
instance.SetNetworkedBehaviour(this);
239+
networkedVars.Add(instance);
240+
}
241+
}
242+
return networkedVars;
243+
}
244+
219245
internal void NetworkedVarInit()
220246
{
221247
if (networkedVarInit)
@@ -337,16 +363,17 @@ private bool CouldHaveDirtyVars()
337363
return false;
338364
}
339365

340-
internal void HandleNetworkedVarDeltas(Stream stream, uint clientId)
366+
367+
internal static void HandleNetworkedVarDeltas(List<INetworkedVar> networkedVarList, Stream stream, uint clientId)
341368
{
342369
using (PooledBitReader reader = PooledBitReader.Get(stream))
343370
{
344-
for (int i = 0; i < networkedVarFields.Count; i++)
371+
for (int i = 0; i < networkedVarList.Count; i++)
345372
{
346373
if (!reader.ReadBool())
347374
continue;
348375

349-
if (isServer && !networkedVarFields[i].CanClientWrite(clientId))
376+
if (NetworkingManager.singleton.isServer && !networkedVarList[i].CanClientWrite(clientId))
350377
{
351378
//This client wrote somewhere they are not allowed. This is critical
352379
//We can't just skip this field. Because we don't actually know how to dummy read
@@ -359,21 +386,21 @@ internal void HandleNetworkedVarDeltas(Stream stream, uint clientId)
359386
return;
360387
}
361388

362-
networkedVarFields[i].ReadDelta(stream);
389+
networkedVarList[i].ReadDelta(stream);
363390
}
364391
}
365392
}
366393

367-
internal void HandleNetworkedVarUpdate(Stream stream, uint clientId)
394+
internal static void HandleNetworkedVarUpdate(List<INetworkedVar> networkedVarList, Stream stream, uint clientId)
368395
{
369396
using (PooledBitReader reader = PooledBitReader.Get(stream))
370397
{
371-
for (int i = 0; i < networkedVarFields.Count; i++)
398+
for (int i = 0; i < networkedVarList.Count; i++)
372399
{
373400
if (!reader.ReadBool())
374401
continue;
375402

376-
if (isServer && !networkedVarFields[i].CanClientWrite(clientId))
403+
if (NetworkingManager.singleton.isServer && !networkedVarList[i].CanClientWrite(clientId))
377404
{
378405
//This client wrote somewhere they are not allowed. This is critical
379406
//We can't just skip this field. Because we don't actually know how to dummy read
@@ -386,11 +413,34 @@ internal void HandleNetworkedVarUpdate(Stream stream, uint clientId)
386413
return;
387414
}
388415

389-
networkedVarFields[i].ReadField(stream);
416+
networkedVarList[i].ReadField(stream);
390417
}
391418
}
392419
}
393420

421+
internal static void WriteNetworkedVarData(List<INetworkedVar> networkedVarList, PooledBitWriter writer, Stream stream, uint clientId)
422+
{
423+
if (networkedVarList.Count == 0)
424+
return;
425+
for (int j = 0; j < networkedVarList.Count; j++)
426+
{
427+
bool canClientRead = networkedVarList[j].CanClientRead(clientId);
428+
writer.WriteBool(canClientRead);
429+
if (canClientRead) networkedVarList[j].WriteField(stream);
430+
}
431+
}
432+
433+
internal static void SetNetworkedVarData(List<INetworkedVar> networkedVarList, PooledBitReader reader, Stream stream)
434+
{
435+
if (networkedVarList.Count == 0)
436+
return;
437+
for (int j = 0; j < networkedVarList.Count; j++)
438+
{
439+
if (reader.ReadBool()) networkedVarList[j].ReadField(stream);
440+
}
441+
}
442+
443+
394444
#endregion
395445

396446
#region MESSAGING_SYSTEM
@@ -2592,7 +2642,9 @@ public void InvokeServerRpc(string methodName, Stream stream, string channel = n
25922642
/// <returns></returns>
25932643
protected NetworkedObject GetNetworkedObject(uint networkId)
25942644
{
2595-
return SpawnManager.SpawnedObjects[networkId];
2645+
if(SpawnManager.SpawnedObjects.ContainsKey(networkId))
2646+
return SpawnManager.SpawnedObjects[networkId];
2647+
return null;
25962648
}
25972649
}
25982650
}

MLAPI/MonoBehaviours/Core/NetworkedObject.cs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,15 @@ internal set
8787
/// Gets if the object has yet been spawned across the network
8888
/// </summary>
8989
public bool isSpawned { get; internal set; }
90-
internal bool? sceneObject = null;
90+
internal bool? destroyWithScene = null;
91+
92+
/// <summary>
93+
/// When enabled this gameobject will not be spawned on the client until the scene it was originally spawned inside at the server is fully loaded on the client.
94+
/// </summary>
95+
[Tooltip("When enabled this gameobject will not be spawned on the client until the scene it was originally spawned inside at the server is fully loaded on the client.")]
96+
public bool SceneDelayedSpawn = false;
97+
98+
internal uint sceneSpawnedInIndex = 0;
9199

92100
private void OnDestroy()
93101
{
@@ -98,9 +106,11 @@ private void OnDestroy()
98106
/// <summary>
99107
/// Spawns this GameObject across the network. Can only be called from the Server
100108
/// </summary>
101-
public void Spawn(Stream spawnPayload = null)
109+
/// <param name="spawnPayload">The writer containing the spawn payload</param>
110+
/// <param name="destroyWithScene">Should the object be destroyd when the scene is changed</param>
111+
public void Spawn(Stream spawnPayload = null, bool destroyWithScene = false)
102112
{
103-
SpawnManager.SpawnObject(this, null, spawnPayload);
113+
SpawnManager.SpawnObject(this, null, spawnPayload, destroyWithScene);
104114
}
105115

106116
/// <summary>
@@ -116,9 +126,10 @@ public void UnSpawn()
116126
/// </summary>
117127
/// <param name="clientId">The clientId to own the object</param>
118128
/// <param name="spawnPayload">The writer containing the spawn payload</param>
119-
public void SpawnWithOwnership(uint clientId, Stream spawnPayload = null)
129+
/// <param name="destroyWithScene">Should the object be destroyd when the scene is changed</param>
130+
public void SpawnWithOwnership(uint clientId, Stream spawnPayload = null, bool destroyWithScene = false)
120131
{
121-
SpawnManager.SpawnObject(this, clientId, spawnPayload);
132+
SpawnManager.SpawnObject(this, clientId, spawnPayload, destroyWithScene);
122133
}
123134

124135
/// <summary>
@@ -211,14 +222,7 @@ internal void WriteNetworkedVarData(Stream stream, uint clientId)
211222
for (int i = 0; i < childNetworkedBehaviours.Count; i++)
212223
{
213224
childNetworkedBehaviours[i].NetworkedVarInit();
214-
if (childNetworkedBehaviours[i].networkedVarFields.Count == 0)
215-
continue;
216-
for (int j = 0; j < childNetworkedBehaviours[i].networkedVarFields.Count; j++)
217-
{
218-
bool canClientRead = childNetworkedBehaviours[i].networkedVarFields[j].CanClientRead(clientId);
219-
writer.WriteBool(canClientRead);
220-
if (canClientRead) childNetworkedBehaviours[i].networkedVarFields[j].WriteField(stream);
221-
}
225+
NetworkedBehaviour.WriteNetworkedVarData(childNetworkedBehaviours[i].networkedVarFields, writer, stream, clientId);
222226
}
223227
}
224228
}
@@ -230,12 +234,7 @@ internal void SetNetworkedVarData(Stream stream)
230234
for (int i = 0; i < childNetworkedBehaviours.Count; i++)
231235
{
232236
childNetworkedBehaviours[i].NetworkedVarInit();
233-
if (childNetworkedBehaviours[i].networkedVarFields.Count == 0)
234-
continue;
235-
for (int j = 0; j < childNetworkedBehaviours[i].networkedVarFields.Count; j++)
236-
{
237-
if (reader.ReadBool()) childNetworkedBehaviours[i].networkedVarFields[j].ReadField(stream);
238-
}
237+
NetworkedBehaviour.SetNetworkedVarData(childNetworkedBehaviours[i].networkedVarFields, reader, stream);
239238
}
240239
}
241240
}

0 commit comments

Comments
 (0)