示例#1
0
        private static DoshikExternalApiType GetOrCreateApiType(DoshikExternalApi api, string externalName)
        {
            // Не сохраняем Ref типы как отдельные типы - окончание Ref добавляется когда это параметр с ref / out модификатором
            if (externalName.EndsWith("Ref"))
            {
                externalName = externalName.Remove(externalName.Length - "Ref".Length, "Ref".Length);
            }

            // хз почему так, но видимо так надо (по другому не работает). В коде графов видел похожие замены
            switch (externalName)
            {
            case "VRCUdonCommonInterfacesIUdonEventReceiver": externalName = "VRCUdonUdonBehaviour"; break;

            case "VRCUdonCommonInterfacesIUdonEventReceiverArray": externalName = "VRCUdonUdonBehaviourArray"; break;
            }

            var apiType = api.Types.Find(x => x.ExternalName == externalName);

            if (apiType == null)
            {
                apiType = new DoshikExternalApiType
                {
                    ExternalName = externalName,
                    Methods      = new List <DoshikExternalApiTypeMethod>()
                };

                api.Types.Add(apiType);
            }

            return(apiType);
        }
示例#2
0
        private void HandleVariableNodes(DoshikExternalApi api, List <DoshikNodeDefinition> nodes)
        {
            foreach (var node in nodes)
            {
                bool CheckIsValid()
                {
                    if (node.InputParameters.Length != 5 || node.OutputParameters.Length != 0)
                    {
                        return(false);
                    }

                    if (node.InputParameters[0].Type != node.Type)
                    {
                        return(false);
                    }

                    if (node.InputParameters[1].Name != "name" || node.InputParameters[1].Type != typeof(string))
                    {
                        return(false);
                    }
                    if (node.InputParameters[2].Name != "public" || node.InputParameters[2].Type != typeof(bool))
                    {
                        return(false);
                    }
                    if (node.InputParameters[3].Name != "synced" || node.InputParameters[3].Type != typeof(bool))
                    {
                        return(false);
                    }
                    if (node.InputParameters[4].Name != "syncMode" || node.InputParameters[4].Type != typeof(string))
                    {
                        return(false);
                    }

                    return(true);
                }

                var externalTypeName = node.Identifier.Remove(0, "Variable_".Length);

                if (!CheckIsValid())
                {
                    LogWarning?.Invoke("invalid variable node");
                    continue;
                }

                var apiType = GetOrCreateApiType(api, externalTypeName);

                apiType.DeclaredAsVariableNode = true;

                if (apiType.DotnetTypeString == null)
                {
                    apiType.DotnetTypeString = GetDotnetTypeAsString(apiType, node);
                }

                if (apiType.DotnetTypeString != GetDotnetTypeAsString(apiType, node))
                {
                    LogWarning?.Invoke("different dotnet types for single node type");
                }
            }
        }
示例#3
0
        private void GenerateCodeNames(DoshikExternalApi api)
        {
            foreach (var apiEvent in api.Events)
            {
                var name = apiEvent.ExternalName;
                // remove first "_"
                name = name.Remove(0, 1);
                name = FirstLetterToUpperCase(name);

                apiEvent.CodeName = name;
            }

            foreach (var apiType in api.Types)
            {
                string firstNamespaceSegment = null;
                string name = apiType.ExternalName;

                bool handled;

                handled = TryHandleTypePrefix("System", "System", ref firstNamespaceSegment, ref name);
                if (!handled)
                {
                    handled = TryHandleTypePrefix("UnityEngine", "UnityEngine", ref firstNamespaceSegment, ref name);
                }
                if (!handled)
                {
                    handled = TryHandleTypePrefix("VRCSDK3Components", "VRCSDK3Components", ref firstNamespaceSegment, ref name);
                }
                if (!handled)
                {
                    handled = TryHandleTypePrefix("VRCSDKBase", "VRCSDKBase", ref firstNamespaceSegment, ref name);
                }
                if (!handled)
                {
                    handled = TryHandleTypePrefix("VRCUdon", "VRCUdon", ref firstNamespaceSegment, ref name);
                }

                if (firstNamespaceSegment == null)
                {
                    apiType.FullyQualifiedCodeName = new string[] { name };
                }
                else
                {
                    apiType.FullyQualifiedCodeName = new string[] { firstNamespaceSegment, name };
                }

                foreach (var method in apiType.Methods)
                {
                    // Пока сохраняем оригинальные имена методов (потом возможно я переименую имена, связанные с операторами, чтобы из кода это выглядело как-то посимпатичнее)
                    method.CodeName = method.ExternalName;
                }
            }
        }
