Compare commits

...

2 Commits

Author SHA1 Message Date
1dce1960f6 More Networking stuff 2025-02-28 12:20:55 +02:00
ff70bad234 Mogus 2025-02-28 11:13:16 +02:00
16 changed files with 687 additions and 249 deletions

View File

@@ -113,11 +113,18 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Networking\Messages\ObjectDamageMessage.cs" />
<Compile Include="Networking\Messages\ObjectSyncMessage.cs" /> <Compile Include="Networking\Messages\ObjectSyncMessage.cs" />
<Compile Include="Networking\Messages\RegisterSyncableMessage.cs" /> <Compile Include="Networking\Messages\RegisterSyncableMessage.cs" />
<Compile Include="Patching\CallPatchedMethod.cs" />
<Compile Include="Patching\InteractableHostPatches.cs" /> <Compile Include="Patching\InteractableHostPatches.cs" />
<Compile Include="Sync\Components\Syncable.cs" /> <Compile Include="Patching\ObjectHealthPatches.cs" />
<Compile Include="Sync\Components\SyncableBase.cs" />
<Compile Include="Sync\Components\SyncableDamage.cs" />
<Compile Include="Sync\Components\SyncableNetworking.cs" />
<Compile Include="Sync\Components\SyncablePhysics.cs" />
<Compile Include="Sync\ObjectSync.cs" /> <Compile Include="Sync\ObjectSync.cs" />
<Compile Include="Sync\ObjectSyncCache.cs" />
<Compile Include="Sync\PlayerSync.cs" /> <Compile Include="Sync\PlayerSync.cs" />
<Compile Include="Facepunch.Steamworks\Callbacks\CallResult.cs" /> <Compile Include="Facepunch.Steamworks\Callbacks\CallResult.cs" />
<Compile Include="Facepunch.Steamworks\Callbacks\ICallbackData.cs" /> <Compile Include="Facepunch.Steamworks\Callbacks\ICallbackData.cs" />

View File

@@ -1,4 +1,5 @@
using StressLevelZero; using StressLevelZero;
using StressLevelZero.Combat;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -52,6 +53,16 @@ namespace BoneSync.Networking
return value; return value;
} }
public void WriteBool(bool value)
{
WriteByte((byte)(value ? 1 : 0));
}
public bool ReadBool()
{
return ReadByte() == 1;
}
public void WriteInt(int value) public void WriteInt(int value)
{ {
WriteBytes(BitConverter.GetBytes(value)); WriteBytes(BitConverter.GetBytes(value));
@@ -168,5 +179,38 @@ namespace BoneSync.Networking
scale = scale scale = scale
}; };
} }
public void WriteEnum<T>(T value) where T : Enum
{
WriteByte((byte)(object)value);
}
public T ReadEnum<T>() where T : Enum
{
return (T)(object)ReadByte();
}
public void WriteSLZAttack(Attack attack)
{
WriteEnum(attack.attackType);
WriteFloat(attack.damage);
}
public void WriteMatrix4x4(UnityEngine.Matrix4x4 matrix)
{
for (int i = 0; i < 16; i++)
{
WriteFloat(matrix[i]);
}
}
public UnityEngine.Matrix4x4 ReadMatrix4x4()
{
UnityEngine.Matrix4x4 matrix = new UnityEngine.Matrix4x4();
for (int i = 0; i < 16; i++)
{
matrix[i] = ReadFloat();
}
return matrix;
}
} }
} }

View File

