Bug fixes and stability improvements:
- Fix crash on improper websocket disconnect - Fix some race conditions by moving to synchronized Queue and List - Fix allowing invalid usernames
This commit is contained in:
parent
563883a275
commit
48e2ece306
4 changed files with 41 additions and 19 deletions
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Timers;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
|
@ -9,7 +10,7 @@ public class TurnQueue
|
|||
public event EventHandler<TurnStatus> Turn;
|
||||
|
||||
// Private fields
|
||||
private readonly Queue<User> queue;
|
||||
private readonly ConcurrentQueue<User> queue;
|
||||
private readonly uint turnTime;
|
||||
private readonly Timer timer;
|
||||
private uint currentTurnRemainingTime;
|
||||
|
@ -32,7 +33,8 @@ public class TurnQueue
|
|||
if (indefiniteTurn != null) return;
|
||||
if (queue.Count == 0) return; // This shouldn't happen, but just in case
|
||||
currentTurnRemainingTime--;
|
||||
Utilities.Log(LogLevel.DEBUG, $"Turn tick, {currentTurnRemainingTime} seconds remaining on {queue.Peek().Username}'s turn");
|
||||
if (!queue.TryPeek(out var u)) return;
|
||||
Utilities.Log(LogLevel.DEBUG, $"Turn tick, {currentTurnRemainingTime} seconds remaining on {u.Username}'s turn");
|
||||
if (currentTurnRemainingTime == 0)
|
||||
{
|
||||
NextTurn();
|
||||
|
@ -48,10 +50,11 @@ public class TurnQueue
|
|||
SendTurnUpdate();
|
||||
return;
|
||||
}
|
||||
queue.Dequeue();
|
||||
queue.TryDequeue(out _);
|
||||
if (queue.Count > 0)
|
||||
{
|
||||
Utilities.Log(LogLevel.DEBUG, $"It is now {queue.Peek().Username}'s turn");
|
||||
if (!queue.TryPeek(out var u)) return;
|
||||
Utilities.Log(LogLevel.DEBUG, $"It is now {u.Username}'s turn");
|
||||
currentTurnRemainingTime = turnTime;
|
||||
}
|
||||
SendTurnUpdate();
|
||||
|
@ -100,7 +103,8 @@ public class TurnQueue
|
|||
}
|
||||
if (!queue.Contains(user)) return;
|
||||
Utilities.Log(LogLevel.DEBUG, $"Removing user {user.Username} to turn queue");
|
||||
if (queue.Peek() == user && indefiniteTurn == null)
|
||||
if (!queue.TryPeek(out var _u)) return;
|
||||
if (_u == user && indefiniteTurn == null)
|
||||
{
|
||||
NextTurn();
|
||||
return;
|
||||
|
@ -128,7 +132,8 @@ public class TurnQueue
|
|||
{
|
||||
if (indefiniteTurn != null) return indefiniteTurn;
|
||||
if (queue.Count == 0) return null;
|
||||
return queue.Peek();
|
||||
if (!queue.TryPeek(out var u)) return null;
|
||||
return u;
|
||||
}
|
||||
|
||||
public void GiveTurn(User user)
|
||||
|
@ -145,7 +150,8 @@ public class TurnQueue
|
|||
AddUser(user);
|
||||
return;
|
||||
}
|
||||
if (queue.Peek() == user) return;
|
||||
if (!queue.TryPeek(out var _u)) return;
|
||||
if (_u == user) return;
|
||||
RemoveUser(user);
|
||||
var _queue = queue.ToArray();
|
||||
queue.Clear();
|
||||
|
|
|
@ -79,8 +79,12 @@ public class User
|
|||
{
|
||||
return;
|
||||
}
|
||||
await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)), WebSocketMessageType.Text, true,
|
||||
CancellationToken.None);
|
||||
|
||||
try
|
||||
{
|
||||
await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)), WebSocketMessageType.Text, true,
|
||||
CancellationToken.None);
|
||||
} catch { /* ignored */ }
|
||||
}
|
||||
|
||||
private async Task ProcessMessage(string[] msgArr)
|
||||
|
@ -343,7 +347,7 @@ public class User
|
|||
{
|
||||
// Ban
|
||||
if (_rank != Rank.Admin && (_rank != Rank.Moderator || !Program.Config.ModPermissions.Ban) || msgArr.Length < 3 || vm == null) return;
|
||||
var user = vm.Users.Find(u => u.Username == msgArr[2]);
|
||||
var user = vm.Users.First(u => u.Username == msgArr[2]);
|
||||
if (user == null) return;
|
||||
await user.Ban(msgArr.Length == 4 ? msgArr[3] : null);
|
||||
}
|
||||
|
@ -366,7 +370,7 @@ public class User
|
|||
{
|
||||
// Mute
|
||||
if (_rank != Rank.Admin && (_rank != Rank.Moderator || !Program.Config.ModPermissions.Mute) || msgArr.Length != 4 || vm == null) return;
|
||||
var user = vm.Users.Find(u => u.Username == msgArr[2]);
|
||||
var user = vm.Users.First(u => u.Username == msgArr[2]);
|
||||
if (user == null) return;
|
||||
bool? permanent = msgArr[3] == "1" ? true : msgArr[3] == "0" ? false : null;
|
||||
if (permanent == null) return;
|
||||
|
@ -377,7 +381,7 @@ public class User
|
|||
{
|
||||
// Kick
|
||||
if (_rank != Rank.Admin && (_rank != Rank.Moderator || !Program.Config.ModPermissions.Kick) || msgArr.Length != 3 || vm == null) return;
|
||||
var user = vm.Users.Find(u => u.Username == msgArr[2]);
|
||||
var user = vm.Users.First(u => u.Username == msgArr[2]);
|
||||
if (user == null) return;
|
||||
await user.Close();
|
||||
}
|
||||
|
@ -386,7 +390,7 @@ public class User
|
|||
{
|
||||
// End turn
|
||||
if (_rank != Rank.Admin && (_rank != Rank.Moderator || !Program.Config.ModPermissions.BypassTurn) || msgArr.Length != 3 || vm == null) return;
|
||||
var user = vm.Users.Find(u => u.Username == msgArr[2]);
|
||||
var user = vm.Users.First(u => u.Username == msgArr[2]);
|
||||
if (user == null) return;
|
||||
vm.TurnQueue.RemoveUser(user);
|
||||
}
|
||||
|
@ -404,7 +408,7 @@ public class User
|
|||
{
|
||||
// Rename user
|
||||
if (_rank != Rank.Admin && (_rank != Rank.Moderator || !Program.Config.ModPermissions.Rename) || msgArr.Length != 4 || vm == null) return;
|
||||
var user = vm.Users.Find(u => u.Username == msgArr[2]);
|
||||
var user = vm.Users.First(u => u.Username == msgArr[2]);
|
||||
if (user == null) return;
|
||||
await user.Rename(msgArr[3]);
|
||||
}
|
||||
|
@ -413,7 +417,7 @@ public class User
|
|||
{
|
||||
// Get IP
|
||||
if (_rank != Rank.Admin && (_rank != Rank.Moderator || !Program.Config.ModPermissions.GrabIP) || msgArr.Length != 3 || vm == null) return;
|
||||
var user = vm.Users.Find(u => u.Username == msgArr[2]);
|
||||
var user = vm.Users.First(u => u.Username == msgArr[2]);
|
||||
if (user == null) return;
|
||||
await SendAsync(Guacutils.Encode("admin", "19", msgArr[2], user.IP.ToString()));
|
||||
}
|
||||
|
@ -497,7 +501,15 @@ public class User
|
|||
using MemoryStream ms = new MemoryStream();
|
||||
do
|
||||
{
|
||||
result = await socket.ReceiveAsync(receivebuffer, token);
|
||||
try
|
||||
{
|
||||
result = await socket.ReceiveAsync(receivebuffer, token);
|
||||
}
|
||||
catch (WebSocketException ex)
|
||||
{
|
||||
await Close(false);
|
||||
return;
|
||||
}
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
Disconnected.Invoke(this, new EventArgs());
|
||||
|
@ -546,9 +558,9 @@ public class User
|
|||
{
|
||||
await SendAsync(Guacutils.Encode("size", "0", size.Width.ToString(), size.Height.ToString()));
|
||||
}
|
||||
public async Task Close()
|
||||
public async Task Close(bool sendDisconnect = true)
|
||||
{
|
||||
await SendAsync(Guacutils.Encode("disconnect"));
|
||||
if (sendDisconnect) SendAsync(Guacutils.Encode("disconnect"));
|
||||
try
|
||||
{
|
||||
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||
|
@ -608,6 +620,7 @@ public class User
|
|||
return;
|
||||
}
|
||||
await this.SendAsync(Guacutils.Encode("rename", "0", "2", _username, ((int)this._rank).ToString()));
|
||||
return;
|
||||
}
|
||||
this._username = newname;
|
||||
if (oldname != null) this.Renamed.Invoke(this, oldname);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using CollabVM.Server.Config;
|
||||
using SixLabors.ImageSharp;
|
||||
|
@ -14,7 +16,7 @@ public class VM
|
|||
// Public properties and events
|
||||
public VMController Controller { get; }
|
||||
public VMConfig Config { get; }
|
||||
public List<User> Users { get; } = new();
|
||||
public SynchronizedCollection<User> Users { get; } = new();
|
||||
public CircularBuffer.CircularBuffer<ChatMessage> ChatHistory { get; } = new((int)Program.Config.Chat.ChatHistoryLength);
|
||||
public TurnQueue TurnQueue { get; } = new(Program.Config.Turns.TurnTime);
|
||||
public ResetVote? Vote { get; private set; }
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<PackageReference Include="Samboy063.Tomlet" Version="5.2.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in a new issue