mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-29 00:32:43 +00:00
commit
206dadcfdb
@ -17,6 +17,11 @@ public class SolanumPatches : QSBPatch
|
||||
[HarmonyPatch(typeof(SolanumAnimController), nameof(SolanumAnimController.LateUpdate))]
|
||||
public static bool SolanumLateUpdateReplacement(SolanumAnimController __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._animatorStateEvents == null)
|
||||
{
|
||||
__instance._animatorStateEvents = __instance._animator.GetBehaviour<AnimatorStateEvents>();
|
||||
@ -43,6 +48,10 @@ public class SolanumPatches : QSBPatch
|
||||
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.Update))]
|
||||
public static bool ReplacementUpdate(NomaiConversationManager __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
|
||||
__instance._playerInWatchVolume = qsbObj.Trigger.Occupants.Any();
|
||||
|
||||
@ -206,4 +215,4 @@ public class SolanumPatches : QSBPatch
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,13 @@ internal abstract class QSBRotatingElements<T, U> : LinkedWorldObject<T, U>
|
||||
{
|
||||
base.OnRemoval();
|
||||
|
||||
foreach (var lightSensor in _qsbLightSensors)
|
||||
if (_qsbLightSensors != null)
|
||||
{
|
||||
lightSensor.OnDetectLocalLight -= OnDetectLocalLight;
|
||||
lightSensor.OnDetectLocalDarkness -= OnDetectLocalDarkness;
|
||||
foreach (var lightSensor in _qsbLightSensors)
|
||||
{
|
||||
lightSensor.OnDetectLocalLight -= OnDetectLocalLight;
|
||||
lightSensor.OnDetectLocalDarkness -= OnDetectLocalDarkness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using OWML.Common;
|
||||
using QSB.ItemSync.Messages;
|
||||
using QSB.ItemSync.WorldObjects.Sockets;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.SectorSync.WorldObjects;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
@ -17,7 +17,9 @@ public class MeteorManager : WorldObjectManager
|
||||
// wait for all late initializers (which includes meteor launchers) to finish
|
||||
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<QSBMeteorLauncher, MeteorLauncher>();
|
||||
QSBWorldSync.Init<QSBMeteor, MeteorController>();
|
||||
|
@ -37,6 +37,13 @@ internal class ModelShipManager : WorldObjectManager
|
||||
|
||||
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)
|
||||
{
|
||||
Instantiate(QSBNetworkManager.singleton.ModelShipPrefab).SpawnWithServerAuthority();
|
||||
|
@ -69,11 +69,14 @@ public class QSBCore : ModBehaviour
|
||||
public static DebugSettings DebugSettings { 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 =
|
||||
{
|
||||
// incompatible mods
|
||||
"Raicuparta.NomaiVR",
|
||||
"xen.NewHorizons",
|
||||
// "xen.NewHorizons",
|
||||
"Vesper.AutoResume",
|
||||
"Vesper.OuterWildsMMO",
|
||||
"_nebula.StopTime",
|
||||
@ -127,6 +130,8 @@ public class QSBCore : ModBehaviour
|
||||
Helper = ModHelper;
|
||||
DebugLog.ToConsole($"* Start of QSB version {QSBVersion} - authored by {Helper.Manifest.Author}", MessageType.Info);
|
||||
|
||||
CheckCompatibilityMods();
|
||||
|
||||
DebugSettings = Helper.Storage.Load<DebugSettings>("debugsettings.json") ?? new DebugSettings();
|
||||
Storage = Helper.Storage.Load<Storage>("storage.json") ?? new Storage();
|
||||
|
||||
@ -270,6 +275,26 @@ public class QSBCore : ModBehaviour
|
||||
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,11 +91,15 @@ public class QSBSectorManager : WorldObjectManager
|
||||
|
||||
// time loop spinning ring
|
||||
{
|
||||
// NH can remove this
|
||||
var TimeLoopRing_Body = GameObject.Find("TimeLoopRing_Body");
|
||||
var Sector_TimeLoopInterior = GameObject.Find("Sector_TimeLoopInterior").GetComponent<Sector>();
|
||||
// use the same trigger as the parent sector
|
||||
FakeSector.Create(TimeLoopRing_Body, Sector_TimeLoopInterior,
|
||||
x => x._triggerRoot = Sector_TimeLoopInterior._triggerRoot);
|
||||
if (TimeLoopRing_Body)
|
||||
{
|
||||
var Sector_TimeLoopInterior = GameObject.Find("Sector_TimeLoopInterior").GetComponent<Sector>();
|
||||
// use the same trigger as the parent sector
|
||||
FakeSector.Create(TimeLoopRing_Body, Sector_TimeLoopInterior,
|
||||
x => x._triggerRoot = Sector_TimeLoopInterior._triggerRoot);
|
||||
}
|
||||
}
|
||||
|
||||
// TH elevators
|
||||
|
@ -7,6 +7,7 @@ using System.Threading;
|
||||
|
||||
namespace QSB.Syncs.Occasional;
|
||||
|
||||
// BUG: somehow, not including DontDestroyOnLoad things makes this fuck up with NH
|
||||
internal class OccasionalManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
@ -21,6 +22,11 @@ internal class OccasionalManager : WorldObjectManager
|
||||
|
||||
foreach (var proxy in cannon._realDebrisSectorProxies)
|
||||
{
|
||||
// NH can remove these
|
||||
if (!proxy)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
SpawnOccasional(proxy.transform.root.GetAttachedOWRigidbody(), gdBody);
|
||||
}
|
||||
|
||||
@ -58,4 +64,4 @@ internal class OccasionalManager : WorldObjectManager
|
||||
|
||||
Bodies.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ using QSB.Utility;
|
||||
using QSB.Utility.LinkedWorldObject;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
@ -20,7 +21,8 @@ namespace QSB.WorldSync;
|
||||
public static class QSBWorldSync
|
||||
{
|
||||
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>
|
||||
/// 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();
|
||||
|
||||
WorldObjectsHash = WorldObjects.Select(x => x.GetType().Name).GetMD5Hash();
|
||||
DebugLog.DebugWrite($"WorldObject hash is {WorldObjectsHash}");
|
||||
foreach (var item in ManagerToBuiltObjects)
|
||||
{
|
||||
var worldObjects = item.Value;
|
||||
var hash = worldObjects.Select(x => x.GetType().Name).GetMD5Hash();
|
||||
ManagerHashes[item.Key] = hash;
|
||||
}
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
new WorldObjectsHashMessage().Send();
|
||||
foreach (var item in ManagerHashes)
|
||||
{
|
||||
new WorldObjectsHashMessage(item.Key, item.Value).Send();
|
||||
}
|
||||
|
||||
new RequestLinksMessage().Send();
|
||||
}
|
||||
|
||||
@ -135,6 +145,9 @@ public static class QSBWorldSync
|
||||
AllObjectsAdded = false;
|
||||
AllObjectsReady = false;
|
||||
|
||||
ManagerToBuiltObjects = new();
|
||||
ManagerHashes = new();
|
||||
|
||||
GameReset();
|
||||
|
||||
foreach (var worldObject in WorldObjects)
|
||||
@ -193,7 +206,8 @@ public static class QSBWorldSync
|
||||
{
|
||||
// So objects have time to be deleted, made, whatever
|
||||
// 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();
|
||||
|
||||
/// <summary>
|
||||
/// not deterministic across platforms
|
||||
/// not deterministic across platforms.
|
||||
/// excludes prefabs and DontDestroyOnLoad objects.
|
||||
/// </summary>
|
||||
public static IEnumerable<TUnityObject> GetUnityObjects<TUnityObject>()
|
||||
where TUnityObject : MonoBehaviour
|
||||
=> 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>()
|
||||
where TWorldObject : WorldObject<TUnityObject>, new()
|
||||
@ -382,6 +439,17 @@ public static class QSBWorldSync
|
||||
return;
|
||||
}
|
||||
|
||||
var className = NameOfCallingClass();
|
||||
|
||||
if (!ManagerToBuiltObjects.ContainsKey(className))
|
||||
{
|
||||
ManagerToBuiltObjects.Add(className, new List<IWorldObject> { worldObject });
|
||||
}
|
||||
else
|
||||
{
|
||||
ManagerToBuiltObjects[className].Add(worldObject);
|
||||
}
|
||||
|
||||
WorldObjects.Add(worldObject);
|
||||
RequestInitialStatesMessage.SendInitialState += worldObject.SendInitialState;
|
||||
|
||||
|
@ -8,23 +8,19 @@ namespace QSB.WorldSync;
|
||||
/// <summary>
|
||||
/// sends QSBWorldSync.WorldObjectsHash to the server for sanity checking
|
||||
/// </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()
|
||||
{
|
||||
var serverHash = QSBWorldSync.WorldObjectsHash;
|
||||
var serverHash = QSBWorldSync.ManagerHashes[Data.managerName];
|
||||
|
||||
if (serverHash != Data)
|
||||
if (serverHash != Data.hash)
|
||||
{
|
||||
// oh fuck oh no oh god
|
||||
DebugLog.ToConsole($"Kicking {From} because their WorldObjects hash is wrong. (server:{serverHash}, client:{Data})", MessageType.Error);
|
||||
new PlayerKickMessage(From, $"WorldObject hash error. (Server:{serverHash}, Client:{Data})").Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.DebugWrite($"WorldObject hash from {From} verified!", MessageType.Success);
|
||||
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 for {Data.managerName}. (Server:{serverHash}, Client:{Data.hash})").Send();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user