@@ -0,0 +1,80 @@
using StressLevelZero.Combat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BoneSync.Networking.Messages
{
public struct ObjectHealthInfo
{
public float _health;
public int _hits;
public Vector3 normal;
public float damage;
public bool crit;
public AttackType attackType;
public ObjectHealthInfo(float health, int hits, Vector3 normal, float damage, bool crit, AttackType attackType)
{
_health = health;
_hits = hits;
this.normal = normal;
this.damage = damage;
this.crit = crit;
this.attackType = attackType;
}
}
public enum ObjectDamageType
{
Unknown = 0,
SyncHealth = 1,
DestructibleTakeDamage = 2,
PropHealthTakeDamage = 3,
}
public struct ObjectDamageInfo
{
public ushort objectId;
public ObjectDamageType eventType;
public ObjectHealthInfo objectHealthInfo;
}
[PacketType(PacketType.ObjectEvent), PacketReliability(PacketReliability.Reliable)]
public class ObjectDamageMessage : NetworkMessage
{
public ObjectDamageInfo objectEventInfo => _objectEventInfo;
private ObjectDamageInfo _objectEventInfo;
public ObjectDamageMessage(ObjectDamageInfo objectEventInfo)
{
_objectEventInfo = objectEventInfo;
byteEncoder.WriteUShort(_objectEventInfo.objectId);
byteEncoder.WriteEnum(_objectEventInfo.eventType);
byteEncoder.WriteFloat(_objectEventInfo.objectHealthInfo._health);
byteEncoder.WriteInt(_objectEventInfo.objectHealthInfo._hits);
if (_objectEventInfo.eventType == ObjectDamageType.SyncHealth) return; // No need to write the rest of the data
byteEncoder.WriteVector3(_objectEventInfo.objectHealthInfo.normal);
byteEncoder.WriteFloat(_objectEventInfo.objectHealthInfo.damage);
byteEncoder.WriteBool(_objectEventInfo.objectHealthInfo.crit);
}
public ObjectDamageMessage(Packet packet)
{
byteEncoder.WriteBytes(packet.Data);
_objectEventInfo.objectId = byteEncoder.ReadUShort();
_objectEventInfo.eventType = byteEncoder.ReadEnum<ObjectDamageType>();
_objectEventInfo.objectHealthInfo._health = byteEncoder.ReadFloat();
_objectEventInfo.objectHealthInfo._hits = byteEncoder.ReadInt();
if (_objectEventInfo.eventType == ObjectDamageType.SyncHealth) return; // No need to read the rest of the data
_objectEventInfo.objectHealthInfo.normal = byteEncoder.ReadVector3();
_objectEventInfo.objectHealthInfo.damage = byteEncoder.ReadFloat();
_objectEventInfo.objectHealthInfo.crit = byteEncoder.ReadBool();
}
public override void Execute()
{
}
}
}

View File

@@ -13,6 +13,7 @@ namespace BoneSync.Networking.Messages
{ {
public SimpleTransform transform; public SimpleTransform transform;
public Vector3 velocity; public Vector3 velocity;
public Vector3 angularVelocity;
} }
public struct ObjectSyncMessageData public struct ObjectSyncMessageData
{ {

View File

@@ -28,7 +28,7 @@ namespace BoneSync.Networking.Messages
public RegisterSyncableMessage(RegisterSyncableInfo info) public RegisterSyncableMessage(RegisterSyncableInfo info)
{ {
_info = info; _info = info;
byteEncoder.WriteByte((byte)_info.type); byteEncoder.WriteEnum<RegisterSyncType>(_info.type);
byteEncoder.WriteString(_info.transformPath); byteEncoder.WriteString(_info.transformPath);
byteEncoder.WriteUlong(_info.ownerId); byteEncoder.WriteUlong(_info.ownerId);
byteEncoder.WriteUShort(_info.id); byteEncoder.WriteUShort(_info.id);
@@ -38,7 +38,7 @@ 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.type = byteEncoder.ReadEnum<RegisterSyncType>();
_info.transformPath = byteEncoder.ReadString(); _info.transformPath = byteEncoder.ReadString();
_info.ownerId = byteEncoder.ReadUlong(); _info.ownerId = byteEncoder.ReadUlong();
_info.id = byteEncoder.ReadUShort(); _info.id = byteEncoder.ReadUShort();

View File

@@ -13,5 +13,6 @@ namespace BoneSync.Networking
PlayerSync = 2, PlayerSync = 2,
RegisterSyncable = 3, RegisterSyncable = 3,
ObjectSync = 4, ObjectSync = 4,
ObjectEvent = 5,
} }
} }

View File

