Reorganize repo
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.luau text eol=lf
|
47
.github/ISSUE_TEMPLATE/BUG-REPORT.md
vendored
|
@ -1,22 +1,25 @@
|
||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: File a bug report for any behavior that you believe is unintentional or problematic
|
about: File a bug report for any behavior that you believe is unintentional or problematic
|
||||||
title: "[BUG]"
|
title: ""
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
---
|
||||||
---
|
|
||||||
|
## Describe the bug
|
||||||
## Describe the bug
|
|
||||||
Put a clear and concise description of what the bug is. This should be short and to the point, not to exceed more than a paragraph. Put the details inside your reproduction steps.
|
Put a clear and concise description of what the bug is. This should be short and to the point, not to exceed more than a paragraph. Put the details inside your reproduction steps.
|
||||||
|
|
||||||
## Reproduction
|
## Reproduction
|
||||||
Make an easy-to-follow guide on how to reproduce it. Does it happen all the time? Will specific features affect reproduction? All these questions should be answered for a good issue.
|
|
||||||
|
Make an easy-to-follow guide on how to reproduce it. Does it happen all the time? Will specific features affect reproduction? All these questions should be answered for a good issue.
|
||||||
This is a good place to put rbxl files or scripts that help explain your reproduction steps.
|
|
||||||
|
This is a good place to put rbxl files or scripts that help explain your reproduction steps.
|
||||||
## Expected Behavior
|
|
||||||
What you expect to happen
|
## Expected Behavior
|
||||||
|
|
||||||
## Actual Behavior
|
What you expect to happen
|
||||||
What actually happens
|
|
||||||
|
## Actual Behavior
|
||||||
|
|
||||||
|
What actually happens
|
||||||
|
|
9
.github/ISSUE_TEMPLATE/DOCUMENTATION.md
vendored
|
@ -1,14 +1,15 @@
|
||||||
---
|
---
|
||||||
name: Documentation
|
name: Documentation
|
||||||
about: Open an issue to add, change, or otherwise modify any part of the documentation.
|
about: Open an issue to add, change, or otherwise modify any part of the documentation.
|
||||||
title: "[DOCS]"
|
title: ""
|
||||||
labels: documentation
|
labels: documentation
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Which Sections Does This Issue Cover?
|
## Which Sections Does This Issue Cover?
|
||||||
|
|
||||||
[Put sections (e.g. Query Concepts), page links, etc as necessary]
|
[Put sections (e.g. Query Concepts), page links, etc as necessary]
|
||||||
|
|
||||||
## What Needs To Change?
|
## What Needs To Change?
|
||||||
What specifically needs to change and what suggestions do you have to change it?
|
|
||||||
|
What specifically needs to change and what suggestions do you have to change it?
|
||||||
|
|
54
.github/ISSUE_TEMPLATE/FEATURE-REQUEST.md
vendored
|
@ -1,27 +1,27 @@
|
||||||
---
|
---
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: File a feature request for something you believe should be added to Jecs
|
about: File a feature request for something you believe should be added to Jecs
|
||||||
title: "[FEATURE]"
|
title: ""
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
---
|
||||||
---
|
|
||||||
|
## Describe your Feature
|
||||||
## Describe your Feature
|
|
||||||
|
You should explain your feature here, and the motivation for why you want it.
|
||||||
You should explain your feature here, and the motivation for why you want it.
|
|
||||||
|
## Implementation
|
||||||
## Implementation
|
|
||||||
|
Explain how you would implement your feature here. Provide relevant API examples and such here (if applicable).
|
||||||
Explain how you would implement your feature here. Provide relevant API examples and such here (if applicable).
|
|
||||||
|
## Alternatives
|
||||||
## Alternatives
|
|
||||||
|
What other alternative implementations or otherwise relevant information is important to why you decided to go with this specific implementation?
|
||||||
What other alternative implementations or otherwise relevant information is important to why you decided to go with this specific implementation?
|
|
||||||
|
## Considerations
|
||||||
## Considerations
|
|
||||||
|
Some questions that need to be answered include the following:
|
||||||
Some questions that need to be answered include the following:
|
|
||||||
- Will old code break in response to this feature?
|
- Will old code break in response to this feature?
|
||||||
- What are the performance impacts with this feature (if any)?
|
- What are the performance impacts with this feature (if any)?
|
||||||
- How is it useful to include?
|
- How is it useful to include?
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
|
syntax = "All"
|
||||||
column_width = 120
|
column_width = 120
|
||||||
line_endings = "Unix"
|
line_endings = "Unix"
|
||||||
indent_type = "Tabs"
|
indent_type = "Tabs"
|
||||||
indent_width = 4
|
indent_width = 4
|
||||||
quote_style = "ForceDouble"
|
quote_style = "AutoPreferDouble"
|
||||||
call_parentheses = "Always"
|
call_parentheses = "Always"
|
||||||
|
space_after_function_names = "Never"
|
||||||
collapse_simple_statement = "Never"
|
collapse_simple_statement = "Never"
|
||||||
syntax = "Luau"
|
|
||||||
|
|
||||||
[sort_requires]
|
|
||||||
enabled = true
|
|
|
@ -1,5 +1,5 @@
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local mirror = require("../mirror/init")
|
local mirror = require("@mirror")
|
||||||
|
|
||||||
type i53 = number
|
type i53 = number
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
--!native
|
--!native
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||||
local jecs = require(ReplicatedStorage.Lib)
|
local jecs = require(ReplicatedStorage.Lib)
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
local ecs = jecs.World.new()
|
local ecs = jecs.World.new()
|
||||||
local mirror = require(ReplicatedStorage.mirror)
|
local mirror = require(ReplicatedStorage.mirror)
|
||||||
local mcs = mirror.World.new()
|
local mcs = mirror.World.new()
|
||||||
|
|
||||||
local C1 = ecs:component()
|
local C1 = ecs:component()
|
||||||
local C2 = ecs:entity()
|
local C2 = ecs:entity()
|
||||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
local C3 = ecs:entity()
|
local C3 = ecs:entity()
|
||||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
local C4 = ecs:entity()
|
local C4 = ecs:entity()
|
||||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
local E1 = mcs:component()
|
local E1 = mcs:component()
|
||||||
local E2 = mcs:entity()
|
local E2 = mcs:entity()
|
||||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
local E3 = mcs:entity()
|
local E3 = mcs:entity()
|
||||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
local E4 = mcs:entity()
|
local E4 = mcs:entity()
|
||||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ParameterGenerator = function()
|
ParameterGenerator = function()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Functions = {
|
Functions = {
|
||||||
Mirror = function()
|
Mirror = function()
|
||||||
local m = mcs:entity()
|
local m = mcs:entity()
|
||||||
for i = 1, 100 do
|
for i = 1, 100 do
|
||||||
mcs:add(m, E3)
|
mcs:add(m, E3)
|
||||||
mcs:remove(m, E3)
|
mcs:remove(m, E3)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Jecs = function()
|
Jecs = function()
|
||||||
local j = ecs:entity()
|
local j = ecs:entity()
|
||||||
for i = 1, 100 do
|
for i = 1, 100 do
|
||||||
ecs:add(j, C3)
|
ecs:add(j, C3)
|
||||||
ecs:remove(j, C3)
|
ecs:remove(j, C3)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
BIN
coverage/amber.png
Normal file
After Width: | Height: | Size: 141 B |
1
coverage/cmd_line
Normal file
|
@ -0,0 +1 @@
|
||||||
|
genhtml coverage.out --output-directory=coverage --synthesize-missing --ignore-errors source
|
BIN
coverage/emerald.png
Normal file
After Width: | Height: | Size: 141 B |
1073
coverage/gcov.css
Normal file
BIN
coverage/glass.png
Normal file
After Width: | Height: | Size: 167 B |
129
coverage/index-sort-f.html
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>LCOV - coverage.out</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gcov.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table width="100%" border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td class="title">LCOV - code coverage report</td></tr>
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="100%">
|
||||||
|
<table cellpadding=1 border=0 width="100%">
|
||||||
|
<tr>
|
||||||
|
<td width="10%" class="headerItem">Current view:</td>
|
||||||
|
<td width="10%" class="headerValue">top level</td>
|
||||||
|
<td width="5%"></td>
|
||||||
|
<td width="5%"></td>
|
||||||
|
<td width="5%" class="headerCovTableHead">Coverage</td>
|
||||||
|
<td width="5%" class="headerCovTableHead" title="Covered + Uncovered code">Total</td>
|
||||||
|
<td width="5%" class="headerCovTableHead" title="Exercised code only">Hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="headerItem">Test:</td>
|
||||||
|
<td class="headerValue">coverage.out</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="headerItem">Lines:</td>
|
||||||
|
<td class="headerCovTableEntryMed">80.3 %</td>
|
||||||
|
<td class="headerCovTableEntry">3222</td>
|
||||||
|
<td class="headerCovTableEntry">2587</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="headerItem">Test Date:</td>
|
||||||
|
<td class="headerValue">2025-03-27 02:15:11</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="headerItem">Functions:</td>
|
||||||
|
<td class="headerCovTableEntryLo">65.3 %</td>
|
||||||
|
<td class="headerCovTableEntry">219</td>
|
||||||
|
<td class="headerCovTableEntry">143</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<table width="80%" cellpadding=1 cellspacing=1 border=0>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="40%"><br></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tableHead" rowspan=2>Directory <span title="Click to sort table by file name" class="tableHeadSort"><a href="index.html"><img src="updown.png" width=10 height=14 alt="Sort by file name" title="Click to sort table by file name" border=0></a></span></td>
|
||||||
|
<td class="tableHead" colspan=4>Line Coverage <span title="Click to sort table by line coverage" class="tableHeadSort"><a href="index-sort-l.html"><img src="updown.png" width=10 height=14 alt="Sort by line coverage" title="Click to sort table by line coverage" border=0></a></span></td>
|
||||||
|
<td class="tableHead" colspan=3>Function Coverage <span title="Click to sort table by function coverage" class="tableHeadSort"><img src="glass.png" width=10 height=14 alt="Sort by function coverage" title="Click to sort table by function coverage" border=0></span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableHead" colspan=2> Rate</td>
|
||||||
|
<td class="tableHead"> Total</td>
|
||||||
|
<td class="tableHead"> Hit</td>
|
||||||
|
<td class="tableHead"> Rate</td>
|
||||||
|
<td class="tableHead"> Total</td>
|
||||||
|
<td class="tableHead"> Hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="jecs/C:/Users/Marcus/Documents/packages/jecs/index.html">jecs/C:/Users/Marcus/Documents/packages/jecs</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="ruby.png" width=72 height=10 alt="71.7%"><img src="snow.png" width=28 height=10 alt="71.7%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerLo">71.7 %</td>
|
||||||
|
<td class="coverNumDflt">1487</td>
|
||||||
|
<td class="coverNumDflt">1066</td>
|
||||||
|
<td class="coverPerLo">53.6 %</td>
|
||||||
|
<td class="coverNumDflt">97</td>
|
||||||
|
<td class="coverNumDflt">52</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="jecs/tools/C:/Users/Marcus/Documents/packages/jecs/tools/index.html">jecs/tools/C:/Users/Marcus/Documents/packages/jecs/tools</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="ruby.png" width=63 height=10 alt="63.0%"><img src="snow.png" width=37 height=10 alt="63.0%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerLo">63.0 %</td>
|
||||||
|
<td class="coverNumDflt">508</td>
|
||||||
|
<td class="coverNumDflt">320</td>
|
||||||
|
<td class="coverPerLo">63.6 %</td>
|
||||||
|
<td class="coverNumDflt">55</td>
|
||||||
|
<td class="coverNumDflt">35</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="mnt/c/Users/Marcus/Documents/packages/jecs/test/test/index.html">/mnt/c/Users/Marcus/Documents/packages/jecs/test/test</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="emerald.png" width=98 height=10 alt="97.9%"><img src="snow.png" width=2 height=10 alt="97.9%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerHi">97.9 %</td>
|
||||||
|
<td class="coverNumDflt">1227</td>
|
||||||
|
<td class="coverNumDflt">1201</td>
|
||||||
|
<td class="coverPerMed">83.6 %</td>
|
||||||
|
<td class="coverNumDflt">67</td>
|
||||||
|
<td class="coverNumDflt">56</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</center>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%" border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
<tr><td class="versionInfo">Generated by: <a href="https://github.com//linux-test-project/lcov">LCOV version 2.0-1</a></td></tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
129
coverage/index-sort-l.html
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>LCOV - coverage.out</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gcov.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table width="100%" border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td class="title">LCOV - code coverage report</td></tr>
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="100%">
|
||||||
|
<table cellpadding=1 border=0 width="100%">
|
||||||
|
<tr>
|
||||||
|
<td width="10%" class="headerItem">Current view:</td>
|
||||||
|
<td width="10%" class="headerValue">top level</td>
|
||||||
|
<td width="5%"></td>
|
||||||
|
<td width="5%"></td>
|
||||||
|
<td width="5%" class="headerCovTableHead">Coverage</td>
|
||||||
|
<td width="5%" class="headerCovTableHead" title="Covered + Uncovered code">Total</td>
|
||||||
|
<td width="5%" class="headerCovTableHead" title="Exercised code only">Hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="headerItem">Test:</td>
|
||||||
|
<td class="headerValue">coverage.out</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="headerItem">Lines:</td>
|
||||||
|
<td class="headerCovTableEntryMed">80.3 %</td>
|
||||||
|
<td class="headerCovTableEntry">3222</td>
|
||||||
|
<td class="headerCovTableEntry">2587</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="headerItem">Test Date:</td>
|
||||||
|
<td class="headerValue">2025-03-27 02:15:11</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="headerItem">Functions:</td>
|
||||||
|
<td class="headerCovTableEntryLo">65.3 %</td>
|
||||||
|
<td class="headerCovTableEntry">219</td>
|
||||||
|
<td class="headerCovTableEntry">143</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<table width="80%" cellpadding=1 cellspacing=1 border=0>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="40%"><br></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tableHead" rowspan=2>Directory <span title="Click to sort table by file name" class="tableHeadSort"><a href="index.html"><img src="updown.png" width=10 height=14 alt="Sort by file name" title="Click to sort table by file name" border=0></a></span></td>
|
||||||
|
<td class="tableHead" colspan=4>Line Coverage <span title="Click to sort table by line coverage" class="tableHeadSort"><img src="glass.png" width=10 height=14 alt="Sort by line coverage" title="Click to sort table by line coverage" border=0></span></td>
|
||||||
|
<td class="tableHead" colspan=3>Function Coverage <span title="Click to sort table by function coverage" class="tableHeadSort"><a href="index-sort-f.html"><img src="updown.png" width=10 height=14 alt="Sort by function coverage" title="Click to sort table by function coverage" border=0></a></span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableHead" colspan=2> Rate</td>
|
||||||
|
<td class="tableHead"> Total</td>
|
||||||
|
<td class="tableHead"> Hit</td>
|
||||||
|
<td class="tableHead"> Rate</td>
|
||||||
|
<td class="tableHead"> Total</td>
|
||||||
|
<td class="tableHead"> Hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="jecs/tools/C:/Users/Marcus/Documents/packages/jecs/tools/index.html">jecs/tools/C:/Users/Marcus/Documents/packages/jecs/tools</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="ruby.png" width=63 height=10 alt="63.0%"><img src="snow.png" width=37 height=10 alt="63.0%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerLo">63.0 %</td>
|
||||||
|
<td class="coverNumDflt">508</td>
|
||||||
|
<td class="coverNumDflt">320</td>
|
||||||
|
<td class="coverPerLo">63.6 %</td>
|
||||||
|
<td class="coverNumDflt">55</td>
|
||||||
|
<td class="coverNumDflt">35</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="jecs/C:/Users/Marcus/Documents/packages/jecs/index.html">jecs/C:/Users/Marcus/Documents/packages/jecs</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="ruby.png" width=72 height=10 alt="71.7%"><img src="snow.png" width=28 height=10 alt="71.7%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerLo">71.7 %</td>
|
||||||
|
<td class="coverNumDflt">1487</td>
|
||||||
|
<td class="coverNumDflt">1066</td>
|
||||||
|
<td class="coverPerLo">53.6 %</td>
|
||||||
|
<td class="coverNumDflt">97</td>
|
||||||
|
<td class="coverNumDflt">52</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="mnt/c/Users/Marcus/Documents/packages/jecs/test/test/index.html">/mnt/c/Users/Marcus/Documents/packages/jecs/test/test</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="emerald.png" width=98 height=10 alt="97.9%"><img src="snow.png" width=2 height=10 alt="97.9%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerHi">97.9 %</td>
|
||||||
|
<td class="coverNumDflt">1227</td>
|
||||||
|
<td class="coverNumDflt">1201</td>
|
||||||
|
<td class="coverPerMed">83.6 %</td>
|
||||||
|
<td class="coverNumDflt">67</td>
|
||||||
|
<td class="coverNumDflt">56</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</center>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%" border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
<tr><td class="versionInfo">Generated by: <a href="https://github.com//linux-test-project/lcov">LCOV version 2.0-1</a></td></tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
129
coverage/index.html
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>LCOV - coverage.out</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gcov.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table width="100%" border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td class="title">LCOV - code coverage report</td></tr>
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="100%">
|
||||||
|
<table cellpadding=1 border=0 width="100%">
|
||||||
|
<tr>
|
||||||
|
<td width="10%" class="headerItem">Current view:</td>
|
||||||
|
<td width="10%" class="headerValue">top level</td>
|
||||||
|
<td width="5%"></td>
|
||||||
|
<td width="5%"></td>
|
||||||
|
<td width="5%" class="headerCovTableHead">Coverage</td>
|
||||||
|
<td width="5%" class="headerCovTableHead" title="Covered + Uncovered code">Total</td>
|
||||||
|
<td width="5%" class="headerCovTableHead" title="Exercised code only">Hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="headerItem">Test:</td>
|
||||||
|
<td class="headerValue">coverage.out</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="headerItem">Lines:</td>
|
||||||
|
<td class="headerCovTableEntryMed">80.3 %</td>
|
||||||
|
<td class="headerCovTableEntry">3222</td>
|
||||||
|
<td class="headerCovTableEntry">2587</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="headerItem">Test Date:</td>
|
||||||
|
<td class="headerValue">2025-03-27 02:15:11</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="headerItem">Functions:</td>
|
||||||
|
<td class="headerCovTableEntryLo">65.3 %</td>
|
||||||
|
<td class="headerCovTableEntry">219</td>
|
||||||
|
<td class="headerCovTableEntry">143</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<table width="80%" cellpadding=1 cellspacing=1 border=0>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="40%"><br></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
<td width="8%"></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tableHead" rowspan=2>Directory <span title="Click to sort table by file name" class="tableHeadSort"><img src="glass.png" width=10 height=14 alt="Sort by file name" title="Click to sort table by file name" border=0></span></td>
|
||||||
|
<td class="tableHead" colspan=4>Line Coverage <span title="Click to sort table by line coverage" class="tableHeadSort"><a href="index-sort-l.html"><img src="updown.png" width=10 height=14 alt="Sort by line coverage" title="Click to sort table by line coverage" border=0></a></span></td>
|
||||||
|
<td class="tableHead" colspan=3>Function Coverage <span title="Click to sort table by function coverage" class="tableHeadSort"><a href="index-sort-f.html"><img src="updown.png" width=10 height=14 alt="Sort by function coverage" title="Click to sort table by function coverage" border=0></a></span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableHead" colspan=2> Rate</td>
|
||||||
|
<td class="tableHead"> Total</td>
|
||||||
|
<td class="tableHead"> Hit</td>
|
||||||
|
<td class="tableHead"> Rate</td>
|
||||||
|
<td class="tableHead"> Total</td>
|
||||||
|
<td class="tableHead"> Hit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="jecs/C:/Users/Marcus/Documents/packages/jecs/index.html">jecs/C:/Users/Marcus/Documents/packages/jecs</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="ruby.png" width=72 height=10 alt="71.7%"><img src="snow.png" width=28 height=10 alt="71.7%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerLo">71.7 %</td>
|
||||||
|
<td class="coverNumDflt">1487</td>
|
||||||
|
<td class="coverNumDflt">1066</td>
|
||||||
|
<td class="coverPerLo">53.6 %</td>
|
||||||
|
<td class="coverNumDflt">97</td>
|
||||||
|
<td class="coverNumDflt">52</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="jecs/tools/C:/Users/Marcus/Documents/packages/jecs/tools/index.html">jecs/tools/C:/Users/Marcus/Documents/packages/jecs/tools</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="ruby.png" width=63 height=10 alt="63.0%"><img src="snow.png" width=37 height=10 alt="63.0%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerLo">63.0 %</td>
|
||||||
|
<td class="coverNumDflt">508</td>
|
||||||
|
<td class="coverNumDflt">320</td>
|
||||||
|
<td class="coverPerLo">63.6 %</td>
|
||||||
|
<td class="coverNumDflt">55</td>
|
||||||
|
<td class="coverNumDflt">35</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="coverFile"><a href="mnt/c/Users/Marcus/Documents/packages/jecs/test/test/index.html">/mnt/c/Users/Marcus/Documents/packages/jecs/test/test</a></td>
|
||||||
|
<td class="coverBar" align="center">
|
||||||
|
<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="emerald.png" width=98 height=10 alt="97.9%"><img src="snow.png" width=2 height=10 alt="97.9%"></td></tr></table>
|
||||||
|
</td>
|
||||||
|
<td class="coverPerHi">97.9 %</td>
|
||||||
|
<td class="coverNumDflt">1227</td>
|
||||||
|
<td class="coverNumDflt">1201</td>
|
||||||
|
<td class="coverPerMed">83.6 %</td>
|
||||||
|
<td class="coverNumDflt">67</td>
|
||||||
|
<td class="coverNumDflt">56</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</center>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%" border=0 cellspacing=0 cellpadding=0>
|
||||||
|
<tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
|
||||||
|
<tr><td class="versionInfo">Generated by: <a href="https://github.com//linux-test-project/lcov">LCOV version 2.0-1</a></td></tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
coverage/ruby.png
Normal file
After Width: | Height: | Size: 141 B |
BIN
coverage/snow.png
Normal file
After Width: | Height: | Size: 141 B |
BIN
coverage/updown.png
Normal file
After Width: | Height: | Size: 117 B |
BIN
demo.rbxl
|
@ -1,48 +1,48 @@
|
||||||
local events = {}
|
local events = {}
|
||||||
|
|
||||||
local function trackers_invoke(event, component, entity, ...)
|
local function trackers_invoke(event, component, entity, ...)
|
||||||
local trackers = events[event][component]
|
local trackers = events[event][component]
|
||||||
if not trackers then
|
if not trackers then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, tracker in trackers do
|
for _, tracker in trackers do
|
||||||
tracker(entity, data)
|
tracker(entity, data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function trackers_init(event, component, fn)
|
local function trackers_init(event, component, fn)
|
||||||
local ob = events[event]
|
local ob = events[event]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connect = function(component, fn)
|
connect = function(component, fn)
|
||||||
local trackers = ob[component]
|
local trackers = ob[component]
|
||||||
if not trackers then
|
if not trackers then
|
||||||
trackers = {}
|
trackers = {}
|
||||||
ob[component] = trackers
|
ob[component] = trackers
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(trackers, fn)
|
table.insert(trackers, fn)
|
||||||
end,
|
end,
|
||||||
invoke = function(component, ...)
|
invoke = function(component, ...)
|
||||||
trackers_invoke(event, component, ...)
|
trackers_invoke(event, component, ...)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
return function(component, fn)
|
return function(component, fn)
|
||||||
local trackers = ob[component]
|
local trackers = ob[component]
|
||||||
if not trackers then
|
if not trackers then
|
||||||
trackers = {}
|
trackers = {}
|
||||||
ob[component] = trackers
|
ob[component] = trackers
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(trackers, fn)
|
table.insert(trackers, fn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local trackers = {
|
local trackers = {
|
||||||
emplace = trackers_init("emplace"),
|
emplace = trackers_init("emplace"),
|
||||||
add = trackers_init("added"),
|
add = trackers_init("added"),
|
||||||
remove = trackers_init("removed")
|
remove = trackers_init("removed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return trackers
|
return trackers
|
||||||
|
|
|
@ -1,67 +1,67 @@
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
--!native
|
--!native
|
||||||
--!strict
|
--!strict
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
local __ = jecs.Wildcard
|
local __ = jecs.Wildcard
|
||||||
local std = ReplicatedStorage.std
|
local std = ReplicatedStorage.std
|
||||||
|
|
||||||
local world = require(std.world)
|
local world = require(std.world)
|
||||||
|
|
||||||
local Position = world:component() :: jecs.Entity<vector>
|
local Position = world:component() :: jecs.Entity<vector>
|
||||||
local Previous = jecs.Rest
|
local Previous = jecs.Rest
|
||||||
local pre = jecs.pair(Position, Previous)
|
local pre = jecs.pair(Position, Previous)
|
||||||
|
|
||||||
local added = world
|
local added = world
|
||||||
:query(Position)
|
:query(Position)
|
||||||
:without(pre)
|
:without(pre)
|
||||||
:cached()
|
:cached()
|
||||||
local changed = world
|
local changed = world
|
||||||
:query(Position, pre)
|
:query(Position, pre)
|
||||||
:cached()
|
:cached()
|
||||||
local removed = world
|
local removed = world
|
||||||
:query(pre)
|
:query(pre)
|
||||||
:without(Position)
|
:without(Position)
|
||||||
:cached()
|
:cached()
|
||||||
|
|
||||||
local children = {}
|
local children = {}
|
||||||
for i = 1, 10 do
|
for i = 1, 10 do
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:set(e, Position, vector.create(i, i, i))
|
world:set(e, Position, vector.create(i, i, i))
|
||||||
table.insert(children, e)
|
table.insert(children, e)
|
||||||
end
|
end
|
||||||
local function flip()
|
local function flip()
|
||||||
return math.random() > 0.5
|
return math.random() > 0.5
|
||||||
end
|
end
|
||||||
local function system()
|
local function system()
|
||||||
for i, child in children do
|
for i, child in children do
|
||||||
world:set(child, Position, vector.create(i,i,i))
|
world:set(child, Position, vector.create(i,i,i))
|
||||||
end
|
end
|
||||||
for e, p in added:iter() do
|
for e, p in added:iter() do
|
||||||
world:set(e, pre, p)
|
world:set(e, pre, p)
|
||||||
end
|
end
|
||||||
for i, child in children do
|
for i, child in children do
|
||||||
if flip() then
|
if flip() then
|
||||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for e, new, old in changed:iter() do
|
for e, new, old in changed:iter() do
|
||||||
if new ~= old then
|
if new ~= old then
|
||||||
world:set(e, pre, new)
|
world:set(e, pre, new)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i, child in children do
|
for i, child in children do
|
||||||
world:remove(child, Position)
|
world:remove(child, Position)
|
||||||
end
|
end
|
||||||
|
|
||||||
for e in removed:iter() do
|
for e in removed:iter() do
|
||||||
world:remove(e, pre)
|
world:remove(e, pre)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local scheduler = require(std.scheduler)
|
local scheduler = require(std.scheduler)
|
||||||
|
|
||||||
scheduler.SYSTEM(system)
|
scheduler.SYSTEM(system)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,90 +1,90 @@
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
--!native
|
--!native
|
||||||
--!strict
|
--!strict
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
local __ = jecs.Wildcard
|
local __ = jecs.Wildcard
|
||||||
local std = ReplicatedStorage.std
|
local std = ReplicatedStorage.std
|
||||||
|
|
||||||
local world = require(std.world)
|
local world = require(std.world)
|
||||||
|
|
||||||
local Position = world:component() :: jecs.Entity<vector>
|
local Position = world:component() :: jecs.Entity<vector>
|
||||||
local Previous = jecs.Rest
|
local Previous = jecs.Rest
|
||||||
local pre = jecs.pair(Position, Previous)
|
local pre = jecs.pair(Position, Previous)
|
||||||
|
|
||||||
local added = world
|
local added = world
|
||||||
:query(Position)
|
:query(Position)
|
||||||
:without(pre)
|
:without(pre)
|
||||||
:cached()
|
:cached()
|
||||||
local changed = world
|
local changed = world
|
||||||
:query(Position, pre)
|
:query(Position, pre)
|
||||||
:cached()
|
:cached()
|
||||||
local removed = world
|
local removed = world
|
||||||
:query(pre)
|
:query(pre)
|
||||||
:without(Position)
|
:without(Position)
|
||||||
:cached()
|
:cached()
|
||||||
|
|
||||||
local children = {}
|
local children = {}
|
||||||
for i = 1, 10 do
|
for i = 1, 10 do
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:set(e, Position, vector.create(i, i, i))
|
world:set(e, Position, vector.create(i, i, i))
|
||||||
table.insert(children, e)
|
table.insert(children, e)
|
||||||
end
|
end
|
||||||
local function flip()
|
local function flip()
|
||||||
return math.random() > 0.5
|
return math.random() > 0.5
|
||||||
end
|
end
|
||||||
local entity_index = world.entity_index
|
local entity_index = world.entity_index
|
||||||
local function copy(archetypes, id)
|
local function copy(archetypes, id)
|
||||||
for _, archetype in archetypes do
|
for _, archetype in archetypes do
|
||||||
|
|
||||||
local to = jecs.archetype_traverse_add(world, pre, archetype)
|
local to = jecs.archetype_traverse_add(world, pre, archetype)
|
||||||
local columns = to.columns
|
local columns = to.columns
|
||||||
local records = to.records
|
local records = to.records
|
||||||
local old = columns[records[pre].column]
|
local old = columns[records[pre].column]
|
||||||
local new = columns[records[id].column]
|
local new = columns[records[id].column]
|
||||||
|
|
||||||
if to ~= archetype then
|
if to ~= archetype then
|
||||||
for _, entity in archetype.entities do
|
for _, entity in archetype.entities do
|
||||||
local r = jecs.entity_index_try_get_fast(entity_index, entity)
|
local r = jecs.entity_index_try_get_fast(entity_index, entity)
|
||||||
jecs.entity_move(entity_index, entity, r, to)
|
jecs.entity_move(entity_index, entity, r, to)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.move(new, 1, #new, 1, old)
|
table.move(new, 1, #new, 1, old)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function system2()
|
local function system2()
|
||||||
for i, child in children do
|
for i, child in children do
|
||||||
world:set(child, Position, vector.create(i,i,i))
|
world:set(child, Position, vector.create(i,i,i))
|
||||||
end
|
end
|
||||||
for e, p in added:iter() do
|
for e, p in added:iter() do
|
||||||
end
|
end
|
||||||
copy(added:archetypes(), Position)
|
copy(added:archetypes(), Position)
|
||||||
for i, child in children do
|
for i, child in children do
|
||||||
if flip() then
|
if flip() then
|
||||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for e, new, old in changed:iter() do
|
for e, new, old in changed:iter() do
|
||||||
if new ~= old then
|
if new ~= old then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
copy(changed:archetypes(), Position)
|
copy(changed:archetypes(), Position)
|
||||||
|
|
||||||
for i, child in children do
|
for i, child in children do
|
||||||
world:remove(child, Position)
|
world:remove(child, Position)
|
||||||
end
|
end
|
||||||
|
|
||||||
for e in removed:iter() do
|
for e in removed:iter() do
|
||||||
world:remove(e, pre)
|
world:remove(e, pre)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local scheduler = require(std.scheduler)
|
local scheduler = require(std.scheduler)
|
||||||
|
|
||||||
scheduler.SYSTEM(system2)
|
scheduler.SYSTEM(system2)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
15
jecs.luau
|
@ -52,7 +52,7 @@ export type Archetype = {
|
||||||
type ecs_record_t = {
|
type ecs_record_t = {
|
||||||
archetype: ecs_archetype_t,
|
archetype: ecs_archetype_t,
|
||||||
row: number,
|
row: number,
|
||||||
dense: i24
|
dense: i24,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ecs_id_record_t = {
|
type ecs_id_record_t = {
|
||||||
|
@ -90,8 +90,8 @@ type ecs_query_data_t = {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ecs_observer_t = {
|
type ecs_observer_t = {
|
||||||
callback: (archetype: ecs_archetype_t) -> (),
|
callback: (archetype: ecs_archetype_t) -> (),
|
||||||
query: ecs_query_data_t,
|
query: ecs_query_data_t,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ecs_observable_t = Map<i53, Map<i53, { ecs_observer_t }>>
|
type ecs_observable_t = Map<i53, Map<i53, { ecs_observer_t }>>
|
||||||
|
@ -295,10 +295,6 @@ local function ecs_pair_second(world: ecs_world_t, e: i53)
|
||||||
return ecs_get_alive(world, obj)
|
return ecs_get_alive(world, obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ecs_component_record(world: ecs_world_t, component: i53)
|
|
||||||
return world.component_index[component]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function query_match(query: ecs_query_data_t,
|
local function query_match(query: ecs_query_data_t,
|
||||||
archetype: ecs_archetype_t)
|
archetype: ecs_archetype_t)
|
||||||
local records = archetype.records
|
local records = archetype.records
|
||||||
|
@ -559,10 +555,10 @@ local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
||||||
local target = 0
|
local target = 0
|
||||||
local is_pair = ECS_IS_PAIR(id)
|
local is_pair = ECS_IS_PAIR(id)
|
||||||
if is_pair then
|
if is_pair then
|
||||||
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id))
|
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id)) :: i53
|
||||||
assert(relation and entity_index_is_alive(
|
assert(relation and entity_index_is_alive(
|
||||||
entity_index, relation), ECS_INTERNAL_ERROR)
|
entity_index, relation), ECS_INTERNAL_ERROR)
|
||||||
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id))
|
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id)) :: i53
|
||||||
assert(target and entity_index_is_alive(
|
assert(target and entity_index_is_alive(
|
||||||
entity_index, target), ECS_INTERNAL_ERROR)
|
entity_index, target), ECS_INTERNAL_ERROR)
|
||||||
end
|
end
|
||||||
|
@ -646,7 +642,6 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
|
||||||
local records: { number } = {}
|
local records: { number } = {}
|
||||||
local counts: {number} = {}
|
local counts: {number} = {}
|
||||||
|
|
||||||
local entity_index = world.entity_index
|
|
||||||
local archetype: ecs_archetype_t = {
|
local archetype: ecs_archetype_t = {
|
||||||
columns = columns,
|
columns = columns,
|
||||||
entities = {},
|
entities = {},
|
||||||
|
|
186
mkdocs.yml
|
@ -1,186 +0,0 @@
|
||||||
site_name: Jecs
|
|
||||||
site_url: jecs.github.io/jecs
|
|
||||||
repo_name: ukendio/jecs
|
|
||||||
repo_url: https://github.com/ukendio/jecs
|
|
||||||
|
|
||||||
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
|
|
4464
package-lock.json
generated
Normal file
|
@ -2,5 +2,4 @@
|
||||||
wally = "upliftgames/wally@0.3.2"
|
wally = "upliftgames/wally@0.3.2"
|
||||||
rojo = "rojo-rbx/rojo@7.4.4"
|
rojo = "rojo-rbx/rojo@7.4.4"
|
||||||
stylua = "johnnymorganz/stylua@2.0.1"
|
stylua = "johnnymorganz/stylua@2.0.1"
|
||||||
selene = "kampfkarren/selene@0.27.1"
|
|
||||||
Blink = "1Axen/Blink@0.14.1"
|
Blink = "1Axen/Blink@0.14.1"
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
std = "roblox"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
global_usage = "allow"
|
|
|
@ -1,25 +1,25 @@
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
local ChildOf = jecs.ChildOf
|
local ChildOf = jecs.ChildOf
|
||||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||||
local pe = require("@tools/entity_visualiser").prettify
|
local pe = require("@tools/entity_visualiser").prettify
|
||||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
||||||
local FriendsWith = world:component()
|
local FriendsWith = world:component()
|
||||||
world:print_snapshot()
|
world:print_snapshot()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
local e2 = world:entity()
|
local e2 = world:entity()
|
||||||
world:delete(e2)
|
world:delete(e2)
|
||||||
|
|
||||||
world:print_snapshot()
|
world:print_snapshot()
|
||||||
local e3 = world:entity()
|
local e3 = world:entity()
|
||||||
world:add(e3, pair(ChildOf, e1))
|
world:add(e3, pair(ChildOf, e1))
|
||||||
local e4 = world:entity()
|
local e4 = world:entity()
|
||||||
world:add(e4, pair(FriendsWith, e3))
|
world:add(e4, pair(FriendsWith, e3))
|
||||||
world:print_snapshot()
|
world:print_snapshot()
|
||||||
world:delete(e1)
|
world:delete(e1)
|
||||||
world:delete(e3)
|
world:delete(e3)
|
||||||
world:print_snapshot()
|
world:print_snapshot()
|
||||||
world:print_entity_index()
|
world:print_entity_index()
|
||||||
world:entity()
|
world:entity()
|
||||||
world:entity()
|
world:entity()
|
||||||
world:print_snapshot()
|
world:print_snapshot()
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Examples
|
|
||||||
|
|
||||||
This folder contains code examples for the Luau/Typescript APIs.
|
|
||||||
|
|
||||||
## Run with Luau
|
|
||||||
To run the examples with Luau, run the following commands from the root of the repository:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd examples/luau
|
|
||||||
luau path/to/file.luau
|
|
||||||
```
|
|
|
@ -1,43 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
local Position = world:component()
|
|
||||||
local Walking = world:component()
|
|
||||||
local Name = world:component()
|
|
||||||
|
|
||||||
-- Create an entity with name Bob
|
|
||||||
local bob = world:entity()
|
|
||||||
|
|
||||||
-- The set operation finds or creates a component, and sets it.
|
|
||||||
world:set(bob, Position, Vector3.new(10, 20, 30))
|
|
||||||
-- Name the entity Bob
|
|
||||||
world:set(bob, Name, "Bob")
|
|
||||||
-- The add operation adds a component without setting a value. This is
|
|
||||||
-- useful for tags, or when adding a component with its default value.
|
|
||||||
world:add(bob, Walking)
|
|
||||||
|
|
||||||
-- Get the value for the Position component
|
|
||||||
local pos = world:get(bob, Position)
|
|
||||||
print(`\{{pos.X}, {pos.Y}, {pos.Z}\}`)
|
|
||||||
|
|
||||||
-- Overwrite the value of the Position component
|
|
||||||
world:set(bob, Position, Vector3.new(40, 50, 60))
|
|
||||||
|
|
||||||
local alice = world:entity()
|
|
||||||
-- Create another named entity
|
|
||||||
world:set(alice, Name, "Alice")
|
|
||||||
world:set(alice, Position, Vector3.new(10, 20, 30))
|
|
||||||
world:add(alice, Walking)
|
|
||||||
|
|
||||||
-- Remove tag
|
|
||||||
world:remove(alice, Walking)
|
|
||||||
|
|
||||||
-- Iterate all entities with Position
|
|
||||||
for entity, p in world:query(Position) do
|
|
||||||
print(`{entity}: \{{p.X}, {p.Y}, {p.Z}\}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Output:
|
|
||||||
-- {10, 20, 30}
|
|
||||||
-- Alice: {10, 20, 30}
|
|
||||||
-- Bob: {40, 50, 60}
|
|
|
@ -1,112 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
local pair = jecs.pair
|
|
||||||
local ChildOf = jecs.ChildOf
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
local Name = world:component()
|
|
||||||
local Position = world:component()
|
|
||||||
local Star = world:component()
|
|
||||||
local Planet = world:component()
|
|
||||||
local Moon = world:component()
|
|
||||||
|
|
||||||
local Vector3
|
|
||||||
do
|
|
||||||
Vector3 = {}
|
|
||||||
Vector3.__index = Vector3
|
|
||||||
|
|
||||||
function Vector3.new(x, y, z)
|
|
||||||
x = x or 0
|
|
||||||
y = y or 0
|
|
||||||
z = z or 0
|
|
||||||
return setmetatable({ X = x, Y = y, Z = z }, Vector3)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vector3.__add(left, right)
|
|
||||||
return Vector3.new(left.X + right.X, left.Y + right.Y, left.Z + right.Z)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vector3.__mul(left, right)
|
|
||||||
if typeof(right) == "number" then
|
|
||||||
return Vector3.new(left.X * right, left.Y * right, left.Z * right)
|
|
||||||
end
|
|
||||||
return Vector3.new(left.X * right.X, left.Y * right.Y, left.Z * right.Z)
|
|
||||||
end
|
|
||||||
|
|
||||||
Vector3.one = Vector3.new(1, 1, 1)
|
|
||||||
Vector3.zero = Vector3.new()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function path(entity)
|
|
||||||
local str = world:get(entity, Name)
|
|
||||||
local parent
|
|
||||||
while true do
|
|
||||||
parent = world:parent(entity)
|
|
||||||
if not parent then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
entity = parent
|
|
||||||
str = world:get(parent, Name) .. "/" .. str
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
|
|
||||||
local function iterate(entity, parent)
|
|
||||||
local p = world:get(entity, Position)
|
|
||||||
local actual = p + parent
|
|
||||||
print(path(entity))
|
|
||||||
print(`\{{actual.X}, {actual.Y}, {actual.Z}}`)
|
|
||||||
|
|
||||||
for child in world:query(pair(ChildOf, entity)) do
|
|
||||||
--print(world:get(child, Name))
|
|
||||||
iterate(child, actual)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local sun = world:entity()
|
|
||||||
world:add(sun, Star)
|
|
||||||
world:set(sun, Position, Vector3.one)
|
|
||||||
world:set(sun, Name, "Sun")
|
|
||||||
do
|
|
||||||
local earth = world:entity()
|
|
||||||
world:set(earth, Name, "Earth")
|
|
||||||
world:add(earth, pair(ChildOf, sun))
|
|
||||||
world:add(earth, Planet)
|
|
||||||
world:set(earth, Position, Vector3.one * 3)
|
|
||||||
|
|
||||||
do
|
|
||||||
local moon = world:entity()
|
|
||||||
world:set(moon, Name, "Moon")
|
|
||||||
world:add(moon, pair(ChildOf, earth))
|
|
||||||
world:add(moon, Moon)
|
|
||||||
world:set(moon, Position, Vector3.one * 0.1)
|
|
||||||
|
|
||||||
print(`Child of Earth? {world:has(moon, pair(ChildOf, earth))}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
local venus = world:entity()
|
|
||||||
world:set(venus, Name, "Venus")
|
|
||||||
world:add(venus, pair(ChildOf, sun))
|
|
||||||
world:add(venus, Planet)
|
|
||||||
world:set(venus, Position, Vector3.one * 2)
|
|
||||||
|
|
||||||
local mercury = world:entity()
|
|
||||||
world:set(mercury, Name, "Mercury")
|
|
||||||
world:add(mercury, pair(ChildOf, sun))
|
|
||||||
world:add(mercury, Planet)
|
|
||||||
world:set(mercury, Position, Vector3.one)
|
|
||||||
|
|
||||||
iterate(sun, Vector3.zero)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Output:
|
|
||||||
-- Child of Earth? true
|
|
||||||
-- Sun
|
|
||||||
-- {1, 1, 1}
|
|
||||||
-- Sun/Mercury
|
|
||||||
-- {2, 2, 2}
|
|
||||||
-- Sun/Venus
|
|
||||||
-- {3, 3, 3}
|
|
||||||
-- Sun/Earth
|
|
||||||
-- {4, 4, 4}
|
|
||||||
-- Sun/Earth/Moon
|
|
||||||
-- {4.1, 4.1, 4.1}
|
|
|
@ -1,21 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
local Model = world:component()
|
|
||||||
|
|
||||||
-- It is important to define hooks for the component before the component is ever used
|
|
||||||
-- otherwise the hooks will never invoke!
|
|
||||||
world:set(Model, jecs.OnRemove, function(entity)
|
|
||||||
-- OnRemove is invoked before the component and its value is removed
|
|
||||||
-- which provides a stable reference to the entity at deletion.
|
|
||||||
-- This means that it is safe to retrieve the data inside of a hook
|
|
||||||
local model = world:get(entity, Model)
|
|
||||||
model:Destroy()
|
|
||||||
end)
|
|
||||||
|
|
||||||
world:set(Model, jecs.OnSet, function(entity, model)
|
|
||||||
-- OnSet is invoked after the data has been assigned.
|
|
||||||
-- It also returns the data for faster access.
|
|
||||||
-- There may be some logic to do some side effects on reassignments
|
|
||||||
model:SetAttribute("entityId", entity)
|
|
||||||
end)
|
|
|
@ -1,63 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
local Position = world:component()
|
|
||||||
local Velocity = world:component()
|
|
||||||
local Name = world:component()
|
|
||||||
|
|
||||||
local Vector3
|
|
||||||
do
|
|
||||||
Vector3 = {}
|
|
||||||
Vector3.__index = Vector3
|
|
||||||
|
|
||||||
function Vector3.new(x, y, z)
|
|
||||||
x = x or 0
|
|
||||||
y = y or 0
|
|
||||||
z = z or 0
|
|
||||||
return setmetatable({ X = x, Y = y, Z = z }, Vector3)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vector3.__add(left, right)
|
|
||||||
return Vector3.new(left.X + right.X, left.Y + right.Y, left.Z + right.Z)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vector3.__mul(left, right)
|
|
||||||
if typeof(right) == "number" then
|
|
||||||
return Vector3.new(left.X * right, left.Y * right, left.Z * right)
|
|
||||||
end
|
|
||||||
return Vector3.new(left.X * right.X, left.Y * right.Y, left.Z * right.Z)
|
|
||||||
end
|
|
||||||
|
|
||||||
Vector3.one = Vector3.new(1, 1, 1)
|
|
||||||
Vector3.zero = Vector3.new()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a few test entities for a Position, Velocity query
|
|
||||||
local e1 = world:entity()
|
|
||||||
world:set(e1, Name, "e1")
|
|
||||||
world:set(e1, Position, Vector3.new(10, 20, 30))
|
|
||||||
world:set(e1, Velocity, Vector3.new(1, 2, 3))
|
|
||||||
|
|
||||||
local e2 = world:entity()
|
|
||||||
world:set(e2, Name, "e2")
|
|
||||||
world:set(e2, Position, Vector3.new(10, 20, 30))
|
|
||||||
world:set(e2, Velocity, Vector3.new(4, 5, 6))
|
|
||||||
|
|
||||||
-- This entity will not match as it does not have Position, Velocity
|
|
||||||
local e3 = world:entity()
|
|
||||||
world:set(e3, Name, "e3")
|
|
||||||
world:set(e3, Position, Vector3.new(10, 20, 30))
|
|
||||||
|
|
||||||
-- Create an uncached query for Position, Velocity.
|
|
||||||
for entity, p, v in world:query(Position, Velocity) do
|
|
||||||
-- Iterate entities matching the query
|
|
||||||
p.X += v.X
|
|
||||||
p.Y += v.Y
|
|
||||||
p.Z += v.Z
|
|
||||||
|
|
||||||
print(`{world:get(entity, Name)}: \{{p.X}, {p.Y}, {p.Z}}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Output:
|
|
||||||
-- e2: {14, 25, 36}
|
|
||||||
-- e1: {11, 22, 33}
|
|
|
@ -1,61 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
|
|
||||||
local world = jecs.World.new()
|
|
||||||
local Name = world:component()
|
|
||||||
|
|
||||||
local function named(ctr, name)
|
|
||||||
local e = ctr(world)
|
|
||||||
world:set(e, Name, name)
|
|
||||||
return e
|
|
||||||
end
|
|
||||||
local function name(e)
|
|
||||||
return world:get(e, Name)
|
|
||||||
end
|
|
||||||
|
|
||||||
local Position = named(world.component, "Position") :: jecs.Entity<Vector3>
|
|
||||||
local Previous = jecs.Rest
|
|
||||||
local PreviousPosition = jecs.pair(Previous, Position)
|
|
||||||
|
|
||||||
local added = world
|
|
||||||
:query(Position)
|
|
||||||
:without(PreviousPosition)
|
|
||||||
:cached()
|
|
||||||
local changed = world
|
|
||||||
:query(Position, PreviousPosition)
|
|
||||||
:cached()
|
|
||||||
local removed = world
|
|
||||||
:query(PreviousPosition)
|
|
||||||
:without(Position)
|
|
||||||
:cached()
|
|
||||||
|
|
||||||
local e1 = named(world.entity, "e1")
|
|
||||||
world:set(e1, Position, Vector3.new(10, 20, 30))
|
|
||||||
|
|
||||||
local e2 = named(world.entity, "e2")
|
|
||||||
world:set(e2, Position, Vector3.new(10, 20, 30))
|
|
||||||
|
|
||||||
for e, p in added:iter() do
|
|
||||||
print(`Added {e}: \{{p.X}, {p.Y}, {p.Z}}`)
|
|
||||||
world:set(e, PreviousPosition, p)
|
|
||||||
end
|
|
||||||
|
|
||||||
world:set(e1, Position, "")
|
|
||||||
|
|
||||||
for e, new, old in changed:iter() do
|
|
||||||
if new ~= old then
|
|
||||||
print(`{name(new)}'s Position changed from \{{old.X}, {old.Y}, {old.Z}\} to \{{new.X}, {new.Y}, {new.Z}\}`)
|
|
||||||
world:set(e, PreviousPosition, new)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
world:remove(e2, Position)
|
|
||||||
|
|
||||||
for e in removed:iter() do
|
|
||||||
print(`Position was removed from {name(e)}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Output:
|
|
||||||
-- Added 265: {10, 20, 30}
|
|
||||||
-- Added 264: {10, 20, 30}
|
|
||||||
-- e1's Position changed from {10, 20, 30} to {999, 999, 1998}
|
|
||||||
-- Position was removed from e2
|
|
|
@ -1,125 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
local pair = jecs.pair
|
|
||||||
local ChildOf = jecs.ChildOf
|
|
||||||
local __ = jecs.Wildcard
|
|
||||||
local Name = jecs.Name
|
|
||||||
local world = jecs.World.new()
|
|
||||||
|
|
||||||
type Id<T = nil> = number & { __T: T }
|
|
||||||
local Voxel = world:component() :: Id
|
|
||||||
local Position = world:component() :: Id<Vector3>
|
|
||||||
local Perception = world:component() :: Id<{
|
|
||||||
range: number,
|
|
||||||
fov: number,
|
|
||||||
dir: Vector3,
|
|
||||||
}>
|
|
||||||
local PrimaryPart = world:component() :: Id<Part>
|
|
||||||
|
|
||||||
local local_player = game:GetService("Players").LocalPlayer
|
|
||||||
|
|
||||||
local function distance(a: Vector3, b: Vector3)
|
|
||||||
return (b - a).Magnitude
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_in_fov(a: Vector3, b: Vector3, forward_dir: Vector3, fov_angle: number)
|
|
||||||
local to_target = b - a
|
|
||||||
|
|
||||||
local forward_xz = Vector3.new(forward_dir.X, 0, forward_dir.Z).Unit
|
|
||||||
local to_target_xz = Vector3.new(to_target.X, 0, to_target.Z).Unit
|
|
||||||
|
|
||||||
local angle_to_target = math.deg(math.atan2(to_target_xz.Z, to_target_xz.X))
|
|
||||||
local forward_angle = math.deg(math.atan2(forward_xz.Z, forward_xz.X))
|
|
||||||
|
|
||||||
local angle_difference = math.abs(forward_angle - angle_to_target)
|
|
||||||
|
|
||||||
if angle_difference > 180 then
|
|
||||||
angle_difference = 360 - angle_difference
|
|
||||||
end
|
|
||||||
|
|
||||||
return angle_difference <= (fov_angle / 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local map = {}
|
|
||||||
local grid = 50
|
|
||||||
|
|
||||||
local function add_to_voxel(source: number, position: Vector3, prev_voxel_id: number?)
|
|
||||||
local hash = position // grid
|
|
||||||
local voxel_id = map[hash]
|
|
||||||
if not voxel_id then
|
|
||||||
voxel_id = world:entity()
|
|
||||||
world:add(voxel_id, Voxel)
|
|
||||||
world:set(voxel_id, Position, hash)
|
|
||||||
map[hash] = voxel_id
|
|
||||||
end
|
|
||||||
if prev_voxel_id ~= nil then
|
|
||||||
world:remove(source, pair(ChildOf, prev_voxel_id))
|
|
||||||
end
|
|
||||||
world:add(source, pair(ChildOf, voxel_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reconcile_client_owned_assembly_to_voxel(dt: number)
|
|
||||||
for e, part, position in world:query(PrimaryPart, Position) do
|
|
||||||
local p = part.Position
|
|
||||||
if p ~= position then
|
|
||||||
world:set(e, Position, p)
|
|
||||||
local voxel_id = world:target(e, ChildOf, 0)
|
|
||||||
if map[p // grid] == voxel_id then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
add_to_voxel(e, p, voxel_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_camera_direction(dt: number)
|
|
||||||
for _, perception in world:query(Perception) do
|
|
||||||
perception.dir = workspace.CurrentCamera.CFrame.LookVector
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function perceive_enemies(dt: number)
|
|
||||||
local it = world:query(Perception, Position, PrimaryPart)
|
|
||||||
-- There is only going to be one entity matching the query
|
|
||||||
local e, self_perception, self_position, self_primary_part = it()
|
|
||||||
|
|
||||||
local voxel_id = map[self_primary_part.Position // grid]
|
|
||||||
local nearby_entities_query = world:query(Position, pair(ChildOf, voxel_id))
|
|
||||||
|
|
||||||
for enemy, target_position in nearby_entities_query do
|
|
||||||
if distance(self_position, target_position) > self_perception.range then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_in_fov(self_position, target_position, self_perception.dir, self_perception.fov) then
|
|
||||||
local p = target_position
|
|
||||||
print(`Entity {world:get(e, Name)} can see target {world:get(enemy, Name)} at ({p.X}, {p.Y}, {p.Z})`)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local player = world:entity()
|
|
||||||
world:set(player, Perception, {
|
|
||||||
range = 100,
|
|
||||||
fov = 90,
|
|
||||||
dir = Vector3.new(1, 0, 0),
|
|
||||||
})
|
|
||||||
world:set(player, Name, "LocalPlayer")
|
|
||||||
local primary_part = (local_player.Character :: Model).PrimaryPart :: Part
|
|
||||||
world:set(player, PrimaryPart, primary_part)
|
|
||||||
world:set(player, Position, Vector3.zero)
|
|
||||||
|
|
||||||
local enemy = world:entity()
|
|
||||||
world:set(enemy, Name, "Enemy $1")
|
|
||||||
world:set(enemy, Position, Vector3.new(50, 0, 20))
|
|
||||||
|
|
||||||
add_to_voxel(player, primary_part.Position)
|
|
||||||
add_to_voxel(enemy, world)
|
|
||||||
|
|
||||||
local dt = 1 / 60
|
|
||||||
reconcile_client_owned_assembly_to_voxel(dt)
|
|
||||||
update_camera_direction(dt)
|
|
||||||
perceive_enemies(dt)
|
|
||||||
|
|
||||||
-- Output:
|
|
||||||
-- LocalPlayer can see target Enemy $1
|
|
|
@ -1,37 +0,0 @@
|
||||||
local jecs = require("@jecs")
|
|
||||||
local pair = jecs.pair
|
|
||||||
local world = jecs.World.new()
|
|
||||||
local Name = world:component()
|
|
||||||
|
|
||||||
local function named(ctr, name)
|
|
||||||
local e = ctr(world)
|
|
||||||
world:set(e, Name, name)
|
|
||||||
return e
|
|
||||||
end
|
|
||||||
local function name(e)
|
|
||||||
return world:get(e, Name)
|
|
||||||
end
|
|
||||||
|
|
||||||
local Eats = world:component()
|
|
||||||
local Apples = named(world.entity, "Apples")
|
|
||||||
local Oranges = named(world.entity, "Oranges")
|
|
||||||
|
|
||||||
local bob = named(world.entity, "Bob")
|
|
||||||
world:set(bob, pair(Eats, Apples), 10)
|
|
||||||
|
|
||||||
local alice = named(world.entity, "Alice")
|
|
||||||
world:set(alice, pair(Eats, Oranges), 5)
|
|
||||||
|
|
||||||
-- Aliasing the wildcard to symbols improves readability and ease of writing
|
|
||||||
local __ = jecs.Wildcard
|
|
||||||
|
|
||||||
-- Create a query that matches edible components
|
|
||||||
for entity, amount in world:query(pair(Eats, __)) do
|
|
||||||
-- Iterate the query
|
|
||||||
local food = world:target(entity, Eats)
|
|
||||||
print(`{name(entity)} eats {amount} {name(food)}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Output:
|
|
||||||
-- Alice eats 5 Oranges
|
|
||||||
-- Bob eats 10 Apples
|
|
316
test/lol.luau
|
@ -1,158 +1,158 @@
|
||||||
local c = {
|
local c = {
|
||||||
white_underline = function(s: any)
|
white_underline = function(s: any)
|
||||||
return `\27[1;4m{s}\27[0m`
|
return `\27[1;4m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
white = function(s: any)
|
white = function(s: any)
|
||||||
return `\27[37;1m{s}\27[0m`
|
return `\27[37;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
green = function(s: any)
|
green = function(s: any)
|
||||||
return `\27[32;1m{s}\27[0m`
|
return `\27[32;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
red = function(s: any)
|
red = function(s: any)
|
||||||
return `\27[31;1m{s}\27[0m`
|
return `\27[31;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
yellow = function(s: any)
|
yellow = function(s: any)
|
||||||
return `\27[33;1m{s}\27[0m`
|
return `\27[33;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
red_highlight = function(s: any)
|
red_highlight = function(s: any)
|
||||||
return `\27[41;1;30m{s}\27[0m`
|
return `\27[41;1;30m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
green_highlight = function(s: any)
|
green_highlight = function(s: any)
|
||||||
return `\27[42;1;30m{s}\27[0m`
|
return `\27[42;1;30m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
gray = function(s: any)
|
gray = function(s: any)
|
||||||
return `\27[30;1m{s}\27[0m`
|
return `\27[30;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
local ECS_PAIR_FLAG = 0x8
|
local ECS_PAIR_FLAG = 0x8
|
||||||
local ECS_ID_FLAGS_MASK = 0x10
|
local ECS_ID_FLAGS_MASK = 0x10
|
||||||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||||
|
|
||||||
type i53 = number
|
type i53 = number
|
||||||
type i24 = number
|
type i24 = number
|
||||||
|
|
||||||
local function ECS_ENTITY_T_LO(e: i53): i24
|
local function ECS_ENTITY_T_LO(e: i53): i24
|
||||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
|
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ECS_GENERATION(e: i53): i24
|
local function ECS_GENERATION(e: i53): i24
|
||||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
|
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local ECS_ID = ECS_ENTITY_T_LO
|
local ECS_ID = ECS_ENTITY_T_LO
|
||||||
|
|
||||||
local function ECS_COMBINE(source: number, target: number): i53
|
local function ECS_COMBINE(source: number, target: number): i53
|
||||||
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
|
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ECS_GENERATION_INC(e: i53)
|
local function ECS_GENERATION_INC(e: i53)
|
||||||
if e > ECS_ENTITY_MASK then
|
if e > ECS_ENTITY_MASK then
|
||||||
local flags = e // ECS_ID_FLAGS_MASK
|
local flags = e // ECS_ID_FLAGS_MASK
|
||||||
local id = flags // ECS_ENTITY_MASK
|
local id = flags // ECS_ENTITY_MASK
|
||||||
local generation = flags % ECS_GENERATION_MASK
|
local generation = flags % ECS_GENERATION_MASK
|
||||||
|
|
||||||
local next_gen = generation + 1
|
local next_gen = generation + 1
|
||||||
if next_gen > ECS_GENERATION_MASK then
|
if next_gen > ECS_GENERATION_MASK then
|
||||||
return id
|
return id
|
||||||
end
|
end
|
||||||
|
|
||||||
return ECS_COMBINE(id, next_gen) + flags
|
return ECS_COMBINE(id, next_gen) + flags
|
||||||
end
|
end
|
||||||
return ECS_COMBINE(e, 1)
|
return ECS_COMBINE(e, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bl()
|
local function bl()
|
||||||
print("")
|
print("")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pe(e)
|
local function pe(e)
|
||||||
local gen = ECS_GENERATION(e)
|
local gen = ECS_GENERATION(e)
|
||||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dprint(tbl: { [number]: number })
|
local function dprint(tbl: { [number]: number })
|
||||||
bl()
|
bl()
|
||||||
print("--------")
|
print("--------")
|
||||||
for i, e in tbl do
|
for i, e in tbl do
|
||||||
print("| "..pe(e).." |")
|
print("| "..pe(e).." |")
|
||||||
print("--------")
|
print("--------")
|
||||||
end
|
end
|
||||||
bl()
|
bl()
|
||||||
end
|
end
|
||||||
|
|
||||||
local max_id = 0
|
local max_id = 0
|
||||||
local alive_count = 0
|
local alive_count = 0
|
||||||
local dense = {}
|
local dense = {}
|
||||||
local sparse = {}
|
local sparse = {}
|
||||||
local function alloc()
|
local function alloc()
|
||||||
if alive_count ~= #dense then
|
if alive_count ~= #dense then
|
||||||
alive_count += 1
|
alive_count += 1
|
||||||
print("*recycled", pe(dense[alive_count]))
|
print("*recycled", pe(dense[alive_count]))
|
||||||
return dense[alive_count]
|
return dense[alive_count]
|
||||||
end
|
end
|
||||||
max_id += 1
|
max_id += 1
|
||||||
local id = max_id
|
local id = max_id
|
||||||
alive_count += 1
|
alive_count += 1
|
||||||
dense[alive_count] = id
|
dense[alive_count] = id
|
||||||
sparse[id] = {
|
sparse[id] = {
|
||||||
dense = alive_count
|
dense = alive_count
|
||||||
}
|
}
|
||||||
print("*allocated", pe(id))
|
print("*allocated", pe(id))
|
||||||
return id
|
return id
|
||||||
end
|
end
|
||||||
|
|
||||||
local function remove(entity)
|
local function remove(entity)
|
||||||
local id = ECS_ID(entity)
|
local id = ECS_ID(entity)
|
||||||
local r = sparse[id]
|
local r = sparse[id]
|
||||||
local index_of_deleted_entity = r.dense
|
local index_of_deleted_entity = r.dense
|
||||||
local last_entity_alive_at_index = alive_count -- last entity alive
|
local last_entity_alive_at_index = alive_count -- last entity alive
|
||||||
alive_count -= 1
|
alive_count -= 1
|
||||||
local last_alive_entity = dense[last_entity_alive_at_index]
|
local last_alive_entity = dense[last_entity_alive_at_index]
|
||||||
local r_swap = sparse[ECS_ID(last_alive_entity)]
|
local r_swap = sparse[ECS_ID(last_alive_entity)]
|
||||||
r_swap.dense = r.dense
|
r_swap.dense = r.dense
|
||||||
r.dense = last_entity_alive_at_index
|
r.dense = last_entity_alive_at_index
|
||||||
dense[index_of_deleted_entity] = last_alive_entity
|
dense[index_of_deleted_entity] = last_alive_entity
|
||||||
dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
|
dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
|
||||||
print("*dellocated", pe(id))
|
print("*dellocated", pe(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function alive(e)
|
local function alive(e)
|
||||||
local r = sparse[ECS_ID(e)]
|
local r = sparse[ECS_ID(e)]
|
||||||
|
|
||||||
return dense[r.dense] == e
|
return dense[r.dense] == e
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pa(e)
|
local function pa(e)
|
||||||
print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
|
print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
|
||||||
end
|
end
|
||||||
|
|
||||||
local tprint = require("@testkit").print
|
local tprint = require("@testkit").print
|
||||||
local e1v0 = alloc()
|
local e1v0 = alloc()
|
||||||
local e2v0 = alloc()
|
local e2v0 = alloc()
|
||||||
local e3v0 = alloc()
|
local e3v0 = alloc()
|
||||||
local e4v0 = alloc()
|
local e4v0 = alloc()
|
||||||
local e5v0 = alloc()
|
local e5v0 = alloc()
|
||||||
pa(e1v0)
|
pa(e1v0)
|
||||||
pa(e4v0)
|
pa(e4v0)
|
||||||
remove(e5v0)
|
remove(e5v0)
|
||||||
pa(e5v0)
|
pa(e5v0)
|
||||||
|
|
||||||
local e5v1 = alloc()
|
local e5v1 = alloc()
|
||||||
pa(e5v0)
|
pa(e5v0)
|
||||||
pa(e5v1)
|
pa(e5v1)
|
||||||
pa(e2v0)
|
pa(e2v0)
|
||||||
print(ECS_ID(e2v0))
|
print(ECS_ID(e2v0))
|
||||||
|
|
||||||
dprint(dense)
|
dprint(dense)
|
||||||
remove(e2v0)
|
remove(e2v0)
|
||||||
dprint(dense)
|
dprint(dense)
|
||||||
|
|
|
@ -1,122 +1,122 @@
|
||||||
local RunService = game:GetService("RunService")
|
local RunService = game:GetService("RunService")
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
_G.__JECS_HI_COMPONENT_ID = 300
|
_G.__JECS_HI_COMPONENT_ID = 300
|
||||||
local ecs = require(ReplicatedStorage.ecs)
|
local ecs = require(ReplicatedStorage.ecs)
|
||||||
|
|
||||||
-- 500 entities
|
-- 500 entities
|
||||||
-- 2-30 components on each entity
|
-- 2-30 components on each entity
|
||||||
-- 300 unique components
|
-- 300 unique components
|
||||||
-- 200 systems
|
-- 200 systems
|
||||||
-- 1-10 components to query per system
|
-- 1-10 components to query per system
|
||||||
|
|
||||||
local startTime = os.clock()
|
local startTime = os.clock()
|
||||||
|
|
||||||
local world = ecs.World.new()
|
local world = ecs.World.new()
|
||||||
|
|
||||||
local components = {}
|
local components = {}
|
||||||
|
|
||||||
for i = 1, 300 do -- 300 components
|
for i = 1, 300 do -- 300 components
|
||||||
components[i] = world:component()
|
components[i] = world:component()
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
for i = 1, 50 do -- 50 archetypes
|
for i = 1, 50 do -- 50 archetypes
|
||||||
local archetype = {}
|
local archetype = {}
|
||||||
|
|
||||||
for _ = 1, math.random(2, 30) do
|
for _ = 1, math.random(2, 30) do
|
||||||
local componentId = math.random(1, #components)
|
local componentId = math.random(1, #components)
|
||||||
|
|
||||||
table.insert(archetype, components[componentId])
|
table.insert(archetype, components[componentId])
|
||||||
end
|
end
|
||||||
|
|
||||||
archetypes[i] = archetype
|
archetypes[i] = archetype
|
||||||
end
|
end
|
||||||
|
|
||||||
for _ = 1, 1000 do -- 1000 entities in the world
|
for _ = 1, 1000 do -- 1000 entities in the world
|
||||||
local componentsToAdd = {}
|
local componentsToAdd = {}
|
||||||
|
|
||||||
local archetypeId = math.random(1, #archetypes)
|
local archetypeId = math.random(1, #archetypes)
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
for _, component in ipairs(archetypes[archetypeId]) do
|
for _, component in ipairs(archetypes[archetypeId]) do
|
||||||
world:set(e, component, {
|
world:set(e, component, {
|
||||||
DummyData = math.random(1, 5000),
|
DummyData = math.random(1, 5000),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function values(t)
|
local function values(t)
|
||||||
local array = {}
|
local array = {}
|
||||||
for _, v in t do
|
for _, v in t do
|
||||||
table.insert(array, v)
|
table.insert(array, v)
|
||||||
end
|
end
|
||||||
return array
|
return array
|
||||||
end
|
end
|
||||||
|
|
||||||
local contiguousComponents = values(components)
|
local contiguousComponents = values(components)
|
||||||
local systemComponentsToQuery = {}
|
local systemComponentsToQuery = {}
|
||||||
|
|
||||||
for _ = 1, 200 do -- 200 systems
|
for _ = 1, 200 do -- 200 systems
|
||||||
local numComponentsToQuery = math.random(1, 10)
|
local numComponentsToQuery = math.random(1, 10)
|
||||||
local componentsToQuery = {}
|
local componentsToQuery = {}
|
||||||
|
|
||||||
for _ = 1, numComponentsToQuery do
|
for _ = 1, numComponentsToQuery do
|
||||||
table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
|
table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(systemComponentsToQuery, componentsToQuery)
|
table.insert(systemComponentsToQuery, componentsToQuery)
|
||||||
end
|
end
|
||||||
|
|
||||||
local worldCreateTime = os.clock() - startTime
|
local worldCreateTime = os.clock() - startTime
|
||||||
local results = {}
|
local results = {}
|
||||||
startTime = os.clock()
|
startTime = os.clock()
|
||||||
|
|
||||||
RunService.Heartbeat:Connect(function()
|
RunService.Heartbeat:Connect(function()
|
||||||
local added = 0
|
local added = 0
|
||||||
local systemStartTime = os.clock()
|
local systemStartTime = os.clock()
|
||||||
debug.profilebegin("systems")
|
debug.profilebegin("systems")
|
||||||
for _, componentsToQuery in ipairs(systemComponentsToQuery) do
|
for _, componentsToQuery in ipairs(systemComponentsToQuery) do
|
||||||
debug.profilebegin("system")
|
debug.profilebegin("system")
|
||||||
for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
|
for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
|
||||||
world:set(
|
world:set(
|
||||||
entityId,
|
entityId,
|
||||||
{
|
{
|
||||||
DummyData = firstComponent.DummyData + 1,
|
DummyData = firstComponent.DummyData + 1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
added += 1
|
added += 1
|
||||||
end
|
end
|
||||||
debug.profileend()
|
debug.profileend()
|
||||||
end
|
end
|
||||||
debug.profileend()
|
debug.profileend()
|
||||||
|
|
||||||
if os.clock() - startTime < 4 then
|
if os.clock() - startTime < 4 then
|
||||||
-- discard first 4 seconds
|
-- discard first 4 seconds
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if results == nil then
|
if results == nil then
|
||||||
return
|
return
|
||||||
elseif #results < 1000 then
|
elseif #results < 1000 then
|
||||||
table.insert(results, os.clock() - systemStartTime)
|
table.insert(results, os.clock() - systemStartTime)
|
||||||
else
|
else
|
||||||
print("added", added)
|
print("added", added)
|
||||||
print("World created in", worldCreateTime * 1000, "ms")
|
print("World created in", worldCreateTime * 1000, "ms")
|
||||||
local sum = 0
|
local sum = 0
|
||||||
for _, result in ipairs(results) do
|
for _, result in ipairs(results) do
|
||||||
sum += result
|
sum += result
|
||||||
end
|
end
|
||||||
print(("Average frame time: %fms"):format((sum / #results) * 1000))
|
print(("Average frame time: %fms"):format((sum / #results) * 1000))
|
||||||
|
|
||||||
results = nil
|
results = nil
|
||||||
|
|
||||||
local n = #world.archetypes
|
local n = #world.archetypes
|
||||||
|
|
||||||
print(
|
print(
|
||||||
("X entities\n%d components\n%d systems\n%d archetypes"):format(
|
("X entities\n%d components\n%d systems\n%d archetypes"):format(
|
||||||
#components,
|
#components,
|
||||||
#systemComponentsToQuery,
|
#systemComponentsToQuery,
|
||||||
n
|
n
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 168 KiB |
Before Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.5 KiB |
|
@ -1,24 +0,0 @@
|
||||||
declare function afterAll(callback: () -> ()): ()
|
|
||||||
declare function afterEach(callback: () -> ()): ()
|
|
||||||
|
|
||||||
declare function beforeAll(callback: () -> ()): ()
|
|
||||||
declare function beforeEach(callback: () -> ()): ()
|
|
||||||
|
|
||||||
declare function describe(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function describeFOCUS(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function fdescribe(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function describeSKIP(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function xdescribe(phrase: string, callback: () -> ()): ()
|
|
||||||
|
|
||||||
declare function expect(value: any): any
|
|
||||||
|
|
||||||
declare function FIXME(optionalMessage: string?): ()
|
|
||||||
declare function FOCUS(): ()
|
|
||||||
declare function SKIP(): ()
|
|
||||||
|
|
||||||
declare function it(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function itFOCUS(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function fit(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function itSKIP(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function xit(phrase: string, callback: () -> ()): ()
|
|
||||||
declare function itFIXME(phrase: string, callback: () -> ()): ()
|
|
|
@ -1,33 +1,33 @@
|
||||||
return {
|
return {
|
||||||
white_underline = function(s: any)
|
white_underline = function(s: any)
|
||||||
return `\27[1;4m{s}\27[0m`
|
return `\27[1;4m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
white = function(s: any)
|
white = function(s: any)
|
||||||
return `\27[37;1m{s}\27[0m`
|
return `\27[37;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
green = function(s: any)
|
green = function(s: any)
|
||||||
return `\27[32;1m{s}\27[0m`
|
return `\27[32;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
red = function(s: any)
|
red = function(s: any)
|
||||||
return `\27[31;1m{s}\27[0m`
|
return `\27[31;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
yellow = function(s: any)
|
yellow = function(s: any)
|
||||||
return `\27[33;1m{s}\27[0m`
|
return `\27[33;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
red_highlight = function(s: any)
|
red_highlight = function(s: any)
|
||||||
return `\27[41;1;30m{s}\27[0m`
|
return `\27[41;1;30m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
green_highlight = function(s: any)
|
green_highlight = function(s: any)
|
||||||
return `\27[42;1;30m{s}\27[0m`
|
return `\27[42;1;30m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
|
|
||||||
gray = function(s: any)
|
gray = function(s: any)
|
||||||
return `\27[30;1m{s}\27[0m`
|
return `\27[30;1m{s}\27[0m`
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local ECS_GENERATION = jecs.ECS_GENERATION
|
local ECS_GENERATION = jecs.ECS_GENERATION
|
||||||
local ECS_ID = jecs.ECS_ID
|
local ECS_ID = jecs.ECS_ID
|
||||||
local ansi = require("@tools/ansi")
|
local ansi = require("@tools/ansi")
|
||||||
|
|
||||||
local function pe(e: any)
|
local function pe(e: any)
|
||||||
local gen = ECS_GENERATION(e)
|
local gen = ECS_GENERATION(e)
|
||||||
return ansi.green(`e{ECS_ID(e)}`)..ansi.yellow(`v{gen}`)
|
return ansi.green(`e{ECS_ID(e)}`) .. ansi.yellow(`v{gen}`)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function name(world: jecs.World, id: any)
|
local function name(world: jecs.World, id: any)
|
||||||
return world:get(id, jecs.Name) or `${id}`
|
return world:get(id, jecs.Name) or `${id}`
|
||||||
end
|
end
|
||||||
|
|
||||||
local function components(world: jecs.World, entity: any)
|
local function components(world: jecs.World, entity: any)
|
||||||
local r = jecs.entity_index_try_get(world.entity_index, entity)
|
local r = jecs.entity_index_try_get(world.entity_index, entity)
|
||||||
if not r then
|
if not r then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetype = r.archetype
|
local archetype = r.archetype
|
||||||
local row = r.row
|
local row = r.row
|
||||||
print(`Entity {pe(entity)}`)
|
print(`Entity {pe(entity)}`)
|
||||||
print("-----------------------------------------------------")
|
print("-----------------------------------------------------")
|
||||||
for i, column in archetype.columns do
|
for i, column in archetype.columns do
|
||||||
local component = archetype.types[i]
|
local component = archetype.types[i]
|
||||||
local n
|
local n
|
||||||
if jecs.IS_PAIR(component) then
|
if jecs.IS_PAIR(component) then
|
||||||
n = `({name(world, jecs.pair_first(world, component))}, {name(world, jecs.pair_second(world, component))})`
|
n = `({name(world, jecs.pair_first(world, component))}, {name(world, jecs.pair_second(world, component))})`
|
||||||
else
|
else
|
||||||
n = name(world, component)
|
n = name(world, component)
|
||||||
end
|
end
|
||||||
local data = column[row] or "TAG"
|
local data = column[row] or "TAG"
|
||||||
print(`| {n} | {data} |`)
|
print(`| {n} | {data} |`)
|
||||||
end
|
end
|
||||||
print("-----------------------------------------------------")
|
print("-----------------------------------------------------")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
components = components,
|
components = components,
|
||||||
prettify = pe,
|
prettify = pe,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,215 +1,216 @@
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local ECS_GENERATION = jecs.ECS_GENERATION
|
local ECS_GENERATION = jecs.ECS_GENERATION
|
||||||
local ECS_ID = jecs.ECS_ID
|
local ECS_ID = jecs.ECS_ID
|
||||||
local __ = jecs.Wildcard
|
local __ = jecs.Wildcard
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
|
|
||||||
local prettify = require("@tools/entity_visualiser").prettify
|
local prettify = require("@tools/entity_visualiser").prettify
|
||||||
|
|
||||||
local pe = prettify
|
local pe = prettify
|
||||||
local ansi = require("@tools/ansi")
|
local ansi = require("@tools/ansi")
|
||||||
|
|
||||||
function print_centered_entity(entity, width: number)
|
function print_centered_entity(entity, width: number)
|
||||||
local entity_str = tostring(entity)
|
local entity_str = tostring(entity)
|
||||||
local entity_length = #entity_str
|
local entity_length = #entity_str
|
||||||
|
|
||||||
local padding_total = width - 2 - entity_length
|
local padding_total = width - 2 - entity_length
|
||||||
|
|
||||||
local padding_left = math.floor(padding_total / 2)
|
local padding_left = math.floor(padding_total / 2)
|
||||||
local padding_right = padding_total - padding_left
|
local padding_right = padding_total - padding_left
|
||||||
|
|
||||||
local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
|
local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
|
||||||
|
|
||||||
print("|" .. centered_str .. "|")
|
print("|" .. centered_str .. "|")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function name(world, e)
|
local function name(world, e)
|
||||||
return world:get(world, e, jecs.Name) or pe(e)
|
return world:get(world, e, jecs.Name) or pe(e)
|
||||||
end
|
end
|
||||||
local padding_enabled = false
|
local padding_enabled = false
|
||||||
local function pad()
|
local function pad()
|
||||||
if padding_enabled then
|
if padding_enabled then
|
||||||
print("")
|
print("")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function lifetime_tracker_add(world: jecs.World, opt)
|
local function lifetime_tracker_add(world: jecs.World, opt)
|
||||||
local entity_index = world.entity_index
|
local entity_index = world.entity_index
|
||||||
local dense_array = entity_index.dense_array
|
local dense_array = entity_index.dense_array
|
||||||
local component_index = world.component_index
|
local component_index = world.component_index
|
||||||
|
|
||||||
local ENTITY_RANGE = (jecs.Rest :: any) + 1
|
local ENTITY_RANGE = (jecs.Rest :: any) + 1
|
||||||
|
|
||||||
local w = setmetatable({}, { __index = world })
|
local w = setmetatable({}, { __index = world })
|
||||||
|
|
||||||
padding_enabled = opt.padding_enabled
|
padding_enabled = opt.padding_enabled
|
||||||
|
|
||||||
local world_entity = world.entity
|
local world_entity = world.entity
|
||||||
w.entity = function(self, entity)
|
w.entity = function(self, entity)
|
||||||
if entity then
|
if entity then
|
||||||
return world_entity(world, entity)
|
return world_entity(world, entity)
|
||||||
end
|
end
|
||||||
local will_recycle = entity_index.max_id ~= entity_index.alive_count
|
local will_recycle = entity_index.max_id ~= entity_index.alive_count
|
||||||
local e = world_entity(world)
|
local e = world_entity(world)
|
||||||
if will_recycle then
|
if will_recycle then
|
||||||
print(`*recycled {pe(e)}`)
|
print(`*recycled {pe(e)}`)
|
||||||
else
|
else
|
||||||
print(`*created {pe(e)}`)
|
print(`*created {pe(e)}`)
|
||||||
end
|
end
|
||||||
pad()
|
pad()
|
||||||
return e
|
return e
|
||||||
end
|
end
|
||||||
w.print_entity_index = function(self)
|
w.print_entity_index = function(self)
|
||||||
local max_id = entity_index.max_id
|
local max_id = entity_index.max_id
|
||||||
local alive_count = entity_index.alive_count
|
local alive_count = entity_index.alive_count
|
||||||
local alive = table.move(dense_array, 1+jecs.Rest::any, alive_count, 1, {})
|
local alive = table.move(dense_array, 1 + jecs.Rest :: any, alive_count, 1, {})
|
||||||
local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
|
local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
|
||||||
|
|
||||||
local sep = "|--------|"
|
local sep = "|--------|"
|
||||||
if #alive > 0 then
|
if #alive > 0 then
|
||||||
print("|-alive--|")
|
print("|-alive--|")
|
||||||
for i = 1, #alive do
|
for i = 1, #alive do
|
||||||
local e = pe(alive[i])
|
local e = pe(alive[i])
|
||||||
print_centered_entity(e, 32)
|
print_centered_entity(e, 32)
|
||||||
print(sep)
|
print(sep)
|
||||||
end
|
end
|
||||||
print("\n")
|
print("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
if #dead > 0 then
|
if #dead > 0 then
|
||||||
print("|--dead--|")
|
print("|--dead--|")
|
||||||
for i = 1, #dead do
|
for i = 1, #dead do
|
||||||
print_centered_entity(pe(dead[i]), 32)
|
print_centered_entity(pe(dead[i]), 32)
|
||||||
print(sep)
|
print(sep)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
pad()
|
pad()
|
||||||
end
|
end
|
||||||
local timelines = {}
|
local timelines = {}
|
||||||
w.print_snapshot = function(self)
|
w.print_snapshot = function(self)
|
||||||
local timeline = #timelines + 1
|
local timeline = #timelines + 1
|
||||||
local entity_column_width = 10
|
local entity_column_width = 10
|
||||||
local status_column_width = 8
|
local status_column_width = 8
|
||||||
|
|
||||||
local header = string.format("| %-" .. entity_column_width .. "s |", "Entity")
|
local header = string.format("| %-" .. entity_column_width .. "s |", "Entity")
|
||||||
for i = 1, timeline do
|
for i = 1, timeline do
|
||||||
header = header .. string.format(" %-" .. status_column_width .. "s |", string.format("T%d", i))
|
header = header .. string.format(" %-" .. status_column_width .. "s |", string.format("T%d", i))
|
||||||
end
|
end
|
||||||
|
|
||||||
local max_id = entity_index.max_id
|
local max_id = entity_index.max_id
|
||||||
local alive_count = entity_index.alive_count
|
local alive_count = entity_index.alive_count
|
||||||
local alive = table.move(dense_array, 1+jecs.Rest::any, alive_count, 1, {})
|
local alive = table.move(dense_array, 1 + jecs.Rest :: any, alive_count, 1, {})
|
||||||
local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
|
local dead = table.move(dense_array, alive_count + 1, max_id, 1, {})
|
||||||
|
|
||||||
local data = {}
|
local data = {}
|
||||||
print("-------------------------------------------------------------------")
|
print("-------------------------------------------------------------------")
|
||||||
print(header)
|
print(header)
|
||||||
|
|
||||||
-- Store the snapshot data for this timeline
|
-- Store the snapshot data for this timeline
|
||||||
for i = ENTITY_RANGE, max_id do
|
for i = ENTITY_RANGE, max_id do
|
||||||
if dense_array[i] then
|
if dense_array[i] then
|
||||||
local entity = dense_array[i]
|
local entity = dense_array[i]
|
||||||
local id = ECS_ID(entity)
|
local id = ECS_ID(entity)
|
||||||
local status = "alive"
|
local status = "alive"
|
||||||
if not world:contains(entity) then
|
if not world:contains(entity) then
|
||||||
status = "dead"
|
status = "dead"
|
||||||
end
|
end
|
||||||
data[id] = status
|
data[id] = status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(timelines, data)
|
table.insert(timelines, data)
|
||||||
|
|
||||||
-- Create a table to hold entity data for sorting
|
-- Create a table to hold entity data for sorting
|
||||||
local entities = {}
|
local entities = {}
|
||||||
for i = ENTITY_RANGE, max_id do
|
for i = ENTITY_RANGE, max_id do
|
||||||
if dense_array[i] then
|
if dense_array[i] then
|
||||||
local entity = dense_array[i]
|
local entity = dense_array[i]
|
||||||
local id = ECS_ID(entity)
|
local id = ECS_ID(entity)
|
||||||
-- Push entity and id into the new `entities` table
|
-- Push entity and id into the new `entities` table
|
||||||
table.insert(entities, {entity = entity, id = id})
|
table.insert(entities, { entity = entity, id = id })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sort the entities by ECS_ID
|
-- Sort the entities by ECS_ID
|
||||||
table.sort(entities, function(a, b)
|
table.sort(entities, function(a, b)
|
||||||
return a.id < b.id
|
return a.id < b.id
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Print the sorted rows
|
-- Print the sorted rows
|
||||||
for _, entity_data in ipairs(entities) do
|
for _, entity_data in ipairs(entities) do
|
||||||
local entity = entity_data.entity
|
local entity = entity_data.entity
|
||||||
local id = entity_data.id
|
local id = entity_data.id
|
||||||
local status = "alive"
|
local status = "alive"
|
||||||
if id > alive_count then
|
if id > alive_count then
|
||||||
status = "dead"
|
status = "dead"
|
||||||
end
|
end
|
||||||
local row = string.format("| %-" .. entity_column_width .. "s |", pe(entity))
|
local row = string.format("| %-" .. entity_column_width .. "s |", pe(entity))
|
||||||
for j = 1, timeline do
|
for j = 1, timeline do
|
||||||
local timeline_data = timelines[j]
|
local timeline_data = timelines[j]
|
||||||
local entity_data = timeline_data[id]
|
local entity_data = timeline_data[id]
|
||||||
if entity_data then
|
if entity_data then
|
||||||
row = row .. string.format(" %-" .. status_column_width .. "s |", entity_data)
|
row = row .. string.format(" %-" .. status_column_width .. "s |", entity_data)
|
||||||
else
|
else
|
||||||
row = row .. string.format(" %-" .. status_column_width .. "s |", "-")
|
row = row .. string.format(" %-" .. status_column_width .. "s |", "-")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print(row)
|
print(row)
|
||||||
end
|
end
|
||||||
print("-------------------------------------------------------------------")
|
print("-------------------------------------------------------------------")
|
||||||
pad()
|
pad()
|
||||||
end
|
end
|
||||||
local world_add = world.add
|
local world_add = world.add
|
||||||
local relations = {}
|
local relations = {}
|
||||||
w.add = function(self, entity: any, component: any)
|
w.add = function(self, entity: any, component: any)
|
||||||
world_add(world, entity, component)
|
world_add(world, entity, component)
|
||||||
if jecs.IS_PAIR(component) then
|
if jecs.IS_PAIR(component) then
|
||||||
local relation = jecs.pair_first(world, component)
|
local relation = jecs.pair_first(world, component)
|
||||||
local target = jecs.pair_second(world, component)
|
local target = jecs.pair_second(world, component)
|
||||||
print(`*added ({pe(relation)}, {pe(target)}) to {pe(entity)}`)
|
print(`*added ({pe(relation)}, {pe(target)}) to {pe(entity)}`)
|
||||||
pad()
|
pad()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
|
||||||
|
local world_delete = world.delete
|
||||||
local world_delete = world.delete
|
w.delete = function(self, e)
|
||||||
w.delete = function(self, e)
|
world_delete(world, e)
|
||||||
world_delete(world, e)
|
|
||||||
|
local idr_t = component_index[pair(__, e)]
|
||||||
local idr_t = component_index[pair(__, e)]
|
if idr_t then
|
||||||
if idr_t then
|
for archetype_id in idr_t.cache do
|
||||||
for archetype_id in idr_t.cache do
|
local archetype = world.archetypes[archetype_id]
|
||||||
local archetype = world.archetypes[archetype_id]
|
for _, id in archetype.types do
|
||||||
for _, id in archetype.types do
|
if not jecs.IS_PAIR(id) then
|
||||||
if not jecs.IS_PAIR(id) then
|
continue
|
||||||
continue
|
end
|
||||||
end
|
local object = jecs.pair_second(world, id)
|
||||||
local object = jecs.pair_second(world, id)
|
if object ~= e then
|
||||||
if object ~= e then
|
continue
|
||||||
continue
|
end
|
||||||
end
|
local id_record = component_index[id]
|
||||||
local id_record = component_index[id]
|
local flags = id_record.flags
|
||||||
local flags = id_record.flags
|
local flags_delete_mask: number = bit32.band(flags, jecs.ECS_ID_DELETE)
|
||||||
local flags_delete_mask: number = bit32.band(flags, jecs.ECS_ID_DELETE)
|
if flags_delete_mask ~= 0 then
|
||||||
if flags_delete_mask ~= 0 then
|
for _, entity in archetype.entities do
|
||||||
for _, entity in archetype.entities do
|
print(`*deleted dependant {pe(entity)} of {pe(e)}`)
|
||||||
print(`*deleted dependant {pe(entity)} of {pe(e)}`)
|
pad()
|
||||||
pad()
|
end
|
||||||
end
|
break
|
||||||
break
|
else
|
||||||
else
|
for _, entity in archetype.entities do
|
||||||
for _, entity in archetype.entities do
|
print(
|
||||||
print(`*removed dependency ({pe(jecs.pair_first(world, id))}, {pe(object)}) from {pe(entity)}`)
|
`*removed dependency ({pe(jecs.pair_first(world, id))}, {pe(object)}) from {pe(entity)}`
|
||||||
end
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
print(`*deleted {pe(e)}`)
|
|
||||||
pad()
|
print(`*deleted {pe(e)}`)
|
||||||
end
|
pad()
|
||||||
return w
|
end
|
||||||
end
|
return w
|
||||||
|
end
|
||||||
return lifetime_tracker_add
|
|
||||||
|
return lifetime_tracker_add
|
||||||
|
|
|
@ -1,108 +1,108 @@
|
||||||
local function dbg_info(n: number): any
|
local function dbg_info(n: number): any
|
||||||
return debug.info(n, "s")
|
return debug.info(n, "s")
|
||||||
end
|
end
|
||||||
local function throw(msg: string)
|
local function throw(msg: string)
|
||||||
local s = 1
|
local s = 1
|
||||||
local root = dbg_info(1)
|
local root = dbg_info(1)
|
||||||
repeat
|
repeat
|
||||||
s += 1
|
s += 1
|
||||||
until dbg_info(s) ~= root
|
until dbg_info(s) ~= root
|
||||||
if warn then
|
if warn then
|
||||||
error(msg, s)
|
error(msg, s)
|
||||||
else
|
else
|
||||||
print(`[jecs] error: {msg}\n`)
|
print(`[jecs] error: {msg}\n`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ASSERT<T>(v: T, msg: string)
|
local function ASSERT<T>(v: T, msg: string)
|
||||||
if v then
|
if v then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
throw(msg)
|
throw(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function runtime_lints_add(world)
|
local function runtime_lints_add(world)
|
||||||
local function get_name(id)
|
local function get_name(id)
|
||||||
return world_get_one_inline(world, id, EcsName)
|
return world_get_one_inline(world, id, EcsName)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bname(id): string
|
local function bname(id): string
|
||||||
local name: string
|
local name: string
|
||||||
if ECS_IS_PAIR(id) then
|
if ECS_IS_PAIR(id) then
|
||||||
local first = get_name(world, ecs_pair_first(world, id))
|
local first = get_name(world, ecs_pair_first(world, id))
|
||||||
local second = get_name(world, ecs_pair_second(world, id))
|
local second = get_name(world, ecs_pair_second(world, id))
|
||||||
name = `pair({first}, {second})`
|
name = `pair({first}, {second})`
|
||||||
else
|
else
|
||||||
return get_name(world, id)
|
return get_name(world, id)
|
||||||
end
|
end
|
||||||
if name then
|
if name then
|
||||||
return name
|
return name
|
||||||
else
|
else
|
||||||
return `${id}`
|
return `${id}`
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ID_IS_TAG(world: World, id)
|
local function ID_IS_TAG(world: World, id)
|
||||||
if ECS_IS_PAIR(id) then
|
if ECS_IS_PAIR(id) then
|
||||||
id = ecs_pair_first(world, id)
|
id = ecs_pair_first(world, id)
|
||||||
end
|
end
|
||||||
return not world_has_one_inline(world, id, EcsComponent)
|
return not world_has_one_inline(world, id, EcsComponent)
|
||||||
end
|
end
|
||||||
|
|
||||||
World.query = function(world: World, ...)
|
World.query = function(world: World, ...)
|
||||||
ASSERT((...), "Requires at least a single component")
|
ASSERT((...), "Requires at least a single component")
|
||||||
return world_query(world, ...)
|
return world_query(world, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
World.set = function(world: World, entity: i53, id: i53, value: any): ()
|
World.set = function(world: World, entity: i53, id: i53, value: any): ()
|
||||||
local is_tag = ID_IS_TAG(world, id)
|
local is_tag = ID_IS_TAG(world, id)
|
||||||
if is_tag and value == nil then
|
if is_tag and value == nil then
|
||||||
local _1 = bname(world, entity)
|
local _1 = bname(world, entity)
|
||||||
local _2 = bname(world, id)
|
local _2 = bname(world, id)
|
||||||
local why = "cannot set component value to nil"
|
local why = "cannot set component value to nil"
|
||||||
throw(why)
|
throw(why)
|
||||||
return
|
return
|
||||||
elseif value ~= nil and is_tag then
|
elseif value ~= nil and is_tag then
|
||||||
local _1 = bname(world, entity)
|
local _1 = bname(world, entity)
|
||||||
local _2 = bname(world, id)
|
local _2 = bname(world, id)
|
||||||
local why = `cannot set a component value because {_2} is a tag`
|
local why = `cannot set a component value because {_2} is a tag`
|
||||||
why ..= `\n[jecs] note: consider using "world:add({_1}, {_2})" instead`
|
why ..= `\n[jecs] note: consider using "world:add({_1}, {_2})" instead`
|
||||||
throw(why)
|
throw(why)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
world_set(world, entity, id, value)
|
world_set(world, entity, id, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
World.add = function(world: World, entity: i53, id: i53, value: any)
|
World.add = function(world: World, entity: i53, id: i53, value: any)
|
||||||
if value ~= nil then
|
if value ~= nil then
|
||||||
local _1 = bname(world, entity)
|
local _1 = bname(world, entity)
|
||||||
local _2 = bname(world, id)
|
local _2 = bname(world, id)
|
||||||
throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`)
|
throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`)
|
||||||
end
|
end
|
||||||
|
|
||||||
world_add(world, entity, id)
|
world_add(world, entity, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
World.get = function(world: World, entity: i53, ...)
|
World.get = function(world: World, entity: i53, ...)
|
||||||
local length = select("#", ...)
|
local length = select("#", ...)
|
||||||
ASSERT(length < 5, "world:get does not support more than 4 components")
|
ASSERT(length < 5, "world:get does not support more than 4 components")
|
||||||
local _1
|
local _1
|
||||||
for i = 1, length do
|
for i = 1, length do
|
||||||
local id = select(i, ...)
|
local id = select(i, ...)
|
||||||
local id_is_tag = not world_has(world, id, EcsComponent)
|
local id_is_tag = not world_has(world, id, EcsComponent)
|
||||||
if id_is_tag then
|
if id_is_tag then
|
||||||
local name = get_name(world, id)
|
local name = get_name(world, id)
|
||||||
if not _1 then
|
if not _1 then
|
||||||
_1 = get_name(world, entity)
|
_1 = get_name(world, entity)
|
||||||
end
|
end
|
||||||
throw(
|
throw(
|
||||||
`cannot get (#{i}) component {name} value because it is a tag.`
|
`cannot get (#{i}) component {name} value because it is a tag.`
|
||||||
.. `\n[jecs] note: If this was intentional, use "world:has({_1}, {name}) instead"`
|
.. `\n[jecs] note: If this was intentional, use "world:has({_1}, {name}) instead"`
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return world_get(world, entity, ...)
|
return world_get(world, entity, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -534,7 +534,7 @@ return {
|
||||||
FINISH = FINISH,
|
FINISH = FINISH,
|
||||||
SKIP = SKIP,
|
SKIP = SKIP,
|
||||||
FOCUS = FOCUS,
|
FOCUS = FOCUS,
|
||||||
CHECK_EXPECT_ERR = CHECK_EXPECT_ERR
|
CHECK_EXPECT_ERR = CHECK_EXPECT_ERR,
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|