Skip to content

Commit 04f5517

Browse files
committed
fix: prevent failed login/player select if an previous client is disconnected during login
1 parent 9e08677 commit 04f5517

File tree

6 files changed

+117
-40
lines changed

6 files changed

+117
-40
lines changed

Intersect.Network/ClientNetwork.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ RSAParameters rsaParameters
4242
StartInterfaces();
4343
}
4444

45-
public override event HandleConnectionEvent OnConnected;
46-
public override event HandleConnectionEvent OnConnectionApproved;
47-
public override event HandleConnectionEvent OnConnectionDenied;
48-
public override event HandleConnectionRequest OnConnectionRequested;
49-
public override event HandleConnectionEvent OnDisconnected;
50-
public override event HandlePacketAvailable OnPacketAvailable;
51-
public override event HandleUnconnectedMessage OnUnconnectedMessage;
45+
public override event HandleConnectionEvent? OnConnected;
46+
public override event HandleConnectionEvent? OnConnectionApproved;
47+
public override event HandleConnectionEvent? OnConnectionDenied;
48+
public override event HandleConnectionRequest? OnConnectionRequested;
49+
public override event HandleConnectionEvent? OnDisconnected;
50+
public override event HandlePacketAvailable? OnPacketAvailable;
51+
public override event HandleUnconnectedMessage? OnUnconnectedMessage;
5252

5353
public IConnection Connection => Connections.FirstOrDefault();
5454

Intersect.Server.Core/Database/PlayerData/User.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,6 @@ out LoginFailureReason failureReason
659659
user = default;
660660
failureReason = new LoginFailureReason(LoginFailureType.ServerError);
661661
return false;
662-
663662
}
664663

665664
try

Intersect.Server.Core/Entities/Player.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ private void RemoveFromInstanceController(Guid mapInstanceId)
472472
}
473473
}
474474

475-
public void TryLogout(bool force = false, bool softLogout = false)
475+
public void TryLogout(bool force = false, bool softLogout = false, TaskCompletionSource? logoutCompletionSource = null)
476476
{
477477
LastOnline = DateTime.Now;
478478
Client = default;
@@ -485,11 +485,15 @@ public void TryLogout(bool force = false, bool softLogout = false)
485485

486486
if (CombatTimer < Timing.Global.Milliseconds || force)
487487
{
488-
Logout(softLogout);
488+
Logout(softLogout: softLogout, logoutCompletionSource: logoutCompletionSource);
489+
}
490+
else
491+
{
492+
logoutCompletionSource?.TrySetResult();
489493
}
490494
}
491495

492-
private void Logout(bool softLogout = false)
496+
private void Logout(bool softLogout = false, TaskCompletionSource? logoutCompletionSource = null)
493497
{
494498
lock (_savingLock)
495499
{
@@ -590,13 +594,14 @@ private void Logout(bool softLogout = false)
590594
User?.TryLogout(softLogout);
591595
}
592596

593-
#if DIAGNOSTIC
594-
var stackTrace = Environment.StackTrace;
595-
#else
596-
var stackTrace = default(string);
597-
#endif
598597
var logoutOperationId = Guid.NewGuid();
599-
DbInterface.Pool.QueueWorkItem(CompleteLogout, logoutOperationId, softLogout, stackTrace);
598+
DbInterface.Pool.QueueWorkItem(
599+
CompleteLogout,
600+
logoutOperationId,
601+
softLogout,
602+
logoutCompletionSource,
603+
Debugger.IsAttached ? Environment.StackTrace : default
604+
);
600605
}
601606

602607
#if DIAGNOSTIC
@@ -606,7 +611,12 @@ private void Logout(bool softLogout = false)
606611
private static readonly HashSet<Guid> _pendingLogouts = [];
607612
private static readonly object _pendingLogoutLock = new();
608613

