avatar/Assets/VRCSDK/Dependencies/VRChat/Scripts/RuntimeBlueprintCreation.cs
2022-09-27 20:47:45 -07:00

422 lines
17 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using VRC.Core;
using VRC.SDKBase;
namespace VRCSDK2
{
#if UNITY_EDITOR
public class RuntimeBlueprintCreation : RuntimeAPICreation
{
public GameObject waitingPanel;
public GameObject blueprintPanel;
public GameObject errorPanel;
public Text titleText;
public InputField blueprintName;
public InputField blueprintDescription;
public RawImage bpImage;
public Image liveBpImage;
public Toggle shouldUpdateImageToggle;
public Toggle contentSex;
public Toggle contentViolence;
public Toggle contentGore;
public Toggle contentOther;
public Toggle developerAvatar;
public Toggle sharePrivate;
public Toggle sharePublic;
public Toggle tagFallback;
public UnityEngine.UI.Button uploadButton;
private ApiAvatar apiAvatar;
new void Start()
{
if (!Application.isEditor || !Application.isPlaying)
return;
base.Start();
var desc = pipelineManager.GetComponent<VRC.SDKBase.VRC_AvatarDescriptor>();
desc.PositionPortraitCamera(imageCapture.shotCamera.transform);
Application.runInBackground = true;
UnityEngine.XR.XRSettings.enabled = false;
uploadButton.onClick.AddListener(SetupUpload);
shouldUpdateImageToggle.onValueChanged.AddListener(ToggleUpdateImage);
Login();
}
void LoginErrorCallback(string obj)
{
VRC.Core.Logger.LogError("Could not log in - " + obj, DebugLevel.Always);
blueprintPanel.SetActive(false);
errorPanel.SetActive(true);
}
void Login()
{
if (!ApiCredentials.Load())
LoginErrorCallback("Not logged in");
else
APIUser.InitialFetchCurrentUser(
delegate (ApiModelContainer<APIUser> c)
{
pipelineManager.user = c.Model as APIUser;
ApiAvatar av = new ApiAvatar() { id = pipelineManager.blueprintId };
av.Get(false,
(c2) =>
{
VRC.Core.Logger.Log("<color=magenta>Updating an existing avatar.</color>", DebugLevel.API);
apiAvatar = c2.Model as ApiAvatar;
pipelineManager.completedSDKPipeline = !string.IsNullOrEmpty(apiAvatar.authorId);
SetupUI();
},
(c2) =>
{
VRC.Core.Logger.Log("<color=magenta>Creating a new avatar.</color>", DebugLevel.API);
apiAvatar = new ApiAvatar();
apiAvatar.id = pipelineManager.blueprintId;
pipelineManager.completedSDKPipeline = !string.IsNullOrEmpty(apiAvatar.authorId);
SetupUI();
});
}, (c) => {
LoginErrorCallback(c.Error);
});
}
void SetupUI()
{
if (!ValidateAssetBundleBlueprintID(apiAvatar.id))
{
blueprintPanel.SetActive(false);
errorPanel.SetActive(true);
OnSDKPipelineError("The asset bundle is out of date. Please rebuild the scene using 'New Build'.", "The blueprint ID in the scene does not match the id in the asset bundle.");
return;
}
if (APIUser.Exists(pipelineManager.user))
{
waitingPanel.SetActive(false);
blueprintPanel.SetActive(true);
errorPanel.SetActive(false);
if (isUpdate)
{
// bp update
if (apiAvatar.authorId == pipelineManager.user.id)
{
titleText.text = "Update Avatar";
// apiAvatar = pipelineManager.user.GetBlueprint(pipelineManager.blueprintId) as ApiAvatar;
blueprintName.text = apiAvatar.name;
contentSex.isOn = apiAvatar.tags.Contains("content_sex");
contentViolence.isOn = apiAvatar.tags.Contains("content_violence");
contentGore.isOn = apiAvatar.tags.Contains("content_gore");
contentOther.isOn = apiAvatar.tags.Contains("content_other");
developerAvatar.isOn = apiAvatar.tags.Contains("developer");
sharePrivate.isOn = apiAvatar.releaseStatus.Contains("private");
sharePublic.isOn = apiAvatar.releaseStatus.Contains("public");
tagFallback.isOn = apiAvatar.tags.Contains("author_quest_fallback");
tagFallback.transform.parent.gameObject.SetActive(true);
switch (pipelineManager.fallbackStatus)
{
case PipelineManager.FallbackStatus.Valid:
#if UNITY_ANDROID
tagFallback.interactable = true;
tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback";
#else
tagFallback.interactable = false;
tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback (change only with Android upload)";
#endif
break;
case PipelineManager.FallbackStatus.InvalidPerformance:
case PipelineManager.FallbackStatus.InvalidRig:
tagFallback.isOn = false; // need to remove tag on this upload, the updated version is not up-to-spec
tagFallback.interactable = false;
tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback (avatar not valid, tag will be cleared)";
break;
}
blueprintDescription.text = apiAvatar.description;
shouldUpdateImageToggle.interactable = true;
shouldUpdateImageToggle.isOn = false;
liveBpImage.enabled = false;
bpImage.enabled = true;
ImageDownloader.DownloadImage(apiAvatar.imageUrl, 0, (Texture2D obj) => bpImage.texture = obj, null);
}
else // user does not own apiAvatar id associated with descriptor
{
Debug.LogErrorFormat("{0} is not an owner of {1}", apiAvatar.authorId, pipelineManager.user.id);
blueprintPanel.SetActive(false);
errorPanel.SetActive(true);
}
}
else
{
titleText.text = "New Avatar";
shouldUpdateImageToggle.interactable = false;
shouldUpdateImageToggle.isOn = true;
liveBpImage.enabled = true;
bpImage.enabled = false;
tagFallback.isOn = false;
// Janky fix for an avatar's blueprint image not showing up the very first time you press publish in a project until you resize the window
// can remove if we fix the underlying issue or move publishing out of Play Mode
string firstTimeResize = $"{Application.identifier}-firstTimeResize";
if (!PlayerPrefs.HasKey(firstTimeResize))
{
GameViewMethods.ResizeGameView();
PlayerPrefs.SetInt(firstTimeResize, 1);
}
tagFallback.transform.parent.gameObject.SetActive(true);
switch (pipelineManager.fallbackStatus)
{
case PipelineManager.FallbackStatus.Valid:
#if UNITY_ANDROID
tagFallback.interactable = true;
tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback";
#else
tagFallback.interactable = false;
tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback (change only with Android upload)";
#endif
break;
case PipelineManager.FallbackStatus.InvalidPerformance:
case PipelineManager.FallbackStatus.InvalidRig:
tagFallback.transform.parent.gameObject.SetActive(true);
tagFallback.interactable = false;
tagFallback.GetComponentInChildren<Text>().text = "Use for Fallback (avatar not valid, tag will be cleared)";
break;
}
}
}
else
{
waitingPanel.SetActive(true);
blueprintPanel.SetActive(false);
errorPanel.SetActive(false);
}
if (APIUser.CurrentUser != null && APIUser.CurrentUser.hasSuperPowers)
developerAvatar.gameObject.SetActive(true);
else
developerAvatar.gameObject.SetActive(false);
}
public void SetupUpload()
{
uploadTitle = "Preparing For Upload";
isUploading = true;
string abPath = UnityEditor.EditorPrefs.GetString("currentBuildingAssetBundlePath");
string unityPackagePath = UnityEditor.EditorPrefs.GetString("VRC_exportedUnityPackagePath");
UnityEditor.EditorPrefs.SetBool("VRCSDK2_scene_changed", true);
UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_sex", contentSex.isOn);
UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_violence", contentViolence.isOn);
UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_gore", contentGore.isOn);
UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_other", contentOther.isOn);
if (string.IsNullOrEmpty(apiAvatar.id))
{
pipelineManager.AssignId();
apiAvatar.id = pipelineManager.blueprintId;
}
string avatarId = apiAvatar.id;
int version = isUpdate ? apiAvatar.version + 1 : 1;
PrepareVRCPathForS3(abPath, avatarId, version, ApiAvatar.VERSION);
if (!string.IsNullOrEmpty(unityPackagePath) && System.IO.File.Exists(unityPackagePath))
{
VRC.Core.Logger.Log("Found unity package path. Preparing to upload!", DebugLevel.All);
PrepareUnityPackageForS3(unityPackagePath, avatarId, version, ApiAvatar.VERSION);
}
StartCoroutine(UploadNew());
}
IEnumerator UploadNew()
{
bool caughtInvalidInput = false;
if (!ValidateNameInput(blueprintName))
caughtInvalidInput = true;
if (caughtInvalidInput)
yield break;
VRC.Core.Logger.Log("Starting upload", DebugLevel.Always);
// upload unity package
if (!string.IsNullOrEmpty(uploadUnityPackagePath))
{
yield return StartCoroutine(UploadFile(uploadUnityPackagePath, isUpdate ? apiAvatar.unityPackageUrl : "", GetFriendlyAvatarFileName("Unity package"), "Unity package",
delegate (string fileUrl)
{
cloudFrontUnityPackageUrl = fileUrl;
}
));
}
// upload asset bundle
if (!string.IsNullOrEmpty(uploadVrcPath))
{
yield return StartCoroutine(UploadFile(uploadVrcPath, isUpdate ? apiAvatar.assetUrl : "", GetFriendlyAvatarFileName("Asset bundle"), "Asset bundle",
delegate (string fileUrl)
{
cloudFrontAssetUrl = fileUrl;
}
));
}
if (isUpdate)
yield return StartCoroutine(UpdateBlueprint());
else
yield return StartCoroutine(CreateBlueprint());
OnSDKPipelineComplete();
}
private string GetFriendlyAvatarFileName(string type)
{
return "Avatar - " + blueprintName.text + " - " + type + " - " + Application.unityVersion + "_" + ApiWorld.VERSION.ApiVersion +
"_" + VRC.Tools.Platform + "_" + API.GetServerEnvironmentForApiUrl();
}
List<string> BuildTags()
{
var tags = new List<string>();
if (contentSex.isOn)
tags.Add("content_sex");
if (contentViolence.isOn)
tags.Add("content_violence");
if (contentGore.isOn)
tags.Add("content_gore");
if (contentOther.isOn)
tags.Add("content_other");
if (APIUser.CurrentUser.hasSuperPowers)
{
if (developerAvatar.isOn)
tags.Add("developer");
}
if (tagFallback.isOn)
tags.Add("author_quest_fallback");
return tags;
}
protected override IEnumerator CreateBlueprint()
{
yield return StartCoroutine(UpdateImage(isUpdate ? apiAvatar.imageUrl : "", GetFriendlyAvatarFileName("Image")));
ApiAvatar avatar = new ApiAvatar
{
id = pipelineManager.blueprintId,
authorName = pipelineManager.user.displayName,
authorId = pipelineManager.user.id,
name = blueprintName.text,
imageUrl = cloudFrontImageUrl,
assetUrl = cloudFrontAssetUrl,
description = blueprintDescription.text,
tags = BuildTags(),
unityPackageUrl = cloudFrontUnityPackageUrl,
releaseStatus = sharePublic.isOn ? "public" : "private"
};
bool doneUploading = false;
bool wasError = false;
avatar.Post(
(c) =>
{
ApiAvatar savedBP = (ApiAvatar)c.Model;
pipelineManager.blueprintId = savedBP.id;
UnityEditor.EditorPrefs.SetString("blueprintID-" + pipelineManager.GetInstanceID().ToString(), savedBP.id);
AnalyticsSDK.AvatarUploaded(savedBP, false);
doneUploading = true;
},
(c) =>
{
Debug.LogError(c.Error);
SetUploadProgress("Saving Avatar", "Error saving blueprint.", 0.0f);
doneUploading = true;
wasError = true;
});
while (!doneUploading)
yield return null;
if (wasError)
yield return new WaitUntil(() => UnityEditor.EditorUtility.DisplayDialog("VRChat SDK", "Error saving blueprint.", "Okay"));
}
protected override IEnumerator UpdateBlueprint()
{
bool doneUploading = false;
apiAvatar.name = blueprintName.text;
apiAvatar.description = blueprintDescription.text;
apiAvatar.assetUrl = cloudFrontAssetUrl;
apiAvatar.releaseStatus = sharePublic.isOn ? "public" : "private";
apiAvatar.tags = BuildTags();
apiAvatar.unityPackageUrl = cloudFrontUnityPackageUrl;
if (shouldUpdateImageToggle.isOn)
{
yield return StartCoroutine(UpdateImage(isUpdate ? apiAvatar.imageUrl : "", GetFriendlyAvatarFileName("Image")));
apiAvatar.imageUrl = cloudFrontImageUrl;
}
SetUploadProgress("Saving Avatar", "Almost finished!!", 0.8f);
apiAvatar.Save(
(c) => { AnalyticsSDK.AvatarUploaded(apiAvatar, true); doneUploading = true; },
(c) => {
Debug.LogError(c.Error);
SetUploadProgress("Saving Avatar", "Error saving blueprint.", 0.0f);
doneUploading = true;
});
while (!doneUploading)
yield return null;
}
void ToggleUpdateImage(bool isOn)
{
if (isOn)
{
bpImage.enabled = false;
liveBpImage.enabled = true;
}
else
{
bpImage.enabled = true;
liveBpImage.enabled = false;
ImageDownloader.DownloadImage(apiAvatar.imageUrl, 0, obj => bpImage.texture = obj, null);
}
}
void OnDestroy()
{
UnityEditor.EditorUtility.ClearProgressBar();
UnityEditor.EditorPrefs.DeleteKey("currentBuildingAssetBundlePath");
}
}
#endif
}