Basic object sync

This commit is contained in:
2025-02-27 21:16:52 +02:00
parent 43f6710697
commit ee1b827c91
14 changed files with 499 additions and 153 deletions

View File

@@ -35,6 +35,7 @@ namespace BoneSync
transport = new SteamTransport(); transport = new SteamTransport();
SceneSync.Initialize(); SceneSync.Initialize();
NetworkMessage.RegisterPacketTypes();
} }
public static void PatchAll() public static void PatchAll()
@@ -62,21 +63,12 @@ namespace BoneSync
{ {
transport.Tick(); transport.Tick();
PlayerRig.Tick(); //PlayerRig.Tick();
if (Input.GetKeyDown(KeyCode.P)) if (Input.GetKeyDown(KeyCode.P))
{ {
MelonLogger.Msg("P key pressed"); MelonLogger.Msg("P key pressed");
PlayerRig playerRig = PlayerRig.InstantiatePlayerRigPrefab(); //PlayerRig playerRig = PlayerRig.InstantiatePlayerRigPrefab();
if (playerRig == null)
{
MelonLogger.Error("Failed to instantiate player rig prefab");
return;
}
else
{
MelonLogger.Msg("Player rig instantiated");
}
} }
if (Input.GetKeyDown(KeyCode.I)) if (Input.GetKeyDown(KeyCode.I))
{ {

View File

@@ -36,6 +36,10 @@ namespace BoneSync.Networking
} }
public byte[] ReadBytes(int count) public byte[] ReadBytes(int count)
{ {
if (Data.Count < count)
{
throw new Exception("Not enough data to read, expected " + count + " but only have " + Data.Count);
}
byte[] value = Data.GetRange(0, count).ToArray(); byte[] value = Data.GetRange(0, count).ToArray();
Data.RemoveRange(0, count); Data.RemoveRange(0, count);
return value; return value;

View File

@@ -12,6 +12,8 @@ namespace BoneSync.Networking.LobbyManager
public abstract ulong GetLobbyId(); public abstract ulong GetLobbyId();
public abstract ulong GetHostId(); public abstract ulong GetHostId();
public abstract ulong GetLocalId(); public abstract ulong GetLocalId();
public bool IsHost => GetLocalId() == GetHostId();
public abstract bool IsConnected();
public abstract void CreateLobby(); public abstract void CreateLobby();
public abstract void JoinLobby(ulong lobbyId); public abstract void JoinLobby(ulong lobbyId);

View File

@@ -42,8 +42,22 @@ namespace BoneSync.Networking.LobbyManager
UpdateLobbyData(); UpdateLobbyData();
}; };
MelonLogger.Msg("SteamLobbyManager initialized"); MelonLogger.Msg("SteamLobbyManager initialized");
}
SteamFriends.OnGameLobbyJoinRequested += (Lobby lobby, SteamId friend) =>
{
MelonLogger.Msg("Joining lobby " + lobby.Id);
JoinLobby(lobby.Id.Value);
};
SteamFriends.OnGameRichPresenceJoinRequested += (Friend friend, string connectString) =>
{
MelonLogger.Msg("Joining lobby " + connectString);
ulong lobbyId = ulong.Parse(connectString.Split(':')[1]);
JoinLobby(lobbyId);
};
SteamNetworkingUtils.InitRelayNetworkAccess();
}
private Lobby _lobbyInstance; private Lobby _lobbyInstance;
public Friend[] LobbyMembers public Friend[] LobbyMembers
@@ -57,6 +71,11 @@ namespace BoneSync.Networking.LobbyManager
private set; private set;
} }
public override bool IsConnected()
{
return _lobbyInstance.Id.IsValid;
}
public override ulong[] GetPeers() => steamIds.Select(x => x.Value).ToArray(); public override ulong[] GetPeers() => steamIds.Select(x => x.Value).ToArray();
public override ulong GetLocalId() => SteamClient.SteamId.Value; public override ulong GetLocalId() => SteamClient.SteamId.Value;
public override ulong GetHostId() => _lobbyInstance.Owner.Id.Value; public override ulong GetHostId() => _lobbyInstance.Owner.Id.Value;
@@ -88,6 +107,7 @@ namespace BoneSync.Networking.LobbyManager
public override void CreateLobby() public override void CreateLobby()
{ {
LeaveLobby();
MelonLogger.Msg("Trying to create lobby"); MelonLogger.Msg("Trying to create lobby");
_ = SteamMatchmaking.CreateLobbyAsync(16); _ = SteamMatchmaking.CreateLobbyAsync(16);
@@ -95,13 +115,16 @@ namespace BoneSync.Networking.LobbyManager
public override void JoinLobby(ulong lobbyId) public override void JoinLobby(ulong lobbyId)
{ {
LeaveLobby();
MelonLogger.Msg("Trying to join lobby " + lobbyId); MelonLogger.Msg("Trying to join lobby " + lobbyId);
_ = SteamMatchmaking.JoinLobbyAsync(lobbyId); _ = SteamMatchmaking.JoinLobbyAsync(lobbyId);
} }
public override void LeaveLobby() public override void LeaveLobby()
{ {
if (!IsConnected()) return;
_lobbyInstance.Leave(); _lobbyInstance.Leave();
_lobbyInstance = new Lobby();
BoneSync.transport.CleanUp(); BoneSync.transport.CleanUp();
} }

View File

@@ -1,4 +1,5 @@
using System; using BoneSync.Sync;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -15,9 +16,11 @@ namespace BoneSync.Networking.Messages
} }
public struct ObjectSyncMessageData public struct ObjectSyncMessageData
{ {
public ulong objectId; public ushort objectId;
public ObjectSyncTransform[] objectSyncTransforms; public ObjectSyncTransform[] objectSyncTransforms;
} }
[PacketType(PacketType.ObjectSync), PacketReliability(PacketReliability.UnreliableNoDelay)]
internal class ObjectSyncMessage : NetworkMessage internal class ObjectSyncMessage : NetworkMessage
{ {
private ObjectSyncMessageData _objectSyncMessageData = new ObjectSyncMessageData(); private ObjectSyncMessageData _objectSyncMessageData = new ObjectSyncMessageData();
@@ -26,7 +29,7 @@ namespace BoneSync.Networking.Messages
public ObjectSyncMessage(ObjectSyncMessageData objectSyncMessageData) public ObjectSyncMessage(ObjectSyncMessageData objectSyncMessageData)
{ {
_objectSyncMessageData = objectSyncMessageData; _objectSyncMessageData = objectSyncMessageData;
byteEncoder.WriteUlong(_objectSyncMessageData.objectId); byteEncoder.WriteUShort(_objectSyncMessageData.objectId);
byte length = (byte)_objectSyncMessageData.objectSyncTransforms.Length; byte length = (byte)_objectSyncMessageData.objectSyncTransforms.Length;
byteEncoder.WriteByte(length); byteEncoder.WriteByte(length);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
@@ -39,7 +42,7 @@ namespace BoneSync.Networking.Messages
public ObjectSyncMessage(Packet packet) public ObjectSyncMessage(Packet packet)
{ {
byteEncoder.WriteBytes(packet.Data); byteEncoder.WriteBytes(packet.Data);
_objectSyncMessageData.objectId = byteEncoder.ReadUlong(); _objectSyncMessageData.objectId = byteEncoder.ReadUShort();
byte length = byteEncoder.ReadByte(); byte length = byteEncoder.ReadByte();
_objectSyncMessageData.objectSyncTransforms = new ObjectSyncTransform[length]; _objectSyncMessageData.objectSyncTransforms = new ObjectSyncTransform[length];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
@@ -49,11 +52,9 @@ namespace BoneSync.Networking.Messages
} }
} }
public override void Execute() public override void Execute()
{ {
throw new NotImplementedException(); ObjectSync.OnObjectSyncMessage(this);
} }
} }
} }

