Added Unity project files

This commit is contained in:
2026-06-07 16:58:24 +01:00
parent 3cc05d260b
commit 23bbcab156
3942 changed files with 453676 additions and 0 deletions

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c4c0a58edfe7425bb1e4b7a5de9a87b1
timeCreated: 1683056861

View File

@ -0,0 +1,8 @@
namespace VRC.SDKBase.Editor.Api
{
public interface IVRCContent
{
string ID { get; set; }
string Name { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2970d5a709404efb9249205c86afaf20
timeCreated: 1687867386

View File

@ -0,0 +1,32 @@
using Newtonsoft.Json;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCAgreement
{
[JsonProperty("id")]
public string ID { get; set; }
public string AgreementCode { get; set; }
public string ContentId { get; set; }
public string AgreementFulltext { get; set; }
public int Version { get; set; }
public string[] Tags { get; set; }
}
public struct VRCAgreementCheckRequest
{
public string UserId { get; set; }
public string AgreementCode { get; set; }
public string ContentId { get; set; }
public int Version { get; set; }
}
public struct VRCAgreementCheckResponse
{
public bool Agreed { get; set; }
public string UserId { get; set; }
public string AgreementCode { get; set; }
public string ContentId { get; set; }
public int Version { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4a6b18f112e459c871dee35e8dc2858
timeCreated: 1739229610

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cffdc642f18a45c9865fc930726518fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using UnityEditor;
namespace VRC.SDKBase.Editor.Api
{
[InitializeOnLoad]
internal static class VRCApiCache
{
private static long DEFAULT_CACHE_TIME;
private static Dictionary<string, (DateTime timestamp, object data)> _cache;
static VRCApiCache()
{
_cache = new Dictionary<string, (DateTime timestamp, object data)>();
DEFAULT_CACHE_TIME = 1000 * 60 * 1; // 1 minute
}
public static T Add<T>(string key, T value)
{
_cache[key] = (DateTime.UtcNow, value);
return value;
}
public static T Get<T>(string key, out bool cached)
{
if (_cache.TryGetValue(key, out var value))
{
if (DateTime.UtcNow.Subtract(value.timestamp).TotalMilliseconds < DEFAULT_CACHE_TIME)
{
cached = true;
return (T) value.data;
}
cached = false;
return default;
}
cached = false;
return default;
}
public static void Invalidate(string key)
{
_cache.Remove(key);
}
public static void Clear()
{
_cache = new Dictionary<string, (DateTime timestamp, object data)>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c2861b3597c849e08c2126c83ef3fca7
timeCreated: 1683145545

View File

@ -0,0 +1,12 @@
namespace VRC.SDKBase.Editor.Api
{
public struct VRCApiError
{
public VRCApiErrorContent Error { get; set; }
public struct VRCApiErrorContent {
public string Message { get; set; }
public int Code { get; set; }
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 67fd710f3e92422eba267779143a9c5f
timeCreated: 1687964156

View File

@ -0,0 +1,59 @@
using System;
using System.Net;
using System.Net.Http;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
[assembly: InternalsVisibleTo("VRC.SDK3A.Editor")]
[assembly: InternalsVisibleTo("VRC.SDK3.Editor")]
namespace VRC.SDKBase.Editor.Api
{
/// <summary>
/// VRCApi request failed
/// </summary>
public class RequestFailedException : Exception
{
public HttpResponseMessage HttpMessage;
public HttpStatusCode StatusCode;
public RequestFailedException(string message) : base(message)
{
}
public RequestFailedException(string message, HttpResponseMessage httpMessage) : base(message)
{
HttpMessage = httpMessage;
}
public RequestFailedException(string message, HttpResponseMessage httpMessage, HttpStatusCode statusCode) : base(message)
{
HttpMessage = httpMessage;
StatusCode = statusCode;
}
}
public class JsonSerializationException : Exception
{
public JsonSerializationException(string message, Exception innerException) : base(message, innerException)
{
}
}
/// <summary>
/// VRChat API returned an error
/// </summary>
public class ApiErrorException : Exception
{
public HttpResponseMessage HttpMessage;
public HttpStatusCode StatusCode;
public string ErrorMessage;
public ApiErrorException(HttpResponseMessage httpMessage, string jsonContent)
{
var json = JsonConvert.DeserializeObject<VRCApiError>(jsonContent, VRCApi.JSON_OPTIONS);
HttpMessage = httpMessage;
StatusCode = httpMessage.StatusCode;
ErrorMessage = json.Error.Message;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 536cf9130a7e41a19f0d7e5d7900210d
timeCreated: 1683135644

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCAssetReviewNotesRequest
{
public string ReviewNotes { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 37f2155d6021416b8ad57feb5216957d
timeCreated: 1743116496

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using VRC.Core;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCAvatar: IVRCContent
{
public enum AvatarVariant
{
Standard,
Impostor
}
public struct AvatarStyles
{
public string Primary { get; set; }
public string Secondary { get; set; }
}
[JsonProperty("id")]
public string ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<string> Tags { get; set; }
public string AuthorName { get; set; }
public string AuthorId { get; set; }
public string ImageUrl { get; set; }
public string ThumbnailImageUrl { get; set; }
public string ReleaseStatus { get; set; }
public AvatarStyles Styles { get; set; }
public bool Lock { get; set; }
public string ActiveAssetReviewId { get; set; }
public bool PendingUpload { get; set; }
[JsonProperty("created_at")]
public DateTime CreatedAt { get; set; }
[JsonProperty("updated_at")]
public DateTime UpdatedAt { get; set; }
public int Version { get; set; }
public List<VRCUnityPackage> UnityPackages { get; set; }
public bool Featured { get; set; }
public string UnityPackageUrl { get; set; }
public Dictionary<string, string> UnityPackageUrlObject { get; set; }
public string GetLatestAssetUrlForPlatform(string platform)
{
string assetUrl = null;
var preferredUnityVersion = new UnityVersion();
if (this.UnityPackages == null) return null;
foreach (var unityPackage in this.UnityPackages)
{
if (UnityVersion.Parse(unityPackage.UnityVersion).CompareTo(preferredUnityVersion) < 0) continue;
if (unityPackage.Variant != null)
{
AvatarVariant variant = (AvatarVariant)Enum.Parse(typeof(AvatarVariant), unityPackage.Variant, true);
if (variant == AvatarVariant.Impostor) continue;
}
if (unityPackage.Platform != platform) continue;
assetUrl = unityPackage.AssetUrl;
preferredUnityVersion = UnityVersion.Parse(unityPackage.UnityVersion);
}
return assetUrl;
}
public string GetLatestAssetUrlForCurrentPlatform()
{
return GetLatestAssetUrlForPlatform(Tools.Platform);
}
/// <summary>
/// Checks if user-editable data is equal between avatar records
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
public bool ContentInfoEqual(VRCAvatar target)
{
return target.Name.Equals(Name) &&
target.Description.Equals(Description) &&
target.Tags.SequenceEqual(Tags) &&
target.ReleaseStatus.Equals(ReleaseStatus);
}
}
// Only a subset of fields is allowed to be changed through the SDK
public struct VRCAvatarChanges {
public string Name { get; set; }
public string Description { get; set; }
public List<string> Tags { get; set; }
public string ReleaseStatus { get; set; }
// Styles should be submitted via style ID and not the style Name
public string PrimaryStyle { get; set; }
public string SecondaryStyle { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 93e3e8f8e94746a6b83e7431081ef622
timeCreated: 1683135174

View File

@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCAvatarStyle
{
[JsonProperty("id")]
public string ID { get; set; }
public string StyleName { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a53e0d0d4a9549dbbb6fedba8fec4f2d
timeCreated: 1744068776

View File

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCFile
{
[JsonProperty("id")]
public string ID { get; set; }
public string Name { get; set; }
public string OwnerId { get; set; }
public string MimeType { get; set; }
public string Extension { get; set; }
public List<VersionEntry> Versions { get; set; }
public class VersionEntry
{
public int Version { get; set; }
public string Status { get; set; }
public DateTime CreatedAt { get; set; }
public bool Deleted { get; set; }
public FileDescriptor File { get; set; }
public FileDescriptor Signature { get; set; }
public FileDescriptor Delta { get; set; }
public class FileDescriptor
{
public string Status { get; set; }
public string URL { get; set; }
public string MD5 { get; set; }
public string Category { get; set; }
public int SizeInBytes { get; set; }
public string FileName { get; set; }
public string UploadId { get; set; }
[JsonProperty("cdns")]
public List<string> CDNs { get; set; }
}
}
public int GetLatestVersion()
{
return (this.Versions?.Count ?? 0) - 1;
}
/// <summary>
/// Returns true if there is a valid version that is not deleted.
/// </summary>
public bool HasExistingOrPendingVersion()
{
var latestVersion = GetLatestVersion();
if (latestVersion > 0)
{
latestVersion -= Versions.Count(v => v == null || v.Deleted);
}
return latestVersion > 0;
}
public bool IsLatestVersionWaiting()
{
if (!HasExistingOrPendingVersion()) return false;
var version = Versions[GetLatestVersion()];
if (version.Status == "waiting") return true;
return ((version.File?.Status == "waiting") ||
(version.Signature?.Status == "waiting"));
}
public bool IsLatestVersionQueued()
{
if (!HasExistingOrPendingVersion()) return false;
var version = Versions[GetLatestVersion()];
if (version.Status == "queued") return true;
return ((version.File?.Status == "queued") ||
(version.Signature?.Status == "queued"));
}
public bool IsLatestVersionErrored()
{
if (!HasExistingOrPendingVersion()) return false;
var version = Versions[GetLatestVersion()];
if (version.Status == "error") return true;
return ((version.File?.Status == "error") ||
(version.Signature?.Status == "error"));
}
public bool HasQueuedOperation()
{
if (IsLatestVersionWaiting()) return false;
return HasExistingOrPendingVersion() && IsLatestVersionQueued();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45cf16ea678f4615920c2d510d154633
timeCreated: 1686655895

View File

@ -0,0 +1,48 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using VRC.Core;
namespace VRC.SDKBase.Editor.Api
{
internal class VRCProgressContent: HttpContent
{
private readonly HttpContent _source;
private readonly Action<float> _onProgress;
public VRCProgressContent(HttpContent source, Action<float> onProgress)
{
_source = source;
_onProgress = onProgress;
}
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
var sendBuffer = new byte[4 * 1024 * 1024];
var totalBytesRead = 0L;
using (var inputStream = await _source.ReadAsStreamAsync())
{
var totalBytes = inputStream.Length;
var bytesRead = 0;
while ((bytesRead = await inputStream.ReadAsync(sendBuffer, 0, sendBuffer.Length)) > 0)
{
await stream.WriteAsync(sendBuffer, 0, bytesRead);
totalBytesRead += bytesRead;
_onProgress((float)totalBytesRead / totalBytes);
Core.Logger.Log($"Sent {totalBytesRead} out of {totalBytes}", API.LOG_CATEGORY);
}
}
}
protected override bool TryComputeLength(out long length)
{
length = _source.Headers.ContentLength.GetValueOrDefault();
return true;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 685a233b2bbf4a99a5f62f2d9727f484
timeCreated: 1686669873

View File

@ -0,0 +1,164 @@
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
[assembly: InternalsVisibleTo("VRC.SDK3A.Editor")]
[assembly: InternalsVisibleTo("VRC.SDK3.Editor")]
namespace VRC.SDKBase.Editor.Api
{
internal class VRCTools
{
internal static byte[] GetFileMD5(string filePath)
{
var hash = MD5.Create();
using var fileStream = File.OpenRead(filePath);
return hash.ComputeHash(fileStream);
}
internal static async Task<string> GenerateFileSignature(string sourceFilePath, string targetFilePath)
{
await using var fileStream = File.OpenRead(sourceFilePath);
await using var inStream = librsync.net.Librsync.ComputeSignature(fileStream);
await using var signatureStream = File.Open(targetFilePath, FileMode.Create, FileAccess.Write);
await inStream.CopyToAsync(signatureStream);
return targetFilePath;
}
internal static async Task CleanupTempFiles(string fileId)
{
var folder = VRC.Tools.GetTempFolderPath(fileId);
while (Directory.Exists(folder))
{
try
{
if (Directory.Exists(folder))
Directory.Delete(folder, true);
}
catch (System.Exception)
{
// ignored
}
await Task.Delay(10);
}
}
internal static string GetMimeTypeFromExtension(string extension)
{
if (extension == ".vrcw")
return "application/x-world";
if (extension == ".vrca")
return "application/x-avatar";
if (extension == ".vrcp")
return "application/x-prop";
if (extension == ".dll")
return "application/x-msdownload";
if (extension == ".unitypackage")
return "application/gzip";
if (extension == ".gz")
return "application/gzip";
if (extension == ".jpg")
return "image/jpg";
if (extension == ".png")
return "image/png";
if (extension == ".sig")
return "application/x-rsync-signature";
if (extension == ".delta")
return "application/x-rsync-delta";
return "application/octet-stream";
}
/// <summary>
/// This expands the send buffer size from around 256KB up to 4MB (our preferred send buffer size)
/// This dramatically speeds up file uploads
///
/// Unfortunately, APIs meant to be used for this aren't available in Mono version of .NET in Unity
/// So we have to use reflection to get to the private fields and properties we need
/// </summary>
/// <param name="targetUrl">Url to expand the send buffer for</param>
/// <param name="sendRequest">Request task to expand the buffer for</param>
/// <param name="cancellationToken"></param>
internal static async Task IncreaseSendBuffer(Uri targetUrl, Task sendRequest,
CancellationToken cancellationToken)
{
var servicePointScheduler = typeof(ServicePoint).GetProperty("Scheduler", BindingFlags.NonPublic | BindingFlags.Instance);
var servicePointGroups = servicePointScheduler?.PropertyType.GetField("groups", BindingFlags.NonPublic | BindingFlags.Instance);
var groupsList = servicePointGroups?.FieldType.GetProperty("Values");
var connectionStateType = servicePointGroups?.FieldType.GenericTypeArguments[1];
var connectionsList = connectionStateType?.GetField("connections", BindingFlags.NonPublic | BindingFlags.Instance);
var connection = connectionsList?.FieldType.GenericTypeArguments[0];
var socket = connection?.GetField("socket", BindingFlags.NonPublic | BindingFlags.Instance);
while (!sendRequest.IsCompleted && !cancellationToken.IsCancellationRequested)
{
await Task.Delay(250, cancellationToken);
try
{
var servicePoint = ServicePointManager.FindServicePoint(targetUrl);
var scheduler = servicePointScheduler?.GetValue(servicePoint);
if (scheduler == null)
{
continue;
}
// This can be null on mac/linux, so we add an extra check
var servicePointGroup = servicePointGroups?.GetValue(scheduler);
if (servicePointGroup == null)
{
continue;
}
var groups = (IEnumerable) groupsList?.GetValue(servicePointGroup);
// we're going to retry finding the active service point
if (groups == null)
{
continue;
}
foreach (var group in groups)
{
var connections = (IEnumerable) connectionsList?.GetValue(group);
if (connections == null)
{
continue;
}
foreach (var webConnection in connections)
{
if (webConnection == null)
{
continue;
}
var socketInstance = (Socket) socket?.GetValue(webConnection);
if (socketInstance != null && socketInstance.Connected &&
socketInstance.SendBufferSize < 4 * 1024 * 1024)
{
socketInstance.SendBufferSize = 4 * 1024 * 1024;
return;
}
}
}
}
catch (Exception e)
{
Core.Logger.LogWarning($"Failed to increase send buffer {e.Message}", Core.API.LOG_CATEGORY);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e259a399dbb4ef19a7a89d2b4233247
timeCreated: 1686658578

View File

@ -0,0 +1,20 @@
using System;
using Newtonsoft.Json;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCUnityPackage
{
[JsonProperty("id")]
public string ID { get; set; }
public string AssetUrl { get; set; }
public string UnityVersion { get; set; }
public long UnitySortNumber { get; set; }
public int AssetVersion { get; set; }
public string Platform { get; set; }
[JsonProperty("created_at")]
public DateTime CreatedAt { get; set; }
public string Variant { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f7f09500761d4a019e606df903a022c8
timeCreated: 1683134817

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using VRC.Core;
namespace VRC.SDKBase.Editor.Api
{
public struct VRCWorld: IVRCContent
{
[JsonProperty("id")]
public string ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string PreviewYoutubeId { get; set; }
public List<string> Tags { get; set; }
public List<string> UdonProducts { get; set; }
public string AuthorName { get; set; }
public string AuthorId { get; set; }
public string ImageUrl { get; set; }
public string ThumbnailImageUrl { get; set; }
public string ReleaseStatus { get; set; }
public int Capacity { get; set; }
public int RecommendedCapacity { get; set; }
public int Favorites { get; set; }
public int Visits { get; set; }
// public int Popularity { get; set; } // this field doesn't deserialize correctly for VERY popular worlds, ignoring in SDK for now
public int Heat { get; set; }
public int Occupants { get; set; }
public int PublicOccupants { get; set; }
public int PrivateOccupants { get; set; }
public List<Instance> Instances { get; set; }
public struct Instance
{
public string ID { get; set; }
public int Occupants { get; set; }
}
[JsonProperty("created_at")]
public DateTime CreatedAt { get; set; }
[JsonProperty("updated_at")]
public DateTime UpdatedAt { get; set; }
public DateTime PublicationDate { get; set; }
public DateTime LabsPublicationDate { get; set; }
public int Version { get; set; }
public List<VRCUnityPackage> UnityPackages { get; set; }
public string Organization { get; set; }
public bool Featured { get; set; }
public string GetLatestAssetUrlForPlatform(string platform)
{
string assetUrl = null;
var preferredUnityVersion = new UnityVersion();
if (this.UnityPackages == null) return null;
foreach (var unityPackage in this.UnityPackages)
{
if (UnityVersion.Parse(unityPackage.UnityVersion).CompareTo(preferredUnityVersion) < 0) continue;
if (unityPackage.Platform != platform) continue;
assetUrl = unityPackage.AssetUrl;
preferredUnityVersion = UnityVersion.Parse(unityPackage.UnityVersion);
}
return assetUrl;
}
}
// Only a subset of fields is allowed to be changed through the SDK
public struct VRCWorldChanges {
public string Name { get; set; }
public string Description { get; set; }
public int Capacity { get; set; }
public int RecommendedCapacity { get; set; }
public string PreviewYoutubeId { get; set; }
public List<string> Tags { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bcf1554494894265b68fbf2e7c089782
timeCreated: 1683134291

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 41dfa12618ada604f98c12a95a3f455a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e2ea84994e53bd458fa73dfcfd0d671
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
#if VRC_SDK_PIPELINE_SAMPLES
using UnityEditor;
namespace VRC.SDKBase.Editor.BuildPipeline.Samples
{
public class VRCSDKBuildRequestedCallbackSample : IVRCSDKBuildRequestedCallback
{
public int callbackOrder => 0;
public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
{
return EditorUtility.DisplayDialog("Build Confirmation", "Are you sure you want to build?", "Yes", "Not Yes");
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a1d20f4241085e46bdddc71b691465b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
{
"name": "VRC.SDKBase.Editor.BuildPipeline",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 21332e1f0d937794d916d2402ba1943a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 027731dadc7571046abaf43e4f52e9ae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,230 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
using VRC.SDKBase;
[InitializeOnLoad]
public class AutoAddSpatialAudioComponents
{
public static bool Enabled = true;
static AutoAddSpatialAudioComponents()
{
EditorApplication.hierarchyChanged += OnHierarchyWindowChanged;
EditorApplication.projectChanged += OnProjectWindowChanged;
RegisterCallbacks();
}
static void OnHierarchyWindowChanged()
{
if (!Enabled)
{
EditorApplication.hierarchyChanged -= OnHierarchyWindowChanged;
return;
}
// check for proper use of VRCSP, and warn
//TryToAddSpatializationToAllAudioSources(true, false);
}
static void OnProjectWindowChanged()
{
RegisterCallbacks();
}
static void RegisterCallbacks()
{
VRCSdkControlPanel._EnableSpatialization = VRCSDKControlPanel_EnableSpatialization;
}
// callback from VrcSdkControlPanel in dll
public static void VRCSDKControlPanel_EnableSpatialization()
{
Debug.Log("Enabling spatialization on 3D AudioSources...");
TryToAddSpatializationToAllAudioSources(false, true);
}
static bool ApplyDefaultSpatializationToAudioSource(AudioSource audioSrc, bool force = false)
{
if (audioSrc == null)
return false;
var vrcsp = audioSrc.gameObject.GetComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
// don't make changes if we already have a vrcsp and we aren't forcing
if (vrcsp != null && !force)
return false;
if (force)
audioSrc.spatialBlend = 1;
bool initValues = force;
// is audio source set to be 2D?
bool is2D = audioSrc.spatialBlend == 0;
if (vrcsp == null)
{
// no onsp and no vrcsp, so add
vrcsp = audioSrc.gameObject.AddComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
if (is2D)
{
// this audio source was marked as 2D, leave the vrcsp disabled
vrcsp.EnableSpatialization = false;
}
initValues = true;
}
audioSrc.spatialize = vrcsp.EnableSpatialization;
vrcsp.enabled = true;
if (initValues)
{
bool isAvatar = audioSrc.GetComponentInParent<VRC.SDKBase.VRC_AvatarDescriptor>();
vrcsp.Gain = isAvatar ? AudioManagerSettings.AvatarAudioMaxGain : AudioManagerSettings.RoomAudioGain;
vrcsp.Near = 0;
vrcsp.Far = isAvatar ? AudioManagerSettings.AvatarAudioMaxRange : AudioManagerSettings.RoomAudioMaxRange;
vrcsp.UseAudioSourceVolumeCurve = false;
}
return true;
}
public static void TryToAddSpatializationToAllAudioSources(bool newAudioSourcesOnly, bool includeInactive)
{
AudioSource[] allAudioSources = includeInactive ? Resources.FindObjectsOfTypeAll<AudioSource>() : Object.FindObjectsByType<AudioSource>(FindObjectsSortMode.None);
foreach (AudioSource src in allAudioSources)
{
if (src == null || src.gameObject == null || src.gameObject.scene != UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene())
{
continue;
}
if (newAudioSourcesOnly)
{
if (!IsNewAudioSource(src))
continue;
UnityEngine.Audio.AudioMixerGroup mixer = AssetDatabase.LoadAssetAtPath<UnityEngine.Audio.AudioMixerGroup>("Assets/VRCSDK/Dependencies/OSPNative/scenes/mixers/SpatializerMixer.mixer");
if (mixer != null)
{
src.outputAudioMixerGroup = mixer;
}
}
if (ApplyDefaultSpatializationToAudioSource(src, false))
{
Debug.Log("Automatically added VRC_SpatialAudioSource component to " + GetGameObjectPath(src.gameObject) + "!");
}
}
}
static bool IsNewAudioSource(AudioSource src)
{
var vrcsp = src.GetComponent<VRC_SpatialAudioSource>();
if (vrcsp != null)
return false;
if (src.clip != null)
return false;
if (src.outputAudioMixerGroup != null)
return false;
if (src.mute || src.bypassEffects || src.bypassReverbZones || !src.playOnAwake || src.loop)
return false;
if (src.priority != 128 ||
!Mathf.Approximately(src.volume, 1.0f) ||
!Mathf.Approximately(src.pitch, 1.0f) ||
!Mathf.Approximately(src.panStereo, 0.0f) ||
!Mathf.Approximately(src.spatialBlend, 0.0f) ||
!Mathf.Approximately(src.reverbZoneMix, 1.0f))
{
return false;
}
if (!Mathf.Approximately(src.dopplerLevel, 1.0f) ||
!Mathf.Approximately(src.spread, 0.0f) ||
src.rolloffMode != AudioRolloffMode.Logarithmic ||
!Mathf.Approximately(src.minDistance, 1.0f) ||
!Mathf.Approximately(src.maxDistance, 500.0f))
{
return false;
}
return true;
}
static string GetGameObjectPath(GameObject obj)
{
string path = "/" + obj.name;
while (obj.transform.parent != null)
{
obj = obj.transform.parent.gameObject;
path = "/" + obj.name + path;
}
return path;
}
public static void ConvertONSPAudioSource(AudioSource src)
{
if (src == null) return;
var onsp = src.GetComponent<ONSPAudioSource>();
if (onsp != null)
{
var vrcsp = src.gameObject.GetComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
if (vrcsp == null)
{
// copy the values from deprecated component
vrcsp = src.gameObject.AddComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
vrcsp.Gain = onsp.Gain;
vrcsp.Near = onsp.Near;
vrcsp.Far = onsp.Far;
vrcsp.UseAudioSourceVolumeCurve = !onsp.UseInvSqr;
vrcsp.EnableSpatialization = onsp.EnableSpatialization;
}
// remove deprecated component
Component.DestroyImmediate(onsp);
}
}
public static void AddVRCSpatialToBareAudioSource(AudioSource src)
{
if (src == null) return;
var vrcsp = src.gameObject.GetComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
if (vrcsp != null) return;
#if VRC_SDK_VRCSDK2
vrcsp = src.gameObject.AddComponent<VRCSDK2.VRC_SpatialAudioSource>();
#elif UDON
vrcsp = src.gameObject.AddComponent<VRC.SDK3.Components.VRCSpatialAudioSource>();
#endif
// add default values
bool isAvatar = src.gameObject.GetComponentInParent<VRC.SDKBase.VRC_AvatarDescriptor>();
vrcsp.Gain = isAvatar ? AudioManagerSettings.AvatarAudioMaxGain : AudioManagerSettings.RoomAudioGain;
vrcsp.Near = 0;
vrcsp.Far = isAvatar ? AudioManagerSettings.AvatarAudioMaxRange : AudioManagerSettings.RoomAudioMaxRange;
vrcsp.UseAudioSourceVolumeCurve = false;
// enable spatialization if src is not 2D
vrcsp.EnableSpatialization = false;
AnimationCurve curve = src.GetCustomCurve(AudioSourceCurveType.SpatialBlend);
if (curve != null)
{
foreach (var key in curve.keys)
{
if (key.value != 0 || key.inTangent != 0)
{
vrcsp.EnableSpatialization = true;
break;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a364ece829b6234888c59987a305a00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
public class EditorCoroutine
{
public static EditorCoroutine Start( IEnumerator _routine )
{
EditorCoroutine coroutine = new EditorCoroutine(_routine);
coroutine.start();
return coroutine;
}
public static EditorCoroutine Start(System.Action _action)
{
EditorCoroutine coroutine = new EditorCoroutine(_action);
coroutine.start();
return coroutine;
}
readonly IEnumerator routine;
EditorCoroutine( IEnumerator _routine )
{
routine = _routine;
}
readonly System.Action action;
EditorCoroutine(System.Action _action)
{
action = _action;
}
void start()
{
EditorApplication.update += update;
}
public void stop()
{
EditorApplication.update -= update;
}
void update()
{
if (routine != null)
{
if (!routine.MoveNext())
stop();
}
else if (action != null)
{
action();
stop();
}
else
stop();
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 89005ebc9543e0a4284893c09ca19b1d
timeCreated: 1473271738
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
using UnityEditor;
using UnityEngine.SceneManagement;
[InitializeOnLoad]
public static class EditorHandling
{
static EditorHandling()
{
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += SceneOpenedCallback;
}
static void SceneOpenedCallback( Scene scene, UnityEditor.SceneManagement.OpenSceneMode mode)
{
// refresh window when scene is opened to display content images correctly
if (null != VRCSdkControlPanel.window) VRCSdkControlPanel.window.Reset();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d6c2e367eaa9564ebf6267ec163cfbd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,358 @@
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections.Generic;
using System.Linq;
using VRC.SDKBase;
namespace VRCSDK2
{
#if VRC_SDK_VRCSDK2
[CustomEditor(typeof(VRCSDK2.VRC_EventHandler))]
public class EventHandlerEditor : UnityEditor.Editor
{
bool showDeferredEvents = false;
static VRCSDK2.VRC_EventHandler.VrcEventType lastAddedEventType = VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage;
public override void OnInspectorGUI()
{
VRCSDK2.VRC_EventHandler myTarget = (VRCSDK2.VRC_EventHandler)target;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("ID:");
EditorGUILayout.EndHorizontal();
if (myTarget.GetComponent<VRCSDK2.VRC_Trigger>() != null)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Add Events via the VRC_Trigger on this object.");
EditorGUILayout.EndHorizontal();
}
else
{
EditorGUI.BeginChangeCheck();
RenderOldEditor(myTarget);
if (EditorGUI.EndChangeCheck())
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
if (myTarget.deferredEvents.Count > 0)
{
showDeferredEvents = EditorGUILayout.Foldout(showDeferredEvents, "Deferred Events");
if (showDeferredEvents)
RenderEvents(myTarget.deferredEvents);
}
}
int[] sendMessageMethodIndicies;
private void RenderOldEditor(VRCSDK2.VRC_EventHandler myTarget)
{
EditorGUILayout.HelpBox("Please use a VRC_Trigger in the future.", MessageType.Error);
if (GUILayout.Button("Add Event Handler"))
myTarget.Events.Add(new VRCSDK2.VRC_EventHandler.VrcEvent());
bool first = true;
int deleteEventIndex = -1;
if (sendMessageMethodIndicies == null || sendMessageMethodIndicies.Length != myTarget.Events.Count)
sendMessageMethodIndicies = new int[myTarget.Events.Count + 1];
for (int i = 0; i < myTarget.Events.Count; ++i)
{
if (!first)
EditorGUILayout.Separator();
first = false;
EditorGUILayout.LabelField("Event " + (i + 1).ToString());
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Event Name");
myTarget.Events[i].Name = EditorGUILayout.TextField(myTarget.Events[i].Name);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Event Type");
myTarget.Events[i].EventType = (VRCSDK2.VRC_EventHandler.VrcEventType)EditorGUILayout.EnumPopup(myTarget.Events[i].EventType);
EditorGUILayout.EndHorizontal();
switch (myTarget.Events[i].EventType)
{
case VRCSDK2.VRC_EventHandler.VrcEventType.AnimationBool:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Variable");
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Operation");
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.AnimationFloat:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Variable");
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Value");
myTarget.Events[i].ParameterFloat = EditorGUILayout.FloatField(myTarget.Events[i].ParameterFloat);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.AnimationTrigger:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Trigger");
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.AudioTrigger:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("AudioSource");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.MeshVisibility:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Mesh");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Operation");
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.PlayAnimation:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Target");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Animation");
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.RunConsoleCommand:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Command");
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Receiver");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Message");
// sorry for this shit show. Below allows us to show a list of public methods, but also allow custom messages
var methods = VRC_EditorTools.GetAccessibleMethodsOnGameObject(myTarget.Events[i].ParameterObject);
List<string> methodList = methods.Values.Aggregate(new List<string>(), (acc, lst) => { acc.AddRange(lst.Select(mi => mi.Name)); return acc; });
methodList.Add("Custom Message");
string[] _choices = methodList.ToArray();
int currentIndex = methodList.Count - 1;
if (methodList.Contains(myTarget.Events[i].ParameterString))
currentIndex = methodList.IndexOf(myTarget.Events[i].ParameterString);
sendMessageMethodIndicies[i] = EditorGUILayout.Popup(currentIndex, _choices);
if (sendMessageMethodIndicies[i] != methodList.Count - 1)
{
myTarget.Events[i].ParameterString = _choices[sendMessageMethodIndicies[i]];
}
else
{
if (methodList.Contains(myTarget.Events[i].ParameterString))
myTarget.Events[i].ParameterString = "";
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
}
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.SetGameObjectActive:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("GameObject");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Operation");
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.SetParticlePlaying:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Target");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Operation");
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.TeleportPlayer:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Location");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Align Room To Destination");
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.SetWebPanelURI:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("URI");
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Panel");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
break;
case VRCSDK2.VRC_EventHandler.VrcEventType.SetWebPanelVolume:
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Volume");
myTarget.Events[i].ParameterFloat = EditorGUILayout.FloatField(myTarget.Events[i].ParameterFloat);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Panel");
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
break;
default:
EditorGUILayout.BeginHorizontal();
GUIStyle redText = new GUIStyle();
redText.normal.textColor = Color.red;
EditorGUILayout.LabelField("Unsupported event type", redText);
EditorGUILayout.EndHorizontal();
break;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Delete " + myTarget.Events[i].Name + "?");
if (GUILayout.Button("delete"))
deleteEventIndex = i;
EditorGUILayout.EndHorizontal();
if (myTarget.Events[i].ParameterObject == null)
myTarget.Events[i].ParameterObject = myTarget.gameObject;
}
if (deleteEventIndex != -1)
myTarget.Events.RemoveAt(deleteEventIndex);
}
private void RenderEvents(IEnumerable<VRCSDK2.VRC_EventHandler.EventInfo> entries)
{
foreach (VRCSDK2.VRC_EventHandler.EventInfo entry in entries)
{
EditorGUILayout.PrefixLabel("Target");
EditorGUILayout.ObjectField(entry.evt.ParameterObject, typeof(GameObject), true);
EditorGUILayout.LabelField(string.Format("Name: {0}", entry.evt.Name));
EditorGUILayout.LabelField(string.Format("Type: {0}", entry.evt.EventType));
EditorGUILayout.LabelField(string.Format("Bool: {0}", entry.evt.ParameterBool));
EditorGUILayout.LabelField(string.Format("Float: {0}", entry.evt.ParameterFloat));
EditorGUILayout.LabelField(string.Format("Int: {0}", entry.evt.ParameterInt));
EditorGUILayout.LabelField(string.Format("String: {0}", entry.evt.ParameterString));
EditorGUILayout.Space();
}
}
public static void RenderEditor(VRCSDK2.VRC_EventHandler myTarget)
{
bool first = true;
int deleteEventIndex = -1;
for (int i = 0; i < myTarget.Events.Count; ++i)
{
if (!first)
EditorGUILayout.Separator();
first = false;
if (RenderEventHeader(myTarget, myTarget.Events[i]))
deleteEventIndex = i;
RenderEventHeader(myTarget, myTarget.Events[i]);
if (myTarget.Events[i].ParameterObject == null)
myTarget.Events[i].ParameterObject = myTarget.gameObject;
}
if (deleteEventIndex != -1)
myTarget.Events.RemoveAt(deleteEventIndex);
}
public static VRCSDK2.VRC_EventHandler.VrcEvent RenderAddEvent(VRCSDK2.VRC_EventHandler myTarget)
{
VRCSDK2.VRC_EventHandler.VrcEvent newEvent = null;
EditorGUILayout.BeginHorizontal();
lastAddedEventType = VRC_EditorTools.FilteredEnumPopup("New Event Type", lastAddedEventType, (v) => v != VRCSDK2.VRC_EventHandler.VrcEventType.SpawnObject && v != VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage);
if (GUILayout.Button("Add"))
{
newEvent = new VRCSDK2.VRC_EventHandler.VrcEvent
{
EventType = lastAddedEventType,
ParameterObject = myTarget.gameObject
};
myTarget.Events.Add(newEvent);
EditorUtility.SetDirty(myTarget);
}
EditorGUILayout.EndHorizontal();
return newEvent;
}
public static bool RenderEventHeader(VRCSDK2.VRC_EventHandler myTarget, VRCSDK2.VRC_EventHandler.VrcEvent evt)
{
EditorGUILayout.BeginHorizontal();
evt.EventType = VRC_EditorTools.FilteredEnumPopup("New Event Type", evt.EventType, (v) => v != VRCSDK2.VRC_EventHandler.VrcEventType.SpawnObject && v != VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage);
bool delete = GUILayout.Button("Remove");
EditorGUILayout.EndHorizontal();
return delete;
}
}
[CustomEditor(typeof(VRC.SDKBase.VRC_EventHandler))]
public class SDKBaseEventHandlerEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("Event Handlers are not supported in VRCSDK3.");
if (GUILayout.Button("replace me with the correct VRC_EventHandler"))
{
var go = ((VRC.SDKBase.VRC_EventHandler)target).gameObject;
DestroyImmediate(target);
go.AddComponent<VRCSDK2.VRC_EventHandler>();
}
}
}
#else
[CustomEditor(typeof(VRC.SDKBase.VRC_EventHandler))]
public class SDKBaseEventHandlerEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("Event Handlers are not supported in VRCSDK3.");
if( GUILayout.Button("delete me") )
DestroyImmediate(target);
}
}
#endif
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4810e652e8242384c834320970702290
timeCreated: 1454469344
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,48 @@
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
#pragma warning disable 0618
using UnityEditor;
using System.Collections;
namespace VRCSDK2
{
[CustomEditor(typeof(VRCSDK2.VRC_KeyEvents))]
public class VRC_KeyEventsEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
}
}
[CustomEditor(typeof(VRCSDK2.VRC_UseEvents))]
public class VRC_UseEventsEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
}
}
[CustomEditor(typeof(VRCSDK2.VRC_TriggerColliderEventTrigger))]
public class VRC_TriggerColliderEventTriggerEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
}
}
[CustomEditor(typeof(VRCSDK2.VRC_TimedEvents))]
public class VRC_TimedEventsEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
}
}
}
#pragma warning restore 0618
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 482185bf29f12074dada194ffef6a682
timeCreated: 1475877803
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,225 @@
#if VRC_SDK_VRCSDK2 && !VRC_CLIENT
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using VRC.SDK3.Editor;
using VRC.SDKBase.Editor;
[CustomEditor(typeof(VRCSDK2.VRC_AvatarDescriptor))]
public class AvatarDescriptorEditor : Editor
{
VRCSDK2.VRC_AvatarDescriptor avatarDescriptor;
VRC.Core.PipelineManager pipelineManager;
SkinnedMeshRenderer selectedMesh;
List<string> blendShapeNames = null;
bool shouldRefreshVisemes = false;
public override void OnInspectorGUI()
{
if (avatarDescriptor == null)
avatarDescriptor = (VRCSDK2.VRC_AvatarDescriptor)target;
if (pipelineManager == null)
{
pipelineManager = avatarDescriptor.GetComponent<VRC.Core.PipelineManager>();
if (pipelineManager == null)
avatarDescriptor.gameObject.AddComponent<VRC.Core.PipelineManager>();
}
// DrawDefaultInspector();
if(VRCSdkControlPanel.window != null)
{
if( GUILayout.Button( "Select this avatar in the SDK control panel" ) )
VRCSdkControlPanelAvatarBuilder.SelectAvatar(avatarDescriptor);
}
avatarDescriptor.ViewPosition = EditorGUILayout.Vector3Field("View Position", avatarDescriptor.ViewPosition);
//avatarDescriptor.Name = EditorGUILayout.TextField("Avatar Name", avatarDescriptor.Name);
avatarDescriptor.Animations = (VRCSDK2.VRC_AvatarDescriptor.AnimationSet)EditorGUILayout.EnumPopup("Default Animation Set", avatarDescriptor.Animations);
avatarDescriptor.CustomStandingAnims = (AnimatorOverrideController)EditorGUILayout.ObjectField("Custom Standing Anims", avatarDescriptor.CustomStandingAnims, typeof(AnimatorOverrideController), true, null);
avatarDescriptor.CustomSittingAnims = (AnimatorOverrideController)EditorGUILayout.ObjectField("Custom Sitting Anims", avatarDescriptor.CustomSittingAnims, typeof(AnimatorOverrideController), true, null);
avatarDescriptor.ScaleIPD = EditorGUILayout.Toggle("Scale IPD", avatarDescriptor.ScaleIPD);
avatarDescriptor.lipSync = (VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle)EditorGUILayout.EnumPopup("Lip Sync", avatarDescriptor.lipSync);
switch (avatarDescriptor.lipSync)
{
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.Default:
if (GUILayout.Button("Auto Detect!"))
AutoDetectLipSync();
break;
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape:
avatarDescriptor.VisemeSkinnedMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", avatarDescriptor.VisemeSkinnedMesh, typeof(SkinnedMeshRenderer), true);
if (avatarDescriptor.VisemeSkinnedMesh != null)
{
DetermineBlendShapeNames();
int current = -1;
for (int b = 0; b < blendShapeNames.Count; ++b)
if (avatarDescriptor.MouthOpenBlendShapeName == blendShapeNames[b])
current = b;
string title = "Jaw Flap Blend Shape";
int next = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
if (next >= 0)
avatarDescriptor.MouthOpenBlendShapeName = blendShapeNames[next];
}
break;
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone:
avatarDescriptor.lipSyncJawBone = (Transform)EditorGUILayout.ObjectField("Jaw Bone", avatarDescriptor.lipSyncJawBone, typeof(Transform), true);
break;
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape:
SkinnedMeshRenderer prev = avatarDescriptor.VisemeSkinnedMesh;
avatarDescriptor.VisemeSkinnedMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", avatarDescriptor.VisemeSkinnedMesh, typeof(SkinnedMeshRenderer), true);
if (avatarDescriptor.VisemeSkinnedMesh != prev)
shouldRefreshVisemes = true;
if (avatarDescriptor.VisemeSkinnedMesh != null)
{
DetermineBlendShapeNames();
if (avatarDescriptor.VisemeBlendShapes == null || avatarDescriptor.VisemeBlendShapes.Length != (int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count)
avatarDescriptor.VisemeBlendShapes = new string[(int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count];
for (int i = 0; i < (int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count; ++i)
{
int current = -1;
for (int b = 0; b < blendShapeNames.Count; ++b)
if (avatarDescriptor.VisemeBlendShapes[i] == blendShapeNames[b])
current = b;
string title = "Viseme: " + ((VRCSDK2.VRC_AvatarDescriptor.Viseme)i).ToString();
int next = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
if (next >= 0)
avatarDescriptor.VisemeBlendShapes[i] = blendShapeNames[next];
}
if (shouldRefreshVisemes)
AutoDetectVisemes();
}
break;
}
EditorGUILayout.LabelField("Unity Version", avatarDescriptor.unityVersion);
}
void DetermineBlendShapeNames()
{
if (avatarDescriptor.VisemeSkinnedMesh != null &&
avatarDescriptor.VisemeSkinnedMesh != selectedMesh)
{
blendShapeNames = new List<string>();
blendShapeNames.Add("-none-");
selectedMesh = avatarDescriptor.VisemeSkinnedMesh;
if ((selectedMesh != null) && (selectedMesh.sharedMesh != null))
{
for (int i = 0; i < selectedMesh.sharedMesh.blendShapeCount; ++i)
blendShapeNames.Add(selectedMesh.sharedMesh.GetBlendShapeName(i));
}
}
}
void AutoDetectVisemes()
{
// prioritize strict - but fallback to looser - naming and don't touch user-overrides
List<string> blendShapes = new List<string>(blendShapeNames);
blendShapes.Remove("-none-");
for (int v = 0; v < avatarDescriptor.VisemeBlendShapes.Length; v++)
{
if (string.IsNullOrEmpty(avatarDescriptor.VisemeBlendShapes[v]))
{
string viseme = ((VRCSDK2.VRC_AvatarDescriptor.Viseme)v).ToString().ToLowerInvariant();
foreach (string s in blendShapes)
{
if (s.ToLowerInvariant() == "vrc.v_" + viseme)
{
avatarDescriptor.VisemeBlendShapes[v] = s;
goto next;
}
}
foreach (string s in blendShapes)
{
if (s.ToLowerInvariant() == "v_" + viseme)
{
avatarDescriptor.VisemeBlendShapes[v] = s;
goto next;
}
}
foreach (string s in blendShapes)
{
if (s.ToLowerInvariant().EndsWith(viseme))
{
avatarDescriptor.VisemeBlendShapes[v] = s;
goto next;
}
}
foreach (string s in blendShapes)
{
if (s.ToLowerInvariant() == viseme)
{
avatarDescriptor.VisemeBlendShapes[v] = s;
goto next;
}
}
foreach (string s in blendShapes)
{
if (s.ToLowerInvariant().Contains(viseme))
{
avatarDescriptor.VisemeBlendShapes[v] = s;
goto next;
}
}
next: { }
}
}
shouldRefreshVisemes = false;
}
void AutoDetectLipSync()
{
var smrs = avatarDescriptor.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var smr in smrs)
{
if (smr.sharedMesh.blendShapeCount > 0)
{
avatarDescriptor.lipSyncJawBone = null;
if (smr.sharedMesh.blendShapeCount > 1)
{
avatarDescriptor.lipSync = VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape;
avatarDescriptor.VisemeSkinnedMesh = smr;
shouldRefreshVisemes = true;
}
else
{
avatarDescriptor.lipSync = VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape;
avatarDescriptor.VisemeSkinnedMesh = null;
}
return;
}
}
Animator a = avatarDescriptor.GetComponent<Animator>();
if (!a)
EditorUtility.DisplayDialog("Ooops", "This avatar has no Animator and can have no lipsync.", "OK");
else if (a.GetBoneTransform(HumanBodyBones.Jaw) != null)
{
avatarDescriptor.lipSync = VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone;
avatarDescriptor.lipSyncJawBone = avatarDescriptor.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Jaw);
avatarDescriptor.VisemeSkinnedMesh = null;
return;
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5e83254bb97e84795ac882692ae124ba
timeCreated: 1450462624
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
[CustomEditor(typeof(VRCSDK2.VRC_ObjectSpawn))]
public class VRCObjectSpawnEditor : Editor
{
VRCSDK2.VRC_ObjectSpawn spawn;
void OnEnable()
{
if (spawn == null)
spawn = (VRCSDK2.VRC_ObjectSpawn)target;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 26a75599848adb449b7aceed5090e35c
timeCreated: 1463516633
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
[CustomEditor(typeof(VRCSDK2.VRC_ObjectSync))]
public class VRCObjectSyncEditor : Editor
{
VRCSDK2.VRC_ObjectSync sync;
void OnEnable()
{
if (sync == null)
sync = (VRCSDK2.VRC_ObjectSync)target;
}
public override void OnInspectorGUI()
{
sync.SynchronizePhysics = EditorGUILayout.Toggle("Synchronize Physics",sync.SynchronizePhysics);
sync.AllowCollisionTransfer = EditorGUILayout.Toggle("Allow Collision Transfer", sync.AllowCollisionTransfer);
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ed4aad2698d3b62408e69b57c7748791
timeCreated: 1463516212
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using UnityEditor;
public class VRCPlayerModEditorWindow : EditorWindow {
public delegate void AddModCallback();
public static AddModCallback addModCallback;
private static VRCSDK2.VRC_PlayerMods myTarget;
private static VRCSDK2.VRCPlayerModFactory.PlayerModType type;
public static void Init (VRCSDK2.VRC_PlayerMods target, AddModCallback callback)
{
// Get existing open window or if none, make a new one:
EditorWindow.GetWindow (typeof (VRCPlayerModEditorWindow));
addModCallback = callback;
myTarget = target;
type = VRCSDK2.VRCPlayerModFactory.PlayerModType.Jump;
}
void OnGUI ()
{
type = (VRCSDK2.VRCPlayerModFactory.PlayerModType)EditorGUILayout.EnumPopup("Mods", type);
if(GUILayout.Button("Add Mod"))
{
VRCSDK2.VRCPlayerMod mod = VRCSDK2.VRCPlayerModFactory.Create(type);
myTarget.AddMod(mod);
addModCallback();
}
}
}
#endif

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8986a640e24a0754ea0aded12234b808
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,109 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System;
namespace VRCSDK2
{
[CustomEditor(typeof(VRCSDK2.VRC_PlayerMods))]
public class VRCPlayerModsEditor : UnityEditor.Editor
{
VRCSDK2.VRC_PlayerMods myTarget;
void OnEnable()
{
if(myTarget == null)
myTarget = (VRCSDK2.VRC_PlayerMods)target;
}
public override void OnInspectorGUI()
{
myTarget.isRoomPlayerMods = EditorGUILayout.Toggle("isRoomPlayerMods", myTarget.isRoomPlayerMods);
List<VRCSDK2.VRCPlayerMod> playerMods = myTarget.playerMods;
for(int i=0; i<playerMods.Count; ++i)
{
VRCSDK2.VRCPlayerMod mod = playerMods[i];
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField(mod.name, EditorStyles.boldLabel);
if( mod.allowNameEdit )
mod.name = EditorGUILayout.TextField( "Mod Name: ", mod.name );
for(int j=0; j<mod.properties.Count; ++j)
{
VRCSDK2.VRCPlayerModProperty prop = mod.properties[j];
myTarget.playerMods[i].properties[j] = DrawFieldForProp(prop);
}
if(GUILayout.Button ("Remove Mod"))
{
myTarget.RemoveMod(mod);
break;
}
EditorGUILayout.EndVertical();
}
if(GUILayout.Button("Add Mods"))
{
VRCPlayerModEditorWindow.AddModCallback adcb = OnInspectorGUI;
VRCPlayerModEditorWindow.Init(myTarget, adcb);
}
}
VRCSDK2.VRCPlayerModProperty DrawFieldForProp(VRCSDK2.VRCPlayerModProperty property)
{
if(property.type.SystemType == typeof(int))
{
property.intValue = EditorGUILayout.IntField(property.name, property.intValue);
}
else if(property.type.SystemType == typeof(float))
{
property.floatValue = EditorGUILayout.FloatField(property.name, property.floatValue);
}
else if(property.type.SystemType == typeof(string))
{
property.stringValue = EditorGUILayout.TextField(property.name, property.stringValue);
}
else if(property.type.SystemType == typeof(bool))
{
property.boolValue = EditorGUILayout.Toggle(property.name, property.boolValue);
}
else if(property.type.SystemType == typeof(GameObject))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField( property.name );
property.gameObjectValue = (GameObject) EditorGUILayout.ObjectField( property.gameObjectValue, typeof( GameObject ), true );
EditorGUILayout.EndHorizontal();
}
else if(property.type.SystemType == typeof(KeyCode))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField( property.name );
property.keyCodeValue = (KeyCode) EditorGUILayout.EnumPopup( property.keyCodeValue );
EditorGUILayout.EndHorizontal();
}
else if(property.type.SystemType == typeof(VRCSDK2.VRC_EventHandler.VrcBroadcastType))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField( property.name );
property.broadcastValue = (VRCSDK2.VRC_EventHandler.VrcBroadcastType) EditorGUILayout.EnumPopup( property.broadcastValue );
EditorGUILayout.EndHorizontal();
}
else if(property.type.SystemType == typeof(VRCSDK2.VRCPlayerModFactory.HealthOnDeathAction))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField( property.name );
property.onDeathActionValue = (VRCSDK2.VRCPlayerModFactory.HealthOnDeathAction) EditorGUILayout.EnumPopup( property.onDeathActionValue);
EditorGUILayout.EndHorizontal();
}
else if(property.type.SystemType == typeof(RuntimeAnimatorController))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField( property.name );
property.animationController = (RuntimeAnimatorController) EditorGUILayout.ObjectField( property.animationController, typeof( RuntimeAnimatorController ), false );
EditorGUILayout.EndHorizontal();
}
return property;
}
}
}
#endif

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 792e7964a56e51f4188e1221751642e9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System;
[CustomEditor(typeof(VRCSDK2.VRC_Station))]
public class VRCPlayerStationEditor : Editor
{
VRCSDK2.VRC_Station myTarget;
SerializedProperty onRemoteEnter;
SerializedProperty onRemoteExit;
SerializedProperty onLocalEnter;
SerializedProperty onLocalExit;
void OnEnable()
{
if(myTarget == null)
myTarget = (VRCSDK2.VRC_Station)target;
onRemoteEnter = serializedObject.FindProperty("OnRemotePlayerEnterStation");
onRemoteExit = serializedObject.FindProperty("OnRemotePlayerExitStation");
onLocalEnter = serializedObject.FindProperty("OnLocalPlayerEnterStation");
onLocalExit = serializedObject.FindProperty("OnLocalPlayerExitStation");
}
public override void OnInspectorGUI()
{
myTarget.PlayerMobility = (VRC.SDKBase.VRCStation.Mobility)EditorGUILayout.EnumPopup("Player Mobility", myTarget.PlayerMobility);
myTarget.canUseStationFromStation = EditorGUILayout.Toggle("Can Use Station From Station", myTarget.canUseStationFromStation);
myTarget.animatorController = (RuntimeAnimatorController)EditorGUILayout.ObjectField("Animator Controller", myTarget.animatorController, typeof(RuntimeAnimatorController), false);
myTarget.disableStationExit = EditorGUILayout.Toggle("Disable Station Exit", myTarget.disableStationExit);
myTarget.seated = EditorGUILayout.Toggle("Seated", myTarget.seated);
myTarget.stationEnterPlayerLocation = (Transform)EditorGUILayout.ObjectField("Player Enter Location", myTarget.stationEnterPlayerLocation, typeof(Transform), true);
myTarget.stationExitPlayerLocation = (Transform)EditorGUILayout.ObjectField("Player Exit Location", myTarget.stationExitPlayerLocation, typeof(Transform), true);
myTarget.controlsObject = (VRC.SDKBase.VRC_ObjectApi)EditorGUILayout.ObjectField("API Object", myTarget.controlsObject, typeof(VRC.SDKBase.VRC_ObjectApi), false);
EditorGUILayout.PropertyField(onRemoteEnter, new GUIContent("On Remote Player Enter"));
EditorGUILayout.PropertyField(onRemoteExit, new GUIContent("On Remote Player Exit"));
EditorGUILayout.PropertyField(onLocalEnter, new GUIContent("On Local Player Enter"));
EditorGUILayout.PropertyField(onLocalExit, new GUIContent("On Local Player Exit"));
}
}
#endif

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5262a02c32e41e047bdfdfc3b63db8ff
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using UnityEditor;
using System.Collections;
[CustomEditor (typeof(VRCSDK2.VRC_SceneDescriptor))]
public class VRCSceneDescriptorEditor : Editor
{
VRCSDK2.VRC_SceneDescriptor sceneDescriptor;
VRC.Core.PipelineManager pipelineManager;
public override void OnInspectorGUI()
{
if(sceneDescriptor == null)
sceneDescriptor = (VRCSDK2.VRC_SceneDescriptor)target;
if(pipelineManager == null)
{
pipelineManager = sceneDescriptor.GetComponent<VRC.Core.PipelineManager>();
if(pipelineManager == null)
sceneDescriptor.gameObject.AddComponent<VRC.Core.PipelineManager>();
}
DrawDefaultInspector();
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e9cbc493bbbc443fb92898aa84d221ec
timeCreated: 1450463561
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,130 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace VRCSDK2
{
//[CustomPropertyDrawer(typeof(VRC_AvatarVariations.VariationCategory))]
//public class PropertyDrawer_AvatarVariation_VariationCategory : PropertyDrawer
//{
// public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
// {
// //EditorGUILayout.Label("blah");
// if (property == null)
// return;
// SerializedProperty nameProperty = property.FindPropertyRelative("name");
// //SerializedProperty mirrorProperty = property.FindPropertyRelative("mirror");
// //SerializedProperty typeProperty = property.FindPropertyRelative("type");
// //SerializedProperty valueProperty = null;
// //switch (typeProperty.enumValueIndex)
// //{
// // case (int)VRC_DataStorage.VrcDataType.Bool:
// // valueProperty = property.FindPropertyRelative("valueBool");
// // break;
// // case (int)VRC_DataStorage.VrcDataType.Float:
// // valueProperty = property.FindPropertyRelative("valueFloat");
// // break;
// // case (int)VRC_DataStorage.VrcDataType.Int:
// // valueProperty = property.FindPropertyRelative("valueInt");
// // break;
// // case (int)VRC_DataStorage.VrcDataType.String:
// // valueProperty = property.FindPropertyRelative("valueString");
// // break;
// // case (int)VRC_DataStorage.VrcDataType.SerializeObject:
// // valueProperty = property.FindPropertyRelative("serializeComponent");
// // break;
// // case (int)VRC_DataStorage.VrcDataType.None:
// // case (int)VRC_DataStorage.VrcDataType.SerializeBytes:
// // break;
// //}
// EditorGUI.BeginProperty(rect, label, property);
// int baseWidth = (int)(rect.width / 4);
// Rect nameRect = new Rect(rect.x, rect.y, baseWidth, rect.height);
// //Rect mirrorRect = new Rect(rect.x + baseWidth, rect.y, baseWidth, rect.height);
// //Rect typeRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth, rect.height);
// //Rect valueRect = new Rect(rect.x + baseWidth * 3, rect.y, baseWidth, rect.height);
// //Rect typeValueRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth * 2, rect.height);
// EditorGUI.PropertyField(nameRect, nameProperty, GUIContent.none);
// //EditorGUI.PropertyField(mirrorRect, mirrorProperty, GUIContent.none);
// //switch (mirrorProperty.enumValueIndex)
// //{
// // case (int)VRC_DataStorage.VrcDataMirror.None:
// // if (valueProperty == null)
// // VRC_EditorTools.FilteredEnumPopup<VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
// // else
// // {
// // VRC_EditorTools.FilteredEnumPopup<VRC_DataStorage.VrcDataType>(typeRect, typeProperty, t => true);
// // EditorGUI.PropertyField(valueRect, valueProperty, GUIContent.none);
// // }
// // break;
// // case (int)VRC_DataStorage.VrcDataMirror.SerializeComponent:
// // typeProperty.enumValueIndex = (int)VRC_DataStorage.VrcDataType.SerializeObject;
// // EditorGUI.PropertyField(typeValueRect, valueProperty, GUIContent.none);
// // break;
// // default:
// // VRC_EditorTools.FilteredEnumPopup<VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
// // break;
// //}
// EditorGUI.EndProperty();
// }
//}
//[CustomEditor(typeof(VRC_AvatarVariations))]
//public class VRC_AvatarVariationsEditor : Editor
//{
// SerializedProperty categories;
// void OnEnable()
// {
// categories = serializedObject.FindProperty("categories");
// }
// public override void OnInspectorGUI()
// {
// //serializedObject.Update();
// // EditorGUILayout.PropertyField(categories);
// //serializedObject.ApplyModifiedProperties();
// //if (target == null)
// // return;
// ////var prop = serializedObject.FindProperty("root");
// ////EditorGUILayout.PropertyField(prop, new GUIContent("Show Help"));
// //VRCSDK2.VRC_AvatarVariations variations = target as VRCSDK2.VRC_AvatarVariations;
// //if (variations.categories == null)
// // variations.categories = new VRC_AvatarVariations.VariationCategory[0];
// //foreach ( var vc in variations.categories )
// //{
// // vc.name = EditorGUILayout.TextField("Variation Name", vc.name);
// //// SerializedProperty triggers = triggersProperty.Copy();
// //// int triggersLength = triggers.arraySize;
// //// List<int> to_remove = new List<int>();
// //// for (int idx = 0; idx < triggersLength; ++idx)
// //// {
// //// SerializedProperty triggerProperty = triggers.GetArrayElementAtIndex(idx);
// //// }
// //// EditorGUILayout.LabelField("");
// ////// helpProperty = serializedObject.FindProperty("ShowHelp");
// ////// EditorGUILayout.PropertyField(helpProperty, new GUIContent("Show Help"));
// //}
// ////EditorGUILayout.
// DrawDefaultInspector();
// }
//}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: eeda995d0ceac6443a54716996eab52e
timeCreated: 1511373338
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using VRC.SDKBase;
namespace VRCSDK2
{
[CustomPropertyDrawer(typeof(VRCSDK2.VRC_DataStorage.VrcDataElement))]
public class CustomDataElementDrawer : PropertyDrawer
{
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
if (property == null)
return;
SerializedProperty nameProperty = property.FindPropertyRelative("name");
SerializedProperty mirrorProperty = property.FindPropertyRelative("mirror");
SerializedProperty typeProperty = property.FindPropertyRelative("type");
SerializedProperty valueProperty = null;
switch (typeProperty.enumValueIndex)
{
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.Bool:
valueProperty = property.FindPropertyRelative("valueBool");
break;
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.Float:
valueProperty = property.FindPropertyRelative("valueFloat");
break;
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.Int:
valueProperty = property.FindPropertyRelative("valueInt");
break;
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.String:
valueProperty = property.FindPropertyRelative("valueString");
break;
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.SerializeObject:
valueProperty = property.FindPropertyRelative("serializeComponent");
break;
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.None:
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.SerializeBytes:
break;
}
EditorGUI.BeginProperty(rect, label, property);
int baseWidth = (int)(rect.width / 4);
Rect nameRect = new Rect(rect.x, rect.y, baseWidth, rect.height);
Rect mirrorRect = new Rect(rect.x + baseWidth, rect.y, baseWidth, rect.height);
Rect typeRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth, rect.height);
Rect valueRect = new Rect(rect.x + baseWidth * 3, rect.y, baseWidth, rect.height);
Rect typeValueRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth * 2, rect.height);
EditorGUI.PropertyField(nameRect, nameProperty, GUIContent.none);
EditorGUI.PropertyField(mirrorRect, mirrorProperty, GUIContent.none);
switch (mirrorProperty.enumValueIndex)
{
case (int)VRCSDK2.VRC_DataStorage.VrcDataMirror.None:
if (valueProperty == null)
VRC_EditorTools.FilteredEnumPopup<VRCSDK2.VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
else
{
VRC_EditorTools.FilteredEnumPopup<VRCSDK2.VRC_DataStorage.VrcDataType>(typeRect, typeProperty, t => true);
EditorGUI.PropertyField(valueRect, valueProperty, GUIContent.none);
}
break;
case (int)VRCSDK2.VRC_DataStorage.VrcDataMirror.SerializeComponent:
typeProperty.enumValueIndex = (int)VRCSDK2.VRC_DataStorage.VrcDataType.SerializeObject;
EditorGUI.PropertyField(typeValueRect, valueProperty, GUIContent.none);
break;
default:
VRC_EditorTools.FilteredEnumPopup<VRCSDK2.VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
break;
}
EditorGUI.EndProperty();
}
}
[CustomEditor(typeof(VRCSDK2.VRC_DataStorage)), CanEditMultipleObjects]
public class VRC_DataStorageEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
VRCSDK2.VRC_ObjectSync os = ((VRCSDK2.VRC_DataStorage)target).GetComponent<VRCSDK2.VRC_ObjectSync>();
if (os != null && os.SynchronizePhysics)
EditorGUILayout.HelpBox("Consider either removing the VRC_ObjectSync or disabling SynchronizePhysics.", MessageType.Warning);
DrawDefaultInspector();
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0ac7998a36f085844847acbc046d4e27
timeCreated: 1478191469
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using UnityEngine;
using UnityEditor;
using VRC_DestructibleStandard = VRC.SDKBase.VRC_DestructibleStandard;
using VRC.SDKBase;
[CustomEditor(typeof(VRC_DestructibleStandard))]
[CanEditMultipleObjects]
public class VRC_DestructibleStandardEditor : Editor
{
VRC_DestructibleStandard ds;
SerializedProperty maxHealth;
SerializedProperty currentHealth;
SerializedProperty healable;
SerializedProperty onDamagedTrigger;
SerializedProperty onDestroyedTrigger;
SerializedProperty onHealedTrigger;
SerializedProperty onFullHealedTrigger;
void OnEnable()
{
maxHealth = serializedObject.FindProperty("maxHealth");
currentHealth = serializedObject.FindProperty("currentHealth");
healable = serializedObject.FindProperty("healable");
onDamagedTrigger = serializedObject.FindProperty("onDamagedTrigger");
onDestroyedTrigger = serializedObject.FindProperty("onDestructedTrigger");
onHealedTrigger = serializedObject.FindProperty("onHealedTrigger");
onFullHealedTrigger = serializedObject.FindProperty("onFullHealedTrigger");
}
public override void OnInspectorGUI()
{
ds = (VRC_DestructibleStandard)target;
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
serializedObject.Update ();
EditorGUILayout.PropertyField(maxHealth, new GUIContent("Max Health"));
EditorGUILayout.PropertyField(currentHealth, new GUIContent("Current Health"));
EditorGUILayout.PropertyField(healable, new GUIContent("Is Healable"));
EditorGUILayout.PropertyField(onDamagedTrigger, new GUIContent("On Damaged Trigger"));
VRC_EditorTools.DrawTriggerActionCallback("On Damaged Action", ds.onDamagedTrigger, ds.onDamagedEvent);
EditorGUILayout.PropertyField(onDestroyedTrigger, new GUIContent("On Destructed Trigger"));
VRC_EditorTools.DrawTriggerActionCallback("On Destructed Action", ds.onDestructedTrigger, ds.onDestructedEvent);
EditorGUILayout.PropertyField(onHealedTrigger, new GUIContent("On Healed Trigger"));
VRC_EditorTools.DrawTriggerActionCallback("On Healed Action", ds.onHealedTrigger, ds.onHealedEvent);
EditorGUILayout.PropertyField(onFullHealedTrigger, new GUIContent("On Full Healed Trigger"));
VRC_EditorTools.DrawTriggerActionCallback("On Full Healed Action", ds.onFullHealedTrigger, ds.onFullHealedEvent);
// Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
serializedObject.ApplyModifiedProperties ();
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3b63b118c0591b548ba1797e6be4292e
timeCreated: 1477161996
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
#if VRC_SDK_VRCSDK2
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(VRCSDK2.VRC_ObjectSync))]
public class VRC_ObjectSyncEditor : Editor {
public override void OnInspectorGUI()
{
VRCSDK2.VRC_ObjectSync c = ((VRCSDK2.VRC_ObjectSync)target);
if ((c.gameObject.GetComponent<Animator>() != null || c.gameObject.GetComponent<Animation>() != null) && c.SynchronizePhysics)
EditorGUILayout.HelpBox("If the Animator or Animation moves the root position of this object then it will conflict with physics synchronization.", MessageType.Warning);
if (c.GetComponent<VRCSDK2.VRC_DataStorage>() != null && c.SynchronizePhysics)
EditorGUILayout.HelpBox("Consider either removing the VRC_DataStorage or disabling SynchronizePhysics.", MessageType.Warning);
DrawDefaultInspector();
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e19a7147a2386554a8e4d6e414f190a2
timeCreated: 1504908295
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,70 @@
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace VRCSDK2
{
[CustomEditor(typeof(VRCSDK2.VRC_Pickup))]
public class VRC_PickupEditor : UnityEditor.Editor
{
private void InspectorField(string propertyName, string humanName)
{
SerializedProperty propertyField = serializedObject.FindProperty(propertyName);
EditorGUILayout.PropertyField(propertyField, new GUIContent(humanName), true);
}
private SerializedProperty momentumTransferMethodProperty;
private SerializedProperty disallowTheftProperty;
private SerializedProperty exactGunProperty;
private SerializedProperty exactGripProperty;
private SerializedProperty allowManipulationWhenEquippedProperty;
private SerializedProperty orientationProperty;
private SerializedProperty autoHoldProperty;
private SerializedProperty interactionTextProperty;
private SerializedProperty useTextProperty;
private SerializedProperty throwVelocityBoostMinSpeedProperty;
private SerializedProperty throwVelocityBoostScaleProperty;
private SerializedProperty pickupableProperty;
private SerializedProperty proximityProperty;
public override void OnInspectorGUI()
{
momentumTransferMethodProperty = serializedObject.FindProperty("MomentumTransferMethod");
disallowTheftProperty = serializedObject.FindProperty("DisallowTheft");
exactGunProperty = serializedObject.FindProperty("ExactGun");
exactGripProperty = serializedObject.FindProperty("ExactGrip");
allowManipulationWhenEquippedProperty = serializedObject.FindProperty("allowManipulationWhenEquipped");
orientationProperty = serializedObject.FindProperty("orientation");
autoHoldProperty = serializedObject.FindProperty("AutoHold");
interactionTextProperty = serializedObject.FindProperty("InteractionText");
useTextProperty = serializedObject.FindProperty("UseText");
throwVelocityBoostMinSpeedProperty = serializedObject.FindProperty("ThrowVelocityBoostMinSpeed");
throwVelocityBoostScaleProperty = serializedObject.FindProperty("ThrowVelocityBoostScale");
pickupableProperty = serializedObject.FindProperty("pickupable");
proximityProperty = serializedObject.FindProperty("proximity");
EditorGUILayout.BeginVertical(GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 30));
EditorGUILayout.PropertyField(momentumTransferMethodProperty, new GUIContent("Momentum Transfer Method"));
EditorGUILayout.PropertyField(disallowTheftProperty, new GUIContent("Disallow Theft"));
EditorGUILayout.PropertyField(exactGunProperty, new GUIContent("Exact Gun"));
EditorGUILayout.PropertyField(exactGripProperty, new GUIContent("Exact Grip"));
EditorGUILayout.PropertyField(allowManipulationWhenEquippedProperty, new GUIContent("Allow Manipulation When Equipped"));
EditorGUILayout.PropertyField(orientationProperty, new GUIContent("Orientation"));
EditorGUILayout.PropertyField(autoHoldProperty, new GUIContent("AutoHold", "If the pickup is supposed to be aligned to the hand (i.e. orientation field is set to Gun or Grip), auto-detect means that it will be Equipped(not dropped when they release trigger), otherwise just hold as a normal pickup."));
EditorGUILayout.PropertyField(interactionTextProperty, new GUIContent("Interaction Text","Text displayed when user hovers over the pickup."));
if (autoHoldProperty.enumValueIndex != (int)VRCSDK2.VRC_Pickup.AutoHoldMode.No)
EditorGUILayout.PropertyField(useTextProperty, new GUIContent("Use Text", "Text to display describing action for clicking button, when this pickup is already being held."));
EditorGUILayout.PropertyField(throwVelocityBoostMinSpeedProperty, new GUIContent("Throw Velocity Boost Min Speed"));
EditorGUILayout.PropertyField(throwVelocityBoostScaleProperty, new GUIContent("Throw Velocity Boost Scale"));
EditorGUILayout.PropertyField(pickupableProperty, new GUIContent("Pickupable"));
EditorGUILayout.PropertyField(proximityProperty, new GUIContent("Proximity"));
EditorGUILayout.EndVertical();
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4aff4e5c0d600c845b29d7b8b7965d68
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
#if VRC_SDK_VRCSDK2
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System;
namespace VRCSDK2
{
[CustomEditor(typeof(VRCSDK2.VRC_PlayerAudioOverride))]
public class VRC_PlayerAudioOverrideEditor : UnityEditor.Editor
{
private bool voShow = true;
private bool voAdv = false;
private bool avShow = true;
private bool avAdv = false;
private SerializedProperty prioProperty;
private SerializedProperty globalProperty;
private SerializedProperty regionProperty;
private SerializedProperty voGainProperty;
private SerializedProperty voNearProperty;
private SerializedProperty voFarProperty;
private SerializedProperty voRadiusProperty;
private SerializedProperty voDisableLpProperty;
private SerializedProperty avGainProperty;
private SerializedProperty avNearProperty;
private SerializedProperty avFarProperty;
private SerializedProperty avRadiusProperty;
private SerializedProperty avForceSpatialProperty;
private SerializedProperty avAllowCustomProperty;
public override void OnInspectorGUI()
{
globalProperty = serializedObject.FindProperty("global");
regionProperty = serializedObject.FindProperty("region");
prioProperty = serializedObject.FindProperty("regionPriority");
voGainProperty = serializedObject.FindProperty("VoiceGain");
voNearProperty = serializedObject.FindProperty("VoiceNear");
voFarProperty = serializedObject.FindProperty("VoiceFar");
voRadiusProperty = serializedObject.FindProperty("VoiceVolumetricRadius");
voDisableLpProperty = serializedObject.FindProperty("VoiceDisableLowpass");
avGainProperty = serializedObject.FindProperty("AvatarGainLimit");
avNearProperty = serializedObject.FindProperty("AvatarNearLimit");
avFarProperty = serializedObject.FindProperty("AvatarFarLimit");
avRadiusProperty = serializedObject.FindProperty("AvatarVolumetricRadiusLimit");
avForceSpatialProperty = serializedObject.FindProperty("AvatarForceSpatial");
avAllowCustomProperty = serializedObject.FindProperty("AvatarAllowCustomCurve");
serializedObject.Update();
EditorGUILayout.BeginVertical();
var ovr = serializedObject.targetObject as VRCSDK2.VRC_PlayerAudioOverride;
EditorGUILayout.PropertyField(globalProperty, new GUIContent("Global"));
if (!ovr.global)
{
EditorGUILayout.PropertyField(regionProperty, new GUIContent("Region"));
EditorGUILayout.PropertyField(prioProperty, new GUIContent("Priority"));
}
voShow = EditorGUILayout.Foldout(voShow, "Voice Settings");
if (voShow)
{
EditorGUILayout.PropertyField(voGainProperty, new GUIContent("Gain"));
EditorGUILayout.PropertyField(voFarProperty, new GUIContent("Far"));
EditorGUI.indentLevel++;
voAdv = EditorGUILayout.Foldout(voAdv, "Advanced Options");
if (voAdv)
{
EditorGUILayout.PropertyField(voNearProperty, new GUIContent("Near"));
EditorGUILayout.PropertyField(voRadiusProperty, new GUIContent("Volumetric Radius"));
EditorGUILayout.PropertyField(voDisableLpProperty, new GUIContent("Disable Lowpass Filter"));
}
EditorGUI.indentLevel--;
}
avShow = EditorGUILayout.Foldout(avShow, "Avatar Audio Limits");
if (avShow)
{
EditorGUILayout.PropertyField(avGainProperty, new GUIContent("Gain Limit"));
EditorGUILayout.PropertyField(avFarProperty, new GUIContent("Far Limit"));
EditorGUI.indentLevel++;
avAdv = EditorGUILayout.Foldout(avAdv, "Advanced Options");
if (avAdv)
{
EditorGUILayout.PropertyField(avNearProperty, new GUIContent("Near Limit"));
EditorGUILayout.PropertyField(avRadiusProperty, new GUIContent("Volumetric Radius Limit"));
EditorGUILayout.PropertyField(avForceSpatialProperty, new GUIContent("Force Spatial"));
EditorGUILayout.PropertyField(avAllowCustomProperty, new GUIContent("Allow Custom Curve"));
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5c545625e0bf93045ac1c5864141c5c1
timeCreated: 1474315179
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System;
namespace VRCSDK2
{
[CustomEditor(typeof(VRCSDK2.VRC_SpatialAudioSource))]
public class VRC_SpatialAudioSourceEditor : UnityEditor.Editor
{
private bool showAdvancedOptions = false;
private SerializedProperty gainProperty;
private SerializedProperty nearProperty;
private SerializedProperty farProperty;
private SerializedProperty volRadiusProperty;
private SerializedProperty enableSpatialProperty;
private SerializedProperty useCurveProperty;
public override void OnInspectorGUI()
{
gainProperty = serializedObject.FindProperty("Gain");
nearProperty = serializedObject.FindProperty("Near");
farProperty = serializedObject.FindProperty("Far");
volRadiusProperty = serializedObject.FindProperty("VolumetricRadius");
enableSpatialProperty = serializedObject.FindProperty("EnableSpatialization");
useCurveProperty = serializedObject.FindProperty("UseAudioSourceVolumeCurve");
serializedObject.Update();
VRCSDK2.VRC_SpatialAudioSource target = serializedObject.targetObject as VRCSDK2.VRC_SpatialAudioSource;
AudioSource source = target.GetComponent<AudioSource>();
EditorGUILayout.BeginVertical();
EditorGUILayout.PropertyField(gainProperty, new GUIContent("Gain"));
EditorGUILayout.PropertyField(farProperty, new GUIContent("Far"));
showAdvancedOptions = EditorGUILayout.Foldout(showAdvancedOptions, "Advanced Options");
bool enableSp = enableSpatialProperty.boolValue;
if (showAdvancedOptions)
{
EditorGUILayout.PropertyField(nearProperty, new GUIContent("Near"));
EditorGUILayout.PropertyField(volRadiusProperty, new GUIContent("Volumetric Radius"));
EditorGUILayout.PropertyField(enableSpatialProperty, new GUIContent("Enable Spatialization"));
if (enableSp)
EditorGUILayout.PropertyField(useCurveProperty, new GUIContent("Use AudioSource Volume Curve"));
}
EditorGUILayout.EndVertical();
if (source != null)
source.spatialize = enableSp;
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0d2d4cba733f5eb4ba170368e67710d2
timeCreated: 1474315179
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,105 @@
#if VRC_SDK_VRCSDK2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using VRC.SDKBase;
[CustomPropertyDrawer(typeof(VRCSDK2.VRC_SyncVideoPlayer.VideoEntry))]
public class CustomVideoEntryDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
SerializedProperty source = property.FindPropertyRelative("Source");
SerializedProperty ratio = property.FindPropertyRelative("AspectRatio");
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
SerializedProperty url = property.FindPropertyRelative("URL");
return EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing
+ EditorGUI.GetPropertyHeight(ratio, new GUIContent("Aspect Ratio"), true) + EditorGUIUtility.standardVerticalSpacing
+ EditorGUI.GetPropertyHeight(speed, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing
+ Mathf.Max(EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true), EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true)) + EditorGUIUtility.standardVerticalSpacing;
}
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
SerializedProperty source = property.FindPropertyRelative("Source");
SerializedProperty ratio = property.FindPropertyRelative("AspectRatio");
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
SerializedProperty url = property.FindPropertyRelative("URL");
EditorGUI.BeginProperty(rect, label, property);
float x = rect.x;
float y = rect.y;
float w = rect.width;
float h = EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing;
VRC_EditorTools.FilteredEnumPopup<UnityEngine.Video.VideoSource>(new Rect(x, y, w, h), source, (e) => e == UnityEngine.Video.VideoSource.Url);
y += h;
if (source.enumValueIndex == (int)UnityEngine.Video.VideoSource.Url)
{
h = EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), url);
y += h;
}
else
{
h = EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), clip);
y += h;
}
h = EditorGUI.GetPropertyHeight(ratio, new GUIContent("AspectRatio"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), ratio);
y += h;
h = EditorGUI.GetPropertyHeight(ratio, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), speed);
if (speed.floatValue == 0f)
speed.floatValue = 1f;
y += h;
EditorGUI.EndProperty();
}
}
[CustomEditor(typeof(VRCSDK2.VRC_SyncVideoPlayer))]
public class SyncVideoPlayerEditor : Editor
{
ReorderableList sourceList;
public override void OnInspectorGUI()
{
SerializedProperty searchRoot = serializedObject.FindProperty("VideoSearchRoot");
EditorGUILayout.PropertyField(searchRoot);
SerializedProperty maxQual = serializedObject.FindProperty("MaxStreamQuality");
EditorGUILayout.PropertyField(maxQual);
EditorGUILayout.Space();
sourceList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
private void OnEnable()
{
SerializedProperty videos = serializedObject.FindProperty("Videos");
sourceList = new ReorderableList(serializedObject, videos);
sourceList.drawElementCallback += (Rect rect, int index, bool active, bool focused) =>
{
EditorGUI.PropertyField(rect, serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index));
};
sourceList.elementHeightCallback += (int index) =>
{
SerializedProperty element = serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index);
return EditorGUI.GetPropertyHeight(element);
};
sourceList.drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, "Videos");
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ae0e74693b7899f47bd98864f94b9311
timeCreated: 1499468412
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,117 @@
#if VRC_SDK_VRCSDK2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using VRC.SDKBase;
[CustomPropertyDrawer(typeof(VRCSDK2.VRC_SyncVideoStream.VideoEntry))]
public class CustomVideoStreamEntryDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
SerializedProperty source = property.FindPropertyRelative("Source");
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
SerializedProperty url = property.FindPropertyRelative("URL");
SerializedProperty live = property.FindPropertyRelative("SyncType");
SerializedProperty sync = property.FindPropertyRelative("SyncMinutes");
return EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing
+ EditorGUI.GetPropertyHeight(speed, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing
+ Mathf.Max(EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true), EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true)) + EditorGUIUtility.standardVerticalSpacing
+ EditorGUI.GetPropertyHeight(live, new GUIContent("SyncType"), true) + EditorGUIUtility.standardVerticalSpacing
+ EditorGUI.GetPropertyHeight(sync, new GUIContent("SyncMinutes"), true) + EditorGUIUtility.standardVerticalSpacing;
}
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
SerializedProperty source = property.FindPropertyRelative("Source");
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
SerializedProperty url = property.FindPropertyRelative("URL");
SerializedProperty live = property.FindPropertyRelative("SyncType");
SerializedProperty sync = property.FindPropertyRelative("SyncMinutes");
EditorGUI.BeginProperty(rect, label, property);
float x = rect.x;
float y = rect.y;
float w = rect.width;
float h = EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing;
VRC_EditorTools.FilteredEnumPopup<UnityEngine.Video.VideoSource>(new Rect(x, y, w, h), source, (e) => e == UnityEngine.Video.VideoSource.Url);
y += h;
if (source.enumValueIndex == (int)UnityEngine.Video.VideoSource.Url)
{
h = EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), url);
y += h;
}
else
{
h = EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), clip);
y += h;
}
h = EditorGUI.GetPropertyHeight(speed, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), speed);
if (speed.floatValue == 0f)
speed.floatValue = 1f;
y += h;
h = EditorGUI.GetPropertyHeight(live, new GUIContent("SyncType"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), live);
y += h;
h = EditorGUI.GetPropertyHeight(sync, new GUIContent("SyncMinutes"), true) + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(new Rect(x, y, w, h), sync);
if (sync.floatValue < 1f)
sync.floatValue = 0;
y += h;
EditorGUI.EndProperty();
}
}
[CustomEditor(typeof(VRCSDK2.VRC_SyncVideoStream))]
public class SyncVideoStreamEditor : Editor
{
ReorderableList sourceList;
public override void OnInspectorGUI()
{
SerializedProperty searchRoot = serializedObject.FindProperty("VideoSearchRoot");
EditorGUILayout.PropertyField(searchRoot);
SerializedProperty maxQual = serializedObject.FindProperty("MaxStreamQuality");
EditorGUILayout.PropertyField(maxQual);
SerializedProperty autoStart = serializedObject.FindProperty("AutoStart");
EditorGUILayout.PropertyField(autoStart);
EditorGUILayout.Space();
sourceList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
private void OnEnable()
{
SerializedProperty videos = serializedObject.FindProperty("Videos");
sourceList = new ReorderableList(serializedObject, videos);
sourceList.drawElementCallback += (Rect rect, int index, bool active, bool focused) =>
{
EditorGUI.PropertyField(rect, serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index));
};
sourceList.elementHeightCallback += (int index) =>
{
SerializedProperty element = serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index);
return EditorGUI.GetPropertyHeight(element);
};
sourceList.drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, "Videos");
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3f9dccfed0b072f49a307b3f20a7e768
timeCreated: 1528745185
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3aecd666943878944a811acb9db2ace7
timeCreated: 1474315179
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,215 @@
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEditor.Build;
using System;
using ZLinq;
using VRC.SDKBase.Editor;
namespace VRCSDK2
{
[CustomEditor(typeof(VRCSDK2.VRC_WebPanel))]
public class VRC_WebPanelEditor : UnityEditor.Editor
{
private void InspectorField(string propertyName, string humanName)
{
SerializedProperty propertyField = serializedObject.FindProperty(propertyName);
EditorGUILayout.PropertyField(propertyField, new GUIContent(humanName), true);
}
bool showFiles = false;
System.Collections.Generic.List<string> directories = null;
System.Collections.Generic.List<string> files = null;
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.BeginVertical();
EditorGUILayout.HelpBox("Do not play any videos with Web Panels, use VRC_SyncVideoPlayer instead!", MessageType.Error);
EditorGUILayout.Space();
InspectorField("proximity", "Proximity for Interactivity");
EditorGUILayout.Space();
VRCSDK2.VRC_WebPanel web = (VRCSDK2.VRC_WebPanel)target;
if (Application.isPlaying)
{
InspectorField("webRoot", "Web Root");
InspectorField("defaultUrl", "URI");
showFiles = web.webData != null && EditorGUILayout.Foldout(showFiles, web.webData.Count.ToString() + " files imported");
if (showFiles)
foreach (var file in web.webData)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(file.path);
EditorGUILayout.LabelField(file.data.Length.ToString());
EditorGUILayout.EndHorizontal();
}
}
else
{
SerializedProperty webRoot = serializedObject.FindProperty("webRoot");
RenderDirectoryList(serializedObject, "webRoot", "Path To Web Content");
if (string.IsNullOrEmpty(webRoot.stringValue))
{
InspectorField("defaultUrl", "Start URI");
}
else
{
RenderWebRootSelector(serializedObject, "defaultUrl", "Start Page");
if (VRCSettings.DisplayHelpBoxes)
{
EditorGUILayout.HelpBox("Javascript API bindings are called with engine.call('methodName', ...), which returns a promise-like object.", MessageType.Info);
EditorGUILayout.HelpBox("Javascript may call ListBindings() to discover available API bindings.", MessageType.Info);
EditorGUILayout.HelpBox("Javascript may listen for the 'onBindingsReady' event to execute script when the page is fully loaded and API bindings are available.", MessageType.Info);
}
}
}
EditorGUILayout.Space();
InspectorField("cookiesEnabled", "Enable Cookies");
InspectorField("interactive", "Is Interactive");
InspectorField("localOnly", "Only Visible Locally");
if (!web.localOnly)
{
InspectorField("syncURI", "Synchronize URI");
InspectorField("syncInput", "Synchronize Mouse Position");
}
InspectorField("transparent", "Transparent Background");
InspectorField("autoFormSubmit", "Input should Submit Forms");
EditorGUILayout.Space();
InspectorField("station", "Interaction Station");
EditorGUILayout.Space();
InspectorField("cursor", "Mouse Cursor Object");
EditorGUILayout.Space();
InspectorField("resolutionWidth", "Resolution Width");
InspectorField("resolutionHeight", "Resolution Height");
InspectorField("displayRegion", "Display Region");
EditorGUILayout.Space();
InspectorField("extraVideoScreens", "Duplicate Screens");
EditorGUILayout.EndVertical();
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
}
private void AddSubDirectories(ref System.Collections.Generic.List<string> l, string root)
{
if (!Directory.Exists(root))
{
return;
}
if (!root.StartsWith(Application.dataPath + Path.DirectorySeparatorChar + "VRCSDK")
|| root == Application.dataPath + Path.DirectorySeparatorChar + "VRCSDK" + Path.DirectorySeparatorChar + "Examples" + Path.DirectorySeparatorChar + "Sample Assets" + Path.DirectorySeparatorChar + "WebRoot")
l.Add(root.Substring(Application.dataPath.Length));
string[] subdirectories = Directory.GetDirectories(root);
foreach (string dir in subdirectories)
AddSubDirectories(ref l, dir);
}
private void RenderDirectoryList(SerializedObject obj, string propertyName, string humanName)
{
if (directories == null)
{
directories = new System.Collections.Generic.List<string>();
directories.Add("No Web Content Directory");
AddSubDirectories(ref directories, Application.dataPath + Path.DirectorySeparatorChar);
}
SerializedProperty target = serializedObject.FindProperty(propertyName);
int selectedIdx = target.stringValue == null ? 0 : directories.IndexOf(target.stringValue);
if (selectedIdx < 0 || selectedIdx >= directories.Count)
selectedIdx = 0;
selectedIdx = EditorGUILayout.Popup(humanName, selectedIdx, directories.ToArray());
if (selectedIdx > 0 && selectedIdx < directories.Count)
target.stringValue = directories[selectedIdx];
else
target.stringValue = null;
}
private void AddSubDirectoryFiles(ref System.Collections.Generic.List<string> l, string root)
{
if (!Directory.Exists(root))
return;
string[] files = Directory.GetFiles(root);
foreach (string file in files.Where(f => f.ToLower().EndsWith(".html") || f.ToLower().EndsWith(".htm")))
l.Add(file.Substring(Application.dataPath.Length));
string[] subdirectories = Directory.GetDirectories(root);
foreach (string dir in subdirectories)
AddSubDirectoryFiles(ref l, dir);
}
private void RenderWebRootSelector(SerializedObject obj, string propertyName, string humanName)
{
SerializedProperty webRoot = obj.FindProperty("webRoot");
SerializedProperty target = serializedObject.FindProperty(propertyName);
if (files == null)
{
files = new System.Collections.Generic.List<string>();
AddSubDirectoryFiles(ref files, Application.dataPath + webRoot.stringValue);
if (files.Count == 0)
{
EditorGUILayout.HelpBox("No suitable html files found in Web Content path.", MessageType.Error);
return;
}
}
int selectedIdx = 0;
try
{
System.Uri uri = string.IsNullOrEmpty(target.stringValue) ? null : new Uri(target.stringValue);
selectedIdx = uri == null ? 0 : files.IndexOf(uri.AbsolutePath.Replace('/', System.IO.Path.DirectorySeparatorChar));
if (selectedIdx < 0 || selectedIdx >= files.Count)
selectedIdx = 0;
}
catch { }
selectedIdx = EditorGUILayout.Popup(humanName, selectedIdx, files.ToArray());
if (selectedIdx >= 0 && selectedIdx < files.Count)
{
System.UriBuilder builder = new UriBuilder()
{
Scheme = "file",
Path = files[selectedIdx].Replace(System.IO.Path.DirectorySeparatorChar, '/'),
Host = ""
};
target.stringValue = builder.Uri.ToString();
}
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d09b36020f697be4d9a0f5a6a48cfa83
timeCreated: 1457992191
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
#if UNITY_EDITOR && VRC_SDK_VRCSDK2
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
namespace VRCSDK2
{
[CustomEditor(typeof(VRC_YouTubeSync))]
public class VRC_YouTubeSyncEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("This component is deprecated, please use the VRC_SyncVideoPlayer component instead.", MessageType.Error);
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 764e26c1ca28e2e45a30c778c1955a47
timeCreated: 1474675311
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e751dcaa3656a324f91244e7b795d83a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,36 @@

using System;
using UnityEngine;
using UnityEngine.UIElements;
// Common interface used by all builder panels inside their respective packages
/// <summary>
/// This interface is reserved for SDK use, refer to Interfaces inside the Public SDK API folder for public APIs
/// </summary>
public interface IVRCSdkControlPanelBuilder
{
void Initialize();
void ShowSettingsOptions();
bool IsValidBuilder(out string message);
void CreateBuilderErrorGUI(VisualElement root);
void CreateValidationsGUI(VisualElement root);
EventHandler OnContentChanged { get; set; }
EventHandler OnShouldRevalidate { get; set; }
void RegisterBuilder(VRCSdkControlPanel baseBuilder);
void SelectAllComponents();
void CreateContentInfoGUI(VisualElement root);
void CreateBuildGUI(VisualElement root);
/// <summary>
/// Returns the image to show within the Builder tab. If no image is provided - the default image is used
/// </summary>
/// <returns></returns>
Texture2D GetHeaderImage()
{
return null;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 310a760e312f2984e85eece367bab19a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,511 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using VRC.Core;
using VRC.Editor;
using VRC.SDKBase.Editor;
[assembly:InternalsVisibleTo("VRC.ExampleCentral.Editor")]
/// This class sets up the basic panel layout and draws the main tabs
/// Implementation of each tab is handled within other files extending this partial class
[ExecuteInEditMode]
public partial class VRCSdkControlPanel : EditorWindow, IVRCSdkPanelApi
{
public static VRCSdkControlPanel window;
[MenuItem("VRChat SDK/Show Control Panel", false, 600)]
internal static void ShowControlPanel()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
VRC.Core.API.SetOnlineMode(true);
ConfigManager.RemoteConfig.Init(() => ShowControlPanel());
return;
}
GetWindow(typeof(VRCSdkControlPanel));
window.titleContent.text = "VRChat SDK";
window.minSize = new Vector2(SdkWindowWidth + 4, 450);
window.maxSize = new Vector2(SdkWindowWidth + 4, 2000);
window.Init();
window.Show();
}
public VRCSdkControlPanel()
{
window = this;
}
#region IMGUI Init
public static GUIStyle titleGuiStyle;
public static GUIStyle boxGuiStyle;
public static GUIStyle infoGuiStyle;
public static GUIStyle listButtonStyleEven;
public static GUIStyle listButtonStyleOdd;
public static GUIStyle listButtonStyleSelected;
public static GUIStyle scrollViewSeparatorStyle;
public static GUIStyle searchBarStyle;
public static GUIStyle accountWindowStyle;
public static GUIStyle centeredLabelStyle;
public static GUIStyle contentDescriptionStyle;
public static GUIStyle contentTitleStyle;
public static GUIStyle unityUpgradeBannerStyle;
void InitializeStyles()
{
titleGuiStyle = new GUIStyle();
titleGuiStyle.fontSize = 15;
titleGuiStyle.fontStyle = FontStyle.BoldAndItalic;
titleGuiStyle.alignment = TextAnchor.MiddleCenter;
titleGuiStyle.wordWrap = true;
if (EditorGUIUtility.isProSkin)
titleGuiStyle.normal.textColor = Color.white;
else
titleGuiStyle.normal.textColor = Color.black;
boxGuiStyle = new GUIStyle
{
padding = new RectOffset(5,5,5,5)
};
if (EditorGUIUtility.isProSkin)
{
boxGuiStyle.normal.background = CreateBackgroundColorImage(new Color(0.3f, 0.3f, 0.3f));
boxGuiStyle.normal.textColor = Color.white;
}
else
{
boxGuiStyle.normal.background = CreateBackgroundColorImage(new Color(0.85f, 0.85f, 0.85f));
boxGuiStyle.normal.textColor = Color.black;
}
infoGuiStyle = new GUIStyle();
infoGuiStyle.wordWrap = true;
if (EditorGUIUtility.isProSkin)
infoGuiStyle.normal.textColor = Color.white;
else
infoGuiStyle.normal.textColor = Color.black;
infoGuiStyle.margin = new RectOffset(10, 10, 10, 10);
listButtonStyleEven = new GUIStyle();
listButtonStyleEven.margin = new RectOffset(0, 0, 0, 0);
listButtonStyleEven.border = new RectOffset(0, 0, 0, 0);
if (EditorGUIUtility.isProSkin)
{
listButtonStyleEven.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
listButtonStyleEven.normal.background = CreateBackgroundColorImage(new Color(0.540f, 0.540f, 0.54f));
}
else
{
listButtonStyleEven.normal.textColor = Color.black;
listButtonStyleEven.normal.background = CreateBackgroundColorImage(new Color(0.85f, 0.85f, 0.85f));
}
listButtonStyleOdd = new GUIStyle();
listButtonStyleOdd.margin = new RectOffset(0, 0, 0, 0);
listButtonStyleOdd.border = new RectOffset(0, 0, 0, 0);
if (EditorGUIUtility.isProSkin)
{
listButtonStyleOdd.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
//listButtonStyleOdd.normal.background = CreateBackgroundColorImage(new Color(0.50f, 0.50f, 0.50f));
}
else
{
listButtonStyleOdd.normal.textColor = Color.black;
listButtonStyleOdd.normal.background = CreateBackgroundColorImage(new Color(0.90f, 0.90f, 0.90f));
}
listButtonStyleSelected = new GUIStyle();
listButtonStyleSelected.normal.textColor = Color.white;
listButtonStyleSelected.margin = new RectOffset(0, 0, 0, 0);
if (EditorGUIUtility.isProSkin)
{
listButtonStyleSelected.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
listButtonStyleSelected.normal.background = CreateBackgroundColorImage(new Color(0.4f, 0.4f, 0.4f));
}
else
{
listButtonStyleSelected.normal.textColor = Color.black;
listButtonStyleSelected.normal.background = CreateBackgroundColorImage(new Color(0.75f, 0.75f, 0.75f));
}
scrollViewSeparatorStyle = new GUIStyle("Toolbar");
scrollViewSeparatorStyle.fixedWidth = SdkWindowWidth + 10;
scrollViewSeparatorStyle.fixedHeight = 4;
scrollViewSeparatorStyle.margin.top = 1;
searchBarStyle = new GUIStyle("Toolbar");
searchBarStyle.fixedWidth = SdkWindowWidth - 8;
searchBarStyle.fixedHeight = 23;
searchBarStyle.padding.top = 3;
accountWindowStyle = new GUIStyle("window")
{
padding = new RectOffset(10, 10, 10, 10),
margin = new RectOffset(0,0,30,30)
};
centeredLabelStyle = new GUIStyle(EditorStyles.boldLabel)
{
alignment = TextAnchor.UpperCenter,
margin = new RectOffset(0,0,0,10)
};
contentDescriptionStyle = new GUIStyle(EditorStyles.wordWrappedLabel)
{
wordWrap = true
};
contentTitleStyle = new GUIStyle(EditorStyles.boldLabel)
{
wordWrap = true
};
unityUpgradeBannerStyle = new GUIStyle
{
normal = new GUIStyleState
{
background = Resources.Load<Texture2D>("vrcSdkMigrateTo2022Splash")
},
alignment = TextAnchor.LowerCenter,
margin = new RectOffset(0,0,20,0),
fixedWidth = 506,
fixedHeight = 148
};
}
#endregion
private void Init()
{
InitializeStyles();
ResetIssues();
InitAccount();
}
private void OnEnable()
{
OnEnableAccount();
_stylesInitialized = false;
OnPanelLoggedIn -= RestoreTab;
OnPanelLoggedIn += RestoreTab;
OnUserPlatformsFetched -= RefreshPlatformSwitcher;
OnUserPlatformsFetched += RefreshPlatformSwitcher;
AssemblyReloadEvents.afterAssemblyReload -= BuilderAssemblyReload;
AssemblyReloadEvents.afterAssemblyReload += BuilderAssemblyReload;
OnSdkPanelEnable?.Invoke(this, null);
_panelState = SdkPanelState.Idle;
OnSdkPanelStateChange?.Invoke(this, _panelState);
TabsEnabled = true;
}
private void OnDisable()
{
OnPanelLoggedIn -= RestoreTab;
OnUserPlatformsFetched -= RefreshPlatformSwitcher;
AssemblyReloadEvents.afterAssemblyReload -= BuilderAssemblyReload;
OnSdkPanelDisable?.Invoke(this, null);
_panelState = SdkPanelState.Idle;
OnSdkPanelStateChange?.Invoke(this, _panelState);
}
private void RestoreTab(object sender, APIUser e)
{
rootVisualElement.schedule.Execute(() =>
{
SelectTab(PanelTab.Builder);
}).ExecuteLater(200);
}
private void OnDestroy()
{
AccountDestroy();
}
public const int SdkWindowWidth = 512;
private readonly bool[] _toolbarOptionsLoggedIn = new bool[4] {true, true, true, true};
private readonly bool[] _toolbarOptionsNotLoggedIn = new bool[4] {true, false, false, true};
private bool _stylesInitialized;
private SdkPanelState _panelState = SdkPanelState.Idle;
private Button _authenticationTabBtn;
private Button _buildTabBtn;
private Button _contentManagerTabBtn;
private Button _settingsTabBtn;
private Button[] _tabButtons;
private VisualElement _sdkPanel;
private VisualElement _builderPanel;
private VisualTreeAsset _builderPanelLayout;
private StyleSheet _builderPanelStyles;
private float _windowOpenTime;
private bool _tabsEnabled;
internal bool TabsEnabled
{
get => _tabsEnabled;
set
{
_tabsEnabled = value;
if (_tabButtons != null)
{
foreach (var button in _tabButtons)
{
button.SetEnabled(value);
}
}
}
}
internal enum PanelTab
{
Account,
Builder,
ContentManager,
Settings
}
private void CreateGUI()
{
if (window == null)
{
window = (VRCSdkControlPanel)EditorWindow.GetWindow(typeof(VRCSdkControlPanel));
}
_windowOpenTime = Time.realtimeSinceStartup;
var visualTree = Resources.Load<VisualTreeAsset>("VRCSdkPanelLayout");
visualTree.CloneTree(rootVisualElement);
var styles = Resources.Load<StyleSheet>("VRCSdkPanelStyles");
rootVisualElement.styleSheets.Add(styles);
rootVisualElement.AddToClassList(EditorGUIUtility.isProSkin ? "dark" : "light");
_sdkPanel = rootVisualElement.Q("sdk-container");
_builderPanel = rootVisualElement.Q("builder-panel");
CreateTabs();
RenderTabs();
rootVisualElement.schedule.Execute(() =>
{
var currentPanel = VRCSettings.ActiveWindowPanel;
if (EditorApplication.isPlaying && currentPanel != 0)
{
SelectTab(PanelTab.Account);
return;
}
// Check that the tabs are enabled, if not - we must re-render tabs
if (APIUser.IsLoggedIn && (!_tabButtons[1].enabledSelf || !_tabButtons[2].enabledSelf))
{
RenderTabs();
return;
}
// When the user isn't logged in - we only allow Settings and Authentication tabs to be viewed
if (APIUser.IsLoggedIn || currentPanel == 0 || currentPanel == 3) return;
SelectTab(PanelTab.Account);
}).Every(500);
var sdkContainer = rootVisualElement.Q("sdk-container");
sdkContainer.Add(new IMGUIContainer(() =>
{
if (!_stylesInitialized)
{
InitializeStyles();
_stylesInitialized = true;
}
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
if (Application.isPlaying)
{
GUI.enabled = false;
GUILayout.Space(20);
EditorGUILayout.LabelField("Unity Application is running ...\nStop it to access the Control Panel", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
GUI.enabled = true;
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
return;
}
EditorGUILayout.Space();
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
switch ((PanelTab) VRCSettings.ActiveWindowPanel)
{
case PanelTab.Builder:
break;
case PanelTab.ContentManager:
ShowContent();
break;
case PanelTab.Settings:
ShowSettings();
break;
case PanelTab.Account:
default:
ShowAccount();
break;
}
}));
EnvConfig.SetActiveSDKDefines();
}
private void CreateTabs()
{
_authenticationTabBtn = rootVisualElement.Q<Button>("tab-authentication");
_buildTabBtn = rootVisualElement.Q<Button>("tab-builder");
_contentManagerTabBtn = rootVisualElement.Q<Button>("tab-content-manager");
_settingsTabBtn = rootVisualElement.Q<Button>("tab-settings");
_tabButtons = new[]
{
_authenticationTabBtn,
_buildTabBtn,
_contentManagerTabBtn,
_settingsTabBtn
};
var currentPanel = VRCSettings.ActiveWindowPanel;
for (int i = 0; i < _tabButtons.Length; i++)
{
var btnIndex = i;
_tabButtons[i].EnableInClassList("active", currentPanel == btnIndex);
_tabButtons[i].SetEnabled(APIUser.IsLoggedIn ? _toolbarOptionsLoggedIn[i] : _toolbarOptionsNotLoggedIn[i]);
_tabButtons[i].clicked += () => SelectTab((PanelTab) btnIndex);
}
}
private void SelectTab(PanelTab tab)
{
if (_tabButtons == null) return;
if (EditorApplication.isPlaying) return;
if (VRCSettings.ActiveWindowPanel == (int) tab)
{
return;
}
VRCSettings.ActiveWindowPanel = (int) tab;
RenderTabs();
}
internal PanelTab CurrentTab => (PanelTab) VRCSettings.ActiveWindowPanel;
private void RenderTabs()
{
var currentPanel = (PanelTab) VRCSettings.ActiveWindowPanel;
if (currentPanel != PanelTab.Builder)
{
if (_defaultHeaderImage == null)
{
_defaultHeaderImage = Resources.Load<Texture2D>("SDK_Panel_Banner");
}
rootVisualElement.Q("banner").style.backgroundImage = _defaultHeaderImage;
}
for (int i = 0; i < _tabButtons.Length; i++)
{
_tabButtons[i].EnableInClassList("active", currentPanel == (PanelTab) i);
if (!TabsEnabled) continue;
_tabButtons[i].SetEnabled(APIUser.IsLoggedIn ? _toolbarOptionsLoggedIn[i] : _toolbarOptionsNotLoggedIn[i]);
}
if (currentPanel == PanelTab.Builder)
{
if (_builderPanel.childCount != 0) return;
ShowBuilders();
_builderPanel.RemoveFromClassList("d-none");
_sdkPanel.AddToClassList("d-none");
}
else if (_builderPanel.childCount == 1)
{
_builderPanel.AddToClassList("d-none");
_sdkPanel.RemoveFromClassList("d-none");
_builderPanel.Remove(_builderPanel.Children().First());
_builderPanel.styleSheets.Remove(_builderPanelStyles);
}
}
[UnityEditor.Callbacks.PostProcessScene]
static void OnPostProcessScene()
{
if (window != null)
window.Reset();
}
private void OnFocus()
{
Reset();
}
public void Reset()
{
ResetIssues();
// style backgrounds may be nulled on scene load. detect if so has happened
if((boxGuiStyle != null) && (boxGuiStyle.normal.background == null))
InitializeStyles();
}
[UnityEditor.Callbacks.DidReloadScripts(int.MaxValue)]
static void DidReloadScripts()
{
try
{
RefreshApiUrlSetting();
}
catch(Exception e)
{
//Unity's Mono is trash and randomly fails to assemblies types.
Debug.LogException(e);
}
}
#region Internal API
[UsedImplicitly]
internal void SetPanelIdle()
{
_panelState = SdkPanelState.Idle;
OnSdkPanelStateChange?.Invoke(this, _panelState);
}
[UsedImplicitly]
internal void SetPanelBuilding()
{
_panelState = SdkPanelState.Building;
OnSdkPanelStateChange?.Invoke(this, _panelState);
}
[UsedImplicitly]
internal void SetPanelUploading()
{
_panelState = SdkPanelState.Uploading;
OnSdkPanelStateChange?.Invoke(this, _panelState);
}
#endregion
#region Public API
public static event EventHandler OnSdkPanelEnable;
public static event EventHandler OnSdkPanelDisable;
public static event EventHandler<SdkPanelState> OnSdkPanelStateChange;
public SdkPanelState PanelState => _panelState;
#endregion
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 20b4cdbdda9655947aab6f8f2c90690f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,686 @@
using System;
using UnityEngine;
using UnityEditor;
using VRC.Core;
using System.Text.RegularExpressions;
using UnityEngine.UIElements;
using VRC.SDKBase.Editor;
public enum TwoFactorType
{
None,
TOTP,
Email,
}
// This file handles the Account tab of the SDK Panel
public partial class VRCSdkControlPanel : EditorWindow
{
static bool isInitialized = false;
static string clientInstallPath;
static bool signingIn = false;
static double latestSignInTime = -1.0d;
static string error = null;
private const string UNITY_UPGRADE_PROMPT_URL = "https://creators.vrchat.com/sdk/upgrade/unity-2022";
private const double LogoutCooldownAfterLogin = 0.75d;
public static bool FutureProofPublishEnabled { get { return UnityEditor.EditorPrefs.GetBool("futureProofPublish", DefaultFutureProofPublishEnabled); } }
//public static bool DefaultFutureProofPublishEnabled { get { return !SDKClientUtilities.IsInternalSDK(); } }
public static bool DefaultFutureProofPublishEnabled { get { return false; } }
internal static event EventHandler<APIUser> OnPanelLoggedIn;
internal static event EventHandler OnPanelLoggedOut;
internal static event EventHandler<ApiUserPlatforms> OnUserPlatformsFetched;
static string storedUsername
{
get
{
return null;
}
set
{
EditorPrefs.DeleteKey("sdk#username");
}
}
static string storedPassword
{
get
{
return null;
}
set
{
EditorPrefs.DeleteKey("sdk#password");
}
}
static string username { get; set; } = null;
static string password { get; set; } = null;
public static ApiServerEnvironment ApiEnvironment => serverEnvironment;
static ApiServerEnvironment serverEnvironment
{
get
{
ApiServerEnvironment env = ApiServerEnvironment.Release;
try
{
env = (ApiServerEnvironment)System.Enum.Parse(typeof(ApiServerEnvironment), UnityEditor.EditorPrefs.GetString("VRC_ApiServerEnvironment", env.ToString()));
}
catch (System.Exception e)
{
Debug.LogError("Invalid server environment name - " + e.ToString());
}
return env;
}
set
{
UnityEditor.EditorPrefs.SetString("VRC_ApiServerEnvironment", value.ToString());
API.SetApiUrlFromEnvironment(value);
}
}
private void OnEnableAccount()
{
entered2faCodeIsInvalid = false;
warningIconGraphic = Resources.Load("2FAIcons/SDK_Warning_Triangle_icon") as Texture2D;
}
public static void RefreshApiUrlSetting()
{
// this forces the static api url variable to be reset from the server environment set in editor prefs.
// needed because the static variable states get cleared when entering / exiting play mode
ApiServerEnvironment env = serverEnvironment;
serverEnvironment = env;
}
public static void InitAccount()
{
if (isInitialized)
return;
if (!APIUser.IsLoggedIn && ApiCredentials.Load())
{
APIUser.InitialFetchCurrentUser(c =>
{
window.rootVisualElement.Q<IMGUIContainer>().MarkDirtyRepaint();
if (c.Model is not APIUser apiUser)
{
VRC.Core.Logger.LogError("Failed to load user information, please log in again");
return;
}
AnalyticsSDK.LoggedInUserChanged(apiUser);
OnPanelLoggedIn?.Invoke(window, apiUser);
ApiUserPlatforms.Fetch(apiUser.id, userPlatforms =>
{
OnUserPlatformsFetched?.Invoke(window, userPlatforms);
}, null);
}, null);
}
// This code proceeds without waiting for the user fetch above to complete
clientInstallPath = SDKClientUtilities.GetSavedVRCInstallPath();
if (string.IsNullOrEmpty(clientInstallPath))
clientInstallPath = SDKClientUtilities.LoadRegistryVRCInstallPath();
signingIn = false;
isInitialized = true;
ClearContent();
}
void OnAccountGUI()
{
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
AccountWindowGUI();
GUILayout.FlexibleSpace();
}
}
void AccountWindowGUI()
{
using (new EditorGUILayout.VerticalScope(accountWindowStyle, GUILayout.Width(340)))
{
EditorGUILayout.LabelField("Account", centeredLabelStyle);
if (signingIn)
{
if (twoFactorAuthenticationEntryType == TwoFactorType.None)
{
EditorGUILayout.LabelField("Signing in as " + username + ".");
}
OnTwoFactorAuthenticationGUI(twoFactorAuthenticationEntryType);
return;
}
if (APIUser.IsLoggedIn)
{
if (Status != "Connected")
{
EditorGUILayout.LabelField(Status);
}
OnCreatorStatusGUI();
// Add a space, pushing this away from the line that contained "Verify" on the previous page.
EditorGUILayout.Space(EditorGUIUtility.singleLineHeight);
// Disable the logout button for a short amount of time after logging in.
// This attempts to patch UX difficulties where users try to click "Verify" on the previous screen as
// 2FA automatically submits so they end up accidentally clicking Logout instead.
bool wasRecentLogin = latestSignInTime >= 0.0d && EditorApplication.timeSinceStartup < latestSignInTime + LogoutCooldownAfterLogin;
using (new EditorGUI.DisabledScope(wasRecentLogin))
{
if (GUILayout.Button("Logout"))
{
storedUsername = username = null;
storedPassword = password = null;
VRC.Tools.ClearCookies();
APIUser.Logout();
ClearContent();
OnPanelLoggedOut?.Invoke(window, EventArgs.Empty);
}
}
return;
}
InitAccount();
ApiServerEnvironment newEnv = ApiServerEnvironment.Release;
if (VRCSettings.DisplayAdvancedSettings)
newEnv = (ApiServerEnvironment)EditorGUILayout.EnumPopup("Use API", serverEnvironment);
if (serverEnvironment != newEnv)
serverEnvironment = newEnv;
const string controlNameUser = "input_user";
const string controlNamePass = "input_pass";
GUI.SetNextControlName(controlNameUser);
username = EditorGUILayout.TextField("Username/Email", username);
GUI.SetNextControlName(controlNamePass);
password = EditorGUILayout.PasswordField("Password", password);
bool attemptKeyboardSignIn = false;
if (
Event.current != null &&
Event.current.type == EventType.KeyUp &&
(Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter))
{
switch (GUI.GetNameOfFocusedControl())
{
case controlNameUser:
GUI.FocusControl(controlNamePass);
break;
case controlNamePass:
attemptKeyboardSignIn = true;
break;
}
}
if (GUILayout.Button("Sign In") || attemptKeyboardSignIn)
{
SignIn(true);
}
if (GUILayout.Button("Sign up"))
{
Application.OpenURL("https://vrchat.com/register");
}
}
}
static void OnCreatorStatusGUI()
{
EditorGUILayout.LabelField("Logged in as:", APIUser.CurrentUser.displayName);
//if (SDKClientUtilities.IsInternalSDK())
// EditorGUILayout.LabelField("Developer Status: ", APIUser.CurrentUser.developerType.ToString());
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField("World Creator Status: ", APIUser.CurrentUser.canPublishWorlds ? "Allowed to publish worlds" : "Not yet allowed to publish worlds");
EditorGUILayout.LabelField("Avatar Creator Status: ", APIUser.CurrentUser.canPublishAvatars ? "Allowed to publish avatars" : "Not yet allowed to publish avatars");
EditorGUILayout.EndVertical();
if (!APIUser.CurrentUser.canPublishWorldsAndAvatars)
{
if (GUILayout.Button("More Info..."))
{
ShowContentPublishPermissionsDialog();
}
}
EditorGUILayout.EndHorizontal();
}
void ShowAccount()
{
if (ConfigManager.RemoteConfig.IsInitialized())
{
if (ConfigManager.RemoteConfig.HasKey("sdkUnityVersion"))
{
string sdkUnityVersion = ConfigManager.RemoteConfig.GetString("sdkUnityVersion");
if (string.IsNullOrEmpty(sdkUnityVersion))
EditorGUILayout.LabelField("Could not fetch remote config.");
else if (Application.unityVersion != sdkUnityVersion)
{
var currentVersion = UnityVersion.Parse(Application.unityVersion).major;
var sdkVersion = UnityVersion.Parse(sdkUnityVersion).major;
if (currentVersion < sdkVersion && sdkVersion == 2022)
{
using (new EditorGUILayout.VerticalScope(unityUpgradeBannerStyle))
{
EditorGUILayout.Space(115);
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.Space();
if (GUILayout.Button("Learn More"))
{
Application.OpenURL(UNITY_UPGRADE_PROMPT_URL);
}
EditorGUILayout.Space();
}
EditorGUILayout.Space(5);
}
}
else
{
EditorGUILayout.LabelField("Unity Version", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Wrong Unity version. Please use " + sdkUnityVersion);
}
}
}
}
else
{
API.SetOnlineMode(true);
ConfigManager.RemoteConfig.Init();
}
OnAccountGUI();
}
private const string TWO_FACTOR_AUTHENTICATION_HELP_URL = "https://docs.vrchat.com/docs/setup-2fa";
private const string ENTER_2FA_CODE_TITLE_STRING = "Enter a numeric code from your authenticator app.";
private const string ENTER_2FA_CODE_LABEL_STRING = "Code:";
private const string ENTER_2FA_CODE_GUI_EVENT = "Authentication Code Field";
private const string ENTER_EMAIL_2FA_CODE_TITLE_STRING = "Check your email for a numeric code.";
private const string CHECKING_2FA_CODE_STRING = "Checking code...";
private const string ENTER_2FA_CODE_INVALID_CODE_STRING = "Oops, that code didn't work.\nTry again!";
private const string ENTER_2FA_CODE_VERIFY_STRING = "Verify";
private const string ENTER_2FA_CODE_CANCEL_STRING = "Cancel";
private const string ENTER_2FA_CODE_HELP_STRING = "Help";
private const int WARNING_ICON_SIZE = 60;
private const int WARNING_FONT_HEIGHT = 14;
static private Texture2D warningIconGraphic;
static bool entered2faCodeIsInvalid;
static bool authorizationCodeWasVerified;
static private int previousAuthenticationCodeLength = 0;
static bool checkingCode;
static string authenticationCode = "";
static string lastCheckedAuthenticationCode = "";
static System.Action onAuthenticationVerifiedAction;
static TwoFactorType _twoFactorAuthenticationEntryType = TwoFactorType.None;
static TwoFactorType twoFactorAuthenticationEntryType
{
get
{
return _twoFactorAuthenticationEntryType;
}
set
{
_twoFactorAuthenticationEntryType = value;
authenticationCode = "";
lastCheckedAuthenticationCode = "";
if (_twoFactorAuthenticationEntryType == TwoFactorType.None && !authorizationCodeWasVerified)
Logout();
}
}
static bool IsValidAuthenticationCodeFormat()
{
bool isValid2faAuthenticationCode = false;
if (!string.IsNullOrEmpty(authenticationCode))
{
// check if the input is a valid 6-digit numberic code (ignoring spaces)
Regex rx = new Regex(@"^(\s*\d\s*){6}$", RegexOptions.Compiled);
MatchCollection matches6DigitCode = rx.Matches(authenticationCode);
isValid2faAuthenticationCode = (matches6DigitCode.Count == 1);
}
return isValid2faAuthenticationCode;
}
static bool IsValidRecoveryCodeFormat()
{
bool isValid2faRecoveryCode = false;
if (!string.IsNullOrEmpty(authenticationCode))
{
// check if the input is a valid 8-digit alpha-numberic code (format xxxx-xxxx) "-" is optional & ignore any spaces
// OTP codes also exclude the letters i,l,o and the digit 1 to prevent any confusion
Regex rx = new Regex(@"^(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}-?(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}$", RegexOptions.Compiled);
MatchCollection matchesRecoveryCode = rx.Matches(authenticationCode);
isValid2faRecoveryCode = (matchesRecoveryCode.Count == 1);
}
return isValid2faRecoveryCode;
}
static void OnTwoFactorAuthenticationGUI(TwoFactorType twoFactorType)
{
if (twoFactorType == TwoFactorType.None)
return;
const int ENTER_2FA_CODE_BORDER_SIZE = 20;
const int ENTER_2FA_CODE_BUTTON_WIDTH = 260;
const int ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH = ENTER_2FA_CODE_BUTTON_WIDTH / 2;
const int ENTER_2FA_CODE_ENTRY_REGION_WIDTH = 130;
const int ENTER_2FA_CODE_MIN_WINDOW_WIDTH = ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH + ENTER_2FA_CODE_ENTRY_REGION_WIDTH + (ENTER_2FA_CODE_BORDER_SIZE * 3);
bool isValidAuthenticationCode = IsValidAuthenticationCodeFormat();
// Invalid code text
if (entered2faCodeIsInvalid)
{
GUIStyle s = new GUIStyle(EditorStyles.label)
{
normal =
{
textColor = new Color(1,0.3f,0.3f)
},
fontSize = WARNING_FONT_HEIGHT,
fixedHeight = WARNING_ICON_SIZE
};
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
using (new EditorGUILayout.VerticalScope())
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.Label(new GUIContent(warningIconGraphic), GUILayout.Width(WARNING_ICON_SIZE), GUILayout.Height(WARNING_ICON_SIZE));
EditorGUILayout.LabelField(ENTER_2FA_CODE_INVALID_CODE_STRING, s);
}
}
GUILayout.FlexibleSpace();
}
}
else if (checkingCode)
{
// Display checking code message
EditorGUILayout.BeginVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
GUIStyle s = new GUIStyle(EditorStyles.label);
s.alignment = TextAnchor.MiddleCenter;
s.fixedHeight = WARNING_ICON_SIZE;
EditorGUILayout.LabelField(CHECKING_2FA_CODE_STRING, s, GUILayout.Height(WARNING_ICON_SIZE));
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
else
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
GUILayout.FlexibleSpace();
GUIStyle titleStyle = new GUIStyle(EditorStyles.label);
titleStyle.alignment = TextAnchor.MiddleCenter;
titleStyle.wordWrap = true;
string twofactorTitle = "Enter Code";
switch (twoFactorType)
{
case TwoFactorType.TOTP:
twofactorTitle = ENTER_2FA_CODE_TITLE_STRING;
break;
case TwoFactorType.Email:
twofactorTitle = ENTER_EMAIL_2FA_CODE_TITLE_STRING;
break;
}
EditorGUILayout.LabelField(twofactorTitle, titleStyle, GUILayout.Width(ENTER_2FA_CODE_MIN_WINDOW_WIDTH - (2 * ENTER_2FA_CODE_BORDER_SIZE)), GUILayout.Height(WARNING_ICON_SIZE), GUILayout.ExpandHeight(true));
GUILayout.FlexibleSpace();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
GUILayout.FlexibleSpace();
Vector2 size = EditorStyles.boldLabel.CalcSize(new GUIContent(ENTER_2FA_CODE_LABEL_STRING));
EditorGUILayout.LabelField(ENTER_2FA_CODE_LABEL_STRING, EditorStyles.boldLabel, GUILayout.MaxWidth(size.x));
authenticationCode = EditorGUILayout.TextField(authenticationCode);
// Verify 2FA code button
if (lastCheckedAuthenticationCode != authenticationCode && IsValidAuthenticationCodeFormat()
|| GUILayout.Button(ENTER_2FA_CODE_VERIFY_STRING, GUILayout.Width(ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH)))
{
lastCheckedAuthenticationCode = authenticationCode;
checkingCode = true;
string authCodeType = API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION;
switch (twoFactorType)
{
case TwoFactorType.TOTP:
authCodeType = API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION;
break;
case TwoFactorType.Email:
authCodeType = API2FA.EMAIL_BASED_ONE_TIME_PASSWORD_AUTHENTICATION;
break;
}
APIUser.VerifyTwoFactorAuthCode(authenticationCode, authCodeType, username, password,
delegate
{
// valid 2FA code submitted
entered2faCodeIsInvalid = false;
authorizationCodeWasVerified = true;
checkingCode = false;
twoFactorAuthenticationEntryType = TwoFactorType.None;
if (null != onAuthenticationVerifiedAction)
onAuthenticationVerifiedAction();
},
delegate
{
entered2faCodeIsInvalid = true;
checkingCode = false;
}
);
}
GUILayout.FlexibleSpace();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
// after user has entered an invalid code causing the invalid code message to be displayed,
// edit the code will change it's length meaning it is invalid format, so we can clear the invalid code setting until they resubmit
if (previousAuthenticationCodeLength != authenticationCode.Length)
{
previousAuthenticationCodeLength = authenticationCode.Length;
entered2faCodeIsInvalid = false;
}
GUI.enabled = true;
GUILayout.FlexibleSpace();
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
// Two-Factor Authentication Help button
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(ENTER_2FA_CODE_HELP_STRING))
{
Application.OpenURL(TWO_FACTOR_AUTHENTICATION_HELP_URL);
}
EditorGUILayout.EndHorizontal();
// Cancel button
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(ENTER_2FA_CODE_CANCEL_STRING))
{
twoFactorAuthenticationEntryType = TwoFactorType.None;
Logout();
}
EditorGUILayout.EndHorizontal();
}
private static string Status
{
get
{
if (!APIUser.IsLoggedIn)
return error == null ? "Please log in." : "Error in authenticating: " + error;
if (signingIn)
return "Logging in.";
else
{
if( serverEnvironment == ApiServerEnvironment.Dev )
return "Connected to " + serverEnvironment.ToString();
return "Connected";
}
}
}
private static void OnAuthenticationCompleted()
{
AttemptLogin();
}
private static void AttemptLogin()
{
APIUser.Login(username, password,
delegate (ApiModelContainer<APIUser> c)
{
APIUser user = c.Model as APIUser;
if (c.Cookies.ContainsKey("twoFactorAuth"))
ApiCredentials.Set(user.username, username, "vrchat", c.Cookies["auth"], c.Cookies["twoFactorAuth"]);
else if (c.Cookies.ContainsKey("auth"))
ApiCredentials.Set(user.username, username, "vrchat", c.Cookies["auth"]);
else
ApiCredentials.SetHumanName(user.username);
signingIn = false;
latestSignInTime = EditorApplication.timeSinceStartup;
error = null;
storedUsername = null;
storedPassword = null;
AnalyticsSDK.LoggedInUserChanged(user);
OnPanelLoggedIn?.Invoke(window, user);
if (!APIUser.CurrentUser.canPublishWorldsAndAvatars)
{
if (UnityEditor.SessionState.GetString("HasShownContentPublishPermissionsDialogForUser", "") != user.id)
{
UnityEditor.SessionState.SetString("HasShownContentPublishPermissionsDialogForUser", user.id);
VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
}
}
// Fetch platforms that the user can publish to
ApiUserPlatforms.Fetch(user.id, userPlatforms => {
OnUserPlatformsFetched?.Invoke(window, userPlatforms);
}, null);
},
delegate (ApiModelContainer<APIUser> c)
{
Logout();
error = c.Error;
EditorUtility.DisplayDialog("Error logging in", error, "OK");
VRC.Core.Logger.Log("Error logging in: " + error);
},
delegate (ApiModelContainer<API2FA> c)
{
window.rootVisualElement.Q<IMGUIContainer>().MarkDirtyRepaint();
if (c.Cookies.ContainsKey("auth"))
ApiCredentials.Set(username, username, "vrchat", c.Cookies["auth"]);
API2FA model2FA = c.Model as API2FA;
if (model2FA.requiresTwoFactorAuth.Contains(API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION))
twoFactorAuthenticationEntryType = TwoFactorType.TOTP;
else if (model2FA.requiresTwoFactorAuth.Contains(API2FA.EMAIL_BASED_ONE_TIME_PASSWORD_AUTHENTICATION))
twoFactorAuthenticationEntryType = TwoFactorType.Email;
else
twoFactorAuthenticationEntryType = TwoFactorType.None;
onAuthenticationVerifiedAction = OnAuthenticationCompleted;
}
);
}
private static object syncObject = new object();
private void SignIn(bool explicitAttempt)
{
lock (syncObject)
{
if (signingIn
|| APIUser.IsLoggedIn
|| (!explicitAttempt && string.IsNullOrEmpty(storedUsername)))
return;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
EditorUtility.DisplayDialog("Error logging in", "Please enter a valid username and password.", "OK");
return;
}
signingIn = true;
}
InitAccount();
AttemptLogin();
// Show login status immediately without needing an automatic repaint (cursor movement).
Repaint();
}
public static void Logout()
{
signingIn = false;
storedUsername = null;
storedPassword = null;
VRC.Tools.ClearCookies();
APIUser.Logout();
OnPanelLoggedOut?.Invoke(window, EventArgs.Empty);
}
private void AccountDestroy()
{
signingIn = false;
isInitialized = false;
}
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5066cd5c1cc208143a1253cac821714a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4c73e735ee0380241b186a8993fa56bf
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
using System;
using JetBrains.Annotations;
namespace VRC.SDKBase.Editor
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
[MeansImplicitUse]
public class VRCSdkControlPanelBuilderAttribute : Attribute
{
public Type Type { get; }
public VRCSdkControlPanelBuilderAttribute(Type type)
{
Type = type;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c768b42ca9a2f2b48afeb1fa03d5e1bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,786 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using UnityEditor;
using VRC.Core;
using VRC.SDKBase.Editor;
using VRC.SDKBase.Editor.Api;
// This file handles the Content tab of the SDK Panel
public partial class VRCSdkControlPanel : EditorWindow
{
const int PageLimit = 20;
static List<ApiAvatar> uploadedAvatars = null;
static List<ApiWorld> uploadedWorlds = null;
static List<ApiAvatar> testAvatars = null;
public static Dictionary<string, Texture2D> ImageCache = new Dictionary<string, Texture2D>();
static List<string> justDeletedContents;
static List<ApiAvatar> justUpdatedAvatars;
static EditorCoroutine fetchingAvatars = null, fetchingWorlds = null;
private static string searchString = "";
private static bool WorldsToggle = true;
private static bool AvatarsToggle = true;
private static bool TestAvatarsToggle = true;
const string WORLDS_WEB_URL = "https://vrchat.com/home/content/worlds";
const string WORLD_WEB_URL = "https://vrchat.com/home/content/worlds/";
const string WORLD_WEB_URL_SUFFIX = "/edit";
const string AVATARS_WEB_URL = "https://vrchat.com/home/avatars";
const string AVATAR_WEB_URL = "https://vrchat.com/home/avatar/";
const int SCROLLBAR_RESERVED_REGION_WIDTH = 50;
const int OPEN_ON_WEB_BUTTON_WIDTH = 100;
const int WORLD_DESCRIPTION_FIELD_WIDTH = 140;
const int WORLD_IMAGE_BUTTON_WIDTH = 100;
const int WORLD_IMAGE_BUTTON_HEIGHT = 100;
const int WORLD_RELEASE_STATUS_FIELD_WIDTH = 150;
const int COPY_WORLD_ID_BUTTON_WIDTH = 75;
const int DELETE_WORLD_BUTTON_WIDTH = 75;
const int WORLD_ALL_INFORMATION_MAX_WIDTH = WORLD_DESCRIPTION_FIELD_WIDTH + WORLD_IMAGE_BUTTON_WIDTH + WORLD_RELEASE_STATUS_FIELD_WIDTH + COPY_WORLD_ID_BUTTON_WIDTH + DELETE_WORLD_BUTTON_WIDTH + OPEN_ON_WEB_BUTTON_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int WORLD_REDUCED_INFORMATION_MAX_WIDTH = WORLD_DESCRIPTION_FIELD_WIDTH + WORLD_IMAGE_BUTTON_WIDTH + WORLD_RELEASE_STATUS_FIELD_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int AVATAR_DESCRIPTION_FIELD_WIDTH = 140;
const int AVATAR_IMAGE_BUTTON_WIDTH = WORLD_IMAGE_BUTTON_WIDTH;
const int AVATAR_IMAGE_BUTTON_HEIGHT = WORLD_IMAGE_BUTTON_HEIGHT;
const int AVATAR_RELEASE_STATUS_FIELD_WIDTH = 150;
const int SET_AVATAR_STATUS_BUTTON_WIDTH = 100;
const int COPY_AVATAR_ID_BUTTON_WIDTH = COPY_WORLD_ID_BUTTON_WIDTH;
const int DELETE_AVATAR_BUTTON_WIDTH = DELETE_WORLD_BUTTON_WIDTH;
const int AVATAR_ALL_INFORMATION_MAX_WIDTH = AVATAR_DESCRIPTION_FIELD_WIDTH + AVATAR_IMAGE_BUTTON_WIDTH + AVATAR_RELEASE_STATUS_FIELD_WIDTH + SET_AVATAR_STATUS_BUTTON_WIDTH + COPY_AVATAR_ID_BUTTON_WIDTH + DELETE_AVATAR_BUTTON_WIDTH + OPEN_ON_WEB_BUTTON_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int AVATAR_REDUCED_INFORMATION_MAX_WIDTH = AVATAR_DESCRIPTION_FIELD_WIDTH + AVATAR_IMAGE_BUTTON_WIDTH + AVATAR_RELEASE_STATUS_FIELD_WIDTH + SCROLLBAR_RESERVED_REGION_WIDTH;
const int MAX_ALL_INFORMATION_WIDTH = WORLD_ALL_INFORMATION_MAX_WIDTH > AVATAR_ALL_INFORMATION_MAX_WIDTH ? WORLD_ALL_INFORMATION_MAX_WIDTH : AVATAR_ALL_INFORMATION_MAX_WIDTH;
const int MAX_REDUCED_INFORMATION_WIDTH = WORLD_REDUCED_INFORMATION_MAX_WIDTH > AVATAR_REDUCED_INFORMATION_MAX_WIDTH ? WORLD_REDUCED_INFORMATION_MAX_WIDTH : AVATAR_REDUCED_INFORMATION_MAX_WIDTH;
public static void ClearContent()
{
uploadedWorlds = null;
uploadedAvatars = null;
testAvatars = null;
ImageCache.Clear();
}
IEnumerator FetchUploadedData()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
ConfigManager.RemoteConfig.Init();
if (!APIUser.IsLoggedIn)
yield break;
ApiCache.Clear();
VRCCachedWebRequest.ClearOld();
if (fetchingAvatars == null)
fetchingAvatars = EditorCoroutine.Start(() => FetchAvatars());
if (fetchingWorlds == null)
fetchingWorlds = EditorCoroutine.Start(() => FetchWorlds());
FetchTestAvatars();
}
private static void FetchAvatars(int offset = 0)
{
ApiAvatar.FetchList(
delegate (IEnumerable<ApiAvatar> obj, bool _)
{
if (obj.FirstOrDefault() != null)
fetchingAvatars = EditorCoroutine.Start(() =>
{
var l = obj.ToList();
int count = l.Count;
SetupAvatarData(l);
FetchAvatars(offset + count);
});
else
{
fetchingAvatars = null;
foreach (ApiAvatar a in uploadedAvatars)
DownloadImage(a.id, a.thumbnailImageUrl);
}
},
delegate (string obj)
{
Debug.LogError("Error fetching your uploaded avatars:\n" + obj);
fetchingAvatars = null;
},
ApiAvatar.Owner.Mine,
ApiAvatar.ReleaseStatus.All,
null,
PageLimit,
offset,
ApiAvatar.SortHeading.None,
ApiAvatar.SortOrder.Descending,
null,
null,
true,
false,
null,
false
);
}
private static void FetchTestAvatars()
{
#if VRC_SDK_VRCSDK3
string sdkAvatarFolder = VRC.SDKBase.Editor.VRC_SdkBuilder.GetLocalLowPath() + "/VRChat/VRChat/Avatars/";
string[] sdkavatars = Directory.GetFiles(sdkAvatarFolder);
string filename = "";
List<ApiAvatar> avatars = new List<ApiAvatar>();
foreach (string sdkap in sdkavatars)
{
if (Path.GetExtension(sdkap) != ".vrca")
continue;
filename = Path.GetFileNameWithoutExtension(sdkap);
ApiAvatar sdka = API.FromCacheOrNew<ApiAvatar>("local:sdk_" + filename);
sdka.assetUrl = sdkap;
sdka.name = filename;
sdka.releaseStatus = "public";
ApiAvatar.AddLocal(sdka);
avatars.Add(sdka);
}
testAvatars = avatars;
#else
testAvatars = new List<ApiAvatar>();
#endif
}
private static void FetchWorlds(int offset = 0)
{
ApiWorld.FetchList(
delegate (IEnumerable<ApiWorld> obj)
{
if (obj.FirstOrDefault() != null)
fetchingWorlds = EditorCoroutine.Start(() =>
{
var l = obj.ToList();
int count = l.Count;
SetupWorldData(l);
FetchWorlds(offset + count);
});
else
{
fetchingWorlds = null;
foreach (ApiWorld w in uploadedWorlds)
DownloadImage(w.id, w.thumbnailImageUrl);
}
},
delegate (string obj)
{
Debug.LogError("Error fetching your uploaded worlds:\n" + obj);
fetchingWorlds = null;
},
"updated",
ApiWorld.SortOwnership.Mine,
ApiWorld.SortOrder.Descending,
offset,
PageLimit,
"",
null,
null,
null,
null,
"",
ApiWorld.ReleaseStatus.All,
null,
null,
true,
false);
}
static void SetupWorldData(List<ApiWorld> worlds)
{
if (worlds == null || uploadedWorlds == null)
return;
worlds.RemoveAll(w => w == null || w.name == null || uploadedWorlds.Any(w2 => w2.id == w.id));
if (worlds.Count > 0)
{
uploadedWorlds.AddRange(worlds);
uploadedWorlds.Sort((w1, w2) => -w1.updated_at.CompareTo(w2.updated_at));
}
}
static void SetupAvatarData(List<ApiAvatar> avatars)
{
if (avatars == null || uploadedAvatars == null)
return;
avatars.RemoveAll(a => a == null || uploadedAvatars.Any(a2 => a2.id == a.id));
foreach (var avatar in avatars)
{
if (string.IsNullOrEmpty(avatar.name))
avatar.name = "(unnamed)";
}
if (avatars.Count > 0)
{
uploadedAvatars.AddRange(avatars);
uploadedAvatars.Sort((a1, a2) => -a1.updated_at.CompareTo(a2.updated_at));
}
}
private static void DownloadImage(string id, string url)
{
if (string.IsNullOrEmpty(url))
return;
if (ImageCache.ContainsKey(id) && ImageCache[id] != null)
return;
EditorCoroutine.Start(VRCCachedWebRequest.Get(url, OnDone));
void OnDone(Texture2D texture)
{
if (texture != null)
ImageCache[id] = texture;
else if (ImageCache.ContainsKey(id))
ImageCache.Remove(id);
}
}
Vector2 contentScrollPos;
bool OnGUIUserInfo()
{
bool updatedContent = false;
if (!ConfigManager.RemoteConfig.IsInitialized())
ConfigManager.RemoteConfig.Init();
if (APIUser.IsLoggedIn && uploadedWorlds != null && uploadedAvatars != null && testAvatars != null)
{
bool expandedLayout = false; // (position.width > MAX_ALL_INFORMATION_WIDTH); // uncomment for future wide layouts
if (!expandedLayout)
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
}
GUILayout.BeginHorizontal();
GUILayout.BeginVertical(searchBarStyle);
EditorGUILayout.BeginHorizontal();
float searchFieldShrinkOffset = 30f;
GUILayoutOption layoutOption = (expandedLayout ? GUILayout.Width(position.width - searchFieldShrinkOffset) : GUILayout.Width(SdkWindowWidth - searchFieldShrinkOffset - 8));
searchString = EditorGUILayout.TextField(searchString, GUI.skin.FindStyle("SearchTextField"), layoutOption);
GUIStyle searchButtonStyle = searchString == string.Empty
? GUI.skin.FindStyle("SearchCancelButtonEmpty")
: GUI.skin.FindStyle("SearchCancelButton");
if (GUILayout.Button(string.Empty, searchButtonStyle))
{
searchString = string.Empty;
GUI.FocusControl(null);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
if (!expandedLayout)
{
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
}
layoutOption = expandedLayout ? GUILayout.Width(position.width) : GUILayout.Width(SdkWindowWidth - 8);
using (var scroll = new EditorGUILayout.ScrollViewScope(contentScrollPos, layoutOption))
{
contentScrollPos = scroll.scrollPosition;
#if UDON
if (uploadedWorlds.Count > 0)
{
WorldsListGUI(expandedLayout, ref updatedContent);
}
if (uploadedAvatars.Count > 0)
{
AvatarsListGUI(expandedLayout, ref updatedContent);
}
if (testAvatars.Count > 0)
{
TestAvatarsListGUI(expandedLayout, ref updatedContent);
}
#else
if (uploadedAvatars.Count > 0)
{
AvatarsListGUI(expandedLayout, ref updatedContent);
}
if (testAvatars.Count > 0)
{
TestAvatarsListGUI(expandedLayout, ref updatedContent);
}
if (uploadedWorlds.Count > 0)
{
WorldsListGUI(expandedLayout, ref updatedContent);
}
#endif
}
if (!expandedLayout)
{
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
if (updatedContent && (null != window)) window.Reset();
return true;
}
else
{
return false;
}
}
private void AvatarsListGUI(bool expandedLayout, ref bool updatedContent)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Avatars", EditorStyles.boldLabel, GUILayout.ExpandWidth(false),
GUILayout.Width(65));
AvatarsToggle = EditorGUILayout.Foldout(AvatarsToggle, new GUIContent(""));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (AvatarsToggle)
{
List<ApiAvatar> tmpAvatars = new List<ApiAvatar>();
if (uploadedAvatars.Count > 0)
tmpAvatars = new List<ApiAvatar>(uploadedAvatars);
if (justUpdatedAvatars != null)
{
foreach (ApiAvatar a in justUpdatedAvatars)
{
int index = tmpAvatars.FindIndex((av) => av.id == a.id);
if (index != -1)
tmpAvatars[index] = a;
}
}
foreach (ApiAvatar a in tmpAvatars)
{
if (justDeletedContents != null && justDeletedContents.Contains(a.id))
{
uploadedAvatars.Remove(a);
continue;
}
if (!a.name.ToLowerInvariant().Contains(searchString.ToLowerInvariant()))
continue;
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
if (ImageCache.ContainsKey(a.id))
{
if (GUILayout.Button(ImageCache[a.id], GUILayout.Height(AVATAR_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(AVATAR_IMAGE_BUTTON_WIDTH)))
Application.OpenURL(a.imageUrl);
}
else
{
if (GUILayout.Button("", GUILayout.Height(AVATAR_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(AVATAR_IMAGE_BUTTON_WIDTH)))
Application.OpenURL(a.imageUrl);
}
if (expandedLayout)
EditorGUILayout.BeginHorizontal();
else
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(a.name, contentTitleStyle);
if (GUILayout.Button("Open on web", GUILayout.Width(OPEN_ON_WEB_BUTTON_WIDTH)))
Application.OpenURL(AVATAR_WEB_URL + a.id);
EditorGUILayout.EndHorizontal();
EditorGUILayout.LabelField("Release Status: " + a.releaseStatus,
GUILayout.Width(AVATAR_RELEASE_STATUS_FIELD_WIDTH));
string oppositeReleaseStatus = a.releaseStatus == "public" ? "private" : "public";
if (GUILayout.Button("Make " + oppositeReleaseStatus,
GUILayout.Width(SET_AVATAR_STATUS_BUTTON_WIDTH)))
{
a.releaseStatus = oppositeReleaseStatus;
a.SaveReleaseStatus((c) =>
{
ApiAvatar savedBP = (ApiAvatar)c.Model;
if (justUpdatedAvatars == null) justUpdatedAvatars = new List<ApiAvatar>();
justUpdatedAvatars.Add(savedBP);
},
(c) =>
{
Debug.LogError(c.Error);
EditorUtility.DisplayDialog("Avatar Updated",
"Failed to change avatar release status", "OK");
});
}
if (GUILayout.Button("Copy ID", GUILayout.Width(COPY_AVATAR_ID_BUTTON_WIDTH)))
{
TextEditor te = new TextEditor();
te.text = a.id;
te.SelectAll();
te.Copy();
}
if (GUILayout.Button("Delete", GUILayout.Width(DELETE_AVATAR_BUTTON_WIDTH)))
{
if (EditorUtility.DisplayDialog("Delete " + a.name + "?",
"Are you sure you want to delete " + a.name + "? This cannot be undone.", "Delete",
"Cancel"))
{
foreach (VRC.Core.PipelineManager pm in FindObjectsByType<VRC.Core.PipelineManager>(FindObjectsSortMode.None)
.Where(pm => pm.blueprintId == a.id))
{
pm.blueprintId = "";
EditorUtility.SetDirty(pm);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(pm.gameObject.scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(pm.gameObject.scene);
}
API.Delete<ApiAvatar>(a.id);
uploadedAvatars.RemoveAll(avatar => avatar.id == a.id);
if (ImageCache.ContainsKey(a.id))
ImageCache.Remove(a.id);
if (justDeletedContents == null) justDeletedContents = new List<string>();
justDeletedContents.Add(a.id);
updatedContent = true;
}
}
if (expandedLayout)
EditorGUILayout.EndHorizontal();
else
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
}
}
private void TestAvatarsListGUI(bool expandedLayout, ref bool updatedContent)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Test Avatars", EditorStyles.boldLabel, GUILayout.ExpandWidth(false),
GUILayout.Width(100));
TestAvatarsToggle = EditorGUILayout.Foldout(TestAvatarsToggle, new GUIContent(""));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (TestAvatarsToggle)
{
List<ApiAvatar> tmpAvatars = new List<ApiAvatar>();
if (testAvatars.Count > 0)
tmpAvatars = new List<ApiAvatar>(testAvatars);
foreach (ApiAvatar a in tmpAvatars)
{
if (!a.name.ToLowerInvariant().Contains(searchString.ToLowerInvariant()))
continue;
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
if (expandedLayout)
EditorGUILayout.BeginHorizontal();
else
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(a.name, contentDescriptionStyle,
GUILayout.Width(expandedLayout
? position.width - MAX_ALL_INFORMATION_WIDTH + AVATAR_DESCRIPTION_FIELD_WIDTH
: AVATAR_DESCRIPTION_FIELD_WIDTH));
if (GUILayout.Button("Delete", GUILayout.Width(DELETE_AVATAR_BUTTON_WIDTH)))
{
if (EditorUtility.DisplayDialog("Delete " + a.name + "?",
"Are you sure you want to delete " + a.name + "? This cannot be undone.", "Delete",
"Cancel"))
{
API.Delete<ApiAvatar>(a.id);
testAvatars.RemoveAll(avatar => avatar.id == a.id);
File.Delete(a.assetUrl);
updatedContent = true;
}
}
if (expandedLayout)
EditorGUILayout.EndHorizontal();
else
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
}
}
private void WorldsListGUI(bool expandedLayout, ref bool updatedContent)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Worlds", EditorStyles.boldLabel, GUILayout.ExpandWidth(false), GUILayout.Width(58));
WorldsToggle = EditorGUILayout.Foldout(WorldsToggle, new GUIContent(""));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (WorldsToggle)
{
List<ApiWorld> tmpWorlds = new List<ApiWorld>();
if (uploadedWorlds.Count > 0)
tmpWorlds = new List<ApiWorld>(uploadedWorlds);
foreach (ApiWorld w in tmpWorlds)
{
if (justDeletedContents != null && justDeletedContents.Contains(w.id))
{
uploadedWorlds.Remove(w);
continue;
}
if (!w.name.ToLowerInvariant().Contains(searchString.ToLowerInvariant()))
continue;
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
if (ImageCache.ContainsKey(w.id))
{
if (GUILayout.Button(ImageCache[w.id], GUILayout.Height(WORLD_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(WORLD_IMAGE_BUTTON_WIDTH)))
Application.OpenURL(w.imageUrl);
}
else
{
if (GUILayout.Button("", GUILayout.Height(WORLD_IMAGE_BUTTON_HEIGHT),
GUILayout.Width(WORLD_IMAGE_BUTTON_WIDTH)))
Application.OpenURL(w.imageUrl);
}
if (expandedLayout)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(w.name, contentDescriptionStyle,
GUILayout.Width(position.width - MAX_ALL_INFORMATION_WIDTH +
WORLD_DESCRIPTION_FIELD_WIDTH));
}
else
{
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal();
#if UDON
if (w.id == _currentBlueprintId)
{
EditorGUILayout.LabelField(w.name + " (Current)", contentTitleStyle);
}
else
{
EditorGUILayout.LabelField(w.name, contentTitleStyle);
}
#else
EditorGUILayout.LabelField(w.name, contentTitleStyle);
#endif
if (GUILayout.Button("Open on web", GUILayout.Width(OPEN_ON_WEB_BUTTON_WIDTH)))
Application.OpenURL(WORLD_WEB_URL + w.id + WORLD_WEB_URL_SUFFIX);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.LabelField("Release Status: " + w.releaseStatus,
GUILayout.Width(WORLD_RELEASE_STATUS_FIELD_WIDTH));
EditorGUILayout.Space(5);
using (new GUILayout.HorizontalScope())
{
#if UDON
if (GUILayout.Button("Set Current", GUILayout.Width(COPY_WORLD_ID_BUTTON_WIDTH)))
{
var pM = FindFirstObjectByType<PipelineManager>();
if (pM != null)
{
Undo.RecordObject(pM, "Set Current World");
pM.blueprintId = w.id;
_currentBlueprintId = w.id;
}
}
#endif
if (GUILayout.Button("Copy ID", GUILayout.Width(COPY_WORLD_ID_BUTTON_WIDTH)))
{
TextEditor te = new TextEditor();
te.text = w.id;
te.SelectAll();
te.Copy();
}
}
if (GUILayout.Button("Delete", GUILayout.Width(DELETE_WORLD_BUTTON_WIDTH)))
{
if (EditorUtility.DisplayDialog("Delete " + w.name + "?",
"Are you sure you want to delete " + w.name + "? This cannot be undone.", "Delete",
"Cancel"))
{
foreach (VRC.Core.PipelineManager pm in FindObjectsByType<VRC.Core.PipelineManager>(FindObjectsSortMode.None)
.Where(pm => pm.blueprintId == w.id))
{
pm.blueprintId = "";
EditorUtility.SetDirty(pm);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(pm.gameObject.scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(pm.gameObject.scene);
}
API.Delete<ApiWorld>(w.id);
uploadedWorlds.RemoveAll(world => world.id == w.id);
if (ImageCache.ContainsKey(w.id))
ImageCache.Remove(w.id);
if (justDeletedContents == null) justDeletedContents = new List<string>();
justDeletedContents.Add(w.id);
updatedContent = true;
}
}
if (expandedLayout)
EditorGUILayout.EndHorizontal();
else
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
}
}
private string _currentBlueprintId;
private void FetchCurrentBlueprintId()
{
#if UDON
var pM = FindFirstObjectByType<PipelineManager>();
_currentBlueprintId = pM != null ? pM.blueprintId : null;
#endif
}
void ShowContent()
{
GUIStyle centeredDescriptionStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
centeredDescriptionStyle.wordWrap = true;
centeredDescriptionStyle.alignment = TextAnchor.MiddleCenter;
FetchCurrentBlueprintId();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
GUILayout.BeginVertical(infoGuiStyle, GUILayout.Width(SdkWindowWidth));
EditorGUILayout.LabelField("We recommend that you use the VRChat website to manage your content.", centeredDescriptionStyle);
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Worlds", GUILayout.Width(OPEN_ON_WEB_BUTTON_WIDTH)))
Application.OpenURL(WORLDS_WEB_URL);
GUILayout.FlexibleSpace();
if (GUILayout.Button("Avatars", GUILayout.Width(OPEN_ON_WEB_BUTTON_WIDTH)))
Application.OpenURL(AVATARS_WEB_URL);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.EndVertical();
if (((PanelTab)VRCSettings.ActiveWindowPanel) == PanelTab.ContentManager)
{
if (uploadedWorlds == null || uploadedAvatars == null || testAvatars == null)
{
if (uploadedWorlds == null)
uploadedWorlds = new List<ApiWorld>();
if (uploadedAvatars == null)
uploadedAvatars = new List<ApiAvatar>();
if (testAvatars == null)
testAvatars = new List<ApiAvatar>();
EditorCoroutine.Start(FetchUploadedData());
}
if (
fetchingWorlds != null
|| fetchingAvatars != null
)
{
GUILayout.BeginVertical(boxGuiStyle, GUILayout.Width(SdkWindowWidth - 8));
EditorGUILayout.Space();
EditorGUILayout.LabelField("Fetching Records", titleGuiStyle);
EditorGUILayout.Space();
GUILayout.EndVertical();
}
else
{
GUILayout.BeginVertical(boxGuiStyle, GUILayout.Width(SdkWindowWidth - 8));
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Fetch updated records from the VRChat server");
if (GUILayout.Button("Fetch"))
ClearContent();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
GUILayout.EndVertical();
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
OnGUIUserInfo();
}
}
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c7333cdb3df19724b84b4a1b05093fe0
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,64 @@
using UnityEditor;
using UnityEngine;
using VRC.Core;
/// This file handles the links inside VRChat SDK top bar menu
namespace VRC.SDKBase.Editor
{
public static class VRCSdkControlPanelHelp
{
public const string AVATAR_OPTIMIZATION_TIPS_URL = "https://creators.vrchat.com/avatars/avatar-optimizing-tips";
public const string AVATAR_RIG_REQUIREMENTS_URL = "https://creators.vrchat.com/avatars/rig-requirements";
public const string AVATAR_WRITE_DEFAULTS_ON_STATES_URL = "https://creators.vrchat.com/avatars/#write-defaults-on-states";
public const string AVATAR_CUSTOM_HEAD_CHOP_URL = "https://creators.vrchat.com/avatars/avatar-dynamics/vrc-headchop";
[MenuItem("VRChat SDK/Help/Developer FAQ")]
public static void ShowDeveloperFAQ()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowDeveloperFAQ());
return;
}
Application.OpenURL(ConfigManager.RemoteConfig.GetString("sdkDeveloperFaqUrl"));
}
[MenuItem("VRChat SDK/Help/VRChat Discord")]
public static void ShowVRChatDiscord()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowVRChatDiscord());
return;
}
Application.OpenURL(ConfigManager.RemoteConfig.GetString("sdkDiscordUrl"));
}
[MenuItem("VRChat SDK/Help/Avatar Optimization Tips")]
public static void ShowAvatarOptimizationTips()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowAvatarOptimizationTips());
return;
}
Application.OpenURL(AVATAR_OPTIMIZATION_TIPS_URL);
}
[MenuItem("VRChat SDK/Help/Avatar Rig Requirements")]
public static void ShowAvatarRigRequirements()
{
if (!ConfigManager.RemoteConfig.IsInitialized())
{
ConfigManager.RemoteConfig.Init(() => ShowAvatarRigRequirements());
return;
}
Application.OpenURL(AVATAR_RIG_REQUIREMENTS_URL);
}
}
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f3507a74e4b8cfd469afac127fa5f4e5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,225 @@
using System;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEditor;
using VRC.Core;
using VRC.SDKBase;
using VRC.SDKBase.Editor;
// This file handles the Settings tab of the SDK Panel
public partial class VRCSdkControlPanel : EditorWindow
{
bool UseDevApi
{
get
{
return VRC.Core.API.GetApiUrl() == VRC.Core.API.devApiUrl;
}
}
Vector2 settingsScroll;
bool showLocalIpAddress;
void ShowSettings()
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
settingsScroll = EditorGUILayout.BeginScrollView(settingsScroll, GUILayout.Width(SdkWindowWidth - 8));
EditorGUILayout.BeginVertical(boxGuiStyle);
EditorGUILayout.LabelField("Developer", EditorStyles.boldLabel);
VRCSettings.DisplayAdvancedSettings = EditorGUILayout.ToggleLeft("Show Extra Options on account page", VRCSettings.DisplayAdvancedSettings);
bool prevDisplayHelpBoxes = VRCSettings.DisplayHelpBoxes;
VRCSettings.DisplayHelpBoxes = EditorGUILayout.ToggleLeft("Show Help Boxes on SDK components", VRCSettings.DisplayHelpBoxes);
if (VRCSettings.DisplayHelpBoxes != prevDisplayHelpBoxes)
{
Editor[] editors = (Editor[])Resources.FindObjectsOfTypeAll<Editor>();
for (int i = 0; i < editors.Length; i++)
{
editors[i].Repaint();
}
}
// API logging
{
bool isLoggingEnabled = UnityEditor.EditorPrefs.GetBool("apiLoggingEnabled");
bool enableLogging = EditorGUILayout.ToggleLeft("API Logging Enabled", isLoggingEnabled);
if (enableLogging != isLoggingEnabled)
{
if (enableLogging)
VRC.Core.Logger.EnableCategory(API.LOG_CATEGORY);
else
VRC.Core.Logger.DisableCategory(API.LOG_CATEGORY);
UnityEditor.EditorPrefs.SetBool("apiLoggingEnabled", enableLogging);
}
}
// Dry Builds
if (APIUser.CurrentUser != null && APIUser.CurrentUser.hasSuperPowers) {
var newDryRun = EditorGUILayout.ToggleLeft(new GUIContent("Dry Run Builds", "This will skip actual builds and uploads and instead pass as if they succeeded"), VRC_EditorTools.DryRunState);
if (newDryRun != VRC_EditorTools.DryRunState)
{
VRC_EditorTools.DryRunState = newDryRun;
}
}
EditorGUILayout.Space();
// DPID based mipmap generation
bool prevDpidMipmaps = VRCPackageSettings.Instance.dpidMipmaps;
GUIContent dpidContent = new GUIContent("Override Kaiser mipmapping with Detail-Preserving Image Downscaling (BETA)",
"Use a state of the art algorithm (DPID) for mipmap generation when Kaiser is selected. This can improve the quality of mipmaps.");
VRCPackageSettings.Instance.dpidMipmaps = EditorGUILayout.ToggleLeft(dpidContent, VRCPackageSettings.Instance.dpidMipmaps);
bool prevDpidConservative = VRCPackageSettings.Instance.dpidConservative;
GUIContent dpidConservativeContent = new GUIContent("Use conservative settings for DPID mipmapping",
"Use conservative settings for DPID mipmapping. This can avoid issues with over-emphasis of details.");
VRCPackageSettings.Instance.dpidConservative = EditorGUILayout.ToggleLeft(dpidConservativeContent, VRCPackageSettings.Instance.dpidConservative);
// When DPID setting changed, mark all textures as dirty
if (VRCPackageSettings.Instance.dpidMipmaps != prevDpidMipmaps ||
(VRCPackageSettings.Instance.dpidMipmaps && VRCPackageSettings.Instance.dpidConservative != prevDpidConservative))
{
VRC.Core.Logger.Log("DPID mipmaps setting changed, marking all textures as dirty");
string[] guids = AssetDatabase.FindAssets("t:Texture");
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
if (importer != null && importer.mipmapFilter == TextureImporterMipFilter.KaiserFilter)
{
importer.SaveAndReimport();
}
}
VRCPackageSettings.Instance.Save();
}
EditorGUILayout.Space();
// Running VRChat constraints in edit mode
bool prevVrcConstraintsInEditMode = VRCSettings.VrcConstraintsInEditMode;
GUIContent vrcConstraintsInEditModeContent = new GUIContent("Execute VRChat Constraints in Edit Mode",
"Allow VRChat Constraints to run while Unity is in Edit mode.");
VRCSettings.VrcConstraintsInEditMode = EditorGUILayout.ToggleLeft(vrcConstraintsInEditModeContent, prevVrcConstraintsInEditMode);
if (VRCSettings.VrcConstraintsInEditMode != prevVrcConstraintsInEditMode)
{
VRC.Dynamics.VRCConstraintManager.CanExecuteConstraintJobsInEditMode = VRCSettings.VrcConstraintsInEditMode;
}
EditorGUILayout.Space();
showLocalIpAddress = EditorGUILayout.Foldout(showLocalIpAddress, "Show Local IP Address", true);
if (showLocalIpAddress)
{
string localIpAddress = GetLocalIPAddress();
EditorGUILayout.HelpBox(localIpAddress, MessageType.None);
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
ShowSettingsOptionsForBuilders();
// debugging
if (APIUser.CurrentUser != null && APIUser.CurrentUser.hasSuperPowers)
{
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxGuiStyle);
EditorGUILayout.LabelField("Logging", EditorStyles.boldLabel);
// All logging
{
bool isLoggingEnabled = UnityEditor.EditorPrefs.GetBool("allLoggingEnabled");
bool enableLogging = EditorGUILayout.ToggleLeft("All Logging Enabled", isLoggingEnabled);
if (enableLogging != isLoggingEnabled)
{
VRC.Core.Logger.SetTreatAllCategoriesAsEnabled(enableLogging);
UnityEditor.EditorPrefs.SetBool("allLoggingEnabled", enableLogging);
}
}
EditorGUILayout.EndVertical();
}
else
{
// if (UnityEditor.EditorPrefs.GetBool("apiLoggingEnabled"))
// UnityEditor.EditorPrefs.SetBool("apiLoggingEnabled", false);
if (UnityEditor.EditorPrefs.GetBool("allLoggingEnabled"))
UnityEditor.EditorPrefs.SetBool("allLoggingEnabled", false);
}
if (APIUser.CurrentUser != null)
{
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxGuiStyle);
// custom vrchat install location
OnVRCInstallPathGUI();
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
static void OnVRCInstallPathGUI()
{
EditorGUILayout.LabelField("VRChat Client", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Installed Client Path: ", clientInstallPath);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("");
if (GUILayout.Button("Edit"))
{
string initPath = "";
if (!string.IsNullOrEmpty(clientInstallPath))
initPath = clientInstallPath;
clientInstallPath = EditorUtility.OpenFilePanel("Choose VRC Client Exe", initPath, "exe");
SDKClientUtilities.SetVRCInstallPath(clientInstallPath);
}
if (GUILayout.Button("Revert to Default"))
{
clientInstallPath = SDKClientUtilities.LoadRegistryVRCInstallPath();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
}
string GetLocalIPAddress()
{
// Note that there are usually many IP addresses on any particular machine (multiple ethernet ports, virtual machine IP addresses)
// So will give you a whole list of them `Dns.GetHostEntry(Dns.GetHostName());`, but it's hard to say which one you care about.
// https://stackoverflow.com/a/27376368
// The following gives you exactly the address you care about by instead opening a UDP socket to get the address that would be used
// As the post mentions, no real connection is established here.
try
{
using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0);
socket.Connect("8.8.8.8", 65530);
IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
var localIP = endPoint.Address.ToString();
return localIP;
}
catch (Exception e)
{
Debug.LogException(e);
return "Unable to get local IP address";
}
}
}

Some files were not shown because too many files have changed in this diff Show More