Files
BoneSync/BoneSync/Networking/NetworkMessage.cs
2025-03-09 21:42:39 +02:00

176 lines
5.6 KiB
C#

using BoneSync.Data;
using BoneSync.Networking.Transport;
using MelonLoader;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
#if TEST
using Xunit;
#endif
namespace BoneSync.Networking
{
public class PacketTypeAttribute : Attribute
{
public PacketTypeAttribute(PacketType type)
{
packetType = type;
}
public PacketType packetType { get; }
}
public class PacketReliabilityAttribute : Attribute
{
public PacketReliabilityAttribute(PacketReliability reliability)
{
this.reliability = reliability;
}
public PacketReliability reliability { get; }
}
public class PacketCatchupAttribute : Attribute
{
public int order { get; }
public PacketCatchupAttribute(int order = 1)
{
this.order = order;
}
}
public abstract class NetworkMessage
{
public ulong senderId
{
private set;
get;
}
private static bool PacketTypesRegistered => PacketTypeMap.Count > 0;
internal static Dictionary<PacketType, Type> PacketTypeMap = new Dictionary<PacketType, Type>();
internal PacketType _packetType;
internal PacketReliability? _reliability;
internal ByteEncoder byteEncoder = new ByteEncoder();
public byte[] GetBytes() => byteEncoder.ToArray();
public PacketType GetPacketType()
{
RegisterPacketTypes();
if (_packetType == PacketType.Unknown)
{
string typeName = GetType().Name;
KeyValuePair<PacketType, Type> found = PacketTypeMap.FirstOrDefault(x => x.Value.Name == typeName);
if (found.Value == null)
{
throw new Exception("Cannot find packetType for " + typeName);
}
_packetType = found.Key;
}
return _packetType;
}
public PacketReliability GetPacketReliability()
{
if (_reliability.HasValue) return _reliability.Value;
PacketReliabilityAttribute reliabilityAttribute = GetType().GetCustomAttribute<PacketReliabilityAttribute>();
if (reliabilityAttribute == null)
{
_reliability = PacketReliability.Reliable;
return PacketReliability.Reliable;
}
_reliability = reliabilityAttribute.reliability;
return _reliability.Value;
}
public static void RegisterPacketTypes()
{
if (PacketTypesRegistered)
{
return;
}
Assembly assembly = Assembly.GetExecutingAssembly();
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.IsSubclassOf(typeof(NetworkMessage)))
{
PacketType? packetType = type.GetCustomAttributesData().Where(x => x.AttributeType == typeof(PacketTypeAttribute)).Select(x => (PacketType)x.ConstructorArguments[0].Value).FirstOrDefault();
PacketTypeMap[packetType.Value] = type;
}
}
}
public static NetworkMessage ParsePacket(Packet packet)
{
//MelonLogger.Msg("Received packet of type " + packet.Info.packetType + " from " + packet.Info.senderId + " Length: " + packet.Data.Length);
// find a class that can parse this packet using Reflection
// and return it
if (!PacketTypeMap.ContainsKey(packet.Info.packetType))
{
throw new Exception("No class found for packet type '" + packet.Info.packetType+"'");
}
Type type = PacketTypeMap[packet.Info.packetType];
// get the constructor that takes a Packet
ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(Packet) }) ?? throw new Exception("No constructor found for type " + type.Name);
NetworkMessage networkMessage = (NetworkMessage)constructor.Invoke(new object[] { packet });
networkMessage.senderId = packet.Info.senderId;
return networkMessage;
}
public Packet GetPacket(ulong senderId, ulong receiverId)
{
PacketInfo packetInfo = new PacketInfo(senderId, receiverId, GetPacketType(), GetPacketReliability());
Packet packet = new Packet(packetInfo, GetBytes());
return packet;
}
public void Broadcast()
{
Send(TransportBase.BORADCAST_ID);
}
public void SendToHost()
{
Send(BoneSync.lobby.GetHostId());
}
public void Send(ulong receiverId)
{
if (BoneSync.IsConnected == false)
{
MelonLogger.Warning("Cannot send packet, not connected to lobby");
return;
}
ulong senderId = BoneSync.lobby.GetLocalId();
Packet packet = GetPacket(senderId, receiverId);
BoneSync.transport.Send(packet);
}
public virtual void Execute()
{
MelonLogger.Warning("Execute not implemented for " + GetType().Name);
}
}
#if TEST
public class NetworkMessageTests
{
[Fact]
public void TestRegisterPackets()
{
NetworkMessage.RegisterPacketTypes();
Assert.NotEmpty(NetworkMessage.PacketTypeMap);
}
}
#endif
}