Added Unity project files
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36c0d886a26373c46be857f2fc441071
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,20 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRC.SDKBase
|
||||
{
|
||||
public class AudioManagerSettings
|
||||
{
|
||||
public const float MinVoiceSendDistance = 25.0f; // meters
|
||||
public const float MaxVoiceSendDistancePctOfFarRange = 0.5f;
|
||||
public const float VoiceFadeOutDistancePctOfFarRange = 0.25f;
|
||||
public const float RoomAudioGain = 10f; // dB
|
||||
public const float RoomAudioMaxRange = 80f; // meters
|
||||
public const float VoiceGain = 15f; // dB
|
||||
public const float VoiceMaxRange = 25f; // meters, this is half the oculus inv sq max range
|
||||
public const float LipsyncGain = 1f; // multiplier, not dB!
|
||||
public const float AvatarAudioMaxGain = 10f; // dB
|
||||
public const float AvatarAudioMaxRange = 40f; // meters
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acadc6659c5ab3446ad0d5de2563f95f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,20 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
public class CommunityLabsConstants
|
||||
{
|
||||
public const string COMMUNITY_LABS_DOCUMENTATION_URL = "https://docs.vrchat.com/docs/vrchat-community-labs";
|
||||
|
||||
public const string MANAGE_WORLD_IN_BROWSER_STRING = "Manage World in Browser";
|
||||
public const string READ_COMMUNITY_LABS_DOCS_STRING = "Read Community Labs Docs";
|
||||
|
||||
public const string UPLOADED_CONTENT_SUCCESSFULLY_MESSAGE = "Content Successfully Uploaded!";
|
||||
|
||||
public const string UPLOADED_NEW_PRIVATE_WORLD_CONFIRMATION_MESSAGE = "You've uploaded a private world. You can find it on the \"Mine\" row in your worlds menu.";
|
||||
public const string UPDATED_PRIVATE_WORLD_CONFIRMATION_MESSAGE = "You've updated your private world. You can find it on the \"Mine\" row in your worlds menu.";
|
||||
public const string PUBLISHED_WORLD_TO_COMMUNITY_LABS_CONFIRMATION_MESSAGE = "You've uploaded and published a world to Community Labs.";
|
||||
public const string UPDATED_COMMUNITY_LABS_WORLD_CONFIRMATION_MESSAGE = "You've updated your Community Labs world.";
|
||||
public const string UPDATED_PUBLIC_WORLD_CONFIRMATION_MESSAGE = "You've updated your public world.";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d047eaa3325d654aa62ccad6f73eb93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace VRC. SDKBase
|
||||
{
|
||||
public static class GameViewMethods
|
||||
{
|
||||
private static readonly Type GameViewType = System.Type.GetType("UnityEditor.GameView,UnityEditor");
|
||||
private static readonly Type PlayModeViewType = System.Type.GetType("UnityEditor.PlayModeView, UnityEditor");
|
||||
|
||||
public static int GetSelectedSizeIndex()
|
||||
{
|
||||
return (int) GetSelectedSizeProperty().GetValue(GetPlayModeViewObject());
|
||||
}
|
||||
|
||||
public static void SetSelectedSizeIndex(int value)
|
||||
{
|
||||
var selectedSizeIndexProp = GetSelectedSizeProperty();
|
||||
selectedSizeIndexProp.SetValue(GetPlayModeViewObject(), value, null);
|
||||
}
|
||||
|
||||
// Set it to something else just to force a refresh
|
||||
public static void ResizeGameView()
|
||||
{
|
||||
int current = GetSelectedSizeIndex();
|
||||
SetSelectedSizeIndex(current == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
private static PropertyInfo GetSelectedSizeProperty()
|
||||
{
|
||||
return GameViewType.GetProperty("selectedSizeIndex",
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
|
||||
private static Object GetPlayModeViewObject()
|
||||
{
|
||||
MethodInfo GetMainPlayModeView = PlayModeViewType.GetMethod("GetMainPlayModeView",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
|
||||
return GetMainPlayModeView.Invoke(null, null);
|
||||
}
|
||||
|
||||
public static void Repaint()
|
||||
{
|
||||
MethodInfo RepaintAll = PlayModeViewType.GetMethod("RepaintAll", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
RepaintAll.Invoke(GetPlayModeViewObject(), null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0c461e358764cd1ab95544e34b0346c
|
||||
timeCreated: 1627603592
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0f7ad406aeb6f64ab5c36b429a466b7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class FallbackMaterialCache
|
||||
{
|
||||
private readonly Dictionary<Material, Material> _fallbackMaterialCache = new Dictionary<Material, Material>();
|
||||
|
||||
public void AddFallbackMaterial(Material material, Material fallbackMaterial)
|
||||
{
|
||||
if(!_fallbackMaterialCache.ContainsKey(material))
|
||||
{
|
||||
_fallbackMaterialCache.Add(material, fallbackMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
#pragma warning disable RS0030 // Banned APIs
|
||||
Debug.LogError($"Attempted to add a duplicate fallback material '{fallbackMaterial.name}' for original material '{material.name}'.");
|
||||
#pragma warning restore RS0030
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetFallbackMaterial(Material material, out Material fallbackMaterial)
|
||||
{
|
||||
if(material != null)
|
||||
{
|
||||
return _fallbackMaterialCache.TryGetValue(material, out fallbackMaterial);
|
||||
}
|
||||
|
||||
fallbackMaterial = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Material[] cachedFallbackMaterials = _fallbackMaterialCache.Values.ToArray();
|
||||
for(int i = cachedFallbackMaterials.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Object.Destroy(cachedFallbackMaterials[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Object.DestroyImmediate(cachedFallbackMaterials[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_fallbackMaterialCache.Clear();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10121679f780956408f9a434a526f553
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,325 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
using VRC.Core;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
[Obsolete("Runtime uploads are deprecated. Use methods provided by the VRC.SDKBase.Editor.Api.VRCApi class for uploads")]
|
||||
public class RuntimeAPICreation : MonoBehaviour
|
||||
{
|
||||
public VRC.Core.PipelineManager pipelineManager;
|
||||
|
||||
protected bool forceNewFileCreation = false;
|
||||
protected bool useFileApi = false;
|
||||
|
||||
protected bool isUploading = false;
|
||||
protected float uploadProgress = 0f;
|
||||
protected string uploadMessage;
|
||||
protected string uploadTitle;
|
||||
|
||||
protected string uploadVrcPath;
|
||||
protected string uploadUnityPackagePath;
|
||||
|
||||
protected string cloudFrontAssetUrl;
|
||||
protected string cloudFrontImageUrl;
|
||||
protected string cloudFrontUnityPackageUrl;
|
||||
|
||||
protected CameraImageCapture imageCapture;
|
||||
|
||||
private bool cancelRequested = false;
|
||||
|
||||
public static bool publishingToCommunityLabs = false;
|
||||
|
||||
private Dictionary<string, string> mRetryState = new Dictionary<string, string>();
|
||||
|
||||
protected bool isUpdate { get { return pipelineManager.completedSDKPipeline; } }
|
||||
|
||||
protected void Start()
|
||||
{
|
||||
if (!Application.isEditor || !Application.isPlaying)
|
||||
return;
|
||||
|
||||
PipelineSaver ps = GameObject.FindFirstObjectByType<PipelineSaver>();
|
||||
pipelineManager = ps.gameObject.GetComponent<PipelineManager>();
|
||||
|
||||
imageCapture = GetComponent<CameraImageCapture>();
|
||||
imageCapture.shotCamera = GameObject.Find("VRCCam").GetComponent<Camera>();
|
||||
|
||||
LoadUploadRetryStateFromCache();
|
||||
|
||||
forceNewFileCreation = UnityEditor.EditorPrefs.GetBool("forceNewFileCreation", true);
|
||||
useFileApi = UnityEditor.EditorPrefs.GetBool("useFileApi", false);
|
||||
|
||||
API.SetOnlineMode(true);
|
||||
}
|
||||
|
||||
protected void Update()
|
||||
{
|
||||
if (isUploading)
|
||||
{
|
||||
bool cancelled = UnityEditor.EditorUtility.DisplayCancelableProgressBar(uploadTitle, uploadMessage, uploadProgress);
|
||||
if (cancelled)
|
||||
{
|
||||
cancelRequested = true;
|
||||
}
|
||||
|
||||
if (EditorApplication.isPaused)
|
||||
EditorApplication.isPaused = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void LoadUploadRetryStateFromCache()
|
||||
{
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(GetUploadRetryStateFilePath());
|
||||
mRetryState = VRC.Tools.ObjDictToStringDict(VRC.Tools.JsonDecode(json) as Dictionary<string, object>);
|
||||
|
||||
Debug.LogFormat("<color=yellow> loaded retry state: {0}</color>", json);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// normal case
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("Loaded upload retry state from: " + GetUploadRetryStateFilePath());
|
||||
}
|
||||
|
||||
protected void SaveUploadRetryState(string key, string val)
|
||||
{
|
||||
if (string.IsNullOrEmpty(val))
|
||||
return;
|
||||
mRetryState[key] = val;
|
||||
SaveUploadRetryState();
|
||||
}
|
||||
|
||||
protected void SaveUploadRetryState()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(GetUploadRetryStateFilePath()));
|
||||
string json = VRC.Tools.JsonEncode(mRetryState);
|
||||
File.WriteAllText(GetUploadRetryStateFilePath(), json);
|
||||
|
||||
Debug.LogFormat("<color=yellow> wrote retry state: {0}</color>", json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("Couldn't save upload retry state: " + GetUploadRetryStateFilePath() + "\n" + e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("Saved upload retry state to: " + GetUploadRetryStateFilePath());
|
||||
}
|
||||
|
||||
protected void ClearUploadRetryState()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(GetUploadRetryStateFilePath()))
|
||||
return;
|
||||
|
||||
File.Delete(GetUploadRetryStateFilePath());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("Couldn't delete upload retry state: " + GetUploadRetryStateFilePath() + "\n" + e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("Cleared upload retry state at: " + GetUploadRetryStateFilePath());
|
||||
}
|
||||
|
||||
protected string GetUploadRetryStateFilePath()
|
||||
{
|
||||
string id = UnityEditor.AssetDatabase.AssetPathToGUID(SceneManager.GetActiveScene().path);
|
||||
return Path.Combine(VRC.Tools.GetTempFolderPath(id), "upload_retry.dat");
|
||||
}
|
||||
|
||||
protected string GetUploadRetryStateValue(string key)
|
||||
{
|
||||
return mRetryState.ContainsKey(key) ? mRetryState[key] : "";
|
||||
}
|
||||
|
||||
protected virtual void DisplayUpdateCompletedDialog(string contentUrl=null)
|
||||
{
|
||||
if (UnityEditor.EditorUtility.DisplayDialog("VRChat SDK", "Update Complete! Launch VRChat to see your uploaded content." + (null==contentUrl ? "" : "\n\nManage content at: " + contentUrl ), (null == contentUrl) ? "Okay" : CommunityLabsConstants.MANAGE_WORLD_IN_BROWSER_STRING, (null == contentUrl) ? "" : "Done" ))
|
||||
{
|
||||
if (null!=contentUrl)
|
||||
{
|
||||
Application.OpenURL(contentUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnSDKPipelineComplete(string contentUrl=null)
|
||||
{
|
||||
VRC.Core.Logger.Log("OnSDKPipelineComplete", API.LOG_CATEGORY);
|
||||
isUploading = false;
|
||||
pipelineManager.completedSDKPipeline = true;
|
||||
ClearUploadRetryState();
|
||||
UnityEditor.EditorPrefs.SetBool("forceNewFileCreation", false);
|
||||
UnityEditor.EditorApplication.isPaused = false;
|
||||
UnityEditor.EditorApplication.isPlaying = false;
|
||||
UnityEditor.EditorUtility.ClearProgressBar();
|
||||
DisplayUpdateCompletedDialog(contentUrl);
|
||||
}
|
||||
|
||||
protected void OnSDKPipelineError(string error, string details)
|
||||
{
|
||||
VRC.Core.Logger.Log("OnSDKPipelineError: " + error + " - " + details, API.LOG_CATEGORY);
|
||||
isUploading = false;
|
||||
pipelineManager.completedSDKPipeline = true;
|
||||
UnityEditor.EditorApplication.isPaused = false;
|
||||
UnityEditor.EditorApplication.isPlaying = false;
|
||||
UnityEditor.EditorUtility.ClearProgressBar();
|
||||
if (cancelRequested)
|
||||
UnityEditor.EditorUtility.DisplayDialog("VRChat SDK", "The update was cancelled.", "Okay");
|
||||
else
|
||||
UnityEditor.EditorUtility.DisplayDialog("VRChat SDK", "Error updating content. " + error + "\n" + details, "Okay");
|
||||
}
|
||||
|
||||
protected void SetUploadProgress(string title, string message, float progress)
|
||||
{
|
||||
uploadTitle = title;
|
||||
uploadMessage = message;
|
||||
uploadProgress = progress;
|
||||
}
|
||||
|
||||
protected bool WasCancelRequested(ApiFile apiFile)
|
||||
{
|
||||
return cancelRequested;
|
||||
}
|
||||
|
||||
protected void PrepareUnityPackageForS3(string packagePath, string blueprintId, int version, AssetVersion assetVersion)
|
||||
{
|
||||
uploadUnityPackagePath = Application.temporaryCachePath + "/" + blueprintId + "_" + version.ToString() + "_" + Application.unityVersion + "_" + assetVersion.ApiVersion + "_" + VRC.Tools.Platform +
|
||||
"_" + API.GetServerEnvironmentForApiUrl() + ".unitypackage";
|
||||
uploadUnityPackagePath.Trim();
|
||||
uploadUnityPackagePath.Replace(' ', '_');
|
||||
|
||||
if (System.IO.File.Exists(uploadUnityPackagePath))
|
||||
System.IO.File.Delete(uploadUnityPackagePath);
|
||||
|
||||
System.IO.File.Copy(packagePath, uploadUnityPackagePath);
|
||||
}
|
||||
|
||||
protected void PrepareVRCPathForS3(string abPath, string blueprintId, int version, AssetVersion assetVersion)
|
||||
{
|
||||
uploadVrcPath = Application.temporaryCachePath + "/" + blueprintId + "_" + version.ToString() + "_" + Application.unityVersion + "_" + assetVersion.ApiVersion + "_" + VRC.Tools.Platform + "_" + API.GetServerEnvironmentForApiUrl() + System.IO.Path.GetExtension(abPath);
|
||||
uploadVrcPath.Trim();
|
||||
uploadVrcPath.Replace(' ', '_');
|
||||
|
||||
if (System.IO.File.Exists(uploadVrcPath))
|
||||
System.IO.File.Delete(uploadVrcPath);
|
||||
|
||||
System.IO.File.Copy(abPath, uploadVrcPath);
|
||||
}
|
||||
|
||||
protected IEnumerator UploadFile(string filename, string existingFileUrl, string friendlyFilename, string fileType, Action<string> onSuccess)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
yield break;
|
||||
VRC.Core.Logger.Log("Uploading " + fileType + "(" + filename + ") ...", API.LOG_CATEGORY);
|
||||
SetUploadProgress("Uploading " + fileType + "...", "", 0.0f);
|
||||
|
||||
string fileId = GetUploadRetryStateValue(filename);
|
||||
if (string.IsNullOrEmpty(fileId))
|
||||
fileId = isUpdate ? ApiFile.ParseFileIdFromFileAPIUrl(existingFileUrl) : "";
|
||||
string errorStr = "";
|
||||
string newFileUrl = "";
|
||||
|
||||
|
||||
yield return StartCoroutine(ApiFileHelper.Instance.UploadFile(filename, forceNewFileCreation ? "" : fileId, friendlyFilename,
|
||||
delegate (ApiFile apiFile, string message)
|
||||
{
|
||||
newFileUrl = apiFile.GetFileURL();
|
||||
if (VRC.Core.Logger.CategoryIsEnabled(API.LOG_CATEGORY))
|
||||
VRC.Core.Logger.Log(fileType + " upload succeeded: " + message + " (" + filename +
|
||||
") => " + apiFile.ToString(), API.LOG_CATEGORY);
|
||||
else
|
||||
VRC.Core.Logger.Log(fileType + " upload succeeded ");
|
||||
},
|
||||
delegate (ApiFile apiFile, string error)
|
||||
{
|
||||
SaveUploadRetryState(filename, apiFile.id);
|
||||
|
||||
errorStr = error;
|
||||
Debug.LogError(fileType + " upload failed: " + error + " (" + filename +
|
||||
") => " + apiFile.ToString());
|
||||
},
|
||||
delegate (ApiFile apiFile, string status, string subStatus, float pct)
|
||||
{
|
||||
SetUploadProgress("Uploading " + fileType + "...", status + (!string.IsNullOrEmpty(subStatus) ? " (" + subStatus + ")" : ""), pct);
|
||||
},
|
||||
WasCancelRequested
|
||||
));
|
||||
|
||||
if (!string.IsNullOrEmpty(errorStr))
|
||||
{
|
||||
OnSDKPipelineError(fileType + " upload failed.", errorStr);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (onSuccess != null)
|
||||
onSuccess(newFileUrl);
|
||||
}
|
||||
|
||||
protected IEnumerator UpdateImage(string existingFileUrl, string friendlyFileName)
|
||||
{
|
||||
string imagePath = imageCapture.TakePicture();
|
||||
|
||||
if (!string.IsNullOrEmpty(imagePath))
|
||||
{
|
||||
yield return StartCoroutine(UploadFile(imagePath, existingFileUrl, friendlyFileName, "Image",
|
||||
delegate (string fileUrl)
|
||||
{
|
||||
cloudFrontImageUrl = fileUrl;
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateBlueprint()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected virtual IEnumerator UpdateBlueprint()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected bool ValidateNameInput(InputField nameInput)
|
||||
{
|
||||
bool isValid = true;
|
||||
if (string.IsNullOrEmpty(nameInput.text))
|
||||
{
|
||||
isUploading = false;
|
||||
UnityEditor.EditorUtility.DisplayDialog("Invalid Input", "Cannot leave the name field empty.", "OK");
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
protected bool ValidateAssetBundleBlueprintID(string blueprintID)
|
||||
{
|
||||
string lastBuiltID = UnityEditor.EditorPrefs.GetString("lastBuiltAssetBundleBlueprintID", "");
|
||||
return !string.IsNullOrEmpty(lastBuiltID) && lastBuiltID == blueprintID;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3132e0ab7e16494a9d492087a1ca447
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,421 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using VRC.Core;
|
||||
using VRC.SDKBase;
|
||||
using VRC.SDK3.Image;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[Obsolete("Runtime uploads are deprecated. Use methods provided by the VRC.SDKBase.Editor.Api.VRCApi class for uploads")]
|
||||
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);
|
||||
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>", API.LOG_CATEGORY);
|
||||
apiAvatar = c2.Model as ApiAvatar;
|
||||
pipelineManager.completedSDKPipeline = !string.IsNullOrEmpty(apiAvatar.authorId);
|
||||
SetupUI();
|
||||
},
|
||||
(c2) =>
|
||||
{
|
||||
VRC.Core.Logger.Log("<color=magenta>Creating a new avatar.</color>", API.LOG_CATEGORY);
|
||||
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 || UNITY_IOS
|
||||
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 || UNITY_IOS
|
||||
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(PipelineManager.ContentType.avatar);
|
||||
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!", API.LOG_CATEGORY);
|
||||
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");
|
||||
|
||||
// upload unity package
|
||||
if (!string.IsNullOrEmpty(uploadUnityPackagePath))
|
||||
{
|
||||
yield return StartCoroutine(UploadFile(uploadUnityPackagePath, "", 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(),
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
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) => {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
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e5ebf65c5dceeb4c909aa7812bd2999
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,669 @@
|
||||
#define COMMUNITY_LABS_SDK
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Resources;
|
||||
using VRC.Core;
|
||||
using System;
|
||||
using System.IO;
|
||||
using VRC.SDK3.Image;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using VRC.SDKBase;
|
||||
#endif
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[Obsolete("Runtime uploads are deprecated. Use methods provided by the VRC.SDKBase.Editor.Api.VRCApi class for uploads")]
|
||||
public class RuntimeWorldCreation : RuntimeAPICreation
|
||||
{
|
||||
public VRC_SceneDescriptor descriptor;
|
||||
|
||||
public GameObject waitingPanel;
|
||||
public GameObject blueprintPanel;
|
||||
public GameObject errorPanel;
|
||||
|
||||
public Text titleText;
|
||||
public InputField blueprintName;
|
||||
public InputField blueprintDescription;
|
||||
public InputField worldCapacity;
|
||||
public RawImage bpImage;
|
||||
public Image liveBpImage;
|
||||
public Toggle shouldUpdateImageToggle;
|
||||
public Toggle releasePublic;
|
||||
public Toggle contentNsfw;
|
||||
|
||||
public Toggle contentSex;
|
||||
public Toggle contentViolence;
|
||||
public Toggle contentGore;
|
||||
public Toggle contentOther;
|
||||
|
||||
public Toggle contentFeatured;
|
||||
public Toggle contentSDKExample;
|
||||
|
||||
public Image showInWorldsMenuGroup;
|
||||
public Toggle showInActiveWorlds;
|
||||
public Toggle showInPopularWorlds;
|
||||
public Toggle showInNewWorlds;
|
||||
|
||||
public InputField userTags;
|
||||
|
||||
public UnityEngine.UI.Button uploadButton;
|
||||
|
||||
public UnityEngine.UI.Button openCommunityLabsDocsButton;
|
||||
|
||||
public GameObject publishToCommunityLabsPanel;
|
||||
|
||||
private Toggle publishToCommLabsToggle;
|
||||
|
||||
private ApiWorld worldRecord;
|
||||
|
||||
private const int MAX_USER_TAGS_FOR_WORLD = 5;
|
||||
private const int MAX_CHARACTERS_ALLOWED_IN_USER_TAG = 20;
|
||||
List<String> customTags;
|
||||
|
||||
public static bool IsCurrentWorldInCommunityLabs = false;
|
||||
public static bool IsCurrentWorldUploaded = false;
|
||||
public static bool IsCurrentWorldPubliclyPublished = false;
|
||||
public static bool HasExceededPublishLimit = false;
|
||||
|
||||
new void Start()
|
||||
{
|
||||
if (!Application.isEditor || !Application.isPlaying)
|
||||
return;
|
||||
|
||||
base.Start();
|
||||
|
||||
IsCurrentWorldInCommunityLabs = false;
|
||||
IsCurrentWorldUploaded = false;
|
||||
IsCurrentWorldPubliclyPublished = false;
|
||||
|
||||
|
||||
descriptor = pipelineManager.GetComponent<VRC.SDKBase.VRC_SceneDescriptor>();
|
||||
descriptor.PositionPortraitCamera(imageCapture.shotCamera.transform);
|
||||
|
||||
Application.runInBackground = true;
|
||||
UnityEngine.XR.XRSettings.enabled = false;
|
||||
|
||||
uploadButton.onClick.AddListener(SetupUpload);
|
||||
|
||||
openCommunityLabsDocsButton.onClick.AddListener(OpenCommunityLabsDocumentation);
|
||||
|
||||
shouldUpdateImageToggle.onValueChanged.AddListener(ToggleUpdateImage);
|
||||
|
||||
releasePublic.gameObject.SetActive(false);
|
||||
|
||||
System.Action<string> onError = (err) => {
|
||||
VRC.Core.Logger.LogError("Could not authenticate - " + err);
|
||||
blueprintPanel.SetActive(false);
|
||||
errorPanel.SetActive(true);
|
||||
};
|
||||
|
||||
if (!ApiCredentials.Load())
|
||||
onError("Not logged in");
|
||||
else
|
||||
APIUser.InitialFetchCurrentUser(
|
||||
delegate (ApiModelContainer<APIUser> c)
|
||||
{
|
||||
UserLoggedInCallback(c.Model as APIUser);
|
||||
},
|
||||
delegate (ApiModelContainer<APIUser> c)
|
||||
{
|
||||
onError(c.Error);
|
||||
}
|
||||
);
|
||||
|
||||
#if !COMMUNITY_LABS_SDK
|
||||
publishToCommunityLabsPanel.gameObject.SetActive(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UserLoggedInCallback(APIUser user)
|
||||
{
|
||||
pipelineManager.user = user;
|
||||
|
||||
ApiWorld model = new ApiWorld();
|
||||
model.id = pipelineManager.blueprintId;
|
||||
model.Fetch(null,
|
||||
(c) =>
|
||||
{
|
||||
VRC.Core.Logger.Log("<color=magenta>Updating an existing world.</color>", API.LOG_CATEGORY);
|
||||
worldRecord = c.Model as ApiWorld;
|
||||
pipelineManager.completedSDKPipeline = !string.IsNullOrEmpty(worldRecord.authorId);
|
||||
GetUserUploadInformationAndSetupUI(model.id);
|
||||
},
|
||||
(c) =>
|
||||
{
|
||||
VRC.Core.Logger.Log("<color=magenta>World record not found, creating a new world.</color>", API.LOG_CATEGORY);
|
||||
worldRecord = new ApiWorld { capacity = 16 };
|
||||
pipelineManager.completedSDKPipeline = false;
|
||||
worldRecord.id = pipelineManager.blueprintId;
|
||||
GetUserUploadInformationAndSetupUI(model.id);
|
||||
});
|
||||
}
|
||||
|
||||
void CheckWorldStatus(string worldId, Action onCheckComplete)
|
||||
{
|
||||
// check if world has been previously uploaded, and if world is in community labs
|
||||
ApiWorld.FetchUploadedWorlds(
|
||||
delegate (IEnumerable<ApiWorld> worlds)
|
||||
{
|
||||
ApiWorld selectedWorld = worlds.FirstOrDefault(w => w.id == worldId);
|
||||
if (null!=selectedWorld)
|
||||
{
|
||||
IsCurrentWorldInCommunityLabs = selectedWorld.IsCommunityLabsWorld;
|
||||
IsCurrentWorldPubliclyPublished = selectedWorld.IsPublicPublishedWorld;
|
||||
IsCurrentWorldUploaded = true;
|
||||
}
|
||||
if (onCheckComplete != null) onCheckComplete();
|
||||
|
||||
},
|
||||
delegate (string err)
|
||||
{
|
||||
IsCurrentWorldInCommunityLabs = false;
|
||||
IsCurrentWorldUploaded = false;
|
||||
IsCurrentWorldPubliclyPublished = false;
|
||||
Debug.Log("CheckWorldStatus error:" + err);
|
||||
if (onCheckComplete != null) onCheckComplete();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void GetUserUploadInformationAndSetupUI(string worldId)
|
||||
{
|
||||
CheckWorldStatus(worldId, delegate()
|
||||
{
|
||||
bool hasSufficientTrustLevelToPublishToCommunityLabs = APIUser.CurrentUser.hasKnownTrustLevel;
|
||||
APIUser.FetchPublishWorldsInformation(
|
||||
(c) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (c["canPublish"].Type == BestHTTP.JSON.Json.TokenType.Boolean)
|
||||
{
|
||||
HasExceededPublishLimit = !(bool)(c["canPublish"]);
|
||||
}
|
||||
else
|
||||
HasExceededPublishLimit = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
HasExceededPublishLimit = true;
|
||||
}
|
||||
|
||||
if(Application.isPlaying)
|
||||
{
|
||||
SetupUI(hasSufficientTrustLevelToPublishToCommunityLabs, HasExceededPublishLimit);
|
||||
}
|
||||
},
|
||||
(c) =>
|
||||
{
|
||||
if(Application.isPlaying)
|
||||
{
|
||||
SetupUI(hasSufficientTrustLevelToPublishToCommunityLabs, HasExceededPublishLimit);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SetupUI(bool hasEnoughTrustToPublishToCL = false, bool hasExceededWeeklyPublishLimit = false)
|
||||
{
|
||||
#if COMMUNITY_LABS_SDK
|
||||
// do not display community labs panel if updating an existing CL world or updating a public world
|
||||
publishToCommunityLabsPanel.gameObject.SetActive(!IsCurrentWorldUploaded);
|
||||
#endif
|
||||
|
||||
if (!ValidateAssetBundleBlueprintID(worldRecord.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;
|
||||
}
|
||||
|
||||
contentFeatured.gameObject.SetActive(APIUser.CurrentUser.hasSuperPowers);
|
||||
contentSDKExample.gameObject.SetActive(APIUser.CurrentUser.hasSuperPowers);
|
||||
|
||||
if (APIUser.Exists(pipelineManager.user))
|
||||
{
|
||||
waitingPanel.SetActive(false);
|
||||
blueprintPanel.SetActive(true);
|
||||
errorPanel.SetActive(false);
|
||||
|
||||
if (string.IsNullOrEmpty(worldRecord.authorId) || worldRecord.authorId == pipelineManager.user.id)
|
||||
{
|
||||
titleText.text = "Configure World";
|
||||
blueprintName.text = worldRecord.name;
|
||||
worldCapacity.text = worldRecord.capacity.ToString();
|
||||
contentSex.isOn = worldRecord.tags.Contains("content_sex");
|
||||
contentViolence.isOn = worldRecord.tags.Contains("content_violence");
|
||||
contentGore.isOn = worldRecord.tags.Contains("content_gore");
|
||||
contentOther.isOn = worldRecord.tags.Contains("content_other");
|
||||
shouldUpdateImageToggle.interactable = isUpdate;
|
||||
shouldUpdateImageToggle.isOn = !isUpdate;
|
||||
liveBpImage.enabled = !isUpdate;
|
||||
bpImage.enabled = isUpdate;
|
||||
|
||||
if (!APIUser.CurrentUser.hasSuperPowers)
|
||||
{
|
||||
releasePublic.gameObject.SetActive(false);
|
||||
releasePublic.isOn = false;
|
||||
releasePublic.interactable = false;
|
||||
|
||||
contentFeatured.isOn = contentSDKExample.isOn = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
contentFeatured.isOn = worldRecord.tags.Contains("content_featured");
|
||||
contentSDKExample.isOn = worldRecord.tags.Contains("content_sdk_example");
|
||||
|
||||
releasePublic.isOn = worldRecord.releaseStatus == "public";
|
||||
releasePublic.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
// "show in worlds menu"
|
||||
if (APIUser.CurrentUser.hasSuperPowers)
|
||||
{
|
||||
showInWorldsMenuGroup.gameObject.SetActive(true);
|
||||
showInActiveWorlds.isOn = !worldRecord.tags.Contains("admin_hide_active");
|
||||
showInPopularWorlds.isOn = !worldRecord.tags.Contains("admin_hide_popular");
|
||||
showInNewWorlds.isOn = !worldRecord.tags.Contains("admin_hide_new");
|
||||
}
|
||||
else
|
||||
{
|
||||
showInWorldsMenuGroup.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
blueprintDescription.text = worldRecord.description;
|
||||
|
||||
userTags.text = "";
|
||||
foreach (var tag in worldRecord.publicTags)
|
||||
{
|
||||
userTags.text = userTags.text + tag.Replace("author_tag_", "");
|
||||
userTags.text = userTags.text + " ";
|
||||
}
|
||||
|
||||
ImageDownloader.DownloadImage(worldRecord.imageUrl, 0, obj => bpImage.texture = obj, null);
|
||||
}
|
||||
else // user does not own world id associated with descriptor
|
||||
{
|
||||
Debug.LogErrorFormat("{0} is not an owner of {1}", worldRecord.authorId, pipelineManager.user.id);
|
||||
blueprintPanel.SetActive(false);
|
||||
errorPanel.SetActive(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
waitingPanel.SetActive(true);
|
||||
blueprintPanel.SetActive(false);
|
||||
errorPanel.SetActive(false);
|
||||
|
||||
if (!APIUser.CurrentUser.hasSuperPowers)
|
||||
{
|
||||
releasePublic.gameObject.SetActive(false);
|
||||
releasePublic.isOn = false;
|
||||
releasePublic.interactable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
releasePublic.gameObject.SetActive(true);
|
||||
releasePublic.isOn = false;
|
||||
}
|
||||
}
|
||||
|
||||
// set up publish to Community Labs checkbox and text
|
||||
int worldsPublishedThisWeek = hasExceededWeeklyPublishLimit ? 1 : 0;
|
||||
int maximumWorldsAllowedToPublishPerWeek = 1;
|
||||
publishToCommLabsToggle = publishToCommunityLabsPanel.GetComponentInChildren<Toggle>();
|
||||
|
||||
if (null != publishToCommLabsToggle)
|
||||
{
|
||||
// disable publishing to CL checkbox if not enough trust or exceeded publish limit
|
||||
publishToCommLabsToggle.interactable = hasEnoughTrustToPublishToCL && !hasExceededWeeklyPublishLimit;
|
||||
|
||||
Text publishText = publishToCommLabsToggle.gameObject.GetComponentInChildren<Text>();
|
||||
if (null != publishText)
|
||||
{
|
||||
if (!hasEnoughTrustToPublishToCL)
|
||||
{
|
||||
publishText.text = "Not enough Trust to Publish to Community Labs";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasExceededWeeklyPublishLimit)
|
||||
{
|
||||
publishText.text = "Publish limit for Community Labs Exceeded\n" + "(" + worldsPublishedThisWeek + "/" + maximumWorldsAllowedToPublishPerWeek + " Published this week)";
|
||||
}
|
||||
else
|
||||
{
|
||||
publishText.text = "Publish to Community Labs\n" + "(" + worldsPublishedThisWeek + "/" + maximumWorldsAllowedToPublishPerWeek + " Published this week)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupUpload()
|
||||
{
|
||||
if (!ParseUserTags())
|
||||
return;
|
||||
|
||||
publishingToCommunityLabs = (publishToCommLabsToggle != null) && (publishToCommLabsToggle.isActiveAndEnabled) && (publishToCommLabsToggle.isOn);
|
||||
|
||||
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.SetInt("VRCSDK2_capacity", System.Convert.ToInt16(worldCapacity.text));
|
||||
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);
|
||||
UnityEditor.EditorPrefs.SetBool("VRCSDK2_release_public", releasePublic.isOn);
|
||||
UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_featured", contentFeatured.isOn);
|
||||
UnityEditor.EditorPrefs.SetBool("VRCSDK2_content_sdk_example", contentSDKExample.isOn);
|
||||
|
||||
if (string.IsNullOrEmpty(worldRecord.id))
|
||||
{
|
||||
pipelineManager.AssignId(PipelineManager.ContentType.world);
|
||||
worldRecord.id = pipelineManager.blueprintId;
|
||||
}
|
||||
|
||||
string blueprintId = worldRecord.id;
|
||||
int version = Mathf.Max(1, worldRecord.version + 1);
|
||||
PrepareVRCPathForS3(abPath, blueprintId, version, ApiWorld.VERSION);
|
||||
|
||||
if (!string.IsNullOrEmpty(unityPackagePath) && System.IO.File.Exists(unityPackagePath))
|
||||
{
|
||||
VRC.Core.Logger.Log("Found unity package path. Preparing to upload!", API.LOG_CATEGORY);
|
||||
PrepareUnityPackageForS3(unityPackagePath, blueprintId, version, ApiWorld.VERSION);
|
||||
}
|
||||
|
||||
StartCoroutine(UploadNew());
|
||||
}
|
||||
|
||||
void OnUploadedWorld()
|
||||
{
|
||||
const string devUrl = "https://dev-api.vrchat.cloud";
|
||||
const string releaseUrl = "https://vrchat.com";
|
||||
|
||||
string uploadedWorldURL = (API.IsDevApi() ? devUrl : releaseUrl) + "/home/world/" + pipelineManager.blueprintId;
|
||||
OnSDKPipelineComplete(uploadedWorldURL);
|
||||
}
|
||||
|
||||
IEnumerator UploadNew()
|
||||
{
|
||||
bool caughtInvalidInput = false;
|
||||
if (!ValidateNameInput(blueprintName))
|
||||
caughtInvalidInput = true;
|
||||
|
||||
if (caughtInvalidInput)
|
||||
yield break;
|
||||
|
||||
VRC.Core.Logger.Log("Starting upload");
|
||||
|
||||
// upload unity package
|
||||
if (!string.IsNullOrEmpty(uploadUnityPackagePath))
|
||||
{
|
||||
yield return StartCoroutine(UploadFile(uploadUnityPackagePath, isUpdate ? worldRecord.unityPackageUrl : "", GetFriendlyWorldFileName("Unity package"), "Unity package",
|
||||
delegate (string fileUrl)
|
||||
{
|
||||
cloudFrontUnityPackageUrl = fileUrl;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// upload asset bundle
|
||||
if (!string.IsNullOrEmpty(uploadVrcPath))
|
||||
{
|
||||
yield return StartCoroutine(UploadFile(uploadVrcPath, isUpdate ? worldRecord.assetUrl : "", GetFriendlyWorldFileName("Asset bundle"), "Asset bundle",
|
||||
delegate (string fileUrl)
|
||||
{
|
||||
cloudFrontAssetUrl = fileUrl;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
if (isUpdate)
|
||||
yield return StartCoroutine(UpdateBlueprint());
|
||||
else
|
||||
yield return StartCoroutine(CreateBlueprint());
|
||||
|
||||
if (publishingToCommunityLabs)
|
||||
{
|
||||
ApiWorld.PublishWorldToCommunityLabs(pipelineManager.blueprintId,
|
||||
(world) => OnUploadedWorld(),
|
||||
(err) =>
|
||||
{
|
||||
Debug.LogError("PublishWorldToCommunityLabs error:" + err);
|
||||
OnUploadedWorld();
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnUploadedWorld();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFriendlyWorldFileName(string type)
|
||||
{
|
||||
return "World - " + 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 (contentFeatured.isOn)
|
||||
tags.Add("content_featured");
|
||||
if (contentSDKExample.isOn)
|
||||
tags.Add("content_sdk_example");
|
||||
if(releasePublic.isOn)
|
||||
tags.Add("admin_approved");
|
||||
}
|
||||
|
||||
// "show in worlds menu"
|
||||
if (APIUser.CurrentUser.hasSuperPowers)
|
||||
{
|
||||
if (!showInActiveWorlds.isOn)
|
||||
tags.Add("admin_hide_active");
|
||||
if (!showInPopularWorlds.isOn)
|
||||
tags.Add("admin_hide_popular");
|
||||
if (!showInNewWorlds.isOn)
|
||||
tags.Add("admin_hide_new");
|
||||
}
|
||||
|
||||
// add any author tags
|
||||
foreach (var word in customTags)
|
||||
{
|
||||
// add all custom tags with "author_tag_" prefix
|
||||
tags.Add("author_tag_" + word);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
bool ParseUserTags()
|
||||
{
|
||||
bool validTags = true;
|
||||
customTags = new List<string>();
|
||||
char[] delimiterChars = { ' ', ',', '.', ':', '\t', '\n', '"', '#' };
|
||||
|
||||
// split user tags into individual words
|
||||
string[] words = userTags.text.Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var word in words)
|
||||
{
|
||||
customTags.Add(word.ToLower());
|
||||
}
|
||||
|
||||
// check that number of tags is within tag limit
|
||||
if (words.Count() > MAX_USER_TAGS_FOR_WORLD)
|
||||
{
|
||||
validTags = false;
|
||||
UnityEditor.EditorUtility.DisplayDialog("Tags are limited to a maximum of " + MAX_USER_TAGS_FOR_WORLD + " per world.", "Please remove excess tags before uploading!", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
// check that no tags exceed maximum tag length
|
||||
int maximumTagLength = 0;
|
||||
foreach (string item in words)
|
||||
{
|
||||
if (item.Length > maximumTagLength)
|
||||
{
|
||||
maximumTagLength = item.Length;
|
||||
}
|
||||
}
|
||||
|
||||
if (maximumTagLength > MAX_CHARACTERS_ALLOWED_IN_USER_TAG)
|
||||
{
|
||||
validTags = false;
|
||||
UnityEditor.EditorUtility.DisplayDialog("Tags are limited to a maximum of " + MAX_CHARACTERS_ALLOWED_IN_USER_TAG + " characters per tag.", "One or more of your tags exceeds the maximum " + MAX_CHARACTERS_ALLOWED_IN_USER_TAG + " character limit.\n\n" + "Please shorten tags before uploading!", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
// make sure tags are all alphanumeric
|
||||
foreach (var word in words)
|
||||
{
|
||||
if (!word.All(char.IsLetterOrDigit))
|
||||
{
|
||||
validTags = false;
|
||||
UnityEditor.EditorUtility.DisplayDialog("Tags should consist of alphanumeric characters only.", "Please remove any non-alphanumeric characters from tags before uploading!", "OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validTags;
|
||||
}
|
||||
|
||||
protected override IEnumerator CreateBlueprint()
|
||||
{
|
||||
yield return StartCoroutine(UpdateImage(isUpdate ? worldRecord.imageUrl : "", GetFriendlyWorldFileName("Image")));
|
||||
|
||||
SetUploadProgress("Saving Blueprint to user", "Almost finished!!", 0.0f);
|
||||
ApiWorld world = new ApiWorld
|
||||
{
|
||||
id = worldRecord.id,
|
||||
authorName = pipelineManager.user.displayName,
|
||||
authorId = pipelineManager.user.id,
|
||||
name = blueprintName.text,
|
||||
imageUrl = cloudFrontImageUrl,
|
||||
assetUrl = cloudFrontAssetUrl,
|
||||
unityPackageUrl = cloudFrontUnityPackageUrl,
|
||||
description = blueprintDescription.text,
|
||||
tags = BuildTags(),
|
||||
releaseStatus = (releasePublic.isOn) ? ("public") : ("private"),
|
||||
capacity = System.Convert.ToInt16(worldCapacity.text),
|
||||
occupants = 0,
|
||||
shouldAddToAuthor = true,
|
||||
udonProducts = descriptor.udonProducts
|
||||
};
|
||||
|
||||
if (APIUser.CurrentUser.hasSuperPowers)
|
||||
world.isCurated = contentFeatured.isOn || contentSDKExample.isOn;
|
||||
else
|
||||
world.isCurated = false;
|
||||
|
||||
bool doneUploading = false;
|
||||
world.Post(
|
||||
(c) =>
|
||||
{
|
||||
ApiWorld savedBP = (ApiWorld)c.Model;
|
||||
pipelineManager.blueprintId = savedBP.id;
|
||||
UnityEditor.EditorPrefs.SetString("blueprintID-" + pipelineManager.GetInstanceID().ToString(), savedBP.id);
|
||||
VRC.Core.Logger.Log("Setting blueprintID on pipeline manager and editor prefs", API.LOG_CATEGORY);
|
||||
doneUploading = true;
|
||||
},
|
||||
(c) => { doneUploading = true; Debug.LogError(c.Error); });
|
||||
|
||||
while (!doneUploading)
|
||||
yield return null;
|
||||
}
|
||||
|
||||
protected override IEnumerator UpdateBlueprint()
|
||||
{
|
||||
bool doneUploading = false;
|
||||
|
||||
worldRecord.name = blueprintName.text;
|
||||
worldRecord.description = blueprintDescription.text;
|
||||
worldRecord.capacity = System.Convert.ToInt16(worldCapacity.text);
|
||||
worldRecord.assetUrl = cloudFrontAssetUrl;
|
||||
worldRecord.tags = BuildTags();
|
||||
worldRecord.releaseStatus = (releasePublic.isOn) ? ("public") : ("private");
|
||||
worldRecord.unityPackageUrl = cloudFrontUnityPackageUrl;
|
||||
worldRecord.isCurated = contentFeatured.isOn || contentSDKExample.isOn;
|
||||
worldRecord.udonProducts = descriptor.udonProducts;
|
||||
|
||||
if (shouldUpdateImageToggle.isOn)
|
||||
{
|
||||
yield return StartCoroutine(UpdateImage(isUpdate ? worldRecord.imageUrl : "", GetFriendlyWorldFileName("Image")));
|
||||
|
||||
worldRecord.imageUrl = cloudFrontImageUrl;
|
||||
}
|
||||
|
||||
SetUploadProgress("Saving Blueprint", "Almost finished!!", 0.0f);
|
||||
worldRecord.Save((c) => doneUploading = true, (c) => { doneUploading = true; Debug.LogError(c.Error); });
|
||||
|
||||
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(worldRecord.imageUrl, 0, obj => bpImage.texture = obj, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisplayUpdateCompletedDialog(string contentUrl = null)
|
||||
{
|
||||
}
|
||||
|
||||
private void OpenCommunityLabsDocumentation()
|
||||
{
|
||||
Application.OpenURL(CommunityLabsConstants.COMMUNITY_LABS_DOCUMENTATION_URL);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
UnityEditor.EditorUtility.ClearProgressBar();
|
||||
UnityEditor.EditorPrefs.DeleteKey("currentBuildingAssetBundlePath");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bd5ee5d69ee0f3449bf2f81fcb7f4e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,18 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
public class SceneSaver
|
||||
{
|
||||
static public void SaveScene()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var activeScene = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene();
|
||||
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(activeScene);
|
||||
|
||||
UnityEditor.EditorApplication.isPaused = false;
|
||||
UnityEditor.EditorApplication.isPlaying = false;
|
||||
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(activeScene.name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d49300ad532d4ae6b569b28de5b7dac
|
||||
timeCreated: 1446917779
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48829de75cd107e4a94337ca7cc3dd3c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,132 @@
|
||||
|
||||
namespace VRC.SDKBase.Validation
|
||||
{
|
||||
public static partial class AvatarValidation
|
||||
{
|
||||
public static readonly string[] ComponentTypeWhiteListCommon = new string[]
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
#if VRC_CLIENT
|
||||
"DynamicBone", // Deprecated, whitelisted in the client only for backwards compatibility
|
||||
"DynamicBoneCollider", // Deprecated, whitelisted in the client only for backwards compatibility
|
||||
#endif // VRC_CLIENT
|
||||
"RootMotion.FinalIK.IKExecutionOrder",
|
||||
"RootMotion.FinalIK.VRIK",
|
||||
"RootMotion.FinalIK.FullBodyBipedIK",
|
||||
"RootMotion.FinalIK.LimbIK",
|
||||
"RootMotion.FinalIK.AimIK",
|
||||
"RootMotion.FinalIK.BipedIK",
|
||||
"RootMotion.FinalIK.GrounderIK",
|
||||
"RootMotion.FinalIK.GrounderFBBIK",
|
||||
"RootMotion.FinalIK.GrounderVRIK",
|
||||
"RootMotion.FinalIK.GrounderQuadruped",
|
||||
"RootMotion.FinalIK.TwistRelaxer",
|
||||
"RootMotion.FinalIK.ShoulderRotator",
|
||||
"RootMotion.FinalIK.FBBIKArmBending",
|
||||
"RootMotion.FinalIK.FBBIKHeadEffector",
|
||||
"RootMotion.FinalIK.FABRIK",
|
||||
"RootMotion.FinalIK.FABRIKChain",
|
||||
"RootMotion.FinalIK.FABRIKRoot",
|
||||
"RootMotion.FinalIK.CCDIK",
|
||||
"RootMotion.FinalIK.RotationLimit",
|
||||
"RootMotion.FinalIK.RotationLimitHinge",
|
||||
"RootMotion.FinalIK.RotationLimitPolygonal",
|
||||
"RootMotion.FinalIK.RotationLimitSpline",
|
||||
"UnityEngine.Cloth",
|
||||
"UnityEngine.Light",
|
||||
"UnityEngine.BoxCollider",
|
||||
"UnityEngine.SphereCollider",
|
||||
"UnityEngine.CapsuleCollider",
|
||||
"UnityEngine.Rigidbody",
|
||||
"UnityEngine.Joint",
|
||||
"UnityEngine.Animations.AimConstraint",
|
||||
"UnityEngine.Animations.LookAtConstraint",
|
||||
"UnityEngine.Animations.ParentConstraint",
|
||||
"UnityEngine.Animations.PositionConstraint",
|
||||
"UnityEngine.Animations.RotationConstraint",
|
||||
"UnityEngine.Animations.ScaleConstraint",
|
||||
"UnityEngine.Camera",
|
||||
"UnityEngine.AudioSource",
|
||||
"ONSPAudioSource",
|
||||
#endif // UNITY_STANDALONE
|
||||
#if !VRC_CLIENT
|
||||
"VRC.Core.PipelineSaver",
|
||||
#endif
|
||||
"VRC.Core.PipelineManager",
|
||||
"UnityEngine.Transform",
|
||||
"UnityEngine.Animator",
|
||||
"UnityEngine.SkinnedMeshRenderer",
|
||||
"LimbIK", // our limbik based on Unity ik
|
||||
"LoadingAvatarTextureAnimation",
|
||||
"UnityEngine.MeshFilter",
|
||||
"UnityEngine.MeshRenderer",
|
||||
"UnityEngine.Animation",
|
||||
"UnityEngine.ParticleSystem",
|
||||
"UnityEngine.ParticleSystemRenderer",
|
||||
"UnityEngine.TrailRenderer",
|
||||
"UnityEngine.FlareLayer",
|
||||
"UnityEngine.GUILayer",
|
||||
"UnityEngine.LineRenderer",
|
||||
"RealisticEyeMovements.EyeAndHeadAnimator",
|
||||
"RealisticEyeMovements.LookTargetController",
|
||||
};
|
||||
|
||||
public static readonly string[] ComponentTypeWhiteListSdk2 = new string[]
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
"VRCSDK2.VRC_SpatialAudioSource",
|
||||
#endif
|
||||
"VRCSDK2.VRC_AvatarDescriptor",
|
||||
"VRCSDK2.VRC_AvatarVariations",
|
||||
"VRCSDK2.VRC_IKFollower",
|
||||
"VRCSDK2.VRC_Station",
|
||||
};
|
||||
|
||||
public static readonly string[] ComponentTypeWhiteListSdk3 = new string[]
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
"VRC.SDK3.Avatars.Components.VRCSpatialAudioSource",
|
||||
#endif
|
||||
"VRC.SDK3.VRCTestMarker",
|
||||
"VRC.SDK3.Avatars.Components.VRCAvatarDescriptor",
|
||||
"VRC.SDK3.Avatars.Components.VRCStation",
|
||||
"VRC.SDK3.Avatars.Components.VRCImpostorSettings",
|
||||
"VRC.SDK3.Avatars.Components.VRCImpostorEnvironment",
|
||||
"VRC.SDK3.Avatars.Components.VRCHeadChop",
|
||||
"VRC.SDK3.Avatars.Components.VRCRaycast",
|
||||
"VRC.SDK3.Dynamics.PhysBone.Components.VRCPhysBone",
|
||||
"VRC.SDK3.Dynamics.PhysBone.Components.VRCPhysBoneCollider",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCAimConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCLookAtConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCParentConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCPositionConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCRotationConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCScaleConstraint",
|
||||
"VRC.SDK3.Dynamics.Contact.Components.VRCContactSender",
|
||||
"VRC.SDK3.Dynamics.Contact.Components.VRCContactReceiver",
|
||||
};
|
||||
|
||||
public static readonly string[] ShaderWhiteList = new string[]
|
||||
{
|
||||
"VRChat/Mobile/Standard Lite",
|
||||
"VRChat/Mobile/Diffuse",
|
||||
"VRChat/Mobile/Bumped Diffuse",
|
||||
"VRChat/Mobile/Bumped Mapped Specular",
|
||||
"VRChat/Mobile/Toon Lit",
|
||||
"VRChat/Mobile/MatCap Lit",
|
||||
|
||||
"VRChat/Mobile/Particles/Additive",
|
||||
"VRChat/Mobile/Particles/Multiply",
|
||||
|
||||
"VRChat/Mobile/Toon Standard",
|
||||
"VRChat/Mobile/Toon Standard (Outline)", // not in client whitelist, will fall back to non-outline variant on mobile
|
||||
};
|
||||
|
||||
public const int MAX_RAYCAST_COMPONENTS_PER_AVATAR = 80; // VRCRaycast + FinalIK
|
||||
|
||||
public const int MAX_AVD_PHYSBONES_PER_AVATAR = 256;
|
||||
public const int MAX_AVD_COLLIDERS_PER_AVATAR = 256;
|
||||
public const int MAX_AVD_CONTACTS_PER_AVATAR = 256;
|
||||
public const int MAX_AVD_CONSTRAINTS_PER_AVATAR = 2000;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65a0b1106808685488e7287333ef73e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,805 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
#if TextMeshPro
|
||||
using TMPro;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.Timeline;
|
||||
using UnityEngine.UI;
|
||||
|
||||
#if VRC_CLIENT
|
||||
using ZLinq;
|
||||
#else
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace VRC.SDKBase.Validation
|
||||
{
|
||||
public static class WorldValidation
|
||||
{
|
||||
private static readonly Lazy<string> _debugCategoryName = new Lazy<string>(InitializeLogging);
|
||||
private static string DebugCategoryName => _debugCategoryName.Value;
|
||||
|
||||
private static string InitializeLogging()
|
||||
{
|
||||
const string categoryName = "WorldValidation";
|
||||
VRC.Core.Logger.DescribeCategory(categoryName, VRC.Core.Logger.Color.red);
|
||||
VRC.Core.Logger.EnableCategory(categoryName);
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
static string[] ComponentTypeWhiteList = null;
|
||||
|
||||
public enum WhiteListConfiguration
|
||||
{
|
||||
None,
|
||||
VRCSDK2,
|
||||
VRCSDK3,
|
||||
Unchanged
|
||||
}
|
||||
|
||||
static WhiteListConfiguration ComponentTypeWhiteListConfiguration = WhiteListConfiguration.None;
|
||||
|
||||
static readonly string[] ComponentTypeWhiteListCommon = new string[]
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
"UnityEngine.Rendering.PostProcessing.PostProcessDebug",
|
||||
"UnityEngine.Rendering.PostProcessing.PostProcessLayer",
|
||||
"UnityEngine.Rendering.PostProcessing.PostProcessVolume",
|
||||
#endif
|
||||
"VRC.Core.PipelineManager",
|
||||
"UiInputField",
|
||||
"VRCProjectSettings",
|
||||
"DynamicBone",
|
||||
"DynamicBoneCollider",
|
||||
"TMPro.TMP_Dropdown",
|
||||
"TMPro.TMP_InputField",
|
||||
"TMPro.TMP_ScrollbarEventHandler",
|
||||
"TMPro.TMP_SelectionCaret",
|
||||
"TMPro.TMP_SpriteAnimator",
|
||||
"TMPro.TMP_SubMesh",
|
||||
"TMPro.TMP_SubMeshUI",
|
||||
"TMPro.TMP_Text",
|
||||
"TMPro.TextMeshPro",
|
||||
"TMPro.TextMeshProUGUI",
|
||||
"TMPro.TextContainer",
|
||||
"TMPro.TMP_Dropdown+DropdownItem",
|
||||
"UnityEngine.EventSystems.EventSystem",
|
||||
"UnityEngine.EventSystems.EventTrigger",
|
||||
"UnityEngine.EventSystems.UIBehaviour",
|
||||
"UnityEngine.EventSystems.BaseInput",
|
||||
"UnityEngine.EventSystems.BaseInputModule",
|
||||
"UnityEngine.EventSystems.PointerInputModule",
|
||||
"UnityEngine.EventSystems.StandaloneInputModule",
|
||||
"UnityEngine.EventSystems.TouchInputModule",
|
||||
"UnityEngine.EventSystems.BaseRaycaster",
|
||||
"UnityEngine.EventSystems.PhysicsRaycaster",
|
||||
"UnityEngine.UI.Button",
|
||||
"UnityEngine.UI.Dropdown",
|
||||
"UnityEngine.UI.Dropdown+DropdownItem",
|
||||
"UnityEngine.UI.Graphic",
|
||||
"UnityEngine.UI.GraphicRaycaster",
|
||||
"UnityEngine.UI.Image",
|
||||
"UnityEngine.UI.InputField",
|
||||
"UnityEngine.UI.Mask",
|
||||
"UnityEngine.UI.MaskableGraphic",
|
||||
"UnityEngine.UI.RawImage",
|
||||
"UnityEngine.UI.RectMask2D",
|
||||
"UnityEngine.UI.Scrollbar",
|
||||
"UnityEngine.UI.ScrollRect",
|
||||
"UnityEngine.UI.Selectable",
|
||||
"UnityEngine.UI.Slider",
|
||||
"UnityEngine.UI.Text",
|
||||
"UnityEngine.UI.Toggle",
|
||||
"UnityEngine.UI.ToggleGroup",
|
||||
"UnityEngine.UI.AspectRatioFitter",
|
||||
"UnityEngine.UI.CanvasScaler",
|
||||
"UnityEngine.UI.ContentSizeFitter",
|
||||
"UnityEngine.UI.GridLayoutGroup",
|
||||
"UnityEngine.UI.HorizontalLayoutGroup",
|
||||
"UnityEngine.UI.HorizontalOrVerticalLayoutGroup",
|
||||
"UnityEngine.UI.LayoutElement",
|
||||
"UnityEngine.UI.LayoutGroup",
|
||||
"UnityEngine.UI.VerticalLayoutGroup",
|
||||
"UnityEngine.UI.BaseMeshEffect",
|
||||
"UnityEngine.UI.Outline",
|
||||
"UnityEngine.UI.PositionAsUV1",
|
||||
"UnityEngine.UI.Shadow",
|
||||
"OVRLipSync",
|
||||
"OVRLipSyncContext",
|
||||
"OVRLipSyncContextBase",
|
||||
"OVRLipSyncContextCanned",
|
||||
"OVRLipSyncContextMorphTarget",
|
||||
"OVRLipSyncContextTextureFlip",
|
||||
"ONSPReflectionZone",
|
||||
"OculusSpatializerUnity",
|
||||
"ONSPAmbisonicsNative",
|
||||
"ONSPAudioSource",
|
||||
"RootMotion.FinalIK.BipedIK",
|
||||
"RootMotion.FinalIK.FingerRig",
|
||||
"RootMotion.FinalIK.Grounder",
|
||||
"RootMotion.FinalIK.GrounderBipedIK",
|
||||
"RootMotion.FinalIK.GrounderFBBIK",
|
||||
"RootMotion.FinalIK.GrounderIK",
|
||||
"RootMotion.FinalIK.GrounderQuadruped",
|
||||
"RootMotion.FinalIK.GrounderVRIK",
|
||||
"RootMotion.FinalIK.AimIK",
|
||||
"RootMotion.FinalIK.CCDIK",
|
||||
"RootMotion.FinalIK.FABRIK",
|
||||
"RootMotion.FinalIK.FABRIKRoot",
|
||||
"RootMotion.FinalIK.FullBodyBipedIK",
|
||||
"RootMotion.FinalIK.IK",
|
||||
"RootMotion.FinalIK.IKExecutionOrder",
|
||||
"RootMotion.FinalIK.LegIK",
|
||||
"RootMotion.FinalIK.LimbIK",
|
||||
"RootMotion.FinalIK.LookAtIK",
|
||||
"RootMotion.FinalIK.TrigonometricIK",
|
||||
"RootMotion.FinalIK.VRIK",
|
||||
"RootMotion.FinalIK.FBBIKArmBending",
|
||||
"RootMotion.FinalIK.FBBIKHeadEffector",
|
||||
"RootMotion.FinalIK.TwistRelaxer",
|
||||
"RootMotion.FinalIK.InteractionObject",
|
||||
"RootMotion.FinalIK.InteractionSystem",
|
||||
"RootMotion.FinalIK.InteractionTarget",
|
||||
"RootMotion.FinalIK.InteractionTrigger",
|
||||
"RootMotion.FinalIK.GenericPoser",
|
||||
"RootMotion.FinalIK.HandPoser",
|
||||
"RootMotion.FinalIK.Poser",
|
||||
"RootMotion.FinalIK.RagdollUtility",
|
||||
"RootMotion.FinalIK.RotationLimit",
|
||||
"RootMotion.FinalIK.RotationLimitAngle",
|
||||
"RootMotion.FinalIK.RotationLimitHinge",
|
||||
"RootMotion.FinalIK.RotationLimitPolygonal",
|
||||
"RootMotion.FinalIK.RotationLimitSpline",
|
||||
"RootMotion.FinalIK.AimPoser",
|
||||
"RootMotion.FinalIK.Amplifier",
|
||||
"RootMotion.FinalIK.BodyTilt",
|
||||
"RootMotion.FinalIK.HitReaction",
|
||||
"RootMotion.FinalIK.HitReactionVRIK",
|
||||
"RootMotion.FinalIK.Inertia",
|
||||
"RootMotion.FinalIK.OffsetModifier",
|
||||
"RootMotion.FinalIK.OffsetModifierVRIK",
|
||||
"RootMotion.FinalIK.OffsetPose",
|
||||
"RootMotion.FinalIK.Recoil",
|
||||
"RootMotion.FinalIK.ShoulderRotator",
|
||||
"RootMotion.Dynamics.AnimationBlocker",
|
||||
"RootMotion.Dynamics.BehaviourBase",
|
||||
"RootMotion.Dynamics.BehaviourFall",
|
||||
"RootMotion.Dynamics.BehaviourPuppet",
|
||||
"RootMotion.Dynamics.JointBreakBroadcaster",
|
||||
"RootMotion.Dynamics.MuscleCollisionBroadcaster",
|
||||
"RootMotion.Dynamics.PressureSensor",
|
||||
"RootMotion.Dynamics.Prop",
|
||||
"RootMotion.Dynamics.PropRoot",
|
||||
"RootMotion.Dynamics.PuppetMaster",
|
||||
"RootMotion.Dynamics.PuppetMasterSettings",
|
||||
// TODO: remove these if they are only needed in editor
|
||||
"RootMotion.Dynamics.BipedRagdollCreator",
|
||||
"RootMotion.Dynamics.RagdollCreator",
|
||||
"RootMotion.Dynamics.RagdollEditor",
|
||||
//
|
||||
"RootMotion.SolverManager",
|
||||
"RootMotion.TriggerEventBroadcaster",
|
||||
"UnityEngine.WindZone",
|
||||
"UnityEngine.Tilemaps.Tilemap",
|
||||
"UnityEngine.Tilemaps.TilemapRenderer",
|
||||
"UnityEngine.Terrain",
|
||||
"UnityEngine.Tree",
|
||||
"UnityEngine.SpriteMask",
|
||||
"UnityEngine.Grid",
|
||||
"UnityEngine.GridLayout",
|
||||
"UnityEngine.AudioSource",
|
||||
"UnityEngine.AudioReverbZone",
|
||||
"UnityEngine.AudioLowPassFilter",
|
||||
"UnityEngine.AudioHighPassFilter",
|
||||
"UnityEngine.AudioDistortionFilter",
|
||||
"UnityEngine.AudioEchoFilter",
|
||||
"UnityEngine.AudioChorusFilter",
|
||||
"UnityEngine.AudioReverbFilter",
|
||||
"UnityEngine.AudioListener", // will be force-disabled on load, but can't be removed since it's a dependency for some of the above
|
||||
"UnityEngine.Playables.PlayableDirector",
|
||||
"UnityEngine.TerrainCollider",
|
||||
"UnityEngine.Canvas",
|
||||
"UnityEngine.CanvasGroup",
|
||||
"UnityEngine.CanvasRenderer",
|
||||
"UnityEngine.TextMesh",
|
||||
"UnityEngine.Animator",
|
||||
"UnityEngine.AI.NavMeshAgent",
|
||||
"UnityEngine.AI.NavMeshObstacle",
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
"Unity.AI.Navigation.NavMeshLink",
|
||||
#else
|
||||
"UnityEngine.AI.OffMeshLink",
|
||||
#endif
|
||||
"UnityEngine.Cloth",
|
||||
"UnityEngine.WheelCollider",
|
||||
"UnityEngine.Rigidbody",
|
||||
"UnityEngine.Joint",
|
||||
"UnityEngine.HingeJoint",
|
||||
"UnityEngine.SpringJoint",
|
||||
"UnityEngine.FixedJoint",
|
||||
"UnityEngine.CharacterJoint",
|
||||
"UnityEngine.ConfigurableJoint",
|
||||
"UnityEngine.ConstantForce",
|
||||
"UnityEngine.Collider",
|
||||
"UnityEngine.BoxCollider",
|
||||
"UnityEngine.SphereCollider",
|
||||
"UnityEngine.MeshCollider",
|
||||
"UnityEngine.CapsuleCollider",
|
||||
"UnityEngine.CharacterController",
|
||||
"UnityEngine.ParticleSystem",
|
||||
"UnityEngine.ParticleSystemRenderer",
|
||||
"UnityEngine.BillboardRenderer",
|
||||
"UnityEngine.Camera",
|
||||
"UnityEngine.FlareLayer",
|
||||
"UnityEngine.SkinnedMeshRenderer",
|
||||
"UnityEngine.Renderer",
|
||||
"UnityEngine.TrailRenderer",
|
||||
"UnityEngine.LineRenderer",
|
||||
"UnityEngine.GUIElement",
|
||||
"UnityEngine.GUILayer",
|
||||
"UnityEngine.Light",
|
||||
"UnityEngine.LightProbeGroup",
|
||||
"UnityEngine.LightProbeProxyVolume",
|
||||
"UnityEngine.LODGroup",
|
||||
"UnityEngine.ReflectionProbe",
|
||||
"UnityEngine.SpriteRenderer",
|
||||
"UnityEngine.Transform",
|
||||
"UnityEngine.RectTransform",
|
||||
"UnityEngine.Rendering.SortingGroup",
|
||||
"UnityEngine.Projector",
|
||||
"UnityEngine.OcclusionPortal",
|
||||
"UnityEngine.OcclusionArea",
|
||||
"UnityEngine.LensFlare",
|
||||
"UnityEngine.Skybox",
|
||||
"UnityEngine.MeshFilter",
|
||||
"UnityEngine.Halo",
|
||||
"UnityEngine.MeshRenderer",
|
||||
"UnityEngine.Collider2D",
|
||||
"UnityEngine.Rigidbody2D",
|
||||
"UnityEngine.CompositeCollider2D",
|
||||
"UnityEngine.ConstantForce2D",
|
||||
"UnityEngine.AreaEffector2D",
|
||||
"UnityEngine.CapsuleCollider2D",
|
||||
"UnityEngine.DistanceJoint2D",
|
||||
"UnityEngine.EdgeCollider2D",
|
||||
"UnityEngine.Effector2D",
|
||||
"UnityEngine.BoxCollider2D",
|
||||
"UnityEngine.CircleCollider2D",
|
||||
"UnityEngine.FixedJoint2D",
|
||||
"UnityEngine.HingeJoint2D",
|
||||
"UnityEngine.FrictionJoint2D",
|
||||
"UnityEngine.PlatformEffector2D",
|
||||
"UnityEngine.PointEffector2D",
|
||||
"UnityEngine.PolygonCollider2D",
|
||||
"UnityEngine.SliderJoint2D",
|
||||
"UnityEngine.SurfaceEffector2D",
|
||||
"UnityEngine.RelativeJoint2D",
|
||||
"UnityEngine.TargetJoint2D",
|
||||
"UnityEngine.WheelJoint2D",
|
||||
"UnityEngine.Joint2D",
|
||||
"UnityEngine.ParticleSystemForceField"
|
||||
};
|
||||
|
||||
static readonly string[] ComponentTypeWhiteListSdk2 = new string[]
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
"VRCSDK2.VRC_CustomRendererBehaviour",
|
||||
"VRCSDK2.VRC_MidiNoteIn",
|
||||
"VRCSDK2.scripts.Scenes.VRC_Panorama",
|
||||
"VRCSDK2.VRC_Water",
|
||||
"UnityStandardAssets.Water.WaterBasic",
|
||||
"UnityStandardAssets.Water.Displace",
|
||||
"UnityStandardAssets.Water.GerstnerDisplace",
|
||||
"UnityStandardAssets.Water.PlanarReflection",
|
||||
"UnityStandardAssets.Water.SpecularLighting",
|
||||
"UnityStandardAssets.Water.Water",
|
||||
"UnityStandardAssets.Water.WaterBase",
|
||||
"UnityStandardAssets.Water.WaterTile",
|
||||
#endif
|
||||
"VRCSDK2.VRCTriggerRelay",
|
||||
"VRCSDK2.VRC_AudioBank",
|
||||
"VRCSDK2.VRC_DataStorage",
|
||||
"VRCSDK2.VRC_EventHandler",
|
||||
"VRCSDK2.VRC_IKFollower",
|
||||
"VRCSDK2.VRC_Label",
|
||||
"VRCSDK2.VRC_KeyEvents",
|
||||
"VRCSDK2.VRC_PhysicsRoot",
|
||||
"VRCSDK2.VRC_CombatSystem",
|
||||
"VRCSDK2.VRC_DestructibleStandard",
|
||||
"VRC_VisualDamage",
|
||||
"VRCSDK2.VRC_OscButtonIn",
|
||||
"VRCSDK2.VRC_GunStats",
|
||||
"VRCSDK2.VRC_JukeBox",
|
||||
"VRCSDK2.VRC_AddDamage",
|
||||
"VRCSDK2.VRC_AddHealth",
|
||||
"VRCSDK2.VRC_AvatarCalibrator",
|
||||
"VRCSDK2.VRC_AvatarPedestal",
|
||||
"VRCSDK2.VRC_NPCSpawn",
|
||||
"VRCSDK2.VRC_ObjectSpawn",
|
||||
"VRCSDK2.VRC_ObjectSync",
|
||||
"VRCSDK2.VRC_Pickup",
|
||||
"VRCSDK2.VRC_PortalMarker",
|
||||
"VRCSDK2.VRC_SlideShow",
|
||||
"VRCSDK2.VRC_SpatialAudioSource",
|
||||
"VRCSDK2.VRC_StationInput",
|
||||
"VRCSDK2.VRC_SyncAnimation",
|
||||
"VRCSDK2.VRC_SyncVideoPlayer",
|
||||
"VRCSDK2.VRC_SyncVideoStream",
|
||||
"VRCSDK2.VRC_VideoScreen",
|
||||
"VRCSDK2.VRC_VideoSpeaker",
|
||||
"VRCSDK2.VRC_PlayerAudioOverride",
|
||||
"VRCSDK2.VRC_MirrorReflection",
|
||||
"VRCSDK2.VRC_PlayerMods",
|
||||
"VRCSDK2.VRC_SceneDescriptor",
|
||||
"VRCSDK2.VRC_SceneResetPosition",
|
||||
"VRCSDK2.VRC_SceneSmoothShift",
|
||||
"VRCSDK2.VRC_SpecialLayer",
|
||||
"VRCSDK2.VRC_Station",
|
||||
"VRCSDK2.VRC_StereoObject",
|
||||
"VRCSDK2.VRC_TimedEvents",
|
||||
"VRCSDK2.VRC_Trigger",
|
||||
"VRCSDK2.VRC_TriggerColliderEventTrigger",
|
||||
"VRCSDK2.VRC_UseEvents",
|
||||
"VRCSDK2.VRC_UiShape",
|
||||
"UnityEngine.Animation",
|
||||
#if !UNITY_2019_4_OR_NEWER
|
||||
"UnityEngine.GUIText",
|
||||
"UnityEngine.GUITexture",
|
||||
#endif
|
||||
"UnityEngine.Video.VideoPlayer",
|
||||
"PhysSound.PhysSoundBase",
|
||||
"PhysSound.PhysSoundObject",
|
||||
"PhysSound.PhysSoundTempAudio",
|
||||
"PhysSound.PhysSoundTempAudioPool",
|
||||
"PhysSound.PhysSoundTerrain",
|
||||
"RealisticEyeMovements.EyeAndHeadAnimator",
|
||||
"RealisticEyeMovements.LookTargetController",
|
||||
"UnityStandardAssets.Cameras.AbstractTargetFollower",
|
||||
"UnityStandardAssets.Cameras.AutoCam",
|
||||
"UnityStandardAssets.Cameras.FreeLookCam",
|
||||
"UnityStandardAssets.Cameras.HandHeldCam",
|
||||
"UnityStandardAssets.Cameras.LookatTarget",
|
||||
"UnityStandardAssets.Cameras.PivotBasedCameraRig",
|
||||
"UnityStandardAssets.Cameras.ProtectCameraFromWallClip",
|
||||
"UnityStandardAssets.Cameras.TargetFieldOfView",
|
||||
"UnityStandardAssets.Characters.FirstPerson.FirstPersonController",
|
||||
"UnityStandardAssets.Characters.FirstPerson.HeadBob",
|
||||
"UnityStandardAssets.Characters.FirstPerson.RigidbodyFirstPersonController",
|
||||
"UnityStandardAssets.Vehicles.Ball.Ball",
|
||||
"UnityStandardAssets.Vehicles.Ball.BallUserControl",
|
||||
"UnityStandardAssets.Characters.ThirdPerson.AICharacterControl",
|
||||
"UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter",
|
||||
"UnityStandardAssets.Characters.ThirdPerson.ThirdPersonUserControl",
|
||||
"UnityStandardAssets.CrossPlatformInput.AxisTouchButton",
|
||||
"UnityStandardAssets.CrossPlatformInput.ButtonHandler",
|
||||
"UnityStandardAssets.CrossPlatformInput.InputAxisScrollbar",
|
||||
"UnityStandardAssets.CrossPlatformInput.Joystick",
|
||||
"UnityStandardAssets.CrossPlatformInput.MobileControlRig",
|
||||
"UnityStandardAssets.CrossPlatformInput.TiltInput",
|
||||
"UnityStandardAssets.CrossPlatformInput.TouchPad",
|
||||
"UnityStandardAssets.Effects.AfterburnerPhysicsForce",
|
||||
"UnityStandardAssets.Effects.ExplosionFireAndDebris",
|
||||
"UnityStandardAssets.Effects.ExplosionPhysicsForce",
|
||||
"UnityStandardAssets.Effects.Explosive",
|
||||
"UnityStandardAssets.Effects.ExtinguishableParticleSystem",
|
||||
"UnityStandardAssets.Effects.FireLight",
|
||||
"UnityStandardAssets.Effects.Hose",
|
||||
"UnityStandardAssets.Effects.ParticleSystemMultiplier",
|
||||
"UnityStandardAssets.Effects.SmokeParticles",
|
||||
"UnityStandardAssets.Effects.WaterHoseParticles",
|
||||
"UnityStandardAssets.Utility.ActivateTrigger",
|
||||
"UnityStandardAssets.Utility.AutoMoveAndRotate",
|
||||
"UnityStandardAssets.Utility.DragRigidbody",
|
||||
"UnityStandardAssets.Utility.DynamicShadowSettings",
|
||||
"UnityStandardAssets.Utility.FollowTarget",
|
||||
"UnityStandardAssets.Utility.FPSCounter",
|
||||
"UnityStandardAssets.Utility.ObjectResetter",
|
||||
"UnityStandardAssets.Utility.ParticleSystemDestroyer",
|
||||
#if !UNITY_2019_4_OR_NEWER
|
||||
"UnityStandardAssets.Utility.SimpleActivatorMenu",
|
||||
#endif
|
||||
"UnityStandardAssets.Utility.SimpleMouseRotator",
|
||||
"UnityStandardAssets.Utility.SmoothFollow",
|
||||
"UnityStandardAssets.Utility.TimedObjectActivator",
|
||||
"UnityStandardAssets.Utility.TimedObjectDestructor",
|
||||
"UnityStandardAssets.Utility.WaypointCircuit",
|
||||
"UnityStandardAssets.Utility.WaypointProgressTracker",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneAiControl",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneAudio",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneController",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneControlSurfaceAnimator",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplanePropellerAnimator",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneUserControl2Axis",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneUserControl4Axis",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.JetParticleEffect",
|
||||
"UnityStandardAssets.Vehicles.Aeroplane.LandingGear",
|
||||
"UnityStandardAssets.Vehicles.Car.BrakeLight",
|
||||
"UnityStandardAssets.Vehicles.Car.CarAIControl",
|
||||
"UnityStandardAssets.Vehicles.Car.CarAudio",
|
||||
"UnityStandardAssets.Vehicles.Car.CarController",
|
||||
"UnityStandardAssets.Vehicles.Car.CarSelfRighting",
|
||||
"UnityStandardAssets.Vehicles.Car.CarUserControl",
|
||||
"UnityStandardAssets.Vehicles.Car.Mudguard",
|
||||
"UnityStandardAssets.Vehicles.Car.SkidTrail",
|
||||
"UnityStandardAssets.Vehicles.Car.Suspension",
|
||||
"UnityStandardAssets.Vehicles.Car.WheelEffects",
|
||||
"RenderHeads.Media.AVProVideo.ApplyToMaterial",
|
||||
"RenderHeads.Media.AVProVideo.ApplyToMesh",
|
||||
"RenderHeads.Media.AVProVideo.AudioOutput",
|
||||
"RenderHeads.Media.AVProVideo.DisplayIMGUI",
|
||||
"RenderHeads.Media.AVProVideo.DisplayUGUI",
|
||||
"RenderHeads.Media.AVProVideo.MediaPlayer",
|
||||
"RenderHeads.Media.AVProVideo.SubtitlesUGUI",
|
||||
"AlphaButtonClickMask",
|
||||
"EventSystemChecker"
|
||||
};
|
||||
|
||||
static readonly string[] ComponentTypeWhiteListSdk3 = new string[]
|
||||
{
|
||||
"VRC.SDK3.VRCDestructibleStandard",
|
||||
"VRC.SDK3.Components.VRCVisualDamage",
|
||||
"VRC.SDK3.Components.VRCAvatarPedestal",
|
||||
"VRC.SDK3.Components.VRCPickup",
|
||||
"VRC.SDK3.Components.VRCPortalMarker",
|
||||
"VRC.SDK3.Components.VRCSpatialAudioSource",
|
||||
"VRC.SDK3.Components.VRCMirrorReflection",
|
||||
"VRC.SDK3.Components.VRCSceneDescriptor",
|
||||
"VRC.SDK3.Components.VRCStation",
|
||||
"VRC.SDK3.Components.VRCUiShape",
|
||||
"VRC.SDK3.Components.VRCObjectSync",
|
||||
"VRC.SDK3.Components.VRCObjectPool",
|
||||
"VRC.SDK3.Components.VRCInputFieldKeyboardOverride",
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE
|
||||
"VRC.SDK3.Components.VRCPlayerObject",
|
||||
#endif
|
||||
#if VRC_ENABLE_PLAYER_PERSISTENCE || VRC_ENABLE_INSTANCE_PERSISTENCE
|
||||
"VRC.SDK3.Components.VRCEnablePersistence",
|
||||
#endif
|
||||
"VRC.SDK3.Video.Components.VRCUnityVideoPlayer",
|
||||
"VRC.SDK3.Video.Components.AVPro.VRCAVProVideoPlayer",
|
||||
"VRC.SDK3.Video.Components.AVPro.VRCAVProVideoScreen",
|
||||
"VRC.SDK3.Video.Components.AVPro.VRCAVProVideoSpeaker",
|
||||
"VRC.SDK3.Midi.VRCMidiListener",
|
||||
"VRC.SDK3.Midi.VRCMidiPlayer",
|
||||
"VRC.SDK3.Components.VRCCameraDollyAnimation",
|
||||
"VRC.SDK3.Components.VRCCameraDollyPath",
|
||||
"VRC.SDK3.Components.VRCCameraDollyPathPoint",
|
||||
"VRC.Udon.UdonBehaviour",
|
||||
"VRC.Udon.AbstractUdonBehaviourEventProxy",
|
||||
"VRC.SDK3.Dynamics.PhysBone.Components.VRCPhysBone",
|
||||
"VRC.SDK3.Dynamics.PhysBone.Components.VRCPhysBoneCollider",
|
||||
"VRC.SDK3.Dynamics.PhysBone.Components.VRCPhysBoneRoot",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCAimConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCLookAtConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCParentConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCPositionConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCRotationConstraint",
|
||||
"VRC.SDK3.Dynamics.Constraint.Components.VRCScaleConstraint",
|
||||
"VRC.SDK3.Dynamics.Contact.Components.VRCContactSender",
|
||||
"VRC.SDK3.Dynamics.Contact.Components.VRCContactReceiver",
|
||||
"UnityEngine.Animations.AimConstraint",
|
||||
"UnityEngine.Animations.LookAtConstraint",
|
||||
"UnityEngine.Animations.ParentConstraint",
|
||||
"UnityEngine.Animations.PositionConstraint",
|
||||
"UnityEngine.Animations.RotationConstraint",
|
||||
"UnityEngine.Animations.ScaleConstraint",
|
||||
"UnityEngine.ParticleSystemForceField",
|
||||
"Unity.AI.Navigation.NavMeshSurface",
|
||||
"Unity.AI.Navigation.NavMeshLink",
|
||||
"Unity.AI.Navigation.NavMeshModifier",
|
||||
"Unity.AI.Navigation.NavMeshModifierVolume",
|
||||
"Cinemachine.Cinemachine3rdPersonAim",
|
||||
"Cinemachine.CinemachineBlendListCamera",
|
||||
"Cinemachine.CinemachineBrain",
|
||||
"Cinemachine.CinemachineCameraOffset",
|
||||
"Cinemachine.CinemachineClearShot",
|
||||
"Cinemachine.CinemachineCollider",
|
||||
"Cinemachine.CinemachineConfiner",
|
||||
"Cinemachine.CinemachineDollyCart",
|
||||
"Cinemachine.CinemachineExternalCamera",
|
||||
"Cinemachine.CinemachineFollowZoom",
|
||||
"Cinemachine.CinemachineFreeLook",
|
||||
"Cinemachine.CinemachineMixingCamera",
|
||||
"Cinemachine.CinemachinePath",
|
||||
"Cinemachine.CinemachinePipeline",
|
||||
"Cinemachine.CinemachinePixelPerfect",
|
||||
"Cinemachine.CinemachineRecomposer",
|
||||
"Cinemachine.CinemachineSmoothPath",
|
||||
"Cinemachine.CinemachineStateDrivenCamera",
|
||||
"Cinemachine.CinemachineStoryboard",
|
||||
"Cinemachine.CinemachineTargetGroup",
|
||||
"Cinemachine.CinemachineVirtualCamera",
|
||||
"Cinemachine.Cinemachine3rdPersonFollow",
|
||||
"Cinemachine.CinemachineBasicMultiChannelPerlin",
|
||||
"Cinemachine.CinemachineComposer",
|
||||
"Cinemachine.CinemachineFramingTransposer",
|
||||
"Cinemachine.CinemachineGroupComposer",
|
||||
"Cinemachine.CinemachineHardLockToTarget",
|
||||
"Cinemachine.CinemachineHardLookAt",
|
||||
"Cinemachine.CinemachineOrbitalTransposer",
|
||||
"Cinemachine.CinemachinePOV",
|
||||
"Cinemachine.CinemachineSameAsFollowTarget",
|
||||
"Cinemachine.CinemachineTrackedDolly",
|
||||
"Cinemachine.CinemachineTransposer",
|
||||
"Cinemachine.CinemachineCore",
|
||||
};
|
||||
|
||||
public static readonly string[] ShaderWhiteList = new string[]
|
||||
{
|
||||
"VRChat/Mobile/Standard Lite",
|
||||
"VRChat/Mobile/Diffuse",
|
||||
"VRChat/Mobile/Bumped Diffuse",
|
||||
"VRChat/Mobile/Bumped Mapped Specular",
|
||||
"VRChat/Mobile/Toon Lit",
|
||||
"VRChat/Mobile/MatCap Lit",
|
||||
"VRChat/Mobile/Lightmapped",
|
||||
"VRChat/Mobile/Skybox",
|
||||
"VRChat/Mobile/Particles/Additive",
|
||||
"VRChat/Mobile/Particles/Multiply",
|
||||
"VRChat/Mobile/World/Supersampled UI",
|
||||
"FX/MirrorReflection",
|
||||
"UI/Default",
|
||||
};
|
||||
|
||||
private static readonly HashSet<int> scannedObjects = new HashSet<int>();
|
||||
|
||||
private static void ConfigureWhiteList(WhiteListConfiguration config)
|
||||
{
|
||||
if(ComponentTypeWhiteListConfiguration == config ||
|
||||
config == WhiteListConfiguration.Unchanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> concatenation = new List<string>();
|
||||
concatenation.AddRange(ComponentTypeWhiteListCommon);
|
||||
|
||||
switch(config)
|
||||
{
|
||||
case WhiteListConfiguration.VRCSDK2:
|
||||
concatenation.AddRange(ComponentTypeWhiteListSdk2);
|
||||
break;
|
||||
case WhiteListConfiguration.VRCSDK3:
|
||||
concatenation.AddRange(ComponentTypeWhiteListSdk3);
|
||||
break;
|
||||
}
|
||||
|
||||
ComponentTypeWhiteListConfiguration = config;
|
||||
ComponentTypeWhiteList = concatenation.ToArray();
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static void RemoveIllegalComponents(List<GameObject> targets, WhiteListConfiguration config, bool retry = true, HashSet<Type> tagWhitelistedTypes = null)
|
||||
{
|
||||
ConfigureWhiteList(config);
|
||||
|
||||
HashSet<Type> whitelist = ValidationUtils.WhitelistedTypes($"world{config}", ComponentTypeWhiteList);
|
||||
|
||||
// combine whitelist types from world tags with cached whitelist
|
||||
tagWhitelistedTypes?.UnionWith(whitelist);
|
||||
|
||||
foreach(GameObject target in targets)
|
||||
{
|
||||
#if VRC_CLIENT
|
||||
ValidationUtils.RemoveIllegalComponents(target, tagWhitelistedTypes ?? whitelist, retry, true);
|
||||
#else
|
||||
ValidationUtils.RemoveIllegalComponents(target, tagWhitelistedTypes ?? whitelist, retry, true, excludeEditorOnly:true, allowRemovingAssets:false);
|
||||
#endif
|
||||
|
||||
SecurityScan(target);
|
||||
AddScanned(target);
|
||||
|
||||
// Must be called after AddScanned to avoid infinite recursion.
|
||||
ScanDropdownTemplates(target, whitelist, config == WhiteListConfiguration.VRCSDK3);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddScanned(GameObject obj)
|
||||
{
|
||||
if(obj == null)
|
||||
return;
|
||||
|
||||
if(!scannedObjects.Contains(obj.GetInstanceID()))
|
||||
scannedObjects.Add(obj.GetInstanceID());
|
||||
|
||||
for(int idx = 0; idx < obj.transform.childCount; ++idx)
|
||||
AddScanned(obj.transform.GetChild(idx)?.gameObject);
|
||||
}
|
||||
|
||||
private static bool WasScanned(GameObject obj)
|
||||
{
|
||||
return scannedObjects.Contains(obj.GetInstanceID());
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static void ScanGameObject(GameObject target, WhiteListConfiguration config)
|
||||
{
|
||||
if(WasScanned(target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigureWhiteList(config);
|
||||
HashSet<Type> whitelist = ValidationUtils.WhitelistedTypes("world" + config, ComponentTypeWhiteList);
|
||||
ScanGameObject(target, whitelist, config == WhiteListConfiguration.VRCSDK3);
|
||||
}
|
||||
|
||||
private static void ScanGameObject(GameObject target, HashSet<Type> whitelist, bool isSDK3)
|
||||
{
|
||||
if(WasScanned(target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if VRC_CLIENT
|
||||
ValidationUtils.RemoveIllegalComponents(target, whitelist);
|
||||
#else
|
||||
ValidationUtils.RemoveIllegalComponents(target, whitelist, excludeEditorOnly:true, allowRemovingAssets:false);
|
||||
#endif
|
||||
|
||||
SecurityScan(target);
|
||||
#if VRC_CLIENT && UDON
|
||||
if (isSDK3)
|
||||
{
|
||||
Core.UnityEventFilter.FilterEvents(target);
|
||||
}
|
||||
#endif
|
||||
AddScanned(target);
|
||||
|
||||
// Must be called after AddScanned to avoid infinite recursion.
|
||||
ScanDropdownTemplates(target, whitelist, isSDK3);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static void ClearScannedGameObjectCache()
|
||||
{
|
||||
scannedObjects.Clear();
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IEnumerable<Shader> FindIllegalShaders(GameObject target)
|
||||
{
|
||||
return ValidationUtils.FindIllegalShaders(target, ShaderWhiteList);
|
||||
}
|
||||
|
||||
private static void SecurityScan(GameObject target)
|
||||
{
|
||||
PlayableDirector[] playableDirectors = target.GetComponentsInChildren<PlayableDirector>(true);
|
||||
foreach(PlayableDirector playableDirector in playableDirectors)
|
||||
{
|
||||
StripPlayableDirectorWithPrefabs(playableDirector);
|
||||
}
|
||||
|
||||
UnityEngine.Video.VideoPlayer[] videoPlayers = target.GetComponentsInChildren<UnityEngine.Video.VideoPlayer>(true);
|
||||
foreach (UnityEngine.Video.VideoPlayer videoPlayer in videoPlayers)
|
||||
{
|
||||
AddAudioSourceToVideoPlayer(videoPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScanDropdownTemplates(GameObject target, HashSet<Type> whitelist, bool isSDK3)
|
||||
{
|
||||
Dropdown[] dropdowns = target.GetComponentsInChildren<Dropdown>(true);
|
||||
foreach(Dropdown dropdown in dropdowns)
|
||||
{
|
||||
if(dropdown == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RectTransform dropdownTemplate = dropdown.template;
|
||||
if(dropdownTemplate == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanGameObject(dropdownTemplate.transform.root.gameObject, whitelist, isSDK3);
|
||||
}
|
||||
|
||||
#if TextMeshPro
|
||||
TMP_Dropdown[] tmpDropdowns = target.GetComponentsInChildren<TMP_Dropdown>(true);
|
||||
foreach(TMP_Dropdown textMeshProDropdown in tmpDropdowns)
|
||||
{
|
||||
if(textMeshProDropdown == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RectTransform dropdownTemplate = textMeshProDropdown.template;
|
||||
if(dropdownTemplate == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanGameObject(dropdownTemplate.transform.root.gameObject, whitelist, isSDK3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void StripPlayableDirectorWithPrefabs(PlayableDirector playableDirector)
|
||||
{
|
||||
if(!(playableDirector.playableAsset is UnityEngine.Timeline.TimelineAsset timelineAsset))
|
||||
return;
|
||||
|
||||
var tracks = timelineAsset.GetOutputTracks().Concat(timelineAsset.GetRootTracks());
|
||||
foreach(TrackAsset track in tracks)
|
||||
{
|
||||
if(!(track is ControlTrack))
|
||||
continue;
|
||||
|
||||
IEnumerable<TimelineClip> clips = track.GetClips();
|
||||
foreach(TimelineClip clip in clips)
|
||||
{
|
||||
if(clip.asset is ControlPlayableAsset controlPlayableAsset && controlPlayableAsset.prefabGameObject != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(playableDirector);
|
||||
VRC.Core.Logger.LogWarning("PlayableDirector containing prefab removed", DebugCategoryName, playableDirector.gameObject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!playableDirector.playableGraph.IsValid())
|
||||
return;
|
||||
|
||||
var audioOutputCount = playableDirector.playableGraph.GetOutputCountByType<AudioPlayableOutput>();
|
||||
for (int i = 0; i < audioOutputCount; i++)
|
||||
{
|
||||
var output = (AudioPlayableOutput)playableDirector.playableGraph.GetOutputByType<AudioPlayableOutput>(i);
|
||||
if (!output.IsOutputValid()) continue;
|
||||
if (output.GetTarget() == null)
|
||||
{
|
||||
// AudioPlayableOutput without a target source will bypass client volume controls.
|
||||
VRC.Core.Logger.LogWarning("Fixing up AudioPlayableOutput without a target source.", DebugCategoryName, playableDirector.gameObject);
|
||||
var addedObj = new GameObject("AudioSource_For_" + playableDirector.name);
|
||||
var addedSrc = addedObj.AddComponent<AudioSource>();
|
||||
addedSrc.spatialize = false;
|
||||
addedSrc.bypassEffects = true;
|
||||
addedSrc.bypassListenerEffects = true;
|
||||
addedSrc.bypassReverbZones = true;
|
||||
addedSrc.spatialBlend = 0.0f;
|
||||
addedSrc.dopplerLevel = 0.0f;
|
||||
addedSrc.rolloffMode = AudioRolloffMode.Custom;
|
||||
addedSrc.SetCustomCurve(AudioSourceCurveType.CustomRolloff, AnimationCurve.Constant(0, 1, 1));
|
||||
#if VRC_CLIENT
|
||||
addedSrc.outputAudioMixerGroup = VRCAudioManager.GetGameGroup();
|
||||
#endif
|
||||
output.SetTarget(addedSrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddAudioSourceToVideoPlayer(UnityEngine.Video.VideoPlayer videoPlayer)
|
||||
{
|
||||
// VideoPlayer objects with output mode set to "Direct" bypass client volume controls.
|
||||
|
||||
if (videoPlayer.audioOutputMode == UnityEngine.Video.VideoAudioOutputMode.Direct)
|
||||
{
|
||||
//if playback is happening (or will) you have to Stop() before you attach an AudioSource.
|
||||
bool play_state = (videoPlayer.isPlaying || videoPlayer.playOnAwake);
|
||||
if (play_state)
|
||||
{
|
||||
videoPlayer.Stop();
|
||||
}
|
||||
|
||||
AudioSource vp_src = videoPlayer.gameObject.AddComponent<AudioSource>();
|
||||
videoPlayer.audioOutputMode = UnityEngine.Video.VideoAudioOutputMode.AudioSource;
|
||||
for (ushort i = 0; i < videoPlayer.audioTrackCount; i++)
|
||||
{
|
||||
videoPlayer.SetTargetAudioSource(i,vp_src);
|
||||
}
|
||||
|
||||
if (play_state)
|
||||
{
|
||||
videoPlayer.Play();
|
||||
}
|
||||
VRC.Core.Logger.LogWarning("VideoPlayer using DIRECT audio output fixed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b03724cd556cb047b2da80492ea28a5
|
||||
timeCreated: 1504829091
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user