Replace entity inspector explorer view

wip/source-generators
copygirl 1 year ago
parent c88530260f
commit 4903728e0e
  1. 395
      src/gaemstone.Client/Systems/EntityInspector.cs
  2. 2
      src/gaemstone.ECS
  3. 8
      src/gaemstone/Flecs/Core.cs
  4. 10
      src/gaemstone/Flecs/Doc.cs

@ -5,9 +5,9 @@ using System.Numerics;
using gaemstone.Client.Utility;
using gaemstone.ECS;
using gaemstone.Flecs;
using gaemstone.Utility;
using ImGuiNET;
using static gaemstone.Client.Systems.ImGuiManager;
using static gaemstone.Flecs.Core;
using Icon = gaemstone.Client.Utility.ForkAwesome;
using ImGuiInternal = ImGuiNET.Internal.ImGui;
@ -75,8 +75,8 @@ public class EntityInspector
SetDocInfo("/flecs/system/System" , 1 , Icon.Cog , 1.0f, 0.7f, 0.7f);
SetDocInfo("/flecs/core/Observer" , 2 , Icon.Eye , 1.0f, 0.8f, 0.8f);
SetDocInfo("/gaemstone/Doc/Relation" , 3 , Icon.ShareAlt , 0.7f, 1.0f, 0.8f);
SetDocInfo("/flecs/core/Tag" , 4 , Icon.Tag , 0.7f, 0.8f, 1.0f);
SetDocInfo("/flecs/core/Component" , 5 , Icon.PencilSquare , 0.6f, 0.6f, 1.0f);
SetDocInfo("/flecs/core/Component" , 4 , Icon.PencilSquare , 0.6f, 0.6f, 1.0f);
SetDocInfo("/flecs/core/Tag" , 5 , Icon.Tag , 0.7f, 0.8f, 1.0f);
SetDocInfo("/flecs/core/Prefab" , 6 , Icon.Cube , 0.9f, 0.8f, 1.0f);
}
@ -94,7 +94,7 @@ public class EntityInspector
}
[System]
public void ShowExplorerWindow(EntityRef window, InspectorWindow _, History? history)
public void ShowInspectorWindow(EntityRef window, InspectorWindow _, History? history)
{
var isOpen = true;
var fontSize = ImGui.GetFontSize();
@ -173,9 +173,9 @@ public class EntityInspector
ImGui.TableSetupColumn("Entity", ImGuiTableColumnFlags.WidthFixed);
ImGui.TableNextColumn();
var hasExpanded = window.Has<Expanded, Core.Wildcard>();
var hasExpanded = window.Has<Expanded, Wildcard>();
if (IconButtonWithToolTip(Icon.Outdent, "Collapse all items in the Explorer View", hasExpanded))
window.Remove<Expanded, Core.Wildcard>();
window.Remove<Expanded, Wildcard>();
if (history != null) {
var hasPrev = ((selected != null) ? history.Current?.Prev : history.Current) != null;
@ -215,6 +215,7 @@ public class EntityInspector
ImGui.SameLine();
if (IconButtonWithToolTip(Icon.Trash, "Delete the current entity", (selected != null))) {
// TODO: Delete history for deleted entity?
SetSelected(window, history, selected!.Parent);
selected.Delete(); // TODO: Confirmation dialog?
}
@ -272,208 +273,174 @@ public class EntityInspector
ImGui.PopStyleVar();
}
private struct EntitySummary
: IComparable<EntitySummary>
private class ExplorerEntry
: IComparable<ExplorerEntry>
{
public Entity Entity { get; init; }
public SpecialType? Type { get; init; }
public string? Name { get; init; }
public string? DocName { get; init; }
public Color? DocColor { get; init; }
public bool HasChildren { get; init; }
public bool IsExpanded { get; init; }
public bool IsDisabled { get; init; }
public int CompareTo(EntitySummary other)
private readonly EntityInspector _module;
public EntityRef Entity { get; }
public int NumChildren { get; }
public bool HasChildren => (NumChildren > 0);
public bool IsExpanded { get; }
public bool IsDisabled { get; }
private int _nameCached; private string? _name;
private int _docNameCached; private string? _docName;
public string? Name => (_nameCached++ > 0) ? _name : _name = Entity.Name;
public string? DocName => (_docNameCached++ > 0) ? _docName : _docName = Entity.GetDocName();
private float? _priority;
private EntityRef? _type;
public float Priority => EnsureTypeCached()._priority!.Value;
public EntityRef? Type => EnsureTypeCached()._type;
public ExplorerEntry(EntityInspector module, EntityRef entity,
int numChildren, bool isExpanded, bool isDisabled)
{
_module = module;
Entity = entity;
NumChildren = numChildren;
IsExpanded = (isExpanded && HasChildren);
IsDisabled = isDisabled;
}
private ExplorerEntry EnsureTypeCached()
{
if (_priority != null) return this;
(_type, _priority) = _module.FindDisplayType(Entity);
return this;
}
public int CompareTo(ExplorerEntry? other)
{
if (other == null) return -1;
return Compare(Priority, other.Priority)
?? Compare(Name, other.Name)
?? Compare(DocName, other.DocName)
?? Compare(Entity.Id, other.Entity.Id)
?? 0;
static int? Compare<T>(T x, T y) {
if (x is null) { if (y is null) return null; else return 1; }
else if (y is null) return -1;
var result = Comparer<T>.Default.Compare(x, y);
return (result != 0) ? result : null;
}
return Compare(Type, other.Type)
?? Compare(Name, other.Name)
?? Compare(DocName, other.DocName)
?? Compare(Entity.Id, other.Entity.Id)
?? 0;
}
public string DisplayName { get {
var name = (DocName != null) ? $"\"{DocName}\"" : Name ?? Entity.Id.ToString();
if (Type != null) name = $"{DisplayIcon} {name}";
return name;
} }
public string? DisplayIcon => Type switch {
SpecialType.Module => Icon.Archive,
SpecialType.System => Icon.Cog,
SpecialType.Relation => Icon.ShareAlt,
SpecialType.Component => Icon.PencilSquare,
SpecialType.Tag => Icon.Tag,
SpecialType.Prefab => Icon.Cube,
_ => null,
};
public Color? DisplayColor => DocColor ?? Type switch {
SpecialType.Module => Color.FromRGB(1.0f, 0.9f, 0.7f),
SpecialType.System => Color.FromRGB(1.0f, 0.7f, 0.7f),
SpecialType.Relation => Color.FromRGB(0.7f, 1.0f, 0.8f),
SpecialType.Component => Color.FromRGB(0.6f, 0.6f, 1.0f),
SpecialType.Tag => Color.FromRGB(0.7f, 0.8f, 1.0f),
SpecialType.Prefab => Color.FromRGB(0.9f, 0.8f, 1.0f),
_ => null,
};
}
public enum SpecialType
{
Module,
System,
Relation,
Component,
Tag,
Prefab,
}
private const int MAX_CHILDREN = 64;
private void ExplorerView(EntityRef window, History? history, EntityRef? selected)
{
var Expanded = window.World.LookupByTypeOrThrow<Expanded>();
List<EntitySummary> GetSummaries(Entity? parent) {
var result = new List<EntitySummary>();
var expression = $"(ChildOf, {parent?.Id ?? 0})" // Must be child of parent, or root entity.
+ ",?(Identifier, Name)" // Name (in hierarchy)
+ ",?(flecs.doc.Description, Name)" // DocName (human-readable)
+ ",?(flecs.doc.Description, flecs.doc.Color)" // DocColor
+ ",[none] ?ChildOf(_, $This)" // HasChildren
+ $",?{Expanded.Id}({window.Id}, $This)" // IsExpanded
+ ",?Disabled" // IsDisabled
+ ",?Module,?flecs.system.System,?gaemstone.Doc.Relation,?Component,?Tag,?Prefab"; // Type
using (var rule = new Rule(window.World, new(expression))) {
foreach (var iter in rule.Iter()) {
var names = iter.FieldOrEmpty<Core.Identifier>(2);
var docNames = iter.FieldOrEmpty<Flecs.Doc.Description>(3);
var docColors = iter.FieldOrEmpty<Flecs.Doc.Description>(4);
var hasChildren = iter.FieldIsSet(5);
var isExpanded = iter.FieldIsSet(6);
var isDisabled = iter.FieldIsSet(7);
var isModule = iter.FieldIsSet(8);
var isSystem = iter.FieldIsSet(9);
var isRelation = iter.FieldIsSet(10);
var components = iter.FieldOrEmpty<Core.Component>(11);
var isTag = iter.FieldIsSet(12);
var isPrefab = iter.FieldIsSet(13);
for (var i = 0; i < iter.Count; i++) {
// Certain built-in components in Flecs actually have a size of 0,
// thus don't actually hold any value and behave more like tags.
// We pretend they are just tags and mark them as such.
var component = components.GetOrNull(i);
var isComponent = (component?.Size > 0);
var isTagEquiv = (component?.Size == 0) || isTag;
var type = isModule ? SpecialType.Module
: isSystem ? SpecialType.System
: isRelation ? SpecialType.Relation
: isComponent ? SpecialType.Component
: isTagEquiv ? SpecialType.Tag
: isPrefab ? SpecialType.Prefab
: (SpecialType?)null;
result.Add(new() {
Entity = iter.Entity(i),
Type = type,
Name = names.GetOrNull(i)?.ToString(),
DocName = docNames.GetOrNull(i)?.ToString(),
DocColor = Color.TryParseHex(docColors.GetOrNull(i)?.ToString()),
HasChildren = hasChildren,
IsExpanded = isExpanded,
IsDisabled = isDisabled,
});
if (result.Count > MAX_CHILDREN)
return result;
}
// For some reason, the analyzer thinks world can be
// nullable, so let's be explicit about the type here.
World world = window.World;
var Wildcard = world.LookupByTypeOrThrow<Wildcard>().Entity;
var Any = world.LookupByTypeOrThrow<Any>().Entity;
var This = world.LookupByTypeOrThrow<This>().Entity;
var Var = world.LookupByTypeOrThrow<Variable>().Entity;
bool IsSpecialEntity(Entity entity)
=> (entity == Wildcard) || (entity == Any)
|| (entity == This) || (entity == Var);
var expId = world.LookupByTypeOrThrow<Expanded>().Id;
List<ExplorerEntry> GetEntries(Entity? parent) {
var result = new List<ExplorerEntry>();
using var rule = new Rule(world, new(
$"(ChildOf, {parent?.Id ?? 0})" // Must be child of parent, or root entity.
+ $",?{expId}({window.Id}, $This)" // Whether entity is expanded in explorer view.
+ $",?Disabled" // Don't filter out disabled entities.
));
foreach (var iter in rule.Iter()) {
var isExpanded = iter.FieldIsSet(2);
var isDisabled = iter.FieldIsSet(3);
for (var i = 0; i < iter.Count; i++) {
var entity = iter.Entity(i);
var count = IsSpecialEntity(entity) ? 0
: IdRef.Pair<ChildOf>(entity).Count;
result.Add(new(this, entity, count, isExpanded, isDisabled));
}
}
result.Sort();
return result;
}
void EntryNode(EntitySummary summary) {
var entity = new EntityRef(window.World, summary.Entity);
var isExpanded = summary.IsExpanded;
var isSelected = (selected == entity);
var flags = ImGuiTreeNodeFlags.OpenOnArrow
| ImGuiTreeNodeFlags.OpenOnDoubleClick
| ImGuiTreeNodeFlags.SpanAvailWidth;
if (!summary.HasChildren) flags |= ImGuiTreeNodeFlags.Leaf
| ImGuiTreeNodeFlags.Bullet
| ImGuiTreeNodeFlags.NoTreePushOnOpen;
if (isSelected) flags |= ImGuiTreeNodeFlags.Selected;
var hasColor = false;
if (summary.DisplayColor is Color color) { ImGui.PushStyleColor(ImGuiCol.Text, color.RGBA); hasColor = true; }
if (summary.DocName != null) ImGui.PushFont(ImGui.GetIO().Fonts.Fonts[2]);
if (summary.IsDisabled) ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().DisabledAlpha);
ImGui.SetNextItemOpen(isExpanded);
ImGui.TreeNodeEx(summary.DisplayName, flags);
if (summary.IsDisabled) ImGui.PopStyleVar();
if (summary.DocName != null) ImGui.PopFont();
if (hasColor) ImGui.PopStyleColor();
// When hovering over the node, display brief description (if available).
if (ImGui.IsItemHovered() && entity.GetDocBrief() is string brief)
ImGui.SetTooltip(brief);
// When node is clicked (but not on the arrow), select this entity.
if (ImGui.IsItemClicked() && !ImGui.IsItemToggledOpen())
SetSelected(window, history, entity, scrollTo: false);
// When node is toggled, toggle (Expanded, entity)
// relation on the inspector window entity.
if (ImGui.IsItemToggledOpen()) {
if (isExpanded) {
isExpanded = false;
window.Remove(Expanded, entity);
} else if (summary.HasChildren) {
isExpanded = true;
window.Add(Expanded, entity);
var spacingX = ImGui.GetStyle().ItemInnerSpacing.X;
var spacingY = ImGui.GetStyle().ItemSpacing.Y;
var nodeHeight = ImGui.GetTextLineHeight();
var scrollToSelected = window.Has<ScrollToSelected>();
float GetIndent(int depth) => depth * (nodeHeight + spacingX);
bool RenderNode(ExplorerEntry entry, int? depth) {
var entity = entry.Entity;
var startY = ImGui.GetCursorPosY();
var isVisible = ImGui.IsRectVisible(new(nodeHeight, nodeHeight));
var isSelected = (entity == selected);
var isRenderPass = (depth != null);
if (isRenderPass) {
// Button for expanding the child entries.
if (entry.HasChildren && !entry.IsExpanded) {
ImGui.SetCursorPosX(GetIndent(depth!.Value));
if (ImGui.Button($"##Expand{entity.Id}", new(nodeHeight)))
window.Add<Expanded>(entity);
}
}
if (window.Has<ScrollToSelected>() && isSelected) {
ImGui.SetScrollHereY();
window.Remove<ScrollToSelected>();
}
// Render the entity text, or a dummy if not necessary.
ImGui.SetCursorPos(new(GetIndent(depth!.Value + 1), startY));
var flags = RenderEntityFlags.IsHeaderLike
| RenderEntityFlags.SpanAvailWidth;
if (isSelected) flags |= RenderEntityFlags.IsSelected;
if (isVisible) RenderEntity(window, history, entity, flags);
else ImGui.Dummy(new(nodeHeight, nodeHeight));
// Scroll to this entry if selected and scrolling was requested.
if (scrollToSelected && isSelected) {
window.Remove<ScrollToSelected>();
ImGui.SetScrollHereY();
}
} else ImGui.Dummy(new(nodeHeight, nodeHeight));
// If node is not expanded, don't run any of code involving child entries.
if (!entry.IsExpanded) return isVisible;
if (isExpanded && summary.HasChildren) {
var children = GetSummaries(entity);
if (children.Count > MAX_CHILDREN) {
ImGui.TreePush();
ImGui.TextWrapped($"{Icon.ExclamationTriangle} Too many children. " +
"If an entity's full path is known, it can be entered in the path input.");
ImGui.TreePop();
} else foreach (var child in children)
EntryNode(child);
ImGui.TreePop();
// Get the child entries of this entity.
var entries = GetEntries(entity);
// If scrolling to selected, make sure every node is sorted.
// Otherwise the target node's position might not be right.
if (!scrollToSelected) {
foreach (var child in entries)
if (RenderNode(child, null))
isVisible = true;
if (!isVisible) return false;
}
if (!isRenderPass) return isVisible;
entries.Sort();
ImGui.SetCursorPosY(startY + nodeHeight);
foreach (var child in entries)
RenderNode(child, depth + 1);
var fullHeight = ImGui.GetCursorPosY() - startY - spacingY;
ImGui.SetCursorPos(new(GetIndent(depth!.Value), startY));
if (ImGui.Button($"##Expand{entity.Id}", new(nodeHeight, fullHeight)))
window.Remove<Expanded>(entity);
return true;
}
foreach (var summary in GetSummaries(Entity.None))
EntryNode(summary);
var entries = GetEntries(null);
entries.Sort();
foreach (var child in entries)
RenderNode(child, 0);
}
private void ComponentsTab(EntityRef window, History? history, EntityRef? selected)
{
if (selected == null) return;
var ChildOf = window.World.LookupByTypeOrThrow<Core.ChildOf>();
var ChildOf = window.World.LookupByTypeOrThrow<ChildOf>();
foreach (var id in selected.Type) {
// Hide ChildOf relations, as they are visible in the explorer.
if (id.IsPair && (id.Id.RelationUnsafe == ChildOf)) continue;
@ -485,8 +452,8 @@ public class EntityInspector
{
if (selected == null) return;
var world = window.World;
var ChildOf = world.LookupByTypeOrThrow<Core.ChildOf>();
var Wildcard = world.LookupByTypeOrThrow<Core.Wildcard>();
var ChildOf = world.LookupByTypeOrThrow<ChildOf>();
var Wildcard = world.LookupByTypeOrThrow<Wildcard>();
if (ImGui.CollapsingHeader($"As {Icon.Tag} Entity", ImGuiTreeNodeFlags.DefaultOpen))
foreach (var iter in Iterator.FromTerm(world, new(selected)))
@ -624,7 +591,7 @@ public class EntityInspector
bool scrollTo = true) // Should entity be scrolled to in the explorer view?
{
if (entity != null) window.Add<Selected>(entity);
else window.Remove<Selected, Core.Wildcard>();
else window.Remove<Selected, Wildcard>();
for (var parent = entity?.Parent; parent != null; parent = parent.Parent)
window.Add<Expanded>(parent);
@ -661,10 +628,10 @@ public class EntityInspector
}
private Rule? _findDisplayTypeRule;
private EntityRef? FindDisplayType(EntityRef entity)
private (EntityRef? DisplayType, float Priority) FindDisplayType(EntityRef entity)
{
var world = entity.World;
var component = world.LookupByTypeOrThrow<Core.Component>();
var component = world.LookupByTypeOrThrow<Component>();
var rule = _findDisplayTypeRule ??= new Rule(world, new(
$"$Type, gaemstone.Doc.DisplayType($Type)"));
@ -675,13 +642,13 @@ public class EntityInspector
foreach (var iter in _findDisplayTypeRule.Iter().SetVar(rule.ThisVar!, entity))
for (var i = 0; i < iter.Count; i++) {
var type = iter.GetVar(typeVar);
if ((type == component) && (entity.GetOrNull<Core.Component>(component)?.Size == 0))
type = world.LookupByTypeOrThrow<Core.Tag>();
if ((type == component) && (entity.GetOrNull<Component>(component)?.Size == 0))
type = world.LookupByTypeOrThrow<Tag>();
var priority = type.GetOrNull<DocPriority>()?.Value ?? float.MaxValue;
if (priority <= curPriority) { curType = type; curPriority = priority; }
}
return curType;
return (curType, curPriority);
}
@ -689,7 +656,7 @@ public class EntityInspector
// == Utility ImGui Functions ==
// =============================
private void RenderIdentifier(EntityRef window, History? history, IdentifierRef id)
private void RenderIdentifier(EntityRef window, History? history, IdRef id)
{
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(2, ImGui.GetStyle().ItemSpacing.Y));
if (id.AsPair() is (EntityRef relation, EntityRef target)) {
@ -705,8 +672,14 @@ public class EntityInspector
ImGui.PopStyleVar();
}
private void RenderEntity(EntityRef window, History? history, EntityRef entity)
private void RenderEntity(
EntityRef window, History? history, EntityRef entity,
RenderEntityFlags flags = default)
{
var spanAvailWidth = (flags & RenderEntityFlags.SpanAvailWidth) != 0;
var isHeaderLike = (flags & RenderEntityFlags.IsHeaderLike) != 0;
var isSelected = (flags & RenderEntityFlags.IsSelected) != 0;
var pos = ImGui.GetCursorScreenPos();
// Adjust based on AlignTextToFramePadding() or similar.
pos.Y += ImGuiInternal.GetCurrentWindow().DC.CurrLineTextBaseOffset;
@ -715,7 +688,7 @@ public class EntityInspector
var dummySize = new Vector2(20, ImGui.GetTextLineHeight());
if (!ImGui.IsRectVisible(pos, pos + dummySize)) { ImGui.Dummy(dummySize); return; }
var displayType = FindDisplayType(entity);
var (displayType, _) = FindDisplayType(entity);
var docColor = Color.TryParseHex(entity.GetDocColor()) ?? Color.TryParseHex(displayType?.GetDocColor());
var docIcon = entity.GetOrNull<DocIcon>()?.Value.ToString() ?? displayType?.GetOrNull<DocIcon>()?.Value.ToString();
var docName = entity.GetDocName(false);
@ -724,22 +697,42 @@ public class EntityInspector
var displayName = (docName != null) ? $"\"{docName}\"" : entity.Name ?? entity.Id.ToString();
if (docIcon is string icon) displayName = $"{icon} {displayName}";
var font = ImGui.GetIO().Fonts.Fonts[(docName != null) ? 2 : 0];
ImGui.PushFont(font); var size = ImGui.CalcTextSize(displayName); ImGui.PopFont();
var font = ImGui.GetIO().Fonts.Fonts[(docName != null) ? 2 : 0];
var color = docColor ?? Color.FromRGBA(ImGui.GetColorU32(ImGuiCol.Text));
if (isDisabled) color = color.WithAlpha(ImGui.GetStyle().DisabledAlpha);
var ctrl = ImGui.IsKeyDown(ImGuiKey.ModCtrl);
// Gotta push the font to calculate size properly.
// Can't just pass it to CalcTextSize for some reason.
ImGui.PushFont(font); var size = ImGui.CalcTextSize(displayName); ImGui.PopFont();
if (isHeaderLike) size.X += ImGui.GetStyle().FramePadding.X * 2;
if (spanAvailWidth) size.X = Math.Max(size.X, ImGui.GetContentRegionAvail().X);
if (isHeaderLike) {
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0);
ImGui.PushStyleColor(ImGuiCol.Button, isSelected ? ImGui.GetColorU32(ImGuiCol.Header)
: Color.Transparent.RGBA);
ImGui.PushStyleColor(ImGuiCol.ButtonActive , ImGui.GetColorU32(ImGuiCol.HeaderActive));
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, ImGui.GetColorU32(ImGuiCol.HeaderHovered));
ImGui.Button($"##{entity.Id}", size);
ImGui.PopStyleColor(3);
ImGui.PopStyleVar();
} else ImGui.InvisibleButton($"##{entity.Id}", size);
var shift = ImGui.IsKeyDown(ImGuiKey.ModShift);
if (ImGui.InvisibleButton(entity.Id.ToString(), size) && (ctrl || shift)) {
var canClick = isHeaderLike || shift;
if (canClick && ImGui.IsItemClicked()) {
if (shift) window = NewEntityInspectorWindow(window.World);
SetSelected(window, history, entity);
// Don't re-select this entity if it's already selected.
// Unless we're opening it in a new inspector window.
// This is to make sure the history doesn't get polluted.
if (!isSelected || shift) SetSelected(window, history, entity, false);
}
var drawList = ImGui.GetWindowDrawList();
if (isHeaderLike) pos.X += ImGui.GetStyle().FramePadding.X;
drawList.AddText(font, ImGui.GetFontSize(), pos, color.RGBA, displayName);
// Draw an underscore (like a hyperlink) if hovered and Ctrl key is held.
if (ImGui.IsItemHovered() && (ctrl || shift)) {
if (!isHeaderLike && canClick && ImGui.IsItemHovered()) {
// Draw a hyperlink-link underscore.
pos.Y -= 1.75f;
drawList.AddLine(pos + new Vector2(0, size.Y), pos + size, color.RGBA);
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
@ -760,4 +753,12 @@ public class EntityInspector
ImGui.EndTooltip();
}
}
[Flags]
private enum RenderEntityFlags
{
SpanAvailWidth = 0b001, // Extend the bounding box to span available width.
IsHeaderLike = 0b010, // Render the element like a header instead of a link.
IsSelected = 0b100, // Render a background showing this as selected.
}
}

@ -1 +1 @@
Subproject commit f9fa808d6842f5b208bbdfa1925f156342ed8a60
Subproject commit 619e2b24c6ff8797aed702a77954adfbd40f6f4d

@ -18,12 +18,14 @@ public static class Core
[Tag] public struct Empty { }
// Can't be in a module class with the same name.
[Path("/flecs/system/System")]
[Tag] public struct System { }
// [Path("/flecs/system/System")]
// [Tag] public struct System { }
// Entities
[Entity] public struct World { }
// Conflicts with World class, and not required?
// [Entity] public struct World { }
[Path("*")] public struct Wildcard { }
[Path("_")] public struct Any { }
[Entity] public struct This { }

@ -30,12 +30,10 @@ public static unsafe class DocExtensions
{
private static EntityRef Set<T>(EntityRef entity, string? value)
{
var world = entity.World;
var descEntity = world.LookupByTypeOrThrow<Doc.Description>();
var docEntity = world.LookupByTypeOrThrow<T>();
var id = Identifier.Pair(descEntity, docEntity);
var world = entity.World;
var id = IdRef.Pair<Doc.Description, T>(world);
var has = entity.Has(id);
var hadId = entity.Has(id);
var alloc = GlobalHeapAllocator.Instance;
if (value != null) {
var ptr = ecs_get_mut_id(world, entity, id);
@ -43,7 +41,7 @@ public static unsafe class DocExtensions
// FIXME: Why does freeing these cause crashes?
// if (has) alloc.Free((nint)desc.Value); // Free previous value.
desc.Value = (void*)(nint)alloc.AllocateCString(value);
} else if (has) {
} else if (hadId) {
// var @ref = ecs_ref_init_id(world, entity, id);
// var ptr = ecs_ref_get_id(world, &@ref, id);
// var desc = Unsafe.AsRef<Doc.Description>(ptr);

Loading…
Cancel
Save