Expand LyraApi package to make it easier to spawn and view entities
This commit is contained in:
parent
fcf4f54e34
commit
dcb48e9acf
|
@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
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
|
||||
Global
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
bin
|
||||
obj
|
|
@ -1,15 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<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>
|
|
@ -1,9 +0,0 @@
|
|||
<?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>
|
|
@ -1,81 +0,0 @@
|
|||
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>));
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package lyra:api;
|
||||
|
||||
world imports {
|
||||
import ecs;
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
namespace ExampleWorld;
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
//using ExampleWorld.wit.imports.lyra.api;
|
||||
using ExampleWorld.wit.imports.lyra.api;
|
||||
using LyraApi;
|
||||
using LyraApi.Ecs;
|
||||
using ImportsWorld.wit.imports.lyra.api;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct Vec3(float x, float y, float z) : IComponent
|
||||
|
@ -23,81 +23,29 @@ struct Vec3(float x, float y, float z) : IComponent
|
|||
/// </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 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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
Entity entity = world.Spawn(pos);
|
||||
Console.WriteLine("C#: Spawned {0}", entity.Id);
|
||||
Entity entity = world.Spawn(new Vec3(7.0f, 30.0f, 18.0f));
|
||||
Console.WriteLine("C#: Spawned entity with id {0}", entity.Id);
|
||||
|
||||
IEcs.EcsDynamicView res = gameWorld.View(infos);
|
||||
(IEcs.Entity, byte[])? row = res.Next();
|
||||
|
||||
if (row.HasValue) {
|
||||
Console.WriteLine("C#: Found position!!!");
|
||||
|
||||
var en = row.Value.Item1;
|
||||
var comp = FromBytes<Vec3>(row.Value.Item2);
|
||||
|
||||
Console.WriteLine("C#: Found component at ({0}, {1}, {2})", comp?.X, comp?.Y, comp?.Z);
|
||||
} else {
|
||||
Console.WriteLine("C#: Could not find position");
|
||||
foreach ((Entity en, Vec3 comp) in world.View<Vec3>())
|
||||
{
|
||||
Console.WriteLine("C#: Found entity at ({0}, {1}, {2})", comp.X, comp.Y, comp.Z);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
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,10 +1,11 @@
|
|||
namespace LyraApi.Ecs;
|
||||
using ImportsWorld.wit.imports.lyra.api;
|
||||
using ExampleWorld.wit.imports.lyra.api;
|
||||
|
||||
public class Entity(IEcs.Entity entity) {
|
||||
public class Entity(IEcs.Entity entity)
|
||||
{
|
||||
internal IEcs.Entity Inner { get; set; } = entity;
|
||||
|
||||
public ulong Id { get => Inner.id.id; }
|
||||
public ulong Generation { get => Inner.generation; }
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
namespace LyraApi.Ecs;
|
||||
|
||||
using ExampleWorld.wit.imports.lyra.api;
|
||||
using LyraApi;
|
||||
|
||||
public class World(IEcs.EcsWorld world)
|
||||
{
|
||||
private IEcs.EcsWorld Inner { get; set; } = 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>();
|
||||
}
|
||||
}
|
|
@ -3,11 +3,14 @@ namespace LyraApi;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static class Marshalling {
|
||||
public static class MarshalUtils
|
||||
{
|
||||
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 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
|
||||
|
@ -15,7 +18,8 @@ public static class Marshalling {
|
|||
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);
|
||||
byte[] arr = new byte[size];
|
||||
|
||||
|
@ -33,13 +37,17 @@ public static class Marshalling {
|
|||
return arr;
|
||||
}
|
||||
|
||||
public static T? FromBytes<T>(byte[] data) where T : struct
|
||||
public static T? FromBytes<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(byte[] data)
|
||||
{
|
||||
var ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
try {
|
||||
var res = (T?) Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T));
|
||||
try
|
||||
{
|
||||
//var res = (T?)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T));
|
||||
var res = Marshal.PtrToStructure<T>(ptr.AddrOfPinnedObject());
|
||||
return res;
|
||||
} finally {
|
||||
}
|
||||
finally
|
||||
{
|
||||
ptr.Free();
|
||||
|
||||
}
|
|
@ -12,48 +12,47 @@
|
|||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<SelfContained>true</SelfContained>
|
||||
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
|
||||
|
||||
<!-- Ignore warning of multiple `PackageReference` items for the `ILCompiler.LLVM` package. -->
|
||||
<NoWarn>$(NoWarn);NU1504</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Copy WIT files from LyraApi to lyra-api wit folder. -->
|
||||
<Target Name="CopyFolderOnBuild" BeforeTargets="WitCompile_InvokeTool">
|
||||
<!-- <Target Name="CopyFolderOnBuild" BeforeTargets="WitCompile_InvokeTool">
|
||||
<ItemGroup>
|
||||
<MyFiles Include="..\LyraApi\wit\**\*.wit" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(MyFiles)" DestinationFolder="wit\deps\lyraapi\%(RecursiveDir)" />
|
||||
</Target>
|
||||
</Target> -->
|
||||
|
||||
<!-- Remove bindgen of LyraApi WIT .-->
|
||||
<Target Name="RemoveBindgenLyraApi" AfterTargets="WitCompile_InvokeTool">
|
||||
<!-- <Target Name="RemoveBindgenLyraApi" AfterTargets="WitCompile_InvokeTool">
|
||||
<ItemGroup>
|
||||
<FilesToDelete Include="obj\Debug\net*\wasi-wasm\wit_bindgen\*.lyra.api.*.cs"/>
|
||||
</ItemGroup>
|
||||
<Delete Files="@(FilesToDelete)" />
|
||||
</Target>
|
||||
</Target> -->
|
||||
|
||||
<!-- 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)' == 'Windows_NT'" Command="powershell -Command "Get-ChildItem -Path 'obj\Debug' -Recurse -Filter '*.cs' ^
|
||||
| Where-Object { $_.FullName -match 'wit_bindgen' } ^
|
||||
| ForEach-Object { (Get-Content -Path $_.FullName) -replace 'ExampleWorld\.wit\.imports\.lyra\.api', 'ImportsWorld.wit.imports.lyra.api' ^
|
||||
| Set-Content -Path $_.FullName }"" />
|
||||
</Target>
|
||||
</Target> -->
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BytecodeAlliance.Componentize.DotNet.Wasm.SDK" Version="0.4.0-preview00007" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Wit Remove="**\*.wit" />
|
||||
<!-- <Wit Include="wit/deps/lyraapi" World="imports" /> -->
|
||||
<Wit Include="wit" World="example" />
|
||||
</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" />
|
||||
<Wit Remove="**\*.wit" />
|
||||
<Wit Include="wit" World="example" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- <ItemGroup>
|
||||
<ProjectReference Include="..\LyraApi\LyraApi.csproj" />
|
||||
</ItemGroup> -->
|
||||
</Project>
|
||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -137,22 +137,8 @@ impl wasm_ecs::HostEcsWorld for Imports {
|
|||
let mut bundle = ecs::DynamicBundle::new();
|
||||
let mut offset = 0;
|
||||
for info in infos {
|
||||
// get the full u128 from the u64 2-tuple
|
||||
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()
|
||||
};
|
||||
// Try to get the Native type's ComponentInfo.
|
||||
let info = lookup.lookup_info(info.into());
|
||||
|
||||
// SAFETY: The components are tightly packed. Adding the offset to the pointer will
|
||||
// get the next component in the buffer.
|
||||
|
@ -283,11 +269,25 @@ impl wasm_ecs::HostEcsDynamicView for Imports {
|
|||
world: WasmResource<wasm_ecs::EcsWorld>,
|
||||
infos: Vec<wasm_ecs::ComponentInfo>,
|
||||
) -> wasmtime::component::Resource<wasm_ecs::EcsDynamicView> {
|
||||
// Create a dynamic view for querying for the components
|
||||
let mut view = DynamicViewState::new();
|
||||
for info in infos.clone() {
|
||||
view.push(QueryDynamicType::from_info(info.into()));
|
||||
}
|
||||
|
||||
// Create the view state
|
||||
let view = {
|
||||
let world_entry = self
|
||||
.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 entry = DynamicViewEntry {
|
||||
|
@ -362,42 +362,32 @@ impl wasm_ecs::HostEcsDynamicView for Imports {
|
|||
}
|
||||
}
|
||||
|
||||
/* fn dynamic_view_next_impl() -> Option<(Entity, Vec<u8>)> {
|
||||
// get the next row in the view and copy the returned components into a tightly packed
|
||||
if let Some((en, row)) = view.next(&world_entry.world) {
|
||||
let row_len = row.iter().map(|d| d.info.layout().size()).sum();
|
||||
let mut row_buf = Vec::<u8>::with_capacity(row_len);
|
||||
|
||||
let mut offset = 0;
|
||||
for comp in row {
|
||||
let comp_size = comp.info.layout().size();
|
||||
unsafe {
|
||||
// SAFETY: the vec has the capacity to store this component because it was
|
||||
// created with Vec::with_capacity.
|
||||
let dst = row_buf.as_mut_ptr().add(offset);
|
||||
|
||||
std::ptr::copy_nonoverlapping(
|
||||
comp.ptr.as_ptr(),
|
||||
dst,
|
||||
comp.info.layout().size(),
|
||||
);
|
||||
|
||||
// make sure to tell the vec that it now has the bytes of this component
|
||||
row_buf.set_len(row_buf.len() + comp_size);
|
||||
}
|
||||
|
||||
offset += comp_size;
|
||||
}
|
||||
|
||||
Some((en.into(), row_buf))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} */
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct GuestTypeLookup(HashMap<u128, lyra_ecs::ComponentInfo>);
|
||||
|
||||
impl GuestTypeLookup {
|
||||
/// Try to find the native type's component info.
|
||||
///
|
||||
/// If the wasm type id does not correspond to a native type's info, the provided `ComponentInfo`
|
||||
/// will be returned. If info is found, the size and alignment will be verified to match using
|
||||
/// asserts and the info of the native type will be returned.
|
||||
fn lookup_info(&self, info: lyra_ecs::ComponentInfo) -> lyra_ecs::ComponentInfo {
|
||||
match self.0.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();
|
||||
assert_eq!(native_align, layout.align(), "Native type alignment is different then scripting type alignment!");
|
||||
assert_eq!(native_size, layout.size(), "Native type size is different then scripting type size!");
|
||||
|
||||
native.clone()
|
||||
},
|
||||
None => info
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl wasm_ecs::Host for Imports {}
|
||||
|
||||
|
|
Loading…
Reference in New Issue