View File

@@ -7,18 +7,30 @@ using System.Threading.Tasks;
namespace BoneSync.Networking.Messages namespace BoneSync.Networking.Messages
{ {
public enum RegisterSyncType
{
RegisterFromPath = 0,
RegisterAndSpawn = 1,
}
public struct RegisterSyncableInfo public struct RegisterSyncableInfo
{ {
public string transformPath; public string transformPath;
public ushort id; public ushort id;
public ulong ownerId;
public RegisterSyncType type;
} }
[PacketType(PacketType.RegisterSyncable)]
internal class RegisterSyncableMessage : NetworkMessage internal class RegisterSyncableMessage : NetworkMessage
{ {
private RegisterSyncableInfo _info; private RegisterSyncableInfo _info;
public RegisterSyncableInfo info => _info;
public RegisterSyncableMessage(RegisterSyncableInfo info) public RegisterSyncableMessage(RegisterSyncableInfo info)
{ {
_info = info; _info = info;
byteEncoder.WriteByte((byte)_info.type);
byteEncoder.WriteString(_info.transformPath); byteEncoder.WriteString(_info.transformPath);
byteEncoder.WriteUlong(_info.ownerId);
byteEncoder.WriteUShort(_info.id); byteEncoder.WriteUShort(_info.id);
} }
@@ -26,13 +38,15 @@ namespace BoneSync.Networking.Messages
public RegisterSyncableMessage(Packet packet) public RegisterSyncableMessage(Packet packet)
{ {
byteEncoder.WriteBytes(packet.Data); byteEncoder.WriteBytes(packet.Data);
_info.type = (RegisterSyncType)byteEncoder.ReadByte();
_info.transformPath = byteEncoder.ReadString(); _info.transformPath = byteEncoder.ReadString();
_info.ownerId = byteEncoder.ReadUlong();
_info.id = byteEncoder.ReadUShort(); _info.id = byteEncoder.ReadUShort();
} }
public override void Execute() public override void Execute()
{ {
ObjectSync.OnRegisterSyncMessage(this);
} }
} }
} }

