mirror of
https://github.com/Sanae6/SmoOnlineServer.git
synced 2024-11-21 18:55:17 +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.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);
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue