0
0
Fork 0
mirror of https://github.com/Sanae6/SmoOnlineServer.git synced 2024-11-25 04:35:18 +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,164 +1,170 @@
using System.Buffers; using System.Buffers;
using System.Net.Sockets; using System.Net;
using System.Runtime.InteropServices; using System.Net.Sockets;
using Shared; using System.Runtime.InteropServices;
using Shared.Packet; using Shared;
using Shared.Packet.Packets; using Shared.Packet;
using Shared.Packet.Packets;
namespace Server;
namespace Server;
public class Server {
private readonly MemoryPool<byte> memoryPool = MemoryPool<byte>.Shared; public class Server {
public readonly List<Client> Clients = new List<Client>(); private readonly MemoryPool<byte> memoryPool = MemoryPool<byte>.Shared;
public readonly Logger Logger = new Logger("Server"); public readonly List<Client> Clients = new List<Client>();
public async Task Listen(ushort port) { public readonly Logger Logger = new Logger("Server");
TcpListener listener = TcpListener.Create(port); public async Task Listen(ushort port) {
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Start(); serverSocket.Bind(new IPEndPoint(IPAddress.Loopback, port));
serverSocket.Listen();
while (true) {
Socket socket = await listener.AcceptSocketAsync(); Logger.Info($"Listening on port {port}");
if (Clients.Count > Constants.MaxClients) { while (true) {
Logger.Warn("Turned away client due to max clients"); Socket socket = await serverSocket.AcceptAsync();
await socket.DisconnectAsync(false);
continue; Logger.Warn("ok");
}
if (Clients.Count > Constants.MaxClients) {
HandleSocket(socket); Logger.Warn("Turned away client due to max clients");
} await socket.DisconnectAsync(false);
} continue;
}
public static void FillPacket<T>(PacketHeader header, T packet, Memory<byte> memory) where T : unmanaged, IPacket {
Span<byte> data = memory.Span; HandleSocket(socket);
}
MemoryMarshal.Write(data, ref header); }
MemoryMarshal.Write(data[Constants.HeaderSize..], ref packet);
} public static void FillPacket<T>(PacketHeader header, T packet, Memory<byte> memory) where T : unmanaged, IPacket {
Span<byte> data = memory.Span;
// broadcast packets to all clients
public async void Broadcast<T>(T packet, Client? sender = null) where T : unmanaged, IPacket { MemoryMarshal.Write(data, ref header);
IMemoryOwner<byte> memory = memoryPool.Rent(Marshal.SizeOf<T>() + Constants.HeaderSize); MemoryMarshal.Write(data[Constants.HeaderSize..], ref packet);
}
PacketHeader header = new PacketHeader {
Id = sender?.Id ?? Guid.Empty, // broadcast packets to all clients
Type = Constants.Packets[typeof(T)].Type, public async Task Broadcast<T>(T packet, Client? sender = null) where T : unmanaged, IPacket {
Sender = PacketSender.Server // todo maybe use client? IMemoryOwner<byte> memory = memoryPool.Rent(Marshal.SizeOf<T>() + Constants.HeaderSize);
};
FillPacket(header, packet, memory.Memory); PacketHeader header = new PacketHeader {
await Broadcast(memory, sender); Id = sender?.Id ?? Guid.Empty,
} Type = Constants.Packets[typeof(T)].Type,
};
/// <summary> FillPacket(header, packet, memory.Memory);
/// Takes ownership of data and disposes once done. await Broadcast(memory, sender);
/// </summary> }
/// <param name="data">Memory owner to dispose once done</param>
/// <param name="sender">Optional sender to not broadcast data to</param> /// <summary>
public async Task Broadcast(IMemoryOwner<byte> data, Client? sender = null) { /// Takes ownership of data and disposes once done.
await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data.Memory))); /// </summary>
data.Dispose(); /// <param name="data">Memory owner to dispose once done</param>
} /// <param name="sender">Optional sender to not broadcast data to</param>
public async Task Broadcast(IMemoryOwner<byte> data, Client? sender = null) {
/// <summary> await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data.Memory)));
/// Broadcasts memory whose memory shouldn't be disposed, should only be fired by server code. data.Dispose();
/// </summary> }
/// <param name="data">Memory to send to the clients</param>
/// <param name="sender">Optional sender to not broadcast data to</param> /// <summary>
public async void Broadcast(Memory<byte> data, Client? sender = null) { /// Broadcasts memory whose memory shouldn't be disposed, should only be fired by server code.
await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data))); /// </summary>
} /// <param name="data">Memory to send to the clients</param>
/// <param name="sender">Optional sender to not broadcast data to</param>
public Client? FindExistingClient(Guid id) { public async void Broadcast(Memory<byte> data, Client? sender = null) {
return Clients.Find(client => client.Id == id); await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data)));
} }
public Client? FindExistingClient(Guid id) {
private async void HandleSocket(Socket socket) { return Clients.Find(client => client.Id == id);
Client client = new Client {Socket = socket}; }
IMemoryOwner<byte> memory = null!;
bool first = true;
try { private async void HandleSocket(Socket socket) {
while (true) { Client client = new Client {Socket = socket};
memory = memoryPool.Rent(Constants.MaxPacketSize); IMemoryOwner<byte> memory = null!;
int size = await socket.ReceiveAsync(memory.Memory, SocketFlags.None); bool first = true;
if (size == 0) { // treat it as a disconnect and exit try {
Logger.Info($"Socket {socket.RemoteEndPoint} disconnected."); while (true) {
await socket.DisconnectAsync(false); memory = memoryPool.Rent(Constants.MaxPacketSize);
break; int size = await socket.ReceiveAsync(memory.Memory, SocketFlags.None);
} if (size == 0) { // treat it as a disconnect and exit
Logger.Info($"Socket {socket.RemoteEndPoint} disconnected.");
PacketHeader header = GetHeader(memory.Memory.Span[..size]); await socket.DisconnectAsync(false);
//Logger.Info($"first = {first}, type = {header.Type}, data = " + memory.Memory.Span[..size].Hex()); break;
// connection initialization }
if (first) {
first = false; PacketHeader header = GetHeader(memory.Memory.Span[..size]);
if (header.Type != PacketType.Connect) { //Logger.Info($"first = {first}, type = {header.Type}, data = " + memory.Memory.Span[..size].Hex());
throw new Exception($"First packet was not init, instead it was {header.Type}"); // connection initialization
} if (first) {
first = false;
ConnectPacket connect = MemoryMarshal.Read<ConnectPacket>(memory.Memory.Span[Constants.HeaderSize..size]); if (header.Type != PacketType.Connect) {
lock (Clients) { throw new Exception($"First packet was not init, instead it was {header.Type}");
switch (connect.ConnectionType) { }
case ConnectionTypes.FirstConnection: {
// do any cleanup required when it comes to new clients ConnectPacket connect = MemoryMarshal.Read<ConnectPacket>(memory.Memory.Span[Constants.HeaderSize..size]);
List<Client> toDisconnect = Clients.FindAll(c => c.Id == header.Id && c.Connected && c.Socket != null); lock (Clients) {
Clients.RemoveAll(c => c.Id == header.Id); bool firstConn = false;
switch (connect.ConnectionType) {
client.Id = header.Id; case ConnectionTypes.FirstConnection: {
Clients.Add(client); firstConn = true;
break;
foreach (Client c in toDisconnect) c.Socket!.DisconnectAsync(false); }
// done disconnecting and removing stale clients with the same id case ConnectionTypes.Reconnecting: {
break; if (FindExistingClient(header.Id) is { } newClient) {
} if (newClient.Connected) throw new Exception($"Tried to join as already connected user {header.Id}");
case ConnectionTypes.Reconnecting: { newClient.Socket = client.Socket;
if (FindExistingClient(header.Id) is { } newClient) { client = newClient;
if (newClient.Connected) throw new Exception($"Tried to join as already connected user {header.Id}"); } else {
newClient.Socket = client.Socket; firstConn = true;
client = newClient; }
} else { break;
// 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? default:
throw new Exception("Could not find a suitable client to reconnect as"); throw new Exception($"Invalid connection type {connect.ConnectionType}");
} }
break; if (firstConn) {
} // do any cleanup required when it comes to new clients
default: List<Client> toDisconnect = Clients.FindAll(c => c.Id == header.Id && c.Connected && c.Socket != null);
throw new Exception($"Invalid connection type {connect.ConnectionType}"); Clients.RemoveAll(c => c.Id == header.Id);
}
} client.Id = header.Id;
Clients.Add(client);
Logger.Info($"Client {socket.RemoteEndPoint} connected.");
Parallel.ForEachAsync(toDisconnect, (c, token) => c.Socket!.DisconnectAsync(false, token));
continue; // Broadcast(connect, client);
} // done disconnecting and removing stale clients with the same id
}
}
// todo support variable length packets when they show up
if (header.Sender == PacketSender.Client) { Logger.Info($"Client {socket.RemoteEndPoint} connected.");
Logger.Warn($"broadcasting {header.Type}");
await Broadcast(memory, client); // continue;
} }
else {
//todo handle server packets :)
} // todo support variable length packets if they show up
} // Logger.Warn($"broadcasting {header.Type}");
} await Broadcast(memory, client);
catch (Exception e) { }
if (e is SocketException {SocketErrorCode: SocketError.ConnectionReset}) { }
Logger.Info($"Client {socket.RemoteEndPoint} disconnected from the server"); catch (Exception e) {
} else { if (e is SocketException {SocketErrorCode: SocketError.ConnectionReset}) {
Logger.Error($"Exception on socket {socket.RemoteEndPoint}, disconnecting for: {e}"); Logger.Info($"Client {socket.RemoteEndPoint} disconnected from the server");
await socket.DisconnectAsync(false); } else {
} Logger.Error($"Exception on socket {socket.RemoteEndPoint}, disconnecting for: {e}");
await socket.DisconnectAsync(false);
memory?.Dispose(); }
}
client.Dispose(); memory?.Dispose();
} }
private static PacketHeader GetHeader(Span<byte> data) { Clients.Remove(client);
//no need to error check, the client will disconnect when the packet is invalid :) Broadcast(new DisconnectPacket(), client).ContinueWith(_ => {
return MemoryMarshal.Read<PacketHeader>(data); client.Dispose();
} });
}
private static PacketHeader GetHeader(Span<byte> data) {
//no need to error check, the client will disconnect when the packet is invalid :)
return MemoryMarshal.Read<PacketHeader>(data);
}
} }