View File

@@ -21,6 +21,16 @@ namespace BoneSync.Networking
public PacketType packetType { get; } public PacketType packetType { get; }
} }
public class PacketReliabilityAttribute : Attribute
{
public PacketReliabilityAttribute(PacketReliability reliability)
{
this.reliability = reliability;
}
public PacketReliability reliability { get; }
}
public abstract class NetworkMessage public abstract class NetworkMessage
{ {
public ulong senderId public ulong senderId
@@ -33,6 +43,7 @@ namespace BoneSync.Networking
internal static Dictionary<PacketType, Type> PacketTypeMap = new Dictionary<PacketType, Type>(); internal static Dictionary<PacketType, Type> PacketTypeMap = new Dictionary<PacketType, Type>();
internal PacketType _packetType; internal PacketType _packetType;
internal PacketReliability? _reliability;
internal ByteEncoder byteEncoder = new ByteEncoder(); internal ByteEncoder byteEncoder = new ByteEncoder();
public byte[] GetBytes() => byteEncoder.ToArray(); public byte[] GetBytes() => byteEncoder.ToArray();
@@ -53,6 +64,20 @@ namespace BoneSync.Networking
return _packetType; 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() public static void RegisterPacketTypes()
{ {
if (PacketTypesRegistered) if (PacketTypesRegistered)
@@ -73,6 +98,7 @@ namespace BoneSync.Networking
} }
public static NetworkMessage ParsePacket(Packet packet) 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 // find a class that can parse this packet using Reflection
// and return it // and return it
if (!PacketTypeMap.ContainsKey(packet.Info.packetType)) if (!PacketTypeMap.ContainsKey(packet.Info.packetType))
@@ -92,7 +118,7 @@ namespace BoneSync.Networking
public Packet GetPacket(int id, ulong senderId, ulong receiverId) public Packet GetPacket(int id, ulong senderId, ulong receiverId)
{ {
PacketInfo packetInfo = new PacketInfo(id, senderId, receiverId, GetPacketType()); PacketInfo packetInfo = new PacketInfo(id, senderId, receiverId, GetPacketType(), GetPacketReliability());
Packet packet = new Packet(packetInfo, GetBytes()); Packet packet = new Packet(packetInfo, GetBytes());
return packet; return packet;
} }
@@ -102,6 +128,11 @@ namespace BoneSync.Networking
} }
public void Send(ulong receiverId) public void Send(ulong receiverId)
{ {
if (BoneSync.lobby.IsConnected() == false)
{
MelonLogger.Warning("Cannot send packet, not connected to lobby");
return;
}
int PacketId = Packet.GenerateId(); int PacketId = Packet.GenerateId();
ulong senderId = BoneSync.lobby.GetLocalId(); ulong senderId = BoneSync.lobby.GetLocalId();
Packet packet = GetPacket(PacketId, senderId, receiverId); Packet packet = GetPacket(PacketId, senderId, receiverId);

View File

@@ -1,4 +1,5 @@
using BoneSync.Networking.Messages; using BoneSync.Networking.Messages;
using Facepunch.Steamworks;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -9,19 +10,34 @@ using Xunit;
namespace BoneSync.Networking namespace BoneSync.Networking
{ {
public enum PacketReliability
{
Unreliable = 0,
UnreliableNoDelay = 1,
Reliable = 2,
ReliableWithBuffering = 3,
}
public struct PacketInfo public struct PacketInfo
{ {
public int id; public int id;
public ulong senderId; public ulong senderId;
public ulong receiverId; public ulong receiverId;
public PacketType packetType; public PacketType packetType;
public PacketReliability reliability;
public PacketInfo(int id, ulong senderId, ulong receiverId, PacketType packetType) public PacketInfo(
int id,
ulong senderId,
ulong receiverId,
PacketType packetType,
PacketReliability reliability = PacketReliability.Reliable
)
{ {
this.id = id; this.id = id;
this.senderId = senderId; this.senderId = senderId;
this.receiverId = receiverId; this.receiverId = receiverId;
this.packetType = packetType; this.packetType = packetType;
this.reliability = reliability;
} }
} }
public class Packet public class Packet
@@ -46,19 +62,21 @@ namespace BoneSync.Networking
ByteEncoder byteEncoder = new ByteEncoder(); ByteEncoder byteEncoder = new ByteEncoder();
byteEncoder.WriteBytes(bytes); byteEncoder.WriteBytes(bytes);
int packetType = byteEncoder.ReadInt(); byte packetType = byteEncoder.ReadByte();
byte reliability = byteEncoder.ReadByte();
int id = byteEncoder.ReadInt(); int id = byteEncoder.ReadInt();
ulong senderId = byteEncoder.ReadUlong(); ulong senderId = byteEncoder.ReadUlong();
ulong receiverId = byteEncoder.ReadUlong(); ulong receiverId = byteEncoder.ReadUlong();
PacketInfo packetInfo = new PacketInfo(id, senderId, receiverId, (PacketType)packetType); PacketInfo packetInfo = new PacketInfo(id, senderId, receiverId, (PacketType)packetType, (PacketReliability)reliability);
return new Packet(packetInfo, byteEncoder.ToArray()); return new Packet(packetInfo, byteEncoder.ToArray());
} }
public byte[] ToBytes() public byte[] ToBytes()
{ {
ByteEncoder byteEncoder = new ByteEncoder(); ByteEncoder byteEncoder = new ByteEncoder();
byteEncoder.WriteInt((int)_packetInfo.packetType); byteEncoder.WriteByte((byte)_packetInfo.packetType);
byteEncoder.WriteByte((byte)_packetInfo.reliability);
byteEncoder.WriteInt(_packetInfo.id); byteEncoder.WriteInt(_packetInfo.id);
byteEncoder.WriteUlong(_packetInfo.senderId); byteEncoder.WriteUlong(_packetInfo.senderId);
byteEncoder.WriteUlong(_packetInfo.receiverId); byteEncoder.WriteUlong(_packetInfo.receiverId);

View File

@@ -11,5 +11,7 @@ namespace BoneSync.Networking
Unknown = 0, Unknown = 0,
LobbyInfo = 1, LobbyInfo = 1,
PlayerSync = 2, PlayerSync = 2,
RegisterSyncable = 3,
ObjectSync = 4,
} }
} }

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using BoneSync.Sync; using BoneSync.Sync;
using BoneSync.Sync.Components; using BoneSync.Sync.Components;
using HarmonyLib; using HarmonyLib;
using MelonLoader;
using StressLevelZero.Interaction; using StressLevelZero.Interaction;
using UnityEngine; using UnityEngine;
@@ -24,8 +25,29 @@ namespace BoneSync.Patching
[HarmonyPatch(nameof(InteractableHost.OnDestroy)), HarmonyPostfix] [HarmonyPatch(nameof(InteractableHost.OnDestroy)), HarmonyPostfix]
public static void OnDestroyPatch(InteractableHost __instance) public static void OnDestroyPatch(InteractableHost __instance)
{ {
MelonLoader.MelonLogger.Msg("InteractableHost destroyed: " + __instance.name); MelonLogger.Msg("InteractableHost destroyed: " + __instance.name);
} }
[HarmonyPatch(nameof(InteractableHost.AttachHand)), HarmonyPostfix]
public static void AttachHandPatch(InteractableHost __instance, Hand hand)
{
MelonLogger.Msg("InteractableHost attached to hand: " + __instance.name + " Hand: " + hand.name);
Syncable syncable = ObjectSync.MakeOrGetSyncable(__instance);
if (syncable == null)
{
MelonLogger.Error("Syncable is null for " + __instance.name);
return;
}
syncable.RegisterSyncable();
}
/*[HarmonyPatch(nameof(InteractableHost.AddForcePullGrip)), HarmonyPostfix]
public static void AddForcePullGripPatch(InteractableHost __instance, ForcePullGrip grip)
{
MelonLoader.MelonLogger.Msg("AddForcePullGrip to hand: " + __instance.name + " Hand: " + grip.name);
}*/
} }
[HarmonyPatch(typeof(InteractableHostManager))] [HarmonyPatch(typeof(InteractableHostManager))]

