From 6810bcf02199beecc7d66146b7eeaf00e229a244 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Sat, 17 Aug 2024 00:24:11 -0500 Subject: [PATCH] CVRLuaToolsExtension: initial testing --- .../CVRLuaToolsExtension.csproj | 6 + .../LuaToolsExtension/Base/Singleton.cs | 44 +++++ .../CVRBaseLuaBehaviourExtensions.cs | 47 ++++++ .../CVRLuaClientBehaviourExtensions.cs | 124 ++++++++++++++ .../LuaToolsExtension/LuaHotReloadManager.cs | 155 ++++++++++++++++++ .../NamedPipes/NamedPipeServer.cs | 105 ++++++++++++ .../LuaToolsExtension/ScriptInfo.cs | 12 ++ CVRLuaToolsExtension/Main.cs | 67 ++++++++ .../Properties/AssemblyInfo.cs | 32 ++++ CVRLuaToolsExtension/README.md | 14 ++ CVRLuaToolsExtension/format.json | 24 +++ NAK_CVR_Mods.sln | 18 ++ References.Items.props | 20 +++ 13 files changed, 668 insertions(+) create mode 100644 CVRLuaToolsExtension/CVRLuaToolsExtension.csproj create mode 100644 CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs create mode 100644 CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs create mode 100644 CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs create mode 100644 CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs create mode 100644 CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs create mode 100644 CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs create mode 100644 CVRLuaToolsExtension/Main.cs create mode 100644 CVRLuaToolsExtension/Properties/AssemblyInfo.cs create mode 100644 CVRLuaToolsExtension/README.md create mode 100644 CVRLuaToolsExtension/format.json diff --git a/CVRLuaToolsExtension/CVRLuaToolsExtension.csproj b/CVRLuaToolsExtension/CVRLuaToolsExtension.csproj new file mode 100644 index 0000000..bec5b03 --- /dev/null +++ b/CVRLuaToolsExtension/CVRLuaToolsExtension.csproj @@ -0,0 +1,6 @@ + + + + LoadedObjectHack + + diff --git a/CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs b/CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs new file mode 100644 index 0000000..3038865 --- /dev/null +++ b/CVRLuaToolsExtension/LuaToolsExtension/Base/Singleton.cs @@ -0,0 +1,44 @@ +using UnityEngine; + +namespace NAK.CVRLuaToolsExtension; + +public abstract class Singleton : MonoBehaviour where T : MonoBehaviour +{ + private static T _instance; + private static readonly object _lock = new(); + private static bool _isShuttingDown; + + public static T Instance + { + get + { + if (_isShuttingDown) + { + Debug.LogWarning($"[Singleton] Instance of {typeof(T)} already destroyed. Returning null."); + return null; + } + + lock (_lock) + { + if (_instance != null) return _instance; + _instance = (T)FindObjectOfType(typeof(T)); + if (_instance != null) return _instance; + GameObject singletonObject = new($"{typeof(T).Name} (Singleton)"); + _instance = singletonObject.AddComponent(); + DontDestroyOnLoad(singletonObject); + return _instance; + } + } + } + + private void OnApplicationQuit() + { + _isShuttingDown = true; + } + + private void OnDestroy() + { + if (_instance == this) + _isShuttingDown = true; + } +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs new file mode 100644 index 0000000..99af0cd --- /dev/null +++ b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRBaseLuaBehaviourExtensions.cs @@ -0,0 +1,47 @@ +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI.CCK.Components; +using ABI.Scripting.CVRSTL.Common; +using UnityEngine; + +namespace NAK.CVRLuaToolsExtension; + +public static class CVRBaseLuaBehaviourExtensions +{ + // check if the script is eligible for hot reload + public static bool IsScriptEligibleForHotReload(this CVRBaseLuaBehaviour script) + { + return script.Context.IsWornByMe // avatar if worn + || script.Context.IsSpawnedByMe // prop if spawned + || script.Context.objContext == CVRLuaObjectContext.WORLD; // always world scripts + } + + // gets the asset id from the script + public static string GetAssetIdFromScript(this CVRBaseLuaBehaviour script) + { + switch (script.Context.objContext) + { + case CVRLuaObjectContext.WORLD: + //return MetaPort.Instance.CurrentWorldId; // do not trust CVRAssetInfo, can be destroyed at runtime + return "SYSTEM"; // default for world scripts is SYSTEM, but TODO: use actual world id + case CVRLuaObjectContext.AVATAR: + Component rootComponent = script.Context.RootComponent; + return rootComponent switch + { + PlayerSetup => MetaPort.Instance.currentAvatarGuid, // local avatar + PuppetMaster puppetMaster => puppetMaster.CVRPlayerEntity.AvatarId, // remote avatar + _ => string.Empty // fuck + }; + case CVRLuaObjectContext.PROP: + { + CVRSpawnable spawnable = (CVRSpawnable)script.Context.RootComponent; + if (!string.IsNullOrEmpty(spawnable.guid)) return spawnable.guid; // after filtering has occured + return spawnable.TryGetComponent(out CVRAssetInfo assetInfo) + ? assetInfo.objectId // before filtering has occured + : string.Empty; // well shit + } + default: + throw new ArgumentOutOfRangeException(); + } + } +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs new file mode 100644 index 0000000..6f77a61 --- /dev/null +++ b/CVRLuaToolsExtension/LuaToolsExtension/Extensions/CVRLuaClientBehaviourExtensions.cs @@ -0,0 +1,124 @@ +using ABI.CCK.Components; +using ABI.Scripting.CVRSTL.Client; +using System.Diagnostics; +using MTJobSystem; + +namespace NAK.CVRLuaToolsExtension; + +public static class CVRLuaClientBehaviourExtensions +{ + internal static readonly Dictionary _isRestarting = new(); + + #region Public Methods + + public static void Restart(this CVRLuaClientBehaviour behaviour) + { + if (_isRestarting.TryGetValue(behaviour, out bool isRestarting) && isRestarting) + { + CVRLuaToolsExtensionMod.Logger.Warning($"Restart is already in progress for {behaviour.ScriptName}."); + return; + } + + _isRestarting[behaviour] = true; + + bool wasEnabled = behaviour.enabled; + if (behaviour.Crashed) + { + CVRLuaToolsExtensionMod.Logger.Warning($"Restarting a script ({behaviour.ScriptName}) in a crashed state. Unable to determine original enabled state, defaulting to true."); + wasEnabled = true; + } + + behaviour.enabled = false; + + Task.Run(() => + { + try + { + behaviour.ResetScriptCompletely(); + if (CVRLuaToolsExtensionMod.EntryAttemptInitOffMainThread.Value) + { + CVRLuaToolsExtensionMod.Logger.Warning("Attempting to initialize the lua script off main thread. This may cause crashes or corruption if you are accessing UnityEngine Objects outside of Unity's callbacks!"); + behaviour.DoInitialLoadOfScript(); + } + } + catch (Exception e) + { + CVRLuaToolsExtensionMod.Logger.Error(e); // don't wanna die in task + } + + MTJobManager.RunOnMainThread("RestartScript", () => + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + + try + { + if (!CVRLuaToolsExtensionMod.EntryAttemptInitOffMainThread.Value) + behaviour.DoInitialLoadOfScript(); + + if (behaviour.InitialCodeRun) behaviour.IsScriptInitialized = true; // allow callbacks to run again + + // invoke custom event on reset + if (!behaviour.ShouldSkipEvent("CVRLuaTools_OnReset")) behaviour.ExecuteEvent(5000, "CVRLuaTools_OnReset"); + + // re-enable the script & invoke the lifecycle events + if (!behaviour.ShouldSkipEvent("Awake")) behaviour.ExecuteEvent(1000, "Awake"); + behaviour.enabled = wasEnabled; + if (wasEnabled) + { + if (!behaviour.ShouldSkipEvent("OnEnable")) behaviour.ExecuteEvent(1000, "OnEnable"); + if (!behaviour.ShouldSkipEvent("Start")) behaviour.ExecuteEvent(1000, "Start"); + } + } + catch (Exception e) + { + CVRLuaToolsExtensionMod.Logger.Error(e); // don't wanna die prior to resetting restart flag + } + + _isRestarting.Remove(behaviour); + + stopwatch.Stop(); + CVRLuaToolsExtensionMod.Logger.Msg($"Restarted {behaviour.ScriptName} in {stopwatch.ElapsedMilliseconds}ms."); + }); + }); + } + + #endregion Public Methods + + #region Private Methods + + private static void ResetScriptCompletely(this CVRLuaClientBehaviour behaviour) + { + var boundObjectEntries = new List(); + foreach (var kvp in behaviour.BoundObjects) + { + LuaScriptFactory.BoundObjectEntry entry = new(behaviour, behaviour.Context, kvp.Key, kvp.Value); + boundObjectEntries.Add(entry); + } + + behaviour.IsScriptInitialized = false; // prevent callbacks from causing null refs while restarting + behaviour.InitialCodeRun = false; // needs to be set so LoadAndRunScriptIfNeeded will run the script again + behaviour.Crashed = false; // reset the crash flag + + behaviour._scriptGlobalFunctions.Clear(); // will be repopulated + behaviour._startupMessageQueue.Clear(); // will be repopulated + behaviour.LogInfo("[CVRLuaToolsExtension] Resetting script...\n"); + + behaviour.script = null; + behaviour.script = LuaScriptFactory.ForLuaBehaviour(behaviour, boundObjectEntries, behaviour.gameObject, behaviour.transform); + + behaviour.InitTimerIfNeeded(); // only null if crashed prior + behaviour.script.AttachDebugger(behaviour.timer); // reattach the debugger + } + + private static void DoInitialLoadOfScript(this CVRLuaClientBehaviour behaviour) + { + behaviour.LogInfo("[CVRLuaToolsExtension] Interpreting " + behaviour.ScriptName + "...\n"); + behaviour.LoadAndRunScript(behaviour.ScriptText); // fucking slow + behaviour.PopulateScriptGlobalFunctionsCache(behaviour.script.Globals); // tbh don't think we need to clear in first place + behaviour.HandleMessageQueueEntries(); // shouldn't be any + behaviour.InitialCodeRun = true; + } + + #endregion Private Methods +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs b/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs new file mode 100644 index 0000000..f9b526b --- /dev/null +++ b/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs @@ -0,0 +1,155 @@ +using System.Text; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.UI; +using ABI.CCK.Components; +using ABI.CCK.Components.ScriptableObjects; +using UnityEngine; + +namespace NAK.CVRLuaToolsExtension; + +public static class LuaHotReloadManager +{ + private static readonly Dictionary> s_AssetIdToLuaClientBehaviourIds = new(); + private static readonly Dictionary s_LuaComponentIdsToLuaClientBehaviour = new(); + + #region Game Events + + public static void OnCVRLuaBaseBehaviourLoadAndRunScript(CVRLuaClientBehaviour clientBehaviour) + { + //CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Script awake: {clientBehaviour.name}"); + + if (!clientBehaviour.IsScriptEligibleForHotReload()) + return; + + var assetId = clientBehaviour.GetAssetIdFromScript(); + if (!s_AssetIdToLuaClientBehaviourIds.ContainsKey(assetId)) + s_AssetIdToLuaClientBehaviourIds[assetId] = new List(); + + var luaComponentId = GetGameObjectPathHashCode(clientBehaviour.transform); + if (s_AssetIdToLuaClientBehaviourIds[assetId].Contains(luaComponentId)) + { + CVRLuaToolsExtensionMod.Logger.Warning( + $"[LuaHotReloadManager] Script already exists: {clientBehaviour.name}"); + return; + } + + s_AssetIdToLuaClientBehaviourIds[assetId].Add(luaComponentId); + s_LuaComponentIdsToLuaClientBehaviour[luaComponentId] = clientBehaviour; + CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Added script: {clientBehaviour.name}"); + } + + public static void OnCVRLuaBaseBehaviourDestroy(CVRLuaClientBehaviour clientBehaviour) + { + //CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Script destroy: {clientBehaviour.name}"); + + var assetId = clientBehaviour.GetAssetIdFromScript(); + if (!s_AssetIdToLuaClientBehaviourIds.ContainsKey(assetId)) + return; + + var luaClientBehaviourIds = s_AssetIdToLuaClientBehaviourIds[assetId]; + foreach (var luaComponentId in luaClientBehaviourIds) + { + if (!s_LuaComponentIdsToLuaClientBehaviour.TryGetValue(luaComponentId, + out CVRLuaClientBehaviour luaClientBehaviour)) + continue; + + if (luaClientBehaviour != clientBehaviour) + continue; + + s_LuaComponentIdsToLuaClientBehaviour.Remove(luaComponentId); + luaClientBehaviourIds.Remove(luaComponentId); + if (luaClientBehaviourIds.Count == 0) s_AssetIdToLuaClientBehaviourIds.Remove(assetId); + CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Removed script: {clientBehaviour.name}"); + break; + } + } + + public static void OnReceiveUpdatedScript(ScriptInfo info) + { + if (!s_AssetIdToLuaClientBehaviourIds.TryGetValue(info.AssetId, out var luaComponentIds)) + { + CVRLuaToolsExtensionMod.Logger.Warning( + $"[LuaHotReloadManager] No scripts found for asset id: {info.AssetId}"); + return; + } + + bool found = false; + foreach (var luaComponentId in luaComponentIds) + { + if (!s_LuaComponentIdsToLuaClientBehaviour.TryGetValue(luaComponentId, + out CVRLuaClientBehaviour clientBehaviour)) + continue; + + found = true; + //CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Reloading script: {info.ScriptName} for {clientBehaviour.name}"); + + if (clientBehaviour.asset.m_ScriptPath == info.ScriptPath) + { + CVRLuaToolsExtensionMod.Logger.Msg("[LuaHotReloadManager] Script path match, updating script."); + clientBehaviour.asset.name = info.ScriptName; + clientBehaviour.asset.m_ScriptText = info.ScriptText; + clientBehaviour.Restart(); + } + else + { + CVRLuaToolsExtensionMod.Logger.Msg("[LuaHotReloadManager] Script path mismatch, creating new script."); + + clientBehaviour.asset = null; + clientBehaviour.asset = ScriptableObject.CreateInstance(); + + clientBehaviour.asset.name = info.ScriptName; + clientBehaviour.asset.m_ScriptPath = info.ScriptPath; + clientBehaviour.asset.m_ScriptText = info.ScriptText; + + clientBehaviour.Restart(); + } + } + + if (found) CohtmlHud.Instance.ViewDropTextImmediate("(Local) CVRLuaTools", "Received script update", "Reloaded script: " + info.ScriptName); + } + + #endregion Game Events + + #region Private Methods + + private static int GetGameObjectPathHashCode(Transform transform) + { + // Attempt to find the root component transform in one step + + Transform rootComponentTransform = null; + + // both CVRAvatar & CVRSpawnable *should* have an asset info component + CVRAssetInfo rootComponent = transform.GetComponentInParent(true); + if (rootComponent != null && rootComponent.type != CVRAssetInfo.AssetType.World) + rootComponentTransform = rootComponent.transform; + + // easy case, no need to crawl up the hierarchy + if (rootComponentTransform == transform) + return 581452743; // hash code for "[Root]" + + StringBuilder pathBuilder = new(transform.name); + Transform parentTransform = transform.parent; + + while (parentTransform != null) + { + // reached root component + // due to object loader renaming root, we can't rely on transform name, so we use "[Root]" instead + if (parentTransform == rootComponentTransform) + { + pathBuilder.Insert(0, "[Root]/"); + break; + } + + pathBuilder.Insert(0, parentTransform.name + "/"); + parentTransform = parentTransform.parent; + } + + string path = pathBuilder.ToString(); + + //Debug.Log($"[LuaComponentManager] Path: {path}"); + + return path.GetHashCode(); + } + + #endregion Private Methods +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs b/CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs new file mode 100644 index 0000000..407be8b --- /dev/null +++ b/CVRLuaToolsExtension/LuaToolsExtension/NamedPipes/NamedPipeServer.cs @@ -0,0 +1,105 @@ +using System.Diagnostics; +using System.IO.Pipes; +using System.Text; +using ABI_RC.Core; + +namespace NAK.CVRLuaToolsExtension.NamedPipes; + +public class NamedPipeServer : Singleton +{ + private const string PipeName = "UnityPipe"; + + public async void StartListening() + { + Stopwatch stopwatch = new(); + while (!CommonTools.IsQuitting) + { + await using NamedPipeServerStream namedPipeServer = new(PipeName, PipeDirection.In); + try + { + CVRLuaToolsExtensionMod.Logger.Msg("Waiting for client..."); + await namedPipeServer.WaitForConnectionAsync(); + stopwatch.Restart(); + if (!namedPipeServer.IsConnected) + continue; + + //Debug.Log("Client connected."); + + // receive and process the ScriptInfo + ScriptInfo receivedScript = await ReceiveScriptInfo(namedPipeServer); + ProcessScriptInfo(receivedScript); + } + catch (Exception e) + { + CVRLuaToolsExtensionMod.Logger.Error(e); + } + + if (namedPipeServer.IsConnected) + { + namedPipeServer.Disconnect(); + CVRLuaToolsExtensionMod.Logger.Msg("Client disconnected."); + } + + stopwatch.Stop(); + CVRLuaToolsExtensionMod.Logger.Msg($"Elapsed time: {stopwatch.ElapsedMilliseconds}ms"); + } + + CVRLuaToolsExtensionMod.Logger.Msg("Quitting..."); + } + + private async Task ReceiveScriptInfo(NamedPipeServerStream stream) + { + // Read LuaComponentId (4 bytes for an int) + byte[] intBuffer = new byte[4]; + await stream.ReadAsync(intBuffer, 0, intBuffer.Length); + int luaComponentId = BitConverter.ToInt32(intBuffer, 0); + + string assetId = await ReadStringAsync(stream); + //CVRLuaToolsExtensionMod.Logger.Msg($"Received assetId: {assetId}"); + + string scriptName = await ReadStringAsync(stream); + //CVRLuaToolsExtensionMod.Logger.Msg($"Received scriptName: {scriptName}"); + + string scriptPath = await ReadStringAsync(stream); + //CVRLuaToolsExtensionMod.Logger.Msg($"Received scriptPath: {scriptPath}"); + + string scriptText = await ReadStringAsync(stream); + //CVRLuaToolsExtensionMod.Logger.Msg($"Received scriptText: {scriptText}"); + + //Debug.Log("ScriptInfo received."); + + return new ScriptInfo + { + LuaComponentId = luaComponentId, + AssetId = assetId, + ScriptName = scriptName, + ScriptPath = scriptPath, + ScriptText = scriptText + }; + } + + private static async Task ReadStringAsync(Stream stream) + { + // Define a buffer size and initialize a memory stream + byte[] buffer = new byte[1024]; + using MemoryStream memoryStream = new(); + + int bytesRead; + while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + memoryStream.Write(buffer, 0, bytesRead); + //CVRLuaToolsExtensionMod.Logger.Msg($"Read {bytesRead} bytes."); + if (bytesRead < buffer.Length) break; // Assuming no partial writes + } + + return Encoding.UTF8.GetString(memoryStream.ToArray()); + } + + private static void ProcessScriptInfo(ScriptInfo scriptInfo) + { + // Handle the received ScriptInfo data + //Debug.Log($"Received ScriptInfo: {scriptInfo.AssetId}, {scriptInfo.LuaComponentId}, {scriptInfo.ScriptName}"); + + LuaHotReloadManager.OnReceiveUpdatedScript(scriptInfo); + } +} diff --git a/CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs b/CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs new file mode 100644 index 0000000..7d7149e --- /dev/null +++ b/CVRLuaToolsExtension/LuaToolsExtension/ScriptInfo.cs @@ -0,0 +1,12 @@ +namespace NAK.CVRLuaToolsExtension; + +[Serializable] +public struct ScriptInfo +{ + public string AssetId; // we will reload all scripts with this asset id + public int LuaComponentId; // the target lua component id + + public string ScriptName; // the new script name + public string ScriptPath; + public string ScriptText; // the new script text +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/Main.cs b/CVRLuaToolsExtension/Main.cs new file mode 100644 index 0000000..29157f2 --- /dev/null +++ b/CVRLuaToolsExtension/Main.cs @@ -0,0 +1,67 @@ +using System.Reflection; +using ABI_RC.Systems.GameEventSystem; +using ABI.CCK.Components; +using HarmonyLib; +using MelonLoader; +using NAK.CVRLuaToolsExtension.NamedPipes; + +namespace NAK.CVRLuaToolsExtension; + +public class CVRLuaToolsExtensionMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + #region Melon Preferences + + private const string ModName = nameof(CVRLuaToolsExtension); + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(ModName); + + internal static readonly MelonPreferences_Entry EntryAttemptInitOffMainThread = + Category.CreateEntry("attempt_init_off_main_thread", false, + "Attempt init off Main Thread", "Attempt to initialize the lua script off main thread."); + + #endregion Melon Preferences + + #region Melon Events + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + HarmonyInstance.Patch( + typeof(CVRLuaClientBehaviour).GetMethod(nameof(CVRLuaClientBehaviour.InitializeLuaVm)), // protected + postfix: new HarmonyMethod(typeof(CVRLuaToolsExtensionMod).GetMethod(nameof(OnCVRBaseLuaBehaviourLoadAndRunScript), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + HarmonyInstance.Patch( + typeof(CVRLuaClientBehaviour).GetMethod(nameof(CVRLuaClientBehaviour.OnDestroy)), // public for some reason + postfix: new HarmonyMethod(typeof(CVRLuaToolsExtensionMod).GetMethod(nameof(OnCVRBaseLuaBehaviourDestroy), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener( + () => NamedPipeServer.Instance.StartListening()); + } + + #endregion Melon Events + + #region Harmony Patches + + // theres no good place to patch where GetOwnerId & GetObjectId would be valid to call... + // pls move InitializeMain after Context is created... pls + private static void OnCVRBaseLuaBehaviourLoadAndRunScript(CVRLuaClientBehaviour __instance) + { + LuaHotReloadManager.OnCVRLuaBaseBehaviourLoadAndRunScript(__instance); + } + + private static void OnCVRBaseLuaBehaviourDestroy(CVRLuaClientBehaviour __instance) + { + LuaHotReloadManager.OnCVRLuaBaseBehaviourDestroy(__instance); + if (CVRLuaClientBehaviourExtensions._isRestarting.ContainsKey(__instance)) + CVRLuaClientBehaviourExtensions._isRestarting.Remove(__instance); + } + + #endregion Harmony Patches +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/Properties/AssemblyInfo.cs b/CVRLuaToolsExtension/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a1c41b2 --- /dev/null +++ b/CVRLuaToolsExtension/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using MelonLoader; +using NAK.CVRLuaToolsExtension.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.CVRLuaToolsExtension))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.CVRLuaToolsExtension))] + +[assembly: MelonInfo( + typeof(NAK.CVRLuaToolsExtension.CVRLuaToolsExtensionMod), + nameof(NAK.CVRLuaToolsExtension), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CVRLuaToolsExtension" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 246, 25, 99)] // red-pink +[assembly: MelonAuthorColor(255, 158, 21, 32)] // red +[assembly: HarmonyDontPatchAll] + +namespace NAK.CVRLuaToolsExtension.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/CVRLuaToolsExtension/README.md b/CVRLuaToolsExtension/README.md new file mode 100644 index 0000000..8a46af2 --- /dev/null +++ b/CVRLuaToolsExtension/README.md @@ -0,0 +1,14 @@ +# IKSimulatedRootAngleFix + +Fixes a small issue with Desktop & HalfBody root angle being incorrectly calculated while on rotating Movement Parents. If you've ever noticed your body/feet insisting on facing opposite of the direction you are rotating, this fixes that. + +--- + +Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive. + +> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use. + +> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. diff --git a/CVRLuaToolsExtension/format.json b/CVRLuaToolsExtension/format.json new file mode 100644 index 0000000..ddf506b --- /dev/null +++ b/CVRLuaToolsExtension/format.json @@ -0,0 +1,24 @@ +{ + "_id": -1, + "name": "AASDefaultProfileFix", + "modversion": "1.0.0", + "gameversion": "2024r175", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Fixes the Default AAS profile not being applied when loading into an avatar without a profile selected.\n\nBy default, the game will not apply anything and the avatar will default to the state found within the Controller parameters.", + "searchtags": [ + "aas", + "profile", + "default", + "fix", + "meow" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r33/AASDefaultProfileFix.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/AASDefaultProfileFix/", + "changelog": "- Initial release", + "embedcolor": "#f61963" +} \ No newline at end of file diff --git a/NAK_CVR_Mods.sln b/NAK_CVR_Mods.sln index f4d81c0..ccdda68 100644 --- a/NAK_CVR_Mods.sln +++ b/NAK_CVR_Mods.sln @@ -85,6 +85,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvatarQueueSystemTweaks", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomSpawnPoint", "CustomSpawnPoint\CustomSpawnPoint.csproj", "{51CA34CA-7684-4819-AC9E-89DFAD63E9AB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NukePostPresentHandoff", "NukePostPresentHandoff\NukePostPresentHandoff.csproj", "{77F332CD-019A-472F-9269-CFDEB087B3F9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CVRLuaToolsExtension", "CVRLuaToolsExtension\CVRLuaToolsExtension.csproj", "{FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stickers", "Stickers\Stickers.csproj", "{E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -255,6 +261,18 @@ Global {51CA34CA-7684-4819-AC9E-89DFAD63E9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {51CA34CA-7684-4819-AC9E-89DFAD63E9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {51CA34CA-7684-4819-AC9E-89DFAD63E9AB}.Release|Any CPU.Build.0 = Release|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77F332CD-019A-472F-9269-CFDEB087B3F9}.Release|Any CPU.Build.0 = Release|Any CPU + {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE6BA6EC-2C11-49E1-A2FB-13F2A1C9E7F9}.Release|Any CPU.Build.0 = Release|Any CPU + {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5F54B3E-A676-4DD5-A6DB-73AFA54BEC5E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/References.Items.props b/References.Items.props index bb32776..b618151 100644 --- a/References.Items.props +++ b/References.Items.props @@ -76,6 +76,14 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\Cohtml.Runtime.dll False + + $(MsBuildThisFileDirectory)\.ManagedLibs\com.dogu.gamium.engine.unity.runtime.dll + False + + + $(MsBuildThisFileDirectory)\.ManagedLibs\com.dogu.gamium.engine.unity.runtime.protocol.dll + False + $(MsBuildThisFileDirectory)\.ManagedLibs\Crc32.NET.dll False @@ -136,6 +144,10 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\MeshBakerCore.dll False + + $(MsBuildThisFileDirectory)\.ManagedLibs\Meta.XR.BuildingBlocks.dll + False + $(MsBuildThisFileDirectory)\.ManagedLibs\Mono.Security.dll False @@ -172,6 +184,14 @@ $(MsBuildThisFileDirectory)\.ManagedLibs\Oculus.LipSync.dll False + + $(MsBuildThisFileDirectory)\.ManagedLibs\Oculus.Platform.dll + False + + + $(MsBuildThisFileDirectory)\.ManagedLibs\Oculus.VR.dll + False + $(MsBuildThisFileDirectory)\.ManagedLibs\PICO.Platform.dll False