diff --git a/Assets/USharpVideo/Scripts/Editor/USharpVideoInspector.cs b/Assets/USharpVideo/Scripts/Editor/USharpVideoInspector.cs index 2ee985c..82a7bdb 100644 --- a/Assets/USharpVideo/Scripts/Editor/USharpVideoInspector.cs +++ b/Assets/USharpVideo/Scripts/Editor/USharpVideoInspector.cs @@ -1,155 +1,170 @@ - -using UnityEditor; -using UdonSharpEditor; -using UnityEditorInternal; -using UnityEngine; -using VRC.SDK3.Video.Components.AVPro; -using System.Reflection; - -#pragma warning disable CS0612 // Type or member is obsolete - -namespace UdonSharp.Video.Internal -{ - [CustomEditor(typeof(USharpVideoPlayer))] - internal class USharpVideoInspector : Editor - { - ReorderableList playlistList; - - SerializedProperty allowSeekProperty; - SerializedProperty defaultUnlockedProperty; - SerializedProperty allowCreatorControlProperty; - - SerializedProperty syncFrequencyProperty; - SerializedProperty syncThresholdProperty; - - SerializedProperty defaultVolumeProperty; - SerializedProperty audioRangeProperty; - - SerializedProperty defaultStreamMode; - - SerializedProperty playlistProperty; - SerializedProperty loopPlaylistProperty; - SerializedProperty shufflePlaylistProperty; - - private void OnEnable() - { - allowSeekProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.allowSeeking)); - defaultUnlockedProperty = serializedObject.FindProperty("defaultUnlocked"); - allowCreatorControlProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.allowInstanceCreatorControl)); - syncFrequencyProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.syncFrequency)); - syncThresholdProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.syncThreshold)); - - defaultVolumeProperty = serializedObject.FindProperty("defaultVolume"); - audioRangeProperty = serializedObject.FindProperty("audioRange"); - - defaultStreamMode = serializedObject.FindProperty("defaultStreamMode"); - - playlistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.playlist)); - loopPlaylistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.loopPlaylist)); - shufflePlaylistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.shufflePlaylist)); - - playlistList = new ReorderableList(serializedObject, playlistProperty, true, true, true, true); - playlistList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => - { - Rect testFieldRect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight); - - EditorGUI.PropertyField(testFieldRect, playlistList.serializedProperty.GetArrayElementAtIndex(index), label: new GUIContent()); - }; - playlistList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, new GUIContent("Default Playlist URLs", "URLs that will play in sequence when you join the world until someone puts in a video.")); }; - } - - public override void OnInspectorGUI() - { - if (UdonSharpGUI.DrawConvertToUdonBehaviourButton(target) || - UdonSharpGUI.DrawProgramSource(target)) - return; - - UdonSharpGUI.DrawUILine(); - - EditorGUILayout.LabelField("General", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(allowSeekProperty); - EditorGUILayout.PropertyField(defaultUnlockedProperty); - EditorGUILayout.PropertyField(allowCreatorControlProperty); - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Sync", EditorStyles.boldLabel); - - EditorGUILayout.PropertyField(syncFrequencyProperty); - EditorGUILayout.PropertyField(syncThresholdProperty); - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Audio", EditorStyles.boldLabel); - - EditorGUI.BeginChangeCheck(); - EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode); - EditorGUILayout.PropertyField(defaultVolumeProperty); - EditorGUI.EndDisabledGroup(); - EditorGUILayout.PropertyField(audioRangeProperty); - - if (EditorGUI.EndChangeCheck()) - { - VideoPlayerManager manager = ((Component)target).GetUdonSharpComponentInChildren(true); - - foreach (AudioSource source in manager.audioSources) - { - if (source) - { - Undo.RecordObject(source, "Change audio properties"); - source.maxDistance = Mathf.Max(0f, audioRangeProperty.floatValue); - source.volume = defaultVolumeProperty.floatValue; - - if (PrefabUtility.IsPartOfPrefabInstance(source)) - PrefabUtility.RecordPrefabInstancePropertyModifications(source); - } - } - - VolumeController[] volumeControllers = ((Component)target).GetUdonSharpComponentsInChildren(true); - - foreach (VolumeController controller in volumeControllers) - { - if (controller.slider) - { - Undo.RecordObject(controller.slider, "Change audio properties"); - controller.slider.value = defaultVolumeProperty.floatValue; - - if (PrefabUtility.IsPartOfPrefabInstance(controller.slider)) - PrefabUtility.RecordPrefabInstancePropertyModifications(controller.slider); - } - } - } - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Playlist", EditorStyles.boldLabel); - - playlistList.DoLayoutList(); - EditorGUILayout.PropertyField(loopPlaylistProperty); - EditorGUILayout.PropertyField(shufflePlaylistProperty); - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Stream Settings", EditorStyles.boldLabel); - - EditorGUILayout.PropertyField(defaultStreamMode); - - VRCAVProVideoPlayer avProPlayer = ((Component)target).GetComponentInChildren(true); - - if (avProPlayer) - { - EditorGUI.BeginChangeCheck(); - bool newLowLatencyMode = EditorGUILayout.Toggle(new GUIContent("Low Latency Stream", "Whether the stream player should use low latency mode for RTSP streams"), avProPlayer.UseLowLatency); - - if (EditorGUI.EndChangeCheck()) - { - //FieldInfo lowLatencyField = typeof(VRCAVProVideoPlayer).GetField("useLowLatency", BindingFlags.Instance | BindingFlags.NonPublic); - - SerializedObject avproPlayerSerializedObject = new SerializedObject(avProPlayer); - SerializedProperty lowLatencyField = avproPlayerSerializedObject.FindProperty("useLowLatency"); - - lowLatencyField.boolValue = newLowLatencyMode; - avproPlayerSerializedObject.ApplyModifiedProperties(); - } - } - - serializedObject.ApplyModifiedProperties(); - } - } -} + +using UnityEditor; +using UdonSharpEditor; +using UnityEditorInternal; +using UnityEngine; +using VRC.SDK3.Video.Components.AVPro; +using System.Reflection; + +#pragma warning disable CS0612 // Type or member is obsolete + +namespace UdonSharp.Video.Internal +{ + [CustomEditor(typeof(USharpVideoPlayer))] + internal class USharpVideoInspector : Editor + { + ReorderableList playlistList; + ReorderableList allowlistList; + + SerializedProperty allowSeekProperty; + SerializedProperty defaultUnlockedProperty; + SerializedProperty allowCreatorControlProperty; + + SerializedProperty syncFrequencyProperty; + SerializedProperty syncThresholdProperty; + + SerializedProperty defaultVolumeProperty; + SerializedProperty audioRangeProperty; + + SerializedProperty defaultStreamMode; + + SerializedProperty playlistProperty; + SerializedProperty loopPlaylistProperty; + SerializedProperty shufflePlaylistProperty; + + SerializedProperty allowlistProperty; + + private void OnEnable() + { + allowSeekProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.allowSeeking)); + defaultUnlockedProperty = serializedObject.FindProperty("defaultUnlocked"); + allowCreatorControlProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.allowInstanceCreatorControl)); + syncFrequencyProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.syncFrequency)); + syncThresholdProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.syncThreshold)); + + defaultVolumeProperty = serializedObject.FindProperty("defaultVolume"); + audioRangeProperty = serializedObject.FindProperty("audioRange"); + + defaultStreamMode = serializedObject.FindProperty("defaultStreamMode"); + + playlistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.playlist)); + loopPlaylistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.loopPlaylist)); + shufflePlaylistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.shufflePlaylist)); + + allowlistProperty = serializedObject.FindProperty(nameof(USharpVideoPlayer.allowlist)); + + playlistList = new ReorderableList(serializedObject, playlistProperty, true, true, true, true); + playlistList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => + { + Rect playlistFieldRect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight); + + EditorGUI.PropertyField(playlistFieldRect, playlistList.serializedProperty.GetArrayElementAtIndex(index), label: new GUIContent()); + }; + playlistList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, new GUIContent("Default Playlist URLs", "URLs that will play in sequence when you join the world until someone puts in a video.")); }; + + allowlistList = new ReorderableList(serializedObject, allowlistProperty, true, true, true, true); + allowlistList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => + { + Rect allowlistFieldRect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight); + + EditorGUI.PropertyField(allowlistFieldRect, allowlistList.serializedProperty.GetArrayElementAtIndex(index), label: new GUIContent()); + }; + allowlistList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, new GUIContent("Allowed CDN's and video hosting sites", "Add CDN domains such as catbox.moe or youtube.com that you want the video player to allow here.")); }; + } + + public override void OnInspectorGUI() + { + if (UdonSharpGUI.DrawConvertToUdonBehaviourButton(target) || + UdonSharpGUI.DrawProgramSource(target)) + return; + + UdonSharpGUI.DrawUILine(); + + EditorGUILayout.LabelField("General", EditorStyles.boldLabel); + allowlistList.DoLayoutList(); + EditorGUILayout.PropertyField(allowSeekProperty); + EditorGUILayout.PropertyField(defaultUnlockedProperty); + EditorGUILayout.PropertyField(allowCreatorControlProperty); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Sync", EditorStyles.boldLabel); + + EditorGUILayout.PropertyField(syncFrequencyProperty); + EditorGUILayout.PropertyField(syncThresholdProperty); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Audio", EditorStyles.boldLabel); + + EditorGUI.BeginChangeCheck(); + EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode); + EditorGUILayout.PropertyField(defaultVolumeProperty); + EditorGUI.EndDisabledGroup(); + EditorGUILayout.PropertyField(audioRangeProperty); + + if (EditorGUI.EndChangeCheck()) + { + VideoPlayerManager manager = ((Component)target).GetUdonSharpComponentInChildren(true); + + foreach (AudioSource source in manager.audioSources) + { + if (source) + { + Undo.RecordObject(source, "Change audio properties"); + source.maxDistance = Mathf.Max(0f, audioRangeProperty.floatValue); + source.volume = defaultVolumeProperty.floatValue; + + if (PrefabUtility.IsPartOfPrefabInstance(source)) + PrefabUtility.RecordPrefabInstancePropertyModifications(source); + } + } + + VolumeController[] volumeControllers = ((Component)target).GetUdonSharpComponentsInChildren(true); + + foreach (VolumeController controller in volumeControllers) + { + if (controller.slider) + { + Undo.RecordObject(controller.slider, "Change audio properties"); + controller.slider.value = defaultVolumeProperty.floatValue; + + if (PrefabUtility.IsPartOfPrefabInstance(controller.slider)) + PrefabUtility.RecordPrefabInstancePropertyModifications(controller.slider); + } + } + } + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Playlist", EditorStyles.boldLabel); + + playlistList.DoLayoutList(); + EditorGUILayout.PropertyField(loopPlaylistProperty); + EditorGUILayout.PropertyField(shufflePlaylistProperty); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Stream Settings", EditorStyles.boldLabel); + + EditorGUILayout.PropertyField(defaultStreamMode); + + VRCAVProVideoPlayer avProPlayer = ((Component)target).GetComponentInChildren(true); + + if (avProPlayer) + { + EditorGUI.BeginChangeCheck(); + bool newLowLatencyMode = EditorGUILayout.Toggle(new GUIContent("Low Latency Stream", "Whether the stream player should use low latency mode for RTSP streams"), avProPlayer.UseLowLatency); + + if (EditorGUI.EndChangeCheck()) + { + //FieldInfo lowLatencyField = typeof(VRCAVProVideoPlayer).GetField("useLowLatency", BindingFlags.Instance | BindingFlags.NonPublic); + + SerializedObject avproPlayerSerializedObject = new SerializedObject(avProPlayer); + SerializedProperty lowLatencyField = avproPlayerSerializedObject.FindProperty("useLowLatency"); + + lowLatencyField.boolValue = newLowLatencyMode; + avproPlayerSerializedObject.ApplyModifiedProperties(); + } + } + + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Assets/USharpVideo/Scripts/USharpVideoPlayer.asset b/Assets/USharpVideo/Scripts/USharpVideoPlayer.asset index 8e61360..fa11169 100644 --- a/Assets/USharpVideo/Scripts/USharpVideoPlayer.asset +++ b/Assets/USharpVideo/Scripts/USharpVideoPlayer.asset @@ -12,22 +12,20 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: c333ccfdd0cbdbc4ca30cef2dd6e6b9b, type: 3} m_Name: USharpVideoPlayer m_EditorClassIdentifier: - serializedUdonProgramAsset: {fileID: 11400000, guid: ca2adc1e85449f749b4524df31a2a0f1, + serializedUdonProgramAsset: {fileID: 11400000, guid: e075f6e1149bd0b41b3aba38dfe7938c, type: 2} udonAssembly: assemblyError: sourceCsScript: {fileID: 11500000, guid: a387f0336d7ee344baf6e00b581a5365, type: 3} + scriptVersion: 2 + compiledVersion: 2 behaviourSyncMode: 4 - behaviourIDHeapVarName: __refl_const_intnl_udonTypeID - compileErrors: [] hasInteractEvent: 0 + scriptID: 1873806842537421278 serializationData: SerializedFormat: 2 SerializedBytes: - ReferencedUnityObjects: - - {fileID: 11500000, guid: 61a08afb94ef7364d8358a64333fb431, type: 3} - - {fileID: 11500000, guid: 0ce6496606301e64ea5fb1e7516662c3, type: 3} - - {fileID: 11500000, guid: a8947e6a8f7f7b24a9a65140fa6bf344, type: 3} + ReferencedUnityObjects: [] SerializedBytesString: Prefab: {fileID: 0} PrefabModificationsReferencedUnityObjects: [] @@ -46,7 +44,7 @@ MonoBehaviour: Data: - Name: Entry: 12 - Data: 55 + Data: 49 - Name: Entry: 7 Data: @@ -56,40 +54,40 @@ MonoBehaviour: - Name: $v Entry: 7 Data: 2|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 3|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + - Name: k__BackingField + Entry: 1 + Data: _videoPlayerManager + - Name: k__BackingField Entry: 7 - Data: 4|System.RuntimeType, mscorlib + Data: 3|System.RuntimeType, mscorlib - Name: Entry: 1 Data: UdonSharp.Video.VideoPlayerManager, Assembly-CSharp - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: VRCUdonUdonBehaviour - - Name: symbolOriginalName - Entry: 1 - Data: _videoPlayerManager - - Name: symbolUniqueName + - Name: k__BackingField + Entry: 7 + Data: 4|System.RuntimeType, mscorlib + - Name: Entry: 1 - Data: _videoPlayerManager - - Name: symbolDefaultValue + Data: VRC.Udon.UdonBehaviour, VRC.Udon + - Name: + Entry: 8 + Data: + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 Data: 5|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: @@ -101,9 +99,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 10 - Data: 0 - Name: Entry: 8 Data: @@ -119,48 +114,42 @@ MonoBehaviour: - Name: $v Entry: 7 Data: 6|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 7|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + - Name: k__BackingField + Entry: 1 + Data: allowSeeking + - Name: k__BackingField Entry: 7 - Data: 8|System.RuntimeType, mscorlib + Data: 7|System.RuntimeType, mscorlib - Name: Entry: 1 Data: System.Boolean, mscorlib - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: allowSeeking - - Name: symbolUniqueName - Entry: 1 - Data: allowSeeking - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 9|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 8|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 10|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 9|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: Whether to allow video seeking with the progress bar on the video @@ -169,7 +158,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 11|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule + Data: 10|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -179,9 +168,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -196,43 +182,37 @@ MonoBehaviour: Data: defaultUnlocked - Name: $v Entry: 7 - Data: 12|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 13|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: defaultUnlocked - - Name: symbolUniqueName + Data: 11|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: defaultUnlocked - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 14|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 12|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 15|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 13|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: If enabled defaults to unlocked so anyone can put in a URL @@ -241,7 +221,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 16|UnityEngine.SerializeField, UnityEngine.CoreModule + Data: 14|UnityEngine.SerializeField, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -251,9 +231,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -268,43 +245,37 @@ MonoBehaviour: Data: allowInstanceCreatorControl - Name: $v Entry: 7 - Data: 17|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 18|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName + Data: 15|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: allowInstanceCreatorControl - - Name: symbolUniqueName - Entry: 1 - Data: allowInstanceCreatorControl - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 19|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 16|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 20|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 17|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: If enabled allows the instance creator to always control the video player @@ -314,7 +285,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 21|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule + Data: 18|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -324,9 +295,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -341,49 +309,43 @@ MonoBehaviour: Data: syncFrequency - Name: $v Entry: 7 - Data: 22|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 23|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 19|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: syncFrequency + - Name: k__BackingField Entry: 7 - Data: 24|System.RuntimeType, mscorlib + Data: 20|System.RuntimeType, mscorlib - Name: Entry: 1 Data: System.Single, mscorlib - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: syncFrequency - - Name: symbolUniqueName - Entry: 1 - Data: syncFrequency - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 25|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 21|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 26|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 22|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: How often the video player should check if it is more than Sync Threshold @@ -393,7 +355,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 27|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule + Data: 23|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -403,9 +365,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -420,43 +379,37 @@ MonoBehaviour: Data: syncThreshold - Name: $v Entry: 7 - Data: 28|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 29|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName + Data: 24|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: syncThreshold - - Name: symbolUniqueName - Entry: 1 - Data: syncThreshold - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 30|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 25|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 31|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 26|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: How many seconds desynced from the owner the client needs to be to trigger @@ -466,7 +419,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 32|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule + Data: 27|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -476,9 +429,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -493,43 +443,37 @@ MonoBehaviour: Data: defaultVolume - Name: $v Entry: 7 - Data: 33|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 34|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: defaultVolume - - Name: symbolUniqueName + Data: 28|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: defaultVolume - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 35|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 29|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 3 - Name: Entry: 7 - Data: 36|UnityEngine.RangeAttribute, UnityEngine.CoreModule + Data: 30|UnityEngine.RangeAttribute, UnityEngine.CoreModule - Name: min Entry: 4 Data: 0 @@ -541,7 +485,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 37|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 31|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: The default volume for the volume slider on the video player @@ -550,7 +494,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 38|UnityEngine.SerializeField, UnityEngine.CoreModule + Data: 32|UnityEngine.SerializeField, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -560,9 +504,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -577,43 +518,37 @@ MonoBehaviour: Data: audioRange - Name: $v Entry: 7 - Data: 39|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 40|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: audioRange - - Name: symbolUniqueName + Data: 33|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: audioRange - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 41|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 34|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 42|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 35|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: The max range of the audio sources on this video player @@ -622,7 +557,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 43|UnityEngine.SerializeField, UnityEngine.CoreModule + Data: 36|UnityEngine.SerializeField, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -632,9 +567,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -649,49 +581,43 @@ MonoBehaviour: Data: localSyncOffset - Name: $v Entry: 7 - Data: 44|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 45|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: localSyncOffset - - Name: symbolUniqueName + Data: 37|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: localSyncOffset - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 46|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 38|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 47|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule + Data: 39|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule - Name: Entry: 8 Data: - Name: Entry: 7 - Data: 48|System.NonSerializedAttribute, mscorlib + Data: 40|System.NonSerializedAttribute, mscorlib - Name: Entry: 8 Data: @@ -701,9 +627,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -718,49 +641,43 @@ MonoBehaviour: Data: playlist - Name: $v Entry: 7 - Data: 49|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 50|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 41|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: playlist + - Name: k__BackingField Entry: 7 - Data: 51|System.RuntimeType, mscorlib + Data: 42|System.RuntimeType, mscorlib - Name: Entry: 1 Data: VRC.SDKBase.VRCUrl[], VRCSDKBase - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: VRCSDKBaseVRCUrlArray - - Name: symbolOriginalName - Entry: 1 - Data: playlist - - Name: symbolUniqueName - Entry: 1 - Data: playlist - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 42 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 52|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 43|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 53|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 44|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: List of urls to play automatically when the world is loaded until someone @@ -774,9 +691,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -788,46 +702,104 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: defaultStreamMode + Data: allowlist - Name: $v Entry: 7 - Data: 54|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol + Data: 45|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: allowlist + - Name: k__BackingField Entry: 7 - Data: 55|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 46|System.RuntimeType, mscorlib + - Name: + Entry: 1 + Data: System.String[], mscorlib + - Name: + Entry: 8 + Data: + - Name: k__BackingField Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 + Data: 46 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: + Entry: 6 + Data: + - Name: + Entry: 8 + Data: + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes + Entry: 7 + Data: 47|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + - Name: + Entry: 12 Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName + - Name: + Entry: 7 + Data: 48|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + - Name: tooltip Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName + Data: Trusted URL's of websites you want to be able to load videos from. This + prevents malicious users from throwing IP grabbers into the player + - Name: + Entry: 8 + Data: + - Name: + Entry: 13 + Data: + - Name: + Entry: 8 + Data: + - Name: + Entry: 8 + Data: + - Name: + Entry: 8 + Data: + - Name: + Entry: 7 + Data: + - Name: $k Entry: 1 Data: defaultStreamMode - - Name: symbolUniqueName + - Name: $v + Entry: 7 + Data: 49|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: defaultStreamMode - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 56|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 50|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 57|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 51|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: Should default to the stream player? This is usually used when you want @@ -837,7 +809,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 58|UnityEngine.SerializeField, UnityEngine.CoreModule + Data: 52|UnityEngine.SerializeField, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -847,9 +819,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -864,43 +833,37 @@ MonoBehaviour: Data: loopPlaylist - Name: $v Entry: 7 - Data: 59|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 60|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: loopPlaylist - - Name: symbolUniqueName + Data: 53|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: loopPlaylist - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 61|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 54|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 2 - Name: Entry: 7 - Data: 62|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 55|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: If the default playlist should loop @@ -909,7 +872,7 @@ MonoBehaviour: Data: - Name: Entry: 7 - Data: 63|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule + Data: 56|JetBrains.Annotations.PublicAPIAttribute, UnityEngine.CoreModule - Name: Entry: 8 Data: @@ -919,9 +882,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -936,43 +896,37 @@ MonoBehaviour: Data: shufflePlaylist - Name: $v Entry: 7 - Data: 64|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 65|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 1 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: shufflePlaylist - - Name: symbolUniqueName + Data: 57|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: shufflePlaylist - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: true + - Name: _fieldAttributes Entry: 7 - Data: 66|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 58|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 67|UnityEngine.TooltipAttribute, UnityEngine.CoreModule + Data: 59|UnityEngine.TooltipAttribute, UnityEngine.CoreModule - Name: tooltip Entry: 1 Data: If the default playlist should be shuffled upon world load @@ -985,9 +939,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1002,49 +953,43 @@ MonoBehaviour: Data: _syncedURL - Name: $v Entry: 7 - Data: 68|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 69|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 60|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _syncedURL + - Name: k__BackingField Entry: 7 - Data: 70|System.RuntimeType, mscorlib + Data: 61|System.RuntimeType, mscorlib - Name: Entry: 1 Data: VRC.SDKBase.VRCUrl, VRCSDKBase - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + - Name: k__BackingField + Entry: 9 + Data: 61 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: VRCSDKBaseVRCUrl - - Name: symbolOriginalName - Entry: 1 - Data: _syncedURL - - Name: symbolUniqueName - Entry: 1 - Data: _syncedURL - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 71|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 62|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 72|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 63|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1054,9 +999,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1071,49 +1013,43 @@ MonoBehaviour: Data: _syncedVideoIdx - Name: $v Entry: 7 - Data: 73|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 74|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 64|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _syncedVideoIdx + - Name: k__BackingField Entry: 7 - Data: 75|System.RuntimeType, mscorlib + Data: 65|System.RuntimeType, mscorlib - Name: Entry: 1 Data: System.Int32, mscorlib - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _syncedVideoIdx - - Name: symbolUniqueName - Entry: 1 - Data: _syncedVideoIdx - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 76|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 66|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 77|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 67|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1123,9 +1059,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1140,37 +1073,31 @@ MonoBehaviour: Data: _currentVideoIdx - Name: $v Entry: 7 - Data: 78|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 79|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _currentVideoIdx - - Name: symbolUniqueName + Data: 68|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _currentVideoIdx - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 80|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 69|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 0 @@ -1180,9 +1107,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1197,43 +1121,37 @@ MonoBehaviour: Data: _isMasterOnly - Name: $v Entry: 7 - Data: 81|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 82|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 70|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _isMasterOnly + - Name: k__BackingField Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _isMasterOnly - - Name: symbolUniqueName - Entry: 1 - Data: _isMasterOnly - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 83|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 71|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 84|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 72|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1243,9 +1161,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1260,43 +1175,37 @@ MonoBehaviour: Data: _nextPlaylistIndex - Name: $v Entry: 7 - Data: 85|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 86|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 73|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _nextPlaylistIndex + - Name: k__BackingField Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _nextPlaylistIndex - - Name: symbolUniqueName - Entry: 1 - Data: _nextPlaylistIndex - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 87|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 74|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 88|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 75|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1306,9 +1215,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1323,43 +1229,37 @@ MonoBehaviour: Data: _networkTimeVideoStart - Name: $v Entry: 7 - Data: 89|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 90|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 76|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _networkTimeVideoStart + - Name: k__BackingField Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _networkTimeVideoStart - - Name: symbolUniqueName - Entry: 1 - Data: _networkTimeVideoStart - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 91|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 77|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 92|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 78|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1369,9 +1269,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1386,37 +1283,31 @@ MonoBehaviour: Data: _localNetworkTimeStart - Name: $v Entry: 7 - Data: 93|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 94|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _localNetworkTimeStart - - Name: symbolUniqueName + Data: 79|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _localNetworkTimeStart - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 95|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 80|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 0 @@ -1426,9 +1317,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1443,43 +1331,37 @@ MonoBehaviour: Data: _ownerPlaying - Name: $v Entry: 7 - Data: 96|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 97|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 81|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _ownerPlaying + - Name: k__BackingField Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _ownerPlaying - - Name: symbolUniqueName - Entry: 1 - Data: _ownerPlaying - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 98|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib + Data: 82|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 99|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 83|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1489,9 +1371,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1506,44 +1385,37 @@ MonoBehaviour: Data: _ownerPaused - Name: $v Entry: 7 - Data: 100|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 101|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 84|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _ownerPaused + - Name: k__BackingField Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _ownerPaused - - Name: symbolUniqueName - Entry: 1 - Data: _ownerPaused - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 102|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 85|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 103|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 86|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1553,9 +1425,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1570,38 +1439,31 @@ MonoBehaviour: Data: _locallyPaused - Name: $v Entry: 7 - Data: 104|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 105|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _locallyPaused - - Name: symbolUniqueName + Data: 87|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _locallyPaused - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 106|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 88|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 0 @@ -1611,9 +1473,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1628,44 +1487,37 @@ MonoBehaviour: Data: _loopVideo - Name: $v Entry: 7 - Data: 107|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 108|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 89|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _loopVideo + - Name: k__BackingField Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _loopVideo - - Name: symbolUniqueName - Entry: 1 - Data: _loopVideo - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 109|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 90|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 110|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 91|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1675,9 +1527,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1692,38 +1541,31 @@ MonoBehaviour: Data: _localLoopVideo - Name: $v Entry: 7 - Data: 111|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 112|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _localLoopVideo - - Name: symbolUniqueName + Data: 92|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _localLoopVideo - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 113|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 93|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 0 @@ -1733,9 +1575,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1750,44 +1589,37 @@ MonoBehaviour: Data: _shuffleSeed - Name: $v Entry: 7 - Data: 114|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 115|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 94|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _shuffleSeed + - Name: k__BackingField Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _shuffleSeed - - Name: symbolUniqueName - Entry: 1 - Data: _shuffleSeed - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 116|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 95|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 117|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 96|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -1797,9 +1629,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1814,38 +1643,31 @@ MonoBehaviour: Data: _lastVideoTime - Name: $v Entry: 7 - Data: 118|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 119|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: _lastVideoTime - - Name: symbolUniqueName + Data: 97|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _lastVideoTime - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 120|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 98|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 0 @@ -1855,9 +1677,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -1872,107 +1691,43 @@ MonoBehaviour: Data: _registeredControlHandlers - Name: $v Entry: 7 - Data: 121|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 122|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 99|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _registeredControlHandlers + - Name: k__BackingField Entry: 7 - Data: 123|System.RuntimeType, mscorlib + Data: 100|System.RuntimeType, mscorlib - Name: Entry: 1 Data: UdonSharp.Video.VideoControlHandler[], Assembly-CSharp - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: UnityEngineComponentArray - - Name: symbolOriginalName - Entry: 1 - Data: _registeredControlHandlers - - Name: symbolUniqueName - Entry: 1 - Data: _registeredControlHandlers - - Name: symbolDefaultValue - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: fieldAttributes + - Name: k__BackingField Entry: 7 - Data: 124|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 101|System.RuntimeType, mscorlib - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: userBehaviourSource - Entry: 10 - Data: 1 - - Name: - Entry: 8 - Data: + Entry: 1 + Data: UnityEngine.Component[], UnityEngine.CoreModule - Name: Entry: 8 Data: - - Name: + - Name: k__BackingField Entry: 7 - Data: - - Name: $k - Entry: 1 - Data: _registeredScreenHandlers - - Name: $v - Entry: 7 - Data: 125|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 126|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 7 - Data: 127|System.RuntimeType, mscorlib - - Name: - Entry: 1 - Data: UdonSharp.Video.VideoScreenHandler[], Assembly-CSharp + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib - Name: - Entry: 8 - Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: UnityEngineComponentArray - - Name: symbolOriginalName - Entry: 1 - Data: _registeredScreenHandlers - - Name: symbolUniqueName - Entry: 1 - Data: _registeredScreenHandlers - - Name: symbolDefaultValue Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 128|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 102|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -1983,9 +1738,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 10 - Data: 2 - Name: Entry: 8 Data: @@ -1997,162 +1749,40 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: _registeredCallbackReceivers + Data: _registeredScreenHandlers - Name: $v Entry: 7 - Data: 129|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 130|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 7 - Data: 131|System.RuntimeType, mscorlib - - Name: - Entry: 1 - Data: UdonSharp.UdonSharpBehaviour[], UdonSharp.Runtime - - Name: - Entry: 8 - Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName + Data: 103|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 - Data: UnityEngineComponentArray - - Name: symbolOriginalName - Entry: 1 - Data: _registeredCallbackReceivers - - Name: symbolUniqueName - Entry: 1 - Data: _registeredCallbackReceivers - - Name: symbolDefaultValue - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: fieldAttributes + Data: _registeredScreenHandlers + - Name: k__BackingField Entry: 7 - Data: 132|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: + Data: 104|System.RuntimeType, mscorlib - Name: - Entry: 8 - Data: - - Name: userBehaviourSource - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 7 - Data: - - Name: $k - Entry: 1 - Data: MAX_RETRY_COUNT - - Name: $v - Entry: 7 - Data: 133|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 134|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName Entry: 1 - Data: MAX_RETRY_COUNT - - Name: symbolUniqueName - Entry: 1 - Data: MAX_RETRY_COUNT - - Name: symbolDefaultValue - Entry: 6 - Data: + Data: UdonSharp.Video.VideoScreenHandler[], Assembly-CSharp - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 9 + Data: 101 + - Name: k__BackingField Entry: 7 - Data: 135|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: userBehaviourSource - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 8 - Data: + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib - Name: - Entry: 7 - Data: - - Name: $k - Entry: 1 - Data: DEFAULT_RETRY_TIMEOUT - - Name: $v - Entry: 7 - Data: 136|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 137|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: DEFAULT_RETRY_TIMEOUT - - Name: symbolUniqueName - Entry: 1 - Data: DEFAULT_RETRY_TIMEOUT - - Name: symbolDefaultValue Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 138|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 105|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2163,9 +1793,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2177,156 +1804,40 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: RATE_LIMIT_RETRY_TIMEOUT + Data: _registeredCallbackReceivers - Name: $v Entry: 7 - Data: 139|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 140|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: RATE_LIMIT_RETRY_TIMEOUT - - Name: symbolUniqueName + Data: 106|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 - Data: RATE_LIMIT_RETRY_TIMEOUT - - Name: symbolDefaultValue - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: fieldAttributes + Data: _registeredCallbackReceivers + - Name: k__BackingField Entry: 7 - Data: 141|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: 107|System.RuntimeType, mscorlib - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: userBehaviourSource - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 7 - Data: - - Name: $k Entry: 1 - Data: VIDEO_ERROR_RETRY_TIMEOUT - - Name: $v - Entry: 7 - Data: 142|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 143|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: VIDEO_ERROR_RETRY_TIMEOUT - - Name: symbolUniqueName - Entry: 1 - Data: VIDEO_ERROR_RETRY_TIMEOUT - - Name: symbolDefaultValue - Entry: 6 - Data: + Data: UdonSharp.UdonSharpBehaviour[], UdonSharp.Runtime - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 9 + Data: 101 + - Name: k__BackingField Entry: 7 - Data: 144|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib - Name: - Entry: 8 - Data: - - Name: userBehaviourSource - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 7 - Data: - - Name: $k - Entry: 1 - Data: PLAYLIST_ERROR_RETRY_COUNT - - Name: $v - Entry: 7 - Data: 145|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 146|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: PLAYLIST_ERROR_RETRY_COUNT - - Name: symbolUniqueName - Entry: 1 - Data: PLAYLIST_ERROR_RETRY_COUNT - - Name: symbolDefaultValue Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 147|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 108|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2337,9 +1848,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2354,37 +1862,31 @@ MonoBehaviour: Data: _loadingVideo - Name: $v Entry: 7 - Data: 148|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 149|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _loadingVideo - - Name: symbolUniqueName + Data: 109|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _loadingVideo - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 150|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 110|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2395,9 +1897,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2412,153 +1911,31 @@ MonoBehaviour: Data: _currentLoadingTime - Name: $v Entry: 7 - Data: 151|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 152|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: _currentLoadingTime - - Name: symbolUniqueName + Data: 111|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _currentLoadingTime - - Name: symbolDefaultValue - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: fieldAttributes - Entry: 7 - Data: 153|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: userBehaviourSource - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 8 - Data: - - Name: - Entry: 7 - Data: - - Name: $k - Entry: 1 - Data: _currentRetryCount - - Name: $v - Entry: 7 - Data: 154|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 155|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + - Name: k__BackingField Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _currentRetryCount - - Name: symbolUniqueName - Entry: 1 - Data: _currentRetryCount - - Name: symbolDefaultValue - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: fieldAttributes + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField Entry: 7 - Data: 156|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], - mscorlib + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: userBehaviourSource Entry: 6 Data: - Name: Entry: 8 Data: - - Name: - Entry: 8 - Data: - - Name: + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: - - Name: $k - Entry: 1 - Data: _videoTargetStartTime - - Name: $v - Entry: 7 - Data: 157|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 158|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: _videoTargetStartTime - - Name: symbolUniqueName - Entry: 1 - Data: _videoTargetStartTime - - Name: symbolDefaultValue - Entry: 6 - Data: - - Name: - Entry: 8 - Data: - - Name: fieldAttributes - Entry: 7 - Data: 159|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 112|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2569,9 +1946,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2583,40 +1957,34 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: _playlistErrorCount - - Name: $v - Entry: 7 - Data: 160|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 161|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _playlistErrorCount - - Name: symbolUniqueName + Data: _currentRetryCount + - Name: $v + Entry: 7 + Data: 113|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 - Data: _playlistErrorCount - - Name: symbolDefaultValue + Data: _currentRetryCount + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 162|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 114|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2627,9 +1995,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2641,40 +2006,34 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: _waitForSync + Data: _videoTargetStartTime - Name: $v Entry: 7 - Data: 163|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 164|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName + Data: 115|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 - Data: _waitForSync - - Name: symbolUniqueName - Entry: 1 - Data: _waitForSync - - Name: symbolDefaultValue + Data: _videoTargetStartTime + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 165|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 116|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2685,9 +2044,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2699,40 +2055,34 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: PLAYER_MODE_UNITY + Data: _playlistErrorCount - Name: $v Entry: 7 - Data: 166|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 167|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName + Data: 117|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 - Data: PLAYER_MODE_UNITY - - Name: symbolUniqueName - Entry: 1 - Data: PLAYER_MODE_UNITY - - Name: symbolDefaultValue + Data: _playlistErrorCount + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 168|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 118|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2743,9 +2093,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2757,40 +2104,34 @@ MonoBehaviour: Data: - Name: $k Entry: 1 - Data: PLAYER_MODE_AVPRO + Data: _waitForSync - Name: $v Entry: 7 - Data: 169|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 170|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 258 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName + Data: 119|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: PLAYER_MODE_AVPRO - - Name: symbolUniqueName - Entry: 1 - Data: PLAYER_MODE_AVPRO - - Name: symbolDefaultValue + Data: _waitForSync + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 171|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 120|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2801,9 +2142,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2818,44 +2156,38 @@ MonoBehaviour: Data: currentPlayerMode - Name: $v Entry: 7 - Data: 172|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 173|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 121|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: currentPlayerMode + - Name: k__BackingField Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 3 Data: 1 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: currentPlayerMode - - Name: symbolUniqueName - Entry: 1 - Data: currentPlayerMode - - Name: symbolDefaultValue - Entry: 6 - Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 174|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 122|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 Data: 1 - Name: Entry: 7 - Data: 175|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime + Data: 123|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime - Name: Entry: 8 Data: @@ -2865,9 +2197,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2882,37 +2211,31 @@ MonoBehaviour: Data: _localPlayerMode - Name: $v Entry: 7 - Data: 176|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 177|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _localPlayerMode - - Name: symbolUniqueName + Data: 124|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _localPlayerMode - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 178|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 125|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2923,9 +2246,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2940,37 +2260,31 @@ MonoBehaviour: Data: _videoSync - Name: $v Entry: 7 - Data: 179|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 180|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _videoSync - - Name: symbolUniqueName + Data: 126|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _videoSync - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 181|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 127|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -2981,9 +2295,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -2998,37 +2309,31 @@ MonoBehaviour: Data: _ranInit - Name: $v Entry: 7 - Data: 182|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 183|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _ranInit - - Name: symbolUniqueName + Data: 128|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _ranInit - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 184|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 129|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3039,9 +2344,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3056,37 +2358,31 @@ MonoBehaviour: Data: _lastCurrentTime - Name: $v Entry: 7 - Data: 185|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 186|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName + Data: 130|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _lastCurrentTime - - Name: symbolUniqueName - Entry: 1 - Data: _lastCurrentTime - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 187|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 131|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3097,9 +2393,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3114,37 +2407,31 @@ MonoBehaviour: Data: _lastMasterLocked - Name: $v Entry: 7 - Data: 188|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 189|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _lastMasterLocked - - Name: symbolUniqueName + Data: 132|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _lastMasterLocked - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 190|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 133|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3155,9 +2442,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3172,37 +2456,31 @@ MonoBehaviour: Data: _lastSyncTime - Name: $v Entry: 7 - Data: 191|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 192|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 24 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemSingle - - Name: symbolOriginalName - Entry: 1 - Data: _lastSyncTime - - Name: symbolUniqueName + Data: 134|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _lastSyncTime - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 9 + Data: 20 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 193|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 135|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3213,9 +2491,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3230,37 +2505,31 @@ MonoBehaviour: Data: _delayedSyncAllowed - Name: $v Entry: 7 - Data: 194|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 195|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _delayedSyncAllowed - - Name: symbolUniqueName + Data: 136|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _delayedSyncAllowed - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 196|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 137|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3271,9 +2540,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3288,37 +2554,31 @@ MonoBehaviour: Data: _finalSyncCounter - Name: $v Entry: 7 - Data: 197|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 198|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 75 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemInt32 - - Name: symbolOriginalName - Entry: 1 - Data: _finalSyncCounter - - Name: symbolUniqueName + Data: 138|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _finalSyncCounter - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 9 + Data: 65 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 199|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 139|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3329,9 +2589,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3346,37 +2603,31 @@ MonoBehaviour: Data: _shuffled - Name: $v Entry: 7 - Data: 200|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 201|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType - Entry: 9 - Data: 8 - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemBoolean - - Name: symbolOriginalName - Entry: 1 - Data: _shuffled - - Name: symbolUniqueName + Data: 140|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField Entry: 1 Data: _shuffled - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 9 + Data: 7 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 202|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 141|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3387,9 +2638,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3404,43 +2652,37 @@ MonoBehaviour: Data: _lastStatusText - Name: $v Entry: 7 - Data: 203|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 204|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 142|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _lastStatusText + - Name: k__BackingField Entry: 7 - Data: 205|System.RuntimeType, mscorlib + Data: 143|System.RuntimeType, mscorlib - Name: Entry: 1 Data: System.String, mscorlib - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: SystemString - - Name: symbolOriginalName - Entry: 1 - Data: _lastStatusText - - Name: symbolUniqueName - Entry: 1 - Data: _lastStatusText - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 143 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 206|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 144|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3451,9 +2693,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: @@ -3468,43 +2707,37 @@ MonoBehaviour: Data: _lastAssignedRenderTexture - Name: $v Entry: 7 - Data: 207|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor - - Name: fieldSymbol - Entry: 7 - Data: 208|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor - - Name: internalType + Data: 145|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor + - Name: k__BackingField + Entry: 1 + Data: _lastAssignedRenderTexture + - Name: k__BackingField Entry: 7 - Data: 209|System.RuntimeType, mscorlib + Data: 146|System.RuntimeType, mscorlib - Name: Entry: 1 Data: UnityEngine.Texture, UnityEngine.CoreModule - Name: Entry: 8 Data: - - Name: declarationType - Entry: 3 - Data: 2 - - Name: syncMode - Entry: 3 - Data: 0 - - Name: symbolResolvedTypeName - Entry: 1 - Data: UnityEngineTexture - - Name: symbolOriginalName - Entry: 1 - Data: _lastAssignedRenderTexture - - Name: symbolUniqueName - Entry: 1 - Data: _lastAssignedRenderTexture - - Name: symbolDefaultValue + - Name: k__BackingField + Entry: 9 + Data: 146 + - Name: k__BackingField + Entry: 7 + Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib + - Name: Entry: 6 Data: - Name: Entry: 8 Data: - - Name: fieldAttributes + - Name: k__BackingField + Entry: 5 + Data: false + - Name: _fieldAttributes Entry: 7 - Data: 210|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], + Data: 147|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib - Name: Entry: 12 @@ -3515,9 +2748,6 @@ MonoBehaviour: - Name: Entry: 8 Data: - - Name: userBehaviourSource - Entry: 6 - Data: - Name: Entry: 8 Data: diff --git a/Assets/USharpVideo/Scripts/USharpVideoPlayer.cs b/Assets/USharpVideo/Scripts/USharpVideoPlayer.cs index e029779..3f089e2 100644 --- a/Assets/USharpVideo/Scripts/USharpVideoPlayer.cs +++ b/Assets/USharpVideo/Scripts/USharpVideoPlayer.cs @@ -1,1576 +1,1605 @@ - -#define USE_SERVER_TIME_MS // Uses GetServerTimeMilliseconds instead of the server datetime which in theory is less reliable - -using JetBrains.Annotations; -using UdonSharp; -using UnityEngine; -using VRC.SDK3.Components.Video; -using VRC.SDKBase; - -namespace UdonSharp.Video -{ - [AddComponentMenu("Udon Sharp/Video/USharp Video Player")] - [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] - public class USharpVideoPlayer : UdonSharpBehaviour - { - // Video player references - private VideoPlayerManager _videoPlayerManager; - - [Tooltip("Whether to allow video seeking with the progress bar on the video")] - [PublicAPI] - public bool allowSeeking = true; - - [Tooltip("If enabled defaults to unlocked so anyone can put in a URL")] - [SerializeField] - private bool defaultUnlocked = true; - - [Tooltip("If enabled allows the instance creator to always control the video player regardless of if they are master or not")] - [PublicAPI] - public bool allowInstanceCreatorControl = true; - - [Tooltip("How often the video player should check if it is more than Sync Threshold out of sync with the video time")] - [PublicAPI] - public float syncFrequency = 8.0f; - [Tooltip("How many seconds desynced from the owner the client needs to be to trigger a resync")] - [PublicAPI] - public float syncThreshold = 0.85f; - - [Range(0f, 1f)] - [Tooltip("The default volume for the volume slider on the video player")] - [SerializeField] - private float defaultVolume = 0.5f; - -#pragma warning disable CS0414 - [Tooltip("The max range of the audio sources on this video player")] - [SerializeField] - private float audioRange = 40f; -#pragma warning restore CS0414 - - /// - /// Local offset from the network time to sync the video - /// Can be used for things like making a video player sync up with someone singing - /// - [PublicAPI, System.NonSerialized] - public float localSyncOffset; - - [Tooltip("List of urls to play automatically when the world is loaded until someone puts in another URL")] - public VRCUrl[] playlist = new VRCUrl[0]; - - [Tooltip("Should default to the stream player? This is usually used when you want to put a live stream in the default playlist.")] - [SerializeField] - private bool defaultStreamMode; - - [Tooltip("If the default playlist should loop")] - [PublicAPI] - public bool loopPlaylist; - - [Tooltip("If the default playlist should be shuffled upon world load")] - public bool shufflePlaylist; - - /// - /// The URL that we should currently be playing and that other people are playing - /// - [UdonSynced] - private VRCUrl _syncedURL = VRCUrl.Empty; - - /// - /// The video sequence identifier, gets incremented whenever a new video is put in. Used to determine in combination with _currentVideoIdx if we need to load the new URL - /// - [UdonSynced] - private int _syncedVideoIdx; - private int _currentVideoIdx; - - /// - /// If we're locked so only the master may put in URLs - /// - [UdonSynced] - private bool _isMasterOnly = true; - - [UdonSynced] - private int _nextPlaylistIndex; - -#if USE_SERVER_TIME_MS - [UdonSynced] - private int _networkTimeVideoStart; - private int _localNetworkTimeStart; -#else - [UdonSynced] - private double _videoStartNetworkTime; - private double _localVideoStartTime; - - [UdonSynced] - private long _networkTimeStart; - private System.DateTime _localNetworkTimeStart; -#endif - - [UdonSynced] - private bool _ownerPlaying; - - [UdonSynced] - private bool _ownerPaused; - private bool _locallyPaused; - - [UdonSynced] - private bool _loopVideo; - private bool _localLoopVideo; - - [UdonSynced] - private int _shuffleSeed; - - // The last unpaused time in the video - private float _lastVideoTime; - - VideoControlHandler[] _registeredControlHandlers; - VideoScreenHandler[] _registeredScreenHandlers; - UdonSharpBehaviour[] _registeredCallbackReceivers; - - // Video loading state - const int MAX_RETRY_COUNT = 4; - const float DEFAULT_RETRY_TIMEOUT = 35.0f; - const float RATE_LIMIT_RETRY_TIMEOUT = 5.5f; - const float VIDEO_ERROR_RETRY_TIMEOUT = 5f; - const float PLAYLIST_ERROR_RETRY_COUNT = 4; - - private bool _loadingVideo; - private float _currentLoadingTime; // Counts down to 0 while loading - private int _currentRetryCount; - private float _videoTargetStartTime; - private int _playlistErrorCount; - - private bool _waitForSync; - - // Player mode tracking - const int PLAYER_MODE_UNITY = 0; - const int PLAYER_MODE_AVPRO = 1; - - [UdonSynced] - private int currentPlayerMode = PLAYER_MODE_UNITY; - private int _localPlayerMode = PLAYER_MODE_UNITY; - - private bool _videoSync = true; - - private bool _ranInit; - - private void Start() - { - if (_ranInit) - return; - - _ranInit = true; - - _videoPlayerManager = GetVideoManager(); - _videoPlayerManager.Start(); - - if (_registeredControlHandlers == null) - _registeredControlHandlers = new VideoControlHandler[0]; - - if (_registeredCallbackReceivers == null) - _registeredCallbackReceivers = new UdonSharpBehaviour[0]; - - if (Networking.IsOwner(gameObject)) - { - if (defaultUnlocked) - _isMasterOnly = false; - - if (defaultStreamMode) - { - SetPlayerMode(PLAYER_MODE_AVPRO); - _nextPlaylistIndex = 0; // SetPlayerMode sets this to -1, but we want to be able to keep it intact so reset to 0 - } - - _shuffleSeed = Random.Range(0, 10000); - } - - _lastMasterLocked = _isMasterOnly; - - SetUILocked(_isMasterOnly); - -#if !USE_SERVER_TIME_MS - _networkTimeStart = Networking.GetNetworkDateTime().Ticks; - _localNetworkTimeStart = new System.DateTime(_networkTimeStart, System.DateTimeKind.Utc); -#endif - - PlayNextVideoFromPlaylist(); - - SetVolume(defaultVolume); - - // Serialize the default setup state from the master once regardless of if a video has played - QueueSerialize(); - - LogMessage("USharpVideo v1.0.1 Initialized"); - } - - public override void OnVideoReady() - { - ResetVideoLoad(); - _playlistErrorCount = 0; - - if (IsUsingAVProPlayer()) - { - float duration = _videoPlayerManager.GetDuration(); - - if (duration == float.MaxValue || float.IsInfinity(duration) || IsRTSPStream()) - _videoSync = false; - else - _videoSync = true; - } - else - _videoSync = true; - - if (_videoSync) - { - if (Networking.IsOwner(gameObject)) - { - _waitForSync = false; - _videoPlayerManager.Play(); - } - else - { - if (_ownerPlaying) - { - _waitForSync = false; - _locallyPaused = false; - _videoPlayerManager.Play(); - - SyncVideo(); - } - else - { -#if USE_SERVER_TIME_MS - if (_networkTimeVideoStart == 0) -#else - if (_videoStartNetworkTime == 0f || _videoStartNetworkTime > GetNetworkTime() - _videoPlayerManager.GetDuration()) // Todo: remove the 0f check and see how this actually gets set to 0 while the owner is playing -#endif - { - _waitForSync = true; - SetStatusText("Waiting for owner sync..."); - } - else - { - _waitForSync = false; - SyncVideo(); - SetStatusText(""); -#if USE_SERVER_TIME_MS - LogMessage($"Loaded into world with complete video, duration: {_videoPlayerManager.GetDuration()}, start net time: {_networkTimeVideoStart}"); -#else - LogMessage($"Loaded into world with complete video, duration: {_videoPlayerManager.GetDuration()}, start net time: {_videoStartNetworkTime}, subtracted net time {GetNetworkTime() - _videoPlayerManager.GetDuration()}"); -#endif - } - } - } - } - else // Live streams should start asap - { - _waitForSync = false; - _videoPlayerManager.Play(); - } - } - - public override void OnVideoStart() - { - if (Networking.IsOwner(gameObject)) - { - SetPausedInternal(false, false); - -#if USE_SERVER_TIME_MS - _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds() - (int)(_videoTargetStartTime * 1000f); -#else - _videoStartNetworkTime = GetNetworkTime() - _videoTargetStartTime; -#endif - - if (IsInVideoMode()) - { - _videoPlayerManager.SetTime(_videoTargetStartTime); - } - - _ownerPlaying = true; - - QueueSerialize(); - - LogMessage($"Started video: {_syncedURL}"); - } - else if (!_ownerPlaying) // Watchers pause and wait for sync from owner - { - _videoPlayerManager.Pause(); - _waitForSync = true; - } - else - { - SetPausedInternal(_ownerPaused, false); - SyncVideo(); - LogMessage($"Started video: {_syncedURL}"); - } - - SetStatusText(""); - - _videoTargetStartTime = 0f; - - SetUIPaused(_locallyPaused); - - UpdateRenderTexture(); - - SendCallback("OnUSharpVideoPlay"); - } - - private bool IsRTSPStream() - { - string urlStr = _syncedURL.ToString(); - - return IsUsingAVProPlayer() && - _videoPlayerManager.GetDuration() == 0f && - IsRTSPURL(urlStr); - } - - private bool IsRTSPURL(string urlStr) - { - return urlStr.StartsWith("rtsp://", System.StringComparison.OrdinalIgnoreCase) || - urlStr.StartsWith("rtmp://", System.StringComparison.OrdinalIgnoreCase) || // RTMP isn't really supported in VRC's context and it's probably never going to be, but we'll just be safe here - urlStr.StartsWith("rtspt://", System.StringComparison.OrdinalIgnoreCase) || // rtsp over TCP - urlStr.StartsWith("rtspu://", System.StringComparison.OrdinalIgnoreCase); // rtsp over UDP - } - - public override void OnVideoEnd() - { - // VRC falsely throws OnVideoEnd instantly on RTSP streams since they report 0 length - if (IsRTSPStream()) - return; - - if (Networking.IsOwner(gameObject)) - { - _ownerPlaying = false; - _ownerPaused = _locallyPaused = false; - - SetStatusText(""); - SetUIPaused(false); - - PlayNextVideoFromPlaylist(); - QueueSerialize(); - } - - SendCallback("OnUSharpVideoEnd"); - - UpdateRenderTexture(); - } - - // Workaround for bug that needs to be addressed in U# where calling built in methods with parameters will get the parameters overwritten when called from other UdonBehaviours - public void _OnVideoErrorCallback(VideoError videoError) - { - OnVideoError(videoError); - } - - public override void OnVideoError(VideoError videoError) - { - if (videoError == VideoError.RateLimited) - { - SetStatusText("Rate limited, retrying..."); - LogWarning("Rate limited, retrying..."); - _currentLoadingTime = RATE_LIMIT_RETRY_TIMEOUT; - return; - } - - if (videoError == VideoError.PlayerError) - { - SetStatusText("Video error, retrying..."); - LogError("Video player error when trying to load " + _syncedURL); - _loadingVideo = true; // Apparently OnVideoReady gets fired erroneously?? - _currentLoadingTime = VIDEO_ERROR_RETRY_TIMEOUT; - return; - } - - ResetVideoLoad(); - _videoTargetStartTime = 0f; - - _videoPlayerManager.Stop(); - - LogError($"Video '{_syncedURL}' failed to play with error {videoError}"); - - switch (videoError) - { - case VideoError.InvalidURL: - SetStatusText("Invalid URL"); - break; - case VideoError.AccessDenied: - SetStatusText("Video blocked, enable untrusted URLs"); - break; - default: - SetStatusText("Failed to load video"); - break; - } - - ++_playlistErrorCount; - PlayNextVideoFromPlaylist(); - - SendCallback("OnUSharpVideoError"); - } - - public override void OnVideoPause() { } - public override void OnVideoPlay() { } - - public override void OnVideoLoop() - { -#if USE_SERVER_TIME_MS - _localNetworkTimeStart = _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds(); -#else - _localVideoStartTime = _videoStartNetworkTime = GetNetworkTime(); -#endif - - QueueSerialize(); - } - - private float _lastCurrentTime; - - private void Update() - { - if (_loadingVideo) - UpdateVideoLoad(); - - if (_locallyPaused) - { - if (IsInVideoMode()) - { - // Keep the target time the same while paused -#if USE_SERVER_TIME_MS - _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds() - (int)(_videoPlayerManager.GetTime() * 1000f); -#else - _videoStartNetworkTime = GetNetworkTime() - _videoPlayerManager.GetTime(); -#endif - } - } - else - _lastCurrentTime = _videoPlayerManager.GetTime(); - - if (Networking.IsOwner(gameObject) || !_waitForSync) - { - SyncVideoIfTime(); - } - else if (_ownerPlaying) - { - _videoPlayerManager.Play(); - LogMessage($"Started video: {_syncedURL}"); - _waitForSync = false; - SyncVideo(); - } - - UpdateRenderTexture(); // Needed because AVPro can swap textures whenever - } - - /// - /// Uncomment this to prevent people from taking ownership of the video player when they shouldn't be able to - /// - //public override bool OnOwnershipRequest(VRCPlayerApi requestingPlayer, VRCPlayerApi requestedOwner) - //{ - // return !_isMasterOnly || IsPrivlegedUser(requestedOwner); - //} - - private bool _lastMasterLocked; - - public override void OnDeserialization() - { - if (Networking.IsOwner(gameObject)) - return; - -#if !USE_SERVER_TIME_MS - _localNetworkTimeStart = new System.DateTime(_networkTimeStart, System.DateTimeKind.Utc); -#endif - - SetPausedInternal(_ownerPaused, false); - SetLoopingInternal(_loopVideo); - - if (_localPlayerMode != currentPlayerMode) - SetPlayerMode(currentPlayerMode); - - if (_isMasterOnly != _lastMasterLocked) - SetLockedInternal(_isMasterOnly); - - if (!_ownerPlaying && _videoPlayerManager.IsPlaying()) - _videoPlayerManager.Stop(); - - if (_currentVideoIdx != _syncedVideoIdx) - { - _currentVideoIdx = _syncedVideoIdx; - - _videoPlayerManager.Stop(); - StartVideoLoad(_syncedURL); - -#if USE_SERVER_TIME_MS - _localNetworkTimeStart = _networkTimeVideoStart; -#else - _localVideoStartTime = _videoStartNetworkTime; -#endif - - LogMessage("Playing synced " + _syncedURL); - } -#if USE_SERVER_TIME_MS - else if (_networkTimeVideoStart != _localNetworkTimeStart) // Detect seeks - { - _localNetworkTimeStart = _networkTimeVideoStart; -#else - else if (_videoStartNetworkTime != _localVideoStartTime) // Detect seeks - { - _localVideoStartTime = _videoStartNetworkTime; -#endif - SyncVideo(); - } - - if (!_locallyPaused && IsInVideoMode()) - { - float duration = GetVideoManager().GetDuration(); - - // If the owner did a seek on the video after it finished, we need to start playing it again -#if USE_SERVER_TIME_MS - if ((Networking.GetServerTimeInMilliseconds() - _networkTimeVideoStart) / 1000f < duration - 3f) -#else - if (GetNetworkTime() - _videoStartNetworkTime < duration - 3f) -#endif - _videoPlayerManager.Play(); - } - - SendCallback("OnUSharpVideoDeserialization"); - } - - public override void OnOwnershipTransferred(VRCPlayerApi player) - { - SendUIOwnerUpdate(); - - SendCallback("OnUSharpVideoOwnershipChange"); - } - - // Supposedly there's some case where late joiners don't receive data, so do a serialization just in case here. - public override void OnPlayerJoined(VRCPlayerApi player) - { - if (!player.isLocal) - QueueSerialize(); - } - - /// - /// Stops playback of the video completely and clears data - /// - [PublicAPI] - public void StopVideo() - { - if (!Networking.IsOwner(gameObject)) - return; - -#if USE_SERVER_TIME_MS - _networkTimeVideoStart = 0; -#else - _videoStartNetworkTime = 0f; -#endif - _ownerPlaying = false; - _locallyPaused = _ownerPaused = false; - _videoTargetStartTime = 0f; - _lastCurrentTime = 0f; - - _videoPlayerManager.Stop(); - SetUIPaused(false); - ResetVideoLoad(); - - QueueSerialize(); - - SendCallback("OnUSharpVideoStop"); - } - - /// - /// Play a video with the specified URL, only works if the player is allowed to use the video player - /// - /// - [PublicAPI] - public void PlayVideo(VRCUrl url) - { - PlayVideoInternal(url, true); - } - - /// - /// Returns the URL that the video player currently has loaded - /// - /// - [PublicAPI] - public VRCUrl GetCurrentURL() => _syncedURL; - - private void PlayVideoInternal(VRCUrl url, bool stopPlaylist) - { - if (!CanControlVideoPlayer()) - return; - - string urlStr = url.Get(); - - if (!ValidateURL(urlStr)) - return; - - bool wasOwner = Networking.IsOwner(gameObject); - - TakeOwnership(); - - if (stopPlaylist) - _nextPlaylistIndex = -1; - - StopVideo(); - - _syncedURL = url; - - if (wasOwner) - ++_syncedVideoIdx; - else // Add two to avoid having conflicts where the old owner increases the count - _syncedVideoIdx += 2; - - _currentVideoIdx = _syncedVideoIdx; - - StartVideoLoad(url); - _ownerPlaying = false; -#if USE_SERVER_TIME_MS - _networkTimeVideoStart = 0; -#endif - - _videoTargetStartTime = GetVideoStartTime(urlStr); - - QueueSerialize(); - - SendCallback("OnUSharpVideoLoadStart"); - } - - private void ResetVideoLoad() - { - _loadingVideo = false; - _currentRetryCount = 0; - _currentLoadingTime = DEFAULT_RETRY_TIMEOUT; - } - - private void UpdateVideoLoad() - { - //if (_loadingVideo) // Checked in caller now since it's cheaper - { - _currentLoadingTime -= Time.deltaTime; - - if (_currentLoadingTime <= 0f) - { - _currentLoadingTime = DEFAULT_RETRY_TIMEOUT; - - if (++_currentRetryCount > MAX_RETRY_COUNT) - { - OnVideoError(VideoError.Unknown); - } - else - { - LogMessage("Retrying load"); - - SetStatusText("Retrying load..."); - _videoPlayerManager.LoadURL(_syncedURL); - } - } - } - } - - private float _lastSyncTime; - - private void SyncVideoIfTime() - { - float timeSinceStartup = Time.realtimeSinceStartup; - - if (timeSinceStartup - _lastSyncTime > syncFrequency) - { - _lastSyncTime = timeSinceStartup; - SyncVideo(); - } - } - - /// - /// Syncs the video time if it's too far diverged from the network time - /// - [PublicAPI] - public void SyncVideo() - { - if (IsInVideoMode()) - { -#if USE_SERVER_TIME_MS - float offsetTime = Mathf.Clamp((Networking.GetServerTimeInMilliseconds() - _networkTimeVideoStart) / 1000f + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); -#else - float offsetTime = Mathf.Clamp((float)(GetNetworkTime() - _videoStartNetworkTime) + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); -#endif - - if (Mathf.Abs(_videoPlayerManager.GetTime() - offsetTime) > syncThreshold) - { - _videoPlayerManager.SetTime(offsetTime); - LogMessage($"Syncing video to {offsetTime:N2}"); - } - } - } - - /// - /// Syncs the video time regardless of how far diverged it is from the network time, can be used as a less aggressive audio resync in some cases - /// - [PublicAPI] - public void ForceSyncVideo() - { - if (IsInVideoMode()) - { -#if USE_SERVER_TIME_MS - float offsetTime = Mathf.Clamp((Networking.GetServerTimeInMilliseconds() - _networkTimeVideoStart) / 1000f + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); -#else - float offsetTime = Mathf.Clamp((float)(GetNetworkTime() - _videoStartNetworkTime) + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); -#endif - - float syncNudgeTime = Mathf.Max(0f, offsetTime - 1f); - _videoPlayerManager.SetTime(syncNudgeTime); // Seek to slightly earlier before syncing to the real time to get the video player to jump cleanly - _videoPlayerManager.SetTime(offsetTime); - LogMessage($"Syncing video to {offsetTime:N2}"); - } - } - - private void StartVideoLoad(VRCUrl url) - { -#if UNITY_EDITOR - LogMessage($"Started video load for URL: {url}"); -#else - LogMessage($"Started video load for URL: {url}, requested by {Networking.GetOwner(gameObject).displayName}"); -#endif - - SetStatusText("Loading video..."); - ResetVideoLoad(); - _loadingVideo = true; - _videoPlayerManager.LoadURL(url); - - AddUIUrlHistory(url); - } - - private void SetPausedInternal(bool paused, bool updatePauseTime) - { - if (Networking.IsOwner(gameObject)) - _ownerPaused = paused; - - if (_locallyPaused != paused) - { - _locallyPaused = paused; - - if (IsInVideoMode()) - { - if (_ownerPaused) - _videoPlayerManager.Pause(); - else - { - _videoPlayerManager.Play(); - if (updatePauseTime) - _videoTargetStartTime = _lastCurrentTime; - } - } - else - { - if (_ownerPaused) - _videoPlayerManager.Stop(); - else - StartVideoLoad(_syncedURL); - } - - SetUIPaused(paused); - - if (_locallyPaused) - SendCallback("OnUSharpVideoPause"); - else - SendCallback("OnUSharpVideoUnpause"); - } - - QueueRateLimitedSerialize(); - } - - /// - /// Pauses the video if we have control of the video player. - /// - /// - [PublicAPI] - public void SetPaused(bool paused) - { - if (Networking.IsOwner(gameObject)) - SetPausedInternal(paused, true); - } - - [PublicAPI] - public bool IsPaused() - { - return _ownerPaused; - } - - private void SetLoopingInternal(bool loop) - { - if (loop == _localLoopVideo) - return; - - _loopVideo = _localLoopVideo = loop; - - _videoPlayerManager.SetLooping(loop); - - SetUILooping(loop); - - QueueRateLimitedSerialize(); - } - - /// - /// Sets whether the currently playing video should loop and restart once it ends. - /// - /// - [PublicAPI] - public void SetLooping(bool loop) - { - if (Networking.IsOwner(gameObject)) - SetLoopingInternal(loop); - } - - [PublicAPI] - public bool IsLooping() - { - return _localLoopVideo; - } - - [PublicAPI] - public float GetVolume() => _videoPlayerManager.GetVolume(); - - /// - /// Sets the audio source volume for the audio sources used by this video player. - /// - /// - [PublicAPI] - public void SetVolume(float volume) - { - volume = Mathf.Clamp01(volume); - if (volume == _videoPlayerManager.GetVolume()) - return; - - _videoPlayerManager.SetVolume(volume); - SetUIVolume(volume); - } - - [PublicAPI] - public bool IsMuted() => _videoPlayerManager.IsMuted(); - - /// - /// Mutes audio from this video player - /// - /// - [PublicAPI] - public void SetMuted(bool muted) - { - _videoPlayerManager.SetMuted(muted); - SetUIMuted(muted); - } - - private bool _delayedSyncAllowed = true; - private int _finalSyncCounter; - - /// - /// Takes a float in the range 0 to 1 and seeks the video to that % through the time - /// Is intended to be used with progress bar-type-things - /// - /// - [PublicAPI] - public void SeekTo(float progress) - { - if (!allowSeeking || !Networking.IsOwner(gameObject)) - return; - - float newTargetTime = _videoPlayerManager.GetDuration() * progress; - _lastVideoTime = newTargetTime; - _lastCurrentTime = newTargetTime; - -#if USE_SERVER_TIME_MS - _localNetworkTimeStart = _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds() - (int)(newTargetTime * 1000f); -#else - _videoStartNetworkTime = GetNetworkTime() - newTargetTime; - _localVideoStartTime = _videoStartNetworkTime; -#endif - - if (!_locallyPaused && !GetVideoManager().IsPlaying()) - GetVideoManager().Play(); - - SyncVideo(); - QueueRateLimitedSerialize(); - } - - /// - /// Used on things that are easily spammable to prevent flooding the network unintentionally. - /// Will allow 1 sync every half second and then will send a final sync to propagate the final changed values of things at the end - /// - private void QueueRateLimitedSerialize() - { - //QueueSerialize(); // Debugging line :D this serialization method can potentially hide some issues so we want to disable it sometimes and verify stuff works right - - if (_delayedSyncAllowed) - { - QueueSerialize(); - _delayedSyncAllowed = false; - SendCustomEventDelayedSeconds(nameof(_UnlockDelayedSync), 0.5f); - } - - ++_finalSyncCounter; - SendCustomEventDelayedSeconds(nameof(_SendFinalSync), 0.8f); - } - - public void _UnlockDelayedSync() - { - _delayedSyncAllowed = true; - } - - // Handles running a final sync after doing a QueueRateLimitedSerialize, so that the final changes of the seek time get propagated - public void _SendFinalSync() - { - if (--_finalSyncCounter == 0) - QueueSerialize(); - } - - /// - /// Determines if the local player can control this video player. This means the player is either the master, the instance creator, or the video player is unlocked. - /// - /// - [PublicAPI] - public bool CanControlVideoPlayer() - { - return !_isMasterOnly || IsPrivilegedUser(Networking.LocalPlayer); - } - - /// - /// If the given player is allowed to take important actions on this video player such as changing the video or locking the video player. - /// This is what you would extend if you want to add an access control list or something similar. - /// - /// - /// - [PublicAPI] - public bool IsPrivilegedUser(VRCPlayerApi player) - { -#if UNITY_EDITOR - if (player == null) - return true; -#endif - - return player.isMaster || (allowInstanceCreatorControl && player.isInstanceOwner); - } - - /// - /// Takes ownership of the video player if allowed - /// - [PublicAPI] - public void TakeOwnership() - { - if (Networking.IsOwner(gameObject)) - return; - - if (CanControlVideoPlayer()) - Networking.SetOwner(Networking.LocalPlayer, gameObject); - } - - [PublicAPI] - public void QueueSerialize() - { - if (!Networking.IsOwner(gameObject)) - return; - - RequestSerialization(); - } - - private bool _shuffled; - - /// - /// Plays the next video from the video player's built-in playlist - /// - [PublicAPI] - public void PlayNextVideoFromPlaylist() - { - if (_nextPlaylistIndex == -1 || playlist.Length == 0 || !Networking.IsOwner(gameObject)) - return; - - if (loopPlaylist && _playlistErrorCount > PLAYLIST_ERROR_RETRY_COUNT) - { - LogError("Maximum number of retries for playlist video looping hit. Stopping playlist playback."); - _nextPlaylistIndex = -1; - return; - } - - if (shufflePlaylist && !_shuffled) - { - Random.InitState(_shuffleSeed); - - for (int i = 0; i < playlist.Length - 1; ++i) - { - int r = Random.Range(i, playlist.Length); - if (i == r) { continue; } - VRCUrl flipVal = playlist[r]; - playlist[r] = playlist[i]; - playlist[i] = flipVal; - } - - _shuffled = true; - } - - int currentIdx = _nextPlaylistIndex++; - - if (currentIdx >= playlist.Length) - { - if (loopPlaylist) - { - _nextPlaylistIndex = 1; - currentIdx = 0; - } - else - { - // We reached the end of the playlist - _nextPlaylistIndex = -1; - return; - } - } - - PlayVideoInternal(playlist[currentIdx], false); - } - - [PublicAPI] - public int GetPlaylistIndex() => _nextPlaylistIndex > 0 ? _nextPlaylistIndex - 1 : -1; - - [PublicAPI] - public void SetNextPlaylistVideo(int nextPlaylistIdx) - { - _nextPlaylistIndex = nextPlaylistIdx; - } - - /// - /// Sets whether this video player is locked to the master which means only the master has the ability to put new videos in. - /// - /// - [PublicAPI] - public void SetLocked(bool locked) - { - if (Networking.IsOwner(gameObject)) - SetLockedInternal(locked); - } - - private void SetLockedInternal(bool locked) - { - _isMasterOnly = locked; - _lastMasterLocked = _isMasterOnly; - - SetUILocked(locked); - - QueueSerialize(); - - SendCallback("OnUSharpVideoLockChange"); - } - - [PublicAPI] - public bool IsLocked() - { - return _isMasterOnly; - } - - /// - /// Sets the video player to use the Unity video player as a backend - /// - [PublicAPI] - public void SetToUnityPlayer() - { - if (CanControlVideoPlayer()) - { - TakeOwnership(); - SetPlayerMode(PLAYER_MODE_UNITY); - } - } - - /// - /// Sets the video player to use AVPro as the backend. - /// AVPro supports streams so this is aliased in UI as the "Stream" player to avoid confusion - /// - [PublicAPI] - public void SetToAVProPlayer() - { - if (CanControlVideoPlayer()) - { - TakeOwnership(); - SetPlayerMode(PLAYER_MODE_AVPRO); - } - } - - private void SetPlayerMode(int newPlayerMode) - { - if (_localPlayerMode == newPlayerMode) - return; - - StopVideo(); - - if (Networking.IsOwner(gameObject)) - _syncedURL = VRCUrl.Empty; - - currentPlayerMode = newPlayerMode; - - _locallyPaused = _ownerPaused = false; - - _nextPlaylistIndex = -1; - - _localPlayerMode = newPlayerMode; - - ResetVideoLoad(); - - if (IsUsingUnityPlayer()) - { - _videoPlayerManager.SetToVideoPlayerMode(); - SetUIToVideoMode(); - } - else - { - _videoPlayerManager.SetToStreamPlayerMode(); - SetUIToStreamMode(); - } - - QueueSerialize(); - - UpdateRenderTexture(); - - SendCallback("OnUSharpVideoModeChange"); - } - - /// - /// Are we playing a standard video where we know the length and need to sync its time across clients? - /// - /// - [PublicAPI] - public bool IsInVideoMode() - { - return _videoSync; - } - - /// - /// Are we playing some type of live stream where we do not know the length of the stream and do not need to sync time across clients? - /// - /// - [PublicAPI] - public bool IsInStreamMode() - { - return !_videoSync; - } - - [PublicAPI] - public bool IsUsingUnityPlayer() - { - return _localPlayerMode == PLAYER_MODE_UNITY; - } - - [PublicAPI] - public bool IsUsingAVProPlayer() - { - return _localPlayerMode == PLAYER_MODE_AVPRO; - } - - /// - /// Reloads the video on the video player, usually used if the video playback has encountered some internal issue or if the audio has gotten desynced from the video - /// - [PublicAPI] - public void Reload() - { - if ((_ownerPlaying || Networking.IsOwner(gameObject)) && !_loadingVideo) - { - StartVideoLoad(_syncedURL); - - if (Networking.IsOwner(gameObject)) - _videoTargetStartTime = GetVideoManager().GetTime(); - - SendCallback("OnUSharpVideoReload"); - } - } - - public VideoPlayerManager GetVideoManager() - { - if (_videoPlayerManager) - return _videoPlayerManager; - - _videoPlayerManager = GetComponentInChildren(true); - if (_videoPlayerManager == null) - LogError("Video Player Manager not found, make sure you have a manager setup properly"); - - return _videoPlayerManager; - } - -#region Utilities - /// - /// Parses the start time of a YouTube video from the URL. - /// If no time is found or given URL is not a YouTube URL, returns 0.0 - /// - /// - /// - private float GetVideoStartTime(string url) - { - // Attempt to parse out a start time from YouTube links with t= or start= - if (url.Contains("youtube.com/watch") || - url.Contains("youtu.be/")) - { - int tIndex = url.IndexOf("?t=", System.StringComparison.Ordinal); - if (tIndex == -1) tIndex = url.IndexOf("&t=", System.StringComparison.Ordinal); - if (tIndex == -1) tIndex = url.IndexOf("?start=", System.StringComparison.Ordinal); - if (tIndex == -1) tIndex = url.IndexOf("&start=", System.StringComparison.Ordinal); - - if (tIndex == -1) - return 0f; - - char[] urlArr = url.ToCharArray(); - int numIdx = url.IndexOf('=', tIndex) + 1; - - string intStr = ""; - - while (numIdx < urlArr.Length) - { - char currentChar = urlArr[numIdx]; - if (!char.IsNumber(currentChar)) - break; - - intStr += currentChar; - - ++numIdx; - } - - if (string.IsNullOrWhiteSpace(intStr)) - return 0f; - - int secondsCount = 0; - if (int.TryParse(intStr, out secondsCount)) - return secondsCount; - } - - return 0f; - } - - /// - /// Checks for URL sanity and throws warnings if it's not nice. - /// - /// - private bool ValidateURL(string url) - { - if (url.Contains("youtube.com/watch") || - url.Contains("youtu.be/")) - { - if (url.IndexOf("&list=", System.StringComparison.OrdinalIgnoreCase) != -1) - LogWarning($"URL '{url}' input with playlist link, this can slow down YouTubeDL link resolves significantly see: https://vrchat.canny.io/feature-requests/p/add-no-playlist-to-ytdl-arguments-for-url-resolution"); - } - - if (string.IsNullOrWhiteSpace(url)) // Don't do anything if the player entered an empty URL by accident - return false; - - //if (!url.StartsWith("https://", System.StringComparison.OrdinalIgnoreCase) && - // !url.StartsWith("http://", System.StringComparison.OrdinalIgnoreCase) && - // !IsRTSPURL(url)) - int idx = url.IndexOf("://", System.StringComparison.Ordinal); - if (idx < 1 || idx > 8) // I'm not sure exactly what rule VRC uses so just check for the :// in an expected spot since it seems like VRC checks that it has a protocol at least. - { - LogError($"Invalid URL '{url}' provided"); - SetStatusText("Invalid URL"); - SendCustomEventDelayedSeconds(nameof(_LateClearStatusInternal), 2f); - return false; - } - - // Longer than most browsers support, see: https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers. I'm not sure if this length will even play in the video player. - // Most CDN's keep their URLs under 1000 characters so this should be more than reasonable - // Prevents people from pasting a book and breaking sync on the video player xd - if (url.Length > 4096) - { - LogError($"Video URL is too long! url: '{url}'"); - SetStatusText("Invalid URL"); - SendCustomEventDelayedSeconds(nameof(_LateClearStatusInternal), 2f); - return false; - } - - return true; - } - - public void _LateClearStatusInternal() - { - if (_videoPlayerManager.IsPlaying() && !_loadingVideo) - { - SetStatusText(""); - } - } - -#if !USE_SERVER_TIME_MS - /// - /// Gets network time with some degree of ms resolution unlike GetServerTimeInSeconds which is 1 second resolution - /// - /// - double GetNetworkTime() - { - //return Networking.GetServerTimeInSeconds(); - return (Networking.GetNetworkDateTime() - _localNetworkTimeStart).TotalSeconds; - } -#endif - - private void LogMessage(string message) - { - Debug.Log("[USharpVideo] " + message, this); - } - - private void LogWarning(string message) - { - Debug.LogWarning("[USharpVideo] " + message, this); - } - - private void LogError(string message) - { - Debug.LogError("[USharpVideo] " + message, this); - } -#endregion - -#region UI Control handling - public void RegisterControlHandler(VideoControlHandler newControlHandler) - { - if (_registeredControlHandlers == null) - _registeredControlHandlers = new VideoControlHandler[0]; - - foreach (VideoControlHandler controlHandler in _registeredControlHandlers) - { - if (newControlHandler == controlHandler) - return; - } - - VideoControlHandler[] newControlHandlers = new VideoControlHandler[_registeredControlHandlers.Length + 1]; - _registeredControlHandlers.CopyTo(newControlHandlers, 0); - _registeredControlHandlers = newControlHandlers; - - _registeredControlHandlers[_registeredControlHandlers.Length - 1] = newControlHandler; - - newControlHandler.SetLocked(_isMasterOnly); - newControlHandler.SetLooping(_localLoopVideo); - newControlHandler.SetPaused(_locallyPaused); - newControlHandler.SetStatusText(_lastStatusText); - - VideoPlayerManager manager = GetVideoManager(); - newControlHandler.SetVolume(manager.GetVolume()); - newControlHandler.SetMuted(manager.IsMuted()); - } - - public void UnregisterControlHandler(VideoControlHandler controlHandler) - { - if (_registeredControlHandlers == null) - _registeredControlHandlers = new VideoControlHandler[0]; - - int controlHandlerCount = _registeredControlHandlers.Length; - for (int i = 0; i < controlHandlerCount; ++i) - { - VideoControlHandler handler = _registeredControlHandlers[i]; - - if (controlHandler == handler) - { - VideoControlHandler[] newControlHandlers = new VideoControlHandler[controlHandlerCount - 1]; - - for (int j = 0; j < i; ++ j) - newControlHandlers[j] = _registeredControlHandlers[j]; - - for (int j = i + 1; j < controlHandlerCount; ++j) - newControlHandlers[j - 1] = _registeredControlHandlers[j]; - - _registeredControlHandlers = newControlHandlers; - - return; - } - } - } - - private string _lastStatusText = ""; - - private void SetStatusText(string statusText) - { - if (statusText == _lastStatusText) - return; - - _lastStatusText = statusText; - - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetStatusText(statusText); - } - - private void SetUIPaused(bool paused) - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetPaused(paused); - } - - private void SetUILocked(bool locked) - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetLocked(locked); - } - - private void AddUIUrlHistory(VRCUrl url) - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.AddURLToHistory(url); - } - - private void SetUIToVideoMode() - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetToVideoPlayerMode(); - } - - private void SetUIToStreamMode() - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetToStreamPlayerMode(); - } - - private void SendUIOwnerUpdate() - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.OnVideoPlayerOwnerTransferred(); - } - - private void SetUILooping(bool looping) - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetLooping(looping); - } - - private void SetUIVolume(float volume) - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetVolume(volume); - } - - private void SetUIMuted(bool muted) - { - foreach (VideoControlHandler handler in _registeredControlHandlers) - handler.SetMuted(muted); - } -#endregion - -#region Video Screen Handling - public void RegisterScreenHandler(VideoScreenHandler newScreenHandler) - { - if (_registeredScreenHandlers == null) - _registeredScreenHandlers = new VideoScreenHandler[0]; - - foreach (VideoScreenHandler controlHandler in _registeredScreenHandlers) - { - if (newScreenHandler == controlHandler) - return; - } - - VideoScreenHandler[] newControlHandlers = new VideoScreenHandler[_registeredScreenHandlers.Length + 1]; - _registeredScreenHandlers.CopyTo(newControlHandlers, 0); - _registeredScreenHandlers = newControlHandlers; - - _registeredScreenHandlers[_registeredScreenHandlers.Length - 1] = newScreenHandler; - } - - public void UnregisterScreenHandler(VideoScreenHandler screenHandler) - { - if (_registeredScreenHandlers == null) - _registeredScreenHandlers = new VideoScreenHandler[0]; - - int controlHandlerCount = _registeredScreenHandlers.Length; - for (int i = 0; i < controlHandlerCount; ++i) - { - VideoScreenHandler handler = _registeredScreenHandlers[i]; - - if (screenHandler == handler) - { - VideoScreenHandler[] newControlHandlers = new VideoScreenHandler[controlHandlerCount - 1]; - - for (int j = 0; j < i; ++j) - newControlHandlers[j] = _registeredScreenHandlers[j]; - - for (int j = i + 1; j < controlHandlerCount; ++j) - newControlHandlers[j - 1] = _registeredScreenHandlers[j]; - - _registeredScreenHandlers = newControlHandlers; - - return; - } - } - } - - private Texture _lastAssignedRenderTexture; - - private void UpdateRenderTexture() - { - if (_registeredScreenHandlers == null) - return; - - Texture renderTexture = _videoPlayerManager.GetVideoTexture(); - - if (_lastAssignedRenderTexture == renderTexture) - return; - - foreach (VideoScreenHandler handler in _registeredScreenHandlers) - { - if (handler) - { - handler.UpdateVideoTexture(renderTexture, IsUsingAVProPlayer()); - } - } - - _lastAssignedRenderTexture = renderTexture; - - SendCallback("OnUSharpVideoRenderTextureChange"); - } -#endregion - -#region Callback Receivers - /// - /// Registers an UdonSharpBehaviour as a callback receiver for events that happen on this video player. - /// Callback receivers can be used to react to state changes on the video player without needing to check periodically. - /// - /// - [PublicAPI] - public void RegisterCallbackReceiver(UdonSharpBehaviour callbackReceiver) - { - if (!callbackReceiver) - return; - - if (_registeredCallbackReceivers == null) - _registeredCallbackReceivers = new UdonSharpBehaviour[0]; - - foreach (UdonSharpBehaviour currReceiver in _registeredCallbackReceivers) - { - if (callbackReceiver == currReceiver) - return; - } - - UdonSharpBehaviour[] newControlHandlers = new UdonSharpBehaviour[_registeredCallbackReceivers.Length + 1]; - _registeredCallbackReceivers.CopyTo(newControlHandlers, 0); - _registeredCallbackReceivers = newControlHandlers; - - _registeredCallbackReceivers[_registeredCallbackReceivers.Length - 1] = callbackReceiver; - } - - [PublicAPI] - public void UnregisterCallbackReceiver(UdonSharpBehaviour callbackReceiver) - { - if (!callbackReceiver) - return; - - if (_registeredCallbackReceivers == null) - _registeredCallbackReceivers = new UdonSharpBehaviour[0]; - - int callbackReceiverCount = _registeredCallbackReceivers.Length; - for (int i = 0; i < callbackReceiverCount; ++i) - { - UdonSharpBehaviour currHandler = _registeredCallbackReceivers[i]; - - if (callbackReceiver == currHandler) - { - UdonSharpBehaviour[] newCallbackReceivers = new UdonSharpBehaviour[callbackReceiverCount - 1]; - - for (int j = 0; j < i; ++j) - newCallbackReceivers[j] = _registeredCallbackReceivers[j]; - - for (int j = i + 1; j < callbackReceiverCount; ++j) - newCallbackReceivers[j - 1] = _registeredCallbackReceivers[j]; - - _registeredCallbackReceivers = newCallbackReceivers; - - return; - } - } - } - - private void SendCallback(string callbackName) - { - foreach (UdonSharpBehaviour callbackReceiver in _registeredCallbackReceivers) - { - if (callbackReceiver) - { - callbackReceiver.SendCustomEvent(callbackName); - } - } - } -#endregion - } -} + +#define USE_SERVER_TIME_MS // Uses GetServerTimeMilliseconds instead of the server datetime which in theory is less reliable + +using JetBrains.Annotations; +using UdonSharp; +using UnityEngine; +using VRC.SDK3.Components.Video; +using VRC.SDKBase; + +namespace UdonSharp.Video +{ + [AddComponentMenu("Udon Sharp/Video/USharp Video Player")] + [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] + public class USharpVideoPlayer : UdonSharpBehaviour + { + // Video player references + private VideoPlayerManager _videoPlayerManager; + + [Tooltip("Whether to allow video seeking with the progress bar on the video")] + [PublicAPI] + public bool allowSeeking = true; + + [Tooltip("If enabled defaults to unlocked so anyone can put in a URL")] + [SerializeField] + private bool defaultUnlocked = true; + + [Tooltip("If enabled allows the instance creator to always control the video player regardless of if they are master or not")] + [PublicAPI] + public bool allowInstanceCreatorControl = true; + + [Tooltip("How often the video player should check if it is more than Sync Threshold out of sync with the video time")] + [PublicAPI] + public float syncFrequency = 8.0f; + [Tooltip("How many seconds desynced from the owner the client needs to be to trigger a resync")] + [PublicAPI] + public float syncThreshold = 0.85f; + + [Range(0f, 1f)] + [Tooltip("The default volume for the volume slider on the video player")] + [SerializeField] + private float defaultVolume = 0.5f; + +#pragma warning disable CS0414 + [Tooltip("The max range of the audio sources on this video player")] + [SerializeField] + private float audioRange = 40f; +#pragma warning restore CS0414 + + /// + /// Local offset from the network time to sync the video + /// Can be used for things like making a video player sync up with someone singing + /// + [PublicAPI, System.NonSerialized] + public float localSyncOffset; + + [Tooltip("List of urls to play automatically when the world is loaded until someone puts in another URL")] + public VRCUrl[] playlist = new VRCUrl[0]; + + [Tooltip("Trusted URL's of websites you want to be able to load videos from. This prevents malicious users from throwing IP grabbers into the player")] + public string[] allowlist = new string[0]; + + [Tooltip("Should default to the stream player? This is usually used when you want to put a live stream in the default playlist.")] + [SerializeField] + private bool defaultStreamMode; + + [Tooltip("If the default playlist should loop")] + [PublicAPI] + public bool loopPlaylist; + + [Tooltip("If the default playlist should be shuffled upon world load")] + public bool shufflePlaylist; + + /// + /// The URL that we should currently be playing and that other people are playing + /// + [UdonSynced] + private VRCUrl _syncedURL = VRCUrl.Empty; + + /// + /// The video sequence identifier, gets incremented whenever a new video is put in. Used to determine in combination with _currentVideoIdx if we need to load the new URL + /// + [UdonSynced] + private int _syncedVideoIdx; + private int _currentVideoIdx; + + /// + /// If we're locked so only the master may put in URLs + /// + [UdonSynced] + private bool _isMasterOnly = true; + + [UdonSynced] + private int _nextPlaylistIndex; + +#if USE_SERVER_TIME_MS + [UdonSynced] + private int _networkTimeVideoStart; + private int _localNetworkTimeStart; +#else + [UdonSynced] + private double _videoStartNetworkTime; + private double _localVideoStartTime; + + [UdonSynced] + private long _networkTimeStart; + private System.DateTime _localNetworkTimeStart; +#endif + + [UdonSynced] + private bool _ownerPlaying; + + [UdonSynced] + private bool _ownerPaused; + private bool _locallyPaused; + + [UdonSynced] + private bool _loopVideo; + private bool _localLoopVideo; + + [UdonSynced] + private int _shuffleSeed; + + // The last unpaused time in the video + private float _lastVideoTime; + + VideoControlHandler[] _registeredControlHandlers; + VideoScreenHandler[] _registeredScreenHandlers; + UdonSharpBehaviour[] _registeredCallbackReceivers; + + // Video loading state + const int MAX_RETRY_COUNT = 4; + const float DEFAULT_RETRY_TIMEOUT = 35.0f; + const float RATE_LIMIT_RETRY_TIMEOUT = 5.5f; + const float VIDEO_ERROR_RETRY_TIMEOUT = 5f; + const float PLAYLIST_ERROR_RETRY_COUNT = 4; + + private bool _loadingVideo; + private float _currentLoadingTime; // Counts down to 0 while loading + private int _currentRetryCount; + private float _videoTargetStartTime; + private int _playlistErrorCount; + + private bool _waitForSync; + + // Player mode tracking + const int PLAYER_MODE_UNITY = 0; + const int PLAYER_MODE_AVPRO = 1; + + [UdonSynced] + private int currentPlayerMode = PLAYER_MODE_UNITY; + private int _localPlayerMode = PLAYER_MODE_UNITY; + + private bool _videoSync = true; + + private bool _ranInit; + + private void Start() + { + if (_ranInit) + return; + + _ranInit = true; + + _videoPlayerManager = GetVideoManager(); + _videoPlayerManager.Start(); + + if (_registeredControlHandlers == null) + _registeredControlHandlers = new VideoControlHandler[0]; + + if (_registeredCallbackReceivers == null) + _registeredCallbackReceivers = new UdonSharpBehaviour[0]; + + if (Networking.IsOwner(gameObject)) + { + if (defaultUnlocked) + _isMasterOnly = false; + + if (defaultStreamMode) + { + SetPlayerMode(PLAYER_MODE_AVPRO); + _nextPlaylistIndex = 0; // SetPlayerMode sets this to -1, but we want to be able to keep it intact so reset to 0 + } + + _shuffleSeed = Random.Range(0, 10000); + } + + _lastMasterLocked = _isMasterOnly; + + SetUILocked(_isMasterOnly); + +#if !USE_SERVER_TIME_MS + _networkTimeStart = Networking.GetNetworkDateTime().Ticks; + _localNetworkTimeStart = new System.DateTime(_networkTimeStart, System.DateTimeKind.Utc); +#endif + + PlayNextVideoFromPlaylist(); + + SetVolume(defaultVolume); + + // Serialize the default setup state from the master once regardless of if a video has played + QueueSerialize(); + + LogMessage("USharpVideo v1.0.1 Initialized"); + } + + public override void OnVideoReady() + { + ResetVideoLoad(); + _playlistErrorCount = 0; + + if (IsUsingAVProPlayer()) + { + float duration = _videoPlayerManager.GetDuration(); + + if (duration == float.MaxValue || float.IsInfinity(duration) || IsRTSPStream()) + _videoSync = false; + else + _videoSync = true; + } + else + _videoSync = true; + + if (_videoSync) + { + if (Networking.IsOwner(gameObject)) + { + _waitForSync = false; + _videoPlayerManager.Play(); + } + else + { + if (_ownerPlaying) + { + _waitForSync = false; + _locallyPaused = false; + _videoPlayerManager.Play(); + + SyncVideo(); + } + else + { +#if USE_SERVER_TIME_MS + if (_networkTimeVideoStart == 0) +#else + if (_videoStartNetworkTime == 0f || _videoStartNetworkTime > GetNetworkTime() - _videoPlayerManager.GetDuration()) // Todo: remove the 0f check and see how this actually gets set to 0 while the owner is playing +#endif + { + _waitForSync = true; + SetStatusText("Waiting for owner sync..."); + } + else + { + _waitForSync = false; + SyncVideo(); + SetStatusText(""); +#if USE_SERVER_TIME_MS + LogMessage($"Loaded into world with complete video, duration: {_videoPlayerManager.GetDuration()}, start net time: {_networkTimeVideoStart}"); +#else + LogMessage($"Loaded into world with complete video, duration: {_videoPlayerManager.GetDuration()}, start net time: {_videoStartNetworkTime}, subtracted net time {GetNetworkTime() - _videoPlayerManager.GetDuration()}"); +#endif + } + } + } + } + else // Live streams should start asap + { + _waitForSync = false; + _videoPlayerManager.Play(); + } + } + + public override void OnVideoStart() + { + if (Networking.IsOwner(gameObject)) + { + SetPausedInternal(false, false); + +#if USE_SERVER_TIME_MS + _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds() - (int)(_videoTargetStartTime * 1000f); +#else + _videoStartNetworkTime = GetNetworkTime() - _videoTargetStartTime; +#endif + + if (IsInVideoMode()) + { + _videoPlayerManager.SetTime(_videoTargetStartTime); + } + + _ownerPlaying = true; + + QueueSerialize(); + + LogMessage($"Started video: {_syncedURL}"); + } + else if (!_ownerPlaying) // Watchers pause and wait for sync from owner + { + _videoPlayerManager.Pause(); + _waitForSync = true; + } + else + { + SetPausedInternal(_ownerPaused, false); + SyncVideo(); + LogMessage($"Started video: {_syncedURL}"); + } + + SetStatusText(""); + + _videoTargetStartTime = 0f; + + SetUIPaused(_locallyPaused); + + UpdateRenderTexture(); + + SendCallback("OnUSharpVideoPlay"); + } + + private bool IsRTSPStream() + { + string urlStr = _syncedURL.ToString(); + + return IsUsingAVProPlayer() && + _videoPlayerManager.GetDuration() == 0f && + IsRTSPURL(urlStr); + } + + private bool IsRTSPURL(string urlStr) + { + return urlStr.StartsWith("rtsp://", System.StringComparison.OrdinalIgnoreCase) || + urlStr.StartsWith("rtmp://", System.StringComparison.OrdinalIgnoreCase) || // RTMP isn't really supported in VRC's context and it's probably never going to be, but we'll just be safe here + urlStr.StartsWith("rtspt://", System.StringComparison.OrdinalIgnoreCase) || // rtsp over TCP + urlStr.StartsWith("rtspu://", System.StringComparison.OrdinalIgnoreCase); // rtsp over UDP + } + + public override void OnVideoEnd() + { + // VRC falsely throws OnVideoEnd instantly on RTSP streams since they report 0 length + if (IsRTSPStream()) + return; + + if (Networking.IsOwner(gameObject)) + { + _ownerPlaying = false; + _ownerPaused = _locallyPaused = false; + + SetStatusText(""); + SetUIPaused(false); + + PlayNextVideoFromPlaylist(); + QueueSerialize(); + } + + SendCallback("OnUSharpVideoEnd"); + + UpdateRenderTexture(); + } + + // Workaround for bug that needs to be addressed in U# where calling built in methods with parameters will get the parameters overwritten when called from other UdonBehaviours + public void _OnVideoErrorCallback(VideoError videoError) + { + OnVideoError(videoError); + } + + public override void OnVideoError(VideoError videoError) + { + if (videoError == VideoError.RateLimited) + { + SetStatusText("Rate limited, retrying..."); + LogWarning("Rate limited, retrying..."); + _currentLoadingTime = RATE_LIMIT_RETRY_TIMEOUT; + return; + } + + if (videoError == VideoError.PlayerError) + { + SetStatusText("Video error, retrying..."); + LogError("Video player error when trying to load " + _syncedURL); + _loadingVideo = true; // Apparently OnVideoReady gets fired erroneously?? + _currentLoadingTime = VIDEO_ERROR_RETRY_TIMEOUT; + return; + } + + ResetVideoLoad(); + _videoTargetStartTime = 0f; + + _videoPlayerManager.Stop(); + + LogError($"Video '{_syncedURL}' failed to play with error {videoError}"); + + switch (videoError) + { + case VideoError.InvalidURL: + SetStatusText("Invalid URL"); + break; + case VideoError.AccessDenied: + SetStatusText("Video blocked, enable untrusted URLs"); + break; + default: + SetStatusText("Failed to load video"); + break; + } + + ++_playlistErrorCount; + PlayNextVideoFromPlaylist(); + + SendCallback("OnUSharpVideoError"); + } + + public override void OnVideoPause() { } + public override void OnVideoPlay() { } + + public override void OnVideoLoop() + { +#if USE_SERVER_TIME_MS + _localNetworkTimeStart = _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds(); +#else + _localVideoStartTime = _videoStartNetworkTime = GetNetworkTime(); +#endif + + QueueSerialize(); + } + + private float _lastCurrentTime; + + private void Update() + { + if (_loadingVideo) + UpdateVideoLoad(); + + if (_locallyPaused) + { + if (IsInVideoMode()) + { + // Keep the target time the same while paused +#if USE_SERVER_TIME_MS + _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds() - (int)(_videoPlayerManager.GetTime() * 1000f); +#else + _videoStartNetworkTime = GetNetworkTime() - _videoPlayerManager.GetTime(); +#endif + } + } + else + _lastCurrentTime = _videoPlayerManager.GetTime(); + + if (Networking.IsOwner(gameObject) || !_waitForSync) + { + SyncVideoIfTime(); + } + else if (_ownerPlaying) + { + _videoPlayerManager.Play(); + LogMessage($"Started video: {_syncedURL}"); + _waitForSync = false; + SyncVideo(); + } + + UpdateRenderTexture(); // Needed because AVPro can swap textures whenever + } + + /// + /// Uncomment this to prevent people from taking ownership of the video player when they shouldn't be able to + /// + //public override bool OnOwnershipRequest(VRCPlayerApi requestingPlayer, VRCPlayerApi requestedOwner) + //{ + // return !_isMasterOnly || IsPrivlegedUser(requestedOwner); + //} + + private bool _lastMasterLocked; + + public override void OnDeserialization() + { + if (Networking.IsOwner(gameObject)) + return; + +#if !USE_SERVER_TIME_MS + _localNetworkTimeStart = new System.DateTime(_networkTimeStart, System.DateTimeKind.Utc); +#endif + + SetPausedInternal(_ownerPaused, false); + SetLoopingInternal(_loopVideo); + + if (_localPlayerMode != currentPlayerMode) + SetPlayerMode(currentPlayerMode); + + if (_isMasterOnly != _lastMasterLocked) + SetLockedInternal(_isMasterOnly); + + if (!_ownerPlaying && _videoPlayerManager.IsPlaying()) + _videoPlayerManager.Stop(); + + if (_currentVideoIdx != _syncedVideoIdx) + { + _currentVideoIdx = _syncedVideoIdx; + + _videoPlayerManager.Stop(); + StartVideoLoad(_syncedURL); + +#if USE_SERVER_TIME_MS + _localNetworkTimeStart = _networkTimeVideoStart; +#else + _localVideoStartTime = _videoStartNetworkTime; +#endif + + LogMessage("Playing synced " + _syncedURL); + } +#if USE_SERVER_TIME_MS + else if (_networkTimeVideoStart != _localNetworkTimeStart) // Detect seeks + { + _localNetworkTimeStart = _networkTimeVideoStart; +#else + else if (_videoStartNetworkTime != _localVideoStartTime) // Detect seeks + { + _localVideoStartTime = _videoStartNetworkTime; +#endif + SyncVideo(); + } + + if (!_locallyPaused && IsInVideoMode()) + { + float duration = GetVideoManager().GetDuration(); + + // If the owner did a seek on the video after it finished, we need to start playing it again +#if USE_SERVER_TIME_MS + if ((Networking.GetServerTimeInMilliseconds() - _networkTimeVideoStart) / 1000f < duration - 3f) +#else + if (GetNetworkTime() - _videoStartNetworkTime < duration - 3f) +#endif + _videoPlayerManager.Play(); + } + + SendCallback("OnUSharpVideoDeserialization"); + } + + public override void OnOwnershipTransferred(VRCPlayerApi player) + { + SendUIOwnerUpdate(); + + SendCallback("OnUSharpVideoOwnershipChange"); + } + + // Supposedly there's some case where late joiners don't receive data, so do a serialization just in case here. + public override void OnPlayerJoined(VRCPlayerApi player) + { + if (!player.isLocal) + QueueSerialize(); + } + + /// + /// Stops playback of the video completely and clears data + /// + [PublicAPI] + public void StopVideo() + { + if (!Networking.IsOwner(gameObject)) + return; + +#if USE_SERVER_TIME_MS + _networkTimeVideoStart = 0; +#else + _videoStartNetworkTime = 0f; +#endif + _ownerPlaying = false; + _locallyPaused = _ownerPaused = false; + _videoTargetStartTime = 0f; + _lastCurrentTime = 0f; + + _videoPlayerManager.Stop(); + SetUIPaused(false); + ResetVideoLoad(); + + QueueSerialize(); + + SendCallback("OnUSharpVideoStop"); + } + + /// + /// Play a video with the specified URL, only works if the player is allowed to use the video player + /// + /// + [PublicAPI] + public void PlayVideo(VRCUrl url) + { + PlayVideoInternal(url, true); + } + + /// + /// Returns the URL that the video player currently has loaded + /// + /// + [PublicAPI] + public VRCUrl GetCurrentURL() => _syncedURL; + + private void PlayVideoInternal(VRCUrl url, bool stopPlaylist) + { + if (!CanControlVideoPlayer()) + return; + + string urlStr = url.Get(); + + if (!isWhitelistedUrl(urlStr)) + return; + + if (!ValidateURL(urlStr)) + return; + + bool wasOwner = Networking.IsOwner(gameObject); + + TakeOwnership(); + + if (stopPlaylist) + _nextPlaylistIndex = -1; + + StopVideo(); + + _syncedURL = url; + + if (wasOwner) + ++_syncedVideoIdx; + else // Add two to avoid having conflicts where the old owner increases the count + _syncedVideoIdx += 2; + + _currentVideoIdx = _syncedVideoIdx; + + StartVideoLoad(url); + _ownerPlaying = false; +#if USE_SERVER_TIME_MS + _networkTimeVideoStart = 0; +#endif + + _videoTargetStartTime = GetVideoStartTime(urlStr); + + QueueSerialize(); + + SendCallback("OnUSharpVideoLoadStart"); + } + + private void ResetVideoLoad() + { + _loadingVideo = false; + _currentRetryCount = 0; + _currentLoadingTime = DEFAULT_RETRY_TIMEOUT; + } + + private void UpdateVideoLoad() + { + //if (_loadingVideo) // Checked in caller now since it's cheaper + { + _currentLoadingTime -= Time.deltaTime; + + if (_currentLoadingTime <= 0f) + { + _currentLoadingTime = DEFAULT_RETRY_TIMEOUT; + + if (++_currentRetryCount > MAX_RETRY_COUNT) + { + OnVideoError(VideoError.Unknown); + } + else + { + LogMessage("Retrying load"); + + SetStatusText("Retrying load..."); + _videoPlayerManager.LoadURL(_syncedURL); + } + } + } + } + + private float _lastSyncTime; + + private void SyncVideoIfTime() + { + float timeSinceStartup = Time.realtimeSinceStartup; + + if (timeSinceStartup - _lastSyncTime > syncFrequency) + { + _lastSyncTime = timeSinceStartup; + SyncVideo(); + } + } + + /// + /// Syncs the video time if it's too far diverged from the network time + /// + [PublicAPI] + public void SyncVideo() + { + if (IsInVideoMode()) + { +#if USE_SERVER_TIME_MS + float offsetTime = Mathf.Clamp((Networking.GetServerTimeInMilliseconds() - _networkTimeVideoStart) / 1000f + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); +#else + float offsetTime = Mathf.Clamp((float)(GetNetworkTime() - _videoStartNetworkTime) + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); +#endif + + if (Mathf.Abs(_videoPlayerManager.GetTime() - offsetTime) > syncThreshold) + { + _videoPlayerManager.SetTime(offsetTime); + LogMessage($"Syncing video to {offsetTime:N2}"); + } + } + } + + /// + /// Syncs the video time regardless of how far diverged it is from the network time, can be used as a less aggressive audio resync in some cases + /// + [PublicAPI] + public void ForceSyncVideo() + { + if (IsInVideoMode()) + { +#if USE_SERVER_TIME_MS + float offsetTime = Mathf.Clamp((Networking.GetServerTimeInMilliseconds() - _networkTimeVideoStart) / 1000f + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); +#else + float offsetTime = Mathf.Clamp((float)(GetNetworkTime() - _videoStartNetworkTime) + localSyncOffset, 0f, _videoPlayerManager.GetDuration()); +#endif + + float syncNudgeTime = Mathf.Max(0f, offsetTime - 1f); + _videoPlayerManager.SetTime(syncNudgeTime); // Seek to slightly earlier before syncing to the real time to get the video player to jump cleanly + _videoPlayerManager.SetTime(offsetTime); + LogMessage($"Syncing video to {offsetTime:N2}"); + } + } + + private void StartVideoLoad(VRCUrl url) + { +#if UNITY_EDITOR + LogMessage($"Started video load for URL: {url}"); +#else + LogMessage($"Started video load for URL: {url}, requested by {Networking.GetOwner(gameObject).displayName}"); +#endif + + SetStatusText("Loading video..."); + ResetVideoLoad(); + _loadingVideo = true; + _videoPlayerManager.LoadURL(url); + + AddUIUrlHistory(url); + } + + private void SetPausedInternal(bool paused, bool updatePauseTime) + { + if (Networking.IsOwner(gameObject)) + _ownerPaused = paused; + + if (_locallyPaused != paused) + { + _locallyPaused = paused; + + if (IsInVideoMode()) + { + if (_ownerPaused) + _videoPlayerManager.Pause(); + else + { + _videoPlayerManager.Play(); + if (updatePauseTime) + _videoTargetStartTime = _lastCurrentTime; + } + } + else + { + if (_ownerPaused) + _videoPlayerManager.Stop(); + else + StartVideoLoad(_syncedURL); + } + + SetUIPaused(paused); + + if (_locallyPaused) + SendCallback("OnUSharpVideoPause"); + else + SendCallback("OnUSharpVideoUnpause"); + } + + QueueRateLimitedSerialize(); + } + + /// + /// Pauses the video if we have control of the video player. + /// + /// + [PublicAPI] + public void SetPaused(bool paused) + { + if (Networking.IsOwner(gameObject)) + SetPausedInternal(paused, true); + } + + [PublicAPI] + public bool IsPaused() + { + return _ownerPaused; + } + + private void SetLoopingInternal(bool loop) + { + if (loop == _localLoopVideo) + return; + + _loopVideo = _localLoopVideo = loop; + + _videoPlayerManager.SetLooping(loop); + + SetUILooping(loop); + + QueueRateLimitedSerialize(); + } + + /// + /// Sets whether the currently playing video should loop and restart once it ends. + /// + /// + [PublicAPI] + public void SetLooping(bool loop) + { + if (Networking.IsOwner(gameObject)) + SetLoopingInternal(loop); + } + + [PublicAPI] + public bool IsLooping() + { + return _localLoopVideo; + } + + [PublicAPI] + public float GetVolume() => _videoPlayerManager.GetVolume(); + + /// + /// Sets the audio source volume for the audio sources used by this video player. + /// + /// + [PublicAPI] + public void SetVolume(float volume) + { + volume = Mathf.Clamp01(volume); + if (volume == _videoPlayerManager.GetVolume()) + return; + + _videoPlayerManager.SetVolume(volume); + SetUIVolume(volume); + } + + [PublicAPI] + public bool IsMuted() => _videoPlayerManager.IsMuted(); + + /// + /// Mutes audio from this video player + /// + /// + [PublicAPI] + public void SetMuted(bool muted) + { + _videoPlayerManager.SetMuted(muted); + SetUIMuted(muted); + } + + private bool _delayedSyncAllowed = true; + private int _finalSyncCounter; + + /// + /// Takes a float in the range 0 to 1 and seeks the video to that % through the time + /// Is intended to be used with progress bar-type-things + /// + /// + [PublicAPI] + public void SeekTo(float progress) + { + if (!allowSeeking || !Networking.IsOwner(gameObject)) + return; + + float newTargetTime = _videoPlayerManager.GetDuration() * progress; + _lastVideoTime = newTargetTime; + _lastCurrentTime = newTargetTime; + +#if USE_SERVER_TIME_MS + _localNetworkTimeStart = _networkTimeVideoStart = Networking.GetServerTimeInMilliseconds() - (int)(newTargetTime * 1000f); +#else + _videoStartNetworkTime = GetNetworkTime() - newTargetTime; + _localVideoStartTime = _videoStartNetworkTime; +#endif + + if (!_locallyPaused && !GetVideoManager().IsPlaying()) + GetVideoManager().Play(); + + SyncVideo(); + QueueRateLimitedSerialize(); + } + + /// + /// Used on things that are easily spammable to prevent flooding the network unintentionally. + /// Will allow 1 sync every half second and then will send a final sync to propagate the final changed values of things at the end + /// + private void QueueRateLimitedSerialize() + { + //QueueSerialize(); // Debugging line :D this serialization method can potentially hide some issues so we want to disable it sometimes and verify stuff works right + + if (_delayedSyncAllowed) + { + QueueSerialize(); + _delayedSyncAllowed = false; + SendCustomEventDelayedSeconds(nameof(_UnlockDelayedSync), 0.5f); + } + + ++_finalSyncCounter; + SendCustomEventDelayedSeconds(nameof(_SendFinalSync), 0.8f); + } + + public void _UnlockDelayedSync() + { + _delayedSyncAllowed = true; + } + + // Handles running a final sync after doing a QueueRateLimitedSerialize, so that the final changes of the seek time get propagated + public void _SendFinalSync() + { + if (--_finalSyncCounter == 0) + QueueSerialize(); + } + + /// + /// Determines if the local player can control this video player. This means the player is either the master, the instance creator, or the video player is unlocked. + /// + /// + [PublicAPI] + public bool CanControlVideoPlayer() + { + return !_isMasterOnly || IsPrivilegedUser(Networking.LocalPlayer); + } + + /// + /// If the given player is allowed to take important actions on this video player such as changing the video or locking the video player. + /// This is what you would extend if you want to add an access control list or something similar. + /// + /// + /// + [PublicAPI] + public bool IsPrivilegedUser(VRCPlayerApi player) + { +#if UNITY_EDITOR + if (player == null) + return true; +#endif + + return player.isMaster || (allowInstanceCreatorControl && player.isInstanceOwner); + } + + /// + /// Takes ownership of the video player if allowed + /// + [PublicAPI] + public void TakeOwnership() + { + if (Networking.IsOwner(gameObject)) + return; + + if (CanControlVideoPlayer()) + Networking.SetOwner(Networking.LocalPlayer, gameObject); + } + + [PublicAPI] + public void QueueSerialize() + { + if (!Networking.IsOwner(gameObject)) + return; + + RequestSerialization(); + } + + private bool _shuffled; + + /// + /// Plays the next video from the video player's built-in playlist + /// + [PublicAPI] + public void PlayNextVideoFromPlaylist() + { + if (_nextPlaylistIndex == -1 || playlist.Length == 0 || !Networking.IsOwner(gameObject)) + return; + + if (loopPlaylist && _playlistErrorCount > PLAYLIST_ERROR_RETRY_COUNT) + { + LogError("Maximum number of retries for playlist video looping hit. Stopping playlist playback."); + _nextPlaylistIndex = -1; + return; + } + + if (shufflePlaylist && !_shuffled) + { + Random.InitState(_shuffleSeed); + + for (int i = 0; i < playlist.Length - 1; ++i) + { + int r = Random.Range(i, playlist.Length); + if (i == r) { continue; } + VRCUrl flipVal = playlist[r]; + playlist[r] = playlist[i]; + playlist[i] = flipVal; + } + + _shuffled = true; + } + + int currentIdx = _nextPlaylistIndex++; + + if (currentIdx >= playlist.Length) + { + if (loopPlaylist) + { + _nextPlaylistIndex = 1; + currentIdx = 0; + } + else + { + // We reached the end of the playlist + _nextPlaylistIndex = -1; + return; + } + } + + PlayVideoInternal(playlist[currentIdx], false); + } + + [PublicAPI] + public int GetPlaylistIndex() => _nextPlaylistIndex > 0 ? _nextPlaylistIndex - 1 : -1; + + [PublicAPI] + public void SetNextPlaylistVideo(int nextPlaylistIdx) + { + _nextPlaylistIndex = nextPlaylistIdx; + } + + /// + /// Sets whether this video player is locked to the master which means only the master has the ability to put new videos in. + /// + /// + [PublicAPI] + public void SetLocked(bool locked) + { + if (Networking.IsOwner(gameObject)) + SetLockedInternal(locked); + } + + private void SetLockedInternal(bool locked) + { + _isMasterOnly = locked; + _lastMasterLocked = _isMasterOnly; + + SetUILocked(locked); + + QueueSerialize(); + + SendCallback("OnUSharpVideoLockChange"); + } + + [PublicAPI] + public bool IsLocked() + { + return _isMasterOnly; + } + + /// + /// Sets the video player to use the Unity video player as a backend + /// + [PublicAPI] + public void SetToUnityPlayer() + { + if (CanControlVideoPlayer()) + { + TakeOwnership(); + SetPlayerMode(PLAYER_MODE_UNITY); + } + } + + /// + /// Sets the video player to use AVPro as the backend. + /// AVPro supports streams so this is aliased in UI as the "Stream" player to avoid confusion + /// + [PublicAPI] + public void SetToAVProPlayer() + { + if (CanControlVideoPlayer()) + { + TakeOwnership(); + SetPlayerMode(PLAYER_MODE_AVPRO); + } + } + + private void SetPlayerMode(int newPlayerMode) + { + if (_localPlayerMode == newPlayerMode) + return; + + StopVideo(); + + if (Networking.IsOwner(gameObject)) + _syncedURL = VRCUrl.Empty; + + currentPlayerMode = newPlayerMode; + + _locallyPaused = _ownerPaused = false; + + _nextPlaylistIndex = -1; + + _localPlayerMode = newPlayerMode; + + ResetVideoLoad(); + + if (IsUsingUnityPlayer()) + { + _videoPlayerManager.SetToVideoPlayerMode(); + SetUIToVideoMode(); + } + else + { + _videoPlayerManager.SetToStreamPlayerMode(); + SetUIToStreamMode(); + } + + QueueSerialize(); + + UpdateRenderTexture(); + + SendCallback("OnUSharpVideoModeChange"); + } + + /// + /// Are we playing a standard video where we know the length and need to sync its time across clients? + /// + /// + [PublicAPI] + public bool IsInVideoMode() + { + return _videoSync; + } + + /// + /// Are we playing some type of live stream where we do not know the length of the stream and do not need to sync time across clients? + /// + /// + [PublicAPI] + public bool IsInStreamMode() + { + return !_videoSync; + } + + [PublicAPI] + public bool IsUsingUnityPlayer() + { + return _localPlayerMode == PLAYER_MODE_UNITY; + } + + [PublicAPI] + public bool IsUsingAVProPlayer() + { + return _localPlayerMode == PLAYER_MODE_AVPRO; + } + + /// + /// Reloads the video on the video player, usually used if the video playback has encountered some internal issue or if the audio has gotten desynced from the video + /// + [PublicAPI] + public void Reload() + { + if ((_ownerPlaying || Networking.IsOwner(gameObject)) && !_loadingVideo) + { + StartVideoLoad(_syncedURL); + + if (Networking.IsOwner(gameObject)) + _videoTargetStartTime = GetVideoManager().GetTime(); + + SendCallback("OnUSharpVideoReload"); + } + } + + public VideoPlayerManager GetVideoManager() + { + if (_videoPlayerManager) + return _videoPlayerManager; + + _videoPlayerManager = GetComponentInChildren(true); + if (_videoPlayerManager == null) + LogError("Video Player Manager not found, make sure you have a manager setup properly"); + + return _videoPlayerManager; + } + +#region Utilities + /// + /// Parses the start time of a YouTube video from the URL. + /// If no time is found or given URL is not a YouTube URL, returns 0.0 + /// + /// + /// + private float GetVideoStartTime(string url) + { + // Attempt to parse out a start time from YouTube links with t= or start= + if (url.Contains("youtube.com/watch") || + url.Contains("youtu.be/")) + { + int tIndex = url.IndexOf("?t=", System.StringComparison.Ordinal); + if (tIndex == -1) tIndex = url.IndexOf("&t=", System.StringComparison.Ordinal); + if (tIndex == -1) tIndex = url.IndexOf("?start=", System.StringComparison.Ordinal); + if (tIndex == -1) tIndex = url.IndexOf("&start=", System.StringComparison.Ordinal); + + if (tIndex == -1) + return 0f; + + char[] urlArr = url.ToCharArray(); + int numIdx = url.IndexOf('=', tIndex) + 1; + + string intStr = ""; + + while (numIdx < urlArr.Length) + { + char currentChar = urlArr[numIdx]; + if (!char.IsNumber(currentChar)) + break; + + intStr += currentChar; + + ++numIdx; + } + + if (string.IsNullOrWhiteSpace(intStr)) + return 0f; + + int secondsCount = 0; + if (int.TryParse(intStr, out secondsCount)) + return secondsCount; + } + + return 0f; + } + + /// + /// Checks for URL sanity and throws warnings if it's not nice. + /// + /// + private bool ValidateURL(string url) + { + + if (url.Contains("youtube.com/watch") || + url.Contains("youtu.be/")) + { + if (url.IndexOf("&list=", System.StringComparison.OrdinalIgnoreCase) != -1) + LogWarning($"URL '{url}' input with playlist link, this can slow down YouTubeDL link resolves significantly see: https://vrchat.canny.io/feature-requests/p/add-no-playlist-to-ytdl-arguments-for-url-resolution"); + } + + if (string.IsNullOrWhiteSpace(url)) // Don't do anything if the player entered an empty URL by accident + return false; + + //if (!url.StartsWith("https://", System.StringComparison.OrdinalIgnoreCase) && + // !url.StartsWith("http://", System.StringComparison.OrdinalIgnoreCase) && + // !IsRTSPURL(url)) + int idx = url.IndexOf("://", System.StringComparison.Ordinal); + if (idx < 1 || idx > 8) // I'm not sure exactly what rule VRC uses so just check for the :// in an expected spot since it seems like VRC checks that it has a protocol at least. + { + LogError($"Invalid URL '{url}' provided"); + SetStatusText("Invalid URL"); + SendCustomEventDelayedSeconds(nameof(_LateClearStatusInternal), 2f); + return false; + } + + // Longer than most browsers support, see: https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers. I'm not sure if this length will even play in the video player. + // Most CDN's keep their URLs under 1000 characters so this should be more than reasonable + // Prevents people from pasting a book and breaking sync on the video player xd + if (url.Length > 4096) + { + LogError($"Video URL is too long! url: '{url}'"); + SetStatusText("Invalid URL"); + SendCustomEventDelayedSeconds(nameof(_LateClearStatusInternal), 2f); + return false; + } + + return true; + } + + public void _LateClearStatusInternal() + { + if (_videoPlayerManager.IsPlaying() && !_loadingVideo) + { + SetStatusText(""); + } + } + +#if !USE_SERVER_TIME_MS + /// + /// Gets network time with some degree of ms resolution unlike GetServerTimeInSeconds which is 1 second resolution + /// + /// + double GetNetworkTime() + { + //return Networking.GetServerTimeInSeconds(); + return (Networking.GetNetworkDateTime() - _localNetworkTimeStart).TotalSeconds; + } +#endif + + private void LogMessage(string message) + { + Debug.Log("[USharpVideo] " + message, this); + } + + private void LogWarning(string message) + { + Debug.LogWarning("[USharpVideo] " + message, this); + } + + private void LogError(string message) + { + Debug.LogError("[USharpVideo] " + message, this); + } + + private bool isWhitelistedUrl(string url) + { + bool isWhitelisted = false; + + foreach (string whitelist_url in allowlist) + { + if (url.Contains($"{whitelist_url}" + "/")) + { + isWhitelisted = true; + + Debug.Log($"{whitelist_url} is whitelisted domain"); + + return true; + } + } + + Debug.LogWarning("DOMAIN IS NOT WHITELISTED!!!"); + + return false; + } + +#endregion + +#region UI Control handling + public void RegisterControlHandler(VideoControlHandler newControlHandler) + { + if (_registeredControlHandlers == null) + _registeredControlHandlers = new VideoControlHandler[0]; + + foreach (VideoControlHandler controlHandler in _registeredControlHandlers) + { + if (newControlHandler == controlHandler) + return; + } + + VideoControlHandler[] newControlHandlers = new VideoControlHandler[_registeredControlHandlers.Length + 1]; + _registeredControlHandlers.CopyTo(newControlHandlers, 0); + _registeredControlHandlers = newControlHandlers; + + _registeredControlHandlers[_registeredControlHandlers.Length - 1] = newControlHandler; + + newControlHandler.SetLocked(_isMasterOnly); + newControlHandler.SetLooping(_localLoopVideo); + newControlHandler.SetPaused(_locallyPaused); + newControlHandler.SetStatusText(_lastStatusText); + + VideoPlayerManager manager = GetVideoManager(); + newControlHandler.SetVolume(manager.GetVolume()); + newControlHandler.SetMuted(manager.IsMuted()); + } + + public void UnregisterControlHandler(VideoControlHandler controlHandler) + { + if (_registeredControlHandlers == null) + _registeredControlHandlers = new VideoControlHandler[0]; + + int controlHandlerCount = _registeredControlHandlers.Length; + for (int i = 0; i < controlHandlerCount; ++i) + { + VideoControlHandler handler = _registeredControlHandlers[i]; + + if (controlHandler == handler) + { + VideoControlHandler[] newControlHandlers = new VideoControlHandler[controlHandlerCount - 1]; + + for (int j = 0; j < i; ++ j) + newControlHandlers[j] = _registeredControlHandlers[j]; + + for (int j = i + 1; j < controlHandlerCount; ++j) + newControlHandlers[j - 1] = _registeredControlHandlers[j]; + + _registeredControlHandlers = newControlHandlers; + + return; + } + } + } + + private string _lastStatusText = ""; + + private void SetStatusText(string statusText) + { + if (statusText == _lastStatusText) + return; + + _lastStatusText = statusText; + + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetStatusText(statusText); + } + + private void SetUIPaused(bool paused) + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetPaused(paused); + } + + private void SetUILocked(bool locked) + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetLocked(locked); + } + + private void AddUIUrlHistory(VRCUrl url) + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.AddURLToHistory(url); + } + + private void SetUIToVideoMode() + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetToVideoPlayerMode(); + } + + private void SetUIToStreamMode() + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetToStreamPlayerMode(); + } + + private void SendUIOwnerUpdate() + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.OnVideoPlayerOwnerTransferred(); + } + + private void SetUILooping(bool looping) + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetLooping(looping); + } + + private void SetUIVolume(float volume) + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetVolume(volume); + } + + private void SetUIMuted(bool muted) + { + foreach (VideoControlHandler handler in _registeredControlHandlers) + handler.SetMuted(muted); + } +#endregion + +#region Video Screen Handling + public void RegisterScreenHandler(VideoScreenHandler newScreenHandler) + { + if (_registeredScreenHandlers == null) + _registeredScreenHandlers = new VideoScreenHandler[0]; + + foreach (VideoScreenHandler controlHandler in _registeredScreenHandlers) + { + if (newScreenHandler == controlHandler) + return; + } + + VideoScreenHandler[] newControlHandlers = new VideoScreenHandler[_registeredScreenHandlers.Length + 1]; + _registeredScreenHandlers.CopyTo(newControlHandlers, 0); + _registeredScreenHandlers = newControlHandlers; + + _registeredScreenHandlers[_registeredScreenHandlers.Length - 1] = newScreenHandler; + } + + public void UnregisterScreenHandler(VideoScreenHandler screenHandler) + { + if (_registeredScreenHandlers == null) + _registeredScreenHandlers = new VideoScreenHandler[0]; + + int controlHandlerCount = _registeredScreenHandlers.Length; + for (int i = 0; i < controlHandlerCount; ++i) + { + VideoScreenHandler handler = _registeredScreenHandlers[i]; + + if (screenHandler == handler) + { + VideoScreenHandler[] newControlHandlers = new VideoScreenHandler[controlHandlerCount - 1]; + + for (int j = 0; j < i; ++j) + newControlHandlers[j] = _registeredScreenHandlers[j]; + + for (int j = i + 1; j < controlHandlerCount; ++j) + newControlHandlers[j - 1] = _registeredScreenHandlers[j]; + + _registeredScreenHandlers = newControlHandlers; + + return; + } + } + } + + private Texture _lastAssignedRenderTexture; + + private void UpdateRenderTexture() + { + if (_registeredScreenHandlers == null) + return; + + Texture renderTexture = _videoPlayerManager.GetVideoTexture(); + + if (_lastAssignedRenderTexture == renderTexture) + return; + + foreach (VideoScreenHandler handler in _registeredScreenHandlers) + { + if (handler) + { + handler.UpdateVideoTexture(renderTexture, IsUsingAVProPlayer()); + } + } + + _lastAssignedRenderTexture = renderTexture; + + SendCallback("OnUSharpVideoRenderTextureChange"); + } +#endregion + +#region Callback Receivers + /// + /// Registers an UdonSharpBehaviour as a callback receiver for events that happen on this video player. + /// Callback receivers can be used to react to state changes on the video player without needing to check periodically. + /// + /// + [PublicAPI] + public void RegisterCallbackReceiver(UdonSharpBehaviour callbackReceiver) + { + if (!callbackReceiver) + return; + + if (_registeredCallbackReceivers == null) + _registeredCallbackReceivers = new UdonSharpBehaviour[0]; + + foreach (UdonSharpBehaviour currReceiver in _registeredCallbackReceivers) + { + if (callbackReceiver == currReceiver) + return; + } + + UdonSharpBehaviour[] newControlHandlers = new UdonSharpBehaviour[_registeredCallbackReceivers.Length + 1]; + _registeredCallbackReceivers.CopyTo(newControlHandlers, 0); + _registeredCallbackReceivers = newControlHandlers; + + _registeredCallbackReceivers[_registeredCallbackReceivers.Length - 1] = callbackReceiver; + } + + [PublicAPI] + public void UnregisterCallbackReceiver(UdonSharpBehaviour callbackReceiver) + { + if (!callbackReceiver) + return; + + if (_registeredCallbackReceivers == null) + _registeredCallbackReceivers = new UdonSharpBehaviour[0]; + + int callbackReceiverCount = _registeredCallbackReceivers.Length; + for (int i = 0; i < callbackReceiverCount; ++i) + { + UdonSharpBehaviour currHandler = _registeredCallbackReceivers[i]; + + if (callbackReceiver == currHandler) + { + UdonSharpBehaviour[] newCallbackReceivers = new UdonSharpBehaviour[callbackReceiverCount - 1]; + + for (int j = 0; j < i; ++j) + newCallbackReceivers[j] = _registeredCallbackReceivers[j]; + + for (int j = i + 1; j < callbackReceiverCount; ++j) + newCallbackReceivers[j - 1] = _registeredCallbackReceivers[j]; + + _registeredCallbackReceivers = newCallbackReceivers; + + return; + } + } + } + + private void SendCallback(string callbackName) + { + foreach (UdonSharpBehaviour callbackReceiver in _registeredCallbackReceivers) + { + if (callbackReceiver) + { + callbackReceiver.SendCustomEvent(callbackName); + } + } + } +#endregion + } +}