Files
BoneSync/BoneSync/Networking/NetworkMessage.cs
Aaro Varis 335df69fcd Refactor event patching and transport systems
- Convert TransportBase from abstract class to interface; replace BORADCAST_ID constant with BROADCAST_ID property in transport implementations.
- Update SteamTransport to implement the new interface and provide its own BROADCAST_ID.
- Refactor event patching: introduce UnityEventPatching to map proxy events to originals, and update UnityEventPatch<T> to use proxy events for patching.
- Ensure patched UnityEvents replace originals in SyncablePhysics, so all invocations go through patch logic.
- Improve robustness and maintainability of both networking and event patching systems.
2026-01-13 17:34:02 +02:00

205 lines
6.6 KiB
C#

using BoneSync.Data;
using BoneSync.Networking.Transport;
using BoneSync.Sync;
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)
{
this.order = order;
}
}
public class AlwaysExecuteAttribute : Attribute
{
public AlwaysExecuteAttribute() {}
}
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 int? _catchupOrder;
internal bool? _alwaysExecute;
internal ByteEncoder byteEncoder = new ByteEncoder();
public byte[] GetBytes() => byteEncoder.ToArray();
public bool GetAlwaysExecute()
{
if (_alwaysExecute.HasValue) return _alwaysExecute.Value;
AlwaysExecuteAttribute alwaysExecuteAttribute = GetType().GetCustomAttribute<AlwaysExecuteAttribute>();
_alwaysExecute = alwaysExecuteAttribute != null;
return _alwaysExecute.Value;
}
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 int GetCatchupOrder()
{
if (_catchupOrder.HasValue) return _catchupOrder.Value;
PacketCatchupAttribute catchupAttribute = GetType().GetCustomAttribute<PacketCatchupAttribute>();
if (catchupAttribute == null)
{
_catchupOrder = -1;
return -1;
}
_catchupOrder = catchupAttribute.order;
return _catchupOrder.Value;
}
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)
{
//SyncLogger.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(), 0, (byte)SceneSync.CurrentSceneIndex);
Packet packet = new Packet(packetInfo, GetBytes());
return packet;
}
public void Broadcast()
{
Send(BoneSync.transport.BROADCAST_ID);
}
public void SendToHost()
{
Send(BoneSync.lobby.GetHostId());
}
public void Send(ulong receiverId)
{
if (BoneSync.IsConnected == false)
{
SyncLogger.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()
{
SyncLogger.Warning("Execute not implemented for " + GetType().Name);
}
}
#if TEST
public class NetworkMessageTests
{
[Fact]
public void TestRegisterPackets()
{
NetworkMessage.RegisterPacketTypes();
Assert.NotEmpty(NetworkMessage.PacketTypeMap);
}
}
#endif
}