mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-30 12:32:55 +00:00
commit
206dadcfdb
@ -17,6 +17,11 @@ public class SolanumPatches : QSBPatch
|
|||||||
[HarmonyPatch(typeof(SolanumAnimController), nameof(SolanumAnimController.LateUpdate))]
|
[HarmonyPatch(typeof(SolanumAnimController), nameof(SolanumAnimController.LateUpdate))]
|
||||||
public static bool SolanumLateUpdateReplacement(SolanumAnimController __instance)
|
public static bool SolanumLateUpdateReplacement(SolanumAnimController __instance)
|
||||||
{
|
{
|
||||||
|
if (!QSBWorldSync.AllObjectsReady)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (__instance._animatorStateEvents == null)
|
if (__instance._animatorStateEvents == null)
|
||||||
{
|
{
|
||||||
__instance._animatorStateEvents = __instance._animator.GetBehaviour<AnimatorStateEvents>();
|
__instance._animatorStateEvents = __instance._animator.GetBehaviour<AnimatorStateEvents>();
|
||||||
@ -43,6 +48,10 @@ public class SolanumPatches : QSBPatch
|
|||||||
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.Update))]
|
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.Update))]
|
||||||
public static bool ReplacementUpdate(NomaiConversationManager __instance)
|
public static bool ReplacementUpdate(NomaiConversationManager __instance)
|
||||||
{
|
{
|
||||||
|
if (!QSBWorldSync.AllObjectsReady)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
|
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
|
||||||
__instance._playerInWatchVolume = qsbObj.Trigger.Occupants.Any();
|
__instance._playerInWatchVolume = qsbObj.Trigger.Occupants.Any();
|
||||||
|
|
||||||
|
@ -37,12 +37,15 @@ internal abstract class QSBRotatingElements<T, U> : LinkedWorldObject<T, U>
|
|||||||
{
|
{
|
||||||
base.OnRemoval();
|
base.OnRemoval();
|
||||||
|
|
||||||
|
if (_qsbLightSensors != null)
|
||||||
|
{
|
||||||
foreach (var lightSensor in _qsbLightSensors)
|
foreach (var lightSensor in _qsbLightSensors)
|
||||||
{
|
{
|
||||||
lightSensor.OnDetectLocalLight -= OnDetectLocalLight;
|
lightSensor.OnDetectLocalLight -= OnDetectLocalLight;
|
||||||
lightSensor.OnDetectLocalDarkness -= OnDetectLocalDarkness;
|
lightSensor.OnDetectLocalDarkness -= OnDetectLocalDarkness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnDetectLocalLight()
|
private void OnDetectLocalLight()
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
|
using OWML.Common;
|
||||||
using QSB.ItemSync.Messages;
|
using QSB.ItemSync.Messages;
|
||||||
using QSB.ItemSync.WorldObjects.Sockets;
|
using QSB.ItemSync.WorldObjects.Sockets;
|
||||||
using QSB.Messaging;
|
using QSB.Messaging;
|
||||||
using QSB.Player;
|
using QSB.Player;
|
||||||
using QSB.SectorSync.WorldObjects;
|
using QSB.SectorSync.WorldObjects;
|
||||||
|
using QSB.Utility;
|
||||||
using QSB.WorldSync;
|
using QSB.WorldSync;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
@ -17,7 +17,9 @@ public class MeteorManager : WorldObjectManager
|
|||||||
// wait for all late initializers (which includes meteor launchers) to finish
|
// wait for all late initializers (which includes meteor launchers) to finish
|
||||||
await UniTask.WaitUntil(() => LateInitializerManager.isDoneInitializing, cancellationToken: ct);
|
await UniTask.WaitUntil(() => LateInitializerManager.isDoneInitializing, cancellationToken: ct);
|
||||||
|
|
||||||
WhiteHoleVolume = QSBWorldSync.GetUnityObject<WhiteHoleVolume>();
|
// NH can make multiple so ensure its the stock whitehole
|
||||||
|
var whiteHole = QSBWorldSync.GetUnityObjects<AstroObject>().First(x => x.GetAstroObjectName() == AstroObject.Name.WhiteHole);
|
||||||
|
WhiteHoleVolume = whiteHole?.GetComponentInChildren<WhiteHoleVolume>();
|
||||||
QSBWorldSync.Init<QSBFragment, FragmentIntegrity>();
|
QSBWorldSync.Init<QSBFragment, FragmentIntegrity>();
|
||||||
QSBWorldSync.Init<QSBMeteorLauncher, MeteorLauncher>();
|
QSBWorldSync.Init<QSBMeteorLauncher, MeteorLauncher>();
|
||||||
QSBWorldSync.Init<QSBMeteor, MeteorController>();
|
QSBWorldSync.Init<QSBMeteor, MeteorController>();
|
||||||
|
@ -37,6 +37,13 @@ internal class ModelShipManager : WorldObjectManager
|
|||||||
|
|
||||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
// NH can remove this
|
||||||
|
var modelShip = QSBWorldSync.GetUnityObject<RemoteFlightConsole>()._modelShipBody;
|
||||||
|
if (!modelShip)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (QSBCore.IsHost)
|
if (QSBCore.IsHost)
|
||||||
{
|
{
|
||||||
Instantiate(QSBNetworkManager.singleton.ModelShipPrefab).SpawnWithServerAuthority();
|
Instantiate(QSBNetworkManager.singleton.ModelShipPrefab).SpawnWithServerAuthority();
|
||||||
|
@ -69,11 +69,14 @@ public class QSBCore : ModBehaviour
|
|||||||
public static DebugSettings DebugSettings { get; private set; } = new();
|
public static DebugSettings DebugSettings { get; private set; } = new();
|
||||||
public static Storage Storage { get; private set; } = new();
|
public static Storage Storage { get; private set; } = new();
|
||||||
|
|
||||||
|
public const string NEW_HORIZONS = "xen.NewHorizons";
|
||||||
|
public const string NEW_HORIZONS_COMPAT = "xen.NHQSBCompat";
|
||||||
|
|
||||||
public static readonly string[] IncompatibleMods =
|
public static readonly string[] IncompatibleMods =
|
||||||
{
|
{
|
||||||
// incompatible mods
|
// incompatible mods
|
||||||
"Raicuparta.NomaiVR",
|
"Raicuparta.NomaiVR",
|
||||||
"xen.NewHorizons",
|
// "xen.NewHorizons",
|
||||||
"Vesper.AutoResume",
|
"Vesper.AutoResume",
|
||||||
"Vesper.OuterWildsMMO",
|
"Vesper.OuterWildsMMO",
|
||||||
"_nebula.StopTime",
|
"_nebula.StopTime",
|
||||||
@ -127,6 +130,8 @@ public class QSBCore : ModBehaviour
|
|||||||
Helper = ModHelper;
|
Helper = ModHelper;
|
||||||
DebugLog.ToConsole($"* Start of QSB version {QSBVersion} - authored by {Helper.Manifest.Author}", MessageType.Info);
|
DebugLog.ToConsole($"* Start of QSB version {QSBVersion} - authored by {Helper.Manifest.Author}", MessageType.Info);
|
||||||
|
|
||||||
|
CheckCompatibilityMods();
|
||||||
|
|
||||||
DebugSettings = Helper.Storage.Load<DebugSettings>("debugsettings.json") ?? new DebugSettings();
|
DebugSettings = Helper.Storage.Load<DebugSettings>("debugsettings.json") ?? new DebugSettings();
|
||||||
Storage = Helper.Storage.Load<Storage>("storage.json") ?? new Storage();
|
Storage = Helper.Storage.Load<Storage>("storage.json") ?? new Storage();
|
||||||
|
|
||||||
@ -270,6 +275,26 @@ public class QSBCore : ModBehaviour
|
|||||||
DebugLog.ToConsole($"DEBUG MODE = {DebugSettings.DebugMode}");
|
DebugLog.ToConsole($"DEBUG MODE = {DebugSettings.DebugMode}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckCompatibilityMods()
|
||||||
|
{
|
||||||
|
var mainMod = "";
|
||||||
|
var compatMod = "";
|
||||||
|
var missingCompat = false;
|
||||||
|
|
||||||
|
if (Helper.Interaction.ModExists(NEW_HORIZONS) && !Helper.Interaction.ModExists(NEW_HORIZONS_COMPAT))
|
||||||
|
{
|
||||||
|
mainMod = NEW_HORIZONS;
|
||||||
|
compatMod = NEW_HORIZONS_COMPAT;
|
||||||
|
missingCompat = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingCompat)
|
||||||
|
{
|
||||||
|
DebugLog.ToConsole($"FATAL - You have mod \"{mainMod}\" installed, which is not compatible with QSB without the compatibility mod \"{compatMod}\". " +
|
||||||
|
$"Either disable the mod, or install/enable the compatibility mod.", MessageType.Fatal);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -91,12 +91,16 @@ public class QSBSectorManager : WorldObjectManager
|
|||||||
|
|
||||||
// time loop spinning ring
|
// time loop spinning ring
|
||||||
{
|
{
|
||||||
|
// NH can remove this
|
||||||
var TimeLoopRing_Body = GameObject.Find("TimeLoopRing_Body");
|
var TimeLoopRing_Body = GameObject.Find("TimeLoopRing_Body");
|
||||||
|
if (TimeLoopRing_Body)
|
||||||
|
{
|
||||||
var Sector_TimeLoopInterior = GameObject.Find("Sector_TimeLoopInterior").GetComponent<Sector>();
|
var Sector_TimeLoopInterior = GameObject.Find("Sector_TimeLoopInterior").GetComponent<Sector>();
|
||||||
// use the same trigger as the parent sector
|
// use the same trigger as the parent sector
|
||||||
FakeSector.Create(TimeLoopRing_Body, Sector_TimeLoopInterior,
|
FakeSector.Create(TimeLoopRing_Body, Sector_TimeLoopInterior,
|
||||||
x => x._triggerRoot = Sector_TimeLoopInterior._triggerRoot);
|
x => x._triggerRoot = Sector_TimeLoopInterior._triggerRoot);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TH elevators
|
// TH elevators
|
||||||
foreach (var elevator in QSBWorldSync.GetUnityObjects<Elevator>())
|
foreach (var elevator in QSBWorldSync.GetUnityObjects<Elevator>())
|
||||||
|
@ -7,6 +7,7 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace QSB.Syncs.Occasional;
|
namespace QSB.Syncs.Occasional;
|
||||||
|
|
||||||
|
// BUG: somehow, not including DontDestroyOnLoad things makes this fuck up with NH
|
||||||
internal class OccasionalManager : WorldObjectManager
|
internal class OccasionalManager : WorldObjectManager
|
||||||
{
|
{
|
||||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||||
@ -21,6 +22,11 @@ internal class OccasionalManager : WorldObjectManager
|
|||||||
|
|
||||||
foreach (var proxy in cannon._realDebrisSectorProxies)
|
foreach (var proxy in cannon._realDebrisSectorProxies)
|
||||||
{
|
{
|
||||||
|
// NH can remove these
|
||||||
|
if (!proxy)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
SpawnOccasional(proxy.transform.root.GetAttachedOWRigidbody(), gdBody);
|
SpawnOccasional(proxy.transform.root.GetAttachedOWRigidbody(), gdBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using QSB.Utility;
|
|||||||
using QSB.Utility.LinkedWorldObject;
|
using QSB.Utility.LinkedWorldObject;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -20,7 +21,8 @@ namespace QSB.WorldSync;
|
|||||||
public static class QSBWorldSync
|
public static class QSBWorldSync
|
||||||
{
|
{
|
||||||
public static WorldObjectManager[] Managers;
|
public static WorldObjectManager[] Managers;
|
||||||
public static string WorldObjectsHash { get; private set; }
|
public static Dictionary<string, string> ManagerHashes { get; private set; } = new();
|
||||||
|
public static Dictionary<string, List<IWorldObject>> ManagerToBuiltObjects { get; private set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set when all WorldObjectManagers have called Init() on all their objects (AKA all the objects are created)
|
/// Set when all WorldObjectManagers have called Init() on all their objects (AKA all the objects are created)
|
||||||
@ -85,12 +87,20 @@ public static class QSBWorldSync
|
|||||||
|
|
||||||
DeterministicManager.OnWorldObjectsAdded();
|
DeterministicManager.OnWorldObjectsAdded();
|
||||||
|
|
||||||
WorldObjectsHash = WorldObjects.Select(x => x.GetType().Name).GetMD5Hash();
|
foreach (var item in ManagerToBuiltObjects)
|
||||||
DebugLog.DebugWrite($"WorldObject hash is {WorldObjectsHash}");
|
{
|
||||||
|
var worldObjects = item.Value;
|
||||||
|
var hash = worldObjects.Select(x => x.GetType().Name).GetMD5Hash();
|
||||||
|
ManagerHashes[item.Key] = hash;
|
||||||
|
}
|
||||||
|
|
||||||
if (!QSBCore.IsHost)
|
if (!QSBCore.IsHost)
|
||||||
{
|
{
|
||||||
new WorldObjectsHashMessage().Send();
|
foreach (var item in ManagerHashes)
|
||||||
|
{
|
||||||
|
new WorldObjectsHashMessage(item.Key, item.Value).Send();
|
||||||
|
}
|
||||||
|
|
||||||
new RequestLinksMessage().Send();
|
new RequestLinksMessage().Send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +145,9 @@ public static class QSBWorldSync
|
|||||||
AllObjectsAdded = false;
|
AllObjectsAdded = false;
|
||||||
AllObjectsReady = false;
|
AllObjectsReady = false;
|
||||||
|
|
||||||
|
ManagerToBuiltObjects = new();
|
||||||
|
ManagerHashes = new();
|
||||||
|
|
||||||
GameReset();
|
GameReset();
|
||||||
|
|
||||||
foreach (var worldObject in WorldObjects)
|
foreach (var worldObject in WorldObjects)
|
||||||
@ -193,7 +206,8 @@ public static class QSBWorldSync
|
|||||||
{
|
{
|
||||||
// So objects have time to be deleted, made, whatever
|
// So objects have time to be deleted, made, whatever
|
||||||
// i.e. wait until Start has been called
|
// i.e. wait until Start has been called
|
||||||
Delay.RunNextFrame(() => BuildWorldObjects(loadScene).Forget());
|
// TODO: see if this number of frames actually works. TWEAK!
|
||||||
|
Delay.RunFramesLater(10, () => BuildWorldObjects(loadScene).Forget());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,12 +320,55 @@ public static class QSBWorldSync
|
|||||||
=> GetUnityObjects<TUnityObject>().Single();
|
=> GetUnityObjects<TUnityObject>().Single();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// not deterministic across platforms
|
/// not deterministic across platforms.
|
||||||
|
/// excludes prefabs and DontDestroyOnLoad objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IEnumerable<TUnityObject> GetUnityObjects<TUnityObject>()
|
public static IEnumerable<TUnityObject> GetUnityObjects<TUnityObject>()
|
||||||
where TUnityObject : MonoBehaviour
|
where TUnityObject : MonoBehaviour
|
||||||
=> Resources.FindObjectsOfTypeAll<TUnityObject>()
|
=> Resources.FindObjectsOfTypeAll<TUnityObject>()
|
||||||
.Where(x => x.gameObject.scene.name != null);
|
.Where(x => x.gameObject.scene.name is not (null or "DontDestroyOnLoad"));
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/48570616
|
||||||
|
public static string NameOfCallingClass()
|
||||||
|
{
|
||||||
|
string fullName;
|
||||||
|
Type declaringType;
|
||||||
|
var skipFrames = 2;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var method = new StackFrame(skipFrames, false).GetMethod();
|
||||||
|
declaringType = method.DeclaringType;
|
||||||
|
if (declaringType == null)
|
||||||
|
{
|
||||||
|
return method.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipFrames++;
|
||||||
|
fullName = CleanupFullName(declaringType.FullName);
|
||||||
|
}
|
||||||
|
while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase) || declaringType == typeof(QSBWorldSync));
|
||||||
|
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CleanupFullName(string fullName)
|
||||||
|
{
|
||||||
|
var ret = fullName;
|
||||||
|
|
||||||
|
var indexOfPlus = fullName.LastIndexOf('+');
|
||||||
|
if (indexOfPlus != -1)
|
||||||
|
{
|
||||||
|
ret = fullName.Remove(indexOfPlus);
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexOfDot = ret.LastIndexOf('.');
|
||||||
|
if (indexOfDot != -1)
|
||||||
|
{
|
||||||
|
ret = ret.Substring(indexOfDot + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Init<TWorldObject, TUnityObject>()
|
public static void Init<TWorldObject, TUnityObject>()
|
||||||
where TWorldObject : WorldObject<TUnityObject>, new()
|
where TWorldObject : WorldObject<TUnityObject>, new()
|
||||||
@ -382,6 +439,17 @@ public static class QSBWorldSync
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var className = NameOfCallingClass();
|
||||||
|
|
||||||
|
if (!ManagerToBuiltObjects.ContainsKey(className))
|
||||||
|
{
|
||||||
|
ManagerToBuiltObjects.Add(className, new List<IWorldObject> { worldObject });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ManagerToBuiltObjects[className].Add(worldObject);
|
||||||
|
}
|
||||||
|
|
||||||
WorldObjects.Add(worldObject);
|
WorldObjects.Add(worldObject);
|
||||||
RequestInitialStatesMessage.SendInitialState += worldObject.SendInitialState;
|
RequestInitialStatesMessage.SendInitialState += worldObject.SendInitialState;
|
||||||
|
|
||||||
|
@ -8,23 +8,19 @@ namespace QSB.WorldSync;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// sends QSBWorldSync.WorldObjectsHash to the server for sanity checking
|
/// sends QSBWorldSync.WorldObjectsHash to the server for sanity checking
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class WorldObjectsHashMessage : QSBMessage<string>
|
internal class WorldObjectsHashMessage : QSBMessage<(string managerName, string hash)>
|
||||||
{
|
{
|
||||||
public WorldObjectsHashMessage() : base(QSBWorldSync.WorldObjectsHash) => To = 0;
|
public WorldObjectsHashMessage(string managerName, string hash) : base((managerName, hash)) => To = 0;
|
||||||
|
|
||||||
public override void OnReceiveRemote()
|
public override void OnReceiveRemote()
|
||||||
{
|
{
|
||||||
var serverHash = QSBWorldSync.WorldObjectsHash;
|
var serverHash = QSBWorldSync.ManagerHashes[Data.managerName];
|
||||||
|
|
||||||
if (serverHash != Data)
|
if (serverHash != Data.hash)
|
||||||
{
|
{
|
||||||
// oh fuck oh no oh god
|
// oh fuck oh no oh god
|
||||||
DebugLog.ToConsole($"Kicking {From} because their WorldObjects hash is wrong. (server:{serverHash}, client:{Data})", MessageType.Error);
|
DebugLog.ToConsole($"Kicking {From} because their WorldObjects hash for {Data.managerName} is wrong. (server:{serverHash}, client:{Data.hash})", MessageType.Error);
|
||||||
new PlayerKickMessage(From, $"WorldObject hash error. (Server:{serverHash}, Client:{Data})").Send();
|
new PlayerKickMessage(From, $"WorldObject hash error for {Data.managerName}. (Server:{serverHash}, Client:{Data.hash})").Send();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DebugLog.DebugWrite($"WorldObject hash from {From} verified!", MessageType.Success);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user