Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
Robin C. Ladiges | 497b5b44d6 | |
Robin C. Ladiges | 4de654b6e4 | |
Robin C. Ladiges | 082e480b1e | |
Robin C. Ladiges | 9511d07f09 | |
Robin C. Ladiges | dc20a9c831 | |
Robin C. Ladiges | 61e6fcf2a3 | |
Robin C. Ladiges | 20ee74d0d6 |
|
@ -148,30 +148,27 @@ public static class BanLists {
|
|||
|
||||
public static void Crash(
|
||||
Client user,
|
||||
bool permanent = false,
|
||||
bool dispose_user = true,
|
||||
int delay_ms = 0
|
||||
int delay_ms = 0
|
||||
) {
|
||||
user.Ignored = true;
|
||||
Task.Run(async () => {
|
||||
if (delay_ms > 0) {
|
||||
await Task.Delay(delay_ms);
|
||||
}
|
||||
bool permanent = user.Banned;
|
||||
await user.Send(new ChangeStagePacket {
|
||||
Id = (permanent ? "$agogus/ban4lyfe" : "$among$us/cr4sh%"),
|
||||
Stage = (permanent ? "$ejected" : "$agogusStage"),
|
||||
Scenario = (sbyte) (permanent ? 69 : 21),
|
||||
SubScenarioType = (byte) (permanent ? 21 : 69),
|
||||
});
|
||||
if (dispose_user) {
|
||||
user.Dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void CrashMultiple(string[] args, MUCH much) {
|
||||
foreach (Client user in much(args).toActUpon) {
|
||||
Crash(user, true);
|
||||
user.Banned = true;
|
||||
Crash(user);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,8 +242,9 @@ public static class BanLists {
|
|||
}
|
||||
|
||||
foreach (Client user in res.toActUpon) {
|
||||
user.Banned = true;
|
||||
BanClient(user);
|
||||
Crash(user, true);
|
||||
Crash(user);
|
||||
}
|
||||
|
||||
Save();
|
||||
|
|
|
@ -13,6 +13,7 @@ public class Client : IDisposable {
|
|||
public readonly ConcurrentDictionary<string, object?> Metadata = new ConcurrentDictionary<string, object?>(); // can be used to store any information about a player
|
||||
public bool Connected = false;
|
||||
public bool Ignored = false;
|
||||
public bool Banned = false;
|
||||
public CostumePacket? CurrentCostume = null; // required for proper client sync
|
||||
public string Name {
|
||||
get => Logger.Name;
|
||||
|
@ -41,8 +42,9 @@ public class Client : IDisposable {
|
|||
}
|
||||
|
||||
public void Dispose() {
|
||||
if (Socket?.Connected is true)
|
||||
if (Socket?.Connected is true) {
|
||||
Socket.Disconnect(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,9 +53,14 @@ public class Client : IDisposable {
|
|||
|
||||
PacketAttribute packetAttribute = Constants.PacketMap[typeof(T)];
|
||||
try {
|
||||
// don't send most packets to ignored players
|
||||
if (Ignored && packetAttribute.Type != PacketType.Init && packetAttribute.Type != PacketType.ChangeStage) {
|
||||
memory.Dispose();
|
||||
return;
|
||||
}
|
||||
Server.FillPacket(new PacketHeader {
|
||||
Id = sender?.Id ?? Id,
|
||||
Type = packetAttribute.Type,
|
||||
Id = sender?.Id ?? Id,
|
||||
Type = packetAttribute.Type,
|
||||
PacketSize = packet.Size
|
||||
}, packet, memory.Memory);
|
||||
}
|
||||
|
@ -69,11 +76,17 @@ public class Client : IDisposable {
|
|||
public async Task Send(Memory<byte> data, Client? sender) {
|
||||
PacketHeader header = new PacketHeader();
|
||||
header.Deserialize(data.Span);
|
||||
if (!Connected && header.Type is not PacketType.Connect) {
|
||||
|
||||
if (!Connected && !Ignored && header.Type != PacketType.Connect) {
|
||||
Server.Logger.Error($"Didn't send {header.Type} to {Id} because they weren't connected yet");
|
||||
return;
|
||||
}
|
||||
|
||||
// don't send most packets to ignored players
|
||||
if (Ignored && header.Type != PacketType.Init && header.Type != PacketType.ChangeStage) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Socket!.SendAsync(data[..(Constants.HeaderSize + header.PacketSize)], SocketFlags.None);
|
||||
}
|
||||
|
||||
|
@ -83,11 +96,22 @@ public class Client : IDisposable {
|
|||
Metadata.TryRemove("seeking", out tmp);
|
||||
Metadata.TryRemove("lastCostumePacket", out tmp);
|
||||
Metadata.TryRemove("lastCapturePacket", out tmp);
|
||||
Metadata.TryRemove("lastTagPacket", out tmp);
|
||||
Metadata.TryRemove("lastGamePacket", out tmp);
|
||||
Metadata.TryRemove("lastPlayerPacket", out tmp);
|
||||
}
|
||||
|
||||
public TagPacket? GetTagPacket() {
|
||||
var time = (Time?) (this.Metadata.ContainsKey("time") ? this.Metadata["time"] : null);
|
||||
var seek = (bool?) (this.Metadata.ContainsKey("seeking") ? this.Metadata["seeking"] : null);
|
||||
if (time == null && seek == null) { return null; }
|
||||
return new TagPacket {
|
||||
UpdateType = (seek != null ? TagPacket.TagUpdate.State : 0) | (time != null ? TagPacket.TagUpdate.Time: 0),
|
||||
IsIt = seek ?? false,
|
||||
Seconds = (byte) (time?.Seconds ?? 0),
|
||||
Minutes = (ushort) (time?.Minutes ?? 0),
|
||||
};
|
||||
}
|
||||
|
||||
public static bool operator ==(Client? left, Client? right) {
|
||||
return left is { } leftClient && right is { } rightClient && leftClient.Id == rightClient.Id;
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ async Task ClientSyncShineBag(Client client) {
|
|||
try {
|
||||
if ((bool?) client.Metadata["speedrun"] ?? false) return;
|
||||
ConcurrentBag<int> clientBag = (ConcurrentBag<int>) (client.Metadata["shineSync"] ??= new ConcurrentBag<int>());
|
||||
foreach (int shine in shineBag.Except(clientBag).ToArray()) {
|
||||
foreach (int shine in shineBag.Except(clientBag).Except(Settings.Instance.Shines.Excluded).ToArray()) {
|
||||
if (!client.Connected) return;
|
||||
await client.Send(new ShinePacket {
|
||||
ShineId = shine
|
||||
|
@ -120,11 +120,20 @@ void logError(Task x) {
|
|||
server.PacketHandler = (c, p) => {
|
||||
switch (p) {
|
||||
case GamePacket gamePacket: {
|
||||
if (BanLists.Enabled && BanLists.IsStageBanned(gamePacket.Stage)) {
|
||||
c.Logger.Warn($"Crashing player for entering banned stage {gamePacket.Stage}.");
|
||||
BanLists.Crash(c, false, false, 500);
|
||||
// crash ignored player
|
||||
if (c.Ignored) {
|
||||
c.Logger.Info($"Crashing ignored player after entering stage {gamePacket.Stage}.");
|
||||
BanLists.Crash(c, 500);
|
||||
return false;
|
||||
}
|
||||
|
||||
// crash player entering a banned stage
|
||||
if (BanLists.Enabled && BanLists.IsStageBanned(gamePacket.Stage)) {
|
||||
c.Logger.Warn($"Crashing player for entering banned stage {gamePacket.Stage}.");
|
||||
BanLists.Crash(c, 500);
|
||||
return false;
|
||||
}
|
||||
|
||||
c.Logger.Info($"Got game packet {gamePacket.Stage}->{gamePacket.ScenarioNum}");
|
||||
|
||||
// reset lastPlayerPacket on stage changes
|
||||
|
@ -170,9 +179,13 @@ server.PacketHandler = (c, p) => {
|
|||
break;
|
||||
}
|
||||
|
||||
// ignore all other packets from ignored players
|
||||
case IPacket pack when c.Ignored: {
|
||||
return false;
|
||||
}
|
||||
|
||||
case TagPacket tagPacket: {
|
||||
// c.Logger.Info($"Got tag packet: {tagPacket.IsIt}");
|
||||
c.Metadata["lastTagPacket"] = tagPacket;
|
||||
if ((tagPacket.UpdateType & TagPacket.TagUpdate.State) != 0) c.Metadata["seeking"] = tagPacket.IsIt;
|
||||
if ((tagPacket.UpdateType & TagPacket.TagUpdate.Time) != 0)
|
||||
c.Metadata["time"] = new Time(tagPacket.Minutes, tagPacket.Seconds, DateTime.Now);
|
||||
|
@ -185,7 +198,7 @@ server.PacketHandler = (c, p) => {
|
|||
break;
|
||||
}
|
||||
|
||||
case CostumePacket costumePacket:
|
||||
case CostumePacket costumePacket: {
|
||||
c.Logger.Info($"Got costume packet: {costumePacket.BodyName}, {costumePacket.CapName}");
|
||||
c.Metadata["lastCostumePacket"] = costumePacket;
|
||||
c.CurrentCostume = costumePacket;
|
||||
|
@ -194,9 +207,14 @@ server.PacketHandler = (c, p) => {
|
|||
#pragma warning restore CS4014
|
||||
c.Metadata["loadedSave"] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case ShinePacket shinePacket: {
|
||||
if (!Settings.Instance.Shines.Enabled) return false;
|
||||
if (Settings.Instance.Shines.Excluded.Contains(shinePacket.ShineId)) {
|
||||
c.Logger.Info($"Got moon {shinePacket.ShineId} (excluded)");
|
||||
return false;
|
||||
}
|
||||
if (c.Metadata["loadedSave"] is false) break;
|
||||
ConcurrentBag<int> playerBag = (ConcurrentBag<int>)c.Metadata["shineSync"]!;
|
||||
shineBag.Add(shinePacket.ShineId);
|
||||
|
@ -559,12 +577,16 @@ CommandHandler.RegisterCommand("flip", args => {
|
|||
});
|
||||
|
||||
CommandHandler.RegisterCommand("shine", args => {
|
||||
const string optionUsage = "Valid options: list, clear, sync, send, set";
|
||||
const string optionUsage = "Valid options: list, clear, sync, send, set, include, exclude";
|
||||
if (args.Length < 1)
|
||||
return optionUsage;
|
||||
switch (args[0]) {
|
||||
case "list" when args.Length == 1:
|
||||
return $"Shines: {string.Join(", ", shineBag)}";
|
||||
return $"Shines: {string.Join(", ", shineBag)}" + (
|
||||
Settings.Instance.Shines.Excluded.Count() > 0
|
||||
? "\nExcluded Shines: " + string.Join(", ", Settings.Instance.Shines.Excluded)
|
||||
: ""
|
||||
);
|
||||
case "clear" when args.Length == 1:
|
||||
shineBag.Clear();
|
||||
Task.Run(async () => {
|
||||
|
@ -601,6 +623,21 @@ CommandHandler.RegisterCommand("shine", args => {
|
|||
|
||||
return optionUsage;
|
||||
}
|
||||
case "exclude" when args.Length == 2:
|
||||
case "include" when args.Length == 2: {
|
||||
if (int.TryParse(args[1], out int sid)) {
|
||||
if (args[0] == "exclude") {
|
||||
Settings.Instance.Shines.Excluded.Add(sid);
|
||||
Settings.SaveSettings();
|
||||
return $"Exclude shine {sid} from syncing.";
|
||||
} else {
|
||||
Settings.Instance.Shines.Excluded.Remove(sid);
|
||||
Settings.SaveSettings();
|
||||
return $"No longer exclude shine {sid} from syncing.";
|
||||
}
|
||||
}
|
||||
return optionUsage;
|
||||
}
|
||||
default:
|
||||
return optionUsage;
|
||||
}
|
||||
|
|
166
Server/Server.cs
166
Server/Server.cs
|
@ -29,13 +29,9 @@ public class Server {
|
|||
Socket socket = token.HasValue ? await serverSocket.AcceptAsync(token.Value) : await serverSocket.AcceptAsync();
|
||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
|
||||
if (BanLists.Enabled && BanLists.IsIPv4Banned(((IPEndPoint) socket.RemoteEndPoint!).Address!)) {
|
||||
Logger.Warn($"Ignoring banned IPv4 address {socket.RemoteEndPoint}");
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Warn($"Accepted connection for client {socket.RemoteEndPoint}");
|
||||
|
||||
// start sub thread to handle client
|
||||
try {
|
||||
#pragma warning disable CS4014
|
||||
Task.Run(() => HandleSocket(socket))
|
||||
|
@ -78,27 +74,29 @@ public class Server {
|
|||
public delegate void PacketReplacer<in T>(Client from, Client to, T value); // replacer must send
|
||||
|
||||
public void BroadcastReplace<T>(T packet, Client sender, PacketReplacer<T> packetReplacer) where T : struct, IPacket {
|
||||
foreach (Client client in Clients.Where(client => client.Connected && sender.Id != client.Id)) packetReplacer(sender, client, packet);
|
||||
foreach (Client client in Clients.Where(c => c.Connected && !c.Ignored && sender.Id != c.Id)) {
|
||||
packetReplacer(sender, client, packet);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Broadcast<T>(T packet, Client sender) where T : struct, IPacket {
|
||||
IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.RentZero(Constants.HeaderSize + packet.Size);
|
||||
PacketHeader header = new PacketHeader {
|
||||
Id = sender?.Id ?? Guid.Empty,
|
||||
Type = Constants.PacketMap[typeof(T)].Type,
|
||||
PacketSize = packet.Size
|
||||
Id = sender?.Id ?? Guid.Empty,
|
||||
Type = Constants.PacketMap[typeof(T)].Type,
|
||||
PacketSize = packet.Size,
|
||||
};
|
||||
FillPacket(header, packet, memory.Memory);
|
||||
await Broadcast(memory, sender);
|
||||
}
|
||||
|
||||
public Task Broadcast<T>(T packet) where T : struct, IPacket {
|
||||
return Task.WhenAll(Clients.Where(c => c.Connected).Select(async client => {
|
||||
return Task.WhenAll(Clients.Where(c => c.Connected && !c.Ignored).Select(async client => {
|
||||
IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.RentZero(Constants.HeaderSize + packet.Size);
|
||||
PacketHeader header = new PacketHeader {
|
||||
Id = client.Id,
|
||||
Type = Constants.PacketMap[typeof(T)].Type,
|
||||
PacketSize = packet.Size
|
||||
Id = client.Id,
|
||||
Type = Constants.PacketMap[typeof(T)].Type,
|
||||
PacketSize = packet.Size,
|
||||
};
|
||||
FillPacket(header, packet, memory.Memory);
|
||||
await client.Send(memory.Memory, client);
|
||||
|
@ -112,7 +110,7 @@ public class Server {
|
|||
/// <param name="data">Memory owner to dispose once done</param>
|
||||
/// <param name="sender">Optional sender to not broadcast data to</param>
|
||||
public async Task Broadcast(IMemoryOwner<byte> data, Client? sender = null) {
|
||||
await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data.Memory, sender)));
|
||||
await Task.WhenAll(Clients.Where(c => c.Connected && !c.Ignored && c != sender).Select(client => client.Send(data.Memory, sender)));
|
||||
data.Dispose();
|
||||
}
|
||||
|
||||
|
@ -122,7 +120,7 @@ public class Server {
|
|||
/// <param name="data">Memory to send to the clients</param>
|
||||
/// <param name="sender">Optional sender to not broadcast data to</param>
|
||||
public async void Broadcast(Memory<byte> data, Client? sender = null) {
|
||||
await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data, sender)));
|
||||
await Task.WhenAll(Clients.Where(c => c.Connected && !c.Ignored && c != sender).Select(client => client.Send(data, sender)));
|
||||
}
|
||||
|
||||
public Client? FindExistingClient(Guid id) {
|
||||
|
@ -134,9 +132,7 @@ public class Server {
|
|||
Client client = new Client(socket) {Server = this};
|
||||
var remote = socket.RemoteEndPoint;
|
||||
IMemoryOwner<byte> memory = null!;
|
||||
await client.Send(new InitPacket {
|
||||
MaxPlayers = Settings.Instance.Server.MaxPlayers
|
||||
});
|
||||
|
||||
bool first = true;
|
||||
try {
|
||||
while (true) {
|
||||
|
@ -159,8 +155,9 @@ public class Server {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!await Read(memory.Memory[..Constants.HeaderSize], Constants.HeaderSize, 0))
|
||||
if (!await Read(memory.Memory[..Constants.HeaderSize], Constants.HeaderSize, 0)) {
|
||||
break;
|
||||
}
|
||||
PacketHeader header = GetHeader(memory.Memory.Span[..Constants.HeaderSize]);
|
||||
Range packetRange = Constants.HeaderSize..(Constants.HeaderSize + header.PacketSize);
|
||||
if (header.PacketSize > 0) {
|
||||
|
@ -168,71 +165,100 @@ public class Server {
|
|||
memory = memoryPool.Rent(Constants.HeaderSize + header.PacketSize);
|
||||
memTemp.Memory.Span[..Constants.HeaderSize].CopyTo(memory.Memory.Span[..Constants.HeaderSize]);
|
||||
memTemp.Dispose();
|
||||
if (!await Read(memory.Memory, header.PacketSize, Constants.HeaderSize))
|
||||
if (!await Read(memory.Memory, header.PacketSize, Constants.HeaderSize)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (client.Ignored) {
|
||||
memory.Dispose();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// connection initialization
|
||||
if (first) {
|
||||
first = false;
|
||||
if (header.Type != PacketType.Connect) throw new Exception($"First packet was not init, instead it was {header.Type}");
|
||||
first = false; // only do this once
|
||||
|
||||
// first client packet has to be the client init
|
||||
if (header.Type != PacketType.Connect) {
|
||||
throw new Exception($"First packet was not init, instead it was {header.Type} ({remote})");
|
||||
}
|
||||
|
||||
ConnectPacket connect = new ConnectPacket();
|
||||
connect.Deserialize(memory.Memory.Span[packetRange]);
|
||||
|
||||
bool wasFirst = connect.ConnectionType == ConnectPacket.ConnectionTypes.FirstConnection;
|
||||
client.Id = header.Id;
|
||||
client.Name = connect.ClientName;
|
||||
|
||||
if (BanLists.Enabled && BanLists.IsProfileBanned(header.Id)) {
|
||||
client.Id = header.Id;
|
||||
client.Name = connect.ClientName;
|
||||
// is the IPv4 address banned?
|
||||
if (BanLists.Enabled && BanLists.IsIPv4Banned(((IPEndPoint) socket.RemoteEndPoint!).Address!)) {
|
||||
Logger.Warn($"Ignoring banned IPv4 address for {client.Name} ({client.Id}/{remote})");
|
||||
client.Ignored = true;
|
||||
client.Logger.Warn($"Ignoring banned profile ID {header.Id}");
|
||||
client.Banned = true;
|
||||
}
|
||||
// is the profile ID banned?
|
||||
else if (BanLists.Enabled && BanLists.IsProfileBanned(client.Id)) {
|
||||
client.Logger.Warn($"Ignoring banned profile ID for {client.Name} ({client.Id}/{remote})");
|
||||
client.Ignored = true;
|
||||
client.Banned = true;
|
||||
}
|
||||
// is the server full?
|
||||
else if (Clients.Count(x => x.Connected) >= Settings.Instance.Server.MaxPlayers) {
|
||||
client.Logger.Error($"Ignoring player {client.Name} ({client.Id}/{remote}) as server reached max players of {Settings.Instance.Server.MaxPlayers}");
|
||||
client.Ignored = true;
|
||||
}
|
||||
|
||||
// send server init (required to crash ignored players later)
|
||||
await client.Send(new InitPacket {
|
||||
MaxPlayers = (client.Ignored ? (ushort) 1 : Settings.Instance.Server.MaxPlayers),
|
||||
});
|
||||
|
||||
// don't init or announce an ignored client to other players any further
|
||||
if (client.Ignored) {
|
||||
memory.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
bool wasFirst = connect.ConnectionType == ConnectPacket.ConnectionTypes.FirstConnection;
|
||||
|
||||
// add client to the set of connected players
|
||||
lock (Clients) {
|
||||
if (Clients.Count(x => x.Connected) == Settings.Instance.Server.MaxPlayers) {
|
||||
client.Logger.Error($"Turned away as server is at max clients");
|
||||
// is the server full? (check again, to prevent race conditions)
|
||||
if (Clients.Count(x => x.Connected) >= Settings.Instance.Server.MaxPlayers) {
|
||||
client.Logger.Error($"Ignoring player {client.Name} ({client.Id}/{remote}) as server reached max players of {Settings.Instance.Server.MaxPlayers}");
|
||||
client.Ignored = true;
|
||||
memory.Dispose();
|
||||
goto disconnect;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool firstConn = true;
|
||||
// detect and handle reconnections
|
||||
bool isClientNew = true;
|
||||
switch (connect.ConnectionType) {
|
||||
case ConnectPacket.ConnectionTypes.FirstConnection:
|
||||
case ConnectPacket.ConnectionTypes.Reconnecting: {
|
||||
client.Id = header.Id;
|
||||
if (FindExistingClient(header.Id) is { } oldClient) {
|
||||
firstConn = false;
|
||||
if (FindExistingClient(client.Id) is { } oldClient) {
|
||||
isClientNew = false;
|
||||
client = new Client(oldClient, socket);
|
||||
client.Name = connect.ClientName;
|
||||
Clients.Remove(oldClient);
|
||||
Clients.Add(client);
|
||||
if (oldClient.Connected) {
|
||||
oldClient.Logger.Info($"Disconnecting already connected client {oldClient.Socket?.RemoteEndPoint} for {client.Socket?.RemoteEndPoint}");
|
||||
oldClient.Dispose();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
connect.ConnectionType = ConnectPacket.ConnectionTypes.FirstConnection;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Exception($"Invalid connection type {connect.ConnectionType}");
|
||||
default: {
|
||||
throw new Exception($"Invalid connection type {connect.ConnectionType} for {client.Name} ({client.Id}/{remote})");
|
||||
}
|
||||
}
|
||||
|
||||
client.Name = connect.ClientName;
|
||||
client.Connected = true;
|
||||
if (firstConn) {
|
||||
|
||||
if (isClientNew) {
|
||||
// do any cleanup required when it comes to new clients
|
||||
List<Client> toDisconnect = Clients.FindAll(c => c.Id == header.Id && c.Connected && c.Socket != null);
|
||||
Clients.RemoveAll(c => c.Id == header.Id);
|
||||
List<Client> toDisconnect = Clients.FindAll(c => c.Id == client.Id && c.Connected && c.Socket != null);
|
||||
Clients.RemoveAll(c => c.Id == client.Id);
|
||||
|
||||
Clients.Add(client);
|
||||
|
||||
|
@ -240,18 +266,19 @@ public class Server {
|
|||
// done disconnecting and removing stale clients with the same id
|
||||
|
||||
ClientJoined?.Invoke(client, connect);
|
||||
// a new connection, not a reconnect, for an existing client
|
||||
} else if (wasFirst) {
|
||||
}
|
||||
// a known client reconnects, but with a new first connection (e.g. after a restart)
|
||||
else if (wasFirst) {
|
||||
client.CleanMetadataOnNewConnection();
|
||||
}
|
||||
}
|
||||
|
||||
// for all other clients that are already connected
|
||||
List<Client> otherConnectedPlayers = Clients.FindAll(c => c.Id != header.Id && c.Connected && c.Socket != null);
|
||||
List<Client> otherConnectedPlayers = Clients.FindAll(c => c.Id != client.Id && c.Connected && c.Socket != null);
|
||||
await Parallel.ForEachAsync(otherConnectedPlayers, async (other, _) => {
|
||||
IMemoryOwner<byte> tempBuffer = MemoryPool<byte>.Shared.RentZero(Constants.HeaderSize + (other.CurrentCostume.HasValue ? Math.Max(connect.Size, other.CurrentCostume.Value.Size) : connect.Size));
|
||||
|
||||
// make the other client known to the (new) client
|
||||
// make the other client known to the new client
|
||||
PacketHeader connectHeader = new PacketHeader {
|
||||
Id = other.Id,
|
||||
Type = PacketType.Connect,
|
||||
|
@ -266,7 +293,7 @@ public class Server {
|
|||
connectPacket.Serialize(tempBuffer.Memory.Span[Constants.HeaderSize..]);
|
||||
await client.Send(tempBuffer.Memory[..(Constants.HeaderSize + connect.Size)], null);
|
||||
|
||||
// tell the (new) client what costume the other client has
|
||||
// tell the new client what costume the other client has
|
||||
if (other.CurrentCostume.HasValue) {
|
||||
connectHeader.Type = PacketType.Costume;
|
||||
connectHeader.PacketSize = other.CurrentCostume.Value.Size;
|
||||
|
@ -277,7 +304,7 @@ public class Server {
|
|||
|
||||
tempBuffer.Dispose();
|
||||
|
||||
// make the other client reset their puppet cache for this client, if it is a new connection (after restart)
|
||||
// make the other client reset their puppet cache for this new client, if it is a new connection (after restart)
|
||||
if (wasFirst) {
|
||||
await SendEmptyPackets(client, other);
|
||||
}
|
||||
|
@ -287,14 +314,19 @@ public class Server {
|
|||
|
||||
// send missing or outdated packets from others to the new client
|
||||
await ResendPackets(client);
|
||||
} else if (header.Id != client.Id && client.Id != Guid.Empty) {
|
||||
}
|
||||
else if (header.Id != client.Id && client.Id != Guid.Empty) {
|
||||
throw new Exception($"Client {client.Name} sent packet with invalid client id {header.Id} instead of {client.Id}");
|
||||
}
|
||||
|
||||
try {
|
||||
// parse the packet
|
||||
IPacket packet = (IPacket) Activator.CreateInstance(Constants.PacketIdMap[header.Type])!;
|
||||
packet.Deserialize(memory.Memory.Span[Constants.HeaderSize..(Constants.HeaderSize + packet.Size)]);
|
||||
|
||||
// process the packet
|
||||
if (PacketHandler?.Invoke(client, packet) is false) {
|
||||
// don't broadcast the packet to everyone
|
||||
memory.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
@ -302,7 +334,9 @@ public class Server {
|
|||
catch (Exception e) {
|
||||
client.Logger.Error($"Packet handler warning: {e}");
|
||||
}
|
||||
|
||||
#pragma warning disable CS4014
|
||||
// broadcast the packet to everyone
|
||||
Broadcast(memory, client)
|
||||
.ContinueWith(x => { if (x.Exception != null) { Logger.Error(x.Exception.ToString()); } });
|
||||
#pragma warning restore CS4014
|
||||
|
@ -311,7 +345,8 @@ public class Server {
|
|||
catch (Exception e) {
|
||||
if (e is SocketException {SocketErrorCode: SocketError.ConnectionReset}) {
|
||||
client.Logger.Info($"Disconnected from the server: Connection reset");
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
client.Logger.Error($"Disconnecting due to exception: {e}");
|
||||
if (socket.Connected) {
|
||||
#pragma warning disable CS4014
|
||||
|
@ -324,7 +359,7 @@ public class Server {
|
|||
memory?.Dispose();
|
||||
}
|
||||
|
||||
disconnect:
|
||||
// client disconnected
|
||||
if (client.Name != "Unknown User" && client.Id != Guid.Parse("00000000-0000-0000-0000-000000000000")) {
|
||||
Logger.Info($"Client {remote} ({client.Name}/{client.Id}) disconnected from the server");
|
||||
}
|
||||
|
@ -333,7 +368,6 @@ public class Server {
|
|||
}
|
||||
|
||||
bool wasConnected = client.Connected;
|
||||
// Clients.Remove(client)
|
||||
client.Connected = false;
|
||||
try {
|
||||
client.Dispose();
|
||||
|
@ -349,22 +383,26 @@ public class Server {
|
|||
}
|
||||
|
||||
private async Task ResendPackets(Client client) {
|
||||
async Task trySend<T>(Client other, string packetType) where T : struct, IPacket {
|
||||
if (! other.Metadata.ContainsKey(packetType)) { return; }
|
||||
async Task trySendPack<T>(Client other, T? packet) where T : struct, IPacket {
|
||||
if (packet == null) { return; }
|
||||
try {
|
||||
await client.Send((T) other.Metadata[packetType]!, other);
|
||||
await client.Send((T) packet, other);
|
||||
}
|
||||
catch {
|
||||
// lol who gives a fuck
|
||||
}
|
||||
};
|
||||
async Task trySendMeta<T>(Client other, string packetType) where T : struct, IPacket {
|
||||
if (!other.Metadata.ContainsKey(packetType)) { return; }
|
||||
await trySendPack<T>(other, (T) other.Metadata[packetType]!);
|
||||
};
|
||||
await Parallel.ForEachAsync(this.ClientsConnected, async (other, _) => {
|
||||
if (client.Id == other.Id) { return; }
|
||||
await trySend<CostumePacket>(other, "lastCostumePacket");
|
||||
await trySend<CapturePacket>(other, "lastCapturePacket");
|
||||
await trySend<TagPacket>(other, "lastTagPacket");
|
||||
await trySend<GamePacket>(other, "lastGamePacket");
|
||||
await trySend<PlayerPacket>(other, "lastPlayerPacket");
|
||||
await trySendMeta<CostumePacket>(other, "lastCostumePacket");
|
||||
await trySendMeta<CapturePacket>(other, "lastCapturePacket");
|
||||
await trySendPack<TagPacket>(other, other.GetTagPacket());
|
||||
await trySendMeta<GamePacket>(other, "lastGamePacket");
|
||||
await trySendMeta<PlayerPacket>(other, "lastPlayerPacket");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ public class Settings {
|
|||
|
||||
public class ShineTable {
|
||||
public bool Enabled { get; set; } = true;
|
||||
public ISet<int> Excluded { get; set; } = new SortedSet<int> { 496 };
|
||||
}
|
||||
|
||||
public class PersistShinesTable
|
||||
|
|
Loading…
Reference in New Issue