diff --git a/core/Address.cs b/core/Address.cs
index eed8da7..5cfd3c0 100644
--- a/core/Address.cs
+++ b/core/Address.cs
@@ -6,7 +6,9 @@ namespace Data
{
public struct Address : IEquatable
{
- public FixedString value;
+ private FixedString value;
+
+ public readonly byte Length => value.Length;
public Address(FixedString value)
{
@@ -126,6 +128,36 @@ public readonly bool EndsWith(USpan other)
return true;
}
+ public readonly uint IndexOf(char character)
+ {
+ return value.IndexOf(character);
+ }
+
+ public readonly uint LastIndexOf(char character)
+ {
+ return value.LastIndexOf(character);
+ }
+
+ public readonly bool TryIndexOf(char character, out uint index)
+ {
+ return value.TryIndexOf(character, out index);
+ }
+
+ public readonly bool TryLastIndexOf(char character, out uint index)
+ {
+ return value.TryLastIndexOf(character, out index);
+ }
+
+ public readonly Address Slice(uint start, uint length)
+ {
+ return new(value.Slice(start, length));
+ }
+
+ public readonly Address Slice(uint start)
+ {
+ return new(value.Slice(start));
+ }
+
public readonly bool Matches(string other)
{
USpan buffer = stackalloc char[other.Length];
@@ -188,9 +220,9 @@ public static implicit operator Address(string value)
return new(value);
}
- public static Address Get() where T : unmanaged, IDataReference
+ public static implicit operator FixedString(Address address)
{
- return default(T).Value;
+ return address.value;
}
}
}
\ No newline at end of file
diff --git a/core/Components/IsData.cs b/core/Components/IsData.cs
deleted file mode 100644
index 4e16a53..0000000
--- a/core/Components/IsData.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Worlds;
-
-namespace Data.Components
-{
- [Component]
- public struct IsData
- {
- public uint version;
-
- public IsData(uint version)
- {
- this.version = version;
- }
- }
-}
\ No newline at end of file
diff --git a/core/Components/IsDataRequest.cs b/core/Components/IsDataRequest.cs
index 6368870..767e163 100644
--- a/core/Components/IsDataRequest.cs
+++ b/core/Components/IsDataRequest.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Unmanaged;
using Worlds;
@@ -7,31 +8,41 @@ namespace Data.Components
[Component]
public struct IsDataRequest
{
- public Address address;
- public uint version;
+ public readonly Address address;
+ public RequestStatus status;
+ public TimeSpan timeout;
- public IsDataRequest(USpan address)
+ public IsDataRequest(USpan address, RequestStatus status, TimeSpan timeout)
{
- version = default;
this.address = new(address);
+ this.status = status;
+ this.timeout = timeout;
}
- public IsDataRequest(Address address)
+ public IsDataRequest(Address address, RequestStatus status, TimeSpan timeout)
{
- version = default;
this.address = address;
+ this.status = status;
+ this.timeout = timeout;
}
- public IsDataRequest(string address)
+ public IsDataRequest(string address, RequestStatus status, TimeSpan timeout)
{
- version = default;
this.address = new(address);
+ this.status = status;
+ this.timeout = timeout;
}
- public IsDataRequest(IEnumerable address)
+ public IsDataRequest(IEnumerable address, RequestStatus status, TimeSpan timeout)
{
- version = default;
this.address = new(address);
+ this.status = status;
+ this.timeout = timeout;
+ }
+
+ public readonly IsDataRequest BecomeLoaded()
+ {
+ return new(address, RequestStatus.Loaded, timeout);
}
}
}
\ No newline at end of file
diff --git a/core/Components/Name.cs b/core/Components/Name.cs
deleted file mode 100644
index 15c6a7e..0000000
--- a/core/Components/Name.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Unmanaged;
-using Worlds;
-
-namespace Data.Components
-{
- [Component]
- public struct Name
- {
- public FixedString value;
-
- public Name(FixedString value)
- {
- this.value = value;
- }
-
- public Name(USpan value)
- {
- this.value = new FixedString(value);
- }
- }
-}
\ No newline at end of file
diff --git a/core/DataRequest.cs b/core/DataRequest.cs
index ebb5a33..047477b 100644
--- a/core/DataRequest.cs
+++ b/core/DataRequest.cs
@@ -2,8 +2,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Threading.Tasks;
-using System.Threading;
using Unmanaged;
using Worlds;
@@ -12,93 +10,49 @@ namespace Data
///
/// An entity that will contain data loaded from its address.
///
- public readonly struct DataRequest : IEntity
+ public readonly partial struct DataRequest : IDataRequest
{
- private readonly Entity entity;
+ public readonly Address Address => GetComponent().address;
+ public readonly bool IsLoaded => GetComponent().status == RequestStatus.Loaded;
- ///
- /// Address that this data request is looking for.
- ///
- public readonly Address Address
+ public readonly USpan Bytes
{
get
{
- ref IsDataRequest component = ref entity.GetComponent();
- return component.address;
- }
- }
-
- public readonly USpan Data
- {
- get
- {
- ThrowIfDataNotAvailable();
- return entity.GetArray().As();
- }
- }
+ ThrowIfNotLoaded();
- ///
- /// Checks if the data has been loaded.
- ///
- public readonly bool IsLoaded
- {
- get
- {
- if (entity.TryGetComponent(out IsData data))
- {
- return data.version == entity.GetComponent().version;
- }
- else
- {
- return false;
- }
+ return GetArray().As();
}
}
- readonly uint IEntity.Value => entity.value;
- readonly World IEntity.World => entity.world;
-
readonly void IEntity.Describe(ref Archetype archetype)
{
archetype.AddComponentType();
+ archetype.AddArrayType();
}
-#if NET
- [Obsolete("Default constructor not supported", true)]
- public DataRequest()
- {
- throw new NotSupportedException();
- }
-#endif
-
- public DataRequest(World world, uint existingEntity)
- {
- this.entity = new(world, existingEntity);
- }
-
- public DataRequest(World world, USpan address)
- {
- entity = new Entity(world, new IsDataRequest(address));
- }
-
- public DataRequest(World world, Address address)
+ public DataRequest(World world, USpan address, TimeSpan timeout = default)
{
- entity = new Entity(world, new IsDataRequest(address));
+ this.world = world;
+ value = world.CreateEntity(new IsDataRequest(address, RequestStatus.Submitted, timeout));
}
- public DataRequest(World world, string address)
+ public DataRequest(World world, Address address, TimeSpan timeout = default)
{
- entity = new Entity(world, new IsDataRequest(address));
+ this.world = world;
+ value = world.CreateEntity(new IsDataRequest(address, RequestStatus.Submitted, timeout));
}
- public DataRequest(World world, IEnumerable address)
+ public DataRequest(World world, string address, TimeSpan timeout = default)
{
- entity = new Entity(world, new IsDataRequest(address));
+ this.world = world;
+ value = world.CreateEntity(new IsDataRequest(address, RequestStatus.Submitted, timeout));
}
- public readonly void Dispose()
+ public DataRequest(World world, IEnumerable address, TimeSpan timeout = default)
{
- entity.Dispose();
+ this.world = world;
+ value = world.CreateEntity(new IsDataRequest(address, RequestStatus.Submitted, timeout));
}
public readonly override string ToString()
@@ -110,7 +64,7 @@ public readonly bool TryGetData(out USpan data)
{
if (IsLoaded)
{
- data = entity.GetArray().As();
+ data = Bytes;
return true;
}
else
@@ -120,46 +74,20 @@ public readonly bool TryGetData(out USpan data)
}
}
- public readonly void SetAddress(Address address)
- {
- ref IsDataRequest component = ref entity.GetComponent();
- component.address = address;
- component.version++;
- }
-
- ///
- /// Reads the data as a UTF8 string.
- ///
- /// Amount of char values copied.
- public readonly uint CopyDataAsUTF8To(USpan buffer)
+ public readonly BinaryReader CreateBinaryReader()
{
- ThrowIfDataNotAvailable();
+ ThrowIfNotLoaded();
- using BinaryReader reader = new(Data);
- return reader.ReadUTF8Span(buffer);
- }
-
- public async Task UntilLoaded(Update action, CancellationToken cancellation = default)
- {
- World world = entity.GetWorld();
- while (!IsLoaded)
- {
- await action(world, cancellation);
- }
+ return new(Bytes);
}
[Conditional("DEBUG")]
- public void ThrowIfDataNotAvailable()
+ private readonly void ThrowIfNotLoaded()
{
if (!IsLoaded)
{
- throw new InvalidOperationException($"Data not yet available on `{entity.GetEntityValue()}`");
+ throw new InvalidOperationException($"Data entity `{value}` at address `{Address}` has not been loaded");
}
}
-
- public static implicit operator Entity(DataRequest request)
- {
- return request.entity;
- }
}
-}
+}
\ No newline at end of file
diff --git a/core/DataSource.cs b/core/DataSource.cs
index c0435e2..ddf272a 100644
--- a/core/DataSource.cs
+++ b/core/DataSource.cs
@@ -1,5 +1,4 @@
using Data.Components;
-using System;
using Unmanaged;
using Worlds;
@@ -9,38 +8,25 @@ namespace Data
/// Represents a span of that can be found with
/// a .
///
- public readonly struct DataSource : IDataSource
+ public readonly partial struct DataSource : IDataSource
{
- private readonly Entity entity;
-
- public readonly USpan Bytes => entity.GetArray().As();
- public readonly Address Address => entity.GetComponent().address;
-
- readonly uint IEntity.Value => entity.value;
- readonly World IEntity.World => entity.world;
+ public readonly Address Address => GetComponent().address;
+ public readonly USpan Bytes => GetArray().As();
readonly void IEntity.Describe(ref Archetype archetype)
{
archetype.AddComponentType();
- archetype.AddArrayElementType();
+ archetype.AddArrayType();
}
-#if NET
- [Obsolete("Default constructor not supported.", true)]
- public DataSource()
- {
- throw new NotSupportedException();
- }
-#endif
-
///
/// Creates an empty data source.
///
public DataSource(World world, Address address)
{
- entity = new(world);
- entity.AddComponent(new IsDataSource(address));
- entity.CreateArray();
+ this.world = world;
+ value = world.CreateEntity(new IsDataSource(address));
+ world.CreateArray(value);
}
///
@@ -48,9 +34,9 @@ public DataSource(World world, Address address)
///
public DataSource(World world, Address address, USpan bytes)
{
- entity = new(world);
- entity.AddComponent(new IsDataSource(address));
- entity.CreateArray(bytes.As());
+ this.world = world;
+ value = world.CreateEntity(new IsDataSource(address));
+ world.CreateArray(value, bytes.As());
}
///
@@ -58,10 +44,10 @@ public DataSource(World world, Address address, USpan bytes)
///
public DataSource(World world, Address address, USpan text)
{
- entity = new(world);
- entity.AddComponent(new IsDataSource(address));
- entity.CreateArray();
- Write(text);
+ this.world = world;
+ value = world.CreateEntity(new IsDataSource(address));
+ world.CreateArray(value);
+ WriteUTF8(text);
}
///
@@ -69,15 +55,10 @@ public DataSource(World world, Address address, USpan text)
///
public DataSource(World world, Address address, string text)
{
- entity = new(world);
- entity.AddComponent(new IsDataSource(address));
- entity.CreateArray();
- Write(text);
- }
-
- public readonly void Dispose()
- {
- entity.Dispose();
+ this.world = world;
+ value = world.CreateEntity(new IsDataSource(address));
+ world.CreateArray(value);
+ WriteUTF8(text);
}
public readonly override string ToString()
@@ -89,43 +70,37 @@ public readonly override string ToString()
public readonly uint ToString(USpan buffer)
{
- Address name = Address;
- return name.ToString(buffer);
- }
-
- public readonly void Clear()
- {
- entity.ResizeArray(0);
+ return Address.ToString(buffer);
}
///
/// Appends the given text as UTF8 formatted bytes.
///
- public readonly void Write(USpan text)
+ public readonly void WriteUTF8(USpan text)
{
using BinaryWriter writer = new(4);
- writer.WriteUTF8Text(text);
- Write(writer.GetBytes());
+ writer.WriteUTF8(text);
+ Write(writer.AsSpan());
}
///
/// Appends the given text as UTF8 formatted bytes.
///
- public readonly void Write(FixedString text)
+ public readonly void WriteUTF8(FixedString text)
{
using BinaryWriter writer = new(4);
- writer.WriteUTF8Text(text);
- Write(writer.GetBytes());
+ writer.WriteUTF8(text);
+ Write(writer.AsSpan());
}
///
/// Appends the given text as UTF8 formatted bytes.
///
- public readonly void Write(string text)
+ public readonly void WriteUTF8(string text)
{
using BinaryWriter writer = new(4);
- writer.WriteUTF8Text(text);
- Write(writer.GetBytes());
+ writer.WriteUTF8(text);
+ Write(writer.AsSpan());
}
///
@@ -133,9 +108,14 @@ public readonly void Write(string text)
///
public readonly void Write(USpan bytes)
{
- USpan array = entity.GetArray();
- array = entity.ResizeArray(bytes.Length + array.Length);
+ uint length = GetArrayLength();
+ USpan array = ResizeArray(bytes.Length + length);
bytes.CopyTo(array.As());
}
+
+ public readonly BinaryReader CreateBinaryReader()
+ {
+ return new(Bytes);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/core/EmbeddedAddress.cs b/core/EmbeddedAddress.cs
deleted file mode 100644
index 9aa82fd..0000000
--- a/core/EmbeddedAddress.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-
-namespace Data
-{
- public readonly struct EmbeddedAddress
- {
- private static readonly List all = new();
- private static readonly HashSet addresses = new();
-
- public static IReadOnlyList All => all;
-
- public readonly Assembly assembly;
- public readonly Address address;
-
- public EmbeddedAddress(Assembly assembly, Address address)
- {
- this.assembly = assembly;
- this.address = address;
- }
-
- public static void Register() where T : unmanaged
- {
- T resources = new();
- Assembly assembly = typeof(T).Assembly;
- if (resources is IEmbeddedResources embeddedResources)
- {
- foreach (Address address in embeddedResources.Addresses)
- {
- Register(assembly, address);
- }
- }
- else if (resources is IDataReference dataReference)
- {
- Register(assembly, dataReference.Value);
- }
- else
- {
- throw new NotImplementedException($"Handling of `{typeof(T).Name}` as an embedded resource is not implemented");
- }
- }
-
- public static void Register(Assembly assembly, string address)
- {
- Register(assembly, new Address(address));
- }
-
- public static void Register(Assembly assembly, Address address)
- {
- if (addresses.Add(address))
- {
- all.Add(new EmbeddedAddress(assembly, address));
- }
- }
- }
-}
diff --git a/core/EmbeddedResource.cs b/core/EmbeddedResource.cs
new file mode 100644
index 0000000..d575deb
--- /dev/null
+++ b/core/EmbeddedResource.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Unmanaged;
+
+namespace Data
+{
+ public readonly struct EmbeddedResource
+ {
+ public readonly Address address;
+
+ private readonly GCHandle assembly;
+
+ public readonly Assembly Assembly => (Assembly)(assembly.Target ?? throw new System.InvalidOperationException("Assembly has been garbage collected, and is no longer available"));
+
+ public EmbeddedResource(Assembly assembly, Address address)
+ {
+ this.assembly = GCHandle.Alloc(assembly, GCHandleType.Weak);
+ this.address = address;
+ }
+
+ ///
+ /// Creates a new binary reader with the contents of this embedded resource.
+ ///
+ public readonly BinaryReader CreateBinaryReader()
+ {
+ Assembly assembly = Assembly;
+ string[] names = assembly.GetManifestResourceNames();
+ string resourcePath = $"{assembly.GetName().Name}.{address.ToString().Replace('/', '.')}";
+ System.IO.Stream stream = assembly.GetManifestResourceStream(resourcePath) ?? throw new Exception($"Embedded resource at `{resourcePath}` could not be found");
+ stream.Position = 0;
+ return new(stream);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/EmbeddedResourceRegistry.cs b/core/EmbeddedResourceRegistry.cs
new file mode 100644
index 0000000..f6590ff
--- /dev/null
+++ b/core/EmbeddedResourceRegistry.cs
@@ -0,0 +1,84 @@
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Data
+{
+ ///
+ /// Registry containing all known embedded resources.
+ ///
+ public static class EmbeddedResourceRegistry
+ {
+ private static readonly List all = new();
+ private static readonly List addresses = new();
+
+ public static IReadOnlyList All => all;
+
+ public static void Load() where T : unmanaged, IEmbeddedResourceBank
+ {
+ T template = default;
+ Assembly assembly = typeof(T).Assembly;
+ template.Load(new(Register));
+
+ void Register(Address address)
+ {
+ EmbeddedResourceRegistry.Register(assembly, address);
+ }
+ }
+
+ public static void Register(Assembly assembly, Address address)
+ {
+ all.Add(new EmbeddedResource(assembly, address));
+ addresses.Add(address);
+ }
+
+ private static bool TryGetMatch(Address address, out int index)
+ {
+ for (int i = 0; i < addresses.Count; i++)
+ {
+ if (addresses[i].Matches(address))
+ {
+ index = i;
+ return true;
+ }
+ }
+
+ index = -1;
+ return false;
+ }
+
+ public static bool Contains(Address address)
+ {
+ return TryGetMatch(address, out _);
+ }
+
+ public static bool TryGet(Address address, out EmbeddedResource resource)
+ {
+ if (TryGetMatch(address, out int index))
+ {
+ resource = all[index];
+ return true;
+ }
+
+ resource = default;
+ return false;
+ }
+
+ public static EmbeddedResource Get(Address address)
+ {
+ TryGetMatch(address, out int index);
+ return all[index];
+ }
+
+ public static Address Get() where T : unmanaged, IEmbeddedResource
+ {
+ T template = default;
+ Address address = template.Address;
+ if (!addresses.Contains(address))
+ {
+ Register(typeof(T).Assembly, address);
+ }
+
+ return address;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/Extensions/NameExtensions.cs b/core/Extensions/NameExtensions.cs
deleted file mode 100644
index 7a47042..0000000
--- a/core/Extensions/NameExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Data.Components;
-using Unmanaged;
-using Worlds;
-
-namespace Data
-{
- public static class NameExtensions
- {
- public static ref FixedString GetName(this T entity) where T : unmanaged, IEntity
- {
- return ref entity.AsEntity().GetComponent().value;
- }
- }
-}
diff --git a/core/Functions/Register.cs b/core/Functions/Register.cs
new file mode 100644
index 0000000..91fe68c
--- /dev/null
+++ b/core/Functions/Register.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Data.Functions
+{
+ public readonly struct Register
+ {
+ private readonly Action function;
+
+ public Register(Action function)
+ {
+ this.function = function;
+ }
+
+ public readonly void Invoke(Address address)
+ {
+ function(address);
+ }
+
+ public readonly void Invoke() where T : unmanaged, IEmbeddedResource
+ {
+ T template = default;
+ function(template.Address);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/Message/HandleDataRequest.cs b/core/Message/HandleDataRequest.cs
new file mode 100644
index 0000000..bfa37bd
--- /dev/null
+++ b/core/Message/HandleDataRequest.cs
@@ -0,0 +1,20 @@
+using Data.Components;
+using Unmanaged;
+using Worlds;
+
+namespace Data.Messages
+{
+ public struct HandleDataRequest
+ {
+ public readonly Entity entity;
+ public IsDataRequest request;
+
+ public readonly USpan Bytes => entity.GetArray().As();
+
+ public HandleDataRequest(Entity entity, IsDataRequest request)
+ {
+ this.entity = entity;
+ this.request = request;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/RequestStatus.cs b/core/RequestStatus.cs
new file mode 100644
index 0000000..45e67bc
--- /dev/null
+++ b/core/RequestStatus.cs
@@ -0,0 +1,11 @@
+namespace Data
+{
+ public enum RequestStatus : byte
+ {
+ Unknown = 0,
+ Submitted = 1,
+ Loading = 2,
+ Loaded = 3,
+ NotFound = 4
+ }
+}
\ No newline at end of file
diff --git a/core/RequestedDataNotFoundException.cs b/core/RequestedDataNotFoundException.cs
deleted file mode 100644
index 607d17c..0000000
--- a/core/RequestedDataNotFoundException.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace Data
-{
- ///
- /// Thrown when requested data could not be found.
- ///
- public class RequestedDataNotFoundException : Exception
- {
- public RequestedDataNotFoundException(string message) : base(message)
- {
- }
- }
-}
diff --git a/core/Types/IDataReference.cs b/core/Types/IDataReference.cs
deleted file mode 100644
index 4703499..0000000
--- a/core/Types/IDataReference.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Data
-{
- public interface IDataReference
- {
- Address Value { get; }
- }
-}
diff --git a/core/Types/IDataRequest.cs b/core/Types/IDataRequest.cs
new file mode 100644
index 0000000..d5fd7de
--- /dev/null
+++ b/core/Types/IDataRequest.cs
@@ -0,0 +1,8 @@
+using Worlds;
+
+namespace Data
+{
+ public interface IDataRequest : IEntity
+ {
+ }
+}
\ No newline at end of file
diff --git a/core/Types/IDataSource.cs b/core/Types/IDataSource.cs
index 74f32aa..f5091e6 100644
--- a/core/Types/IDataSource.cs
+++ b/core/Types/IDataSource.cs
@@ -5,4 +5,4 @@ namespace Data
public interface IDataSource : IEntity
{
}
-}
+}
\ No newline at end of file
diff --git a/core/Types/IEmbeddedResource.cs b/core/Types/IEmbeddedResource.cs
new file mode 100644
index 0000000..368e60b
--- /dev/null
+++ b/core/Types/IEmbeddedResource.cs
@@ -0,0 +1,7 @@
+namespace Data
+{
+ public interface IEmbeddedResource
+ {
+ Address Address { get; }
+ }
+}
\ No newline at end of file
diff --git a/core/Types/IEmbeddedResourceBank.cs b/core/Types/IEmbeddedResourceBank.cs
new file mode 100644
index 0000000..635fa3f
--- /dev/null
+++ b/core/Types/IEmbeddedResourceBank.cs
@@ -0,0 +1,9 @@
+using Data.Functions;
+
+namespace Data
+{
+ public interface IEmbeddedResourceBank
+ {
+ void Load(Register register);
+ }
+}
diff --git a/core/Types/IEmbeddedResources.cs b/core/Types/IEmbeddedResources.cs
deleted file mode 100644
index e1e9d7e..0000000
--- a/core/Types/IEmbeddedResources.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Collections.Generic;
-
-namespace Data
-{
- public interface IEmbeddedResources
- {
- IEnumerable Addresses { get; }
- }
-}
diff --git a/generator/Data.Generator.csproj b/generator/Data.Generator.csproj
index 9b48518..d01c699 100644
--- a/generator/Data.Generator.csproj
+++ b/generator/Data.Generator.csproj
@@ -22,4 +22,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/generator/EmbeddedAddressTableGenerator.cs b/generator/EmbeddedAddressTableGenerator.cs
deleted file mode 100644
index 78aa1d8..0000000
--- a/generator/EmbeddedAddressTableGenerator.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using Microsoft.CodeAnalysis;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-
-namespace Data.Generator
-{
- [Generator(LanguageNames.CSharp)]
- public class EmbeddedAddressTableGenerator : IIncrementalGenerator
- {
- private static readonly SourceBuilder source = new();
- private static readonly SourceBuilder debug = new();
- private const string TypeName = "EmbeddedAddressTable";
- private const string Namespace = "Data";
-
- void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context)
- {
- context.RegisterSourceOutput(context.CompilationProvider, Generate);
- }
-
- private static void Generate(SourceProductionContext context, Compilation compilation)
- {
- context.AddSource($"{TypeName}.generated.cs", Generate(compilation));
- }
-
- public static string Generate(Compilation compilation)
- {
- source.Clear();
- source.AppendLine($"namespace {Namespace}");
- source.BeginGroup();
- {
- source.AppendLine($"internal static partial class {TypeName}");
- source.BeginGroup();
- {
- source.AppendLine($"public static void RegisterAll()");
- source.BeginGroup();
- {
- foreach (MetadataReference assemblyReference in compilation.References)
- {
- if (compilation.GetAssemblyOrModuleSymbol(assemblyReference) is IAssemblySymbol assemblySymbol)
- {
- Stack stack = new();
- stack.Push(assemblySymbol.GlobalNamespace);
- while (stack.Count > 0)
- {
- ISymbol current = stack.Pop();
- if (current is INamespaceSymbol namespaceSymbol)
- {
- foreach (ISymbol member in namespaceSymbol.GetNamespaceMembers())
- {
- stack.Push(member);
- }
-
- foreach (ISymbol member in namespaceSymbol.GetTypeMembers())
- {
- stack.Push(member);
- }
- }
- else if (current is ITypeSymbol typeSymbol)
- {
- debug.AppendLine($"Type: {typeSymbol.ToDisplayString()}");
- ImmutableArray interfaces = typeSymbol.AllInterfaces;
- foreach (INamedTypeSymbol interfaceSymbol in interfaces)
- {
- if (interfaceSymbol.ToDisplayString() == "Data.IEmbeddedResources")
- {
- AppendRegistration(typeSymbol);
- }
- }
-
- foreach (ISymbol member in typeSymbol.GetMembers())
- {
- stack.Push(member);
- }
- }
- }
- }
- }
- }
- source.EndGroup();
- }
- source.EndGroup();
- }
- source.EndGroup();
- return source.ToString();
- }
-
- private static void AppendRegistration(ITypeSymbol type)
- {
- source.AppendLine($"EmbeddedAddress.Register<{GetFullTypeName(type)}>();");
- }
-
- private static string GetFullTypeName(ITypeSymbol type)
- {
- return type.ToDisplayString();
- }
- }
-}
\ No newline at end of file
diff --git a/generator/Generators/EmbeddedResourceBankGenerator.cs b/generator/Generators/EmbeddedResourceBankGenerator.cs
new file mode 100644
index 0000000..338eb5c
--- /dev/null
+++ b/generator/Generators/EmbeddedResourceBankGenerator.cs
@@ -0,0 +1,146 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Threading;
+using Types;
+
+namespace Data.Generator
+{
+ [Generator(LanguageNames.CSharp)]
+ public class EmbeddedResourceBankGenerator : IIncrementalGenerator
+ {
+ public const string TypeNameFormat = "{0}DataBank";
+
+ void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ IncrementalValuesProvider types = context.SyntaxProvider.CreateSyntaxProvider(Predicate, Transform);
+ context.RegisterSourceOutput(types.Collect(), Generate);
+ }
+
+ private void Generate(SourceProductionContext context, ImmutableArray typesArray)
+ {
+ List types = new();
+ foreach (ITypeSymbol? type in typesArray)
+ {
+ if (type is not null)
+ {
+ types.Add(type);
+ }
+ }
+
+ if (types.Count > 0)
+ {
+ string source = Generate(types, out string typeName);
+ context.AddSource($"{typeName}.generated.cs", source);
+ }
+ }
+
+ private static bool Predicate(SyntaxNode node, CancellationToken token)
+ {
+ return node.IsKind(SyntaxKind.StructDeclaration);
+ }
+
+ private static ITypeSymbol? Transform(GeneratorSyntaxContext context, CancellationToken token)
+ {
+ StructDeclarationSyntax node = (StructDeclarationSyntax)context.Node;
+ SemanticModel semanticModel = context.SemanticModel;
+ ITypeSymbol? type = semanticModel.GetDeclaredSymbol(node);
+ if (type is null)
+ {
+ return null;
+ }
+
+ if (type is INamedTypeSymbol namedType)
+ {
+ if (namedType.IsGenericType)
+ {
+ return null;
+ }
+ }
+
+ if (type.IsRefLikeType)
+ {
+ return null;
+ }
+
+ if (type.DeclaredAccessibility != Accessibility.Public && type.DeclaredAccessibility != Accessibility.Internal)
+ {
+ return null;
+ }
+
+ if (type.IsUnmanaged())
+ {
+ if (type.HasInterface("Data.IEmbeddedResource"))
+ {
+ return type;
+ }
+ }
+
+ return null;
+ }
+
+ public static string Generate(IReadOnlyList types, out string typeName)
+ {
+ string? assemblyName = types[0].ContainingAssembly?.Name;
+ if (assemblyName is not null && assemblyName.EndsWith(".Core"))
+ {
+ assemblyName = assemblyName.Substring(0, assemblyName.Length - 5);
+ }
+
+ SourceBuilder source = new();
+ source.AppendLine("using Unmanaged;");
+ source.AppendLine("using Worlds;");
+ source.AppendLine("using Data;");
+ source.AppendLine("using Data.Functions;");
+ source.AppendLine();
+
+ if (assemblyName is not null)
+ {
+ source.Append("namespace ");
+ source.AppendLine(assemblyName);
+ source.BeginGroup();
+ }
+
+ typeName = TypeNameFormat.Replace("{0}", assemblyName ?? "");
+ typeName = typeName.Replace(".", "");
+ source.Append("public readonly struct ");
+ source.Append(typeName);
+ source.Append(" : IEmbeddedResourceBank");
+ source.AppendLine();
+
+ source.BeginGroup();
+ {
+ source.AppendLine("readonly void IEmbeddedResourceBank.Load(Register function)");
+ source.BeginGroup();
+ {
+ foreach (ITypeSymbol? type in types)
+ {
+ if (type is not null)
+ {
+ AppendRegister(source, type);
+ }
+ }
+ }
+ source.EndGroup();
+ }
+ source.EndGroup();
+
+ if (assemblyName is not null)
+ {
+ source.EndGroup();
+ }
+
+ return source.ToString();
+ }
+
+ private static void AppendRegister(SourceBuilder source, ITypeSymbol type)
+ {
+ source.Append("function.Invoke<");
+ source.Append(type.ToDisplayString());
+ source.Append(">();");
+ source.AppendLine();
+ }
+ }
+}
\ No newline at end of file
diff --git a/generator/Generators/EmbeddedResourceRegistryLoaderGenerator.cs b/generator/Generators/EmbeddedResourceRegistryLoaderGenerator.cs
new file mode 100644
index 0000000..2a1deff
--- /dev/null
+++ b/generator/Generators/EmbeddedResourceRegistryLoaderGenerator.cs
@@ -0,0 +1,96 @@
+using Microsoft.CodeAnalysis;
+using Types;
+
+namespace Data.Generator
+{
+ [Generator(LanguageNames.CSharp)]
+ public class EmbeddedResourceRegistryLoaderGenerator : IIncrementalGenerator
+ {
+ private const string TypeName = "EmbeddedResourceRegistryLoader";
+
+ void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ context.RegisterSourceOutput(context.CompilationProvider, Generate);
+ }
+
+ private static void Generate(SourceProductionContext context, Compilation compilation)
+ {
+ if (compilation.GetEntryPoint(context.CancellationToken) is not null)
+ {
+ context.AddSource($"{TypeName}.generated.cs", Generate(compilation));
+ }
+ }
+
+ public static string Generate(Compilation compilation)
+ {
+ string? assemblyName = compilation.AssemblyName;
+ SourceBuilder builder = new();
+ builder.AppendLine("using Data;");
+ builder.AppendLine();
+
+ if (assemblyName is not null)
+ {
+ builder.Append("namespace ");
+ builder.Append(assemblyName);
+ builder.AppendLine();
+ builder.BeginGroup();
+ }
+
+ builder.AppendLine($"internal static partial class {TypeName}");
+ builder.BeginGroup();
+ {
+ builder.AppendLine($"public static void Load()");
+ builder.BeginGroup();
+ {
+ foreach (ITypeSymbol type in compilation.GetAllTypes())
+ {
+ if (type.IsRefLikeType)
+ {
+ continue;
+ }
+
+ if (type.DeclaredAccessibility == Accessibility.Private || type.DeclaredAccessibility == Accessibility.ProtectedOrInternal)
+ {
+ continue;
+ }
+
+ if (type is INamedTypeSymbol namedType)
+ {
+ if (namedType.IsGenericType)
+ {
+ continue;
+ }
+ }
+
+ if (type.HasInterface("Data.IEmbeddedResourceBank"))
+ {
+ AppendRegistration(builder, type);
+ }
+ }
+ }
+ builder.EndGroup();
+ }
+ builder.EndGroup();
+
+ if (assemblyName is not null)
+ {
+ builder.EndGroup();
+ }
+
+ return builder.ToString();
+ }
+
+ private static void AppendRegistration(SourceBuilder builder, ITypeSymbol type)
+ {
+ builder.Append("EmbeddedResourceRegistry.Load<");
+ builder.Append(GetFullTypeName(type));
+ builder.Append(">();");
+ builder.AppendLine();
+ }
+
+ private static string GetFullTypeName(ITypeSymbol type)
+ {
+ return type.ToDisplayString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/generator/SourceBuilder.cs b/generator/SourceBuilder.cs
deleted file mode 100644
index 5952512..0000000
--- a/generator/SourceBuilder.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-
-namespace Data.Generator
-{
- public class SourceBuilder
- {
- private readonly StringBuilder builder = new();
- private int indentation;
-
- public IEnumerable Lines
- {
- get
- {
- string[] lines = builder.ToString().Split('\n');
- for (int i = 0; i < lines.Length; i++)
- {
- yield return lines[i].TrimEnd('\r');
- }
- }
- }
-
- public override string ToString()
- {
- return builder.ToString();
- }
-
- public void Clear()
- {
- indentation = 0;
- builder.Clear();
- }
-
- public void BeginGroup()
- {
- AppendIndentation();
- builder.Append('{');
- builder.AppendLine();
- indentation++;
- }
-
- public void EndGroup()
- {
- indentation--;
- AppendIndentation();
- builder.Append('}');
- builder.AppendLine();
- }
-
- public void AppendIndentation()
- {
- for (int i = 0; i < indentation; i++)
- {
- builder.Append(" ");
- }
- }
-
- public void AppendLine(object text)
- {
- AppendIndentation();
- builder.AppendLine(text.ToString());
- }
- }
-}
\ No newline at end of file
diff --git a/tests/AddressTests.cs b/tests/AddressTests.cs
index 8cb1b9d..702891b 100644
--- a/tests/AddressTests.cs
+++ b/tests/AddressTests.cs
@@ -2,23 +2,11 @@
{
public class AddressTests
{
- [Test]
- public void CheckDataReferenceEquality()
- {
- Address defaultMaterialAddress = Address.Get();
- Assert.That(defaultMaterialAddress.ToString(), Is.EqualTo("Assets/Materials/unlit.mat"));
- }
-
[Test]
public void AddressEquality()
{
Address a = new("abacus");
Assert.That(a.Matches("*/abacus"), Is.True);
}
-
- public readonly struct DefaultMaterial : IDataReference
- {
- readonly Address IDataReference.Value => "Assets/Materials/unlit.mat";
- }
}
}
\ No newline at end of file
diff --git a/tests/Assets/data1.txt b/tests/Assets/data1.txt
new file mode 100644
index 0000000..9c5a87c
--- /dev/null
+++ b/tests/Assets/data1.txt
@@ -0,0 +1 @@
+this is some data, yo
\ No newline at end of file
diff --git a/tests/Data.Tests.csproj b/tests/Data.Tests.csproj
index 97e86aa..9f7efd7 100644
--- a/tests/Data.Tests.csproj
+++ b/tests/Data.Tests.csproj
@@ -2,7 +2,6 @@
net9.0
- Rendering.Tests
disable
enable
false
@@ -10,6 +9,12 @@
true
+
+
+ Never
+
+
+
all
diff --git a/tests/DataEntityTests.cs b/tests/DataEntityTests.cs
index 375515e..6b0cf86 100644
--- a/tests/DataEntityTests.cs
+++ b/tests/DataEntityTests.cs
@@ -13,9 +13,9 @@ public void LoadDataFromEntity()
Assert.That(data.Address.ToString(), Is.EqualTo("hello"));
- using BinaryReader reader = new(data.Bytes);
+ using BinaryReader reader = data.CreateBinaryReader();
USpan buffer = stackalloc char[128];
- uint length = reader.ReadUTF8Span(buffer);
+ uint length = reader.ReadUTF8(buffer);
Assert.That(buffer.Slice(0, length).ToString(), Is.EqualTo("data"));
}
diff --git a/tests/EmbeddedResourceBankTests.cs b/tests/EmbeddedResourceBankTests.cs
new file mode 100644
index 0000000..0fd9b40
--- /dev/null
+++ b/tests/EmbeddedResourceBankTests.cs
@@ -0,0 +1,33 @@
+using Data.Functions;
+using Unmanaged;
+
+namespace Data.Tests
+{
+ public class EmbeddedResourceBankTests : DataTests
+ {
+ [Test]
+ public void LoadEmbeddedResource()
+ {
+ Assert.That(EmbeddedResourceRegistry.Contains("Assets/data1.txt"), Is.False);
+
+ EmbeddedResourceRegistry.Load();
+
+ Assert.That(EmbeddedResourceRegistry.Contains("Assets/data1.txt"), Is.True);
+ EmbeddedResource embeddedResource = EmbeddedResourceRegistry.Get("Assets/data1.txt");
+ Assert.That(embeddedResource.Assembly, Is.EqualTo(typeof(CustomResourceBank).Assembly));
+ Assert.That(embeddedResource.address.ToString(), Is.EqualTo("Assets/data1.txt"));
+ using BinaryReader data = embeddedResource.CreateBinaryReader();
+ USpan buffer = stackalloc char[128];
+ uint length = data.ReadUTF8(buffer);
+ Assert.That(buffer.Slice(0, length).ToString(), Is.EqualTo("this is some data, yo"));
+ }
+
+ public readonly struct CustomResourceBank : IEmbeddedResourceBank
+ {
+ void IEmbeddedResourceBank.Load(Register register)
+ {
+ register.Invoke("Assets/data1.txt");
+ }
+ }
+ }
+}
\ No newline at end of file