View file

@ -1,19 +1,19 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Shared.Packet; using Shared.Packet;
using Shared.Packet.Packets; using Shared.Packet.Packets;
namespace Shared; namespace Shared;
public static class Constants { public static class Constants {
public const int MaxPacketSize = 256; public const int MaxPacketSize = 256;
public const int MaxClients = 4; 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 // dictionary of packet types to packet
public static readonly Dictionary<Type, PacketAttribute> Packets = Assembly public static readonly Dictionary<Type, PacketAttribute> Packets = Assembly
.GetExecutingAssembly() .GetExecutingAssembly()
.GetTypes() .GetTypes()
.Where(type => type.IsAssignableTo(typeof(IPacket))) .Where(type => type.IsAssignableTo(typeof(IPacket)))
.ToDictionary(type => type, type => type.GetCustomAttribute<PacketAttribute>()!); .ToDictionary(type => type, type => type.GetCustomAttribute<PacketAttribute>()!);
} }

View file

@ -1,10 +1,21 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Shared.Packet.Packets;
namespace Shared.Packet;
namespace Shared.Packet;
[StructLayout(LayoutKind.Sequential)]
public struct PacketHeader { [StructLayout(LayoutKind.Sequential)]
public Guid Id; public struct PacketHeader : IPacket {
public PacketType Type; // public int Length;
public PacketSender Sender; public Guid Id;
public PacketType Type;
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

@ -1,12 +1,14 @@
namespace Shared.Packet; namespace Shared.Packet;
public enum PacketType { public enum PacketType {
Unknown, Unknown,
Player, Player,
Game, Cap,
Connect, Game,
Disconnect, Tag,
Costume, Connect,
Shine, Disconnect,
Command Costume,
Shine,
Command
} }

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

@ -1,12 +1,13 @@
namespace Shared.Packet.Packets; namespace Shared.Packet.Packets;
[Packet(PacketType.Command)] [Packet(PacketType.Command)]
public struct CommandPacket : IPacket { public struct CommandPacket : IPacket {
public void Serialize(Span<byte> data) { //todo: implement something for this
public void Serialize(Span<byte> data) {
}
}
public void Deserialize(Span<byte> data) {
public void Deserialize(Span<byte> data) {
}
}
} }

View file

@ -1,15 +1,15 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
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 void Serialize(Span<byte> data) { public void Serialize(Span<byte> data) {
MemoryMarshal.Write(data, ref ConnectionType); MemoryMarshal.Write(data, ref ConnectionType);
} }
public void Deserialize(Span<byte> data) { public void Deserialize(Span<byte> data) {
ConnectionType = MemoryMarshal.Read<ConnectionTypes>(data); ConnectionType = MemoryMarshal.Read<ConnectionTypes>(data);
} }
} }

