├── .gitignore
├── EditorForks
├── Properties
│ └── AssemblyInfo.cs
├── UnityEditorForks.csproj
├── WorkaroundAssetDatabaseSaveAssetIfDirty.cs
├── WorkaroundUIToolkitMissingDefaultInspector.cs
├── WorkaroundUnityEditorSelectCreatedObject.cs
├── WorkaroundUnityMenuCommandContext.cs
├── WorkaroundUnityMissingAssetDatabaseFindAPI.cs
├── WorkaroundUnityPrefabEditingSafe.cs
└── WorkaroundUnityUIToolkitBrokenObjectSelector.cs
├── EngineForks
├── FixUnityUndoBlock.cs
├── MissingClasses
│ └── 2019-or-earlier
│ │ └── HelpBox.cs
├── Properties
│ └── AssemblyInfo.cs
├── UnityEngineForks.csproj
├── WorkaroundStarterAssetsDeletedInputFirstPersonController.cs
├── WorkaroundStarterAssetsDeletedInputStarterAssetsInputs.cs
├── WorkaroundUnityIMGUISetColor.cs
├── WorkaroundUnityInternal.cs
├── WorkaroundUnityMissingEditorVersion.cs
├── WorkaroundUnityMissingGetComponentsOnAncestors.cs
├── WorkaroundUnityMissingPlayerPrefsAPI.cs
├── WorkaroundUnityMissingTryGetComponent.cs
├── WorkaroundUnityTextGeneratorMissingErrorReporting.cs
├── WorkaroundUnityTexture2DPixelsPerPoint.cs
├── WorkaroundUnityUIToolkitButtonsWithImages.cs
├── WorkaroundUnityUIToolkitFindOrCreate.cs
├── WorkaroundUnityUIToolkitFoldoutToggleIsPrivate.cs
├── WorkaroundUnityUIToolkitMarginsAll.cs
└── WorkaroundUnityUIToolkitToggleBrokenLabelBug.cs
├── PublishersFork.sln
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Critical! Do not commit your Unity folder!
2 | UnityEditorFolderSymlink/
3 |
4 | # IDEs
5 | .idea
6 |
7 | # Microsoft C# build artifacts
8 | EditorForks/bin/
9 | EditorForks/obj/
10 | EngineForks/bin/
11 | EngineForks/obj/
12 | UnityEditorFolderSymlink
13 |
--------------------------------------------------------------------------------
/EditorForks/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("UnityEditorForks")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("UnityEditorForks")]
12 | [assembly: AssemblyCopyright("Copyright © 2022")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("E43B98FF-ACF1-4561-B21E-355F0D4B5113")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/EditorForks/UnityEditorForks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {E43B98FF-ACF1-4561-B21E-355F0D4B5113}
8 | Library
9 | Properties
10 | UnityEditorForks
11 | UnityEditorForks
12 | v4.8
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | ..\UnityEditorFolderSymlink\Data\Managed\UnityEditor.dll
41 |
42 |
43 | ..\UnityEditorFolderSymlink\Data\Managed\UnityEngine.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
64 |
--------------------------------------------------------------------------------
/EditorForks/WorkaroundAssetDatabaseSaveAssetIfDirty.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using UnityEditor;
4 |
5 | namespace UnityEditorForks
6 | {
7 | ///
8 | /// c.f. this post: https://forum.unity.com/threads/resource-assetdatabase-saveassetifdirty-fix-backport-polyfill.1410765/
9 | ///
10 | /// The Unity core API call "SaveAssetIfDirty" isn't included in a lot of current Unity versions, and the set for
11 | /// which it is/isn't present is non-trivial. This class works around that by providing one method call that works
12 | /// on all versions of Unity.
13 | ///
14 | public class WorkaroundAssetDatabaseSaveAssetIfDirty
15 | {
16 | static bool _saveIfDirtyMethodRetrieved;
17 | static MethodInfo _saveIfDirtyObjMethod;
18 | static MethodInfo _saveIfDirtyGuidMethod;
19 |
20 | static bool cacheReflectionsAndReturnResult(bool useGUID)
21 | {
22 | if (!_saveIfDirtyMethodRetrieved)
23 | {
24 | _saveIfDirtyMethodRetrieved = true;
25 | // fail silently
26 | try
27 | {
28 | var assembiles = AppDomain.CurrentDomain.GetAssemblies();
29 | foreach (var assembly in assembiles)
30 | {
31 | if (assembly.GetName().Name == "UnityEditor")
32 | {
33 | var assetDatabaseType = assembly.GetType("UnityEditor.AssetDatabase", throwOnError: true);
34 | _saveIfDirtyObjMethod = assetDatabaseType.GetMethod("SaveAssetIfDirty", new Type[] { typeof(UnityEngine.Object) });
35 | _saveIfDirtyGuidMethod = assetDatabaseType.GetMethod("SaveAssetIfDirty", new Type[] { typeof(GUID) });
36 | break;
37 | }
38 | }
39 | if (useGUID)
40 | return _saveIfDirtyGuidMethod != null;
41 | else
42 | return _saveIfDirtyObjMethod != null;
43 |
44 | }
45 | catch (Exception)
46 | {
47 | return false;
48 | }
49 | }
50 |
51 | if (useGUID)
52 | return _saveIfDirtyGuidMethod != null;
53 | else
54 | return _saveIfDirtyObjMethod != null;
55 | }
56 |
57 | static object[] _parameters = new object[1];
58 |
59 | public static void SaveAssetIfDirty(UnityEngine.Object obj)
60 | {
61 | #if UNITY_2021_2_OR_NEWER
62 | // Available in all versions.
63 | AssetDatabase.SaveAssetIfDirty(this);
64 | #elif UNITY_2020_3_OR_NEWER
65 | try
66 | {
67 | // Available in some:
68 | // Unity 2020.3.16+ has it
69 | // Unity 2021.1.17+ has it
70 | // Unity 2021.2.0+ has it
71 | if (cacheReflectionsAndReturnResult(useGUID: false))
72 | {
73 | _parameters[0] = obj;
74 | _saveIfDirtyObjMethod.Invoke(null, _parameters);
75 | }
76 | else
77 | {
78 | AssetDatabase.SaveAssets();
79 | }
80 | }
81 | catch(Exception)
82 | {
83 | AssetDatabase.SaveAssets();
84 | }
85 | #else
86 | // SaveAssetIfDirty is never available.
87 | AssetDatabase.SaveAssets();
88 | #endif
89 | }
90 |
91 | public static void SaveAssetIfDirty(GUID guid)
92 | {
93 | #if UNITY_2021_2_OR_NEWER
94 | // Available in all versions.
95 | AssetDatabase.SaveAssetIfDirty(this);
96 | #elif UNITY_2020_3_OR_NEWER
97 | // Available in some:
98 | // Unity 2020.3.16+ has it
99 | // Unity 2021.1.17+ has it
100 | // Unity 2021.2.0+ has it
101 | if (cacheReflectionsAndReturnResult(useGUID: true))
102 | {
103 | _parameters[0] = guid;
104 | _saveIfDirtyObjMethod.Invoke(null, _parameters);
105 | }
106 | else
107 | {
108 | AssetDatabase.SaveAssets();
109 | }
110 | #else
111 | // SaveAssetIfDirty is never available.
112 | AssetDatabase.SaveAssets();
113 | #endif
114 | }
115 | }
116 | }
--------------------------------------------------------------------------------
/EditorForks/WorkaroundUIToolkitMissingDefaultInspector.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEditor;
4 | using UnityEditor.UIElements;
5 | using UnityEngine;
6 | using UnityEngine.UIElements;
7 |
8 | ///
9 | /// DrawDefaultInspector was a core feature of the Editor APIs for the 15+ years. The UIToolkit team decided to ignore
10 | /// it and break it and provide no alternative in their new system until 3 years after releasing the 'production ready'
11 | /// version. Unity 2022 and later have this method but it was never backported despite having zero dependencies and
12 | /// despite being a required part of Unity's own APIs.
13 | ///
14 | public class WorkaroundUIToolkitMissingDefaultInspector
15 | {
16 | ///
17 | /// This implementation provided by UIToolkit team via the forums,
18 | /// see the pre-release version here: https://forum.unity.com/threads/property-drawers.595369/#post-5426619
19 | ///
20 | ///
21 | ///
22 | ///
23 | public static void FillDefaultInspector( VisualElement container, SerializedObject serializedObject, bool hideScript)
24 | {
25 | SerializedProperty property = serializedObject.GetIterator();
26 | if (property.NextVisible(true)) // Expand first child.
27 | {
28 | do
29 | {
30 | if (property.propertyPath == "m_Script" && hideScript)
31 | {
32 | continue;
33 | }
34 | var field = new PropertyField(property);
35 | field.name = "PropertyField:" + property.propertyPath;
36 |
37 |
38 | if (property.propertyPath == "m_Script" && serializedObject.targetObject != null)
39 | {
40 | field.SetEnabled(false);
41 | }
42 |
43 | container.Add(field);
44 | }
45 | while (property.NextVisible(false));
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/EditorForks/WorkaroundUnityEditorSelectCreatedObject.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEditor;
3 |
4 | namespace UnityEditorForks
5 | {
6 | ///
7 | /// After you create an object in Editor, in 99.9% of cases you immediately want to let the user rename it - Unity's
8 | /// staff themselves make this the default behaviour for all Unity code, but so far have refused to let anyone else do it directly,
9 | /// so we have to ping the (public!) methods, and implement some (required!) artificial delays.
10 | ///
11 | /// Usage:
12 | /// 1. Create a new object in script - e.g. "var go = new GameObject()"
13 | /// 2. Call "go.SelectAndRename();"
14 | ///
15 | /// ...this will cause the new object to be automatically selected in the Editor *and* the UI built-in 'rename new object'
16 | /// interface to be triggered, allowing the user to interactively name it without wasting time and keystrokes.
17 | ///
18 | public static class WorkaroundUnityEditorSelectCreatedObject
19 | {
20 | public static void SelectAndRename( this GameObject go )
21 | {
22 | Selection.activeGameObject = go;
23 |
24 | #if UNITY_2021_1_OR_NEWER
25 | EditorApplication.update += Delay1Frame_Rename;
26 | #else // Unity broke this in Unity 2021-ish; since approx Unity-2021 you need to use the other approach
27 | // Workaround for 10+ years bug that Unity still doesn't let you select things in Hierarchy:
28 | ((EditorWindow)Resources.FindObjectsOfTypeAll( typeof(UnityEditor.Editor).Assembly.GetType( "UnityEditor.SceneHierarchyWindow" ) )[0]).Focus();
29 | #endif
30 | }
31 |
32 | /// Unity engineers couldn't make 'create object' execute correctly within a single frame, requiring MULTIPLE FRAMES before you can call the 'real' edit-name method
33 | private static void Delay1Frame_Rename()
34 | {
35 | EditorApplication.update -= Delay1Frame_Rename;
36 | EditorApplication.update += Rename;
37 | }
38 |
39 | static void Rename()
40 | {
41 | EditorApplication.update -= Rename;
42 | ((EditorWindow)Resources.FindObjectsOfTypeAll( typeof(UnityEditor.Editor).Assembly.GetType( "UnityEditor.SceneHierarchyWindow" ) )[0]).Focus();
43 | EditorApplication.ExecuteMenuItem( "Window/General/Hierarchy" );
44 | EditorApplication.ExecuteMenuItem( "Edit/Rename" );
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/EditorForks/WorkaroundUnityMenuCommandContext.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 | using UnityEngine;
3 |
4 | namespace UnityEditorForks
5 | {
6 | ///
7 | /// Unity's MenuCommand is missing a key feature: 'which GameObject did the user right-click to execute this menu?',
8 | /// which you typically want when using Unity's [MenuItem] attribute.
9 | ///
10 | /// The code in this class is based on Unity's own API docs where they read Selection.* when command.context is null.
11 | ///
12 | public static class WorkaroundUnityMenuCommandContext
13 | {
14 | ///
15 | ///
16 | ///
17 | /// The Unity-API 'command.context' if not-null, otherwise the selected GameObject from Hierarchy panel
18 | public static GameObject ContextGameObject( this MenuCommand command )
19 | {
20 | GameObject _candidate;
21 | if( command != null && command.context != null )
22 | _candidate = (command.context as GameObject);
23 | else if( Selection.activeGameObject != null )
24 | _candidate = Selection.activeGameObject;
25 | else
26 | _candidate = null;
27 |
28 | return _candidate;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/EditorForks/WorkaroundUnityMissingAssetDatabaseFindAPI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using UnityEditor;
6 | using UnityEngine;
7 | using Object = UnityEngine.Object;
8 |
9 | namespace UnityEditorForks
10 | {
11 | ///
12 | /// Unity staff know that 'AssetDatabase.FindAssets' is broken in multiple core cases, but refuse to
13 | /// fix it (and keep refusing to update their docs to admit that they know it's broken - so the docs are also wrong).
14 | /// This class aims to fix the common cases that Unity won't. Most of these could be fixed in fewer lines of code by
15 | /// Unity fixing their internal classes e.g. SearchFilter - Unity staff don't seem to understand what 'internal' keyword
16 | /// in C# actuall means.
17 | ///
18 | /// Without heavy use of complex Reflection calls, only Unity staff can access the true Find API in Unity 2019-2023
19 | /// and the replacement ("use magic strings with the AssetDatabase.Find methods") is extremely fragile and incorrectly documented
20 | /// (I logged bugs against this more than
21 | /// two years ago, that were accepted, but Unity still hasn't fixed them - still hasn't corrected their own docs).
22 | ///
23 | /// This class wraps the bad API call into a sensible one, and adds some obvious missing API calls they should/would
24 | /// have included if they'd accepted input from real users before publishing it.
25 | ///
26 | /// 2023 UPDATE: this also mostly fixes the multiple-years-old bug in Unity that if the Type you're searching for doesn't exist
27 | /// in a file with the same literal name (due to sheer laziness by Unity staff) then Unity returns incorrect results.
28 | /// Unity's bug breaks all searches for T where T is itself Generic (e.g. T = MyClass). Unity can't be
29 | /// bothered to throw an exception, or to document this known failing - they just return wrong results instead.
30 | ///
31 | public static class WorkaroundUnityMissingAssetDatabaseFindAPI
32 | {
33 | ///
34 | /// Unity's new FindAssets API fails to support type-search, it supports only 'subtypes of ScriptableObject search',
35 | /// because they didn't think at all about the names or use-cases of their new API when creating it. So the obvious
36 | /// search-by-type will ALWAYS fail (by design - Unity's bad design) if you need to find 'assets that contain this
37 | /// type'. This method fixes that mistake in Unity's codebase by letting you search for "assets that contain
38 | /// components of type T".
39 | ///
40 | ///
41 | ///
42 | ///
43 | public static List FindAssetPrefabsByComponentType( bool debugSettingsDiscovery = false ) where T : UnityEngine.Object
44 | {
45 | var prefabAssets = AssetDatabase.FindAssets( "t:prefab" );
46 |
47 | var foundComponentInstances = new List();
48 | for( int i = 0; i < prefabAssets.Length; i++ )
49 | {
50 | if( AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath( prefabAssets[i] ) ).TryGetComponent( out var component ) )
51 | foundComponentInstances.Add( component );
52 | }
53 |
54 | if( debugSettingsDiscovery ) Debug.Log( "Found objects at: " + string.Join( ",", foundComponentInstances.Select( settings => AssetDatabase.GetAssetPath( settings ) ) ) );
55 |
56 | return foundComponentInstances;
57 | }
58 |
59 | ///
60 | /// Unity has an equivalent API call but for the past 4+ years they've refused to let non-Unity staff access it; you
61 | /// can access it via reflection and override their incorrect use of 'internal', or (their official recommendation)
62 | /// you can fake it using various combinations of their undocumented features (e.g. "a:" etc) of FindAssets().
63 | ///
64 | /// It is particularly frustrating that we're converting a Type to a string so that Unity's code can convert it back
65 | /// to a Type, and re-create the generic we had in the first place but which a Unity team refuses to let anyone
66 | /// else call directly.
67 | ///
68 | ///
69 | ///
70 | ///
71 | public static List FindAssetsByType( bool debugSettingsDiscovery = false ) where T : UnityEngine.Object
72 | {
73 | /** Unity's API is wrongly documented - it does NOT support 'types' it only supports 'classnames'; we have
74 | * to do the conversion manually when those things differ
75 | */
76 | if( typeof(T).IsGenericType )
77 | {
78 | var settingsFileAssets = AssetDatabase.FindAssets( "t:ScriptableObject" );
79 | var foundObjects = new List();
80 | for( int i = 0; i < settingsFileAssets.Length; i++ )
81 | {
82 | var so = AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath( settingsFileAssets[i] ) );
83 | if( so is T )
84 | foundObjects.Add( so as T );
85 | }
86 |
87 | if( debugSettingsDiscovery ) Debug.Log( "Found " + settingsFileAssets.Length + " assets of type (" + typeof(T) + "): " + string.Join( ",", foundObjects.Select( settings => AssetDatabase.GetAssetPath( settings ) ) ) );
88 |
89 | return foundObjects;
90 | }
91 | else
92 | {
93 | var settingsFileAssets = AssetDatabase.FindAssets( "t:" + typeof(T).Name );
94 | var foundObjects = new List();
95 | for( int i = 0; i < settingsFileAssets.Length; i++ )
96 | {
97 | foundObjects.Add( AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath( settingsFileAssets[i] ) ) );
98 | }
99 |
100 | if( debugSettingsDiscovery ) Debug.Log( "Found objects at: " + string.Join( ",", foundObjects.Select( settings => AssetDatabase.GetAssetPath( settings ) ) ) );
101 |
102 | return foundObjects;
103 | }
104 | }
105 |
106 | ///
107 | /// Identical to except that it's non-generic (for when you're working with
108 | /// runtime-resolved Type objects, instead of compile-time resolved classnames)
109 | ///
110 | ///
111 | ///
112 | ///
113 | public static List