Compare commits
No commits in common. "main" and "dotnet-guest/split-lyra-api" have entirely different histories.
main
...
dotnet-gue
File diff suppressed because it is too large
Load Diff
|
@ -13,8 +13,6 @@ tokio = { version = "1.41.1", features = ["full"] }
|
||||||
wasmtime = "26.0.1"
|
wasmtime = "26.0.1"
|
||||||
wasmtime-wasi = "26.0.1"
|
wasmtime-wasi = "26.0.1"
|
||||||
lyra-ecs = { path = "./lyra-engine/crates/lyra-ecs" }
|
lyra-ecs = { path = "./lyra-engine/crates/lyra-ecs" }
|
||||||
lyra-reflect = { path = "./lyra-engine/crates/lyra-reflect" }
|
|
||||||
lyra-engine = { path = "./lyra-engine" }
|
|
||||||
slab = "0.4.9"
|
slab = "0.4.9"
|
||||||
thiserror = "2.0.0"
|
thiserror = "2.0.0"
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.0.31903.59
|
VisualStudioVersion = 17.0.31903.59
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-guest-test", "guests\csharp\dotnet-guest-test\dotnet-guest-test.csproj", "{68F96E6F-472F-409E-B36E-9C5E7206CCDE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LyraApi", "guests/csharp/LyraApi/LyraApi.csproj", "{A4003DF0-C8FF-4A50-A66C-9777E900ED21}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-guest-test", "guests/csharp/dotnet-guest-test\dotnet-guest-test.csproj", "{68F96E6F-472F-409E-B36E-9C5E7206CCDE}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
bin
|
||||||
|
obj
|
|
@ -1,8 +1,7 @@
|
||||||
namespace LyraApi.Ecs;
|
namespace LyraApi.Ecs;
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
using ImportsWorld.wit.imports.lyra.api;
|
||||||
|
|
||||||
public class Entity(IEcs.Entity entity)
|
public class Entity(IEcs.Entity entity) {
|
||||||
{
|
|
||||||
internal IEcs.Entity Inner { get; set; } = entity;
|
internal IEcs.Entity Inner { get; set; } = entity;
|
||||||
|
|
||||||
public ulong Id { get => Inner.id.id; }
|
public ulong Id { get => Inner.id.id; }
|
|
@ -0,0 +1,15 @@
|
||||||
|
using ImportsWorld.wit.imports.lyra.api;
|
||||||
|
|
||||||
|
namespace LyraApi.Ecs;
|
||||||
|
|
||||||
|
public interface IComponent {
|
||||||
|
public static string HostName { get; }
|
||||||
|
public static ulong HostSize { get; }
|
||||||
|
public static ulong HostAlignment { get; }
|
||||||
|
public static ulong TypeId { get; }
|
||||||
|
|
||||||
|
public IEcs.ComponentInfo GetComponentInfo() {
|
||||||
|
var typeId = new IEcs.WasmTypeId((TypeId, 0));
|
||||||
|
return new IEcs.ComponentInfo(HostName, HostSize, HostAlignment, typeId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace LyraApi.Ecs;
|
||||||
|
|
||||||
|
using ImportsWorld.wit.imports.lyra.api;
|
||||||
|
using LyraApi;
|
||||||
|
|
||||||
|
public class World(IEcs.EcsWorld world) {
|
||||||
|
private IEcs.EcsWorld Inner { get; set; } = world;
|
||||||
|
|
||||||
|
public Entity Spawn(params IComponent[] comps) {
|
||||||
|
List<IEcs.ComponentInfo> infos = comps.Select(c => c.GetComponentInfo()).ToList();
|
||||||
|
byte[] bytes = comps.SelectMany(c => Marshalling.GetBytes(c)).ToArray();
|
||||||
|
return new Entity(Inner.Spawn(bytes, infos));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<RootNamespace>LyraApi</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
|
||||||
|
<UseAppHost>false</UseAppHost>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BytecodeAlliance.Componentize.DotNet.Wasm.SDK" Version="0.4.0-preview00007" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Wit Remove="**\*.wit" />
|
||||||
|
<Wit Include="wit" World="imports" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-alpha.1.24531.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -3,14 +3,11 @@ namespace LyraApi;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
public static class MarshalUtils
|
public static class Marshalling {
|
||||||
{
|
|
||||||
internal struct AlignmentHelper<T> where T : unmanaged
|
internal struct AlignmentHelper<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value 0
|
|
||||||
public byte Padding;
|
public byte Padding;
|
||||||
public T Target;
|
public T Target;
|
||||||
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int AlignmentOf<T>() where T : unmanaged
|
public static int AlignmentOf<T>() where T : unmanaged
|
||||||
|
@ -18,8 +15,7 @@ public static class MarshalUtils
|
||||||
return (int)Marshal.OffsetOf<AlignmentHelper<T>>(nameof(AlignmentHelper<T>.Target));
|
return (int)Marshal.OffsetOf<AlignmentHelper<T>>(nameof(AlignmentHelper<T>.Target));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] GetBytes<T>([DisallowNull] T data)
|
public static byte[] GetBytes<T>([DisallowNull] T data) {
|
||||||
{
|
|
||||||
int size = Marshal.SizeOf(data);
|
int size = Marshal.SizeOf(data);
|
||||||
byte[] arr = new byte[size];
|
byte[] arr = new byte[size];
|
||||||
|
|
||||||
|
@ -37,17 +33,13 @@ public static class MarshalUtils
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T? FromBytes<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(byte[] data)
|
public static T? FromBytes<T>(byte[] data) where T : struct
|
||||||
{
|
{
|
||||||
var ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
|
var ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||||
try
|
try {
|
||||||
{
|
var res = (T?) Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T));
|
||||||
//var res = (T?)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T));
|
|
||||||
var res = Marshal.PtrToStructure<T>(ptr.AddrOfPinnedObject());
|
|
||||||
return res;
|
return res;
|
||||||
}
|
} finally {
|
||||||
finally
|
|
||||||
{
|
|
||||||
ptr.Free();
|
ptr.Free();
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||||
|
<clear />
|
||||||
|
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
|
||||||
|
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,81 @@
|
||||||
|
package lyra:api;
|
||||||
|
|
||||||
|
interface ecs {
|
||||||
|
record entity-id {
|
||||||
|
id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
record entity {
|
||||||
|
id: entity-id,
|
||||||
|
generation: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
record wasm-type-id {
|
||||||
|
// represents a u128, can be converted into that with mem::transmute
|
||||||
|
inner: tuple<u64, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record component-info {
|
||||||
|
host-name: option<string>,
|
||||||
|
/// The size of the component in memory.
|
||||||
|
size: u64,
|
||||||
|
/// The alignment of the component in memory.
|
||||||
|
alignment: u64,
|
||||||
|
/// The type id of the component.
|
||||||
|
///
|
||||||
|
/// This must be unique between component types since its used to identify the components
|
||||||
|
/// in spawning and querying.
|
||||||
|
type-id: wasm-type-id,
|
||||||
|
}
|
||||||
|
|
||||||
|
resource ecs-dynamic-view {
|
||||||
|
constructor(wrld: borrow<ecs-world>, component-infos: list<component-info>);
|
||||||
|
|
||||||
|
/// Get the bytes of the next row in the view.
|
||||||
|
///
|
||||||
|
/// A row contains multiple component serialized as bytes. The buffer is tighly packed.
|
||||||
|
next: func() -> option<tuple<entity, list<u8>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource ecs-world {
|
||||||
|
constructor();
|
||||||
|
|
||||||
|
/// Spawn an entity.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `components`: A tightly packed byte buffer containing the components to spawn
|
||||||
|
/// with the entity. This expects the components in the same order of `component-infos`.
|
||||||
|
/// * `component-infos`: A list of `component-infos` uses to identify the components
|
||||||
|
/// and specify the layouts of them.
|
||||||
|
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
|
||||||
|
|
||||||
|
/// Insert components into an existing entity.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `en`: The entity to insert into.
|
||||||
|
/// * `components`: A tightly packed byte buffer containing the components to spawn
|
||||||
|
/// with the entity. This expects the components in the same order of `component-infos`.
|
||||||
|
/// * `component-infos`: A list of `component-infos` uses to identify the components
|
||||||
|
/// and specify the layouts of them.
|
||||||
|
insert: func(en: entity, components: list<u8>, component-infos: list<component-info>);
|
||||||
|
|
||||||
|
/// Query for a list of entities and their components.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `component-infos`: The `component-info`'s of the components that you are querying.
|
||||||
|
///
|
||||||
|
/// Returns: an iterator that returns the byte buffers of each row.
|
||||||
|
view: func(component-infos: list<component-info>) -> ecs-dynamic-view;
|
||||||
|
|
||||||
|
/// Query for components from a single entity.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `en`: The entity to query components from.
|
||||||
|
/// * `component-infos`: The `component-info`'s of the components that you are querying.
|
||||||
|
///
|
||||||
|
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
||||||
|
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
||||||
|
|
||||||
|
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package lyra:api;
|
||||||
|
|
||||||
|
world imports {
|
||||||
|
import ecs;
|
||||||
|
}
|
|
@ -1,48 +1,103 @@
|
||||||
namespace ExampleWorld;
|
namespace ExampleWorld;
|
||||||
|
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using LyraApi.Asset;
|
using System.Runtime.InteropServices;
|
||||||
|
//using ExampleWorld.wit.imports.lyra.api;
|
||||||
|
using LyraApi;
|
||||||
using LyraApi.Ecs;
|
using LyraApi.Ecs;
|
||||||
using LyraApi.Engine;
|
using ImportsWorld.wit.imports.lyra.api;
|
||||||
using LyraApi.Math;
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct Vec3(float x, float y, float z) : IComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The X component.
|
||||||
|
/// </summary>
|
||||||
|
public float X { get; set; } = x;
|
||||||
|
/// <summary>
|
||||||
|
/// The Y component
|
||||||
|
/// </summary>
|
||||||
|
public float Y { get; set; } = y;
|
||||||
|
/// <summary>
|
||||||
|
/// The Z component
|
||||||
|
/// </summary>
|
||||||
|
public float Z { get; set; } = z;
|
||||||
|
|
||||||
|
public static string HostName { get => "Vec3"; }
|
||||||
|
public static ulong HostSize { get => (ulong)Marshal.SizeOf<Vec3>(); }
|
||||||
|
public static ulong HostAlignment { get => (ulong)Marshalling.AlignmentOf<Vec3>(); }
|
||||||
|
public static ulong TypeId { get => 4124409524; }
|
||||||
|
}
|
||||||
|
|
||||||
public class ExampleWorldImpl : IExampleWorld
|
public class ExampleWorldImpl : IExampleWorld
|
||||||
{
|
{
|
||||||
|
internal struct AlignmentHelper<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public byte Padding;
|
||||||
|
public T Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int AlignmentOf<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return (int)Marshal.OffsetOf<AlignmentHelper<T>>(nameof(AlignmentHelper<T>.Target));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] GetBytes<T>([DisallowNull] T data) {
|
||||||
|
int size = Marshal.SizeOf(data);
|
||||||
|
byte[] arr = new byte[size];
|
||||||
|
|
||||||
|
IntPtr ptr = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ptr = Marshal.AllocHGlobal(size);
|
||||||
|
Marshal.StructureToPtr(data, ptr, true);
|
||||||
|
Marshal.Copy(ptr, arr, 0, size);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T? FromBytes<T>(byte[] data) where T : struct
|
||||||
|
{
|
||||||
|
var ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||||
|
try {
|
||||||
|
var res = (T?) Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T));
|
||||||
|
return res;
|
||||||
|
} finally {
|
||||||
|
ptr.Free();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void OnInit(IEcs.EcsWorld gameWorld, IEcs.Entity owningEntity)
|
public static void OnInit(IEcs.EcsWorld gameWorld, IEcs.Entity owningEntity)
|
||||||
{
|
{
|
||||||
|
var pos = new Vec3(7.0f, 30.0f, 18.0f);
|
||||||
|
var size = new IEcs.WasmTypeId((Vec3.TypeId, 0));
|
||||||
|
var info = new IEcs.ComponentInfo("Vec3", (ulong) Marshal.SizeOf<Vec3>(), (ulong) AlignmentOf<Vec3>(), size);
|
||||||
|
var infos = new List<IEcs.ComponentInfo>
|
||||||
|
{
|
||||||
|
info
|
||||||
|
};
|
||||||
|
|
||||||
var world = new World(gameWorld);
|
var world = new World(gameWorld);
|
||||||
Entity entity = world.Spawn(new Vec3(7.0f, 30.0f, 18.0f));
|
Entity entity = world.Spawn(pos);
|
||||||
Console.WriteLine("C#: Spawned entity with id {0}", entity.Id);
|
Console.WriteLine("C#: Spawned {0}", entity.Id);
|
||||||
|
|
||||||
foreach ((Entity en, Vec3 comp) in world.View<Vec3>())
|
IEcs.EcsDynamicView res = gameWorld.View(infos);
|
||||||
{
|
(IEcs.Entity, byte[])? row = res.Next();
|
||||||
Console.WriteLine("C#: Found entity at ({0}, {1}, {2})", comp.X, comp.Y, comp.Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DeltaTime? dt = world.GetResource<DeltaTime>();
|
if (row.HasValue) {
|
||||||
if (dt != null) {
|
Console.WriteLine("C#: Found position!!!");
|
||||||
Console.WriteLine($"C#: Delta time: {dt?.Seconds:0.##}");
|
|
||||||
} */
|
|
||||||
|
|
||||||
AssetManager? man = world.GetResource<AssetManager>();
|
var en = row.Value.Item1;
|
||||||
if (man != null) {
|
var comp = FromBytes<Vec3>(row.Value.Item2);
|
||||||
Handle<ImageHandle> han = man.Request<ImageHandle>("test_assets/white.png");
|
|
||||||
Console.WriteLine($"C#: Asset uuid: {han.Uuid}");
|
|
||||||
|
|
||||||
// wait for asset to load before trying to get the data
|
Console.WriteLine("C#: Found component at ({0}, {1}, {2})", comp?.X, comp?.Y, comp?.Z);
|
||||||
han.WaitForLoadRecursive();
|
} else {
|
||||||
if (!han.IsLoaded)
|
Console.WriteLine("C#: Could not find position");
|
||||||
{
|
|
||||||
Console.WriteLine("C#: Asset is still not loaded, even after waiting!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// will be null if the image hasn't loaded yet
|
|
||||||
ImageHandle? img = han.GetData();
|
|
||||||
Console.WriteLine($"C#: Size of image: ({img?.Width}, {img?.Height})");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("C#: No manager found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
using LyraApi.Ecs;
|
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace LyraApi.Asset;
|
|
||||||
|
|
||||||
public class AssetManager(IAsset.AssetManager assetManager) : IResource
|
|
||||||
{
|
|
||||||
internal IAsset.AssetManager inner = assetManager;
|
|
||||||
|
|
||||||
public static ulong TypeId => 567234789345;
|
|
||||||
|
|
||||||
public static object? FromWasmResult(IEcs.WorldResourceResult result)
|
|
||||||
{
|
|
||||||
switch (result.Tag)
|
|
||||||
{
|
|
||||||
case IEcs.WorldResourceResult.NONE:
|
|
||||||
return null;
|
|
||||||
case IEcs.WorldResourceResult.WASM_RESOURCE:
|
|
||||||
var handle = IAsset.AssetManager.FromEcsResource(result.AsWasmResource) ?? throw new MismatchedResourceResultException();
|
|
||||||
return new AssetManager(handle);
|
|
||||||
case IEcs.WorldResourceResult.BYTES:
|
|
||||||
return null;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UntypedHandle Request(string path)
|
|
||||||
{
|
|
||||||
return new UntypedHandle(inner.Request(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handle<T> Request<T>(string path) where T : IAssetHandle
|
|
||||||
{
|
|
||||||
return Request(path).AsHandle<T>();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
namespace LyraApi.Asset;
|
|
||||||
|
|
||||||
public class Handle<T>(UntypedHandle handle) where T : IAssetHandle
|
|
||||||
{
|
|
||||||
internal UntypedHandle inner = handle;
|
|
||||||
|
|
||||||
public bool Watched { get => inner.Watched; }
|
|
||||||
public ulong Version { get => inner.Version; }
|
|
||||||
public Guid Uuid { get => inner.Uuid; }
|
|
||||||
public string? Path { get => inner.Path; }
|
|
||||||
public bool IsLoaded { get => inner.IsLoaded; }
|
|
||||||
|
|
||||||
public void WaitForLoad()
|
|
||||||
{
|
|
||||||
inner.WaitForLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitForLoadRecursive()
|
|
||||||
{
|
|
||||||
inner.WaitForLoadRecursive();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? GetData()
|
|
||||||
{
|
|
||||||
if (IsLoaded)
|
|
||||||
{
|
|
||||||
return (T?)T.FromRawHandle(inner);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace LyraApi.Asset;
|
|
||||||
|
|
||||||
public interface IAssetHandle
|
|
||||||
{
|
|
||||||
public abstract static object? FromRawHandle(UntypedHandle untypedHandle);
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
|
|
||||||
namespace LyraApi.Asset;
|
|
||||||
|
|
||||||
public class ImageHandle(IAsset.ImageHandle handle) : IAssetHandle
|
|
||||||
{
|
|
||||||
internal IAsset.ImageHandle inner = handle;
|
|
||||||
|
|
||||||
public uint? Height => inner.Height();
|
|
||||||
public uint? Width => inner.Width();
|
|
||||||
|
|
||||||
public static object? FromRawHandle(UntypedHandle untypedHandle)
|
|
||||||
{
|
|
||||||
var handle = IAsset.ImageHandle.FromRawHandle(untypedHandle.inner);
|
|
||||||
return handle != null ? new ImageHandle(handle) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the image's pixels as native endian bytes.
|
|
||||||
/// </summary>
|
|
||||||
///
|
|
||||||
/// Keep in mind that this does copy the image's pixels from the host.
|
|
||||||
/// This is pretty slow.
|
|
||||||
public byte[]? GetImageBytes()
|
|
||||||
{
|
|
||||||
return inner.GetBytes();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
|
|
||||||
namespace LyraApi.Asset;
|
|
||||||
|
|
||||||
public class UntypedHandle(IAsset.AssetHandle handle)
|
|
||||||
{
|
|
||||||
internal IAsset.AssetHandle inner = handle;
|
|
||||||
|
|
||||||
public bool Watched { get => inner.IsWatched(); }
|
|
||||||
public ulong Version { get => inner.Version(); }
|
|
||||||
public Guid Uuid { get => Guid.Parse(inner.Uuid()); }
|
|
||||||
public string? Path { get => inner.Path(); }
|
|
||||||
public bool IsLoaded { get => inner.IsLoaded(); }
|
|
||||||
|
|
||||||
public void WaitForLoad()
|
|
||||||
{
|
|
||||||
inner.WaitForLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitForLoadRecursive()
|
|
||||||
{
|
|
||||||
inner.WaitRecurseDependenciesLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handle<T> AsHandle<T>() where T : IAssetHandle
|
|
||||||
{
|
|
||||||
return new Handle<T>(this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System.Reflection;
|
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
using LyraApi.Ecs;
|
|
||||||
|
|
||||||
public class Component(string hostName, ulong hostSize, ulong hostAlignment, ulong typeId)
|
|
||||||
{
|
|
||||||
public string HostName { get; } = hostName;
|
|
||||||
public ulong HostSize { get; } = hostSize;
|
|
||||||
public ulong HostAlignment { get; } = hostAlignment;
|
|
||||||
public ulong TypeId { get; } = typeId;
|
|
||||||
|
|
||||||
public static Component FromComponent<T>() where T : IComponent
|
|
||||||
{
|
|
||||||
return new Component(T.HostName, T.HostSize, T.HostAlignment, T.TypeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComponentInfo GetComponentInfo()
|
|
||||||
{
|
|
||||||
return new ComponentInfo(HostName, HostSize, HostAlignment, TypeId);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
|
|
||||||
namespace LyraApi.Ecs;
|
|
||||||
|
|
||||||
public readonly struct ComponentInfo
|
|
||||||
{
|
|
||||||
internal readonly IEcs.ComponentInfo info;
|
|
||||||
|
|
||||||
public readonly string? HostName => info.hostName;
|
|
||||||
public readonly ulong Size => info.size;
|
|
||||||
public readonly ulong Alignment => info.alignment;
|
|
||||||
public readonly ulong TypeId => info.typeId.inner.Item1;
|
|
||||||
|
|
||||||
public ComponentInfo(IEcs.ComponentInfo info)
|
|
||||||
{
|
|
||||||
this.info = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComponentInfo(string hostName, ulong size, ulong alignment, ulong typeId)
|
|
||||||
{
|
|
||||||
var wasmTypeId = new IEcs.WasmTypeId((typeId, 0));
|
|
||||||
this.info = new IEcs.ComponentInfo(hostName, size, alignment, wasmTypeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ComponentInfo FromType<T>() where T : IComponent
|
|
||||||
{
|
|
||||||
var typeId = new IEcs.WasmTypeId((T.TypeId, 0));
|
|
||||||
var info = new IEcs.ComponentInfo(T.HostName, T.HostSize, T.HostAlignment, typeId);
|
|
||||||
return new ComponentInfo(info);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
using System.Data.SqlTypes;
|
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
|
|
||||||
namespace LyraApi.Ecs;
|
|
||||||
|
|
||||||
public interface IComponent
|
|
||||||
{
|
|
||||||
public abstract static string HostName { get; }
|
|
||||||
public abstract static ulong HostSize { get; }
|
|
||||||
public abstract static ulong HostAlignment { get; }
|
|
||||||
public abstract static ulong TypeId { get; }
|
|
||||||
|
|
||||||
public abstract static object? TakeFromBytes(byte[] bytes);
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using static ExampleWorld.wit.imports.lyra.api.IEcs;
|
|
||||||
|
|
||||||
namespace LyraApi.Ecs;
|
|
||||||
|
|
||||||
public interface IResource
|
|
||||||
{
|
|
||||||
public abstract static ulong TypeId { get; }
|
|
||||||
|
|
||||||
public abstract static object? FromWasmResult(WorldResourceResult result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MismatchedResourceResultException : Exception {}
|
|
|
@ -1,20 +0,0 @@
|
||||||
using System.ComponentModel;
|
|
||||||
using ExampleWorld;
|
|
||||||
|
|
||||||
namespace LyraApi.Ecs;
|
|
||||||
|
|
||||||
public class View
|
|
||||||
{
|
|
||||||
internal List<ComponentInfo> infos;
|
|
||||||
|
|
||||||
private View(List<ComponentInfo> infos)
|
|
||||||
{
|
|
||||||
this.infos = infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static View Create<T1>() where T1 : IComponent
|
|
||||||
{
|
|
||||||
List<ComponentInfo> infos = [ComponentInfo.FromType<T1>()];
|
|
||||||
return new View(infos);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System.Runtime.InteropServices.Swift;
|
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
|
|
||||||
namespace LyraApi.Ecs;
|
|
||||||
|
|
||||||
public class ViewResult
|
|
||||||
{
|
|
||||||
private List<Component> components;
|
|
||||||
private IEcs.EcsDynamicView inner;
|
|
||||||
|
|
||||||
internal ViewResult(List<Component> components, IEcs.EcsDynamicView inner)
|
|
||||||
{
|
|
||||||
this.components = components;
|
|
||||||
this.inner = inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<(Entity, T1)> Get<T1>() where T1 : IComponent
|
|
||||||
{
|
|
||||||
(IEcs.Entity, byte[])? row = inner.Next();
|
|
||||||
|
|
||||||
if (row is (var entity, byte[] bytes))
|
|
||||||
{
|
|
||||||
byte[] compBytes = bytes.Take((int)T1.HostSize).ToArray();
|
|
||||||
var t1 = (T1?)T1.TakeFromBytes(compBytes);
|
|
||||||
|
|
||||||
if (t1 != null)
|
|
||||||
{
|
|
||||||
yield return (new Entity(entity), t1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
namespace LyraApi.Ecs;
|
|
||||||
|
|
||||||
using ExampleWorld.wit.imports.lyra.api;
|
|
||||||
using LyraApi;
|
|
||||||
using LyraApi.Asset;
|
|
||||||
|
|
||||||
public class World(IEcs.EcsWorld world)
|
|
||||||
{
|
|
||||||
internal IEcs.EcsWorld inner = world;
|
|
||||||
|
|
||||||
private Entity Spawn(List<IEcs.ComponentInfo> infos, params object[] comps)
|
|
||||||
{
|
|
||||||
byte[] bytes = comps.SelectMany(c => MarshalUtils.GetBytes(c)).ToArray();
|
|
||||||
return new Entity(inner.Spawn(bytes, infos));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity Spawn<T1>(T1 c1) where T1 : IComponent
|
|
||||||
{
|
|
||||||
List<IEcs.ComponentInfo> infos = [ComponentInfo.FromType<T1>().info];
|
|
||||||
return Spawn(infos, c1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<(Entity, T1)> View<T1>() where T1 : IComponent
|
|
||||||
{
|
|
||||||
List<Component> comps = [Component.FromComponent<T1>()];
|
|
||||||
List<IEcs.ComponentInfo> infos = comps.Select(c => c.GetComponentInfo().info).ToList();
|
|
||||||
|
|
||||||
IEcs.EcsDynamicView dynamicView = inner.View(infos);
|
|
||||||
return new ViewResult(comps, dynamicView).Get<T1>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? GetResource<T>() where T : IResource
|
|
||||||
{
|
|
||||||
IEcs.WorldResourceResult result = inner.GetResource(Utils.ToWasmTypeId(T.TypeId));
|
|
||||||
return (T?)T.FromWasmResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* public AssetManager? GetAssetManager()
|
|
||||||
{
|
|
||||||
IAsset.AssetManager? assetManager = IAsset.AssetManager.FromWorld(inner);
|
|
||||||
|
|
||||||
if (assetManager != null)
|
|
||||||
{
|
|
||||||
return new AssetManager(assetManager);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using LyraApi;
|
|
||||||
using LyraApi.Ecs;
|
|
||||||
using static ExampleWorld.wit.imports.lyra.api.IEcs;
|
|
||||||
|
|
||||||
namespace LyraApi.Engine;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct DeltaTime : IResource {
|
|
||||||
public float Seconds;
|
|
||||||
|
|
||||||
public static ulong TypeId => 83716348954;
|
|
||||||
|
|
||||||
public static object? FromWasmResult(WorldResourceResult result)
|
|
||||||
{
|
|
||||||
return result.Tag switch
|
|
||||||
{
|
|
||||||
WorldResourceResult.NONE | WorldResourceResult.WASM_RESOURCE => null,
|
|
||||||
WorldResourceResult.BYTES => MarshalUtils.FromBytes<DeltaTime>(result.AsBytes),
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using LyraApi.Ecs;
|
|
||||||
|
|
||||||
namespace LyraApi.Math;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct Vec3(float x, float y, float z) : IComponent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The X component.
|
|
||||||
/// </summary>
|
|
||||||
public float X { get; set; } = x;
|
|
||||||
/// <summary>
|
|
||||||
/// The Y component
|
|
||||||
/// </summary>
|
|
||||||
public float Y { get; set; } = y;
|
|
||||||
/// <summary>
|
|
||||||
/// The Z component
|
|
||||||
/// </summary>
|
|
||||||
public float Z { get; set; } = z;
|
|
||||||
|
|
||||||
public static string HostName => "Vec3";
|
|
||||||
public static ulong HostSize => (ulong)Marshal.SizeOf<Vec3>();
|
|
||||||
public static ulong HostAlignment => (ulong)MarshalUtils.AlignmentOf<Vec3>();
|
|
||||||
public static ulong TypeId => 4124409524;
|
|
||||||
|
|
||||||
public static object? TakeFromBytes(byte[] bytes)
|
|
||||||
{
|
|
||||||
byte[] taken = bytes.Take((int)HostSize).ToArray();
|
|
||||||
return MarshalUtils.FromBytes<Vec3>(taken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
using static ExampleWorld.wit.imports.lyra.api.IEcs;
|
|
||||||
|
|
||||||
internal static class Utils {
|
|
||||||
public static WasmTypeId ToWasmTypeId(ulong typeId)
|
|
||||||
{
|
|
||||||
return new WasmTypeId((typeId, 0));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,51 +12,48 @@
|
||||||
<InvariantGlobalization>true</InvariantGlobalization>
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
|
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
|
||||||
|
|
||||||
<!-- Ignore warning of multiple `PackageReference` items for the `ILCompiler.LLVM` package. -->
|
|
||||||
<NoWarn>$(NoWarn);NU1504</NoWarn>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Copy WIT files from LyraApi to lyra-api wit folder. -->
|
<!-- Copy WIT files from LyraApi to lyra-api wit folder. -->
|
||||||
<Target Name="CopyLyraApiWit" BeforeTargets="WitCompile_InvokeTool">
|
<Target Name="CopyFolderOnBuild" BeforeTargets="WitCompile_InvokeTool">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<MyFiles Include="..\..\rust\common-api\wit\**\*.wit" />
|
<MyFiles Include="..\LyraApi\wit\**\*.wit" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Copy SourceFiles="@(MyFiles)" DestinationFolder="wit\deps\lyraapi\%(RecursiveDir)" />
|
<Copy SourceFiles="@(MyFiles)" DestinationFolder="wit\deps\lyraapi\%(RecursiveDir)" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="CopyComponentGuestWit" BeforeTargets="WitCompile_InvokeTool">
|
|
||||||
<Copy SourceFiles="..\..\rust\witguest\wit\world.wit" DestinationFiles="wit\world.wit" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<!-- Remove bindgen of LyraApi WIT .-->
|
<!-- Remove bindgen of LyraApi WIT .-->
|
||||||
<!-- <Target Name="RemoveBindgenLyraApi" AfterTargets="WitCompile_InvokeTool">
|
<Target Name="RemoveBindgenLyraApi" AfterTargets="WitCompile_InvokeTool">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FilesToDelete Include="obj\Debug\net*\wasi-wasm\wit_bindgen\*.lyra.api.*.cs"/>
|
<FilesToDelete Include="obj\Debug\net*\wasi-wasm\wit_bindgen\*.lyra.api.*.cs"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Delete Files="@(FilesToDelete)" />
|
<Delete Files="@(FilesToDelete)" />
|
||||||
</Target> -->
|
</Target>
|
||||||
|
|
||||||
<!-- Reuse bindgen from LyraApi package instead of using local bindgen. -->
|
<!-- Reuse bindgen from LyraApi package instead of using local bindgen. -->
|
||||||
<!-- <Target Name="UpdateLyraApiReferences" AfterTargets="WitCompile_InvokeTool; RemoveBindgenLyraApi">
|
<Target Name="UpdateLyraApiReferences" AfterTargets="WitCompile_InvokeTool; RemoveBindgenLyraApi">
|
||||||
<Exec Condition="'$(OS)' == 'Unix'" Command="find obj/Debug -type f -wholename '*wit_bindgen/*.cs' -exec sed -i 's/ExampleWorld\.wit\.imports\.lyra\.api/ImportsWorld.wit.imports.lyra.api/g' {} \;" />
|
<Exec Condition="'$(OS)' == 'Unix'" Command="find obj/Debug -type f -wholename '*wit_bindgen/*.cs' -exec sed -i 's/ExampleWorld\.wit\.imports\.lyra\.api/ImportsWorld.wit.imports.lyra.api/g' {} \;" />
|
||||||
<Exec Condition="'$(OS)' == 'Windows_NT'" Command="powershell -Command "Get-ChildItem -Path 'obj\Debug' -Recurse -Filter '*.cs' ^
|
<Exec Condition="'$(OS)' == 'Windows_NT'" Command="powershell -Command "Get-ChildItem -Path 'obj\Debug' -Recurse -Filter '*.cs' ^
|
||||||
| Where-Object { $_.FullName -match 'wit_bindgen' } ^
|
| Where-Object { $_.FullName -match 'wit_bindgen' } ^
|
||||||
| ForEach-Object { (Get-Content -Path $_.FullName) -replace 'ExampleWorld\.wit\.imports\.lyra\.api', 'ImportsWorld.wit.imports.lyra.api' ^
|
| ForEach-Object { (Get-Content -Path $_.FullName) -replace 'ExampleWorld\.wit\.imports\.lyra\.api', 'ImportsWorld.wit.imports.lyra.api' ^
|
||||||
| Set-Content -Path $_.FullName }"" />
|
| Set-Content -Path $_.FullName }"" />
|
||||||
</Target> -->
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BytecodeAlliance.Componentize.DotNet.Wasm.SDK" Version="0.4.0-preview00007" />
|
<PackageReference Include="BytecodeAlliance.Componentize.DotNet.Wasm.SDK" Version="0.4.0-preview00007" />
|
||||||
<PackageReference Include="runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-alpha.1.24531.4" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Wit Remove="**\*.wit" />
|
<Wit Remove="**\*.wit" />
|
||||||
|
<!-- <Wit Include="wit/deps/lyraapi" World="imports" /> -->
|
||||||
<Wit Include="wit" World="example" />
|
<Wit Include="wit" World="example" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- <ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-alpha.1.24531.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\LyraApi\LyraApi.csproj" />
|
<ProjectReference Include="..\LyraApi\LyraApi.csproj" />
|
||||||
</ItemGroup> -->
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
interface asset {
|
|
||||||
use ecs.{ecs-world, ecs-resource};
|
|
||||||
|
|
||||||
resource asset-handle {
|
|
||||||
version: func() -> u64;
|
|
||||||
uuid: func() -> string;
|
|
||||||
path: func() -> option<string>;
|
|
||||||
is-watched: func() -> bool;
|
|
||||||
is-loaded: func() -> bool;
|
|
||||||
wait-for-load: func();
|
|
||||||
wait-recurse-dependencies-load: func();
|
|
||||||
}
|
|
||||||
|
|
||||||
resource image-handle {
|
|
||||||
from-raw-handle: static func(raw-handle: borrow<asset-handle>) -> option<image-handle>;
|
|
||||||
|
|
||||||
height: func() -> option<u32>;
|
|
||||||
width: func() -> option<u32>;
|
|
||||||
get-bytes: func() -> option<list<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource asset-manager {
|
|
||||||
from-ecs-resource: static func(w: borrow<ecs-resource>) -> option<asset-manager>;
|
|
||||||
request: func(path: string) -> asset-handle;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,19 +37,6 @@ interface ecs {
|
||||||
next: func() -> option<tuple<entity, list<u8>>>;
|
next: func() -> option<tuple<entity, list<u8>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An resource that can be "downcasted" to a specific type.
|
|
||||||
///
|
|
||||||
/// You can use `T.from-ecs-resource` to "downcast" the type
|
|
||||||
resource ecs-resource {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
variant world-resource-result {
|
|
||||||
none,
|
|
||||||
wasm-resource(ecs-resource),
|
|
||||||
bytes(list<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
resource ecs-world {
|
resource ecs-world {
|
||||||
constructor();
|
constructor();
|
||||||
|
|
||||||
|
@ -89,8 +76,6 @@ interface ecs {
|
||||||
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
||||||
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
||||||
|
|
||||||
get-resource: func(type-id: wasm-type-id) -> world-resource-result;
|
|
||||||
|
|
||||||
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,5 +2,4 @@ package lyra:api;
|
||||||
|
|
||||||
world imports {
|
world imports {
|
||||||
import ecs;
|
import ecs;
|
||||||
import asset;
|
|
||||||
}
|
}
|
|
@ -11,7 +11,6 @@ interface math {
|
||||||
/// An example world for the component to target.
|
/// An example world for the component to target.
|
||||||
world example {
|
world example {
|
||||||
import math;
|
import math;
|
||||||
import lyra:api/asset;
|
|
||||||
use lyra:api/ecs.{ecs-world, entity};
|
use lyra:api/ecs.{ecs-world, entity};
|
||||||
|
|
||||||
import host-print: func(msg: string);
|
import host-print: func(msg: string);
|
||||||
|
|
|
@ -8,9 +8,6 @@ pub use component::*;
|
||||||
mod bundle;
|
mod bundle;
|
||||||
pub use bundle::*;
|
pub use bundle::*;
|
||||||
|
|
||||||
pub mod resource;
|
|
||||||
pub use resource::EcsResource;
|
|
||||||
|
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
|
||||||
pub use api_derive as macros;
|
pub use api_derive as macros;
|
||||||
|
@ -32,12 +29,6 @@ impl WasmTypeId {
|
||||||
inner: unsafe { mem::transmute(TypeId::of::<T>()) },
|
inner: unsafe { mem::transmute(TypeId::of::<T>()) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn from_raw(id: u128) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: unsafe { mem::transmute(id) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum OwnedBorrow<'a, T> {
|
enum OwnedBorrow<'a, T> {
|
||||||
|
@ -130,11 +121,6 @@ impl<'a> World<'a> {
|
||||||
self.inner.view_one(en, &infos)
|
self.inner.view_one(en, &infos)
|
||||||
.map(|bytes| B::from_bytes(bytes))
|
.map(|bytes| B::from_bytes(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resource<T: EcsResource>(&self) -> Option<T> {
|
|
||||||
let res = self.inner.get_resource(T::TYPE_ID);
|
|
||||||
T::from_wasm_result(&res)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct View<B: Bundle> {
|
pub struct View<B: Bundle> {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use crate::lyra::api::ecs::{WasmTypeId, WorldResourceResult};
|
|
||||||
use super::EcsResource;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct DeltaTime(f32);
|
|
||||||
|
|
||||||
impl Deref for DeltaTime {
|
|
||||||
type Target = f32;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EcsResource for DeltaTime {
|
|
||||||
const TYPE_ID: WasmTypeId = WasmTypeId::from_raw(83716348954);
|
|
||||||
|
|
||||||
fn from_wasm_result(result: &WorldResourceResult) -> Option<Self> {
|
|
||||||
match result {
|
|
||||||
WorldResourceResult::None => None,
|
|
||||||
WorldResourceResult::WasmResource(_) => None,
|
|
||||||
WorldResourceResult::Bytes(vec) => Some(Self(*bytemuck::from_bytes::<f32>(&vec))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
use crate::lyra::api::ecs::{WasmTypeId, WorldResourceResult};
|
|
||||||
|
|
||||||
mod dt;
|
|
||||||
pub use dt::*;
|
|
||||||
|
|
||||||
pub trait EcsResource: Sized {
|
|
||||||
const TYPE_ID: WasmTypeId;
|
|
||||||
|
|
||||||
fn from_wasm_result(result: &WorldResourceResult) -> Option<Self>;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
interface asset {
|
|
||||||
use ecs.{ecs-world, ecs-resource};
|
|
||||||
|
|
||||||
resource asset-handle {
|
|
||||||
version: func() -> u64;
|
|
||||||
uuid: func() -> string;
|
|
||||||
path: func() -> option<string>;
|
|
||||||
is-watched: func() -> bool;
|
|
||||||
is-loaded: func() -> bool;
|
|
||||||
wait-for-load: func();
|
|
||||||
wait-recurse-dependencies-load: func();
|
|
||||||
}
|
|
||||||
|
|
||||||
resource image-handle {
|
|
||||||
from-raw-handle: static func(raw-handle: borrow<asset-handle>) -> option<image-handle>;
|
|
||||||
|
|
||||||
height: func() -> option<u32>;
|
|
||||||
width: func() -> option<u32>;
|
|
||||||
get-bytes: func() -> option<list<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource asset-manager {
|
|
||||||
from-ecs-resource: static func(w: borrow<ecs-resource>) -> option<asset-manager>;
|
|
||||||
request: func(path: string) -> asset-handle;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,19 +37,6 @@ interface ecs {
|
||||||
next: func() -> option<tuple<entity, list<u8>>>;
|
next: func() -> option<tuple<entity, list<u8>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An resource that can be "downcasted" to a specific type.
|
|
||||||
///
|
|
||||||
/// You can use `T.from-ecs-resource` to "downcast" the type
|
|
||||||
resource ecs-resource {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
variant world-resource-result {
|
|
||||||
none,
|
|
||||||
wasm-resource(ecs-resource),
|
|
||||||
bytes(list<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
resource ecs-world {
|
resource ecs-world {
|
||||||
constructor();
|
constructor();
|
||||||
|
|
||||||
|
@ -89,8 +76,6 @@ interface ecs {
|
||||||
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
||||||
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
||||||
|
|
||||||
get-resource: func(type-id: wasm-type-id) -> world-resource-result;
|
|
||||||
|
|
||||||
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,5 +2,4 @@ package lyra:api;
|
||||||
|
|
||||||
world imports {
|
world imports {
|
||||||
import ecs;
|
import ecs;
|
||||||
import asset;
|
|
||||||
}
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
use common_api::{math::{Vec3, Vec4}, resource::DeltaTime, World};
|
use common_api::{math::{Vec3, Vec4}, World};
|
||||||
|
|
||||||
wit_bindgen::generate!({
|
wit_bindgen::generate!({
|
||||||
world: "example",
|
world: "example",
|
||||||
|
|
||||||
with: {
|
with: {
|
||||||
"lyra:api/ecs": common_api::bindings::lyra::api::ecs,
|
"lyra:api/ecs": common_api::bindings::lyra::api::ecs,
|
||||||
"lyra:api/asset": common_api::bindings::lyra::api::asset,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,9 +22,6 @@ impl Guest for Component {
|
||||||
.unwrap().unwrap();
|
.unwrap().unwrap();
|
||||||
println!("Found entity at {pos3:?}");
|
println!("Found entity at {pos3:?}");
|
||||||
|
|
||||||
let dt = world.get_resource::<DeltaTime>().unwrap();
|
|
||||||
println!("dt is {}", *dt);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
interface asset {
|
|
||||||
use ecs.{ecs-world, ecs-resource};
|
|
||||||
|
|
||||||
resource asset-handle {
|
|
||||||
version: func() -> u64;
|
|
||||||
uuid: func() -> string;
|
|
||||||
path: func() -> option<string>;
|
|
||||||
is-watched: func() -> bool;
|
|
||||||
is-loaded: func() -> bool;
|
|
||||||
wait-for-load: func();
|
|
||||||
wait-recurse-dependencies-load: func();
|
|
||||||
}
|
|
||||||
|
|
||||||
resource image-handle {
|
|
||||||
from-raw-handle: static func(raw-handle: borrow<asset-handle>) -> option<image-handle>;
|
|
||||||
|
|
||||||
height: func() -> option<u32>;
|
|
||||||
width: func() -> option<u32>;
|
|
||||||
get-bytes: func() -> option<list<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource asset-manager {
|
|
||||||
from-ecs-resource: static func(w: borrow<ecs-resource>) -> option<asset-manager>;
|
|
||||||
request: func(path: string) -> asset-handle;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,19 +37,6 @@ interface ecs {
|
||||||
next: func() -> option<tuple<entity, list<u8>>>;
|
next: func() -> option<tuple<entity, list<u8>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An resource that can be "downcasted" to a specific type.
|
|
||||||
///
|
|
||||||
/// You can use `T.from-ecs-resource` to "downcast" the type
|
|
||||||
resource ecs-resource {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
variant world-resource-result {
|
|
||||||
none,
|
|
||||||
wasm-resource(ecs-resource),
|
|
||||||
bytes(list<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
resource ecs-world {
|
resource ecs-world {
|
||||||
constructor();
|
constructor();
|
||||||
|
|
||||||
|
@ -89,8 +76,6 @@ interface ecs {
|
||||||
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
||||||
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
||||||
|
|
||||||
get-resource: func(type-id: wasm-type-id) -> world-resource-result;
|
|
||||||
|
|
||||||
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,5 +2,4 @@ package lyra:api;
|
||||||
|
|
||||||
world imports {
|
world imports {
|
||||||
import ecs;
|
import ecs;
|
||||||
import asset;
|
|
||||||
}
|
}
|
|
@ -11,7 +11,6 @@ interface math {
|
||||||
/// An example world for the component to target.
|
/// An example world for the component to target.
|
||||||
world example {
|
world example {
|
||||||
import math;
|
import math;
|
||||||
import lyra:api/asset;
|
|
||||||
use lyra:api/ecs.{ecs-world, entity};
|
use lyra:api/ecs.{ecs-world, entity};
|
||||||
|
|
||||||
import host-print: func(msg: string);
|
import host-print: func(msg: string);
|
||||||
|
|
145
src/asset.rs
145
src/asset.rs
|
@ -1,145 +0,0 @@
|
||||||
use async_trait::async_trait;
|
|
||||||
use wasmtime::component::Resource as WasmResource;
|
|
||||||
|
|
||||||
use crate::{lyra, Imports};
|
|
||||||
|
|
||||||
use lyra::api::asset as wasm_asset;
|
|
||||||
use lyra::api::ecs as wasm_ecs;
|
|
||||||
|
|
||||||
impl wasm_asset::Host for Imports {}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl wasm_asset::HostAssetManager for Imports {
|
|
||||||
async fn from_ecs_resource(&mut self, res: WasmResource<wasm_ecs::EcsResource>) -> Option<WasmResource<wasm_asset::AssetManager>> {
|
|
||||||
let res_rep = res.rep() as u32;
|
|
||||||
let res = self.resource_data_slab.get_mut(res_rep as _)?;
|
|
||||||
res.borrow += 1;
|
|
||||||
|
|
||||||
// ensure that the
|
|
||||||
if !res.data.is::<lyra_engine::assets::ResourceManager>() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(WasmResource::new_own(res_rep as _))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn request(
|
|
||||||
&mut self,
|
|
||||||
this: WasmResource<wasm_asset::AssetManager>,
|
|
||||||
path: String,
|
|
||||||
) -> WasmResource<wasm_asset::AssetHandle> {
|
|
||||||
let man = self.resource_data_slab.get(this.rep() as _)
|
|
||||||
.unwrap()
|
|
||||||
.data.get::<lyra_engine::assets::ResourceManager>();
|
|
||||||
let res = man.request_raw(&path).unwrap();
|
|
||||||
let rep = self.asset_handles_slab.insert(res);
|
|
||||||
WasmResource::new_own(rep as _)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drop(&mut self, this: WasmResource<wasm_asset::AssetManager>) -> wasmtime::Result<()> {
|
|
||||||
self.resource_data_slab.drop_ref(this.rep() as _);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl wasm_asset::HostAssetHandle for Imports {
|
|
||||||
async fn version(&mut self, this: WasmResource<wasm_asset::AssetHandle>) -> u64 {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap();
|
|
||||||
han.version() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn uuid(&mut self, this: WasmResource<wasm_asset::AssetHandle>) -> String {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap();
|
|
||||||
han.uuid().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn path(&mut self, this: WasmResource<wasm_asset::AssetHandle>) -> Option<String> {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap();
|
|
||||||
han.path()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_watched(&mut self, this: WasmResource<wasm_asset::AssetHandle>) -> bool {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap();
|
|
||||||
han.is_watched()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_loaded(&mut self, this: WasmResource<wasm_asset::AssetHandle>) -> bool {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap();
|
|
||||||
han.is_loaded()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_load(&mut self, this: WasmResource<wasm_asset::AssetHandle>) {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap().clone();
|
|
||||||
|
|
||||||
tokio::task::spawn_blocking(move || {
|
|
||||||
han.wait_for_load();
|
|
||||||
}).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_recurse_dependencies_load(
|
|
||||||
&mut self,
|
|
||||||
this: WasmResource<wasm_asset::AssetHandle>,
|
|
||||||
) {
|
|
||||||
let han = self.asset_handles_slab.get(this.rep() as _)
|
|
||||||
.unwrap().clone();
|
|
||||||
|
|
||||||
tokio::task::spawn_blocking(move || {
|
|
||||||
han.wait_recurse_dependencies_load();
|
|
||||||
}).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drop(&mut self, this: WasmResource<wasm_asset::AssetHandle>) -> wasmtime::Result<()> {
|
|
||||||
self.asset_handles_slab.drop_ref(this.rep() as _);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl wasm_asset::HostImageHandle for Imports {
|
|
||||||
async fn from_raw_handle(&mut self, raw_handle: WasmResource<wasm_asset::AssetHandle>) -> Option<WasmResource<wasm_asset::ImageHandle>> {
|
|
||||||
let untyped = self.asset_handles_slab.get(raw_handle.rep() as _)?;
|
|
||||||
// image will be none if the asset is not an image asset
|
|
||||||
let image = untyped.as_typed::<lyra_engine::assets::Image>();
|
|
||||||
if image.is_some() {
|
|
||||||
let rep = raw_handle.rep();
|
|
||||||
self.asset_handles_slab.increment_ref(rep as _);
|
|
||||||
|
|
||||||
Some(WasmResource::new_own(rep))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn height(&mut self, this: WasmResource<wasm_asset::ImageHandle>) -> Option<u32> {
|
|
||||||
let untyped = self.asset_handles_slab.get(this.rep() as _)?;
|
|
||||||
let image = untyped.as_typed::<lyra_engine::assets::Image>()?;
|
|
||||||
let image = image.data_ref()?;
|
|
||||||
Some(image.height())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn width(&mut self, this: WasmResource<wasm_asset::ImageHandle>) -> Option<u32> {
|
|
||||||
let untyped = self.asset_handles_slab.get(this.rep() as _)?;
|
|
||||||
let image = untyped.as_typed::<lyra_engine::assets::Image>()?;
|
|
||||||
let image = image.data_ref()?;
|
|
||||||
Some(image.width())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_bytes(&mut self, this: WasmResource<wasm_asset::ImageHandle>) -> Option<Vec<u8>> {
|
|
||||||
let untyped = self.asset_handles_slab.get(this.rep() as _)?;
|
|
||||||
let image = untyped.as_typed::<lyra_engine::assets::Image>()?;
|
|
||||||
let image = image.data_ref()?;
|
|
||||||
Some(image.as_bytes().to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drop(&mut self, this: WasmResource<wasm_asset::ImageHandle>) -> wasmtime::Result<()> {
|
|
||||||
self.asset_handles_slab.drop_ref(this.rep() as _);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
294
src/main.rs
294
src/main.rs
|
@ -1,7 +1,5 @@
|
||||||
use std::alloc::Layout;
|
use std::alloc::Layout;
|
||||||
use std::any::TypeId;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
|
@ -9,29 +7,20 @@ use async_trait::async_trait;
|
||||||
use common_api::math::Vec3;
|
use common_api::math::Vec3;
|
||||||
use component::witguest::math;
|
use component::witguest::math;
|
||||||
use ecs::query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType};
|
use ecs::query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType};
|
||||||
use lyra_ecs::{query::dynamic::DynamicViewOne, World};
|
use lyra_ecs::{query::dynamic::DynamicViewOne, DynTypeId, World};
|
||||||
|
|
||||||
use lyra_ecs as ecs;
|
use lyra_ecs as ecs;
|
||||||
|
|
||||||
use lyra_engine::DeltaTime;
|
|
||||||
use lyra_reflect::{ReflectedResource, TypeRegistry};
|
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
|
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
|
||||||
|
|
||||||
use wasmtime::component::Resource as WasmResource;
|
use wasmtime::component::Resource as WasmResource;
|
||||||
|
|
||||||
mod proxy;
|
|
||||||
pub use proxy::*;
|
|
||||||
|
|
||||||
mod asset;
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use asset::*;
|
|
||||||
|
|
||||||
/* #[allow(unused_imports)]
|
|
||||||
pub(crate) mod lyra_engine {
|
pub(crate) mod lyra_engine {
|
||||||
pub use lyra_ecs as ecs;
|
pub use lyra_ecs as ecs;
|
||||||
} */
|
}
|
||||||
|
|
||||||
wasmtime::component::bindgen!({
|
wasmtime::component::bindgen!({
|
||||||
world: "example",
|
world: "example",
|
||||||
|
@ -86,87 +75,14 @@ struct DynamicViewEntry {
|
||||||
view: DynamicViewStateIter,
|
view: DynamicViewStateIter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct RefCountedData<T> {
|
|
||||||
pub borrow: u64,
|
|
||||||
pub data: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::Deref for RefCountedData<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::DerefMut for RefCountedData<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Slab of reference counted data.
|
|
||||||
pub(crate) struct SlabRcData<T>(Slab<RefCountedData<T>>);
|
|
||||||
|
|
||||||
impl<T> From<Slab<RefCountedData<T>>> for SlabRcData<T> {
|
|
||||||
fn from(value: Slab<RefCountedData<T>>) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::Deref for SlabRcData<T> {
|
|
||||||
type Target = Slab<RefCountedData<T>>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::DerefMut for SlabRcData<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> SlabRcData<T> {
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
|
||||||
Self(Slab::with_capacity(capacity))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn increment_ref(&mut self, r: usize) {
|
|
||||||
if let Some(count) = self.0.get_mut(r) {
|
|
||||||
count.borrow += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drop_ref(&mut self, r: usize) {
|
|
||||||
if let Some(count) = self.0.get_mut(r) {
|
|
||||||
// only remove the data from the slab if this is the only borrow
|
|
||||||
if count.borrow == 1 {
|
|
||||||
self.0.remove(r);
|
|
||||||
} else {
|
|
||||||
count.borrow -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert a new reference counted element into the slab.
|
|
||||||
pub fn insert(&mut self, data: T) -> usize {
|
|
||||||
self.0.insert(RefCountedData { borrow: 1, data, })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for DynamicViewEntry {}
|
unsafe impl Send for DynamicViewEntry {}
|
||||||
|
|
||||||
pub struct Imports {
|
struct Imports {
|
||||||
pub(crate) world_slab: Slab<WorldEntry>,
|
world_slab: Slab<WorldEntry>,
|
||||||
pub(crate) world_views_slab: Slab<DynamicViewEntry>,
|
world_views_slab: Slab<DynamicViewEntry>,
|
||||||
pub(crate) resource_data_slab: SlabRcData<lyra_ecs::ResourceData>,
|
|
||||||
pub(crate) asset_handles_slab: SlabRcData<lyra_engine::assets::UntypedResHandle>,
|
|
||||||
|
|
||||||
pub(crate) ctx: WasiCtx,
|
ctx: WasiCtx,
|
||||||
pub(crate) table: ResourceTable,
|
table: ResourceTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasiView for Imports {
|
impl WasiView for Imports {
|
||||||
|
@ -221,8 +137,22 @@ impl wasm_ecs::HostEcsWorld for Imports {
|
||||||
let mut bundle = ecs::DynamicBundle::new();
|
let mut bundle = ecs::DynamicBundle::new();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for info in infos {
|
for info in infos {
|
||||||
// Try to get the Native type's ComponentInfo.
|
// get the full u128 from the u64 2-tuple
|
||||||
let info = lookup.lookup_info(info.into());
|
let full_id = unsafe { std::mem::transmute::<_, u128>(info.type_id.inner) };
|
||||||
|
// Try to get the Native type's ComponentInfo. If something is found, verify that
|
||||||
|
// the size and alignment match.
|
||||||
|
let info = match lookup.0.get(&full_id) {
|
||||||
|
Some(native) => {
|
||||||
|
let native_align = native.layout().align() as u64;
|
||||||
|
let native_size = native.layout().size() as u64;
|
||||||
|
|
||||||
|
assert_eq!(native_align, info.alignment, "Native type alignment is different then scripting type alignment!");
|
||||||
|
assert_eq!(native_size, info.size, "Native type size is different then scripting type size!");
|
||||||
|
|
||||||
|
native.clone()
|
||||||
|
},
|
||||||
|
None => info.into()
|
||||||
|
};
|
||||||
|
|
||||||
// SAFETY: The components are tightly packed. Adding the offset to the pointer will
|
// SAFETY: The components are tightly packed. Adding the offset to the pointer will
|
||||||
// get the next component in the buffer.
|
// get the next component in the buffer.
|
||||||
|
@ -325,43 +255,6 @@ impl wasm_ecs::HostEcsWorld for Imports {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_resource(&mut self, this: wasmtime::component::Resource<wasm_ecs::EcsWorld>, type_id: wasm_ecs::WasmTypeId) -> wasm_ecs::WorldResourceResult {
|
|
||||||
let world_entry = self
|
|
||||||
.world_slab
|
|
||||||
.get(this.rep() as _)
|
|
||||||
.ok_or(WasmError::InvalidResourceHandle("EcsWorld")).unwrap();
|
|
||||||
let world = &world_entry.world;
|
|
||||||
|
|
||||||
let native_tid = {
|
|
||||||
let native_type = world.get_resource::<GuestTypeLookup>().unwrap();
|
|
||||||
native_type.lookup_type_id(type_id)
|
|
||||||
.expect("failed to find native type id of wasm type")
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = {
|
|
||||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
|
||||||
reg.get_type(native_tid).unwrap().clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let proxy = data.get_data::<WasmProxied>().unwrap().clone();
|
|
||||||
let refl = data.get_data::<ReflectedResource>().unwrap().clone();
|
|
||||||
|
|
||||||
// SAFETY: reflecting the resource borrows from a different part of the world
|
|
||||||
// then marshaling would.
|
|
||||||
let w = NonNull::from(world);
|
|
||||||
let world = unsafe { w.as_ref() };
|
|
||||||
let refl = refl.reflect(world).unwrap();
|
|
||||||
let res = proxy.marshal_to_bytes(self, this.rep(), refl.deref());
|
|
||||||
|
|
||||||
match res {
|
|
||||||
ProxyResult::None => wasm_ecs::WorldResourceResult::None,
|
|
||||||
ProxyResult::Bytes(bytes) => wasm_ecs::WorldResourceResult::Bytes(bytes),
|
|
||||||
ProxyResult::WasmResource(data) => {
|
|
||||||
let rep = self.resource_data_slab.insert(data);
|
|
||||||
wasm_ecs::WorldResourceResult::WasmResource(WasmResource::new_own(rep as _))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drop(
|
async fn drop(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -390,25 +283,11 @@ impl wasm_ecs::HostEcsDynamicView for Imports {
|
||||||
world: WasmResource<wasm_ecs::EcsWorld>,
|
world: WasmResource<wasm_ecs::EcsWorld>,
|
||||||
infos: Vec<wasm_ecs::ComponentInfo>,
|
infos: Vec<wasm_ecs::ComponentInfo>,
|
||||||
) -> wasmtime::component::Resource<wasm_ecs::EcsDynamicView> {
|
) -> wasmtime::component::Resource<wasm_ecs::EcsDynamicView> {
|
||||||
|
// Create a dynamic view for querying for the components
|
||||||
// Create the view state
|
let mut view = DynamicViewState::new();
|
||||||
let view = {
|
for info in infos.clone() {
|
||||||
let world_entry = self
|
view.push(QueryDynamicType::from_info(info.into()));
|
||||||
.world_slab
|
}
|
||||||
.get_mut(world.rep() as _)
|
|
||||||
.ok_or(WasmError::InvalidResourceHandle("EcsWorld"))
|
|
||||||
.unwrap();
|
|
||||||
let lookup = world_entry.world.get_resource::<GuestTypeLookup>().unwrap();
|
|
||||||
|
|
||||||
// Create a dynamic view for querying for the components
|
|
||||||
let mut view = DynamicViewState::new();
|
|
||||||
for info in infos.clone() {
|
|
||||||
let info = lookup.lookup_info(info.into());
|
|
||||||
view.push(QueryDynamicType::from_info(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
view
|
|
||||||
};
|
|
||||||
|
|
||||||
let world_rep = world.rep();
|
let world_rep = world.rep();
|
||||||
let entry = DynamicViewEntry {
|
let entry = DynamicViewEntry {
|
||||||
|
@ -483,52 +362,41 @@ impl wasm_ecs::HostEcsDynamicView for Imports {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
/* fn dynamic_view_next_impl() -> Option<(Entity, Vec<u8>)> {
|
||||||
struct GuestTypeLookup {
|
// get the next row in the view and copy the returned components into a tightly packed
|
||||||
infos: HashMap<u128, lyra_ecs::ComponentInfo>,
|
if let Some((en, row)) = view.next(&world_entry.world) {
|
||||||
type_ids: HashMap<u128, TypeId>,
|
let row_len = row.iter().map(|d| d.info.layout().size()).sum();
|
||||||
}
|
let mut row_buf = Vec::<u8>::with_capacity(row_len);
|
||||||
|
|
||||||
impl GuestTypeLookup {
|
let mut offset = 0;
|
||||||
/// Try to find the native type's component info.
|
for comp in row {
|
||||||
///
|
let comp_size = comp.info.layout().size();
|
||||||
/// If the wasm type id does not correspond to a native type's info, the provided `ComponentInfo`
|
unsafe {
|
||||||
/// will be returned. If info is found, the size and alignment will be verified to match using
|
// SAFETY: the vec has the capacity to store this component because it was
|
||||||
/// asserts and the info of the native type will be returned.
|
// created with Vec::with_capacity.
|
||||||
fn lookup_info(&self, info: lyra_ecs::ComponentInfo) -> lyra_ecs::ComponentInfo {
|
let dst = row_buf.as_mut_ptr().add(offset);
|
||||||
match self.infos.get(&info.type_id().as_unknown().unwrap()) {
|
|
||||||
Some(native) => {
|
|
||||||
let native_align = native.layout().align() as usize;
|
|
||||||
let native_size = native.layout().size() as usize;
|
|
||||||
|
|
||||||
let layout = info.layout();
|
std::ptr::copy_nonoverlapping(
|
||||||
assert_eq!(native_align, layout.align(), "Native type alignment is different then scripting type alignment!");
|
comp.ptr.as_ptr(),
|
||||||
assert_eq!(native_size, layout.size(), "Native type size is different then scripting type size!");
|
dst,
|
||||||
|
comp.info.layout().size(),
|
||||||
|
);
|
||||||
|
|
||||||
native.clone()
|
// make sure to tell the vec that it now has the bytes of this component
|
||||||
},
|
row_buf.set_len(row_buf.len() + comp_size);
|
||||||
None => info
|
}
|
||||||
|
|
||||||
|
offset += comp_size;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_type_id(&self, wasm_id: wasm_ecs::WasmTypeId) -> Option<TypeId> {
|
Some((en.into(), row_buf))
|
||||||
// SAFETY: a (u64, u64) is the same as a u128
|
} else {
|
||||||
let id: u128 = unsafe { std::mem::transmute(wasm_id) };
|
None
|
||||||
self.type_ids.get(&id).cloned()
|
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
#[async_trait]
|
#[derive(Default, Clone)]
|
||||||
impl wasm_ecs::HostEcsResource for Imports {
|
struct GuestTypeLookup(HashMap<u128, lyra_ecs::ComponentInfo>);
|
||||||
async fn drop(
|
|
||||||
&mut self,
|
|
||||||
this: wasmtime::component::Resource<wasm_ecs::EcsResource>,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
self.resource_data_slab.drop_ref(this.rep() as _);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl wasm_ecs::Host for Imports {}
|
impl wasm_ecs::Host for Imports {}
|
||||||
|
@ -544,8 +412,12 @@ async fn main() -> wasmtime::Result<()> {
|
||||||
// Configure the linker
|
// Configure the linker
|
||||||
let mut linker = wasmtime::component::Linker::new(&engine);
|
let mut linker = wasmtime::component::Linker::new(&engine);
|
||||||
|
|
||||||
|
//wasmtime_wasi::preview0::add_to_linker_sync(&mut linker, |s| s)?;
|
||||||
|
//wasmtime_wasi::bindings::Imports::add_to_linker(&mut linker, |s| s)?;
|
||||||
wasmtime_wasi::add_to_linker_async(&mut linker)?;
|
wasmtime_wasi::add_to_linker_async(&mut linker)?;
|
||||||
Example::add_to_linker(&mut linker, |s| s)?;
|
Example::add_to_linker(&mut linker, |s| s)?;
|
||||||
|
//lyra::api::ecs::add_to_linker(&mut linker, |s| s)?;
|
||||||
|
//Api::add_to_linker(&mut linker, |s| s)?;
|
||||||
|
|
||||||
let mut builder = WasiCtxBuilder::new();
|
let mut builder = WasiCtxBuilder::new();
|
||||||
builder.inherit_stdio();
|
builder.inherit_stdio();
|
||||||
|
@ -557,8 +429,6 @@ async fn main() -> wasmtime::Result<()> {
|
||||||
// wants another world for some reason.
|
// wants another world for some reason.
|
||||||
world_slab: Slab::with_capacity(1),
|
world_slab: Slab::with_capacity(1),
|
||||||
world_views_slab: Slab::with_capacity(10),
|
world_views_slab: Slab::with_capacity(10),
|
||||||
resource_data_slab: SlabRcData::with_capacity(25),
|
|
||||||
asset_handles_slab: SlabRcData::with_capacity(25),
|
|
||||||
ctx: builder.build(),
|
ctx: builder.build(),
|
||||||
table: ResourceTable::new(),
|
table: ResourceTable::new(),
|
||||||
},
|
},
|
||||||
|
@ -569,24 +439,13 @@ async fn main() -> wasmtime::Result<()> {
|
||||||
let bytes = std::fs::read("guests/csharp/dotnet-guest-test/bin/Debug/net9.0/wasi-wasm/native/dotnet-guest-test.wasm")?;
|
let bytes = std::fs::read("guests/csharp/dotnet-guest-test/bin/Debug/net9.0/wasi-wasm/native/dotnet-guest-test.wasm")?;
|
||||||
let component = wasmtime::component::Component::new(&engine, bytes)?;
|
let component = wasmtime::component::Component::new(&engine, bytes)?;
|
||||||
|
|
||||||
let (script_en, (world_res_a, _)) = {
|
let (script_en, (world_res_a, world_res_b)) = {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
world.add_resource(DeltaTime::from(100.00));
|
|
||||||
world.add_resource(lyra_engine::assets::ResourceManager::default());
|
|
||||||
|
|
||||||
let mut lookup = GuestTypeLookup::default();
|
let mut lookup = GuestTypeLookup::default();
|
||||||
lookup.infos.insert(4124409524, lyra_ecs::ComponentInfo::new::<Vec3>());
|
lookup.0.insert(4124409524, lyra_ecs::ComponentInfo::new::<Vec3>());
|
||||||
lookup.type_ids.insert(83716348954, TypeId::of::<lyra_engine::DeltaTime>());
|
|
||||||
lookup.type_ids.insert(567234789345, TypeId::of::<lyra_engine::assets::ResourceManager>());
|
|
||||||
world.add_resource(lookup);
|
world.add_resource(lookup);
|
||||||
|
|
||||||
{
|
|
||||||
let mut reg = world.get_resource_or_default::<TypeRegistry>();
|
|
||||||
add_proxy_type_data::<DeltaTime>(&mut reg);
|
|
||||||
add_proxy_type_data::<lyra_engine::assets::ResourceManager>(&mut reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
let script_en = world.spawn(());
|
let script_en = world.spawn(());
|
||||||
|
|
||||||
let data = store.data_mut();
|
let data = store.data_mut();
|
||||||
|
@ -608,6 +467,35 @@ async fn main() -> wasmtime::Result<()> {
|
||||||
//call_script_stage_async(&mut store, &instance, "update", world_res_b, script_en.into()).await?;
|
//call_script_stage_async(&mut store, &instance, "update", world_res_b, script_en.into()).await?;
|
||||||
println!("RUST: Guest is done");
|
println!("RUST: Guest is done");
|
||||||
|
|
||||||
|
let rep = world_res_b.rep();
|
||||||
|
let w = store.data().world_slab.get(rep as _).unwrap();
|
||||||
|
let w = &w.world;
|
||||||
|
println!("RUST: Got {} archetypes", w.archetype_count());
|
||||||
|
for a in w.archetypes.values() {
|
||||||
|
println!("RUST: Archetype {}", a.id().0);
|
||||||
|
for col in &a.columns {
|
||||||
|
println!("RUST: Column type id: {:?}", col.info.type_id());
|
||||||
|
|
||||||
|
if col.info.type_id().is_id(DynTypeId::Unknown(4124409524)) {
|
||||||
|
println!("RUST: Found C# Vec3");
|
||||||
|
let pos = unsafe { col.get::<Vec3>(0) };
|
||||||
|
println!("RUST: Entity 0 pos: {:?}", *pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dv = w.dynamic_view();
|
||||||
|
dv.push(QueryDynamicType::from_info(lyra_ecs::ComponentInfo::new::<Vec3>()));
|
||||||
|
let iter = dv.into_iter();
|
||||||
|
|
||||||
|
for (_, comps) in iter {
|
||||||
|
let first = comps.first().unwrap();
|
||||||
|
unsafe {
|
||||||
|
let v: Vec3 = std::ptr::read(first.ptr.as_ptr() as _);
|
||||||
|
println!("RUST: Found native Vec3! {:?}", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
64
src/proxy.rs
64
src/proxy.rs
|
@ -1,64 +0,0 @@
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
use crate::Imports;
|
|
||||||
use common_api::bytemuck;
|
|
||||||
use lyra_engine::DeltaTime;
|
|
||||||
use lyra_reflect::{FromType, Reflect, ReflectedResource, TypeRegistry};
|
|
||||||
|
|
||||||
pub enum ProxyResult {
|
|
||||||
None,
|
|
||||||
WasmResource(lyra_ecs::ResourceData),
|
|
||||||
Bytes(Vec<u8>)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait WasmProxy {
|
|
||||||
fn marshal_to_bytes(&self, imports: &mut Imports, world_idx: u32) -> ProxyResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct WasmProxied {
|
|
||||||
fn_marshal: for<'a> fn (&mut Imports, world_idx: u32, &'a dyn Reflect) -> ProxyResult,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasmProxied {
|
|
||||||
pub fn marshal_to_bytes(&self, imports: &mut Imports, world_idx: u32, reflected: &dyn Reflect) -> ProxyResult {
|
|
||||||
(self.fn_marshal)(imports, world_idx, reflected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromType<T> for WasmProxied
|
|
||||||
where
|
|
||||||
T: WasmProxy + 'static
|
|
||||||
{
|
|
||||||
fn from_type() -> Self {
|
|
||||||
WasmProxied {
|
|
||||||
fn_marshal: |imports, world_idx: u32, reflect| {
|
|
||||||
let this: &T = reflect.as_any().downcast_ref().unwrap();
|
|
||||||
this.marshal_to_bytes(imports, world_idx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_proxy_type_data<T: WasmProxy + lyra_ecs::ResourceObject + lyra_reflect::Reflect>(reg: &mut TypeRegistry) {
|
|
||||||
let ty = reg.get_type_or_default(TypeId::of::<T>());
|
|
||||||
ty.add_data(<ReflectedResource as FromType::<T>>::from_type());
|
|
||||||
ty.add_data(<WasmProxied as FromType::<T>>::from_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasmProxy for DeltaTime {
|
|
||||||
fn marshal_to_bytes(&self, _: &mut Imports, _: u32) -> ProxyResult {
|
|
||||||
ProxyResult::Bytes(bytemuck::bytes_of(&**self).to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasmProxy for lyra_engine::assets::ResourceManager {
|
|
||||||
fn marshal_to_bytes(&self, imports: &mut Imports, world_idx: u32) -> ProxyResult {
|
|
||||||
let world = &imports.world_slab.get(world_idx as _)
|
|
||||||
.unwrap().world;
|
|
||||||
match world.get_resource_data::<Self>() {
|
|
||||||
Some(data) => ProxyResult::WasmResource(data),
|
|
||||||
None => ProxyResult::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
Loading…
Reference in New Issue