View File

@@ -17,9 +17,8 @@ namespace BoneSync.Player
{ {
internal class PlayerRig internal class PlayerRig
{ {
public const string RIGMANAGER_SCENE_NAME = "[RigManager (Default Brett)]"; //public const string RIGMANAGER_SCENE_NAME = "[RigManager (Default Brett)]";
private static GameObject _rigPrefabCache = null;
private static List<PlayerRig> _playerRigs = new List<PlayerRig>(); private static List<PlayerRig> _playerRigs = new List<PlayerRig>();
private GameObject playerRig; private GameObject playerRig;
@@ -29,100 +28,18 @@ namespace BoneSync.Player
private Animator repAnimator; private Animator repAnimator;
public void UpdatePlayerSync(PlayerSyncInfo playerSyncInfo) public void UpdatePlayerSync(PlayerSyncInfo playerSyncInfo)
{ {
playerRig.transform.ApplySimpleTransform(playerSyncInfo.headPos); //playerRig.transform.ApplySimpleTransform(playerSyncInfo.headPos);
} }
public static void Tick() public PlayerRig()
{ {
TryRegisterRigPrefab();
foreach (PlayerRig playerRig in _playerRigs)
{
try
{
playerRig.UpdateRig();
} catch (Exception e)
{
MelonLogger.Error(e.Message);
//MelonLogger.Error("Failed to update player rig");
}
}
}
public static void TryRegisterRigPrefab()
{
return; // disable this for now
if (_rigPrefabCache) return;
GetPlayerRigPrefab();
}
public static GameObject GetPlayerRigPrefab()
{
if (_rigPrefabCache == null)
{
// find the rig game object in the scene
GameObject playerRig = GameObject.Find(RIGMANAGER_SCENE_NAME);
if (playerRig == null)
{
MelonLogger.Error("Failed to find RigManager scene object");
return null;
}
// create a clone of the rig
GameObject playerRep = GameObject.Instantiate(playerRig);
playerRep.gameObject.SetActive(false);
playerRep.name = "[RigManager (Networked)]";
RigManager repRigManager = playerRep.GetComponent<RigManager>();
repRigManager.oculusControllerRig.gameObject.SetActive(false);
repRigManager.steamControllerRig.gameObject.SetActive(false);
repRigManager.uiRig.gameObject.SetActive(false);
repRigManager.enabled = false;
_rigPrefabCache = playerRep;
MelonLogger.Msg("Player rig prefab created");
}
return _rigPrefabCache;
}
public static PlayerRig InstantiatePlayerRigPrefab()
{
GameObject prefab = GetPlayerRigPrefab();
if (prefab == null)
{
return null;
}
GameObject go = GameObject.Instantiate(prefab);
go.SetActive(true);
PlayerRig playerRig = new PlayerRig(go);
return playerRig;
}
private void UpdateRig()
{
Vector3 velocity = Vector3.zero;
Vector3 acceleration = Vector3.zero;
//SkeletonRig skeletonRig = rigManager.gameWorldSkeletonRig;
//skeletonRig.OnLateUpdate();
rigManager.ControllerRig.gameObject.SetActive(false);
characterAnimationManager.OnLateUpdate();
body.FullBodyUpdate(velocity, acceleration);
body.ArtToBlender.UpdateBlender();
repAnimator.Update(Time.deltaTime);
}
private PlayerRig(GameObject go)
{
playerRig = go;
rigManager = playerRig.GetComponent<RigManager>();
rigManager.enabled = true;
UpdateRig();
_playerRigs.Add(this); _playerRigs.Add(this);
} }
public void Destroy() public void Destroy()
{ {
_playerRigs.Remove(this); _playerRigs.Remove(this);

View File

@@ -11,6 +11,7 @@ using UnityEngine.Experimental.PlayerLoop;
using BoneSync.Networking.Messages; using BoneSync.Networking.Messages;
using BoneSync.Networking; using BoneSync.Networking;
using StressLevelZero.Data; using StressLevelZero.Data;
using System.Collections;
namespace BoneSync.Sync.Components namespace BoneSync.Sync.Components
@@ -46,22 +47,95 @@ namespace BoneSync.Sync.Components
[RegisterTypeInIl2Cpp] [RegisterTypeInIl2Cpp]
public class Syncable : MonoBehaviour public class Syncable : MonoBehaviour
{ {
public const int SYNC_FPS = 20;
public Syncable(IntPtr intPtr) : base(intPtr) { } public static List<Syncable> syncablesCache = new List<Syncable>();
public Syncable(IntPtr intPtr) : base(intPtr) {
syncablesCache.Add(this);
}
private bool _syncCoroutineRunning;
public ulong _ownerId
{
private set;
get;
}
private ushort _syncId; private ushort _syncId;
public bool Registered => _syncId != 0;
private ulong _lastSyncTime; private ulong _lastSyncTime;
private ulong _ownerId;
private bool _attemptedRegister;
public bool Registered => _syncId != 0;
public bool isStale => Time.time - _lastSyncTime > 5f; public bool isStale => Time.time - _lastSyncTime > 5f;
public bool isOwner => _ownerId == BoneSync.lobby.GetLocalId();
public ushort GetSyncId() => _syncId;
public void SetSyncId(ushort id)
{
_syncId = id;
ObjectSyncCache.UpdateSyncId(this);
}
public InteractableHost interactableHost; public InteractableHost interactableHost;
public InteractableHostManager interactableManager; public InteractableHostManager interactableManager;
public Poolee poolee; public Poolee poolee;
private Rigidbody[] rigidbodies;
private Transform[] transforms; private Transform[] transforms;
private void SetKinematic(bool kinematic)
{
foreach (Rigidbody rb in rigidbodies)
{
rb.isKinematic = kinematic;
}
}
public ObjectSyncTransform[] GetObjectSyncTransforms()
{
ObjectSyncTransform[] objectSyncTransforms = new ObjectSyncTransform[transforms.Length];
for (int i = 0; i < transforms.Length; i++)
{
objectSyncTransforms[i] = new ObjectSyncTransform()
{
transform = new SimpleTransform(transforms[i]),
velocity = Vector3.zero
};
}
return objectSyncTransforms;
}
public void ApplyObjectSyncTransforms(ObjectSyncTransform[] objectSyncTransforms)
{
if (objectSyncTransforms.Length != transforms.Length)
{
MelonLogger.Warning("ObjectSyncTransforms length mismatch: " + objectSyncTransforms.Length + " != " + transforms.Length);
return;
}
for (int i = 0; i < objectSyncTransforms.Length; i++)
{
ObjectSyncTransform objectSyncTransform = objectSyncTransforms[i];
transforms[i].ApplySimpleTransform(objectSyncTransform.transform);
}
}
public async Task SyncCoroutineAsync()
{
MelonLogger.Msg("Running sync coroutine for: " + gameObject.name);
if (_syncCoroutineRunning) return;
_syncCoroutineRunning = true;
while (isOwner)
{
MelonLogger.Msg("Sending object sync message for: " + gameObject.name);
ObjectSync.SendObjectSyncMessage(this);
await Task.Delay( GetSyncId() == 0 ? 1000 : 1000 / SYNC_FPS);
}
_syncCoroutineRunning = false;
return;
}
public void OnEnable() public void OnEnable()
{ {
FindComponents(); FindComponents();
@@ -88,22 +162,26 @@ namespace BoneSync.Sync.Components
{ {
if (poolee && poolee.pool) if (poolee && poolee.pool)
{ {
return null; return "";
} }
if (interactableHost) if (interactableHost || interactableManager)
{ {
// get full path from root return transform.GetPath();
return interactableHost.transform.GetPath();
} }
return null; return "";
} }
public void FindComponents() public void FindComponents()
{ {
ObjectSyncCache.RemoveSyncable(this);
interactableManager = GetComponent<InteractableHostManager>(); interactableManager = GetComponent<InteractableHostManager>();
interactableHost = GetComponent<InteractableHost>(); interactableHost = GetComponent<InteractableHost>();
poolee = GetComponent<Poolee>(); poolee = GetComponent<Poolee>();
rigidbodies = GetComponentsInChildren<Rigidbody>();
UpdateTransformList(); UpdateTransformList();
ObjectSyncCache.AddSyncable(this);
} }
public bool ShouldSync() public bool ShouldSync()
@@ -123,39 +201,48 @@ namespace BoneSync.Sync.Components
public void DiscardSyncable() public void DiscardSyncable()
{ {
ObjectSyncCache.RemoveSyncable(this);
Destroy(this); Destroy(this);
} }
public void OnDisable() public void OnDisable()
{
if (!Registered)
{ {
DiscardSyncable(); DiscardSyncable();
return;
} }
public ObjectSyncMessageData GetObjectSyncData() MelonLogger.Warning("Registered Syncable disabled: " + transform.GetPath());
{
ObjectSyncMessageData data = new ObjectSyncMessageData()
{
objectId = _syncId,
objectSyncTransforms = new ObjectSyncTransform[transforms.Length],
};
for (int i = 0; i < transforms.Length; i++)
{
data.objectSyncTransforms[i] = new ObjectSyncTransform()
{
transform = new SimpleTransform(transforms[i]),
velocity = Vector3.zero
};
}
return data;
} }
public void SetOwner(ulong ownerId)
{
_ownerId = ownerId;
MelonLogger.Msg("Set owner for " + gameObject.name + " to " + ownerId);
_ = SyncCoroutineAsync();
UpdateKinematic();
}
private void UpdateKinematic()
{
SetKinematic(_ownerId != BoneSync.lobby.GetLocalId() && Registered);
}
private void _SendRegisterSync()
{
MelonLogger.Msg("Registering syncable object: " + gameObject.name);
SetOwner(BoneSync.lobby.GetLocalId());
SetSyncId(ObjectSync.SendRegisterSyncableMessage(this));
}
public void RegisterSyncable() public void RegisterSyncable()
{ {
if (!BoneSync.lobby.IsConnected()) return;
if (_attemptedRegister) return;
if (Registered) return; if (Registered) return;
if (!ShouldSync()) return; if (!ShouldSync()) return;
MelonLogger.Msg("Registering syncable object: " + gameObject.name); _attemptedRegister = true;
ObjectSync.RegisterSyncable(this); _SendRegisterSync();
} }
} }

View File

@@ -1,5 +1,8 @@
using BoneSync.Sync.Components; using BoneSync.Networking.Messages;
using BoneSync.Sync.Components;
using MelonLoader;
using StressLevelZero.Interaction; using StressLevelZero.Interaction;
using StressLevelZero.Pool;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -9,12 +12,206 @@ using UnityEngine;
namespace BoneSync.Sync namespace BoneSync.Sync
{ {
public static class ObjectSyncCache
{
private static Dictionary<string, Syncable> _pathToSyncable = new Dictionary<string, Syncable>();
private static Dictionary<ushort, Syncable> _idToSyncable = new Dictionary<ushort, Syncable>();
private static Dictionary<Poolee, Syncable> _pooleeToSyncable = new Dictionary<Poolee, Syncable>();
private static Dictionary<InteractableHost, Syncable> _interactableHostToSyncable = new Dictionary<InteractableHost, Syncable>();
private static Dictionary<InteractableHostManager, Syncable> _interactableHostManagerToSyncable = new Dictionary<InteractableHostManager, Syncable>();
public static void AddSyncable(Syncable syncable)
{
string path = syncable.transform.GetPath();
if (path != null && path != "")
{
_pathToSyncable[path] = syncable;
}
if (syncable.GetSyncId() != 0)
{
_idToSyncable[syncable.GetSyncId()] = syncable;
}
if (syncable.interactableHost)
{
_interactableHostToSyncable[syncable.interactableHost] = syncable;
}
if (syncable.interactableManager)
{
_interactableHostManagerToSyncable[syncable.interactableManager] = syncable;
}
if (syncable.poolee)
{
_pooleeToSyncable[syncable.poolee] = syncable;
}
}
public static void RemoveSyncable(Syncable syncable)
{
if (syncable.transform)
{
_pathToSyncable.Remove(syncable.transform.GetPath());
}
if (syncable.GetSyncId() != 0)
{
_idToSyncable.Remove(syncable.GetSyncId());
}
if (syncable.interactableHost)
{
_interactableHostToSyncable.Remove(syncable.interactableHost);
}
if (syncable.interactableManager)
{
_interactableHostManagerToSyncable.Remove(syncable.interactableManager);
}
if (syncable.poolee)
{
_pooleeToSyncable.Remove(syncable.poolee);
}
}
public static void UpdateSyncId(Syncable syncable)
{
// remove other enties where value is the same
foreach (KeyValuePair<ushort, Syncable> entry in _idToSyncable)
{
if (entry.Value == syncable)
{
_idToSyncable.Remove(entry.Key);
}
}
ushort id = syncable.GetSyncId();
if (_idToSyncable.ContainsKey(id))
{
_idToSyncable.Remove(id);
}
_idToSyncable[id] = syncable;
}
public static Syncable GetSyncable(string path)
{
if (_pathToSyncable.ContainsKey(path))
{
return _pathToSyncable[path];
}
return null;
}
public static Syncable GetSyncable(ushort id)
{
if (_idToSyncable.ContainsKey(id))
{
return _idToSyncable[id];
}
return null;
}
public static Syncable GetSyncable(Poolee poolee)
{
if (_pooleeToSyncable.ContainsKey(poolee))
{
return _pooleeToSyncable[poolee];
}
return null;
}
public static Syncable GetSyncable(InteractableHost interactableHost)
{
if (_interactableHostToSyncable.ContainsKey(interactableHost))
{
return _interactableHostToSyncable[interactableHost];
}
return null;
}
public static Syncable GetSyncable(InteractableHostManager interactableHostManager)
{
if (_interactableHostManagerToSyncable.ContainsKey(interactableHostManager))
{
return _interactableHostManagerToSyncable[interactableHostManager];
}
return null;
}
public static void DISCARD_ALL_SYNCABLES() {
foreach (Syncable syncable in Syncable.syncablesCache)
{
syncable.DiscardSyncable();
}
}
}
internal class ObjectSync internal class ObjectSync
{ {
private static ushort _nextSyncableId = 1;
public static void RegisterSyncable(Syncable syncable) public static ushort GetNextId()
{ {
return _nextSyncableId++;
}
public static RegisterSyncableInfo? GetRegisterInfo(Syncable syncable)
{
RegisterSyncableInfo? info = null;
string path = syncable.GetSyncableWorldPath();
if (path.Length > 0)
{
info = new RegisterSyncableInfo()
{
type = RegisterSyncType.RegisterFromPath,
transformPath = path,
ownerId = syncable._ownerId,
};
}
else if (syncable.poolee)
{
info = new RegisterSyncableInfo()
{
type = RegisterSyncType.RegisterAndSpawn,
transformPath = syncable.poolee.transform.GetPath(),
};
}
return info;
}
public static ushort SendRegisterSyncableMessage(Syncable syncable)
{
RegisterSyncableInfo? infoNullable = GetRegisterInfo(syncable);
if (infoNullable == null)
{
MelonLogger.Msg("No valid registeration method for syncable");
return 0;
}
RegisterSyncableInfo info = infoNullable.Value;
RegisterSyncableMessage message = new RegisterSyncableMessage(info);
if (BoneSync.lobby.IsHost)
{
info.id = GetNextId();
message.Broadcast();
}
else
{
info.id = 0;
message.Send(BoneSync.lobby.GetHostId());
}
return info.id;
}
public static void SendObjectSyncMessage(Syncable syncable)
{
MelonLogger.Msg("Sending object sync message for: " + syncable.transform.name);
ObjectSyncTransform[] objectSyncTransforms = syncable.GetObjectSyncTransforms();
ObjectSyncMessageData data = new ObjectSyncMessageData()
{
objectId = syncable.GetSyncId(),
objectSyncTransforms = objectSyncTransforms,
};
ObjectSyncMessage message = new ObjectSyncMessage(data);
message.Broadcast();
} }
private static Syncable _MakeOrGetSyncable(GameObject gameObject, bool deleteSubSyncabled = true) private static Syncable _MakeOrGetSyncable(GameObject gameObject, bool deleteSubSyncabled = true)
{ {
@@ -34,6 +231,10 @@ namespace BoneSync.Sync
{ {
syncable = gameObject.AddComponent<Syncable>(); syncable = gameObject.AddComponent<Syncable>();
} }
else
{
syncable.FindComponents();
}
return syncable; return syncable;
} }
@@ -47,5 +248,41 @@ namespace BoneSync.Sync
{ {
return _MakeOrGetSyncable(interactableHostManager.gameObject); return _MakeOrGetSyncable(interactableHostManager.gameObject);
} }
public static void OnRegisterSyncMessage(RegisterSyncableMessage registerSyncableMessage)
{
Syncable syncable = null;
RegisterSyncableInfo info = registerSyncableMessage.info;
if (BoneSync.lobby.IsHost)
{
info.id = GetNextId();
new RegisterSyncableMessage(info).Broadcast();
}
switch (info.type)
{
case RegisterSyncType.RegisterFromPath:
MelonLogger.Msg("Registering syncable from path: " + info.transformPath + " with id: " + info.id);
syncable = ObjectSyncCache.GetSyncable(info.transformPath);
break;
case RegisterSyncType.RegisterAndSpawn:
break;
}
syncable.SetSyncId(info.id);
syncable.SetOwner(info.ownerId);
}
public static void OnObjectSyncMessage(ObjectSyncMessage objectSyncMessage)
{
ObjectSyncMessageData data = objectSyncMessage.objectSyncMessageData;
Syncable syncable = ObjectSyncCache.GetSyncable(data.objectId);
if (syncable == null)
{
MelonLogger.Msg("Syncable not found for id: " + data.objectId);
return;
}
syncable.ApplyObjectSyncTransforms(data.objectSyncTransforms);
}
} }
} }

View File

@@ -20,7 +20,7 @@ namespace BoneSync.Sync
{ {
if (!_playerRigMap.ContainsKey(playerId)) if (!_playerRigMap.ContainsKey(playerId))
{ {
PlayerRig playerRig = PlayerRig.InstantiatePlayerRigPrefab(); PlayerRig playerRig = new PlayerRig();
_playerRigMap.Add(playerId, playerRig); _playerRigMap.Add(playerId, playerRig);
} }
@@ -38,12 +38,8 @@ namespace BoneSync.Sync
public static void SyncPlayer() public static void SyncPlayer()
{ {
if (!_localPlayerRig)
{
_localPlayerRig = GameObject.Find(PlayerRig.RIGMANAGER_SCENE_NAME);
}
PlayerSyncInfo playerSyncInfo = new PlayerSyncInfo(); PlayerSyncInfo playerSyncInfo = new PlayerSyncInfo();
playerSyncInfo.headPos = new SimpleTransform(_localPlayerRig.transform); playerSyncInfo.headPos = new SimpleTransform();
playerSyncInfo.leftHandPos = new SimpleTransform(); playerSyncInfo.leftHandPos = new SimpleTransform();
playerSyncInfo.rightHandPos = new SimpleTransform(); playerSyncInfo.rightHandPos = new SimpleTransform();