Releases: rlipsc/polymorph
v0.3.1
Overview
This release focuses on features for organisation and ease of use.
Perhaps the biggest change is that system types and initialisation now defaults to being output by makeEcs, rather than immediately in the module they're defined.
As a result, ECS code output is localised to a single module.
This leads to several improvements for larger projects where components, systems, and sealing could be in separate modules:
- A single import to use the sealed ECS (the module with
makeEcs). - Easier scope management for supporting imports.
- No need to forward system types from where they're defined.
In particular, systems which use fields: blocks to initialise values are now in the same scope as the ECS output, which is much more intuitive.
Previously, these initialisation were output in the module that created the system definition (with defineSystem/makeSystem), which might not be the same module the system execution procedures were output (from makeEcsCommit/commitSystems), leading to both modules needing their support imports and increasing module dependence complexity.
To further help with this, ecsImport has been added. This instructs makeEcs to output import statements relative to the ecsImport call site, letting you organise system code and support modules together without worrying about where makeEcs is run.
There's also a lot of quality of life improvements, including aliasing components in systems, being able to register previously defined types as components, and better parsing of system bodies.
Changed
-
System registration and generation has been separated.
Systems types and instantiation are now deferred to
makeSystemby default.This means systems always know the full ECS when generated, which can be important if systems defined later claim ownership to components.
Another advantage is easier use of imports, since systems are output in one place.
Breaking changes
- The new default is to defer system types and instantiation to
makeEcs. - The previous behaviour of outputting systems when first defined can be obtained by setting
EcsSysOptions.committosdcInPlace.
Added
-
accessnow works with the component type in systems and events so you can writeComponentType.access. -
Component access/aliasing templates let you omit
itemwhen accessing components in systems and events:makeSystem "noItem", [MyComponent]: all: echo myComponent # Equivalent to 'item.myComponent'.
You can rename these components using a colon in the system's requirements:
makeSystem "noItem", [mc: MyComponent]: all: echo mc # Equivalent to 'item.myComponent'.
Aside from increasing readability, this can be useful for component libraries that use externally defined components:
template systemUsingComp*(userComponent: typedesc) {.dirty.} = makeSystem "useExternalComp", [uc: userComponent]: all: # As 'userComponent' is a type, to access its field in the # system's 'item' we'd have to either know the field name # already, or create a macro to generate the appropriate ident. # Now, we can just use the alias: echo uc
You can control whether access templates are output per system with
EcsSysOptions.itemTemplates. -
Previously defined types can now be used as components:
type MyType = object register defaultCompOpts: MyType
-
Included an
addComponentsthat operates on a whole system. -
defineToGrouplets you wrap system definitions to a group. -
ecsImportandecsImportFromreplays imports inmakeEcsusing the call site as a path prefix. If this doesn't compile, the import is passed through verbatim.This allows systems deferred to
makeEcsto naturally access other modules in a relative context, despite not being instantiated there.# subdir/module_1 var foo*: int
# subdir/module_2 register defaultCompOpts: type Bar* = object value*: int # Record import relative to our current path # to be output later by 'makeEcs'. ecsImport module_1 # This system uses our 'bar' variable from subdir/module_1. makeSystem "bar", [Bar]: all: echo foo + bar.value
# module_3.nim import module_2 # makeEcs now outputs 'import subdir/module_1'. makeEcsCommit "run"
-
System option for exporting fields by default.
-
onEcsCommitAll,onEcsCommitGroups, andonEcsNextCommitevents that are triggered on system commit. -
toTemplatelets you produce templates of live entities for use withconstruct. -
A string operator for
seq[ComponentTypeId]. -
More examples.
Fixed
- Off-by-one in
componentCount. - Component template options weren't stored.
- Inclusion test for multiple negated components.
- Check for assertions instead of 'defined(debug)'.
- Wrap 'addComponent' in a block for private ECS.
- Logging now properly handles identity.
- Removal of export markers for private scoped ECS now properly copies or passes through nodes instead of modifying in-place.
- System block parsing no longer skips recursion for call/command nodes that don't match iteration blocks. Iteration blocks are now correctly processed when not at the system body root.
Improved
- README documentation.
- Restructured existing examples and added more comments.
- Moved component removal to after events.
- Better entity to string output.
- Included {.line.} for 'item' templates for better line capture when debugging.
- The expression is now displayed when components can't be parsed.
- Better event spectrum coverage tests.
- Handle non-idents in stream command parsing.
- More line info logging for remove and delete.
- Update message for unsafe item access.
- Don't export ecsstatedb and pollute your namespace.
- Line info is now included for system definitions when logging.
- Handle nnkConv for findType.
ecsstatedbis now pregenerated and doesn't need to be rebuilt each compilation.- Drop call site path from import uniqueness check.
- Mark 'curGroup' template as used.
- Use the base type for metadata lists.
- Use static analysis for unnested events.
- 'deExport' now handles 'when', better unused hints.
- Remove typetraits dependency.
- No seqs with 'cisArray' and no storage when owned.
- Remove imports and check within template.
- When parsing systems the nodes are now checked recursively, allowing things like
allblocks to be placed within other blocks. - Improve event error messages.
- Add imports for
os,timesandcpuinfoonly when used. - Allow the
instanceTypetemplate withinregisterComponentsfor easier creation of instance fields from component types. constructcan now be passed a count when building multiple entities from aConstructionTemplate.
v0.3.0
Summary
This release introduces component negation for system requirements and improves events with compile time consistency checks.
Added
-
System requirements allow component negation with the
notprefix.For example:
makeSystem "mySystem", [Comp1, not Comp2]: discard
Negating components expands the potential design space for systems and allows the creation of mutually exclusive systems.
-
Events now perform static analysis to ensure that embedded mutations
don't invalidate other events expected within a state change. This
means you can more safely mutate entities within events whilst having
your design checked when you compile.When event mutations clash with coherent state changes a reason is
output along with traces for both the calling event stack, and the
event that cause the clashing mutation.This means this is allowed:
MyComponent.onAdd: entity.remove MyComponent
But this will raise a compile time error:
MyComponent.onAdd: entity.remove MyComponent MyComponent.onAddCallback: echo "T"
To turn this feature off, compile with
-d:ecsPermissive. -
System owned components can be declared with the
ownprefix.For example:
makeSystem "mySystem", [Comp1, own Comp2]
-
allandstreamblocks can now be used at any level of code within
the system body and not just at the top level. -
allandstreamblocks will raise a compile time error if they
have been embedded within themselves (this was not possible before
as only the top level was checked). -
Systems can now be defined with no components, allowing general
purpose code in the system workflow.These systems have the same control features, like pausing and
extracting to groups, but cannot use blocks that rely on entities
such asall,stream, or add/remove events, since they don't have
agroupsfield to store component data. -
Events now allow accessing the current entity and system with
entityandsys, as well ascurEntityandcurSystem. -
The compile log (e.g.,
-d:ecsLog) now includes the source file and
line of system definitions. -
Errors for field/option mismatch with
makeSystemnow show the
source file and line of the originaldefineSystem. -
The error for attempting to redefine a system body with
makeSystem
now show the source file and line of the originalmakeSystem. -
constructis now{.discardable.}. -
construct(quantity: int)allows easier repeat construction of a
ComponentList. -
hasAnytakes multiple component types and returns true when any of
them exist on the entity. -
fetchnow supports returning multiple components to a tuple like
theaddoperation. -
accessTypetemplate returns the source component type from an
instance type. -
Improved error message when existing components are added.
-
Systems now have a type class for generic access to all systems.
-
registercan be used instead ofregisterComponents. -
makeEcsCommitperformsmakeEcs, thencommitSystems. -
Comping with
-d:ecsLogDetailsis now much, much more detailed.
Operations now list their compile time invocation history, including
output of entity mutations by operation. -
EcsSysOptionsnow includes athreadingfield that, when set to
sthDistribute, will set up athreadsseq. This is set to the
number of cores when the system is instantiated. Spawning threads and
joining them is currently up to the user. Future versions will manage
threads for you when this option is enabled. -
All ECS operations are mangled with a unique identifier to avoid
variable collisions and allow better operation nesting.Pass
-d:ecsNoMangleto output code without appending a signature -
Useful with-d:ecsLogCodeto make the logged code easier to read. -
Improvements and additions to test suites such as negation testing.
Changes
-
Streamlined and standardised events. More data access within events.
All relevant events now includeentityaccess, and component events
involving systems now get access to the system row withitem. -
Events that have access to the
itemtemplate now include asserts
fromassertItemand staticecsStrictchecks. -
Deleting the host/calling entity within an event is now a compile
time error. Other entities within events can still be deleted. -
hascan now take multiple arguments:echo entity.has(A, B, C). -
deletenow parses the entity's components for systems to query,
instead of querying all the systems.This makes destruction do work proportional to the number of run time
components, instead of the number of compile time systems. This
should meandeleteis faster. -
Only
delete,constructandcloneare now generated as procs, as
these state changes are fixed to the design. All other state changes
are lazily constructed through generics and macros.This offers two benefits: state changes can be interleaved in events,
and there's no time wasted on unused procs during compilation.The original assumption with generating procs for 'commonly used'
operations such as 'addComponent Type()' would save compilation time
over running a macro to generate. However, the opposite was often
true; compile times were increased by generating procedures that
weren't used. -
System compile time errors are checked before any macrocache updates.
This change means you can usewhen not compiles(...)with defining
a system without create partial system entries. -
makeSystemfor a system previously defined withdefineSystemnow
ignores component order when checking matching requirements.
Note that the definition order is still semantically significant when
a system is first defined, as this informs the order of fields within
the system's item type. -
Component events that involve systems are announced as system events
whenechoRunningis active. -
Component system events for missing systems create an
errorinstead
of usingdoAssertfor a cleaner message. -
Strict checking outputs specific messages for removing and deleting.
-
addComponentis now atemplateand only constructed when used.
This reduces the need to build the code for every single combination
of singular adds to an entity which can constrain compilation for
some designs. -
EcsSysOptions.assertItemfordefaultSystemOptionsis set to the
value ofcompileOptions("assertions"). This means by default debug
builds will assert valid state withinitem.
Fixed
-
The
onDeleteevent is now properly exported. -
Multiple callback bodies such as
onAddCallback,onRemoveCallback,
now append to statements within a single callback proc instead of
trying to compile multiple procs with clashing names. -
onEcsBuiltis now reset at the end ofmakeEcsso that future
ECS outputs using the same identity don't output the code again.
Unfortunately this isn't possible withonEntityChangebecause
state changes require access aftermakeEcshas completed. -
Running systems at time intervals now applies to the entire system
body, and will process multipleallorstreamblocks.
Previously, eachall/streamwould reset timings, meaning only the
first block would be run.
Removed
-
Unused reference to
inSystemDeleteRowmacrocache entry. -
commithas been removed from the intercept update event (see
breaking changes). -
Removed
newEntityTemplateandinitComponentsas these are
better supported bycl().
Breaking changes
-
ecsStrictis now the default. To disable strict item checking, pass
-d:ecsPermissive.Strict checking will produce a compile time error if a system/event
context accessesitemafter doing something that has a possibility
to remove/invalidate the host context. However, this check doesn't
understand control flow or conditional statements, merely top to
bottom evaluation, and fully run time bound operations such as
construct,clone,transition, anddeletecount as affecting
all components. -
Similarly, trying to
deletetheentitythat invokes an event will
now halt compilation with an error. This feature helps maintains
state integrity when events remove the calling entity. -
The
onUpdateevent has changed:- the
commitproc has been removed. This event no longer allows
ignoring data updates whencommitis not called.
The intention of the
commitmechanism was to give the user explicit
control over whether to apply given component data. There is an
argument that this kind of mid-state data discarding invalidates the
transactional data integrity of state changes for seemingly little
value.The unique semantics of this event is also a potential for bugs,
since ifcommitis not called, the data is dropped.Finally, the cancelling special case hasn't come up (anecdotally),
and is probably better served with a specific buffer mechanism at the
component level.- this event is now only called when
updateis used on a component
instance, and not in other contexts like new entities (for which you
should useonInitoronAdd/onAddCallback).
- the
-
Events are now performed separately from system/entity state changes
and should be more lenient to embedding ECS state changes within
events. Event invocation order may differ slightly from previous
versions. -
Within
caseSystem, theSystemTupleTypetemplate was renamed
toItemType. -
Generation history for components with
seqstorage are now lost
when the last i...
v0.2.2
v0.2.2 2021-8-13
Added
EcsEntityOptionsincludes astrDefaultoption to define the default behaviour of the$operator for entities and components between displaying component data and just listing components.
Fixed
- Updated mention of
ContainertoComponentin theREADME.md. - Updated link in
README.mdfor the Polymersspaceshooterdemo.
Improved
- Expanded the ECS introduction in the
README.mdand some clean up of headings for options. - Nimble doesn't mention an invalid project structure.
v0.2.1
v0.2.1 2021-8-7
Added
- Private scope ECS generation.
sysProcessedcan be used withinstreamblocks to get the number of rows/entities the block has processed.
Fixed
defineSystemOwnerwasn't passing custom fields.stream multipassandstream stochasticno longer produce unknown identifier errors.streamblocks starting at the last item no longer skip to the first item.- Streaming owner systems no longer process an extra row.
- Using non-component types in system requirements now produces a compile error.
useSetnow includes components to the set.- The correct ident is now used internally for
transition. - The
onSystemAddToandonSystemRemoveFromevents no longer fail to compile in rare cases.
Improved
- System stream parameters can be unordered.
- More features documented in the
README.md.
Changed
- Owner systems now index from zero. Owned component instances are
validwhen uninitialised, andfetchreturns an instance set to-1when it cannot find an owned component.
v0.2.0
This release is a complete internal restructuring to support system-owned components, new events, and a macrocache backend. Whilst effort has been made to maintain the current API, there are some significant changes.
Added
-
Ease of use:
-
Improved documentation to cover all library functionality.
-
Added code examples from documentation to the
examplesfolder. -
More descriptive error messages.
-
onEcsBuiltallows you to emit code aftermakeEcshas finished. This code can use entities and ECS operations, which can be useful for utility functions and setup for systems and libraries. -
defaultComponentOptions,defaultEntityOptions, anddefaultSystemOptionsnow have shorter aliases asdefaultCompOpts,defaultEntOpts, anddefaultSysOpts. -
EcsEntityOptionsallows selection of error response between assertions and raising exceptions.
-
-
Components:
-
removeComponent,addComponent, andfetchComponentcan now be writtenremove,add, andfetch. -
You can now remove multiple components in one state change with
remove/removeComponents. -
update/updateComponentssupports updating multiple components with aComponentList. Only existing components on the entity are updated, and others in theComponentListare ignored. -
typeNameconverts a run timeComponentTypeIdto the string of the type it represents. -
componentCountreturns the number of components in use for a component type or instance type. -
componentStoragelets you access the component's storage list. -
registerComponentswill ignore types with the{.notComponent.}pragma.
-
-
Systems:
-
You can now write code directly in the system body that is executed when the system is run. This means blocks are no longer a requirement of system bodies and you don't need to use
startblocks to declare variables. Another benefit is you can write system code that respects thepausedstate even when not using any blocks. -
The
allandstreamblocks are now expanded within the system body. This allows multiple passes and further processing such as running code in the body between these blocks. -
Systems now allow multiple blocks of the same type.
-
Systems passed the same component more than once will now throw an error at compile time.
-
System fields can be defined with the
fields:block withinmakeSystem. When a system has previously been defined withdefineSystem, thefields:block is checked to match this definition. -
Systems now support a
stream stochastic:block to randomly select items to process. -
Systems can be grouped to run as separate procedures with
defineGroup. These systems are removed from the pending output ofcommitSystemsand are instead emitted withcommitGroup. It's a compile time error to emit groups with systems that don't have bodies defined. -
The system utility
clearallows deleting all entities in a system. -
The system utility
removeallows removing multiple components from all entities in a system. -
expectSystemschecks that an entity has a specific set of systems and willdoAssertif the entity does not satisfy these systems. -
systemsUsedreturns a string containing the systems that would be used for entities with a particular set of components. -
analyseSystemprovides statistical analysis of the memory addresses accessed by a system. -
Systems include a
deleteListseqthat will delete any entities added to it after the system has finished executing. -
Systems include an
idfield with the system'sSystemIndex. -
caseSystemallows generic operations based on a run timeSystemIndexin a similar way tocaseComponent. -
System options given to
defineSystemare now checked to match options passed tomakeSystemOpts.
-
-
ECS identities:
- Multiple ECS may be generated separately using
constidentity strings.
- Multiple ECS may be generated separately using
-
System owned components:
-
Systems can now own the storage and lifetime of components they use with
defineSystemOwner. Owned components are inlined into the groups field of the owner system and depend on the system row to exist. When iterating, systems access their owned components in order as contiguous memory and without indirection. Access to owned components from other systems is a single indirection.- A component type can only be owned by one system.
- Adding owned components to entities without meeting the full component requirements of the owner systems will result in an error.
- Removing owned components will cascade system removal for other owned components in the system.
-
-
System events:
-
When defining a system, you can now use
addedandremovedblocks to let you process state changes in a system context. These events let you work with sets of components usingitemas if you were using anallorstreamblock. -
Actions in
addedandremovedare performed immediately as part of a state change such asaddorremove, and not during the system's normal cycle.
-
-
onEntityChangeevent:- This event is triggered for any entity that has components added or removed.
-
Run-time construction:
-
Creating entities with
constructandcloneis now more efficient and flexible.- These operations are now generated as integrated state changes for dynamic sets of components.
constructbuilds a map of types then updates all relevant systems with a single generated state change.- Previously
addwas called for each component, with significant overhead. clonecan elide some validity checks.
-
The
clmacro makes building aComponentListeasy by allowing both source component types and container types to be mixed, whilst handling the internaltypeIdfield for you. This macro also reduces boilerplate by handling a single component without having to typecast it toComponent. -
constructandclonenow call user events. -
Post construction events allow adding/removing components.
-
transitionallows state machine like switching of components between twoComponentListparameters.
-
-
Misc utilities:
-
high(entityType: typedesc[EntityId] | typedesc[EntityRef])returns the highestEntityIdcurrently in use. -
EntityRefnow has avalidtemplate.
-
-
New templates within
caseComponent:-
isOwnedreturns a bool constant indicating if the component type is owned. -
owningSystemIndexreturns theSystemIndexof the owner system (orInvalidSystemif none). -
owningSystemlets you access the component's owner system variable. Note that this is only generated for components that are owned. -
componentDatalets you access the component's storage list.
-
-
Compile switches:
-
ecsLog: output ECS generation progress. -
ecsLogDetails: output detailed ECS generation information. -
ecsStrictincludes a check at compile time to catch accessingitemafter a remove or delete has affected the system (which can potentially change whatitemrefers to).
-
Changes
-
The library now uses the
macrocachemodule to store the ECS state instead of{.compileTime.}variables. -
The default stream count is now
1, changed from10. -
User events now run outside of state changes instead of inside them, and can rely on the state being fully defined when invoked.
-
SystemIndexnow starts from1andSystemIndex(0) == InvalidSystemIndex. PreviouslyInvalidSystemIndexwas equal toSystemIndex(-1). This change means default (zero) values are invalid, andSystemIndexachieves parity withComponentTypeId.
Breaking changes
-
Removed:
-
makeSystemOptFieldshas been removed, as it is no longer needed. Fields inmakeSystemandmakeSystemOptscan be defined using thefields:block. -
The
tmplandinitmacros generated for each component have been removed. Use theclmacro or manually initialise component containers withmakeContainer. -
The
deleteEntitytemplate withinallandstreamblocks has been removed as it offers no value overentity.delete. -
reprocessRowhas been removed. This is now handled automatically at compile time. -
addFetchhas been removed. This operation was ambiguous withaddComponentand whilstaddComponentreturns a component instance,addFetchreturned aComponentRef, which is less useful. To obtain aComponentReffrom an instance, use thetoReftemplate. -
amFieldTemplatesinEcsCompOptions.accessMethodhas been removed as it did not offer any significant functionality overamDotOp. -
Within
caseComponent, thecomponentRefInittemplate has been removed as these initialisers are no longer present.
-
-
Changed:
-
System execution order:
defineSystemnow sets the order that systems are run bycommitSystems, where previously this was set when the system body was defined bymakeSystemormakeSystemBody. When no matchingdefineSystemexists,makeSystemappends order as before when it invokesdefineSystem. This means the order must be defined beforemakeEcsis run, whereas before it could be defined aftermakeEcs. -
commitSystemswill only output systems that have not already been output and are not grouped. -
The construction callback API has changed: constructor/clone callbacks now return a
seq[Component]which is processed after the callback to ensure systems are correctly linked. Construction/clone callbacks should create the appropriateComponentcontainers and add them to theseqresult. -
Within
caseComponent, the templatecomponentInstanceIdshas been renamedcomponentGenerations. -
The compile switch `debugSystemPerformance...
-
Initial release
v0.1.0 Replace readme with manual