private void OnDestroy() { foreach (var bone in GetComponents <DynamicBone>()) { JigglySolverApi.DynamicBoneOnDestroyPatch(bone.Pointer); } }
private static IntPtr GetProcAddressSafe(IntPtr module, string name) { if (module == IntPtr.Zero) { return(IntPtr.Zero); } var result = JigglySolverApi.GetProcAddress(module, name); if (result == IntPtr.Zero) { MelonLogger.Error($"No entry point for {name}"); } else { MelonDebug.Msg($"Entry point for {name} is {result}"); } return(result); }
public static void SetOffsets() { var ga = JigglySolverApi.LoadLibraryA("GameAssembly.dll"); if (ga == IntPtr.Zero) { MelonLogger.Error("GameAssembly was not found, crash will likely follow"); } var icalls = new ICallOffsets { get_transform_component = GetICall("UnityEngine.Component::get_transform"), get_transform_ltw_matrix = GetICall("UnityEngine.Transform::get_localToWorldMatrix_Injected"), get_component_enabled = GetICall("UnityEngine.Behaviour::get_enabled"), get_transform_child_count = GetICall("UnityEngine.Transform::get_childCount"), set_transform_position_and_rotation = GetICall("UnityEngine.Transform::SetPositionAndRotation_Injected"), gchandle_create = GetProcAddressSafe(ga, nameof(IL2CPP.il2cpp_gchandle_new)), gchandle_drop = GetProcAddressSafe(ga, nameof(IL2CPP.il2cpp_gchandle_free)), gchandle_get = GetProcAddressSafe(ga, nameof(IL2CPP.il2cpp_gchandle_get_target)), transform_get_local_rotation = GetICall("UnityEngine.Transform::get_localRotation_Injected") }; var objectOffsets = new ObjectOffsets { cached_ptr = GetFieldOffset <UnityEngine.Object>(nameof(UnityEngine.Object.m_CachedPtr)), }; var colliderOffsets = new ColliderComponentOffsets { collider_radius = GetFieldOffset <DynamicBoneCollider>(nameof(DynamicBoneCollider.m_Radius)), collider_center = GetFieldOffset <DynamicBoneCollider>(nameof(DynamicBoneCollider.m_Center)), collider_bound = GetFieldOffset <DynamicBoneCollider>(nameof(DynamicBoneCollider.m_Bound)), collider_direction = GetFieldOffset <DynamicBoneCollider>(nameof(DynamicBoneCollider.m_Direction)), collider_height = GetFieldOffset <DynamicBoneCollider>(nameof(DynamicBoneCollider.m_Height)), }; var listOffsets = new ListClassOffsets { size = GetFieldOffset <List <Il2CppSystem.Object> >(nameof(List <int> ._size)), store = GetFieldOffset <List <Il2CppSystem.Object> >(nameof(List <int> ._items)), array_store = (uint)IntPtr.Size * 4 // classPtr, monitor, bounds, maxSize }; var boneOffsets = new BoneComponentOffsets { collider_list = GetFieldOffset <DynamicBone>(nameof(DynamicBone.m_Colliders)), particle_list = GetFieldOffset <DynamicBone>(nameof(DynamicBone.field_Private_List_1_ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique_0)), force = GetFieldOffset <DynamicBone>(nameof(DynamicBone.m_Force)), gravity = GetFieldOffset <DynamicBone>(nameof(DynamicBone.m_Gravity)), local_gravity = GetFieldOffset <DynamicBone>(nameof(DynamicBone.field_Private_Vector3_0)), freeze_axis = GetFieldOffset <DynamicBone>(nameof(DynamicBone.m_FreezeAxis)), update_rate = GetFieldOffset <DynamicBone>(nameof(DynamicBone.m_UpdateRate)), root = GetFieldOffset <DynamicBone>(nameof(DynamicBone.m_Root)), }; var particleOffsets = new ParticleClassOffsets { transform = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Transform_0)), parent_index = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Int32_0)), damping = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Single_0)), elasticity = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Single_1)), stiffness = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Single_2)), inert = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Single_3)), radius = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Single_4)), end_offset = GetFieldOffset <DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique>(nameof(DynamicBone.ObjectNPrivateTrInSiVeSiQuVeSiVeSiUnique.field_Public_Vector3_2)), }; JigglySolverApi.SetPointersAndOffsets(ref icalls, ref colliderOffsets, ref boneOffsets, ref particleOffsets, ref listOffsets, ref objectOffsets); }
public override void OnApplicationStart() { ClassInjector.RegisterTypeInIl2Cpp <BoneDeleteHandler>(); var category = MelonPreferences.CreateCategory("Turbones"); var enableCollisionChecks = category.CreateEntry("OptimizedCollisionChecks", true, "Enable optimized collision checks"); var enableUpdate = category.CreateEntry("OptimizedUpdate", true, "Enable optimized simulation"); var updateMultiThread = category.CreateEntry("OptimizedMultiThread", false, "Enable multithreading (placebo!)"); var threadCount = category.CreateEntry("DynamicBoneThreads", Math.Max(1, Environment.ProcessorCount / 2 - 1), "Thread count (placebo!)", dont_save_default: true); var dllName = "JigglyRustSolver.dll"; try { using var resourceStream = Assembly.GetExecutingAssembly() .GetManifestResourceStream(typeof(TurbonesMod), dllName); using var fileStream = File.Open("VRChat_Data/Plugins/" + dllName, FileMode.Create, FileAccess.Write); resourceStream.CopyTo(fileStream); } catch (IOException ex) { MelonLogger.Warning("Failed to write native dll; will attempt loading it anyway. This is normal if you're running multiple instances of VRChat"); MelonDebug.Msg(ex.ToString()); } if (!JigglySolverApi.Initialize("VRChat_Data/Plugins/" + dllName)) { MelonLogger.Error("Error initializing native library; mod won't work"); return; } ourDynBoneCollideEntryPoint = Marshal.ReadIntPtr((IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod( typeof(DynamicBoneCollider).GetMethod(nameof(DynamicBoneCollider .Method_Public_Void_byref_Vector3_Single_0))).GetValue(null)); ourDynBoneUpdateEntryPoint = Marshal.ReadIntPtr((IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod( typeof(DynamicBone).GetMethod(nameof(DynamicBone .Method_Private_Void_Single_Boolean_0))).GetValue(null)); var isCollidePatched = false; unsafe void PatchCollide() { if (isCollidePatched) { return; fixed(IntPtr *a = &ourDynBoneCollideEntryPoint) MelonUtils.NativeHookAttach((IntPtr)a, JigglySolverApi.LibDynBoneCollideEntryPoint); MelonLogger.Msg("Patched DynamicBone Collide"); isCollidePatched = true; } unsafe void UnpatchCollide() { if (!isCollidePatched) { return; fixed(IntPtr *a = &ourDynBoneCollideEntryPoint) MelonUtils.NativeHookDetach((IntPtr)a, JigglySolverApi.LibDynBoneCollideEntryPoint); MelonLogger.Msg("Unpatched DynamicBone Collide"); isCollidePatched = false; } unsafe void RepatchUpdate(bool useFast, bool useMt) { // TODO: re-enable multithreading if it ever gets useful/stable useMt = false; if (ourLastPatchPointer != IntPtr.Zero) { fixed(IntPtr *a = &ourDynBoneUpdateEntryPoint) MelonUtils.NativeHookDetach((IntPtr)a, ourLastPatchPointer); MelonLogger.Msg("Unpatched DynamicBone Update"); ourLastPatchPointer = IntPtr.Zero; } if (!CheckWasSuccessful) return; } if (useFast) { ourLastPatchPointer = useMt ? JigglySolverApi.LibDynBoneUpdateMultiThreaded : JigglySolverApi.LibDynBoneUpdateSingleThreaded; fixed(IntPtr *a = &ourDynBoneUpdateEntryPoint) MelonUtils.NativeHookAttach((IntPtr)a, ourLastPatchPointer); MelonLogger.Msg($"Patched DynamicBone Update (multithreaded: {useMt})"); } else { ourLastPatchPointer = JigglySolverApi.DynamicBoneUpdateNotifyPatch; fixed(IntPtr *a = &ourDynBoneUpdateEntryPoint) MelonUtils.NativeHookAttach((IntPtr)a, ourLastPatchPointer); JigglySolverApi.SetOriginalBoneUpdateDelegate(ourDynBoneUpdateEntryPoint); MelonLogger.Msg($"Patched DynamicBone Update (notify)"); } } CheckDummyThree(); enableCollisionChecks.OnValueChanged += (_, v) => { if (v) PatchCollide(); }