示例#4
0
        private void HandleEventNodes(DoshikExternalApi api, List <DoshikNodeDefinition> nodes)
        {
            foreach (var node in nodes)
            {
                bool CheckIsValid()
                {
                    if (node.InputParameters.Length != 0)
                    {
                        return(false);
                    }

                    return(true);
                }

                // Игнорируем кастомные события
                if (node.Identifier == "Event_Custom")
                {
                    continue;
                }

                var externalEventName = "_" + FirstLetterToLowerCase(node.Identifier.Remove(0, "Event_".Length));

                if (!CheckIsValid())
                {
                    LogWarning?.Invoke("invalid event node");
                    continue;
                }

                bool eventCreated;
                var  apiEvent = GetOrCreateApiEvent(api, externalEventName, out eventCreated);

                if (!eventCreated)
                {
                    LogWarning?.Invoke("event already defined");
                    continue;
                }

                for (var nodeOutputParameterIdx = 0; nodeOutputParameterIdx < node.OutputParameters.Length; nodeOutputParameterIdx++)
                {
                    var nodeParameter = node.OutputParameters[nodeOutputParameterIdx];

                    var apiType = GetOrCreateApiType(api, nodeParameter.Type);

                    apiEvent.InParameters.Add(new DoshikExternalApiMethodParameter()
                    {
                        Name = string.IsNullOrEmpty(nodeParameter.Name) ? ("_" + nodeOutputParameterIdx.ToString()) : nodeParameter.Name,
                        Type = apiType
                    });
                }
            }
        }
示例#5
0
        private void HandleConstNodes(DoshikExternalApi api, List <DoshikNodeDefinition> nodes)
        {
            foreach (var node in nodes)
            {
                bool CheckIsValid()
                {
                    if (node.InputParameters.Length != 1 || node.OutputParameters.Length != 1)
                    {
                        return(false);
                    }

                    if (node.InputParameters[0].Type != node.OutputParameters[0].Type || node.InputParameters[0].Type != node.Type)
                    {
                        return(false);
                    }

                    return(true);
                }

                // Игнорируем тип null
                if (node.Identifier == "Const_Null")
                {
                    continue;
                }

                var externalTypeName = node.Identifier.Remove(0, "Const_".Length);

                if (!CheckIsValid())
                {
                    LogWarning?.Invoke("invalid const node");
                    continue;
                }

                var apiType = GetOrCreateApiType(api, externalTypeName);

                apiType.DeclaredAsConstNode = true;

                if (apiType.DotnetTypeString == null)
                {
                    apiType.DotnetTypeString = GetDotnetTypeAsString(apiType, node);
                }

                if (apiType.DotnetTypeString != GetDotnetTypeAsString(apiType, node))
                {
                    LogWarning?.Invoke("different dotnet types for single node type");
                }
            }
        }
示例#6
0
        private DoshikExternalApiType GetOrCreateApiType(DoshikExternalApi api, Type dotnetType)
        {
            var externalName = MakeExternalNameFromDotnetType(dotnetType);

            var apiType = GetOrCreateApiType(api, externalName);

            if (apiType.DotnetTypeString == null)
            {
                apiType.DotnetTypeString = GetDotnetTypeAsString(dotnetType);
            }

            if (apiType.DotnetTypeString != GetDotnetTypeAsString(dotnetType))
            {
                LogWarning?.Invoke("different dotnet types for single node type");
            }

            return(apiType);
        }
