0
0
Fork 0
mirror of https://github.com/Sanae6/SmoOnlineServer.git synced 2024-11-22 03:05:16 +00:00

Server overhaul, might rewrite though

This commit is contained in:
Sanae 2022-02-04 03:45:38 -06:00
parent cdad20ddd2
commit de16e4a787
16 changed files with 469 additions and 359 deletions

View file

@ -1,4 +1,5 @@
using System.Buffers;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Shared;
@ -12,12 +13,16 @@ public class Server {
public readonly List<Client> Clients = new List<Client>();
public readonly Logger Logger = new Logger("Server");
public async Task Listen(ushort port) {
TcpListener listener = TcpListener.Create(port);
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(new IPEndPoint(IPAddress.Loopback, port));
serverSocket.Listen();
listener.Start();
Logger.Info($"Listening on port {port}");
while (true) {
Socket socket = await listener.AcceptSocketAsync();
Socket socket = await serverSocket.AcceptAsync();
Logger.Warn("ok");
if (Clients.Count > Constants.MaxClients) {
Logger.Warn("Turned away client due to max clients");
@ -37,13 +42,12 @@ public class Server {
}
// broadcast packets to all clients
public async void Broadcast<T>(T packet, Client? sender = null) where T : unmanaged, IPacket {
public async Task Broadcast<T>(T packet, Client? sender = null) where T : unmanaged, IPacket {
IMemoryOwner<byte> memory = memoryPool.Rent(Marshal.SizeOf<T>() + Constants.HeaderSize);
PacketHeader header = new PacketHeader {
Id = sender?.Id ?? Guid.Empty,
Type = Constants.Packets[typeof(T)].Type,
Sender = PacketSender.Server // todo maybe use client?
};
FillPacket(header, packet, memory.Memory);
await Broadcast(memory, sender);
@ -98,17 +102,10 @@ public class Server {
ConnectPacket connect = MemoryMarshal.Read<ConnectPacket>(memory.Memory.Span[Constants.HeaderSize..size]);
lock (Clients) {
bool firstConn = false;
switch (connect.ConnectionType) {
case ConnectionTypes.FirstConnection: {
// 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);
client.Id = header.Id;
Clients.Add(client);
foreach (Client c in toDisconnect) c.Socket!.DisconnectAsync(false);
// done disconnecting and removing stale clients with the same id
firstConn = true;
break;
}
case ConnectionTypes.Reconnecting: {
@ -117,32 +114,37 @@ public class Server {
newClient.Socket = client.Socket;
client = newClient;
} else {
// TODO MAJOR: IF CLIENT COULD NOT BE FOUND, SERVER SHOULD GRACEFULLY TELL CLIENT THAT ON DISCONNECT
// can be done via disconnect packet when that gets more data?
throw new Exception("Could not find a suitable client to reconnect as");
firstConn = true;
}
break;
}
default:
throw new Exception($"Invalid connection type {connect.ConnectionType}");
}
if (firstConn) {
// 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);
client.Id = header.Id;
Clients.Add(client);
Parallel.ForEachAsync(toDisconnect, (c, token) => c.Socket!.DisconnectAsync(false, token));
// Broadcast(connect, client);
// done disconnecting and removing stale clients with the same id
}
}
Logger.Info($"Client {socket.RemoteEndPoint} connected.");
continue;
// continue;
}
// todo support variable length packets when they show up
if (header.Sender == PacketSender.Client) {
Logger.Warn($"broadcasting {header.Type}");
// todo support variable length packets if they show up
// Logger.Warn($"broadcasting {header.Type}");
await Broadcast(memory, client);
}
else {
//todo handle server packets :)
}
}
}
catch (Exception e) {
if (e is SocketException {SocketErrorCode: SocketError.ConnectionReset}) {
@ -154,7 +156,11 @@ public class Server {
memory?.Dispose();
}
Clients.Remove(client);
Broadcast(new DisconnectPacket(), client).ContinueWith(_ => {
client.Dispose();
});
}
private static PacketHeader GetHeader(Span<byte> data) {

View file

@ -8,7 +8,7 @@ namespace Shared;
public static class Constants {
public const int MaxPacketSize = 256;
public const int MaxClients = 4;
public static int HeaderSize => Marshal.SizeOf<PacketHeader>();
public static int HeaderSize { get; } = Marshal.SizeOf<PacketHeader>();
// dictionary of packet types to packet
public static readonly Dictionary<Type, PacketAttribute> Packets = Assembly

View file

@ -1,10 +1,21 @@
using System.Runtime.InteropServices;
using Shared.Packet.Packets;
namespace Shared.Packet;
[StructLayout(LayoutKind.Sequential)]
public struct PacketHeader {
public struct PacketHeader : IPacket {
// public int Length;
public Guid Id;
public PacketType Type;
public PacketSender Sender;
public void Serialize(Span<byte> data) {
// MemoryMarshal.Write(data, ref Length);
MemoryMarshal.Write(data, ref Id);
MemoryMarshal.Write(data[16..], ref Type);
}
public void Deserialize(Span<byte> data) {
Id = MemoryMarshal.Read<Guid>(data);
Type = MemoryMarshal.Read<PacketType>(data[16..]);
}
}

View file

@ -1,6 +0,0 @@
namespace Shared.Packet;
public enum PacketSender {
Server,
Client
}

View file

@ -3,7 +3,9 @@
public enum PacketType {
Unknown,
Player,
Cap,
Game,
Tag,
Connect,
Disconnect,
Costume,

View file

@ -0,0 +1,20 @@
using System.Runtime.InteropServices;
using Shared.Packet.Packets;
namespace Shared.Packet;
public static class PacketUtils {
public static void SerializeHeaded<T>(Span<byte> data, PacketHeader header, T t) where T : struct, IPacket {
header.Serialize(data);
t.Serialize(data[Constants.HeaderSize..]);
}
public static T Deserialize<T>(Span<byte> data) where T : IPacket, new() {
T packet = new T();
packet.Deserialize(data);
return packet;
}
public static int SizeOf<T>() where T : struct, IPacket {
return Constants.HeaderSize + Marshal.SizeOf<T>();
}
}

View file

@ -2,6 +2,7 @@
[Packet(PacketType.Command)]
public struct CommandPacket : IPacket {
//todo: implement something for this
public void Serialize(Span<byte> data) {
}

View file

@ -6,7 +6,9 @@ namespace Shared.Packet.Packets;
public struct CostumePacket : IPacket {
public const int CostumeNameSize = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CostumeNameSize)]
public string BodyName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CostumeNameSize)]
public string CapName;
public void Serialize(Span<byte> data) {
Span<char> strData = MemoryMarshal.Cast<byte, char>(data);

View file

@ -2,6 +2,7 @@
[Packet(PacketType.Disconnect)]
public struct DisconnectPacket : IPacket {
//empty packet
public void Serialize(Span<byte> data) {
}

View file

@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace Shared.Packet.Packets;
public class GamePacket : IPacket {
public int ScenarioNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = PlayerPacket.NameSize)]
public string Stage;
public void Serialize(Span<byte> data) {
MemoryMarshal.Write(data, ref ScenarioNum);
Stage.CopyTo(MemoryMarshal.Cast<byte, char>(data[4..]));
}
public void Deserialize(Span<byte> data) {
ScenarioNum = MemoryMarshal.Read<int>(data);
Stage = new string(MemoryMarshal.Cast<byte, char>(data[4..]));
}
}

View file

@ -1,4 +1,6 @@
namespace Shared.Packet.Packets;
using System.Runtime.InteropServices;
namespace Shared.Packet.Packets;
// Packet interface for type safety
public interface IPacket {

View file

@ -0,0 +1,8 @@
namespace Shared.Packet.Packets;
[Flags]
public enum MovementFlags : byte {
IsFlat,
IsCapThrown,
IsSeeker
}

View file

@ -1,5 +1,6 @@
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
namespace Shared.Packet.Packets;
@ -9,14 +10,18 @@ public struct PlayerPacket : IPacket {
public Vector3 Position;
public Quaternion Rotation;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public float[] AnimationBlendWeights;
public float AnimationRate;
public bool Flat;
public bool Is2d;
public bool ThrowingCap;
public bool Seeker;
public bool IsIt;
public int ScenarioNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string Stage;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string Act;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string SubAct;
public void Serialize(Span<byte> data) {
@ -26,16 +31,19 @@ public struct PlayerPacket : IPacket {
MemoryMarshal.Write(data[offset..], ref Rotation);
offset += Marshal.SizeOf<Quaternion>();
AnimationBlendWeights.CopyTo(MemoryMarshal.Cast<byte, float>(data[offset..(offset += 4 * 6)]));
MemoryMarshal.Write(data[(offset += 4)..], ref AnimationRate);
MemoryMarshal.Write(data[offset..], ref AnimationRate);
offset += 4;
MemoryMarshal.Write(data[offset++..], ref Flat);
MemoryMarshal.Write(data[offset++..], ref Is2d);
MemoryMarshal.Write(data[offset++..], ref ThrowingCap);
MemoryMarshal.Write(data[offset++..], ref Seeker);
MemoryMarshal.Write(data[(offset += 4)..], ref ScenarioNum);
Span<char> strData = MemoryMarshal.Cast<byte, char>(data[offset..]);
Stage.CopyTo(strData[..NameSize]);
Act.CopyTo(strData[NameSize..(2 * NameSize)]);
SubAct.CopyTo(strData[(2 * NameSize)..(3 * NameSize)]);
MemoryMarshal.Write(data[offset++..], ref IsIt);
MemoryMarshal.Write(data[offset..], ref ScenarioNum);
offset += 5;
// Span<char> strData = MemoryMarshal.Cast<byte, char>(data[offset..]);
Encoding.UTF8.GetBytes(Stage).CopyTo(data[offset..(offset + NameSize)]);
offset += NameSize;
Encoding.UTF8.GetBytes(Act).CopyTo(data[offset..(offset + NameSize)]);
offset += NameSize;
Encoding.UTF8.GetBytes(SubAct).CopyTo(data[offset..]);
}
public void Deserialize(Span<byte> data) {
@ -44,17 +52,18 @@ public struct PlayerPacket : IPacket {
offset += Marshal.SizeOf<Vector3>();
Rotation = MemoryMarshal.Read<Quaternion>(data[offset..]);
offset += Marshal.SizeOf<Quaternion>();
AnimationBlendWeights = MemoryMarshal.Cast<byte, float>(data[offset..(offset + 4 * 6)]).ToArray();
offset += 4 * 6;
AnimationRate = MemoryMarshal.Read<float>(data[(offset += 4)..]);
offset += 4;
Flat = MemoryMarshal.Read<bool>(data[offset++..]);
AnimationBlendWeights = MemoryMarshal.Cast<byte, float>(data[offset..(offset += 4 * 6)]).ToArray();
AnimationRate = MemoryMarshal.Read<float>(data[offset..(offset += 4)]);
Is2d = MemoryMarshal.Read<bool>(data[offset++..]);
ThrowingCap = MemoryMarshal.Read<bool>(data[offset++..]);
Seeker = MemoryMarshal.Read<bool>(data[offset++..]);
ScenarioNum = MemoryMarshal.Read<int>(data[(offset += 4)..]);
Span<char> strData = MemoryMarshal.Cast<byte, char>(data[offset..]);
Stage = new string(strData[..NameSize].TrimEnd('\0'));
Act = new string(strData[NameSize..(2 * NameSize)].TrimEnd('\0'));
SubAct = new string(strData[(2 * NameSize)..(3 * NameSize)].TrimEnd('\0'));
IsIt = MemoryMarshal.Read<bool>(data[offset++..]);
// offset++; // padding
ScenarioNum = MemoryMarshal.Read<int>(data[offset..]);
offset += 5;
Stage = new string(Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0'));
offset += NameSize;
Act = new string(Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0'));
offset += NameSize;
SubAct = new string(Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0'));
}
}

View file

@ -8,18 +8,48 @@ using Shared.Packet;
using Shared.Packet.Packets;
TcpClient client = new TcpClient("127.0.0.1", 1027);
Guid ownId = new Guid();
Guid ownId = new Guid(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Guid otherId = Guid.Empty;
Logger logger = new Logger("Client");
NetworkStream stream = client.GetStream();
PacketHeader coolHeader = new PacketHeader {
Type = PacketType.Connect,
Sender = PacketSender.Client,
Id = ownId
};
int e = 0;
double d = 0;
Vector3 basePoint = Vector3.Zero;
PlayerPacket? playerPacket = null;
async void Funny() {
Memory<byte> memory = new Memory<byte>(new byte[256]);
{
PacketHeader header = new PacketHeader {
Id = ownId,
Type = PacketType.Player
};
MemoryMarshal.Write(memory.Span, ref header);
}
while (true) {
d += Math.PI / 32;
if (playerPacket == null) {
// logger.Warn($"Waiting...");
await Task.Delay(300);
continue;
}
PlayerPacket packet = playerPacket.Value;
Vector3 pos = basePoint;
pos.X += 100f * (float) Math.Cos(d);
pos.Y += 300f;
pos.Z += 100f * (float) Math.Sin(d);
packet.Position = pos;
packet.Serialize(memory.Span[Constants.HeaderSize..]);
logger.Warn($"Current strs:{packet.Stage}-{packet.Act}-{packet.SubAct} {packet.Is2d} {packet.ThrowingCap} {packet.IsIt}");
await stream.WriteAsync(memory);
await Task.Delay(50);
}
}
async Task S() {
IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(256);
@ -34,23 +64,26 @@ async Task S() {
continue;
}
d += Math.PI / 180;
header.Id = ownId;
MemoryMarshal.Write(owner.Memory.Span, ref header);
unsafe {
MemoryMarshal.Write(owner.Memory.Span, ref coolHeader);
// unbelievably shitty way to marshal playerpacket
fixed (byte* basePtr = owner.Memory.Span) {
byte* dataPtr = basePtr + Constants.HeaderSize;
Vector3 pos = Unsafe.Read<Vector3>(dataPtr);
pos.X += 1000f * (float)Math.Cos(d);
pos.Z += 1000f * (float)Math.Sin(d);
Unsafe.Write(dataPtr, pos);
fixed (byte* data = owner.Memory.Span[Constants.HeaderSize..]) {
logger.Error($"{Marshal.OffsetOf<PlayerPacket>(nameof(PlayerPacket.AnimationBlendWeights))} {Marshal.OffsetOf<PlayerPacket>(nameof(PlayerPacket.AnimationRate))}");
PlayerPacket packet = Marshal.PtrToStructure<PlayerPacket>((IntPtr) data);
playerPacket = packet;
basePoint = packet.Position;
}
}
Console.WriteLine($"aargh {coolHeader.Id} {owner.Memory.Span[..256].Hex()}");
await stream.WriteAsync(owner.Memory);
// packet.SubAct = "";
}
}
}
PacketHeader coolHeader = new PacketHeader {
Type = PacketType.Connect,
Id = ownId
};
IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(256);
MemoryMarshal.Write(owner.Memory.Span[..], ref coolHeader);
ConnectPacket connect = new ConnectPacket {
@ -60,4 +93,6 @@ MemoryMarshal.Write(owner.Memory.Span[Constants.HeaderSize..256], ref connect);
await stream.WriteAsync(owner.Memory);
coolHeader.Type = PacketType.Player;
MemoryMarshal.Write(owner.Memory.Span[..], ref coolHeader);
logger.Info("Connected");
Task.Run(Funny);
await S();