341 lines
13 KiB
C#
341 lines
13 KiB
C#
using BoneSync.Data;
|
|
using BoneSync.Networking;
|
|
using BoneSync.Networking.Messages;
|
|
using BoneSync.Patching;
|
|
using BoneSync.Sync.Components;
|
|
using MelonLoader;
|
|
using StressLevelZero.Data;
|
|
using StressLevelZero.Interaction;
|
|
using StressLevelZero.Pool;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace BoneSync.Sync
|
|
{
|
|
internal class ObjectSync
|
|
{
|
|
private static ushort _nextSyncableId = 1;
|
|
public static ushort GetNextId()
|
|
{
|
|
return _nextSyncableId++;
|
|
}
|
|
|
|
|
|
public static RegisterSyncableInfo? GetRegisterInfo(Syncable syncable, bool allowClientRegister)
|
|
{
|
|
ulong ownerId = BoneSync.lobby.GetLocalId();
|
|
RegisterSyncableInfo? info = null;
|
|
string path = syncable.GetSyncableWorldPath();
|
|
if (path.Length > 0)
|
|
{
|
|
info = new RegisterSyncableInfo()
|
|
{
|
|
type = RegisterSyncType.RegisterFromPath,
|
|
transformPath = path,
|
|
ownerId = ownerId,
|
|
};
|
|
}
|
|
else if (syncable.poolee && syncable.poolee.pool && (BoneSync.lobby.IsHost || allowClientRegister))
|
|
{
|
|
string spawnableRealTitle = syncable?.poolee?.spawnObject?.title;
|
|
string spawnableAlternateTitle = syncable?.poolee?.pool?.name.Replace("pool - ", ""); // hacky, but use when real title is unknown
|
|
|
|
string spawnableTitle = spawnableRealTitle ?? spawnableAlternateTitle;
|
|
|
|
MelonLogger.Msg("Spawnable title: " + spawnableTitle);
|
|
info = new RegisterSyncableInfo()
|
|
{
|
|
ownerId = ownerId,
|
|
type = RegisterSyncType.RegisterAndSpawn,
|
|
spawnInfo = new SpawnPoolableInfo()
|
|
{
|
|
spawnableTitle = spawnableTitle,
|
|
spawnLocation = new SimpleSyncTransform(syncable.poolee.transform),
|
|
},
|
|
};
|
|
}
|
|
return info;
|
|
}
|
|
|
|
public static ushort SendRegisterSyncableMessage(Syncable syncable)
|
|
{
|
|
MelonLogger.Msg("Sending register syncable message for: " + syncable.transform.name);
|
|
RegisterSyncableInfo? infoNullable = GetRegisterInfo(syncable, syncable.ClientSpawningAllowed());
|
|
|
|
if (infoNullable == null)
|
|
{
|
|
MelonLogger.Warning("No valid registeration method for syncable");
|
|
return 0;
|
|
}
|
|
|
|
RegisterSyncableInfo info = infoNullable.Value;
|
|
|
|
|
|
|
|
if (BoneSync.lobby.IsHost)
|
|
{
|
|
info.id = GetNextId();
|
|
new RegisterSyncableMessage(info).Broadcast();
|
|
} else
|
|
{
|
|
info.callbackId = (ushort)Random.Range(1, ushort.MaxValue);
|
|
ObjectSyncCache.CallbackIdToSyncable[info.callbackId] = syncable;
|
|
new RegisterSyncableMessage(info).SendToHost();
|
|
}
|
|
|
|
MelonLogger.Msg("Sending register syncable message for: " + syncable.transform.name + " with id: " + info.id + " and type : " + info.type);
|
|
|
|
return info.id;
|
|
}
|
|
|
|
public static void SendObjectSyncMessage(Syncable syncable)
|
|
{
|
|
if (!syncable.Registered) return;
|
|
//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)
|
|
{
|
|
//Scene scene = gameObject.scene;
|
|
//MelonLogger.Msg("Making or getting syncable for: " + gameObject.name);
|
|
if (gameObject == null)
|
|
{
|
|
return null;
|
|
}
|
|
Syncable syncable = gameObject.GetComponent<Syncable>();
|
|
|
|
// delete all sub syncables
|
|
if (deleteSubSyncabled)
|
|
{
|
|
try
|
|
{
|
|
Syncable[] subSyncables = gameObject.GetComponentsInChildren<Syncable>(true);
|
|
for (int i = 0; i < subSyncables.Length; i++)
|
|
{
|
|
Syncable subSyncable = subSyncables[i];
|
|
if (subSyncable == syncable) continue;
|
|
if (subSyncable == null) continue;
|
|
bool isRegistered = subSyncable.Registered;
|
|
bool isPlugged = subSyncable.IsPlugged();
|
|
|
|
MelonLogger.Msg("Discarding subSyncable: " + subSyncable.transform.GetPath() + " registered:" + isRegistered + " plugged:" + isPlugged);
|
|
if (isRegistered || isPlugged) continue;
|
|
subSyncable.DiscardSyncable();
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MelonLogger.Warning("Failed to delete sub syncables: " + e.Message);
|
|
}
|
|
}
|
|
|
|
if (syncable == null)
|
|
{
|
|
syncable = gameObject.AddComponent<Syncable>();
|
|
// MelonLogger.Msg("Created syncable for: " + gameObject.name);
|
|
}
|
|
return syncable;
|
|
}
|
|
|
|
private static Syncable _GetSyncableFromCache(GameObject gameObject)
|
|
{
|
|
bool success = Syncable.syncablesCache.TryGetValue(gameObject, out Syncable syncable);
|
|
if (!success)
|
|
{
|
|
return null;
|
|
}
|
|
return syncable;
|
|
}
|
|
|
|
public static Syncable MakeOrGetSyncable(GameObject gameObject)
|
|
{
|
|
Syncable syncable = _GetSyncableFromCache(gameObject);
|
|
if (syncable == null)
|
|
{
|
|
syncable = _MakeOrGetSyncable(gameObject);
|
|
}
|
|
return syncable;
|
|
}
|
|
public static Syncable MakeOrGetSyncable(Poolee poolee)
|
|
{
|
|
return _MakeOrGetSyncable(poolee.gameObject);
|
|
}
|
|
|
|
public static Syncable MakeOrGetSyncable(InteractableHost interactableHost)
|
|
{
|
|
if (interactableHost.manager) return MakeOrGetSyncable(interactableHost.manager);
|
|
return _MakeOrGetSyncable(interactableHost.gameObject);
|
|
}
|
|
|
|
public static Syncable MakeOrGetSyncable(InteractableHostManager interactableHostManager)
|
|
{
|
|
return _MakeOrGetSyncable(interactableHostManager.gameObject, true);
|
|
}
|
|
|
|
public static Syncable SpawnPooleeAndMakeSyncable(SpawnPoolableInfo spawnInfo)
|
|
{
|
|
|
|
SimpleSyncTransform spawnLocation = spawnInfo.spawnLocation;
|
|
|
|
SpawnableObject spawnableObject = SpawnableManager.GetSpawnable(spawnInfo.spawnableTitle);
|
|
if (spawnableObject == null) {
|
|
MelonLogger.Warning("[SpawnPooleeAndMakeSyncable] Failed to find spawnable: " + spawnInfo.spawnableTitle);
|
|
return null;
|
|
}
|
|
|
|
Pool pool = SpawnableManager.GetPool(spawnableObject);
|
|
|
|
if (!pool)
|
|
{
|
|
MelonLogger.Warning("[SpawnPooleeAndMakeSyncable] Failed to find pool: " + spawnInfo.spawnableTitle);
|
|
return null;
|
|
}
|
|
Poolee poolee = CallPatchedMethods.InstantiatePoolee(pool, spawnLocation.position, spawnLocation.rotation, pool.Prefab.transform.localScale);
|
|
|
|
Syncable syncable = MakeOrGetSyncable(poolee);
|
|
MelonLogger.Msg("Spawned poolee with syncable: " + poolee.transform.GetPath());
|
|
return syncable;
|
|
}
|
|
public static void OnRegisterSyncMessage(RegisterSyncableMessage registerSyncableMessage)
|
|
{
|
|
MelonLogger.Msg("Received register sync message");
|
|
Syncable syncable = null;
|
|
RegisterSyncableInfo info = registerSyncableMessage.info;
|
|
if (BoneSync.lobby.IsHost)
|
|
{
|
|
info.id = GetNextId();
|
|
new RegisterSyncableMessage(info).Broadcast();
|
|
} else
|
|
{
|
|
// only host can register syncables, all spawn requests should be sent to host
|
|
if (registerSyncableMessage.senderId != BoneSync.lobby.GetHostId())
|
|
{
|
|
MelonLogger.Warning("Received register sync message from non-host: " + registerSyncableMessage.senderId);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (info.id == 0)
|
|
{
|
|
MelonLogger.Warning("Received register sync message with id 0");
|
|
return;
|
|
}
|
|
|
|
bool hasCallback = info.callbackId != 0;
|
|
if (hasCallback)
|
|
{
|
|
MelonLogger.Msg("Received register sync message with callback id: " + info.callbackId);
|
|
}
|
|
|
|
if (hasCallback && ObjectSyncCache.CallbackIdToSyncable.ContainsKey(info.callbackId))
|
|
{
|
|
MelonLogger.Msg("Found syncable for callback id: " + info.callbackId);
|
|
syncable = ObjectSyncCache.CallbackIdToSyncable[info.callbackId];
|
|
ObjectSyncCache.CallbackIdToSyncable.Remove(info.callbackId);
|
|
} else
|
|
{
|
|
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:
|
|
MelonLogger.Msg("Registering and spawning syncable from pool: " + info.spawnInfo.Value.spawnableTitle + " with id: " + info.id);
|
|
syncable = SpawnPooleeAndMakeSyncable(info.spawnInfo.Value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!syncable)
|
|
{
|
|
MelonLogger.Warning("Failed to create/obtain syncable for register sync message "+ info.id);
|
|
return;
|
|
}
|
|
|
|
syncable.SetSyncId(info.id);
|
|
syncable.SetOwner(info.ownerId);
|
|
}
|
|
|
|
public static void OnObjectSyncMessage(ObjectSyncMessage objectSyncMessage)
|
|
{
|
|
ObjectSyncMessageData data = objectSyncMessage.objectSyncMessageData;
|
|
ushort objectId = data.objectId;
|
|
|
|
if (objectId == 0) return;
|
|
|
|
if (objectId >= _nextSyncableId && !BoneSync.lobby.IsHost)
|
|
{
|
|
_nextSyncableId = (ushort)(objectId + 1);
|
|
}
|
|
Syncable syncable = ObjectSyncCache.GetSyncable(objectId);
|
|
if (syncable == null)
|
|
{
|
|
//MelonLogger.Msg("SyncEvent: Syncable not found for id: " + objectId);
|
|
return;
|
|
}
|
|
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();
|
|
}
|
|
|
|
internal static void OnOwnershipChangeMessage(ushort syncId, ulong newOwnerId, bool force)
|
|
{
|
|
Syncable syncable = ObjectSyncCache.GetSyncable(syncId);
|
|
if (syncable == null)
|
|
{
|
|
MelonLogger.Warning("Ownership transfer request for non-existant syncable: " + syncId);
|
|
return;
|
|
}
|
|
if (force)
|
|
{
|
|
syncable.SetOwner(newOwnerId);
|
|
}
|
|
else
|
|
{
|
|
syncable.OnOwnershipTransferRequest(newOwnerId);
|
|
}
|
|
}
|
|
}
|
|
}
|