mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 09:30:03 +00:00
Add pages to docs
This commit is contained in:
parent
4375150683
commit
144cd044e8
6 changed files with 711 additions and 278 deletions
45
docs/api-types.md
Normal file
45
docs/api-types.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# World
|
||||||
|
|
||||||
|
A World contains all ECS data
|
||||||
|
Games can have multiple worlds, although typically only one is necessary. These worlds are isolated from each other, meaning they donot share the same entities nor component IDs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Entity
|
||||||
|
|
||||||
|
An unique id.
|
||||||
|
|
||||||
|
Entities consist out of a number unique to the entity in the lower 32 bits, and a counter used to track entity liveliness in the upper 32 bits. When an id is recycled, its generation count is increased. This causes recycled ids to be very large (>4 billion), which is normal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# QueryIter
|
||||||
|
|
||||||
|
A result from the `World:query` function.
|
||||||
|
|
||||||
|
Queries are used to iterate over entities that match against the set collection of components.
|
||||||
|
|
||||||
|
Calling it in a loop will allow iteration over the results.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
for id, enemy, charge, model in world:query(Enemy, Charge, Model) do
|
||||||
|
-- Do something
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### QueryIter.without
|
||||||
|
|
||||||
|
QueryIter.without(iter: QueryIter
|
||||||
|
...: [Entity](../api-types/Entity)): QueryIter
|
||||||
|
|
||||||
|
|
||||||
|
Create a new Query Iterator from the filter
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
... The collection of components to filter archetypes against.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
The new query iterator.
|
||||||
|
|
189
docs/api/world.md
Normal file
189
docs/api/world.md
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
# World
|
||||||
|
|
||||||
|
### World.new
|
||||||
|
|
||||||
|
World.new(): [World](../api-types/World)
|
||||||
|
|
||||||
|
Create a new world.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
A new world
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.entity
|
||||||
|
|
||||||
|
World.entity(world: [World](../api-types/World)): [Entity](../api-types/Entity)
|
||||||
|
|
||||||
|
Creates an entity in the world.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
A new entiity id
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.target
|
||||||
|
|
||||||
|
World.target(world: [World](../api-types/World),
|
||||||
|
entity: [Entity](../api-types/Entity),
|
||||||
|
rel: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
|
||||||
|
|
||||||
|
Get the target of a relationship.
|
||||||
|
|
||||||
|
This will return a target (second element of a pair) of the entity for the specified relationship.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
entity The entity.
|
||||||
|
rel The relationship between the entity and the target.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
The first target for the relationship
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.add
|
||||||
|
|
||||||
|
World.add(world: [World](../api-types/World),
|
||||||
|
entity: [Entity](../api-types/Entity),
|
||||||
|
id: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
|
||||||
|
|
||||||
|
Add a (component) id to an entity.
|
||||||
|
|
||||||
|
This operation adds a single (component) id to an entity.
|
||||||
|
If the entity already has the id, this operation will have no side effects.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
entity The entity.
|
||||||
|
id The id to add.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.remove
|
||||||
|
|
||||||
|
World.remove(world: [World](../api-types/World),
|
||||||
|
entity: [Entity](../api-types/Entity),
|
||||||
|
id: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
|
||||||
|
|
||||||
|
Remove a (component) id to an entity.
|
||||||
|
|
||||||
|
This operation removes a single (component) id to an entity.
|
||||||
|
If the entity already has the id, this operation will have no side effects.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
entity The entity.
|
||||||
|
id The id to add.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.get
|
||||||
|
|
||||||
|
World.get(world: [World](../api-types/World),
|
||||||
|
entity: [Entity](../api-types/Entity),
|
||||||
|
id: [Entity](../api-types/Entity)): any
|
||||||
|
|
||||||
|
Gets the component data.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
entity The entity.
|
||||||
|
id The id of component to get.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
The component data, nil if the entity does not have the componnet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.set
|
||||||
|
|
||||||
|
World.set(world: [World](../api-types/World),
|
||||||
|
entity: [Entity](../api-types/Entity),
|
||||||
|
id: [Entity](../api-types/Entity)
|
||||||
|
data: any)
|
||||||
|
|
||||||
|
Set the value of a component.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
entity The entity.
|
||||||
|
id The id of the componment set.
|
||||||
|
data The data to the component.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### World.query
|
||||||
|
|
||||||
|
World.query(world: [World](../api-types/World),
|
||||||
|
...: [Entity](../api-types/Entity)): [QueryIter](../api-types/QueryIter)
|
||||||
|
|
||||||
|
Create a QueryIter from the list of filters.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
world The world.
|
||||||
|
... The collection of components to match entities against.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
The query iterator.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Pair
|
||||||
|
|
||||||
|
### pair
|
||||||
|
|
||||||
|
pair(first: [Entity](../api-types/Entity),
|
||||||
|
second: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
|
||||||
|
|
||||||
|
Creates a composite key.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
first The first element.
|
||||||
|
second The second element.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
The pair of the two elements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### IS_PAIR
|
||||||
|
|
||||||
|
jecs.IS_PAIR(id: [Entity](../api-types/Entity)): boolean
|
||||||
|
|
||||||
|
Creates a composite key.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
id The id to check.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
If id is a pair.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
|
||||||
|
### OnAdd
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### OnRemove
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Rest
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### OnSet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Wildcard
|
||||||
|
|
||||||
|
Matches any id, returns all matches.
|
||||||
|
|
5
docs/world.md
Normal file
5
docs/world.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# World
|
||||||
|
|
||||||
|
A World contains all ECS data
|
||||||
|
Games can have multiple worlds, although typically only one is necessary. These worlds are isolated from each other.
|
||||||
|
is queryable and can be used to get entities with a specific set of components. Entities are simply ever-increasing integers.
|
12
lib/init.lua
12
lib/init.lua
|
@ -103,14 +103,6 @@ local function ECS_IS_PAIR(e: number)
|
||||||
return (e % 2 ^ 4) // FLAGS_PAIR ~= 0
|
return (e % 2 ^ 4) // FLAGS_PAIR ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function separate(e: number)
|
|
||||||
local _typeFlags = e % 0x10
|
|
||||||
-- Revert to //= after highligting gets fixed
|
|
||||||
--
|
|
||||||
e = e // ECS_ID_FLAGS_MASK
|
|
||||||
return e // ECS_ENTITY_MASK, e % ECS_GENERATION_MASK, _typeFlags
|
|
||||||
end
|
|
||||||
|
|
||||||
-- HIGH 24 bits LOW 24 bits
|
-- HIGH 24 bits LOW 24 bits
|
||||||
local function ECS_GENERATION(e: i53)
|
local function ECS_GENERATION(e: i53)
|
||||||
e = e // 0x10
|
e = e // 0x10
|
||||||
|
@ -118,7 +110,9 @@ local function ECS_GENERATION(e: i53)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ECS_GENERATION_INC(e: i53)
|
local function ECS_GENERATION_INC(e: i53)
|
||||||
local id, generation, flags = separate(e)
|
local flags = e // 0x10
|
||||||
|
local id = flags // ECS_ENTITY_MASK
|
||||||
|
local generation = flags % ECS_GENERATION_MASK
|
||||||
|
|
||||||
return ECS_COMBINE(id, generation + 1) + flags
|
return ECS_COMBINE(id, generation + 1) + flags
|
||||||
end
|
end
|
||||||
|
|
186
mkdocs.yml
Normal file
186
mkdocs.yml
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
site_name: Fusion
|
||||||
|
site_url: https://elttob.uk/Fusion/
|
||||||
|
repo_name: dphfox/Fusion
|
||||||
|
repo_url: https://github.com/dphfox/Fusion
|
||||||
|
|
||||||
|
extra:
|
||||||
|
version:
|
||||||
|
provider: mike
|
||||||
|
|
||||||
|
theme:
|
||||||
|
name: material
|
||||||
|
custom_dir: docs/assets/overrides
|
||||||
|
logo: assets/logo
|
||||||
|
favicon: assets/logo-dark.svg
|
||||||
|
palette:
|
||||||
|
- media: "(prefers-color-scheme: dark)"
|
||||||
|
scheme: fusiondoc-dark
|
||||||
|
toggle:
|
||||||
|
icon: octicons/sun-24
|
||||||
|
title: Switch to light theme
|
||||||
|
- media: "(prefers-color-scheme: light)"
|
||||||
|
scheme: fusiondoc-light
|
||||||
|
toggle:
|
||||||
|
icon: octicons/moon-24
|
||||||
|
title: Switch to dark theme
|
||||||
|
font:
|
||||||
|
text: Plus Jakarta Sans
|
||||||
|
code: JetBrains Mono
|
||||||
|
features:
|
||||||
|
- navigation.tabs
|
||||||
|
- navigation.top
|
||||||
|
- navigation.sections
|
||||||
|
- navigation.instant
|
||||||
|
- navigation.indexes
|
||||||
|
- search.suggest
|
||||||
|
- search.highlight
|
||||||
|
icon:
|
||||||
|
repo: octicons/mark-github-16
|
||||||
|
|
||||||
|
extra_css:
|
||||||
|
- assets/theme/fusiondoc.css
|
||||||
|
- assets/theme/colours.css
|
||||||
|
- assets/theme/code.css
|
||||||
|
- assets/theme/paragraph.css
|
||||||
|
- assets/theme/page.css
|
||||||
|
- assets/theme/admonition.css
|
||||||
|
- assets/theme/404.css
|
||||||
|
- assets/theme/api-reference.css
|
||||||
|
- assets/theme/dev-tools.css
|
||||||
|
|
||||||
|
extra_javascript:
|
||||||
|
- assets/scripts/smooth-scroll.js
|
||||||
|
|
||||||
|
nav:
|
||||||
|
- Home: index.md
|
||||||
|
- Tutorials:
|
||||||
|
- Get Started: tutorials/index.md
|
||||||
|
- Installing Fusion: tutorials/get-started/installing-fusion.md
|
||||||
|
- Developer Tools: tutorials/get-started/developer-tools.md
|
||||||
|
- Getting Help: tutorials/get-started/getting-help.md
|
||||||
|
- Fundamentals:
|
||||||
|
- Scopes: tutorials/fundamentals/scopes.md
|
||||||
|
- Values: tutorials/fundamentals/values.md
|
||||||
|
- Observers: tutorials/fundamentals/observers.md
|
||||||
|
- Computeds: tutorials/fundamentals/computeds.md
|
||||||
|
- Tables:
|
||||||
|
- ForValues: tutorials/tables/forvalues.md
|
||||||
|
- ForKeys: tutorials/tables/forkeys.md
|
||||||
|
- ForPairs: tutorials/tables/forpairs.md
|
||||||
|
- Animation:
|
||||||
|
- Tweens: tutorials/animation/tweens.md
|
||||||
|
- Springs: tutorials/animation/springs.md
|
||||||
|
- Roblox:
|
||||||
|
- Hydration: tutorials/roblox/hydration.md
|
||||||
|
- New Instances: tutorials/roblox/new-instances.md
|
||||||
|
- Parenting: tutorials/roblox/parenting.md
|
||||||
|
- Events: tutorials/roblox/events.md
|
||||||
|
- Change Events: tutorials/roblox/change-events.md
|
||||||
|
- Outputs: tutorials/roblox/outputs.md
|
||||||
|
- References: tutorials/roblox/references.md
|
||||||
|
- Best Practices:
|
||||||
|
- Components: tutorials/best-practices/components.md
|
||||||
|
- Instance Handling: tutorials/best-practices/instance-handling.md
|
||||||
|
- Callbacks: tutorials/best-practices/callbacks.md
|
||||||
|
- State: tutorials/best-practices/state.md
|
||||||
|
- Sharing Values: tutorials/best-practices/sharing-values.md
|
||||||
|
- Error Safety: tutorials/best-practices/error-safety.md
|
||||||
|
- Optimisation: tutorials/best-practices/optimisation.md
|
||||||
|
|
||||||
|
- Examples:
|
||||||
|
- Home: examples/index.md
|
||||||
|
- Cookbook:
|
||||||
|
- examples/cookbook/index.md
|
||||||
|
- Player List: examples/cookbook/player-list.md
|
||||||
|
- Animated Computed: examples/cookbook/animated-computed.md
|
||||||
|
- Fetch Data From Server: examples/cookbook/fetch-data-from-server.md
|
||||||
|
- Light & Dark Theme: examples/cookbook/light-and-dark-theme.md
|
||||||
|
- Button Component: examples/cookbook/button-component.md
|
||||||
|
- Loading Spinner: examples/cookbook/loading-spinner.md
|
||||||
|
- Drag & Drop: examples/cookbook/drag-and-drop.md
|
||||||
|
- API Reference:
|
||||||
|
- api-reference/index.md
|
||||||
|
- General:
|
||||||
|
- Errors: api-reference/general/errors.md
|
||||||
|
- Types:
|
||||||
|
- Contextual: api-reference/general/types/contextual.md
|
||||||
|
- Version: api-reference/general/types/version.md
|
||||||
|
- Members:
|
||||||
|
- Contextual: api-reference/general/members/contextual.md
|
||||||
|
- Safe: api-reference/general/members/safe.md
|
||||||
|
- version: api-reference/general/members/version.md
|
||||||
|
- Memory:
|
||||||
|
- Types:
|
||||||
|
- Scope: api-reference/memory/types/scope.md
|
||||||
|
- ScopedObject: api-reference/memory/types/scopedobject.md
|
||||||
|
- Task: api-reference/memory/types/task.md
|
||||||
|
- Members:
|
||||||
|
- deriveScope: api-reference/memory/members/derivescope.md
|
||||||
|
- doCleanup: api-reference/memory/members/docleanup.md
|
||||||
|
- scoped: api-reference/memory/members/scoped.md
|
||||||
|
- State:
|
||||||
|
- Types:
|
||||||
|
- UsedAs: api-reference/state/types/usedas.md
|
||||||
|
- Computed: api-reference/state/types/computed.md
|
||||||
|
- Dependency: api-reference/state/types/dependency.md
|
||||||
|
- Dependent: api-reference/state/types/dependent.md
|
||||||
|
- For: api-reference/state/types/for.md
|
||||||
|
- Observer: api-reference/state/types/observer.md
|
||||||
|
- StateObject: api-reference/state/types/stateobject.md
|
||||||
|
- Use: api-reference/state/types/use.md
|
||||||
|
- Value: api-reference/state/types/value.md
|
||||||
|
- Members:
|
||||||
|
- Computed: api-reference/state/members/computed.md
|
||||||
|
- ForKeys: api-reference/state/members/forkeys.md
|
||||||
|
- ForPairs: api-reference/state/members/forpairs.md
|
||||||
|
- ForValues: api-reference/state/members/forvalues.md
|
||||||
|
- Observer: api-reference/state/members/observer.md
|
||||||
|
- peek: api-reference/state/members/peek.md
|
||||||
|
- Value: api-reference/state/members/value.md
|
||||||
|
- Roblox:
|
||||||
|
- Types:
|
||||||
|
- Child: api-reference/roblox/types/child.md
|
||||||
|
- PropertyTable: api-reference/roblox/types/propertytable.md
|
||||||
|
- SpecialKey: api-reference/roblox/types/specialkey.md
|
||||||
|
- Members:
|
||||||
|
- Attribute: api-reference/roblox/members/attribute.md
|
||||||
|
- AttributeChange: api-reference/roblox/members/attributechange.md
|
||||||
|
- AttributeOut: api-reference/roblox/members/attributeout.md
|
||||||
|
- Children: api-reference/roblox/members/children.md
|
||||||
|
- Hydrate: api-reference/roblox/members/hydrate.md
|
||||||
|
- New: api-reference/roblox/members/new.md
|
||||||
|
- OnChange: api-reference/roblox/members/onchange.md
|
||||||
|
- OnEvent: api-reference/roblox/members/onevent.md
|
||||||
|
- Out: api-reference/roblox/members/out.md
|
||||||
|
- Ref: api-reference/roblox/members/ref.md
|
||||||
|
- Animation:
|
||||||
|
- Types:
|
||||||
|
- Animatable: api-reference/animation/types/animatable.md
|
||||||
|
- Spring: api-reference/animation/types/spring.md
|
||||||
|
- Tween: api-reference/animation/types/tween.md
|
||||||
|
- Members:
|
||||||
|
- Tween: api-reference/animation/members/tween.md
|
||||||
|
- Spring: api-reference/animation/members/spring.md
|
||||||
|
- Extras:
|
||||||
|
- Home: extras/index.md
|
||||||
|
- Backgrounds: extras/backgrounds.md
|
||||||
|
- Brand Guidelines: extras/brand-guidelines.md
|
||||||
|
|
||||||
|
markdown_extensions:
|
||||||
|
- admonition
|
||||||
|
- attr_list
|
||||||
|
- meta
|
||||||
|
- md_in_html
|
||||||
|
- pymdownx.superfences
|
||||||
|
- pymdownx.betterem
|
||||||
|
- pymdownx.details
|
||||||
|
- pymdownx.tabbed:
|
||||||
|
alternate_style: true
|
||||||
|
- pymdownx.inlinehilite
|
||||||
|
- toc:
|
||||||
|
permalink: true
|
||||||
|
- pymdownx.highlight:
|
||||||
|
guess_lang: false
|
||||||
|
- pymdownx.emoji:
|
||||||
|
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||||
|
emoji_generator: !!python/name:materialx.emoji.to_svg
|
552
tests/world.lua
552
tests/world.lua
|
@ -1,5 +1,5 @@
|
||||||
local testkit = require("../testkit")
|
|
||||||
local jecs = require("../lib/init")
|
local jecs = require("../lib/init")
|
||||||
|
local testkit = require("../testkit")
|
||||||
local __ = jecs.Wildcard
|
local __ = jecs.Wildcard
|
||||||
local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
|
local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
|
||||||
local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
|
local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
|
||||||
|
@ -11,329 +11,343 @@ local ECS_PAIR_OBJECT = jecs.ECS_PAIR_OBJECT
|
||||||
|
|
||||||
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
||||||
local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
||||||
local ok, err: string? = pcall(fn, ...)
|
local ok, err: string? = pcall(fn, ...)
|
||||||
|
|
||||||
if not CHECK(not ok, 2) then
|
if not CHECK(not ok, 2) then
|
||||||
local i = string.find(err :: string, " ")
|
local i = string.find(err :: string, " ")
|
||||||
assert(i)
|
assert(i)
|
||||||
local msg = string.sub(err :: string, i+1)
|
local msg = string.sub(err :: string, i + 1)
|
||||||
CHECK(msg == s, 2)
|
CHECK(msg == s, 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local N = 10
|
local N = 10
|
||||||
|
|
||||||
TEST("world", function()
|
TEST("world", function()
|
||||||
do CASE "should be iterable"
|
do
|
||||||
local world = jecs.World.new()
|
CASE("should be iterable")
|
||||||
local A = world:component()
|
local world = jecs.World.new()
|
||||||
local B = world:component()
|
local A = world:component()
|
||||||
local eA = world:entity()
|
local B = world:component()
|
||||||
world:set(eA, A, true)
|
local eA = world:entity()
|
||||||
local eB = world:entity()
|
world:set(eA, A, true)
|
||||||
world:set(eB, B, true)
|
local eB = world:entity()
|
||||||
local eAB = world:entity()
|
world:set(eB, B, true)
|
||||||
world:set(eAB, A, true)
|
local eAB = world:entity()
|
||||||
world:set(eAB, B, true)
|
world:set(eAB, A, true)
|
||||||
|
world:set(eAB, B, true)
|
||||||
|
|
||||||
local count = 0
|
local count = 0
|
||||||
for id, data in world do
|
for id, data in world do
|
||||||
count += 1
|
count += 1
|
||||||
if id == eA then
|
if id == eA then
|
||||||
CHECK(data[A] == true)
|
CHECK(data[A] == true)
|
||||||
CHECK(data[B] == nil)
|
CHECK(data[B] == nil)
|
||||||
elseif id == eB then
|
elseif id == eB then
|
||||||
CHECK(data[A] == nil)
|
CHECK(data[A] == nil)
|
||||||
CHECK(data[B] == true)
|
CHECK(data[B] == true)
|
||||||
elseif id == eAB then
|
elseif id == eAB then
|
||||||
CHECK(data[A] == true)
|
CHECK(data[A] == true)
|
||||||
CHECK(data[B] == true)
|
CHECK(data[B] == true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- components are registered in the entity index as well
|
-- components are registered in the entity index as well
|
||||||
-- so this test has to add 2 to account for them
|
-- so this test has to add 2 to account for them
|
||||||
CHECK(count == 3 + 2)
|
CHECK(count == 3 + 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should query all matching entities"
|
do
|
||||||
local world = jecs.World.new()
|
CASE("should query all matching entities")
|
||||||
local A = world:component()
|
local world = jecs.World.new()
|
||||||
local B = world:component()
|
local A = world:component()
|
||||||
|
local B = world:component()
|
||||||
|
|
||||||
local entities = {}
|
local entities = {}
|
||||||
for i = 1, N do
|
for i = 1, N do
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
|
|
||||||
world:set(id, A, true)
|
world:set(id, A, true)
|
||||||
if i > 5 then world:set(id, B, true) end
|
if i > 5 then
|
||||||
entities[i] = id
|
world:set(id, B, true)
|
||||||
end
|
end
|
||||||
|
entities[i] = id
|
||||||
|
end
|
||||||
|
|
||||||
for id in world:query(A) do
|
for id in world:query(A) do
|
||||||
table.remove(entities, CHECK(table.find(entities, id)))
|
table.remove(entities, CHECK(table.find(entities, id)))
|
||||||
end
|
end
|
||||||
|
|
||||||
CHECK(#entities == 0)
|
CHECK(#entities == 0)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
do
|
||||||
|
CASE("should query all matching entities when irrelevant component is removed")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
local A = world:component()
|
||||||
|
local B = world:component()
|
||||||
|
local C = world:component()
|
||||||
|
|
||||||
do CASE "should query all matching entities when irrelevant component is removed"
|
local entities = {}
|
||||||
local world = jecs.World.new()
|
for i = 1, N do
|
||||||
local A = world:component()
|
local id = world:entity()
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
|
|
||||||
local entities = {}
|
-- specifically put them in disorder to track regression
|
||||||
for i = 1, N do
|
-- https://github.com/Ukendio/jecs/pull/15
|
||||||
local id = world:entity()
|
world:set(id, B, true)
|
||||||
|
world:set(id, A, true)
|
||||||
|
if i > 5 then
|
||||||
|
world:remove(id, B)
|
||||||
|
end
|
||||||
|
entities[i] = id
|
||||||
|
end
|
||||||
|
|
||||||
-- specifically put them in disorder to track regression
|
local added = 0
|
||||||
-- https://github.com/Ukendio/jecs/pull/15
|
for id in world:query(A) do
|
||||||
world:set(id, B, true)
|
added += 1
|
||||||
world:set(id, A, true)
|
table.remove(entities, CHECK(table.find(entities, id)))
|
||||||
if i > 5 then world:remove(id, B) end
|
end
|
||||||
entities[i] = id
|
|
||||||
end
|
|
||||||
|
|
||||||
local added = 0
|
CHECK(added == N)
|
||||||
for id in world:query(A) do
|
end
|
||||||
added += 1
|
|
||||||
table.remove(entities, CHECK(table.find(entities, id)))
|
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(added == N)
|
do
|
||||||
end
|
CASE("should query all entities without B")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
local A = world:component()
|
||||||
|
local B = world:component()
|
||||||
|
|
||||||
do CASE "should query all entities without B"
|
local entities = {}
|
||||||
local world = jecs.World.new()
|
for i = 1, N do
|
||||||
local A = world:component()
|
local id = world:entity()
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local entities = {}
|
world:set(id, A, true)
|
||||||
for i = 1, N do
|
if i < 5 then
|
||||||
local id = world:entity()
|
entities[i] = id
|
||||||
|
else
|
||||||
|
world:set(id, B, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
world:set(id, A, true)
|
for id in world:query(A):without(B) do
|
||||||
if i < 5 then
|
table.remove(entities, CHECK(table.find(entities, id)))
|
||||||
entities[i] = id
|
end
|
||||||
else
|
|
||||||
world:set(id, B, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
for id in world:query(A):without(B) do
|
CHECK(#entities == 0)
|
||||||
table.remove(entities, CHECK(table.find(entities, id)))
|
end
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(#entities == 0)
|
do
|
||||||
|
CASE("should allow setting components in arbitrary order")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
|
||||||
end
|
local Health = world:entity()
|
||||||
|
local Poison = world:component()
|
||||||
|
|
||||||
do CASE "should allow setting components in arbitrary order"
|
local id = world:entity()
|
||||||
local world = jecs.World.new()
|
world:set(id, Poison, 5)
|
||||||
|
world:set(id, Health, 50)
|
||||||
|
|
||||||
local Health = world:entity()
|
CHECK(world:get(id, Poison) == 5)
|
||||||
local Poison = world:component()
|
end
|
||||||
|
|
||||||
local id = world:entity()
|
do
|
||||||
world:set(id, Poison, 5)
|
CASE("should allow deleting components")
|
||||||
world:set(id, Health, 50)
|
local world = jecs.World.new()
|
||||||
|
|
||||||
CHECK(world:get(id, Poison) == 5)
|
local Health = world:entity()
|
||||||
end
|
local Poison = world:component()
|
||||||
|
|
||||||
do CASE "should allow deleting components"
|
local id = world:entity()
|
||||||
local world = jecs.World.new()
|
world:set(id, Poison, 5)
|
||||||
|
world:set(id, Health, 50)
|
||||||
|
local id1 = world:entity()
|
||||||
|
world:set(id1, Poison, 500)
|
||||||
|
world:set(id1, Health, 50)
|
||||||
|
|
||||||
local Health = world:entity()
|
world:delete(id)
|
||||||
local Poison = world:component()
|
|
||||||
|
|
||||||
local id = world:entity()
|
CHECK(world:get(id, Poison) == nil)
|
||||||
world:set(id, Poison, 5)
|
CHECK(world:get(id, Health) == nil)
|
||||||
world:set(id, Health, 50)
|
CHECK(world:get(id1, Poison) == 500)
|
||||||
local id1 = world:entity()
|
CHECK(world:get(id1, Health) == 50)
|
||||||
world:set(id1, Poison, 500)
|
end
|
||||||
world:set(id1, Health, 50)
|
|
||||||
|
|
||||||
world:delete(id)
|
do
|
||||||
|
CASE("should allow remove that doesn't exist on entity")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
|
||||||
CHECK(world:get(id, Poison) == nil)
|
local Health = world:entity()
|
||||||
CHECK(world:get(id, Health) == nil)
|
local Poison = world:component()
|
||||||
CHECK(world:get(id1, Poison) == 500)
|
|
||||||
CHECK(world:get(id1, Health) == 50)
|
|
||||||
|
|
||||||
end
|
local id = world:entity()
|
||||||
|
world:set(id, Health, 50)
|
||||||
|
world:remove(id, Poison)
|
||||||
|
|
||||||
do CASE "should allow remove that doesn't exist on entity"
|
CHECK(world:get(id, Poison) == nil)
|
||||||
local world = jecs.World.new()
|
CHECK(world:get(id, Health) == 50)
|
||||||
|
end
|
||||||
|
|
||||||
local Health = world:entity()
|
do
|
||||||
local Poison = world:component()
|
CASE("should increment generation")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
local e = world:entity()
|
||||||
|
CHECK(ECS_ID(e) == 1 + jecs.Rest)
|
||||||
|
CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e)
|
||||||
|
CHECK(ECS_GENERATION(e) == 0) -- 0
|
||||||
|
e = ECS_GENERATION_INC(e)
|
||||||
|
CHECK(ECS_GENERATION(e) == 1) -- 1
|
||||||
|
end
|
||||||
|
|
||||||
local id = world:entity()
|
do
|
||||||
world:set(id, Health, 50)
|
CASE("should get alive from index in the dense array")
|
||||||
world:remove(id, Poison)
|
local world = jecs.World.new()
|
||||||
|
local _e = world:entity()
|
||||||
|
local e2 = world:entity()
|
||||||
|
local e3 = world:entity()
|
||||||
|
|
||||||
CHECK(world:get(id, Poison) == nil)
|
CHECK(IS_PAIR(world:entity()) == false)
|
||||||
CHECK(world:get(id, Health) == 50)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should increment generation"
|
local pair = ECS_PAIR(e2, e3)
|
||||||
local world = jecs.World.new()
|
CHECK(IS_PAIR(pair) == true)
|
||||||
local e = world:entity()
|
CHECK(ECS_PAIR_RELATION(world.entityIndex, pair) == e2)
|
||||||
CHECK(ECS_ID(e) == 1 + jecs.Rest)
|
CHECK(ECS_PAIR_OBJECT(world.entityIndex, pair) == e3)
|
||||||
CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e)
|
end
|
||||||
CHECK(ECS_GENERATION(e) == 0) -- 0
|
|
||||||
e = ECS_GENERATION_INC(e)
|
|
||||||
CHECK(ECS_GENERATION(e) == 1) -- 1
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should get alive from index in the dense array"
|
do
|
||||||
local world = jecs.World.new()
|
CASE("should allow querying for relations")
|
||||||
local _e = world:entity()
|
local world = jecs.World.new()
|
||||||
local e2 = world:entity()
|
local Eats = world:entity()
|
||||||
local e3 = world:entity()
|
local Apples = world:entity()
|
||||||
|
local bob = world:entity()
|
||||||
|
|
||||||
CHECK(IS_PAIR(world:entity()) == false)
|
world:set(bob, ECS_PAIR(Eats, Apples), true)
|
||||||
|
for e, bool in world:query(ECS_PAIR(Eats, Apples)) do
|
||||||
|
CHECK(e == bob)
|
||||||
|
CHECK(bool)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local pair = ECS_PAIR(e2, e3)
|
do
|
||||||
CHECK(IS_PAIR(pair) == true)
|
CASE("should allow wildcards in queries")
|
||||||
CHECK(ECS_PAIR_RELATION(world.entityIndex, pair) == e2)
|
local world = jecs.World.new()
|
||||||
CHECK(ECS_PAIR_OBJECT(world.entityIndex, pair) == e3)
|
local Eats = world:entity()
|
||||||
end
|
local Apples = world:entity()
|
||||||
|
local bob = world:entity()
|
||||||
|
|
||||||
do CASE "should allow querying for relations"
|
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||||
local world = jecs.World.new()
|
|
||||||
local Eats = world:entity()
|
|
||||||
local Apples = world:entity()
|
|
||||||
local bob = world:entity()
|
|
||||||
|
|
||||||
world:set(bob, ECS_PAIR(Eats, Apples), true)
|
|
||||||
for e, bool in world:query(ECS_PAIR(Eats, Apples)) do
|
|
||||||
CHECK(e == bob)
|
|
||||||
CHECK(bool)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should allow wildcards in queries"
|
|
||||||
local world = jecs.World.new()
|
|
||||||
local Eats = world:entity()
|
|
||||||
local Apples = world:entity()
|
|
||||||
local bob = world:entity()
|
|
||||||
|
|
||||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
|
||||||
|
|
||||||
local w = jecs.Wildcard
|
|
||||||
for e, data in world:query(ECS_PAIR(Eats, w)) do
|
|
||||||
CHECK(e == bob)
|
|
||||||
CHECK(data == "bob eats apples")
|
|
||||||
end
|
|
||||||
for e, data in world:query(ECS_PAIR(w, Apples)) do
|
|
||||||
CHECK(e == bob)
|
|
||||||
CHECK(data == "bob eats apples")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should match against multiple pairs"
|
local w = jecs.Wildcard
|
||||||
local world = jecs.World.new()
|
for e, data in world:query(ECS_PAIR(Eats, w)) do
|
||||||
local Eats = world:entity()
|
CHECK(e == bob)
|
||||||
local Apples = world:entity()
|
CHECK(data == "bob eats apples")
|
||||||
local Oranges =world:entity()
|
end
|
||||||
local bob = world:entity()
|
for e, data in world:query(ECS_PAIR(w, Apples)) do
|
||||||
local alice = world:entity()
|
CHECK(e == bob)
|
||||||
|
CHECK(data == "bob eats apples")
|
||||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
end
|
||||||
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
|
end
|
||||||
|
|
||||||
local w = jecs.Wildcard
|
|
||||||
local count = 0
|
|
||||||
for e, data in world:query(ECS_PAIR(Eats, w)) do
|
|
||||||
count += 1
|
|
||||||
if e == bob then
|
|
||||||
CHECK(data == "bob eats apples")
|
|
||||||
else
|
|
||||||
CHECK(data == "alice eats oranges")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(count == 2)
|
do
|
||||||
count = 0
|
CASE("should match against multiple pairs")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
local Eats = world:entity()
|
||||||
|
local Apples = world:entity()
|
||||||
|
local Oranges = world:entity()
|
||||||
|
local bob = world:entity()
|
||||||
|
local alice = world:entity()
|
||||||
|
|
||||||
for e, data in world:query(ECS_PAIR(w, Apples)) do
|
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||||
count += 1
|
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
|
||||||
CHECK(data == "bob eats apples")
|
|
||||||
end
|
|
||||||
CHECK(count == 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should only relate alive entities"
|
local w = jecs.Wildcard
|
||||||
|
local count = 0
|
||||||
local world = jecs.World.new()
|
for e, data in world:query(ECS_PAIR(Eats, w)) do
|
||||||
local Eats = world:entity()
|
count += 1
|
||||||
local Apples = world:entity()
|
if e == bob then
|
||||||
local Oranges = world:entity()
|
CHECK(data == "bob eats apples")
|
||||||
local bob = world:entity()
|
else
|
||||||
local alice = world:entity()
|
CHECK(data == "alice eats oranges")
|
||||||
|
end
|
||||||
world:set(bob, Apples, "apples")
|
end
|
||||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
|
||||||
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
|
|
||||||
|
|
||||||
world:delete(Apples)
|
CHECK(count == 2)
|
||||||
local Wildcard = jecs.Wildcard
|
count = 0
|
||||||
|
|
||||||
local count = 0
|
|
||||||
for _, data in world:query(ECS_PAIR(Wildcard, Apples)) do
|
|
||||||
count += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
world:delete(ECS_PAIR(Eats, Apples))
|
for e, data in world:query(ECS_PAIR(w, Apples)) do
|
||||||
|
count += 1
|
||||||
CHECK(count == 0)
|
CHECK(data == "bob eats apples")
|
||||||
CHECK(world:get(bob, ECS_PAIR(Eats, Apples)) == nil)
|
end
|
||||||
end
|
CHECK(count == 1)
|
||||||
|
end
|
||||||
|
|
||||||
do CASE "should error when setting invalid pair"
|
do
|
||||||
local world = jecs.World.new()
|
CASE("should only relate alive entities")
|
||||||
local Eats = world:entity()
|
|
||||||
local Apples = world:entity()
|
|
||||||
local bob = world:entity()
|
|
||||||
|
|
||||||
world:delete(Apples)
|
local world = jecs.World.new()
|
||||||
|
local Eats = world:entity()
|
||||||
|
local Apples = world:entity()
|
||||||
|
local Oranges = world:entity()
|
||||||
|
local bob = world:entity()
|
||||||
|
local alice = world:entity()
|
||||||
|
|
||||||
CHECK_NO_ERR("Apples should be dead", function()
|
world:set(bob, Apples, "apples")
|
||||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||||
end)
|
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "should find target for ChildOf"
|
world:delete(Apples)
|
||||||
local world = jecs.World.new()
|
local Wildcard = jecs.Wildcard
|
||||||
|
|
||||||
local ChildOf = world:component()
|
local count = 0
|
||||||
local Name = world:component()
|
for _, data in world:query(ECS_PAIR(Wildcard, Apples)) do
|
||||||
|
count += 1
|
||||||
|
end
|
||||||
|
|
||||||
local function parent(entity)
|
world:delete(ECS_PAIR(Eats, Apples))
|
||||||
return world:target(entity, ChildOf)
|
|
||||||
end
|
|
||||||
|
|
||||||
local bob = world:entity()
|
CHECK(count == 0)
|
||||||
local alice = world:entity()
|
CHECK(world:get(bob, ECS_PAIR(Eats, Apples)) == nil)
|
||||||
local sara = world:entity()
|
end
|
||||||
|
|
||||||
world:add(bob, ECS_PAIR(ChildOf, alice))
|
|
||||||
world:set(bob, Name, "bob")
|
|
||||||
world:add(sara, ECS_PAIR(ChildOf, alice))
|
|
||||||
world:set(sara, Name, "sara")
|
|
||||||
CHECK(parent(bob) == alice) -- O(1)
|
|
||||||
|
|
||||||
local count = 0
|
do
|
||||||
for _, name in world:query(Name, ECS_PAIR(ChildOf, alice)) do
|
CASE("should error when setting invalid pair")
|
||||||
print(name)
|
local world = jecs.World.new()
|
||||||
count += 1
|
local Eats = world:entity()
|
||||||
end
|
local Apples = world:entity()
|
||||||
CHECK(count == 2)
|
local bob = world:entity()
|
||||||
end
|
|
||||||
|
world:delete(Apples)
|
||||||
|
|
||||||
|
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
CASE("should find target for ChildOf")
|
||||||
|
local world = jecs.World.new()
|
||||||
|
|
||||||
|
local ChildOf = world:component()
|
||||||
|
local Name = world:component()
|
||||||
|
|
||||||
|
local function parent(entity)
|
||||||
|
return world:target(entity, ChildOf)
|
||||||
|
end
|
||||||
|
|
||||||
|
local bob = world:entity()
|
||||||
|
local alice = world:entity()
|
||||||
|
local sara = world:entity()
|
||||||
|
|
||||||
|
world:add(bob, ECS_PAIR(ChildOf, alice))
|
||||||
|
world:set(bob, Name, "bob")
|
||||||
|
world:add(sara, ECS_PAIR(ChildOf, alice))
|
||||||
|
world:set(sara, Name, "sara")
|
||||||
|
CHECK(parent(bob) == alice) -- O(1)
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
for _, name in world:query(Name, ECS_PAIR(ChildOf, alice)) do
|
||||||
|
print(name)
|
||||||
|
count += 1
|
||||||
|
end
|
||||||
|
CHECK(count == 2)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
FINISH()
|
FINISH()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue