Compare commits

...

3 Commits

Author SHA1 Message Date
6d3f9f8afb packet parsing optimisations 2026-02-17 16:35:20 +02:00
1fa8516ebe byte encoder 2026-02-17 15:33:20 +02:00
3dbe62a64d more sync fixes 2026-02-17 15:31:23 +02:00
10 changed files with 147 additions and 53 deletions

View File

@@ -66,7 +66,7 @@ namespace BoneSync.Data
Pool pool = GetPool(spawnableObject); Pool pool = GetPool(spawnableObject);
if (pool == null) if (pool == null)
{ {
SyncLogger.Warning("[SpawnPooleeAndMakeSyncable] Failed to find pool: " + spawnableObject.title); SyncLogger.Warning("Failed to find pool: " + spawnableObject.title);
return null; return null;
} }
Poolee poolee = CallPatchedMethods.InstantiatePoolee(pool, position, rotation, pool.Prefab.transform.localScale); Poolee poolee = CallPatchedMethods.InstantiatePoolee(pool, position, rotation, pool.Prefab.transform.localScale);

View File

@@ -31,7 +31,7 @@ namespace BoneSync
public static LobbyManager lobby; public static LobbyManager lobby;
public static TransportBase transport; public static ITransportBase transport;
public override void OnApplicationStart() public override void OnApplicationStart()
{ {
SteamClient.Init(823500, true); SteamClient.Init(823500, true);
@@ -116,7 +116,7 @@ namespace BoneSync
IsConnected = lobby.IsConnected(); IsConnected = lobby.IsConnected();
transport.Tick(); transport.Tick();
Packet.TryCatchup(); Packet.TryCatchup();
PlayerRig.Tick(); PlayerRig.Tick(Time.fixedDeltaTime);
} }
public override void OnLateUpdate() public override void OnLateUpdate()

View File

@@ -28,7 +28,7 @@ namespace BoneSync.Networking.Messages
public SimpleEventType eventType; public SimpleEventType eventType;
public byte index; public byte index;
public byte length; public byte length;
//public object[] args; public byte[] extraData;
} }
[PacketType(PacketType.SimpleObjectEventSync), PacketReliability(PacketReliability.ReliableFast), PacketCatchup(20)] [PacketType(PacketType.SimpleObjectEventSync), PacketReliability(PacketReliability.ReliableFast), PacketCatchup(20)]
public class SimpleSyncableEventMessage : NetworkMessage public class SimpleSyncableEventMessage : NetworkMessage
@@ -41,6 +41,12 @@ namespace BoneSync.Networking.Messages
byteEncoder.WriteByte((byte)simpleSyncableEvent.eventType); byteEncoder.WriteByte((byte)simpleSyncableEvent.eventType);
byteEncoder.WriteByte(simpleSyncableEvent.index); byteEncoder.WriteByte(simpleSyncableEvent.index);
byteEncoder.WriteByte(simpleSyncableEvent.length); byteEncoder.WriteByte(simpleSyncableEvent.length);
if (simpleSyncableEvent.extraData == null)
{
simpleSyncableEvent.extraData = new byte[0];
}
byteEncoder.WriteByte((byte)simpleSyncableEvent.extraData.Length);
byteEncoder.WriteBytes(simpleSyncableEvent.extraData);
} }
public SimpleSyncableEventMessage(Packet packet) public SimpleSyncableEventMessage(Packet packet)
@@ -50,6 +56,8 @@ namespace BoneSync.Networking.Messages
eventData.eventType = (SimpleEventType)byteEncoder.ReadByte(); eventData.eventType = (SimpleEventType)byteEncoder.ReadByte();
eventData.index = byteEncoder.ReadByte(); eventData.index = byteEncoder.ReadByte();
eventData.length = byteEncoder.ReadByte(); eventData.length = byteEncoder.ReadByte();
ushort extraDataLength = byteEncoder.ReadByte();
eventData.extraData = byteEncoder.ReadBytes(extraDataLength);
} }
public override void Execute() public override void Execute()

View File

@@ -1,14 +1,15 @@
using BoneSync.Data; using System;
using BoneSync.Networking.Transport;
using BoneSync.Sync;
using MelonLoader;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BoneSync.Data;
using BoneSync.Networking.Transport;
using BoneSync.Sync;
using MelonLoader;
using Oculus.Platform;
#if TEST #if TEST
using Xunit; using Xunit;
#endif #endif
@@ -137,19 +138,34 @@ namespace BoneSync.Networking
} }
} }
} }
public static NetworkMessage ParsePacket(Packet packet)
private static readonly Dictionary<PacketType, ConstructorInfo> ConstructorCache = new Dictionary<PacketType, ConstructorInfo>();
public static ConstructorInfo GetConstructor(Packet packet)
{ {
//SyncLogger.Msg("Received packet of type " + packet.Info.packetType + " from " + packet.Info.senderId + " Length: " + packet.Data.Length); if (ConstructorCache.ContainsKey(packet.Info.packetType))
// find a class that can parse this packet using Reflection {
// and return it return ConstructorCache[packet.Info.packetType];
}
if (!PacketTypeMap.ContainsKey(packet.Info.packetType)) if (!PacketTypeMap.ContainsKey(packet.Info.packetType))
{ {
throw new Exception("No class found for packet type '" + packet.Info.packetType+"'"); throw new Exception("No class found for packet type '" + packet.Info.packetType + "'");
} }
Type type = PacketTypeMap[packet.Info.packetType]; Type type = PacketTypeMap[packet.Info.packetType];
// get the constructor that takes a Packet // 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); ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(Packet) }) ?? throw new Exception("No constructor found for type " + type.Name);
ConstructorCache[packet.Info.packetType] = constructor;
return constructor;
}
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
ConstructorInfo constructor = GetConstructor(packet);
NetworkMessage networkMessage = (NetworkMessage)constructor.Invoke(new object[] { packet }); NetworkMessage networkMessage = (NetworkMessage)constructor.Invoke(new object[] { packet });
networkMessage.senderId = packet.Info.senderId; networkMessage.senderId = packet.Info.senderId;
return networkMessage; return networkMessage;

View File

@@ -13,7 +13,7 @@ using Oculus.Platform;
namespace BoneSync.Networking.Transport namespace BoneSync.Networking.Transport
{ {
internal class SteamTransport : TransportBase internal class SteamTransport : ITransportBase
{ {
public SteamTransport() public SteamTransport()

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace BoneSync.Networking.Transport namespace BoneSync.Networking.Transport
{ {
public interface TransportBase public interface ITransportBase
{ {
ulong BROADCAST_ID { get; } ulong BROADCAST_ID { get; }
void Send(Packet packet); void Send(Packet packet);

View File

@@ -55,11 +55,17 @@ namespace BoneSync.Player
SyncLogger.Msg("Loaded playerRepBundle success"); SyncLogger.Msg("Loaded playerRepBundle success");
} }
public static void Tick() public static void Tick(float deltaTime)
{ {
foreach (PlayerRig playerRig in _playerRigs.Values) foreach (PlayerRig playerRig in _playerRigs.Values)
{ {
playerRig.UpdateIK(); try {
playerRig.UpdatePlayerTransforms(deltaTime);
playerRig.UpdateIK(deltaTime);
} catch (Exception e)
{
SyncLogger.Error("Error updating player rig for " + playerRig._ownerId);
}
} }
} }
@@ -78,15 +84,53 @@ namespace BoneSync.Player
characterAnimationManager.ApplyFingerCurl(handedness, 1f - fingerCurl.thumb, 1f - fingerCurl.index, 1f - fingerCurl.middle, 1f - fingerCurl.ring, 1f - fingerCurl.pinky); characterAnimationManager.ApplyFingerCurl(handedness, 1f - fingerCurl.thumb, 1f - fingerCurl.index, 1f - fingerCurl.middle, 1f - fingerCurl.ring, 1f - fingerCurl.pinky);
} }
private Vector3 _targetRootPos = Vector3.zero;
private Vector3 _targetHeadPos = Vector3.zero;
private Quaternion _targetHeadRot = Quaternion.identity;
private Vector3 _targetLeftHandPos = Vector3.zero;
private Quaternion _targetLeftHandRot = Quaternion.identity;
private Vector3 _targetRightHandPos = Vector3.zero;
private Quaternion _targetRightHandRot = Quaternion.identity;
private void UpdatePlayerTransforms(float deltaTime)
{
float step = deltaTime * 5f; // smoothing factor
playerRig.transform.position = Vector3.Lerp(playerRig.transform.position, _targetRootPos, step);
headTransform.position = Vector3.Lerp(headTransform.position, _targetHeadPos, step);
leftHandTransform.position = Vector3.Lerp(leftHandTransform.position, _targetLeftHandPos, step);
rightHandTransform.position = Vector3.Lerp(rightHandTransform.position, _targetRightHandPos, step);
headTransform.rotation = Quaternion.Lerp(headTransform.rotation, _targetHeadRot, step);
leftHandTransform.rotation = Quaternion.Lerp(leftHandTransform.rotation, _targetLeftHandRot, step);
rightHandTransform.rotation = Quaternion.Lerp(rightHandTransform.rotation, _targetRightHandRot, step);
}
public void UpdatePlayerSync(PlayerSyncInfo playerSyncInfo) public void UpdatePlayerSync(PlayerSyncInfo playerSyncInfo)
{ {
EnsurePlayerRig(); EnsurePlayerRig();
//SyncLogger.Msg("Updating player sync for " + _ownerId); //SyncLogger.Msg("Updating player sync for " + _ownerId);
playerRig.transform.position = playerSyncInfo.rootPos; //playerRig.transform.position = playerSyncInfo.rootPos;
_targetRootPos = playerSyncInfo.rootPos;
headTransform.ApplySimpleTransform(playerSyncInfo.headPos); _targetHeadRot = playerSyncInfo.headPos.rotation;
leftHandTransform.ApplySimpleTransform(playerSyncInfo.leftHandPos); _targetHeadPos = playerSyncInfo.headPos.position;
rightHandTransform.ApplySimpleTransform(playerSyncInfo.rightHandPos);
_targetLeftHandPos = playerSyncInfo.leftHandPos.position;
_targetLeftHandRot = playerSyncInfo.leftHandPos.rotation;
_targetRightHandPos = playerSyncInfo.rightHandPos.position;
_targetRightHandRot = playerSyncInfo.rightHandPos.rotation;
//headTransform.ApplySimpleTransform(playerSyncInfo.headPos);
//leftHandTransform.ApplySimpleTransform(playerSyncInfo.leftHandPos);
//rightHandTransform.ApplySimpleTransform(playerSyncInfo.rightHandPos);
SetFingerCurl(Handedness.LEFT, playerSyncInfo.leftHandFingerCurl); SetFingerCurl(Handedness.LEFT, playerSyncInfo.leftHandFingerCurl);
SetFingerCurl(Handedness.RIGHT, playerSyncInfo.rightHandFingerCurl); SetFingerCurl(Handedness.RIGHT, playerSyncInfo.rightHandFingerCurl);
@@ -96,6 +140,8 @@ namespace BoneSync.Player
UpdatePoseRadius(Handedness.LEFT, playerSyncInfo.poseRadiusLeft); UpdatePoseRadius(Handedness.LEFT, playerSyncInfo.poseRadiusLeft);
UpdatePoseRadius(Handedness.RIGHT, playerSyncInfo.poseRadiusRight); UpdatePoseRadius(Handedness.RIGHT, playerSyncInfo.poseRadiusRight);
UpdatePlayerTransforms( 1f / RIG_SYNC_FPS); // immediately update transforms to avoid lag
} }
public void UpdatePose(Handedness hand, int index) public void UpdatePose(Handedness hand, int index)
@@ -232,25 +278,18 @@ namespace BoneSync.Player
} }
public void UpdateIK() public void UpdateIK(float deltaTime)
{ {
// Catch errors so other players arent broken // Catch errors so other players arent broken
try
{
if (body == null || characterAnimationManager == null || animator == null || playerRig == null) return;
animator.Update(Time.fixedDeltaTime); if (body == null || characterAnimationManager == null || animator == null || playerRig == null) return;
characterAnimationManager.OnLateUpdate();
Vector3 repInputVel = Vector3.zero;
body.FullBodyUpdate(repInputVel, Vector3.zero); animator.Update(deltaTime);
body.ArtToBlender.UpdateBlender(); characterAnimationManager.OnLateUpdate();
} Vector3 repInputVel = Vector3.zero;
catch (Exception e)
{ body.FullBodyUpdate(repInputVel, Vector3.zero);
SyncLogger.Warning("Failed to update player rig " + _ownerId); body.ArtToBlender.UpdateBlender();
SyncLogger.Warning(e.ToString());
}
} }
@@ -263,7 +302,7 @@ namespace BoneSync.Player
public static void OnPlayerSync(PlayerSyncMessage playerSyncMessage) public static void OnPlayerSync(PlayerSyncMessage playerSyncMessage)
{ {
//SyncLogger.Msg("Player Sync Received " + playerSyncMessage.senderId); //SyncLogger.Msg("Player Sync Received " + playerSyncMessage.senderId);
PlayerRig playerRig = PlayerRig.GetPlayerRig(playerSyncMessage.senderId); PlayerRig playerRig = GetPlayerRig(playerSyncMessage.senderId);
if (playerRig == null) return; if (playerRig == null) return;
playerRig.UpdatePlayerSync(playerSyncMessage.playerSyncInfo); playerRig.UpdatePlayerSync(playerSyncMessage.playerSyncInfo);

View File

@@ -76,13 +76,13 @@ namespace BoneSync.Sync.Components
get; get;
} }
private ushort _syncId; private ushort _syncId;
private float _lastSyncTime; private float _lastObjectSyncTime;
private bool _isInHolster; private bool _isInHolster;
private bool _attemptedRegister; private bool _attemptedRegister;
public bool Registered => _syncId != 0; public bool Registered => _syncId != 0;
public bool isStale => Time.realtimeSinceStartup - _lastSyncTime > 30f; public bool isStale => Time.realtimeSinceStartup - _lastObjectSyncTime > 30f;
public bool isOwner => _ownerId == BoneSync.lobby.GetLocalId(); public bool isOwner => _ownerId == BoneSync.lobby.GetLocalId();
public bool isValid public bool isValid

View File

@@ -20,7 +20,7 @@ namespace BoneSync.Sync.Components
private Queue<byte[]> _simpleEventQueue = new Queue<byte[]>(); private Queue<byte[]> _simpleEventQueue = new Queue<byte[]>();
private void SendObjectSync() private void SendObjectSync()
{ {
_lastSyncTime = Time.realtimeSinceStartup; _lastObjectSyncTime = Time.realtimeSinceStartup;
ObjectSync.SendObjectSyncMessage(this); ObjectSync.SendObjectSyncMessage(this);
} }
private IEnumerator OwnerCoroutineAsync() private IEnumerator OwnerCoroutineAsync()
@@ -164,11 +164,11 @@ namespace BoneSync.Sync.Components
return true; return true;
} }
public bool AddSimpleEventToQueue(SimpleEventType eType, byte index = 0, byte len = 0) public bool AddSimpleEventToQueue(SimpleEventType eType, byte index = 0, byte len = 0, byte[] extraData = null)
{ {
RegisterSyncable(); RegisterSyncable();
if (!isOwner && Registered) { return false; } if (!isOwner && Registered) { return false; }
_simpleEventQueue.Enqueue(new byte[] { (byte)eType, index, len }); _simpleEventQueue.Enqueue(new byte[] { (byte)eType, index, len }.Concat(extraData ?? new byte[0]).ToArray());
TryCatchUpSimpleEvents(); TryCatchUpSimpleEvents();
return true; return true;
} }
@@ -184,11 +184,11 @@ namespace BoneSync.Sync.Components
if (_simpleEventQueue.Count > 0 && Registered && isOwner) if (_simpleEventQueue.Count > 0 && Registered && isOwner)
{ {
byte[] eventData = _simpleEventQueue.Dequeue(); byte[] eventData = _simpleEventQueue.Dequeue();
_SendSimpleEvent((SimpleEventType)eventData[0], eventData[1], eventData[2]); _SendSimpleEvent((SimpleEventType)eventData[0], eventData[1], eventData[2], eventData.Skip(3).ToArray());
TryCatchUpSimpleEvents(); TryCatchUpSimpleEvents();
} }
} }
private void _SendSimpleEvent(SimpleEventType eType, byte index = 0, byte len = 0) private void _SendSimpleEvent(SimpleEventType eType, byte index = 0, byte len = 0, byte[] extraData = null)
{ {
SyncLogger.Msg("Sending simple event: " + eType); SyncLogger.Msg("Sending simple event: " + eType);
SimpleSyncableEvent data = new SimpleSyncableEvent() SimpleSyncableEvent data = new SimpleSyncableEvent()
@@ -196,7 +196,8 @@ namespace BoneSync.Sync.Components
syncId = _syncId, syncId = _syncId,
eventType = eType, eventType = eType,
index = index, index = index,
length = len length = len,
extraData = extraData
}; };
SimpleSyncableEventMessage simpleSyncEvent = new SimpleSyncableEventMessage(data); SimpleSyncableEventMessage simpleSyncEvent = new SimpleSyncableEventMessage(data);