@@ -72,17 +72,18 @@ namespace BoneSync.Networking.Transport
return processed > 0; return processed > 0;
} }
public void SendP2P(SteamId peer, byte[] data) public void SendP2P(SteamId peer, byte[] data, P2PSend sendType)
{ {
if (peer == BoneSync.lobby.GetLocalId()) if (peer == BoneSync.lobby.GetLocalId())
{ {
//MelonLogger.Msg("Trying to send packet to self"); //MelonLogger.Msg("Trying to send packet to self");
return; return;
} }
SteamNetworking.SendP2PPacket(peer, data, data.Length, 0, P2PSend.Reliable); SteamNetworking.SendP2PPacket(peer, data, data.Length, 0, sendType);
} }
public override void Send(Packet packet) public override void Send(Packet packet)
{ {
P2PSend sendType = (P2PSend)packet.Info.reliability;
LobbyManager.LobbyManager _instance = BoneSync.lobby; LobbyManager.LobbyManager _instance = BoneSync.lobby;
if (_instance == null) if (_instance == null)
{ {
@@ -93,14 +94,14 @@ namespace BoneSync.Networking.Transport
{ {
foreach (SteamId peer in _instance.GetPeers()) foreach (SteamId peer in _instance.GetPeers())
{ {
SendP2P(peer, packet.ToBytes()); SendP2P(peer, packet.ToBytes(), sendType);
} }
return; return;
} }
else else
{ {
SendP2P(packet.Info.receiverId, packet.ToBytes()); SendP2P(packet.Info.receiverId, packet.ToBytes(), sendType);
} }
} }

View File

@@ -0,0 +1,42 @@
using StressLevelZero.Combat;
using StressLevelZero.Pool;
using StressLevelZero.Props;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BoneSync.Patching
{
public static class CallPatchedMethods
{
public static bool allowPatchedMethodCall
{
private set;
get;
}
public static void TakeDamage(ObjectDestructable __instance, ref Vector3 normal, ref float damage, ref bool crit, ref AttackType attackType)
{
allowPatchedMethodCall = true;
__instance.TakeDamage(normal, damage, crit, attackType);
allowPatchedMethodCall = false;
}
public static void TakeDamage(Prop_Health __instance, ref float damage, ref bool crit, ref AttackType attackType)
{
allowPatchedMethodCall = true;
__instance.TAKEDAMAGE(damage, crit, attackType);
allowPatchedMethodCall = false;
}
public static GameObject PoolSpawn(Pool __instance, ref Vector3 position, ref Quaternion rotation, ref Il2CppSystem.Nullable<Vector3> scale, ref Il2CppSystem.Nullable<bool> autoEnable)
{
allowPatchedMethodCall = true;
GameObject result = __instance.Spawn(position, rotation, scale, autoEnable);
allowPatchedMethodCall = false;
return result;
}
}
}

View File

@@ -0,0 +1,64 @@
using BoneSync.Networking.Messages;
using BoneSync.Sync;
using BoneSync.Sync.Components;
using HarmonyLib;
using StressLevelZero.Combat;
using StressLevelZero.Props;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BoneSync.Patching
{
[HarmonyPatch(typeof(ObjectDestructable))]
public class ObjectDestructablePatches
{
[HarmonyPatch(nameof(ObjectDestructable.TakeDamage))]
[HarmonyPrefix]
private static bool TakeDamagePatch(ObjectDestructable __instance, ref Vector3 normal, ref float damage, ref bool crit, ref AttackType attackType)
{
if (CallPatchedMethods.allowPatchedMethodCall) return true;
MelonLoader.MelonLogger.Msg("ObjectDestructable.TakeDamage: " + damage);
Syncable syncable = ObjectSync.GetSyncableFromCache(__instance.gameObject);
if (syncable != null)
{
if (!syncable.Registered) return true;
MelonLoader.MelonLogger.Msg("Patch: ObjectDestructable.TakeDamage: " + damage + " " + syncable.gameObject.name);
ObjectHealthInfo objectHealthInfo = new ObjectHealthInfo(__instance._health, __instance._hits, normal, damage, crit, attackType);
ObjectSync.SendObjectDamageMessage(syncable, ObjectDamageType.DestructibleTakeDamage, objectHealthInfo);
return false; // prevent networked objects from taking damage locally
}
return true;
}
}
[HarmonyPatch(typeof(Prop_Health))]
public class PropHealthPatches
{
[HarmonyPatch(nameof(Prop_Health.TAKEDAMAGE))]
[HarmonyPrefix]
private static bool TakeDamagePatch(Prop_Health __instance, ref float damage, ref bool crit, ref AttackType attackType)
{
if (CallPatchedMethods.allowPatchedMethodCall) return true;
MelonLoader.MelonLogger.Msg("Prop_Health.TAKEDAMAGE: " + damage);
Syncable syncable = ObjectSync.GetSyncableFromCache(__instance.gameObject);
if (syncable != null)
{
if (!syncable.Registered) return true;
MelonLoader.MelonLogger.Msg("Patch: Prop_Health.TAKEDAMAGE: " + damage + " " + syncable.gameObject.name);
ObjectHealthInfo objectHealthInfo = new ObjectHealthInfo(__instance.cur_Health, __instance.hits, Vector3.zero, damage, crit, attackType);
ObjectSync.SendObjectDamageMessage(syncable, ObjectDamageType.PropHealthTakeDamage, objectHealthInfo);
return false; // prevent networked objects from taking damage locally
}
return true;
}
}
}

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using HarmonyLib; using HarmonyLib;
@@ -11,31 +12,31 @@ using UnityEngine;
namespace BoneSync.Patching namespace BoneSync.Patching
{ {
/*
[HarmonyPatch(typeof(Pool))] [HarmonyPatch(typeof(Pool))]
internal class PoolPatches internal class PoolPatches
{ {
[HarmonyPatch(nameof(Pool.Spawn))] [HarmonyPatch(nameof(Pool.Spawn))]
[HarmonyPostfix] [HarmonyPostfix]
private static void SpawnPatch(Pool __instance, ref GameObject __result) private static void SpawnPatchPost(Pool __instance, ref GameObject __result)
{ {
MelonLogger.Msg("Spawned object: " + __result.name); //MelonLogger.Msg("Spawned object: " + __result.name);
} }
[HarmonyPatch(nameof(Pool.FlagPooleeForRespawn))] [HarmonyPatch(nameof(Pool.Spawn))]
[HarmonyPostfix] [HarmonyPrefix]
private static void FlagPooleeForRespawnPatch(Pool __instance, ref GameObject p) private static bool SpawnPatchPre(Pool __instance, ref Vector3 position, ref Quaternion rotation, ref Il2CppSystem.Nullable<Vector3> scale, ref Il2CppSystem.Nullable<bool> autoEnable)
{ {
MelonLogger.Msg("Flagged object for respawn: " + p.name); if (CallPatchedMethods.allowPatchedMethodCall) return true;
if (BoneSync.lobby.IsConnected())
{
MelonLogger.Msg("Patched Spawning object in pool: " + __instance.name);
return false;
} }
[HarmonyPatch(nameof(Pool.InstantiatePoolee))] return true;
[HarmonyPostfix]
private static void InstantiatePooleePatch(Pool __instance, ref Poolee __result)
{
MelonLogger.Msg("Instantiated object: " + __result.name);
} }
}
}*/
} }

View File

@@ -12,6 +12,12 @@ using BoneSync.Networking.Messages;
using BoneSync.Networking; using BoneSync.Networking;
using StressLevelZero.Data; using StressLevelZero.Data;
using System.Collections; using System.Collections;
using StressLevelZero.Props;
using BoneSync.Patching;
using StressLevelZero.Props.Weapons;
using StressLevelZero.AI;
using PuppetMasta;
using UnityEngine.SceneManagement;
namespace BoneSync.Sync.Components namespace BoneSync.Sync.Components
@@ -45,13 +51,13 @@ namespace BoneSync.Sync.Components
} }
[RegisterTypeInIl2Cpp] [RegisterTypeInIl2Cpp]
public class Syncable : MonoBehaviour public partial class Syncable : MonoBehaviour
{ {
public const int SYNC_FPS = 20; public const int SYNC_FPS = 20;
public static List<Syncable> syncablesCache = new List<Syncable>(); public static Dictionary<GameObject, Syncable> syncablesCache = new Dictionary<GameObject, Syncable>();
public Syncable(IntPtr intPtr) : base(intPtr) { public Syncable(IntPtr intPtr) : base(intPtr) {
syncablesCache.Add(this); syncablesCache[gameObject] = this;
} }
private bool _syncCoroutineRunning; private bool _syncCoroutineRunning;
@@ -62,7 +68,7 @@ namespace BoneSync.Sync.Components
get; get;
} }
private ushort _syncId; private ushort _syncId;
private ulong _lastSyncTime; private float _lastSyncTime;
private bool _attemptedRegister; private bool _attemptedRegister;
@@ -70,6 +76,15 @@ namespace BoneSync.Sync.Components
public bool isStale => Time.time - _lastSyncTime > 5f; public bool isStale => Time.time - _lastSyncTime > 5f;
public bool isOwner => _ownerId == BoneSync.lobby.GetLocalId(); public bool isOwner => _ownerId == BoneSync.lobby.GetLocalId();
public bool ShouldSendSync()
{
if (!Registered) return false;
if (!isOwner) return false;
if (isStale) return true;
return false;
}
public ushort GetSyncId() => _syncId; public ushort GetSyncId() => _syncId;
public void SetSyncId(ushort id) public void SetSyncId(ushort id)
{ {
@@ -78,84 +93,28 @@ namespace BoneSync.Sync.Components
ObjectSyncCache.UpdateSyncId(this); ObjectSyncCache.UpdateSyncId(this);
} }
public InteractableHost interactableHost; public InteractableHost interactableHost { private set; get; }
public InteractableHostManager interactableManager; public InteractableHostManager interactableManager { private set; get; }
public Poolee poolee; public Poolee poolee { private set; get; }
private Prop_Health propHealth;
private ObjectDestructable objectDestructable;
private Rigidbody[] rigidbodies; private Rigidbody[] rigidbodies;
private Transform[] transforms; private Transform[] _transforms;
private void SetKinematic(bool kinematic) private Gun gun;
{ private Magazine magazine;
foreach (Rigidbody rb in rigidbodies)
{
rb.isKinematic = kinematic;
}
}
public ObjectSyncTransform[] GetObjectSyncTransforms() private Plug[] plugs;
{
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) private AIBrain aiBrain;
{ private PuppetMaster puppetMaster;
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();
MelonLogger.Msg("Syncable enabled: " + transform.GetPath());
}
private void UpdateTransformList()
{
if (interactableManager)
{
transforms = interactableManager.hosts.Select(host => host.transform).ToArray();
}
else if (interactableHost)
{
transforms = new Transform[] { interactableHost.transform };
}
else if (poolee)
{
transforms = new Transform[] { poolee.transform };
}
} }
public string GetSyncableWorldPath() public string GetSyncableWorldPath()
@@ -178,13 +137,18 @@ namespace BoneSync.Sync.Components
interactableManager = GetComponent<InteractableHostManager>(); interactableManager = GetComponent<InteractableHostManager>();
interactableHost = GetComponent<InteractableHost>(); interactableHost = GetComponent<InteractableHost>();
poolee = GetComponent<Poolee>(); poolee = GetComponent<Poolee>();
rigidbodies = GetComponentsInChildren<Rigidbody>(); propHealth = GetComponent<Prop_Health>();
objectDestructable = GetComponent<ObjectDestructable>();
gun = GetComponent<Gun>();
magazine = GetComponent<Magazine>();
plugs = GetComponentsInChildren<Plug>();
//rigidbodies = GetComponentsInChildren<Rigidbody>();
UpdateTransformList(); UpdateTransformList();
ObjectSyncCache.AddSyncable(this); ObjectSyncCache.AddSyncable(this);
} }
public bool ShouldSync() public bool CanBeSynced()
{ {
FindComponents(); FindComponents();
if (interactableManager && interactableManager.hosts.Count > 0) if (interactableManager && interactableManager.hosts.Count > 0)
@@ -199,8 +163,15 @@ namespace BoneSync.Sync.Components
return false; return false;
} }
public void OnDestroy()
{
DiscardSyncable();
MelonLogger.Msg("Syncable destroyed: " + transform.GetPath());
}
public void DiscardSyncable() public void DiscardSyncable()
{ {
syncablesCache.Remove(gameObject);
ObjectSyncCache.RemoveSyncable(this); ObjectSyncCache.RemoveSyncable(this);
Destroy(this); Destroy(this);
} }
@@ -216,34 +187,14 @@ namespace BoneSync.Sync.Components
MelonLogger.Warning("Registered Syncable disabled: " + transform.GetPath()); MelonLogger.Warning("Registered Syncable disabled: " + transform.GetPath());
} }
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 (!BoneSync.lobby.IsConnected()) return;
if (_attemptedRegister) return; if (_attemptedRegister) return;
if (Registered) return; if (Registered) return;
if (!ShouldSync()) return; if (!CanBeSynced()) return;
_attemptedRegister = true; _attemptedRegister = true;
_SendRegisterSync(); _SendRegisterSync();
} }
} }
} }

