Skip to content

Commit 2a84e1d

Browse files
committed
feat: UniqueNameAuthenticator
1 parent 91a8b7c commit 2a84e1d

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using Mirror;
5+
using UnityEngine;
6+
using UnityEngine.Events;
7+
8+
namespace Mirror.Authenticators
9+
{
10+
public class UniqueNameAuthenticator : NetworkAuthenticator
11+
{
12+
readonly HashSet<NetworkConnectionToClient> connectionsPendingDisconnect = new HashSet<NetworkConnectionToClient>();
13+
public static readonly HashSet<string> playerNames = new HashSet<string>();
14+
15+
[Header("Client Username")]
16+
public string playerName;
17+
18+
[Header("Events")]
19+
public UnityEvent<string> OnAuthSuccess = new UnityEvent<string>();
20+
public UnityEvent<string> OnAuthFailure = new UnityEvent<string>();
21+
22+
#region Messages
23+
24+
public struct AuthRequestMessage : NetworkMessage
25+
{
26+
// use whatever credentials make sense for your game
27+
// for example, you might want to pass the accessToken if using oauth
28+
public string authUsername;
29+
}
30+
31+
public struct AuthResponseMessage : NetworkMessage
32+
{
33+
public bool success;
34+
public string message;
35+
}
36+
37+
#endregion
38+
39+
#region Server
40+
41+
// RuntimeInitializeOnLoadMethod -> fast playmode without domain reload
42+
[UnityEngine.RuntimeInitializeOnLoadMethod]
43+
static void ResetStatics()
44+
{
45+
playerNames.Clear();
46+
}
47+
48+
/// <summary>
49+
/// Called on server from StartServer to initialize the Authenticator
50+
/// <para>Server message handlers should be registered in this method.</para>
51+
/// </summary>
52+
public override void OnStartServer()
53+
{
54+
// register a handler for the authentication request we expect from client
55+
NetworkServer.RegisterHandler<AuthRequestMessage>(OnAuthRequestMessage, false);
56+
}
57+
58+
/// <summary>
59+
/// Called on server from StopServer to reset the Authenticator
60+
/// <para>Server message handlers should be registered in this method.</para>
61+
/// </summary>
62+
public override void OnStopServer()
63+
{
64+
// unregister the handler for the authentication request
65+
NetworkServer.UnregisterHandler<AuthRequestMessage>();
66+
}
67+
68+
/// <summary>
69+
/// Called on server from OnServerConnectInternal when a client needs to authenticate
70+
/// </summary>
71+
/// <param name="conn">Connection to client.</param>
72+
public override void OnServerAuthenticate(NetworkConnectionToClient conn)
73+
{
74+
// do nothing...wait for AuthRequestMessage from client
75+
}
76+
77+
/// <summary>
78+
/// Called on server when the client's AuthRequestMessage arrives
79+
/// </summary>
80+
/// <param name="conn">Connection to client.</param>
81+
/// <param name="msg">The message payload</param>
82+
public void OnAuthRequestMessage(NetworkConnectionToClient conn, AuthRequestMessage msg)
83+
{
84+
Debug.Log($"Authentication Request: {msg.authUsername}");
85+
86+
if (connectionsPendingDisconnect.Contains(conn)) return;
87+
88+
// check the credentials by calling your web server, database table, playfab api, or any method appropriate.
89+
if (!playerNames.Contains(msg.authUsername))
90+
{
91+
// Add the name to the HashSet
92+
playerNames.Add(msg.authUsername);
93+
94+
// Store username in authenticationData
95+
// This will be read in Player.OnStartServer
96+
// to set the playerName SyncVar.
97+
conn.authenticationData = msg.authUsername;
98+
99+
// create and send msg to client so it knows to proceed
100+
AuthResponseMessage authResponseMessage = new AuthResponseMessage
101+
{
102+
success = true,
103+
message = "Success"
104+
};
105+
106+
conn.Send(authResponseMessage);
107+
108+
// Accept the successful authentication
109+
ServerAccept(conn);
110+
}
111+
else
112+
{
113+
connectionsPendingDisconnect.Add(conn);
114+
115+
// create and send msg to client so it knows to disconnect
116+
AuthResponseMessage authResponseMessage = new AuthResponseMessage
117+
{
118+
success = false,
119+
message = "Username already in use...try again"
120+
};
121+
122+
conn.Send(authResponseMessage);
123+
124+
// must set NetworkConnection isAuthenticated = false
125+
conn.isAuthenticated = false;
126+
127+
// disconnect the client after 1 second so that response message gets delivered
128+
StartCoroutine(DelayedDisconnect(conn, 1f));
129+
}
130+
}
131+
132+
IEnumerator DelayedDisconnect(NetworkConnectionToClient conn, float waitTime)
133+
{
134+
yield return new WaitForSeconds(waitTime);
135+
136+
// Reject the unsuccessful authentication
137+
ServerReject(conn);
138+
139+
yield return null;
140+
141+
// remove conn from pending connections
142+
connectionsPendingDisconnect.Remove(conn);
143+
}
144+
145+
#endregion
146+
147+
#region Client
148+
149+
// Called by UI element UsernameInput.OnValueChanged
150+
public void SetPlayername(string username)
151+
{
152+
playerName = username;
153+
}
154+
155+
/// <summary>
156+
/// Called on client from StartClient to initialize the Authenticator
157+
/// <para>Client message handlers should be registered in this method.</para>
158+
/// </summary>
159+
public override void OnStartClient()
160+
{
161+
// register a handler for the authentication response we expect from server
162+
NetworkClient.RegisterHandler<AuthResponseMessage>(OnAuthResponseMessage, false);
163+
}
164+
165+
/// <summary>
166+
/// Called on client from StopClient to reset the Authenticator
167+
/// <para>Client message handlers should be unregistered in this method.</para>
168+
/// </summary>
169+
public override void OnStopClient()
170+
{
171+
// unregister the handler for the authentication response
172+
NetworkClient.UnregisterHandler<AuthResponseMessage>();
173+
}
174+
175+
/// <summary>
176+
/// Called on client from OnClientConnectInternal when a client needs to authenticate
177+
/// </summary>
178+
public override void OnClientAuthenticate()
179+
{
180+
NetworkClient.Send(new AuthRequestMessage { authUsername = playerName });
181+
}
182+
183+
/// <summary>
184+
/// Called on client when the server's AuthResponseMessage arrives
185+
/// </summary>
186+
/// <param name="msg">The message payload</param>
187+
public void OnAuthResponseMessage(AuthResponseMessage msg)
188+
{
189+
if (msg.success)
190+
{
191+
Debug.Log($"Authentication Response: Success: {msg.message}");
192+
193+
// Invoke Success Action
194+
OnAuthSuccess.Invoke(msg.message);
195+
196+
// Authentication has been accepted
197+
ClientAccept();
198+
}
199+
else
200+
{
201+
Debug.LogError($"Authentication Response: Failure: {msg.message}");
202+
203+
// Name was not unique, so stop the client
204+
// StopHost works for both host client and remote clients
205+
NetworkManager.singleton.StopHost();
206+
207+
// Invoke Failure Action after StopHost
208+
OnAuthFailure.Invoke(msg.message);
209+
}
210+
}
211+
212+
#endregion
213+
}
214+
}

Assets/Mirror/Authenticators/UniqueNameAuthenticator.cs.meta

Lines changed: 11 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)