示例#7
0
        public static void SetApiToCache(DoshikExternalApi api)
        {
            var cacheFolder = GetCacheFolder();

            Directory.CreateDirectory(cacheFolder);

            var fileName = GetCacheFileName(cacheFolder);

            var formatting = Formatting.None;

            var settings = new JsonSerializerSettings();

            settings.ReferenceLoopHandling      = ReferenceLoopHandling.Serialize;
            settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;

            var apiJson = JsonConvert.SerializeObject(api, formatting, settings);

            File.WriteAllText(fileName, apiJson);

            _memoryCachedApi = api;
        }
示例#8
0
        private static DoshikExternalApiEvent GetOrCreateApiEvent(DoshikExternalApi api, string externalName, out bool created)
        {
            created = false;

            var apiEvent = api.Events.Find(x => x.ExternalName == externalName);

            if (apiEvent == null)
            {
                apiEvent = new DoshikExternalApiEvent
                {
                    ExternalName = externalName,
                    InParameters = new List <DoshikExternalApiMethodParameter>()
                };

                api.Events.Add(apiEvent);

                created = true;
            }

            return(apiEvent);
        }
示例#9
0
        public DoshikExternalApi Generate(string externalApiVersion)
        {
            // Получаем все ноды (типы нод), которые можно использовать в языке графов
            var allNodes = DoshikNodeDefinitionGetter.GetAllNodeDefinitions(new Dictionary <string, Type> { /*{ "VRCUdonUdonBehaviour", typeof(UdonBehaviour) }*/
            })
                           .GroupBy(x => x.Identifier)
                           .Select(x => x.First())
                           .OrderBy(x => x.Identifier);

            var constNodes    = new List <DoshikNodeDefinition>();
            var typeNodes     = new List <DoshikNodeDefinition>();
            var variableNodes = new List <DoshikNodeDefinition>();
            var eventNodes    = new List <DoshikNodeDefinition>();
            var methodNodes   = new List <DoshikNodeDefinition>();

            // Распределяем ноды по категориям (среди нераспределенных останутся операторы ветвления и т.д. - они нам не нужны)
            foreach (var node in allNodes)
            {
                if (node.Identifier.StartsWith("Const_"))
                {
                    constNodes.Add(node);
                }
                else if (node.Identifier.StartsWith("Type_"))
                {
                    typeNodes.Add(node);
                }
                else if (node.Identifier.StartsWith("Variable_"))
                {
                    variableNodes.Add(node);
                }
                else if (node.Identifier.StartsWith("Event_"))
                {
                    eventNodes.Add(node);
                }
                else if (node.Identifier.Contains(".__"))
                {
                    methodNodes.Add(node);
                }
            }

            var api = new DoshikExternalApi
            {
                ExternalVersion = externalApiVersion,

                Types  = new List <DoshikExternalApiType>(),
                Events = new List <DoshikExternalApiEvent>()
            };

            // Обрабатываем все эти ноды по разному, в зависимости от категории, формируя АПИ

            HandleConstNodes(api, constNodes);
            HandleTypeNodes(api, typeNodes);
            HandleVariableNodes(api, variableNodes);

            // Обработка ивентов и методов идет в конце, т.к. там идет референс параметр по дотнет типу (эти типы уже должны быть созданы в апи к этому времени)

            HandleMethodNodes(api, methodNodes);
            HandleEventNodes(api, eventNodes);

            GenerateCodeNames(api);

            return(api);
        }
示例#10
0
        private void HandleMethodNodes(DoshikExternalApi api, List <DoshikNodeDefinition> nodes)
        {
            foreach (var node in nodes)
            {
                bool CheckIsValid()
                {
                    return(true);
                }

                const string typeAndFullSignatureSeparatorString = ".__";
                const string methodAndSignatureSeparatorString   = "__";

                var typeAndFullSignatureSeparator = node.Identifier.IndexOf(typeAndFullSignatureSeparatorString);
                var externalTypeName = node.Identifier.Substring(0, typeAndFullSignatureSeparator);
                var fullSignature    = node.Identifier.Substring(typeAndFullSignatureSeparator + typeAndFullSignatureSeparatorString.Length, node.Identifier.Length - (typeAndFullSignatureSeparator + typeAndFullSignatureSeparatorString.Length));

                var methodAndSignatureSeparator = fullSignature.IndexOf(methodAndSignatureSeparatorString);
                var externalMethodName          = fullSignature.Substring(0, methodAndSignatureSeparator);
                var externalMethodSignatureName = fullSignature.Substring(methodAndSignatureSeparator + methodAndSignatureSeparatorString.Length, fullSignature.Length - (methodAndSignatureSeparator + methodAndSignatureSeparatorString.Length));

                if (!CheckIsValid())
                {
                    LogWarning?.Invoke("invalid method node");
                    continue;
                }

                var apiType = GetOrCreateApiType(api, externalTypeName);

                if (apiType.DotnetTypeString == null)
                {
                    apiType.DotnetTypeString = GetDotnetTypeAsString(apiType, node);
                }

                if (apiType.DotnetTypeString != GetDotnetTypeAsString(apiType, node))
                {
                    LogWarning?.Invoke("different dotnet types for single node type");
                }

                var apiMethod = GetOrCreateMethod(apiType, externalMethodName);

                bool methodOverloadCreated;
                // externalTypeName в данном случае не всегда будет равно apiType.ExternalName
                var apiMethodOverload = GetOrCreateMethodOverload(apiMethod, externalTypeName, externalMethodSignatureName, out methodOverloadCreated);

                if (!methodOverloadCreated)
                {
                    LogWarning?.Invoke("method overload already defined");
                    continue;
                }

                apiMethodOverload.IsStatic = true;

                for (var nodeInputParameterIdx = 0; nodeInputParameterIdx < node.InputParameters.Length; nodeInputParameterIdx++)
                {
                    var nodeParameter = node.InputParameters[nodeInputParameterIdx];

                    var parameterApiType = GetOrCreateApiType(api, nodeParameter.Type);

                    var isInstanceParameter = false;

                    if (nodeInputParameterIdx == 0 && nodeParameter.Name == "instance")
                    {
                        // ToDo: раньше я проверял еще на apiType == parameterApiType для того чтобы уточнить что это именно instance метод,
                        // но для интерфейсов, которые имплементятся в udonbehaviour-е почему-то первый параметр имеет тип UnityEngine.Object,
                        // а не тип интерфейса (который указан в текущем apiType), по этому я оставляю только проверку по имени первого параметра
                        // есть еще варианты с проверкой по extern-сигнатуры, т.к. для instance методов в ней не указывается instance параметр,
                        // но там вроде тоже конвенция нестабильно соблюдается, так что не хочется на нее опираться.

                        isInstanceParameter = true;
                    }

                    if (isInstanceParameter)
                    {
                        apiMethodOverload.IsStatic = false;
                    }
                    else
                    {
                        apiMethodOverload.InParameters.Add(new DoshikExternalApiMethodParameter()
                        {
                            Name = string.IsNullOrEmpty(nodeParameter.Name) ? ("_" + nodeInputParameterIdx.ToString()) : nodeParameter.Name,
                            Type = parameterApiType
                        });
                    }
                }

                for (var nodeOutputParameterIdx = 0; nodeOutputParameterIdx < node.OutputParameters.Length; nodeOutputParameterIdx++)
                {
                    var nodeParameter = node.OutputParameters[nodeOutputParameterIdx];

                    var parameterApiType = GetOrCreateApiType(api, nodeParameter.Type);

                    var isMain = nodeOutputParameterIdx == 0 && string.IsNullOrEmpty(nodeParameter.Name);

                    if (isMain)
                    {
                        apiMethodOverload.OutParameterType = parameterApiType;
                    }
                    else
                    {
                        apiMethodOverload.ExtraOutParameters.Add(new DoshikExternalApiMethodParameter()
                        {
                            Name = string.IsNullOrEmpty(nodeParameter.Name) ? ("_" + nodeOutputParameterIdx.ToString()) : nodeParameter.Name,
                            Type = parameterApiType
                        });
                    }
                }
            }
        }