View File

@@ -0,0 +1,58 @@
using BoneSync.Networking.Messages;
using BoneSync.Patching;
using MelonLoader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BoneSync.Sync.Components
{
public partial class Syncable : MonoBehaviour
{
private void SetHealth(float health, int hits = 0)
{
if (gameObject.activeSelf == false && health > 0)
{
gameObject.SetActive(true);
}
if (propHealth)
{
propHealth.cur_Health = health;
propHealth.hits = hits;
}
if (objectDestructable)
{
objectDestructable._health = health;
objectDestructable._hits = hits;
}
}
public void Damage(ObjectDamageInfo damageInfo)
{
ObjectHealthInfo healthInfo = damageInfo.objectHealthInfo;
SetHealth(healthInfo._health, healthInfo._hits); // always set health first
ObjectDamageType eventType = damageInfo.eventType;
if (eventType == ObjectDamageType.SyncHealth) return;
if (eventType == ObjectDamageType.DestructibleTakeDamage && objectDestructable)
{
MelonLogger.Msg("NetworkDestructableTakeDamage: " + healthInfo.damage);
CallPatchedMethods.TakeDamage(objectDestructable, ref healthInfo.normal, ref healthInfo.damage, ref healthInfo.crit, ref healthInfo.attackType);
return;
}
if (eventType == ObjectDamageType.PropHealthTakeDamage && propHealth)
{
MelonLogger.Msg("NetworkPropHealthTakeDamage: " + healthInfo.damage);
CallPatchedMethods.TakeDamage(propHealth, ref healthInfo.damage, ref healthInfo.crit, ref healthInfo.attackType);
return;
}
}
}
}

