Merge pull request #581 from misternebula/nh-stuff

Nh stuff
This commit is contained in:
_nebula 2022-12-10 22:05:30 +00:00 committed by GitHub
commit 206dadcfdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 150 additions and 28 deletions

View File

@ -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();

View File

@ -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()
{ {

View File

@ -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;

View File

@ -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>();

View File

@ -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();

View File

@ -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);
}
}
} }
/* /*

View File

@ -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>())

View File

@ -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);
} }

View File

@ -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;

View File

@ -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);
} }
} }
} }