View file

@ -1,22 +1,24 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Shared.Packet.Packets; namespace Shared.Packet.Packets;
[Packet(PacketType.Costume)] [Packet(PacketType.Costume)]
public struct CostumePacket : IPacket { public struct CostumePacket : IPacket {
public const int CostumeNameSize = 0x20; public const int CostumeNameSize = 0x20;
public string BodyName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CostumeNameSize)]
public string CapName; public string BodyName;
public void Serialize(Span<byte> data) { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CostumeNameSize)]
Span<char> strData = MemoryMarshal.Cast<byte, char>(data); public string CapName;
BodyName.CopyTo(strData[..CostumeNameSize]); public void Serialize(Span<byte> data) {
CapName.CopyTo(strData[CostumeNameSize..]); Span<char> strData = MemoryMarshal.Cast<byte, char>(data);
} BodyName.CopyTo(strData[..CostumeNameSize]);
CapName.CopyTo(strData[CostumeNameSize..]);
public void Deserialize(Span<byte> data) { }
Span<char> strData = MemoryMarshal.Cast<byte, char>(data);
BodyName = new string(strData[..CostumeNameSize].TrimEnd('\0')); public void Deserialize(Span<byte> data) {
CapName = new string(strData[CostumeNameSize..].TrimEnd('\0')); Span<char> strData = MemoryMarshal.Cast<byte, char>(data);
} BodyName = new string(strData[..CostumeNameSize].TrimEnd('\0'));
CapName = new string(strData[CostumeNameSize..].TrimEnd('\0'));
}
} }

