Skip to main content

Using Generated Code

Exposing Interface Implementation to Components

Use the static method CreateWaaSInstance() defined as a member of the interface.

// <auto-generated />

partial interface IEnv
{
public static IInstance CreateWaaSInstance(IEnv target);
}

CreateWaaSInstance() is a method that wraps an interface implemented in C# and converts it to IInstance. By converting it to IInstance, it can be imported when instantiating a component. It can be used when you want to provide the necessary implementation to the component from the C# side.

var component = LoadComponent();

var instance = component.Instantiate(null, new Dictionary<string, ISortedExportable>()
{
{ "my-game:my-sequencer/env", IEnv.CreateWaaSInstance(new EnvImpl()) }
});

public class EnvImpl : IEnv { /* Implement here */ }

Handling Ownership

When Owned<T> is passed to C# code as an argument, you need to do one of the following:

  • Drop ownership by calling Owned<T>.Dispose()
  • Transfer ownership by passing Owned<T> to another function

If you do not do this, resources will leak.

Linking Resources to C# Objects

If you want to provide the implementation of a resource type on the C# side, you can link the resource to a C# object.

To link a resource to a C# object, inherit HostResourceTypeBase<T> in the implementation class of the resource type. You can convert the resource type and C# object using the Wrap() or Unwrap() method.

using WaaS.ComponentModel.Binding;
using WaaS.ComponentModel.Runtime;
using System;
using System.IO;

[ComponentInterface(@"env")]
public partial interface IEnv
{
[ComponentResource(@"stream")]
public partial interface IStreamResourceImpl : IResourceImpl
{
[ComponentApi("[constructor]stream")]
Owned<IStreamResourceImpl> Create();

[ComponentApi("[method]stream.write-byte")]
void WriteByte(Borrowed<IStreamResourceImpl> self, byte value);
}
}

public class StreamResourceType : HostResourceTypeBase<Stream>, IEnv.IStreamResourceImpl
{
public Owned<IEnv.IStreamResourceImpl> Create()
{
var stream = File.Open("hoge.txt", FileMode.Create, FileAccess.Write);

// Stream -> Owned<IStreamResourceImpl>
var handle = Wrap<IEnv.IStreamResourceImpl>(stream);

return handle;
}

public void WriteByte(Borrowed<IEnv.IStreamResourceImpl> self, byte value)
{
// Borrowed<IStreamResourceImpl> -> Stream
var stream = Unwrap(self);

stream.WriteByte(value);
}

public IResourceType Type => this;
}

public class EnvImpl : IEnv
{
public IEnv.IStreamResourceImpl Stream { get; } = new StreamResourceType();
}
warning

When creating Owned<T> with Wrap(), the object will be referenced from the internal table. By returning Owned<T> as a return value, the reference will be released when the resource is no longer needed.

Calling Functions exposed by Components

You can use a struct Wrapper defined as a member of the interface by the Source Generator.

// <auto-generated />

partial interface IEnv
{
public readonly struct Wrapper : IEnv
{
public Wrapper(IInstance instance, ExecutionContext context);

public ValueTask ShowMessage(string @speaker, string @message);
public ValueTask<uint> ShowOptions(ReadOnlyMemory<string> @options)
}
}

Wrapper is a struct that wraps externally created IInstance and converts it to IEnv.
It is assumed that the target instance exports functions that correspond to IEnv (no automatic check is performed).
It can be used when calling functions exported by components from C#.

var component = await componentAsset.LoadComponentAsync();

var instance = component.Instantiate(null, new Dictionary<string, ISortedExportable>());

using var context = new ExecutionContext();

var wrapper = new IEnv.Wrapper(instance, context);

await wraapper.ShowMessage("Me", "Hello");