├── .github
└── workflows
│ └── dotnet.yml
├── .gitignore
├── .vs
├── ProjectSettings.json
├── VRCMods
│ ├── DesignTimeBuild
│ │ └── .dtbcache.v2
│ └── v16
│ │ └── .suo
├── VSWorkspaceState.json
└── slnx.sqlite
├── AdvancedSafety
├── AdvancedSafety.csproj
├── AdvancedSafetyMod.cs
├── AdvancedSafetySettings.cs
├── AvatarHiding.cs
├── BundleVerifier
│ ├── BundleDlContext.cs
│ ├── BundleDlInterceptor.cs
│ ├── BundleDownloadMethods.cs
│ ├── BundleHashCache.cs
│ ├── BundleVerifier.zip
│ ├── BundleVerifierMod.cs
│ ├── RestrictedProcessRunner
│ │ ├── BundleVerifierProcessHandle.cs
│ │ ├── Interop
│ │ │ ├── InteropMethods.cs
│ │ │ ├── JobHandle.cs
│ │ │ └── ProcessHandle.cs
│ │ ├── MemoryMapWriterStream.cs
│ │ └── RestrictedProcessHandle.cs
│ └── VerifierExitCodes.cs
├── ComponentAdjustment.cs
├── FinalIkPatches.cs
├── NativeStructs.cs
├── PortalHiding.cs
├── PriorityQueue.cs
├── QuickMenuHideAvatarButtonHandler.cs
├── ReaderPatches.cs
├── ScanningReflectionCache.cs
├── SortingOrderHammerer.cs
└── UiExpansionKitSupport.cs
├── CameraMinus
├── CameraMinus.csproj
└── CameraMinusMod.cs
├── Common
├── CustomizedMelonMod.cs
├── NativePatchUtils.cs
├── _dummy2_.dll
└── _dummy_.dll
├── Directory.Build.props
├── EmojiPageButtons
├── EmojiPageButtons.csproj
└── EmojiPageButtonsMod.cs
├── FavCat
├── Adapters
│ ├── DbAvatarAdapter.cs
│ ├── DbPlayerAdapter.cs
│ ├── DbWorldAdapter.cs
│ └── IStoredModelAdapter.cs
├── AssetsHandler.cs
├── CustomLists
│ ├── CustomPicker.cs
│ ├── CustomPickerList.cs
│ ├── IPickerElement.cs
│ └── PickerPool.cs
├── Database
│ ├── DatabaseEntity.cs
│ ├── DatabaseFavoriteHandler.cs
│ ├── DatabaseImageHandler.cs
│ ├── LocalStoreDatabase.Avatar.cs
│ ├── LocalStoreDatabase.Player.cs
│ ├── LocalStoreDatabase.World.cs
│ ├── LocalStoreDatabase.cs
│ └── Stored
│ │ ├── INamedStoredObject.cs
│ │ ├── StoredAvatar.cs
│ │ ├── StoredCategory.cs
│ │ ├── StoredCategoryOrder.cs
│ │ ├── StoredFavorite.cs
│ │ ├── StoredImageInfo.cs
│ │ ├── StoredPlayer.cs
│ │ └── StoredWorld.cs
├── ExportProcessor.cs
├── Extensions.cs
├── FavCat.csproj
├── FavCat.csproj.DotSettings
├── FavCatMod.cs
├── FavCatSettings.cs
├── GlobalImageCache.cs
├── ImportFolderProcessor.cs
├── Modules
│ ├── AvatarModule.cs
│ ├── ExtendedFavoritesModuleBase.cs
│ ├── PlayersModule.cs
│ └── WorldsModule.cs
├── ReFetchFavoritesProcessor.cs
├── ScanningReflectionCache.cs
└── extraui
├── Finitizer
├── Finitizer.csproj
└── FinitizerMod.cs
├── FriendsPlusHome
├── FriendsPlusHome.csproj
└── FriendsPlusHomeMod.cs
├── IKTweaks
├── AnimationsHandler.cs
├── CachedSolver.cs
├── CustomSpineSolver.cs
├── HandOffsetsManager.cs
├── IKTweaks.csproj
├── IKTweaksMod.cs
├── IkTweaksSettings.cs
├── Math.cs
├── VrIkHandling.cs
├── WallFreezeFixer.cs
└── WeightAdjustmentCookie.cs
├── ILRepack
├── ILRepack.Lib.MSBuild.Task.dll
├── ILRepack.Lib.MSBuild.Task.targets
└── ILRepack.dll
├── IntegrityCheckGenerator
├── IntegrityCheckGenerator.cs
└── IntegrityCheckGenerator.csproj
├── IntegrityCheckWeaver
├── DummyThree.cs
├── IntegrityCheckWeaver.csproj
├── Program.cs
└── Utils.cs
├── JoinNotifier
├── JoinNotifier.csproj
├── JoinNotifierMod.cs
├── JoinNotifierSettings.cs
├── NetworkManagerHooks.cs
└── joinnotifier.assetbundle
├── LICENSE
├── LagFreeScreenshots
├── API
│ ├── LfsApi.cs
│ ├── MetadataV2.cs
│ └── ScreenshotRotation.cs
├── AwaitProvider.cs
├── EventHandler.cs
├── LagFreeScreenshots.csproj
├── LagFreeScreenshotsMod.cs
├── Metadata.cs
├── PngUtils.cs
└── PresetScreenshotSizes.cs
├── Libs
└── .touch
├── LibsAlt
└── .touch
├── Malicious-Mods.md
├── MirrorResolutionUnlimiter
├── MirrorResolutionUnlimiter.csproj
├── MirrorResolutionUnlimiterMod.cs
├── OriginalPixelLightsSettingKeeper.cs
└── UiExtensionsAddon.cs
├── ParticleAndBoneLimiterSettings
├── CustomParticleSettingsUiHandler.cs
├── ParticleAndBoneLimiterSettings.cs
└── ParticleAndBoneLimiterSettings.csproj
├── README.md
├── ReleaseChangelog.md
├── ScaleGoesBrr
├── ScaleGoesBrr.csproj
├── ScaleGoesBrrComponent.cs
└── ScaleGoesBrrMod.cs
├── SparkleBeGone
├── SparkleBeGone.csproj
├── SparkleBeGoneMod.cs
└── sparklebegone
├── Styletor
├── API
│ └── StyletorApi.cs
├── BundledStyles
│ ├── basic-recolorable.styletor.zip
│ ├── bigger-mic-button.styletor.zip
│ ├── default-fixes.styletor.zip
│ └── qm-background-lemon.styletor.zip
├── ExtraHandlers
│ ├── ActionMenuHandler.cs
│ └── UiLasersHandler.cs
├── Jsons
│ ├── SpriteJson.cs
│ └── StyleMetadata.cs
├── SettingsHolder.cs
├── StyleEngineWrapper.cs
├── Styles
│ ├── BuiltinStyleExporter.cs
│ ├── ColorizerManager.cs
│ ├── OverrideStyle.cs
│ ├── OverridesStyleSheet.cs
│ └── StylesLoader.cs
├── Styletor.csproj
├── StyletorMod.cs
└── Utils
│ ├── Extensions.cs
│ ├── SpriteSnipperUtil.cs
│ └── Utils.cs
├── TrueShaderAntiCrash
├── DxbcShaderFilter.dll
├── ShaderFilterApi.cs
├── TrueShaderAntiCrash.csproj
└── TrueShaderAntiCrashMod.cs
├── Turbones
├── BoneDeleteHandler.cs
├── JigglyRustSolver.dll
├── JigglySolverApi.cs
├── NativeStructs.cs
├── Offsets.cs
├── Turbones.csproj
└── TurbonesMod.cs
├── UIExpansionKit
├── API
│ ├── BuiltinUiUtils.cs
│ ├── Classes
│ │ └── AwaitProvider.cs
│ ├── Controls
│ │ ├── FluentExtensions.cs
│ │ └── Interfaces.cs
│ ├── ExpandedMenu.cs
│ ├── ExpansionKitApi.cs
│ ├── ICustomLayoutedMenu.cs
│ ├── ICustomShowableLayoutedMenu.cs
│ ├── IShowableMenu.cs
│ ├── LayoutDescription.cs
│ └── TaskUtilities.cs
├── ButtonFactory.cs
├── COPYING.LESSER
├── Components
│ ├── DestroyListener.cs
│ ├── EnableDisableListener.cs
│ ├── StyleElementWrapper.cs
│ └── StyleEngineUpdateDriver.cs
├── ControlsImpl
│ ├── BaseMenuControl.cs
│ ├── Dummies.cs
│ ├── MenuControlWithText.cs
│ └── MenuToggle.cs
├── CustomCameraPageImpl.cs
├── CustomExpandoOverlayImpl.cs
├── CustomFullMenuPopupImpl.cs
├── CustomLayoutedPageImpl.cs
├── CustomLayoutedPageWithOwnedMenuImpl.cs
├── CustomQuickMenuPageImpl.cs
├── EnumPrefUtil.cs
├── ExpansionKitSettings.cs
├── Extensions.cs
├── FieldInject
│ ├── InjectedField.cs
│ └── NativeStructs.cs
├── ModSettingsHandler.cs
├── PinnedPrefUtil.cs
├── PreloadedBundleContents.cs
├── Resources
│ ├── UixBase.styletor.zip
│ ├── modui.assetbundle
│ └── uix-style-main.vrcss
├── ScanningReflectionCache.cs
├── StylingHelper.cs
├── UIExpansionKit.csproj
├── UIExpansionKit.csproj.DotSettings
├── UiExpansionKitMod.cs
└── UnityUtils.cs
├── VRCMods.sln
├── VRCMods.sln.DotSettings
└── ViewPointTweaker
├── ViewPointTweaker.csproj
└── ViewPointTweakerMod.cs
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: .NET
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | create:
7 | tags:
8 | - '*'
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: windows-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 |
18 | - name: Checkout libs
19 | uses: actions/checkout@v2
20 | with:
21 | repository: ${{ secrets.LibsRepository }}
22 | token: ${{ secrets.LibsRepositoryToken }}
23 | path: Libs
24 |
25 | - name: Setup .NET
26 | uses: actions/setup-dotnet@v1
27 | with:
28 | dotnet-version: 5.0.x
29 |
30 | - name: Restore dependencies
31 | run: dotnet restore
32 |
33 | - name: Build
34 | run: dotnet build --no-restore
35 |
36 | - name: Publish a release
37 | uses: softprops/action-gh-release@v1
38 | if: startsWith(github.ref, 'refs/tags/')
39 | with:
40 | body_path: ReleaseChangelog.md
41 | files: |
42 | Output/Debug/net472/AdvancedSafety.dll
43 | Output/Debug/net472/CameraMinus.dll
44 | Output/Debug/net472/FavCat-merged.dll
45 | Output/Debug/net472/Finitizer.dll
46 | Output/Debug/net472/FriendsPlusHome.dll
47 | Output/Debug/net472/IKTweaks.dll
48 | Output/Debug/net472/JoinNotifier.dll
49 | Output/Debug/net472/LagFreeScreenshots.dll
50 | Output/Debug/net472/MirrorResolutionUnlimiter.dll
51 | Output/Debug/net472/ParticleAndBoneLimiterSettings.dll
52 | Output/Debug/net472/ScaleGoesBrr.dll
53 | Output/Debug/net472/Styletor.dll
54 | Output/Debug/net472/TrueShaderAntiCrash.dll
55 | Output/Debug/net472/Turbones.dll
56 | Output/Debug/net472/UIExpansionKit.dll
57 | Output/Debug/net472/ViewPointTweaker.dll
58 | env:
59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | bin/
3 | obj/
4 | /packages
5 | Libs/*.dll
6 | Libs/*.db
7 | LibsAlt/*.dll
8 | *.user
9 | /Output
10 | IKTweaks/FinalIK
11 | .vs
--------------------------------------------------------------------------------
/.vs/ProjectSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "CurrentProjectSetting": null
3 | }
--------------------------------------------------------------------------------
/.vs/VRCMods/DesignTimeBuild/.dtbcache.v2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/.vs/VRCMods/DesignTimeBuild/.dtbcache.v2
--------------------------------------------------------------------------------
/.vs/VRCMods/v16/.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/.vs/VRCMods/v16/.suo
--------------------------------------------------------------------------------
/.vs/VSWorkspaceState.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExpandedNodes": [
3 | ""
4 | ],
5 | "SelectedNode": "\\VRCMods.sln",
6 | "PreviewInSolutionExplorer": false
7 | }
--------------------------------------------------------------------------------
/.vs/slnx.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/.vs/slnx.sqlite
--------------------------------------------------------------------------------
/AdvancedSafety/AdvancedSafety.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | true
7 | latest
8 | 1.6.1.0
9 | true
10 |
11 |
12 |
13 | none
14 | false
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/BundleVerifier.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/AdvancedSafety/BundleVerifier/BundleVerifier.zip
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/RestrictedProcessRunner/BundleVerifierProcessHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace AdvancedSafety.BundleVerifier.RestrictedProcessRunner
5 | {
6 | public sealed class BundleVerifierProcessHandle : IDisposable
7 | {
8 | private readonly RestrictedProcessHandle myProcessHandle;
9 |
10 | public BundleVerifierProcessHandle(string executablePath, string sharedMemoryName, TimeSpan maxTime, ulong maxMemory, int minFps, int maxComponents)
11 | {
12 | var pid = Process.GetCurrentProcess().Id;
13 |
14 | myProcessHandle = new RestrictedProcessHandle(executablePath, $"-batchmode -nolog -nographics {maxComponents} {pid} {sharedMemoryName} {minFps}");
15 |
16 | myProcessHandle.SetLimits(maxTime, maxMemory, false, false, false);
17 | myProcessHandle.Start();
18 | }
19 |
20 | public int? WaitForExit(TimeSpan timeout) => myProcessHandle.WaitForExit(timeout);
21 |
22 | public void Dispose()
23 | {
24 | myProcessHandle.Dispose();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/RestrictedProcessRunner/Interop/JobHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace AdvancedSafety.BundleVerifier.RestrictedProcessRunner.Interop
5 | {
6 |
7 | public sealed class JobHandle : IDisposable
8 | {
9 | private IntPtr myJobHandle;
10 |
11 | public JobHandle()
12 | {
13 | InteropMethods.SECURITY_ATTRIBUTES empty = default;
14 | myJobHandle = InteropMethods.CreateJobObject(ref empty, null);
15 | if (myJobHandle == IntPtr.Zero)
16 | {
17 | throw new ApplicationException("Unable to create job object");
18 | }
19 | }
20 |
21 | internal IntPtr Handle => myJobHandle;
22 |
23 | public unsafe void SetLimits(TimeSpan? cpuTimeLimit, ulong? memoryBytes, bool allowNetwork, bool allowDesktop,
24 | bool allowChildProcesses)
25 | {
26 | {
27 | InteropMethods.JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits = default;
28 |
29 | if (cpuTimeLimit != null)
30 | {
31 | limits.BasicLimitInformation.LimitFlags |= InteropMethods.JobBasicLimitFlags.PROCESS_TIME;
32 | limits.BasicLimitInformation.PerProcessUserTimeLimit = cpuTimeLimit.Value.Ticks;
33 | }
34 |
35 | if (memoryBytes != null)
36 | {
37 | limits.BasicLimitInformation.LimitFlags |= InteropMethods.JobBasicLimitFlags.PROCESS_MEMORY;
38 | limits.ProcessMemoryLimit = (IntPtr)memoryBytes.Value;
39 | }
40 |
41 | if (!allowChildProcesses)
42 | {
43 | limits.BasicLimitInformation.ActiveProcessLimit = 1;
44 | limits.BasicLimitInformation.LimitFlags |= InteropMethods.JobBasicLimitFlags.ACTIVE_PROCESS;
45 | }
46 |
47 | limits.BasicLimitInformation.LimitFlags |= InteropMethods.JobBasicLimitFlags.KILL_ON_JOB_CLOSE;
48 |
49 | InteropMethods.SetInformationJobObject(myJobHandle,
50 | InteropMethods.JobObjectInfoClass.JobObjectExtendedLimitInformation, (IntPtr)(&limits),
51 | Marshal.SizeOf());
52 | }
53 |
54 | if (!allowDesktop)
55 | {
56 | var uiLimits = new InteropMethods.JOBOBJECT_BASIC_UI_RESTRICTIONS
57 | { UIRestrictionsClass = InteropMethods.UiRestrictionClass.ALL };
58 | InteropMethods.SetInformationJobObject(myJobHandle,
59 | InteropMethods.JobObjectInfoClass.JobObjectBasicUIRestrictions, (IntPtr)(&uiLimits),
60 | Marshal.SizeOf());
61 | }
62 |
63 | if (!allowNetwork)
64 | {
65 | var limits = new InteropMethods.JOBOBJECT_NET_RATE_CONTROL_INFORMATION
66 | {
67 | ControlFlags = InteropMethods.JOB_OBJECT_NET_RATE_CONTROL_FLAGS.JOB_OBJECT_NET_RATE_CONTROL_ENABLE |
68 | InteropMethods.JOB_OBJECT_NET_RATE_CONTROL_FLAGS
69 | .JOB_OBJECT_NET_RATE_CONTROL_MAX_BANDWIDTH,
70 | MaxBandwidth = 0
71 | };
72 |
73 | InteropMethods.SetInformationJobObject(myJobHandle,
74 | InteropMethods.JobObjectInfoClass.JobObjectNetRateControlInformation, (IntPtr)(&limits),
75 | Marshal.SizeOf());
76 | }
77 | }
78 |
79 | private void ReleaseUnmanagedResources()
80 | {
81 | if (myJobHandle != IntPtr.Zero) InteropMethods.CloseHandle(myJobHandle);
82 | myJobHandle = IntPtr.Zero;
83 | }
84 |
85 | public void Dispose()
86 | {
87 | ReleaseUnmanagedResources();
88 | GC.SuppressFinalize(this);
89 | }
90 |
91 | ~JobHandle()
92 | {
93 | ReleaseUnmanagedResources();
94 | }
95 | }
96 | }
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/RestrictedProcessRunner/Interop/ProcessHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Runtime.InteropServices;
4 | using System.Threading;
5 |
6 | namespace AdvancedSafety.BundleVerifier.RestrictedProcessRunner.Interop
7 | {
8 |
9 | public sealed class ProcessHandle : IDisposable
10 | {
11 | private IntPtr myProcessHandle;
12 | private IntPtr myThreadHandle;
13 |
14 | private int myIsStarted;
15 |
16 | public ProcessHandle(string commandLine)
17 | {
18 | InteropMethods.SECURITY_ATTRIBUTES empty = default;
19 | InteropMethods.PROCESS_INFORMATION processInfo = default;
20 | var startupInfo = new InteropMethods.STARTUPINFO
21 | {
22 | cb = Marshal.SizeOf(),
23 | };
24 | var created = InteropMethods.CreateProcess(null, commandLine, ref empty, ref empty, false,
25 | InteropMethods.ProcessCreationFlags.CREATE_NO_WINDOW |
26 | InteropMethods.ProcessCreationFlags.CREATE_SUSPENDED,
27 | IntPtr.Zero, null, ref startupInfo, out processInfo);
28 | if (!created)
29 | throw new ArgumentException("Can't create process");
30 |
31 | myProcessHandle = processInfo.hProcess;
32 | myThreadHandle = processInfo.hThread;
33 | }
34 |
35 | public ProcessHandle(string commandLine, JobHandle job) : this(commandLine)
36 | {
37 | InteropMethods.AssignProcessToJobObject(job.Handle, myProcessHandle);
38 | }
39 |
40 | public void Start()
41 | {
42 | if (Interlocked.CompareExchange(ref myIsStarted, 1, 0) == 0)
43 | InteropMethods.ResumeThread(myThreadHandle);
44 | else
45 | throw new ApplicationException("Already started");
46 | }
47 |
48 | public int? WaitForExit(TimeSpan timeout)
49 | {
50 | var waitTime = Stopwatch.StartNew();
51 | do
52 | {
53 | InteropMethods.GetExitCodeProcess(myProcessHandle, out var exitCode);
54 | if (exitCode != InteropMethods.STILL_ACTIVE)
55 | return exitCode;
56 | Thread.Sleep(33);
57 | } while (waitTime.Elapsed < timeout);
58 |
59 | return null;
60 | }
61 |
62 | public int? GetExitCode()
63 | {
64 | InteropMethods.GetExitCodeProcess(myProcessHandle, out var exitCode);
65 | if (exitCode != InteropMethods.STILL_ACTIVE)
66 | return exitCode;
67 | return null;
68 | }
69 |
70 | private void ReleaseUnmanagedResources()
71 | {
72 | if (myProcessHandle != IntPtr.Zero) InteropMethods.CloseHandle(myProcessHandle);
73 | myProcessHandle = IntPtr.Zero;
74 |
75 | if (myThreadHandle != IntPtr.Zero) InteropMethods.CloseHandle(myThreadHandle);
76 | myThreadHandle = IntPtr.Zero;
77 | }
78 |
79 | private void Dispose(bool disposing)
80 | {
81 | ReleaseUnmanagedResources();
82 | }
83 |
84 | public void Dispose()
85 | {
86 | Dispose(true);
87 | GC.SuppressFinalize(this);
88 | }
89 |
90 | ~ProcessHandle()
91 | {
92 | Dispose(false);
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/RestrictedProcessRunner/MemoryMapWriterStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.MemoryMappedFiles;
4 | using System.Threading;
5 |
6 | namespace AdvancedSafety.BundleVerifier.RestrictedProcessRunner
7 | {
8 | public class MemoryMapWriterStream : Stream
9 | {
10 | private readonly MemoryMappedViewAccessor myView;
11 | private int myPosition;
12 |
13 | public MemoryMapWriterStream(MemoryMappedFile mapFile)
14 | {
15 | myView = mapFile.CreateViewAccessor();
16 | }
17 |
18 | public override void Flush() { }
19 |
20 | internal unsafe byte* GetPointer()
21 | {
22 | byte* ptr = null;
23 | myView.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
24 | return ptr;
25 | }
26 |
27 | internal void ReleasePointer() => myView.SafeMemoryMappedViewHandle.ReleasePointer();
28 |
29 | public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
30 | public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
31 |
32 | public override void SetLength(long value) => myView.Write(0, (int) value);
33 |
34 | public override void Write(byte[] buffer, int offset, int count)
35 | {
36 | if (count + myPosition > myView.Capacity - 8)
37 | throw new IOException($"Stream is full (capacity {myView.Capacity}, requires {count + myPosition}");
38 |
39 | myView.WriteArray(myPosition + 8, buffer, offset, count);
40 | myPosition += count;
41 | Interlocked.MemoryBarrier();
42 | myView.Write(4, myPosition);
43 | }
44 |
45 | protected override void Dispose(bool disposing)
46 | {
47 | base.Dispose(disposing);
48 | if (disposing)
49 | myView.Dispose();
50 | }
51 |
52 | public override bool CanRead => false;
53 | public override bool CanSeek => false;
54 | public override bool CanWrite => true;
55 | public override long Length => Position;
56 | public override long Position { get => myPosition; set => throw new NotSupportedException(); }
57 | }
58 | }
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/RestrictedProcessRunner/RestrictedProcessHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AdvancedSafety.BundleVerifier.RestrictedProcessRunner.Interop;
3 |
4 | namespace AdvancedSafety.BundleVerifier.RestrictedProcessRunner
5 | {
6 | public sealed class RestrictedProcessHandle : IDisposable
7 | {
8 | private readonly JobHandle myJobHandle;
9 | private readonly ProcessHandle myProcessHandle;
10 |
11 | public RestrictedProcessHandle(string processPath, string commandline)
12 | {
13 | myJobHandle = new JobHandle();
14 |
15 | try
16 | {
17 | var cli = $"\"{processPath.Replace("\"", "")}\" {commandline}";
18 | myProcessHandle = new ProcessHandle(cli, myJobHandle);
19 | }
20 | catch
21 | {
22 | myJobHandle.Dispose();
23 | throw;
24 | }
25 | }
26 |
27 | public int? WaitForExit(TimeSpan timeout) => myProcessHandle.WaitForExit(timeout);
28 |
29 | public void Dispose()
30 | {
31 | myJobHandle.Dispose();
32 | myProcessHandle.Dispose();
33 | }
34 |
35 | public void SetLimits(TimeSpan? cpuTimeLimit, ulong? memoryBytes, bool allowNetwork, bool allowDesktop, bool allowChildProcesses) =>
36 | myJobHandle.SetLimits(cpuTimeLimit, memoryBytes, allowNetwork, allowDesktop, allowChildProcesses);
37 |
38 | public void Start() => myProcessHandle.Start();
39 | }
40 | }
--------------------------------------------------------------------------------
/AdvancedSafety/BundleVerifier/VerifierExitCodes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace AdvancedSafety.BundleVerifier
4 | {
5 | public static class VerifierExitCodes
6 | {
7 | private static readonly Dictionary ourCodeDescriptions = new()
8 | {
9 | { 179, "Generic exception (report a bug!)" },
10 | { 180, "CLI argument parse error (report a bug!)" },
11 | { 181, "VRChat process suddenly died" },
12 | { 182, "Loaded bundle was null" },
13 | { 183, "Loaded bundle did not contain a prefab" },
14 | { 184, "Prefab instantiation failed" },
15 | { 185, "Minimum framerate not achieved" },
16 | { 186, "Over component limit" },
17 | { -1073741819, "Generic crash (corrupted bundle)" }, // 0xc0000005
18 | { -2147483645, "Breakpoint crash (corrupted bundle)" }, // 0x80000003
19 | };
20 |
21 | internal static string GetExitCodeDescription(int? exitCode)
22 | {
23 | if (!exitCode.HasValue) return "Timed out";
24 |
25 | return ourCodeDescriptions.TryGetValue(exitCode.Value, out var result) ? result : "Unknown";
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/AdvancedSafety/FinalIkPatches.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using HarmonyLib;
3 | using RootMotion.FinalIK;
4 | using UnityEngine;
5 |
6 | namespace AdvancedSafety
7 | {
8 | /**
9 | * Source: FinalIkSanity (https://github.com/FenrixTheFox/FinalIKSanity, GPLv3) and Requi (contributed personally by him)
10 | */
11 | public static class FinalIkPatches
12 | {
13 | public static void ApplyPatches(HarmonyLib.Harmony harmony)
14 | {
15 | harmony.Patch(typeof(IKSolverHeuristic).GetMethods().First(m => m.Name.Equals("IsValid") && m.GetParameters().Length == 1),
16 | new HarmonyMethod(typeof(FinalIkPatches), nameof(IkSolverIsValid)));
17 |
18 | harmony.Patch(typeof(IKSolverAim).GetMethod(nameof(IKSolverAim.GetClampedIKPosition)),
19 | new HarmonyMethod(typeof(FinalIkPatches), nameof(IkSolverAimGetClampedIKPosition)));
20 |
21 | harmony.Patch(typeof(IKSolverFullBody).GetMethod(nameof(IKSolverFullBody.Solve)),
22 | new HarmonyMethod(typeof(FinalIkPatches), nameof(IKSolverFullBodySolve)));
23 |
24 | harmony.Patch(typeof(IKSolverFABRIKRoot).GetMethod(nameof(IKSolverFABRIKRoot.OnUpdate)),
25 | new HarmonyMethod(typeof(FinalIkPatches), nameof(IKSolverFABRIKRootOnUpdate)));
26 | }
27 |
28 | private static void IkSolverAimGetClampedIKPosition(ref IKSolverAim __instance)
29 | {
30 | if (__instance == null) return;
31 | __instance.clampSmoothing = Mathf.Clamp(__instance.clampSmoothing, 0, 2);
32 | }
33 |
34 | private static bool IkSolverIsValid(ref IKSolverHeuristic __instance, ref bool __result, ref string message)
35 | {
36 | if (__instance == null) return true;
37 | if (__instance.maxIterations <= 64) return true;
38 |
39 | __result = false;
40 | message = "The solver requested too many iterations.";
41 |
42 | return false;
43 | }
44 |
45 | private static void IKSolverFullBodySolve(ref IKSolverFullBody __instance)
46 | {
47 | if (__instance == null) return;
48 | __instance.iterations = Mathf.Clamp(__instance.iterations, 0, 10);
49 | }
50 |
51 | private static bool IKSolverFABRIKRootOnUpdate(ref IKSolverFABRIKRoot __instance)
52 | {
53 | if (__instance == null)
54 | return true;
55 | return __instance.iterations <= 10;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/AdvancedSafety/NativeStructs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace AdvancedSafety
5 | {
6 | // These are copypasted from Unhollower. TODO: use actual structs from unhollower once/if they become public
7 | [StructLayout(LayoutKind.Sequential)]
8 | internal struct Il2CppFieldInfo_24_1
9 | {
10 | public IntPtr name; // const char*
11 | public IntPtr typePtr; // const
12 | public IntPtr parentClassPtr; // non-const?
13 | public int offset; // If offset is -1, then it's thread static
14 | public uint token;
15 | }
16 | }
--------------------------------------------------------------------------------
/AdvancedSafety/PriorityQueue.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace AdvancedSafety
4 | {
5 | public class PriorityQueue
6 | {
7 | private readonly List myBackingStorage;
8 | private readonly IComparer myComparer;
9 |
10 | public PriorityQueue(IComparer comparer)
11 | {
12 | myComparer = comparer;
13 | myBackingStorage = new List();
14 | }
15 |
16 | public void Enqueue(T value)
17 | {
18 | myBackingStorage.Add(value);
19 | SiftUp(myBackingStorage.Count - 1);
20 | }
21 |
22 | public T Dequeue()
23 | {
24 | if (myBackingStorage.Count == 0)
25 | return default(T);
26 | Swap(0, myBackingStorage.Count - 1);
27 | var result = myBackingStorage[myBackingStorage.Count - 1];
28 | myBackingStorage.RemoveAt(myBackingStorage.Count - 1);
29 | SiftDown(0);
30 | return result;
31 | }
32 |
33 | public T Peek()
34 | {
35 | if (myBackingStorage.Count == 0)
36 | return default(T);
37 | return myBackingStorage[0];
38 | }
39 |
40 | public int Count => myBackingStorage.Count;
41 |
42 | private void Swap(int i1, int i2)
43 | {
44 | var value1 = myBackingStorage[i1];
45 | var value2 = myBackingStorage[i2];
46 | myBackingStorage[i1] = value2;
47 | myBackingStorage[i2] = value1;
48 | }
49 |
50 | private void SiftDown(int i)
51 | {
52 | var childIndex1 = i * 2 + 1;
53 | var childIndex2 = i * 2 + 2;
54 | if (childIndex1 >= myBackingStorage.Count)
55 | return;
56 | var child1 = myBackingStorage[childIndex1];
57 | if (childIndex2 >= myBackingStorage.Count)
58 | {
59 | var compared = myComparer.Compare(myBackingStorage[i], child1);
60 | if (compared > 0)
61 | {
62 | Swap(i, childIndex1);
63 | }
64 | return;
65 | }
66 | var child2 = myBackingStorage[childIndex2];
67 | var compared1 = myComparer.Compare(myBackingStorage[i], child1);
68 | var compared2 = myComparer.Compare(myBackingStorage[i], child2);
69 | if (compared1 > 0 || compared2 > 0)
70 | {
71 | var compared12 = myComparer.Compare(child1, child2);
72 | if (compared12 > 0)
73 | {
74 | Swap(i, childIndex2);
75 | SiftDown(childIndex2);
76 | }
77 | else
78 | {
79 | Swap(i, childIndex1);
80 | SiftDown(childIndex1);
81 | }
82 | }
83 | }
84 |
85 | private void SiftUp(int i)
86 | {
87 | if (i == 0)
88 | return;
89 | var parentIndex = (i - 1) / 2;
90 | var compared = myComparer.Compare(myBackingStorage[i], myBackingStorage[parentIndex]);
91 | if (compared < 0)
92 | {
93 | Swap(i, parentIndex);
94 | SiftUp(parentIndex);
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/AdvancedSafety/QuickMenuHideAvatarButtonHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UIExpansionKit;
3 | using UnityEngine;
4 |
5 | namespace AdvancedSafety
6 | {
7 | public class QuickMenuHideAvatarButtonHandler : MonoBehaviour
8 | {
9 | private float myTimeAccumulator;
10 |
11 | public QuickMenuHideAvatarButtonHandler(IntPtr ptr) : base(ptr)
12 | {
13 | }
14 |
15 | private void Update()
16 | {
17 | myTimeAccumulator += Time.deltaTime;
18 | if (myTimeAccumulator < .5f) return;
19 |
20 | myTimeAccumulator = 0;
21 |
22 | var player = UiExpansionKitSupport.GetUserSelectedInQm()?.GetPlayer();
23 | if (player == null) return;
24 | var vrcPlayer = player.prop_VRCPlayer_0;
25 | if (vrcPlayer == null) return;
26 |
27 | UiExpansionKitSupport.QuickMenuUpdateTick(vrcPlayer);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/AdvancedSafety/ScanningReflectionCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using UnhollowerRuntimeLib.XrefScans;
5 | using VRC;
6 |
7 | namespace AdvancedSafety
8 | {
9 | public static class ScanningReflectionCache
10 | {
11 | private static Action ourReloadAllAvatarsDelegate;
12 |
13 | public static void ReloadAllAvatars(bool excludeSelf)
14 | {
15 | // xref yoinked from https://github.com/loukylor/VRC-Mods/commit/11abd2a62caaae5d0f817e20a0f6852d632da6bb#diff-ba8a2891e08655e0e158ea852ef76bbcb1ea58fc831a6817875c5efa21fef610R84 (GPLv3)
16 | if (ourReloadAllAvatarsDelegate == null)
17 | {
18 | var targetMethod = typeof(VRCPlayer).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
19 | .Single(it => it.Name.StartsWith("Method_Public_Void_Boolean_") && it.GetParameters().Length == 1 &&
20 | it.GetParameters()[0].IsOptional && XrefScanner.UsedBy(it).Any(jt =>
21 | {
22 | if (jt.Type != XrefType.Method) return false;
23 | var m = jt.TryResolve();
24 | if (m == null) return false;
25 | return m.DeclaringType == typeof(FeaturePermissionManager) &&
26 | m.Name.StartsWith("Method_Public_Void_");
27 | }));
28 | ourReloadAllAvatarsDelegate = (Action) Delegate.CreateDelegate(typeof(Action), targetMethod);
29 | }
30 |
31 | ourReloadAllAvatarsDelegate(VRCPlayer.field_Internal_Static_VRCPlayer_0, excludeSelf);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/AdvancedSafety/SortingOrderHammerer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnhollowerBaseLib;
3 | using UnityEngine;
4 |
5 | namespace AdvancedSafety
6 | {
7 | public class SortingOrderHammerer : MonoBehaviour
8 | {
9 | private Renderer[] myRenderersToHammer;
10 |
11 | public SortingOrderHammerer(IntPtr ptr) : base(ptr)
12 | {
13 | }
14 |
15 | private void Start()
16 | {
17 | myRenderersToHammer = gameObject.GetComponentsInChildren(true);
18 | }
19 |
20 | private void LateUpdate()
21 | {
22 | for (var i = 0; i < myRenderersToHammer.Length; i++)
23 | {
24 | if(myRenderersToHammer[i] is null) continue;
25 |
26 | try
27 | {
28 | myRenderersToHammer[i].sortingOrder = 0;
29 | }
30 | catch (Il2CppException) // this would imply a deleted renderer
31 | {
32 | myRenderersToHammer[i] = null;
33 | }
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/CameraMinus/CameraMinus.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | 3.1.0.0
7 | CameraMinus
8 |
9 |
10 |
11 | none
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Common/CustomizedMelonMod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using MelonLoader;
6 |
7 | #if CUSTOMIZED_MOD_INTERNAL
8 | internal
9 | #else
10 | public
11 | #endif
12 | abstract class CustomizedMelonMod : MelonMod
13 | {
14 | //internal static bool CheckWasSuccessful;
15 | //internal static bool MustStayFalse = false;
16 | //internal static bool MustStayTrue = true;
17 | //internal static bool RanCheck3 = false;
18 |
19 | static CustomizedMelonMod()
20 | {
21 | //LoaderIntegrityCheck.CheckIntegrity();
22 | //CheckWasSuccessful = true;
23 | }
24 |
25 | protected CustomizedMelonMod()
26 | {
27 | //RuntimeHelpers.RunClassConstructor(typeof(CustomizedMelonMod).TypeHandle);
28 | //
29 | //if (CheckWasSuccessful && !MustStayFalse && MustStayTrue) return;
30 | //
31 | //AnnoyingMessagePrinter.PrintWarningMessage();
32 | //
33 | //Console.In.ReadLine();
34 | //
35 | //Environment.Exit(1);
36 | //Marshal.GetDelegateForFunctionPointer(Marshal.AllocHGlobal(16))();
37 | }
38 |
39 | public override void OnSceneWasLoaded(int buildIndex, string sceneName)
40 | {
41 | //if (RanCheck3) return;
42 | //
43 | //LoaderIntegrityCheck.CheckDummyThree();
44 | //RanCheck3 = true;
45 | }
46 |
47 | protected void DoAfterUiManagerInit(Action code)
48 | {
49 | MelonCoroutines.Start(OnUiManagerInitCoro(code));
50 | }
51 |
52 | private static IEnumerator OnUiManagerInitCoro(Action code)
53 | {
54 | while (VRCUiManager.prop_VRCUiManager_0 == null)
55 | yield return null;
56 |
57 | code();
58 | }
59 | }
--------------------------------------------------------------------------------
/Common/NativePatchUtils.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 | using HarmonyLib;
7 | using MelonLoader;
8 | using UnhollowerBaseLib;
9 |
10 | #nullable enable
11 |
12 | internal static class NativePatchUtils
13 | {
14 | // ReSharper disable once CollectionNeverQueried.Local
15 | private static readonly List ourPinnedDelegates = new();
16 | private static readonly Dictionary ourOriginalPointers = new();
17 |
18 | internal static void NativePatch(MethodInfo original, out T callOriginal, MethodInfo patch)
19 | where T : MulticastDelegate
20 | {
21 | var patchDelegate = (T) Delegate.CreateDelegate(typeof(T), patch);
22 | NativePatch(original, out callOriginal, patchDelegate);
23 | }
24 |
25 | internal static void NativePatch(IntPtr originalPointer, out T callOriginal, MethodInfo patch, string? context = null)
26 | where T : MulticastDelegate
27 | {
28 | var patchDelegate = (T) Delegate.CreateDelegate(typeof(T), patch);
29 | NativePatch(originalPointer, out callOriginal, patchDelegate, context);
30 | }
31 |
32 | internal static unsafe void NativePatch(MethodInfo original, out T callOriginal, T patchDelegate) where T : MulticastDelegate
33 | {
34 | if (original == null) throw new ArgumentNullException(nameof(original));
35 |
36 | var originalPointer = *(IntPtr*) (IntPtr) UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(original).GetValue(null);
37 | NativePatch(originalPointer, out callOriginal, patchDelegate, original.FullDescription());
38 | }
39 |
40 | internal static unsafe void NativePatch(IntPtr originalPointer, out T callOriginal, T patchDelegate, string? context = null) where T : MulticastDelegate
41 | {
42 | ourPinnedDelegates.Add(patchDelegate);
43 |
44 | var patchPointer = Marshal.GetFunctionPointerForDelegate(patchDelegate);
45 | MelonUtils.NativeHookAttach((IntPtr)(&originalPointer), patchPointer);
46 | if (ourOriginalPointers.ContainsKey(originalPointer))
47 | MelonLogger.Warning($"Method {context ?? patchDelegate.Method.FullDescription()} has multiple native patches within single mod. Bug?");
48 | ourOriginalPointers[originalPointer] = patchPointer;
49 | callOriginal = Marshal.GetDelegateForFunctionPointer(originalPointer);
50 | }
51 |
52 | internal static unsafe void UnpatchAll()
53 | {
54 | foreach (var keyValuePair in ourOriginalPointers)
55 | {
56 | var pointer = keyValuePair.Key;
57 | MelonUtils.NativeHookDetach((IntPtr) (&pointer), keyValuePair.Value);
58 | }
59 |
60 | ourOriginalPointers.Clear();
61 | ourPinnedDelegates.Clear();
62 | }
63 |
64 | [StructLayout(LayoutKind.Sequential)]
65 | internal struct NativeString
66 | {
67 | public IntPtr Data;
68 | public long Capacity;
69 | public long Unknown;
70 | public long Length;
71 | public int Unknown2;
72 | }
73 | }
--------------------------------------------------------------------------------
/Common/_dummy2_.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Common/_dummy2_.dll
--------------------------------------------------------------------------------
/Common/_dummy_.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Common/_dummy_.dll
--------------------------------------------------------------------------------
/EmojiPageButtons/EmojiPageButtons.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | 1.0.3.0
7 | EmojiPageButtons
8 |
9 |
10 |
11 | none
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/FavCat/Adapters/DbAvatarAdapter.cs:
--------------------------------------------------------------------------------
1 | using FavCat.CustomLists;
2 | using FavCat.Database.Stored;
3 | using UnityEngine;
4 | using VRC.Core;
5 |
6 | namespace FavCat.Adapters
7 | {
8 | internal class DbAvatarAdapter : IPickerElement, IStoredModelAdapter
9 | {
10 | private readonly StoredAvatar myAvatar;
11 | private readonly StoredFavorite? myFavorite;
12 |
13 | public DbAvatarAdapter(StoredAvatar avatar, StoredFavorite? favorite)
14 | {
15 | myAvatar = avatar;
16 | myFavorite = favorite;
17 | }
18 |
19 | public string Name => myAvatar.Name;
20 | public string ImageUrl => myAvatar.ThumbnailUrl;
21 | public string Id => myAvatar.AvatarId;
22 | public bool IsPrivate => myAvatar.ReleaseStatus != "public";
23 | public bool SupportsDesktop => myAvatar.SupportedPlatforms == ApiModel.SupportedPlatforms.StandaloneWindows || myAvatar.SupportedPlatforms == ApiModel.SupportedPlatforms.All;
24 | public bool SupportsQuest => myAvatar.SupportedPlatforms == ApiModel.SupportedPlatforms.Android || myAvatar.SupportedPlatforms == ApiModel.SupportedPlatforms.All;
25 | public bool IsInaccessible => IsPrivate && myAvatar.AuthorId != APIUser.CurrentUser.id;
26 |
27 | public StoredAvatar Model => myAvatar;
28 | public StoredFavorite? StoredFavorite => myFavorite;
29 |
30 | public Color? CornerIconColor => null;
31 | }
32 | }
--------------------------------------------------------------------------------
/FavCat/Adapters/DbPlayerAdapter.cs:
--------------------------------------------------------------------------------
1 | using FavCat.CustomLists;
2 | using FavCat.Database.Stored;
3 | using FavCat.Modules;
4 | using UnityEngine;
5 |
6 | namespace FavCat.Adapters
7 | {
8 | public class DbPlayerAdapter : IPickerElement, IStoredModelAdapter
9 | {
10 | private readonly StoredPlayer myPlayer;
11 | private readonly StoredFavorite? myFavorite;
12 |
13 | public DbPlayerAdapter(StoredPlayer player, StoredFavorite? favorite)
14 | {
15 | myPlayer = player;
16 | myFavorite = favorite;
17 | }
18 |
19 | public string Id => myPlayer.PlayerId;
20 | public string Name => myPlayer.Name;
21 | public string ImageUrl => myPlayer.ThumbnailUrl;
22 | public bool IsPrivate => PlayersModule.GetOnlineApiUser(Id)?.location == "private";
23 | public bool IsInaccessible => false;
24 | public bool SupportsDesktop => false;
25 | public bool SupportsQuest => false;
26 | public StoredPlayer Model => myPlayer;
27 | public StoredFavorite? StoredFavorite => myFavorite;
28 |
29 | public Color? CornerIconColor
30 | {
31 | get
32 | {
33 | var onlineUser = PlayersModule.GetOnlineApiUser(Id);
34 | if (onlineUser == null)
35 | return Color.gray;
36 |
37 | switch (onlineUser.status)
38 | {
39 | case "join me":
40 | return new Color(0, 0.75f, 0.75f, 1);
41 | case "online":
42 | case "active":
43 | return new Color(0, 0.75f, 0, 1);
44 | case "ask me":
45 | return new Color(1f, 0.5f, 0, 1);
46 | case "busy":
47 | return new Color(0.75f, 0, 0, 1);
48 | default:
49 | return new Color(0.5f, 0.5f, 0, 1);
50 | }
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/FavCat/Adapters/DbWorldAdapter.cs:
--------------------------------------------------------------------------------
1 | using FavCat.CustomLists;
2 | using FavCat.Database.Stored;
3 | using UnityEngine;
4 | using VRC.Core;
5 |
6 | namespace FavCat.Adapters
7 | {
8 | public class DbWorldAdapter : IPickerElement, IStoredModelAdapter
9 | {
10 | private readonly StoredWorld myWorld;
11 | private readonly StoredFavorite? myFavorite;
12 |
13 | public DbWorldAdapter(StoredWorld world, StoredFavorite? favorite)
14 | {
15 | myWorld = world;
16 | myFavorite = favorite;
17 | }
18 |
19 | public string Id => myWorld.WorldId;
20 | public string Name => myWorld.Name;
21 | public string ImageUrl => myWorld.ThumbnailUrl;
22 | public bool IsPrivate => myWorld.ReleaseStatus != "public";
23 | public bool IsInaccessible => false;
24 |
25 | public bool SupportsDesktop => (myWorld.SupportedPlatforms & ApiModel.SupportedPlatforms.StandaloneWindows) != 0;
26 | public bool SupportsQuest => (myWorld.SupportedPlatforms & ApiModel.SupportedPlatforms.Android) != 0;
27 |
28 | public StoredWorld Model => myWorld;
29 | public StoredFavorite? StoredFavorite => myFavorite;
30 |
31 | public Color? CornerIconColor => null;
32 | }
33 | }
--------------------------------------------------------------------------------
/FavCat/Adapters/IStoredModelAdapter.cs:
--------------------------------------------------------------------------------
1 | using FavCat.Database.Stored;
2 |
3 | namespace FavCat.Adapters
4 | {
5 | public interface IStoredModelAdapter where T: class
6 | {
7 | T Model { get; }
8 | StoredFavorite? StoredFavorite { get; }
9 | }
10 | }
--------------------------------------------------------------------------------
/FavCat/AssetsHandler.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Reflection;
3 | using FavCat.CustomLists;
4 | using UnhollowerRuntimeLib;
5 | using UnityEngine;
6 |
7 | #nullable disable
8 |
9 | namespace FavCat
10 | {
11 | public static class AssetsHandler
12 | {
13 | private static AssetBundle myAssetBundle;
14 |
15 | public static GameObject ListPrefab;
16 | public static GameObject PickerPrefab;
17 |
18 | public static Sprite IconPC;
19 | public static Sprite IconQuest;
20 | public static Sprite IconUni;
21 |
22 | public static Sprite PreviewLoading;
23 | public static Sprite PreviewError;
24 |
25 | public static void Load()
26 | {
27 | T NoUnload(T obj) where T: Object
28 | {
29 | obj.hideFlags |= HideFlags.DontUnloadUnusedAsset;
30 | return obj;
31 | }
32 |
33 | using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("FavCat.extraui"))
34 | using (var tempStream = new MemoryStream((int) stream.Length))
35 | {
36 | stream.CopyTo(tempStream);
37 |
38 | myAssetBundle = AssetBundle.LoadFromMemory_Internal(tempStream.ToArray(), 0);
39 | myAssetBundle.hideFlags |= HideFlags.DontUnloadUnusedAsset;
40 | }
41 |
42 | var screensRoot = GameObject.Find("UserInterface/MenuContent/Screens").transform;
43 | ListPrefab = Object.Instantiate(myAssetBundle.LoadAsset_Internal("Assets/ExtraUi/CustomUiList.prefab", Il2CppType.Of()).Cast(), screensRoot, false);
44 | ListPrefab.SetActive(false);
45 |
46 | var pickerPoolRoot = new GameObject("CustomPickerPool");
47 | pickerPoolRoot.transform.SetParent(screensRoot);
48 |
49 | var pickerPrefab = Object.Instantiate(myAssetBundle.LoadAsset_Internal("Assets/ExtraUi/CustomPicker.prefab", Il2CppType.Of()).Cast(), screensRoot, false);
50 | pickerPrefab.SetActive(false);
51 | PickerPrefab = pickerPrefab;
52 |
53 | IconPC = NoUnload(myAssetBundle.LoadAsset("Assets/ExtraUI/EUI-IconPC.png", Il2CppType.Of()).Cast());
54 | IconQuest = NoUnload(myAssetBundle.LoadAsset("Assets/ExtraUI/EUI-IconQ.png", Il2CppType.Of()).Cast());
55 | IconUni = NoUnload(myAssetBundle.LoadAsset("Assets/ExtraUI/EUI-IconU.png", Il2CppType.Of()).Cast());
56 |
57 | PreviewLoading = NoUnload(myAssetBundle.LoadAsset("Assets/ExtraUI/EUI-LoadingPreview.png", Il2CppType.Of()).Cast());
58 | PreviewError = NoUnload(myAssetBundle.LoadAsset("Assets/ExtraUI/EUI-ErrorPreview.png", Il2CppType.Of()).Cast());
59 |
60 | new PickerPool(pickerPrefab, pickerPoolRoot);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/FavCat/CustomLists/IPickerElement.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace FavCat.CustomLists
4 | {
5 | public interface IPickerElement
6 | {
7 | string Id { get; }
8 | string Name { get; }
9 | string ImageUrl { get; }
10 |
11 | bool IsPrivate { get; }
12 | bool IsInaccessible { get; }
13 | bool SupportsDesktop { get; }
14 | bool SupportsQuest { get; }
15 |
16 | Color? CornerIconColor { get; }
17 | }
18 | }
--------------------------------------------------------------------------------
/FavCat/CustomLists/PickerPool.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace FavCat.CustomLists
5 | {
6 | public class PickerPool
7 | {
8 | private readonly GameObject myPickerPrefab;
9 | private readonly Transform myPoolRoot;
10 | public static PickerPool Instance { get; private set; }
11 |
12 | private readonly Stack myObjects = new Stack();
13 |
14 | private int myObjectsBorrowed;
15 |
16 | public PickerPool(GameObject pickerPrefab, GameObject poolRoot)
17 | {
18 | myPickerPrefab = pickerPrefab;
19 | myPoolRoot = poolRoot.transform;
20 |
21 | poolRoot.SetActive(false);
22 |
23 | Instance = this;
24 | }
25 |
26 | public GameObject Request()
27 | {
28 | myObjectsBorrowed++;
29 |
30 | if (myObjects.Count == 0)
31 | {
32 | var result = Object.Instantiate(myPickerPrefab);
33 | result.AddComponent();
34 | return result;
35 | }
36 |
37 | return myObjects.Pop();
38 | }
39 |
40 | public void Release(GameObject go)
41 | {
42 | myObjectsBorrowed--;
43 |
44 | /*if (myObjects.Count > myObjectsBorrowed + 100)
45 | {
46 | Object.Destroy(go);
47 | return;
48 | }*/ // todo: better shrinking?
49 |
50 | go.transform.SetParent(myPoolRoot, false);
51 | myObjects.Push(go);
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/FavCat/Database/DatabaseEntity.cs:
--------------------------------------------------------------------------------
1 | namespace FavCat.Database
2 | {
3 | public enum DatabaseEntity
4 | {
5 | Avatar,
6 | Player,
7 | World
8 | }
9 | }
--------------------------------------------------------------------------------
/FavCat/Database/LocalStoreDatabase.Avatar.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using FavCat.Database.Stored;
6 | using MelonLoader;
7 | using UIExpansionKit.API;
8 | using VRC.Core;
9 |
10 | namespace FavCat.Database
11 | {
12 | public partial class LocalStoreDatabase
13 | {
14 | internal void RunBackgroundAvatarSearch(string text, Action> callback)
15 | {
16 | MelonLogger.Msg($"Running local avatar search for text {text}");
17 | var ownerId = APIUser.CurrentUser.id;
18 | Task.Run(() => {
19 | var searchText = text.ToLowerInvariant();
20 | var list = myStoredAvatars
21 | .Find(stored =>
22 | (stored.Name.ToLower().Contains(searchText) ||
23 | stored.Description != null && stored.Description.ToLower().Contains(searchText) ||
24 | stored.AuthorName.ToLower().Contains(searchText))
25 | && (stored.ReleaseStatus == "public" || stored.AuthorId == ownerId))
26 | .ToList();
27 |
28 | callback(list);
29 | }).NoAwait();
30 | }
31 |
32 | internal void RunBackgroundAvatarSearchByUser(string userId, Action> callback)
33 | {
34 | MelonLogger.Msg($"Running local avatar search for user {userId}");
35 | var ownerId = APIUser.CurrentUser.id;
36 | Task.Run(() => {
37 | var list = myStoredAvatars.Find(stored =>
38 | stored.AuthorId == userId && (stored.ReleaseStatus == "public" || stored.AuthorId == ownerId))
39 | .ToList();
40 |
41 | callback(list);
42 | }).NoAwait();
43 | }
44 |
45 | private StoredAvatar? GetAvatar(string id)
46 | {
47 | return myStoredAvatars.FindById(id);
48 | }
49 |
50 | public void UpdateStoredAvatar(ApiAvatar avatar)
51 | {
52 | var id = avatar.id;
53 | if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(avatar.name)) return;
54 |
55 | var storedAvatar = new StoredAvatar
56 | {
57 | AvatarId = id,
58 | AuthorId = avatar.authorId,
59 | Name = avatar.name,
60 | Description = avatar.description,
61 | AuthorName = avatar.authorName,
62 | ThumbnailUrl = avatar.thumbnailImageUrl,
63 | ImageUrl = avatar.imageUrl,
64 | ReleaseStatus = avatar.releaseStatus,
65 | Platform = avatar.platform,
66 | CreatedAt = DateTime.FromBinary(avatar.created_at.ToBinary()),
67 | UpdatedAt = DateTime.FromBinary(avatar.updated_at.ToBinary()),
68 | SupportedPlatforms = avatar.supportedPlatforms
69 | };
70 |
71 | myUpdateThreadQueue.Enqueue(() =>
72 | {
73 | var preExisting = GetAvatar(id);
74 |
75 | if (preExisting != null)
76 | {
77 | if (preExisting.UpdatedAt > storedAvatar.UpdatedAt) return;
78 |
79 | if (avatar.assetUrl == null) storedAvatar.SupportedPlatforms = preExisting.SupportedPlatforms;
80 |
81 | storedAvatar.Description ??= preExisting.Description;
82 | }
83 |
84 | myStoredAvatars.Upsert(storedAvatar);
85 | });
86 | }
87 |
88 | public void CompletelyDeleteAvatar(string avatarId)
89 | {
90 | myStoredAvatars.Delete(avatarId);
91 | AvatarFavorites.DeleteFavoriteFromAllCategories(avatarId);
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/FavCat/Database/LocalStoreDatabase.Player.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using FavCat.Database.Stored;
6 | using MelonLoader;
7 | using UIExpansionKit.API;
8 | using VRC.Core;
9 |
10 | namespace FavCat.Database
11 | {
12 | public partial class LocalStoreDatabase
13 | {
14 | public void UpdateStoredPlayer(APIUser player)
15 | {
16 | if (string.IsNullOrEmpty(player.id) || string.IsNullOrEmpty(player.displayName))
17 | return;
18 |
19 | var storedPlayer = new StoredPlayer
20 | {
21 | PlayerId = player.id,
22 | Name = player.displayName,
23 | ThumbnailUrl = player.profilePicThumbnailImageUrl // already includes override/avatar check
24 | };
25 |
26 | myUpdateThreadQueue.Enqueue(() =>
27 | {
28 | myStoredPlayers.Upsert(storedPlayer);
29 | });
30 | }
31 |
32 | internal void RunBackgroundPlayerSearch(string text, Action> callback)
33 | {
34 | MelonLogger.Msg($"Running local player search for text {text}");
35 | Task.Run(() => {
36 | var searchText = text.ToLowerInvariant();
37 | var list = myStoredPlayers.Find(stored => stored.Name.Contains(searchText)).ToList();
38 |
39 | callback(list);
40 | }).NoAwait();
41 | }
42 |
43 | public void CompletelyDeletePlayer(string userId)
44 | {
45 | myStoredPlayers.Delete(userId);
46 | PlayerFavorites.DeleteFavoriteFromAllCategories(userId);
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/FavCat/Database/LocalStoreDatabase.World.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using FavCat.Database.Stored;
6 | using MelonLoader;
7 | using UIExpansionKit.API;
8 | using VRC.Core;
9 |
10 | namespace FavCat.Database
11 | {
12 | public partial class LocalStoreDatabase
13 | {
14 | internal void RunBackgroundWorldSearch(string text, Action> callback)
15 | {
16 | MelonLogger.Msg($"Running local world search for text {text}");
17 | Task.Run(() => {
18 | var searchText = text.ToLowerInvariant();
19 | var list = myStoredWorlds.Find(stored =>
20 | stored.Name.ToLower().Contains(searchText) ||
21 | stored.Description != null && stored.Description.ToLower().Contains(searchText) ||
22 | stored.AuthorName.ToLower().Contains(searchText)).ToList();
23 |
24 | callback(list);
25 | }).NoAwait();
26 | }
27 |
28 | public void UpdateStoredWorld(ApiWorld world)
29 | {
30 | var id = world.id;
31 | if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(world.name)) return;
32 |
33 | var storedWorld = new StoredWorld
34 | {
35 | WorldId = id,
36 | AuthorId = world.authorId,
37 | Name = world.name,
38 | Description = world.description,
39 | AuthorName = world.authorName,
40 | ThumbnailUrl = world.thumbnailImageUrl,
41 | ImageUrl = world.imageUrl,
42 | ReleaseStatus = world.releaseStatus,
43 | Platform = world.platform,
44 | Version = world.version,
45 | CreatedAt = DateTime.FromBinary(world.created_at.ToBinary()),
46 | UpdatedAt = DateTime.FromBinary(world.updated_at.ToBinary()),
47 | SupportedPlatforms = world.supportedPlatforms,
48 |
49 | Capacity = world.capacity,
50 | Tags = world.tags.ToArray(),
51 | };
52 |
53 | var hasNoAssetUrl = world.assetUrl == null;
54 |
55 | myUpdateThreadQueue.Enqueue(() =>
56 | {
57 | var preExisting = myStoredWorlds.FindById(id);
58 | if (preExisting != null)
59 | {
60 | if (hasNoAssetUrl) storedWorld.SupportedPlatforms = preExisting.SupportedPlatforms;
61 |
62 | storedWorld.Description ??= preExisting.Description;
63 | }
64 |
65 | myStoredWorlds.Upsert(storedWorld);
66 | });
67 | }
68 |
69 | public void CompletelyDeleteWorld(string worldId)
70 | {
71 | myStoredWorlds.Delete(worldId);
72 | WorldFavorites.DeleteFavoriteFromAllCategories(worldId);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/INamedStoredObject.cs:
--------------------------------------------------------------------------------
1 | namespace FavCat.Database.Stored
2 | {
3 | public interface INamedStoredObject
4 | {
5 | string Name { get; }
6 | string? AuthorName { get; }
7 | }
8 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredAvatar.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LiteDB;
3 | using VRC.Core;
4 |
5 | #nullable disable
6 |
7 | namespace FavCat.Database.Stored
8 | {
9 | public class StoredAvatar: INamedStoredObject
10 | {
11 | [BsonId] public string AvatarId { get; set; }
12 | public string Name { get; set; }
13 | public string? Description { get; set; }
14 | public string AuthorId { get; set; }
15 | public string AuthorName { get; set; }
16 | public string ImageUrl { get; set; }
17 | public string ThumbnailUrl { get; set; }
18 | public string ReleaseStatus { get; set; }
19 | public string Platform { get; set; }
20 | public DateTime CreatedAt { get; set; }
21 | public DateTime UpdatedAt { get; set; }
22 | public ApiModel.SupportedPlatforms SupportedPlatforms { get; set; }
23 | }
24 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredCategory.cs:
--------------------------------------------------------------------------------
1 | using LiteDB;
2 |
3 | #nullable disable
4 |
5 | namespace FavCat.Database.Stored
6 | {
7 | public class StoredCategory
8 | {
9 | [BsonId] public string CategoryName { get; set; }
10 | public string SortType { get; set; }
11 | public int VisibleRows { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredCategoryOrder.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using LiteDB;
3 |
4 | namespace FavCat.Database.Stored
5 | {
6 | public struct CategoryInfo
7 | {
8 | public string Name { get; set; }
9 | public bool IsExternal { get; set; }
10 | }
11 |
12 | public class StoredCategoryOrder
13 | {
14 | [BsonId] public DatabaseEntity EntityType { get; set; }
15 | public List Order { get; set; } = new List();
16 | public List DefaultListsToHide { get; set; } = new List();
17 | }
18 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredFavorite.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LiteDB;
3 |
4 | namespace FavCat.Database.Stored
5 | {
6 | public class StoredFavorite
7 | {
8 | [BsonId] public int FavoriteId { get; set; }
9 |
10 | public string ObjectId { get; set; }
11 | public string Category { get; set; }
12 | public DateTime AddedOn { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredImageInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LiteDB;
3 |
4 | #nullable disable
5 |
6 | namespace FavCat.Database.Stored
7 | {
8 | public class StoredImageInfo
9 | {
10 | [BsonId] public string Id { get; set; }
11 | public DateTime LastAccessed { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredPlayer.cs:
--------------------------------------------------------------------------------
1 | using LiteDB;
2 |
3 | #nullable disable
4 |
5 | namespace FavCat.Database.Stored
6 | {
7 | public class StoredPlayer : INamedStoredObject
8 | {
9 | [BsonId] public string PlayerId { get; set; }
10 | public string Name { get; set; }
11 | public string ThumbnailUrl { get; set; }
12 |
13 | [BsonIgnore] public string? AuthorName => null;
14 | }
15 | }
--------------------------------------------------------------------------------
/FavCat/Database/Stored/StoredWorld.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LiteDB;
3 | using VRC.Core;
4 |
5 | #nullable disable
6 |
7 | namespace FavCat.Database.Stored
8 | {
9 | public class StoredWorld : INamedStoredObject
10 | {
11 | [BsonId] public string WorldId { get; set; }
12 | public string Name { get; set; }
13 | public string? Description { get; set; }
14 | public string AuthorId { get; set; }
15 | public string AuthorName { get; set; }
16 | public string ImageUrl { get; set; }
17 | public string ThumbnailUrl { get; set; }
18 | public string ReleaseStatus { get; set; }
19 | public string Platform { get; set; }
20 | public DateTime CreatedAt { get; set; }
21 | public DateTime UpdatedAt { get; set; }
22 | public int Version { get; set; }
23 | public ApiModel.SupportedPlatforms SupportedPlatforms { get; set; }
24 |
25 | public string[] Tags { get; set; }
26 | public int Capacity { get; set; }
27 | }
28 | }
--------------------------------------------------------------------------------
/FavCat/ExportProcessor.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using System.Text.RegularExpressions;
4 | using System.Threading.Tasks;
5 | using FavCat.Database;
6 | using FavCat.Database.Stored;
7 |
8 | namespace FavCat
9 | {
10 | public static class ExportProcessor
11 | {
12 | public static bool IsExportingFavorites;
13 | public static int TotalCategories;
14 | public static int ProcessedCategories;
15 | public static async Task DoExportFavorites(DatabaseFavoriteHandler favorites) where T:class, INamedStoredObject
16 | {
17 | IsExportingFavorites = true;
18 | ProcessedCategories = 0;
19 | TotalCategories = 1;
20 | try
21 | {
22 | await Task.Delay(100);
23 |
24 | var exportDir = "UserData/FavCatExport";
25 | if (!Directory.Exists(exportDir))
26 | Directory.CreateDirectory(exportDir);
27 |
28 | var storedCategories = favorites.GetCategories().ToList();
29 | TotalCategories = storedCategories.Count;
30 | foreach (var category in storedCategories)
31 | {
32 | var fileName = favorites.EntityType + "-" + SanitizeForFileName(category.CategoryName) + ".txt";
33 | using var writer = new StreamWriter(exportDir + "/" + fileName);
34 | foreach (var listFavorite in favorites.ListFavorites(category.CategoryName).ToList())
35 | {
36 | var authorNameSuffix = listFavorite.Item2.AuthorName;
37 | authorNameSuffix = authorNameSuffix == null ? "" : $" by {authorNameSuffix}";
38 | await writer.WriteLineAsync(listFavorite.Item1.ObjectId + " " + listFavorite.Item2.Name + authorNameSuffix)
39 | .ConfigureAwait(false);
40 | }
41 |
42 | ProcessedCategories++;
43 | }
44 | }
45 | finally
46 | {
47 | IsExportingFavorites = false;
48 | }
49 | }
50 |
51 | private static readonly Regex ourBadFileChars = new Regex("[\\/+:*?<>\"|]");
52 | private static string SanitizeForFileName(string s)
53 | {
54 | return ourBadFileChars.Replace(s, "");
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/FavCat/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.RegularExpressions;
3 | using UnityEngine;
4 |
5 | namespace FavCat
6 | {
7 | public static class Extensions
8 | {
9 | internal static string StripParenthesis(this string s)
10 | {
11 | return Regex.Replace(s, "\\s*\\([0-9\\s]*\\)\\s*", "", RegexOptions.None);
12 | }
13 |
14 | internal static V GetOrDefault(this IDictionary dict, K key, V defaultValue)
15 | {
16 | return dict.TryGetValue(key, out var value) ? value : defaultValue;
17 | }
18 |
19 | internal static void SetSiblingIndex2(this Transform t, int index)
20 | {
21 | var currentIndex = t.GetSiblingIndex();
22 | if(index < currentIndex || index == t.parent.childCount)
23 | t.SetSiblingIndex(index);
24 | else if (index == currentIndex)
25 | return;
26 | else // this kind of move needs to account for the spot vacated by this transform
27 | t.SetSiblingIndex(index - 1);
28 | }
29 |
30 | internal static T GetOrAddComponent(this Component c) where T : Component
31 | {
32 | var existing = c.GetComponent();
33 | if (existing) return existing;
34 | return c.gameObject.AddComponent();
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FavCat/FavCat.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | net48
5 | latest
6 | true
7 | enable
8 | False
9 | 1.1.15.0
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | $(OutputPath)
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/FavCat/FavCat.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | False
--------------------------------------------------------------------------------
/FavCat/FavCatSettings.cs:
--------------------------------------------------------------------------------
1 | using MelonLoader;
2 | using UIExpansionKit.API;
3 |
4 | #nullable disable
5 |
6 | namespace FavCat
7 | {
8 | public static class FavCatSettings
9 | {
10 | private const string SettingsCategory = "FavCat";
11 | internal static MelonPreferences_Entry DatabasePath;
12 | internal static MelonPreferences_Entry ImageCachePath;
13 | private static MelonPreferences_Entry AnnoyingMessageSeen;
14 |
15 | internal static MelonPreferences_Entry EnableAvatarFavs;
16 | internal static MelonPreferences_Entry EnableWorldFavs;
17 | internal static MelonPreferences_Entry EnablePlayerFavs;
18 |
19 | private static MelonPreferences_Entry ImageCacheMode;
20 | private static MelonPreferences_Entry ImageCacheMaxSize;
21 | internal static MelonPreferences_Entry HidePopupAfterFav;
22 |
23 | internal static MelonPreferences_Entry MakeClickSounds;
24 | internal static MelonPreferences_Entry AvatarSearchMode;
25 | internal static MelonPreferences_Entry SortPlayersByOnline;
26 |
27 | internal static MelonPreferences_Entry SortPlayersByJoinable;
28 |
29 | internal static void RegisterSettings()
30 | {
31 | var avatarSearchModeName = "AvatarSearchMode";
32 |
33 | var category = MelonPreferences.CreateCategory(SettingsCategory, "FavCat");
34 |
35 | DatabasePath = category.CreateEntry("DatabasePath", "./UserData", "Database directory path", is_hidden: true, dont_save_default: false);
36 | ImageCachePath = category.CreateEntry("ImageCachePath", "./UserData", "Image cache directory path", is_hidden: true, dont_save_default: false);
37 | AnnoyingMessageSeen = category.CreateEntry("AnnoyingMessageSeen", "", is_hidden: true);
38 |
39 | EnableAvatarFavs = category.CreateEntry("EnableAvatarFavs", true, "Enable avatar favorites (restart required)");
40 | EnableWorldFavs = category.CreateEntry("EnableWorldFavs", true, "Enable world favorites (restart required)");
41 | EnablePlayerFavs = category.CreateEntry("EnablePlayerFavs", true, "Enable player favorites (restart required)");
42 |
43 | ImageCacheMode = category.CreateEntry("ImageCachingMode", "full", "Image caching mode");
44 | ImageCacheMaxSize = category.CreateEntry("ImageCacheMaxSize", 4096, "Image cache max size (MB)");
45 | HidePopupAfterFav = category.CreateEntry("HidePopupAfterFav", true, "Hide favorite popup after (un)favoriting a world or a player");
46 |
47 | MakeClickSounds = category.CreateEntry("MakeClickSounds", true, "Click sounds");
48 | AvatarSearchMode = category.CreateEntry(avatarSearchModeName, "select", "Avatar search result action");
49 | SortPlayersByOnline = category.CreateEntry(nameof(SortPlayersByOnline), true, "Show offline players at the end of the list");
50 |
51 | SortPlayersByJoinable = category.CreateEntry(nameof(SortPlayersByJoinable), true, "Show players in private instances at the end of the list");
52 |
53 | ExpansionKitApi.RegisterSettingAsStringEnum(SettingsCategory, "ImageCachingMode", new []{("full", "Full local image cache (fastest, safest)"), ("fast", "Fast, use more RAM"), ("builtin", "Preserve RAM, more API requests")});
54 | ExpansionKitApi.RegisterSettingAsStringEnum(SettingsCategory, avatarSearchModeName, new []{("select", "Select avatar"), ("author", "Show avatar author (safer)")});
55 | }
56 |
57 | public static bool UseLocalImageCache => ImageCacheMode.Value == "full";
58 | public static bool CacheImagesInMemory => ImageCacheMode.Value == "fast";
59 | public static long MaxCacheSizeBytes => ImageCacheMaxSize.Value * 1024L * 1024L;
60 |
61 | public static string DontShowAnnoyingMessage
62 | {
63 | get => AnnoyingMessageSeen.Value;
64 | set
65 | {
66 | AnnoyingMessageSeen.Value = value;
67 | MelonPreferences.Save();
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/FavCat/ReFetchFavoritesProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text.RegularExpressions;
6 | using System.Threading.Tasks;
7 | using FavCat.Database.Stored;
8 | using LiteDB;
9 | using MelonLoader;
10 | using UIExpansionKit.API;
11 | using VRC.Core;
12 | using Random = UnityEngine.Random;
13 |
14 | namespace FavCat
15 | {
16 | public static class ReFetchFavoritesProcessor
17 | {
18 | public static bool ImportRunning { get; private set; }
19 |
20 | public static string ImportStatusOuter { get; private set; } = "Not importing";
21 | public static string ImportStatusInner { get; private set; } = "";
22 |
23 | public static async Task ReFetchFavorites()
24 | {
25 | ImportRunning = true;
26 | ImportStatusOuter = "Re-fetch running...";
27 |
28 | var database = FavCatMod.Database;
29 |
30 | ImportStatusOuter = "Fetching worlds...";
31 | var worldFavs = database.WorldFavorites.myStoredFavorites.FindAll().ToList();
32 | for (var i = 0; i < worldFavs.Count; i++)
33 | {
34 | ImportStatusInner = i + "/" + worldFavs.Count;
35 |
36 | var storedFavorite = worldFavs[i];
37 | if (database.myStoredWorlds.FindById(storedFavorite.ObjectId) != null) continue;
38 |
39 | await TaskUtilities.YieldToMainThread();
40 |
41 | new ApiWorld {id = storedFavorite.ObjectId}.Fetch(null, new Action(c =>
42 | {
43 | if (c.Code == 404) database.CompletelyDeleteWorld(storedFavorite.ObjectId);
44 | }));
45 |
46 | await Task.Delay(TimeSpan.FromSeconds(5f + Random.Range(0f, 5f))).ConfigureAwait(false);
47 | }
48 |
49 | ImportStatusOuter = "Fetching players...";
50 | var playerFavs = database.PlayerFavorites.myStoredFavorites.FindAll().ToList();
51 | for (var i = 0; i < playerFavs.Count; i++)
52 | {
53 | ImportStatusInner = i + "/" + playerFavs.Count;
54 |
55 | var storedFavorite = playerFavs[i];
56 | if (database.myStoredPlayers.FindById(storedFavorite.ObjectId) != null) continue;
57 |
58 | await TaskUtilities.YieldToMainThread();
59 |
60 | new APIUser {id = storedFavorite.ObjectId}.Fetch(new Action(c =>
61 | {
62 | if (c.Code == 404) database.CompletelyDeletePlayer(storedFavorite.ObjectId);
63 | }));
64 |
65 | await Task.Delay(TimeSpan.FromSeconds(5f + Random.Range(0f, 5f))).ConfigureAwait(false);
66 | }
67 |
68 | ImportRunning = false;
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/FavCat/ScanningReflectionCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using UnhollowerRuntimeLib.XrefScans;
5 | using VRC;
6 | using VRC.Core;
7 |
8 | namespace FavCat
9 | {
10 | public static class ScanningReflectionCache
11 | {
12 | private delegate void DisplayErrorAvatarDelegate(SimpleAvatarPedestal @this);
13 | private delegate void PedestalRefreshDelegate(SimpleAvatarPedestal @this, ApiAvatar avatar);
14 |
15 | private static DisplayErrorAvatarDelegate? ourDisplayErrorAvatarDelegate;
16 | private static PedestalRefreshDelegate? ourPedestalRefreshDelegate;
17 |
18 | private static Action? ourShowWorldInfoPageDelegate;
19 |
20 | public static void DisplayWorldInfoPage(ApiWorld world, ApiWorldInstance? instance, bool hasInstanceId, APIUser? user)
21 | {
22 | if (ourShowWorldInfoPageDelegate == null)
23 | {
24 | var target = typeof(UiWorldList)
25 | .GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static).Single(it =>
26 | it.Name.StartsWith("Method_Public_Static_Void_ApiWorld_ApiWorldInstance_Boolean_APIUser_") &&
27 | XrefScanner.XrefScan(it).Any(jt =>
28 | jt.Type == XrefType.Global && jt.ReadAsObject()?.ToString() ==
29 | "UserInterface/MenuContent/Screens/WorldInfo"));
30 |
31 | ourShowWorldInfoPageDelegate = (Action) Delegate.CreateDelegate(typeof(Action), target);
32 | }
33 |
34 | ourShowWorldInfoPageDelegate(world, instance, hasInstanceId, user);
35 | }
36 |
37 | public static void DisplayErrorAvatar(this SimpleAvatarPedestal @this)
38 | {
39 | if (ourDisplayErrorAvatarDelegate == null)
40 | {
41 | var target = typeof(SimpleAvatarPedestal)
42 | .GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Single(
43 | it =>
44 | {
45 | if (it.ReturnType != typeof(void) || it.Name.Contains("_PDM_")) return false;
46 | var parameters = it.GetParameters();
47 | if (parameters.Length != 0)
48 | return false;
49 |
50 | return XrefScanner.XrefScan(it).Any(jt =>
51 | jt.Type == XrefType.Global && jt.ReadAsObject()?.ToString() == "local");
52 | });
53 |
54 | ourDisplayErrorAvatarDelegate =
55 | (DisplayErrorAvatarDelegate) Delegate.CreateDelegate(typeof(DisplayErrorAvatarDelegate), target);
56 | }
57 |
58 | ourDisplayErrorAvatarDelegate(@this);
59 | }
60 |
61 | public static void Refresh(this SimpleAvatarPedestal pedestal, ApiAvatar avatar)
62 | {
63 | if (ourPedestalRefreshDelegate == null)
64 | {
65 | var target = typeof(SimpleAvatarPedestal)
66 | .GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Single(
67 | it =>
68 | {
69 | if (it.ReturnType != typeof(void)) return false;
70 | var parameters = it.GetParameters();
71 | if (parameters.Length != 1 || parameters[0].ParameterType != typeof(ApiAvatar))
72 | return false;
73 |
74 | var strings = XrefScanner.XrefScan(it)
75 | .Select(jt => jt.Type == XrefType.Global ? jt.ReadAsObject()?.ToString() : null)
76 | .Where(jt => jt != null).ToHashSet();
77 | return strings.Contains("Refreshing with : ");
78 | });
79 |
80 | ourPedestalRefreshDelegate =
81 | (PedestalRefreshDelegate) Delegate.CreateDelegate(typeof(PedestalRefreshDelegate), target);
82 | }
83 |
84 | ourPedestalRefreshDelegate(pedestal, avatar);
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/FavCat/extraui:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/FavCat/extraui
--------------------------------------------------------------------------------
/Finitizer/Finitizer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | 1.3.2.0
7 | true
8 | latest
9 | true
10 | none
11 | false
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/FriendsPlusHome/FriendsPlusHome.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | 1.1.2.0
7 | FriendsPlusHome
8 |
9 |
10 |
11 | none
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/IKTweaks/AnimationsHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using UnhollowerBaseLib;
4 | using UnityEngine;
5 |
6 | namespace IKTweaks
7 | {
8 | internal class AnimationsHandler
9 | {
10 | private readonly Il2CppStructArray myMuscles = new(HumanTrait.MuscleCount);
11 | private readonly HumanPoseHandler myPoseHandler;
12 | private readonly Transform myHips;
13 | private readonly CachedSolver mySolver;
14 |
15 | public AnimationsHandler(HumanPoseHandler poseHandler, Transform hips, in CachedSolver solver)
16 | {
17 | myPoseHandler = poseHandler;
18 | myHips = hips;
19 | mySolver = solver;
20 | }
21 |
22 | internal void ResetBonePositions(bool onlySpine, bool hasLegTargets)
23 | {
24 | if (mySolver.Solver.IKPositionWeight < 0.9f) return;
25 |
26 | myHips.get_position_Injected(out var hipPos);
27 | myHips.get_rotation_Injected(out var hipRot);
28 |
29 | myPoseHandler.GetHumanPose(out var bodyPos, out var bodyRot, myMuscles);
30 |
31 | for (var i = 0; i < myMuscles.Count; i++)
32 | {
33 | var currentMask = ourBoneResetMasks[i];
34 | if (onlySpine && currentMask != BoneResetMask.Spine) continue;
35 | if (!hasLegTargets && (currentMask == BoneResetMask.LeftLeg || currentMask == BoneResetMask.RightLeg)) continue;
36 |
37 | if (IkTweaksSettings.IgnoreAnimationsModeParsed == IgnoreAnimationsMode.All)
38 | {
39 | myMuscles[i] *= currentMask == BoneResetMask.Never ? 1 : 0;
40 | continue;
41 | }
42 |
43 | switch (currentMask)
44 | {
45 | case BoneResetMask.Never:
46 | break;
47 | case BoneResetMask.Spine:
48 | myMuscles[i] *= 1 - mySolver.Spine.pelvisPositionWeight;
49 | break;
50 | case BoneResetMask.LeftArm:
51 | myMuscles[i] *= 1 - mySolver.LeftArm.positionWeight;
52 | break;
53 | case BoneResetMask.RightArm:
54 | myMuscles[i] *= 1 - mySolver.RightArm.positionWeight;
55 | break;
56 | case BoneResetMask.LeftLeg:
57 | myMuscles[i] *= 1 - mySolver.LeftLeg.positionWeight;
58 | break;
59 | case BoneResetMask.RightLeg:
60 | myMuscles[i] *= 1 - mySolver.RightLeg.positionWeight;
61 | break;
62 | default:
63 | throw new ArgumentOutOfRangeException();
64 | }
65 | }
66 |
67 | myPoseHandler.SetHumanPose(ref bodyPos, ref bodyRot, myMuscles);
68 |
69 | myHips.position = hipPos;
70 | myHips.rotation = hipRot;
71 | }
72 |
73 |
74 | private enum BoneResetMask
75 | {
76 | Never,
77 | Spine,
78 | LeftArm,
79 | RightArm,
80 | LeftLeg,
81 | RightLeg,
82 | }
83 |
84 | private static readonly string[] ourNeverBones = { "Index", "Thumb", "Middle", "Ring", "Little", "Jaw", "Eye" };
85 | private static readonly string[] ourArmBones = { "Arm", "Forearm", "Hand", "Shoulder" };
86 | private static readonly string[] ourLegBones = { "Leg", "Foot", "Toes" };
87 |
88 | private static BoneResetMask JudgeBone(string name)
89 | {
90 | if (ourNeverBones.Any(name.Contains))
91 | return BoneResetMask.Never;
92 |
93 | if (ourArmBones.Any(name.Contains))
94 | {
95 | return name.Contains("Left") ? BoneResetMask.LeftArm : BoneResetMask.RightArm;
96 | }
97 |
98 | if (ourLegBones.Any(name.Contains))
99 | return name.Contains("Left") ? BoneResetMask.LeftLeg : BoneResetMask.RightLeg;
100 |
101 | return BoneResetMask.Spine;
102 | }
103 |
104 | private static readonly BoneResetMask[] ourBoneResetMasks = HumanTrait.MuscleName.Select(JudgeBone).ToArray();
105 | }
106 | }
--------------------------------------------------------------------------------
/IKTweaks/CachedSolver.cs:
--------------------------------------------------------------------------------
1 | using RootMotion.FinalIK;
2 |
3 | namespace IKTweaks
4 | {
5 | public struct CachedSolver
6 | {
7 | public readonly IKSolverVR Solver;
8 | public readonly IKSolverVR.Spine Spine;
9 | public readonly IKSolverVR.Leg LeftLeg;
10 | public readonly IKSolverVR.Leg RightLeg;
11 | public readonly IKSolverVR.Arm LeftArm;
12 | public readonly IKSolverVR.Arm RightArm;
13 | public readonly IKSolverVR.Locomotion Locomotion;
14 |
15 | public CachedSolver(IKSolverVR solver)
16 | {
17 | Solver = solver;
18 | Spine = solver.spine;
19 | LeftArm = solver.leftArm;
20 | LeftLeg = solver.leftLeg;
21 | RightArm = solver.rightArm;
22 | RightLeg = solver.rightLeg;
23 | Locomotion = solver.locomotion;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/IKTweaks/HandOffsetsManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using VRC.SDKBase;
4 |
5 | namespace IKTweaks
6 | {
7 | public class HandOffsetsManager: IDisposable
8 | {
9 | private readonly Transform myLeftHandOffset;
10 | private readonly Transform myRightHandOffset;
11 |
12 | private readonly float myOriginalScale;
13 |
14 | public HandOffsetsManager(VRCVrIkController controller)
15 | {
16 | var ikControllerParent = controller.field_Private_IkController_0.transform;
17 |
18 | var leftEffector = ikControllerParent.Find("LeftEffector");
19 | var rightEffector = ikControllerParent.Find("RightEffector");
20 |
21 | myLeftHandOffset = MakeTarget(leftEffector);
22 | myRightHandOffset = MakeTarget(rightEffector);
23 |
24 | var vrik = controller.field_Private_VRIK_0;
25 | vrik.solver.leftArm.target = myLeftHandOffset;
26 | vrik.solver.rightArm.target = myRightHandOffset;
27 |
28 | myOriginalScale = vrik.GetComponent().ViewPosition.y;
29 |
30 | UpdatePositionOffset(Vector3.zero, IkTweaksSettings.HandPositionOffset.Value);
31 | UpdateRotationOffset(Vector3.zero, IkTweaksSettings.HandAngleOffset.Value);
32 | }
33 |
34 | internal void UpdatePositionOffset(Vector3 _, Vector3 newOffset)
35 | {
36 | newOffset *= myOriginalScale;
37 |
38 | myLeftHandOffset.localPosition = newOffset;
39 | myRightHandOffset.localPosition = new Vector3 { x = -newOffset.x, y = newOffset.y, z = newOffset.z };
40 | }
41 |
42 | internal void UpdateRotationOffset(Vector3 _, Vector3 newOffset)
43 | {
44 | myLeftHandOffset.localEulerAngles = newOffset;
45 | myRightHandOffset.localEulerAngles = new Vector3 { x = newOffset.x, y = -newOffset.y, z = -newOffset.z };
46 | }
47 |
48 | private static Transform MakeTarget(Transform parent)
49 | {
50 | var newXf = new GameObject("IktOffset").transform;
51 | newXf.SetParent(parent, false);
52 | return newXf;
53 | }
54 |
55 | public void Dispose()
56 | {
57 | if (myLeftHandOffset != null) UnityEngine.Object.Destroy(myLeftHandOffset.gameObject);
58 | if (myRightHandOffset != null) UnityEngine.Object.Destroy(myRightHandOffset.gameObject);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/IKTweaks/IKTweaks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net48
4 | true
5 | latest
6 | true
7 | 2.0.0
8 | true
9 |
10 |
11 | none
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/ILRepack/ILRepack.Lib.MSBuild.Task.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/ILRepack/ILRepack.Lib.MSBuild.Task.dll
--------------------------------------------------------------------------------
/ILRepack/ILRepack.Lib.MSBuild.Task.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | $(ProjectDir)ILRepack.targets
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 |
33 |
34 |
35 |
36 |
37 | $([System.IO.Directory]::GetFiles("%(Directories.Identity)", "*", System.IO.SearchOption.AllDirectories).get_Length())
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ILRepack/ILRepack.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/ILRepack/ILRepack.dll
--------------------------------------------------------------------------------
/IntegrityCheckGenerator/IntegrityCheckGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/IntegrityCheckWeaver/DummyThree.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Mono.Cecil;
4 | using Mono.Cecil.Cil;
5 |
6 | namespace IntegrityCheckWeaver
7 | {
8 | public static class DummyThree
9 | {
10 | public static byte[] ProduceDummyThree()
11 | {
12 | var assemblyName = new AssemblyNameDefinition(Utils.CompletelyRandomString(), new Version(1, 0, 0));
13 | var assembly = AssemblyDefinition.CreateAssembly(assemblyName, assemblyName.Name + ".dll", ModuleKind.Dll);
14 |
15 | var moduleType = assembly.MainModule.GetType("");
16 |
17 | void AddFunnyDelegate()
18 | {
19 | var dgType = new TypeDefinition("", Utils.CompletelyRandomString(), TypeAttributes.Sealed, assembly.MainModule.ImportReference(typeof(MulticastDelegate)));
20 | var invokeMethod = new MethodDefinition("Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot |
21 | MethodAttributes.Public, assembly.MainModule.ImportReference(typeof(void)));
22 | invokeMethod.ImplAttributes = MethodImplAttributes.CodeTypeMask;
23 | dgType.Methods.Add(invokeMethod);
24 |
25 | assembly.MainModule.Types.Add(dgType);
26 |
27 | dgType.Methods.Add(MakeCctor(assembly));
28 |
29 | // Try triggering type loads
30 | moduleType.Fields.Add(new FieldDefinition(Utils.CompletelyRandomString(), FieldAttributes.Private | FieldAttributes.Static, dgType));
31 | dgType.Fields.Add(new FieldDefinition(Utils.CompletelyRandomString(), FieldAttributes.Private | FieldAttributes.Static, dgType));
32 | }
33 |
34 | for (var i = 0; i < Utils.RandomInt(5, 30); i++)
35 | AddFunnyDelegate();
36 |
37 | moduleType.Methods.Add(MakeCctor(assembly));
38 |
39 | var memoryStream = new MemoryStream();
40 |
41 | assembly.Write(memoryStream);
42 |
43 | return memoryStream.ToArray();
44 | }
45 |
46 | private static MethodDefinition MakeCctor(AssemblyDefinition assembly)
47 | {
48 | var voidReference = assembly.MainModule.ImportReference(typeof(void));
49 | var cctorMethod = new MethodDefinition(".cctor", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.SpecialName |
50 | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName, voidReference);
51 | var cctorBuilder = cctorMethod.Body.GetILProcessor();
52 |
53 | cctorBuilder.Emit(OpCodes.Ldc_I4_1);
54 | cctorBuilder.Emit(OpCodes.Conv_I);
55 | cctorBuilder.Emit(OpCodes.Calli, new CallSite(voidReference));
56 | cctorBuilder.Emit(OpCodes.Ret);
57 |
58 | return cctorMethod;
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/IntegrityCheckWeaver/IntegrityCheckWeaver.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net472
6 | net6.0;net472
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/IntegrityCheckWeaver/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using Mono.Cecil;
6 | using Mono.Cecil.Cil;
7 |
8 | namespace IntegrityCheckWeaver
9 | {
10 | static class Program
11 | {
12 | ///
13 | /// Why, you may ask?
14 | /// Because certain individuals with incredibly loose morals are hell-bent on making the game worse for everyone
15 | ///
16 | static int Main(string[] args)
17 | {
18 | if (args.Length <= 0 || !File.Exists(args[0]))
19 | {
20 | Console.Error.WriteLine("Bad arguments");
21 | return 1;
22 | }
23 |
24 | using var assembly = AssemblyDefinition.ReadAssembly(new FileStream(args[0], FileMode.Open, FileAccess.ReadWrite));
25 | var modType = assembly.MainModule.Types.SingleOrDefault(it => it.BaseType?.Name == "MelonMod");
26 |
27 | if (modType == null)
28 | {
29 | Console.Error.WriteLine("Required types not found");
30 | return 1;
31 | }
32 |
33 | var dummyOneResource = DummyThree.ProduceDummyThree();
34 | var dummyOneName = Utils.CompletelyRandomString() + ".dll";
35 | assembly.MainModule.Resources.Add(new EmbeddedResource(dummyOneName, ManifestResourceAttributes.Private, dummyOneResource));
36 |
37 | var dummyThreeResource = DummyThree.ProduceDummyThree();
38 | var dummyThreeName = Utils.CompletelyRandomString() + ".dll";
39 | assembly.MainModule.Resources.Add(new EmbeddedResource(dummyThreeName, ManifestResourceAttributes.Private, dummyThreeResource));
40 |
41 | var dummyTwoName = Utils.CompletelyRandomString() + ".dll";
42 |
43 | assembly.MainModule.Resources.Remove(assembly.MainModule.Resources.Single(it => it.Name.EndsWith("_dummy_.dll"))); // is replaced
44 | assembly.MainModule.Resources.Single(it => it.Name.EndsWith("_dummy2_.dll")).Name = dummyTwoName;
45 |
46 | var methodRenameMap = CleanMethods(modType);
47 |
48 | foreach (var method in modType.Methods)
49 | foreach (var instr in method.Body.Instructions)
50 | if (instr.OpCode == OpCodes.Ldstr)
51 | {
52 | var value = (string)instr.Operand;
53 | instr.Operand = value switch
54 | {
55 | "_dummy_.dll" => dummyOneName,
56 | "_dummy2_.dll" => dummyTwoName,
57 | "_dummy3_.dll" => dummyThreeName,
58 | _ => methodRenameMap.TryGetValue(value, out var renamed) ? renamed : value
59 | };
60 | }
61 |
62 | assembly.Write();
63 |
64 | return 0;
65 | }
66 |
67 | private static readonly HashSet ourNamesToRename = new()
68 | {
69 | "CheckA",
70 | "CheckB",
71 | "CheckC",
72 | "PatchTest",
73 | "ReturnFalse",
74 | "CheckWasSuccessful",
75 | "MustStayFalse",
76 | "MustStayTrue",
77 | "RanCheck3",
78 | "CheckDummyThree",
79 | "ourAnnoyingMessages"
80 | };
81 |
82 | private static Dictionary CleanMethods(TypeDefinition type)
83 | {
84 | var result = new Dictionary();
85 |
86 | foreach (var method in type.Methods)
87 | if (ourNamesToRename.Contains(method.Name))
88 | {
89 | var newName = Utils.CompletelyRandomString();
90 | result[method.Name] = newName;
91 | method.Name = newName;
92 | }
93 |
94 | foreach (var property in type.Properties)
95 | if (ourNamesToRename.Contains(property.Name))
96 | property.Name = Utils.CompletelyRandomString();
97 |
98 | foreach (var field in type.Fields)
99 | if (ourNamesToRename.Contains(field.Name))
100 | field.Name = Utils.CompletelyRandomString();
101 |
102 | return result;
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/IntegrityCheckWeaver/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace IntegrityCheckWeaver
5 | {
6 | public static class Utils
7 | {
8 | private static readonly Random ourRandom = new(DateTime.Now.GetHashCode());
9 | private static readonly (char, int)[] ourFrequencyTable = {
10 | ('E', 21912), ('T', 16587), ('A', 14810), ('O', 14003), ('I', 13318), ('N', 12666), ('S', 11450),
11 | ('R', 10977), ('H', 10795), ('D', 7874), ('L', 7253), ('U', 5246), ('C', 4943), ('M', 4761), ('F', 4200),
12 | ('Y', 3853), ('W', 3819), ('G', 3693), ('P', 3316), ('B', 2715), ('V', 2019), ('K', 1257), ('X', 315),
13 | ('Q', 205), ('J', 188), ('Z', 128),
14 | };
15 |
16 | private static readonly int ourFrequencyTableSum = ourFrequencyTable.Sum(it => it.Item2);
17 |
18 | private static char RandomLetter()
19 | {
20 | var v = ourRandom.Next(0, ourFrequencyTableSum);
21 | var i = 0;
22 | while (v > ourFrequencyTable[i].Item2)
23 | {
24 | v -= ourFrequencyTable[i].Item2;
25 | i++;
26 | }
27 |
28 | return char.ToLowerInvariant(ourFrequencyTable[i].Item1);
29 | }
30 |
31 | internal static string CompletelyRandomString()
32 | {
33 | var length = ourRandom.Next(5, 21);
34 | var chars = new char[length];
35 |
36 | for (var i = 0; i < length; i++)
37 | {
38 | chars[i] = RandomLetter();
39 | if (ourRandom.Next(0, i + 1) == 0)
40 | chars[i] = char.ToUpperInvariant(chars[i]);
41 | }
42 |
43 | return new string(chars);
44 | }
45 |
46 | internal static int RandomInt(int from, int to)
47 | {
48 | return ourRandom.Next(from, to);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/JoinNotifier/JoinNotifier.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net48
4 | true
5 | true
6 | true
7 | JoinNotifier
8 |
9 |
10 | none
11 | false
12 | 1.0.7
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/JoinNotifier/NetworkManagerHooks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MelonLoader;
3 | using VRC;
4 | using VRC.Core;
5 |
6 | namespace JoinNotifier
7 | {
8 | public static class NetworkManagerHooks
9 | {
10 | private static bool IsInitialized;
11 | private static bool SeenFire;
12 | private static bool AFiredFirst;
13 |
14 | public static event Action OnJoin;
15 | public static event Action OnLeave;
16 |
17 | public static void EventHandlerA(Player player)
18 | {
19 | if (!SeenFire)
20 | {
21 | AFiredFirst = true;
22 | SeenFire = true;
23 |
24 | MelonDebug.Msg("A fired first");
25 | }
26 |
27 | if (player == null) return;
28 | (AFiredFirst ? OnJoin : OnLeave)?.Invoke(player);
29 | }
30 |
31 | public static void EventHandlerB(Player player)
32 | {
33 | if (!SeenFire)
34 | {
35 | AFiredFirst = false;
36 | SeenFire = true;
37 |
38 | MelonDebug.Msg("B fired first");
39 | }
40 |
41 | if (player == null) return;
42 | (AFiredFirst ? OnLeave : OnJoin)?.Invoke(player);
43 | }
44 |
45 | public static void Initialize()
46 | {
47 | if (IsInitialized) return;
48 | if (ReferenceEquals(NetworkManager.field_Internal_Static_NetworkManager_0, null)) return;
49 |
50 | var field0 = NetworkManager.field_Internal_Static_NetworkManager_0.field_Internal_VRCEventDelegate_1_Player_0;
51 | var field1 = NetworkManager.field_Internal_Static_NetworkManager_0.field_Internal_VRCEventDelegate_1_Player_1;
52 |
53 | AddDelegate(field0, EventHandlerA);
54 | AddDelegate(field1, EventHandlerB);
55 |
56 | IsInitialized = true;
57 | }
58 |
59 | private static void AddDelegate(VRCEventDelegate field, Action eventHandlerA)
60 | {
61 | field.field_Private_HashSet_1_UnityAction_1_T_0.Add(eventHandlerA);
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/JoinNotifier/joinnotifier.assetbundle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/JoinNotifier/joinnotifier.assetbundle
--------------------------------------------------------------------------------
/LagFreeScreenshots/API/LfsApi.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | namespace LagFreeScreenshots.API
4 | {
5 | public static class LfsApi
6 | {
7 | ///
8 | /// Called after a creenshot is taken and written to disk
9 | ///
10 | public static event ScreenshotSavedEventV2? OnScreenshotSavedV2;
11 |
12 | public delegate void ScreenshotSavedEventV2(string filePath, int width, int height, MetadataV2? metadata);
13 |
14 | internal static void InvokeScreenshotSaved(string filePath, int width, int height, MetadataV2? metadataV2) =>
15 | OnScreenshotSavedV2?.Invoke(filePath, width, height, metadataV2);
16 | }
17 | }
--------------------------------------------------------------------------------
/LagFreeScreenshots/API/MetadataV2.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Globalization;
3 | using System.Linq;
4 | using UnityEngine;
5 | using VRC;
6 | using VRC.Core;
7 |
8 | namespace LagFreeScreenshots.API
9 | {
10 | public class MetadataV2
11 | {
12 | public readonly ScreenshotRotation ImageRotation;
13 | public readonly APIUser ApiUser;
14 | public readonly ApiWorldInstance WorldInstance;
15 | public Vector3 Position;
16 | public readonly List<(Player, Vector3)> PlayerList;
17 |
18 | public MetadataV2(ScreenshotRotation imageRotation, APIUser apiUser, ApiWorldInstance apiWorldInstance, Vector3 position, List<(Player, Vector3)> playerList)
19 | {
20 | ImageRotation = imageRotation;
21 | ApiUser = apiUser;
22 | WorldInstance = apiWorldInstance;
23 | Position = position;
24 | PlayerList = playerList;
25 | }
26 |
27 | public override string ToString()
28 | {
29 | var worldString = "null,0,Not in any world";
30 | if (WorldInstance != null && WorldInstance.world != null)
31 | worldString = WorldInstance.world.id + "," + WorldInstance.name + "," + WorldInstance.world.name;
32 |
33 | var positionString = Position.x.ToString(CultureInfo.InvariantCulture) + "," + Position.y.ToString(CultureInfo.InvariantCulture) + "," + Position.z.ToString(CultureInfo.InvariantCulture);
34 |
35 | return "lfs|2|author:"
36 | + ApiUser.id + "," + ApiUser.displayName
37 | + "|world:" + worldString
38 | + "|pos:" + positionString
39 | + (ImageRotation != ScreenshotRotation.NoRotation ? "|rq:" + ImageRotation : "")
40 | + "|players:" + string.Join(";", PlayerList.Select(PlayerListToString));
41 | }
42 |
43 | private static string PlayerListToString((Player, Vector3) playerData)
44 | {
45 | if (playerData.Item1 == null || playerData.Item1.prop_APIUser_0 == null) return "null,0,0,0,null";
46 | return playerData.Item1.prop_APIUser_0.id + "," +
47 | playerData.Item2.x.ToString("0.00", CultureInfo.InvariantCulture) + "," +
48 | playerData.Item2.y.ToString("0.00", CultureInfo.InvariantCulture) + "," +
49 | playerData.Item2.z.ToString("0.00", CultureInfo.InvariantCulture) + "," +
50 | playerData.Item1.prop_APIUser_0.displayName;
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/LagFreeScreenshots/API/ScreenshotRotation.cs:
--------------------------------------------------------------------------------
1 | namespace LagFreeScreenshots.API
2 | {
3 | public enum ScreenshotRotation
4 | {
5 | AutoRotationDisabled = -1,
6 | NoRotation = 0,
7 | Clockwise90 = 1,
8 | Clockwise180 = 2,
9 | CounterClockwise90 = 3
10 | }
11 | }
--------------------------------------------------------------------------------
/LagFreeScreenshots/AwaitProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using MelonLoader;
6 |
7 | namespace LagFreeScreenshots
8 | {
9 | public class AwaitProvider
10 | {
11 | private readonly Queue myToMainThreadQueue = new Queue();
12 |
13 | public void Flush()
14 | {
15 | List toProcess;
16 |
17 | if (myToMainThreadQueue.Count == 0)
18 | return;
19 |
20 | lock (myToMainThreadQueue)
21 | {
22 | toProcess = myToMainThreadQueue.ToList();
23 | myToMainThreadQueue.Clear();
24 | }
25 |
26 | foreach (var action in toProcess)
27 | {
28 | try
29 | {
30 | action();
31 | }
32 | catch (Exception ex)
33 | {
34 | MelonLogger.Warning($"Exception in task: {ex}");
35 | }
36 | }
37 | }
38 |
39 | public YieldAwaitable Yield()
40 | {
41 | return new YieldAwaitable(myToMainThreadQueue);
42 | }
43 |
44 | public readonly struct YieldAwaitable : INotifyCompletion
45 | {
46 | private readonly Queue myToMainThreadQueue;
47 |
48 | public YieldAwaitable(Queue toMainThreadQueue)
49 | {
50 | myToMainThreadQueue = toMainThreadQueue;
51 | }
52 |
53 | public bool IsCompleted => false;
54 |
55 | public YieldAwaitable GetAwaiter() => this;
56 |
57 | public void GetResult() { }
58 |
59 | public void OnCompleted(Action continuation)
60 | {
61 | lock (myToMainThreadQueue)
62 | myToMainThreadQueue.Enqueue(continuation);
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/LagFreeScreenshots/EventHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LagFreeScreenshots.API;
3 |
4 | namespace LagFreeScreenshots
5 | {
6 | [Obsolete("Use LagFreeScreenshots.API.LfsApi")]
7 | public static class EventHandler
8 | {
9 | ///
10 | /// Calls when an screenshot is saved
11 | ///
12 | public static event Action OnScreenshotSaved;
13 |
14 | internal static void InvokeScreenshotSaved(string filePath, int width, int height, MetadataV2 metadata)
15 | {
16 | OnScreenshotSaved?.Invoke(filePath, width, height, metadata == null ? null : new Metadata(metadata));
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/LagFreeScreenshots/LagFreeScreenshots.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | true
7 | latest
8 | LagFreeScreenshots
9 |
10 |
11 |
12 | none
13 | false
14 | 1.4.1.0
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LagFreeScreenshots/Metadata.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using LagFreeScreenshots.API;
5 | using UnityEngine;
6 | using VRC;
7 | using VRC.Core;
8 |
9 | namespace LagFreeScreenshots
10 | {
11 | [Obsolete("Use LagFreeScreenshots.API.MetadataV2")]
12 | public class Metadata
13 | {
14 | public int ImageRotation;
15 | public APIUser ApiUser;
16 | public ApiWorldInstance WorldInstance;
17 | public Vector3 Position;
18 | public List> PlayerList;
19 |
20 | public Metadata(int imageRotation, APIUser apiUser, ApiWorldInstance apiWorldInstance, Vector3 position, List> playerList)
21 | {
22 | ImageRotation = imageRotation;
23 | ApiUser = apiUser;
24 | WorldInstance = apiWorldInstance;
25 | Position = position;
26 | PlayerList = playerList;
27 | }
28 |
29 | public Metadata(MetadataV2 newMetadata) : this((int) newMetadata.ImageRotation, newMetadata.ApiUser,
30 | newMetadata.WorldInstance, newMetadata.Position,
31 | newMetadata.PlayerList.ConvertAll(it => Tuple.Create(it.Item1, it.Item2)))
32 | {
33 |
34 | }
35 |
36 | public string ConvertToString()
37 | {
38 | var worldString = "null,0,Not in any world";
39 | if (WorldInstance != null && WorldInstance.world != null)
40 | worldString = WorldInstance.world.id + "," + WorldInstance.name + "," + WorldInstance.world.name;
41 |
42 | var positionString = Position.x.ToString(CultureInfo.InvariantCulture) + "," + Position.y.ToString(CultureInfo.InvariantCulture) + "," + Position.z.ToString(CultureInfo.InvariantCulture);
43 |
44 | return "lfs|2|author:"
45 | + ApiUser.id + "," + ApiUser.displayName
46 | + "|world:" + worldString
47 | + "|pos:" + positionString
48 | + (ImageRotation != -1 ? "|rq:" + ImageRotation : "")
49 | + "|players:" + string.Join(";", PlayerList.ConvertAll(new Converter, string>(PlayerListToString)));
50 | }
51 |
52 | private static string PlayerListToString(Tuple playerData)
53 | {
54 | if (playerData.Item1 == null || playerData.Item1.prop_APIUser_0 == null) return "null,0,0,0,null";
55 | return playerData.Item1.prop_APIUser_0.id + "," +
56 | playerData.Item2.x.ToString("0.00", CultureInfo.InvariantCulture) + "," +
57 | playerData.Item2.y.ToString("0.00", CultureInfo.InvariantCulture) + "," +
58 | playerData.Item2.z.ToString("0.00", CultureInfo.InvariantCulture) + "," +
59 | playerData.Item1.prop_APIUser_0.displayName;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/LagFreeScreenshots/PngUtils.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text;
3 |
4 | namespace LagFreeScreenshots
5 | {
6 | public static class PngUtils
7 | {
8 | // https://stackoverflow.com/questions/24082305/how-is-png-crc-calculated-exactly
9 | private static readonly uint[] ourCRCTable = Enumerable.Range(0, 256).Select(n =>
10 | {
11 | uint c = (uint) n;
12 | for (var k = 0; k <= 7; k++)
13 | {
14 | if ((c & 1) == 1)
15 | c = 0xEDB88320 ^ ((c >> 1) & 0x7FFFFFFF);
16 | else
17 | c = ((c >> 1) & 0x7FFFFFFF);
18 | }
19 |
20 | return c;
21 | }).ToArray();
22 |
23 | private static uint PngCrc32(byte[] stream, int offset, int length, uint crc)
24 | {
25 | uint c = crc ^ 0xffffffff;
26 | var endOffset = offset + length;
27 | for (var i = offset; i < endOffset; i++)
28 | {
29 | c = ourCRCTable[(c ^ stream[i]) & 255] ^ ((c >> 8) & 0xFFFFFF);
30 | }
31 |
32 | return c ^ 0xffffffff;
33 | }
34 |
35 | internal static byte[] ProducePngDescriptionTextChunk(string text)
36 | {
37 | var keyword = "Description";
38 | var chunkDataSize = keyword.Length + 1 + 1 + 1 + 1 + 1 + Encoding.UTF8.GetByteCount(text);
39 | var chunkBytes = new byte[12 + chunkDataSize];
40 | chunkBytes[0] = (byte) (chunkDataSize >> 24);
41 | chunkBytes[1] = (byte) (chunkDataSize >> 16);
42 | chunkBytes[2] = (byte) (chunkDataSize >> 8);
43 | chunkBytes[3] = (byte) (chunkDataSize >> 0);
44 |
45 | chunkBytes[4] = (byte) 'i';
46 | chunkBytes[5] = (byte) 'T';
47 | chunkBytes[6] = (byte) 'X';
48 | chunkBytes[7] = (byte) 't';
49 |
50 | Encoding.UTF8.GetBytes(keyword, 0, keyword.Length, chunkBytes, 8);
51 |
52 | chunkBytes[8 + keyword.Length + 0] = 0; // null separator
53 | chunkBytes[8 + keyword.Length + 1] = 0; // compression flag
54 | chunkBytes[8 + keyword.Length + 2] = 0; // compression method
55 | chunkBytes[8 + keyword.Length + 3] = 0; // null separator
56 | chunkBytes[8 + keyword.Length + 4] = 0; // null separator
57 |
58 | Encoding.UTF8.GetBytes(text, 0, text.Length, chunkBytes, 8 + keyword.Length + 5);
59 |
60 | var crc = PngCrc32(chunkBytes, 4, chunkBytes.Length - 8, 0);
61 |
62 | chunkBytes[chunkBytes.Length - 4] = (byte) (crc >> 24);
63 | chunkBytes[chunkBytes.Length - 3] = (byte) (crc >> 16);
64 | chunkBytes[chunkBytes.Length - 2] = (byte) (crc >> 8);
65 | chunkBytes[chunkBytes.Length - 1] = (byte) (crc >> 0);
66 |
67 | return chunkBytes;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/LagFreeScreenshots/PresetScreenshotSizes.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace LagFreeScreenshots
4 | {
5 | internal enum PresetScreenshotSizes
6 | {
7 | Default,
8 | Custom,
9 |
10 | [Description("Thumbnail 100x100")]
11 | Thumbnail,
12 |
13 | [Description("Square 1024x1024")]
14 | Square,
15 |
16 | [Description("1280x720")]
17 | _720p,
18 |
19 | [Description("1920x1080 (VRC default)")]
20 | _1080p,
21 |
22 | [Description("4K (3840x2160)")]
23 | _4K,
24 |
25 | [Description("8K (7680x4320)")]
26 | _8K,
27 |
28 | [Description("12K (11520x6480)")]
29 | _12K,
30 |
31 | [Description("16K (15360x8640)")]
32 | _16K,
33 | }
34 | }
--------------------------------------------------------------------------------
/Libs/.touch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Libs/.touch
--------------------------------------------------------------------------------
/LibsAlt/.touch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/LibsAlt/.touch
--------------------------------------------------------------------------------
/MirrorResolutionUnlimiter/MirrorResolutionUnlimiter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net48
4 | true
5 | true
6 | latest
7 | true
8 | 1.1.4.0
9 | none
10 |
11 |
12 |
13 | {85b11d98-e013-4e5d-95f3-5437de63acf4}
14 | UIExpansionKit
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/MirrorResolutionUnlimiter/OriginalPixelLightsSettingKeeper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace MirrorResolutionUnlimiter
5 | {
6 | public class OriginalPixelLightsSettingKeeper : MonoBehaviour
7 | {
8 | public bool OriginalValue;
9 |
10 | public OriginalPixelLightsSettingKeeper(IntPtr obj0) : base(obj0)
11 | {
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/MirrorResolutionUnlimiter/UiExtensionsAddon.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using UIExpansionKit.API;
3 | using UnityEngine;
4 | using VRC.SDKBase;
5 |
6 | namespace MirrorResolutionUnlimiter
7 | {
8 | public static class UiExtensionsAddon
9 | {
10 | private static int PlayerLayer = 1 << 9; // todo: ask unity for these
11 | private static int PlayerLocalLayer = 1 << 10;
12 | private static int UiLayer = 1 << 5;
13 | private static int UiMenuLayer = 1 << 12;
14 | private static int UiInternalLayer = 1 << 19;
15 | private static int MirrorReflectionLayer = 1 << 18;
16 |
17 | [MethodImpl(MethodImplOptions.NoInlining)]
18 | public static void Init()
19 | {
20 | ExpansionKitApi.GetExpandedMenu(ExpandedMenu.SettingsMenu).AddSimpleButton("Optimize mirrors", OptimizeMirrors);
21 | ExpansionKitApi.GetExpandedMenu(ExpandedMenu.SettingsMenu).AddSimpleButton("Beautify mirrors", BeautifyMirrors);
22 |
23 | ExpansionKitApi.RegisterSettingAsStringEnum(MirrorResolutionUnlimiterMod.ModCategory,
24 | MirrorResolutionUnlimiterMod.PixelLightsSetting,
25 | new[] {("default", "World default"), ("allow", "Force allow"), ("disable", "Force disable")});
26 | }
27 |
28 | private static void BeautifyMirrors()
29 | {
30 | foreach (var vrcMirrorReflection in Object.FindObjectsOfType())
31 | if (vrcMirrorReflection.isActiveAndEnabled)
32 | if (MirrorResolutionUnlimiterMod.UiInMirrors.Value)
33 | vrcMirrorReflection.m_ReflectLayers = -1 & ~PlayerLocalLayer;
34 | else
35 | vrcMirrorReflection.m_ReflectLayers =
36 | -1 & ~UiLayer & ~UiMenuLayer & ~PlayerLocalLayer & ~UiInternalLayer;
37 |
38 | }
39 |
40 | private static void OptimizeMirrors()
41 | {
42 | foreach (var vrcMirrorReflection in Object.FindObjectsOfType())
43 | if (vrcMirrorReflection.isActiveAndEnabled)
44 | vrcMirrorReflection.m_ReflectLayers = PlayerLayer | MirrorReflectionLayer | (MirrorResolutionUnlimiterMod.UiInMirrors.Value ? UiMenuLayer | UiInternalLayer | UiLayer : 0);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/ParticleAndBoneLimiterSettings/CustomParticleSettingsUiHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UIExpansionKit;
3 | using UnityEngine;
4 |
5 | namespace ParticleAndBoneLimiterSettings
6 | {
7 | public class CustomParticleSettingsUiHandler : MonoBehaviour
8 | {
9 | internal static PreloadedBundleContents UixBundle;
10 |
11 | public CustomParticleSettingsUiHandler(IntPtr ptr) : base(ptr)
12 | {
13 | }
14 |
15 | private void Awake()
16 | {
17 | ParticleAndBoneLimiterSettingsMod.InitializeSettingsCategory(gameObject);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/ParticleAndBoneLimiterSettings/ParticleAndBoneLimiterSettings.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | 1.1.5.0
7 | ParticleAndBoneLimiterSettings
8 |
9 |
10 |
11 | none
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ReleaseChangelog.md:
--------------------------------------------------------------------------------
1 | Read the [Malicious Mods and you](https://github.com/knah/VRCMods/blob/master/Malicious-Mods.md) doc!
2 |
3 | Changes:
4 | * LagFreeScreenshot: fixed for build 1203
5 | * ScaleGoesBrr: fixed issues with UI having weird scale if calibrating while scaled
6 | * ScaleGoesBrr: fixed head-follow calibration failing to follow the head vertically
7 | * IKTweaks: initial update for build 1203/IK 2.0
8 | * Removed all calibration-related options
9 | * You must use IK 2.0 for IKTweaks to work. It won't do anything for legacy IK.
10 | * Most mod features now work in 3/4-point tracking too
11 | * IKT spine solver now obeys lock modes. "Lock head" is same as old IKTweaks, "Lock both" is "Lock head but ignore angle limits"
12 | * Added an option to disable elbow-torso avoidance
13 | * Overall performance improvements
14 | * Fully open-source now - no FinalIK dependency anymore!
15 |
16 | **USE IT AT YOUR OWN RISK.** Modding the client is against VRChat ToS. I am not responsible for any bans or other punishments you may get by using these mods!
--------------------------------------------------------------------------------
/ScaleGoesBrr/ScaleGoesBrr.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | none
7 | 1.1.2.0
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SparkleBeGone/SparkleBeGone.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | true
7 | 1.1.0.0
8 | true
9 | latest
10 | SparkleBeGone
11 |
12 |
13 |
14 | none
15 | false
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SparkleBeGone/sparklebegone:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/SparkleBeGone/sparklebegone
--------------------------------------------------------------------------------
/Styletor/API/StyletorApi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace Styletor.API
6 | {
7 | ///
8 | /// Provides an API for other mods to be used
9 | /// Be aware that Styletor is GPLv3 (unlike UI Expansion Kit, which is LGPLv3), so your mod has to be provided under a GPL-compatible license (so [a]GPLv3) to use these in any form
10 | /// Check README for another way to provide styles embedded in a mod without using these
11 | ///
12 | public static class StyletorApi
13 | {
14 | internal static readonly List>>> StyleProviders = new();
15 |
16 | ///
17 | /// Registers the given style provider.
18 | /// The provided Func should return an enumerable with named ZIP file streams with styles
19 | /// Streams returned will be closed by Styletor
20 | /// This Func can be called at any time when styles are being reloaded
21 | ///
22 | public static void RegisterStylesProvider(Func>> zipStyleStreams)
23 | {
24 | StyleProviders.Add(zipStyleStreams);
25 | ReloadStyles();
26 | }
27 |
28 | ///
29 | /// Reloads all styles, including styles on disk and style providers
30 | ///
31 | public static void ReloadStyles()
32 | {
33 | StyletorMod.Instance.ReloadStyles();
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Styletor/BundledStyles/basic-recolorable.styletor.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Styletor/BundledStyles/basic-recolorable.styletor.zip
--------------------------------------------------------------------------------
/Styletor/BundledStyles/bigger-mic-button.styletor.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Styletor/BundledStyles/bigger-mic-button.styletor.zip
--------------------------------------------------------------------------------
/Styletor/BundledStyles/default-fixes.styletor.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Styletor/BundledStyles/default-fixes.styletor.zip
--------------------------------------------------------------------------------
/Styletor/BundledStyles/qm-background-lemon.styletor.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Styletor/BundledStyles/qm-background-lemon.styletor.zip
--------------------------------------------------------------------------------
/Styletor/ExtraHandlers/UiLasersHandler.cs:
--------------------------------------------------------------------------------
1 | using Styletor.Utils;
2 | using UIExpansionKit;
3 | using UnityEngine;
4 | using VRC.UI;
5 |
6 | namespace Styletor.ExtraHandlers
7 | {
8 | public class UiLasersHandler
9 | {
10 | private readonly SpriteRenderer myMouseCursorSprite;
11 | private readonly SpriteRenderer myLeftDotSprite;
12 | private readonly SpriteRenderer myRightDotSprite;
13 |
14 | private readonly HandDotCursor myLeftHand;
15 | private readonly HandDotCursor myRightHand;
16 |
17 | private readonly SettingsHolder mySettings;
18 |
19 | private readonly Color myDefaultDotColor;
20 | private readonly Color myDefaultCursorColor;
21 |
22 | private readonly Color myDefaultLaserColorDark;
23 | private readonly Color myDefaultLaserColorBright;
24 |
25 | public UiLasersHandler(SettingsHolder settings)
26 | {
27 | mySettings = settings;
28 |
29 | myMouseCursorSprite = UnityUtils
30 | .FindInactiveObjectInActiveRoot("_Application/CursorManager/MouseArrow/VRCUICursorIcon")!
31 | .GetComponent();
32 |
33 | myDefaultCursorColor = myMouseCursorSprite.color;
34 |
35 | myLeftDotSprite = UnityUtils
36 | .FindInactiveObjectInActiveRoot("_Application/CursorManager/DotLeftHand/VRCUICursorIcon")!
37 | .GetComponent();
38 |
39 | myDefaultDotColor = myLeftDotSprite.color;
40 |
41 | myRightDotSprite = UnityUtils
42 | .FindInactiveObjectInActiveRoot("_Application/CursorManager/DotRightHand/VRCUICursorIcon")!
43 | .GetComponent();
44 |
45 | myLeftHand = UnityUtils
46 | .FindInactiveObjectInActiveRoot("_Application/CursorManager/DotLeftHand")!
47 | .GetComponent();
48 |
49 | myDefaultLaserColorDark = myLeftHand.field_Public_Color_0;
50 | myDefaultLaserColorBright = myLeftHand.field_Public_Color_1;
51 |
52 | myRightHand = UnityUtils
53 | .FindInactiveObjectInActiveRoot("_Application/CursorManager/DotRightHand")!
54 | .GetComponent();
55 |
56 | mySettings.RegisterUpdateDelegate(mySettings.UiLasersModeEntry, mySettings.UiLasersColorEntry, UpdateColors);
57 | }
58 |
59 | public void UpdateColors()
60 | {
61 | var color = mySettings.GetColorForMode(mySettings.UiLasersModeEntry, mySettings.UiLasersColorEntry);
62 |
63 | myLeftDotSprite.color = color ?? myDefaultDotColor;
64 | myRightDotSprite.color = color ?? myDefaultDotColor;
65 | myMouseCursorSprite.color = color ?? myDefaultCursorColor;
66 |
67 | var colorDarker = color?.RGBMultipliedClamped(0.5f);
68 |
69 | myLeftHand.field_Public_Color_0 = colorDarker ?? myDefaultLaserColorDark;
70 | myRightHand.field_Public_Color_0 = colorDarker ?? myDefaultLaserColorDark;
71 |
72 | myLeftHand.field_Public_Color_1 = color ?? myDefaultLaserColorBright;
73 | myRightHand.field_Public_Color_1 = color ?? myDefaultLaserColorBright;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/Styletor/Jsons/SpriteJson.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Styletor.Jsons
4 | {
5 | public class SpriteJson
6 | {
7 | // These correspond to Sprite.border
8 | public float BorderLeft;
9 | public float BorderBottom;
10 | public float BorderRight;
11 | public float BorderTop;
12 |
13 | // These correspond to Sprite.pivot, in 0-1 range
14 | public float PivotX = 0.5f;
15 | public float PivotY = 0.5f;
16 |
17 | // Sprite.pixelsPerUnit
18 | public float PixelsPerUnit = 100;
19 |
20 | public SpriteJson()
21 | {
22 | }
23 |
24 | public SpriteJson(Sprite sprite)
25 | {
26 | var pivotRel = sprite.pivot / sprite.rect.size;
27 | var border = sprite.border;
28 |
29 | PixelsPerUnit = sprite.pixelsPerUnit;
30 | PivotX = pivotRel.x;
31 | PivotY = pivotRel.y;
32 |
33 | BorderLeft = border.x;
34 | BorderBottom = border.y;
35 | BorderRight = border.z;
36 | BorderTop = border.w;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/Styletor/Jsons/StyleMetadata.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Styletor.Jsons
4 | {
5 | public class StyleMetadata
6 | {
7 | ///
8 | /// User-visible name of this style
9 | ///
10 | public string Name = UnnamedName;
11 |
12 | ///
13 | /// User-visible description of this style
14 | ///
15 | public string Description = "";
16 |
17 | ///
18 | /// User-visible author name of this style
19 | ///
20 | public string Author = "";
21 |
22 | ///
23 | /// VRC build number this style was created for, i.e. "1137"
24 | ///
25 | public string? VrcBuildNumber;
26 |
27 | ///
28 | /// If true, this style is a mix-in and can be disabled or enabled (as opposed to chosen). It will be applied after VRC base style
29 | ///
30 | public bool IsMixin;
31 |
32 | ///
33 | /// Sorting priority of this mix-in relative to others.
34 | /// Mixins with larger numbers will override properties from mixins with lower numbers.
35 | /// User-chosen non-mixin style is applied at priority 0
36 | ///
37 | public int MixinPriority = 1;
38 |
39 | ///
40 | /// If true, this mix-in starts disabled by default.
41 | /// Mostly useful for bundled styles.
42 | ///
43 | public bool DisabledByDefault;
44 |
45 | ///
46 | /// A list of image names that will be turned into grayscale and overridden.
47 | /// VRChat has some graphics with baked-in colors, which usually makes them non-colorable.
48 | /// Use this so that you don't have to include (copyrighted) VRChat assets turned grayscale into your skin.
49 | ///
50 | public List SpritesToGrayscale = new();
51 |
52 | public const string UnnamedName = "";
53 | }
54 | }
--------------------------------------------------------------------------------
/Styletor/Styles/ColorizerManager.cs:
--------------------------------------------------------------------------------
1 | using MelonLoader;
2 | using Styletor.Utils;
3 | using UnityEngine;
4 |
5 | namespace Styletor.Styles
6 | {
7 | public class ColorizerManager
8 | {
9 | private readonly SettingsHolder mySettings;
10 | public string MenuColorBase { get; private set; } = "";
11 | public string MenuColorHighlight { get; private set; } = "";
12 | public string MenuColorBackground { get; private set; } = "";
13 | public string MenuColorDarklight { get; private set; } = "";
14 |
15 | public string MenuColorText { get; private set; } = "";
16 | public string MenuColorTextHigh { get; private set; } = "";
17 |
18 | public string MenuColorAccent { get; private set; } = "";
19 | public string MenuColorAccentDarker { get; private set; } = "";
20 |
21 | public ColorizerManager(SettingsHolder settings)
22 | {
23 | mySettings = settings;
24 |
25 | settings.BaseColorEntry.OnValueChanged += (_, _) => { UpdateColors(); };
26 | settings.AccentColorEntry.OnValueChanged += (_, _) => { UpdateColors(); };
27 | settings.TextColorEntry.OnValueChanged += (_, _) => { UpdateColors(); };
28 | UpdateColors();
29 | }
30 |
31 | private void UpdateColors()
32 | {
33 | UpdateColors(mySettings.BaseColor, mySettings.AccentColor, mySettings.TextColor);
34 | }
35 |
36 | public string ReplacePlaceholders(string input)
37 | {
38 | return input
39 | .Replace("$BASE$", MenuColorBase)
40 | .Replace("$HIGH$", MenuColorHighlight)
41 | .Replace("$BG$", MenuColorBackground)
42 | .Replace("$DARK$", MenuColorDarklight)
43 | .Replace("$TEXT$", MenuColorText)
44 | .Replace("$TEXTHI$", MenuColorTextHigh)
45 | .Replace("$ACCT$", MenuColorAccent)
46 | .Replace("$ACCDK$", MenuColorAccentDarker)
47 | ;
48 | }
49 |
50 | public void UpdateColors(Color @base, Color accent, Color text)
51 | {
52 | var highlight = @base.RGBMultipliedClamped(1.1f);
53 | var background = @base.RGBMultipliedClamped(0.9f);
54 | var dark = @base.RGBMultipliedClamped(0.5f);
55 |
56 | MenuColorBase = ColorToHex(@base);
57 | MenuColorHighlight = ColorToHex(highlight);
58 | MenuColorBackground = ColorToHex(background);
59 | MenuColorDarklight = ColorToHex(dark);
60 |
61 | MenuColorText = ColorToHex(text.RGBMultipliedClamped(0.9f));
62 | MenuColorTextHigh = ColorToHex(text);
63 |
64 | MenuColorAccent = ColorToHex(accent);
65 | MenuColorAccentDarker = ColorToHex(accent.RGBMultipliedClamped(0.7f));
66 | }
67 |
68 | private static string PartToHex(float f) => ((int)(f * 255)).ToString("x2");
69 | private static string ColorToHex(Color c) => $"#{PartToHex(c.r)}{PartToHex(c.g)}{PartToHex(c.b)}";
70 | }
71 | }
--------------------------------------------------------------------------------
/Styletor/Styletor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | 0.3.3.0
6 | latest
7 | enable
8 | none
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | False
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Styletor/Utils/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Il2CppSystem.IO;
3 | using UnhollowerBaseLib;
4 | using UnityEngine;
5 | using Object = UnityEngine.Object;
6 | using Stream = System.IO.Stream;
7 |
8 | #nullable enable
9 |
10 | namespace Styletor.Utils
11 | {
12 | public static class Utils
13 | {
14 | private delegate byte LoadTextureDelegate(IntPtr texturePtr, IntPtr arrayPtr, byte makeNonReadable);
15 | private delegate IntPtr EncodeAsPngDelegate(IntPtr texturePtr);
16 |
17 | private static readonly LoadTextureDelegate ourLoadTextureDelegate = IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage");
18 | private static readonly EncodeAsPngDelegate ourEncodeAsPng = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToPNG");
19 |
20 | public static Il2CppStructArray? EncodeAsPng(this Texture2D texture)
21 | {
22 | var arrayPtr = ourEncodeAsPng(texture.Pointer);
23 | if (arrayPtr == IntPtr.Zero) return null;
24 | return new Il2CppStructArray(arrayPtr);
25 | }
26 |
27 | public static Texture2D? LoadTexture(string filePath)
28 | {
29 | return LoadTexture(File.ReadAllBytes(filePath));
30 | }
31 |
32 | public static Texture2D? LoadTexture(Stream stream)
33 | {
34 | return LoadTexture(stream.ReadAllBytes());
35 | }
36 |
37 | public static Texture2D? LoadTexture(Il2CppStructArray bytes)
38 | {
39 | var texture = new Texture2D(2, 2, TextureFormat.ARGB32, true);
40 | var success = ourLoadTextureDelegate(texture.Pointer, bytes.Pointer, 1);
41 | if (success == 0)
42 | {
43 | Object.Destroy(texture);
44 | return null;
45 | }
46 |
47 | return texture;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/TrueShaderAntiCrash/DxbcShaderFilter.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/TrueShaderAntiCrash/DxbcShaderFilter.dll
--------------------------------------------------------------------------------
/TrueShaderAntiCrash/ShaderFilterApi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace TrueShaderAntiCrash
5 | {
6 | public static class ShaderFilterApi
7 | {
8 | public const string DLLName = "DxbcShaderFilter";
9 |
10 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
11 | private delegate void TriBool(bool limitLoops, bool limitGeometry, bool limitTesselation);
12 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
13 | private delegate void OneFloat(float value);
14 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
15 | private delegate void OneInt(int value);
16 |
17 | private static TriBool ourSetFilterState;
18 | private static OneFloat ourSetTess;
19 | private static OneInt ourSetLoops;
20 | private static OneInt ourSetGeom;
21 |
22 | public static void Init(IntPtr hmodule)
23 | {
24 | ourSetFilterState = Marshal.GetDelegateForFunctionPointer(GetProcAddress(hmodule, nameof(SetFilteringState)));
25 | ourSetTess = Marshal.GetDelegateForFunctionPointer(GetProcAddress(hmodule, nameof(SetMaxTesselationPower)));
26 | ourSetLoops = Marshal.GetDelegateForFunctionPointer(GetProcAddress(hmodule, nameof(SetLoopLimit)));
27 | ourSetGeom = Marshal.GetDelegateForFunctionPointer(GetProcAddress(hmodule, nameof(SetGeometryLimit)));
28 | }
29 |
30 | [DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)]
31 | static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
32 |
33 | public static void SetFilteringState(bool limitLoops, bool limitGeometry, bool limitTesselation) => ourSetFilterState(limitLoops, limitGeometry, limitTesselation);
34 | public static void SetMaxTesselationPower(float maxTesselation) => ourSetTess(maxTesselation);
35 | public static void SetLoopLimit(int limit) => ourSetLoops(limit);
36 | public static void SetGeometryLimit(int limit) => ourSetGeom(limit);
37 | }
38 | }
--------------------------------------------------------------------------------
/TrueShaderAntiCrash/TrueShaderAntiCrash.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | latest
6 | 1.0.6.0
7 | true
8 | TrueShaderAntiCrash
9 |
10 |
11 |
12 | none
13 | false
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Turbones/BoneDeleteHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace Turbones
5 | {
6 | public class BoneDeleteHandler : MonoBehaviour
7 | {
8 | public BoneDeleteHandler(IntPtr ptr) : base(ptr)
9 | {
10 | }
11 |
12 | private void OnDestroy()
13 | {
14 | foreach (var bone in GetComponents())
15 | {
16 | JigglySolverApi.DynamicBoneOnDestroyPatch(bone.Pointer);
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Turbones/JigglyRustSolver.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PatchedPlusPlus/VRCMods/b20a738530ece2eb3bc118de9852ed0ba51c26e6/Turbones/JigglyRustSolver.dll
--------------------------------------------------------------------------------
/Turbones/NativeStructs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Turbones
5 | {
6 |
7 | [StructLayout(LayoutKind.Sequential)]
8 | internal struct ICallOffsets
9 | {
10 | public IntPtr get_transform_component;
11 | public IntPtr get_transform_ltw_matrix;
12 | public IntPtr get_component_enabled;
13 | public IntPtr set_transform_position_and_rotation;
14 | public IntPtr get_transform_child_count;
15 |
16 | public IntPtr gchandle_create;
17 | public IntPtr gchandle_drop;
18 | public IntPtr gchandle_get;
19 |
20 | public IntPtr transform_get_local_rotation;
21 | }
22 |
23 | [StructLayout(LayoutKind.Sequential)]
24 | internal struct ColliderComponentOffsets {
25 | public uint collider_radius;
26 | public uint collider_height;
27 | public uint collider_center;
28 | public uint collider_bound;
29 | public uint collider_direction;
30 | }
31 |
32 | [StructLayout(LayoutKind.Sequential)]
33 | internal struct ParticleClassOffsets {
34 | public uint transform;
35 | public uint parent_index;
36 | public uint damping;
37 | public uint elasticity;
38 | public uint stiffness;
39 | public uint inert;
40 | public uint radius;
41 | public uint end_offset;
42 | }
43 |
44 | [StructLayout(LayoutKind.Sequential)]
45 | internal struct BoneComponentOffsets {
46 | public uint particle_list;
47 | public uint collider_list;
48 | public uint gravity;
49 | public uint force;
50 | public uint local_gravity;
51 | public uint freeze_axis;
52 | public uint update_rate;
53 | public uint root;
54 | }
55 |
56 | [StructLayout(LayoutKind.Sequential)]
57 | internal struct ListClassOffsets
58 | {
59 | public uint size;
60 | public uint store;
61 | public uint array_store;
62 | }
63 |
64 | [StructLayout(LayoutKind.Sequential)]
65 | internal struct ObjectOffsets
66 | {
67 | public uint cached_ptr;
68 | }
69 | }
--------------------------------------------------------------------------------
/Turbones/Turbones.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | true
6 | true
7 | 1.1.2.0
8 | Turbones
9 |
10 |
11 |
12 | none
13 | false
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/UIExpansionKit/API/BuiltinUiUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Il2CppSystem.Collections.Generic;
3 | using UnityEngine;
4 | using UnityEngine.UI;
5 |
6 | namespace UIExpansionKit.API
7 | {
8 | public static class BuiltinUiUtils
9 | {
10 | ///
11 | /// Shows a VRC keyboard input popup. Requires a big menu page to be open.
12 | ///
13 | /// Text displayed in popup header
14 | /// Initial text in the input
15 | /// The input type
16 | /// true means a numeric keypad
17 | /// Invoked when the user clicks the confirm button
18 | /// The text on confirmation button
19 | /// Invoked if user cancels text input
20 | /// Text show in the input field if no text is already there
21 | /// Keep this set to true or the universe will implode
22 | /// Invoked with the instance of input popup shown
23 | public static void ShowInputPopup(string title, string initialText, InputField.InputType inputType, bool isNumeric,
24 | string confirmButtonText, Action, Text> onComplete, Action onCancel,
25 | string placeholderText, bool closeAfterInput, Action onPopupShown)
26 | {
27 | ScanningReflectionCache.ShowUiInputPopup(title, initialText, inputType, isNumeric, confirmButtonText, onComplete, onCancel, placeholderText, closeAfterInput, onPopupShown);
28 | }
29 |
30 | ///
31 | /// Shows a VRC keyboard input popup. Requires a big menu page to be open.
32 | ///
33 | /// Text displayed in popup header
34 | /// Initial text in the input
35 | /// The input type
36 | /// true means a numeric keypad
37 | /// Invoked when the user clicks the confirm button
38 | /// The text on confirmation button
39 | /// Invoked if user cancels text input
40 | /// Text show in the input field if no text is already there
41 | /// Keep this set to true or the universe will implode
42 | /// Invoked with the instance of input popup shown
43 | /// If true, text length limit label will be shown
44 | /// Maximum text length
45 | public static void ShowInputPopup(string title, string initialText, InputField.InputType inputType, bool isNumeric,
46 | string confirmButtonText, Action, Text> onComplete, Action onCancel = null,
47 | string placeholderText = "Enter text...", bool closeAfterInput = true, Action onPopupShown = null,
48 | bool showLimitLabel = false, int textLengthLimit = 0)
49 | {
50 | ScanningReflectionCache.ShowUiInputPopup(title, initialText, inputType, isNumeric, confirmButtonText, onComplete, onCancel, placeholderText, closeAfterInput, onPopupShown, showLimitLabel, textLengthLimit);
51 | }
52 |
53 | public static event Action QuickMenuClosed;
54 | public static event Action FullMenuClosed;
55 | public static event Action OnMenuOpened;
56 |
57 | internal static void InvokeQuickMenuClosed() => QuickMenuClosed?.Invoke();
58 | internal static void InvokeFullMenuClosed() => FullMenuClosed?.Invoke();
59 | internal static void InvokeMenuOpened(ExpandedMenu menu) => OnMenuOpened?.Invoke(menu);
60 | }
61 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/Classes/AwaitProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using MelonLoader;
6 |
7 | namespace UIExpansionKit.API.Classes
8 | {
9 | ///
10 | /// This class implements a simple delegate queue intended for interop with async/await
11 | ///
12 | public class AwaitProvider
13 | {
14 | private readonly Queue myToMainThreadQueue = new();
15 | ///
16 | /// The name of this queue specified in constructor; used in exception messages
17 | ///
18 | public readonly string QueueName;
19 |
20 | ///
21 | /// Creates a new queue
22 | ///
23 | /// Queue's name; used in exception messages
24 | public AwaitProvider(string queueName)
25 | {
26 | QueueName = queueName;
27 | }
28 |
29 | ///
30 | /// Invokes all delegates currently in the queue.
31 | ///
32 | public void Flush()
33 | {
34 | List toProcess;
35 |
36 | if (myToMainThreadQueue.Count == 0)
37 | return;
38 |
39 | lock (myToMainThreadQueue)
40 | {
41 | toProcess = myToMainThreadQueue.ToList();
42 | myToMainThreadQueue.Clear();
43 | }
44 |
45 | foreach (var action in toProcess)
46 | {
47 | try
48 | {
49 | action();
50 | }
51 | catch (Exception ex)
52 | {
53 | MelonLogger.Warning($"Exception in delegate queue {QueueName}: {ex}");
54 | }
55 | }
56 | }
57 |
58 | ///
59 | /// Adds an action to the queue. It will be invoked next time `Flush` is called.
60 | ///
61 | public void Add(Action action)
62 | {
63 | if (action == null) throw new ArgumentNullException(nameof(action));
64 | lock (myToMainThreadQueue)
65 | myToMainThreadQueue.Enqueue(action);
66 | }
67 |
68 | ///
69 | /// Returns an awaitable object (usable with `await` keyword). After it is awaited, async method execution execution will continue when `Flush` is invoked
70 | ///
71 | public YieldAwaitable Yield()
72 | {
73 | return new(myToMainThreadQueue);
74 | }
75 |
76 | public readonly struct YieldAwaitable : INotifyCompletion
77 | {
78 | private readonly Queue myToMainThreadQueue;
79 |
80 | public YieldAwaitable(Queue toMainThreadQueue)
81 | {
82 | myToMainThreadQueue = toMainThreadQueue;
83 | }
84 |
85 | public bool IsCompleted => false;
86 |
87 | public YieldAwaitable GetAwaiter() => this;
88 |
89 | public void GetResult() { }
90 |
91 | public void OnCompleted(Action continuation)
92 | {
93 | lock (myToMainThreadQueue)
94 | myToMainThreadQueue.Enqueue(continuation);
95 | }
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/Controls/FluentExtensions.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace UIExpansionKit.API.Controls
4 | {
5 | public static class FluentExtensions
6 | {
7 | public static T SetVisible(this T @this, bool visible) where T : IMenuControl
8 | {
9 | @this.Visible = visible;
10 | return @this;
11 | }
12 |
13 | public static T SetText(this T @this, string text) where T : IMenuControlWithText
14 | {
15 | @this.Text = text;
16 | return @this;
17 | }
18 |
19 | public static T SetAnchor(this T @this, TextAnchor anchor) where T : IMenuControlWithText
20 | {
21 | @this.Anchor = anchor;
22 | return @this;
23 | }
24 |
25 | public static T SetSelected(this T @this, bool selected) where T : IMenuToggle
26 | {
27 | @this.Selected = selected;
28 | return @this;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/Controls/Interfaces.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | #nullable enable
5 |
6 | namespace UIExpansionKit.API.Controls
7 | {
8 | public interface IMenuControl
9 | {
10 | ///
11 | /// Whether this control is visible or not.
12 | /// Invisible controls don't take up space in grid-like UI layouts
13 | ///
14 | public bool Visible { get; set; }
15 |
16 |
17 | ///
18 | /// This event is called when an instance of this control is created.
19 | /// It's recommended to not rely on its internal structure, unless the prefab for it is supplied by you.
20 | ///
21 | public event Action OnInstanceCreated;
22 |
23 | ///
24 | /// The current instance of this control.
25 | ///
26 | public GameObject? CurrentInstance { get; }
27 | }
28 |
29 | public interface IMenuControlWithText : IMenuControl
30 | {
31 | ///
32 | /// The current text shown on this control
33 | ///
34 | public string Text { get; set; }
35 |
36 | ///
37 | /// Text alignment for this control
38 | ///
39 | public TextAnchor Anchor { get; set; }
40 | }
41 |
42 | public interface IMenuButton : IMenuControlWithText
43 | {
44 | }
45 |
46 | public interface IMenuToggle : IMenuControlWithText
47 | {
48 | ///
49 | /// The state of the toggle. Manually setting it will fire set handler if the state changed.
50 | ///
51 | public bool Selected { get; set; }
52 | }
53 |
54 | public interface IMenuLabel : IMenuControlWithText
55 | {
56 | }
57 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/ExpandedMenu.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace UIExpansionKit.API
4 | {
5 | ///
6 | /// Menus supported by UI Expansion Kit
7 | ///
8 | public enum ExpandedMenu
9 | {
10 | ///
11 | /// Normal QuickMenu (nobody selected)
12 | ///
13 | QuickMenu,
14 |
15 | ///
16 | /// QuickMenu with a user (in instance) selected
17 | ///
18 | UserQuickMenu,
19 |
20 | ///
21 | /// Big avatar menu
22 | ///
23 | AvatarMenu,
24 |
25 | ///
26 | /// Big list of worlds menu
27 | ///
28 | WorldMenu,
29 |
30 | ///
31 | /// Big world details menu
32 | ///
33 | WorldDetailsMenu,
34 |
35 | ///
36 | /// Big user details menu
37 | ///
38 | UserDetailsMenu,
39 |
40 | ///
41 | /// Big social menu
42 | ///
43 | SocialMenu,
44 |
45 | ///
46 | /// Big settings menu. This constant indicates the usual sidebar of big menu, not Mod Settings popup
47 | ///
48 | SettingsMenu,
49 |
50 | ///
51 | /// Big safety menu
52 | ///
53 | SafetyMenu,
54 |
55 | ///
56 | /// Here Quick Menu tab
57 | ///
58 | QuickMenuHere,
59 |
60 | [Obsolete("There is no Emote page in Quick Menu anymore; buttons added here will end up on the Here page")]
61 | EmoteQuickMenu = QuickMenuHere,
62 |
63 | ///
64 | /// Emoji Quick Menu page
65 | ///
66 | EmojiQuickMenu,
67 |
68 | ///
69 | /// Camera Quick Menu page
70 | ///
71 | CameraQuickMenu,
72 |
73 | ///
74 | /// UI Elements Quick Menu page
75 | ///
76 | UiElementsQuickMenu,
77 |
78 | ///
79 | /// Quick Menu Audio Settings tab
80 | ///
81 | QuickMenuAudioSettings,
82 |
83 | [Obsolete("There is no Moderation page in Quick Menu anymore; buttons added here will end up on the Here page")]
84 | ModerationQuickMenu = QuickMenuAudioSettings,
85 |
86 | ///
87 | /// Quick Menu avatar stats/info page
88 | ///
89 | AvatarStatsQuickMenu,
90 |
91 | ///
92 | /// The invites tab in quick menu
93 | ///
94 | InvitesTab,
95 |
96 | ///
97 | /// The handheld camera object
98 | ///
99 | Camera,
100 |
101 | ///
102 | /// The user selection quick menu for not-in-instance users
103 | ///
104 | UserQuickMenuRemote,
105 | }
106 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/ICustomLayoutedMenu.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using UIExpansionKit.API.Controls;
4 | using UnityEngine;
5 |
6 | namespace UIExpansionKit.API
7 | {
8 | [SuppressMessage("ReSharper", "MethodOverloadWithOptionalParameter")]
9 | public interface ICustomLayoutedMenu
10 | {
11 | [Obsolete("Use the overload without instanceConsumer")]
12 | void AddSimpleButton(string text, Action onClick, Action instanceConsumer = null);
13 |
14 | ///
15 | /// Adds a simple button to this custom menu
16 | ///
17 | /// User-visible button text
18 | /// Button click action
19 | IMenuButton AddSimpleButton(string text, Action onClick);
20 |
21 | ///
22 | /// Adds a simple button to this custom menu
23 | ///
24 | /// User-visible button text, receives the pressed button as parameter
25 | /// Button click action
26 | IMenuButton AddSimpleButton(string text, Action onClick);
27 |
28 | [Obsolete("Use the overload without instanceConsumer")]
29 | void AddToggleButton(string text, Action onClick, Func getInitialState = null, Action instanceConsumer = null);
30 |
31 | ///
32 | /// Adds a toggle button to this custom menu
33 | ///
34 | /// User-visible button text
35 | /// This action will be called when button state is toggled
36 | /// (optional) this func will be called to get the initial state of this button. If will default to not-set if this is not provided.
37 | /// (optional) this action will be invoked when the button is instantiated
38 | IMenuToggle AddToggleButton(string text, Action onClick, Func getInitialState = null);
39 |
40 | [Obsolete("Use the overload without instanceConsumer")]
41 | void AddCustomButton(GameObject gameObject, Action instanceConsumer = null);
42 |
43 | ///
44 | /// Registers a custom button prefab. This prefab can be instantiated multiple times.
45 | ///
46 | /// Button prefab
47 | IMenuControl AddCustomButton(GameObject gameObject);
48 |
49 | [Obsolete("Use the overload without instanceConsumer")]
50 | void AddLabel(string text, Action instanceConsumer = null);
51 |
52 | ///
53 | /// Adds a label to custom menu
54 | ///
55 | /// User-visible text
56 | IMenuLabel AddLabel(string text);
57 |
58 | ///
59 | /// Adds an empty spot in menu layout.
60 | ///
61 | void AddSpacer();
62 |
63 | ///
64 | /// Adds an empty spot in menu layout.
65 | ///
66 | IMenuControl AddSpacerEx();
67 |
68 | ///
69 | /// This event is called when this menu's content root in created.
70 | /// Your mod code should subscribe to this event if you want to add custom gameobjects to the menu.
71 | ///
72 | event Action OnContentRootCreated;
73 |
74 | ///
75 | /// If true, created buttons will be more appropriate for quick menu usage (i.e. square buttons) as opposed to plain menu lists.
76 | ///
77 | void SetUseQuickMenuLikeComponents(bool isQuickMenu);
78 | }
79 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/ICustomShowableLayoutedMenu.cs:
--------------------------------------------------------------------------------
1 | namespace UIExpansionKit.API
2 | {
3 | public interface ICustomShowableLayoutedMenu : ICustomLayoutedMenu, IShowableMenu
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/IShowableMenu.cs:
--------------------------------------------------------------------------------
1 | namespace UIExpansionKit.API
2 | {
3 | public interface IShowableMenu
4 | {
5 | ///
6 | /// Shows this menu on top of parent menu.
7 | /// Requires the parent menu to be open.
8 | ///
9 | /// If another custom menu(s) is/are already showing, and this is false, the other menu(s) will be hidden; otherwise, this menu will be shown on top of it/them
10 | void Show(bool onTop = false);
11 |
12 | ///
13 | /// Hides this menu if it's showing. If there are menus showing on top of it, they will be hidden too.
14 | ///
15 | void Hide();
16 | }
17 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/LayoutDescription.cs:
--------------------------------------------------------------------------------
1 | namespace UIExpansionKit.API
2 | {
3 | ///
4 | /// This struct describes the required layout for a custom menu
5 | ///
6 | public struct LayoutDescription
7 | {
8 | ///
9 | /// Number of columns in grid layout of this menu. Does not change menu width.
10 | ///
11 | public int NumColumns;
12 |
13 | ///
14 | /// Height of single row in menu-units
15 | ///
16 | public int RowHeight;
17 |
18 | ///
19 | /// Number of rows visible at a time. Adding more rows will enable scrolling. Affects menu height.
20 | ///
21 | public int NumRows;
22 |
23 | public static LayoutDescription QuickMenu3Columns = new LayoutDescription {NumColumns = 3, RowHeight = 380 / 3, NumRows = 3};
24 | public static LayoutDescription QuickMenu4Columns = new LayoutDescription {NumColumns = 4, RowHeight = 95, NumRows = 4 };
25 | public static LayoutDescription WideSlimList = new LayoutDescription {NumColumns = 1, RowHeight = 50, NumRows = 8 };
26 |
27 | public LayoutDescription With(int? numColumns = null, int? rowHeight = null, int? numRows = null)
28 | {
29 | return new LayoutDescription
30 | {
31 | NumColumns = numColumns ?? NumColumns,
32 | RowHeight = rowHeight ?? RowHeight,
33 | NumRows = numRows ?? NumRows,
34 | };
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/UIExpansionKit/API/TaskUtilities.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using MelonLoader;
3 | using UIExpansionKit.API.Classes;
4 |
5 | namespace UIExpansionKit.API
6 | {
7 | ///
8 | /// This class contains utility methods for Task/async/await-based code
9 | ///
10 | public static class TaskUtilities
11 | {
12 | internal static readonly AwaitProvider ourMainThreadQueue = new("UIExpansionKit.ToMainThread");
13 | internal static readonly AwaitProvider ourFrameEndQueue = new("UIExpansionKit.FrameEnd");
14 |
15 | ///
16 | /// Returns an awaitable object used to return an async method's execution to the main thread.
17 | /// After the returned object is awaited, execution will continue on main thread inside of `Update` event
18 | /// Can also be used to wait for the next frame
19 | ///
20 | public static AwaitProvider.YieldAwaitable YieldToMainThread()
21 | {
22 | return ourMainThreadQueue.Yield();
23 | }
24 |
25 | ///
26 | /// Returns an awaitable object used to return an async method's execution to the main thread.
27 | /// After the returned object is awaited, execution will continue on main thread inside of `OnGUI` event
28 | ///
29 | public static AwaitProvider.YieldAwaitable YieldToFrameEnd()
30 | {
31 | return ourFrameEndQueue.Yield();
32 | }
33 |
34 | ///
35 | /// Adds a handler to a Task that prints a message to console if an exception is thrown within that task
36 | ///
37 | /// A string that will be included in the error message to identify the task
38 | public static void NoAwait(this Task task, string taskInfo = "Task")
39 | {
40 | task.ContinueWith(tsk =>
41 | {
42 | if (tsk.IsFaulted)
43 | MelonLogger.Error($"Free-floating {taskInfo} failed with exception: {tsk.Exception}");
44 | });
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/UIExpansionKit/ButtonFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MelonLoader;
3 | using TMPro;
4 | using UIExpansionKit.API;
5 | using UnityEngine;
6 | using UnityEngine.UI;
7 | using Object = UnityEngine.Object;
8 |
9 | namespace UIExpansionKit
10 | {
11 | internal static class ButtonFactory
12 | {
13 | internal static void CreateButtonForRegistration(ExpansionKitApi.ButtonRegistration registration, Transform root, bool isQuickMenu)
14 | {
15 | try
16 | {
17 | CreateButtonForRegistrationImpl(registration, root, isQuickMenu);
18 | }
19 | catch (Exception ex)
20 | {
21 | MelonLogger.Error($"Exception when creating a button for registration of {registration}: {ex}");
22 | }
23 | }
24 |
25 | private static void CreateButtonForRegistrationImpl(ExpansionKitApi.ButtonRegistration registration, Transform root, bool isQuickMenu)
26 | {
27 | if (registration.Prefab != null)
28 | {
29 | var newObject = Object.Instantiate(registration.Prefab, root, false);
30 | registration.InstanceConsumer?.Invoke(newObject);
31 | }
32 | else if(registration.Text != null)
33 | {
34 | var stuff = UiExpansionKitMod.Instance.StuffBundle;
35 |
36 | if (registration.Action != null)
37 | {
38 | var clickButtonPrefab = stuff.QuickMenuButton;
39 |
40 | var buttonInstance = Object.Instantiate(clickButtonPrefab, root, false);
41 | var textComponent = buttonInstance.GetComponentInChildren(true);
42 | textComponent.text = registration.Text;
43 | var legacyTextComponent = buttonInstance.GetComponentInChildren(true);
44 | legacyTextComponent.text = registration.Text;
45 | buttonInstance.GetComponent