Implementing shine sync and other minor fixes

This commit is contained in:
Sanae 2022-02-14 13:45:58 -06:00
parent d8e151712d
commit d60aa07e1f
7 changed files with 82 additions and 11 deletions

View File

@ -1,4 +1,6 @@
using System.Net.Sockets;
using System.Buffers;
using System.Collections.Concurrent;
using System.Net.Sockets;
using Shared;
using Shared.Packet;
using Shared.Packet.Packets;
@ -6,24 +8,39 @@ using Shared.Packet.Packets;
namespace Server;
public class Client : IDisposable {
public readonly Dictionary<string, object> Metadata = new Dictionary<string, object>(); // can be used to store any information about a player
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 CostumePacket? CurrentCostume = null;
public CostumePacket? CurrentCostume = null; // required for proper client sync
public string Name {
get => Logger.Name;
set => Logger.Name = value;
}
public Guid Id;
public Socket? Socket;
public Server Server { get; init; }
public Logger Logger { get; init; } = new Logger("Unknown User");
public void Dispose() {
Socket?.Disconnect(false);
}
public async Task Send(ReadOnlyMemory<byte> data, Client? other) {
public async Task Send<T>(T packet, Client? sender = null) where T : unmanaged, IPacket {
IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.Rent(Constants.MaxPacketSize);
PacketHeader header = new PacketHeader {
Id = sender?.Id ?? Guid.Empty,
Type = Constants.PacketMap[typeof(T)].Type
};
Server.FillPacket(header, packet, memory.Memory);
}
public async Task Send(ReadOnlyMemory<byte> data, Client? sender) {
if (!Connected) {
Server.Logger.Info($"Didn't send {(PacketType) data.Span[16]} to {Id} because they weren't connected yet");
return;
}
// Server.Logger.Info($"Sending {(PacketType) data.Span[16]} to {Id} from {other?.Id.ToString() ?? "server"}");
await Socket!.SendAsync(data[..Constants.MaxPacketSize], SocketFlags.None);
}

View File

@ -1,3 +1,43 @@
Server.Server server = new Server.Server();
using System.Collections.Concurrent;
using Server;
using Shared.Packet.Packets;
Server.Server server = new Server.Server();
ConcurrentBag<int> shineBag = new ConcurrentBag<int>();
server.ClientJoined += async (c, type) => {
c.Metadata["shineSync"] = new ConcurrentBag<int>();
c.Metadata["loadedSave"] = false;
};
async Task ClientSyncShineBag(Client client) {
foreach (int shine in shineBag.Except((ConcurrentBag<int>) client.Metadata["shineSync"]))
await client.Send(new ShinePacket {
ShineId = shine
});
}
async void SyncShineBag() {
await Parallel.ForEachAsync(server.Clients, async (client, _) => {
await ClientSyncShineBag(client);
});
}
server.PacketHandler += async (c, p) => {
switch (p) {
case CostumePacket:
await ClientSyncShineBag(c);
c.Metadata["loadedSave"] = true;
break;
case ShinePacket shinePacket: {
if (c.Metadata["loadedSave"] is false) return;
ConcurrentBag<int> playerBag = (ConcurrentBag<int>) c.Metadata["shineSync"];
shineBag.Add(shinePacket.ShineId);
playerBag.Add(shinePacket.ShineId);
SyncShineBag();
break;
}
}
};
await server.Listen(1027);

View File

@ -12,9 +12,12 @@ public class Server {
public readonly List<Client> Clients = new List<Client>();
public readonly Logger Logger = new Logger("Server");
private readonly MemoryPool<byte> memoryPool = MemoryPool<byte>.Shared;
public event Action<Client, IPacket> PacketHandler = null!;
public event Action<Client, ConnectPacket> ClientJoined = null!;
public async Task Listen(ushort port) {
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, port));
serverSocket.Listen();
@ -114,12 +117,14 @@ public class Server {
}
case ConnectionTypes.Reconnecting: {
client.Id = header.Id;
client.Name = connect.ClientName;
if (FindExistingClient(header.Id) is { } newClient) {
if (newClient.Connected) throw new Exception($"Tried to join as already connected user {header.Id}");
newClient.Socket = client.Socket;
client = newClient;
} else {
firstConn = true;
connect.ConnectionType = ConnectionTypes.FirstConnection;
}
break;
@ -139,6 +144,8 @@ public class Server {
Parallel.ForEachAsync(toDisconnect, (c, token) => c.Socket!.DisconnectAsync(false, token));
// done disconnecting and removing stale clients with the same id
ClientJoined?.Invoke(client, connect);
}
}
@ -161,6 +168,7 @@ public class Server {
other.CurrentCostume.Value.Serialize(tempBuffer.Memory.Span[Constants.HeaderSize..]);
await client.Send(tempBuffer.Memory, null);
}
tempBuffer.Dispose();
});
@ -174,6 +182,7 @@ public class Server {
costumePacket.Deserialize(memory.Memory.Span[Constants.HeaderSize..]);
client.CurrentCostume = costumePacket;
}
await Broadcast(memory, client);
}
} catch (Exception e) {

View File

@ -8,7 +8,11 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Shared.csproj"/>
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Tomlyn" Version="0.11.0" />
</ItemGroup>
</Project>

View File

@ -5,7 +5,7 @@ public class Logger {
Name = name;
}
public string Name { get; }
public string Name { get; set; }
public void Info(string text) {
Console.ResetColor();

View File

@ -1,16 +1,20 @@
using System.Runtime.InteropServices;
using System.Text;
namespace Shared.Packet.Packets;
[Packet(PacketType.Connect)]
public struct ConnectPacket : IPacket {
public ConnectionTypes ConnectionType;
public string ClientName;
public void Serialize(Span<byte> data) {
MemoryMarshal.Write(data, ref ConnectionType);
Encoding.UTF8.GetBytes(ClientName).CopyTo(data[4..(4 + Constants.CostumeNameSize)]);
}
public void Deserialize(Span<byte> data) {
ConnectionType = MemoryMarshal.Read<ConnectionTypes>(data);
ClientName = Encoding.UTF8.GetString(data[4..(4 + Constants.CostumeNameSize)]).TrimNullTerm();
}
}

View File

@ -5,15 +5,12 @@ namespace Shared.Packet.Packets;
[Packet(PacketType.Shine)]
public struct ShinePacket : IPacket {
public int ShineId;
public bool IsGrand;
public void Serialize(Span<byte> data) {
MemoryMarshal.Write(data, ref ShineId);
MemoryMarshal.Write(data, ref IsGrand);
}
public void Deserialize(Span<byte> data) {
ShineId = MemoryMarshal.Read<int>(data);
IsGrand = MemoryMarshal.Read<bool>(data);
}
}