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