View file

@ -1,12 +1,13 @@
namespace Shared.Packet.Packets; namespace Shared.Packet.Packets;
[Packet(PacketType.Disconnect)] [Packet(PacketType.Disconnect)]
public struct DisconnectPacket : IPacket { public struct DisconnectPacket : IPacket {
public void Serialize(Span<byte> data) { //empty packet
public void Serialize(Span<byte> data) {
}
}
public void Deserialize(Span<byte> data) {
public void Deserialize(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,7 +1,9 @@
namespace Shared.Packet.Packets; using System.Runtime.InteropServices;
// Packet interface for type safety namespace Shared.Packet.Packets;
public interface IPacket {
void Serialize(Span<byte> data); // Packet interface for type safety
void Deserialize(Span<byte> data); public interface IPacket {
void Serialize(Span<byte> data);
void Deserialize(Span<byte> data);
} }

View file

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

View file

@ -1,60 +1,69 @@
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Shared.Packet.Packets;
namespace Shared.Packet.Packets;
[Packet(PacketType.Player)]
public struct PlayerPacket : IPacket { [Packet(PacketType.Player)]
public const int NameSize = 0x30; public struct PlayerPacket : IPacket {
public const int NameSize = 0x30;
public Vector3 Position;
public Quaternion Rotation; public Vector3 Position;
public float[] AnimationBlendWeights; public Quaternion Rotation;
public float AnimationRate; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public bool Flat; public float[] AnimationBlendWeights;
public bool ThrowingCap; public float AnimationRate;
public bool Seeker; public bool Is2d;
public int ScenarioNum; public bool ThrowingCap;
public string Stage; public bool IsIt;
public string Act; public int ScenarioNum;
public string SubAct; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string Stage;
public void Serialize(Span<byte> data) { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
int offset = 0; public string Act;
MemoryMarshal.Write(data, ref Position); [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
offset += Marshal.SizeOf<Vector3>(); public string SubAct;
MemoryMarshal.Write(data[offset..], ref Rotation);
offset += Marshal.SizeOf<Quaternion>(); public void Serialize(Span<byte> data) {
AnimationBlendWeights.CopyTo(MemoryMarshal.Cast<byte, float>(data[offset..(offset += 4 * 6)])); int offset = 0;
MemoryMarshal.Write(data[(offset += 4)..], ref AnimationRate); MemoryMarshal.Write(data, ref Position);
offset += 4; offset += Marshal.SizeOf<Vector3>();
MemoryMarshal.Write(data[offset++..], ref Flat); MemoryMarshal.Write(data[offset..], ref Rotation);
MemoryMarshal.Write(data[offset++..], ref ThrowingCap); offset += Marshal.SizeOf<Quaternion>();
MemoryMarshal.Write(data[offset++..], ref Seeker); AnimationBlendWeights.CopyTo(MemoryMarshal.Cast<byte, float>(data[offset..(offset += 4 * 6)]));
MemoryMarshal.Write(data[(offset += 4)..], ref ScenarioNum); MemoryMarshal.Write(data[offset..], ref AnimationRate);
Span<char> strData = MemoryMarshal.Cast<byte, char>(data[offset..]); offset += 4;
Stage.CopyTo(strData[..NameSize]); MemoryMarshal.Write(data[offset++..], ref Is2d);
Act.CopyTo(strData[NameSize..(2 * NameSize)]); MemoryMarshal.Write(data[offset++..], ref ThrowingCap);
SubAct.CopyTo(strData[(2 * NameSize)..(3 * NameSize)]); MemoryMarshal.Write(data[offset++..], ref IsIt);
} MemoryMarshal.Write(data[offset..], ref ScenarioNum);
offset += 5;
public void Deserialize(Span<byte> data) { // Span<char> strData = MemoryMarshal.Cast<byte, char>(data[offset..]);
int offset = 0; Encoding.UTF8.GetBytes(Stage).CopyTo(data[offset..(offset + NameSize)]);
Position = MemoryMarshal.Read<Vector3>(data); offset += NameSize;
offset += Marshal.SizeOf<Vector3>(); Encoding.UTF8.GetBytes(Act).CopyTo(data[offset..(offset + NameSize)]);
Rotation = MemoryMarshal.Read<Quaternion>(data[offset..]); offset += NameSize;
offset += Marshal.SizeOf<Quaternion>(); Encoding.UTF8.GetBytes(SubAct).CopyTo(data[offset..]);
AnimationBlendWeights = MemoryMarshal.Cast<byte, float>(data[offset..(offset + 4 * 6)]).ToArray(); }
offset += 4 * 6;
AnimationRate = MemoryMarshal.Read<float>(data[(offset += 4)..]); public void Deserialize(Span<byte> data) {
offset += 4; int offset = 0;
Flat = MemoryMarshal.Read<bool>(data[offset++..]); Position = MemoryMarshal.Read<Vector3>(data);
ThrowingCap = MemoryMarshal.Read<bool>(data[offset++..]); offset += Marshal.SizeOf<Vector3>();
Seeker = MemoryMarshal.Read<bool>(data[offset++..]); Rotation = MemoryMarshal.Read<Quaternion>(data[offset..]);
ScenarioNum = MemoryMarshal.Read<int>(data[(offset += 4)..]); offset += Marshal.SizeOf<Quaternion>();
Span<char> strData = MemoryMarshal.Cast<byte, char>(data[offset..]); AnimationBlendWeights = MemoryMarshal.Cast<byte, float>(data[offset..(offset += 4 * 6)]).ToArray();
Stage = new string(strData[..NameSize].TrimEnd('\0')); AnimationRate = MemoryMarshal.Read<float>(data[offset..(offset += 4)]);
Act = new string(strData[NameSize..(2 * NameSize)].TrimEnd('\0')); Is2d = MemoryMarshal.Read<bool>(data[offset++..]);
SubAct = new string(strData[(2 * NameSize)..(3 * NameSize)].TrimEnd('\0')); ThrowingCap = MemoryMarshal.Read<bool>(data[offset++..]);
} 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

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" /> <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -8,18 +8,48 @@ using Shared.Packet;
using Shared.Packet.Packets; using Shared.Packet.Packets;
TcpClient client = new TcpClient("127.0.0.1", 1027); 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; Guid otherId = Guid.Empty;
Logger logger = new Logger("Client"); Logger logger = new Logger("Client");
NetworkStream stream = client.GetStream(); NetworkStream stream = client.GetStream();
PacketHeader coolHeader = new PacketHeader {
Type = PacketType.Connect,
Sender = PacketSender.Client,
Id = ownId
};
int e = 0; int e = 0;
double d = 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() { async Task S() {
IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(256); IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(256);
@ -34,23 +64,26 @@ async Task S() {
continue; continue;
} }
d += Math.PI / 180; header.Id = ownId;
MemoryMarshal.Write(owner.Memory.Span, ref header);
unsafe { unsafe {
MemoryMarshal.Write(owner.Memory.Span, ref coolHeader); fixed (byte* data = owner.Memory.Span[Constants.HeaderSize..]) {
// unbelievably shitty way to marshal playerpacket logger.Error($"{Marshal.OffsetOf<PlayerPacket>(nameof(PlayerPacket.AnimationBlendWeights))} {Marshal.OffsetOf<PlayerPacket>(nameof(PlayerPacket.AnimationRate))}");
fixed (byte* basePtr = owner.Memory.Span) { PlayerPacket packet = Marshal.PtrToStructure<PlayerPacket>((IntPtr) data);
byte* dataPtr = basePtr + Constants.HeaderSize; playerPacket = packet;
Vector3 pos = Unsafe.Read<Vector3>(dataPtr); basePoint = packet.Position;
pos.X += 1000f * (float)Math.Cos(d);
pos.Z += 1000f * (float)Math.Sin(d);
Unsafe.Write(dataPtr, pos);
} }
} }
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); IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(256);
MemoryMarshal.Write(owner.Memory.Span[..], ref coolHeader); MemoryMarshal.Write(owner.Memory.Span[..], ref coolHeader);
ConnectPacket connect = new ConnectPacket { ConnectPacket connect = new ConnectPacket {
@ -60,4 +93,6 @@ MemoryMarshal.Write(owner.Memory.Span[Constants.HeaderSize..256], ref connect);
await stream.WriteAsync(owner.Memory); await stream.WriteAsync(owner.Memory);
coolHeader.Type = PacketType.Player; coolHeader.Type = PacketType.Player;
MemoryMarshal.Write(owner.Memory.Span[..], ref coolHeader); MemoryMarshal.Write(owner.Memory.Span[..], ref coolHeader);
logger.Info("Connected");
Task.Run(Funny);
await S(); await S();