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
|
||||
about: File a bug report for any behavior that you believe is unintentional or problematic
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
This is a good place to put rbxl files or scripts that help explain your reproduction steps.
|
||||
|
||||
## Expected Behavior
|
||||
What you expect to happen
|
||||
|
||||
## Actual Behavior
|
||||
What actually happens
|
||||
---
|
||||
name: Bug report
|
||||
about: File a bug report for any behavior that you believe is unintentional or problematic
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
This is a good place to put rbxl files or scripts that help explain your reproduction steps.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
What you expect to happen
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
What actually happens
|
||||
|
|
9
.github/ISSUE_TEMPLATE/DOCUMENTATION.md
vendored
|
@ -1,14 +1,15 @@
|
|||
---
|
||||
name: Documentation
|
||||
about: Open an issue to add, change, or otherwise modify any part of the documentation.
|
||||
title: "[DOCS]"
|
||||
title: ""
|
||||
labels: documentation
|
||||
assignees: ''
|
||||
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Which Sections Does This Issue Cover?
|
||||
|
||||
[Put sections (e.g. Query Concepts), page links, etc as necessary]
|
||||
|
||||
## 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
|
||||
about: File a feature request for something you believe should be added to Jecs
|
||||
title: "[FEATURE]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Describe your Feature
|
||||
|
||||
You should explain your feature here, and the motivation for why you want it.
|
||||
|
||||
## Implementation
|
||||
|
||||
Explain how you would implement your feature here. Provide relevant API examples and such here (if applicable).
|
||||
|
||||
## Alternatives
|
||||
|
||||
What other alternative implementations or otherwise relevant information is important to why you decided to go with this specific implementation?
|
||||
|
||||
## Considerations
|
||||
|
||||
Some questions that need to be answered include the following:
|
||||
- Will old code break in response to this feature?
|
||||
- What are the performance impacts with this feature (if any)?
|
||||
- How is it useful to include?
|
||||
---
|
||||
name: Feature Request
|
||||
about: File a feature request for something you believe should be added to Jecs
|
||||
title: ""
|
||||
labels: enhancement
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Describe your Feature
|
||||
|
||||
You should explain your feature here, and the motivation for why you want it.
|
||||
|
||||
## Implementation
|
||||
|
||||
Explain how you would implement your feature here. Provide relevant API examples and such here (if applicable).
|
||||
|
||||
## Alternatives
|
||||
|
||||
What other alternative implementations or otherwise relevant information is important to why you decided to go with this specific implementation?
|
||||
|
||||
## Considerations
|
||||
|
||||
Some questions that need to be answered include the following:
|
||||
|
||||
- Will old code break in response to this feature?
|
||||
- What are the performance impacts with this feature (if any)?
|
||||
- How is it useful to include?
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
syntax = "All"
|
||||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Tabs"
|
||||
indent_width = 4
|
||||
quote_style = "ForceDouble"
|
||||
quote_style = "AutoPreferDouble"
|
||||
call_parentheses = "Always"
|
||||
space_after_function_names = "Never"
|
||||
collapse_simple_statement = "Never"
|
||||
syntax = "Luau"
|
||||
|
||||
[sort_requires]
|
||||
enabled = true
|
|
@ -1,5 +1,5 @@
|
|||
local jecs = require("@jecs")
|
||||
local mirror = require("../mirror/init")
|
||||
local mirror = require("@mirror")
|
||||
|
||||
type i53 = number
|
||||
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
--!optimize 2
|
||||
--!native
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||
local jecs = require(ReplicatedStorage.Lib)
|
||||
local pair = jecs.pair
|
||||
local ecs = jecs.World.new()
|
||||
local mirror = require(ReplicatedStorage.mirror)
|
||||
local mcs = mirror.World.new()
|
||||
|
||||
local C1 = ecs:component()
|
||||
local C2 = ecs:entity()
|
||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C3 = ecs:entity()
|
||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C4 = ecs:entity()
|
||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E1 = mcs:component()
|
||||
local E2 = mcs:entity()
|
||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E3 = mcs:entity()
|
||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E4 = mcs:entity()
|
||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
|
||||
return {
|
||||
ParameterGenerator = function()
|
||||
end,
|
||||
|
||||
Functions = {
|
||||
Mirror = function()
|
||||
local m = mcs:entity()
|
||||
for i = 1, 100 do
|
||||
mcs:add(m, E3)
|
||||
mcs:remove(m, E3)
|
||||
end
|
||||
end,
|
||||
|
||||
Jecs = function()
|
||||
local j = ecs:entity()
|
||||
for i = 1, 100 do
|
||||
ecs:add(j, C3)
|
||||
ecs:remove(j, C3)
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
--!optimize 2
|
||||
--!native
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||
local jecs = require(ReplicatedStorage.Lib)
|
||||
local pair = jecs.pair
|
||||
local ecs = jecs.World.new()
|
||||
local mirror = require(ReplicatedStorage.mirror)
|
||||
local mcs = mirror.World.new()
|
||||
|
||||
local C1 = ecs:component()
|
||||
local C2 = ecs:entity()
|
||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C3 = ecs:entity()
|
||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C4 = ecs:entity()
|
||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E1 = mcs:component()
|
||||
local E2 = mcs:entity()
|
||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E3 = mcs:entity()
|
||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E4 = mcs:entity()
|
||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
|
||||
return {
|
||||
ParameterGenerator = function()
|
||||
end,
|
||||
|
||||
Functions = {
|
||||
Mirror = function()
|
||||
local m = mcs:entity()
|
||||
for i = 1, 100 do
|
||||
mcs:add(m, E3)
|
||||
mcs:remove(m, E3)
|
||||
end
|
||||
end,
|
||||
|
||||
Jecs = function()
|
||||
local j = ecs:entity()
|
||||
for i = 1, 100 do
|
||||
ecs:add(j, C3)
|
||||
ecs:remove(j, C3)
|
||||
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 function trackers_invoke(event, component, entity, ...)
|
||||
local trackers = events[event][component]
|
||||
if not trackers then
|
||||
return
|
||||
end
|
||||
|
||||
for _, tracker in trackers do
|
||||
tracker(entity, data)
|
||||
end
|
||||
end
|
||||
|
||||
local function trackers_init(event, component, fn)
|
||||
local ob = events[event]
|
||||
|
||||
return {
|
||||
connect = function(component, fn)
|
||||
local trackers = ob[component]
|
||||
if not trackers then
|
||||
trackers = {}
|
||||
ob[component] = trackers
|
||||
end
|
||||
|
||||
table.insert(trackers, fn)
|
||||
end,
|
||||
invoke = function(component, ...)
|
||||
trackers_invoke(event, component, ...)
|
||||
end
|
||||
}
|
||||
return function(component, fn)
|
||||
local trackers = ob[component]
|
||||
if not trackers then
|
||||
trackers = {}
|
||||
ob[component] = trackers
|
||||
end
|
||||
|
||||
table.insert(trackers, fn)
|
||||
end
|
||||
end
|
||||
|
||||
local trackers = {
|
||||
emplace = trackers_init("emplace"),
|
||||
add = trackers_init("added"),
|
||||
remove = trackers_init("removed")
|
||||
}
|
||||
|
||||
return trackers
|
||||
local events = {}
|
||||
|
||||
local function trackers_invoke(event, component, entity, ...)
|
||||
local trackers = events[event][component]
|
||||
if not trackers then
|
||||
return
|
||||
end
|
||||
|
||||
for _, tracker in trackers do
|
||||
tracker(entity, data)
|
||||
end
|
||||
end
|
||||
|
||||
local function trackers_init(event, component, fn)
|
||||
local ob = events[event]
|
||||
|
||||
return {
|
||||
connect = function(component, fn)
|
||||
local trackers = ob[component]
|
||||
if not trackers then
|
||||
trackers = {}
|
||||
ob[component] = trackers
|
||||
end
|
||||
|
||||
table.insert(trackers, fn)
|
||||
end,
|
||||
invoke = function(component, ...)
|
||||
trackers_invoke(event, component, ...)
|
||||
end
|
||||
}
|
||||
return function(component, fn)
|
||||
local trackers = ob[component]
|
||||
if not trackers then
|
||||
trackers = {}
|
||||
ob[component] = trackers
|
||||
end
|
||||
|
||||
table.insert(trackers, fn)
|
||||
end
|
||||
end
|
||||
|
||||
local trackers = {
|
||||
emplace = trackers_init("emplace"),
|
||||
add = trackers_init("added"),
|
||||
remove = trackers_init("removed")
|
||||
}
|
||||
|
||||
return trackers
|
||||
|
|
|
@ -1,67 +1,67 @@
|
|||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local jecs = require(ReplicatedStorage.ecs)
|
||||
local __ = jecs.Wildcard
|
||||
local std = ReplicatedStorage.std
|
||||
|
||||
local world = require(std.world)
|
||||
|
||||
local Position = world:component() :: jecs.Entity<vector>
|
||||
local Previous = jecs.Rest
|
||||
local pre = jecs.pair(Position, Previous)
|
||||
|
||||
local added = world
|
||||
:query(Position)
|
||||
:without(pre)
|
||||
:cached()
|
||||
local changed = world
|
||||
:query(Position, pre)
|
||||
:cached()
|
||||
local removed = world
|
||||
:query(pre)
|
||||
:without(Position)
|
||||
:cached()
|
||||
|
||||
local children = {}
|
||||
for i = 1, 10 do
|
||||
local e = world:entity()
|
||||
world:set(e, Position, vector.create(i, i, i))
|
||||
table.insert(children, e)
|
||||
end
|
||||
local function flip()
|
||||
return math.random() > 0.5
|
||||
end
|
||||
local function system()
|
||||
for i, child in children do
|
||||
world:set(child, Position, vector.create(i,i,i))
|
||||
end
|
||||
for e, p in added:iter() do
|
||||
world:set(e, pre, p)
|
||||
end
|
||||
for i, child in children do
|
||||
if flip() then
|
||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||
end
|
||||
end
|
||||
for e, new, old in changed:iter() do
|
||||
if new ~= old then
|
||||
world:set(e, pre, new)
|
||||
end
|
||||
end
|
||||
|
||||
for i, child in children do
|
||||
world:remove(child, Position)
|
||||
end
|
||||
|
||||
for e in removed:iter() do
|
||||
world:remove(e, pre)
|
||||
end
|
||||
end
|
||||
local scheduler = require(std.scheduler)
|
||||
|
||||
scheduler.SYSTEM(system)
|
||||
|
||||
return 0
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local jecs = require(ReplicatedStorage.ecs)
|
||||
local __ = jecs.Wildcard
|
||||
local std = ReplicatedStorage.std
|
||||
|
||||
local world = require(std.world)
|
||||
|
||||
local Position = world:component() :: jecs.Entity<vector>
|
||||
local Previous = jecs.Rest
|
||||
local pre = jecs.pair(Position, Previous)
|
||||
|
||||
local added = world
|
||||
:query(Position)
|
||||
:without(pre)
|
||||
:cached()
|
||||
local changed = world
|
||||
:query(Position, pre)
|
||||
:cached()
|
||||
local removed = world
|
||||
:query(pre)
|
||||
:without(Position)
|
||||
:cached()
|
||||
|
||||
local children = {}
|
||||
for i = 1, 10 do
|
||||
local e = world:entity()
|
||||
world:set(e, Position, vector.create(i, i, i))
|
||||
table.insert(children, e)
|
||||
end
|
||||
local function flip()
|
||||
return math.random() > 0.5
|
||||
end
|
||||
local function system()
|
||||
for i, child in children do
|
||||
world:set(child, Position, vector.create(i,i,i))
|
||||
end
|
||||
for e, p in added:iter() do
|
||||
world:set(e, pre, p)
|
||||
end
|
||||
for i, child in children do
|
||||
if flip() then
|
||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||
end
|
||||
end
|
||||
for e, new, old in changed:iter() do
|
||||
if new ~= old then
|
||||
world:set(e, pre, new)
|
||||
end
|
||||
end
|
||||
|
||||
for i, child in children do
|
||||
world:remove(child, Position)
|
||||
end
|
||||
|
||||
for e in removed:iter() do
|
||||
world:remove(e, pre)
|
||||
end
|
||||
end
|
||||
local scheduler = require(std.scheduler)
|
||||
|
||||
scheduler.SYSTEM(system)
|
||||
|
||||
return 0
|
||||
|
|
|
@ -1,90 +1,90 @@
|
|||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local jecs = require(ReplicatedStorage.ecs)
|
||||
local __ = jecs.Wildcard
|
||||
local std = ReplicatedStorage.std
|
||||
|
||||
local world = require(std.world)
|
||||
|
||||
local Position = world:component() :: jecs.Entity<vector>
|
||||
local Previous = jecs.Rest
|
||||
local pre = jecs.pair(Position, Previous)
|
||||
|
||||
local added = world
|
||||
:query(Position)
|
||||
:without(pre)
|
||||
:cached()
|
||||
local changed = world
|
||||
:query(Position, pre)
|
||||
:cached()
|
||||
local removed = world
|
||||
:query(pre)
|
||||
:without(Position)
|
||||
:cached()
|
||||
|
||||
local children = {}
|
||||
for i = 1, 10 do
|
||||
local e = world:entity()
|
||||
world:set(e, Position, vector.create(i, i, i))
|
||||
table.insert(children, e)
|
||||
end
|
||||
local function flip()
|
||||
return math.random() > 0.5
|
||||
end
|
||||
local entity_index = world.entity_index
|
||||
local function copy(archetypes, id)
|
||||
for _, archetype in archetypes do
|
||||
|
||||
local to = jecs.archetype_traverse_add(world, pre, archetype)
|
||||
local columns = to.columns
|
||||
local records = to.records
|
||||
local old = columns[records[pre].column]
|
||||
local new = columns[records[id].column]
|
||||
|
||||
if to ~= archetype then
|
||||
for _, entity in archetype.entities do
|
||||
local r = jecs.entity_index_try_get_fast(entity_index, entity)
|
||||
jecs.entity_move(entity_index, entity, r, to)
|
||||
end
|
||||
end
|
||||
|
||||
table.move(new, 1, #new, 1, old)
|
||||
|
||||
end
|
||||
end
|
||||
local function system2()
|
||||
for i, child in children do
|
||||
world:set(child, Position, vector.create(i,i,i))
|
||||
end
|
||||
for e, p in added:iter() do
|
||||
end
|
||||
copy(added:archetypes(), Position)
|
||||
for i, child in children do
|
||||
if flip() then
|
||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||
end
|
||||
end
|
||||
|
||||
for e, new, old in changed:iter() do
|
||||
if new ~= old then
|
||||
end
|
||||
end
|
||||
|
||||
copy(changed:archetypes(), Position)
|
||||
|
||||
for i, child in children do
|
||||
world:remove(child, Position)
|
||||
end
|
||||
|
||||
for e in removed:iter() do
|
||||
world:remove(e, pre)
|
||||
end
|
||||
end
|
||||
local scheduler = require(std.scheduler)
|
||||
|
||||
scheduler.SYSTEM(system2)
|
||||
|
||||
return 0
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local jecs = require(ReplicatedStorage.ecs)
|
||||
local __ = jecs.Wildcard
|
||||
local std = ReplicatedStorage.std
|
||||
|
||||
local world = require(std.world)
|
||||
|
||||
local Position = world:component() :: jecs.Entity<vector>
|
||||
local Previous = jecs.Rest
|
||||
local pre = jecs.pair(Position, Previous)
|
||||
|
||||
local added = world
|
||||
:query(Position)
|
||||
:without(pre)
|
||||
:cached()
|
||||
local changed = world
|
||||
:query(Position, pre)
|
||||
:cached()
|
||||
local removed = world
|
||||
:query(pre)
|
||||
:without(Position)
|
||||
:cached()
|
||||
|
||||
local children = {}
|
||||
for i = 1, 10 do
|
||||
local e = world:entity()
|
||||
world:set(e, Position, vector.create(i, i, i))
|
||||
table.insert(children, e)
|
||||
end
|
||||
local function flip()
|
||||
return math.random() > 0.5
|
||||
end
|
||||
local entity_index = world.entity_index
|
||||
local function copy(archetypes, id)
|
||||
for _, archetype in archetypes do
|
||||
|
||||
local to = jecs.archetype_traverse_add(world, pre, archetype)
|
||||
local columns = to.columns
|
||||
local records = to.records
|
||||
local old = columns[records[pre].column]
|
||||
local new = columns[records[id].column]
|
||||
|
||||
if to ~= archetype then
|
||||
for _, entity in archetype.entities do
|
||||
local r = jecs.entity_index_try_get_fast(entity_index, entity)
|
||||
jecs.entity_move(entity_index, entity, r, to)
|
||||
end
|
||||
end
|
||||
|
||||
table.move(new, 1, #new, 1, old)
|
||||
|
||||
end
|
||||
end
|
||||
local function system2()
|
||||
for i, child in children do
|
||||
world:set(child, Position, vector.create(i,i,i))
|
||||
end
|
||||
for e, p in added:iter() do
|
||||
end
|
||||
copy(added:archetypes(), Position)
|
||||
for i, child in children do
|
||||
if flip() then
|
||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||
end
|
||||
end
|
||||
|
||||
for e, new, old in changed:iter() do
|
||||
if new ~= old then
|
||||
end
|
||||
end
|
||||
|
||||
copy(changed:archetypes(), Position)
|
||||
|
||||
for i, child in children do
|
||||
world:remove(child, Position)
|
||||
end
|
||||
|
||||
for e in removed:iter() do
|
||||
world:remove(e, pre)
|
||||
end
|
||||
end
|
||||
local scheduler = require(std.scheduler)
|
||||
|
||||
scheduler.SYSTEM(system2)
|
||||
|
||||
return 0
|
||||
|
|
15
jecs.luau
|
@ -52,7 +52,7 @@ export type Archetype = {
|
|||
type ecs_record_t = {
|
||||
archetype: ecs_archetype_t,
|
||||
row: number,
|
||||
dense: i24
|
||||
dense: i24,
|
||||
}
|
||||
|
||||
type ecs_id_record_t = {
|
||||
|
@ -90,8 +90,8 @@ type ecs_query_data_t = {
|
|||
}
|
||||
|
||||
type ecs_observer_t = {
|
||||
callback: (archetype: ecs_archetype_t) -> (),
|
||||
query: ecs_query_data_t,
|
||||
callback: (archetype: ecs_archetype_t) -> (),
|
||||
query: ecs_query_data_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)
|
||||
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,
|
||||
archetype: ecs_archetype_t)
|
||||
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 is_pair = ECS_IS_PAIR(id)
|
||||
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(
|
||||
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(
|
||||
entity_index, target), ECS_INTERNAL_ERROR)
|
||||
end
|
||||
|
@ -646,7 +642,6 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
|
|||
local records: { number } = {}
|
||||
local counts: {number} = {}
|
||||
|
||||
local entity_index = world.entity_index
|
||||
local archetype: ecs_archetype_t = {
|
||||
columns = columns,
|
||||
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"
|
||||
rojo = "rojo-rbx/rojo@7.4.4"
|
||||
stylua = "johnnymorganz/stylua@2.0.1"
|
||||
selene = "kampfkarren/selene@0.27.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 pair = jecs.pair
|
||||
local ChildOf = jecs.ChildOf
|
||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||
local pe = require("@tools/entity_visualiser").prettify
|
||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
||||
local FriendsWith = world:component()
|
||||
world:print_snapshot()
|
||||
local e1 = world:entity()
|
||||
local e2 = world:entity()
|
||||
world:delete(e2)
|
||||
|
||||
world:print_snapshot()
|
||||
local e3 = world:entity()
|
||||
world:add(e3, pair(ChildOf, e1))
|
||||
local e4 = world:entity()
|
||||
world:add(e4, pair(FriendsWith, e3))
|
||||
world:print_snapshot()
|
||||
world:delete(e1)
|
||||
world:delete(e3)
|
||||
world:print_snapshot()
|
||||
world:print_entity_index()
|
||||
world:entity()
|
||||
world:entity()
|
||||
world:print_snapshot()
|
||||
local jecs = require("@jecs")
|
||||
local pair = jecs.pair
|
||||
local ChildOf = jecs.ChildOf
|
||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||
local pe = require("@tools/entity_visualiser").prettify
|
||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
||||
local FriendsWith = world:component()
|
||||
world:print_snapshot()
|
||||
local e1 = world:entity()
|
||||
local e2 = world:entity()
|
||||
world:delete(e2)
|
||||
|
||||
world:print_snapshot()
|
||||
local e3 = world:entity()
|
||||
world:add(e3, pair(ChildOf, e1))
|
||||
local e4 = world:entity()
|
||||
world:add(e4, pair(FriendsWith, e3))
|
||||
world:print_snapshot()
|
||||
world:delete(e1)
|
||||
world:delete(e3)
|
||||
world:print_snapshot()
|
||||
world:print_entity_index()
|
||||
world:entity()
|
||||
world:entity()
|
||||
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 = {
|
||||
white_underline = function(s: any)
|
||||
return `\27[1;4m{s}\27[0m`
|
||||
end,
|
||||
|
||||
white = function(s: any)
|
||||
return `\27[37;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green = function(s: any)
|
||||
return `\27[32;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red = function(s: any)
|
||||
return `\27[31;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
yellow = function(s: any)
|
||||
return `\27[33;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red_highlight = function(s: any)
|
||||
return `\27[41;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green_highlight = function(s: any)
|
||||
return `\27[42;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
gray = function(s: any)
|
||||
return `\27[30;1m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
local ECS_PAIR_FLAG = 0x8
|
||||
local ECS_ID_FLAGS_MASK = 0x10
|
||||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||
|
||||
type i53 = number
|
||||
type i24 = number
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
local function ECS_GENERATION(e: i53): i24
|
||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
|
||||
end
|
||||
|
||||
local ECS_ID = ECS_ENTITY_T_LO
|
||||
|
||||
local function ECS_COMBINE(source: number, target: number): i53
|
||||
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
|
||||
end
|
||||
|
||||
local function ECS_GENERATION_INC(e: i53)
|
||||
if e > ECS_ENTITY_MASK then
|
||||
local flags = e // ECS_ID_FLAGS_MASK
|
||||
local id = flags // ECS_ENTITY_MASK
|
||||
local generation = flags % ECS_GENERATION_MASK
|
||||
|
||||
local next_gen = generation + 1
|
||||
if next_gen > ECS_GENERATION_MASK then
|
||||
return id
|
||||
end
|
||||
|
||||
return ECS_COMBINE(id, next_gen) + flags
|
||||
end
|
||||
return ECS_COMBINE(e, 1)
|
||||
end
|
||||
|
||||
local function bl()
|
||||
print("")
|
||||
end
|
||||
|
||||
local function pe(e)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function dprint(tbl: { [number]: number })
|
||||
bl()
|
||||
print("--------")
|
||||
for i, e in tbl do
|
||||
print("| "..pe(e).." |")
|
||||
print("--------")
|
||||
end
|
||||
bl()
|
||||
end
|
||||
|
||||
local max_id = 0
|
||||
local alive_count = 0
|
||||
local dense = {}
|
||||
local sparse = {}
|
||||
local function alloc()
|
||||
if alive_count ~= #dense then
|
||||
alive_count += 1
|
||||
print("*recycled", pe(dense[alive_count]))
|
||||
return dense[alive_count]
|
||||
end
|
||||
max_id += 1
|
||||
local id = max_id
|
||||
alive_count += 1
|
||||
dense[alive_count] = id
|
||||
sparse[id] = {
|
||||
dense = alive_count
|
||||
}
|
||||
print("*allocated", pe(id))
|
||||
return id
|
||||
end
|
||||
|
||||
local function remove(entity)
|
||||
local id = ECS_ID(entity)
|
||||
local r = sparse[id]
|
||||
local index_of_deleted_entity = r.dense
|
||||
local last_entity_alive_at_index = alive_count -- last entity alive
|
||||
alive_count -= 1
|
||||
local last_alive_entity = dense[last_entity_alive_at_index]
|
||||
local r_swap = sparse[ECS_ID(last_alive_entity)]
|
||||
r_swap.dense = r.dense
|
||||
r.dense = last_entity_alive_at_index
|
||||
dense[index_of_deleted_entity] = last_alive_entity
|
||||
dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
|
||||
print("*dellocated", pe(id))
|
||||
end
|
||||
|
||||
local function alive(e)
|
||||
local r = sparse[ECS_ID(e)]
|
||||
|
||||
return dense[r.dense] == e
|
||||
end
|
||||
|
||||
local function pa(e)
|
||||
print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
|
||||
end
|
||||
|
||||
local tprint = require("@testkit").print
|
||||
local e1v0 = alloc()
|
||||
local e2v0 = alloc()
|
||||
local e3v0 = alloc()
|
||||
local e4v0 = alloc()
|
||||
local e5v0 = alloc()
|
||||
pa(e1v0)
|
||||
pa(e4v0)
|
||||
remove(e5v0)
|
||||
pa(e5v0)
|
||||
|
||||
local e5v1 = alloc()
|
||||
pa(e5v0)
|
||||
pa(e5v1)
|
||||
pa(e2v0)
|
||||
print(ECS_ID(e2v0))
|
||||
|
||||
dprint(dense)
|
||||
remove(e2v0)
|
||||
dprint(dense)
|
||||
local c = {
|
||||
white_underline = function(s: any)
|
||||
return `\27[1;4m{s}\27[0m`
|
||||
end,
|
||||
|
||||
white = function(s: any)
|
||||
return `\27[37;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green = function(s: any)
|
||||
return `\27[32;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red = function(s: any)
|
||||
return `\27[31;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
yellow = function(s: any)
|
||||
return `\27[33;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red_highlight = function(s: any)
|
||||
return `\27[41;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green_highlight = function(s: any)
|
||||
return `\27[42;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
gray = function(s: any)
|
||||
return `\27[30;1m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
local ECS_PAIR_FLAG = 0x8
|
||||
local ECS_ID_FLAGS_MASK = 0x10
|
||||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||
|
||||
type i53 = number
|
||||
type i24 = number
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
local function ECS_GENERATION(e: i53): i24
|
||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
|
||||
end
|
||||
|
||||
local ECS_ID = ECS_ENTITY_T_LO
|
||||
|
||||
local function ECS_COMBINE(source: number, target: number): i53
|
||||
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
|
||||
end
|
||||
|
||||
local function ECS_GENERATION_INC(e: i53)
|
||||
if e > ECS_ENTITY_MASK then
|
||||
local flags = e // ECS_ID_FLAGS_MASK
|
||||
local id = flags // ECS_ENTITY_MASK
|
||||
local generation = flags % ECS_GENERATION_MASK
|
||||
|
||||
local next_gen = generation + 1
|
||||
if next_gen > ECS_GENERATION_MASK then
|
||||
return id
|
||||
end
|
||||
|
||||
return ECS_COMBINE(id, next_gen) + flags
|
||||
end
|
||||
return ECS_COMBINE(e, 1)
|
||||
end
|
||||
|
||||
local function bl()
|
||||
print("")
|
||||
end
|
||||
|
||||
local function pe(e)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function dprint(tbl: { [number]: number })
|
||||
bl()
|
||||
print("--------")
|
||||
for i, e in tbl do
|
||||
print("| "..pe(e).." |")
|
||||
print("--------")
|
||||
end
|
||||
bl()
|
||||
end
|
||||
|
||||
local max_id = 0
|
||||
local alive_count = 0
|
||||
local dense = {}
|
||||
local sparse = {}
|
||||
local function alloc()
|
||||
if alive_count ~= #dense then
|
||||
alive_count += 1
|
||||
print("*recycled", pe(dense[alive_count]))
|
||||
return dense[alive_count]
|
||||
end
|
||||
max_id += 1
|
||||
local id = max_id
|
||||
alive_count += 1
|
||||
dense[alive_count] = id
|
||||
sparse[id] = {
|
||||
dense = alive_count
|
||||
}
|
||||
print("*allocated", pe(id))
|
||||
return id
|
||||
end
|
||||
|
||||
local function remove(entity)
|
||||
local id = ECS_ID(entity)
|
||||
local r = sparse[id]
|
||||
local index_of_deleted_entity = r.dense
|
||||
local last_entity_alive_at_index = alive_count -- last entity alive
|
||||
alive_count -= 1
|
||||
local last_alive_entity = dense[last_entity_alive_at_index]
|
||||
local r_swap = sparse[ECS_ID(last_alive_entity)]
|
||||
r_swap.dense = r.dense
|
||||
r.dense = last_entity_alive_at_index
|
||||
dense[index_of_deleted_entity] = last_alive_entity
|
||||
dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
|
||||
print("*dellocated", pe(id))
|
||||
end
|
||||
|
||||
local function alive(e)
|
||||
local r = sparse[ECS_ID(e)]
|
||||
|
||||
return dense[r.dense] == e
|
||||
end
|
||||
|
||||
local function pa(e)
|
||||
print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
|
||||
end
|
||||
|
||||
local tprint = require("@testkit").print
|
||||
local e1v0 = alloc()
|
||||
local e2v0 = alloc()
|
||||
local e3v0 = alloc()
|
||||
local e4v0 = alloc()
|
||||
local e5v0 = alloc()
|
||||
pa(e1v0)
|
||||
pa(e4v0)
|
||||
remove(e5v0)
|
||||
pa(e5v0)
|
||||
|
||||
local e5v1 = alloc()
|
||||
pa(e5v0)
|
||||
pa(e5v1)
|
||||
pa(e2v0)
|
||||
print(ECS_ID(e2v0))
|
||||
|
||||
dprint(dense)
|
||||
remove(e2v0)
|
||||
dprint(dense)
|
||||
|
|
|
@ -1,122 +1,122 @@
|
|||
local RunService = game:GetService("RunService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
_G.__JECS_HI_COMPONENT_ID = 300
|
||||
local ecs = require(ReplicatedStorage.ecs)
|
||||
|
||||
-- 500 entities
|
||||
-- 2-30 components on each entity
|
||||
-- 300 unique components
|
||||
-- 200 systems
|
||||
-- 1-10 components to query per system
|
||||
|
||||
local startTime = os.clock()
|
||||
|
||||
local world = ecs.World.new()
|
||||
|
||||
local components = {}
|
||||
|
||||
for i = 1, 300 do -- 300 components
|
||||
components[i] = world:component()
|
||||
end
|
||||
|
||||
local archetypes = {}
|
||||
for i = 1, 50 do -- 50 archetypes
|
||||
local archetype = {}
|
||||
|
||||
for _ = 1, math.random(2, 30) do
|
||||
local componentId = math.random(1, #components)
|
||||
|
||||
table.insert(archetype, components[componentId])
|
||||
end
|
||||
|
||||
archetypes[i] = archetype
|
||||
end
|
||||
|
||||
for _ = 1, 1000 do -- 1000 entities in the world
|
||||
local componentsToAdd = {}
|
||||
|
||||
local archetypeId = math.random(1, #archetypes)
|
||||
local e = world:entity()
|
||||
for _, component in ipairs(archetypes[archetypeId]) do
|
||||
world:set(e, component, {
|
||||
DummyData = math.random(1, 5000),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function values(t)
|
||||
local array = {}
|
||||
for _, v in t do
|
||||
table.insert(array, v)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
local contiguousComponents = values(components)
|
||||
local systemComponentsToQuery = {}
|
||||
|
||||
for _ = 1, 200 do -- 200 systems
|
||||
local numComponentsToQuery = math.random(1, 10)
|
||||
local componentsToQuery = {}
|
||||
|
||||
for _ = 1, numComponentsToQuery do
|
||||
table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
|
||||
end
|
||||
|
||||
table.insert(systemComponentsToQuery, componentsToQuery)
|
||||
end
|
||||
|
||||
local worldCreateTime = os.clock() - startTime
|
||||
local results = {}
|
||||
startTime = os.clock()
|
||||
|
||||
RunService.Heartbeat:Connect(function()
|
||||
local added = 0
|
||||
local systemStartTime = os.clock()
|
||||
debug.profilebegin("systems")
|
||||
for _, componentsToQuery in ipairs(systemComponentsToQuery) do
|
||||
debug.profilebegin("system")
|
||||
for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
|
||||
world:set(
|
||||
entityId,
|
||||
{
|
||||
DummyData = firstComponent.DummyData + 1,
|
||||
}
|
||||
)
|
||||
added += 1
|
||||
end
|
||||
debug.profileend()
|
||||
end
|
||||
debug.profileend()
|
||||
|
||||
if os.clock() - startTime < 4 then
|
||||
-- discard first 4 seconds
|
||||
return
|
||||
end
|
||||
|
||||
if results == nil then
|
||||
return
|
||||
elseif #results < 1000 then
|
||||
table.insert(results, os.clock() - systemStartTime)
|
||||
else
|
||||
print("added", added)
|
||||
print("World created in", worldCreateTime * 1000, "ms")
|
||||
local sum = 0
|
||||
for _, result in ipairs(results) do
|
||||
sum += result
|
||||
end
|
||||
print(("Average frame time: %fms"):format((sum / #results) * 1000))
|
||||
|
||||
results = nil
|
||||
|
||||
local n = #world.archetypes
|
||||
|
||||
print(
|
||||
("X entities\n%d components\n%d systems\n%d archetypes"):format(
|
||||
#components,
|
||||
#systemComponentsToQuery,
|
||||
n
|
||||
)
|
||||
)
|
||||
end
|
||||
end)
|
||||
local RunService = game:GetService("RunService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
_G.__JECS_HI_COMPONENT_ID = 300
|
||||
local ecs = require(ReplicatedStorage.ecs)
|
||||
|
||||
-- 500 entities
|
||||
-- 2-30 components on each entity
|
||||
-- 300 unique components
|
||||
-- 200 systems
|
||||
-- 1-10 components to query per system
|
||||
|
||||
local startTime = os.clock()
|
||||
|
||||
local world = ecs.World.new()
|
||||
|
||||
local components = {}
|
||||
|
||||
for i = 1, 300 do -- 300 components
|
||||
components[i] = world:component()
|
||||
end
|
||||
|
||||
local archetypes = {}
|
||||
for i = 1, 50 do -- 50 archetypes
|
||||
local archetype = {}
|
||||
|
||||
for _ = 1, math.random(2, 30) do
|
||||
local componentId = math.random(1, #components)
|
||||
|
||||
table.insert(archetype, components[componentId])
|
||||
end
|
||||
|
||||
archetypes[i] = archetype
|
||||
end
|
||||
|
||||
for _ = 1, 1000 do -- 1000 entities in the world
|
||||
local componentsToAdd = {}
|
||||
|
||||
local archetypeId = math.random(1, #archetypes)
|
||||
local e = world:entity()
|
||||
for _, component in ipairs(archetypes[archetypeId]) do
|
||||
world:set(e, component, {
|
||||
DummyData = math.random(1, 5000),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function values(t)
|
||||
local array = {}
|
||||
for _, v in t do
|
||||
table.insert(array, v)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
local contiguousComponents = values(components)
|
||||
local systemComponentsToQuery = {}
|
||||
|
||||
for _ = 1, 200 do -- 200 systems
|
||||
local numComponentsToQuery = math.random(1, 10)
|
||||
local componentsToQuery = {}
|
||||
|
||||
for _ = 1, numComponentsToQuery do
|
||||
table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
|
||||
end
|
||||
|
||||
table.insert(systemComponentsToQuery, componentsToQuery)
|
||||
end
|
||||
|
||||
local worldCreateTime = os.clock() - startTime
|
||||
local results = {}
|
||||
startTime = os.clock()
|
||||
|
||||
RunService.Heartbeat:Connect(function()
|
||||
local added = 0
|
||||
local systemStartTime = os.clock()
|
||||
debug.profilebegin("systems")
|
||||
for _, componentsToQuery in ipairs(systemComponentsToQuery) do
|
||||
debug.profilebegin("system")
|
||||
for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
|
||||
world:set(
|
||||
entityId,
|
||||
{
|
||||
DummyData = firstComponent.DummyData + 1,
|
||||
}
|
||||
)
|
||||
added += 1
|
||||
end
|
||||
debug.profileend()
|
||||
end
|
||||
debug.profileend()
|
||||
|
||||
if os.clock() - startTime < 4 then
|
||||
-- discard first 4 seconds
|
||||
return
|
||||
end
|
||||
|
||||
if results == nil then
|
||||
return
|
||||
elseif #results < 1000 then
|
||||
table.insert(results, os.clock() - systemStartTime)
|
||||
else
|
||||
print("added", added)
|
||||
print("World created in", worldCreateTime * 1000, "ms")
|
||||
local sum = 0
|
||||
for _, result in ipairs(results) do
|
||||
sum += result
|
||||
end
|
||||
print(("Average frame time: %fms"):format((sum / #results) * 1000))
|
||||
|
||||
results = nil
|
||||
|
||||
local n = #world.archetypes
|
||||
|
||||
print(
|
||||
("X entities\n%d components\n%d systems\n%d archetypes"):format(
|
||||
#components,
|
||||
#systemComponentsToQuery,
|
||||
n
|
||||
)
|
||||
)
|
||||
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 {
|
||||
white_underline = function(s: any)
|
||||
return `\27[1;4m{s}\27[0m`
|
||||
end,
|
||||
|
||||
white = function(s: any)
|
||||
return `\27[37;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green = function(s: any)
|
||||
return `\27[32;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red = function(s: any)
|
||||
return `\27[31;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
yellow = function(s: any)
|
||||
return `\27[33;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red_highlight = function(s: any)
|
||||
return `\27[41;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green_highlight = function(s: any)
|
||||
return `\27[42;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
gray = function(s: any)
|
||||
return `\27[30;1m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
return {
|
||||
white_underline = function(s: any)
|
||||
return `\27[1;4m{s}\27[0m`
|
||||
end,
|
||||
|
||||
white = function(s: any)
|
||||
return `\27[37;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green = function(s: any)
|
||||
return `\27[32;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red = function(s: any)
|
||||
return `\27[31;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
yellow = function(s: any)
|
||||
return `\27[33;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red_highlight = function(s: any)
|
||||
return `\27[41;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green_highlight = function(s: any)
|
||||
return `\27[42;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
gray = function(s: any)
|
||||
return `\27[30;1m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
local jecs = require("@jecs")
|
||||
local ECS_GENERATION = jecs.ECS_GENERATION
|
||||
local ECS_ID = jecs.ECS_ID
|
||||
local ansi = require("@tools/ansi")
|
||||
|
||||
local function pe(e: any)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return ansi.green(`e{ECS_ID(e)}`)..ansi.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function name(world: jecs.World, id: any)
|
||||
return world:get(id, jecs.Name) or `${id}`
|
||||
end
|
||||
|
||||
local function components(world: jecs.World, entity: any)
|
||||
local r = jecs.entity_index_try_get(world.entity_index, entity)
|
||||
if not r then
|
||||
return false
|
||||
end
|
||||
|
||||
local archetype = r.archetype
|
||||
local row = r.row
|
||||
print(`Entity {pe(entity)}`)
|
||||
print("-----------------------------------------------------")
|
||||
for i, column in archetype.columns do
|
||||
local component = archetype.types[i]
|
||||
local n
|
||||
if jecs.IS_PAIR(component) then
|
||||
n = `({name(world, jecs.pair_first(world, component))}, {name(world, jecs.pair_second(world, component))})`
|
||||
else
|
||||
n = name(world, component)
|
||||
end
|
||||
local data = column[row] or "TAG"
|
||||
print(`| {n} | {data} |`)
|
||||
end
|
||||
print("-----------------------------------------------------")
|
||||
return true
|
||||
end
|
||||
|
||||
return {
|
||||
components = components,
|
||||
prettify = pe,
|
||||
}
|
||||
local jecs = require("@jecs")
|
||||
local ECS_GENERATION = jecs.ECS_GENERATION
|
||||
local ECS_ID = jecs.ECS_ID
|
||||
local ansi = require("@tools/ansi")
|
||||
|
||||
local function pe(e: any)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return ansi.green(`e{ECS_ID(e)}`) .. ansi.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function name(world: jecs.World, id: any)
|
||||
return world:get(id, jecs.Name) or `${id}`
|
||||
end
|
||||
|
||||
local function components(world: jecs.World, entity: any)
|
||||
local r = jecs.entity_index_try_get(world.entity_index, entity)
|
||||
if not r then
|
||||
return false
|
||||
end
|
||||
|
||||
local archetype = r.archetype
|
||||
local row = r.row
|
||||
print(`Entity {pe(entity)}`)
|
||||
print("-----------------------------------------------------")
|
||||
for i, column in archetype.columns do
|
||||
local component = archetype.types[i]
|
||||
local n
|
||||
if jecs.IS_PAIR(component) then
|
||||
n = `({name(world, jecs.pair_first(world, component))}, {name(world, jecs.pair_second(world, component))})`
|
||||
else
|
||||
n = name(world, component)
|
||||
end
|
||||
local data = column[row] or "TAG"
|
||||
print(`| {n} | {data} |`)
|
||||
end
|
||||
print("-----------------------------------------------------")
|
||||
return true
|
||||
end
|
||||
|
||||
return {
|
||||
components = components,
|
||||
prettify = pe,
|
||||
}
|
||||
|
|
|
@ -1,215 +1,216 @@
|
|||
local jecs = require("@jecs")
|
||||
local ECS_GENERATION = jecs.ECS_GENERATION
|
||||
local ECS_ID = jecs.ECS_ID
|
||||
local __ = jecs.Wildcard
|
||||
local pair = jecs.pair
|
||||
|
||||
local prettify = require("@tools/entity_visualiser").prettify
|
||||
|
||||
local pe = prettify
|
||||
local ansi = require("@tools/ansi")
|
||||
|
||||
function print_centered_entity(entity, width: number)
|
||||
local entity_str = tostring(entity)
|
||||
local entity_length = #entity_str
|
||||
|
||||
local padding_total = width - 2 - entity_length
|
||||
|
||||
local padding_left = math.floor(padding_total / 2)
|
||||
local padding_right = padding_total - padding_left
|
||||
|
||||
local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
|
||||
|
||||
print("|" .. centered_str .. "|")
|
||||
end
|
||||
|
||||
local function name(world, e)
|
||||
return world:get(world, e, jecs.Name) or pe(e)
|
||||
end
|
||||
local padding_enabled = false
|
||||
local function pad()
|
||||
if padding_enabled then
|
||||
print("")
|
||||
end
|
||||
end
|
||||
|
||||
local function lifetime_tracker_add(world: jecs.World, opt)
|
||||
local entity_index = world.entity_index
|
||||
local dense_array = entity_index.dense_array
|
||||
local component_index = world.component_index
|
||||
|
||||
local ENTITY_RANGE = (jecs.Rest :: any) + 1
|
||||
|
||||
local w = setmetatable({}, { __index = world })
|
||||
|
||||
padding_enabled = opt.padding_enabled
|
||||
|
||||
local world_entity = world.entity
|
||||
w.entity = function(self, entity)
|
||||
if entity then
|
||||
return world_entity(world, entity)
|
||||
end
|
||||
local will_recycle = entity_index.max_id ~= entity_index.alive_count
|
||||
local e = world_entity(world)
|
||||
if will_recycle then
|
||||
print(`*recycled {pe(e)}`)
|
||||
else
|
||||
print(`*created {pe(e)}`)
|
||||
end
|
||||
pad()
|
||||
return e
|
||||
end
|
||||
w.print_entity_index = function(self)
|
||||
local max_id = entity_index.max_id
|
||||
local alive_count = entity_index.alive_count
|
||||
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 sep = "|--------|"
|
||||
if #alive > 0 then
|
||||
print("|-alive--|")
|
||||
for i = 1, #alive do
|
||||
local e = pe(alive[i])
|
||||
print_centered_entity(e, 32)
|
||||
print(sep)
|
||||
end
|
||||
print("\n")
|
||||
end
|
||||
|
||||
if #dead > 0 then
|
||||
print("|--dead--|")
|
||||
for i = 1, #dead do
|
||||
print_centered_entity(pe(dead[i]), 32)
|
||||
print(sep)
|
||||
end
|
||||
end
|
||||
pad()
|
||||
end
|
||||
local timelines = {}
|
||||
w.print_snapshot = function(self)
|
||||
local timeline = #timelines + 1
|
||||
local entity_column_width = 10
|
||||
local status_column_width = 8
|
||||
|
||||
local header = string.format("| %-" .. entity_column_width .. "s |", "Entity")
|
||||
for i = 1, timeline do
|
||||
header = header .. string.format(" %-" .. status_column_width .. "s |", string.format("T%d", i))
|
||||
end
|
||||
|
||||
local max_id = entity_index.max_id
|
||||
local alive_count = entity_index.alive_count
|
||||
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 data = {}
|
||||
print("-------------------------------------------------------------------")
|
||||
print(header)
|
||||
|
||||
-- Store the snapshot data for this timeline
|
||||
for i = ENTITY_RANGE, max_id do
|
||||
if dense_array[i] then
|
||||
local entity = dense_array[i]
|
||||
local id = ECS_ID(entity)
|
||||
local status = "alive"
|
||||
if not world:contains(entity) then
|
||||
status = "dead"
|
||||
end
|
||||
data[id] = status
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(timelines, data)
|
||||
|
||||
-- Create a table to hold entity data for sorting
|
||||
local entities = {}
|
||||
for i = ENTITY_RANGE, max_id do
|
||||
if dense_array[i] then
|
||||
local entity = dense_array[i]
|
||||
local id = ECS_ID(entity)
|
||||
-- Push entity and id into the new `entities` table
|
||||
table.insert(entities, {entity = entity, id = id})
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort the entities by ECS_ID
|
||||
table.sort(entities, function(a, b)
|
||||
return a.id < b.id
|
||||
end)
|
||||
|
||||
-- Print the sorted rows
|
||||
for _, entity_data in ipairs(entities) do
|
||||
local entity = entity_data.entity
|
||||
local id = entity_data.id
|
||||
local status = "alive"
|
||||
if id > alive_count then
|
||||
status = "dead"
|
||||
end
|
||||
local row = string.format("| %-" .. entity_column_width .. "s |", pe(entity))
|
||||
for j = 1, timeline do
|
||||
local timeline_data = timelines[j]
|
||||
local entity_data = timeline_data[id]
|
||||
if entity_data then
|
||||
row = row .. string.format(" %-" .. status_column_width .. "s |", entity_data)
|
||||
else
|
||||
row = row .. string.format(" %-" .. status_column_width .. "s |", "-")
|
||||
end
|
||||
end
|
||||
print(row)
|
||||
end
|
||||
print("-------------------------------------------------------------------")
|
||||
pad()
|
||||
end
|
||||
local world_add = world.add
|
||||
local relations = {}
|
||||
w.add = function(self, entity: any, component: any)
|
||||
world_add(world, entity, component)
|
||||
if jecs.IS_PAIR(component) then
|
||||
local relation = jecs.pair_first(world, component)
|
||||
local target = jecs.pair_second(world, component)
|
||||
print(`*added ({pe(relation)}, {pe(target)}) to {pe(entity)}`)
|
||||
pad()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local world_delete = world.delete
|
||||
w.delete = function(self, e)
|
||||
world_delete(world, e)
|
||||
|
||||
local idr_t = component_index[pair(__, e)]
|
||||
if idr_t then
|
||||
for archetype_id in idr_t.cache do
|
||||
local archetype = world.archetypes[archetype_id]
|
||||
for _, id in archetype.types do
|
||||
if not jecs.IS_PAIR(id) then
|
||||
continue
|
||||
end
|
||||
local object = jecs.pair_second(world, id)
|
||||
if object ~= e then
|
||||
continue
|
||||
end
|
||||
local id_record = component_index[id]
|
||||
local flags = id_record.flags
|
||||
local flags_delete_mask: number = bit32.band(flags, jecs.ECS_ID_DELETE)
|
||||
if flags_delete_mask ~= 0 then
|
||||
for _, entity in archetype.entities do
|
||||
print(`*deleted dependant {pe(entity)} of {pe(e)}`)
|
||||
pad()
|
||||
end
|
||||
break
|
||||
else
|
||||
for _, entity in archetype.entities do
|
||||
print(`*removed dependency ({pe(jecs.pair_first(world, id))}, {pe(object)}) from {pe(entity)}`)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print(`*deleted {pe(e)}`)
|
||||
pad()
|
||||
end
|
||||
return w
|
||||
end
|
||||
|
||||
return lifetime_tracker_add
|
||||
local jecs = require("@jecs")
|
||||
local ECS_GENERATION = jecs.ECS_GENERATION
|
||||
local ECS_ID = jecs.ECS_ID
|
||||
local __ = jecs.Wildcard
|
||||
local pair = jecs.pair
|
||||
|
||||
local prettify = require("@tools/entity_visualiser").prettify
|
||||
|
||||
local pe = prettify
|
||||
local ansi = require("@tools/ansi")
|
||||
|
||||
function print_centered_entity(entity, width: number)
|
||||
local entity_str = tostring(entity)
|
||||
local entity_length = #entity_str
|
||||
|
||||
local padding_total = width - 2 - entity_length
|
||||
|
||||
local padding_left = math.floor(padding_total / 2)
|
||||
local padding_right = padding_total - padding_left
|
||||
|
||||
local centered_str = string.rep(" ", padding_left) .. entity_str .. string.rep(" ", padding_right)
|
||||
|
||||
print("|" .. centered_str .. "|")
|
||||
end
|
||||
|
||||
local function name(world, e)
|
||||
return world:get(world, e, jecs.Name) or pe(e)
|
||||
end
|
||||
local padding_enabled = false
|
||||
local function pad()
|
||||
if padding_enabled then
|
||||
print("")
|
||||
end
|
||||
end
|
||||
|
||||
local function lifetime_tracker_add(world: jecs.World, opt)
|
||||
local entity_index = world.entity_index
|
||||
local dense_array = entity_index.dense_array
|
||||
local component_index = world.component_index
|
||||
|
||||
local ENTITY_RANGE = (jecs.Rest :: any) + 1
|
||||
|
||||
local w = setmetatable({}, { __index = world })
|
||||
|
||||
padding_enabled = opt.padding_enabled
|
||||
|
||||
local world_entity = world.entity
|
||||
w.entity = function(self, entity)
|
||||
if entity then
|
||||
return world_entity(world, entity)
|
||||
end
|
||||
local will_recycle = entity_index.max_id ~= entity_index.alive_count
|
||||
local e = world_entity(world)
|
||||
if will_recycle then
|
||||
print(`*recycled {pe(e)}`)
|
||||
else
|
||||
print(`*created {pe(e)}`)
|
||||
end
|
||||
pad()
|
||||
return e
|
||||
end
|
||||
w.print_entity_index = function(self)
|
||||
local max_id = entity_index.max_id
|
||||
local alive_count = entity_index.alive_count
|
||||
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 sep = "|--------|"
|
||||
if #alive > 0 then
|
||||
print("|-alive--|")
|
||||
for i = 1, #alive do
|
||||
local e = pe(alive[i])
|
||||
print_centered_entity(e, 32)
|
||||
print(sep)
|
||||
end
|
||||
print("\n")
|
||||
end
|
||||
|
||||
if #dead > 0 then
|
||||
print("|--dead--|")
|
||||
for i = 1, #dead do
|
||||
print_centered_entity(pe(dead[i]), 32)
|
||||
print(sep)
|
||||
end
|
||||
end
|
||||
pad()
|
||||
end
|
||||
local timelines = {}
|
||||
w.print_snapshot = function(self)
|
||||
local timeline = #timelines + 1
|
||||
local entity_column_width = 10
|
||||
local status_column_width = 8
|
||||
|
||||
local header = string.format("| %-" .. entity_column_width .. "s |", "Entity")
|
||||
for i = 1, timeline do
|
||||
header = header .. string.format(" %-" .. status_column_width .. "s |", string.format("T%d", i))
|
||||
end
|
||||
|
||||
local max_id = entity_index.max_id
|
||||
local alive_count = entity_index.alive_count
|
||||
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 data = {}
|
||||
print("-------------------------------------------------------------------")
|
||||
print(header)
|
||||
|
||||
-- Store the snapshot data for this timeline
|
||||
for i = ENTITY_RANGE, max_id do
|
||||
if dense_array[i] then
|
||||
local entity = dense_array[i]
|
||||
local id = ECS_ID(entity)
|
||||
local status = "alive"
|
||||
if not world:contains(entity) then
|
||||
status = "dead"
|
||||
end
|
||||
data[id] = status
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(timelines, data)
|
||||
|
||||
-- Create a table to hold entity data for sorting
|
||||
local entities = {}
|
||||
for i = ENTITY_RANGE, max_id do
|
||||
if dense_array[i] then
|
||||
local entity = dense_array[i]
|
||||
local id = ECS_ID(entity)
|
||||
-- Push entity and id into the new `entities` table
|
||||
table.insert(entities, { entity = entity, id = id })
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort the entities by ECS_ID
|
||||
table.sort(entities, function(a, b)
|
||||
return a.id < b.id
|
||||
end)
|
||||
|
||||
-- Print the sorted rows
|
||||
for _, entity_data in ipairs(entities) do
|
||||
local entity = entity_data.entity
|
||||
local id = entity_data.id
|
||||
local status = "alive"
|
||||
if id > alive_count then
|
||||
status = "dead"
|
||||
end
|
||||
local row = string.format("| %-" .. entity_column_width .. "s |", pe(entity))
|
||||
for j = 1, timeline do
|
||||
local timeline_data = timelines[j]
|
||||
local entity_data = timeline_data[id]
|
||||
if entity_data then
|
||||
row = row .. string.format(" %-" .. status_column_width .. "s |", entity_data)
|
||||
else
|
||||
row = row .. string.format(" %-" .. status_column_width .. "s |", "-")
|
||||
end
|
||||
end
|
||||
print(row)
|
||||
end
|
||||
print("-------------------------------------------------------------------")
|
||||
pad()
|
||||
end
|
||||
local world_add = world.add
|
||||
local relations = {}
|
||||
w.add = function(self, entity: any, component: any)
|
||||
world_add(world, entity, component)
|
||||
if jecs.IS_PAIR(component) then
|
||||
local relation = jecs.pair_first(world, component)
|
||||
local target = jecs.pair_second(world, component)
|
||||
print(`*added ({pe(relation)}, {pe(target)}) to {pe(entity)}`)
|
||||
pad()
|
||||
end
|
||||
end
|
||||
|
||||
local world_delete = world.delete
|
||||
w.delete = function(self, e)
|
||||
world_delete(world, e)
|
||||
|
||||
local idr_t = component_index[pair(__, e)]
|
||||
if idr_t then
|
||||
for archetype_id in idr_t.cache do
|
||||
local archetype = world.archetypes[archetype_id]
|
||||
for _, id in archetype.types do
|
||||
if not jecs.IS_PAIR(id) then
|
||||
continue
|
||||
end
|
||||
local object = jecs.pair_second(world, id)
|
||||
if object ~= e then
|
||||
continue
|
||||
end
|
||||
local id_record = component_index[id]
|
||||
local flags = id_record.flags
|
||||
local flags_delete_mask: number = bit32.band(flags, jecs.ECS_ID_DELETE)
|
||||
if flags_delete_mask ~= 0 then
|
||||
for _, entity in archetype.entities do
|
||||
print(`*deleted dependant {pe(entity)} of {pe(e)}`)
|
||||
pad()
|
||||
end
|
||||
break
|
||||
else
|
||||
for _, entity in archetype.entities do
|
||||
print(
|
||||
`*removed dependency ({pe(jecs.pair_first(world, id))}, {pe(object)}) from {pe(entity)}`
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print(`*deleted {pe(e)}`)
|
||||
pad()
|
||||
end
|
||||
return w
|
||||
end
|
||||
|
||||
return lifetime_tracker_add
|
||||
|
|
|
@ -1,108 +1,108 @@
|
|||
local function dbg_info(n: number): any
|
||||
return debug.info(n, "s")
|
||||
end
|
||||
local function throw(msg: string)
|
||||
local s = 1
|
||||
local root = dbg_info(1)
|
||||
repeat
|
||||
s += 1
|
||||
until dbg_info(s) ~= root
|
||||
if warn then
|
||||
error(msg, s)
|
||||
else
|
||||
print(`[jecs] error: {msg}\n`)
|
||||
end
|
||||
end
|
||||
|
||||
local function ASSERT<T>(v: T, msg: string)
|
||||
if v then
|
||||
return
|
||||
end
|
||||
throw(msg)
|
||||
end
|
||||
|
||||
local function runtime_lints_add(world)
|
||||
local function get_name(id)
|
||||
return world_get_one_inline(world, id, EcsName)
|
||||
end
|
||||
|
||||
local function bname(id): string
|
||||
local name: string
|
||||
if ECS_IS_PAIR(id) then
|
||||
local first = get_name(world, ecs_pair_first(world, id))
|
||||
local second = get_name(world, ecs_pair_second(world, id))
|
||||
name = `pair({first}, {second})`
|
||||
else
|
||||
return get_name(world, id)
|
||||
end
|
||||
if name then
|
||||
return name
|
||||
else
|
||||
return `${id}`
|
||||
end
|
||||
end
|
||||
|
||||
local function ID_IS_TAG(world: World, id)
|
||||
if ECS_IS_PAIR(id) then
|
||||
id = ecs_pair_first(world, id)
|
||||
end
|
||||
return not world_has_one_inline(world, id, EcsComponent)
|
||||
end
|
||||
|
||||
World.query = function(world: World, ...)
|
||||
ASSERT((...), "Requires at least a single component")
|
||||
return world_query(world, ...)
|
||||
end
|
||||
|
||||
World.set = function(world: World, entity: i53, id: i53, value: any): ()
|
||||
local is_tag = ID_IS_TAG(world, id)
|
||||
if is_tag and value == nil then
|
||||
local _1 = bname(world, entity)
|
||||
local _2 = bname(world, id)
|
||||
local why = "cannot set component value to nil"
|
||||
throw(why)
|
||||
return
|
||||
elseif value ~= nil and is_tag then
|
||||
local _1 = bname(world, entity)
|
||||
local _2 = bname(world, id)
|
||||
local why = `cannot set a component value because {_2} is a tag`
|
||||
why ..= `\n[jecs] note: consider using "world:add({_1}, {_2})" instead`
|
||||
throw(why)
|
||||
return
|
||||
end
|
||||
|
||||
world_set(world, entity, id, value)
|
||||
end
|
||||
|
||||
World.add = function(world: World, entity: i53, id: i53, value: any)
|
||||
if value ~= nil then
|
||||
local _1 = bname(world, entity)
|
||||
local _2 = bname(world, id)
|
||||
throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`)
|
||||
end
|
||||
|
||||
world_add(world, entity, id)
|
||||
end
|
||||
|
||||
World.get = function(world: World, entity: i53, ...)
|
||||
local length = select("#", ...)
|
||||
ASSERT(length < 5, "world:get does not support more than 4 components")
|
||||
local _1
|
||||
for i = 1, length do
|
||||
local id = select(i, ...)
|
||||
local id_is_tag = not world_has(world, id, EcsComponent)
|
||||
if id_is_tag then
|
||||
local name = get_name(world, id)
|
||||
if not _1 then
|
||||
_1 = get_name(world, entity)
|
||||
end
|
||||
throw(
|
||||
`cannot get (#{i}) component {name} value because it is a tag.`
|
||||
.. `\n[jecs] note: If this was intentional, use "world:has({_1}, {name}) instead"`
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return world_get(world, entity, ...)
|
||||
end
|
||||
end
|
||||
local function dbg_info(n: number): any
|
||||
return debug.info(n, "s")
|
||||
end
|
||||
local function throw(msg: string)
|
||||
local s = 1
|
||||
local root = dbg_info(1)
|
||||
repeat
|
||||
s += 1
|
||||
until dbg_info(s) ~= root
|
||||
if warn then
|
||||
error(msg, s)
|
||||
else
|
||||
print(`[jecs] error: {msg}\n`)
|
||||
end
|
||||
end
|
||||
|
||||
local function ASSERT<T>(v: T, msg: string)
|
||||
if v then
|
||||
return
|
||||
end
|
||||
throw(msg)
|
||||
end
|
||||
|
||||
local function runtime_lints_add(world)
|
||||
local function get_name(id)
|
||||
return world_get_one_inline(world, id, EcsName)
|
||||
end
|
||||
|
||||
local function bname(id): string
|
||||
local name: string
|
||||
if ECS_IS_PAIR(id) then
|
||||
local first = get_name(world, ecs_pair_first(world, id))
|
||||
local second = get_name(world, ecs_pair_second(world, id))
|
||||
name = `pair({first}, {second})`
|
||||
else
|
||||
return get_name(world, id)
|
||||
end
|
||||
if name then
|
||||
return name
|
||||
else
|
||||
return `${id}`
|
||||
end
|
||||
end
|
||||
|
||||
local function ID_IS_TAG(world: World, id)
|
||||
if ECS_IS_PAIR(id) then
|
||||
id = ecs_pair_first(world, id)
|
||||
end
|
||||
return not world_has_one_inline(world, id, EcsComponent)
|
||||
end
|
||||
|
||||
World.query = function(world: World, ...)
|
||||
ASSERT((...), "Requires at least a single component")
|
||||
return world_query(world, ...)
|
||||
end
|
||||
|
||||
World.set = function(world: World, entity: i53, id: i53, value: any): ()
|
||||
local is_tag = ID_IS_TAG(world, id)
|
||||
if is_tag and value == nil then
|
||||
local _1 = bname(world, entity)
|
||||
local _2 = bname(world, id)
|
||||
local why = "cannot set component value to nil"
|
||||
throw(why)
|
||||
return
|
||||
elseif value ~= nil and is_tag then
|
||||
local _1 = bname(world, entity)
|
||||
local _2 = bname(world, id)
|
||||
local why = `cannot set a component value because {_2} is a tag`
|
||||
why ..= `\n[jecs] note: consider using "world:add({_1}, {_2})" instead`
|
||||
throw(why)
|
||||
return
|
||||
end
|
||||
|
||||
world_set(world, entity, id, value)
|
||||
end
|
||||
|
||||
World.add = function(world: World, entity: i53, id: i53, value: any)
|
||||
if value ~= nil then
|
||||
local _1 = bname(world, entity)
|
||||
local _2 = bname(world, id)
|
||||
throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`)
|
||||
end
|
||||
|
||||
world_add(world, entity, id)
|
||||
end
|
||||
|
||||
World.get = function(world: World, entity: i53, ...)
|
||||
local length = select("#", ...)
|
||||
ASSERT(length < 5, "world:get does not support more than 4 components")
|
||||
local _1
|
||||
for i = 1, length do
|
||||
local id = select(i, ...)
|
||||
local id_is_tag = not world_has(world, id, EcsComponent)
|
||||
if id_is_tag then
|
||||
local name = get_name(world, id)
|
||||
if not _1 then
|
||||
_1 = get_name(world, entity)
|
||||
end
|
||||
throw(
|
||||
`cannot get (#{i}) component {name} value because it is a tag.`
|
||||
.. `\n[jecs] note: If this was intentional, use "world:has({_1}, {name}) instead"`
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return world_get(world, entity, ...)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -534,7 +534,7 @@ return {
|
|||
FINISH = FINISH,
|
||||
SKIP = SKIP,
|
||||
FOCUS = FOCUS,
|
||||
CHECK_EXPECT_ERR = CHECK_EXPECT_ERR
|
||||
CHECK_EXPECT_ERR = CHECK_EXPECT_ERR,
|
||||
}
|
||||
end,
|
||||
|
||||
|
|