From ba26a1faae0c5f3f76ccef66a6bbe0d57faceac2 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:39:16 -0500 Subject: [PATCH 01/10] [CustomRichPresence] Move to .Experimental folder --- .../CustomRichPresence}/CustomRichPresence.csproj | 0 {CustomRichPresence => .Experimental/CustomRichPresence}/Main.cs | 0 .../CustomRichPresence}/Properties/AssemblyInfo.cs | 0 .../CustomRichPresence}/README.md | 0 .../CustomRichPresence}/format.json | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {CustomRichPresence => .Experimental/CustomRichPresence}/CustomRichPresence.csproj (100%) rename {CustomRichPresence => .Experimental/CustomRichPresence}/Main.cs (100%) rename {CustomRichPresence => .Experimental/CustomRichPresence}/Properties/AssemblyInfo.cs (100%) rename {CustomRichPresence => .Experimental/CustomRichPresence}/README.md (100%) rename {CustomRichPresence => .Experimental/CustomRichPresence}/format.json (100%) diff --git a/CustomRichPresence/CustomRichPresence.csproj b/.Experimental/CustomRichPresence/CustomRichPresence.csproj similarity index 100% rename from CustomRichPresence/CustomRichPresence.csproj rename to .Experimental/CustomRichPresence/CustomRichPresence.csproj diff --git a/CustomRichPresence/Main.cs b/.Experimental/CustomRichPresence/Main.cs similarity index 100% rename from CustomRichPresence/Main.cs rename to .Experimental/CustomRichPresence/Main.cs diff --git a/CustomRichPresence/Properties/AssemblyInfo.cs b/.Experimental/CustomRichPresence/Properties/AssemblyInfo.cs similarity index 100% rename from CustomRichPresence/Properties/AssemblyInfo.cs rename to .Experimental/CustomRichPresence/Properties/AssemblyInfo.cs diff --git a/CustomRichPresence/README.md b/.Experimental/CustomRichPresence/README.md similarity index 100% rename from CustomRichPresence/README.md rename to .Experimental/CustomRichPresence/README.md diff --git a/CustomRichPresence/format.json b/.Experimental/CustomRichPresence/format.json similarity index 100% rename from CustomRichPresence/format.json rename to .Experimental/CustomRichPresence/format.json From 392390cde7a097f851f94f984bf0a130b7c8d342 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:39:32 -0500 Subject: [PATCH 02/10] [YouAreMyPropNowWeAreHavingSoftTacosLater] Initial push --- NAK_CVR_Mods.sln | 6 + .../Main.cs | 479 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 32 ++ .../README.md | 19 + ...eMyPropNowWeAreHavingSoftTacosLater.csproj | 6 + .../format.json | 23 + 6 files changed, 565 insertions(+) create mode 100644 YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs create mode 100644 YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs create mode 100644 YouAreMyPropNowWeAreHavingSoftTacosLater/README.md create mode 100644 YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj create mode 100644 YouAreMyPropNowWeAreHavingSoftTacosLater/format.json diff --git a/NAK_CVR_Mods.sln b/NAK_CVR_Mods.sln index 976736f..26deacd 100644 --- a/NAK_CVR_Mods.sln +++ b/NAK_CVR_Mods.sln @@ -54,6 +54,8 @@ EndProject EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCCVirtualSteeringWheel", "RCCVirtualSteeringWheel\RCCVirtualSteeringWheel.csproj", "{4A378F81-3805-41E8-9565-A8A89A8C00D6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YouAreMyPropNowWeAreHavingSoftTacosLater", "YouAreMyPropNowWeAreHavingSoftTacosLater\YouAreMyPropNowWeAreHavingSoftTacosLater.csproj", "{8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}" +EndProject EndProject EndProject EndProject @@ -289,6 +291,10 @@ Global {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED2CAA2D-4E49-4636-86C4-367D0CDC3572}.Release|Any CPU.Build.0 = Release|Any CPU + {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DA821CC-F911-4FCB-8C29-5EF3D76A5F76}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs b/YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs new file mode 100644 index 0000000..645f7c0 --- /dev/null +++ b/YouAreMyPropNowWeAreHavingSoftTacosLater/Main.cs @@ -0,0 +1,479 @@ +using System.Collections; +using System.Reflection; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.IO; +using ABI_RC.Core.Networking; +using ABI_RC.Core.Networking.API.Responses; +using ABI_RC.Core.Player; +using ABI_RC.Core.Util; +using ABI_RC.Systems.GameEventSystem; +using ABI_RC.Systems.Movement; +using ABI.CCK.Components; +using DarkRift; +using HarmonyLib; +using MelonLoader; +using UnityEngine; +using UnityEngine.SceneManagement; +using Object = UnityEngine.Object; +using Random = UnityEngine.Random; + +namespace NAK.YouAreMyPropNowWeAreHavingSoftTacosLater; + +public class YouAreMyPropNowWeAreHavingSoftTacosLaterMod : MelonMod +{ + #region Melon Events + + public override void OnInitializeMelon() + { + #region CVRPickupObject Patches + + HarmonyInstance.Patch( + typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.OnGrab), + BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRPickupObjectOnGrab), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.OnDrop), BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRPickupObjectOnDrop), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + #endregion CVRPickupObject Patches + + #region CVRAttachment Patches + + HarmonyInstance.Patch( // Cannot compile when using nameof + typeof(CVRAttachment).GetMethod("\u003CAttachInternal\u003Eg__DoAttachmentSetup\u007C43_0", + BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRAttachmentAttachInternal), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + HarmonyInstance.Patch( + typeof(CVRAttachment).GetMethod(nameof(CVRAttachment.DeAttach)), + prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRAttachmentDeAttach), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + #endregion CVRAttachment Patches + + #region CVRSyncHelper Patches + + HarmonyInstance.Patch( // Replaces method, original needlessly ToArray??? + typeof(CVRSyncHelper).GetMethod(nameof(CVRSyncHelper.ClearProps), + BindingFlags.Public | BindingFlags.Static), + prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRSyncHelperClearProps), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + #endregion CVRSyncHelper Patches + + #region CVRDownloadManager Patches + + HarmonyInstance.Patch( + typeof(CVRDownloadManager).GetMethod(nameof(CVRDownloadManager.QueueTask), + BindingFlags.Public | BindingFlags.Instance), + prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnCVRDownloadManagerQueueTask), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + #endregion CVRDownloadManager Patches + + #region BetterBetterCharacterController Patches + + HarmonyInstance.Patch( + typeof(BetterBetterCharacterController).GetMethod(nameof(BetterBetterCharacterController.SetSitting), + BindingFlags.Public | BindingFlags.Instance), + prefix: new HarmonyMethod(typeof(YouAreMyPropNowWeAreHavingSoftTacosLaterMod).GetMethod(nameof(OnBetterBetterCharacterControllerSetSitting), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + #endregion BetterBetterCharacterController Patches + + #region CVRWorld Game Events + + CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoad); + CVRGameEventSystem.World.OnUnload.AddListener(OnWorldUnload); + + #endregion CVRWorld Game Events + + #region Instances Game Events + + CVRGameEventSystem.Instance.OnConnected.AddListener(OnInstanceConnected); + + #endregion Instances Game Events + } + + #endregion Melon Events + + #region Harmony Patches + + private static readonly List _heldPropData = new(); + private static GameObject _persistantPropsContainer; + private static GameObject GetOrCreatePropsContainer() + { + if (_persistantPropsContainer != null) return _persistantPropsContainer; + _persistantPropsContainer = new("[NAK] PersistantProps"); + _persistantPropsContainer.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); + _persistantPropsContainer.transform.localScale = Vector3.one; + Object.DontDestroyOnLoad(_persistantPropsContainer); + return _persistantPropsContainer; + } + + private static readonly Dictionary _keyToPropData = new(); + private static readonly Stack _spawnablePositionStack = new(); + private static bool _ignoreNextSeatExit; + private static float _heightOffset; + + private static void OnCVRPickupObjectOnGrab(CVRPickupObject __instance) + { + if (!GetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; + if (!_heldPropData.Contains(propData)) _heldPropData.Add(propData); + } + + private static void OnCVRPickupObjectOnDrop(CVRPickupObject __instance) + { + if (!GetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; + if (_heldPropData.Contains(propData)) _heldPropData.Remove(propData); + } + + private static void OnCVRAttachmentAttachInternal(CVRAttachment __instance) + { + if (!GetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; + if (!_heldPropData.Contains(propData)) _heldPropData.Add(propData); + } + + private static void OnCVRAttachmentDeAttach(CVRAttachment __instance) + { + if (!__instance._isAttached) return; // Can invoke DeAttach without being attached + if (!GetPropData(__instance.GetComponentInParent(true), out CVRSyncHelper.PropData propData)) return; + if (_heldPropData.Contains(propData)) _heldPropData.Remove(propData); + } + + // ReSharper disable UnusedParameter.Local + private static bool OnCVRDownloadManagerQueueTask(string assetId, DownloadTask.ObjectType type, string assetUrl, string fileId, long fileSize, string fileKey, string toAttach, + string fileHash = null, UgcTagsData tagsData = null, CVRLoadingAvatarController loadingAvatarController = null, + bool joinOnComplete = false, bool isHomeRequested = false, int compatibilityVersion = 0, int encryptionAlgorithm = 0, + string spawnerId = null) + { + if (type != DownloadTask.ObjectType.Prop) return true; // Only care about props + + // toAttach is our instanceId, lets find the propData + if (!GetPropDataById(toAttach, out CVRSyncHelper.PropData newPropData)) return true; + + // Check if this is a prop we requested to spawn + Vector3 identity = GetIdentityKeyFromPropData(newPropData); + if (!_keyToPropData.Remove(identity, out CVRSyncHelper.PropData originalPropData)) return true; + + // Remove original prop data from held + if (_heldPropData.Contains(originalPropData)) _heldPropData.Remove(originalPropData); + + // If original prop data is null spawn a new prop i guess :( + if (originalPropData.Spawnable == null) return true; + + // Add the new prop data to our held props in place of the old one + if (!_heldPropData.Contains(newPropData)) _heldPropData.Add(newPropData); + + // Apply new prop data to the spawnable + newPropData.Spawnable = originalPropData.Spawnable; + newPropData.Wrapper = originalPropData.Wrapper; + newPropData.Wrapper.name = $"p+{newPropData.ObjectId}~{newPropData.InstanceId}"; + + // Copy sync values + Array.Copy(newPropData.CustomFloats, originalPropData.CustomFloats, newPropData.CustomFloatsAmount); + + CVRSyncHelper.ApplyPropValuesSpawn(newPropData); + + // Place the prop in the additive content scene + PlacePropInAdditiveContentScene(newPropData.Spawnable); + + // Clear old data so Recycle() doesn't delete our prop + originalPropData.Spawnable = null; + originalPropData.Wrapper = null; + originalPropData.Recycle(); + + return false; + } + + private static bool OnCVRSyncHelperClearProps() // Prevent deleting of our held props on scene load + { + for (var index = CVRSyncHelper.Props.Count - 1; index >= 0; index--) + { + CVRSyncHelper.PropData prop = CVRSyncHelper.Props[index]; + if (prop.Spawnable != null && _heldPropData.Contains(prop)) + continue; // Do not recycle props that are valid & held + + DeleteOrRecycleProp(prop); + } + + CVRSyncHelper.MySpawnedPropInstanceIds.Clear(); + return false; + } + + private static bool OnBetterBetterCharacterControllerSetSitting(bool isSitting, CVRSeat cvrSeat = null, bool callExitSeat = true) + { + if (!_ignoreNextSeatExit) return true; + _ignoreNextSeatExit = false; + if (BetterBetterCharacterController.Instance._lastCvrSeat == null) return true; // run original + return false; // dont run if there is a chair & we skipped it + } + + #endregion Harmony Patches + + #region Game Events + + private object _worldLoadTimer; + + private void OnWorldLoad(string _) + { + CVRWorld worldInstance = CVRWorld.Instance; + if (worldInstance != null && !worldInstance.allowSpawnables) + { + foreach (CVRSyncHelper.PropData prop in _heldPropData) DeleteOrRecycleProp(prop); // Delete all props we kept + return; + } + + for (var index = _heldPropData.Count - 1; index >= 0; index--) + { + CVRSyncHelper.PropData prop = _heldPropData[index]; + if (prop.Spawnable == null) + { + DeleteOrRecycleProp(prop); + return; + } + + // apply positions + int stackCount = _spawnablePositionStack.Count; + for (int i = stackCount - 1; i >= 0; i--) _spawnablePositionStack.Pop().ReapplyOffsets(); + } + + // Start a timer, and anything that did not load within 3 seconds will be destroyed + if (_worldLoadTimer != null) + { + MelonCoroutines.Stop(_worldLoadTimer); + _worldLoadTimer = null; + } + _worldLoadTimer = MelonCoroutines.Start(DestroyPersistantPropContainerInFive()); + _ignoreNextSeatExit = true; // just in case we are in a car / vehicle + } + + private IEnumerator DestroyPersistantPropContainerInFive() + { + yield return new WaitForSeconds(3f); + _worldLoadTimer = null; + Object.Destroy(_persistantPropsContainer); + _persistantPropsContainer = null; + _keyToPropData.Clear(); // no more chances + } + + private static void OnWorldUnload(string _) + { + // Prevent deleting of our held props on scene destruction + foreach (CVRSyncHelper.PropData prop in _heldPropData) + { + if (prop.Spawnable == null) continue; + PlacePropInPersistantPropsContainer(prop.Spawnable); + _spawnablePositionStack.Push(new SpawnablePositionContainer(prop.Spawnable)); + } + + // Likely in a vehicle + _heightOffset = BetterBetterCharacterController.Instance._lastCvrSeat != null + ? GetHeightOffsetFromPlayer() + : 0f; + } + + private static void OnInstanceConnected(string _) + { + // Request the server to respawn our props by GUID, and add a secret key to the propData to identify it + + foreach (CVRSyncHelper.PropData prop in _heldPropData) + { + if (prop.Spawnable == null) continue; + + // Generate a new identity key for the prop (this is used to identify the prop when we respawn it) + Vector3 identityKey = new(Random.Range(0, 1000), Random.Range(0, 1000), Random.Range(0, 1000)); + _keyToPropData.Add(identityKey, prop); + + SpawnPropFromGuid(prop.ObjectId, + new Vector3(prop.PositionX, prop.PositionY, prop.PositionZ), + new Vector3(prop.RotationX, prop.RotationY, prop.RotationZ), + identityKey); + } + } + + #endregion Game Events + + #region Util + + private static bool GetPropData(CVRSpawnable spawnable, out CVRSyncHelper.PropData propData) + { + if (spawnable == null) + { + propData = null; + return false; + } + foreach (CVRSyncHelper.PropData data in CVRSyncHelper.Props) + { + if (data.InstanceId != spawnable.instanceId) continue; + propData = data; + return true; + } + propData = null; + return false; + } + + private static bool GetPropDataById(string instanceId, out CVRSyncHelper.PropData propData) + { + foreach (CVRSyncHelper.PropData data in CVRSyncHelper.Props) + { + if (data.InstanceId != instanceId) continue; + propData = data; + return true; + } + propData = null; + return false; + } + + private static void PlacePropInAdditiveContentScene(CVRSpawnable spawnable) + { + spawnable.transform.parent.SetParent(null); // Unparent from the prop container + SceneManager.MoveGameObjectToScene(spawnable.transform.parent.gameObject, + SceneManager.GetSceneByName(CVRObjectLoader.AdditiveContentSceneName)); + } + + private static void PlacePropInPersistantPropsContainer(CVRSpawnable spawnable) + { + spawnable.transform.parent.SetParent(GetOrCreatePropsContainer().transform); + } + + private static void DeleteOrRecycleProp(CVRSyncHelper.PropData prop) + { + if (prop.Spawnable == null) prop.Recycle(); + else prop.Spawnable.Delete(); + if (_heldPropData.Contains(prop)) _heldPropData.Remove(prop); + } + + private static void SpawnPropFromGuid(string propGuid, Vector3 position, Vector3 rotation, Vector3 identityKey) + { + using DarkRiftWriter darkRiftWriter = DarkRiftWriter.Create(); + darkRiftWriter.Write(propGuid); + darkRiftWriter.Write(position.x); + darkRiftWriter.Write(position.y); + darkRiftWriter.Write(position.z); + darkRiftWriter.Write(rotation.x); + darkRiftWriter.Write(rotation.y); + darkRiftWriter.Write(rotation.z); + darkRiftWriter.Write(identityKey.x); // for scale, but unused by CVR + darkRiftWriter.Write(identityKey.y); // we will use this to identify our prop + darkRiftWriter.Write(identityKey.z); // and recycle existing instance if it exists + darkRiftWriter.Write(0f); // if not zero, prop spawn will be rejected by gs + using Message message = Message.Create(10050, darkRiftWriter); + NetworkManager.Instance.GameNetwork.SendMessage(message, SendMode.Reliable); + } + + private static Vector3 GetIdentityKeyFromPropData(CVRSyncHelper.PropData propData) + => new(propData.ScaleX, propData.ScaleY, propData.ScaleZ); + + private const int WORLD_RAYCAST_LAYER_MASK = + (1 << 0) | // Default + (1 << 16) | (1 << 17) | (1 << 18) | (1 << 19) | + (1 << 20) | (1 << 21) | (1 << 22) | (1 << 23) | + (1 << 24) | (1 << 25) | (1 << 26) | (1 << 27) | + (1 << 28) | (1 << 29) | (1 << 30) | (1 << 31); + + private static float GetHeightOffsetFromPlayer() + { + Vector3 playerPos = PlayerSetup.Instance.GetPlayerPosition(); + Ray ray = new(playerPos, Vector3.down); + + // ReSharper disable once Unity.PreferNonAllocApi + RaycastHit[] hits = Physics.RaycastAll(ray, 1000f, WORLD_RAYCAST_LAYER_MASK, QueryTriggerInteraction.Ignore); + Scene baseScene = SceneManager.GetActiveScene(); + + float closestDist = float.MaxValue; + Vector3 closestPoint = Vector3.zero; + bool foundValidHit = false; + + foreach (RaycastHit hit in hits) + { + if (hit.collider.gameObject.scene != baseScene) continue; // Ignore objects not in the world scene + if (!(hit.distance < closestDist)) continue; + closestDist = hit.distance; + closestPoint = hit.point; + foundValidHit = true; + } + + if (!foundValidHit) return 0f; // TODO: idk if i should do this + float offset = playerPos.y - closestPoint.y; + return Mathf.Clamp(offset, 0f, 20f); + } + + #endregion Util + + #region Helper Classes + + private readonly struct SpawnablePositionContainer + { + private readonly CVRSpawnable _spawnable; + private readonly Vector3[] _posOffsets; + private readonly Quaternion[] _rotOffsets; + + public SpawnablePositionContainer(CVRSpawnable spawnable) + { + _spawnable = spawnable; + + int syncedTransforms = 1 + _spawnable.subSyncs.Count; // root + subSyncs + + _posOffsets = new Vector3[syncedTransforms]; + _rotOffsets = new Quaternion[syncedTransforms]; + + Transform playerTransform = PlayerSetup.Instance.transform; + + // Save root offset relative to player + Transform _spawnableTransform = _spawnable.transform; + _posOffsets[0] = playerTransform.InverseTransformPoint(_spawnableTransform.position); + _rotOffsets[0] = Quaternion.Inverse(playerTransform.rotation) * _spawnableTransform.rotation; + + // Save subSync offsets relative to player + for (int i = 0; i < _spawnable.subSyncs.Count; i++) + { + Transform subSyncTransform = _spawnable.subSyncs[i].transform; + if (subSyncTransform == null) continue; + _posOffsets[i + 1] = playerTransform.InverseTransformPoint(subSyncTransform.position); + _rotOffsets[i + 1] = Quaternion.Inverse(playerTransform.rotation) * subSyncTransform.rotation; + } + } + + public void ReapplyOffsets() + { + Transform playerTransform = PlayerSetup.Instance.transform; + + // Reapply to root + Vector3 rootWorldPos = playerTransform.TransformPoint(_posOffsets[0]); + rootWorldPos.y += _heightOffset; + _spawnable.transform.position = rootWorldPos; + _spawnable.transform.rotation = playerTransform.rotation * _rotOffsets[0]; + + // Reapply to subSyncs + for (int i = 0; i < _spawnable.subSyncs.Count; i++) + { + Transform subSyncTransform = _spawnable.subSyncs[i].transform; + if (subSyncTransform == null) continue; + + Vector3 subWorldPos = playerTransform.TransformPoint(_posOffsets[i + 1]); + subWorldPos.y += _heightOffset; + subSyncTransform.position = subWorldPos; + subSyncTransform.rotation = playerTransform.rotation * _rotOffsets[i + 1]; + } + + // hack + _spawnable.needsUpdate = true; + _spawnable.UpdateSubSyncValues(); + _spawnable.sendUpdate(); + } + } + + #endregion Helper Classes +} \ No newline at end of file diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs b/YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bfc0a13 --- /dev/null +++ b/YouAreMyPropNowWeAreHavingSoftTacosLater/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using MelonLoader; +using NAK.YouAreMyPropNowWeAreHavingSoftTacosLater.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater))] + +[assembly: MelonInfo( + typeof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater.YouAreMyPropNowWeAreHavingSoftTacosLaterMod), + nameof(NAK.YouAreMyPropNowWeAreHavingSoftTacosLater), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater" +)] + +[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.YouAreMyPropNowWeAreHavingSoftTacosLater.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/YouAreMyPropNowWeAreHavingSoftTacosLater/README.md b/YouAreMyPropNowWeAreHavingSoftTacosLater/README.md new file mode 100644 index 0000000..9ddff99 --- /dev/null +++ b/YouAreMyPropNowWeAreHavingSoftTacosLater/README.md @@ -0,0 +1,19 @@ +# YouAreMyPropNowWeAreHavingSoftTacosLater + +Lets you bring held & attached props through world loads. + +https://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO + +## Examples +https://fixupx.com/NotAKidoS/status/1910545346922422675 + +--- + +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/YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj b/YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj new file mode 100644 index 0000000..5a8badc --- /dev/null +++ b/YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.csproj @@ -0,0 +1,6 @@ + + + + YouAreMineNow + + diff --git a/YouAreMyPropNowWeAreHavingSoftTacosLater/format.json b/YouAreMyPropNowWeAreHavingSoftTacosLater/format.json new file mode 100644 index 0000000..78c5bbc --- /dev/null +++ b/YouAreMyPropNowWeAreHavingSoftTacosLater/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "YouAreMyPropNowWeAreHavingSoftTacosLater", + "modversion": "1.0.0", + "gameversion": "2025r179", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Lets you bring held & attached props through world loads.\nhttps://youtu.be/9P6Jeh-VN58?si=eXTPGyKB_0wq1gZO", + "searchtags": [ + "prop", + "spawn", + "friend", + "load" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/YouAreMyPropNowWeAreHavingSoftTacosLater.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/YouAreMyPropNowWeAreHavingSoftTacosLater/", + "changelog": "- Initial Release", + "embedcolor": "#00FFFF" +} \ No newline at end of file From 0cde9ecb21676ed05b5b74dff1d82aea8e658f30 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 11 Apr 2025 12:39:51 +0000 Subject: [PATCH 03/10] [NAK_CVR_Mods] Update mod list in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a11162..84b53d5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ |------|-------------|----------| | [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](ASTExtension/ASTExtension.zip) | | [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | [Download](AvatarQueueSystemTweaks/AvatarQueueSystemTweaks.zip) | -| [CustomRichPresence](CustomRichPresence/README.md) | Gives the Drop Prop button more utility by allowing you to drop props in the air. | [Download](CustomRichPresence/CustomRichPresence.zip) | | [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](CustomSpawnPoint/CustomSpawnPoint.zip) | | [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in HalfbodyIK. | [Download](FuckToes/FuckToes.zip) | | [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | [Download](KeepVelocityOnExitFlight/KeepVelocityOnExitFlight.zip) | @@ -20,12 +19,14 @@ | [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | [Download](SmootherRay/SmootherRay.zip) | | [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | [Download](Stickers/Stickers.zip) | | [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](ThirdPerson/ThirdPerson.zip) | +| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held & attached props through world loads. | [Download](YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.zip) | ### Experimental Mods | Name | Description | Download | |------|-------------|----------| | [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | [Download](.Experimental/CVRLuaToolsExtension/CVRLuaToolsExtension.zip) | +| [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Gives the Drop Prop button more utility by allowing you to drop props in the air. | [Download](.Experimental/CustomRichPresence/CustomRichPresence.zip) | | [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction. | [Download](.Experimental/LuaNetworkVariables/LuaNetworkVariables.zip) | | [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | [Download](.Experimental/LuaTTS/LuaTTS.zip) | | [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | [Download](.Experimental/OriginShift/OriginShift.zip) | From b246f71e6eac1cbe754a939b87ded0b732302c0e Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:40:53 -0500 Subject: [PATCH 04/10] [CustomRichPresence] fix readme --- .Experimental/CustomRichPresence/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.Experimental/CustomRichPresence/README.md b/.Experimental/CustomRichPresence/README.md index 33bd3fd..1f37b48 100644 --- a/.Experimental/CustomRichPresence/README.md +++ b/.Experimental/CustomRichPresence/README.md @@ -1,6 +1,6 @@ -# DropPropTweak +# Custom Rich Presence -Gives the Drop Prop button more utility by allowing you to drop props in the air. +Lets you customize the Steam & Discord rich presence messages & values. --- From a2aa3b9871ccaab0657967d5e6ada19d3a53d6c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 11 Apr 2025 12:41:14 +0000 Subject: [PATCH 05/10] [NAK_CVR_Mods] Update mod list in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84b53d5..b8a81e8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ | Name | Description | Download | |------|-------------|----------| | [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | [Download](.Experimental/CVRLuaToolsExtension/CVRLuaToolsExtension.zip) | -| [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Gives the Drop Prop button more utility by allowing you to drop props in the air. | [Download](.Experimental/CustomRichPresence/CustomRichPresence.zip) | +| [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Lets you customize the Steam & Discord rich presence messages & values. | [Download](.Experimental/CustomRichPresence/CustomRichPresence.zip) | | [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction. | [Download](.Experimental/LuaNetworkVariables/LuaNetworkVariables.zip) | | [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | [Download](.Experimental/LuaTTS/LuaTTS.zip) | | [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | [Download](.Experimental/OriginShift/OriginShift.zip) | From 8f8f2ad1fb780126d98d07e34a35850c6e2d62ef Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:45:31 -0500 Subject: [PATCH 06/10] [NAK_CVR_Mods] fixed generated download link in readme --- .github/scripts/update-modlist.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/update-modlist.js b/.github/scripts/update-modlist.js index b404561..37cf1c5 100644 --- a/.github/scripts/update-modlist.js +++ b/.github/scripts/update-modlist.js @@ -52,11 +52,11 @@ function formatTable(mods, baseDir) { let rows = mods.map(modPath => { const modName = path.basename(modPath); const readmeLink = path.join(modPath, 'README.md'); - const zipLink = path.join(modPath, `${modName}.zip`); + const dllLink = path.join(modPath, `${modName}.dll`); const readmePath = path.join(modPath, 'README.md'); const description = extractDescription(readmePath); - return `| [${modName}](${readmeLink}) | ${description} | [Download](${zipLink}) |`; + return `| [${modName}](${readmeLink}) | ${description} | [Download](${dllLink}) |`; }); return [ From a2e29149e29397d96956a26fe8d32cdb87db77cb Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:46:51 -0500 Subject: [PATCH 07/10] [NAK_CVR_Mods] test --- .github/scripts/update-modlist.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/scripts/update-modlist.js b/.github/scripts/update-modlist.js index 37cf1c5..383e275 100644 --- a/.github/scripts/update-modlist.js +++ b/.github/scripts/update-modlist.js @@ -1,6 +1,5 @@ const fs = require('fs'); const path = require('path'); - const ROOT = '.'; const EXPERIMENTAL = '.Experimental'; const README_PATH = 'README.md'; @@ -19,7 +18,7 @@ function extractDescription(readmePath) { try { const content = fs.readFileSync(readmePath, 'utf8'); const lines = content.split('\n'); - + // Find the first header (# something) let headerIndex = -1; for (let i = 0; i < lines.length; i++) { @@ -28,7 +27,7 @@ function extractDescription(readmePath) { break; } } - + // If we found a header, look for the first non-empty line after it if (headerIndex !== -1) { for (let i = headerIndex + 1; i < lines.length; i++) { @@ -38,7 +37,7 @@ function extractDescription(readmePath) { } } } - + return 'No description available'; } catch (error) { console.error(`Error reading ${readmePath}:`, error); @@ -46,19 +45,29 @@ function extractDescription(readmePath) { } } +function checkIfDllExists(modPath, modName) { + const dllPath = path.join(modPath, `${modName}.dll`); + return fs.existsSync(dllPath); +} + function formatTable(mods, baseDir) { if (mods.length === 0) return ''; - + let rows = mods.map(modPath => { const modName = path.basename(modPath); const readmeLink = path.join(modPath, 'README.md'); - const dllLink = path.join(modPath, `${modName}.dll`); const readmePath = path.join(modPath, 'README.md'); const description = extractDescription(readmePath); - return `| [${modName}](${readmeLink}) | ${description} | [Download](${dllLink}) |`; + // Check if DLL exists and format download cell accordingly + const hasDll = checkIfDllExists(modPath, modName); + const downloadCell = hasDll + ? `[Download](${path.join(modPath, `${modName}.dll`)})` + : 'No Download'; + + return `| [${modName}](${readmeLink}) | ${description} | ${downloadCell} |`; }); - + return [ `### ${baseDir === EXPERIMENTAL ? 'Experimental Mods' : 'Released Mods'}`, '', @@ -73,7 +82,6 @@ function updateReadme(modListSection) { const readme = fs.readFileSync(README_PATH, 'utf8'); const before = readme.split(MARKER_START)[0]; const after = readme.split(MARKER_END)[1]; - const newReadme = `${before}${MARKER_START}\n\n${modListSection}\n${MARKER_END}${after}`; fs.writeFileSync(README_PATH, newReadme); } From d5eb9ae1a08553803510dd8efa591b7c61beb074 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 11 Apr 2025 12:47:11 +0000 Subject: [PATCH 08/10] [NAK_CVR_Mods] Update mod list in README --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b8a81e8..017ca92 100644 --- a/README.md +++ b/README.md @@ -6,31 +6,31 @@ | Name | Description | Download | |------|-------------|----------| -| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](ASTExtension/ASTExtension.zip) | -| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | [Download](AvatarQueueSystemTweaks/AvatarQueueSystemTweaks.zip) | -| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](CustomSpawnPoint/CustomSpawnPoint.zip) | -| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in HalfbodyIK. | [Download](FuckToes/FuckToes.zip) | -| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | [Download](KeepVelocityOnExitFlight/KeepVelocityOnExitFlight.zip) | -| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | [Download](LazyPrune/LazyPrune.zip) | -| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | [Download](PropLoadingHexagon/PropLoadingHexagon.zip) | -| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.zip) | -| [RelativeSync](RelativeSync/README.md) | Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. | [Download](RelativeSync/RelativeSync.zip) | -| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](ShareBubbles/ShareBubbles.zip) | -| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | [Download](SmootherRay/SmootherRay.zip) | -| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | [Download](Stickers/Stickers.zip) | -| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](ThirdPerson/ThirdPerson.zip) | -| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held & attached props through world loads. | [Download](YouAreMyPropNowWeAreHavingSoftTacosLater/YouAreMyPropNowWeAreHavingSoftTacosLater.zip) | +| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | No Download | +| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | No Download | +| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | No Download | +| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in HalfbodyIK. | No Download | +| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | No Download | +| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | No Download | +| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | No Download | +| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | No Download | +| [RelativeSync](RelativeSync/README.md) | Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. | No Download | +| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | No Download | +| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | No Download | +| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | No Download | +| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | No Download | +| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held & attached props through world loads. | No Download | ### Experimental Mods | Name | Description | Download | |------|-------------|----------| -| [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | [Download](.Experimental/CVRLuaToolsExtension/CVRLuaToolsExtension.zip) | -| [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Lets you customize the Steam & Discord rich presence messages & values. | [Download](.Experimental/CustomRichPresence/CustomRichPresence.zip) | -| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction. | [Download](.Experimental/LuaNetworkVariables/LuaNetworkVariables.zip) | -| [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | [Download](.Experimental/LuaTTS/LuaTTS.zip) | -| [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | [Download](.Experimental/OriginShift/OriginShift.zip) | -| [ScriptingSpoofer](.Experimental/ScriptingSpoofer/README.md) | Prevents **local** scripts from accessing your Username or UserID by spoofing them with random values each session. | [Download](.Experimental/ScriptingSpoofer/ScriptingSpoofer.zip) | +| [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | No Download | +| [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Lets you customize the Steam & Discord rich presence messages & values. | No Download | +| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction. | No Download | +| [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | No Download | +| [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | No Download | +| [ScriptingSpoofer](.Experimental/ScriptingSpoofer/README.md) | Prevents **local** scripts from accessing your Username or UserID by spoofing them with random values each session. | No Download | From 1fbfcd80cdb6b443079df37b825022d3edf61a5b Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:52:15 -0500 Subject: [PATCH 09/10] [NAK_CVR_Mods] 2 --- .github/scripts/update-modlist.js | 160 +++++++++++++++++++++++------- 1 file changed, 124 insertions(+), 36 deletions(-) diff --git a/.github/scripts/update-modlist.js b/.github/scripts/update-modlist.js index 383e275..8ed69bf 100644 --- a/.github/scripts/update-modlist.js +++ b/.github/scripts/update-modlist.js @@ -1,10 +1,56 @@ const fs = require('fs'); const path = require('path'); +const https = require('https'); + +// Configuration const ROOT = '.'; const EXPERIMENTAL = '.Experimental'; const README_PATH = 'README.md'; const MARKER_START = ''; const MARKER_END = ''; +const REPO_OWNER = process.env.REPO_OWNER || 'NotAKidoS'; +const REPO_NAME = process.env.REPO_NAME || 'NAK_CVR_Mods'; + +// Function to get latest release info from GitHub API +async function getLatestRelease() { + return new Promise((resolve, reject) => { + const options = { + hostname: 'api.github.com', + path: `/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`, + method: 'GET', + headers: { + 'User-Agent': 'Node.js GitHub Release Checker', + 'Accept': 'application/vnd.github.v3+json' + } + }; + + const req = https.request(options, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + if (res.statusCode === 200) { + try { + resolve(JSON.parse(data)); + } catch (e) { + reject(new Error(`Failed to parse GitHub API response: ${e.message}`)); + } + } else { + reject(new Error(`GitHub API request failed with status code: ${res.statusCode}`)); + } + }); + }); + + req.on('error', (e) => { + reject(new Error(`GitHub API request error: ${e.message}`)); + }); + + req.end(); + }); +} function getModFolders(baseDir) { const entries = fs.readdirSync(baseDir, { withFileTypes: true }); @@ -45,37 +91,69 @@ function extractDescription(readmePath) { } } -function checkIfDllExists(modPath, modName) { - const dllPath = path.join(modPath, `${modName}.dll`); - return fs.existsSync(dllPath); -} - -function formatTable(mods, baseDir) { +async function formatTable(mods, baseDir) { if (mods.length === 0) return ''; - let rows = mods.map(modPath => { - const modName = path.basename(modPath); - const readmeLink = path.join(modPath, 'README.md'); - const readmePath = path.join(modPath, 'README.md'); - const description = extractDescription(readmePath); + try { + // Get the latest release info from GitHub + const latestRelease = await getLatestRelease(); + const releaseAssets = latestRelease.assets || []; - // Check if DLL exists and format download cell accordingly - const hasDll = checkIfDllExists(modPath, modName); - const downloadCell = hasDll - ? `[Download](${path.join(modPath, `${modName}.dll`)})` - : 'No Download'; - - return `| [${modName}](${readmeLink}) | ${description} | ${downloadCell} |`; - }); - - return [ - `### ${baseDir === EXPERIMENTAL ? 'Experimental Mods' : 'Released Mods'}`, - '', - '| Name | Description | Download |', - '|------|-------------|----------|', - ...rows, - '' - ].join('\n'); + // Create a map of available files in the release + const availableFiles = {}; + releaseAssets.forEach(asset => { + availableFiles[asset.name] = asset.browser_download_url; + }); + + let rows = mods.map(modPath => { + const modName = path.basename(modPath); + const readmeLink = path.join(modPath, 'README.md'); + const readmePath = path.join(modPath, 'README.md'); + const description = extractDescription(readmePath); + + // Check if the DLL exists in the latest release + const dllFilename = `${modName}.dll`; + let downloadSection; + + if (availableFiles[dllFilename]) { + downloadSection = `[Download](${availableFiles[dllFilename]})`; + } else { + downloadSection = 'No Download'; + } + + return `| [${modName}](${readmeLink}) | ${description} | ${downloadSection} |`; + }); + + return [ + `### ${baseDir === EXPERIMENTAL ? 'Experimental Mods' : 'Released Mods'}`, + '', + '| Name | Description | Download |', + '|------|-------------|----------|', + ...rows, + '' + ].join('\n'); + } catch (error) { + console.error('Error fetching release information:', error); + + // Fallback to showing "No Download" for all mods if we can't fetch release info + let rows = mods.map(modPath => { + const modName = path.basename(modPath); + const readmeLink = path.join(modPath, 'README.md'); + const readmePath = path.join(modPath, 'README.md'); + const description = extractDescription(readmePath); + + return `| [${modName}](${readmeLink}) | ${description} | No Download |`; + }); + + return [ + `### ${baseDir === EXPERIMENTAL ? 'Experimental Mods' : 'Released Mods'}`, + '', + '| Name | Description | Download |', + '|------|-------------|----------|', + ...rows, + '' + ].join('\n'); + } } function updateReadme(modListSection) { @@ -86,12 +164,22 @@ function updateReadme(modListSection) { fs.writeFileSync(README_PATH, newReadme); } -const mainMods = getModFolders(ROOT).filter(dir => !dir.startsWith(EXPERIMENTAL)); -const experimentalMods = getModFolders(EXPERIMENTAL); +async function main() { + try { + const mainMods = getModFolders(ROOT).filter(dir => !dir.startsWith(EXPERIMENTAL)); + const experimentalMods = getModFolders(EXPERIMENTAL); + + const mainModsTable = await formatTable(mainMods, ROOT); + const experimentalModsTable = await formatTable(experimentalMods, EXPERIMENTAL); + + const tableContent = [mainModsTable, experimentalModsTable].join('\n'); + updateReadme(tableContent); + + console.log('README.md updated successfully!'); + } catch (error) { + console.error('Error updating README:', error); + process.exit(1); + } +} -const tableContent = [ - formatTable(mainMods, ROOT), - formatTable(experimentalMods, EXPERIMENTAL) -].join('\n'); - -updateReadme(tableContent); \ No newline at end of file +main(); \ No newline at end of file From 2af27cc81d1278b956e2d043cf499b35f002f6b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 11 Apr 2025 12:52:38 +0000 Subject: [PATCH 10/10] [NAK_CVR_Mods] Update mod list in README --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 017ca92..cb01fcb 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,19 @@ | Name | Description | Download | |------|-------------|----------| -| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | No Download | -| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | No Download | -| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | No Download | +| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ASTExtension.dll) | +| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/AvatarQueueSystemTweaks.dll) | +| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/CustomSpawnPoint.dll) | | [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in HalfbodyIK. | No Download | -| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | No Download | -| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | No Download | -| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | No Download | -| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | No Download | -| [RelativeSync](RelativeSync/README.md) | Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. | No Download | -| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | No Download | -| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | No Download | -| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | No Download | -| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | No Download | +| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/KeepVelocityOnExitFlight.dll) | +| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/LazyPrune.dll) | +| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/PropLoadingHexagon.dll) | +| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/RCCVirtualSteeringWheel.dll) | +| [RelativeSync](RelativeSync/README.md) | Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/RelativeSync.dll) | +| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ShareBubbles.dll) | +| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/SmootherRay.dll) | +| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/Stickers.dll) | +| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ThirdPerson.dll) | | [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held & attached props through world loads. | No Download | ### Experimental Mods