private void OnGUISceneCheck(VRC_SceneDescriptor scene)
        {
            CheckUploadChanges(scene);

#if !VRC_SDK_VRCSDK2 && !VRC_SDK_VRCSDK3
            bool isSdk3Scene = false;
#elif VRC_SDK_VRCSDK2 && !VRC_SDK_VRCSDK3
            bool isSdk3Scene = false;
#elif !VRC_SDK_VRCSDK2 && VRC_SDK_VRCSDK3
            bool isSdk3Scene = true; //Do we actually use this variable anywhere?
#else
            bool isSdk3Scene = scene as VRCSceneDescriptor != null;
#endif

            List <VRC_EventHandler> sdkBaseEventHandlers =
                new List <VRC_EventHandler>(Object.FindObjectsOfType <VRC_EventHandler>());
#if VRC_SDK_VRCSDK2
            if (isSdk3Scene == false)
            {
                for (int i = sdkBaseEventHandlers.Count - 1; i >= 0; --i)
                {
                    if (sdkBaseEventHandlers[i] as VRCSDK2.VRC_EventHandler)
                    {
                        sdkBaseEventHandlers.RemoveAt(i);
                    }
                }
            }
#endif
            if (sdkBaseEventHandlers.Count > 0)
            {
                _builder.OnGUIError(scene,
                                    "You have Event Handlers in your scene that are not allowed in this build configuration.",
                                    delegate
                {
                    List <Object> gos = sdkBaseEventHandlers.ConvertAll(item => (Object)item.gameObject);
                    Selection.objects = gos.ToArray();
                },
                                    delegate
                {
                    foreach (VRC_EventHandler eh in sdkBaseEventHandlers)
                    {
#if VRC_SDK_VRCSDK2
                        GameObject go = eh.gameObject;
                        if (isSdk3Scene == false)
                        {
                            if (VRC_SceneDescriptor.Instance as VRCSDK2.VRC_SceneDescriptor != null)
                            {
                                go.AddComponent <VRCSDK2.VRC_EventHandler>();
                            }
                        }
#endif
                        Object.DestroyImmediate(eh);
                    }
                });
            }

            Vector3 g = Physics.gravity;
            if (Math.Abs(g.x) > float.Epsilon || Math.Abs(g.z) > float.Epsilon)
            {
                _builder.OnGUIWarning(scene,
                                      "Gravity vector is not straight down. Though we support different gravity, player orientation is always 'upwards' so things don't always behave as you intend.",
                                      delegate { SettingsService.OpenProjectSettings("Project/Physics"); }, null);
            }
            if (g.y > 0)
            {
                _builder.OnGUIWarning(scene,
                                      "Gravity vector is not straight down, inverted or zero gravity will make walking extremely difficult.",
                                      delegate { SettingsService.OpenProjectSettings("Project/Physics"); }, null);
            }
            if (Math.Abs(g.y) < float.Epsilon)
            {
                _builder.OnGUIWarning(scene,
                                      "Zero gravity will make walking extremely difficult, though we support different gravity, player orientation is always 'upwards' so this may not have the effect you're looking for.",
                                      delegate { SettingsService.OpenProjectSettings("Project/Physics"); }, null);
            }

            if (CheckFogSettings())
            {
                _builder.OnGUIWarning(
                    scene,
                    "Fog shader stripping is set to Custom, this may lead to incorrect or unnecessary shader variants being included in the build. You should use Automatic unless you change the fog mode at runtime.",
                    delegate { SettingsService.OpenProjectSettings("Project/Graphics"); },
                    delegate
                {
                    EnvConfig.SetFogSettings(
                        new EnvConfig.FogSettings(EnvConfig.FogSettings.FogStrippingMode.Automatic));
                });
            }

            if (scene.autoSpatializeAudioSources)
            {
                _builder.OnGUIWarning(scene,
                                      "Your scene previously used the 'Auto Spatialize Audio Sources' feature. This has been deprecated, press 'Fix' to disable. Also, please add VRC_SpatialAudioSource to all your audio sources. Make sure Spatial Blend is set to 3D for the sources you want to spatialize.",
                                      null,
                                      delegate { scene.autoSpatializeAudioSources = false; }
                                      );
            }

            AudioSource[] audioSources = Object.FindObjectsOfType <AudioSource>();
            foreach (AudioSource a in audioSources)
            {
                if (a.GetComponent <ONSPAudioSource>() != null)
                {
                    _builder.OnGUIWarning(scene,
                                          "Found audio source(s) using ONSP, this is deprecated. Press 'fix' to convert to VRC_SpatialAudioSource.",
                                          delegate { Selection.activeObject = a.gameObject; },
                                          delegate
                    {
                        Selection.activeObject = a.gameObject;
                        AutoAddSpatialAudioComponents.ConvertONSPAudioSource(a);
                    }
                                          );
                    break;
                }
                else if (a.GetComponent <VRC_SpatialAudioSource>() == null)
                {
                    string msg =
                        "Found 3D audio source with no VRC Spatial Audio component, this is deprecated. Press 'fix' to add a VRC_SpatialAudioSource.";
                    if (IsAudioSource2D(a))
                    {
                        msg =
                            "Found 2D audio source with no VRC Spatial Audio component, this is deprecated. Press 'fix' to add a (disabled) VRC_SpatialAudioSource.";
                    }

                    _builder.OnGUIWarning(scene, msg,
                                          delegate { Selection.activeObject = a.gameObject; },
                                          delegate
                    {
                        Selection.activeObject = a.gameObject;
                        AutoAddSpatialAudioComponents.AddVRCSpatialToBareAudioSource(a);
                    }
                                          );
                    break;
                }
            }

            if (VRCSdkControlPanel.HasSubstances())
            {
                _builder.OnGUIWarning(scene,
                                      "One or more scene objects have Substance materials. This is not supported and may break in game. Please bake your Substances to regular materials.",
                                      () => { Selection.objects = VRCSdkControlPanel.GetSubstanceObjects(); },
                                      null);
            }

#if VRC_SDK_VRCSDK2
            foreach (VRC_DataStorage ds in Object.FindObjectsOfType <VRC_DataStorage>())
            {
                VRCSDK2.VRC_ObjectSync os = ds.GetComponent <VRCSDK2.VRC_ObjectSync>();
                if (os != null && os.SynchronizePhysics)
                {
                    _builder.OnGUIWarning(scene, ds.name + " has a VRC_DataStorage and VRC_ObjectSync, with SynchronizePhysics enabled.",
                                          delegate { Selection.activeObject = os.gameObject; }, null);
                }
            }
#endif

            string vrcFilePath = UnityWebRequest.UnEscapeURL(EditorPrefs.GetString("lastVRCPath"));
            if (!string.IsNullOrEmpty(vrcFilePath) &&
                ValidationHelpers.CheckIfAssetBundleFileTooLarge(ContentType.World, vrcFilePath, out int fileSize))
            {
                _builder.OnGUIWarning(scene,
                                      ValidationHelpers.GetAssetBundleOverSizeLimitMessageSDKWarning(ContentType.World, fileSize), null,
                                      null);
            }

            foreach (GameObject go in Object.FindObjectsOfType <GameObject>())
            {
                if (go.transform.parent == null)
                {
                    // check root game objects
#if UNITY_ANDROID
                    IEnumerable <Shader> illegalShaders = VRCSDK2.Validation.WorldValidation.FindIllegalShaders(go);
                    foreach (Shader s in illegalShaders)
                    {
                        _builder.OnGUIWarning(scene, "World uses unsupported shader '" + s.name + "'. This could cause low performance or future compatibility issues.", null, null);
                    }
#endif
                }
                else
                {
                    // Check sibling game objects
                    for (int idx = 0; idx < go.transform.parent.childCount; ++idx)
                    {
                        Transform t = go.transform.parent.GetChild(idx);
                        if (t == go.transform)
                        {
                            continue;
                        }

#if VRC_SDK_VRCSDK2
                        bool allowedType = (t.GetComponent <VRCSDK2.VRC_ObjectSync>() ||
                                            t.GetComponent <VRCSDK2.VRC_SyncAnimation>() ||
                                            t.GetComponent <VRC_SyncVideoPlayer>() ||
                                            t.GetComponent <VRC_SyncVideoStream>());
                        if (t.name != go.transform.name || allowedType)
                        {
                            continue;
                        }
#else
                        if (t.name != go.transform.name)
                        {
                            continue;
                        }
#endif

                        string    path = t.name;
                        Transform p    = t.parent;
                        while (p != null)
                        {
                            path = p.name + "/" + path;
                            p    = p.parent;
                        }

                        _builder.OnGUIWarning(scene,
                                              "Sibling objects share the same path, which may break network events: " + path,
                                              delegate
                        {
                            List <Object> gos = new List <Object>();
                            for (int c = 0; c < go.transform.parent.childCount; ++c)
                            {
                                if (go.transform.parent.GetChild(c).name == go.name)
                                {
                                    gos.Add(go.transform.parent.GetChild(c).gameObject);
                                }
                            }
                            Selection.objects = gos.ToArray();
                        },
                                              delegate
                        {
                            List <Object> gos = new List <Object>();
                            for (int c = 0; c < go.transform.parent.childCount; ++c)
                            {
                                if (go.transform.parent.GetChild(c).name == go.name)
                                {
                                    gos.Add(go.transform.parent.GetChild(c).gameObject);
                                }
                            }
                            Selection.objects = gos.ToArray();
                            for (int i = 0; i < gos.Count; ++i)
                            {
                                gos[i].name = gos[i].name + "-" + i.ToString("00");
                            }
                        });
                        break;
                    }
                }
            }

            // detect dynamic materials and prefabs with identical names (since these could break triggers)
            VRC_Trigger[]     triggers  = Tools.FindSceneObjectsOfTypeAll <VRC_Trigger>();
            List <GameObject> prefabs   = new List <GameObject>();
            List <Material>   materials = new List <Material>();

#if VRC_SDK_VRCSDK2
            AssetExporter.FindDynamicContent(ref prefabs, ref materials);
#elif VRC_SDK_VRCSDK3
            AssetExporter.FindDynamicContent(ref prefabs, ref materials);
#endif

            foreach (VRC_Trigger t in triggers)
            {
                foreach (VRC_Trigger.TriggerEvent triggerEvent in t.Triggers)
                {
                    foreach (VRC_EventHandler.VrcEvent e in triggerEvent.Events.Where(evt =>
                                                                                      evt.EventType == VRC_EventHandler.VrcEventType.SpawnObject))
                    {
                        GameObject go =
                            AssetDatabase.LoadAssetAtPath(e.ParameterString, typeof(GameObject)) as GameObject;
                        if (go == null)
                        {
                            continue;
                        }
                        foreach (GameObject existing in prefabs)
                        {
                            if (go == existing || go.name != existing.name)
                            {
                                continue;
                            }
                            _builder.OnGUIWarning(scene,
                                                  "Trigger prefab '" + AssetDatabase.GetAssetPath(go).Replace(".prefab", "") +
                                                  "' has same name as a prefab in another folder. This may break the trigger.",
                                                  delegate { Selection.objects = new Object[] { go }; },
                                                  delegate
                            {
                                AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(go),
                                                          go.name + "-" + go.GetInstanceID());
                                AssetDatabase.Refresh();
                                e.ParameterString = AssetDatabase.GetAssetPath(go);
                            });
                        }
                    }

                    foreach (VRC_EventHandler.VrcEvent e in triggerEvent.Events.Where(evt =>
                                                                                      evt.EventType == VRC_EventHandler.VrcEventType.SetMaterial))
                    {
                        Material mat = AssetDatabase.LoadAssetAtPath <Material>(e.ParameterString);
                        if (mat == null || mat.name.Contains("(Instance)"))
                        {
                            continue;
                        }
                        foreach (Material existing in materials)
                        {
                            if (mat == existing || mat.name != existing.name)
                            {
                                continue;
                            }
                            _builder.OnGUIWarning(scene,
                                                  "Trigger material '" + AssetDatabase.GetAssetPath(mat).Replace(".mat", "") +
                                                  "' has same name as a material in another folder. This may break the trigger.",
                                                  delegate { Selection.objects = new Object[] { mat }; },
                                                  delegate
                            {
                                AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(mat),
                                                          mat.name + "-" + mat.GetInstanceID());
                                AssetDatabase.Refresh();
                                e.ParameterString = AssetDatabase.GetAssetPath(mat);
                            });
                        }
                    }
                }
            }
        }
        public override void OnGUIAvatar(VRC_AvatarDescriptor avatar)
        {
            EditorGUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle);
            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();

            GUILayout.Label("Offline Testing", VRCSdkControlPanel.infoGuiStyle);
            GUILayout.Label(
                "Before uploading your avatar you may build and test it in the VRChat client. Other users will not able to see the test avatar.",
                VRCSdkControlPanel.infoGuiStyle);

            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();

            GUI.enabled = (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows ||
                           EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64) &&
                          (_builder.NoGuiErrorsOrIssues() || Core.APIUser.CurrentUser.developerType ==
                           Core.APIUser.DeveloperType.Internal);
            if (GUILayout.Button("Build & Test"))
            {
                if (Core.APIUser.CurrentUser.canPublishAvatars)
                {
                    VRC_SdkBuilder.ExportAndTestAvatarBlueprint(avatar.gameObject);

                    EditorUtility.DisplayDialog("VRChat SDK", "Test Avatar Built", "OK");
                }
                else
                {
                    VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                }
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space();

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            EditorGUILayout.Separator();

            EditorGUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle);
            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();

            GUILayout.Label("Online Publishing", VRCSdkControlPanel.infoGuiStyle);
            GUILayout.Label(
                "In order for other people to see your avatar in VRChat it must be built and published to our game servers.",
                VRCSdkControlPanel.infoGuiStyle);

            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();

            GUI.enabled = _builder.NoGuiErrorsOrIssues() ||
                          Core.APIUser.CurrentUser.developerType == Core.APIUser.DeveloperType.Internal;
            if (GUILayout.Button(VRCSdkControlPanel.GetBuildAndPublishButtonString()))
            {
                bool buildBlocked = !VRCBuildPipelineCallbacks.OnVRCSDKBuildRequested(VRCSDKRequestedBuildType.Avatar);
                if (!buildBlocked)
                {
                    if (Core.APIUser.CurrentUser.canPublishAvatars)
                    {
                        EnvConfig.FogSettings originalFogSettings = EnvConfig.GetFogSettings();
                        EnvConfig.SetFogSettings(
                            new EnvConfig.FogSettings(EnvConfig.FogSettings.FogStrippingMode.Custom, true, true, true));

#if UNITY_ANDROID
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", true);
#else
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", false);
#endif

                        VRC_SdkBuilder.shouldBuildUnityPackage = VRCSdkControlPanel.FutureProofPublishEnabled;
                        VRC_SdkBuilder.ExportAndUploadAvatarBlueprint(avatar.gameObject);

                        EnvConfig.SetFogSettings(originalFogSettings);

                        // this seems to workaround a Unity bug that is clearing the formatting of two levels of Layout
                        // when we call the upload functions
                        return;
                    }
                    else
                    {
                        VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                    }
                }
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space();

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            GUI.enabled = true;
        }