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:
parent
d8e151712d
commit
d60aa07e1f
7 changed files with 82 additions and 11 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue