Skip to content

Commit 330b0b8

Browse files
committed
Merge branch 'master' of https://github.com/MidLevel/MLAPI
2 parents 1294979 + d91b16c commit 330b0b8

22 files changed

+599
-106
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using MLAPI;
3+
using MLAPI.Messaging;
4+
using UnityEngine;
5+
6+
namespace MLAPI_Examples
7+
{
8+
public class BasicRpcPositionSync : NetworkedBehaviour
9+
{
10+
// Send position no more than 20 times a second
11+
public int SendsPerSecond = 20;
12+
// Dont send position unless we have moved at least 1 cm
13+
public float MinimumMovement = 0.01f;
14+
// Dont send position unless we have rotated at least 1 degree
15+
public float MinimumRotation = 1f;
16+
// Allows you to change the channel position updates are sent on. Its prefered to be UnreliableSequenced for fast paced.
17+
public string Channel = "MLAPI_DEFAULT_MESSAGE";
18+
19+
private float lastSendTime;
20+
private Vector3 lastSendPosition;
21+
private Quaternion lastSendRotation;
22+
23+
public override void NetworkStart()
24+
{
25+
// This is called when the object is spawned. Once this gets invoked. The object is ready for RPC and var changes.
26+
27+
// Set the defaults to prevent a position update straight after spawn with the same redundant values. (The MLAPI syncs positions on spawn)
28+
lastSendPosition = transform.position;
29+
lastSendRotation = transform.rotation;
30+
}
31+
32+
private void Update()
33+
{
34+
// Check if its time to send a new position update
35+
if (IsOwner && Time.time - lastSendTime > (1f / SendsPerSecond))
36+
{
37+
// Check if we have moved enough or rotated enough for a position update
38+
if (Vector3.Distance(lastSendPosition, transform.position) >= MinimumMovement || Quaternion.Angle(lastSendRotation, transform.rotation) > MinimumRotation)
39+
{
40+
// We moved enough.
41+
42+
// Set the last states
43+
lastSendTime = Time.time;
44+
lastSendPosition = transform.position;
45+
lastSendRotation = transform.rotation;
46+
47+
if (IsClient)
48+
{
49+
// If we are a client. (A client can be either a normal client or a HOST), we want to send a ServerRPC. ServerRPCs does work for host to make code consistent.
50+
InvokeServerRpc(SendPositionToServer, transform.position, transform.rotation, Channel);
51+
}
52+
else if (IsServer)
53+
{
54+
// This is a strict server with no client attached. We can thus send the ClientRPC straight away without the server inbetween.
55+
InvokeClientRpcOnEveryone(SetPosition, transform.position, transform.rotation, Channel);
56+
}
57+
}
58+
}
59+
}
60+
61+
[ServerRPC(RequireOwnership = true)]
62+
public void SendPositionToServer(Vector3 position, Quaternion rotation)
63+
{
64+
// This code gets ran on the server at the request of clients or the host
65+
66+
// Tell every client EXCEPT the owner (since they are the ones that actually send the position) to apply the new position
67+
InvokeClientRpcOnEveryoneExcept(SetPosition, OwnerClientId, position, rotation, Channel);
68+
}
69+
70+
[ClientRPC]
71+
public void SetPosition(Vector3 position, Quaternion rotation)
72+
{
73+
// This code gets ran on the clients at the request of the server.
74+
75+
transform.position = position;
76+
transform.rotation = rotation;
77+
}
78+
}
79+
}