View File

@@ -0,0 +1,46 @@
using MelonLoader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BoneSync.Sync.Components
{
// SyncableNetworking.cs
public partial class Syncable : MonoBehaviour
{
private void SendObjectSync()
{
_lastSyncTime = Time.time;
ObjectSync.SendObjectSyncMessage(this);
}
public async Task SyncCoroutineAsync()
{
MelonLogger.Msg("Running sync coroutine for: " + transform.GetPath());
if (_syncCoroutineRunning) return;
_syncCoroutineRunning = true;
while (isOwner)
{
if (ShouldSendSync()) SendObjectSync();
await Task.Delay(!Registered ? 1000 : 1000 / SYNC_FPS);
}
_syncCoroutineRunning = false;
return;
}
public void SetOwner(ulong ownerId)
{
MelonLogger.Msg("Setting owner for " + _syncId + " to " + ownerId);
_ownerId = ownerId;
_ = SyncCoroutineAsync();
UpdateKinematic();
}
private void _SendRegisterSync()
{
MelonLogger.Msg("Registering syncable object: " + gameObject.name);
SetOwner(BoneSync.lobby.GetLocalId());
SetSyncId(ObjectSync.SendRegisterSyncableMessage(this));
}
}
}

View File

@@ -0,0 +1,89 @@
using BoneSync.Networking;
using BoneSync.Networking.Messages;
using BoneSync.Patching;
using MelonLoader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace BoneSync.Sync.Components
{
public partial class Syncable : MonoBehaviour
{
private void SetKinematic(bool kinematic)
{
foreach (Rigidbody rb in rigidbodies)
{
rb.isKinematic = kinematic;
}
}
public ObjectSyncTransform[] GetObjectSyncTransforms()
{
ObjectSyncTransform[] objectSyncTransforms = new ObjectSyncTransform[rigidbodies.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 != rigidbodies.Length)
{
MelonLogger.Warning("ObjectSyncTransforms length mismatch: " + objectSyncTransforms.Length + " != " + _transforms.Length);
return;
}
for (int i = 0; i < objectSyncTransforms.Length; i++)
{
ObjectSyncTransform objectSyncTransform = objectSyncTransforms[i];
rigidbodies[i].angularVelocity = objectSyncTransform.angularVelocity;
rigidbodies[i].velocity = objectSyncTransform.velocity;
rigidbodies[i].position = objectSyncTransform.transform.position;
rigidbodies[i].rotation = objectSyncTransform.transform.rotation;
_transforms[i].localScale = objectSyncTransform.transform.scale;
//_transforms[i].ApplySimpleTransform(objectSyncTransform.transform);
}
}
private Rigidbody[] UpdateRigidbodyList()
{
if (interactableManager)
{
return interactableManager.hosts.Select(host => host.rb).ToArray();
}
if (interactableHost)
{
return new Rigidbody[] { interactableHost.rb };
}
if (poolee)
{
return new Rigidbody[] { poolee.GetComponentInChildren<Rigidbody>() };
}
return new Rigidbody[0];
}
private void UpdateTransformList()
{
// get non-null rigidbodies
Rigidbody[] rbs = UpdateRigidbodyList().Where(rb => rb != null).ToArray();
rigidbodies = rbs;
_transforms = rbs.Select(rb => rb.transform).ToArray();
}
private void UpdateKinematic()
{
SetKinematic(_ownerId != BoneSync.lobby.GetLocalId() && Registered);
}
// on collision
}
}

