Beispiel #1
0
        public static IEnumerable <Warning> Warnings(FlowGraph graph, IEnumerable <IUnitPortDefinition> definitions = null)
        {
            if (definitions == null)
            {
                definitions = LinqUtility.Concat <IUnitPortDefinition>(graph.controlInputDefinitions,
                                                                       graph.controlOutputDefinitions,
                                                                       graph.valueInputDefinitions,
                                                                       graph.valueOutputDefinitions);
            }

            var hasDuplicate = definitions.DistinctBy(d => d.key).Count() != definitions.Count();

            if (hasDuplicate)
            {
                yield return(Warning.Caution("Some port definitions with non-unique keys are currently ignored."));
            }

            foreach (var definition in definitions)
            {
                if (!definition.isValid)
                {
                    yield return(InvalidWarning(definition));
                }
            }
        }
Beispiel #2
0
        protected override IEnumerable <Warning> Warnings()
        {
            foreach (var baseWarning in base.Warnings())
            {
                yield return(baseWarning);
            }

            var graph = transition.nest.graph;

            if (graph == null)
            {
                yield break;
            }

            using (var recursion = Recursion.New(1))
            {
                if (!graph.GetUnitsRecursive(recursion).OfType <IEventUnit>().Any())
                {
                    yield return(Warning.Caution("Transition graph is missing an event."));
                }
            }

            using (var recursion = Recursion.New(1))
            {
                if (!graph.GetUnitsRecursive(recursion).OfType <TriggerStateTransition>().Any())
                {
                    yield return(Warning.Caution("Transition graph is missing a trigger unit."));
                }
            }
        }
Beispiel #3
0
        protected override IEnumerable <Warning> Warnings()
        {
            foreach (var baseWarning in base.Warnings())
            {
                yield return(baseWarning);
            }

            if (target.member != null && target.member.isReflected)
            {
                var obsoleteAttribute = target.member.info.GetAttribute <ObsoleteAttribute>();

                if (obsoleteAttribute != null)
                {
                    if (obsoleteAttribute.Message != null)
                    {
                        Debug.LogWarning($"\"{target.member.name}\" unit member is deprecated: {obsoleteAttribute.Message}");
                        yield return(Warning.Caution("Deprecated: " + obsoleteAttribute.Message));
                    }
                    else
                    {
                        Debug.LogWarning($"\"{target.member.name}\" unit member is deprecated.");
                        yield return(Warning.Caution($"Member {target.member.name} is deprecated."));
                    }
                }
            }
        }
Beispiel #4
0
 public static Warning InvalidWarning(IUnitPortDefinition definition)
 {
     if (!StringUtility.IsNullOrWhiteSpace(definition.label))
     {
         return(Warning.Caution($"{definition.GetType().HumanName().ToLower().FirstCharacterToUpper()} '{definition.label}' is not properly configured and is currently ignored."));
     }
     else
     {
         return(Warning.Caution($"A {definition.GetType().HumanName().ToLower()} with incomplete configuration is currently ignored."));
     }
 }
Beispiel #5
0
        protected override IEnumerable <Warning> Warnings()
        {
            foreach (var baseWarning in base.Warnings())
            {
                yield return(baseWarning);
            }

            if (transition.childGraph == null)
            {
                yield return(Warning.Caution("Missing transition graph."));
            }

            if (transition.nest.hasBackgroundEmbed)
            {
                yield return(Warning.Caution("Background embed graph detected."));
            }
        }
Beispiel #6
0
        protected override IEnumerable <Warning> Warnings()
        {
            foreach (var baseWarning in base.Warnings())
            {
                yield return(baseWarning);
            }

            if (unit.graph != null)
            {
                foreach (var definitionWarning in UnitPortDefinitionUtility.Warnings(unit.graph, LinqUtility.Concat <IUnitPortDefinition>(unit.graph.controlOutputDefinitions, unit.graph.valueOutputDefinitions)))
                {
                    yield return(definitionWarning);
                }

                if (unit.graph.units.Count(unit => unit is GraphOutput) > 1)
                {
                    yield return(Warning.Caution("Multiple output units in the same graph. Only one of them will be used."));
                }
            }
        }
        protected virtual IEnumerable <Warning> Warnings()
        {
            var isEntered = IsEntered();

            if (!unit.isDefined)
            {
                if (unit.definitionException != null)
                {
                    yield return(Warning.Exception(unit.definitionException));
                }
                else if (!unit.canDefine)
                {
                    yield return(Warning.Caution("Unit is not properly configured."));
                }
            }

            if (!isEntered)
            {
                yield return(Warning.Info("Unit is never entered."));
            }

            // Obsolete attribute is not inherited, so traverse the chain manually
            var obsoleteAttribute = unit.GetType().AndHierarchy().FirstOrDefault(t => t.HasAttribute <ObsoleteAttribute>())?.GetAttribute <ObsoleteAttribute>();

            if (obsoleteAttribute != null)
            {
                var unitName = BoltFlowNameUtility.UnitTitle(unit.GetType(), true, false);

                if (obsoleteAttribute.Message != null)
                {
                    Debug.LogWarning($"\"{unitName}\" unit is deprecated: {obsoleteAttribute.Message}");
                    yield return(Warning.Caution($"Deprecated: {obsoleteAttribute.Message}"));
                }
                else
                {
                    Debug.LogWarning($"\"{unitName}\" unit is deprecated.");
                    yield return(Warning.Caution("This unit is deprecated."));
                }
            }

            if (unit.isDefined)
            {
                foreach (var invalidInput in unit.invalidInputs)
                {
                    yield return(Warning.Caution($"{PortLabel(invalidInput)} is not used by this unit."));
                }

                foreach (var invalidOutput in unit.invalidOutputs)
                {
                    yield return(Warning.Caution($"{PortLabel(invalidOutput)} is not provided by this unit."));
                }

                foreach (var validPort in unit.validPorts)
                {
                    if (validPort.hasInvalidConnection)
                    {
                        yield return(Warning.Caution($"{PortLabel(validPort)} has an invalid connection."));
                    }
                }
            }

            foreach (var controlInput in unit.controlInputs)
            {
                if (!controlInput.hasValidConnection)
                {
                    continue;
                }

                foreach (var relation in controlInput.relations)
                {
                    if (relation.source is ValueInput)
                    {
                        var valueInput = (ValueInput)relation.source;

                        foreach (var warning in ValueInputWarnings(valueInput))
                        {
                            yield return(warning);
                        }
                    }
                }
            }

            foreach (var controlOutput in unit.controlOutputs)
            {
                if (!controlOutput.hasValidConnection)
                {
                    continue;
                }

                var controlInputs = controlOutput.relations.Select(r => r.source).OfType <ControlInput>();

                var isTriggered = !controlInputs.Any() || controlInputs.Any(ci => !ci.isPredictable || ci.couldBeEntered);

                foreach (var relation in controlOutput.relations)
                {
                    if (relation.source is ValueInput)
                    {
                        var valueInput = (ValueInput)relation.source;

                        foreach (var warning in ValueInputWarnings(valueInput))
                        {
                            yield return(warning);
                        }
                    }
                }

                if (isEntered && !isTriggered)
                {
                    yield return(Warning.Caution($"{PortLabel(controlOutput)} is connected, but it is never triggered."));
                }
            }

            foreach (var valueOutput in unit.valueOutputs)
            {
                if (!valueOutput.hasValidConnection)
                {
                    continue;
                }

                foreach (var relation in valueOutput.relations)
                {
                    if (relation.source is ControlInput)
                    {
                        var controlInput = (ControlInput)relation.source;

                        if (isEntered && controlInput.isPredictable && !controlInput.couldBeEntered)
                        {
                            yield return(Warning.Severe($"{PortLabel(controlInput)} is required, but it is never entered."));
                        }
                    }
                    else if (relation.source is ValueInput)
                    {
                        var valueInput = (ValueInput)relation.source;

                        foreach (var warning in ValueInputWarnings(valueInput))
                        {
                            yield return(warning);
                        }
                    }
                }
            }
        }
        private IEnumerable <Warning> ValueInputWarnings(ValueInput valueInput)
        {
            // We can disable null reference check if no self is available
            // and the port requires an owner, for example in macros.
            var trustFutureOwner = valueInput.nullMeansSelf && reference.self == null;

            var checkForNullReference = BoltFlow.Configuration.predictPotentialNullReferences && !valueInput.allowsNull && !trustFutureOwner;

            var checkForMissingComponent = BoltFlow.Configuration.predictPotentialMissingComponents && typeof(Component).IsAssignableFrom(valueInput.type);

            // Note that we cannot directly check the input's predicted value, because it
            // will return false for safeguard specifically because it might be missing requirements.
            // Therefore, we first check the connected value, then the default value.

            // If the port is connected to a predictable output, use the connected value to perform checks.
            if (valueInput.hasValidConnection)
            {
                var valueOutput = valueInput.validConnectedPorts.Single();

                if (Flow.CanPredict(valueOutput, reference))
                {
                    if (checkForNullReference)
                    {
                        if (Flow.Predict(valueOutput, reference) == null)
                        {
                            yield return(Warning.Severe($"{PortLabel(valueInput)} cannot be null."));
                        }
                    }

                    if (checkForMissingComponent)
                    {
                        var connectedPredictedValue = Flow.Predict(valueOutput, reference);

                        // This check is necessary, because the predicted value could be
                        // incompatible as connections with non-guaranteed conversions are allowed.
                        if (ConversionUtility.CanConvert(connectedPredictedValue, typeof(GameObject), true))
                        {
                            var gameObject = ConversionUtility.Convert <GameObject>(connectedPredictedValue);

                            if (gameObject != null)
                            {
                                var component = (Component)ConversionUtility.Convert(gameObject, valueInput.type);

                                if (component == null)
                                {
                                    yield return(Warning.Caution($"{PortLabel(valueInput)} is missing a {valueInput.type.DisplayName()} component."));
                                }
                            }
                        }
                    }
                }
            }
            // If the port isn't connected but has a default value, use the default value to perform checks.
            else if (valueInput.hasDefaultValue)
            {
                if (checkForNullReference)
                {
                    if (Flow.Predict(valueInput, reference) == null)
                    {
                        yield return(Warning.Severe($"{PortLabel(valueInput)} cannot be null."));
                    }
                }

                if (checkForMissingComponent)
                {
                    var unconnectedPredictedValue = Flow.Predict(valueInput, reference);

                    if (ConversionUtility.CanConvert(unconnectedPredictedValue, typeof(GameObject), true))
                    {
                        var gameObject = ConversionUtility.Convert <GameObject>(unconnectedPredictedValue);

                        if (gameObject != null)
                        {
                            var component = (Component)ConversionUtility.Convert(gameObject, valueInput.type);

                            if (component == null)
                            {
                                yield return(Warning.Caution($"{PortLabel(valueInput)} is missing a {valueInput.type.DisplayName()} component."));
                            }
                        }
                    }
                }
            }
            // The value isn't connected and has no default value,
            // therefore it is certain to be missing at runtime.
            else
            {
                yield return(Warning.Severe($"{PortLabel(valueInput)} is missing."));
            }
        }