363 lines
9.9 KiB
C#
Raw Normal View History

2022-01-28 20:49:07 -08:00
using Cysharp.Threading.Tasks;
using MonoMod.Utils;
2022-01-28 20:49:07 -08:00
using OWML.Common;
2022-01-08 11:41:55 +00:00
using QSB.ConversationSync.Patches;
using QSB.LogSync;
using QSB.Messaging;
using QSB.Player.TransformSync;
2022-01-12 19:18:59 -08:00
using QSB.TriggerSync.WorldObjects;
2020-09-29 21:34:46 +01:00
using QSB.Utility;
2021-12-07 15:56:08 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
2022-01-28 20:49:07 -08:00
using System.Threading;
2020-12-23 22:43:05 +00:00
using UnityEngine;
2020-08-13 19:25:12 +02:00
namespace QSB.WorldSync;
public static class QSBWorldSync
2020-08-13 19:25:12 +02:00
{
public static WorldObjectManager[] Managers;
/// <summary>
/// Set when all WorldObjectManagers have called Init() on all their objects (AKA all the objects are created)
/// </summary>
public static bool AllObjectsAdded { get; private set; }
/// <summary>
/// Set when all WorldObjects have finished running Init()
/// </summary>
public static bool AllObjectsReady { get; private set; }
private static CancellationTokenSource _cts;
private static readonly Dictionary<WorldObjectManager, UniTask> _managersBuilding = new();
private static readonly Dictionary<IWorldObject, UniTask> _objectsIniting = new();
public static async UniTaskVoid BuildWorldObjects(OWScene scene)
2020-12-02 21:29:53 +00:00
{
if (_cts != null)
{
return;
}
_cts = new CancellationTokenSource();
2022-01-28 21:05:04 -08:00
if (!PlayerTransformSync.LocalInstance)
{
DebugLog.ToConsole("Warning - Tried to build WorldObjects when LocalPlayer is not ready! Building when ready...", MessageType.Warning);
await UniTask.WaitUntil(() => PlayerTransformSync.LocalInstance, cancellationToken: _cts.Token);
}
2022-01-28 21:05:04 -08:00
GameInit();
2022-01-28 21:05:04 -08:00
foreach (var manager in Managers)
{
2022-02-25 00:25:24 -08:00
switch (manager.WorldObjectScene)
{
2022-02-25 00:25:24 -08:00
case WorldObjectScene.SolarSystem when QSBSceneManager.CurrentScene != OWScene.SolarSystem:
case WorldObjectScene.Eye when QSBSceneManager.CurrentScene != OWScene.EyeOfTheUniverse:
continue;
}
var task = UniTask.Create(async () =>
{
await manager.Try("building world objects", async () =>
2022-01-28 20:49:07 -08:00
{
await manager.BuildWorldObjects(scene, _cts.Token);
DebugLog.DebugWrite($"Built {manager}", MessageType.Info);
2022-01-28 20:49:07 -08:00
});
_managersBuilding.Remove(manager);
});
if (!task.Status.IsCompleted())
{
_managersBuilding.Add(manager, task);
}
}
await _managersBuilding.Values;
if (_cts == null)
{
return;
}
2022-01-28 20:49:07 -08:00
AllObjectsAdded = true;
DebugLog.DebugWrite("World Objects added.", MessageType.Success);
await _objectsIniting.Values;
if (_cts == null)
{
return;
}
2022-01-28 20:49:07 -08:00
AllObjectsReady = true;
DebugLog.DebugWrite("World Objects ready.", MessageType.Success);
2022-01-28 20:49:07 -08:00
DeterministicManager.WorldObjectsReady();
if (!QSBCore.IsHost)
{
new RequestInitialStatesMessage().Send();
}
}
public static void RemoveWorldObjects()
{
if (_cts == null)
{
return;
}
if (_managersBuilding.Count > 0)
{
DebugLog.DebugWrite($"{_managersBuilding.Count} managers still building", MessageType.Warning);
}
if (_objectsIniting.Count > 0)
{
DebugLog.DebugWrite($"{_objectsIniting.Count} objects still initing", MessageType.Warning);
}
_cts.Cancel();
_cts.Dispose();
_cts = null;
_managersBuilding.Clear();
_objectsIniting.Clear();
AllObjectsAdded = false;
AllObjectsReady = false;
GameReset();
foreach (var item in WorldObjects)
{
item.Try("removing", item.OnRemoval);
}
WorldObjects.Clear();
UnityObjectsToWorldObjects.Clear();
foreach (var manager in Managers)
{
manager.Try("unbuilding world objects", manager.UnbuildWorldObjects);
}
}
2020-09-22 21:11:29 +01:00
// =======================================================================================================
2020-12-14 21:41:56 +01:00
public static readonly List<CharacterDialogueTree> OldDialogueTrees = new();
public static readonly Dictionary<string, bool> DialogueConditions = new();
private static readonly Dictionary<string, bool> PersistentConditions = new();
public static readonly List<FactReveal> ShipLogFacts = new();
2022-01-08 11:41:55 +00:00
private static readonly List<IWorldObject> WorldObjects = new();
private static readonly Dictionary<MonoBehaviour, IWorldObject> UnityObjectsToWorldObjects = new();
2022-01-08 11:41:55 +00:00
private static void GameInit()
{
DebugLog.DebugWrite($"GameInit QSBWorldSync", MessageType.Info);
2022-01-08 11:41:55 +00:00
OldDialogueTrees.Clear();
OldDialogueTrees.AddRange(GetUnityObjects<CharacterDialogueTree>().SortDeterministic());
2022-01-08 11:41:55 +00:00
if (!QSBCore.IsHost)
{
return;
2022-01-08 11:41:55 +00:00
}
DebugLog.DebugWrite($"DIALOGUE CONDITIONS :");
DialogueConditions.Clear();
DialogueConditions.AddRange(DialogueConditionManager.SharedInstance._dictConditions);
foreach (var item in DialogueConditions)
2022-01-08 11:41:55 +00:00
{
DebugLog.DebugWrite($"- {item.Key}, {item.Value}");
2022-01-08 11:41:55 +00:00
}
DebugLog.DebugWrite($"PERSISTENT CONDITIONS :");
var dictConditions = PlayerData._currentGameSave.dictConditions;
var syncedConditions = dictConditions.Where(x => ConversationPatches.PersistentConditionsToSync.Contains(x.Key));
PersistentConditions.Clear();
PersistentConditions.AddRange(syncedConditions.ToDictionary(x => x.Key, x => x.Value));
foreach (var item in PersistentConditions)
2021-02-25 22:45:32 +00:00
{
DebugLog.DebugWrite($"- {item.Key}, {item.Value}");
}
}
2021-06-18 22:38:32 +01:00
private static void GameReset()
{
DebugLog.DebugWrite($"GameReset QSBWorldSync", MessageType.Info);
2021-12-11 22:04:48 -08:00
OldDialogueTrees.Clear();
DialogueConditions.Clear();
PersistentConditions.Clear();
ShipLogFacts.Clear();
}
2020-09-06 09:07:31 +01:00
public static IEnumerable<IWorldObject> GetWorldObjects() => WorldObjects;
2021-06-18 22:38:32 +01:00
public static IEnumerable<TWorldObject> GetWorldObjects<TWorldObject>()
where TWorldObject : IWorldObject
=> WorldObjects.OfType<TWorldObject>();
2021-06-18 22:38:32 +01:00
public static TWorldObject GetWorldObject<TWorldObject>(this int objectId)
where TWorldObject : IWorldObject
{
if (!WorldObjects.IsInRange(objectId))
{
DebugLog.ToConsole($"Warning - Tried to find {typeof(TWorldObject).Name} id {objectId}. Count is {WorldObjects.Count}.", MessageType.Warning);
return default;
2021-03-18 16:57:56 +00:00
}
2021-02-18 10:34:35 +00:00
if (WorldObjects[objectId] is not TWorldObject worldObject)
2020-12-02 21:29:53 +00:00
{
DebugLog.ToConsole($"Error - {typeof(TWorldObject).Name} id {objectId} is actually {WorldObjects[objectId].GetType().Name}.", MessageType.Error);
return default;
2021-12-15 21:09:10 +00:00
}
return worldObject;
}
public static TWorldObject GetWorldObject<TWorldObject>(this MonoBehaviour unityObject)
where TWorldObject : IWorldObject
{
if (!unityObject)
2021-12-15 21:09:10 +00:00
{
DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:NULL, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Error);
return default;
2021-12-15 21:09:10 +00:00
}
if (!UnityObjectsToWorldObjects.TryGetValue(unityObject, out var worldObject))
2021-12-15 21:09:10 +00:00
{
DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{unityObject.GetType().Name}, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Error);
return default;
2022-01-12 19:12:37 -08:00
}
return (TWorldObject)worldObject;
}
2022-01-25 02:13:48 -08:00
/// <summary>
/// not deterministic across platforms
/// </summary>
public static IEnumerable<TUnityObject> GetUnityObjects<TUnityObject>()
where TUnityObject : MonoBehaviour
=> Resources.FindObjectsOfTypeAll<TUnityObject>()
.Where(x => x.gameObject.scene.name != null);
public static void Init<TWorldObject, TUnityObject>()
where TWorldObject : WorldObject<TUnityObject>, new()
where TUnityObject : MonoBehaviour
{
var list = GetUnityObjects<TUnityObject>().SortDeterministic();
Init<TWorldObject, TUnityObject>(list);
}
2022-01-12 19:12:37 -08:00
public static void Init<TWorldObject, TUnityObject>(params Type[] typesToExclude)
where TWorldObject : WorldObject<TUnityObject>, new()
where TUnityObject : MonoBehaviour
{
var list = GetUnityObjects<TUnityObject>()
.Where(x => !typesToExclude.Contains(x.GetType()))
.SortDeterministic();
Init<TWorldObject, TUnityObject>(list);
}
2022-01-28 23:22:13 -08:00
/// <summary>
/// make sure to sort the list!
/// </summary>
public static void Init<TWorldObject, TUnityObject>(IEnumerable<TUnityObject> listToInitFrom)
where TWorldObject : WorldObject<TUnityObject>, new()
where TUnityObject : MonoBehaviour
{
foreach (var item in listToInitFrom)
{
var obj = new TWorldObject
{
AttachedObject = item,
ObjectId = WorldObjects.Count
};
AddAndInit(obj, item);
2020-12-02 21:29:53 +00:00
}
}
2020-09-06 09:07:31 +01:00
public static void Init<TWorldObject, TUnityObject>(Func<TUnityObject, OWTriggerVolume> triggerSelector)
where TWorldObject : QSBTrigger<TUnityObject>, new()
where TUnityObject : MonoBehaviour
{
var list = GetUnityObjects<TUnityObject>().SortDeterministic();
foreach (var owner in list)
2020-12-11 13:14:58 +00:00
{
var item = triggerSelector(owner);
if (!item)
2020-12-11 13:14:58 +00:00
{
continue;
2020-12-11 13:14:58 +00:00
}
2021-06-18 22:38:32 +01:00
var obj = new TWorldObject
{
AttachedObject = item,
ObjectId = WorldObjects.Count,
TriggerOwner = owner
};
AddAndInit(obj, item);
2020-12-11 13:14:58 +00:00
}
}
private static void AddAndInit<TWorldObject, TUnityObject>(TWorldObject worldObject, TUnityObject unityObject)
where TWorldObject : WorldObject<TUnityObject>
where TUnityObject : MonoBehaviour
{
WorldObjects.Add(worldObject);
UnityObjectsToWorldObjects.Add(unityObject, worldObject);
2020-12-19 10:56:25 +00:00
var task = UniTask.Create(async () =>
2022-01-08 11:41:55 +00:00
{
await worldObject.Try("initing", () => worldObject.Init(_cts.Token));
_objectsIniting.Remove(worldObject);
});
if (!task.Status.IsCompleted())
{
_objectsIniting.Add(worldObject, task);
}
}
2022-01-08 11:41:55 +00:00
public static void SetDialogueCondition(string name, bool state)
{
if (!QSBCore.IsHost)
{
DebugLog.ToConsole("Warning - Cannot write to dialogue condition dict when not server!", MessageType.Warning);
return;
2022-01-08 11:41:55 +00:00
}
DialogueConditions[name] = state;
}
public static void SetPersistentCondition(string name, bool state)
{
if (!QSBCore.IsHost)
2020-12-19 10:56:25 +00:00
{
DebugLog.ToConsole("Warning - Cannot write to persistent condition dict when not server!", MessageType.Warning);
return;
}
2021-06-18 22:38:32 +01:00
PersistentConditions[name] = state;
}
2021-06-18 22:38:32 +01:00
public static void AddFactReveal(string id, bool saveGame)
{
if (!QSBCore.IsHost)
{
DebugLog.ToConsole("Warning - Cannot write to fact list when not server!", MessageType.Warning);
return;
}
if (ShipLogFacts.Any(x => x.Id == id))
{
return;
2020-12-19 10:56:25 +00:00
}
ShipLogFacts.Add(new FactReveal
{
Id = id,
SaveGame = saveGame
});
2020-12-02 21:29:53 +00:00
}
}