609-
public void CompleteLogout(Guid logoutOperationId, bool softLogout, string? stackTrace = default)
614+
public void CompleteLogout(
615+
Guid logoutOperationId,
616+
bool softLogout,
617+
TaskCompletionSource? logoutCompletionSource,
618+
string? stackTrace = default
619+
)
610620
{
611621
if (logoutOperationId != default)
612622
{
@@ -648,9 +658,10 @@ public void CompleteLogout(Guid logoutOperationId, bool softLogout, string? stac
648658
throw new UnreachableException();
649659
}
650660
}
651-
catch
661+
catch (Exception exception)
652662
{
653663
Log.Warn($"Crashed while saving for logout {logoutOperationId}");
664+
logoutCompletionSource?.TrySetException(exception);
654665
throw;
655666
}
656667

@@ -669,6 +680,8 @@ public void CompleteLogout(Guid logoutOperationId, bool softLogout, string? stac
669680
{
670681
_pendingLogouts.Remove(Id);
671682
}
683+
684+
logoutCompletionSource?.TrySetResult();
672685
}
673686

674687
#if DIAGNOSTIC

Intersect.Server.Core/General/Globals.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ public static partial class Globals
1111

1212
public static readonly object ClientLock = new object();
1313

14-
public static readonly IDictionary<Guid, Client> ClientLookup = new Dictionary<Guid, Client>();
14+
public static readonly Dictionary<Guid, Client> ClientLookup = [];
1515

16-
public static readonly List<Client> Clients = new List<Client>();
17-
18-
public static Client[] ClientArray = new Client[0];
16+
public static readonly List<Client> Clients = [];
1917

2018
public static long Cps = 0;
2119

Intersect.Server.Core/Networking/Client.cs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public UserRights Power
118118
}
119119
}
120120

121-
public User User { get; private set; }
121+
public User? User { get; private set; }
122122

123123
public List<Player> Characters => User?.Players;
124124

@@ -166,22 +166,32 @@ public void Pinged()
166166
}
167167
}
168168

