diff --git a/BoneSync/BoneSync.csproj b/BoneSync/BoneSync.csproj index 298185c..0555f47 100644 --- a/BoneSync/BoneSync.csproj +++ b/BoneSync/BoneSync.csproj @@ -1,5 +1,7 @@  + + Debug @@ -13,6 +15,8 @@ 512 true + + true @@ -35,37 +39,85 @@ - ..\..\..\..\AppData\Roaming\r2modmanPlus-local\BONEWORKS\profiles\aaaa\MelonLoader\MelonLoader.dll + ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Stationeers\MelonLoader\MelonLoader.dll + + + ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll + + + ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll + + + ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + + ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + + ..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + + + ..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.9.3\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.9.3\lib\net452\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.9.3\lib\net452\xunit.execution.desktop.dll + + + + - - - + {0289f09e-d594-46a9-96a7-b0f31f5d97b0} Facepunch.Steamworks.Win64 + + + + + + + - COPY "$(TargetPath)" "C:\Program Files (x86)\Steam\steamapps\common\BONEWORKS\BONEWORKS\Mods" + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/BoneSync/MelonLoaderMod.cs b/BoneSync/MelonLoaderMod.cs index 68dde4c..5365064 100644 --- a/BoneSync/MelonLoaderMod.cs +++ b/BoneSync/MelonLoaderMod.cs @@ -16,8 +16,8 @@ namespace BoneSync public class BoneSync : MelonMod { - LobbyManager lobby; - TransportBase transport; + public static LobbyManager lobby; + public static TransportBase transport; public override void OnApplicationStart() { MelonLogger.Msg("OnApplicationStart"); diff --git a/BoneSync/Networking/LobbyManager/LobbyManager.cs b/BoneSync/Networking/LobbyManager/LobbyManager.cs index da19ed3..a42d18a 100644 --- a/BoneSync/Networking/LobbyManager/LobbyManager.cs +++ b/BoneSync/Networking/LobbyManager/LobbyManager.cs @@ -9,7 +9,7 @@ using Facepunch.Steamworks.Data; namespace BoneSync.Networking.LobbyManager { - internal abstract class LobbyManager + public abstract class LobbyManager { public abstract ulong GetLobbyId(); public abstract ulong GetHostId(); diff --git a/BoneSync/Networking/Messages/LobbyInfoMessage.cs b/BoneSync/Networking/Messages/LobbyInfoMessage.cs new file mode 100644 index 0000000..ea6d841 --- /dev/null +++ b/BoneSync/Networking/Messages/LobbyInfoMessage.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace BoneSync.Networking.Messages +{ + [PacketType(PacketType.LobbyInfo)] + public class LobbyInfoMessage : NetworkMessage + { + public string LobbyName; + public string HostName; + public int MaxPlayers; + public int CurrentPlayers; + public LobbyInfoMessage(string lobbyName, string hostName, int maxPlayers, int currentPlayers) + { + LobbyName = lobbyName; + HostName = hostName; + MaxPlayers = maxPlayers; + CurrentPlayers = currentPlayers; + byteEncoder.WriteString(LobbyName); + byteEncoder.WriteString(HostName); + byteEncoder.WriteInt(MaxPlayers); + byteEncoder.WriteInt(CurrentPlayers); + } + + public LobbyInfoMessage(Packet packet) + { + byteEncoder = new ByteEncoder(packet.Data); + + LobbyName = byteEncoder.ReadString(); + HostName = byteEncoder.ReadString(); + MaxPlayers = byteEncoder.ReadInt(); + CurrentPlayers = byteEncoder.ReadInt(); + } + + public override void Execute() + { + throw new NotImplementedException(); + } + } + + public class LobbyInfoMessageTests + { + [Fact] + public void TestLobbyInfoMessage() + { + NetworkMessage.RegisterPacketTypes(); + + LobbyInfoMessage message = new LobbyInfoMessage("TestLobby", "TestHost", 10, 5); + Packet packet = message.GetPacket(0,0,0); + Assert.Equal(PacketType.LobbyInfo, packet.Info.packetType); + LobbyInfoMessage newMessage = (LobbyInfoMessage)NetworkMessage.ParsePacket(packet); + Assert.Equal("TestLobby", newMessage.LobbyName); + Assert.Equal("TestHost", newMessage.HostName); + Assert.Equal(10, newMessage.MaxPlayers); + Assert.Equal(5, newMessage.CurrentPlayers); + } + } +} diff --git a/BoneSync/Networking/Messages/NetworkMessage.cs b/BoneSync/Networking/Messages/NetworkMessage.cs new file mode 100644 index 0000000..cf085c4 --- /dev/null +++ b/BoneSync/Networking/Messages/NetworkMessage.cs @@ -0,0 +1,120 @@ +using BoneSync.Networking.Transport; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace BoneSync.Networking.Messages +{ + public class PacketTypeAttribute : Attribute + { + public PacketTypeAttribute(PacketType type) + { + packetType = type; + } + + public PacketType packetType { get; } + } + + public abstract class NetworkMessage + { + private static bool _packetTypesRegistered = false; + + internal static Dictionary PacketTypeMap = new Dictionary(); + + internal PacketType _packetType; + internal ByteEncoder byteEncoder = new ByteEncoder(); + + + public byte[] GetBytes() => byteEncoder.ToArray(); + + public PacketType GetPacketType() + { + if (_packetType == PacketType.Unknown) + { + Type type = GetType(); + _packetType = PacketTypeMap.FirstOrDefault(x => x.Value == type).Key; + if (_packetType == PacketType.Unknown) + { + throw new Exception("Cannot find packetType for " + type.Name); + } + } + return _packetType; + } + + public static void RegisterPacketTypes() + { + if (_packetTypesRegistered) + { + return; + } + _packetTypesRegistered = true; + 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.Add((PacketType)packetType, type); + } + } + } + public static NetworkMessage ParsePacket(Packet packet) + { + // 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) }); + // create an instance of the class + if (constructor == null) + { + throw new Exception("No constructor found for type " + type.Name); + } + NetworkMessage networkMessage = (NetworkMessage)constructor.Invoke(new object[] { packet }); + return networkMessage; + } + + + + public Packet GetPacket(int id, ulong senderId, ulong receiverId) + { + PacketInfo packetInfo = new PacketInfo(id, senderId, receiverId, GetPacketType()); + Packet packet = new Packet(packetInfo, GetBytes()); + return packet; + } + public void Broadcast() + { + Send(0); + } + public void Send(ulong receiverId) + { + int PacketId = Packet.GenerateId(); + ulong senderId = BoneSync.lobby.GetLocalId(); + Packet packet = GetPacket(PacketId, senderId, receiverId); + BoneSync.transport.Send(packet); + } + + public abstract void Execute(); + + } + public class NetworkMessageTests + { + [Fact] + public void TestRegisterPackets() + { + NetworkMessage.RegisterPacketTypes(); + Assert.NotEmpty(NetworkMessage.PacketTypeMap); + } + } +} diff --git a/BoneSync/Networking/Packet.cs b/BoneSync/Networking/Packet.cs index e61305e..4a55bd4 100644 --- a/BoneSync/Networking/Packet.cs +++ b/BoneSync/Networking/Packet.cs @@ -3,22 +3,19 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Xunit; namespace BoneSync.Networking { - public enum PacketType - { - LobbyInfo = 0, - CharacterInfo = 1, - } - internal struct PacketInfo + + public struct PacketInfo { public int id; - public int senderId; + public ulong senderId; public ulong receiverId; public PacketType packetType; - public PacketInfo(int id, int senderId, ulong receiverId, PacketType packetType) + public PacketInfo(int id, ulong senderId, ulong receiverId, PacketType packetType) { this.id = id; this.senderId = senderId; @@ -26,30 +23,29 @@ namespace BoneSync.Networking this.packetType = packetType; } } - internal class Packet + public class Packet { - public PacketInfo Info { get => _packetInfo; private set => _packetInfo = value; } - private PacketInfo _packetInfo; - private byte[] dataBytes; + private PacketInfo _packetInfo; + private byte[] _dataBytes; private PacketInfo packetInfo; public Packet(PacketInfo packetInfo, byte[] data) { - this.packetInfo = packetInfo; - this.dataBytes = data; + _packetInfo = packetInfo; + _dataBytes = data; } public static Packet FromBytes(byte[] bytes) { ByteEncoder byteEncoder = new ByteEncoder(); byteEncoder.WriteBytes(bytes); - PacketInfo packetInfo = new PacketInfo(byteEncoder.ReadInt(), byteEncoder.ReadInt(), byteEncoder.ReadUlong(), (PacketType)byteEncoder.ReadInt()); + PacketInfo packetInfo = new PacketInfo(byteEncoder.ReadInt(), byteEncoder.ReadUlong(), byteEncoder.ReadUlong(), (PacketType)byteEncoder.ReadInt()); return new Packet(packetInfo, byteEncoder.ToArray()); } @@ -57,15 +53,41 @@ namespace BoneSync.Networking { ByteEncoder byteEncoder = new ByteEncoder(); byteEncoder.WriteInt(packetInfo.id); - byteEncoder.WriteInt(packetInfo.senderId); + byteEncoder.WriteUlong(packetInfo.senderId); byteEncoder.WriteUlong(packetInfo.receiverId); byteEncoder.WriteInt((int)packetInfo.packetType); - byteEncoder.WriteBytes(dataBytes); + byteEncoder.WriteBytes(_dataBytes); return byteEncoder.ToArray(); } - public byte[] Data => dataBytes; + public byte[] Data => _dataBytes; + public static int GenerateId() + { + return new Random().Next(); + } + } + public class PacketTests + { + [Fact] + public void TestPacketParsing() + { + Random random = new Random(); + + byte[] randomDataBytes = new byte[1024]; + random.NextBytes(randomDataBytes); + PacketInfo originalPacketInfo = new PacketInfo(1, 2, 3, PacketType.LobbyInfo); + Packet originalPacket = new Packet(originalPacketInfo, randomDataBytes); + + byte[] bytes = originalPacket.ToBytes(); + + Packet parsedPacket = Packet.FromBytes(bytes); + Assert.Equal(originalPacket.Info, parsedPacket.Info); + Assert.Equal(originalPacket.Data, parsedPacket.Data); + + Assert.Equal(randomDataBytes, parsedPacket.Data); + Assert.NotEqual(bytes, randomDataBytes); + } } } diff --git a/BoneSync/Networking/PacketTypes.cs b/BoneSync/Networking/PacketTypes.cs new file mode 100644 index 0000000..05e3cbc --- /dev/null +++ b/BoneSync/Networking/PacketTypes.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BoneSync.Networking +{ + public enum PacketType + { + Unknown = 0, + LobbyInfo = 1, + CharacterInfo = 2, + } +} diff --git a/BoneSync/Networking/Transport/TransportBase.cs b/BoneSync/Networking/Transport/TransportBase.cs index f10c39f..891e1d4 100644 --- a/BoneSync/Networking/Transport/TransportBase.cs +++ b/BoneSync/Networking/Transport/TransportBase.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace BoneSync.Networking.Transport { - internal abstract class TransportBase + public abstract class TransportBase { public abstract void Send(Packet packet); public abstract bool Tick(); diff --git a/BoneSync/packages.config b/BoneSync/packages.config new file mode 100644 index 0000000..a400c1b --- /dev/null +++ b/BoneSync/packages.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file