This commit is contained in:
2025-02-25 13:09:38 +02:00
10 changed files with 141 additions and 73 deletions

View File

@@ -25,8 +25,7 @@ namespace BoneSync
SteamLobbyManager steamLobbyManager = new SteamLobbyManager();
lobby = steamLobbyManager;
transport = new SteamTransport(steamLobbyManager);
transport = new SteamTransport();
}

View File

@@ -11,15 +11,11 @@ namespace BoneSync.Networking.LobbyManager
{
public abstract class LobbyManager
{
public abstract ulong[] GetPeers();
public abstract ulong GetLobbyId();
public abstract ulong GetHostId();
public abstract ulong GetLocalId();
public virtual void CreateLobby()
{
}
public virtual void Initialize()
{

View File

@@ -10,36 +10,52 @@ namespace BoneSync.Networking.LobbyManager
{
internal class SteamLobbyManager : LobbyManager
{
public SteamId[] Peers;
private Lobby lobbyInstance;
public override ulong GetLocalId()
private Lobby _lobbyInstance;
public Friend[] LobbyMembers
{
return SteamClient.SteamId.Value;
get;
private set;
}
public override ulong GetHostId()
public SteamId[] steamIds
{
return lobbyInstance.Owner.Id;
}
public override ulong GetLobbyId()
{
return lobbyInstance.Id;
}
public override void CreateLobby()
{
get;
private set;
}
public SteamId? getPeerFromId(ulong id)
public override ulong[] GetPeers() => steamIds.Select(x => x.Value).ToArray();
public override ulong GetLocalId() => SteamClient.SteamId.Value;
public override ulong GetHostId() => _lobbyInstance.Owner.Id.Value;
public override ulong GetLobbyId() => _lobbyInstance.Id.Value;
private void UpdateLobbyData()
{
foreach (SteamId peer in Peers)
LobbyMembers = _lobbyInstance.Members.ToArray();
steamIds = LobbyMembers.Select(x => x.Id).ToArray();
}
public override void Initialize()
{
SteamMatchmaking.OnLobbyCreated += (Result result, Lobby lobby) =>
{
if (peer.Value == id)
_lobbyInstance = lobby;
UpdateLobbyData();
};
SteamMatchmaking.OnLobbyEntered += (Lobby lobby) =>
{
_lobbyInstance = lobby;
UpdateLobbyData();
};
SteamMatchmaking.OnLobbyMemberLeave += (Lobby lobby, Friend friend) =>
{
if (friend.Id == SteamClient.SteamId)
{
return peer;
_lobbyInstance = new Lobby();
}
}
return null;
UpdateLobbyData();
};
SteamMatchmaking.OnLobbyMemberJoined += (Lobby lobby, Friend friend) =>
{
UpdateLobbyData();
};
}
}

View File

@@ -22,7 +22,7 @@ namespace BoneSync.Networking.Messages
public abstract class NetworkMessage
{
private static bool _packetTypesRegistered = false;
private static bool PacketTypesRegistered => PacketTypeMap.Count > 0;
internal static Dictionary<PacketType, Type> PacketTypeMap = new Dictionary<PacketType, Type>();
@@ -34,25 +34,26 @@ namespace BoneSync.Networking.Messages
public PacketType GetPacketType()
{
RegisterPacketTypes();
if (_packetType == PacketType.Unknown)
{
Type type = GetType();
_packetType = PacketTypeMap.FirstOrDefault(x => x.Value == type).Key;
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 " + type.Name);
throw new Exception("Cannot find packetType for " + typeName);
}
_packetType = found.Key;
}
return _packetType;
}
public static void RegisterPacketTypes()
{
if (_packetTypesRegistered)
if (PacketTypesRegistered)
{
return;
}
_packetTypesRegistered = true;
Assembly assembly = Assembly.GetExecutingAssembly();
Type[] types = assembly.GetTypes();
foreach (Type type in types)
@@ -61,7 +62,7 @@ namespace BoneSync.Networking.Messages
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);
PacketTypeMap[packetType.Value] = type;
}
}
}
@@ -75,12 +76,7 @@ namespace BoneSync.Networking.Messages
}
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);
}
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 });
return networkMessage;
}
@@ -95,7 +91,7 @@ namespace BoneSync.Networking.Messages
}
public void Broadcast()
{
Send(0);
Send(TransportBase.BORADCAST_ID);
}
public void Send(ulong receiverId)
{

View File

@@ -1,4 +1,5 @@
using System;
using BoneSync.Networking.Messages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -33,7 +34,6 @@ namespace BoneSync.Networking
private PacketInfo _packetInfo;
private byte[] _dataBytes;
private PacketInfo packetInfo;
public Packet(PacketInfo packetInfo, byte[] data)
{
@@ -45,21 +45,34 @@ namespace BoneSync.Networking
{
ByteEncoder byteEncoder = new ByteEncoder();
byteEncoder.WriteBytes(bytes);
PacketInfo packetInfo = new PacketInfo(byteEncoder.ReadInt(), byteEncoder.ReadUlong(), byteEncoder.ReadUlong(), (PacketType)byteEncoder.ReadInt());
int packetType = byteEncoder.ReadInt();
int id = byteEncoder.ReadInt();
ulong senderId = byteEncoder.ReadUlong();
ulong receiverId = byteEncoder.ReadUlong();
PacketInfo packetInfo = new PacketInfo(id, senderId, receiverId, (PacketType)packetType);
return new Packet(packetInfo, byteEncoder.ToArray());
}
public byte[] ToBytes()
{
ByteEncoder byteEncoder = new ByteEncoder();
byteEncoder.WriteInt(packetInfo.id);
byteEncoder.WriteUlong(packetInfo.senderId);
byteEncoder.WriteUlong(packetInfo.receiverId);
byteEncoder.WriteInt((int)packetInfo.packetType);
byteEncoder.WriteInt((int)_packetInfo.packetType);
byteEncoder.WriteInt(_packetInfo.id);
byteEncoder.WriteUlong(_packetInfo.senderId);
byteEncoder.WriteUlong(_packetInfo.receiverId);
byteEncoder.WriteBytes(_dataBytes);
return byteEncoder.ToArray();
}
public static bool OnPacketReceived(Packet packet)
{
NetworkMessage networkMessage = NetworkMessage.ParsePacket(packet);
networkMessage.Execute();
return true;
}
public byte[] Data => _dataBytes;
public static int GenerateId()
@@ -77,15 +90,21 @@ namespace BoneSync.Networking
byte[] randomDataBytes = new byte[1024];
random.NextBytes(randomDataBytes);
PacketInfo originalPacketInfo = new PacketInfo(1, 2, 3, PacketType.LobbyInfo);
PacketInfo originalPacketInfo = new PacketInfo(Packet.GenerateId(), 1, 2, 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.Info.id, parsedPacket.Info.id);
Assert.Equal(originalPacket.Info.senderId, parsedPacket.Info.senderId);
Assert.Equal(originalPacket.Info.receiverId, parsedPacket.Info.receiverId);
Assert.Equal(originalPacket.Info.packetType, parsedPacket.Info.packetType);
Assert.Equal(originalPacket.Data, parsedPacket.Data);
Assert.Equal(randomDataBytes, parsedPacket.Data);
Assert.NotEqual(bytes, randomDataBytes);
}

View File

@@ -1,62 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using BoneSync.Networking.LobbyManager;
using Facepunch.Steamworks;
using Facepunch.Steamworks.Data;
using MelonLoader;
namespace BoneSync.Networking.Transport
{
internal class SteamTransport : TransportBase
{
SteamLobbyManager _instance;
public override event Action<Packet> OnReceive;
public SteamTransport(SteamLobbyManager lobbyManager)
public SteamTransport()
{
SteamNetworking.OnP2PSessionRequest += OnP2PSessionRequest;
}
private void OnP2PSessionRequest(SteamId steamId)
{
SteamNetworking.AcceptP2PSessionWithUser(steamId);
MelonLogger.Msg("P2P Request from " + steamId);
if (BoneSync.lobby.GetPeers().Contains(steamId))
SteamNetworking.AcceptP2PSessionWithUser(steamId);
}
private void ProcessPacket(P2Packet steamPacket)
{
Packet packet = Packet.FromBytes(steamPacket.Data);
bool isTarget = packet.Info.receiverId == 0 || packet.Info.receiverId == _instance.GetLocalId();
bool isTarget = packet.Info.receiverId == BORADCAST_ID || packet.Info.receiverId == BoneSync.lobby.GetLocalId();
if (isTarget)
{
OnReceive?.Invoke(packet);
Packet.OnPacketReceived(packet);
}
}
public override bool Tick()
{
int processed = 0;
while (SteamNetworking.IsP2PPacketAvailable())
{
P2Packet packet = (P2Packet)SteamNetworking.ReadP2PPacket();
ProcessPacket(packet);
P2Packet? packet = SteamNetworking.ReadP2PPacket();
if (!packet.HasValue) continue;
ProcessPacket(packet.Value);
processed++;
}
return true;
return processed > 0;
}
public void SendP2P(SteamId peer, byte[] data)
{
if (peer == BoneSync.lobby.GetLocalId())
{
//MelonLogger.Msg("Trying to send packet to self");
return;
}
SteamNetworking.SendP2PPacket(peer, data, data.Length, 0, P2PSend.Reliable);
}
public override void Send(Packet packet)
{
if (packet.Info.receiverId == 0)
LobbyManager.LobbyManager _instance = BoneSync.lobby;
if (_instance == null)
{
foreach (SteamId peer in _instance.Peers)
MelonLogger.Msg("Lobby instance is null");
return;
}
if (packet.Info.receiverId == BORADCAST_ID)
{
foreach (SteamId peer in _instance.GetPeers())
{
byte[] packetBytes = packet.ToBytes();
SteamNetworking.SendP2PPacket(peer, packetBytes, packetBytes.Length, 0, P2PSend.Reliable);
SendP2P(peer, packet.ToBytes());
}
return;
}
else
{
SteamId peer = (SteamId)_instance.getPeerFromId(packet.Info.receiverId);
SteamNetworking.SendP2PPacket(peer, packet.ToBytes(), packet.ToBytes().Length, 0, P2PSend.Reliable);
SendP2P(packet.Info.receiverId, packet.ToBytes());
}
}

View File

@@ -8,9 +8,8 @@ namespace BoneSync.Networking.Transport
{
public abstract class TransportBase
{
public const int BORADCAST_ID = 0;
public abstract void Send(Packet packet);
public abstract bool Tick();
public abstract event Action<Packet> OnReceive;
}
}

View File

@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net472" />
<package id="MSTest.Analyzers" version="3.8.2" targetFramework="net472" developmentDependency="true" />
<package id="MSTest.TestFramework" version="3.8.2" targetFramework="net472" />
<package id="System.Collections.Immutable" version="1.5.0" targetFramework="net472" />
<package id="System.Reflection.Metadata" version="1.6.0" targetFramework="net472" />
<package id="xunit" version="2.9.3" targetFramework="net472" />
<package id="xunit.abstractions" version="2.0.3" targetFramework="net472" />
<package id="xunit.analyzers" version="1.18.0" targetFramework="net472" developmentDependency="true" />
<package id="xunit.analyzers" version="1.20.0" targetFramework="net472" developmentDependency="true" />
<package id="xunit.assert" version="2.9.3" targetFramework="net472" />
<package id="xunit.core" version="2.9.3" targetFramework="net472" />
<package id="xunit.extensibility.core" version="2.9.3" targetFramework="net472" />

View File

@@ -24,11 +24,15 @@
<RepositoryUrl>https://github.com/Facepunch/Facepunch.Steamworks.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<None Remove="steam_api.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="steam_api64.dll" PackagePath="\content\" Pack="true" PackageCopyToOutput="true">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="steam_api64.dll" PackagePath="\content\" Pack="true" PackageCopyToOutput="true">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<None Include="Facepunch.Steamworks.jpg" Pack="true" PackagePath="\" />
</ItemGroup>

17
testEnvironments.json Normal file
View File

@@ -0,0 +1,17 @@
{
"version": "1",
"environments": [
// See https://aka.ms/remotetesting for more details
// about how to configure remote environments.
//{
// "name": "WSL Ubuntu",
// "type": "wsl",
// "wslDistribution": "Ubuntu"
//},
//{
// "name": "Docker dotnet/sdk",
// "type": "docker",
// "dockerImage": "mcr.microsoft.com/dotnet/sdk"
//}
]
}