169-
public void Disconnect(string? reason = default, bool shutdown = false, bool loggingOut = false)
169+
public void Disconnect(
170+
string? reason = default,
171+
bool shutdown = false,
172+
bool loggingOut = false,
173+
TaskCompletionSource? logoutCompletionSource = null
174+
)
170175
{
171176
lock (Globals.ClientLock)
172177
{
173178
if (Connection == null)
174179
{
180+
logoutCompletionSource?.TrySetResult();
175181
return;
176182
}
177183

178184
if (!loggingOut)
179185
{
180-
Logout(shutdown);
186+
Logout(force: shutdown, logoutCompletionSource: logoutCompletionSource);
187+
}
188+
else
189+
{
190+
logoutCompletionSource?.TrySetResult();
181191
}
182192

183193
Globals.Clients.Remove(this);
184-
Globals.ClientArray = Globals.Clients.ToArray();
194+
185195
if (Connection != default)
186196
{
187197
Globals.ClientLookup.Remove(Connection.Guid);
@@ -225,20 +235,25 @@ public static Client CreateBeta4Client(IApplicationContext context, INetwork net
225235
lock (Globals.ClientLock)
226236
{
227237
Globals.Clients.Add(client);
228-
Globals.ClientArray = Globals.Clients.ToArray();
229238
Globals.ClientLookup.Add(connection.Guid, client);
230239
}
231240

232241
return client;
233242
}
234243

235-
public void Logout(bool force = false)
244+
public void Logout(bool force = false, TaskCompletionSource? logoutCompletionSource = null)
236245
{
237-
var entity = Entity;
238-
entity?.TryLogout();
239-
Entity = null;
246+
if (Entity is { } entity)
247+
{
248+
entity.TryLogout(logoutCompletionSource: logoutCompletionSource);
249+
Entity = null;
250+
}
251+
else
252+
{
253+
logoutCompletionSource?.TrySetResult();
254+
}
240255

241-
if (User != null && User.LoginTime != null)
256+
if (User is { LoginTime: not null })
242257
{
243258
User.PlayTimeSeconds += (ulong)(DateTime.UtcNow - (DateTime)User.LoginTime).TotalSeconds;
244259
User.LoginTime = null;

Intersect.Server.Core/Networking/PacketHandler.cs

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -521,15 +521,16 @@ public void HandlePacket(Client client, LoginPacket packet)
521521
return;
522522
}
523523

524-
if (!User.TryLogin(packet.Username, packet.Password, out var user, out var failureReason))
524+
var username = packet.Username;
525+
if (!User.TryLogin(username, packet.Password, out var user, out var failureReason))
525526
{
526527
UserActivityHistory.LogActivity(
527528
Guid.Empty,
528529
Guid.Empty,
529530
client?.Ip,
530531
UserActivityHistory.PeerType.Client,
531532
UserActivityHistory.UserAction.FailedLogin,
532-
$"{packet.Username},{failureReason.Type}"
533+
$"{username},{failureReason.Type}"
533534
);
534535

535536
if (failureReason.Type == LoginFailureType.InvalidCredentials)
@@ -545,26 +546,77 @@ public void HandlePacket(Client client, LoginPacket packet)
545546
return;
546547
}
547548

549+
List<TaskCompletionSource> logoutCompletionSources = [];
550+
551+
var disconnectedClients = false;
548552
lock (Globals.ClientLock)
549553
{
550-
foreach (var cli in Globals.Clients.ToArray())
554+
foreach (var otherClient in Globals.Clients.ToArray())
551555
{
552-
if (cli == client)
556+
if (otherClient == null)
557+
{
558+
continue;
559+
}
560+
561+
if (otherClient == client)
553562
{
554563
continue;
555564
}
556565

557-
if (cli?.IsEditor ?? false)
566+
if (otherClient.IsEditor)
558567
{
559568
continue;
560569
}
561570

562-
if (!string.Equals(cli?.Name, packet.Username, StringComparison.InvariantCultureIgnoreCase))
571+
if (!string.Equals(otherClient.Name, username, StringComparison.InvariantCultureIgnoreCase))
563572
{
564573
continue;
565574
}
566575

567-
cli?.Disconnect();
576+
TaskCompletionSource logoutCompletionSource = new();
577+
otherClient.Disconnect(logoutCompletionSource: logoutCompletionSource);
578+
logoutCompletionSources.Add(logoutCompletionSource);
579+
580+
disconnectedClients = true;
581+
}
582+
}
583+
584+
if (disconnectedClients)
585+
{
586+
var disconnectionCount = logoutCompletionSources.Count;
587+
Log.Info($"Login of {username} waiting on {disconnectionCount} clients before continuing...");
588+
589+
Task.WaitAll(logoutCompletionSources.Select(source => source.Task).ToArray());
590+
591+
Log.Info($"Continuing login of {username}...");
592+
593+
if (!User.TryLogin(
594+
username,
595+
packet.Password,
596+
out user,
597+
out failureReason
598+
))
599+
{
600+
UserActivityHistory.LogActivity(
601+
Guid.Empty,
602+
Guid.Empty,
603+
client?.Ip,
604+
UserActivityHistory.PeerType.Client,
605+
UserActivityHistory.UserAction.FailedLogin,
606+
$"{username},{failureReason.Type}"
607+
);
608+
609+
if (failureReason.Type == LoginFailureType.InvalidCredentials)
610+
{
611+
client.FailedAttempt();
612+
PacketSender.SendError(client, Strings.Account.BadLogin, Strings.General.NoticeError);
613+
}
614+
else
615+
{
616+
PacketSender.SendError(client, Strings.Account.UnknownServerErrorRetryLogin, Strings.General.NoticeError);
617+
}
618+
619+
return;
568620
}
569621
}
570622

0 commit comments

Comments
 (0)