MLAPI/Core/NetworkingManager.cs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,15 @@ internal set
8787
}
8888
private ulong localClientId;
8989
/// <summary>
90-
/// Gets a dictionary of connected clients and their clientId keys
90+
/// Gets a dictionary of connected clients and their clientId keys. This is only populated on the server.
9191
/// </summary>
9292
public readonly Dictionary<ulong, NetworkedClient> ConnectedClients = new Dictionary<ulong, NetworkedClient>();
9393
/// <summary>
94-
/// Gets a list of connected clients
94+
/// Gets a list of connected clients. This is only populated on the server.
9595
/// </summary>
9696
public readonly List<NetworkedClient> ConnectedClientsList = new List<NetworkedClient>();
9797
/// <summary>
98-
/// Gets a dictionary of the clients that have been accepted by the transport but are still pending by the MLAPI.
98+
/// Gets a dictionary of the clients that have been accepted by the transport but are still pending by the MLAPI. This is only populated on the server.
9999
/// </summary>
100100
public readonly Dictionary<ulong, PendingClient> PendingClients = new Dictionary<ulong, PendingClient>();
101101
/// <summary>
@@ -149,19 +149,33 @@ internal set
149149
/// </summary>
150150
public bool IsConnectedClient { get; internal set; }
151151
/// <summary>
152-
/// The callback to invoke once a client connects
152+
/// The callback to invoke once a client connects. This callback is only ran on the server and on the local client that connects.
153153
/// </summary>
154-
public Action<ulong> OnClientConnectedCallback = null;
154+
public event Action<ulong> OnClientConnectedCallback = null;
155+
internal void InvokeOnClientConnectedCallback(ulong clientId)
156+
{
157+
if (OnClientConnectedCallback != null)
158+
{
159+
OnClientConnectedCallback(clientId);
160+
}
161+
}
155162
/// <summary>
156-
/// The callback to invoke when a client disconnects
163+
/// The callback to invoke when a client disconnects. This callback is only ran on the server and on the local client that disconnects.
157164
/// </summary>
158-
public Action<ulong> OnClientDisconnectCallback = null;
165+
public event Action<ulong> OnClientDisconnectCallback = null;
166+
internal void InvokeOnClientDisconnectCallback(ulong clientId)
167+
{
168+
if (OnClientDisconnectCallback != null)
169+
{
170+
OnClientDisconnectCallback(clientId);
171+
}
172+
}
159173
/// <summary>
160174
/// The callback to invoke once the server is ready
161175
/// </summary>
162-
public Action OnServerStarted = null;
176+
public event Action OnServerStarted = null;
163177
/// <summary>
164-
/// Delegate type called when connection has been approved
178+
/// Delegate type called when connection has been approved. This only has to be set on the server.
165179
/// </summary>
166180
/// <param name="createPlayerObject">If true, a player object will be created. Otherwise the client will have no object.</param>
167181
/// <param name="playerPrefabHash">The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used.</param>
@@ -172,7 +186,14 @@ internal set
172186
/// <summary>
173187
/// The callback to invoke during connection approval
174188
/// </summary>
175-
public Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
189+
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
190+
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action)
191+
{
192+
if (ConnectionApprovalCallback != null)
193+
{
194+
ConnectionApprovalCallback(payload, clientId, action);
195+
}
196+
}
176197
/// <summary>
177198
/// The current NetworkingConfiguration
178199
/// </summary>

MLAPI/Messaging/InternalMessageHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ internal static void HandleConnectionRequest(ulong clientId, Stream stream)
186186
if (NetworkingManager.Singleton.NetworkConfig.ConnectionApproval)
187187
{
188188
byte[] connectionBuffer = reader.ReadByteArray();
189-
NetworkingManager.Singleton.ConnectionApprovalCallback(connectionBuffer, clientId, (createPlayerObject, playerPrefabHash, approved, position, rotation) =>
189+
NetworkingManager.Singleton.InvokeConnectionApproval(connectionBuffer, clientId, (createPlayerObject, playerPrefabHash, approved, position, rotation) =>
190190
{
191191
NetworkingManager.Singleton.HandleApproval(clientId, createPlayerObject, playerPrefabHash, approved, position, rotation);
192192
});
@@ -288,7 +288,7 @@ void DelayedSpawnAction(Stream continuationStream)
288288

289289
NetworkingManager.Singleton.IsConnectedClient = true;
290290

291-
if (NetworkingManager.Singleton.OnClientConnectedCallback != null) NetworkingManager.Singleton.OnClientConnectedCallback.Invoke(NetworkingManager.Singleton.LocalClientId);
291+
NetworkingManager.Singleton.InvokeOnClientConnectedCallback(NetworkingManager.Singleton.LocalClientId);
292292
}
293293
}
294294