View File

@@ -9,138 +9,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
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; private static ushort _nextSyncableId = 1;
@@ -201,7 +73,8 @@ namespace BoneSync.Sync
public static void SendObjectSyncMessage(Syncable syncable) public static void SendObjectSyncMessage(Syncable syncable)
{ {
MelonLogger.Msg("Sending object sync message for: " + syncable.transform.name); if (!syncable.Registered) return;
//MelonLogger.Msg("Sending object sync message for: " + syncable.transform.name);
ObjectSyncTransform[] objectSyncTransforms = syncable.GetObjectSyncTransforms(); ObjectSyncTransform[] objectSyncTransforms = syncable.GetObjectSyncTransforms();
ObjectSyncMessageData data = new ObjectSyncMessageData() ObjectSyncMessageData data = new ObjectSyncMessageData()
@@ -215,6 +88,9 @@ namespace BoneSync.Sync
} }
private static Syncable _MakeOrGetSyncable(GameObject gameObject, bool deleteSubSyncabled = true) private static Syncable _MakeOrGetSyncable(GameObject gameObject, bool deleteSubSyncabled = true)
{ {
//Scene scene = gameObject.scene;
//MelonLogger.Msg("Making or getting syncable for: " + gameObject.name + " in scene: " + scene.name);
Syncable[] subSyncables = gameObject.GetComponentsInChildren<Syncable>(); Syncable[] subSyncables = gameObject.GetComponentsInChildren<Syncable>();
Syncable syncable = gameObject.GetComponent<Syncable>(); Syncable syncable = gameObject.GetComponent<Syncable>();
@@ -238,6 +114,16 @@ namespace BoneSync.Sync
return syncable; return syncable;
} }
public static Syncable GetSyncableFromCache(GameObject gameObject)
{
bool success = Syncable.syncablesCache.TryGetValue(gameObject, out Syncable syncable);
if (!success)
{
return null;
}
return syncable;
}
public static Syncable MakeOrGetSyncable(InteractableHost interactableHost) public static Syncable MakeOrGetSyncable(InteractableHost interactableHost)
{ {
if (interactableHost.manager) return MakeOrGetSyncable(interactableHost.manager); if (interactableHost.manager) return MakeOrGetSyncable(interactableHost.manager);
@@ -279,10 +165,37 @@ namespace BoneSync.Sync
Syncable syncable = ObjectSyncCache.GetSyncable(data.objectId); Syncable syncable = ObjectSyncCache.GetSyncable(data.objectId);
if (syncable == null) if (syncable == null)
{ {
MelonLogger.Msg("Syncable not found for id: " + data.objectId); MelonLogger.Msg("SyncEvent: Syncable not found for id: " + data.objectId);
return; return;
} }
syncable.ApplyObjectSyncTransforms(data.objectSyncTransforms); syncable.ApplyObjectSyncTransforms(data.objectSyncTransforms);
} }
public static void OnObjectDamageMessage(ObjectDamageMessage damageMessge)
{
ObjectDamageInfo damageInfo = damageMessge.objectEventInfo;
if (damageInfo.objectId == 0) return;
Syncable syncable = ObjectSyncCache.GetSyncable(damageInfo.objectId);
if (syncable == null)
{
MelonLogger.Msg("DamageEvent: Syncable not found for id: " + damageInfo.objectId);
return;
}
syncable.Damage(damageInfo);
}
public static void SendObjectDamageMessage(Syncable syncable, ObjectDamageType damageType, ObjectHealthInfo healthInfo)
{
ObjectDamageInfo damageInfo = new ObjectDamageInfo()
{
objectId = syncable.GetSyncId(),
objectHealthInfo = healthInfo,
eventType = damageType,
};
ObjectDamageMessage message = new ObjectDamageMessage(damageInfo);
message.Broadcast();
message.Execute();
}
} }
} }

View File

@@ -0,0 +1,140 @@
using BoneSync.Sync.Components;
using StressLevelZero.Interaction;
using StressLevelZero.Pool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Values)
{
syncable.DiscardSyncable();
}
}
}
}