From e11c1c7b1f53058a3f1104c93a8545740af6f155 Mon Sep 17 00:00:00 2001 From: AtiLion Date: Fri, 12 Jul 2019 12:22:52 +0200 Subject: [PATCH] Initial code push --- .gitignore | 3 + README.md | 20 ++ VRCExtended.sln | 25 ++ VRCExtended/AntiCrasherConfig.cs | 72 ++++ VRCExtended/ExtendedLogger.cs | 91 +++++ VRCExtended/ExtendedServer.cs | 12 + VRCExtended/ExtendedUser.cs | 327 ++++++++++++++++++ VRCExtended/Patches/PageUserInfo.cs | 52 +++ VRCExtended/Patches/PortalInternal.cs | 60 ++++ VRCExtended/Properties/AssemblyInfo.cs | 36 ++ VRCExtended/VRCExtended.cs | 259 ++++++++++++++ VRCExtended/VRCExtended.csproj | 103 ++++++ .../Obfuscation/OBFUnityActionInternal.cs | 76 ++++ VRCExtended/VRChat/UI/VRCEUi.cs | 293 ++++++++++++++++ VRCExtended/VRChat/UI/VRCEUiButton.cs | 93 +++++ VRCExtended/VRChat/UI/VRCEUiVolumeControl.cs | 91 +++++ VRCExtended/VRChat/VRCEPlayer.cs | 69 ++++ VRCExtended/VRChat/VRCPlayerManager.cs | 95 +++++ VRCExtended/VolumeControl.cs | 52 +++ 19 files changed, 1829 insertions(+) create mode 100644 VRCExtended.sln create mode 100644 VRCExtended/AntiCrasherConfig.cs create mode 100644 VRCExtended/ExtendedLogger.cs create mode 100644 VRCExtended/ExtendedServer.cs create mode 100644 VRCExtended/ExtendedUser.cs create mode 100644 VRCExtended/Patches/PageUserInfo.cs create mode 100644 VRCExtended/Patches/PortalInternal.cs create mode 100644 VRCExtended/Properties/AssemblyInfo.cs create mode 100644 VRCExtended/VRCExtended.cs create mode 100644 VRCExtended/VRCExtended.csproj create mode 100644 VRCExtended/VRChat/Obfuscation/OBFUnityActionInternal.cs create mode 100644 VRCExtended/VRChat/UI/VRCEUi.cs create mode 100644 VRCExtended/VRChat/UI/VRCEUiButton.cs create mode 100644 VRCExtended/VRChat/UI/VRCEUiVolumeControl.cs create mode 100644 VRCExtended/VRChat/VRCEPlayer.cs create mode 100644 VRCExtended/VRChat/VRCPlayerManager.cs create mode 100644 VRCExtended/VolumeControl.cs diff --git a/.gitignore b/.gitignore index 3e759b7..1923505 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# Project Specific +lib/ + # User-specific files *.suo *.user diff --git a/README.md b/README.md index 3816153..ce418da 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,23 @@ If you have any more questions feel free to ask them in the [Issues](https://git > - Does this mod work in VR? *Yes, the mod is designed to work fully with VR as well as desktop* > - Avatars are taking longer to load with local colliders. *An avatar with a ton of colliders will take longer to load as it has to register all of them* > - Sometimes all of the colliders go weird. *If an avatar has a world sized bone collider on it, it may cause things to start and look weird as the collider interacts with all of yours* + +## How to compile ## + 1. Make sure you have a program that can compile C# code + 2. Create a folder called *"lib"* + 3. Make sure the lib folder contains the following files: + - Assembly-CSharp.dll + - Assembly-CSharp-firstpass.dll + - Newtonsoft.Json.dll + - UnityEngine.AudioModule.dll + - UnityEngine.CoreModule.dll + - UnityEngine.dll + - UnityEngine.ParticleSystemModule.dll + - UnityEngine.PhysicsModule.dll + - UnityEngine.TextRenderingModule.dll + - UnityEngine.UI.dll + - VRCCore-Standalone.dll + - VRCSDK2.dll + - VRCModLoader.dll + - VRCTools.dll +4. Use your compiler to compile the code \ No newline at end of file diff --git a/VRCExtended.sln b/VRCExtended.sln new file mode 100644 index 0000000..f1771e2 --- /dev/null +++ b/VRCExtended.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29009.5 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCExtended", "VRCExtended\VRCExtended.csproj", "{7DF0802E-DB23-417B-A5CD-949B0471D3C4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DF0802E-DB23-417B-A5CD-949B0471D3C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DF0802E-DB23-417B-A5CD-949B0471D3C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DF0802E-DB23-417B-A5CD-949B0471D3C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DF0802E-DB23-417B-A5CD-949B0471D3C4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E8021B1F-1141-4C18-99AC-C52B4A136F9F} + EndGlobalSection +EndGlobal diff --git a/VRCExtended/AntiCrasherConfig.cs b/VRCExtended/AntiCrasherConfig.cs new file mode 100644 index 0000000..e312ecf --- /dev/null +++ b/VRCExtended/AntiCrasherConfig.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace VRCExtended +{ + internal class AntiCrasherConfig + { + #region AntiCrasherConfig Properties + public static AntiCrasherConfig Instance { get; private set; } + #endregion + + #region Polygon Crasher + public bool PolyLimit { get; set; } + public int MaxPolygons { get; set; } + #endregion + + #region Particle Crasher + public bool ParticleLimit { get; set; } + public bool DetectPotentialCrasher { get; set; } + public int MaxParticles { get; set; } + #endregion + + #region Shader Crasher + public bool ShaderBlacklist { get; set; } + public bool UseOnlineBlacklist { get; set; } + public string[] BlacklistedShaders { get; set; } + #endregion + + public AntiCrasherConfig() => + Instance = this; + + public static void CreateDefault() + { + AntiCrasherConfig acc = new AntiCrasherConfig(); + + acc.PolyLimit = true; + acc.MaxPolygons = 150000; + + acc.ParticleLimit = true; + acc.DetectPotentialCrasher = false; + acc.MaxParticles = 200; + + acc.ShaderBlacklist = true; + acc.UseOnlineBlacklist = false; + acc.BlacklistedShaders = new string[] + { + "pretty", + "bluescreen", + "tesselation", + "tesselated", + "crasher", + "instant crash paralyzer", + "worldkill", + "tessellation", + "tessellated", + "oofer", + "starnest", + "xxx", + "dbtc", + "kyuzu", + "distancebased", + "waifuserp", + "loops", + "izzy", + "star", + "diebitch" + }; + } + } +} diff --git a/VRCExtended/ExtendedLogger.cs b/VRCExtended/ExtendedLogger.cs new file mode 100644 index 0000000..f0a6a77 --- /dev/null +++ b/VRCExtended/ExtendedLogger.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using VRCModLogger = VRCModLoader.VRCModLogger; + +namespace VRCExtended +{ + internal static class ExtendedLogger + { + public static void Log(string text) + { + ConsoleColor oldColor = Console.ForegroundColor; + if (oldColor == ConsoleColor.Black) + oldColor = ConsoleColor.Gray; + + if (Console.CursorLeft > 1) + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Green; + VRCModLogger.Log("[VRCExtended] " + text); + Console.ForegroundColor = oldColor; + } + public static void Log(object obj) + { + ConsoleColor oldColor = Console.ForegroundColor; + if (oldColor == ConsoleColor.Black) + oldColor = ConsoleColor.Gray; + + if (Console.CursorLeft > 1) + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Green; + VRCModLogger.Log("[VRCExtended] " + obj.ToString()); + Console.ForegroundColor = oldColor; + } + + public static void LogWarning(string text) + { + ConsoleColor oldColor = Console.ForegroundColor; + if (oldColor == ConsoleColor.Black) + oldColor = ConsoleColor.Gray; + + if (Console.CursorLeft > 1) + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Yellow; + VRCModLogger.Log("[VRCExtended] " + text); + Console.ForegroundColor = oldColor; + } + public static void LogWarning(object obj) + { + ConsoleColor oldColor = Console.ForegroundColor; + if (oldColor == ConsoleColor.Black) + oldColor = ConsoleColor.Gray; + + if (Console.CursorLeft > 1) + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Yellow; + VRCModLogger.Log("[VRCExtended] " + obj.ToString()); + Console.ForegroundColor = oldColor; + } + + public static void LogError(string text, Exception exception = null) + { + ConsoleColor oldColor = Console.ForegroundColor; + if (oldColor == ConsoleColor.Black) + oldColor = ConsoleColor.Gray; + + if (Console.CursorLeft > 1) + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Red; + VRCModLogger.LogError("[VRCExtended] " + text); + if (exception != null) + VRCModLogger.LogError(exception.ToString()); + Console.ForegroundColor = oldColor; + } + public static void LogError(object obj, Exception exception = null) + { + ConsoleColor oldColor = Console.ForegroundColor; + if (oldColor == ConsoleColor.Black) + oldColor = ConsoleColor.Gray; + + if (Console.CursorLeft > 1) + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Red; + VRCModLogger.LogError("[VRCExtended] " + obj.ToString()); + if (exception != null) + VRCModLogger.LogError(exception.ToString()); + Console.ForegroundColor = oldColor; + } + } +} diff --git a/VRCExtended/ExtendedServer.cs b/VRCExtended/ExtendedServer.cs new file mode 100644 index 0000000..4067055 --- /dev/null +++ b/VRCExtended/ExtendedServer.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace VRCExtended +{ + internal static class ExtendedServer + { + public static List Users = new List(); + } +} diff --git a/VRCExtended/ExtendedUser.cs b/VRCExtended/ExtendedUser.cs new file mode 100644 index 0000000..e7baf61 --- /dev/null +++ b/VRCExtended/ExtendedUser.cs @@ -0,0 +1,327 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using VRC; + +using VRChat; + +using VRCTools; + +using UnityEngine; + +using VRC_AvatarDescriptor = VRCSDK2.VRC_AvatarDescriptor; + +namespace VRCExtended +{ + internal class ExtendedUser : VRCEPlayer + { + #region Individual Volume Variables + private float _volumeAvatar = 1f; + private float _volumeVoice = 1f; + #endregion + + #region AntiCrash Variables + private static Shader _defaultShader; + + private Dictionary _fallbackShaders = new Dictionary(); + private Dictionary _fallbackParticleLimits = new Dictionary(); + private Dictionary _fallbackMeshes = new Dictionary(); + #endregion + + #region Local Colliders Properties + public List BoneColliders { get; private set; } = new List(); + public List Bones { get; private set; } = new List(); + #endregion + + #region Individual Volume Properties + public float VolumeAvatar + { + get => _volumeAvatar; + set + { + float prc = value / 100; + + foreach(AudioSource audio in Sounds) + audio.volume -= prc * audio.volume; + } + } + public float VolumeVoice + { + get => _volumeVoice; + set + { + foreach(AudioSource audio in Avatar.GetComponentsInChildren()) + { + audio.volume = value; + _volumeVoice = value; + } + /*if (Voice == null) + return; + ExtendedLogger.Log("Test"); + Voice.volume = value; + _volumeVoice = value;*/ + } + } + #endregion + + public ExtendedUser(VRCEPlayer player) : base(player.Player) => Setup(); + public ExtendedUser(Player player) : base(player) => Setup(); + public ExtendedUser() : base() => Setup(); + + #region Setup Functions + private void Setup() + { + AvatarManager.OnAvatarCreated += OnAvatarCreated; + + ExtendedUser self = ExtendedServer.Users.FirstOrDefault(a => a.APIUser == APIUser); + if(self != null) + { + _volumeAvatar = self._volumeAvatar; + _volumeVoice = self._volumeVoice; + + BoneColliders = self.BoneColliders; + Bones = self.Bones; + + _fallbackShaders = self._fallbackShaders; + _fallbackParticleLimits = self._fallbackParticleLimits; + _fallbackMeshes = self._fallbackMeshes; + } + } + #endregion + + #region Avatar Events + void OnAvatarCreated(GameObject avatar, VRC_AvatarDescriptor avatarDescriptor, bool _1) + { + if (ModPrefs.GetBool("vrcextended", "userSpecificVolume")) + VolumeAvatar = _volumeAvatar; + if(ModPrefs.GetBool("vrcextended", "localColliders")) + { + foreach(ExtendedUser user in ExtendedServer.Users) + { + if (user.APIUser == APIUser) + continue; + + if (ModPrefs.GetBool("vrcextended", "multiLocalColliders") || (ModPrefs.GetBool("vrcextended", "selfLocalColliders") && IsSelf) || user.APIUser == Instance.APIUser) + foreach (DynamicBone bone in user.Bones) + foreach (DynamicBoneCollider collider in BoneColliders) + if (bone.m_Colliders.Contains(collider)) + bone.m_Colliders.Remove(collider); + } + BoneColliders.Clear(); + Bones.Clear(); + + BoneColliders.AddRange(Avatar.GetComponentsInChildren(true)); + Bones.AddRange(Avatar.GetComponentsInChildren(true).Where(a => a.m_Colliders.Count > 1)); + + foreach(ExtendedUser user in ExtendedServer.Users) + { + if (user.APIUser == APIUser) + continue; + + if (ModPrefs.GetBool("vrcextended", "multiLocalColliders") || (ModPrefs.GetBool("vrcextended", "selfLocalColliders") && IsSelf) || user.APIUser == Instance.APIUser) + foreach (DynamicBone bone in Bones) + foreach (DynamicBoneCollider collider in user.BoneColliders) + bone.m_Colliders.Add(collider); + if(ModPrefs.GetBool("vrcextended", "multiLocalColliders") || (ModPrefs.GetBool("vrcextended", "selfLocalColliders") && user.APIUser == Instance.APIUser) || IsSelf) + foreach (DynamicBone bone in user.Bones) + foreach (DynamicBoneCollider collider in BoneColliders) + bone.m_Colliders.Add(collider); + } + ExtendedLogger.Log("Added local colliders to " + APIUser.displayName + "!"); + } + if(ModPrefs.GetBool("vrcextended", "antiCrasher")) + { + _fallbackShaders.Clear(); + RemoveCrashShaders(); + + _fallbackParticleLimits.Clear(); + LimitParticles(); + + _fallbackMeshes.Clear(); + RemoveCrashMesh(); + } + } + #endregion + + #region AntiCrash Functions + public void RemoveCrashShaders() + { + if (!ModPrefs.GetBool("vrcextended", "antiCrasher")) + return; + if (_defaultShader == null) + _defaultShader = Shader.Find("Standard"); + + // Change cached + if (_fallbackShaders.Count > 0) + { + foreach(Material material in _fallbackShaders.Keys) + { + material.shader = _defaultShader; + ExtendedLogger.LogWarning("Removed blacklisted shader " + material.shader.name + " from " + APIUser.displayName); + } + return; + } + + // Change non-cached + Renderer[] renderers = Avatar.GetComponentsInChildren(true); + int rLength = renderers.Length; + + for(int i = 0; i < rLength; i++) + { + foreach(Material material in renderers[i].materials) + { + foreach(string blacklistedName in AntiCrasherConfig.Instance.BlacklistedShaders) + { + if(material.shader.name.ToLower().Contains(blacklistedName)) + { + if (!_fallbackShaders.ContainsKey(material)) + _fallbackShaders.Add(material, material.shader); + material.shader = _defaultShader; + + ExtendedLogger.LogWarning("Removed blacklisted shader " + material.shader.name + " from " + APIUser.displayName); + break; + } + } + } + } + } + public void RestoreCrashShaders() + { + if (ModPrefs.GetBool("vrcextended", "antiCrasher")) + return; + if (_fallbackShaders.Count < 1) + return; + + foreach(Material material in _fallbackShaders.Keys) + { + material.shader = _fallbackShaders[material]; + ExtendedLogger.LogWarning("Restored blacklisted shader " + material.shader.name + " to " + APIUser.displayName); + } + } + + public void LimitParticles() + { + if (!ModPrefs.GetBool("vrcextended", "antiCrasher")) + return; + int maxPerSystem; + + // Change cached + if (_fallbackParticleLimits.Count > 0) + { + maxPerSystem = AntiCrasherConfig.Instance.MaxParticles / _fallbackParticleLimits.Count; + + foreach (ParticleSystem particleSystem in _fallbackParticleLimits.Keys) + { + ParticleSystem.MainModule mainModule = particleSystem.main; + + if (mainModule.maxParticles > maxPerSystem) + mainModule.maxParticles = maxPerSystem; + } + return; + } + + // Change non-cached + ParticleSystem[] particleSystems = Avatar.GetComponentsInChildren(true); + + if (particleSystems.Length < 1) + return; + maxPerSystem = AntiCrasherConfig.Instance.MaxParticles / particleSystems.Length; + + foreach(ParticleSystem particleSystem in particleSystems) + { + ParticleSystem.MainModule mainModule = particleSystem.main; + + if (!_fallbackParticleLimits.ContainsKey(particleSystem)) + _fallbackParticleLimits.Add(particleSystem, mainModule.maxParticles); + + if (mainModule.maxParticles > maxPerSystem) + mainModule.maxParticles = maxPerSystem; + } + } + public void RestoreParticleLimits() + { + if (ModPrefs.GetBool("vrcextended", "antiCrasher")) + return; + if (_fallbackParticleLimits.Count < 1) + return; + + foreach(ParticleSystem particleSystem in _fallbackParticleLimits.Keys) + { + ParticleSystem.MainModule mainModule = particleSystem.main; + + mainModule.maxParticles = _fallbackParticleLimits[particleSystem]; + } + } + + public void RemoveCrashMesh() + { + if (!ModPrefs.GetBool("vrcextended", "antiCrasher")) + return; + + // Change cached + if(_fallbackMeshes.Count > 0) + { + foreach(MeshRenderer mesh in _fallbackMeshes.Keys) + { + mesh.enabled = false; + ExtendedLogger.LogWarning("Removed crasher mesh from " + APIUser.displayName); + } + return; + } + + // Change non-cached + MeshRenderer[] meshes = Avatar.GetComponentsInChildren(); + + foreach(MeshRenderer mesh in meshes) + { + MeshFilter filter = mesh.GetComponent(); + + if (filter == null) + continue; + List triangles = new List(); + uint meshCount = 0; + + if(!filter.sharedMesh.isReadable) + { + if (!_fallbackMeshes.ContainsKey(mesh)) + _fallbackMeshes.Add(mesh, filter); + + mesh.enabled = false; + ExtendedLogger.LogWarning("Removed unreadable mesh from " + APIUser.displayName); + continue; + } + for(int i = 0; i < filter.sharedMesh.subMeshCount; i++) + { + filter.sharedMesh.GetTriangles(triangles, i); + meshCount += (uint)(triangles.Count / 3); + triangles.Clear(); + } + + if(meshCount > AntiCrasherConfig.Instance.MaxPolygons) + { + if (!_fallbackMeshes.ContainsKey(mesh)) + _fallbackMeshes.Add(mesh, filter); + + mesh.enabled = false; + ExtendedLogger.LogWarning("Removed crasher mesh from " + APIUser.displayName); + } + } + } + public void RestoreCrashMesh() + { + if (ModPrefs.GetBool("vrcextended", "antiCrasher")) + return; + if (_fallbackMeshes.Count < 1) + return; + + foreach(MeshRenderer mesh in _fallbackMeshes.Keys) + { + mesh.enabled = true; + ExtendedLogger.LogWarning("Restored crasher mesh to " + APIUser.displayName); + } + } + #endregion + } +} diff --git a/VRCExtended/Patches/PageUserInfo.cs b/VRCExtended/Patches/PageUserInfo.cs new file mode 100644 index 0000000..4ea18b4 --- /dev/null +++ b/VRCExtended/Patches/PageUserInfo.cs @@ -0,0 +1,52 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using VRC; +using VRC.UI; +using VRC.Core; + +using Harmony; + +using VRCTools; + +namespace VRCExtended.Patches +{ + internal class Patch_PageUserInfo + { + private Dictionary _volume = new Dictionary(); + + public static APIUser SelectedAPI { get; private set; } + + public static void Setup() + { + // Setup harmony instances + HarmonyInstance iSetupUserInfo = HarmonyInstance.Create("vrcextended.pageuserinfo.setupuserinfo"); + + // Patch + try + { + iSetupUserInfo.Patch(typeof(PageUserInfo).GetMethods(BindingFlags.Public | BindingFlags.Instance).First(a => a.Name == "SetupUserInfo" && a.GetParameters().Length > 1), null, + new HarmonyMethod(typeof(Patch_PageUserInfo).GetMethod("SetupUserInfo", BindingFlags.NonPublic | BindingFlags.Static))); + ExtendedLogger.Log("Patched PageUserInfo.SetupUserInfo"); + } + catch (Exception ex) + { + ExtendedLogger.LogError("Failed to patch PageUserInfo!", ex); + return; + } + } + + private static void SetupUserInfo(PageUserInfo __instance) + { + SelectedAPI = __instance.user; + + if (APIUser.CurrentUser.id == __instance.user.id) + VRCExtended.RefreshButton.Button.interactable = false; + else + VRCExtended.RefreshButton.Button.interactable = true; + } + } +} diff --git a/VRCExtended/Patches/PortalInternal.cs b/VRCExtended/Patches/PortalInternal.cs new file mode 100644 index 0000000..840e91f --- /dev/null +++ b/VRCExtended/Patches/PortalInternal.cs @@ -0,0 +1,60 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Harmony; + +using VRCTools; + +namespace VRCExtended.Patches +{ + internal class Patch_PortalInternal + { + #region Continuation Variables + private static PortalInternal portalEnter = null; + #endregion + + public static void Setup() + { + // Setup harmony instances + HarmonyInstance iEnter = HarmonyInstance.Create("vrcextended.portalinternal.enter"); + + // Patch + try + { + iEnter.Patch(typeof(PortalInternal).GetMethod("Enter", BindingFlags.Public | BindingFlags.Instance), + new HarmonyMethod(typeof(Patch_PortalInternal).GetMethod("Enter", BindingFlags.Static | BindingFlags.NonPublic))); + ExtendedLogger.Log("Patched PortalInternal.Enter"); + } + catch (Exception ex) + { + ExtendedLogger.LogError("Failed to patch PortalInternal: " + ex); + return; + } + } + + private static bool Enter(PortalInternal __instance, MethodInfo __originalMethod) + { + if (portalEnter == __instance) + return true; + + ExtendedLogger.Log("User entered portal"); + if (ModPrefs.GetBool("vrcextended", "askUsePortal")) + { + VRCUiPopupManagerUtils.ShowPopup("Enter Portal", "Do you really want to enter the portal?", "No", delegate () + { + VRCUiPopupManagerUtils.GetVRCUiPopupManager().HideCurrentPopup(); + }, "Yes", delegate () + { + portalEnter = __instance; + VRCUiPopupManagerUtils.GetVRCUiPopupManager().HideCurrentPopup(); + __originalMethod.Invoke(__instance, new object[] { }); + }); + return false; + } + return true; + } + } +} diff --git a/VRCExtended/Properties/AssemblyInfo.cs b/VRCExtended/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..af08bda --- /dev/null +++ b/VRCExtended/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("VRCExtended")] +[assembly: AssemblyDescription("Adds extra features to VRChat as well as some useful API functions")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("AtiLion")] +[assembly: AssemblyProduct("VRCExtended")] +[assembly: AssemblyCopyright("Copyright © AtiLion 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7df0802e-db23-417b-a5cd-949b0471d3c4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.0.1.0")] +[assembly: AssemblyFileVersion("0.0.1.0")] diff --git a/VRCExtended/VRCExtended.cs b/VRCExtended/VRCExtended.cs new file mode 100644 index 0000000..c6d8d67 --- /dev/null +++ b/VRCExtended/VRCExtended.cs @@ -0,0 +1,259 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Newtonsoft.Json; + +using VRCModLoader; +using VRCTools; +using VRChat; +using VRChat.UI; + +using UnityEngine; +using UnityEngine.UI; + +using VRCExtended.Patches; + +using VRC.Core; +using VRC.UI; + +using ModPrefs = VRCTools.ModPrefs; + +namespace VRCExtended +{ + // Crasher: 1m polygons in a mesh, tons of particles, shader blacklisting + // ApiCache: Potential way of refreshing users + + [VRCModInfo("VRCExtended", "0.0.1.0", "AtiLion")] + internal class VRCExtended : VRCMod + { + #region VRCExtended Variables + private bool _initialized = false; + private bool _initSocial = false; + #endregion + + #region VRCExtended UI Variables + public static VRCEUiVolumeControl volVoice; + public static VRCEUiVolumeControl volAvatar; + #endregion + + #region VRCExtended UI Properties + public static VRCEUiButton RefreshButton { get; private set; } + #endregion + + #region VRCMod Functions + void OnApplicationStart() + { + ExtendedLogger.Log("Loading VRCExtended..."); + + // Setup config + ModPrefs.RegisterCategory("vrcextended", "VRCExtended"); + + // Exploits + ModPrefs.RegisterPrefBool("vrcextended", "askUsePortal", true, "Ask to use portal"); +#if DEBUG + ModPrefs.RegisterPrefBool("vrcextended", "antiCrasher", false, "Prevent crashers"); // Use shader blacklisting and polygon limit +#else + ModPrefs.RegisterPrefBool("vrcextended", "antiCrasher", false, "Prevent crashers", true); +#endif + + // Avatar + ModPrefs.RegisterPrefBool("vrcextended", "localColliders", false, "Local colliders"); + ModPrefs.RegisterPrefBool("vrcextended", "multiLocalColliders", false, "Others have local colliders"); + ModPrefs.RegisterPrefBool("vrcextended", "selfLocalColliders", true, "Others can touch you"); + + // Players + ModPrefs.RegisterPrefBool("vrcextended", "userSpecificVolume", false, "User specific volumes", true); // Can't get this shit to work + + // Get AntiCrasher config + if(File.Exists("antiCrash.json")) + { + try + { + JsonConvert.DeserializeObject(File.ReadAllText("antiCrash.json")); + ExtendedLogger.Log("Loaded AntiCrasher config!"); + } + catch (Exception ex) + { + ExtendedLogger.LogError("Failed to read/parse AntiCrasher config! Using default values...", ex); + + AntiCrasherConfig.CreateDefault(); + } + } + else + { + AntiCrasherConfig.CreateDefault(); + ExtendedLogger.Log("Loaded default AntiCrasher config!"); + } + + // Get VolumeControl config + VolumeControl.Setup(); + + // Run patches + Patch_PortalInternal.Setup(); + Patch_PageUserInfo.Setup(); + + ExtendedLogger.Log("Loaded VRCExtended!"); + } + + void OnLevelWasLoaded(int level) // Level 0 = Loading Screen, Level 1 = Login Screen, Level -1 = Game + { + if (level == 0) + return; + + if(level == 1 && !_initialized) + { + // Setup systems + VRCPlayerManager.Setup(); + VRCEPlayer.Setup(); + + if (VRCEUi.UserInfoScreen == null) + { + ExtendedLogger.LogError("Failed to find UserInfo screen!"); + return; + } + + // -550 - 250f, 230f - 280f + if(ModPrefs.GetBool("vrcextended", "userSpecificVolume")) + { + /*volVoice = new VRCEUiVolumeControl("volumeVoice", new Vector2(-620f, -230f), "Voice", 1f, VRCEUi.UserInfoScreen.transform); + volAvatar = new VRCEUiVolumeControl("volumeAvatar", new Vector2(-290f, -230f), "Avatar", 1f, VRCEUi.UserInfoScreen.transform); + if (!volVoice.Success || !volAvatar.Success) + { + ExtendedLogger.LogError("Failed to create volume sliders on UserInfo!"); + return; + } + + // Setup volume events + volVoice.Slider.onValueChanged.AddListener(delegate (float volume) + { + if (Patch_PageUserInfo.SelectedUser == null) + return; + Patch_PageUserInfo.SelectedUser.VolumeVoice = volume; + ExtendedLogger.Log("Volume: " + Patch_PageUserInfo.SelectedUser.Voice.volume); + }); + volAvatar.Slider.onValueChanged.AddListener(delegate (float volume) + { + if (Patch_PageUserInfo.SelectedUser == null) + return; + Patch_PageUserInfo.SelectedUser.VolumeAvatar = volume; + });*/ + } + /*Transform target = VRCEUi.UserInfoScreen.transform.Find("AvatarImage"); + ExtendedLogger.Log("Transform: " + target.name); + foreach (Component component in target.GetComponents()) + ExtendedLogger.Log(" - " + component); + for(int i = 0; i < target.childCount; i++) + { + ExtendedLogger.Log("Transform: " + target.GetChild(i).name); + foreach (Component component in target.GetChild(i).GetComponents()) + ExtendedLogger.Log(" - " + component); + }*/ + + VRCPlayerManager.OnPlayerJoined += delegate (VRCEPlayer player) + { + ExtendedServer.Users.Add(new ExtendedUser(player)); + ExtendedLogger.Log("Player joined: " + player.APIUser.displayName); + }; + VRCPlayerManager.OnPlayerLeft += delegate (VRCEPlayer player) + { + ExtendedServer.Users.Remove(new ExtendedUser(player)); + ExtendedLogger.Log("Player left: " + player.APIUser.displayName); + }; + + Transform btnPlaylists = VRCEUi.InternalUserInfoScreen.PlaylistsButton; + if(btnPlaylists == null) + { + ExtendedLogger.LogError("Failed to get Playlists button!"); + return; + } + Vector3 pos = btnPlaylists.GetComponent().localPosition; + RefreshButton = new VRCEUiButton("Refresh", new Vector2(pos.x, pos.y + 75f), "Refresh", VRCEUi.InternalUserInfoScreen.UserPanel); + RefreshButton.Button.onClick.AddListener(() => + { + if (Patch_PageUserInfo.SelectedAPI == null) + return; + + ApiCache.Invalidate(Patch_PageUserInfo.SelectedAPI.id); + APIUser.FetchUser(Patch_PageUserInfo.SelectedAPI.id, (APIUser user) => + { + PageUserInfo pageUserInfo = VRCEUi.UserInfoScreen.GetComponent(); + if (pageUserInfo != null) + pageUserInfo.SetupUserInfo(user); + }, + (string error) => + { + ExtendedLogger.LogError(error); + }); + }); + ExtendedLogger.Log("Setup PageUserInfo refresh button!"); + + if(VRCEUi.SocialScreen != null && false) // Doesn't function properly + { + VRCUiManagerUtils.OnPageShown += (page) => + { + if (_initSocial) + return; + if (page.GetType() != typeof(VRCUiPageSocial)) + return; + UiVRCList[] lists = page.GetComponentsInChildren(true); + + foreach(UiVRCList list in lists) + { + if (list.GetType() != typeof(UiUserList) || list.transform.name == "Search" || list.transform.name == "FriendRequests") + continue; + Transform expandButton = list.transform.Find("Button"); + RectTransform rtExpand = expandButton.GetComponent(); + VRCEUiButton btnRefresh = new VRCEUiButton("Refresh", new Vector2(rtExpand.localPosition.x, rtExpand.localPosition.y), "Refresh", list.transform); + RectTransform rtRefresh = btnRefresh.Control.GetComponent(); + Transform title = list.transform.Find("Title"); + + if(title != null) + { + RectTransform rtTitle = title.GetComponent(); + + rtTitle.localPosition += new Vector3(220f, 0f, 0f); + } + rtExpand.localPosition += new Vector3(220f, 0f, 0f); + rtRefresh.sizeDelta -= new Vector2(0f, 25f); + + btnRefresh.Button.onClick.AddListener(() => + { + ((UiUserList)list).Refresh(); + }); + /*ExtendedLogger.Log("---------------------------"); + Transform target = list.transform; + ExtendedLogger.Log("Transform: " + target.name); + foreach (Component component in target.GetComponents()) + ExtendedLogger.Log(" - " + component); + for(int i = 0; i < target.childCount; i++) + { + ExtendedLogger.Log("Transform: " + target.GetChild(i).name); + foreach (Component component in target.GetChild(i).GetComponents()) + ExtendedLogger.Log(" - " + component); + }*/ + } + _initSocial = true; + }; + } + + _initialized = true; + return; + } + ExtendedServer.Users.Clear(); + } + + void OnApplicationQuit() + { + if (!File.Exists("antiCrash.json")) + { + File.WriteAllText("antiCrash.json", JsonConvert.SerializeObject(AntiCrasherConfig.Instance, Formatting.Indented)); + ExtendedLogger.Log("Saved AntiCrasher config!"); + } + } +#endregion + } +} diff --git a/VRCExtended/VRCExtended.csproj b/VRCExtended/VRCExtended.csproj new file mode 100644 index 0000000..073bd8c --- /dev/null +++ b/VRCExtended/VRCExtended.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {7DF0802E-DB23-417B-A5CD-949B0471D3C4} + Library + Properties + VRCExtended + VRCExtended + v3.5 + 512 + true + + + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + + + + ..\lib\Assembly-CSharp.dll + + + ..\lib\Assembly-CSharp-firstpass.dll + + + False + ..\lib\Newtonsoft.Json.dll + + + + + + + + + ..\lib\UnityEngine.dll + + + ..\lib\UnityEngine.AudioModule.dll + + + ..\lib\UnityEngine.CoreModule.dll + + + ..\lib\UnityEngine.ParticleSystemModule.dll + + + ..\lib\UnityEngine.PhysicsModule.dll + + + ..\lib\UnityEngine.TextRenderingModule.dll + + + ..\lib\UnityEngine.UI.dll + + + ..\lib\VRCCore-Standalone.dll + + + ..\lib\VRCModLoader.dll + + + ..\lib\VRCSDK2.dll + + + ..\lib\VRCTools.dll + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VRCExtended/VRChat/Obfuscation/OBFUnityActionInternal.cs b/VRCExtended/VRChat/Obfuscation/OBFUnityActionInternal.cs new file mode 100644 index 0000000..234d593 --- /dev/null +++ b/VRCExtended/VRChat/Obfuscation/OBFUnityActionInternal.cs @@ -0,0 +1,76 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using UnityEngine.Events; + +using VRCExtended; + +namespace VRChat.Obfuscation +{ + public class OBFUnityActionInternal + { + #region OBFUnityActionInternal Properties + public MethodInfo MethodAdd { get; private set; } + public MethodInfo MethodExecute { get; private set; } + public MethodInfo MethodRemove { get; private set; } + + public object Instance { get; private set; } + #endregion + + public OBFUnityActionInternal(Type type, object instance) + { + MethodInfo[] addRemoveMethods = type.GetMethods().Where(a => a.GetParameters().Length > 0 && a.GetParameters()[0].ParameterType == typeof(UnityAction)).ToArray(); + if(addRemoveMethods.Length < 2) + { + ExtendedLogger.LogError("Failed to find required UnityActionInternal functions for type: " + type.Name + "!"); + return; + } + + if(addRemoveMethods[0].GetMethodBody().GetILAsByteArray().Length > addRemoveMethods[1].GetMethodBody().GetILAsByteArray().Length) + { + MethodAdd = addRemoveMethods[0]; + MethodRemove = addRemoveMethods[1]; + } + else + { + MethodAdd = addRemoveMethods[1]; + MethodRemove = addRemoveMethods[0]; + } + MethodExecute = type.GetMethods().First(a => a.GetParameters()[0].ParameterType == typeof(T)); + + ExtendedLogger.Log("Found Execute method in " + type.Name + " with name: " + MethodExecute.Name + "!"); + ExtendedLogger.Log("Found Add method in " + type.Name + " with name: " + MethodAdd.Name + "!"); + ExtendedLogger.Log("Found Remove method in " + type.Name + " with name: " + MethodRemove.Name + "!"); + + Instance = instance; + } + + #region UnityActionInternal Functions + public void Add(UnityAction action) + { + if (Instance == null) + return; + + MethodAdd.Invoke(Instance, new object[] { action }); + } + public void Remove(UnityAction action) + { + if (Instance == null) + return; + + MethodRemove.Invoke(Instance, new object[] { action }); + } + + public void Execute(T val) + { + if (Instance == null) + return; + + MethodExecute.Invoke(Instance, new object[] { val }); + } + #endregion + } +} diff --git a/VRCExtended/VRChat/UI/VRCEUi.cs b/VRCExtended/VRChat/UI/VRCEUi.cs new file mode 100644 index 0000000..6c7a5cd --- /dev/null +++ b/VRCExtended/VRChat/UI/VRCEUi.cs @@ -0,0 +1,293 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using UnityEngine; +using UnityEngine.UI; + +using VRC.UI; + +using VRCExtended; + +namespace VRChat.UI +{ + public static class VRCEUi + { + #region VRChat Menu Variables + private static QuickMenu _quickMenu; + + private static GameObject _avatarScreen; + private static GameObject _detailsScreen; + private static GameObject _playlistsScreen; + private static GameObject _socialScreen; + private static GameObject _settingsScreen; + private static GameObject _safetyScreen; + private static GameObject _userInfoScreen; + private static GameObject _worldsScreen; + #endregion + + #region VRChat Menu Properties + public static QuickMenu QuickMenu + { + get + { + if (_quickMenu == null) + _quickMenu = (QuickMenu)typeof(QuickMenu).GetMethod("get_Instance", BindingFlags.Public | BindingFlags.Static).Invoke(null, null); + return _quickMenu; + } + } + + public static GameObject AvatarScreen + { + get + { + if(_avatarScreen == null) + _avatarScreen = GameObject.Find(QuickMenu.avatarScreenPath); + return _avatarScreen; + } + } + public static GameObject DetailsScreen + { + get + { + if (_detailsScreen == null) + _detailsScreen = GameObject.Find(QuickMenu.detailsScreenPath); + return _detailsScreen; + } + } + public static GameObject PlaylistsScreen + { + get + { + if (_playlistsScreen == null) + _playlistsScreen = GameObject.Find(QuickMenu.playlistsScreenPath); + return _playlistsScreen; + } + } + public static GameObject SocialScreen + { + get + { + if (_socialScreen == null) + _socialScreen = GameObject.Find(QuickMenu.socialScreenPath); + return _socialScreen; + } + } + public static GameObject SettingsScreen + { + get + { + if (_settingsScreen == null) + _settingsScreen = GameObject.Find(QuickMenu.settingsScreenPath); + return _settingsScreen; + } + } + public static GameObject SafetyScreen + { + get + { + if (_safetyScreen == null) + _safetyScreen = GameObject.Find(QuickMenu.safetyScreenPath); + return _safetyScreen; + } + } + public static GameObject UserInfoScreen + { + get + { + if (_userInfoScreen == null) + _userInfoScreen = GameObject.Find(QuickMenu.userInfoScreenPath); + return _userInfoScreen; + } + } + public static GameObject WorldsScreen + { + get + { + if (_worldsScreen == null) + _worldsScreen = GameObject.Find(QuickMenu.worldsScreenPath); + return _worldsScreen; + } + } + #endregion + + #region VRChat UI Functions + public static Transform DuplicateButton(Transform button, string name, string text, Vector2 offset, Transform parent = null) + { + // Create new one + GameObject goButton = GameObject.Instantiate(button.gameObject); + if(goButton == null) + { + ExtendedLogger.LogError("Could not duplicate button!"); + return null; + } + + // Get UI components + Button objButton = goButton.GetComponentInChildren