MLAPI/NetworkedVar/NetworkedVarSettings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ namespace MLAPI.NetworkedVar
1212
public class NetworkedVarSettings
1313
{
1414
/// <summary>
15-
/// Defines the read permissions for this var
15+
/// Defines the write permissions for this var
1616
/// </summary>
1717
public NetworkedVarPermission WritePermission = NetworkedVarPermission.ServerOnly;
1818
/// <summary>
19-
/// Defines the write permissions for this var
19+
/// Defines the read permissions for this var
2020
/// </summary>
2121
public NetworkedVarPermission ReadPermission = NetworkedVarPermission.Everyone;
2222
/// <summary>

MLAPI/SceneManagement/NetworkSceneManager.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@ public static class NetworkSceneManager
2323
/// </summary>
2424
public delegate void SceneSwitchedDelegate();
2525
/// <summary>
26+
/// Delegate for when a scene switch has been initiated
27+
/// </summary>
28+
public delegate void SceneSwitchStartedDelegate(AsyncOperation operation);
29+
/// <summary>
2630
/// Event that is invoked when the scene is switched
2731
/// </summary>
2832
public static event SceneSwitchedDelegate OnSceneSwitched;
33+
/// <summary>
34+
/// Event that is invoked when a local scene switch has started
35+
/// </summary>
36+
public static event SceneSwitchStartedDelegate OnSceneSwitchStarted;
2937

3038
internal static readonly HashSet<string> registeredSceneNames = new HashSet<string>();
3139
internal static readonly Dictionary<string, uint> sceneNameToIndex = new Dictionary<string, uint>();
@@ -105,12 +113,18 @@ public static SceneSwitchProgress SwitchScene(string sceneName)
105113

106114
// Switch scene
107115
AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
116+
108117
nextSceneName = sceneName;
109118

110119
sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(switchSceneProgress.guid, null); };
111120

112121
switchSceneProgress.SetSceneLoadOperation(sceneLoad);
113122

123+
if (OnSceneSwitchStarted != null)
124+
{
125+
OnSceneSwitchStarted(sceneLoad);
126+
}
127+
114128
return switchSceneProgress;
115129
}
116130

@@ -122,10 +136,6 @@ internal static void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream
122136
if (LogHelper.CurrentLogLevel <= LogLevel.Normal) LogHelper.LogWarning("Server requested a scene switch to a non registered scene");
123137
return;
124138
}
125-
else if (SceneManager.GetActiveScene().name == sceneIndexToString[sceneIndex])
126-
{
127-
return; //This scene is already loaded. This usually happends at first load
128-
}
129139

130140
lastScene = SceneManager.GetActiveScene();
131141

@@ -137,12 +147,18 @@ internal static void OnSceneSwitch(uint sceneIndex, Guid switchSceneGuid, Stream
137147
string sceneName = sceneIndexToString[sceneIndex];
138148

139149
AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
150+
140151
nextSceneName = sceneName;
141152

142153
sceneLoad.completed += (AsyncOperation asyncOp2) =>
143154
{
144155
OnSceneLoaded(switchSceneGuid, objectStream);
145156
};
157+
158+
if (OnSceneSwitchStarted != null)
159+
{
160+
OnSceneSwitchStarted(sceneLoad);
161+
}
146162
}
147163

148164
internal static void OnFirstSceneSwitchSync(uint sceneIndex, Guid switchSceneGuid)

