diff --git a/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs b/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs index f9b526b..ec44859 100644 --- a/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs +++ b/CVRLuaToolsExtension/LuaToolsExtension/LuaHotReloadManager.cs @@ -9,79 +9,64 @@ namespace NAK.CVRLuaToolsExtension; public static class LuaHotReloadManager { - private static readonly Dictionary> s_AssetIdToLuaClientBehaviourIds = new(); - private static readonly Dictionary s_LuaComponentIdsToLuaClientBehaviour = new(); + // (asset id + lua component id) -> index is component reference + private static readonly List s_CombinedKeys = new(); + private static readonly List s_LuaComponentInstances = 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)) + // check if the component is already in the list (shouldn't happen) + if (s_LuaComponentInstances.Contains(clientBehaviour)) { - CVRLuaToolsExtensionMod.Logger.Warning( - $"[LuaHotReloadManager] Script already exists: {clientBehaviour.name}"); + CVRLuaToolsExtensionMod.Logger.Warning($"[LuaHotReloadManager] Script already added: {clientBehaviour.name}"); return; } + + // combine the assetId and instanceId into a single key, so multiple instances of the same script can be tracked + string assetId = clientBehaviour.GetAssetIdFromScript(); + int instanceId = GetGameObjectPathHashCode(clientBehaviour.transform); + int combinedKey = GenerateCombinedKey(assetId.GetHashCode(), instanceId); + + s_CombinedKeys.Add(combinedKey); + s_LuaComponentInstances.Add(clientBehaviour); - 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)) + if (!clientBehaviour.IsScriptEligibleForHotReload()) return; - var luaClientBehaviourIds = s_AssetIdToLuaClientBehaviourIds[assetId]; - foreach (var luaComponentId in luaClientBehaviourIds) + if (!s_LuaComponentInstances.Contains(clientBehaviour)) { - 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; + CVRLuaToolsExtensionMod.Logger.Warning($"[LuaHotReloadManager] Eligible for Hot Reload script destroyed without being tracked first: {clientBehaviour.name}"); + return; } + + int index = s_LuaComponentInstances.IndexOf(clientBehaviour); + s_CombinedKeys.RemoveAt(index); + s_LuaComponentInstances.RemoveAt(index); + + CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Removed script: {clientBehaviour.name}"); } - + 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; - } + int combinedKey = GenerateCombinedKey(info.AssetId.GetHashCode(), info.LuaComponentId); bool found = false; - foreach (var luaComponentId in luaComponentIds) + for (int i = 0; i < s_CombinedKeys.Count; i++) { - if (!s_LuaComponentIdsToLuaClientBehaviour.TryGetValue(luaComponentId, - out CVRLuaClientBehaviour clientBehaviour)) + if (combinedKey != s_CombinedKeys[i]) continue; - found = true; - //CVRLuaToolsExtensionMod.Logger.Msg($"[LuaHotReloadManager] Reloading script: {info.ScriptName} for {clientBehaviour.name}"); + CVRLuaClientBehaviour clientBehaviour = s_LuaComponentInstances[i]; if (clientBehaviour.asset.m_ScriptPath == info.ScriptPath) { @@ -89,6 +74,7 @@ public static class LuaHotReloadManager clientBehaviour.asset.name = info.ScriptName; clientBehaviour.asset.m_ScriptText = info.ScriptText; clientBehaviour.Restart(); + found = true; } else { @@ -102,25 +88,31 @@ public static class LuaHotReloadManager clientBehaviour.asset.m_ScriptText = info.ScriptText; clientBehaviour.Restart(); + found = true; } } - if (found) CohtmlHud.Instance.ViewDropTextImmediate("(Local) CVRLuaTools", "Received script update", "Reloaded script: " + info.ScriptName); + if (found) + { + CohtmlHud.Instance.ViewDropTextImmediate("(Local) CVRLuaTools", "Received script update", "Reloaded script: " + info.ScriptName); + } } #endregion Game Events #region Private Methods + private static int GenerateCombinedKey(int assetId, int instanceId) + { + return (assetId << 16) | instanceId; // yes + } + 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 + Transform rootComponentTransform = null; CVRAssetInfo rootComponent = transform.GetComponentInParent(true); - if (rootComponent != null && rootComponent.type != CVRAssetInfo.AssetType.World) + if (rootComponent != null && rootComponent.type != CVRAssetInfo.AssetType.World) // ignore if under world instance rootComponentTransform = rootComponent.transform; // easy case, no need to crawl up the hierarchy diff --git a/CVRLuaToolsExtension/Main.cs b/CVRLuaToolsExtension/Main.cs index 29157f2..1dad9d9 100644 --- a/CVRLuaToolsExtension/Main.cs +++ b/CVRLuaToolsExtension/Main.cs @@ -4,6 +4,7 @@ using ABI.CCK.Components; using HarmonyLib; using MelonLoader; using NAK.CVRLuaToolsExtension.NamedPipes; +using UnityEngine; namespace NAK.CVRLuaToolsExtension; diff --git a/CVRLuaToolsExtension/Properties/AssemblyInfo.cs b/CVRLuaToolsExtension/Properties/AssemblyInfo.cs index a1c41b2..c60d4b0 100644 --- a/CVRLuaToolsExtension/Properties/AssemblyInfo.cs +++ b/CVRLuaToolsExtension/Properties/AssemblyInfo.cs @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.CVRLuaToolsExtension.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.0"; + public const string Version = "1.0.1"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/CVRLuaToolsExtension/README.md b/CVRLuaToolsExtension/README.md index 8a46af2..581625d 100644 --- a/CVRLuaToolsExtension/README.md +++ b/CVRLuaToolsExtension/README.md @@ -1,6 +1,6 @@ -# IKSimulatedRootAngleFix +# CVRLuaToolsExtension -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. +Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. --- diff --git a/CVRLuaToolsExtension/format.json b/CVRLuaToolsExtension/format.json index ddf506b..5ef4f21 100644 --- a/CVRLuaToolsExtension/format.json +++ b/CVRLuaToolsExtension/format.json @@ -1,24 +1,24 @@ { "_id": -1, - "name": "AASDefaultProfileFix", + "name": "CVRLuaToolsExtension", "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.", + "description": "Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality.", "searchtags": [ - "aas", - "profile", - "default", - "fix", - "meow" + "lua", + "scripting", + "hotreload", + "reload", + "development" ], "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/", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r33/CVRLuaToolsExtension.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/CVRLuaToolsExtension/", "changelog": "- Initial release", "embedcolor": "#f61963" } \ No newline at end of file