public static unsafe void ApplyPatches() { try { // PDM's are scary:( MethodInfo avatarChangedMethod = typeof(VRCAvatarManager).GetMethods().FirstOrDefault(method => method.Name.StartsWith("Method_Private_Void_ApiAvatar_GameObject_Action_1_Boolean_")); IntPtr original = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(avatarChangedMethod).GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&original), typeof(ImmersiveTouch).GetMethod(nameof(ImmersiveTouch.OnAvatarChanged), BindingFlags.Static | BindingFlags.Public) !.MethodHandle.GetFunctionPointer()); avatarChangedDelegate = Marshal.GetDelegateForFunctionPointer <AvatarChangedDelegate>(original); } catch (Exception e) { MelonLogger.Error($"Failed to patch: OnAvatarChanged\n{e}"); } try { IntPtr original = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(DynamicBone).GetMethod(nameof(DynamicBone.Method_Private_Void_Boolean_0))).GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&original), typeof(ImmersiveTouch).GetMethod(nameof(ImmersiveTouch.OnUpdateParticles), BindingFlags.Static | BindingFlags.Public) !.MethodHandle.GetFunctionPointer()); updateParticlesDelegate = Marshal.GetDelegateForFunctionPointer <UpdateParticlesDelegate>(original); } catch (Exception e) { MelonLogger.Error($"Failed to patch: OnUpdateParticles\n{e}"); } try { IntPtr original = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(DynamicBoneCollider).GetMethod(nameof(DynamicBoneCollider.Method_Public_Void_byref_Vector3_Single_0))).GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&original), typeof(ImmersiveTouch).GetMethod(nameof(ImmersiveTouch.OnCollide), BindingFlags.Static | BindingFlags.Public) !.MethodHandle.GetFunctionPointer()); collideDelegate = Marshal.GetDelegateForFunctionPointer <CollideDelegate>(original); } catch (Exception e) { MelonLogger.Error($"Failed to patch: OnCollide\n{e}"); } }
public static unsafe void SetupHooking() { try { var intPtr = (IntPtr)typeof(VRCAvatarManager.MulticastDelegateNPublicSealedVoGaVRBoUnique) .GetField( "NativeMethodInfoPtr_Invoke_Public_Virtual_New_Void_GameObject_VRC_AvatarDescriptor_Boolean_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(intPtr, new Action <IntPtr, IntPtr, IntPtr, bool>(OnAvatarInstantiated).Method.MethodHandle .GetFunctionPointer()); _onAvatarInstantiatedDelegate = Marshal.GetDelegateForFunctionPointer <AvatarInstantiatedDelegate>(*(IntPtr *)(void *)intPtr); intPtr = (IntPtr)typeof(VRCAvatarManager) .GetField( "NativeMethodInfoPtr_Method_Public_Boolean_ApiAvatar_String_Single_MulticastDelegateNPublicSealedVoGaVRBoUnique_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(intPtr, new Action <IntPtr, IntPtr, string, float, IntPtr>(OnAvatarSwitch).Method.MethodHandle .GetFunctionPointer()); _avatarSwitch = Marshal.GetDelegateForFunctionPointer <OnAvatarSwitchDelegate>(*(IntPtr *)(void *)intPtr); } catch (Exception ex) { MelonLogger.Msg("Patch Failed " + ex); } }
public override MethodBase DetourTo(MethodBase replacement) { DebugCheck(); DynamicMethodDefinition newreplacementdmd = CopyOriginal(); HarmonyManipulator.Manipulate(Original, Original.GetPatchInfo(), new ILContext(newreplacementdmd.Definition)); MethodInfo newreplacement = newreplacementdmd.Generate(); MethodInfo il2CppShim = CreateIl2CppShim(newreplacement).Generate(); Type il2CppShimDelegateType = DelegateTypeFactory.instance.CreateDelegateType(il2CppShim, CallingConvention.Cdecl); Delegate il2CppShimDelegate = il2CppShim.CreateDelegate(il2CppShimDelegateType); IntPtr il2CppShimDelegatePtr = il2CppShimDelegate.GetFunctionPointer(); if (methodDetourPointer != IntPtr.Zero) { MelonUtils.NativeHookDetach(copiedMethodInfoPointer, methodDetourPointer); } MelonUtils.NativeHookAttach(copiedMethodInfoPointer, il2CppShimDelegatePtr); methodDetourPointer = il2CppShimDelegatePtr; PatchTools_RememberObject(Original, new LemonTuple <MethodInfo, MethodInfo, Delegate> { Item1 = newreplacement, Item2 = il2CppShim, Item3 = il2CppShimDelegate }); return(newreplacement); }
private static unsafe TDelegate Patch <TDelegate>(MethodBase originalMethod, IntPtr patchDetour) { IntPtr original = *(IntPtr *)UnhollowerSupport.MethodBaseToIl2CppMethodInfoPointer(originalMethod); MelonUtils.NativeHookAttach((IntPtr)(&original), patchDetour); return(Marshal.GetDelegateForFunctionPointer <TDelegate>(original)); }
private unsafe static bool PatchMonoExport() { IntPtr monolib = MonoLibrary.GetLibPtr(); if (monolib == IntPtr.Zero) { return(false); } NativeLibrary monoLibrary = new NativeLibrary(monolib); IntPtr export = monoLibrary.GetExport("mono_unity_get_unitytls_interface"); if (export == IntPtr.Zero) { Logger.Error("Failed to find mono_unity_get_unitytls_interface! This should never happen..."); return(false); } Logger.Msg("Patching mono_unity_get_unitytls_interface..."); MethodInfo patch = typeof(Il2CppUnityTls_Module).GetMethod("GetUnityTlsInterface", BindingFlags.NonPublic | BindingFlags.Static); IntPtr patchptr = patch.MethodHandle.GetFunctionPointer(); MelonUtils.NativeHookAttach((IntPtr)(&export), patchptr); return(true); }
private static unsafe bool ApplyUser32SetTimerPatch() { IntPtr original = PEUtils.GetExportedFunctionPointerForModule("USER32.dll", "SetTimer"); MelonLogger.Msg($"User32::SetTimer original: 0x{(long)original:X}"); if (original == IntPtr.Zero) { MelonLogger.Error("Failed to find USER32.dll::SetTimer"); return(false); } // We get a native function pointer to User32SetTimerDetour from our current class //IntPtr detourPtr = typeof(Core).GetMethod("User32SetTimerDetour", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); IntPtr detourPtr = Marshal.GetFunctionPointerForDelegate((User32SetTimerDelegate)User32SetTimerDetour); if (detourPtr == IntPtr.Zero) { MelonLogger.Error("Failed to find User32SetTimerDetour"); return(false); } // And we patch SetTimer to replace it by our hook MelonLogger.Msg($"Applying USER32.dll::SetTimer Hook at 0x{original.ToInt64():X}"); MelonUtils.NativeHookAttach((IntPtr)(&original), detourPtr); MelonLogger.Msg($"Creating delegate for original USER32.dll::SetTimer (0x{original.ToInt64():X})"); user32SetTimerOriginal = (User32SetTimerDelegate)Marshal.GetDelegateForFunctionPointer(original, typeof(User32SetTimerDelegate)); MelonLogger.Msg("Applied USER32.dll::SetTimer patch"); return(true); }
private static unsafe TDelegate Patch <TDelegate>(MethodBase originalMethod, IntPtr patchDetour) { IntPtr original = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(originalMethod).GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&original), patchDetour); return(Marshal.GetDelegateForFunctionPointer <TDelegate>(original)); }
unsafe public T Detour <T>(IntPtr @from, T to) where T : Delegate { IntPtr *targetVarPointer = &from; PinnedDelegates.Add(to); MelonUtils.NativeHookAttach((IntPtr)targetVarPointer, to.GetFunctionPointer()); return(from.GetDelegate <T>()); }
private static void InstallIl2CppPatch(PatchInfo patchInfo, DynamicMethod il2CppShim) { IntPtr methodInfoPtr = patchInfo.copiedMethodInfoPointer; IntPtr oldDetourPtr = patchInfo.methodDetourPointer; IntPtr newDetourPtr = il2CppShim.MethodHandle.GetFunctionPointer(); if (oldDetourPtr != IntPtr.Zero) { MelonUtils.NativeHookDetach(methodInfoPtr, oldDetourPtr); } MelonUtils.NativeHookAttach(methodInfoPtr, newDetourPtr); patchInfo.methodDetourPointer = newDetourPtr; }
public static void Patch() { unsafe { var setupMethod = typeof(PlayerManager).GetMethods() .Where(method => method.Name.StartsWith("Method_Public_Static_IEnumerable_1_Player_Vector3_Single_Nullable_1_Int32_Func_2_Player_Boolean_")) .OrderBy(UnhollowerSupport.GetIl2CppMethodCallerCount).Last(); var originalMethod = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(setupMethod).GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&originalMethod), typeof(PlayerIEnumerableSetup).GetMethod(nameof(EnumerableSetup), BindingFlags.Static | BindingFlags.Public) !.MethodHandle.GetFunctionPointer()); _playerIEnumerableSetupDelegate = Marshal.GetDelegateForFunctionPointer <PlayerIEnumerableSetupDelegate>(originalMethod); } }
public static void Erect(Record record) { var trampoline = Trampoline.Generate(record.FinalInitVa); unsafe { var p = (IntPtr *)Marshal.AllocHGlobal(IntPtr.Size); * p = record.FinalInitVa; MelonUtils.NativeHookAttach((IntPtr)p, trampoline.Address); var handle = trampoline.Finish(*p, Bridge); BRIDGE_INDEX.Add(handle, record); Marshal.FreeHGlobal((IntPtr)p); } }
public static void Patch() { unsafe { var setupMethod = typeof(PageWorldInfo).GetMethods() .Where(m => m.Name.StartsWith("Method_Public_Void_ApiWorld_ApiWorldInstance_Boolean_Boolean_") && !m.Name.Contains("PDM")) .OrderBy(m => m.GetCustomAttribute <CallerCountAttribute>().Count) .Last(); // Thanks to Knah var originalMethod = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(setupMethod).GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&originalMethod), typeof(WorldInfoSetup).GetMethod(nameof(Postfix), BindingFlags.Static | BindingFlags.Public) !.MethodHandle.GetFunctionPointer()); worldInfoSetupDelegate = Marshal.GetDelegateForFunctionPointer <WorldInfoSetupDelegate>(originalMethod); } }
private unsafe static bool PatchIl2CppExport() { NativeLibrary il2cppLibrary = NativeLibrary.Load(Path.Combine(MelonUtils.GameDirectory, "GameAssembly.dll")); IntPtr export = il2cppLibrary.GetExport("il2cpp_unity_install_unitytls_interface"); if (export == IntPtr.Zero) { Logger.Error("Failed to find il2cpp_unity_install_unitytls_interface!"); return(false); } Logger.Msg("Patching il2cpp_unity_install_unitytls_interface..."); MethodInfo patch = typeof(Il2CppUnityTls_Module).GetMethod("SetUnityTlsInterface", BindingFlags.NonPublic | BindingFlags.Static); IntPtr patchptr = patch.MethodHandle.GetFunctionPointer(); MelonUtils.NativeHookAttach((IntPtr)(&export), patchptr); OriginalSetUnityTlsInterface = (dSetUnityTlsInterface)Marshal.GetDelegateForFunctionPointer(export, typeof(dSetUnityTlsInterface)); return(true); }
private unsafe static bool PatchExports() { IntPtr monolib = MonoLibrary.GetLibPtr(); if (monolib == IntPtr.Zero) { Logger.Warning("Unable to find Mono Library Pointer!"); return(false); } NativeLibrary monoLibrary = new NativeLibrary(monolib); IntPtr mono_export = monoLibrary.GetExport("mono_unity_get_unitytls_interface"); if (mono_export == IntPtr.Zero) { Logger.Warning("Unable to find Mono's mono_unity_get_unitytls_interface Export!"); return(false); } NativeLibrary il2cppLibrary = NativeLibrary.Load(Path.Combine(MelonUtils.GameDirectory, "GameAssembly.dll")); IntPtr il2cpp_export = il2cppLibrary.GetExport("il2cpp_unity_install_unitytls_interface"); if (il2cpp_export == IntPtr.Zero) { Logger.Warning("Unable to find Il2Cpp's il2cpp_unity_install_unitytls_interface Export!"); return(false); } Logger.Msg("Patching mono_unity_get_unitytls_interface..."); MelonUtils.NativeHookAttach((IntPtr)(&mono_export), typeof(Il2CppUnityTls_Module).GetMethod("GetUnityTlsInterface", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer()); Logger.Msg("Patching il2cpp_unity_install_unitytls_interface..."); MelonUtils.NativeHookAttach((IntPtr)(&il2cpp_export), typeof(Il2CppUnityTls_Module).GetMethod("SetUnityTlsInterface", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer()); OriginalSetUnityTlsInterface = (dSetUnityTlsInterface)Marshal.GetDelegateForFunctionPointer(il2cpp_export, typeof(dSetUnityTlsInterface)); return(true); }
public override void OnApplicationStart() { AdvancedSafetySettings.RegisterSettings(); var matchingMethods = typeof(AssetManagement) .GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly).Where(it => it.Name.StartsWith("Method_Public_Static_Object_Object_Vector3_Quaternion_Boolean_Boolean_Boolean_") && it.GetParameters().Length == 6).ToList(); foreach (var matchingMethod in matchingMethods) { unsafe { var originalMethodPointer = *(IntPtr *)(IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(matchingMethod).GetValue(null); ObjectInstantiateDelegate originalInstantiateDelegate = null; ObjectInstantiateDelegate replacement = (assetPtr, pos, rot, allowCustomShaders, isUI, validate, nativeMethodPointer) => ObjectInstantiatePatch(assetPtr, pos, rot, allowCustomShaders, isUI, validate, nativeMethodPointer, originalInstantiateDelegate); ourPinnedDelegates.Add(replacement); MelonUtils.NativeHookAttach((IntPtr)(&originalMethodPointer), Marshal.GetFunctionPointerForDelegate(replacement)); originalInstantiateDelegate = Marshal.GetDelegateForFunctionPointer <ObjectInstantiateDelegate>(originalMethodPointer); } } unsafe { var originalMethodPointer = *(IntPtr *)(IntPtr)UnhollowerUtils .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(AMEnumA).GetMethod( nameof(AMEnumA.MoveNext))) .GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&originalMethodPointer), typeof(AdvancedSafetyMod).GetMethod(nameof(MoveNextPatchA), BindingFlags.Static | BindingFlags.NonPublic) !.MethodHandle.GetFunctionPointer()); ourMoveNextA = originalMethodPointer; } unsafe { var originalMethodPointer = *(IntPtr *)(IntPtr)UnhollowerUtils .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(AMEnumB).GetMethod( nameof(AMEnumB.MoveNext))) .GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&originalMethodPointer), typeof(AdvancedSafetyMod).GetMethod(nameof(MoveNextPatchB), BindingFlags.Static | BindingFlags.NonPublic) !.MethodHandle.GetFunctionPointer()); ourMoveNextB = originalMethodPointer; } unsafe { var originalMethodPointer = *(IntPtr *)(IntPtr)UnhollowerUtils .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(AMEnumC).GetMethod( nameof(AMEnumC.MoveNext))) .GetValue(null); MelonUtils.NativeHookAttach((IntPtr)(&originalMethodPointer), typeof(AdvancedSafetyMod).GetMethod(nameof(MoveNextPatchC), BindingFlags.Static | BindingFlags.NonPublic) !.MethodHandle.GetFunctionPointer()); ourMoveNextC = originalMethodPointer; } unsafe { var originalMethodInfo = (Il2CppMethodInfo *)(IntPtr)UnhollowerUtils .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(AMEnumB).GetMethod( nameof(AMEnumB.MoveNext))) .GetValue(null); var methodInfoCopy = (Il2CppMethodInfo *)Marshal.AllocHGlobal(Marshal.SizeOf <Il2CppMethodInfo>()); * methodInfoCopy = *originalMethodInfo; ourInvokeMethodInfo = (IntPtr)methodInfoCopy; } foreach (ProcessModule module in Process.GetCurrentProcess().Modules) { if (!module.FileName.Contains("UnityPlayer")) { continue; } // ProduceHelper<AudioMixer,0>::Produce, thanks to Ben for finding an adjacent method ourAudioMixerReadPointer = module.BaseAddress + 0x4997C0; var patchDelegate = new AudioMixerReadDelegate(AudioMixerReadPatch); ourPinnedDelegates.Add(patchDelegate); unsafe { fixed(IntPtr *mixerReadAddress = &ourAudioMixerReadPointer) MelonUtils.NativeHookAttach((IntPtr)mixerReadAddress, Marshal.GetFunctionPointerForDelegate(patchDelegate)); ourAudioMixerReadDelegate = Marshal.GetDelegateForFunctionPointer <AudioMixerReadDelegate>(ourAudioMixerReadPointer); } break; } SceneManager.add_sceneLoaded(new Action <Scene, LoadSceneMode>((s, _) => { if (s.buildIndex == -1) { ourCanReadAudioMixers = false; MelonDebug.Msg("No reading audio mixers anymore"); } })); SceneManager.add_sceneUnloaded(new Action <Scene>(s => { if (s.buildIndex == -1) { // allow loading mixers from world assetbundles ourCanReadAudioMixers = true; MelonDebug.Msg("Can read audio mixers now"); } })); PortalHiding.OnApplicationStart(); AvatarHiding.OnApplicationStart(Harmony); if (MelonHandler.Mods.Any(it => it.Info.SystemType.Name == nameof(UiExpansionKitMod))) { typeof(UiExpansionKitSupport).GetMethod(nameof(UiExpansionKitSupport.OnApplicationStart), BindingFlags.Static | BindingFlags.Public) !.Invoke(null, new object[0]); } }
public override void OnApplicationStart() { /* Register settings */ Settings.RegisterConfig(); /* Load audio settings */ WorldAudio.LoadConfig(); /* Load avatar parameters */ Parameters.LoadConfig(); /* Load our custom UI elements */ UiExpansion.LoadUiObjects(); /* TODO: Consider switching to operator+ when everyone had to update the assembly unhollower */ /* The current solution might be prefereable so we are always first */ VRCAvatarManager.field_Private_Static_Action_3_Player_GameObject_VRC_AvatarDescriptor_0 += (Il2CppSystem.Action <Player, GameObject, VRC.SDKBase.VRC_AvatarDescriptor>)OnAvatarInstantiate; /* Register async, awaiting network manager */ MelonCoroutines.Start(RegisterJoinLeaveNotifier()); ExpansionKitApi.GetExpandedMenu(ExpandedMenu.QuickMenu).AddSimpleButton("Player List", PlayerList); ExpansionKitApi.GetExpandedMenu(ExpandedMenu.QuickMenu).AddSimpleButton("WorldCleanup", MainMenu); ExpansionKitApi.GetExpandedMenu(ExpandedMenu.UserQuickMenu).AddSimpleButton("Avatar Toggles", OnUserQuickMenu); /* Hook into setter for parameter properties */ unsafe { var param_prop_bool_set = (IntPtr)typeof(AvatarParameter).GetField("NativeMethodInfoPtr_Method_Public_Virtual_Final_New_set_Void_Boolean_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(param_prop_bool_set, new Action <IntPtr, bool>(Parameters.BoolPropertySetter).Method.MethodHandle.GetFunctionPointer()); Parameters._boolPropertySetterDelegate = Marshal.GetDelegateForFunctionPointer <Parameters.BoolPropertySetterDelegate>(*(IntPtr *)(void *)param_prop_bool_set); var param_prop_int_set = (IntPtr)typeof(AvatarParameter).GetField("NativeMethodInfoPtr_Method_Public_Virtual_Final_New_set_Void_Int32_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(param_prop_int_set, new Action <IntPtr, int>(Parameters.IntPropertySetter).Method.MethodHandle.GetFunctionPointer()); Parameters._intPropertySetterDelegate = Marshal.GetDelegateForFunctionPointer <Parameters.IntPropertySetterDelegate>(*(IntPtr *)(void *)param_prop_int_set); var param_prop_float_set = (IntPtr)typeof(AvatarParameter).GetField("NativeMethodInfoPtr_Method_Public_Virtual_Final_New_set_Void_Single_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(param_prop_float_set, new Action <IntPtr, float>(Parameters.FloatPropertySetter).Method.MethodHandle.GetFunctionPointer()); Parameters._floatPropertySetterDelegate = Marshal.GetDelegateForFunctionPointer <Parameters.FloatPropertySetterDelegate>(*(IntPtr *)(void *)param_prop_float_set); } AMUtils.AddToModsFolder("Player Toggles", () => { /* Filter inactive avatar objects */ s_PlayerList = s_PlayerList.Where(o => o.Value).ToDictionary(o => o.Key, o => o.Value); /* Order by physical distance to camera */ var query = from player in s_PlayerList orderby Vector3.Distance(player.Value.transform.position, Camera.main.transform.position) select player; /* Only allow a max of 10 players there at once */ /* TODO: Consider adding multiple pages */ var remaining_count = 10; foreach (var entry in query) { var manager = entry.Value.GetComponentInParent <VRCAvatarManager>(); /* Ignore SDK2 & avatars w/o custom expressions */ if (!manager.HasCustomExpressions()) { continue; } var avatar_id = entry.Value.GetComponent <VRC.Core.PipelineManager>().blueprintId; var user_icon = s_Portraits[avatar_id].Get(); /* Source default expression icon */ var menu_icons = ActionMenuDriver.prop_ActionMenuDriver_0.field_Public_MenuIcons_0; var default_expression = menu_icons.defaultExpression; CustomSubMenu.AddSubMenu(entry.Key, () => { if (entry.Value == null || !entry.Value.active) { return; } var parameters = manager.GetAllAvatarParameters(); var filtered = Parameters.FilterDefaultParameters(parameters); var avatar_descriptor = manager.prop_VRCAvatarDescriptor_0; CustomSubMenu.AddToggle("Lock", filtered.Any(Parameters.IsLocked), (state) => { filtered.ForEach(state ? Parameters.Lock : Parameters.Unlock); }, icon: UiExpansion.LockClosedIcon); CustomSubMenu.AddButton("Save", () => Parameters.StoreParameters(manager), icon: UiExpansion.SaveIcon); AvatarParameter FindParameter(string name) { foreach (var parameter in parameters) { if (parameter.field_Private_String_0 == name) { return(parameter); } } return(null); } void ExpressionSubmenu(VRCExpressionsMenu expressions_menu) { if (entry.Value == null || !entry.Value.active) { return; } void FourAxisControl(VRCExpressionsMenu.Control control, Action <Vector2> callback) { CustomSubMenu.AddFourAxisPuppet( control.TruncatedName(), callback, icon: control.icon ?? default_expression, topButtonText: control.labels[0]?.TruncatedName() ?? "Up", rightButtonText: control.labels[1]?.TruncatedName() ?? "Right", downButtonText: control.labels[2]?.TruncatedName() ?? "Down", leftButtonText: control.labels[3]?.TruncatedName() ?? "Left"); } foreach (var control in expressions_menu.controls) { try { switch (control.type) { case VRCExpressionsMenu.Control.ControlType.Button: /* Note: Action Menu "Buttons" are actually Toggles */ /* that set on press and revert on release. */ /* TODO: Add proper implementation. */ case VRCExpressionsMenu.Control.ControlType.Toggle: { var param = FindParameter(control.parameter.name); var current_value = param.GetValue(); var default_value = avatar_descriptor.expressionParameters.FindParameter(control.parameter.name)?.defaultValue ?? 0f; var target_value = control.value; void SetIntFloat(bool state) => param.SetValue(state ? target_value : default_value); void SetBool(bool state) => param.SetValue(state ? 1f : 0f); CustomSubMenu.AddToggle( control.TruncatedName(), current_value == target_value, param.prop_ParameterType_0 == AvatarParameter.ParameterType.Bool ? SetBool : SetIntFloat, icon: control.icon ?? default_expression); break; } case VRCExpressionsMenu.Control.ControlType.SubMenu: { CustomSubMenu.AddSubMenu(control.TruncatedName(), () => ExpressionSubmenu(control.subMenu), icon: control.icon ?? default_expression); break; } case VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet: { var horizontal = FindParameter(control.subParameters[0]?.name); var vertical = FindParameter(control.subParameters[1]?.name); FourAxisControl(control, (value) => { horizontal.SetFloatProperty(value.x); vertical.SetFloatProperty(value.y); }); break; } case VRCExpressionsMenu.Control.ControlType.FourAxisPuppet: { var up = FindParameter(control.subParameters[0]?.name); var down = FindParameter(control.subParameters[1]?.name); var left = FindParameter(control.subParameters[2]?.name); var right = FindParameter(control.subParameters[3]?.name); FourAxisControl(control, (value) => { up.SetFloatProperty(Math.Max(0, value.y)); down.SetFloatProperty(-Math.Min(0, value.y)); left.SetFloatProperty(Math.Max(0, value.x)); right.SetFloatProperty(-Math.Min(0, value.x)); }); break; } case VRCExpressionsMenu.Control.ControlType.RadialPuppet: { var param = FindParameter(control.subParameters[0]?.name); CustomSubMenu.AddRestrictedRadialPuppet(control.TruncatedName(), param.SetValue, startingValue: param.GetValue(), icon: control.icon ?? default_expression); break; } } } catch (Exception e) { MelonLogger.Error(e.StackTrace); } } } ExpressionSubmenu(avatar_descriptor.expressionsMenu); }, icon: user_icon); if (--remaining_count == 0) { break; } } }); MelonLogger.Msg(ConsoleColor.Green, "WorldCleanup ready!"); }
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(); }
public static unsafe void Init() { _getTextMeshPointer = (IntPtr)typeof(TextMesh).GetField("NativeMethodInfoPtr_get_text_Public_get_String_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(_getTextMeshPointer, Utils.GetMethod(nameof(Patch)).MethodHandle.GetFunctionPointer()); }
public override void OnApplicationStart() { advInvPreferencesCategory = MelonPreferences.CreateCategory("AdvancedInvites", "Advanced Invites"); advInvPreferencesCategory.CreateEntry("DeleteNotifications", InviteHandler.DeleteNotifications, "Delete Notification After Successful Use"); advInvPreferencesCategory.CreateEntry("BlacklistEnabled", blacklistEnabled, "Blacklist System"); advInvPreferencesCategory.CreateEntry("WhitelistEnabled", whitelistEnabled, "Whitelist System"); advInvPreferencesCategory.CreateEntry("NotificationVolume", .8f, "Notification Volume"); advInvPreferencesCategory.CreateEntry("JoinMeNotifyRequest", joinMeNotifyRequest, "Join Me Req Notification Sound"); advInvPreferencesCategory.CreateEntry("IgnoreBusyStatus", ignoreBusyStatus, "Ignore Busy Status"); advInvPreferencesCategory.CreateEntry("InviteSoundEnabled", true, "Invite Sound"); advInvPreferencesCategory.CreateEntry("InviteRequestSoundEnabled", true, "Invite-Request Sound"); advInvPreferencesCategory.CreateEntry("VoteToKickSoundEnabled", false, "Vote-Kick Sound", true); advInvPreferencesCategory.CreateEntry("FriendRequestSoundEnabled", false, "Friend-Request Sound", true); OnPreferencesLoaded(); Localization.Load(); #if DEBUG DebugTesting.Test(); try { MethodInfo sendNotificationMethod = typeof(NotificationManager).GetMethod( nameof(NotificationManager.Method_Public_Void_String_String_String_String_NotificationDetails_ArrayOf_Byte_0), BindingFlags.Public | BindingFlags.Instance); Harmony.Patch(sendNotificationMethod, new HarmonyMethod( typeof(AdvancedInviteSystem).GetMethod(nameof(SendNotificationPatch), BindingFlags.NonPublic | BindingFlags.Static))); } catch (Exception e) { MelonLogger.Error("Error Patching SendNotification: " + e.Message); } #endif try { unsafe { // Appears to be NotificationManager.Method_Private_Void_Notification_1 MethodInfo acceptNotificationMethod = typeof(NotificationManager).GetMethods(BindingFlags.Public | BindingFlags.Instance).Single( m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Notification) && m.XRefScanFor("AcceptNotification for notification:")); IntPtr originalMethod = *(IntPtr *)(IntPtr)UnhollowerUtils .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(acceptNotificationMethod).GetValue(null); MelonUtils.NativeHookAttach( (IntPtr)(&originalMethod), typeof(AdvancedInviteSystem).GetMethod(nameof(AcceptNotificationPatch), BindingFlags.Static | BindingFlags.NonPublic) !.MethodHandle .GetFunctionPointer()); acceptNotificationDelegate = Marshal.GetDelegateForFunctionPointer <AcceptNotificationDelegate>(originalMethod); } } catch (Exception e) { MelonLogger.Error("Error Patching AcceptNotification: " + e.Message); } try { //Appears to be NotificationManager.Method_Private_String_Notification_1 MethodInfo addNotificationMethod = typeof(NotificationManager).GetMethods(BindingFlags.Public | BindingFlags.Instance).Single( m => m.Name.StartsWith("Method_Private_") && m.ReturnType == typeof(string) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Notification) && m.XRefScanFor("imageUrl")); Harmony.Patch( addNotificationMethod, postfix: new HarmonyMethod( typeof(AdvancedInviteSystem).GetMethod(nameof(AddNotificationPatch), BindingFlags.NonPublic | BindingFlags.Static))); } catch (Exception e) { MelonLogger.Error("Error Patching AddNotification: " + e.Message); } UserPermissionHandler.LoadSettings(); WorldPermissionHandler.LoadSettings(); MelonCoroutines.Start(WaitForVrChatUiManager()); }