MLAPI/Spawning/SpawnManager.cs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,21 +271,32 @@ internal static NetworkedObject CreateLocalNetworkedObject(bool softCreate, ulon
271271
}
272272
else
273273
{
274-
GameObject prefab = NetworkingManager.Singleton.NetworkConfig.NetworkedPrefabs[GetNetworkedPrefabIndexOfHash(prefabHash)].Prefab;
274+
int prefabIndex = GetNetworkedPrefabIndexOfHash(prefabHash);
275275

276-
NetworkedObject networkedObject = ((position == null && rotation == null) ? MonoBehaviour.Instantiate(prefab) : MonoBehaviour.Instantiate(prefab, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity))).GetComponent<NetworkedObject>();
277-
278-
if (parent != null)
276+
if (prefabIndex < 0)
279277
{
280-
networkedObject.transform.SetParent(parent.transform, true);
281-
}
278+
if (LogHelper.CurrentLogLevel <= LogLevel.Error) LogHelper.LogError("Failed to create object locally. [PrefabHash=" + prefabHash + "]. Hash could not be found. Is the prefab registered?");
282279

283-
if (NetworkSceneManager.isSpawnedObjectsPendingInDontDestroyOnLoad)
284-
{
285-
GameObject.DontDestroyOnLoad(networkedObject.gameObject);
280+
return null;
286281
}
282+
else
283+
{
284+
GameObject prefab = NetworkingManager.Singleton.NetworkConfig.NetworkedPrefabs[prefabIndex].Prefab;
287285

288-
return networkedObject;
286+
NetworkedObject networkedObject = ((position == null && rotation == null) ? MonoBehaviour.Instantiate(prefab) : MonoBehaviour.Instantiate(prefab, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity))).GetComponent<NetworkedObject>();
287+
288+
if (parent != null)
289+
{
290+
networkedObject.transform.SetParent(parent.transform, true);
291+
}
292+
293+
if (NetworkSceneManager.isSpawnedObjectsPendingInDontDestroyOnLoad)
294+
{
295+
GameObject.DontDestroyOnLoad(networkedObject.gameObject);
296+
}
297+
298+
return networkedObject;
299+
}
289300
}
290301
}
291302
else

