using OWML.Common; using QSB.OrbSync.TransformSync; using QSB.OrbSync.WorldObjects; using QSB.Utility; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; namespace QSB.WorldSync { public static class QSBWorldSync { public static List OrbSyncList { get; } = new List(); public static List OldOrbList { get; set; } = new List(); public static List OldDialogueTrees { get; set; } = new List(); public static Dictionary DialogueConditions { get; } = new Dictionary(); public static List ShipLogFacts { get; } = new List(); private static readonly List WorldObjects = new List(); private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; private static readonly Dictionary WorldObjectsToUnityObjects = new Dictionary(); public static IEnumerable GetWorldObjects() => WorldObjects.OfType(); public static TWorldObject GetWorldFromId(int id) { if (id < 0 || id >= GetWorldObjects().Count()) { DebugLog.ToConsole($"Warning - Tried to find {typeof(TWorldObject).Name} id {id}. Count is {GetWorldObjects().Count()}.", MessageType.Warning); return default; } return GetWorldObjects().ToList()[id]; } public static TWorldObject GetWorldFromUnity(TUnityObject unityObject) where TWorldObject : WorldObject where TUnityObject : MonoBehaviour { if (unityObject == null) { DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{typeof(TUnityObject).Name}.", MessageType.Error); return default; } if (!QSBCore.IsInMultiplayer) { DebugLog.ToConsole($"Warning - Trying to run GetWorldFromUnity while not in multiplayer!"); return default; } if (!WorldObjectsToUnityObjects.ContainsKey(unityObject)) { DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"!", MessageType.Error); return default; } return WorldObjectsToUnityObjects[unityObject] as TWorldObject; } public static int GetIdFromUnity(TUnityObject unityObject) where TWorldObject : WorldObject where TUnityObject : MonoBehaviour => GetWorldFromUnity(unityObject).ObjectId; public static int GetIdFromTypeSubset(TTypeSubset typeSubset) { var index = GetWorldObjects().ToList().IndexOf(typeSubset); if (index == -1) { DebugLog.ToConsole($"Warning - {(typeSubset as IWorldObject).Name} doesn't exist in list of {typeof(TTypeSubset).Name} !", MessageType.Warning); } return index; } public static void RemoveWorldObjects() { var itemsToRemove = WorldObjects.Where(x => x is TWorldObject); foreach (var item in itemsToRemove) { WorldObjectsToUnityObjects.Remove(item.ReturnObject() as MonoBehaviour); try { item.OnRemoval(); } catch (Exception e) { DebugLog.ToConsole($"Error - Exception in OnRemoval() for {item.GetType()}. Message : {e.InnerException.Message}, Stack trace : {e.InnerException.StackTrace}", MessageType.Error); } } DebugLog.DebugWrite($"Removing {typeof(TWorldObject).Name} : {WorldObjects.Count(x => x is TWorldObject)} instances."); WorldObjects.RemoveAll(x => x is TWorldObject); } public static List Init() where TWorldObject : WorldObject where TUnityObject : MonoBehaviour { RemoveWorldObjects(); var list = Resources.FindObjectsOfTypeAll().ToList(); DebugLog.DebugWrite($"{typeof(TWorldObject).Name} init : {list.Count} instances.", MessageType.Info); for (var id = 0; id < list.Count; id++) { var obj = CreateWorldObject(); obj.Init(list[id], id); WorldObjectsToUnityObjects.Add(list[id], obj); } return list; } private static TWorldObject CreateWorldObject() where TWorldObject : IWorldObject { var worldObject = (TWorldObject)Activator.CreateInstance(typeof(TWorldObject)); WorldObjects.Add(worldObject); return worldObject; } public static void RaiseEvent(T instance, string eventName, params object[] args) { if (!(typeof(T) .GetField(eventName, Flags)? .GetValue(instance) is MulticastDelegate multiDelegate)) { return; } var delegateList = multiDelegate.GetInvocationList().ToList(); foreach (var del in delegateList) { del.DynamicInvoke(args); } } public static void HandleSlotStateChange(NomaiInterfaceSlot slot, NomaiInterfaceOrb affectingOrb, bool state) { var slotList = GetWorldObjects().ToList(); if (!slotList.Any()) { return; } var qsbSlot = slotList.FirstOrDefault(x => x.AttachedObject == slot); if (qsbSlot == null) { DebugLog.ToConsole($"Error - No QSBOrbSlot found for {slot.name}!", MessageType.Error); return; } var orbSync = OrbSyncList.FirstOrDefault(x => x.AttachedOrb == affectingOrb); if (orbSync == null) { DebugLog.ToConsole($"Error - No NomaiOrbTransformSync found for {affectingOrb.name} (For slot {slot.name})!", MessageType.Error); return; } if (orbSync.HasAuthority) { qsbSlot.HandleEvent(state, OldOrbList.IndexOf(affectingOrb)); } } public static void SetDialogueCondition(string name, bool state) { if (!QSBCore.IsServer) { DebugLog.ToConsole("Warning - Cannot write to condition dict when not server!", MessageType.Warning); return; } DialogueConditions[name] = state; } public static void AddFactReveal(string id, bool saveGame, bool showNotification) { if (!QSBCore.IsServer) { DebugLog.ToConsole("Warning - Cannot write to fact list when not server!", MessageType.Warning); return; } if (ShipLogFacts.Any(x => x.Id == id)) { return; } ShipLogFacts.Add(new FactReveal { Id = id, SaveGame = saveGame, ShowNotification = showNotification }); } } }