mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
Merge branch 'master' of https://github.com/vis2k/Mirror
This commit is contained in:
commit
830850266a
8
Assets/Mirror/Authenticators.meta
Normal file
8
Assets/Mirror/Authenticators.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b2f9d254154cd942ba40b06b869b8f3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
116
Assets/Mirror/Authenticators/BasicAuthenticator.cs
Normal file
116
Assets/Mirror/Authenticators/BasicAuthenticator.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Mirror.Authenticators
|
||||
{
|
||||
[AddComponentMenu("Network/Authenticators/BasicAuthenticator")]
|
||||
public class BasicAuthenticator : NetworkAuthenticator
|
||||
{
|
||||
[Header("Custom Properties")]
|
||||
|
||||
// set these in the inspector
|
||||
public string username;
|
||||
public string password;
|
||||
|
||||
public class AuthRequestMessage : MessageBase
|
||||
{
|
||||
// use whatever credentials make sense for your game
|
||||
// for example, you might want to pass the accessToken if using oauth
|
||||
public string authUsername;
|
||||
public string authPassword;
|
||||
}
|
||||
|
||||
public class AuthResponseMessage : MessageBase
|
||||
{
|
||||
public byte code;
|
||||
public string message;
|
||||
}
|
||||
|
||||
public override void OnStartServer()
|
||||
{
|
||||
// register a handler for the authentication request we expect from client
|
||||
NetworkServer.RegisterHandler<AuthRequestMessage>(OnAuthRequestMessage);
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
// register a handler for the authentication response we expect from server
|
||||
NetworkClient.RegisterHandler<AuthResponseMessage>(OnAuthResponseMessage);
|
||||
}
|
||||
|
||||
public override void OnServerAuthenticate(NetworkConnection conn)
|
||||
{
|
||||
// do nothing...wait for AuthRequestMessage from client
|
||||
}
|
||||
|
||||
public override void OnClientAuthenticate(NetworkConnection conn)
|
||||
{
|
||||
AuthRequestMessage authRequestMessage = new AuthRequestMessage
|
||||
{
|
||||
authUsername = username,
|
||||
authPassword = password
|
||||
};
|
||||
|
||||
NetworkClient.Send(authRequestMessage);
|
||||
}
|
||||
|
||||
public void OnAuthRequestMessage(NetworkConnection conn, AuthRequestMessage msg)
|
||||
{
|
||||
Debug.LogFormat("Authentication Request: {0} {1}", msg.authUsername, msg.authPassword);
|
||||
|
||||
// check the credentials by calling your web server, database table, playfab api, or any method appropriate.
|
||||
if (msg.authUsername == username && msg.authPassword == password)
|
||||
{
|
||||
// create and send msg to client so it knows to proceed
|
||||
AuthResponseMessage authResponseMessage = new AuthResponseMessage
|
||||
{
|
||||
code = 100,
|
||||
message = "Success"
|
||||
};
|
||||
|
||||
NetworkServer.SendToClient(conn.connectionId, authResponseMessage);
|
||||
|
||||
// Invoke the event to complete a successful authentication
|
||||
base.OnServerAuthenticated.Invoke(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// create and send msg to client so it knows to disconnect
|
||||
AuthResponseMessage authResponseMessage = new AuthResponseMessage
|
||||
{
|
||||
code = 200,
|
||||
message = "Invalid Credentials"
|
||||
};
|
||||
|
||||
NetworkServer.SendToClient(conn.connectionId, authResponseMessage);
|
||||
|
||||
// must set NetworkConnection isAuthenticated = false
|
||||
conn.isAuthenticated = false;
|
||||
|
||||
// disconnect the client after 1 second so that response message gets delivered
|
||||
Invoke(nameof(conn.Disconnect), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAuthResponseMessage(NetworkConnection conn, AuthResponseMessage msg)
|
||||
{
|
||||
if (msg.code == 100)
|
||||
{
|
||||
Debug.LogFormat("Authentication Response: {0}", msg.message);
|
||||
|
||||
// Invoke the event to complete a successful authentication
|
||||
base.OnClientAuthenticated.Invoke(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogErrorFormat("Authentication Response: {0}", msg.message);
|
||||
|
||||
// Set this on the client for local reference
|
||||
conn.isAuthenticated = false;
|
||||
|
||||
// disconnect the client
|
||||
conn.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirror/Authenticators/BasicAuthenticator.cs.meta
Normal file
11
Assets/Mirror/Authenticators/BasicAuthenticator.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28496b776660156428f00cf78289c1ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
14
Assets/Mirror/Authenticators/Mirror.Authenticators.asmdef
Normal file
14
Assets/Mirror/Authenticators/Mirror.Authenticators.asmdef
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Mirror.Authenticators",
|
||||
"references": [
|
||||
"Mirror"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e720aa64e3f58fb4880566a322584340
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -130,6 +130,12 @@ public static bool UnpackMessage(NetworkReader messageReader, out int msgType)
|
||||
networkMessage.conn.Disconnect();
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// TODO: Figure out the correct channel
|
||||
NetworkDiagnostics.OnReceive(message, -1, networkMessage.reader.Length);
|
||||
}
|
||||
|
||||
handler(networkMessage.conn, message);
|
||||
};
|
||||
}
|
||||
|
90
Assets/Mirror/Runtime/NetworkAuthenticator.cs
Normal file
90
Assets/Mirror/Runtime/NetworkAuthenticator.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
/// <summary>
|
||||
/// Unity Event for the NetworkConnection
|
||||
/// </summary>
|
||||
[Serializable] public class UnityEventNetworkConnection : UnityEvent<NetworkConnection> { }
|
||||
|
||||
/// <summary>
|
||||
/// Base class for implementing component-based authentication during the Connect phase
|
||||
/// </summary>
|
||||
[HelpURL("https://mirror-networking.com/xmldocs/articles/Concepts/Authentication.html")]
|
||||
public abstract class NetworkAuthenticator : MonoBehaviour
|
||||
{
|
||||
[Header("Event Listeners (optional)")]
|
||||
|
||||
/// <summary>
|
||||
/// Notify subscribers on the server when a client is authenticated
|
||||
/// </summary>
|
||||
[Tooltip("Mirror has an internal subscriber to this event. You can add your own here.")]
|
||||
public UnityEventNetworkConnection OnServerAuthenticated = new UnityEventNetworkConnection();
|
||||
|
||||
/// <summary>
|
||||
/// Notify subscribers on the client when the client is authenticated
|
||||
/// </summary>
|
||||
[Tooltip("Mirror has an internal subscriber to this event. You can add your own here.")]
|
||||
public UnityEventNetworkConnection OnClientAuthenticated = new UnityEventNetworkConnection();
|
||||
|
||||
#region server
|
||||
|
||||
/// <summary>
|
||||
/// Called on server from StartServer to initialize the Authenticator
|
||||
/// <para>Server message handlers should be registered in this method.</para>
|
||||
/// </summary>
|
||||
public abstract void OnStartServer();
|
||||
|
||||
/// <summary>
|
||||
/// Called on client from StartClient to initialize the Authenticator
|
||||
/// <para>Client message handlers should be registered in this method.</para>
|
||||
/// </summary>
|
||||
public abstract void OnStartClient();
|
||||
|
||||
// This will get more code in the near future
|
||||
internal void OnServerAuthenticateInternal(NetworkConnection conn)
|
||||
{
|
||||
OnServerAuthenticate(conn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on server from OnServerAuthenticateInternal when a client needs to authenticate
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection to client.</param>
|
||||
public abstract void OnServerAuthenticate(NetworkConnection conn);
|
||||
|
||||
#endregion
|
||||
|
||||
#region client
|
||||
|
||||
// This will get more code in the near future
|
||||
internal void OnClientAuthenticateInternal(NetworkConnection conn)
|
||||
{
|
||||
OnClientAuthenticate(conn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on client from OnClientAuthenticateInternal when a client needs to authenticate
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection of the client.</param>
|
||||
public abstract void OnClientAuthenticate(NetworkConnection conn);
|
||||
|
||||
#endregion
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// automatically assign NetworkManager field if we add this to NetworkManager
|
||||
NetworkManager manager = GetComponent<NetworkManager>();
|
||||
if (manager != null && manager.authenticator == null)
|
||||
{
|
||||
manager.authenticator = this;
|
||||
UnityEditor.Undo.RecordObject(gameObject, "Assigned NetworkManager authenticator");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirror/Runtime/NetworkAuthenticator.cs.meta
Normal file
11
Assets/Mirror/Runtime/NetworkAuthenticator.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 407fc95d4a8257f448799f26cdde0c2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -32,6 +32,17 @@ public class NetworkConnection : IDisposable
|
||||
/// </remarks>
|
||||
public int connectionId = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Flag that indicates the client has been authenticated.
|
||||
/// </summary>
|
||||
public bool isAuthenticated;
|
||||
|
||||
/// <summary>
|
||||
/// General purpose object to hold authentication data, character selection, tokens, etc.
|
||||
/// associated with the connection for reference after Authentication completes.
|
||||
/// </summary>
|
||||
public object authenticationData;
|
||||
|
||||
/// <summary>
|
||||
/// Flag that tells if the connection has been marked as "ready" by a client calling ClientScene.Ready().
|
||||
/// <para>This property is read-only. It is set by the system on the client when ClientScene.Ready() is called, and set by the system on the server when a ready message is received from a client.</para>
|
||||
@ -212,6 +223,7 @@ public virtual bool Send<T>(T msg, int channelId = Channels.DefaultReliable) whe
|
||||
{
|
||||
// pack message and send
|
||||
byte[] message = MessagePacker.Pack(msg);
|
||||
NetworkDiagnostics.OnSend(msg, channelId, message.Length, 1);
|
||||
return SendBytes(message, channelId);
|
||||
}
|
||||
|
||||
|
80
Assets/Mirror/Runtime/NetworkDiagnostics.cs
Normal file
80
Assets/Mirror/Runtime/NetworkDiagnostics.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides profiling information from mirror
|
||||
/// A profiler can subscribe to these events and
|
||||
/// present the data in a friendly way to the user
|
||||
/// </summary>
|
||||
public static class NetworkDiagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes an outgoing message
|
||||
/// </summary>
|
||||
public readonly struct MessageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The message being sent
|
||||
/// </summary>
|
||||
public readonly IMessageBase message;
|
||||
/// <summary>
|
||||
/// channel through which the message was sent
|
||||
/// </summary>
|
||||
public readonly int channel;
|
||||
/// <summary>
|
||||
/// how big was the message (does not include transport headers)
|
||||
/// </summary>
|
||||
public readonly int bytes;
|
||||
/// <summary>
|
||||
/// How many connections was the message sent to
|
||||
/// If an object has a lot of observers this count could be high
|
||||
/// </summary>
|
||||
public readonly int count;
|
||||
|
||||
internal MessageInfo(IMessageBase message, int channel, int bytes, int count)
|
||||
{
|
||||
this.message = message;
|
||||
this.channel = channel;
|
||||
this.bytes = bytes;
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
#region Out messages
|
||||
/// <summary>
|
||||
/// Event that gets raised when Mirror sends a message
|
||||
/// Subscribe to this if you want to diagnose the network
|
||||
/// </summary>
|
||||
public static event Action<MessageInfo> OutMessageEvent;
|
||||
|
||||
// ENABLE_PROFILER is enabled by Unity when enabling profiling.
|
||||
[Conditional("ENABLE_PROFILER")]
|
||||
internal static void OnSend<T>(T message, int channel, int bytes, int count) where T : IMessageBase
|
||||
{
|
||||
MessageInfo outMessage = new MessageInfo(message, channel, bytes, count);
|
||||
OutMessageEvent?.Invoke(outMessage);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region In messages
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets raised when Mirror receives a message
|
||||
/// Subscribe to this if you want to profile the network
|
||||
/// </summary>
|
||||
public static event Action<MessageInfo> InMessageEvent;
|
||||
|
||||
// ENABLE_PROFILER is enabled by Unity when enabling profiling.
|
||||
[Conditional("ENABLE_PROFILER")]
|
||||
internal static void OnReceive<T>(T message, int channel, int bytes) where T : IMessageBase
|
||||
{
|
||||
MessageInfo inMessage = new MessageInfo(message, channel, bytes, 1);
|
||||
InMessageEvent?.Invoke(inMessage);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/Mirror/Runtime/NetworkDiagnostics.cs.meta
Normal file
11
Assets/Mirror/Runtime/NetworkDiagnostics.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3754b39e5f8740fd93f3337b2c4274e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -92,6 +92,10 @@ public class NetworkManager : MonoBehaviour
|
||||
[FormerlySerializedAs("m_MaxConnections")]
|
||||
public int maxConnections = 4;
|
||||
|
||||
[Header("Authentication")]
|
||||
|
||||
public NetworkAuthenticator authenticator;
|
||||
|
||||
[Header("Spawn Info")]
|
||||
|
||||
/// <summary>
|
||||
@ -394,6 +398,12 @@ public bool StartServer()
|
||||
if (runInBackground)
|
||||
Application.runInBackground = true;
|
||||
|
||||
if (authenticator != null)
|
||||
{
|
||||
authenticator.OnStartServer();
|
||||
authenticator.OnServerAuthenticated.AddListener(OnServerAuthenticated);
|
||||
}
|
||||
|
||||
ConfigureServerFrameRate();
|
||||
|
||||
if (!NetworkServer.Listen(maxConnections))
|
||||
@ -461,6 +471,12 @@ public void StartClient()
|
||||
{
|
||||
InitializeSingleton();
|
||||
|
||||
if (authenticator != null)
|
||||
{
|
||||
authenticator.OnStartClient();
|
||||
authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated);
|
||||
}
|
||||
|
||||
if (runInBackground)
|
||||
Application.runInBackground = true;
|
||||
|
||||
@ -497,6 +513,13 @@ public virtual void StartHost()
|
||||
void ConnectLocalClient()
|
||||
{
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager StartHost");
|
||||
|
||||
if (authenticator != null)
|
||||
{
|
||||
authenticator.OnStartClient();
|
||||
authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated);
|
||||
}
|
||||
|
||||
networkAddress = "localhost";
|
||||
NetworkServer.ActivateLocalClientScene();
|
||||
NetworkClient.ConnectLocalServer();
|
||||
@ -522,6 +545,9 @@ public void StopServer()
|
||||
if (!NetworkServer.active)
|
||||
return;
|
||||
|
||||
if (authenticator != null)
|
||||
authenticator.OnServerAuthenticated.RemoveListener(OnServerAuthenticated);
|
||||
|
||||
OnStopServer();
|
||||
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager StopServer");
|
||||
@ -541,6 +567,9 @@ public void StopServer()
|
||||
/// </summary>
|
||||
public void StopClient()
|
||||
{
|
||||
if (authenticator != null)
|
||||
authenticator.OnClientAuthenticated.RemoveListener(OnClientAuthenticated);
|
||||
|
||||
OnStopClient();
|
||||
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager StopClient");
|
||||
@ -752,6 +781,27 @@ void OnServerConnectInternal(NetworkConnection conn, ConnectMessage connectMsg)
|
||||
{
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager.OnServerConnectInternal");
|
||||
|
||||
if (authenticator != null)
|
||||
{
|
||||
// we have an authenticator - let it handle authentication
|
||||
authenticator.OnServerAuthenticateInternal(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// authenticate immediately
|
||||
OnServerAuthenticated(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// called after successful authentication
|
||||
void OnServerAuthenticated(NetworkConnection conn)
|
||||
{
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager.OnServerAuthenticated");
|
||||
|
||||
// set connection to authenticated
|
||||
conn.isAuthenticated = true;
|
||||
|
||||
// proceed with the login handshake by calling OnServerConnect
|
||||
if (networkSceneName != "" && networkSceneName != offlineScene)
|
||||
{
|
||||
SceneMessage msg = new SceneMessage() { sceneName = networkSceneName };
|
||||
@ -823,6 +873,27 @@ void OnClientConnectInternal(NetworkConnection conn, ConnectMessage message)
|
||||
{
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager.OnClientConnectInternal");
|
||||
|
||||
if (authenticator != null)
|
||||
{
|
||||
// we have an authenticator - let it handle authentication
|
||||
authenticator.OnClientAuthenticateInternal(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// authenticate immediately
|
||||
OnClientAuthenticated(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// called after successful authentication
|
||||
void OnClientAuthenticated(NetworkConnection conn)
|
||||
{
|
||||
if (LogFilter.Debug) Debug.Log("NetworkManager.OnClientAuthenticated");
|
||||
|
||||
// set connection to authenticated
|
||||
conn.isAuthenticated = true;
|
||||
|
||||
// proceed with the login handshake by calling OnClientConnect
|
||||
string loadedSceneName = SceneManager.GetActiveScene().name;
|
||||
if (string.IsNullOrEmpty(onlineScene) || onlineScene == offlineScene || loadedSceneName == onlineScene)
|
||||
{
|
||||
|
@ -251,6 +251,7 @@ static bool SendToObservers<T>(NetworkIdentity identity, T msg) where T: IMessag
|
||||
{
|
||||
result &= kvp.Value.SendBytes(bytes);
|
||||
}
|
||||
NetworkDiagnostics.OnSend(msg, Channels.DefaultReliable, bytes.Length, identity.observers.Count);
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
@ -296,6 +297,9 @@ public static bool SendToAll<T>(T msg, int channelId = Channels.DefaultReliable)
|
||||
{
|
||||
result &= kvp.Value.SendBytes(bytes, channelId);
|
||||
}
|
||||
|
||||
NetworkDiagnostics.OnSend(msg, channelId, bytes.Length, connections.Count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -344,6 +348,7 @@ public static bool SendToReady<T>(NetworkIdentity identity, T msg, bool includeS
|
||||
{
|
||||
// pack message into byte[] once
|
||||
byte[] bytes = MessagePacker.Pack(msg);
|
||||
int count = 0;
|
||||
|
||||
bool result = true;
|
||||
foreach (KeyValuePair<int, NetworkConnection> kvp in identity.observers)
|
||||
@ -353,8 +358,10 @@ public static bool SendToReady<T>(NetworkIdentity identity, T msg, bool includeS
|
||||
kvp.Value.isReady)
|
||||
{
|
||||
result &= kvp.Value.SendBytes(bytes, channelId);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
NetworkDiagnostics.OnSend(msg, channelId, bytes.Length, count);
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
|
@ -1,4 +1,4 @@
|
||||
![Mirror Logo](https://i.imgur.com/aZRKBeh.png)
|
||||
![Mirror Logo](https://i.imgur.com/MBpESqo.png)
|
||||
|
||||
[![Download](https://img.shields.io/badge/asset_store-brightgreen.svg)](https://www.assetstore.unity3d.com/#!/content/129321)
|
||||
[![Documentation](https://img.shields.io/badge/documentation-brightgreen.svg)](https://mirror-networking.com/xmldocs)
|
||||
|
@ -16,67 +16,45 @@ When you have a multiplayer game, often you need to store information about your
|
||||
|
||||
- Use a web service in your website
|
||||
|
||||
Trying to write a comprehensive authentication framework that cover all these is very complex. There is no one size fit all, and we would quickly end up with bloated code.
|
||||
Mirror includes an `Authenticator` abstract class that allows you to implement any authentication scheme you need.
|
||||
|
||||
Instead, **Mirror does not perform authentication**, but we provide hooks you can use to implement any of these.
|
||||
## Encryption Warning
|
||||
|
||||
Here is an example of how to implement simple username/password authentication:
|
||||
By default Mirror uses Telepathy, which is not encrypted, so if you want to do authentication through Mirror, we highly recommend you use a transport that supports encryption.
|
||||
|
||||
1. Select your `NetworkManager` game object in the unity editor.
|
||||
## Custom Authenticators
|
||||
|
||||
2. In the inspector, under `Spawn Info`, disable `Auto Create Player`
|
||||
To make your own custom Authenticator, you can just create a new script in your project (not in the Mirror folders) that inherits from `Authenticator` and override the methods as needed.
|
||||
|
||||
3. Call `AddPlayer` in your client to pass the credentials.
|
||||
- When a client is authenticated to your satisfaction, you simply call `base.OnServerAuthenticated.Invoke(conn)` and `base.OnClientAuthenticated.Invoke(conn)` on the server and client, respectively. Mirror is listening for these events to proceed with the connection sequence.
|
||||
|
||||
4. Override the `OnServerAddPlayer` method and validate the user's credential.
|
||||
- In the inspector you can optionally subscribe your own methods to the OnServerAuthenticated and OnClientAuthenticated events.
|
||||
|
||||
For example this would be part of your `NetworkManager` class:
|
||||
Here are some tips for custom Authenticators:
|
||||
|
||||
```cs
|
||||
class MyGameNetworkManager : NetworkManager
|
||||
{
|
||||
class CredentialsMessage : MessageBase
|
||||
{
|
||||
// use whatever credentials make sense for your game
|
||||
// for example, you might want to pass the accessToken if using oauth
|
||||
public string username;
|
||||
public string password;
|
||||
}
|
||||
- `OnStartServer` and `OnStartClient` are the appropriate methods to register server and client messages and their handlers. They're called from StartServer/StartHost, and StartClient, respectively.
|
||||
|
||||
// this gets called on the client after it has connected to the server
|
||||
public override void OnClientConnect(NetworkConnection conn)
|
||||
{
|
||||
base.OnClientConnect(conn);
|
||||
- Send a message to the client if authentication fails, especially if there's some issue they can resolve.
|
||||
|
||||
CredentialsMessage msg = new CredentialsMessage()
|
||||
{
|
||||
// perhaps get the username and password from textboxes instead
|
||||
username = "Joe",
|
||||
password = "Gaba Gaba"
|
||||
};
|
||||
- Call the `Disconnect()` method of the `NetworkConnection` on the server and client when authentication fails. If you want to give the user a few tries to get their credentials right, you certainly can, but Mirror will not do the disconnect for you.
|
||||
|
||||
ClientScene.AddPlayer(conn, MessagePacker.Pack(msg));
|
||||
}
|
||||
- Remember to put a small delay on the Disconnect call on the server if you send a failure message so that it has a chance to be delivered before the connection is dropped.
|
||||
|
||||
// this gets called in your server when the client requests to add a player.
|
||||
public override void OnServerAddPlayer(NetworkConnection conn, AddPlayerMessage extraMessage)
|
||||
{
|
||||
CredentialsMessage msg = MessagePacker.Unpack<CredentialsMessage>(extraMessage.value);
|
||||
- `NetworkConnection` has an `AuthenticationData` object where you can drop a class instance of any data you need to persist on the server related to the authentication, such as account id's, tokens, character selection, etc.
|
||||
|
||||
// check the credentials by calling your web server, database table, playfab api, or any method appropriate.
|
||||
if (msg.username == "Joe" && msg.password == "Gaba Gaba")
|
||||
{
|
||||
// proceed to regular add player
|
||||
base.OnServerAddPlayer(conn, extraMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Now that you have the foundation of a custom Authenticator component, the rest is up to you. You can exchange any number of custom messages between the server and client as necessary to complete your authentication process before approving the client.
|
||||
|
||||
## Warning
|
||||
## Basic Authenticator
|
||||
|
||||
Mirror includes a Basic Authenticator in the Mirror / Authenticators folder which just uses a simple username and password.
|
||||
|
||||
- Drag the Basic Authenticator script to the inspector of the object in your scene that has Network Manager
|
||||
|
||||
- The Basic Authenticator component will automatically be assigned to the Authenticator field in Network Manager
|
||||
|
||||
When you're done, it should look like this:
|
||||
|
||||
![Inspector showing Basic Authentication component](BasicAuthentication.PNG)
|
||||
|
||||
> **Note:** You don't need to assign anything to the event lists unless you want to subscribe to the events in your own code for your own purposes. Mirror has internal listeners for both events.
|
||||
|
||||
By default Mirror uses Telepathy, which is not encrypted. The above code sample works, but if you want to do authentication through Mirror, we highly recommend you use a transport that supports encryption.
|
||||
|
BIN
doc/articles/Guides/BasicAuthentication.PNG
Normal file
BIN
doc/articles/Guides/BasicAuthentication.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
@ -1,18 +1,23 @@
|
||||
# Ignorance
|
||||
|
||||
## What is Ignorance?
|
||||
Ignorance is a Transport layer developed by [Coburn](http://github.com/softwareguy) that integrates Mirror into the [ENET-C#](https://github.com/nxrighthere/ENet-CSharp) wrapper by nxrighthere. It was one of the first three transports to be integrated into Mirror. Ignorance implements a reliable and unreliable sequenced UDP transport for both 64bit desktop (Windows, Mac OS and Linux) and possibly mobile devices (Android, iOS) as well as channel support (first channel is reliable, second is unreliable).
|
||||
Ignorance is a reliable UDP transport layer that utilizes the native ENET C Networking library via a [custom fork of ENet-CSharp](https://github.com/SoftwareGuy/ENet-CSharp) providing an reliable and unreliable sequenced UDP transport for both 64Bit desktop operating systems (Windows, Mac OS and Linux) and Mobile OSes (Apple iOS and Android). It also supports up to 255 channels and 4096 clients connected at one time.
|
||||
|
||||
ENET is a solid reliable UDP C++ network library that is mature and stable. There's a reason that the developer went with it.
|
||||
ENET is a solid reliable UDP C++ network library that is mature and stable. Unity's LLAPI needs a replacement. Ignorance was designed with that goal in mind - fill the gap and provide a solid, performant RUDP transport for Mirror.
|
||||
|
||||
Ignorance at the time of this article is at version 1.0.9.1 and fully supports Unity 2017.4 LTS. A version for Unity 2018 LTS with async support is planned and should arrive in 2019. *Ignorance was also designed to address that ugly TCP vs UDP war that many developers fought in.*
|
||||
## Why Ignorance over the Unity LLAPI?
|
||||
Unity's old LLAPI was horridly inefficient, and lots of testing has shown that you will get reduced performance using Unity LLAPI in your project. This is due to the design of the old networking code - Unity Tech made "by design" decisions and poor bug fixes that were seen to other developers as band-aids over a gaping wound. They did not care about performance or bug fixes.
|
||||
|
||||
![The Ignorance Transport component in the Inspector window](IgnoranceTransport.PNG)
|
||||
Unity LLAPI was also closed source, meaning the Mirror developers could not take a knife to it and make it better. This is where the concept of Ignorance took shape.
|
||||
|
||||
## Who develops Ignorance?
|
||||
[Coburn](http://github.com/softwareguy) is the lead developer of the transport. Oiran Studio actively uses this transport for networked game projects. It is currently also being utilized by some game projects, where you can find on the Mirror Discord server.
|
||||
|
||||
## Why would I want to use reliable UDP over TCP?
|
||||
- if you have realtime communications that you need speed over reliability (VoIP...)
|
||||
- if you need channels
|
||||
- if you need custom channel send types
|
||||
- if you need a data hose for your game (a first person shooter, racing game, etc)
|
||||
- if you just want something that works (that's why you chose Mirror, right?)
|
||||
|
||||
## Why wouldn't I want to use reliable UDP over TCP?
|
||||
- if you have **mission critical** things (as in, data **NEEDS** to go from A and B, no exceptions)
|
||||
@ -21,11 +26,11 @@ Ignorance at the time of this article is at version 1.0.9.1 and fully supports U
|
||||
- if you're making a Minecraft-like game and need to keep everyone in sync
|
||||
|
||||
## I want to know more about reliable UDP...
|
||||
A little explaination is required. UDP is best described as a "shattershot" data transmission protocol, which means you just spray packets at a destination and hope for the best. The remote destination may or may not receive those packets, nor are they going to be in order. For example, if you have a packet stream that is:
|
||||
A little explaination is required. UDP is best described as a "shattershot" data transmission protocol, which means you just spray and pray that packets at a destination and hope for the best. The remote destination may or may not receive those packets, nor are they going to be in order. For example, if you have a packet stream that is:
|
||||
```
|
||||
1 2 3 4 5 6 7
|
||||
```
|
||||
...then it may end up like any of the following on the other end due to packets arriving out of order (note that a dot means packet loss in these examples:
|
||||
...then it may end up like any of the following on the other end due to packets arriving out of order. A dot in the following example means that packet went missing.
|
||||
```
|
||||
7 6 1 3 2 4 5
|
||||
7 6 . . 4 . 1
|
||||
@ -33,28 +38,25 @@ A little explaination is required. UDP is best described as a "shattershot" data
|
||||
1 2 3 5 4 6 7
|
||||
```
|
||||
|
||||
Now, about sequencing and reliable mode. **Sequencing** basically tags packets so they know what number they are when being dispatched. So if you send packets `100, 101, 102` to the remote destination, the other end will reconstruct the packet in that order rather than in a different order (like `101, 100, 102`). If a packet is missing, it'll be skipped but the network library will take note that it's missing and compensate.
|
||||
For example, say you lost a packet and that contained a player's health update. Everyone else might know they took 69 damage, but that client will still have the old value of say, 72 health. Without reliable UDP, you can become out of sync very quickly. When you're out of sync, the game is over - everything will start operating very strangely.
|
||||
|
||||
## Sequencing and Reliable Delivery
|
||||
|
||||
### Sequencing
|
||||
**Sequencing** basically tags packets so they know what number they are when being dispatched. So if you send packets `100, 101, 102` to the remote destination, the other end will reconstruct the packet in that order rather than in a different order (like `101, 100, 102`). If a packet is missing, it'll be skipped but the network library will take note that it's missing and compensate.
|
||||
|
||||
**Reliable** mode just tells ENET to send this while waiting for the remote to acknowledge packet reception, before claiming it was 'lost'. ENET will still classify said packets as lost if it doesn't hear back from the remote, but it will retransmit them to compensate for lossy connections or high latency situations. Reliable mode tries to emulate some of TCP's resending if not acknowledged in time, but as UDP does not have all the overhead TCP protocol has, it adds some packet overhead.
|
||||
|
||||
**Best of all, you DON'T NEED TO WORRY about all this! Ignorance in it's default state will use reliable UDP, which means the carrier pidegons will work overtime to get data from A to B correctly and in one piece.***
|
||||
|
||||
## Why Ignorance over the Unity LLAPI?
|
||||
Unity's old LLAPI was horridly inefficient, and lots of testing has shown that you will get reduced performance using Unity LLAPI in your project. This is due to the design of the old networking code - Unity Tech made "by design" decisions and poor bug fixes that were seen to other developers as band-aids over a gaping wound. They did not care about performance or bug fixes.
|
||||
|
||||
Unity LLAPI was also closed source, meaning the Mirror developers could not take a knife to it and make it better. This is where the concept of Ignorance took shape.
|
||||
|
||||
## Who develops Ignorance?
|
||||
[Coburn](http://github.com/softwareguy) is the lead developer of the transport. Oiran Studio actively uses this transport for networked game projects. It is currently also being utilized by some game projects, where you can find said developers on the Mirror Discord server.
|
||||
Ignorance comes with two channels in both Reliable and Unreliable mode by default. There are other channel modes that developers can test as different ones might suit different loads, but the average person does not need to worry about this. Ignorance comes with sane defaults out of the box.
|
||||
|
||||
## Does Ignorance support Websockets?
|
||||
No, it does not. This is due to an issue with the fact that ENET is a C++ packet pumper and the ENET C# wrapper uses native C++ calls to talk to the network stack. Since it is not possible to convert a native ENET binary to WebGL, you will need to look elsewhere for Websockets support.
|
||||
No, it does not. Mirror comes with built-in websockets support.
|
||||
|
||||
## Where can I get Ignorance?
|
||||
[Grab the latest build from the releases page on the Ignorance repository](https://github.com/SoftwareGuy/Ignorance.md). Follow the instructions - it's simply a extra line or two of code plus dropping DLLs into your project.
|
||||
[Grab the latest build from the releases page on the Ignorance repository](https://github.com/SoftwareGuy/Ignorance). Simply import the Unity Package from the release you downloaded.
|
||||
|
||||
## Where can I get support?
|
||||
You can get support by opening a issue ticket on the [Ignorance repository issue tracker](https://github.com/SoftwareGuy/Ignorance/issues) or the #ignorance channel in the Mirror Discord server.
|
||||
|
||||
## I still don't understand what this transport is, my head is spinning, help!
|
||||
Sorry, but this is probably not for you then. Come by the discord and we'll do our best to explain it in plain english.
|
||||
Come by the Discord and we'll do our best to explain it in plain english.
|
||||
|
Loading…
Reference in New Issue
Block a user