stride icon indicating copy to clipboard operation
stride copied to clipboard

Custom Assets can't use inherited types as parameters.

Open Doprez opened this issue 8 months ago • 4 comments

Is your feature request related to a problem? Please describe. I was trying to use a custom asset that used a base type called BaseItem as the parameter but it threw errors on compilation.

Describe the solution you'd like It would be a massive time saver to remove the boilerplate of creating individual items when the base type could be used. Below is what I was trying to do:

[DataContract]
[AssetDescription(FileExtension, AllowArchetype = false)]
[AssetContentType(typeof(ResourceItem))]
[AssetFormatVersion(nameof(Definitiions), CurrentVersion, "1.0.0.0")]
public class ResourceItemAsset : Asset
{
    private const string CurrentVersion = "1.0.0.0";
    public const string FileExtension = $".{nameof(ResourceItem)}";

    public BaseItem Item { get; set; }
}

/// <summary> Compiler which transforms your <see cref="ResourceItemAsset"/> into <see cref="ResourceItem"/> when building your game </summary>
[AssetCompiler(typeof(ResourceItemAsset), typeof(AssetCompilationContext))]
public sealed class ResourceItemCompiler : AssetCompilerBase
{
    protected override void Prepare(AssetCompilerContext context, AssetItem assetItem, string targetUrlInStorage, AssetCompilerResult result)
    {
        var asset = (ResourceItemAsset)assetItem.Asset;

        // you can have many build steps, each one is running an AssetCommand
        result.BuildSteps = new AssetBuildStep(assetItem);
        result.BuildSteps.Add(new ResourceItemDesignToRuntimeCommand(targetUrlInStorage, asset, assetItem.Package));
    }

    /// <summary>
    /// An <see cref="AssetCommand"/> that converts design time asset into runtime asset.
    /// </summary>
    public class ResourceItemDesignToRuntimeCommand(string url, ResourceItemAsset parameters, IAssetFinder assetFinder)
        : AssetCommand<ResourceItemAsset>(url, parameters, assetFinder)
    {
        protected override Task<ResultStatus> DoCommandOverride(ICommandContext commandContext)
        {
            var assetManager = new ContentManager(MicrothreadLocalDatabases.ProviderService);

            assetManager.Save(Url, Parameters.Item);

            commandContext.Logger.Info($"Saved {nameof(ResourceItem)}: {Url}");

            return Task.FromResult(ResultStatus.Successful);
        }
    }
}

BaseType:

[DataContract(Inherited = true)]
public abstract class BaseItem
{
    /// <summary>
    /// The unique identifier for the item.
    /// </summary>
    [DataMemberIgnore]
    public Guid Id { get; set; } = Guid.NewGuid();

    /// <summary>
    /// The Name for the item.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// The description of the item.
    /// </summary>
    public string Description { get; set; }

    /// <summary>
    /// Used to determine the base trading value of an item.
    /// </summary>
    public int Value { get; set; }

    /// <summary>
    /// The weight of the item, used for inventory management and affecting the movement stats of the holder.
    /// </summary>
    public int Weight { get; set; }
}

One of the inheritted types:

[ContentSerializer(typeof(DataContentSerializerWithReuse<ConsumableItem>))]
[ReferenceSerializer, DataSerializerGlobal(typeof(ReferenceSerializer<ConsumableItem>), Profile = "Content")]
public class ConsumableItem : BaseItem
{
}

Describe alternatives you've considered The alternative is just to create an asset for each individual Item type.

Additional context This architecture works within a Component type in a scene of GameStudio but the AssetCompiler seem to be the blocker for here for custom assets.

Doprez avatar Apr 14 '25 17:04 Doprez

What is the compilation error?

Kryptos-FR avatar Apr 14 '25 19:04 Kryptos-FR

So I tried to recreate a example project and now I'm getting the error in GameStudio directly as a template error.

Error: Failed to create new asset from template.
InvalidOperationException: Error when serializing reference.
   at Stride.Core.Serialization.Contents.ReferenceSerializer`1.Serialize(T& obj, ArchiveMode mode, SerializationStream stream) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\core\Stride.Core.Serialization\Serialization\Contents\ReferenceSerializer.cs:line 86
   at Stride.Core.Serialization.MemberReuseSerializer`1.Serialize(T& obj, ArchiveMode mode, SerializationStream stream) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\core\Stride.Core\Serialization\MemberSerializerGenerated.cs:line 1225
   at Stride.Core.DataSerializers.MyGame10AssetsDefinitions_GameItemAssetSerializer.Serialize(GameItemAsset& obj, ArchiveMode mode, SerializationStream stream)
   at Stride.Core.Serialization.DataSerializer`1.Serialize(Object& obj, ArchiveMode mode, SerializationStream stream) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\core\Stride.Core\Serialization\DataSerializer.cs:line 80
   at Stride.Core.Serialization.MemberReuseSerializer`1.SerializeExtended(T& obj, ArchiveMode mode, SerializationStream stream, DataSerializer`1 dataSerializer) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\core\Stride.Core\Serialization\MemberSerializerGenerated.cs:line 1429
   at Stride.Core.Assets.AssetCloner..ctor(Object value, AssetClonerFlags flags, IEnumerable`1 externalIdentifiables) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\AssetCloner.cs:line 73
   at Stride.Core.Assets.AssetCloner.Clone(Object asset, AssetClonerFlags flags, HashSet`1 externalIdentifiable, Dictionary`2& idRemapping) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\AssetCloner.cs:line 225
   at Stride.Core.Assets.AssetCloner.Clone(Object asset, AssetClonerFlags flags, Dictionary`2& idRemapping) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\AssetCloner.cs:line 240
   at Stride.Core.Assets.Analysis.AssetDependencyManager.TrackAsset(AssetId assetId) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\Analysis\AssetDependencyManager.cs:line 349
   at Stride.Core.Assets.Analysis.AssetDependencyManager.TrackAsset(AssetItem assetItemSource) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\Analysis\AssetDependencyManager.cs:line 321
   at Stride.Core.Assets.Analysis.AssetDependencyManager.Assets_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\Analysis\AssetDependencyManager.cs:line 576
   at Stride.Core.Assets.PackageAssetCollection.Add(AssetItem item) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\assets\Stride.Core.Assets\PackageAssetCollection.cs:line 137
   at Stride.Assets.Presentation.Templates.AssetTemplateGenerator.Run(AssetTemplateGeneratorParameters parameters) in C:\BuildAgent\work\b5f46e3c4829a09e\sources\editor\Stride.Assets.Presentation\Templates\AssetTemplateGenerator.cs:line 52

I think the original error was happening on build possibly because of an improper clean? I cant seem to get the same error as before but it still does not let me create the asset due to the above.

Project example:

AbstractAssetIssueRepro.zip

Once you open the GameStudio, you will see the error when adding the asset GameItems->GameItem.

Doprez avatar Apr 14 '25 20:04 Doprez

One more thing to add here as I didn't notice it before and its probably relevant. The inheriting types do not serialize the base class properties. I am assuming I either need to make a custom serializer for the type that includes the abstract data or in my case I found an interface to be more useful for what I was doing.

Doprez avatar Apr 24 '25 19:04 Doprez