Example #1
0
        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}"); }
        }
Example #2
0
        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);
        }
Example #4
0
        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));
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        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));
        }
Example #8
0
        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;
        }
Example #10
0
        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);
            }
        }
Example #11
0
        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);
            }
        }
Example #12
0
        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);
            }
        }
Example #13
0
        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);
        }
Example #14
0
        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);
        }
Example #15
0
        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]);
            }
        }
Example #16
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!");
        }
Example #17
0
        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(); }
Example #18
0
 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());
 }
Example #19
0
        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());
        }