0
0
Fork 0
mirror of https://github.com/Sanae6/SmoOnlineServer.git synced 2024-11-25 04:35:18 +00:00

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;
using Shared.Packet; using Shared.Packet;
using Shared.Packet.Packets; using Shared.Packet.Packets;
@ -6,24 +8,39 @@ using Shared.Packet.Packets;
namespace Server; namespace Server;
public class Client : IDisposable { 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 bool Connected = false;
public CostumePacket? CurrentCostume = null; // required for proper client sync
public CostumePacket? CurrentCostume = null; public string Name {
get => Logger.Name;
set => Logger.Name = value;
}
public Guid Id; public Guid Id;
public Socket? Socket; public Socket? Socket;
public Server Server { get; init; } public Server Server { get; init; }
public Logger Logger { get; init; } = new Logger("Unknown User");
public void Dispose() { public void Dispose() {
Socket?.Disconnect(false); 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) { if (!Connected) {
Server.Logger.Info($"Didn't send {(PacketType) data.Span[16]} to {Id} because they weren't connected yet"); Server.Logger.Info($"Didn't send {(PacketType) data.Span[16]} to {Id} because they weren't connected yet");
return; return;
} }
// Server.Logger.Info($"Sending {(PacketType) data.Span[16]} to {Id} from {other?.Id.ToString() ?? "server"}"); // Server.Logger.Info($"Sending {(PacketType) data.Span[16]} to {Id} from {other?.Id.ToString() ?? "server"}");
await Socket!.SendAsync(data[..Constants.MaxPacketSize], SocketFlags.None); 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); await server.Listen(1027);

View file

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

View file

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

View file

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

View file

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