View File

@@ -23,7 +23,10 @@ namespace BoneSync.Sync.Components
{ {
SyncLogger.Msg("ButtonToggle:" + eventType + " " + toggle.transform.GetPath()); SyncLogger.Msg("ButtonToggle:" + eventType + " " + toggle.transform.GetPath());
byte index = (byte)Array.IndexOf(buttonToggles, toggle); byte index = (byte)Array.IndexOf(buttonToggles, toggle);
return AddSimpleEventToQueue(eventType, index, (byte)buttonToggles.Length); bool isPressed = toggle._isPressed;
bool hasBeenPressed = toggle._hasBeenPressed;
byte[] buttonState = BitPacking.PackBits(new bool[] { isPressed, hasBeenPressed });
return AddSimpleEventToQueue(eventType, index, (byte)buttonToggles.Length, buttonState);
} }
bool ButtonOnPress(ButtonToggle toggle) bool ButtonOnPress(ButtonToggle toggle)
{ {
@@ -144,7 +147,7 @@ namespace BoneSync.Sync.Components
public void ApplyObjectSyncTransforms(ObjectSyncTransform[] objectSyncTransforms) public void ApplyObjectSyncTransforms(ObjectSyncTransform[] objectSyncTransforms)
{ {
_lastSyncTime = Time.realtimeSinceStartup; _lastObjectSyncTime = Time.realtimeSinceStartup;
if (IsPlugged()) { return; } if (IsPlugged()) { return; }
if (objectSyncTransforms.Length != rigidbodies.Length) if (objectSyncTransforms.Length != rigidbodies.Length)
{ {
@@ -234,14 +237,32 @@ namespace BoneSync.Sync.Components
if (len != eventData.length) { SyncLogger.Warning("ButtonPress length mismatch: " + len + " != " + eventData.length); } if (len != eventData.length) { SyncLogger.Warning("ButtonPress length mismatch: " + len + " != " + eventData.length); }
if (index < buttonToggles.Length) if (index < buttonToggles.Length)
{ {
buttonToggles[index]?.onPress.BypassPatchInvoke(); ButtonToggle toggle = buttonToggles[index];
if (toggle == null)
{
SyncLogger.Warning("ButtonToggle at index " + index + " is null");
break;
}
bool[] buttonState = BitPacking.UnpackBits(eventData.extraData, 2);
toggle._isPressed = buttonState[0];
toggle._hasBeenPressed = buttonState[1];
toggle.onPress.BypassPatchInvoke();
} }
break; break;
case SimpleEventType.OnButtonRelease: case SimpleEventType.OnButtonRelease:
if (len != eventData.length) { SyncLogger.Warning("ButtonPress length mismatch: " + len + " != " + eventData.length); } if (len != eventData.length) { SyncLogger.Warning("ButtonPress length mismatch: " + len + " != " + eventData.length); }
if (index < buttonToggles.Length) if (index < buttonToggles.Length)
{ {
buttonToggles[index]?.onDepress.BypassPatchInvoke(); ButtonToggle toggle = buttonToggles[index];
if (toggle == null)
{
SyncLogger.Warning("ButtonToggle at index " + index + " is null");
break;
}
bool[] buttonState = BitPacking.UnpackBits(eventData.extraData, 2);
toggle._isPressed = buttonState[0];
toggle._hasBeenPressed = buttonState[1];
toggle.onDepress.BypassPatchInvoke();
} }
break; break;
case SimpleEventType.OnButtonOneShot: case SimpleEventType.OnButtonOneShot:
@@ -250,7 +271,16 @@ namespace BoneSync.Sync.Components
} }
if (index < buttonToggles.Length) if (index < buttonToggles.Length)
{ {
buttonToggles[index]?.onPressOneShot.BypassPatchInvoke(); ButtonToggle toggle = buttonToggles[index];
if (toggle == null)
{
SyncLogger.Warning("ButtonToggle at index " + index + " is null");
break;
}
bool[] buttonState = BitPacking.UnpackBits(eventData.extraData, 2);
toggle._isPressed = buttonState[0];
toggle._hasBeenPressed = buttonState[1];
toggle.onPressOneShot.BypassPatchInvoke();
} }
break; break;
case SimpleEventType.CartGo: case SimpleEventType.CartGo: