Skip to content

Custom Assets can't use inherited types as parameters. #2713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Doprez opened this issue Apr 14, 2025 · 4 comments
Open

Custom Assets can't use inherited types as parameters. #2713

Doprez opened this issue Apr 14, 2025 · 4 comments
Labels
enhancement New feature or request

Comments

@Doprez
Copy link
Contributor

Doprez commented Apr 14, 2025

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 Doprez added the enhancement New feature or request label Apr 14, 2025
@Kryptos-FR
Copy link
Member

What is the compilation error?

@Doprez
Copy link
Contributor Author

Doprez commented Apr 14, 2025

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.

@Kryptos-FR Kryptos-FR changed the title Custom Assets cant use inheritted types as parameters. Custom Assets can't use inherited types as parameters. Apr 15, 2025
@Doprez
Copy link
Contributor Author

Doprez commented Apr 24, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants