using System.Buffers.Binary; using System.Net.Sockets; namespace OnekoOnline.Net; static class NetBase { public static async Task GetReliableData(NetworkStream stream, CancellationToken token = default) { byte[] infoBytes = new byte[PacketInfo.SizeOf]; await stream.ReadExactlyAsync(infoBytes, token); PacketInfo info = PacketInfo.Deserialize(infoBytes); if (info.Type == PacketType.Ping || info.Type == PacketType.Disconnect) return new(info.Type, [], info.FromId); byte[] data = new byte[info.DataSize]; await stream.ReadExactlyAsync(data, token); return new Packet(info.Type, data, info.FromId); } public static async Task SendReliableData(Packet packet, NetworkStream stream, CancellationToken token = default) { byte[] info = new byte[PacketInfo.SizeOf]; new PacketInfo(packet).Serialize(info); await stream.WriteAsync(info, token); await stream.WriteAsync(packet.Data, token); } } public readonly struct Packet(PacketType type, byte[] data, int id) { public readonly PacketType Type = type; public readonly byte[] Data = data; public readonly int FromId = id; } public enum PacketType : byte { MousePosition, OnekoState, Ping, OnekoSpritesheet, Username, UserId, Disconnect } public struct PacketInfo { public readonly int DataSize; public readonly PacketType Type; public readonly int FromId; public const int SizeOf = (sizeof(int)*2) + 1; public PacketInfo(Packet packet) { DataSize = packet.Data.Length; Type = packet.Type; FromId = packet.FromId; } public PacketInfo(PacketType type, int size, int id) { Type = type; DataSize = size; FromId = id; } public readonly void Serialize(Span span) { span[0] = (byte)Type; BinaryPrimitives.WriteInt32LittleEndian(span[1..], DataSize); BinaryPrimitives.WriteInt32LittleEndian(span[(1+sizeof(int))..], FromId); } public static PacketInfo Deserialize(ReadOnlySpan span) { PacketType type = (PacketType)span[0]; int size = BinaryPrimitives.ReadInt32LittleEndian(span[1..]); int id = BinaryPrimitives.ReadInt32LittleEndian(span[(1+sizeof(int))..]); return new PacketInfo(type, size, id); } } class User(int id) { public readonly int Id = id; //Oneko Stuff public byte[]? SpriteSheet; public string? Username; public bool ExchangedData => SpriteSheet != null && Username != null; }