docs/_api/BitStream.md

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,18 @@ permalink: /api/bit-stream/
382382
</div>
383383
</div>
384384
<br>
385+
<div style="line-height: 1;">
386+
<h4 markdown="1"><b>public ``Task`` CopyToAsync(``Stream`` destination, ``CancellationToken`` cancellationToken);</b></h4>
387+
<h5 markdown="1">Inherited from: ``Stream``</h5>
388+
<h5><b>Parameters</b></h5>
389+
<div>
390+
<p style="font-size: 20px; color: #444;" markdown="1">``Stream`` destination</p>
391+
</div>
392+
<div>
393+
<p style="font-size: 20px; color: #444;" markdown="1">``CancellationToken`` cancellationToken</p>
394+
</div>
395+
</div>
396+
<br>
385397
<div style="line-height: 1;">
386398
<h4 markdown="1"><b>public ``Task`` CopyToAsync(``Stream`` destination, ``int`` bufferSize, ``CancellationToken`` cancellationToken);</b></h4>
387399
<h5 markdown="1">Inherited from: ``Stream``</h5>
@@ -505,6 +517,18 @@ permalink: /api/bit-stream/
505517
</div>
506518
</div>
507519
<br>
520+
<div style="line-height: 1;">
521+
<h4 markdown="1"><b>public ``ValueTask<int>`` ReadAsync(``Memory<byte>`` buffer, ``CancellationToken`` cancellationToken);</b></h4>
522+
<h5 markdown="1">Inherited from: ``Stream``</h5>
523+
<h5><b>Parameters</b></h5>
524+
<div>
525+
<p style="font-size: 20px; color: #444;" markdown="1">``Memory<byte>`` buffer</p>
526+
</div>
527+
<div>
528+
<p style="font-size: 20px; color: #444;" markdown="1">``CancellationToken`` cancellationToken</p>
529+
</div>
530+
</div>
531+
<br>
508532
<div style="line-height: 1;">
509533
<h4 markdown="1"><b>public ``IAsyncResult`` BeginWrite(``byte[]`` buffer, ``int`` offset, ``int`` count, ``AsyncCallback`` callback, ``object`` state);</b></h4>
510534
<h5 markdown="1">Inherited from: ``Stream``</h5>
@@ -569,44 +593,32 @@ permalink: /api/bit-stream/
569593
</div>
570594
<br>
571595
<div style="line-height: 1;">
572-
<h4 markdown="1"><b>public ``int`` Read(``Span<byte>`` destination);</b></h4>
596+
<h4 markdown="1"><b>public ``ValueTask`` WriteAsync(``ReadOnlyMemory<byte>`` buffer, ``CancellationToken`` cancellationToken);</b></h4>
573597
<h5 markdown="1">Inherited from: ``Stream``</h5>
574598
<h5><b>Parameters</b></h5>
575599
<div>
576-
<p style="font-size: 20px; color: #444;" markdown="1">``Span<byte>`` destination</p>
600+
<p style="font-size: 20px; color: #444;" markdown="1">``ReadOnlyMemory<byte>`` buffer</p>
577601
</div>
578-
</div>
579-
<br>
580-
<div style="line-height: 1;">
581-
<h4 markdown="1"><b>public ``void`` Write(``ReadOnlySpan<byte>`` source);</b></h4>
582-
<h5 markdown="1">Inherited from: ``Stream``</h5>
583-
<h5><b>Parameters</b></h5>
584602
<div>
585-
<p style="font-size: 20px; color: #444;" markdown="1">``ReadOnlySpan<byte>`` source</p>
603+
<p style="font-size: 20px; color: #444;" markdown="1">``CancellationToken`` cancellationToken</p>
586604
</div>
587605
</div>
588606
<br>
589607
<div style="line-height: 1;">
590-
<h4 markdown="1"><b>public ``ValueTask<int>`` ReadAsync(``Memory<byte>`` destination, ``CancellationToken`` cancellationToken);</b></h4>
608+
<h4 markdown="1"><b>public ``int`` Read(``Span<byte>`` buffer);</b></h4>
591609
<h5 markdown="1">Inherited from: ``Stream``</h5>
592610
<h5><b>Parameters</b></h5>
593611
<div>
594-
<p style="font-size: 20px; color: #444;" markdown="1">``Memory<byte>`` destination</p>
595-
</div>
596-
<div>
597-
<p style="font-size: 20px; color: #444;" markdown="1">``CancellationToken`` cancellationToken</p>
612+
<p style="font-size: 20px; color: #444;" markdown="1">``Span<byte>`` buffer</p>
598613
</div>
599614
</div>
600615
<br>
601616
<div style="line-height: 1;">
602-
<h4 markdown="1"><b>public ``ValueTask`` WriteAsync(``ReadOnlyMemory<byte>`` source, ``CancellationToken`` cancellationToken);</b></h4>
617+
<h4 markdown="1"><b>public ``void`` Write(``ReadOnlySpan<byte>`` buffer);</b></h4>
603618
<h5 markdown="1">Inherited from: ``Stream``</h5>
604619
<h5><b>Parameters</b></h5>
605620
<div>
606-
<p style="font-size: 20px; color: #444;" markdown="1">``ReadOnlyMemory<byte>`` source</p>
607-
</div>
608-
<div>
609-
<p style="font-size: 20px; color: #444;" markdown="1">``CancellationToken`` cancellationToken</p>
621+
<p style="font-size: 20px; color: #444;" markdown="1">``ReadOnlySpan<byte>`` buffer</p>
610622
</div>
611623
</div>
612624
<br>

0 commit comments

Comments
 (0)