using System; using System.Collections.Generic; using UnityEngine; using System.Linq; using UnityEditor; namespace Unity.Multiplayer.Editor { [Serializable] internal struct AutomaticSelectionOptions { private Dictionary m_CompleteComponentsList; internal Dictionary CompleteComponentsList { get { if (m_CompleteComponentsList == null) BakeCompleteComponentsList(); return m_CompleteComponentsList; } } [SerializeField] private bool m_StripRenderComponents; [SerializeField] private bool m_StripUIComponents; [SerializeField] private bool m_StripAudioComponents; public bool StripRenderingComponents { get => m_StripRenderComponents; set { // Don't bake if value didn't change if (m_StripRenderComponents == value) return; m_StripRenderComponents = value; BakeCompleteComponentsList(); } } public bool StripUIComponents { get => m_StripUIComponents; set { // Don't bake if value didn't change if (m_StripUIComponents == value) return; m_StripUIComponents = value; BakeCompleteComponentsList(); } } public bool StripAudioComponents { get => m_StripAudioComponents; set { // Don't bake if value didn't change if (m_StripAudioComponents == value) return; m_StripAudioComponents = value; BakeCompleteComponentsList(); } } [SerializeField] private SerializedDictionary m_CustomComponentsList; private SerializedDictionary CustomComponentsList { get { if (m_CustomComponentsList == null) m_CustomComponentsList = new(); return m_CustomComponentsList; } set { m_CustomComponentsList = value; BakeCompleteComponentsList(); } } public Dictionary GetCustomComponents() { var dictionary = new Dictionary(); foreach (var kvp in CustomComponentsList) { // This will happen if the script was deleted. if (kvp.Key.Value == null) continue; dictionary[kvp.Key.Value] = kvp.Value; } return dictionary; } public void SetCustomComponents(Dictionary customComponents) { CustomComponentsList.Clear(); if (customComponents != null) foreach (var kvp in customComponents) SetCustomComponentMultiplayerRoleFlags(kvp.Key, kvp.Value, false); BakeCompleteComponentsList(); } public MultiplayerRoleFlags GetMultiplayerRoleMaskForComponentType(Type type) => GetMultiplayerRoleFlagsForType(type); public void SetMultiplayerRoleMaskForComponentType(Type type, MultiplayerRoleFlags mask) => SetCustomComponentMultiplayerRoleFlags(type, mask, true); internal void SetCustomComponentMultiplayerRoleFlags(Type type, MultiplayerRoleFlags target, bool bake = true) { if (!type.IsSubclassOf(typeof(Component))) throw new ArgumentException("Only subclasses of Component can be selected."); if (target == MultiplayerRoleFlags.ClientAndServer) CustomComponentsList.Remove(new SerializedType(type)); else CustomComponentsList[new SerializedType(type)] = target; if (bake) BakeCompleteComponentsList(); } internal AutomaticSelectionOptions Clone() { var options = this; options.m_CustomComponentsList = null; options.m_CompleteComponentsList = null; options.SetCustomComponents(this.GetCustomComponents()); return options; } internal static void GetBuiltinStrippingComponentsForServer(in AutomaticSelectionOptions options, List types) { if (options.StripRenderingComponents) { var components = TypeCache.GetTypesDerivedFrom() .Concat(new[] { typeof(Camera), typeof(Light), typeof(ReflectionProbe), typeof(LightProbeGroup), typeof(LightProbeProxyVolume) }); #if UNITY_RENDER_PIPELINE_PRESENT components = components.Concat(TypeCache.GetTypesDerivedFrom()); #endif foreach (var component in components) types.Add(component); } if (options.StripUIComponents) { var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where ( a => a.GetName().Name == "UnityEngine.UI" || a.GetName().Name == "UnityEngine.Canvas" || a.GetName().Name == "UnityEngine.UIElementsModule" ); var components = assemblies.SelectMany(a => a.GetTypes().Where(t => IsClassOrSubclassOf(t, typeof(Component)))); foreach (var component in components) types.Add(component); } if (options.StripAudioComponents) { var assembly = typeof(AudioClip).Assembly; var components = assembly.GetTypes().Where(t => IsClassOrSubclassOf(t, typeof(Component))); foreach (var component in components) types.Add(component); } } private void BakeCompleteComponentsList() { if (m_CompleteComponentsList == null) m_CompleteComponentsList = new(); m_CompleteComponentsList.Clear(); var builtinServerTypes = new List(); GetBuiltinStrippingComponentsForServer(this, builtinServerTypes); foreach (var type in builtinServerTypes) { m_CompleteComponentsList[type] = MultiplayerRoleFlags.ClientAndServer & ~MultiplayerRoleFlags.Server; } foreach (var component in CustomComponentsList) { if (component.Key.Value == null) continue; if (!m_CompleteComponentsList.TryGetValue(component.Key.Value, out var maskValue)) maskValue = MultiplayerRoleFlags.ClientAndServer; maskValue &= component.Value; m_CompleteComponentsList[component.Key.Value] = maskValue; } } internal static bool IsClassOrSubclassOf(Type type, Type parentType) => type == parentType || type.IsSubclassOf(parentType); internal bool IsComponentSelected(Type type) => CompleteComponentsList.Keys.Any(selectedType => IsClassOrSubclassOf(type, selectedType)); internal IEnumerable> GetSelectedParentComponents(Type type) => CompleteComponentsList.Where(selectionValue => AutomaticSelectionOptions.IsClassOrSubclassOf(type, selectionValue.Key)); internal MultiplayerRoleFlags GetMultiplayerRoleFlagsForType(Type type) { if (!CompleteComponentsList.TryGetValue(type, out var target)) target = MultiplayerRoleFlags.ClientAndServer; return target; } internal MultiplayerRoleFlags GetInheritMultiplayerRoleFlagsForType(System.Type type) { var parentTypes = CompleteComponentsList.Where(kvp => IsClassOrSubclassOf(type, kvp.Key)); var target = MultiplayerRoleFlags.ClientAndServer; foreach (var item in parentTypes) target &= item.Value; return target; } } }