Add VRChat and GitHub packages to Unity project

This commit is contained in:
gunnbr committed 2021-01-18 09:14:18 -08:00
1 parent 0334a52b21
commit 85538aaccc
3297 files changed
+847877

No files matched your search

@@ -0,0 +1,9 @@
public interface IVRCSdkControlPanelBuilder
{
void ShowSettingsOptions();
bool IsValidBuilder(out string message);
void ShowBuilder();
void RegisterBuilder(VRCSdkControlPanel baseBuilder);
void SelectAllComponents();
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 310a760e312f2984e85eece367bab19a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,246 @@
using UnityEditor;
using UnityEngine;
using VRC.Core;
[ExecuteInEditMode]
public partial class VRCSdkControlPanel : EditorWindow
{
public static VRCSdkControlPanel window;
[MenuItem("VRChat SDK/Show Control Panel", false, 600)]
static void ShowControlPanel()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
VRC.Core.API.SetOnlineMode(true, "vrchat");
ConfigManager.RemoteConfig.Init(() => ShowControlPanel());
return;
}
window = (VRCSdkControlPanel)EditorWindow.GetWindow(typeof(VRCSdkControlPanel));
window.titleContent.text = "VRChat SDK";
window.minSize = new Vector2(SdkWindowWidth + 4, 600);
window.maxSize = new Vector2(SdkWindowWidth + 4, 2000);
window.Init();
window.Show();
}
void Update()
{
Repaint();
}
public static GUIStyle titleGuiStyle;
public static GUIStyle boxGuiStyle;
public static GUIStyle infoGuiStyle;
public static GUIStyle listButtonStyleEven;
public static GUIStyle listButtonStyleOdd;
public static GUIStyle listButtonStyleSelected;
public static GUIStyle scrollViewSeparatorStyle;
public static GUIStyle searchBarStyle;
void InitializeStyles()
{
titleGuiStyle = new GUIStyle();
titleGuiStyle.fontSize = 15;
titleGuiStyle.fontStyle = FontStyle.BoldAndItalic;
titleGuiStyle.alignment = TextAnchor.MiddleCenter;
titleGuiStyle.wordWrap = true;
if (EditorGUIUtility.isProSkin)
titleGuiStyle.normal.textColor = Color.white;
else
titleGuiStyle.normal.textColor = Color.black;
boxGuiStyle = new GUIStyle();
if (EditorGUIUtility.isProSkin)
{
boxGuiStyle.normal.background = CreateBackgroundColorImage(new Color(0.3f, 0.3f, 0.3f));
boxGuiStyle.normal.textColor = Color.white;
}
else
{
boxGuiStyle.normal.background = CreateBackgroundColorImage(new Color(0.85f, 0.85f, 0.85f));
boxGuiStyle.normal.textColor = Color.black;
}
infoGuiStyle = new GUIStyle();
infoGuiStyle.wordWrap = true; ;
if (EditorGUIUtility.isProSkin)
infoGuiStyle.normal.textColor = Color.white;
else
infoGuiStyle.normal.textColor = Color.black;
infoGuiStyle.margin = new RectOffset(10, 10, 10, 10);
listButtonStyleEven = new GUIStyle();
listButtonStyleEven.margin = new RectOffset(0, 0, 0, 0);
listButtonStyleEven.border = new RectOffset(0, 0, 0, 0);
if (EditorGUIUtility.isProSkin)
{
listButtonStyleEven.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
listButtonStyleEven.normal.background = CreateBackgroundColorImage(new Color(0.540f, 0.540f, 0.54f));
}
else
{
listButtonStyleEven.normal.textColor = Color.black;
listButtonStyleEven.normal.background = CreateBackgroundColorImage(new Color(0.85f, 0.85f, 0.85f));
}
listButtonStyleOdd = new GUIStyle();
listButtonStyleOdd.margin = new RectOffset(0, 0, 0, 0);
listButtonStyleOdd.border = new RectOffset(0, 0, 0, 0);
if (EditorGUIUtility.isProSkin)
{
listButtonStyleOdd.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
//listButtonStyleOdd.normal.background = CreateBackgroundColorImage(new Color(0.50f, 0.50f, 0.50f));
}
else
{
listButtonStyleOdd.normal.textColor = Color.black;
listButtonStyleOdd.normal.background = CreateBackgroundColorImage(new Color(0.90f, 0.90f, 0.90f));
}
listButtonStyleSelected = new GUIStyle();
listButtonStyleSelected.normal.textColor = Color.white;
listButtonStyleSelected.margin = new RectOffset(0, 0, 0, 0);
if (EditorGUIUtility.isProSkin)
{
listButtonStyleSelected.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
listButtonStyleSelected.normal.background = CreateBackgroundColorImage(new Color(0.4f, 0.4f, 0.4f));
}
else
{
listButtonStyleSelected.normal.textColor = Color.black;
listButtonStyleSelected.normal.background = CreateBackgroundColorImage(new Color(0.75f, 0.75f, 0.75f));
}
scrollViewSeparatorStyle = new GUIStyle("Toolbar");
scrollViewSeparatorStyle.fixedWidth = SdkWindowWidth + 10;
scrollViewSeparatorStyle.fixedHeight = 4;
scrollViewSeparatorStyle.margin.top = 1;
searchBarStyle = new GUIStyle("Toolbar");
searchBarStyle.fixedWidth = SdkWindowWidth;
searchBarStyle.fixedHeight = 23;
searchBarStyle.padding.top = 3;
}
void Init()
{
InitializeStyles();
ResetIssues();
InitAccount();
}
void OnEnable()
{
OnEnableAccount();
AssemblyReloadEvents.afterAssemblyReload += BuilderAssemblyReload;
}
void OnDisable()
{
AssemblyReloadEvents.afterAssemblyReload -= BuilderAssemblyReload;
}
void OnDestroy()
{
AccountDestroy();
}
public const int SdkWindowWidth = 518;
void OnGUI()
{
if (window == null)
{
window = (VRCSdkControlPanel)EditorWindow.GetWindow(typeof(VRCSdkControlPanel));
InitializeStyles();
}
if (_bannerImage == null)
_bannerImage = AssetDatabase.LoadAssetAtPath("Assets/VRCSDK/Dependencies/VRChat/SdkGraphics/SDK_Panel_Banner.png", typeof(Texture2D)) as Texture2D;
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
GUILayout.Box(_bannerImage);
if (Application.isPlaying)
{
GUI.enabled = false;
GUILayout.Space(20);
EditorGUILayout.LabelField("Unity Application is running ...\nStop it to access the Control Panel", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
GUI.enabled = true;
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
return;
}
EditorGUILayout.Space();
EnvConfig.SetActiveSDKDefines();
#if VRC_SDK_VRCSDK2
VRCSettings.Get().activeWindowPanel = GUILayout.Toolbar(VRCSettings.Get().activeWindowPanel, new string[] { "Authentication", "Builder", "Content Manager", "Settings" }, GUILayout.Width(SdkWindowWidth));
int showPanel = VRCSettings.Get().activeWindowPanel;
#elif VRC_SDK_VRCSDK3
VRC.SDK3.Editor.VRCSettings.Get().activeWindowPanel = GUILayout.Toolbar(VRC.SDK3.Editor.VRCSettings.Get().activeWindowPanel, new string[] { "Authentication", "Builder", "Content Manager", "Settings" }, GUILayout.Width(SdkWindowWidth));
int showPanel = VRC.SDK3.Editor.VRCSettings.Get().activeWindowPanel;
#else
int showPanel = 0;
#endif
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
if (APIUser.IsLoggedInWithCredentials == false && showPanel != 3)
showPanel = 0;
switch (showPanel)
{
case 1:
ShowBuilders();
break;
case 2:
ShowContent();
break;
case 3:
ShowSettings();
break;
case 0:
default:
ShowAccount();
break;
}
}
[UnityEditor.Callbacks.PostProcessScene]
static void OnPostProcessScene()
{
if (window != null)
window.Reset();
}
private void OnFocus()
{
Reset();
}
public void Reset()
{
ResetIssues();
// style backgrounds may be nulled on scene load. detect if so has happened
if((boxGuiStyle != null) && (boxGuiStyle.normal.background == null))
InitializeStyles();
}
[UnityEditor.Callbacks.DidReloadScripts(int.MaxValue)]
static void DidReloadScripts()
{
RefreshApiUrlSetting();
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 20b4cdbdda9655947aab6f8f2c90690f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,594 @@
using UnityEngine;
using UnityEditor;
using VRC.Core;
using System.Text.RegularExpressions;
public partial class VRCSdkControlPanel : EditorWindow
{
static bool isInitialized = false;
static string clientInstallPath;
static bool signingIn = false;
static string error = null;
public static bool FutureProofPublishEnabled { get { return UnityEditor.EditorPrefs.GetBool("futureProofPublish", DefaultFutureProofPublishEnabled); } }
//public static bool DefaultFutureProofPublishEnabled { get { return !SDKClientUtilities.IsInternalSDK(); } }
public static bool DefaultFutureProofPublishEnabled { get { return true; } }
static string storedUsername
{
get
{
if (EditorPrefs.HasKey("sdk#username"))
return EditorPrefs.GetString("sdk#username");
return null;
}
set
{
EditorPrefs.SetString("sdk#username", value);
if (string.IsNullOrEmpty(value))
EditorPrefs.DeleteKey("sdk#username");
}
}
static string storedPassword
{
get
{
if (EditorPrefs.HasKey("sdk#password"))
return EditorPrefs.GetString("sdk#password");
return null;
}
set
{
EditorPrefs.SetString("sdk#password", value);
if (string.IsNullOrEmpty(value))
EditorPrefs.DeleteKey("sdk#password");
}
}
static string _username = null;
static string _password = null;
static string username
{
get
{
if (!string.IsNullOrEmpty(_username))
return _username;
else
_username = storedUsername;
return _username;
}
set
{
_username = value;
}
}
static string password
{
get
{
if (!string.IsNullOrEmpty(_password))
return _password;
else
_password = storedPassword;
return _password;
}
set
{
_password = value;
}
}
static ApiServerEnvironment serverEnvironment
{
get
{
ApiServerEnvironment env = ApiServerEnvironment.Release;
try
{
env = (ApiServerEnvironment)System.Enum.Parse(typeof(ApiServerEnvironment), UnityEditor.EditorPrefs.GetString("VRC_ApiServerEnvironment", env.ToString()));
}
catch (System.Exception e)
{
Debug.LogError("Invalid server environment name - " + e.ToString());
}
return env;
}
set
{
UnityEditor.EditorPrefs.SetString("VRC_ApiServerEnvironment", value.ToString());
API.SetApiUrlFromEnvironment(value);
}
}
private void OnEnableAccount()
{
entered2faCodeIsInvalid = false;
warningIconGraphic = Resources.Load("2FAIcons/SDK_Warning_Triangle_icon") as Texture2D;
}
public static void RefreshApiUrlSetting()
{
// this forces the static api url variable to be reset from the server environment set in editor prefs.
// needed because the static variable states get cleared when entering / exiting play mode
ApiServerEnvironment env = serverEnvironment;
serverEnvironment = env;
}
public static void InitAccount()
{
if (isInitialized)
return;
if (!APIUser.IsLoggedInWithCredentials && ApiCredentials.Load())
APIUser.FetchCurrentUser((c) => AnalyticsSDK.LoggedInUserChanged(c.Model as APIUser), null);
clientInstallPath = SDKClientUtilities.GetSavedVRCInstallPath();
if (string.IsNullOrEmpty(clientInstallPath))
clientInstallPath = SDKClientUtilities.LoadRegistryVRCInstallPath();
signingIn = false;
isInitialized = true;
ClearContent();
}
public static bool OnShowStatus()
{
API.SetOnlineMode(true);
SignIn(false);
EditorGUILayout.BeginVertical();
if (APIUser.IsLoggedInWithCredentials)
{
OnCreatorStatusGUI();
}
EditorGUILayout.EndVertical();
return APIUser.IsLoggedInWithCredentials;
}
static bool OnAccountGUI()
{
const int ACCOUNT_LOGIN_BORDER_SPACING = 20;
EditorGUILayout.Separator();
EditorGUILayout.Separator();
EditorGUILayout.Separator();
EditorGUILayout.Separator();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Space(ACCOUNT_LOGIN_BORDER_SPACING);
GUILayout.BeginVertical("Account", "window", GUILayout.Height(150), GUILayout.Width(340));
if (signingIn)
{
EditorGUILayout.LabelField("Signing in as " + username + ".");
}
else if (APIUser.IsLoggedInWithCredentials)
{
if (Status != "Connected")
EditorGUILayout.LabelField(Status);
OnCreatorStatusGUI();
GUILayout.BeginHorizontal();
GUILayout.Label("");
if (GUILayout.Button("Logout"))
{
storedUsername = username = null;
storedPassword = password = null;
VRC.Tools.ClearCookies();
APIUser.Logout();
ClearContent();
}
GUILayout.EndHorizontal();
}
else
{
InitAccount();
ApiServerEnvironment newEnv = ApiServerEnvironment.Release;
#if VRC_SDK_VRCSDK2
if (VRCSettings.Get().DisplayAdvancedSettings)
newEnv = (ApiServerEnvironment)EditorGUILayout.EnumPopup("Use API", serverEnvironment);
#elif VRC_SDK_VRCSDK3
if (VRC.SDK3.Editor.VRCSettings.Get().DisplayAdvancedSettings)
newEnv = (ApiServerEnvironment)EditorGUILayout.EnumPopup("Use API", serverEnvironment);
#endif
if (serverEnvironment != newEnv)
serverEnvironment = newEnv;
username = EditorGUILayout.TextField("Username", username);
password = EditorGUILayout.PasswordField("Password", password);
if (GUILayout.Button("Sign In"))
SignIn(true);
if (GUILayout.Button("Sign up"))
Application.OpenURL("http://vrchat.com/register");
}
if (showTwoFactorAuthenticationEntry)
{
OnTwoFactorAuthenticationGUI();
}
GUILayout.EndVertical();
GUILayout.Space(ACCOUNT_LOGIN_BORDER_SPACING);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
return !signingIn;
}
static void OnCreatorStatusGUI()
{
EditorGUILayout.LabelField("Logged in as:", APIUser.CurrentUser.displayName);
//if (SDKClientUtilities.IsInternalSDK())
// EditorGUILayout.LabelField("Developer Status: ", APIUser.CurrentUser.developerType.ToString());
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField("World Creator Status: ", APIUser.CurrentUser.canPublishWorlds ? "Allowed to publish worlds" : "Not yet allowed to publish worlds");
EditorGUILayout.LabelField("Avatar Creator Status: ", APIUser.CurrentUser.canPublishAvatars ? "Allowed to publish avatars" : "Not yet allowed to publish avatars");
EditorGUILayout.EndVertical();
if (!APIUser.CurrentUser.canPublishAllContent)
{
if (GUILayout.Button("More Info..."))
{
VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
}
}
EditorGUILayout.EndHorizontal();
}
void ShowAccount()
{
if (VRC.Core.ConfigManager.RemoteConfig.IsInitialized())
{
if (VRC.Core.ConfigManager.RemoteConfig.HasKey("sdkUnityVersion"))
{
string sdkUnityVersion = VRC.Core.ConfigManager.RemoteConfig.GetString("sdkUnityVersion");
if (string.IsNullOrEmpty(sdkUnityVersion))
EditorGUILayout.LabelField("Could not fetch remote config.");
else if (Application.unityVersion != sdkUnityVersion)
{
EditorGUILayout.LabelField("Unity Version", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Wrong Unity version. Please use " + sdkUnityVersion);
}
}
}
else
{
VRC.Core.API.SetOnlineMode(true, "vrchat");
VRC.Core.ConfigManager.RemoteConfig.Init();
}
OnAccountGUI();
}
private const string TWO_FACTOR_AUTHENTICATION_HELP_URL = "https://docs.vrchat.com/docs/setup-2fa";
private const string ENTER_2FA_CODE_TITLE_STRING = "Enter a numeric code from your authenticator app (or one of your saved recovery codes).";
private const string ENTER_2FA_CODE_LABEL_STRING = "Code:";
private const string CHECKING_2FA_CODE_STRING = "Checking code...";
private const string ENTER_2FA_CODE_INVALID_CODE_STRING = "Invalid Code";
private const string ENTER_2FA_CODE_VERIFY_STRING = "Verify";
private const string ENTER_2FA_CODE_CANCEL_STRING = "Cancel";
private const string ENTER_2FA_CODE_HELP_STRING = "Help";
private const int WARNING_ICON_SIZE = 60;
private const int WARNING_FONT_HEIGHT = 18;
static private Texture2D warningIconGraphic;
static bool entered2faCodeIsInvalid;
static bool authorizationCodeWasVerified;
static private int previousAuthenticationCodeLength = 0;
static bool checkingCode;
static string authenticationCode = "";
static System.Action onAuthenticationVerifiedAction;
static bool _showTwoFactorAuthenticationEntry = false;
static bool showTwoFactorAuthenticationEntry
{
get
{
return _showTwoFactorAuthenticationEntry;
}
set
{
_showTwoFactorAuthenticationEntry = value;
authenticationCode = "";
if (!_showTwoFactorAuthenticationEntry && !authorizationCodeWasVerified)
Logout();
}
}
static bool IsValidAuthenticationCodeFormat()
{
bool isValid2faAuthenticationCode = false;
if (!string.IsNullOrEmpty(authenticationCode))
{
// check if the input is a valid 6-digit numberic code (ignoring spaces)
Regex rx = new Regex(@"^(\s*\d\s*){6}$", RegexOptions.Compiled);
MatchCollection matches6DigitCode = rx.Matches(authenticationCode);
isValid2faAuthenticationCode = (matches6DigitCode.Count == 1);
}
return isValid2faAuthenticationCode;
}
static bool IsValidRecoveryCodeFormat()
{
bool isValid2faRecoveryCode = false;
if (!string.IsNullOrEmpty(authenticationCode))
{
// check if the input is a valid 8-digit alpha-numberic code (format xxxx-xxxx) "-" is optional & ignore any spaces
// OTP codes also exclude the letters i,l,o and the digit 1 to prevent any confusion
Regex rx = new Regex(@"^(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}-?(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}$", RegexOptions.Compiled);
MatchCollection matchesRecoveryCode = rx.Matches(authenticationCode);
isValid2faRecoveryCode = (matchesRecoveryCode.Count == 1);
}
return isValid2faRecoveryCode;
}
static void OnTwoFactorAuthenticationGUI()
{
const int ENTER_2FA_CODE_BORDER_SIZE = 20;
const int ENTER_2FA_CODE_BUTTON_WIDTH = 260;
const int ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH = ENTER_2FA_CODE_BUTTON_WIDTH / 2;
const int ENTER_2FA_CODE_ENTRY_REGION_WIDTH = 130;
const int ENTER_2FA_CODE_MIN_WINDOW_WIDTH = ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH + ENTER_2FA_CODE_ENTRY_REGION_WIDTH + (ENTER_2FA_CODE_BORDER_SIZE * 3);
bool isValidAuthenticationCode = IsValidAuthenticationCodeFormat();
// Invalid code text
if (entered2faCodeIsInvalid)
{
GUIStyle s = new GUIStyle(EditorStyles.label);
s.alignment = TextAnchor.UpperLeft;
s.normal.textColor = Color.red;
s.fontSize = WARNING_FONT_HEIGHT;
s.padding = new RectOffset(0, 0, (WARNING_ICON_SIZE - s.fontSize) / 2, 0);
s.fixedHeight = WARNING_ICON_SIZE;
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
var textDimensions = s.CalcSize(new GUIContent(ENTER_2FA_CODE_INVALID_CODE_STRING));
GUILayout.Label(new GUIContent(warningIconGraphic), GUILayout.Width(WARNING_ICON_SIZE), GUILayout.Height(WARNING_ICON_SIZE));
EditorGUILayout.LabelField(ENTER_2FA_CODE_INVALID_CODE_STRING, s, GUILayout.Width(textDimensions.x));
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
else if (checkingCode)
{
// Display checking code message
EditorGUILayout.BeginVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
GUIStyle s = new GUIStyle(EditorStyles.label);
s.alignment = TextAnchor.MiddleCenter;
s.fixedHeight = WARNING_ICON_SIZE;
EditorGUILayout.LabelField(CHECKING_2FA_CODE_STRING, s, GUILayout.Height(WARNING_ICON_SIZE));
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
else
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
GUILayout.FlexibleSpace();
GUIStyle titleStyle = new GUIStyle(EditorStyles.label);
titleStyle.alignment = TextAnchor.MiddleCenter;
titleStyle.wordWrap = true;
EditorGUILayout.LabelField(ENTER_2FA_CODE_TITLE_STRING, titleStyle, GUILayout.Width(ENTER_2FA_CODE_MIN_WINDOW_WIDTH - (2 * ENTER_2FA_CODE_BORDER_SIZE)), GUILayout.Height(WARNING_ICON_SIZE), GUILayout.ExpandHeight(true));
GUILayout.FlexibleSpace();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
GUILayout.FlexibleSpace();
Vector2 size = EditorStyles.boldLabel.CalcSize(new GUIContent(ENTER_2FA_CODE_LABEL_STRING));
EditorGUILayout.LabelField(ENTER_2FA_CODE_LABEL_STRING, EditorStyles.boldLabel, GUILayout.MaxWidth(size.x));
authenticationCode = EditorGUILayout.TextField(authenticationCode);
// Verify 2FA code button
if (GUILayout.Button(ENTER_2FA_CODE_VERIFY_STRING, GUILayout.Width(ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH)))
{
checkingCode = true;
APIUser.VerifyTwoFactorAuthCode(authenticationCode, isValidAuthenticationCode ? API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION : API2FA.ONE_TIME_PASSWORD_AUTHENTICATION, username, password,
delegate
{
// valid 2FA code submitted
entered2faCodeIsInvalid = false;
authorizationCodeWasVerified = true;
checkingCode = false;
showTwoFactorAuthenticationEntry = false;
if (null != onAuthenticationVerifiedAction)
onAuthenticationVerifiedAction();
},
delegate
{
entered2faCodeIsInvalid = true;
checkingCode = false;
}
);
}
GUILayout.FlexibleSpace();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
// after user has entered an invalid code causing the invalid code message to be displayed,
// edit the code will change it's length meaning it is invalid format, so we can clear the invalid code setting until they resubmit
if (previousAuthenticationCodeLength != authenticationCode.Length)
{
previousAuthenticationCodeLength = authenticationCode.Length;
entered2faCodeIsInvalid = false;
}
GUI.enabled = true;
GUILayout.FlexibleSpace();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
// Two-Factor Authentication Help button
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(ENTER_2FA_CODE_HELP_STRING))
{
Application.OpenURL(TWO_FACTOR_AUTHENTICATION_HELP_URL);
}
EditorGUILayout.EndHorizontal();
// Cancel button
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(ENTER_2FA_CODE_CANCEL_STRING))
{
showTwoFactorAuthenticationEntry = false;
Logout();
}
EditorGUILayout.EndHorizontal();
}
private static string Status
{
get
{
if (!APIUser.IsLoggedInWithCredentials)
return error == null ? "Please log in." : "Error in authenticating: " + error;
if (signingIn)
return "Logging in.";
else
{
if( serverEnvironment == ApiServerEnvironment.Dev )
return "Connected to " + serverEnvironment.ToString();
return "Connected";
}
}
}
private static void OnAuthenticationCompleted()
{
AttemptLogin();
}
private static void AttemptLogin()
{
APIUser.Login(username, password,
delegate (ApiModelContainer<APIUser> c)
{
APIUser user = c.Model as APIUser;
if (c.Cookies.ContainsKey("auth"))
ApiCredentials.Set(user.username, username, "vrchat", c.Cookies["auth"]);
else
ApiCredentials.SetHumanName(user.username);
signingIn = false;
error = null;
storedUsername = username;
storedPassword = password;
AnalyticsSDK.LoggedInUserChanged(user);
if (!APIUser.CurrentUser.canPublishAllContent)
{
if (UnityEditor.SessionState.GetString("HasShownContentPublishPermissionsDialogForUser", "") != user.id)
{
UnityEditor.SessionState.SetString("HasShownContentPublishPermissionsDialogForUser", user.id);
VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
}
}
},
delegate (ApiModelContainer<APIUser> c)
{
Logout();
error = c.Error;
VRC.Core.Logger.Log("Error logging in: " + error);
},
delegate (ApiModelContainer<API2FA> c)
{
if (c.Cookies.ContainsKey("auth"))
ApiCredentials.Set(username, username, "vrchat", c.Cookies["auth"]);
showTwoFactorAuthenticationEntry = true;
onAuthenticationVerifiedAction = OnAuthenticationCompleted;
}
);
}
private static object syncObject = new object();
private static void SignIn(bool explicitAttempt)
{
lock (syncObject)
{
if (signingIn
|| APIUser.IsLoggedInWithCredentials
|| (!explicitAttempt && string.IsNullOrEmpty(storedUsername))
|| (!explicitAttempt && string.IsNullOrEmpty(storedPassword)))
return;
signingIn = true;
}
InitAccount();
AttemptLogin();
}
public static void Logout()
{
signingIn = false;
storedUsername = null;
storedPassword = null;
VRC.Tools.ClearCookies();
APIUser.Logout();
}
private void AccountDestroy()
{
signingIn = false;
isInitialized = false;
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5066cd5c1cc208143a1253cac821714a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,639 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using VRCSDK2.Validation.Performance;
using Object = UnityEngine.Object;
using VRC.SDK3.Editor;
using VRC.SDKBase.Editor;
public partial class VRCSdkControlPanel : EditorWindow
{
public static System.Action _EnableSpatialization = null; // assigned in AutoAddONSPAudioSourceComponents
public const string AVATAR_OPTIMIZATION_TIPS_URL = "https://docs.vrchat.com/docs/avatar-optimizing-tips";
public const string AVATAR_RIG_REQUIREMENTS_URL = "https://docs.vrchat.com/docs/rig-requirements";
const string kCantPublishContent = "Before you can upload avatars or worlds, you will need to spend some time in VRChat.";
const string kCantPublishAvatars = "Before you can upload avatars, you will need to spend some time in VRChat.";
const string kCantPublishWorlds = "Before you can upload worlds, you will need to spend some time in VRChat.";
private const string FIX_ISSUES_TO_BUILD_OR_TEST_WARNING_STRING = "You must address the above issues before you can build or test this content!";
static Texture _perfIcon_Excellent;
static Texture _perfIcon_Good;
static Texture _perfIcon_Medium;
static Texture _perfIcon_Poor;
static Texture _perfIcon_VeryPoor;
static Texture _bannerImage;
public void ResetIssues()
{
GUIErrors.Clear();
GUIInfos.Clear();
GUIWarnings.Clear();
GUILinks.Clear();
GUIStats.Clear();
CheckedForIssues = false;
}
public bool CheckedForIssues { get; set; } = false;
class Issue
{
public string issueText;
public System.Action showThisIssue;
public System.Action fixThisIssue;
public PerformanceRating performanceRating;
public Issue(string text, System.Action show, System.Action fix, PerformanceRating rating = PerformanceRating.None)
{
issueText = text;
showThisIssue = show;
fixThisIssue = fix;
performanceRating = rating;
}
public class Equality : IEqualityComparer<Issue>, IComparer<Issue>
{
public bool Equals(Issue b1, Issue b2)
{
return (b1.issueText == b2.issueText);
}
public int Compare(Issue b1, Issue b2)
{
return string.Compare(b1.issueText, b2.issueText);
}
public int GetHashCode(Issue bx)
{
return bx.issueText.GetHashCode();
}
}
}
Dictionary<Object, List<Issue>> GUIErrors = new Dictionary<Object, List<Issue>>();
Dictionary<Object, List<Issue>> GUIWarnings = new Dictionary<Object, List<Issue>>();
Dictionary<Object, List<Issue>> GUIInfos = new Dictionary<Object, List<Issue>>();
Dictionary<Object, List<Issue>> GUILinks = new Dictionary<Object, List<Issue>>();
Dictionary<Object, List<Issue>> GUIStats = new Dictionary<Object, List<Issue>>();
public bool NoGuiErrorsOrIssues()
{
return GUIErrors.Count == 0 && CheckedForIssues;
}
void AddToReport(Dictionary<Object, List<Issue>> report, Object subject, string output, System.Action show, System.Action fix)
{
if (subject == null)
subject = this;
if (!report.ContainsKey(subject))
report.Add(subject, new List<Issue>());
var issue = new Issue(output, show, fix);
if (!report[subject].Contains(issue, new Issue.Equality()))
{
report[subject].Add(issue);
report[subject].Sort(new Issue.Equality());
}
}
void BuilderAssemblyReload()
{
ResetIssues();
}
public void OnGUIError(Object subject, string output, System.Action show, System.Action fix)
{
AddToReport(GUIErrors, subject, output, show, fix);
}
public void OnGUIWarning(Object subject, string output, System.Action show, System.Action fix)
{
AddToReport(GUIWarnings, subject, output, show, fix);
}
public void OnGUIInformation(Object subject, string output)
{
AddToReport(GUIInfos, subject, output, null, null);
}
public void OnGUILink(Object subject, string output, string link)
{
AddToReport(GUILinks, subject, output + "\n" + link, null, null);
}
public void OnGUIStat(Object subject, string output, PerformanceRating rating, System.Action show, System.Action fix)
{
if (subject == null)
subject = this;
if (!GUIStats.ContainsKey(subject))
GUIStats.Add(subject, new List<Issue>());
GUIStats[subject].Add(new Issue(output, show, fix, rating));
}
public int triggerLineMode
{
get { return EditorPrefs.GetInt("VRC.SDKBase_triggerLineMode", 0); }
set { EditorPrefs.SetInt("VRC.SDKBase_triggerLineMode", value); }
}
private void ShowSettingsOptionsForBuilders()
{
if (_sdkBuilders == null)
{
PopulateSdkBuilders();
}
for (int i = 0; i < _sdkBuilders.Length; i++)
{
IVRCSdkControlPanelBuilder builder = _sdkBuilders[i];
builder.ShowSettingsOptions();
if (i < _sdkBuilders.Length - 1)
{
EditorGUILayout.Separator();
}
}
}
private IVRCSdkControlPanelBuilder[] _sdkBuilders;
private static List<Type> GetSdkBuilderTypesFromAttribute()
{
Type sdkBuilderInterfaceType = typeof(IVRCSdkControlPanelBuilder);
Type sdkBuilderAttributeType = typeof(VRCSdkControlPanelBuilderAttribute);
List<Type> moduleTypesFromAttribute = new List<Type>();
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
VRCSdkControlPanelBuilderAttribute[] sdkBuilderAttributes;
try
{
sdkBuilderAttributes = (VRCSdkControlPanelBuilderAttribute[])assembly.GetCustomAttributes(sdkBuilderAttributeType, false);
}
catch
{
sdkBuilderAttributes = new VRCSdkControlPanelBuilderAttribute[0];
}
foreach(VRCSdkControlPanelBuilderAttribute udonWrapperModuleAttribute in sdkBuilderAttributes)
{
if(udonWrapperModuleAttribute == null)
{
continue;
}
if(!sdkBuilderInterfaceType.IsAssignableFrom(udonWrapperModuleAttribute.Type))
{
continue;
}
moduleTypesFromAttribute.Add(udonWrapperModuleAttribute.Type);
}
}
return moduleTypesFromAttribute;
}
private void PopulateSdkBuilders()
{
if (_sdkBuilders != null)
{
return;
}
List<IVRCSdkControlPanelBuilder> builders = new List<IVRCSdkControlPanelBuilder>();
foreach (Type type in GetSdkBuilderTypesFromAttribute())
{
IVRCSdkControlPanelBuilder builder = (IVRCSdkControlPanelBuilder)Activator.CreateInstance(type);
builder.RegisterBuilder(this);
builders.Add(builder);
}
_sdkBuilders = builders.ToArray();
}
void ShowBuilders()
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
if (VRC.Core.ConfigManager.RemoteConfig.IsInitialized())
{
string sdkUnityVersion = VRC.Core.ConfigManager.RemoteConfig.GetString("sdkUnityVersion");
if (Application.unityVersion != sdkUnityVersion)
{
OnGUIWarning(null, "You are not using the recommended Unity version for the VRChat SDK. Content built with this version may not work correctly. Please use Unity " + sdkUnityVersion,
null,
() => { Application.OpenURL("https://unity3d.com/get-unity/download/archive"); }
);
}
}
if (VRCSdk3Analysis.IsSdkDllActive(VRCSdk3Analysis.SdkVersion.VRCSDK2) && VRCSdk3Analysis.IsSdkDllActive(VRCSdk3Analysis.SdkVersion.VRCSDK3))
{
List<Component> sdk2Components = VRCSdk3Analysis.GetSDKInScene(VRCSdk3Analysis.SdkVersion.VRCSDK2);
List<Component> sdk3Components = VRCSdk3Analysis.GetSDKInScene(VRCSdk3Analysis.SdkVersion.VRCSDK3);
if (sdk2Components.Count > 0 && sdk3Components.Count > 0)
{
OnGUIError(null,
"This scene contains components from the VRChat SDK version 2 and version 3. Version two elements will have to be replaced with their version 3 counterparts to build with SDK3 and UDON.",
() => { Selection.objects = sdk2Components.ToArray(); },
null
);
}
}
if (Lightmapping.giWorkflowMode == Lightmapping.GIWorkflowMode.Iterative)
{
OnGUIWarning(null,
"Automatic lightmap generation is enabled, which may stall the Unity build process. Before building and uploading, consider turning off 'Auto Generate' at the bottom of the Lighting Window.",
() =>
{
EditorWindow lightingWindow = GetLightingWindow();
if (lightingWindow)
{
lightingWindow.Show();
lightingWindow.Focus();
}
},
() =>
{
Lightmapping.giWorkflowMode = Lightmapping.GIWorkflowMode.OnDemand;
EditorWindow lightingWindow = GetLightingWindow();
if (!lightingWindow) return;
lightingWindow.Repaint();
Focus();
}
);
}
PopulateSdkBuilders();
IVRCSdkControlPanelBuilder selectedBuilder = null;
string errorMessage = null;
foreach (IVRCSdkControlPanelBuilder sdkBuilder in _sdkBuilders)
{
if (!sdkBuilder.IsValidBuilder(out string message))
{
if (selectedBuilder == null)
{
errorMessage = message;
}
}
else
{
if (selectedBuilder == null)
{
selectedBuilder = sdkBuilder;
errorMessage = null;
}
else
{
errorMessage =
"A Unity scene cannot contain a VRChat Scene Descriptor and also contain VRChat Avatar Descriptors";
}
}
}
if (selectedBuilder == null)
{
#if VRC_SDK_VRCSDK2
EditorGUILayout.LabelField("A VRC_SceneDescriptor or VRC_AvatarDescriptor\nis required to build VRChat SDK Content", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
#elif VRC_SDK_VRCSDK3
EditorGUILayout.LabelField("A VRCSceneDescriptor or VRCAvatarDescriptor\nis required to build VRChat SDK Content", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
#endif
}
else if (errorMessage != null)
{
OnGUIError(null,
errorMessage,
() => {
foreach (IVRCSdkControlPanelBuilder builder in _sdkBuilders)
{
builder.SelectAllComponents();
} },
null
);
OnGUIShowIssues();
}
else
{
selectedBuilder.ShowBuilder();
}
if (Event.current.type == EventType.Used) return;
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
public bool showLayerHelp = false;
bool ShouldShowLightmapWarning
{
get
{
const string GraphicsSettingsAssetPath = "ProjectSettings/GraphicsSettings.asset";
SerializedObject graphicsManager = new SerializedObject(UnityEditor.AssetDatabase.LoadAllAssetsAtPath(GraphicsSettingsAssetPath)[0]);
SerializedProperty lightmapStripping = graphicsManager.FindProperty("m_LightmapStripping");
return lightmapStripping.enumValueIndex == 0;
}
}
bool ShouldShowFogWarning
{
get
{
const string GraphicsSettingsAssetPath = "ProjectSettings/GraphicsSettings.asset";
SerializedObject graphicsManager = new SerializedObject(UnityEditor.AssetDatabase.LoadAllAssetsAtPath(GraphicsSettingsAssetPath)[0]);
SerializedProperty lightmapStripping = graphicsManager.FindProperty("m_FogStripping");
return lightmapStripping.enumValueIndex == 0;
}
}
void DrawIssueBox(MessageType msgType, Texture icon, string message, System.Action show, System.Action fix)
{
bool haveButtons = ((show != null) || (fix != null));
GUIStyle style = new GUIStyle("HelpBox");
style.fixedWidth = (haveButtons ? (SdkWindowWidth - 90) : SdkWindowWidth);
float minHeight = 40;
try
{
EditorGUILayout.BeginHorizontal();
if (icon != null)
{
GUIContent c = new GUIContent(message, icon);
float height = style.CalcHeight(c, style.fixedWidth);
GUILayout.Box(c, style, GUILayout.MinHeight(Mathf.Max(minHeight, height)));
}
else
{
GUIContent c = new GUIContent(message);
float height = style.CalcHeight(c, style.fixedWidth);
Rect rt = GUILayoutUtility.GetRect(c, style, GUILayout.MinHeight(Mathf.Max(minHeight, height)));
EditorGUI.HelpBox(rt, message, msgType); // note: EditorGUILayout resulted in uneven button layout in this case
}
if (haveButtons)
{
EditorGUILayout.BeginVertical();
float buttonHeight = ((show == null || fix == null) ? minHeight : (minHeight * 0.5f));
if ((show != null) && GUILayout.Button("Select", GUILayout.Height(buttonHeight)))
show();
if ((fix != null) && GUILayout.Button("Auto Fix", GUILayout.Height(buttonHeight)))
{
fix();
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
CheckedForIssues = false;
Repaint();
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
}
catch
{
// mutes 'ArgumentException: Getting control 0's position in a group with only 0 controls when doing repaint'
}
}
public void OnGuiFixIssuesToBuildOrTest()
{
GUIStyle s = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter };
EditorGUILayout.Space();
GUILayout.BeginVertical(boxGuiStyle, GUILayout.Height(WARNING_ICON_SIZE), GUILayout.Width(SdkWindowWidth));
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
var textDimensions = s.CalcSize(new GUIContent(FIX_ISSUES_TO_BUILD_OR_TEST_WARNING_STRING));
GUILayout.Label(new GUIContent(warningIconGraphic), GUILayout.Width(WARNING_ICON_SIZE), GUILayout.Height(WARNING_ICON_SIZE));
EditorGUILayout.LabelField(FIX_ISSUES_TO_BUILD_OR_TEST_WARNING_STRING, s, GUILayout.Width(textDimensions.x), GUILayout.Height(WARNING_ICON_SIZE));
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
GUILayout.EndVertical();
}
public void OnGUIShowIssues(Object subject = null)
{
if (subject == null)
subject = this;
EditorGUI.BeginChangeCheck();
GUIStyle style = GUI.skin.GetStyle("HelpBox");
if (GUIErrors.ContainsKey(subject))
foreach (Issue error in GUIErrors[subject].Where(s => !string.IsNullOrEmpty(s.issueText)))
DrawIssueBox(MessageType.Error, null, error.issueText, error.showThisIssue, error.fixThisIssue);
if (GUIWarnings.ContainsKey(subject))
foreach (Issue error in GUIWarnings[subject].Where(s => !string.IsNullOrEmpty(s.issueText)))
DrawIssueBox(MessageType.Warning, null, error.issueText, error.showThisIssue, error.fixThisIssue);
if (GUIStats.ContainsKey(subject))
{
foreach (var kvp in GUIStats[subject].Where(k => k.performanceRating == PerformanceRating.VeryPoor))
DrawIssueBox(MessageType.Warning, GetPerformanceIconForRating(kvp.performanceRating), kvp.issueText, kvp.showThisIssue, kvp.fixThisIssue);
foreach (var kvp in GUIStats[subject].Where(k => k.performanceRating == PerformanceRating.Poor))
DrawIssueBox(MessageType.Warning, GetPerformanceIconForRating(kvp.performanceRating), kvp.issueText, kvp.showThisIssue, kvp.fixThisIssue);
foreach (var kvp in GUIStats[subject].Where(k => k.performanceRating == PerformanceRating.Medium))
DrawIssueBox(MessageType.Warning, GetPerformanceIconForRating(kvp.performanceRating), kvp.issueText, kvp.showThisIssue, kvp.fixThisIssue);
foreach (var kvp in GUIStats[subject].Where(k => k.performanceRating == PerformanceRating.Good || k.performanceRating == PerformanceRating.Excellent))
DrawIssueBox(MessageType.Warning, GetPerformanceIconForRating(kvp.performanceRating), kvp.issueText, kvp.showThisIssue, kvp.fixThisIssue);
}
if (GUIInfos.ContainsKey(subject))
foreach (Issue error in GUIInfos[subject].Where(s => !string.IsNullOrEmpty(s.issueText)))
EditorGUILayout.HelpBox(error.issueText, MessageType.Info);
if (GUILinks.ContainsKey(subject))
{
EditorGUILayout.BeginVertical(style);
foreach (Issue error in GUILinks[subject].Where(s => !string.IsNullOrEmpty(s.issueText)))
{
var s = error.issueText.Split('\n');
EditorGUILayout.BeginHorizontal();
GUILayout.Label(s[0]);
if (GUILayout.Button("Open Link", GUILayout.Width(100)))
Application.OpenURL(s[1]);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(subject);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
private Texture GetPerformanceIconForRating(PerformanceRating value)
{
if (_perfIcon_Excellent == null)
_perfIcon_Excellent = Resources.Load<Texture>("PerformanceIcons/Perf_Great_32");
if (_perfIcon_Good == null)
_perfIcon_Good = Resources.Load<Texture>("PerformanceIcons/Perf_Good_32");
if (_perfIcon_Medium == null)
_perfIcon_Medium = Resources.Load<Texture>("PerformanceIcons/Perf_Medium_32");
if (_perfIcon_Poor == null)
_perfIcon_Poor = Resources.Load<Texture>("PerformanceIcons/Perf_Poor_32");
if (_perfIcon_VeryPoor == null)
_perfIcon_VeryPoor = Resources.Load<Texture>("PerformanceIcons/Perf_Horrible_32");
switch (value)
{
case PerformanceRating.Excellent:
return _perfIcon_Excellent;
case PerformanceRating.Good:
return _perfIcon_Good;
case PerformanceRating.Medium:
return _perfIcon_Medium;
case PerformanceRating.Poor:
return _perfIcon_Poor;
case PerformanceRating.None:
case PerformanceRating.VeryPoor:
return _perfIcon_VeryPoor;
}
return _perfIcon_Excellent;
}
Texture2D CreateBackgroundColorImage(UnityEngine.Color color)
{
int w = 4, h = 4;
Texture2D back = new Texture2D(w, h);
UnityEngine.Color[] buffer = new UnityEngine.Color[w * h];
for (int i = 0; i < w; ++i)
for (int j = 0; j < h; ++j)
buffer[i + w * j] = color;
back.SetPixels(buffer);
back.Apply(false);
return back;
}
public static void DrawContentInfo(string name, string version, string description, string capacity, string releaseStatus, List<string> tags)
{
EditorGUILayout.LabelField("Name: " + name);
EditorGUILayout.LabelField("Version: " + version.ToString());
EditorGUILayout.LabelField("Description: " + description);
if (capacity != null)
EditorGUILayout.LabelField("Capacity: " + capacity);
EditorGUILayout.LabelField("Release: " + releaseStatus);
if (tags != null)
{
string tagString = "";
for (int i = 0; i < tags.Count; i++)
{
if (i != 0) tagString += ", ";
tagString += tags[i];
}
EditorGUILayout.LabelField("Tags: " + tagString);
}
}
public static void DrawContentPlatformSupport(VRC.Core.ApiModel m)
{
if (m.supportedPlatforms == VRC.Core.ApiModel.SupportedPlatforms.StandaloneWindows || m.supportedPlatforms == VRC.Core.ApiModel.SupportedPlatforms.All)
EditorGUILayout.LabelField("Windows Support: YES");
else
EditorGUILayout.LabelField("Windows Support: NO");
if (m.supportedPlatforms == VRC.Core.ApiModel.SupportedPlatforms.Android || m.supportedPlatforms == VRC.Core.ApiModel.SupportedPlatforms.All)
EditorGUILayout.LabelField("Android Support: YES");
else
EditorGUILayout.LabelField("Android Support: NO");
}
public static void DrawBuildTargetSwitcher()
{
EditorGUILayout.LabelField("Active Build Target: " + EditorUserBuildSettings.activeBuildTarget);
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows || EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64 && GUILayout.Button("Switch Build Target to Android"))
{
if (EditorUtility.DisplayDialog("Build Target Switcher", "Are you sure you want to switch your build target to Android? This could take a while.", "Confirm", "Cancel"))
EditorUserBuildSettings.SwitchActiveBuildTargetAsync(BuildTargetGroup.Android, BuildTarget.Android);
}
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android && GUILayout.Button("Switch Build Target to Windows"))
{
if (EditorUtility.DisplayDialog("Build Target Switcher", "Are you sure you want to switch your build target to Windows? This could take a while.", "Confirm", "Cancel"))
EditorUserBuildSettings.SwitchActiveBuildTargetAsync(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64);
}
}
public static string GetBuildAndPublishButtonString()
{
string buildButtonString = "Build & Publish for UNSUPPORTED";
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows || EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64)
buildButtonString = "Build & Publish for Windows";
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
buildButtonString = "Build & Publish for Android";
return buildButtonString;
}
public static Object[] GetSubstanceObjects(GameObject obj = null, bool earlyOut = false)
{
// if 'obj' is null we check entire scene
// if 'earlyOut' is true we only return 1st object (to detect if substances are present)
List<Object> objects = new List<Object>();
if (obj == null) return objects.Count < 1 ? null : objects.ToArray();
Renderer[] renderers = obj ? obj.GetComponentsInChildren<Renderer>(true) : FindObjectsOfType<Renderer>();
if (renderers == null || renderers.Length < 1)
return null;
foreach (Renderer r in renderers)
{
if (r.sharedMaterials.Length < 1)
continue;
foreach (Material m in r.sharedMaterials)
{
if (!m)
continue;
string path = AssetDatabase.GetAssetPath(m);
if (string.IsNullOrEmpty(path))
continue;
if (path.EndsWith(".sbsar", true, System.Globalization.CultureInfo.InvariantCulture))
{
objects.Add(r.gameObject);
if (earlyOut)
return objects.ToArray();
}
}
}
return objects.Count < 1 ? null : objects.ToArray();
}
public static bool HasSubstances(GameObject obj = null)
{
return (GetSubstanceObjects(obj, true) != null);
}
EditorWindow GetLightingWindow()
{
var editorAsm = typeof(UnityEditor.Editor).Assembly;
return EditorWindow.GetWindow(editorAsm.GetType("UnityEditor.LightingWindow"));
}
public static void ShowContentPublishPermissionsDialog()
{
if (!VRC.Core.ConfigManager.RemoteConfig.IsInitialized())
{
VRC.Core.ConfigManager.RemoteConfig.Init(() => ShowContentPublishPermissionsDialog());
return;
}
string message = VRC.Core.ConfigManager.RemoteConfig.GetString("sdkNotAllowedToPublishMessage");
int result = UnityEditor.EditorUtility.DisplayDialogComplex("VRChat SDK", message, "Developer FAQ", "VRChat Discord", "OK");
if (result == 0)
{
ShowDeveloperFAQ();
}
if (result == 1)
{
ShowVRChatDiscord();
}
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4c73e735ee0380241b186a8993fa56bf
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
namespace VRC.SDKBase.Editor
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
[MeansImplicitUse]
public class VRCSdkControlPanelBuilderAttribute : Attribute
{
public Type Type { get; }
public VRCSdkControlPanelBuilderAttribute(Type type)
{
Type = type;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c768b42ca9a2f2b48afeb1fa03d5e1bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,682 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using VRC.Core;
public partial class VRCSdkControlPanel : EditorWindow
{
const int PageLimit = 20;
static List<ApiAvatar> uploadedAvatars = null;
static List<ApiWorld> uploadedWorlds = null;
static List<ApiAvatar> testAvatars = null;
public static Dictionary<string, Texture2D> ImageCache = new Dictionary<string, Texture2D>();
static List<string> justDeletedContents;
static List<ApiAvatar> justUpdatedAvatars;
static EditorCoroutine fetchingAvatars = null, fetchingWorlds = null;
private static string searchString = "";
private static bool WorldsToggle = true;
private static bool AvatarsToggle = true;
private static bool TestAvatarsToggle = true;
const int SCROLLBAR_RESERVED_REGION_WIDTH = 50;
const int WORLD_DESCRIPTION_FIELD_WIDTH = 140;
const int WORLD_IMAGE_BUTTON_WIDTH = 100;
const int WORLD_IMAGE_BUTTON_HEIGHT = 100;
const int WORLD_RELEASE_STATUS_FIELD_WIDTH = 150;
const int COPY_WORLD_ID_BUTTON_WIDTH = 75;
const int DELETE_WORLD_BUTTON_WIDTH = 75;
const int WORLD_ALL_INFORMATION_MAX_WIDTH = WORLD_DESCRIPTION_FIELD_WIDTH + WORLD_IMAGE_BUTTON_WIDTH + WORLD_RELEASE_STATUS_FIELD_WIDTH + COPY_WORLD_ID_BUTTON_WIDTH + DELETE_WORLD_BUTTON_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int WORLD_REDUCED_INFORMATION_MAX_WIDTH = WORLD_DESCRIPTION_FIELD_WIDTH + WORLD_IMAGE_BUTTON_WIDTH + WORLD_RELEASE_STATUS_FIELD_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int AVATAR_DESCRIPTION_FIELD_WIDTH = 140;
const int AVATAR_IMAGE_BUTTON_WIDTH = WORLD_IMAGE_BUTTON_WIDTH;
const int AVATAR_IMAGE_BUTTON_HEIGHT = WORLD_IMAGE_BUTTON_HEIGHT;
const int AVATAR_RELEASE_STATUS_FIELD_WIDTH = 150;
const int SET_AVATAR_STATUS_BUTTON_WIDTH = 100;
const int COPY_AVATAR_ID_BUTTON_WIDTH = COPY_WORLD_ID_BUTTON_WIDTH;
const int DELETE_AVATAR_BUTTON_WIDTH = DELETE_WORLD_BUTTON_WIDTH;
const int AVATAR_ALL_INFORMATION_MAX_WIDTH = AVATAR_DESCRIPTION_FIELD_WIDTH + AVATAR_IMAGE_BUTTON_WIDTH + AVATAR_RELEASE_STATUS_FIELD_WIDTH + SET_AVATAR_STATUS_BUTTON_WIDTH + COPY_AVATAR_ID_BUTTON_WIDTH + DELETE_AVATAR_BUTTON_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int AVATAR_REDUCED_INFORMATION_MAX_WIDTH = AVATAR_DESCRIPTION_FIELD_WIDTH + AVATAR_IMAGE_BUTTON_WIDTH + AVATAR_RELEASE_STATUS_FIELD_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int MAX_ALL_INFORMATION_WIDTH = WORLD_ALL_INFORMATION_MAX_WIDTH > AVATAR_ALL_INFORMATION_MAX_WIDTH ? WORLD_ALL_INFORMATION_MAX_WIDTH : AVATAR_ALL_INFORMATION_MAX_WIDTH;
const int MAX_REDUCED_INFORMATION_WIDTH = WORLD_REDUCED_INFORMATION_MAX_WIDTH > AVATAR_REDUCED_INFORMATION_MAX_WIDTH ? WORLD_REDUCED_INFORMATION_MAX_WIDTH : AVATAR_REDUCED_INFORMATION_MAX_WIDTH;
public static void ClearContent()
{
if (uploadedWorlds != null)
uploadedWorlds = null;
if (uploadedAvatars != null)
uploadedAvatars = null;
if (testAvatars != null)
testAvatars = null;
ImageCache.Clear();
}
IEnumerator FetchUploadedData()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
ConfigManager.RemoteConfig.Init();
if (!APIUser.IsLoggedInWithCredentials)
yield break;
ApiCache.ClearResponseCache();
VRCCachedWWW.ClearOld();
if (fetchingAvatars == null)
fetchingAvatars = EditorCoroutine.Start(() => FetchAvatars());
if (fetchingWorlds == null)
fetchingWorlds = EditorCoroutine.Start(() => FetchWorlds());
FetchTestAvatars();
}
private static void FetchAvatars(int offset = 0)
{
ApiAvatar.FetchList(
delegate (IEnumerable<ApiAvatar> obj)
{
if (obj.FirstOrDefault() != null)
fetchingAvatars = EditorCoroutine.Start(() =>
{
var l = obj.ToList();
int count = l.Count;
SetupAvatarData(l);
FetchAvatars(offset + count);
});
else
{
fetchingAvatars = null;
foreach (ApiAvatar a in uploadedAvatars)
DownloadImage(a.id, a.thumbnailImageUrl);
}
},
delegate (string obj)
{
Debug.LogError("Error fetching your uploaded avatars:\n" + obj);
fetchingAvatars = null;
},
ApiAvatar.Owner.Mine,
ApiAvatar.ReleaseStatus.All,
null,
PageLimit,
offset,
ApiAvatar.SortHeading.None,
ApiAvatar.SortOrder.Descending,
null,
null,
true,
false,
null,
false
);
}
private static void FetchTestAvatars()
{
#if VRC_SDK_VRCSDK3
string sdkAvatarFolder = VRC.SDK3.Editor.VRC_SdkBuilder.GetKnownFolderPath(VRC.SDK3.Editor.VRC_SdkBuilder.LocalLowGUID) + "/VRChat/vrchat/Avatars/";
string[] sdkavatars = Directory.GetFiles(sdkAvatarFolder);
string filename = "";
List<ApiAvatar> avatars = new List<ApiAvatar>();
foreach(string sdkap in sdkavatars)
{
if(Path.GetExtension(sdkap) != ".vrca")
continue;
filename = Path.GetFileNameWithoutExtension(sdkap);
ApiAvatar sdka = API.FromCacheOrNew<ApiAvatar>("local:sdk_" + filename);
sdka.assetUrl = sdkap;
sdka.name = filename;
sdka.releaseStatus = "public";
ApiAvatar.AddLocal(sdka);
avatars.Add(sdka);
}
testAvatars = avatars;
#else
testAvatars = new List<ApiAvatar>();
#endif
}
private static void FetchWorlds(int offset = 0)
{
ApiWorld.FetchList(
delegate (IEnumerable<ApiWorld> obj)
{
if (obj.FirstOrDefault() != null)
fetchingWorlds = EditorCoroutine.Start(() =>
{
var l = obj.ToList();
int count = l.Count;
SetupWorldData(l);
FetchWorlds(offset + count);
});
else
{
fetchingWorlds = null;
foreach (ApiWorld w in uploadedWorlds)
DownloadImage(w.id, w.thumbnailImageUrl);
}
},
delegate (string obj)
{
Debug.LogError("Error fetching your uploaded worlds:\n" + obj);
fetchingWorlds = null;
},
ApiWorld.SortHeading.Updated,
ApiWorld.SortOwnership.Mine,
ApiWorld.SortOrder.Descending,
offset,
PageLimit,
"",
null,
null,
null,
"",
ApiWorld.ReleaseStatus.All,
null,
null,
true,
false);
}
static void SetupWorldData(List<ApiWorld> worlds)
{
if (worlds == null || uploadedWorlds == null)
return;
worlds.RemoveAll(w => w == null || w.name == null || uploadedWorlds.Any(w2 => w2.id == w.id));
if (worlds.Count > 0)
{
uploadedWorlds.AddRange(worlds);
uploadedWorlds.Sort((w1, w2) => w1.name.CompareTo(w2.name));
}
}
static void SetupAvatarData(List<ApiAvatar> avatars)
{
if (avatars == null || uploadedAvatars == null )
return;
avatars.RemoveAll(a => a == null || uploadedAvatars.Any(a2 => a2.id == a.id));
foreach(var avatar in avatars)
{
if (string.IsNullOrEmpty(avatar.name))
avatar.name = "(unnamed)";
}
if (avatars.Count > 0)
{
uploadedAvatars.AddRange(avatars);
uploadedAvatars.Sort((w1, w2) => w1.name.CompareTo(w2.name));
}
}
static void DownloadImage(string id, string url)
{
if (string.IsNullOrEmpty(url))
return;
if (ImageCache.ContainsKey(id) && ImageCache[id] != null)
return;
System.Action<Texture2D> onDone = (texture) =>
{
if (texture != null)
{
ImageCache[id] = texture;
}
else if (ImageCache.ContainsKey(id))
ImageCache.Remove(id);
};
EditorCoroutine.Start(VRCCachedWWW.Get(url, (onDone)));
}
Vector2 contentScrollPos;
bool OnGUIUserInfo()
{
bool updatedContent = false;
if (!ConfigManager.RemoteConfig.IsInitialized())
ConfigManager.RemoteConfig.Init();
if (APIUser.IsLoggedInWithCredentials && uploadedWorlds != null && uploadedAvatars != null && testAvatars != null)
{
bool expandedLayout = false; // (position.width > MAX_ALL_INFORMATION_WIDTH); // uncomment for future wide layouts
if (!expandedLayout)
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
}
GUILayout.BeginHorizontal();
GUILayout.BeginVertical(searchBarStyle);
EditorGUILayout.BeginHorizontal();
float searchFieldShrinkOffset = 30f;
GUILayoutOption layoutOption = (expandedLayout ? GUILayout.Width(position.width - searchFieldShrinkOffset) : GUILayout.Width(SdkWindowWidth - searchFieldShrinkOffset));
searchString = EditorGUILayout.TextField(searchString, GUI.skin.FindStyle("SearchTextField"), layoutOption);
GUIStyle searchButtonStyle = searchString == string.Empty
? GUI.skin.FindStyle("SearchCancelButtonEmpty")
: GUI.skin.FindStyle("SearchCancelButton");
if (GUILayout.Button(string.Empty, searchButtonStyle))
{
searchString = string.Empty;
GUI.FocusControl(null);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
if (!expandedLayout)
{
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
}
layoutOption = (expandedLayout ? GUILayout.Width(position.width) : GUILayout.Width(SdkWindowWidth));
contentScrollPos = EditorGUILayout.BeginScrollView(contentScrollPos, layoutOption);
GUIStyle descriptionStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
descriptionStyle.wordWrap = true;
if (uploadedWorlds.Count > 0)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("WORLDS", EditorStyles.boldLabel, GUILayout.ExpandWidth(false), GUILayout.Width(58));
WorldsToggle = EditorGUILayout.Foldout(WorldsToggle, new GUIContent(""));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (WorldsToggle)
{
List<ApiWorld> tmpWorlds = new List<ApiWorld>();
if (uploadedWorlds.Count > 0)
tmpWorlds = new List<ApiWorld>(uploadedWorlds);
foreach (ApiWorld w in tmpWorlds)
{
if (justDeletedContents != null && justDeletedContents.Contains(w.id))
{
uploadedWorlds.Remove(w);
continue;
}
if (!w.name.ToLowerInvariant().Contains(searchString.ToLowerInvariant()))
{
continue;
}
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal(GUILayout.Width(WORLD_IMAGE_BUTTON_WIDTH));
if (ImageCache.ContainsKey(w.id))
{
if (GUILayout.Button(ImageCache[w.id], GUILayout.Height(WORLD_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(WORLD_IMAGE_BUTTON_WIDTH)))
{
Application.OpenURL(w.imageUrl);
}
}
else
{
if (GUILayout.Button("", GUILayout.Height(WORLD_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(WORLD_IMAGE_BUTTON_WIDTH)))
{
Application.OpenURL(w.imageUrl);
}
}
if (expandedLayout)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(w.name, descriptionStyle,
GUILayout.Width(position.width - MAX_ALL_INFORMATION_WIDTH +
WORLD_DESCRIPTION_FIELD_WIDTH));
}
else
{
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(w.name, descriptionStyle);
}
EditorGUILayout.LabelField("Release Status: " + w.releaseStatus,
GUILayout.Width(WORLD_RELEASE_STATUS_FIELD_WIDTH));
if (GUILayout.Button("Copy ID", GUILayout.Width(COPY_WORLD_ID_BUTTON_WIDTH)))
{
TextEditor te = new TextEditor();
te.text = w.id;
te.SelectAll();
te.Copy();
}
if (GUILayout.Button("Delete", GUILayout.Width(DELETE_WORLD_BUTTON_WIDTH)))
{
if (EditorUtility.DisplayDialog("Delete " + w.name + "?",
"Are you sure you want to delete " + w.name + "? This cannot be undone.", "Delete",
"Cancel"))
{
foreach (VRC.Core.PipelineManager pm in FindObjectsOfType<VRC.Core.PipelineManager>()
.Where(pm => pm.blueprintId == w.id))
{
pm.blueprintId = "";
pm.completedSDKPipeline = false;
UnityEditor.EditorUtility.SetDirty(pm);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(pm.gameObject.scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(pm.gameObject.scene);
}
API.Delete<ApiWorld>(w.id);
uploadedWorlds.RemoveAll(world => world.id == w.id);
if (ImageCache.ContainsKey(w.id))
ImageCache.Remove(w.id);
if (justDeletedContents == null) justDeletedContents = new List<string>();
justDeletedContents.Add(w.id);
updatedContent = true;
}
}
if (expandedLayout)
EditorGUILayout.EndHorizontal();
else
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
}
}
if (uploadedAvatars.Count > 0)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("AVATARS", EditorStyles.boldLabel, GUILayout.ExpandWidth(false), GUILayout.Width(65));
AvatarsToggle = EditorGUILayout.Foldout(AvatarsToggle, new GUIContent(""));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (AvatarsToggle)
{
List<ApiAvatar> tmpAvatars = new List<ApiAvatar>();
if (uploadedAvatars.Count > 0)
tmpAvatars = new List<ApiAvatar>(uploadedAvatars);
if (justUpdatedAvatars != null)
{
foreach (ApiAvatar a in justUpdatedAvatars)
{
int index = tmpAvatars.FindIndex((av) => av.id == a.id);
if (index != -1)
tmpAvatars[index] = a;
}
}
foreach (ApiAvatar a in tmpAvatars)
{
if (justDeletedContents != null && justDeletedContents.Contains(a.id))
{
uploadedAvatars.Remove(a);
continue;
}
if (!a.name.ToLowerInvariant().Contains(searchString.ToLowerInvariant()))
{
continue;
}
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal(GUILayout.Width(AVATAR_DESCRIPTION_FIELD_WIDTH));
if (ImageCache.ContainsKey(a.id))
{
if (GUILayout.Button(ImageCache[a.id], GUILayout.Height(AVATAR_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(AVATAR_IMAGE_BUTTON_WIDTH)))
{
Application.OpenURL(a.imageUrl);
}
}
else
{
if (GUILayout.Button("", GUILayout.Height(AVATAR_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(AVATAR_IMAGE_BUTTON_WIDTH)))
{
Application.OpenURL(a.imageUrl);
}
}
if (expandedLayout)
EditorGUILayout.BeginHorizontal();
else
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(a.name, descriptionStyle,
GUILayout.Width(expandedLayout
? position.width - MAX_ALL_INFORMATION_WIDTH + AVATAR_DESCRIPTION_FIELD_WIDTH
: AVATAR_DESCRIPTION_FIELD_WIDTH));
EditorGUILayout.LabelField("Release Status: " + a.releaseStatus,
GUILayout.Width(AVATAR_RELEASE_STATUS_FIELD_WIDTH));
string oppositeReleaseStatus = a.releaseStatus == "public" ? "private" : "public";
if (GUILayout.Button("Make " + oppositeReleaseStatus,
GUILayout.Width(SET_AVATAR_STATUS_BUTTON_WIDTH)))
{
a.releaseStatus = oppositeReleaseStatus;
a.SaveReleaseStatus((c) =>
{
ApiAvatar savedBP = (ApiAvatar) c.Model;
if (justUpdatedAvatars == null) justUpdatedAvatars = new List<ApiAvatar>();
justUpdatedAvatars.Add(savedBP);
},
(c) =>
{
Debug.LogError(c.Error);
EditorUtility.DisplayDialog("Avatar Updated",
"Failed to change avatar release status", "OK");
});
}
if (GUILayout.Button("Copy ID", GUILayout.Width(COPY_AVATAR_ID_BUTTON_WIDTH)))
{
TextEditor te = new TextEditor();
te.text = a.id;
te.SelectAll();
te.Copy();
}
if (GUILayout.Button("Delete", GUILayout.Width(DELETE_AVATAR_BUTTON_WIDTH)))
{
if (EditorUtility.DisplayDialog("Delete " + a.name + "?",
"Are you sure you want to delete " + a.name + "? This cannot be undone.", "Delete",
"Cancel"))
{
foreach (VRC.Core.PipelineManager pm in FindObjectsOfType<VRC.Core.PipelineManager>()
.Where(pm => pm.blueprintId == a.id))
{
pm.blueprintId = "";
pm.completedSDKPipeline = false;
UnityEditor.EditorUtility.SetDirty(pm);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(pm.gameObject.scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(pm.gameObject.scene);
}
API.Delete<ApiAvatar>(a.id);
uploadedAvatars.RemoveAll(avatar => avatar.id == a.id);
if (ImageCache.ContainsKey(a.id))
ImageCache.Remove(a.id);
if (justDeletedContents == null) justDeletedContents = new List<string>();
justDeletedContents.Add(a.id);
updatedContent = true;
}
}
if (expandedLayout)
EditorGUILayout.EndHorizontal();
else
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
}
}
if (testAvatars.Count > 0)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Test Avatars", EditorStyles.boldLabel, GUILayout.ExpandWidth(false), GUILayout.Width(100));
TestAvatarsToggle = EditorGUILayout.Foldout(TestAvatarsToggle, new GUIContent(""));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (TestAvatarsToggle)
{
List<ApiAvatar> tmpAvatars = new List<ApiAvatar>();
if (testAvatars.Count > 0)
tmpAvatars = new List<ApiAvatar>(testAvatars);
foreach (ApiAvatar a in tmpAvatars)
{
if (!a.name.ToLowerInvariant().Contains(searchString.ToLowerInvariant()))
{
continue;
}
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
if (expandedLayout)
EditorGUILayout.BeginHorizontal();
else
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(a.name, descriptionStyle,
GUILayout.Width(expandedLayout
? position.width - MAX_ALL_INFORMATION_WIDTH + AVATAR_DESCRIPTION_FIELD_WIDTH
: AVATAR_DESCRIPTION_FIELD_WIDTH));
if (GUILayout.Button("Delete", GUILayout.Width(DELETE_AVATAR_BUTTON_WIDTH)))
{
if (EditorUtility.DisplayDialog("Delete " + a.name + "?",
"Are you sure you want to delete " + a.name + "? This cannot be undone.", "Delete",
"Cancel"))
{
API.Delete<ApiAvatar>(a.id);
testAvatars.RemoveAll(avatar => avatar.id == a.id);
File.Delete(a.assetUrl);
updatedContent = true;
}
}
if (expandedLayout)
EditorGUILayout.EndHorizontal();
else
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
}
}
EditorGUILayout.EndScrollView();
if (!expandedLayout)
{
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
if ((updatedContent) && (null != window)) window.Reset();
return true;
}
else
{
return false;
}
}
void ShowContent()
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
if (uploadedWorlds == null || uploadedAvatars == null || testAvatars == null)
{
if (uploadedWorlds == null)
uploadedWorlds = new List<ApiWorld>();
if (uploadedAvatars == null)
uploadedAvatars = new List<ApiAvatar>();
if (testAvatars == null)
testAvatars = new List<ApiAvatar>();
EditorCoroutine.Start(FetchUploadedData());
}
if( fetchingWorlds != null || fetchingAvatars != null )
{
GUILayout.BeginVertical(boxGuiStyle, GUILayout.Width(SdkWindowWidth));
EditorGUILayout.Space();
EditorGUILayout.LabelField("Fetching Records", titleGuiStyle);
EditorGUILayout.Space();
GUILayout.EndVertical();
}
else
{
GUILayout.BeginVertical(boxGuiStyle, GUILayout.Width(SdkWindowWidth));
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Fetch updated records from the VRChat server");
if( GUILayout.Button("Fetch") )
{
ClearContent();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
GUILayout.EndVertical();
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
OnGUIUserInfo();
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c7333cdb3df19724b84b4a1b05093fe0
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VRC;
using VRC.Core;
using VRC.SDKBase;
public partial class VRCSdkControlPanel : EditorWindow
{
[MenuItem("VRChat SDK/Help/Developer FAQ")]
public static void ShowDeveloperFAQ()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowDeveloperFAQ());
return;
}
Application.OpenURL(ConfigManager.RemoteConfig.GetString("sdkDeveloperFaqUrl"));
}
[MenuItem("VRChat SDK/Help/VRChat Discord")]
public static void ShowVRChatDiscord()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowVRChatDiscord());
return;
}
Application.OpenURL(ConfigManager.RemoteConfig.GetString("sdkDiscordUrl"));
}
[MenuItem("VRChat SDK/Help/Avatar Optimization Tips")]
public static void ShowAvatarOptimizationTips()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowAvatarOptimizationTips());
return;
}
Application.OpenURL(AVATAR_OPTIMIZATION_TIPS_URL);
}
[MenuItem("VRChat SDK/Help/Avatar Rig Requirements")]
public static void ShowAvatarRigRequirements()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowAvatarRigRequirements());
return;
}
Application.OpenURL(AVATAR_RIG_REQUIREMENTS_URL);
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f3507a74e4b8cfd469afac127fa5f4e5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,265 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using VRC.Core;
public partial class VRCSdkControlPanel : EditorWindow
{
bool UseDevApi
{
get
{
return VRC.Core.API.GetApiUrl() == VRC.Core.API.devApiUrl;
}
}
string clientVersionDate;
string sdkVersionDate;
Vector2 settingsScroll;
private void Awake()
{
GetClientSdkVersionInformation();
}
public void GetClientSdkVersionInformation()
{
clientVersionDate = VRC.Core.SDKClientUtilities.GetTestClientVersionDate();
sdkVersionDate = VRC.Core.SDKClientUtilities.GetSDKVersionDate();
}
public void OnConfigurationChanged()
{
GetClientSdkVersionInformation();
}
void ShowSettings()
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
settingsScroll = EditorGUILayout.BeginScrollView(settingsScroll, GUILayout.Width(SdkWindowWidth));
EditorGUILayout.BeginVertical(boxGuiStyle);
EditorGUILayout.LabelField("Developer", EditorStyles.boldLabel);
#if VRC_SDK_VRCSDK2
VRCSettings.Get().DisplayAdvancedSettings = EditorGUILayout.ToggleLeft("Show Extra Options on build page and account page", VRCSettings.Get().DisplayAdvancedSettings);
bool prevDisplayHelpBoxes = VRCSettings.Get().DisplayHelpBoxes;
VRCSettings.Get().DisplayHelpBoxes = EditorGUILayout.ToggleLeft("Show Help Boxes on SDK components", VRCSettings.Get().DisplayHelpBoxes);
if (VRCSettings.Get().DisplayHelpBoxes != prevDisplayHelpBoxes)
#elif VRC_SDK_VRCSDK3
VRC.SDK3.Editor.VRCSettings.Get().DisplayAdvancedSettings = EditorGUILayout.ToggleLeft("Show Extra Options on build page and account page", VRC.SDK3.Editor.VRCSettings.Get().DisplayAdvancedSettings);
bool prevDisplayHelpBoxes = VRC.SDK3.Editor.VRCSettings.Get().DisplayHelpBoxes;
VRC.SDK3.Editor.VRCSettings.Get().DisplayHelpBoxes = EditorGUILayout.ToggleLeft("Show Help Boxes on SDK components", VRC.SDK3.Editor.VRCSettings.Get().DisplayHelpBoxes);
if (VRC.SDK3.Editor.VRCSettings.Get().DisplayHelpBoxes != prevDisplayHelpBoxes)
#endif
{
Editor[] editors = (Editor[])Resources.FindObjectsOfTypeAll<Editor>();
for (int i = 0; i < editors.Length; i++)
{
editors[i].Repaint();
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
ShowSdk23CompatibilitySettings();
EditorGUILayout.Separator();
ShowSettingsOptionsForBuilders();
// debugging
if (APIUser.CurrentUser != null && APIUser.CurrentUser.hasSuperPowers)
{
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxGuiStyle);
EditorGUILayout.LabelField("Logging", EditorStyles.boldLabel);
// API logging
{
bool isLoggingEnabled = UnityEditor.EditorPrefs.GetBool("apiLoggingEnabled");
bool enableLogging = EditorGUILayout.ToggleLeft("API Logging Enabled", isLoggingEnabled);
if (enableLogging != isLoggingEnabled)
{
if (enableLogging)
VRC.Core.Logger.AddDebugLevel(DebugLevel.API);
else
VRC.Core.Logger.RemoveDebugLevel(DebugLevel.API);
UnityEditor.EditorPrefs.SetBool("apiLoggingEnabled", enableLogging);
}
}
// All logging
{
bool isLoggingEnabled = UnityEditor.EditorPrefs.GetBool("allLoggingEnabled");
bool enableLogging = EditorGUILayout.ToggleLeft("All Logging Enabled", isLoggingEnabled);
if (enableLogging != isLoggingEnabled)
{
if (enableLogging)
VRC.Core.Logger.AddDebugLevel(DebugLevel.All);
else
VRC.Core.Logger.RemoveDebugLevel(DebugLevel.All);
UnityEditor.EditorPrefs.SetBool("allLoggingEnabled", enableLogging);
}
}
EditorGUILayout.EndVertical();
}
else
{
if (UnityEditor.EditorPrefs.GetBool("apiLoggingEnabled"))
UnityEditor.EditorPrefs.SetBool("apiLoggingEnabled", false);
if (UnityEditor.EditorPrefs.GetBool("allLoggingEnabled"))
UnityEditor.EditorPrefs.SetBool("allLoggingEnabled", false);
}
// Future proof upload
{
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxGuiStyle);
EditorGUILayout.LabelField("Publish", EditorStyles.boldLabel);
bool futureProofPublish = UnityEditor.EditorPrefs.GetBool("futureProofPublish", DefaultFutureProofPublishEnabled);
futureProofPublish = EditorGUILayout.ToggleLeft("Future Proof Publish", futureProofPublish);
if (UnityEditor.EditorPrefs.GetBool("futureProofPublish", DefaultFutureProofPublishEnabled) != futureProofPublish)
{
UnityEditor.EditorPrefs.SetBool("futureProofPublish", futureProofPublish);
}
EditorGUILayout.LabelField("Client Version Date", clientVersionDate);
EditorGUILayout.LabelField("SDK Version Date", sdkVersionDate);
EditorGUILayout.EndVertical();
}
if (APIUser.CurrentUser != null)
{
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxGuiStyle);
// custom vrchat install location
OnVRCInstallPathGUI();
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
static void OnVRCInstallPathGUI()
{
EditorGUILayout.LabelField("VRChat Client", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Installed Client Path: ", clientInstallPath);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("");
if (GUILayout.Button("Edit"))
{
string initPath = "";
if (!string.IsNullOrEmpty(clientInstallPath))
initPath = clientInstallPath;
clientInstallPath = EditorUtility.OpenFilePanel("Choose VRC Client Exe", initPath, "exe");
SDKClientUtilities.SetVRCInstallPath(clientInstallPath);
window.OnConfigurationChanged();
}
if (GUILayout.Button("Revert to Default"))
{
clientInstallPath = SDKClientUtilities.LoadRegistryVRCInstallPath();
window.OnConfigurationChanged();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
}
static void ShowSdk23CompatibilitySettings()
{
return;
// EditorGUILayout.BeginVertical(boxGuiStyle);
// EditorGUILayout.LabelField("VRCSDK2 & VRCSDK3 Compatibility", EditorStyles.boldLabel);
//#if !VRC_CLIENT
// bool sdk2Present = VRCSdk3Analysis.GetSDKInScene(VRCSdk3Analysis.SdkVersion.VRCSDK2).Count > 0;
// bool sdk3Present = VRCSdk3Analysis.GetSDKInScene(VRCSdk3Analysis.SdkVersion.VRCSDK3).Count > 0;
// bool sdk2DllActive = VRCSdk3Analysis.IsSdkDllActive(VRCSdk3Analysis.SdkVersion.VRCSDK2);
// bool sdk3DllActive = VRCSdk3Analysis.IsSdkDllActive(VRCSdk3Analysis.SdkVersion.VRCSDK3);
// if ( sdk2DllActive && sdk3DllActive)
// {
// GUILayout.TextArea("You have not yet configured this project for development with VRCSDK2 and Triggers or VRCSDK3 and Udon. ");
// if (sdk2Present && sdk3Present)
// {
// GUILayout.TextArea("This scene contains both SDK2 and SDK3 elements. " +
// "Please modify this scene to contain only one type or the other before completing your configuration.");
// }
// else if (sdk2Present)
// {
// GUILayout.TextArea("This scene contains SDK2 scripts. " +
// "Check below to configure this project for use with VRCSDK2 or remove your VRCSDK2 scripts to upgrade to VRCSDK3");
// bool downgrade = EditorGUILayout.ToggleLeft("Configure for use with VRCSDK2 and Triggers", false);
// if (downgrade)
// VRCSdk3Analysis.SetSdkVersionActive(VRCSdk3Analysis.SdkVersion.VRCSDK2);
// }
// else if (sdk3Present)
// {
// GUILayout.TextArea("This scene contains only SDK3 scripts and it ready to upgrade. " +
// "Click below to get started.");
// bool upgrade = EditorGUILayout.ToggleLeft("Configure for use with VRCSDK3 and Udon - Let's Rock!", false);
// if (upgrade)
// VRCSdk3Analysis.SetSdkVersionActive(VRCSdk3Analysis.SdkVersion.VRCSDK3);
// }
// else
// {
// GUILayout.TextArea("This scene is a blank slate. " +
// "Click below to get started.");
// bool upgrade = EditorGUILayout.ToggleLeft("Configure for use with VRCSDK3 and Udon - Let's Rock!", false);
// if (upgrade)
// VRCSdk3Analysis.SetSdkVersionActive(VRCSdk3Analysis.SdkVersion.VRCSDK3);
// }
// }
// else if (sdk2DllActive)
// {
// GUILayout.TextArea("This project has been configured to be built with VRCSDK2. " +
// "To upgrade, VRCSDK3 must be enabled here.");
// bool upgrade = EditorGUILayout.ToggleLeft("VRCSDK3 Scripts can be used", false);
// if (upgrade)
// VRCSdk3Analysis.SetSdkVersionActive(VRCSdk3Analysis.SdkVersion.VRCSDK3);
// }
// else if (sdk3DllActive)
// {
// GUILayout.TextArea("This project has been configured to be built with VRCSDK3. " +
// "Congratulations, you're ready to go. " +
// "You can still downgrade by activating VRCSDK2 here.");
// bool downgrade = EditorGUILayout.ToggleLeft("VRCSDK2 Scripts can be used", false);
// if (downgrade)
// VRCSdk3Analysis.SetSdkVersionActive(VRCSdk3Analysis.SdkVersion.VRCSDK2);
// }
// else
// {
// GUILayout.TextArea("Somehow you have disabled both VRCSDK2 and VRCSDK3. Oops. " +
// "Click here to begin development with VRCSDK3.");
// bool begin = EditorGUILayout.ToggleLeft("VRCSDK3 Scripts can be used", false);
// if (begin)
// VRCSdk3Analysis.SetSdkVersionActive(VRCSdk3Analysis.SdkVersion.VRCSDK3);
// }
//#else
// GUILayout.TextArea("I think you're in the main VRChat project. " +
// "You should not be enabling or disabling SDKs from here.");
//#endif
// EditorGUILayout.EndVertical();
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8357b9b7ef2416946ae86f465a64c0e0
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2a720a30f1043247af7742fdfd0b8e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: