[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n[*.{lua,js,json}]\ncharset = utf-8\nindent_style = space\nindent_size = 3\n"
  },
  {
    "path": ".gitignore",
    "content": "# IDE\n.idea\n.vscode\n\nluacov.report.out\n\nsrc/*.zip\n"
  },
  {
    "path": ".luacov",
    "content": "return {\n\tinclude = {\n\t\t\"^src\",\n      \"src%/.+$\"\n\t},\n\texclude = {\n\t\t\"%.test$\",\n\t},\n   runreport = true,\n   deletestats = true,\n   --reporter = \"html\"\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\n\nenv:\n  - LUA=\"lua=5.1\"\n  - LUA=\"lua=5.2\"\n  - LUA=\"lua=5.3\"\n  - LUA=\"lua=5.4\"\n  - LUA=\"luajit=2.0\"\n  - LUA=\"luajit=2.1\"\n\nbefore_install:\n  - pip install hererocks\n  - hererocks lua_install --$LUA -r latest\n  - source lua_install/bin/activate\n\nscript:\n  - lua test.lua -v && tail -22 ./luacov.report.out && lua build.lua\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to ECS Lua\nThanks for considering contributing to ECS Lua! This guide has a few tips and guidelines to make contributing to the \nproject as easy as possible.\n\n## Bug Reports\nAny bugs (or things that look like bugs) can be reported on the [GitHub issue tracker](https://github.com/nidorx/ecs-lua/issues).\n\nMake sure you check to see if someone has already reported your bug first! Don't fret about it; if we notice a duplicate \nwe'll send you a link to the right issue!\n\n## Feature Requests\nIf there are any features you think are missing from ECS Lua, you can post a request in the \n[GitHub issue tracker](https://github.com/nidorx/ecs-lua/issues).\n\nJust like bug reports, take a peak at the issue tracker for duplicates before opening a new feature request.\n\n## Documentation\n[ECS Lua documentation](https://nidorx.github.io/ecs-lua/) is built using [Docsify](https://docsify.js.org/#/), a \nfairly simple documentation generator.\n\n## Working on ECS Lua\nTo get started working on ECS Lua, you'll need:\n* Git\n* Lua 5.1\n\nYou can run all of ECS Lua tests with:\n\n```sh\nlua test.lua -v\n```\n\nThe LuaCov coverage report is available in the `luacov.report.out` file.\n\nTo build the concatenated and minified versions, run the command\n\n```sh\nlua build.lua\n```\n\n## Pull Requests\nBefore starting a pull request, open an issue about the feature or bug. This helps us prevent duplicated and wasted \neffort. These issues are a great place to ask for help if you run into problems!\n\n### Code Style\n\nIn short:\n\n- **SPACE** for indentation\n- Identation size = 3 spaces\n- Double quotes\n- One statement per line\n\n### Tests\nWhen submitting a bug fix, create a test that verifies the broken behavior and that the bug fix works. This helps us \navoid regressions!\n\nWhen submitting a new feature, add tests for all functionality.\n\nWe use [LuaCov](https://keplerproject.github.io/luacov) for keeping track of code coverage. We'd like it to be as \nclose to 100% as possible, but it's not always possible. Adding tests just for the purpose of getting coverage isn't \nuseful; we should strive to make only useful tests!\n"
  },
  {
    "path": "ECS.lua",
    "content": "--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tThis is a minified version of ECS Lua, to see the full source code visit\n\thttps://github.com/nidorx/ecs-lua\n\n   Discussions about this script are at https://devforum.roblox.com/t/841175\n\n\t------------------------------------------------------------------------------\n\n\tMIT License\n\n\tCopyright (c) 2021 Alex Rodin\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.\n]]\nlocal a,b={},{}local function c(d)if(not a[d])then a[d]={r=b[d]()}end return a[d].r end b[\"Archetype\"]=function()local d={}local e={}local f={}local g=0 local h={}h.__index=h function h.Of(i)local j={}local k={}for m,n in ipairs(i)do if(n.IsCType and not n.isComponent)then if n.IsQualifier then if k[n]==nil then k[n]=true table.insert(j,n.Id)end n=n.SuperClass end if k[n]==nil then k[n]=true table.insert(j,n.Id)end end end table.sort(j)local l=\"_\"..table.concat(j,\"_\")if d[l]==nil then d[l]=setmetatable({id=l,_components=k},h)g=g+1 end return d[l]end function h.Version()return g end function h:Has(i)return(self._components[i]==true)end function h:With(i)if self._components[i]==true then return self end local j=e[self]if not j then j={}e[self]=j end local k=j[i]if k==nil then local l={i}for m,n in pairs(self._components)do table.insert(l,m)end k=h.Of(l)j[i]=k end return k end function h:WithAll(i)local j={}for k,l in pairs(self._components)do table.insert(j,k)end for k,l in ipairs(i)do if self._components[l]==nil then table.insert(j,l)end end return h.Of(j)end function h:Without(i)if self._components[i]==nil then return self end local j=f[self]if not j then j={}f[self]=j end local k=j[i]if k==nil then local l={}for m,n in pairs(self._components)do if m~=i then table.insert(l,m)end end k=h.Of(l)j[i]=k end return k end function h:WithoutAll(i)local j={}for l,m in ipairs(i)do j[m]=true end local k={}for l,m in pairs(self._components)do if j[l]==nil then table.insert(k,l)end end return h.Of(k)end h.EMPTY=h.Of({})return h end b[\"Component\"]=function()local d=c(\"Utility\")local e=c(\"ComponentFSM\")local f=d.copyDeep local g=d.mergeDeep local h=0 local function i(l,m)h=h+1 local n={Id=h,IsCType=true,SuperClass=m}n.__index=n if m==nil then m=n m._Qualifiers={[\"Primary\"]=n}m._QualifiersArr={n}m._Initializers={}else m.HasQualifier=true n.IsQualifier=true n.HasQualifier=true end local o=m._Qualifiers local p=m._QualifiersArr setmetatable(n,{__call=function(q,r)return n.New(r)end,__index=function(q,r)if(r==\"States\")then return m.__States end if(r==\"Case\"or r==\"StateInitial\")then return rawget(m,r)end end,__newindex=function(q,r,s)if(r==\"Case\"or r==\"States\"or r==\"StateInitial\")then if n==m then if(r==\"States\")then if not m.IsFSM then e.AddCapability(m,s)for t,u in pairs(o)do if u~=m then e.AddMethods(m,u)end end end else rawset(q,r,s)end end else rawset(q,r,s)end end})if m.IsFSM then e.AddMethods(m,n)end function n.Qualifier(q)if type(q)~=\"string\"then for s,t in ipairs(p)do if t==q then return q end end return nil end local r=o[q]if r==nil then r=i(l,m)o[q]=r table.insert(p,r)end return r end function n.Qualifiers(...)local q={...}if#q==0 then return p else local r={}local s={}for t,u in ipairs({...})do local v=n.Qualifier(u)if v and s[v]==nil then s[v]=true table.insert(r,v)end end return r end end function n.New(q)if(q~=nil and type(q)~=\"table\")then q={value=q}end local r=setmetatable(l(q)or{},n)for s,t in ipairs(m._Initializers)do t(r)end r.isComponent=true r._qualifiers={[n]=r}return r end function n:GetType()return n end function n:Is(q)return q==n or q==m end function n:Primary()return self._qualifiers[m]end function n:Qualified(q)return self._qualifiers[n.Qualifier(q)]end function n:QualifiedAll()local q={}for r,s in pairs(o)do q[r]=self._qualifiers[s]end return q end function n:Merge(q)if m.HasQualifier then if self==q then return end if self._qualifiers==q._qualifiers then return end if not q:Is(m)then return end local r=n local s=q:GetType()local t if r==m then t=self._qualifiers elseif s==m then t=q._qualifiers elseif self._qualifiers[m]~=nil then t=self._qualifiers[m]._qualifiers elseif q._qualifiers[m]~=nil then t=q._qualifiers[m]._qualifiers end if t~=nil then if self._qualifiers~=t then for u,v in pairs(self._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end if q._qualifiers~=t then for u,v in pairs(q._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end else for u,v in pairs(q._qualifiers)do if r~=u then self._qualifiers[u]=v v._qualifiers=self._qualifiers end end end end end function n:Detach()if not m.HasQualifier then return end self._qualifiers[n]=nil self._qualifiers={[n]=self}end return n end local function j(l)return l or{}end local k={}function k.Create(l)local m=j if l~=nil then local n=type(l)if(n==\"function\")then m=l else if(n~=\"table\")then l={value=l}end m=function(o)local p=f(l)if(o~=nil)then g(p,o)end return p end end end return i(m,nil)end return k end b[\"ComponentFSM\"]=function()local d=c(\"Query\")local e=d.Filter(function(g,h)local i=h.States local j=h.IsSuperClass local k=h.ComponentClass if j then local l=k.Qualifiers()for m,n in ipairs(l)do local o=g[n]if(o~=nil and i[o:GetState()]==true)then return true end end return false else local l=g[k]if l==nil then return false end return i[l:GetState()]==true end end)local f={}function f.AddCapability(g,h)g.IsFSM=true local i=setmetatable({},{__newindex=function(j,k,l)if(type(l)~=\"table\")then l={l}end if table.find(l,\"*\")then rawset(j,k,\"*\")else local m=table.find(l,k)if m~=nil then table.remove(l,m)if#l==0 then l=\"*\"end end rawset(j,k,l)end end})rawset(g,\"__States\",i)for j,k in pairs(h)do if g.StateInitial==nil then g.StateInitial=j end i[j]=k end f.AddMethods(g,g)table.insert(g._Initializers,function(j)j:SetState(g.StateInitial)end)end function f.AddMethods(g,h)h.IsFSM=true local i=g.States function h.In(...)local j={}local k=0 for l,m in ipairs({...})do if(i[m]~=nil and j[m]==nil)then k=k+1 j[m]=true end end if k==0 then return{}end return e({States=j,IsSuperClass=(h==g),ComponentClass=h,})end function h:SetState(j)if(j==nil or i[j]==nil)then return end local k=self:GetState()if(k==j)then return end if(k~=nil)then local m=i[k]if(m~=\"*\"and table.find(m,j)==nil)then return end end self._state=j self._statePrev=k self._stateTime=os.clock()local l=g.Case and g.Case[j]if l then l(self,k)end end function h:GetState()return self._state or g.StateInitial end function h:GetPrevState()return self._statePrev or nil end function h:GetStateTime()return self._stateTime or 0 end end return f end b[\"ECS\"]=function()local d=c(\"Query\")local e=c(\"World\")local f=c(\"System\")local g=c(\"Archetype\")local h=c(\"Component\")local function i(k)e.LoopManager=k end pcall(function()if(game and game.ClassName==\"DataModel\")then i(c(\"RobloxLoopManager\")())end end)local j={Query=d,World=e.New,System=f.Create,Archetype=g,Component=h.Create,SetLoopManager=i}if _G.ECS==nil then _G.ECS=j else local k=_G.warn or print k(\"ECS Lua was not registered in the global variables, there is already another object registered.\")end return j end b[\"Entity\"]=function()local d=c(\"Archetype\")local e=0 local function f(m,...)local n={...}local o=m._data if(#n==1)then local q=n[1]if(q.IsCType and not q.isComponent)then return o[q]else return nil end end local p={}for q,r in ipairs(n)do if(r.IsCType and not r.isComponent)then table.insert(p,o[r])end end return table.unpack(p)end local function g(m,n,o)local p=m._data local q for r,s in ipairs(o.Qualifiers())do if s~=o then q=p[s]if q then break end end end if q then q:Merge(n)end end local function h(m,...)local n={...}local o=m._data local p=m.archetype local q=p local r={}local s=n[1]if(s and s.IsCType and not s.isComponent)then local t=n[2]local u if t==nil then local v=o[s]if v then v:Detach()end o[s]=nil q=q:Without(s)elseif(type(t)==\"table\"and t.isComponent)then local v=o[s]if(v~=t)then if v then v:Detach()end s=t:GetType()o[s]=t q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,t,s)end end else local v=o[s]if v then v:Detach()end local w=s(t)o[s]=w q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,w,s)end end else for t,u in ipairs(n)do if(u.isComponent)then local v=u:GetType()local w=o[v]if(w~=u)then if w then w:Detach()end o[v]=u q=q:With(v)if(v.HasQualifier or v.IsQualifier)then g(m,u,v)end end end end end if(p~=q)then m.archetype=q m._onChange:Fire(m,p)end end local function i(m,...)local n=m._data local o=m.archetype local p=o for q,r in ipairs({...})do if r.isComponent then local s=r:GetType()local t=n[s]if t then t:Detach()end n[s]=nil p=p:Without(s)elseif r.IsCType then local s=n[r]if s then s:Detach()end n[r]=nil p=p:Without(r)end end if m.archetype~=p then m.archetype=p m._onChange:Fire(m,o)end end local function j(m,n)local o=m._data local p={}if(n~=nil and n.IsCType and not n.isComponent)then local q=n.Qualifiers()for r,s in ipairs(q)do local t=o[s]if t then table.insert(p,t)end end else for q,r in pairs(o)do table.insert(p,r)end end return p end local function k(m,n)if(n~=nil and n.IsCType and not n.isComponent)then local o=m._data local p=n.Qualifiers()for q,r in ipairs(p)do local s=o[r]if s then return s end end end end local l={__index=function(m,n)if(type(n)==\"table\")then return f(m,n)end end,__newindex=function(m,n,o)local p=true if(type(n)==\"table\"and(n.IsCType and not n.isComponent))then h(m,n,o)else rawset(m,n,o)end end}function l.New(m,n)local o=d.EMPTY local p={}if(n~=nil and#n>0)then local q={}for r,s in ipairs(n)do local t=s:GetType()table.insert(q,t)p[t]=s end o=d.Of(q)end e=e+1 return setmetatable({_data=p,_onChange=m,id=e,isAlive=false,archetype=o,Get=f,Set=h,Unset=i,GetAll=j,GetAny=k,},l)end return l end b[\"EntityRepository\"]=function()local d=c(\"Event\")local e={}e.__index=e function e.New()return setmetatable({_archetypes={},_entitiesArchetype={},},e)end function e:Insert(f)if(self._entitiesArchetype[f]==nil)then local g=f.archetype local h=self._archetypes[g]if(h==nil)then h={count=0,entities={}}self._archetypes[g]=h end h.entities[f]=true h.count=h.count+1 self._entitiesArchetype[f]=g else self:Update(f)end end function e:Remove(f)local g=self._entitiesArchetype[f]if g==nil then return end self._entitiesArchetype[f]=nil local h=self._archetypes[g]if(h~=nil and h.entities[f]==true)then h.entities[f]=nil h.count=h.count-1 if(h.count==0)then self._archetypes[g]=nil end end end function e:Update(f)local g=self._entitiesArchetype[f]if(g==nil or g==f.archetype)then return end self:Remove(f)self:Insert(f)end function e:Query(f)local g={}for h,i in pairs(self._archetypes)do if f:Match(h)then table.insert(g,i.entities)end end return f:Result(g),#g>0 end function e:FastCheck(f)for g,h in pairs(self._archetypes)do if f:Match(g)then return true end end return false end return e end b[\"Event\"]=function()local d={}d.__index=d function d.New(f,g)return setmetatable({_event=f,_handler=g},d)end function d:Disconnect()local f=self._event if(f and not f.destroyed)then local g=table.find(f._handlers,self._handler)if g~=nil then table.remove(f._handlers,g)end end setmetatable(self,nil)end local e={}e.__index=e function e.New()return setmetatable({_handlers={}},e)end function e:Connect(f)if(type(f)==\"function\")then table.insert(self._handlers,f)return d.New(self,f)end error((\"Event:Connect(%s)\"):format(typeof(f)),2)end function e:Fire(...)if not self.destroyed then for f,g in ipairs(self._handlers)do g(table.unpack({...}))end end end function e:Destroy()setmetatable(self,nil)self._handlers=nil self.destroyed=true end return e end b[\"Query\"]=function()local d=c(\"QueryResult\")local e={}local f={}f.__index=f setmetatable(f,{__call=function(i,j,k,l)return f.New(j,k,l)end,})local function g(i,j,k)local l={}local m={}local n={}for o,p in ipairs(i)do if(l[p]==nil)then if(p.IsCType and not p.isComponent)then l[p]=true table.insert(m,p)table.insert(n,p.Id)else if p.Filter then l[p]=true p[j]=true table.insert(k,p)end end end end if#m>0 then table.sort(n)local o=\"_\"..table.concat(n,\"_\")return m,o end end function f.New(i,j,k)local l={}local m,n,o if(j~=nil)then j,m=g(j,\"IsAnyFilter\",l)end if(i~=nil)then i,n=g(i,\"IsAllFilter\",l)end if(k~=nil)then k,o=g(k,\"IsNoneFilter\",l)end return setmetatable({isQuery=true,_any=j,_all=i,_none=k,_anyKey=m,_allKey=n,_noneKey=o,_cache={},_clauses=#l>0 and l or nil},f)end function f:Result(i)return d.New(i,self._clauses)end function f:Match(i)local j=self._cache local k=j[i]if k~=nil then return k else local l=e[i]if(l==nil)then l={Any={},All={},None={}}e[i]=l end local m=self._noneKey if m then local p=l.None[m]if(p==nil)then p=true for q,r in ipairs(self._none)do if i:Has(r)then p=false break end end l.None[m]=p end if(p==false)then j[i]=false return false end end local n=self._anyKey if n then local p=l.Any[n]if(p==nil)then p=false if(l.All[n]==true)then p=true else for q,r in ipairs(self._any)do if i:Has(r)then p=true break end end end l.Any[n]=p end if(p==false)then j[i]=false return false end end local o=self._allKey if o then local p=l.All[o]if(p==nil)then local q=true for r,s in ipairs(self._all)do if(not i:Has(s))then q=false break end end if q then p=true else p=false end l.All[o]=p end j[i]=p return p end j[i]=true return true end end local function h()local i={isQueryBuilder=true}local j function i.All(...)j=nil i._all={...}return i end function i.Any(...)j=nil i._any={...}return i end function i.None(...)j=nil i._none={...}return i end function i.Build()if j==nil then j=f.New(i._all,i._any,i._none)end return j end return i end function f.All(...)return h().All(...)end function f.Any(...)return h().Any(...)end function f.None(...)return h().None(...)end function f.Filter(i)return function(j)return{Filter=i,Config=j}end end return f end b[\"QueryResult\"]=function()local function d(l,m,n)return m,(l(m)==true),true end local function e(l,m,n)return l(m),true,true end local function f(l,m,n)local o=(n<=l)return m,o,o end local function g(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=false break end end return m,o,true end local function h(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==false)then o=false break end end return m,o,true end local function i(l,m,n)local o=false for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=true break end end return m,o,true end local j={}local k={}k.__index=k function k.New(l,m)local n=j if(m and#m>0)then local o={}local p={}local q={}n={}for r,s in ipairs(m)do if s.IsNoneFilter then table.insert(q,s)elseif s.IsAnyFilter then table.insert(p,s)else table.insert(o,s)end end if(#q>0)then table.insert(n,{g,q})end if(#o>0)then table.insert(n,{h,o})end if(#p>0)then table.insert(n,{i,p})end end return setmetatable({chunks=l,_pipeline=n,},k)end function k:With(l,m)local n={}for o,p in ipairs(self._pipeline)do table.insert(n,p)end table.insert(n,{l,m})return setmetatable({chunks=self.chunks,_pipeline=n,},k)end function k:Filter(l)return self:With(d,l)end function k:Map(l)return self:With(e,l)end function k:Limit(l)return self:With(f,l)end function k:AnyMatch(l)local m=false self:ForEach(function(n)if l(n)then m=true end return m end)return m end function k:AllMatch(l)local m=true self:ForEach(function(n)if(not l(n))then m=false end return m==false end)return m end function k:FindAny()local l self:ForEach(function(m)l=m return true end)return l end function k:ToArray()local l={}self:ForEach(function(m)table.insert(l,m)end)return l end function k:Iterator()local l=coroutine.create(function()self:ForEach(function(m,n)coroutine.yield(m,n)end)end)return function()local m,n,o=coroutine.resume(l)return o,n end end function k:ForEach(l)local m=1 local n=self._pipeline local o=#n>0 if(not o)then for p,q in ipairs(self.chunks)do for r,s in pairs(q)do if(l(r,m)==true)then return end m=m+1 end end else for p,q in ipairs(self.chunks)do for r,s in pairs(q)do local t=false local u=true local v=r if(u and o)then for w,x in ipairs(n)do local y,z,A=x[1](x[2],v,m)if(not A)then t=true end if z then v=y else u=false break end end end if u then if(l(v,m)==true)then return end m=m+1 end if t then return end end end end end return k end b[\"RobloxLoopManager\"]=function()local function d()local e=game:GetService(\"RunService\")return{Register=function(f)local g=e.Stepped:Connect(function()f:Update(\"process\",os.clock())end)local h=e.Heartbeat:Connect(function()f:Update(\"transform\",os.clock())end)local i if(not e:IsServer())then i=e.RenderStepped:Connect(function()f:Update(\"render\",os.clock())end)end return function()g:Disconnect()h:Disconnect()if i then i:Disconnect()end end end}end return d end b[\"System\"]=function()local d={\"task\",\"render\",\"process\",\"transform\"}local e={}function e.Create(f,g,h,i)if(f==nil or not table.find(d,f))then error(\"The step parameter must one of \",table.concat(d,\", \"))end if(g and type(g)==\"function\")then i=g g=nil elseif h and type(h)==\"function\"then i=h h=nil end if(g and type(g)==\"table\"and(g.isQuery or g.isQueryBuilder))then h=g g=nil end if(g==nil or g<0)then g=50 end if type(h)==\"function\"then i=h h=nil end if(h and h.isQueryBuilder)then h=h.Build()end local j={Step=f,Order=g,Query=h,}j.__index=j function j.New(k,l)local m=setmetatable({version=0,_world=k,_config=l,},j)if m.Initialize then m:Initialize(l)end return m end function j:GetType()return j end function j:Result(k)return self._world:Exec(k or j.Query)end function j:Destroy()if self.OnDestroy then self.OnDestroy()end setmetatable(self,nil)for k,l in pairs(self)do self[k]=nil end end if i and type(i)==\"function\"then j.Update=i end return j end return e end b[\"SystemExecutor\"]=function()local function d(i)local j={}local k={}for l,m in ipairs(i)do local n=m:GetType()if(m._TaskState==nil)then m._TaskState=\"suspended\"end if not k[n]then local o={Type=n,System=m,Depends={}}k[n]=o table.insert(j,o)end end for l,m in ipairs(j)do local n=m.Type.Before if(n~=nil and#n>0)then for p,q in ipairs(n)do local r=k[q]if r then r.Depends[m]=true end end end local o=m.Type.After if(o~=nil and#o>0)then for p,q in ipairs(o)do local r=k[q]if r then m.Depends[r]=true end end end end return j end local function e(i,j)return i.Order<j.Order end local f={}f.__index=f function f.New(i)local j=setmetatable({_world=i,_onExit={},_onEnter={},_onRemove={},_task={},_render={},_process={},_transform={},_schedulers={},_lastFrameMatchQueries={},_currentFrameMatchQueries={},},f)i:OnQueryMatch(function(k)j._currentFrameMatchQueries[k]=true end)return j end function f:SetSystems(i)local j={}local k={}local l={}local m={}local n={}local o={}local p={}for q,r in pairs(i)do local s=r.Step if r.Update then if s==\"task\"then table.insert(m,r)elseif s==\"process\"then table.insert(o,r)elseif s==\"transform\"then table.insert(p,r)elseif s==\"render\"then table.insert(n,r)end end if(r.Query and r.Query.isQuery and s~=\"task\")then if r.OnExit then table.insert(j,r)end if r.OnEnter then table.insert(k,r)end if r.OnRemove then table.insert(l,r)end end end m=d(m)table.sort(j,e)table.sort(k,e)table.sort(l,e)table.sort(n,e)table.sort(o,e)table.sort(p,e)self._onExit=j self._onEnter=k self._onRemove=l self._task=m self._render=n self._process=o self._transform=p end function f:ExecOnExitEnter(i,j)local k=true local l={}for m,n in pairs(j)do local o=l[n]if not o then o={}l[n]=o end local p=m.archetype local q=o[p]if not q then q={}o[p]=q end table.insert(q,m)k=false end if k then return end self:_ExecOnEnter(i,l)self:_ExecOnExit(i,l)end function f:_ExecOnEnter(i,j)local k=self._world for l,m in ipairs(self._onEnter)do local n=m.Query for o,p in pairs(j)do if not n:Match(o)then for q,r in pairs(p)do if n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnEnter(i,t)m.version=k.version end end end end end end end function f:_ExecOnExit(i,j)local k=self._world for l,m in ipairs(self._onExit)do local n=m.Query for o,p in pairs(j)do if n:Match(o)then for q,r in pairs(p)do if not n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnExit(i,t)m.version=k.version end end end end end end end function f:ExecOnRemove(i,j)local k=true local l={}for n,o in pairs(j)do local p=l[o]if not p then p={}l[o]=p end table.insert(p,n)k=false end if k then return end local m=self._world for n,o in ipairs(self._onRemove)do for p,q in pairs(l)do if o.Query:Match(p)then for r,s in ipairs(q)do m.version=m.version+1 o:OnRemove(i,s)o.version=m.version end end end end end local function g(i,j,k)local l=i._world local m=i._lastFrameMatchQueries local n=i._currentFrameMatchQueries for o,p in ipairs(j)do local q=true if p.Query then local r=p.Query if m[r]==true or n[r]==true then q=true else q=l:FastCheck(r)n[r]=q end end if q then if(p.ShouldUpdate==nil or p.ShouldUpdate(k))then l.version=l.version+1 p:Update(k)p.version=l.version end end end end function f:ExecProcess(i)self._currentFrameMatchQueries={}g(self,self._process,i)end function f:ExecTransform(i)g(self,self._transform,i)end function f:ExecRender(i)g(self,self._render,i)self._lastFrameMatchQueries=self._currentFrameMatchQueries end function f:ExecTasks(i)while i>0 do local j=false local k,l=0,#self._schedulers-1 while k<=l do k=k+1 local m=self._schedulers[k]local n,o=m.Resume(i)if o then j=true end i=i-(n+0.00001)if(i<=0)then break end end if not j then return end end end local function h(i,j,k,l)local m=i.System m._TaskState=\"running\"if(m.ShouldUpdate==nil or m.ShouldUpdate(j))then k.version=k.version+1 m:Update(j)m.version=k.version end m._TaskState=\"suspended\"l(i)end function f:ScheduleTasks(i)local j=self._world local k={}local l={}local m={}local n={}local o={}local p,q=0,#self._task-1 while p<=q do p=p+1 local s=self._task[p]if(s.System._TaskState==\"suspended\")then s.System._TaskState=\"scheduled\"local t=false for u,v in pairs(s.Depends)do t=true if o[u]==nil then o[u]={}end table.insert(o[u],s)end if(not t)then table.insert(k,s)end m[s]=true end end local function r(s)s.Thread=nil s.LastExecTime=nil n[s]=true if o[s]then local t=o[s]local u,v=0,#t-1 while u<=v do u=u+1 local w=t[u]if m[w]then local x=true for y,z in pairs(w.Depends)do if n[y]~=true then x=false break end end if x then m[w]=nil w.LastExecTime=0 w.Thread=coroutine.create(h)table.insert(l,w)end end end end end if#k>0 then local s,t=0,#k-1 while s<=t do s=s+1 local v=k[s]m[v]=nil v.LastExecTime=0 v.Thread=coroutine.create(h)table.insert(l,v)end local u u={Resume=function(v)table.sort(l,function(A,B)return A.LastExecTime<B.LastExecTime end)local w=0 local x,y=0,#l-1 while x<=y do x=x+1 local A=l[x]if A.Thread~=nil then local B=os.clock()A.LastExecTime=B coroutine.resume(A.Thread,A,i,j,r)w=w+(os.clock()-B)if(w>v)then break end end end for A,B in ipairs(l)do if B.Thread==nil then local C=table.find(l,B)if C~=nil then table.remove(l,C)end end end local z=#l>0 if(not z)then local A=table.find(self._schedulers,u)if A~=nil then table.remove(self._schedulers,A)end end return w,z end}table.insert(self._schedulers,u)end end return f end b[\"Timer\"]=function()local d=4 local function e(g)local h=0.0 local i=0.0 return function(j,k,l,m)local n=g.DeltaFixed local o=j-i if o>0.25 then o=0.25 end i=j g.Now=j h=h+o if k==\"process\"then if h>=n then g.Interpolation=1 l(g)local p=0 while(h>=n and p<d)do m(g)p=p+1 g.Process=g.Process+n h=h-n end end else g.Interpolation=math.min(math.max(h/n,0),1)l(g)m(g)end end end local f={}f.__index=f function f.New(g)local h={Now=0,Frame=0,Process=0,Delta=0,DeltaFixed=0,Interpolation=0}local i=setmetatable({Time=h,Frequency=0,_update=e(h)},f)i:SetFrequency(g)return i end function f:SetFrequency(g)if g==nil then g=30 end local h=math.floor(math.abs(g)/2)*2 if h<2 then h=2 end if g~=h then g=h end self.Frequency=g self.Time.DeltaFixed=1000/g/1000 end function f:Update(g,h,i,j)self._update(g,h,i,j)end return f end b[\"Utility\"]=function()local d={}if table.unpack==nil then table.unpack=unpack end if table.find==nil then table.find=function(g,h,i)local j=#g for k=i or 1,j,1 do if g[k]==h then return k end end return nil end end local function e(g)local h={}for i,j in pairs(g)do if type(j)==\"table\"then j=e(j)end h[i]=j end return h end d.copyDeep=e local function f(g,h)for i,j in pairs(h)do if(type(j)==\"table\")then local k=g[i]if(k==nil or type(k)~=\"table\")then g[i]=e(j)else g[i]=f(k,j)end else g[i]=j end end return g end d.mergeDeep=f return d end b[\"World\"]=function()local d=c(\"Timer\")local e=c(\"Event\")local f=c(\"Entity\")local g=c(\"Archetype\")local h=c(\"SystemExecutor\")local i=c(\"EntityRepository\")local j={}j.__index=j function j.New(k,l,m)local n=setmetatable({version=0,maxTasksExecTime=0.013333333333333334,_dirty=false,_timer=d.New(l),_systems={},_repository=i.New(),_entitiesCreated={},_entitiesRemoved={},_entitiesUpdated={},_onQueryMatch=e.New(),_onChangeArchetypeEvent=e.New(),},j)n._executor=h.New(n)n._onChangeArchetypeEvent:Connect(function(o,p,q)n:_OnChangeArchetype(o,p,q)end)if(k~=nil)then for o,p in ipairs(k)do n:AddSystem(p)end end if(not m and j.LoopManager)then n._loopCancel=j.LoopManager.Register(n)end return n end function j:SetFrequency(k)k=self._timer:SetFrequency(k)end function j:GetFrequency(k)return self._timer.Frequency end function j:AddSystem(k,l)if k then if l==nil then l={}end if self._systems[k]==nil then self._systems[k]=k.New(self,l)self._executor:SetSystems(self._systems)end end end function j:Entity(...)local k=f.New(self._onChangeArchetypeEvent,{...})self._dirty=true self._entitiesCreated[k]=true k.version=self.version k.isAlive=false return k end function j:Remove(k)if self._entitiesRemoved[k]==true then return end if self._entitiesCreated[k]==true then self._entitiesCreated[k]=nil else self._repository:Remove(k)self._entitiesRemoved[k]=true if self._entitiesUpdated[k]==nil then self._entitiesUpdated[k]=k.archetype end end self._dirty=true k.isAlive=false end function j:Exec(k)if(k.isQueryBuilder)then k=k.Build()end local l,m=self._repository:Query(k)if m then self._onQueryMatch:Fire(k)end return l end function j:FastCheck(k)if(k.isQueryBuilder)then k=k.Build()end return self._repository:FastCheck(k)end function j:OnQueryMatch(k)return self._onQueryMatch:Connect(k)end function j:Update(k,l)self._timer:Update(l,k,function(m)if k==\"process\"then self._executor:ScheduleTasks(m)end self._executor:ExecTasks(self.maxTasksExecTime)end,function(m)if k==\"process\"then self._executor:ExecProcess(m)elseif k==\"transform\"then self._executor:ExecTransform(m)else self._executor:ExecRender(m)end while self._dirty do self._dirty=false local n={}for q,r in pairs(self._entitiesRemoved)do n[q]=self._entitiesUpdated[q]self._entitiesUpdated[q]=nil end self._entitiesRemoved={}self._executor:ExecOnRemove(m,n)n=nil local o={}local p=false for q,r in pairs(self._entitiesUpdated)do if(r~=q.archetype)then p=true o[q]=r end end self._entitiesUpdated={}for q,r in pairs(self._entitiesCreated)do p=true o[q]=g.EMPTY q.isAlive=true self._repository:Insert(q)end self._entitiesCreated={}if p then self._executor:ExecOnExitEnter(m,o)o=nil end end end)end function j:Destroy()if self._loopCancel then self._loopCancel()self._loopCancel=nil end if self._onChangeArchetypeEvent then self._onChangeArchetypeEvent:Destroy()self._onChangeArchetypeEvent=nil end self._repository=nil if self._systems then for k,l in pairs(self._systems)do l:Destroy()end self._systems=nil end self._timer=nil self._ExecPlan=nil self._entitiesCreated=nil self._entitiesUpdated=nil self._entitiesRemoved=nil setmetatable(self,nil)end function j:_OnChangeArchetype(k,l,m)if k.isAlive then if self._entitiesUpdated[k]==nil then self._dirty=true self._entitiesUpdated[k]=l end self._repository:Update(k)k.version=self.version end end return j end return c(\"ECS\")"
  },
  {
    "path": "ECS_concat.lua",
    "content": "--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tThis is a minified version of ECS Lua, to see the full source code visit\n\thttps://github.com/nidorx/ecs-lua\n\n   Discussions about this script are at https://devforum.roblox.com/t/841175\n\n\t------------------------------------------------------------------------------\n\n\tMIT License\n\n\tCopyright (c) 2021 Alex Rodin\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.\n]]\n\nlocal __M__, __F__ = {}, {}\nlocal function __REQUIRE__(m)\n   if (not __M__[m]) then\n      __M__[m] = { r = __F__[m]() }\n   end\n   return __M__[m].r\nend\n\n__F__[\"Archetype\"] = function()\n   -- src/Archetype.lua\n   \n   local archetypes = {}\n   \n   local CACHE_WITH = {}\n   local CACHE_WITHOUT = {}\n   \n   -- Version of the last registered archetype. Used to cache the systems execution plan\n   local Version = 0\n   \n   --[[\n      An Archetype is a unique combination of component types. The EntityRepository uses the archetype to group all \n      entities that have the same sets of components.\n   \n      An entity can change archetype fluidly over its lifespan. For example, when you add or remove components, \n      the archetype of the affected entity changes.\n   \n      An archetype object is not a container; rather it is an identifier to each unique combination of component \n      types that an application has created at run time, either directly or implicitly.\n   \n      You can create archetypes directly using ECS.Archetype.Of(Components[]). You also implicitly create archetypes \n      whenever you add or remove a component from an entity. An Archetype object is an immutable singleton; \n      creating an archetype with the same set of components, either directly or implicitly, results in the same \n      archetype.\n   \n      The ECS framework uses archetypes to group entities that have the same structure together. The ECS framework stores \n      component data in blocks of memory called chunks. A given chunk stores only entities having the same archetype. \n      You can get the Archetype object for a chunk from its Archetype property.\n   \n      Use ECS.Archetype.Of(Components[]) to get a Archetype reference.\n   ]]\n   local Archetype  = {}\n   Archetype.__index = Archetype\n   \n   --[[\n      Gets the reference to an archetype from the informed components\n   \n      @param componentClasses {ComponentClass[]} Component that define this archetype\n      @return Archetype\n   ]]\n   function Archetype.Of(componentClasses)\n   \n      local ids = {}\n      local cTypes = {}\n      for _, cType in ipairs(componentClasses) do\n         if (cType.IsCType and not cType.isComponent) then\n            if cType.IsQualifier then\n               if cTypes[cType] == nil then    \n                  cTypes[cType] = true\n                  table.insert(ids, cType.Id)\n               end\n               cType = cType.SuperClass\n            end\n            if cTypes[cType] == nil then    \n               cTypes[cType] = true\n               table.insert(ids, cType.Id)\n            end\n         end\n      end\n   \n      table.sort(ids)\n      local id = \"_\" .. table.concat(ids, \"_\")\n   \n      if archetypes[id] == nil then\n         archetypes[id] = setmetatable({\n            id = id,\n            _components = cTypes\n         }, Archetype)\n         Version = Version + 1\n      end\n   \n      return archetypes[id]\n   end\n   \n   --[[\n      Get the version of archetype definitions\n   \n      @return number\n   ]]\n   function Archetype.Version()\n      return Version\n   end\n   \n   --[[\n      Checks whether this archetype has the informed component\n   \n      @param componentClass {ComponentClass}\n      @return bool\n   ]]\n   function Archetype:Has(componentClass)\n      return (self._components[componentClass] == true)\n   end\n   \n   --[[\n      Gets the reference to an archetype that has the current components + the informed component\n   \n      @param componentClass {ComponentClass}\n      @return Archetype\n   ]]\n   function Archetype:With(componentClass)\n      if self._components[componentClass] == true then\n         -- component exists in that list, returns the archetype itself\n         return self\n      end\n   \n      local cache = CACHE_WITH[self]\n      if not cache then\n         cache =  {}\n         CACHE_WITH[self] = cache\n      end\n   \n      local other = cache[componentClass]\n      if other == nil then\n         local componentTs = {componentClass}\n         for component,_ in pairs(self._components) do\n            table.insert(componentTs, component)\n         end\n         other = Archetype.Of(componentTs)\n         cache[componentClass] = other\n      end\n      return other\n   end\n   \n   --[[\n      Gets the reference to an archetype that has the current components + the informed components\n   \n      @param componentClasses {ComponentClass[]}\n      @return Archetype\n   ]]\n   function Archetype:WithAll(componentClasses)\n   \n      local cTypes = {}\n      for component,_ in pairs(self._components) do\n         table.insert(cTypes, component)\n      end\n      \n      for _,component in ipairs(componentClasses) do\n         if self._components[component] == nil then\n            table.insert(cTypes, component)\n         end\n      end\n   \n      return Archetype.Of(cTypes)\n   end\n   \n   --[[\n      Gets the reference to an archetype that has the current components - the informed component\n      \n      @param componentClass {ComponentClass}\n      @return Archetype\n   ]]\n   function Archetype:Without(componentClass)\n   \n      if self._components[componentClass] == nil then\n         -- component does not exist in this list, returns the archetype itself\n         return self\n      end\n   \n      local cache = CACHE_WITHOUT[self]\n      if not cache then\n         cache =  {}\n         CACHE_WITHOUT[self] = cache\n      end\n   \n      local other = cache[componentClass]\n      if other == nil then      \n         local componentTs = {}\n         for component,_ in pairs(self._components) do\n            if component ~= componentClass then\n               table.insert(componentTs, component)\n            end\n         end\n         other =  Archetype.Of(componentTs)\n         cache[componentClass] = other\n      end\n         \n      return other\n   end\n   \n   --[[\n      Gets the reference to an archetype that has the current components - the informed components\n   \n      @param componentClasses {ComponentClass[]}\n      @return Archetype\n   ]]\n   function Archetype:WithoutAll(componentClasses)\n   \n      local toIgnoreIdx = {}\n      for _,component in ipairs(componentClasses) do\n         toIgnoreIdx[component] = true\n      end\n      \n      local cTypes = {}\n      for component,_ in pairs(self._components) do\n         if toIgnoreIdx[component] == nil then\n            table.insert(cTypes, component)\n         end\n      end\n   \n      return Archetype.Of(cTypes)\n   end\n   \n   -- Generic archetype, for entities that do not have components\n   Archetype.EMPTY = Archetype.Of({})\n   \n   return Archetype\n   \nend\n\n__F__[\"Component\"] = function()\n   -- src/Component.lua\n   local Utility = __REQUIRE__(\"Utility\")\n   local ComponentFSM = __REQUIRE__(\"ComponentFSM\")\n   \n   local copyDeep = Utility.copyDeep\n   local mergeDeep = Utility.mergeDeep\n   \n   local CLASS_SEQ = 0\n   \n   --[[\n      @param initializer {function(table) => table}\n      @param superClass {ComponentClass}\n      @return ComponentClass\n   ]]\n   local function createComponentClass(initializer, superClass)\n      CLASS_SEQ = CLASS_SEQ + 1\n   \n      local ComponentClass = {\n         Id = CLASS_SEQ,\n         IsCType = true,\n         -- Primary component\n         SuperClass = superClass\n      }\n      ComponentClass.__index = ComponentClass\n   \n      if superClass == nil then\n         superClass = ComponentClass\n         superClass._Qualifiers = { [\"Primary\"] = ComponentClass }\n         superClass._QualifiersArr = { ComponentClass }\n         superClass._Initializers = {}\n      else\n         superClass.HasQualifier = true\n         ComponentClass.IsQualifier = true\n         ComponentClass.HasQualifier = true\n      end\n   \n      local Qualifiers = superClass._Qualifiers\n      local QualifiersArr = superClass._QualifiersArr\n   \n      setmetatable(ComponentClass, {\n         __call = function(t, value)\n            return ComponentClass.New(value)\n         end,\n         __index = function(t, key)\n            if (key == \"States\") then\n               return superClass.__States       \n            end\n            if (key == \"Case\" or key == \"StateInitial\") then\n               return rawget(superClass, key)       \n            end\n         end,\n         __newindex = function(t, key, value)\n            if (key == \"Case\" or key == \"States\" or key == \"StateInitial\") then\n               -- (FMS) Finite State Machine\n               if ComponentClass == superClass then\n                  if (key == \"States\") then\n                     if not superClass.IsFSM then\n                        ComponentFSM.AddCapability(superClass, value)\n                        for _, qualifiedClass in pairs(Qualifiers) do\n                           if qualifiedClass ~= superClass then\n                              ComponentFSM.AddMethods(superClass, qualifiedClass)               \n                           end\n                        end\n                     end\n                  else\n                     rawset(t, key, value)\n                  end\n               end\n            else\n               rawset(t, key, value)\n            end\n         end\n      })\n   \n      if superClass.IsFSM then\n         ComponentFSM.AddMethods(superClass, ComponentClass)               \n      end\n   \n      --[[\n         Gets a qualifier for this type of component. If the qualifier does not exist, a new class will be created, \n         otherwise it brings the already registered class qualifier reference with the same name.\n   \n         @param qualifier {string|ComponentClass}\n         @return ComponentClass\n      ]]\n      function ComponentClass.Qualifier(qualifier)\n         if type(qualifier) ~= \"string\" then\n            for _, qualifiedClass in ipairs(QualifiersArr) do\n               if qualifiedClass == qualifier then\n                  return qualifier\n               end\n            end\n            return nil\n         end\n   \n         local qualifiedClass = Qualifiers[qualifier]\n         if qualifiedClass == nil then\n            qualifiedClass = createComponentClass(initializer, superClass)\n            Qualifiers[qualifier] = qualifiedClass\n            table.insert(QualifiersArr, qualifiedClass)\n         end\n         return qualifiedClass\n      end\n   \n      --[[\n         Get all qualified class\n   \n         @param ... {string|ComponentClass} (Optional) Allows to filter the specific qualifiers\n         @return ComponentClass[]\n      ]]\n      function ComponentClass.Qualifiers(...)\n         local filter = {...}\n         if #filter == 0 then\n            return QualifiersArr\n         else\n            local qualifiers = {}\n            local cTypes = {}\n            for _,qualifier in ipairs({...}) do\n               local qualifiedClass = ComponentClass.Qualifier(qualifier)\n               if qualifiedClass and cTypes[qualifiedClass] == nil then\n                  cTypes[qualifiedClass] = true\n                  table.insert(qualifiers, qualifiedClass)\n               end\n            end\n            return qualifiers      \n         end\n      end\n   \n      --[[\n         Constructor\n   \n         @param value {any} If the value is not a table, it will be converted to the format \"{ value = value}\"\n         @return Component\n      ]]\n      function ComponentClass.New(value)\n         if (value ~= nil and type(value) ~= \"table\") then\n            -- local MyComponent = Component({ value = Vector3.new(0, 0, 0) })\n            -- local component = MyComponent(Vector3.new(10, 10, 10))\n            value = { value = value }\n         end\n         local component = setmetatable(initializer(value) or {}, ComponentClass)\n         for _, fn in ipairs(superClass._Initializers) do\n            fn(component)\n         end\n         component.isComponent = true\n         component._qualifiers = { [ComponentClass] = component }\n         return component\n      end\n   \n      --[[\n         Get this component's class\n   \n         @return ComponentClass\n      ]]\n      function ComponentClass:GetType()\n         return ComponentClass\n      end\n   \n      --[[\n         Check if this component is of the type informed\n   \n         @param componentClass {ComponentClass}\n         @return bool\n      ]]\n      function ComponentClass:Is(componentClass)\n         return componentClass == ComponentClass or componentClass == superClass\n      end\n   \n      --[[\n         Get the instance for the primary qualifier of this class\n   \n         @return Component|nil\n      ]]\n      function ComponentClass:Primary()\n         return self._qualifiers[superClass]\n      end\n   \n      --[[\n         Get the instance for the given qualifier of this class\n   \n         @param name {string|ComponentClass}\n         @return Component|nil\n      ]]\n      function ComponentClass:Qualified(qualifier)\n         return self._qualifiers[ComponentClass.Qualifier(qualifier)]\n      end\n   \n      --[[\n         Get all instances for all qualifiers of that class\n   \n         @return Component[]\n      ]]\n      function ComponentClass:QualifiedAll()\n         local qualifiedAll = {}\n         for name, qualifiedClass in pairs(Qualifiers) do\n            qualifiedAll[name] = self._qualifiers[qualifiedClass]\n         end\n         return qualifiedAll\n      end\n   \n      --[[\n         Merges data from the other component into the current component. This method should not be invoked, it is used\n         by the entity to ensure correct retrieval of a component's qualifiers.\n   \n         @param other {Component}\n      ]]\n      function ComponentClass:Merge(other)\n         if superClass.HasQualifier then\n            if self == other then\n               return\n            end\n      \n            if self._qualifiers == other._qualifiers then\n               return\n            end\n      \n            if not other:Is(superClass) then\n               return\n            end\n      \n            local selfClass = ComponentClass\n            local otherClass = other:GetType()\n      \n            -- does anyone know the reference to the primary entity?\n            local primaryQualifiers\n            if selfClass == superClass then\n               primaryQualifiers = self._qualifiers\n            elseif otherClass == superClass then\n               primaryQualifiers = other._qualifiers\n            elseif self._qualifiers[superClass] ~= nil then\n               primaryQualifiers = self._qualifiers[superClass]._qualifiers\n            elseif other._qualifiers[superClass] ~= nil then\n               primaryQualifiers = other._qualifiers[superClass]._qualifiers\n            end\n      \n            if primaryQualifiers ~= nil then\n               if self._qualifiers ~= primaryQualifiers then\n                  for qualifiedClass, component in pairs(self._qualifiers) do\n                     if superClass ~= qualifiedClass then\n                        primaryQualifiers[qualifiedClass] = component\n                        component._qualifiers = primaryQualifiers\n                     end\n                  end\n               end\n      \n               if other._qualifiers ~= primaryQualifiers then\n                  for qualifiedClass, component in pairs(other._qualifiers) do\n                     if superClass ~= qualifiedClass then\n                        primaryQualifiers[qualifiedClass] = component\n                        component._qualifiers = primaryQualifiers\n                     end\n                  end\n               end\n            else\n               -- none of the instances know the Primary, use the current object reference\n               for qualifiedClass, component in pairs(other._qualifiers) do\n                  if selfClass ~= qualifiedClass then\n                     self._qualifiers[qualifiedClass] = component\n                     component._qualifiers = self._qualifiers\n                  end\n               end\n            end\n         end\n      end\n   \n      --[[\n         Unlink this component with the other qualifiers\n      ]]\n      function ComponentClass:Detach()\n         if not superClass.HasQualifier then\n            return\n         end\n   \n         -- remove old unlink\n         self._qualifiers[ComponentClass] = nil\n   \n         -- new link\n         self._qualifiers = { [ComponentClass] = self }\n      end\n   \n      return ComponentClass\n   end\n   \n   local function defaultInitializer(value)\n      return value or {}\n   end\n   \n   --[[\n      A Component is an object that can store data but should have not behaviour (As that should be handled by systems). \n   ]]\n   local Component = {}\n   \n   --[[\n      Register a new ComponentClass\n   \n      @param template {table|function(table?) -> table} \n         When `table`, this template will be used for creating component instances\n         When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the \n            component is passed to template function\n         If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format \n            `{ value = template }`.\n      @return ComponentClass  \n   ]]\n   function Component.Create(template)\n   \n      local initializer = defaultInitializer\n   \n      if template ~= nil then\n         local ttype = type(template)\n         if (ttype == \"function\") then\n            initializer = template\n         else\n            if (ttype ~= \"table\") then\n               template = { value = template }\n            end\n   \n            initializer = function(value)\n               local data = copyDeep(template)\n               if (value ~= nil) then\n                  mergeDeep(data, value)\n               end\n               return data\n            end\n         end\n      end\n      \n      return createComponentClass(initializer, nil)\n   end\n   \n   return Component\n   \nend\n\n__F__[\"ComponentFSM\"] = function()\n   -- src/ComponentFSM.lua\n   --[[\n      Facilitate the construction and use of a Finite State Machine (FSM) using ECS\n   \n      Example:\n         local Movement = ECS.Component({ Speed = 0 })\n   \n         Movement.States = {\n            Standing = \"*\",\n            Walking  = {\"Standing\", \"Running\"},\n            Running  = {\"Walking\"}\n         }\n   \n         Movement.StateInitial = \"Standing\"\n   \n         Movement.Case = {\n            Standing = function(self, previous)\n               self.Speed = 0\n            end,\n            Walking = function(self, previous)\n               self.Speed = 5\n            end,\n            Running = function(self, previous)\n               self.Speed = 10\n            end\n         }\n         \n         local movement = entity[Movement]\n         print(movement:GetState()) -- \"Standing\"\n         movement:SetState(\"Walking\")\n         print(movement:GetPrevState()) -- \"Standing\"\n         movement:GetStateTime()\n   \n         if (movement:GetState() == \"Standing\") then\n            movement.Speed = 0\n         end\n   ]]\n   \n   \n   local Query = __REQUIRE__(\"Query\")\n   \n   --[[\n      Filter used in Query and QueryResult\n   \n      @see QueryResult.lua\n   \n      Ex. ECS.Query.All(Movement.In(\"Standing\", \"Walking\"))\n   ]]\n   local queryFilterCTypeStateIn = Query.Filter(function(entity, config)\n      local states = config.States\n      local isSuperClass = config.IsSuperClass\n      local componentClass = config.ComponentClass\n   \n      if isSuperClass then\n         local qualifiers = componentClass.Qualifiers()\n         for _, qualifier in ipairs(qualifiers) do\n            local component = entity[qualifier]\n            if (component ~= nil and states[component:GetState()] == true) then\n               return true\n            end\n         end\n         return false\n      else\n         local component = entity[componentClass]\n         if component == nil then\n            return false\n         end\n         return states[component:GetState()] == true\n      end\n   end)\n   \n   local ComponentFSM = {}\n   \n   --[[\n      Adds FSM capability to a ComponentClass\n   \n      @param superClass {ComonentClass}\n      @param states { {[key=string] => string|string[]}}\n   \n      @see Component.lua - createComponentClass() - ComponentClass__newindex\n   ]]\n   function ComponentFSM.AddCapability(superClass, states)\n      \n      superClass.IsFSM = true\n   \n      local cTypeStates = setmetatable({}, {\n         __newindex = function(states, newState, value)   \n   \n            if (type(value) ~= \"table\") then\n               value = {value}\n            end\n   \n            if table.find(value, \"*\") then\n               rawset(states, newState, \"*\")\n            else\n               local idxSelf = table.find(value, newState)\n               if idxSelf ~= nil then\n                  table.remove(value, idxSelf)\n                  if #value == 0 then\n                     value = \"*\"\n                  end\n               end\n               rawset(states, newState, value)\n            end\n         end\n      })\n      rawset(superClass, \"__States\", cTypeStates)\n   \n      for state,value in pairs(states) do\n         if superClass.StateInitial == nil then\n            superClass.StateInitial = state\n         end\n         cTypeStates[state] = value\n      end\n   \n      ComponentFSM.AddMethods(superClass, superClass)\n   \n      table.insert(superClass._Initializers, function(component)\n         component:SetState(superClass.StateInitial)\n      end)\n   end\n   \n   \n   \n   --[[\n      Adds FSM state change methods to a ComponentClass\n   \n      @param superClass {ComponentClass}\n      @param componentClass {ComponentClass}\n   ]]\n   function ComponentFSM.AddMethods(superClass, componentClass)\n   \n      componentClass.IsFSM = true\n      local cTypeStates = superClass.States\n   \n      --[[\n         Creates a clause used to filter repository entities in a Query or QueryResult\n   \n         @param ... {string[]} \n         @return Clause\n   \n         Ex. ECS.Query.All(Movement.In(\"Walking\", \"Running\"))\n      ]]\n      function componentClass.In(...)\n         \n         local states = {}\n         local count = 0\n         for _,state in ipairs({...}) do\n            if (cTypeStates[state] ~= nil and states[state] == nil) then\n               count = count + 1\n               states[state] = true\n            end\n         end\n   \n         if count == 0 then\n            -- In any state\n            return {}\n         end\n   \n         return queryFilterCTypeStateIn({\n            States = states,\n            IsSuperClass = (componentClass == superClass),\n            ComponentClass = componentClass, \n         })\n      end   \n   \n      --[[\n         Defines the current state of the FSM\n   \n         @param newState {string}\n      ]]\n      function componentClass:SetState(newState)      \n         if (newState == nil or cTypeStates[newState] == nil) then\n            return\n         end\n   \n         local actual = self:GetState()\n         if (actual == newState) then\n            return\n         end\n   \n         if (actual ~= nil ) then\n            local transtions = cTypeStates[actual]\n            if (transtions ~= \"*\" and table.find(transtions, newState) == nil) then\n               -- not allowed\n               return\n            end\n         end\n   \n         self._state = newState\n         self._statePrev = actual\n         self._stateTime = os.clock()\n   \n         local action = superClass.Case and superClass.Case[newState]\n         if action then\n            action(self, actual)\n         end\n      end\n   \n      --[[\n         Get the current state of the FSM\n   \n         @return string\n      ]]\n      function componentClass:GetState()\n         return self._state or superClass.StateInitial\n      end\n   \n      --[[\n         Get the previous state of the FSM\n   \n         @return string|nil\n      ]]\n      function componentClass:GetPrevState()\n         return self._statePrev or nil\n      end\n   \n      --[[\n         Gets the time it changed to the current state\n      ]]\n      function componentClass:GetStateTime()\n         return self._stateTime or 0\n      end\n   end\n   \n   return ComponentFSM\n   \nend\n\n__F__[\"ECS\"] = function()\n   -- src/ECS.lua\n   --[[\n      ECS Lua v2.2.0\n   \n      ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n   \n      https://github.com/nidorx/ecs-lua\n   \n      Discussions about this script are at https://devforum.roblox.com/t/841175\n   \n      ------------------------------------------------------------------------------\n   \n      MIT License\n   \n      Copyright (c) 2020 Alex Rodin\n   \n      Permission is hereby granted, free of charge, to any person obtaining a copy\n      of this software and associated documentation files (the \"Software\"), to deal\n      in the Software without restriction, including without limitation the rights\n      to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n      copies of the Software, and to permit persons to whom the Software is\n      furnished to do so, subject to the following conditions:\n   \n      The above copyright notice and this permission notice shall be included in all\n      copies or substantial portions of the Software.\n   \n      THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n      SOFTWARE.\n   ]]\n   \n   local Query = __REQUIRE__(\"Query\")\n   local World = __REQUIRE__(\"World\")\n   local System = __REQUIRE__(\"System\")\n   local Archetype = __REQUIRE__(\"Archetype\")\n   local Component = __REQUIRE__(\"Component\")\n   \n   local function setLoopManager(manager)\n      World.LoopManager = manager\n   end\n   \n   pcall(function()\n      if (game and game.ClassName == \"DataModel\") then\n         -- is roblox\n         setLoopManager(__REQUIRE__(\"RobloxLoopManager\")())\n      end\n   end)\n   \n   --[[\n     @TODO\n      - Server entities\n      - Client - Server sincronization (snapshot, delta, spatial index, grid manhatham distance)\n      - Table pool (avoid GC)\n      - System readonly? Paralel execution\n      - Debugging?\n      - Benchmark (Local Script vs ECS implementation)\n      - Basic physics (managed)\n      - SharedComponent?\n      - Serializaton\n         - world:Serialize()\n         - world:Serialize(entity)\n         - entity:Serialize()\n         - component:Serialize()\n   ]]\n   local ECS = {\n      Query = Query,\n      World = World.New,\n      System = System.Create,\n      Archetype = Archetype,\n      Component = Component.Create,\n      SetLoopManager = setLoopManager\n   }\n   \n   if _G.ECS == nil then\n      _G.ECS = ECS\n   else\n      local warn = _G.warn or print\n      warn(\"ECS Lua was not registered in the global variables, there is already another object registered.\")\n   end\n   \n   return ECS\n   \nend\n\n__F__[\"Entity\"] = function()\n   -- src/Entity.lua\n   --[[\n      The entity is a fundamental part of the Entity Component System. Everything in your game that has data or an \n      identity of its own is an entity. However, an entity does not contain either data or behavior itself. Instead, \n      the data is stored in the components and the behavior is provided by the systems that process those components. \n   ]]\n   \n   local Archetype = __REQUIRE__(\"Archetype\")\n   \n   local SEQ  = 0\n   \n   --[[\n      [GET]\n      01) comp1 = entity[CompType1]\n      02) comp1 = entity:Get(CompType1)\n      03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n   ]]\n   local function getComponent(entity, ...)\n   \n      local values = {...}\n      local data = entity._data\n      \n      if (#values == 1) then\n         local cType = values[1]\n         if (cType.IsCType and not cType.isComponent) then\n            -- 01) comp1 = entity[CompType1]\n            -- 02) comp1 = entity:Get(CompType1)\n            return data[cType]\n         else\n            return nil\n         end\n      end\n      \n      -- 03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n      local components = {}   \n      for i,cType in ipairs(values) do\n         if (cType.IsCType and not cType.isComponent) then         \n            table.insert(components, data[cType])\n         end\n      end\n      return table.unpack(components)\n   end\n   \n   --[[\n      Merges the qualifiers of a new added component.\n   \n      @param entity {Entity}\n      @param newComponent {Component}\n      @param newComponentClass {ComponentClass}\n   ]]\n   local function mergeComponents(entity, newComponent, newComponentClass)\n      local data = entity._data\n      local otherComponent\n      -- get first instance\n      for _,oCType in ipairs(newComponentClass.Qualifiers()) do\n         if oCType ~= newComponentClass then\n            otherComponent = data[oCType]\n            if otherComponent then\n               break\n            end\n         end\n      end\n   \n      if otherComponent then\n         otherComponent:Merge(newComponent)\n      end\n   end\n   \n   --[[\n      [SET]\n      01) entity[CompType1] = nil\n      02) entity[CompType1] = value\n      03) entity:Set(CompType1, nil)   \n      04) entity:Set(CompType1, value)\n      05) entity:Set(comp1)\n      06) entity:Set(comp1, comp2, ...)\n   ]]\n   local function setComponent(entity, ...)\n   \n      local values = {...}\n      local data = entity._data\n      local archetypeOld = entity.archetype\n      local archetypeNew = archetypeOld\n   \n      local toMerge = {}\n   \n      local cType = values[1]   \n      if (cType and cType.IsCType and not cType.isComponent) then\n         local value = values[2]\n         local component\n         -- 01) entity[CompType1] = nil\n         -- 02) entity[CompType1] = value\n         -- 03) entity:Set(CompType1, nil)   \n         -- 04) entity:Set(CompType1, value)\n         if value == nil then\n            local old = data[cType]\n            if old then\n               old:Detach()\n            end\n   \n            data[cType] = nil\n            archetypeNew = archetypeNew:Without(cType)\n   \n         elseif (type(value) == \"table\" and value.isComponent) then\n            local old = data[cType]         \n            if (old ~= value) then\n               if old then\n                  old:Detach()\n               end\n   \n               cType = value:GetType()\n               data[cType] = value\n               archetypeNew = archetypeNew:With(cType)\n   \n               -- merge components\n               if (cType.HasQualifier or cType.IsQualifier) then\n                  mergeComponents(entity, value, cType)\n               end\n            end\n         else\n            local old = data[cType]\n            if old then\n               old:Detach()\n            end\n   \n            local component = cType(value)\n            data[cType] = component\n            archetypeNew = archetypeNew:With(cType)\n   \n            -- merge components\n            if (cType.HasQualifier or cType.IsQualifier) then\n               mergeComponents(entity, component, cType)\n            end\n         end\n      else\n         -- 05) entity:Set(comp1)\n         -- 06) entity:Set(comp1, comp2, ...)\n         for i,component in ipairs(values) do\n            if (component.isComponent) then\n               local cType = component:GetType()       \n               local old = data[cType]\n               if (old ~= component) then\n                  if old then\n                     old:Detach()\n                  end\n   \n                  data[cType] = component\n                  archetypeNew = archetypeNew:With(cType)\n                  \n                  -- merge components\n                  if (cType.HasQualifier or cType.IsQualifier) then\n                     mergeComponents(entity, component, cType)\n                  end\n               end              \n            end\n         end\n      end\n   \n      if (archetypeOld ~= archetypeNew) then\n         entity.archetype = archetypeNew\n         entity._onChange:Fire(entity, archetypeOld)\n      end\n   end\n   \n   --[[\n      [UNSET]\n      01) enity:Unset(comp1)\n      02) entity[CompType1] = nil\n      03) enity:Unset(CompType1)\n      04) enity:Unset(comp1, comp1, ...)\n      05) enity:Unset(CompType1, CompType2, ...)\n   ]]\n   local function unsetComponent(entity, ...)\n   \n      local data = entity._data\n      local archetypeOld = entity.archetype\n      local archetypeNew = archetypeOld\n   \n      for _,value in ipairs({...}) do\n         if value.isComponent then\n            -- 01) enity:Unset(comp1)\n            -- 04) enity:Unset(comp1, comp1, ...)\n            local cType = value:GetType()  \n            local old = data[cType]\n            if old then\n               old:Detach()\n            end\n            data[cType] = nil\n            archetypeNew = archetypeNew:Without(cType)\n            \n         elseif value.IsCType then\n            -- 02) entity[CompType1] = nil\n            -- 03) enity:Unset(CompType1)\n            -- 05) enity:Unset(CompType1, CompType2, ...)\n            local old = data[value]\n            if old then\n               old:Detach()\n            end\n            data[value] = nil\n            archetypeNew = archetypeNew:Without(value)\n         end\n      end\n   \n      if entity.archetype ~= archetypeNew then\n         entity.archetype = archetypeNew\n         entity._onChange:Fire(entity, archetypeOld)\n      end\n   end\n   \n   --[[\n      01) comps = entity:GetAll()\n      01) qualifiers = entity:GetAll(PrimaryClass)\n   ]]\n   local function getAll(entity, qualifier)\n      local data = entity._data\n      local components = {}\n      if (qualifier ~= nil and qualifier.IsCType and not qualifier.isComponent) then\n         local ctypes = qualifier.Qualifiers()\n         for _,cType in ipairs(ctypes) do\n            local component = data[cType]\n            if component then\n               table.insert(components, component)\n            end\n         end\n      else\n         for _, component in pairs(data) do\n            table.insert(components, component)\n         end\n      end\n   \n      return components\n   end\n   \n   --[[\n      01) comp = entity:GetAny(PrimaryClass)\n   ]]\n   local function getAny(entity, qualifier)\n      if (qualifier ~= nil and qualifier.IsCType and not qualifier.isComponent) then\n         local data = entity._data\n         local ctypes = qualifier.Qualifiers()\n         for _,cType in ipairs(ctypes) do\n            local component = data[cType]\n            if component then\n               return component\n            end\n         end\n      end\n   end\n   \n   local Entity = {\n      __index = function(e, key)\n         if (type(key) == \"table\") then \n            -- 01) local comp1 = entity[CompType1]\n            -- 01) local comps = entity[{CompType1, CompType2, ...}]\n            return getComponent(e, key)\n         end\n      end,\n      __newindex = function(e, key, value)\n         local isComponentSet = true\n         if (type(key) == \"table\" and (key.IsCType and not key.isComponent)) then\n            -- 01) entity[CompType1] = nil\n            -- 02) entity[CompType1] = value\n            setComponent(e, key, value)\n         else\n            rawset(e, key, value)\n         end\n      end\n   }\n   \n   --[[\n      Creates an entity having components of the specified types.\n   \n      @param onChange {Event}\n      @param components {Component[]} (Optional)\n   ]]\n   function Entity.New(onChange, components)\n   \n      local archetype = Archetype.EMPTY\n      local data = {}\n      if (components ~= nil and #components > 0) then\n         local cTypes = {}\n         for _, component in ipairs(components) do\n            local cType = component:GetType()\n            table.insert(cTypes, cType)\n            data[cType] = component\n         end\n         archetype = Archetype.Of(cTypes)\n      end\n   \n      SEQ = SEQ + 1\n   \n      return setmetatable({\n         _data = data,\n         _onChange = onChange,\n         id = SEQ,\n         isAlive = false,\n         archetype = archetype,\n         Get = getComponent,\n         Set = setComponent,\n         Unset = unsetComponent,\n         GetAll = getAll,\n         GetAny = getAny,\n      }, Entity)\n   end\n   \n   return Entity\n   \nend\n\n__F__[\"EntityRepository\"] = function()\n   -- src/EntityRepository.lua\n   local Event = __REQUIRE__(\"Event\")\n   \n   --[[\n      The repository (database) of entities in a world.\n   \n      The repository indexes entities by archetype. Whenever the entity's archetype is changed, the entity is \n      transported to the correct storage.\n   ]]\n   local EntityRepository = {}\n   EntityRepository.__index = EntityRepository\n   \n   --[[\n      Create a new repository\n   \n      @return EntityRepository\n   ]]\n   function EntityRepository.New()\n      return setmetatable({\n         _archetypes = {},\n         _entitiesArchetype = {},\n         \n      }, EntityRepository)\n   end\n   \n   --[[\n      Insert an entity into this repository\n   \n      @param entity {Entity}\n   ]]\n   function EntityRepository:Insert(entity)\n      if (self._entitiesArchetype[entity] == nil) then\n         local archetype = entity.archetype\n         local storage = self._archetypes[archetype]\n         if (storage == nil) then\n            storage = { count = 0, entities = {} }\n            self._archetypes[archetype] = storage\n         end\n      \n         storage.entities[entity] = true\n         storage.count = storage.count + 1\n         \n         self._entitiesArchetype[entity] = archetype\n      else\n         self:Update(entity)\n      end\n   end\n   \n   --[[\n      Remove an entity from this repository\n   \n      @param entity {Entity}\n   ]]\n   function EntityRepository:Remove(entity)\n      local archetypeOld = self._entitiesArchetype[entity]\n      if archetypeOld == nil then\n         return\n      end\n      self._entitiesArchetype[entity] = nil\n   \n      local storage = self._archetypes[archetypeOld]\n      if (storage ~= nil and storage.entities[entity] == true) then\n         storage.entities[entity] = nil\n         storage.count = storage.count - 1\n         if (storage.count == 0) then\n            self._archetypes[archetypeOld] = nil\n         end\n      end\n   end\n   \n   --[[\n      Updates the entity in the repository, if necessary, moves the entity from one storage to another\n   \n      @param entity {Entity}\n   ]]\n   function EntityRepository:Update(entity)\n      local archetypeOld = self._entitiesArchetype[entity]\n      if (archetypeOld == nil or archetypeOld == entity.archetype) then\n         return\n      end\n   \n      self:Remove(entity)\n      self:Insert(entity)\n   end\n   \n   --[[\n      Execute the query entered in this repository\n   \n      @param query {Query}\n      @return QueryResult\n   ]]\n   function EntityRepository:Query(query)\n      local chunks = {}\n      for archetype, storage in pairs(self._archetypes) do\n         if query:Match(archetype) then\n            table.insert(chunks, storage.entities)\n         end\n      end\n      return query:Result(chunks), #chunks > 0\n   end\n   \n   --[[\n      Quick check to find out if a query is applicable.\n   \n      @param query {Query}\n      @return bool\n   ]]\n   function EntityRepository:FastCheck(query)\n      for archetype, storage in pairs(self._archetypes) do\n         if query:Match(archetype) then\n            return true\n         end\n      end\n      return false\n   end\n   \n   return EntityRepository\n   \nend\n\n__F__[\"Event\"] = function()\n   -- src/Event.lua\n   \n   --[[\n      Subscription\n   ]]\n   local Connection = {}\n   Connection.__index = Connection\n   \n   function Connection.New(event, handler)\n      return setmetatable({ _event = event, _handler = handler }, Connection)\n   end\n   \n   -- Unsubscribe\n   function Connection:Disconnect()\n      local event = self._event\n      if (event and not event.destroyed) then\n         local idx = table.find(event._handlers, self._handler)\n         if idx ~= nil then\n            table.remove(event._handlers, idx)\n         end\n      end\n      setmetatable(self, nil)\n   end \n   \n   --[[\n      Observer Pattern\n   \n      Allows the application to fire events of a particular type.\n   ]]\n   local Event = {}\n   Event.__index = Event\n   \n   function Event.New()\n   \treturn setmetatable({ _handlers = {} }, Event)\n   end\n   \n   function Event:Connect(handler)\n   \tif (type(handler) == \"function\") then\n         table.insert(self._handlers, handler)\n         return Connection.New(self, handler)\n   \tend\n   \n      error((\"Event:Connect(%s)\"):format(typeof(handler)), 2)\n   end\n   \n   function Event:Fire(...)\n   \tif not self.destroyed then\n         for i,handler in ipairs(self._handlers) do\n            handler(table.unpack({...}))\n         end\n   \tend\n   end\n   \n   function Event:Destroy()\n   \tsetmetatable(self, nil)\n      self._handlers = nil\n      self.destroyed = true\n   end\n   \n   return Event\n   \nend\n\n__F__[\"Query\"] = function()\n   -- src/Query.lua\n   \n   local QueryResult = __REQUIRE__(\"QueryResult\")\n   \n   --[[\n      Global cache result.\n   \n      The validated components are always the same (reference in memory, except within the archetypes),\n      in this way, you can save the result of a query in an archetype, reducing the overall execution\n      time (since we don't need to iterate all the time)\n   \n      @type KEY string = concat(Array<ComponentClass.Id>, \"_\")\n      @Type {\n         [Archetype] = {\n            Any  = { [KEY] = bool },\n            All  = { [KEY] = bool },\n            None = { [KEY] = bool },\n         }\n      }\n   ]]\n   local CACHE = {}\n   \n   --[[\n      Interface for creating filters for existing entities in the ECS world\n   ]]\n   local Query = {}\n   Query.__index = Query\n   setmetatable(Query, {\n      __call = function(t, all, any, none)\n         return Query.New(all, any, none)\n      end,\n   })\n   \n   local function parseFilters(list, clauseGroup, clauses)\n      local indexed = {}\n      local cTypes = {}\n      local cTypeIds = {}\n      \n      for i,item in ipairs(list) do\n         if (indexed[item] == nil) then\n            if (item.IsCType and not item.isComponent) then\n               indexed[item] = true\n               table.insert(cTypes, item)\n               table.insert(cTypeIds, item.Id)\n            else\n               -- clauses\n               if item.Filter then\n                  indexed[item] = true\n                  item[clauseGroup] = true\n                  table.insert(clauses, item)\n               end\n            end\n         end\n      end\n   \n      if #cTypes > 0 then\n         table.sort(cTypeIds)\n         local cTypesKey = \"_\" .. table.concat(cTypeIds, \"_\")   \n         return cTypes, cTypesKey\n      end\n   end\n   \n   --[[\n      Create a new Query used to filter entities in the world. It makes use of local and global cache in order to \n      decrease the validation time (avoids looping in runtime of systems)\n   \n      @param all {Array<ComponentClass|Clause>} Optional All component types in this array must exist in the archetype\n      @param any {Array<ComponentClass|Clause>} Optional At least one of the component types in this array must exist in the archetype\n      @param none {Array<ComponentClass|Clause>} Optional None of the component types in this array can exist in the archetype\n   ]]\n   function Query.New(all, any, none)\n   \n      -- used by QueryResult\n      local clauses = {}\n   \n      local anyKey, allKey, noneKey\n   \n      if (any ~= nil) then\n         any, anyKey = parseFilters(any, \"IsAnyFilter\", clauses)\n      end\n   \n      if (all ~= nil) then\n         all, allKey = parseFilters(all, \"IsAllFilter\", clauses)\n      end\n   \n      if (none ~= nil) then\n         none, noneKey = parseFilters(none, \"IsNoneFilter\", clauses)\n      end\n   \n      return setmetatable({\n         isQuery = true,\n         _any = any,\n         _all = all,\n         _none = none,\n         _anyKey = anyKey,\n         _allKey = allKey,\n         _noneKey = noneKey,\n         _cache = {}, -- local cache (L1)\n         _clauses = #clauses > 0 and clauses or nil\n      }, Query)\n   end\n   \n   --[[\n      Generate a QueryResult with the chunks entered and the clauses of the current query\n   \n      @param chunks {Chunk}\n      @return QueryResult\n   ]]\n   function Query:Result(chunks)\n      return QueryResult.New(chunks, self._clauses)\n   end\n   \n   --[[\n      Checks if the entered archetype is valid by the query definition\n   \n      @param archetype {Archetype}\n      @return bool\n   ]]\n   function Query:Match(archetype)\n   \n      -- cache L1\n      local localCache = self._cache\n      \n      -- check local cache (L1)\n      local cacheResult = localCache[archetype]\n      if cacheResult ~= nil then\n         return cacheResult\n      else\n         -- check global cache (executed by other filter instance)\n         local globalCache = CACHE[archetype]\n         if (globalCache == nil) then\n            globalCache = { Any = {}, All = {}, None = {} }\n            CACHE[archetype] = globalCache\n         end\n         \n         -- check if these combinations exist in this component array\n   \n         local noneKey = self._noneKey\n         if noneKey then\n            local isNoneValid = globalCache.None[noneKey]\n            if (isNoneValid == nil) then\n               isNoneValid = true\n               for _, cType in ipairs(self._none) do\n                  if archetype:Has(cType) then\n                     isNoneValid = false\n                     break\n                  end\n               end\n               globalCache.None[noneKey] = isNoneValid\n            end\n   \n            if (isNoneValid == false) then\n               localCache[archetype] = false\n               return false\n            end     \n         end\n   \n         local anyKey = self._anyKey\n         if anyKey then\n            local isAnyValid = globalCache.Any[anyKey]\n            if (isAnyValid == nil) then\n               isAnyValid = false\n               if (globalCache.All[anyKey] == true) then\n                  isAnyValid = true\n               else\n                  for _, cType in ipairs(self._any) do\n                     if archetype:Has(cType) then\n                        isAnyValid = true\n                        break\n                     end\n                  end\n               end\n               globalCache.Any[anyKey] = isAnyValid\n            end\n   \n            if (isAnyValid == false) then\n               localCache[archetype] = false\n               return false\n            end\n         end\n   \n         local allKey = self._allKey\n         if allKey then\n            local isAllValid = globalCache.All[allKey]\n            if (isAllValid == nil) then\n               local haveAll = true\n               for _, cType in ipairs(self._all) do\n                  if (not archetype:Has(cType)) then\n                     haveAll = false\n                     break\n                  end\n               end\n   \n               if haveAll then\n                  isAllValid = true\n               else\n                  isAllValid = false\n               end\n   \n               globalCache.All[allKey] = isAllValid\n            end\n   \n            localCache[archetype] = isAllValid\n            return isAllValid\n         end\n   \n         -- empty query = SELECT * FROM\n         localCache[archetype] = true\n         return true\n      end\n   end\n   \n   local function builder()\n      local builder = {\n         isQueryBuilder = true\n      }\n      local query\n   \n      function builder.All(...)\n         query = nil\n         builder._all = {...}\n         return builder\n      end\n      \n      function builder.Any(...)\n         query = nil\n         builder._any = {...}\n         return builder\n      end\n      \n      function builder.None(...)\n         query = nil\n         builder._none = {...}\n         return builder\n      end\n   \n      function builder.Build()\n         if query == nil then\n            query = Query.New(builder._all, builder._any, builder._none)\n         end\n         return query\n      end\n   \n      return builder\n   end\n   \n   function Query.All(...)\n      return builder().All(...)\n   end\n   \n   function Query.Any(...)\n      return builder().Any(...)\n   end\n   \n   function Query.None(...)\n      return builder().None(...)\n   end\n   \n   --[[\n      Create custom filters that can be used in Queries. Its execution is delayed, invoked only in QueryResult methods\n   \n      The result of executing the clause depends on how it was used in the query.\n   \n      Ex. If used in Query.All() the result is the inverse of using the same clause in Query.None()\n   \n         local Player = ECS.Component({ health = 100 })\n   \n         local HealthPlayerFilter = ECS.Query.Filter(function(entity, config)\n            local player = entity[Player]\n            return player.health >= config.minHealth and player.health <= config.maxHealth\n         end)\n   \n         local healthyClause = HealthPlayerFilter({\n            minHealth = 80,\n            maxHealth = 100,\n         })\n   \n         local healthyQuery = ECS.Query.All(Player, healthyClause)\n         world:Exec(healthyQuery):ForEach(function(entity)\n            -- this player is very healthy\n         end)\n   \n         local notHealthyQuery = ECS.Query.All(Player).None(healthyClause)\n         world:Exec(healthyQuery):ForEach(function(entity)\n            -- this player is NOT very healthy\n         end)\n   \n         local dyingClause = HealthPlayerClause({\n            minHealth = 1,\n            maxHealth = 20,\n         })\n   \n         local dyingQuery = ECS.Query.All(Player, dyingClause)\n         world:Exec(dyingQuery):ForEach(function(entity)\n            -- this player is about to die\n         end)\n   \n         local notDyingQuery = ECS.Query.All(Player).None(dyingClause)\n         world:Exec(notDyingQuery):ForEach(function(entity)\n            -- this player is NOT about to die\n         end)\n   \n      @param filter {function(entity, config):bool} \n      @return function(config):Clause\n   ]]\n   function Query.Filter(filter)\n      return function (config)\n         return {\n            Filter = filter,\n            Config = config\n         }\n      end\n   end\n   \n   return Query\n   \nend\n\n__F__[\"QueryResult\"] = function()\n   -- src/QueryResult.lua\n   \n   --[[\n      OperatorFunction = function(param, value, count) => newValue, acceptItem, mustContinue\n   ]]\n   \n   local function operatorFilter(predicate, value, count)\n      return value, (predicate(value) == true), true\n   end\n   \n   local function operatorMap(mapper, value, count)\n      return mapper(value), true, true\n   end\n   \n   local function operatorLimit(limit, value, count)\n      local accept = (count <= limit)\n      return value, accept, accept\n   end\n   \n   local function operatorClauseNone(clauses, value, count)\n      local acceptItem = true\n      for _,clause in ipairs(clauses) do\n         if (clause.Filter(value, clause.Config) == true) then\n            acceptItem = false\n            break\n         end \n      end\n      return value, acceptItem, true\n   end\n   \n   local function operatorClauseAll(clauses, value, count)\n      local acceptItem = true\n      for _,clause in ipairs(clauses) do\n         if (clause.Filter(value, clause.Config) == false) then\n            acceptItem = false\n            break\n         end\n      end\n      return value, acceptItem, true\n   end\n   \n   local function operatorClauseAny(clauses, value, count)\n      local acceptItem = false\n      for _,clause in ipairs(clauses) do\n         if (clause.Filter(value, clause.Config) == true) then\n            acceptItem = true\n            break\n         end\n      end\n      return value, acceptItem, true\n   end\n   \n   local EMPTY_OBJECT = {}\n   \n   --[[\n      The result of a Query that was executed on an EntityStorage.\n   \n      QueryResult provides several methods to facilitate the filtering of entities resulting from the execution of the \n      query.\n   ]]\n   local QueryResult = {}\n   QueryResult.__index = QueryResult\n   \n   --[[\n      Build a new QueryResult\n   \n      @param chunks { Array<{ [Entity] = true }> }\n      @clauses {Clause[]}\n   \n      @see Query.lua\n      @see EntityRepository:Query(query)\n      @return QueryResult\n   ]]\n   function QueryResult.New(chunks, clauses)\n   \n      local pipeline = EMPTY_OBJECT\n      if (clauses and #clauses > 0) then\n         local all = {}\n         local any = {}\n         local none = {}\n   \n         pipeline = {}\n         \n         for i,clause in ipairs(clauses) do\n            if clause.IsNoneFilter then\n               table.insert(none, clause)\n            elseif clause.IsAnyFilter then\n               table.insert(any, clause)\n            else\n               table.insert(all, clause)\n            end \n         end\n   \n         if (#none > 0) then\n            table.insert(pipeline, {operatorClauseNone, none})\n         end\n         \n         if (#all > 0) then\n            table.insert(pipeline, {operatorClauseAll, all})\n         end\n   \n         if (#any > 0) then\n            table.insert(pipeline, {operatorClauseAny, any})\n         end\n        \n      end\n   \n      return setmetatable({\n         chunks = chunks,\n         _pipeline = pipeline,\n      }, QueryResult)\n   end\n   \n   --[[ -------------------------------------------------------------------------------------------------------------------\n      Intermediate Operations\n   \n      Intermediate operations return a new QueryResult. They are always lazy; executing an intermediate operation such as \n      QueryResult:Filter() does not actually perform any filtering, but instead creates a new QueryResult that, when traversed, \n      contains the elements of the initial QueryResult that match the given predicate. Traversal of the pipeline source \n      does not begin until the terminal operation of the pipeline is executed.\n   ]] ---------------------------------------------------------------------------------------------------------------------\n   \n   --[[\n      Returns a QueryResult consisting of the elements of this QueryResult with a new pipeline operation\n   \n      @param operation {function(param, value, count) -> newValue, accept, continues}\n      @param param {any}\n      @return the new QueryResult\n   ]]\n   function QueryResult:With(operation, param)\n      local pipeline = {}\n      for _,operator in ipairs(self._pipeline) do\n         table.insert(pipeline, operator)\n      end\n      table.insert(pipeline, { operation, param })\n   \n      return setmetatable({\n         chunks = self.chunks,\n         _pipeline = pipeline,\n      }, QueryResult)\n   end\n   \n   --[[\n      Returns a QueryResult consisting of the elements of this QueryResult that match the given predicate.\n   \n      @param predicate {function(value) -> bool} a predicate to apply to each element to determine if it should be included\n      @return the new QueryResult\n   ]]\n   function QueryResult:Filter(predicate)\n      return self:With(operatorFilter, predicate)\n   end\n   \n   --[[\n      Returns a QueryResult consisting of the results of applying the given function to the elements of this QueryResult.\n   \n      @param mapper {function(value) -> newValue} a function to apply to each element\n      @return the new QueryResult\n   ]]\n   function QueryResult:Map(mapper)\n      return self:With(operatorMap, mapper)\n   end\n   \n   --[[\n      Returns a QueryResult consisting of the elements of this QueryResult, truncated to be no longer than maxSize in length.\n      \n      This is a short-circuiting stateful intermediate operation.\n   \n      @param maxSize {number}\n      @return the new QueryResult\n   ]]\n   function QueryResult:Limit(maxSize)\n      return self:With(operatorLimit, maxSize)\n   end\n   \n   --[[ -------------------------------------------------------------------------------------------------------------------\n      Terminal Operations\n   \n      Terminal operations, such as QueryResult:ForEach or QueryResult.AllMatch, may traverse the QueryResult to produce a \n      result or a side-effect. After the terminal operation is performed, the pipeline is considered consumed, and can no \n      longer be used; if you need to traverse the same data source again, you must return to the data source to get a new \n      QueryResult.\n   ]] ---------------------------------------------------------------------------------------------------------------------\n   \n   --[[\n      Returns whether any elements of this result match the provided predicate.\n   \n      @param predicate { function(value) -> bool} a predicate to apply to elements of this result\n      @returns true if any elements of the result match the provided predicate, otherwise false\n   ]]\n   function QueryResult:AnyMatch(predicate)\n      local anyMatch = false\n      self:ForEach(function(value)\n         if predicate(value) then\n            anyMatch = true\n         end\n         -- break if true\n         return anyMatch\n      end)\n      return anyMatch\n   end\n   \n   --[[\n      Returns whether all elements of this result match the provided predicate.\n   \n      @param predicate { function(value) -> bool} a predicate to apply to elements of this result\n      @returns true if either all elements of the result match the provided predicate or the result is empty, otherwise false\n   ]]\n   function QueryResult:AllMatch(predicate)\n      local allMatch = true\n      self:ForEach(function(value)\n         if (not predicate(value)) then\n            allMatch = false\n         end\n         -- break if false\n         return allMatch == false\n      end)\n      return allMatch\n   end\n   \n   --[[\n      Returns some element of the result, or nil if the result is empty.\n   \n      This is a short-circuiting terminal operation.\n   \n      The behavior of this operation is explicitly nondeterministic; it is free to select any element in the result. \n      \n      Multiple invocations on the same result may not return the same value.\n   \n      @return {any}\n   ]]\n   function QueryResult:FindAny()\n      local out\n      self:ForEach(function(value)\n         out = value\n         -- break\n         return true\n      end)\n      return out\n   end\n   \n   --[[\n      Returns an array containing the elements of this QueryResult.\n   \n      This is a terminal operation.\n   ]]\n   function QueryResult:ToArray()\n      local array = {}\n      self:ForEach(function(value)\n         table.insert(array, value)\n      end)\n      return array\n   end\n   \n   --[[\n      Returns an Iterator, to use in for loop\n   \n      for count, entity in result:Iterator() do\n         print(entity.id)\n      end\n   ]]\n   function QueryResult:Iterator()\n      local thread = coroutine.create(function()\n         self:ForEach(function(value, count)\n            -- These will be passed back again next iteration\n            coroutine.yield(value, count)\n         end)\n      end)\n   \n      return function()\n         local success, item, index = coroutine.resume(thread)\n         return index, item\n      end\n   end\n   \n   --[[\n      Performs an action for each element of this QueryResult.\n   \n      This is a terminal operation.\n   \n      The behavior of this operation is explicitly nondeterministic. This operation does not guarantee to respect the \n      encounter order of the QueryResult.\n   \n      @param action {function(value, count) -> bool} A action to perform on the elements, breaks execution case returns true\n   ]]\n   function QueryResult:ForEach(action)\n      local count = 1\n      local pipeline = self._pipeline\n   \n      local hasPipeline = #pipeline > 0 \n      if (not hasPipeline) then\n         -- faster\n         for _, entities in ipairs(self.chunks) do\n            for entity, _ in pairs(entities) do\n               if (action(entity, count) == true) then\n                  return\n               end\n               count = count + 1  \n            end\n         end\n      else\n         -- Pipeline this QueryResult, applying callback to each value\n         for i, entities in ipairs(self.chunks) do\n            for entity,_ in pairs(entities) do\n               local mustStop = false\n               local itemAccepted = true\n   \n               local value = entity\n               if (itemAccepted and hasPipeline) then               \n                  for _, operator in ipairs(pipeline) do\n                     local newValue, acceptItem, canContinue = operator[1](operator[2], value, count)\n                     if (not canContinue) then\n                        mustStop = true\n                     end\n      \n                     if acceptItem then\n                        value = newValue\n                     else\n                        itemAccepted = false\n                        break\n                     end\n                  end\n               end\n               \n               if itemAccepted then\n                  if (action(value, count) == true) then\n                     return\n                  end\n                  count = count + 1\n               end\n      \n               if mustStop then\n                  return\n               end\n            end\n         end\n      end\n   end\n   \n   return QueryResult\n   \nend\n\n__F__[\"RobloxLoopManager\"] = function()\n   -- src/RobloxLoopManager.lua\n   local function InitManager()\n      local RunService = game:GetService(\"RunService\")\n      \n      return {\n         Register = function(world)         \n            -- if not RunService:IsRunning() then\n            --    return\n            -- end\n            local beforePhysics = RunService.Stepped:Connect(function()\n               world:Update(\"process\", os.clock())\n            end)\n      \n            local afterPhysics = RunService.Heartbeat:Connect(function()\n               world:Update(\"transform\", os.clock())\n            end)\n      \n            local beforeRender\n            if (not RunService:IsServer()) then\n               beforeRender = RunService.RenderStepped:Connect(function()\n                  world:Update(\"render\", os.clock())\n               end)\n            end\n      \n            return function()\n               beforePhysics:Disconnect()\n               afterPhysics:Disconnect()\n               if beforeRender then\n                  beforeRender:Disconnect()\n               end\n            end\n         end\n      }\n   end\n   \n   return InitManager\n   \nend\n\n__F__[\"System\"] = function()\n   -- src/System.lua\n   \n   local STEPS = { \"task\", \"render\", \"process\", \"transform\" }\n   \n   local System = {}\n   \n   --[[\n      Create new System Class\n   \n      @param step {process|transform|render|task}\n      @param order {number} (Optional) Allows you to set an execution order (for systems that are not `task`). Default 50\n      @param query {Query|QueryBuilder} (Optional) Filters the entities that will be processed by this system\n      @param updateFn {function(self, Time)} (Optional) A shortcut for creating systems that only have the Update method\n      @return SystemClass\n   ]]\n   function System.Create(step, order, query, updateFn)\n   \n      if (step == nil or not table.find(STEPS, step)) then\n         error(\"The step parameter must one of \", table.concat(STEPS, \", \"))\n      end\n   \n      if (order and type(order) == \"function\") then\n         updateFn = order\n         order = nil\n      elseif query and type(query) == \"function\" then\n         updateFn = query\n         query = nil\n      end\n   \n      if (order and type(order) == \"table\" and (order.isQuery or order.isQueryBuilder)) then\n         query = order\n         order = nil\n      end\n   \n      if (order == nil or order < 0) then\n         order = 50\n      end\n   \n      if type(query) == \"function\" then\n         updateFn = query\n         query = nil\n      end\n   \n      if (query and query.isQueryBuilder) then\n         query = query.Build()\n      end\n   \n      local SystemClass = {\n         Step = step,\n         -- Allows you to define the execution priority level for this system\n         Order = order,\n         Query = query,\n         -- After = {SystemC, SystemD}, When the system is a task, it allows you to define that this system should run AFTER other specific systems.\n         -- Before = {SystemA, SystemB}, When the system is a task, it allows you to define that this system should run BEFORE other specific systems.\n         --[[\n   \n            ShouldUpdate(Time) -> bool - Invoked before 'Update', allows you to control the execution of the update\n            Update(Time)\n   \n            [QuerySystem]\n               OnRemove(Time, enity)\n               OnExit(Time, entity)\n               OnEnter(Time, entity)\n         ]]\n      }\n      SystemClass.__index = SystemClass\n   \n      --[[\n         Create an instance of this system\n   \n         @param world {World}\n         @param config {table}\n      ]]\n      function SystemClass.New(world, config)\n         local system = setmetatable({\n            version = 0,\n            _world = world,\n            _config = config,\n         }, SystemClass)\n   \n         if system.Initialize then\n            system:Initialize(config)\n         end\n   \n         return system\n      end\n   \n       --[[\n         Get this system class\n   \n         @return SystemClass\n      ]]\n      function SystemClass:GetType()\n         return SystemClass\n      end\n   \n      --[[\n         Run a query in the world. A shortcut to `self._world:Exec(query)`\n   \n         @query {Query|QueryBuilder} Optional If nil, use default query\n         @return QueryResult\n      ]]\n      function SystemClass:Result(query)\n         return self._world:Exec(query or SystemClass.Query)\n      end\n   \n      --[[\n         destroy this instance\n      ]]\n      function SystemClass:Destroy() \n         if self.OnDestroy then\n            self.OnDestroy()\n         end\n         setmetatable(self, nil)\n         for k,v in pairs(self) do\n            self[k] = nil\n         end\n      end\n   \n      if updateFn and type(updateFn) == \"function\" then\n         SystemClass.Update = updateFn\n      end\n   \n      return SystemClass\n   end\n   \n   return System\n   \nend\n\n__F__[\"SystemExecutor\"] = function()\n   -- src/SystemExecutor.lua\n   \n   --[[\n      After = {SystemC, SystemD}, An update order that requests ECS update this system after it updates another specified system.\n      Before = {SystemA, SystemB}, An update order that requests ECS update this system before it updates another specified system.\n   ]]\n   local function mapTaskDependencies(systems)\n   \n      local nodes = {}\n      local nodesByType = {}\n   \n      for i,system in ipairs(systems) do\n         local sType = system:GetType()\n   \n         if (system._TaskState == nil) then\n            -- suspended, scheduled, running\n            system._TaskState = \"suspended\"\n         end\n   \n         if not nodesByType[sType] then\n            local node = {\n               Type = sType,\n               System = system,\n               -- @type {[Node]=true}\n               Depends = {}\n            }\n            nodesByType[sType] = node\n            table.insert(nodes, node)        \n         end\n      end\n   \n      for _, node in ipairs(nodes) do\n          -- this system will update Before another specified system\n          local before = node.Type.Before\n          if (before ~= nil and #before > 0) then\n             for _,sTypeOther in ipairs(before) do\n                local otherNode = nodesByType[sTypeOther]\n                if otherNode then\n                   otherNode.Depends[node] = true\n                end\n             end\n          end\n   \n         -- this system will update After another specified system\n         local after = node.Type.After\n         if (after ~= nil and #after > 0) then\n            for _,sTypeOther in ipairs(after) do\n               local otherNode = nodesByType[sTypeOther]\n               if otherNode then\n                  node.Depends[otherNode] = true\n               end\n            end\n         end\n      end\n   \n      return nodes\n   end\n   \n   local function orderSystems(a, b)\n      return a.Order < b.Order\n   end\n   \n   --[[\n      Responsible for coordinating and executing the systems methods\n   ]]\n   local SystemExecutor = {}\n   SystemExecutor.__index = SystemExecutor\n   \n   function SystemExecutor.New(world)   \n      local executor =  setmetatable({\n         _world = world,\n         _onExit = {},\n         _onEnter = {},\n         _onRemove = {},\n         _task = {},\n         _render = {},\n         _process = {},\n         _transform = {},\n         _schedulers = {},\n         _lastFrameMatchQueries = {},\n         _currentFrameMatchQueries = {},\n      }, SystemExecutor)\n   \n      world:OnQueryMatch(function(query)\n         executor._currentFrameMatchQueries[query] = true\n      end)\n   \n      return executor\n   end\n   \n   function SystemExecutor:SetSystems(systems)\n      local onExit = {}\n      local onEnter = {}\n      local onRemove = {}\n      -- system:Update()\n      local updateTask = {}\n      local updateRender = {}\n      local updateProcess = {}\n      local updateTransform = {}\n   \n      for _, system in pairs(systems) do      \n         local step = system.Step\n         if system.Update then\n            if step == \"task\" then\n               table.insert(updateTask, system)\n               \n            elseif step == \"process\" then\n               table.insert(updateProcess, system) \n   \n            elseif step == \"transform\" then\n               table.insert(updateTransform, system)\n   \n            elseif step == \"render\" then\n               table.insert(updateRender, system)\n   \n            end\n         end\n   \n         if (system.Query and system.Query.isQuery and step ~= \"task\") then\n            if system.OnExit then\n               table.insert(onExit, system)\n            end\n   \n            if system.OnEnter then\n               table.insert(onEnter, system)\n            end\n      \n            if system.OnRemove then\n               table.insert(onRemove, system)\n            end\n         end\n      end\n   \n      updateTask = mapTaskDependencies(updateTask)\n      \n      table.sort(onExit, orderSystems)\n      table.sort(onEnter, orderSystems)\n      table.sort(onRemove, orderSystems)\n      table.sort(updateRender, orderSystems)\n      table.sort(updateProcess, orderSystems)\n      table.sort(updateTransform, orderSystems)\n   \n      self._onExit = onExit\n      self._onEnter = onEnter\n      self._onRemove = onRemove\n      self._task = updateTask\n      self._render = updateRender\n      self._process = updateProcess\n      self._transform = updateTransform\n   end\n   \n   --[[\n         \n      @param Time\n      @param changedEntities { { [Entity] = Old<Archetype> } }\n   ]]\n   function SystemExecutor:ExecOnExitEnter(Time, changedEntities)\n      local isEmpty = true\n   \n      -- { [Old<Archetype>] = { [New<Archetype>] = {Entity, Entity, ...} } }\n      local oldIndexed = {}\n      for entity, archetypeOld in pairs(changedEntities) do\n         local newIndexed = oldIndexed[archetypeOld]\n         if not newIndexed then\n            newIndexed = {}\n            oldIndexed[archetypeOld] = newIndexed\n         end\n         local archetypeNew = entity.archetype\n   \n         local entities = newIndexed[archetypeNew]\n         if not entities then\n            entities = {}\n            newIndexed[archetypeNew] = entities\n         end\n         table.insert(entities, entity)\n         isEmpty = false\n      end\n      if isEmpty then\n         return\n      end\n      self:_ExecOnEnter(Time, oldIndexed)\n      self:_ExecOnExit(Time, oldIndexed)\n   end\n   \n   --[[\n      Executes the systems' OnEnter method\n   \n      @param Time {Time}\n      @param entities {{[Key=Entity] => Archetype}}\n      ]]\n   function SystemExecutor:_ExecOnEnter(Time, oldIndexed)\n      local world = self._world\n      for _, system in ipairs(self._onEnter) do\n         local query = system.Query\n         for archetypeOld, newIndexed in pairs(oldIndexed) do\n            if not query:Match(archetypeOld) then\n               for archetypeNew, entities in pairs(newIndexed) do\n                  if query:Match(archetypeNew) then\n                     for i,entity in ipairs(entities) do                  \n                        world.version = world.version + 1   -- increment Global System Version (GSV)\n                        system:OnEnter(Time, entity)        -- local dirty = entity.version > system.version\n                        system.version = world.version      -- update last system version with GSV\n                     end\n                  end\n               end\n            end         \n         end\n      end\n   end\n   \n   --[[\n      Executes the systems' OnExit method\n   \n      @param Time {Time}\n      @param entities {{[Key=Entity] => Archetype}}\n   ]]\n   function SystemExecutor:_ExecOnExit(Time, oldIndexed)\n      local world = self._world\n      for _, system in ipairs(self._onExit) do\n         local query = system.Query\n         for archetypeOld, newIndexed in pairs(oldIndexed) do\n            if query:Match(archetypeOld) then\n               for archetypeNew, entities in pairs(newIndexed) do\n                  if not query:Match(archetypeNew) then\n                     for i,entity in ipairs(entities) do                  \n                        world.version = world.version + 1   -- increment Global System Version (GSV)\n                        system:OnExit(Time, entity)         -- local dirty = entity.version > system.version\n                        system.version = world.version      -- update last system version with GSV\n                     end\n                  end\n               end\n            end         \n         end\n      end\n   end\n   \n   --[[\n      Executes the systems' OnRemove method\n   \n      @param Time {Time}\n      @param entities {{[Key=Entity] => Archetype}}\n   ]]\n   function SystemExecutor:ExecOnRemove(Time, removedEntities)\n      \n      local isEmpty = true\n      local oldIndexed = {}\n      for entity, archetypeOld in pairs(removedEntities) do\n         local entities = oldIndexed[archetypeOld]\n         if not entities then\n            entities = {}\n            oldIndexed[archetypeOld] = entities\n         end\n         table.insert(entities, entity)\n         isEmpty = false\n      end\n      if isEmpty then\n         return\n      end\n      \n      local world = self._world\n      for _, system in ipairs(self._onRemove) do \n         for archetypeOld, entities in pairs(oldIndexed) do\n            if system.Query:Match(archetypeOld) then\n               for i,entity in ipairs(entities) do  \n                  world.version = world.version + 1   -- increment Global System Version (GSV)\n                  system:OnRemove(Time, entity)       -- local dirty = entity.version > system.version\n                  system.version = world.version      -- update last system version with GSV\n               end\n            end\n         end\n      end\n   end\n   \n   local function execUpdate(executor, systems, Time)\n      local world = executor._world\n      local lastFrameMatchQueries = executor._lastFrameMatchQueries\n      local currentFrameMatchQueries = executor._currentFrameMatchQueries\n      for j, system in ipairs(systems) do\n         local canExec = true\n         if system.Query then\n            local query = system.Query\n            if lastFrameMatchQueries[query] == true or currentFrameMatchQueries[query] == true then\n               -- If the query ran in the last frame, it is likely to run successfully on this\n               canExec = true\n            else\n               -- Always revalidates, the repository undergoes constant change\n               canExec = world:FastCheck(query)\n               currentFrameMatchQueries[query] = canExec\n            end\n         end\n         if canExec then\n            if (system.ShouldUpdate == nil or system.ShouldUpdate(Time)) then\n               world.version = world.version + 1   -- increment Global System Version (GSV)\n               system:Update(Time)                 -- local dirty = entity.version > system.version\n               system.version = world.version      -- update last system version with GSV\n            end\n         end\n      end\n   end\n   \n   function SystemExecutor:ExecProcess(Time)\n      self._currentFrameMatchQueries = {}\n      execUpdate(self, self._process, Time)   \n   end\n   \n   function SystemExecutor:ExecTransform(Time)\n      execUpdate(self, self._transform, Time)\n   end\n   \n   function SystemExecutor:ExecRender(Time)\n      execUpdate(self, self._render, Time)\n      self._lastFrameMatchQueries = self._currentFrameMatchQueries\n   end\n   \n   --[[\n      Starts the execution of Jobs.\n   \n      Each Job is performed in an individual coroutine\n   \n      @param maxExecTime {number} limits the amount of time jobs can run\n   ]]\n   function SystemExecutor:ExecTasks(maxExecTime)\n      while maxExecTime > 0 do\n         local hasMore = false\n   \n         -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785\n         local i, len = 0, #self._schedulers-1\n         while i <= len do\n            i = i + 1\n   \n            local scheduler = self._schedulers[i]\n            local tasksTime, hasMoreTask = scheduler.Resume(maxExecTime)\n            \n            if hasMoreTask then\n               hasMore = true\n            end\n      \n            maxExecTime = maxExecTime - (tasksTime + 0.00001)\n            \n            if (maxExecTime <= 0) then\n               break\n            end\n         end\n   \n         if not hasMore then\n            return\n         end\n      end\n   end\n   \n   local function execTask(node, Time, world, onComplete)\n      local system = node.System\n      system._TaskState = \"running\"\n      if (system.ShouldUpdate == nil or system.ShouldUpdate(Time)) then\n         world.version = world.version + 1   -- increment Global System Version (GSV)\n         system:Update(Time)                 -- local dirty = entity.version > system.version\n         system.version = world.version      -- update last system version with GSV\n      end\n      system._TaskState = \"suspended\"\n      onComplete(node)\n   end\n   \n   --[[\n      Invoked at the beginning of each frame, it schedules the execution of the next tasks\n   ]]\n   function SystemExecutor:ScheduleTasks(Time)\n      local world = self._world\n   \n      local rootNodes = {}    -- Node[]\n      local runningNodes = {} -- Node[]\n      local scheduled = {}    -- { [Node] = true }\n      local completed = {}    -- { [Node] = true }\n      local dependents = {}   -- { [Node] = Node[] }\n   \n      local i, len = 0, #self._task-1\n      while i <= len do\n         i = i + 1\n         local node = self._task[i]\n         \n         if (node.System._TaskState == \"suspended\") then\n            -- will be executed\n            node.System._TaskState = \"scheduled\"\n   \n            local hasDependencies = false\n            for other,_ in pairs(node.Depends) do\n               hasDependencies = true\n               if dependents[other] == nil then\n                  dependents[other] = {}\n               end\n               table.insert(dependents[other], node)\n            end\n            \n            if (not hasDependencies) then\n               table.insert(rootNodes, node)\n            end\n   \n            scheduled[node] = true\n         end\n      end\n   \n      -- suspended, scheduled, running\n      local function onComplete(node)\n   \n         node.Thread = nil\n         node.LastExecTime = nil\n         completed[node] = true\n   \n         -- alguma outra tarefa depende da execucao deste no para executar?\n         if dependents[node] then\n            local dependentsFromNode = dependents[node]\n   \n            local i, len = 0, #dependentsFromNode-1\n            while i <= len do\n               i = i + 1\n               local dependent = dependentsFromNode[i]\n               if scheduled[dependent] then\n                  local allDependenciesCompleted = true\n                  for otherNode,_ in pairs(dependent.Depends) do\n                     if completed[otherNode] ~= true then\n                        allDependenciesCompleted = false\n                        break\n                     end\n                  end\n      \n                  if allDependenciesCompleted then\n                     scheduled[dependent] = nil\n                     dependent.LastExecTime = 0\n                     dependent.Thread = coroutine.create(execTask)\n                     table.insert(runningNodes, dependent)\n                  end\n               end\n            end\n         end\n      end\n   \n      if #rootNodes > 0 then\n         local i, len = 0, #rootNodes-1\n         while i <= len do\n            i = i + 1\n            local node = rootNodes[i]\n            scheduled[node] = nil\n            node.LastExecTime = 0\n            node.Thread = coroutine.create(execTask)\n            table.insert(runningNodes, node)\n         end\n   \n         local scheduler\n         scheduler = {\n            Resume = function(maxExecTime)\n   \n               -- orders the threads, executing the ones with the least execution time first this prevents long tasks \n               -- from taking up all the processing time\n               table.sort(runningNodes, function(nodeA, nodeB)\n                  return nodeA.LastExecTime < nodeB.LastExecTime\n               end)\n   \n               local totalTime = 0\n   \n               -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785\n               local i, len = 0, #runningNodes-1\n               while i <= len do\n                  i = i + 1\n                  local node = runningNodes[i]\n   \n                  if node.Thread ~= nil then\n                     local execTime = os.clock()\n                     node.LastExecTime = execTime\n      \n                     coroutine.resume(node.Thread, node, Time, world, onComplete)\n      \n                     totalTime = totalTime + (os.clock() - execTime)\n      \n                     if (totalTime > maxExecTime) then\n                        break\n                     end\n                  end\n               end\n   \n               -- remove completed\n               for i,node in ipairs(runningNodes) do\n                  if node.Thread == nil then                  \n                     local idx = table.find(runningNodes, node)\n                     if idx ~= nil then\n                        table.remove(runningNodes, idx)\n                     end\n                  end\n               end\n   \n               local hasMore = #runningNodes > 0\n      \n               if (not hasMore) then\n                  local idx = table.find(self._schedulers, scheduler)\n                  if idx ~= nil then\n                     table.remove(self._schedulers, idx)\n                  end\n               end\n   \n               return totalTime, hasMore\n            end\n         }\n   \n         table.insert(self._schedulers, scheduler)\n      end\n   end\n   \n   return SystemExecutor\n   \nend\n\n__F__[\"Timer\"] = function()\n   -- src/Timer.lua\n   \n   -- if execution is slow, perform a maximum of 4 simultaneous updates in order to keep the fixrate\n   local MAX_SKIP_FRAMES = 4\n   \n   local function loop(Time)\n   \n      local accumulator = 0.0\n      local lastStepTime = 0.0\n   \n      return function (newTime, stepName, beforeUpdateFn, updateFn)\n         local dtFixed = Time.DeltaFixed\n         local stepTime = newTime - lastStepTime\n         if stepTime > 0.25 then\n            stepTime = 0.25\n         end\n         lastStepTime = newTime\n   \n         Time.Now = newTime\n   \n         -- 1000/30/1000 = 0.03333333333333333\n         accumulator = accumulator + stepTime\n         \n         --[[\n            Adjusting the framerate, the world must run on the same frequency,\n            this ensures determinism in the execution of the scripts\n   \n            Each system in \"transform\" step is executed at a predetermined frequency (in Hz).\n   \n            Ex. If the game is running on the client at 30FPS but a system needs to be run at\n            120Hz or 240Hz, this logic will ensure that this frequency is reached\n   \n            @see https://gafferongames.com/post/fix_your_timestep/\n            @see https://gameprogrammingpatterns.com/game-loop.html\n            @see https://bell0bytes.eu/the-game-loop/\n         ]]\n         if stepName == \"process\" then\n            if accumulator >= dtFixed then       \n               Time.Interpolation = 1\n   \n               beforeUpdateFn(Time)\n               local nLoops = 0\n               while (accumulator >= dtFixed and nLoops < MAX_SKIP_FRAMES) do\n                  updateFn(Time)\n                  nLoops = nLoops + 1\n                  Time.Process = Time.Process + dtFixed\n                  accumulator = accumulator - dtFixed\n               end\n            end\n         else\n            Time.Interpolation = math.min(math.max(accumulator/dtFixed, 0), 1)\n            beforeUpdateFn(Time)\n            updateFn(Time)\n         end\n      end\n   end\n   \n   local Timer = {}\n   Timer.__index = Timer\n   \n   function Timer.New(frequency)\n      local Time = {\n         Now = 0,\n         -- The time at the beginning of this frame. The world receives the current time at the beginning\n         -- of each frame, with the value increasing per frame.\n         Frame = 0,         \n         Process = 0, -- The time the latest process step has started.\n         Delta = 0, -- The completion time in seconds since the last frame.\n         DeltaFixed = 0,\n         -- INTERPOLATION: The proportion of time since the previous transform relative to processDeltaTime\n         Interpolation = 0\n      }\n   \n      local timer = setmetatable({\n         -- Public, visible by systems\n         Time = Time,\n         Frequency = 0,\n         _update = loop(Time)\n      }, Timer)\n   \n      timer:SetFrequency(frequency)\n   \n      return timer\n   end\n   \n   --[[\n      Changes the frequency of execution of the \"process\" step\n   \n      @param frequency {number}\n   ]]\n   function Timer:SetFrequency(frequency)\n   \n      -- frequency: number,\n      -- The maximum times per second this system should be updated. Defaults 30\n      if frequency == nil then\n         frequency = 30\n      end\n   \n      local safeFrequency  = math.floor(math.abs(frequency)/2)*2\n      if safeFrequency < 2 then\n         safeFrequency = 2\n      end\n   \n      if frequency ~= safeFrequency then\n         frequency = safeFrequency\n      end\n   \n      self.Frequency = frequency\n      self.Time.DeltaFixed = 1000/frequency/1000\n   end\n   \n   function Timer:Update(now, step, beforeUpdate, update)\n      self._update(now, step, beforeUpdate, update)\n   end\n   \n   return Timer\n   \nend\n\n__F__[\"Utility\"] = function()\n   -- src/Utility.lua\n   --[[\n      Utility library.\n   ]]\n   local Utility = {}\n   \n   if table.unpack == nil then\n   \ttable.unpack = unpack\n   end\n   \n   if table.find == nil then\n      --[[\n         Within the given array-like table haystack, find the first occurrence of value needle, starting from index init \n         or the beginning if not provided. If the value is not found, nil is returned.\n   \n         A linear search algorithm is performed.\n      ]]\n      table.find = function(haystack, needle, init)\n         local len = #haystack\n         for i = init or 1, len, 1 do\n            if haystack[i] == needle then\n               return i\n            end\n         end\n   \n         return nil\n      end\n   end\n   \n   local function copyDeep(src)\n   \tlocal copy = {}\n      for k, v in pairs(src) do\n         if type(v) == \"table\" then\n            v = copyDeep(v)\n         end\n         copy[k] = v\n      end\n   \t\n   \treturn copy\n   end\n   Utility.copyDeep = copyDeep\n   \n   --[[\n      Faz o merge dos atributos src com o dest\n      Quando o um atributo do segundo é um \"table\", faz uma copia do valor\n   ]]\n   local function mergeDeep(dest, src)\n      for k,valueSrc in pairs(src) do\n         if (type(valueSrc) == \"table\") then\n            local valueDest = dest[k]\n            if (valueDest == nil or type(valueDest) ~= \"table\") then\n               dest[k] = copyDeep(valueSrc)\n            else\n               dest[k] = mergeDeep(valueDest, valueSrc)\n            end\n         else\n            dest[k] = valueSrc\n         end\n      end\n   \treturn dest\n   end\n   Utility.mergeDeep = mergeDeep\n   \n   return Utility\n   \nend\n\n__F__[\"World\"] = function()\n   -- src/World.lua\n   local Timer = __REQUIRE__(\"Timer\")\n   local Event = __REQUIRE__(\"Event\")\n   local Entity = __REQUIRE__(\"Entity\")\n   local Archetype = __REQUIRE__(\"Archetype\")\n   local SystemExecutor = __REQUIRE__(\"SystemExecutor\")\n   local EntityRepository = __REQUIRE__(\"EntityRepository\")\n   \n   local World = {}\n   World.__index = World\n   \n   --[[  \n      Create a new world instance\n   \n      @param systemClasses {SystemClass[]} (Optional) Array of system classes\n      @param frequency {number} (Optional) define the frequency that the `process` step will be executed. Default 30\n      @param disableAutoUpdate {bool} (Optional) when `~= false`, the world automatically registers in the `LoopManager`, \n      receiving the `World:Update()` method from it. Default false\n   ]]\n   function World.New(systemClasses, frequency, disableAutoUpdate)   \n      local world = setmetatable({\n         --[[\n            Global System Version (GSV).\n   \n            Before executing the Update method of each system, the world version is incremented, so at this point, the \n            world version will always be higher than the running system version.\n   \n            Whenever an entity archetype is changed (received or lost component) the entity's version is updated to the \n            current version of the world.\n   \n            After executing the System Update method, the version of this system is updated to the current world version.\n   \n            This mechanism allows a system to know if an entity has been modified after the last execution of this same \n            system, as the entity's version is superior to the version of the last system execution. Thus, a system can \n            contain logic if it only operates on \"dirty\" entities, which have undergone changes. The code for this \n            validation on a system is `local isDirty = entity.version > self.version`\n         ]]\n         version = 0,\n         --[[\n            Allows you to define the maximum time that the JobSystem can operate in each frame.\n   \n            The default value is 0.011666666666666665 = ((1000/60/1000)*0.7)\n   \n            A game that runs at 30fps has 0.0333 seconds to do all the processing for each frame, including rendering\n               - 30FPS = ((1000/30/1000)*0.7)/3 = 0.007777777777777777\n   \n            A game that runs at 60fps has 0.0166 seconds to do all the processing for each frame, including rendering\n               - 60FPS = ((1000/60/1000)*0.7)/3 = 0.0038888888888888883\n         ]]\n         maxTasksExecTime = 0.013333333333333334,\n         _dirty = false, -- True when create/remove entity, add/remove entity component (change archetype)\n         _timer = Timer.New(frequency),\n         _systems = {}, -- systems in this world\n         _repository = EntityRepository.New(),\n         _entitiesCreated = {}, -- created during the execution of the Update\n         _entitiesRemoved = {}, -- removed during execution (only removed after the last execution step)\n         _entitiesUpdated = {}, -- changed during execution (received or lost components, therefore, changed the archetype)\n         _onQueryMatch = Event.New(),\n         _onChangeArchetypeEvent = Event.New(),\n      }, World)\n   \n      -- System execution plan\n      world._executor = SystemExecutor.New(world)\n   \n      world._onChangeArchetypeEvent:Connect(function(entity, archetypeOld, archetypeNew)      \n         world:_OnChangeArchetype(entity, archetypeOld, archetypeNew)\n      end)\n   \n      -- add systems\n      if (systemClasses ~= nil) then\n         for _, systemClass in ipairs(systemClasses) do\n            world:AddSystem(systemClass)\n         end\n      end\n   \n      if (not disableAutoUpdate and World.LoopManager) then\n         world._loopCancel = World.LoopManager.Register(world)\n      end\n   \n      return world\n   end\n   \n   --[[\n      Changes the frequency of execution of the \"process\" step\n   \n      @param frequency {number}\n   ]]\n   function World:SetFrequency(frequency) \n      frequency = self._timer:SetFrequency(frequency) \n   end\n   \n   --[[\n      Get the frequency of execution of the \"process\" step\n   \n      @return number\n   ]]\n   function World:GetFrequency(frequency) \n      return self._timer.Frequency\n   end\n   \n   --[[\n      Add a new system to the world.\n   \n      Only one instance per type is accepted. If there is already another instance of this system in the world, any new \n      invocation of this method will be ignored.\n   \n      @param systemClass {SystemClass} The system to be added in the world\n      @param config {table} (Optional) System instance configuration\n   ]]\n   function World:AddSystem(systemClass, config)\n      if systemClass then\n         if config == nil then\n            config = {}\n         end\n        \n         if self._systems[systemClass] == nil then\n            self._systems[systemClass] = systemClass.New(self, config)\n   \n            self._executor:SetSystems(self._systems)\n         end\n      end\n   end\n   \n   --[[\n      Create a new entity.\n   \n      The entity is created in DEAD state (entity.isAlive == false) and will only be visible for queries after the \n      cleaning step (OnRemove, OnEnter, OnExit) of the current step\n   \n      @param args {Component[]}\n      @return Entity\n   ]]\n   function World:Entity(...)\n      local entity = Entity.New(self._onChangeArchetypeEvent, {...})\n   \n      self._dirty = true\n      self._entitiesCreated[entity] = true\n      \n      entity.version = self.version -- update entity version using current Global System Version (GSV)\n      entity.isAlive = false\n   \n      return entity\n   end\n   \n   --[[\n      Performs immediate removal of an entity.\n   \n      If the entity was created in this step and the cleanup process has not happened yet (therefore the entity is \n      inactive, entity.isAlive == false), the `OnRemove` event will never be fired.\n   \n      If the entity is alive (entity.isAlive == true), even though it is removed immediately, the `OnRemove` event will be \n      fired at the end of the current step.\n   \n      @param entity {Entity}\n   ]]\n   function World:Remove(entity)\n   \n      if self._entitiesRemoved[entity] == true then\n         return\n      end\n   \n      if self._entitiesCreated[entity] == true then\n         self._entitiesCreated[entity] = nil\n      else\n         self._repository:Remove(entity)\n         self._entitiesRemoved[entity] = true\n   \n         if self._entitiesUpdated[entity] == nil then\n            self._entitiesUpdated[entity] = entity.archetype\n         end\n      end\n   \n      self._dirty = true\n      entity.isAlive = false\n   end\n   \n   --[[\n      Run a query in this world\n   \n      @param query {Query|QueryBuilder}\n      @return QueryResult\n   ]]\n   function World:Exec(query)\n      if (query.isQueryBuilder) then\n         query = query.Build()\n      end\n   \n      local result, match = self._repository:Query(query)\n   \n      if match then\n         self._onQueryMatch:Fire(query)\n      end\n   \n      return result\n   end\n   \n   --[[\n      Quick check to find out if a query is applicable.\n   \n      @param query {Query|QueryBuilder}\n      @return QueryResult\n   ]]\n   function World:FastCheck(query)\n      if (query.isQueryBuilder) then\n         query = query.Build()\n      end\n   \n      return self._repository:FastCheck(query)\n   end\n   \n   --[[\n      Add a callback that is reported whenever a query has been successfully executed. Used internally \n      to quickly find out if a QuerySystem will run.\n   ]]\n   function World:OnQueryMatch(callback)\n      return self._onQueryMatch:Connect(callback)\n   end\n   \n   --[[\n      Perform world update.\n   \n      When registered, LoopManager will invoke World Update for each step in the sequence.\n   \n      - process At the beginning of each frame\n      - transform After the game engine's physics engine runs\n      - render Before rendering the current frame\n   \n      @param step {\"process\"|\"transform\"|\"render\"}\n      @param now {number} Usually os.clock()\n   ]]\n   function World:Update(step, now)\n   \n      \n      self._timer:Update(\n         now, step,\n         function(Time)\n            --[[\n               JobSystem\n               .------------------.\n               |     pipeline     |\n               |------------------| \n               | s:ShouldUpdate() |\n               | s:Update()       |\n               '------------------'\n            ]]\n            if step == \"process\" then\n               self._executor:ScheduleTasks(Time)\n            end\n            -- run suspended Tasks\n            self._executor:ExecTasks(self.maxTasksExecTime)\n         end,\n         function(Time)\n            --[[\n               .------------------.\n               |     pipeline     |\n               |------------------| \n               | s:ShouldUpdate() |\n               | s:Update()       |\n               |                  |\n               |-- CLEAR ---------|\n               | s:OnRemove()     |\n               | s:OnExit()       |\n               | s:OnEnter()      |\n               '------------------'\n            ]]\n            if step == \"process\" then\n               self._executor:ExecProcess(Time)\n            elseif step == \"transform\" then\n               self._executor:ExecTransform(Time)\n            else\n               self._executor:ExecRender(Time)\n            end\n   \n            -- cleans up after running scripts\n            while self._dirty do\n               self._dirty = false\n            \n               -- 1: remove entities\n               local entitiesRemoved = {}\n               for entity,_ in pairs(self._entitiesRemoved) do\n                  entitiesRemoved[entity] = self._entitiesUpdated[entity]\n                  self._entitiesUpdated[entity] = nil\n               end\n               self._entitiesRemoved = {}\n               self._executor:ExecOnRemove(Time, entitiesRemoved)\n               entitiesRemoved = nil\n            \n               local changed = {}\n               local hasChange = false\n            \n               -- 2: Update entities in memory\n               for entity, archetypeOld in pairs(self._entitiesUpdated) do\n                  if (archetypeOld ~= entity.archetype) then\n                     hasChange = true\n                     changed[entity] = archetypeOld\n                  end\n               end\n               self._entitiesUpdated = {}\n            \n               -- 3: Add new entities\n               for entity, _ in pairs(self._entitiesCreated) do\n                  hasChange = true\n                  changed[entity] = Archetype.EMPTY\n            \n                  entity.isAlive = true\n                  self._repository:Insert(entity) \n               end\n               self._entitiesCreated = {}\n            \n               if hasChange then\n                  self._executor:ExecOnExitEnter(Time, changed)\n                  changed = nil\n               end\n            end\n         end\n      )\n   end\n   \n   --[[\n      Destroy this instance, removing all entities, systems and events\n   ]]\n   function World:Destroy()\n   \n      if self._loopCancel then\n         self._loopCancel()\n         self._loopCancel = nil\n      end\n   \n      if self._onChangeArchetypeEvent then\n         self._onChangeArchetypeEvent:Destroy()\n         self._onChangeArchetypeEvent = nil\n      end\n   \n      self._repository = nil\n   \n      if self._systems then\n         for _,system in pairs(self._systems) do\n            system:Destroy()\n         end\n         self._systems = nil\n      end\n      \n      self._timer = nil\n      self._ExecPlan = nil\n      self._entitiesCreated = nil\n      self._entitiesUpdated = nil\n      self._entitiesRemoved = nil\n   \n      setmetatable(self, nil)\n   end\n   \n   function World:_OnChangeArchetype(entity, archetypeOld, archetypeNew)\n      if entity.isAlive then\n   \n         if self._entitiesUpdated[entity] == nil then\n            self._dirty = true\n            self._entitiesUpdated[entity] = archetypeOld\n         end\n      \n         self._repository:Update(entity)\n   \n         -- update entity version using current Global System Version (GSV)\n         entity.version = self.version\n      end\n   end\n   \n   return World\n   \nend\n\nreturn __REQUIRE__(\"ECS\")"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Alex Rodin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n   <a href=\"https://nidorx.github.io/ecs-lua\">\n      <img \n         src=\"docs/assets/logo.svg\" \n         alt=\"https://nidorx.github.io/ecs-lua\" \n      />\n   </a>\n</p>\n\n<p align=\"center\">\n   <a href=\"https://app.travis-ci.com/nidorx/ecs-lua\">\n      <img src=\"https://app.travis-ci.com/nidorx/ecs-lua.svg?branch=master\" alt=\"Build Status\" />\n   </a>\n</p>\n\n<p align=\"center\">\n  <strong><a href=\"https://nidorx.github.io/ecs-lua#/\">Read the Documentation</a></strong>\n</p>\n\n# What is it?\n\n<strong>ECS Lua</strong> is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n<div align=\"center\">\n\n![](docs/assets/diagram-1.png)\n\n</div>\n\nThe basic idea of this pattern is to stop defining entities using a \n[hierarchy](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) of classes and start doing use of \n[composition](https://en.wikipedia.org/wiki/Object_composition) in a Data Oriented Programming paradigm.\n([More information on Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)).\nProgramming with an ECS can result in code that is more efficient and easier to extend over time.\n\n\n# How does it work?\n\n<div align=\"center\">\n\n![ECS Lua pipeline](docs/assets/pipeline.png)\n\n</div>\n\n\n# Talk is cheap. Show me the code!\n\n```lua\nlocal World, System, Query, Component = ECS.World, ECS.System, ECS.Query, ECS.Component\n\nlocal Health = Component(100)\nlocal Position = Component({ x = 0, y = 0})\n\nlocal isInAcid = Query.Filter(function()\n   return true  -- it's wet season\nend)\n\nlocal InAcidSystem = System(\"process\", Query.All( Health, Position, isInAcid() ))\n\nfunction InAcidSystem:Update()\n   for i, entity in self:Result():Iterator() do\n      local health = entity[Health]\n      health.value = health.value - 0.01\n   end\nend\n\nlocal world = World({ InAcidSystem })\n\nworld:Entity(Position({ x = 5.0 }), Health())\n```\n\n# Features\n\n**ECS Lua** has no external dependencies and is compatible and tested with [Lua 5.1], [Lua 5.2], [Lua 5.3], [Lua 5.4],\n[LuaJit] and [Roblox Luau](https://luau-lang.org/)\n\n- **Game engine agnostic**: It can be used in any engine that has the Lua scripting language.\n- **Ergonomic**: Focused on providing a simple yet efficient API\n- **FSM**: Finite State Machines in an easy and intuitive way\n- **JobSystem**: To running systems in parallel (through [coroutines])\n- **Reactive**: Systems can be informed when an entity changes\n- **Predictable**:\n   - The systems will work in the order they were registered or based on the priority set when registering them.\n   - Reactive events do not generate a random callback when issued, they are executed at a predefined step.\n\n# Goal\n\nTo be a lightweight, simple, ergonomic and high-performance ECS library that can be easily extended. The **ECS Lua**\ndoes not strictly follow _\"pure ECS design\"_.\n\n# Usage\n\nRead our [Full Documentation][docs] to learn how to use **ECS Lua**.\n\n# Get involved\nAll kinds of contributions are welcome!\n\n🐛 **Found a bug?**  \nLet me know by [creating an issue][new-issue].\n\n❓ **Have a question?**  \n[Roblox DevForum][discussions] is a good place to start.\n\n⚙️ **Interested in fixing a [bug][bugs] or adding a [feature][features]?**  \nCheck out the [contributing guidelines](CONTRIBUTING.md).\n\n📖 **Can we improve [our documentation][docs]?**  \nPull requests even for small changes can be helpful. Each page in the docs can be edited by clicking the \n\"Edit on GitHub\" link at the bottom right.\n\n[docs]: https://nidorx.github.io/ecs-lua\n[bugs]: https://github.com/nidorx/ecs-lua/issues?q=is%3Aissue+is%3Aopen+label%3Abug\n[features]: https://github.com/nidorx/ecs-lua/issues?q=is%3Aissue+is%3Aopen+label%3Afeature\n[new-issue]: https://github.com/nidorx/ecs-lua/issues/new/choose\n[discussions]: https://devforum.roblox.com/t/841175\n[Lua 5.1]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.2]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.3]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.4]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[LuaJit]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[coroutines]:http://www.lua.org/pil/9.1.html\n\n# License\n\nThis code is distributed under the terms and conditions of the [MIT license](LICENSE).\n\n\n\n"
  },
  {
    "path": "build.lua",
    "content": "\n\nlocal OUTPUT_CONCAT = \"ECS_concat\"\n\nlocal OUTPUT_MINIFIED = \"ECS\"\n\nlocal SRC_FILES = {\n   \"Archetype\",\n   \"Component\",\n   \"ComponentFSM\",\n   \"ECS\",\n   \"Entity\",\n   \"EntityRepository\",\n   \"Event\",\n   \"Query\",\n   \"QueryResult\",\n   \"RobloxLoopManager\",\n   \"System\",\n   \"SystemExecutor\",\n   \"Timer\",\n   \"Utility\",\n   \"World\"\n}\n\nlocal HEADER = [[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tThis is a minified version of ECS Lua, to see the full source code visit\n\thttps://github.com/nidorx/ecs-lua\n\n   Discussions about this script are at https://devforum.roblox.com/t/841175\n\n\t------------------------------------------------------------------------------\n\n\tMIT License\n\n\tCopyright (c) 2021 Alex Rodin\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.]]\n\nHEADER = \"--[[\\n\"..HEADER..\"\\n]]\\n\"\n\npackage.path = package.path .. \";modules/?.lua\"\n\nlocal function concat()\n   local concatContent = {\n      HEADER,\n      \"local __M__, __F__ = {}, {}\",\n      \"local function __REQUIRE__(m)\",\n      \"   if (not __M__[m]) then\",\n      \"      __M__[m] = { r = __F__[m]() }\",\n      \"   end\",\n      \"   return __M__[m].r\",\n      \"end\",   \n      \"\",   \n   }\n   \n   for i,name in ipairs(SRC_FILES) do\n      local sourceFile = io.open(\"./src/\"..name..\".lua\", \"r\")\n      if not sourceFile then\n         error(\"Could not open the input file `\" .. OUTPUT_MINIFIED .. \"`\", 0)\n      end\n   \n      local content = sourceFile:read( \"*a\" )\n   \n      for _,oname in ipairs(SRC_FILES) do\n         content = content:gsub('require[(][\"\\']'..oname..'[\"\\'][)]', '__REQUIRE__(\"'..oname..'\")')\n      end\n   \n      table.insert(concatContent, table.concat({\n         '__F__[\"'..name..'\"] = function()',\n         (\"   -- src/\"..name..\".lua\\n\"..content):gsub(\"\\n\", \"\\n   \"),\n         \"end\",\n         \"\",\n      }, \"\\n\"))\n      sourceFile:close()\n   end\n   \n   table.insert(concatContent, 'return __REQUIRE__(\"ECS\")')\n   \n   -- write ECS_concat.lua\n   local fileConcat = io.open(OUTPUT_CONCAT..\".lua\", \"w\" )\n   fileConcat:write( table.concat(concatContent, \"\\n\"))\n   fileConcat:close()  \n   \n   -- teste import\n   local ecsConcat = require(OUTPUT_CONCAT)\n   _G.ECS = nil\nend\n\nlocal function minify()\n   local min = require('minify')\n\n   local sourceFile = io.open(OUTPUT_CONCAT..\".lua\", 'r')\n   if not sourceFile then\n      error(\"Could not open the input file `\" .. OUTPUT_CONCAT..\".lua\" .. \"`\", 0)\n   end\n\n   local data = sourceFile:read('*all')\n   local ast = min.CreateLuaParser(data)\n   local global_scope, root_scope = min.AddVariableInfo(ast)\n\n   min.MinifyVariables(global_scope, root_scope)\n   min.StripAst(ast)\n   local minifiedContent = min.AstToString(ast)\n\n   -- write ECS.lua\n   local fileMinified = io.open(OUTPUT_MINIFIED..\".lua\", \"w\" )\n   fileMinified:write(HEADER .. minifiedContent)\n   fileMinified:close()\n\n   -- teste import\n   local ecsMinified = require(OUTPUT_MINIFIED)\n   _G.ECS = nil\nend\n\nconcat()\nminify()\n"
  },
  {
    "path": "docs/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/README.md",
    "content": "# What is it?\n\n**ECS Lua** is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n![](assets/diagram-1.png)\n\nThe basic idea of this pattern is to stop defining entities using a \n[hierarchy](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) of classes and start doing use of \n[composition](https://en.wikipedia.org/wiki/Object_composition) in a Data Oriented Programming paradigm.\n([More information on Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)).\nProgramming with an ECS can result in code that is more efficient and easier to extend over time.\n\n\n# How does it work?\n\n![ECS Lua pipeline](assets/pipeline.png)\n\n\n# Talk is cheap. Show me the code!\n\n```lua\nlocal World, System, Query, Component = ECS.World, ECS.System, ECS.Query, ECS.Component\n\nlocal Health = Component(100)\nlocal Position = Component({ x = 0, y = 0})\n\nlocal isInAcid = Query.Filter(function()\n   return true  -- it's wet season\nend)\n\nlocal InAcidSystem = System(\"process\", Query.All( Health, Position, isInAcid() ))\n\nfunction InAcidSystem:Update()\n   for i, entity in self:Result():Iterator() do\n      local health = entity[Health]\n      health.value = health.value - 0.01\n   end\nend\n\nlocal world = World({ InAcidSystem })\n\nworld.Entity(Position({ x: 5.0 }), Health())\n```\n\n# Features\n\n**ECS Lua** has no external dependencies and is compatible and tested with [Lua 5.1], [Lua 5.2], [Lua 5.3], [Lua 5.4],\n[LuaJit] and [Roblox Luau](https://luau-lang.org/)\n\n- **Game engine agnostic**: It can be used in any engine that has the Lua scripting language.\n- **Ergonomic**: Focused on providing a simple yet efficient API\n- **FSM**: Finite State Machines in an easy and intuitive way\n- **JobSystem**: To running systems in parallel (through [coroutines])\n- **Reactive**: Systems can be informed when an entity changes\n- **Predictable**:\n   - The systems will work in the order they were registered or based on the priority set when registering them.\n   - Reactive events do not generate a random callback when issued, they are executed at a predefined step.\n\n# Goal\n\nTo be a lightweight, simple, ergonomic and high-performance ECS library that can be easily extended. The **ECS Lua**\ndoes not strictly follow _\"pure ECS design\"_.\n\n# Next steps\n\nYou can browse or search for specific subjects in the side menu. Here are some relevant links:\n\n<br>\n<br>\n\n<div class=\"home-row clearfix\" style=\"text-align:center\">\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Installation](assets/icon-download.png \":no-zoom\")](/getting-started?id=installation)\n\n   </div><div class=\"panel-heading\">\n\n   [Installation](/getting-started?id=installation)\n\n   </div></div></div>\n\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![General Concepts](assets/icon-parts.png \":no-zoom\")](/getting-started?id=general-concepts)\n\n   </div><div class=\"panel-heading\">\n\n   [General Concepts](/getting-started?id=general-concepts)\n\n   </div></div></div>\n\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Architecture](assets/icon-advanced.png \":no-zoom\")](/architecture)\n\n   </div><div class=\"panel-heading\">\n\n   [Architecture](/architecture)\n\n   </div></div></div>\n\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Tutorials](assets/icon-tutorial.png \":no-zoom\")](/tutorial)\n\n   </div><div class=\"panel-heading\">\n\n   [Tutorials](/tutorial)\n\n   </div></div></div>\n</div>\n\n[Lua 5.1]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.2]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.3]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.4]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[LuaJit]:https://app.travis-ci.com/github/nidorx/ecs-lua\n"
  },
  {
    "path": "docs/_coverpage.md",
    "content": "<div class=\"logo-container\">\n  <div class=\"heats\">\n    <div class=\"h r1 c1\"></div>\n    <div class=\"h r1 c2\"></div>\n    <div class=\"h r1 c3\"></div>\n    <div class=\"h r1 c4\"></div>\n    <div class=\"h r1 c5\"></div>\n    <div class=\"h r2 c1\"></div>\n    <div class=\"h r2 c2\"></div>\n    <div class=\"h r2 c3\"></div>\n    <div class=\"h r2 c4\"></div>\n    <div class=\"h r2 c5\"></div>\n    <div class=\"h r3 c1\"></div>\n    <div class=\"h r3 c2\"></div>\n    <div class=\"h r3 c3\"></div>\n    <div class=\"h r3 c4\"></div>\n    <div class=\"h r3 c5\"></div>\n    <div class=\"h r4 c1\"></div>\n    <div class=\"h r4 c2\"></div>\n    <div class=\"h r4 c3\"></div>\n    <div class=\"h r4 c4\"></div>\n    <div class=\"h r4 c5\"></div>\n    <div class=\"h r5 c1\"></div>\n    <div class=\"h r5 c2\"></div>\n    <div class=\"h r5 c3\"></div>\n    <div class=\"h r5 c4\"></div>\n    <div class=\"h r5 c5\"></div>\n    \n   <svg \n      class=\"ghost\"\n      width=\"50mm\" \n      height=\"50mm\" \n      version=\"1.1\" \n      viewBox=\"0 0 50 50\">\n      <path d=\"m4.8916 45.894-2e-6 -2.9104h-2.9104l-1e-6 -20.902h2.9104v-8.9958h3.175v-3.1992h3.4396v-2.8862l6.8792-9.1e-6v-2.9104h13.229v2.9104h6.4824v2.8862h3.7041v3.1992h-6.7469v3.199l-3.4396 1e-3 -2e-6 8.4414h3.4396l2e-6 2.9104 6.7469-1.7e-4v-5.5559l6.2178 2.3e-4v20.902h-3.175v2.9265l-6.7469-0.016v-2.9104h-3.0428v-3.1749h-3.4396v3.1749l-3.4396 0.0256v2.8848h-6.6146v-2.9104h-3.175v-3.1749h-3.175v3.1749h-3.7042c0.0062 0.97335 0 1.9371 0 2.9104-2.2049-2e-4 -4.4097 0-6.6146 0zm16.669-18.256-2e-6 -2.9104h3.4397v-8.4426h-3.4397v-3.199h-6.35v3.199h-3.7042l2e-6 8.4425 3.7042 1.5e-4v2.9103z\" fill=\"#fb860c\" class=\"body\"></path>\n      <g class=\"eye eye--left\">\n         <g class=\"pupil\">\n            <g class=\"inner\">\n               <path class=\"pupil-color\" d=\"m18.385 24.728v-5.5563h6.6147v5.5563z\" fill=\"#020202\"></path>\n               <path class=\"eyelid\" d=\"m18.385 24.728v-5.5563h6.6147v5.5563z\" fill=\"#edf4f7\"></path>\n            </g>\n         </g>\n      </g>\n      <g class=\"eye eye--right\">\n         <g class=\"pupil\">\n            <g class=\"inner\">\n               <path class=\"pupil-color\" d=\"m38.494 24.728v-5.5563h6.6147v5.5563z\" fill=\"#020202\"></path>\n               <path class=\"eyelid\"  d=\"m38.494 24.728v-5.5563h6.6147v5.5563z\" fill=\"#edf4f7\"></path>\n            </g>\n         </g>\n      </g>\n   </svg>\n  </div>\n</div>\n\n# ECS Lua\n[![Build Status](https://app.travis-ci.com/nidorx/ecs-lua.svg?branch=master)](https://app.travis-ci.com/nidorx/ecs-lua)\n\n[GitHub](https://github.com/nidorx/ecs-lua)\n[Get Started](/?id=what-is-it)\n\n<!-- background color -->\n![color](#ffffff)\n"
  },
  {
    "path": "docs/_navbar.md",
    "content": "- [Home](/) &nbsp; &nbsp;\n- Language <span class=\"arrow\">&#x25BE;</span>\n  - [English](/)\n  - [Português do Brasil](/pt-br/)\n"
  },
  {
    "path": "docs/_sidebar.md",
    "content": "- [Home](/)\n- [Installation](/getting-started?id=installation)\n- [General Concepts](/getting-started?id=general-concepts)\n   - [Component](/getting-started?id=component)\n   - [Systems and Queries](/getting-started?id=systems-and-queries)\n   - [World](/getting-started?id=world)\n   - [Entity](/getting-started?id=entity)\n- [Example](/getting-started?id=putting-everything-together)\n- [Architecture](/architecture)\n  - [Component](/architecture?id=component)\n    - [Qualifiers](/architecture?id=qualifiers)\n    - [FSM - Finite State Machines](/architecture?id=fsm-finite-state-machines)\n  - [Entity](/architecture?id=entity)\n  - [Query](/architecture?id=query)  \n  - [Sistema](/architecture?id=system)\n    - [Tasks](/architecture?id=task)\n  - [World](/architecture?id=world)\n- [Tutorials](/tutorial)\n  - [Shoot](/tutorial-shoot)\n  - [Pacman](/tutorial-pacman)\n  - [Boids](/tutorial-boids)\n- [API](/api)\n  - [ECS](/api?id=ecs)\n  - [Archetype](/api?id=archetype)\n  - [Component](/api?id=component)\n  - [Entity](/api?id=entity)\n  - [LoopManager](/api?id=loopmanager)\n  - [Query](/api?id=query)\n  - [QueryBuilder](/api?id=querybuilder)\n  - [QueryResult](/api?id=queryresult)\n  - [System](/api?id=system)\n  - [Time](/api?id=time)\n  - [World](/api?id=world)\n\n"
  },
  {
    "path": "docs/api.md",
    "content": "# API\n<div class=\"api-docs\">\n\n\n# ECS\n- `ECS.Query`\n   - _@type_ `QueryClass`\n- `ECS.Archetype`\n   - _@type_ `ArchetypeClass`\n- `ECS.World(systemClasses, frequency, disableAutoUpdate)`\n   - Create a new world instance\n   - _@param_ `systemClasses` `SystemClass[]` _optional_ Array of system classes\n   - _@param_ `frequency` `number` _optional_ Define the frequency that the `process` step will be executed. Default 30\n   - _@param_ `disableAutoUpdate` `bool` _optional_ When `~= false`, the world automatically registers in the `LoopManager`, \n   receiving the `World:Update()` method from it. Default false\n   - _@return_ `World`\n- `ECS.System(step, order, query, updateFn)`\n   - Create new System Class\n   - _@param_ `step` `process|transform|render|task`\n   - _@param_ `order` `number` _optional_ Allows you to set an execution order (for systems that are not `task`). Default 50\n   - _@param_ `query` `Query|QueryBuilder` _optional_ Filters the entities that will be processed by this system\n   - _@param_ `updateFn` `Function(self, Time)` _optional_ A shortcut for creating systems that only have the Update method\n   - _@return_ `SystemClass`\n- `ECS.Component(template)`\n   - Register a new ComponentClass\n   - _@param_ `template` `table|function(table):table|any` \n      - When `table`, this template will be used for creating component instances\n      - When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the \n      component is passed to template function\n      - If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format \n      `{ value = template }`.\n   - _@return_ `ComponentClass`\n- `ECS.SetLoopManager(manager)`\n   - Defines the LoopManager that will be used by the worlds to receive the automatic update\n   - _@param_ `manager` `LoopManager`\n\n# Archetype\n\nAn Archetype is a unique combination of component types. The EntityRepository uses the archetype to group all \nentities that have the same sets of components.\n\nAn entity can change archetype fluidly over its lifespan. For example, when you add or remove components, the archetype \nof the affected entity changes.\n\nAn archetype object is not a container; rather it is an identifier to each unique combination of component types that \nan application has created at run time, either directly or implicitly.\n\nYou can create archetypes directly using `ECS.Archetype.Of(Components[])`. You also implicitly create archetypes \nwhenever you add or remove a component from an entity. An Archetype object is an immutable singleton; creating an \narchetype with the same set of components, either directly or implicitly, results in the same archetype.\n\nThe ECS framework uses archetypes to group entities that have the same structure together. The ECS framework stores \ncomponent data in blocks of memory called chunks. A given chunk stores only entities having the same archetype. You can \nget the Archetype object for a chunk from its Archetype property.\n\nUse `ECS.Archetype.Of(Components[])` to get a Archetype reference.\n\n- `Archetype.EMPTY`\n   - Generic archetype, for entities that do not have components\n   - _@type_ `Archetype`\n- `Archetype.Of(componentClasses)`\n   - Gets the reference to an archetype from the informed components\n   - _@param_ `componentClasses` `ComponentClass[]` Component that define this archetype\n   - _@return_ `Archetype`\n- `Archetype.Version()`\n   - Get the version of archetype definitions\n   - _@return_ `number`\n- `Archetype:Has(componentClass)`\n   - Checks whether this archetype has the informed component\n   - _@param_ `componentClass` `ComponentClass`\n   - _@return_ `bool`\n- `Archetype:With(componentClass)`\n   - Gets the reference to an archetype that has the current components `+` the informed component\n   - _@param_ `componentClass` `ComponentClass`\n   - _@return_ `Archetype`\n- `Archetype:WithAll(componentClasses)`\n   - Gets the reference to an archetype that has the current components `+` the informed components\n   - _@param_ `componentClass` `ComponentClass[]`\n   - _@return_ `Archetype`\n- `Archetype:Without(componentClass)`\n   - Gets the reference to an archetype that has the current components `-` the informed component\n   - _@param_ `componentClass` `ComponentClass`\n   - _@return_ `Archetype`\n- `Archetype:WithoutAll(componentClasses)`\n   - Gets the reference to an archetype that has the current components `-` the informed components\n   - _@param_ `componentClass` `ComponentClass[]`\n   - _@return_ `Archetype`\n\n# Component\n- `ComponentClass.Id`\n   - Identifier of this component\n   - _@type_ `number`\n- `ComponentClass.IsCType`\n   - Indicates that this class is a Component\n   - _@type_ `true`\n- `ComponentClass.SuperClass`\n   - Used internally for Qualifiers, it indicates the base class of this Component (or primary component).\n   - _@type_ `ComponentClass`\n- `ComponentClass.HasQualifier`\n   - Indicates that this Component has qualifiers\n   - _@type_ `bool`\n- `ComponentClass.IsQualifier`\n   - Indicates that this specific class is a Component qualifier.\n   - _@type_ `bool`\n- `ComponentClass.IsFSM`\n   - Indicates that this Component is a [FSM - Finite State Machine][fsm]\n   - _@type_ `bool`\n- `ComponentClass.States`\n   - When set, this Component becomes a [FSM - Finite State Machine][fsm]\n      ```lua\n      local Movement = ECS.Component({ speed = 0 })\n      Movement.States = {\n         Standing = {\"Walking\"},\n         Walking  = \"*\",\n         Running  = {\"Walking\"}\n      }\n      ```\n   - _@type_ `table` _optional_\n- `ComponentClass.StateInitial`\n   - When the component is [FSM][fsm], it allows defining the initial state for new instances.\n   - _@type_ `string` _optional_\n- `ComponentClass.Case`\n   - When the component is [FSM][fsm], it allows the component to handle transactions between states.\n      ```lua\n      Movement.Case = {\n         Standing = function(self, previous)\n            self.speed = 0\n         end,\n         Walking = function(self, previous)\n            self.speed = 5\n         end,\n         Running = function(self, previous)\n            self.speed = 10\n         end\n      }\n      ```\n   - _@type_ `table` _optional_\n- `ComponentClass.Qualifier(qualifier)`\n   - Gets a qualifier for this type of component. If the qualifier does not exist, a new class will be created, \n   otherwise it brings the already registered class qualifier reference with the same name.\n   - _@param_ `qualifier` `string|ComponentClass`\n   - _@return_ `ComponentClass`\n- `ComponentClass.Qualifiers(...)`\n   - Get all qualified class\n   - _@param_ `...` `string|ComponentClass` _optional_ Allows to filter the specific qualifiers\n   - _@return_ `ComponentClass[]`\n- `ComponentClass(value)` | `ComponentClass.New(value)`\n   - Builder, instantiate a new component of this type\n   - _@param_ `value` `any` _optional_  If the value is not a table, it will be converted to the format `{ value = value}`\n   - _@return_ `Component`\n- `ComponentClass:GetType()`\n   - Get this component's class\n   - _@return_ `ComponentClass`\n- `ComponentClass:Is(componentClass)`\n   - Check if this component is of the type informed\n   - _@param_ `componentClass` `ComponentClass|ComponentSuperClass`\n   - _@return_ `bool`\n- `ComponentClass:Primary()`\n   - Get the instance for the primary qualifier of this class\n   - _@return_ `Component|nil`\n- `ComponentClass:Qualified(qualifier)`\n   - Get the instance for the given qualifier of this class\n   - _@param_ `qualifier` `string|ComponentClass`\n   - _@return_ `Component|nil`\n- `ComponentClass:QualifiedAll()`\n   - Get all instances for all qualifiers of that class\n   - _@return_ `Component[]`\n- `ComponentClass:Merge(other)`\n   - Merges data from the other component into the current component. **IMPORTANT!** This method should not be invoked, \n   it is used by the entity to ensure correct retrieval of a component's qualifiers.\n   - _@param_ `other` `Component`\n- `ComponentClass:Detach()`\n   - Unlink this component with the other qualifiers. **IMPORTANT!** This method should not be invoked, it is used by \n   the entity to ensure correct retrieval of a component's qualifiers.\n- `ComponentClass.In(...)`\n   - When the component is [FSM][fsm], creates a clause used to filter repository entities in a Query or QueryResult. \n      ```lua\n      ECS.Query.All(Movement.In(\"Walking\", \"Running\"))\n      ```\n   - _@param_ `...` `string[]`\n   - _@return_ `Clause`\n- `ComponentClass:SetState(newState)`\n   - When the component is [FSM][fsm], defines the current state\n   - _@param_ `newState` `string`\n- `ComponentClass:GetState()`\n   - When the component is [FSM][fsm], get the current state\n   - _@return_ `string`\n- `ComponentClass:GetPrevState()`\n   - When the component is [FSM][fsm], get the previous state\n   - _@return_ `string|nil`\n- `ComponentClass:GetStateTime()`\n   - When the component is [FSM][fsm], gets the time it changed to the current state. Whenever the state is changed, the \n   instant is persisted internally using `os.clock()`\n   - _@return_ `number`\n\n# Entity\n- `Entity.id`\n   - Identifier of this entity\n   - _@type_ `number`\n- `Entity.isAlive`\n   - The entity is created in _DEAD_ state (`entity.isAlive == false`) and will only be visible for queries after the \n   cleaning step _(`OnRemove`,`OnEnter`,`OnExit`)_ by the world\n   - _@type_ `bool`\n- `Entity.archetype`\n   - The entity archetype\n   - _@type_ `Archetype`\n- `Entity.New(onChange, components)`\n   - Creates an entity having components of the specified types.\n   - _@param_ `onChange` `Event`\n   - _@param_ `components` `Component[]` _optional_\n   - _@return_ `Entity`\n- `Entity:Get(componentClass)` | `entity[componentClass]`\n   - Gets an entity component\n   - _@param_ `componentClass` `ComponentClass` \n   - _@return_ `Component`\n- `Entity:Get(...)`\n   - Get multiple entity components at once\n      ```lua\n      local comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n      ```\n   - _@param_ `..` `ComponentClass[]` \n   - _@return_ `Component ...`\n- `Entity:Set(componentClass, value)` | `entity[componentClass] = value`\n   - Sets the value of a component\n   - _@param_ `componentClass` `ComponentClass` \n   - _@param_ `value` `any|nil` When nil, unset the component.\n- `Entity:Set(...)`\n   - Arrow one or more instances of a component\n   - _@param_ `...` `Component`\n- `Entity:Unset(componentClass|Component, ...)` | `entity[componentClass] = nil`\n   - Remove one or more components from the entity\n   - _@param_ `...` `componentClass|Component` \n\n# LoopManager\nIn order for the world's systems to receive an update, the `World:Update(step, now)` method must be invoked on each \nframe. To automate this process, **ECS Lua** provides a functionality so that, at the time of instantiation of a \nnew world, it registers to receive the update automatically.\n\n```lua\nlocal MyLoopManager = {\n   Register = function(world)\n      local beforePhysics = MyGameEngine.BeforePhysics(function()\n         world:Update(\"process\", os.clock())\n      end)\n\n      local afterPhysics = MyGameEngine.AfterPhysics(function()\n         world:Update(\"transform\", os.clock())\n      end)\n\n      local beforeRender\n      if (not MyGameEngine.IsServer()) then\n         beforeRender = MyGameEngine.BeforeRender(function()\n            world:Update(\"render\", os.clock())\n         end)\n      end\n\n      return function()\n         beforePhysics:Disconnect()\n         afterPhysics:Disconnect()\n         if beforeRender then\n            beforeRender:Disconnect()\n         end\n      end\n   end\n}\n\nECS.SetLoopManager(MyLoopManager)\n```\n\n- `LoopManager.Register(world)`\n   - Allows the world to register to be updated.\n   - _@param_ `world` `World` \n   - _@return_ `function` The world will invoke when destroyed\n\n# Query\n- `Query(all, any, none)` | `Query.New(all, any, none)`\n   - Create a new Query used to filter entities in the world. It makes use of local and global cache in order to \n   decrease the validation time (avoids looping in runtime of systems)\n   - _@param_ `all` `Array<ComponentClass|Clause>` _optional_\n   - _@param_ `any` `Array<ComponentClass|Clause>` _optional_\n   - _@param_ `none` `Array<ComponentClass|Clause>` _optional_\n   - _@return_ `Query`\n- `Query.All(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `Query.Any(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `Query.None(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `Query.Filter(filter)`\n   - Create custom filters that can be used in Queries. Its execution is delayed, invoked only in `QueryResult` methods.\n   The result of executing the clause depends on how it was used in the query.\n   Ex. If used in `Query.All()` the result is the inverse of using the same clause in `Query.None()`\n      ```lua\n      local Player = ECS.Component({ health = 100 })\n\n      local HealthPlayerFilter = ECS.Query.Filter(function(entity, config)\n         local player = entity[Player]\n         return player.health >= config.minHealth and player.health <= config.maxHealth\n      end)\n\n      local healthyClause = HealthPlayerFilter({\n         minHealth = 80,\n         maxHealth = 100,\n      })\n\n      local healthyQuery = ECS.Query.All(Player, healthyClause)\n      world:Exec(healthyQuery):ForEach(function(entity)\n         -- this player is very healthy\n      end)\n\n      local notHealthyQuery = ECS.Query.All(Player).None(healthyClause)\n      world:Exec(healthyQuery):ForEach(function(entity)\n         -- this player is NOT very healthy\n      end)\n\n      local dyingClause = HealthPlayerClause({\n         minHealth = 1,\n         maxHealth = 20,\n      })\n\n      local dyingQuery = ECS.Query.All(Player, dyingClause)\n      world:Exec(dyingQuery):ForEach(function(entity)\n         -- this player is about to die\n      end)\n\n      local notDyingQuery = ECS.Query.All(Player).None(dyingClause)\n      world:Exec(notDyingQuery):ForEach(function(entity)\n         -- this player is NOT about to die\n      end)\n      ```\n   - _@param_ `filter` `function(entity, config) -> bool`\n   - _@return_ `function(config) -> Clause`\n- `Query:Result(chunks)`\n   - Generate a `QueryResult` with the chunks entered and the clauses of the current query\n   - _@param_ `chunks` `Array<{ [Entity] = true }>` \n   - _@return_ `QueryResult`\n- `Query:Match(archetype)`\n   - Checks if the entered archetype is valid by the query definition\n   - _@param_ `archetype` `Archetype` \n   - _@return_ `bool`\n\n# QueryBuilder\n- `QueryBuilder.isQueryBuilder`\n   - Indicates that this is an instance of a QueryBuilder\n   - _@type_ `true`\n- `QueryBuilder.All(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `QueryBuilder.Any(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `QueryBuilder.None(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `QueryBuilder.Build()`\n   - _@return_ `Query`\n\n# QueryResult\nThe result of a Query that was executed on an EntityStorage.\n\nQueryResult provides several methods to facilitate the filtering of entities resulting from the execution of the query.\n\n- **Intermediate Operations**\n   -  Intermediate operations return a new QueryResult. They are always lazy; executing an intermediate operation such as \n   `QueryResult:Filter()` does not actually perform any filtering, but instead creates a new QueryResult that, when traversed, \n   contains the elements of the initial QueryResult that match the given predicate. Traversal of the pipeline source \n   does not begin until the terminal operation of the pipeline is executed.\n- **Terminal Operations**\n   - Terminal operations, such as `QueryResult:ForEach` or `QueryResult.AllMatch`, may traverse the QueryResult to produce a \n   result or a side-effect.\n\n- `QueryResult.New(chunks, clauses)`\n   - Build a new QueryResult\n   - _@param_ `chunks` `Array<{ [Entity] = true }>`\n   - _@param_ `clauses` `Clause[]` _optional_\n   - _@return_ `QueryResult`\n- `QueryResult:With(operation, param)`\n   - Returns a QueryResult consisting of the elements of this QueryResult with a new pipeline operation\n   - _@param_ `operation` `function(param, value, count) -> newValue, acceptItem, continuesLoop`\n   - _@param_ `param` `any`\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:Filter(predicate)`\n   - Returns a QueryResult consisting of the elements of this QueryResult that match the given predicate.\n   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to each element to determine if it should be included\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:Map(mapper)`\n   - Returns a QueryResult consisting of the results of applying the given function to the elements of this QueryResult.\n   - _@param_ `mapper` `function(value) -> newValue` a function to apply to each element\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:Limit(maxSize)`\n   - Returns a QueryResult consisting of the elements of this QueryResult, truncated to be no longer than maxSize in length.\n   - _@param_ `maxSize` `number`\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:AnyMatch(predicate)`\n   - Returns whether any elements of this result match the provided predicate.\n   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to elements of this result\n   - _@return_ `true` if any elements of the result match the provided predicate, otherwise `false`\n- `QueryResult:AllMatch(predicate)`\n   - Returns whether all elements of this result match the provided predicate.\n   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to elements of this result\n   - _@return_ `true` if either all elements of the result match the provided predicate or the result is empty, otherwise `false`\n- `QueryResult:FindAny()`\n   - Returns some element of the result, or nil if the result is empty.\n\n   This is a short-circuiting terminal operation.\n\n   The behavior of this operation is explicitly nondeterministic; it is free to select any element in the result. \n   \n   Multiple invocations on the same result may not return the same value.\n   - _@return_ `any`\n- `QueryResult:ForEach(action)`\n   - Performs an action for each element of this QueryResult.\n\n   This is a terminal operation.\n\n   The behavior of this operation is explicitly nondeterministic. This operation does not guarantee to respect the \n   encounter order of the QueryResult.\n   - _@param_ `action` `function(value, count) -> bool` A action to perform on the elements, breaks execution case returns true\n- `QueryResult:ToArray()`\n   - Returns an array containing the elements of this QueryResult.\n   - _@return_ `Array<any>`\n- `QueryResult:Iterator()`\n   - Returns an Iterator, to use in for loop\n      ```lua\n      for count, entity in result:Iterator() do\n         print(entity.id)\n         break\n      end\n      ```  \n   - _@return_ `Iterator`\n\n# System\n\n- `System.Step`\n   - Step that this system will run\n   - _@type_ `string` `process|transform|render|task`\n- `System.Order`\n   - For systems that are not `task`, execution order\n   - _@type_ `number`\n- `System.Query`\n   - Filters the entities that will be processed by this system\n   - _@type_ `number`\n- `System.After`\n   - When the system is a task, it allows you to define that this system should run AFTER other specific systems.\n      ```lua\n      local log = {}\n\n      local Task_A = System.Create('task', function()\n         -- In this example, TASK_A takes time to execute, delaying its execution\n         local i = 0\n         while i <= 4000 do\n            i = i + 1\n            if i%1000 == 0 then\n               coroutine.yield()\n            end\n         end\n         \n         table.insert(log, 'A')\n      end)\n\n      local Task_B = System.Create('task', function()\n         table.insert(log, 'B')\n      end)\n\n      local Task_C = System.Create('task', function()\n         table.insert(log, 'C')\n      end)\n\n      local Task_D = System.Create('task', function()\n         table.insert(log, 'D')\n      end)\n\n      local Task_E = System.Create('task', function()\n         table.insert(log, 'E')\n      end)\n\n      local Task_F = System.Create('task', function()\n         table.insert(log, 'F')\n      end)\n\n      local Task_G = System.Create('task', function()\n         table.insert(log, 'G')\n      end)\n      \n      local Task_H = System.Create('task', function(self)\n         table.insert(log, 'H')\n      end)\n\n      --[[         \n         A<-------C<---+-----F<----+\n                  |    |     |     |\n             +----+    E<----+     H\n             |         |           |\n         B<--+----D<---+------G<---+\n\n         A - has no dependency\n         B - has no dependency\n         C - Depends on A,B\n         D - Depends on B\n         E - Depends on A,B,C,D\n         F - Depends on A,B,C,D,E\n         G - Depends on B,D\n         H - Depends on A,B,C,D,E,F,G\n\n         Completion order will be B,D,G,A,C,E,F,H      \n\n         > In this example, TASK_A takes time to execute, delaying its execution\n      ]]\n      Task_A.Before = {Task_C}\n      Task_B.Before = {Task_D}\n      Task_C.After = {Task_B}\n      Task_D.Before = {Task_G}\n      Task_F.After = {Task_E}\n      Task_E.After = {Task_D, Task_C}\n      Task_C.Before = {Task_F}\n      Task_H.After = {Task_F, Task_G}\n      ```\n   - _@type_ `SystemClass[]`\n- `System.Before`\n   - When the system is a task, it allows you to define that this system should run BEFORE other specific systems.\n   - _@see_ `System.After`\n   - _@type_ `SystemClass[]`\n- `System.version`\n   - System Version (GSV).\n   - _@see_ `World.version`\n   - _@type_ `Number`\n- `System._world`\n   - _@type_ `World`\n- `System._config`\n   - _@type_ `table`\n- `System.New(world, config)`\n   - Create an instance of this system\n   - _@param_ `world` `World`\n   - _@param_ `config` `table`\n   - _@return_ `System`\n- `System:GetType()`\n   - Get this system class\n   - _@return_ `SystemClass`\n- `System:Result(query)`\n   - Run a query in the world. A shortcut to `self._world:Exec(query)`\n   - _@param_ `query` `Query|QueryBuilder` _optional_ If nil, use default query\n   - _@return_ `QueryResult`\n- `System:Destroy()`\n   - Destroy this instance\n- `System:OnDestroy()`\n   - Allows you to perform some processing or cleaning when the instance is being destroyed\n- `System:ShouldUpdate(Time)`\n   - Invoked before 'Update', allows you to control the execution of the update\n   - _@param_ `Time` `Time`\n   - _@return_ `bool` If true, the Update method will be invoked.\n- `System:Update(Time)`\n   - Run the system's main method\n   - _@param_ `Time` `Time`\n- `System:OnRemove(Time, entity)`\n   - When it is a `QuerySystem`, it allows to be informed when an entity with the characteristics of the query is \n   removed from the world. This method is performed in the step cleanup process.\n   - _@param_ `Time` `Time`\n   - _@param_ `entity` `Entity`\n- `System:OnExit(Time, entity)`\n   - When it is a `QuerySystem`, it allows to be informed when an entity has lost the characteristics of that query \n   (has suffered an archetype change and the current query no longer applies). This method is performed in the step \n   cleanup process.\n   - _@param_ `Time` `Time`\n   - _@param_ `entity` `Entity`\n- `System:OnEnter(Time, entity)`\n   - When it is a QuerySystem, it allows to be informed when an entity received the characteristics expected by this \n   query (it suffered an archetype change and the current query now applies). This method is performed in the step \n   cleanup process.\n   - _@param_ `Time` `Time`\n   - _@param_ `entity` `Entity`\n\n\n# Time\nSingleton, reference to the world's global processing time.\n\n- `Time.Now`\n   - World Runtime\n   - _@type_ `number`\n- `Time.NowReal`\n   - Real time, received in `World:Update(step, now)` method\n   - _@type_ `number`\n- `Time.Frame`\n   - The time at the beginning of this frame (process). The world receives the current time at the beginning of each \n   frame, with the value increasing per frame.\n   - _@type_ `number`\n- `Time.FrameReal`\n   - The REAL time at the beginning of this frame (`World:Update(step, now)`).\n   - _@type_ `number`\n- `Time.Process`\n   - The time the latest process step has started.\n   - _@type_ `number`\n- `Time.Delta`\n   - The completion time in seconds since the last frame.\n   - _@type_ `number`\n- `Time.DeltaFixed`\n   - Based on world update frequency (`process` step). \n      ```lua\n      DeltaFixed = 1000/frequency/1000\n      ```\n   - _@see_ `World:SetFrequency()`\n   - _@type_ `number`\n- `Time.Interpolation`\n   - The proportion of time since the previous transform relative to processDeltaTime. Used to do interpolation during \n   the rendering step. Allows the `process` step to run at low frequency _(Ex. 30hz)_ and `render` at the maximum rate \n   of the player's device _(Ex. 60hz)_\n   - _@type_ `number`\n\n# World\n- `World.version`\n   - Global System Version (GSV).\n\n   Before executing the `Update()` method of each system, the world version is incremented, so at this point, the \n   world version will always be higher than the running system version.\n\n   Whenever an entity archetype is changed (received or lost component) the entity's version is updated to the current \n   version of the world.\n\n   After executing the System Update method, the version of this system is updated to the current world version.\n\n   This mechanism allows a system to know if an entity has been modified after the last execution of this same system, \n   as the entity's version is superior to the version of the last system execution. Thus, a system can contain logic if \n   it only operates on \"dirty\" entities, which have undergone changes. The code for this validation on a system is: \n      - `local isDirty = entity.version > self.version`\n   - _@type_ `number`\n- `World.maxTasksExecTime`\n   - Allows you to define the maximum time that the `JobSystem` can operate in each frame. The default value is \n   `0.011666666666666665` = `((1000/60/1000)*0.7)`\n      - A game that runs at 30fps has 0.0333 seconds to do all the processing for each frame, including rendering\n         - 30FPS = `(1000/30/1000)` = `0.03333333333333333`\n      - A game that runs at 60fps has 0.0166 seconds to do all the processing for each frame, including rendering\n         - 60FPS = `(1000/60/1000)` = `0.016666666666666666`\n   - _@type_ `number` Default `0.011666666666666665`\n- `World:SetFrequency(frequency)`\n   - Define the frequency that the `process` step will be executed\n   - _@param_ `frequency` `number` _optional_ Default 30\n- `World:GetFrequency()`\n   - Get the frequency of execution of the `process` step\n   - _@return_ `number`\n- `World:AddSystem(systemClass, config)`\n   - Add a new system to the world. Only one instance per type is accepted. If there is already another instance of this \n   system in the world, any new invocation of this method will be ignored.\n   - _@param_ `systemClass` `SystemClass` The system to be added in the world\n   - _@param_ `config` `table` _optional_ System instance configuration\n- `World:Entity(...)`\n   - Create a new entity. The entity is created in _DEAD_ state (`entity.isAlive == false`) and will only be visible for \n   queries after the cleaning step _(`OnRemove`,`OnEnter`,`OnExit`)_ of the current step\n   - _@param_ `...` `Component[]` _optional_ Instance of the components that this entity will have\n   - _@return_ `Entity`\n- `World:Remove(entity)`\n   - Performs immediate removal of an entity.\n\n   If the entity was created in this step and the cleanup process has not happened yet (therefore the entity is inactive, \n   `entity.isAlive == false`), the `OnRemove` event will never be fired.\n\n   If the entity is alive (`entity.isAlive == true`), even though it is removed immediately, the `OnRemove` event will \n   be fired at the end of the current step.\n   - _@param_ `entity` `Entity`\n- `World:Exec(query)`\n   - Run a query in this world\n   - _@param_ `query` `Query|QueryBuilder`\n   - _@return_ `QueryResult`\n- `World:Update(step, now)`\n   - Perform world update. When registered, the `LoopManager` will invoke World Update for each step in the sequence.\n      - `process` At the beginning of each frame\n      - `transform` After the game engine's physics engine runs\n      - `render` Before rendering the current frame\n   - _@param_ `step` `\"process\"|\"transform\"|\"render\"`\n   - _@param_ `now` `number` Usually os.clock()\n- `World:Destroy()`\n   - Destroy this instance, removing all entities, systems and events\n\n</div>\n\n[fsm]:https://en.wikipedia.org/wiki/Finite-state_machine\n"
  },
  {
    "path": "docs/architecture.md",
    "content": "# Architecture\n\nIn Software Engineering, ECS is the acronym for Entity Component System, is a software architecture pattern used \nprimarily in video game development. An ECS follows the principle of \"composition rather than inheritance\" that allows \ngreater flexibility in defining entities, where each object in a game scene is an entity (eg enemies, projectiles, \nvehicles, etc.). Each entity consists of of one or more components that add behavior or functionality. Therefore, \nthe behavior of an entity can be changed at runtime by simply adding or removing components. This eliminates problems of\nThe ambiguity with which deep and vast inheritance hierarchies, which are difficult to understand, maintain and extend.\n\nFor more details:\n- [Frequently Asked Questions about ECS](https://github.com/SanderMertens/ecs-faq)\n- [Entity Systems Wiki](http://entity-systems.wikidot.com/)\n- [Evolve your hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/)\n- [ECS on Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)\n- [ECS with Elixir](https://yos.io/2016/09/17/entity-component-systems/)\n- [2017 GDC - Overwatch Gameplay Architecture e Netcode](https://www.youtube.com/watch?v=W3aieHjyNvw&ab_channel=GDC)\n\n\n## Component\n\nThey represent the different characteristics of an entity, such as position, speed, geometry, physics, and hit points.\nComponents only store raw data for an aspect of the object and how it interacts with the world. In others words, the \ncomponent labels the entity as having this particular aspect.\n\nIn **ECS Lua**, the creation of a component is done through the `ECS.Component(template)` method.\n\nThe `template` parameter can be of any type, where:\n- When `table`, this template will be used for creating component instances\n   ```lua\n   local Component = ECS.Component({\n      x = 0, y = 0, z = 0 \n   })\n\n   local comp = Component({ x = 33, z = 80 })\n   print(comp.x, comp.y, comp.z) -- > 33, 0, 80\n\n   -- it is the same as\n   local comp = Component.New({ x = 33, z = 80 })\n   print(comp.x, comp.y, comp.z) -- > 33, 0, 80\n   ```\n- When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the \ncomponent is passed to template function\n   ```lua\n   local Component = ECS.Component(function(param)\n      return {\n         x = param.x or 1,\n         y = param.y or 1,\n         z = param.z or 1\n      }\n   end)\n\n   local comp = Component({ x = 33, z = 80 })\n   print(comp.x, comp.y, comp.z) -- > 33, 1, 80\n   ```\n- If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format \n`{ value = template }`.\n   ```lua\n   local Component = ECS.Component(55)\n\n   local comp1 = Component()\n   print(comp1.value) -- > 55\n\n   local comp2 = Component({ value = 80 })\n   print(comp2.value) -- > 80\n\n   local comp3 = Component(\"XPTO\")\n   print(comp3.value) -- > \"XPTO\"\n   ```\n\n### Methods\n\nIn **ECS Lua**, components are classes and can therefore have auxiliary methods.\n\n> IMPORTANT! Avoid creating methods that modify component instance data directly, the ideal is that these logics \nstay within the systems, which are, by definition, responsible for changing the data of the entities and their \ncomponents.\n\n```lua\nlocal Person = ECS.Component({\n   name = \"\",\n   surname = \"\",\n   birth = 0\n})\n\nfunction Person:FullName()\n   return self.name..\" \"..self.surname\nend\n\nfunction Person:Age()\n   return tonumber(os.date(\"%Y\", os.time())) - self.birth\nend\n\n\nlocal person = Person({ name = \"John\", surname = \"Doe\", birth = 2000 })\n\nprint(person:FullName()) -- John Doe\nprint(person:Age()) -- 21\n```\n\n### Qualifiers\n\nIn \"pure ECS\" implementations there is a premise that a component can only be added once to an entity. In the vast \nmajority of scenarios, this is true. For example, you don't want your entity to have two positions, it makes no sense! \nTherefore, your entity will only have one component of type Position.\n\nBut sometimes you will build some functionality that needs your entity to have this behavior, to have more than one \ncomponent of the same **TYPE**. When the framework doesn't support this kind of implementation, you end up with code \nfull of hacks to [work around](https://en.wikipedia.org/wiki/Workaround) the problem.\n\n**ECS Lua** implements the **Qualifiers** mechanism so that you can represent a category of components.\n\nTo illustrate the usage, let's think about the following scenario: I want to add a \n[Buff](https://en.wikipedia.org/wiki/Game_balance#Buff) system to my game so that my character can receive extra life \npoints in specific situations. We want to have the freedom to increase or decrease the amount of buff according to the\nregion of the map where the player is.\n\nLooking at the scenario above, we could create, at first, the solution below. A `HealthBuff` component to record the \namount of extra life and two systems. The first `MapRegionSystem` decides how many additional health points the player \nwill have for each region, while the second `HealthSystem` represents some system functionality that need to get the \nplayer's life total at a certain time.\n\n```lua\n-- components\nlocal Player = ECS.Component({ health = 100, region = \"easy\", healthTotal = 100 })\nlocal HealthBuff = ECS.Component({ value = 10 })\n\n-- systems\nlocal MapRegionSystem = System(\"process\", 1, Query.All(Player))\n\nfunction MapRegionSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]      \n      \n      if player.region == \"easy\" then\n         entity[HealthBuff] = nil -- remove buff\n      else\n         local buff = entity[HealthBuff]\n         if buff == nil then\n            buff = HealthBuff(0)\n            entity:Set(buff)\n         end\n\n         if player.region == \"hard\" then\n            buff.value = 15\n         elseif player.region == \"hell\" then\n            buff.value = 40\n         end  \n      end    \n   end\nend\n\nlocal HealthSystem = System(\"process\", 2, Query.All(Player).Any(HealthBuff)) \n\nfunction HealthSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]\n\n      local buff = entity[HealthBuff]\n      if buff then \n         player.healthTotal = player.health + buff.value\n      else\n         player.healthTotal = player.health\n      end\n   end\nend\n```\n\nSo far quiet. However, imagine now that my player can receive **SEVERAL** buffers.\n   - He can receive a buffer for the character he is using;\n   - another buff when unlocking an item and;\n   - you can also buy buffs from the in-game shop.\n\nIn this new scenario our solution does not meet, because the `MapRegionSystem` system code does not have the information \nabout the other factors, and to attend to it, it will have to know or manage several possible states to decide which is \nthe amount of health the player will receive for being in a specific region. The other systems in the game too needed \nto know the region to decide how much buffer to add. In a \"pure ECS\" solution, we're going to start:\n\n1. share state between systems\n1. create \"Component TAGs\" to facilitate the management of this distributed state,\n1. inflate components with an attribute for each system type.\n\nAt first this doesn't seem to be a problem, but over time, multiple systems will be called unnecessarily (just to do an \nif and not process that entity). These systems now have extra responsibilities, increasing the complexity of the code, \nmaking maintenance difficult and facilitating the appearance of bugs.\n\nIn **ECS Lua** we solve this kind of problem by creating qualifiers, through the static method \n`ComponentClass.Qualifier(qualifier)`. It accepts a string as a parameter and returns a reference to a specialized class \nof our component. This generated class maintains a strong link with the base class, allowing more complex queries.\n\nLet's change our example using qualifiers.\n\n```lua\n-- components\nlocal Player = ECS.Component({ health = 100, region = \"easy\", healthTotal = 100 })\nlocal HealthBuff = ECS.Component({ value = 10 })\nlocal HealthBuffItem = HealthBuff.Qualifier(\"Item\")\nlocal HealthBuffMapRegion = HealthBuff.Qualifier(\"Region\")\nlocal Item = ECS.Component({ rarity = 0 })\n\n-- systems\nlocal PlayerItemSystem = System(\"process\", 1, Query.All(Player, Item))\n\nfunction PlayerItemSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local item = entity[Item]\n      local player = entity[Player]\n      \n      if item.rarity == \"legendary\" then\n         entity[HealthBuffItem] = 15 -- same as entity:Set(HealthBuffItem.New(15))\n      else\n         entity[HealthBuffItem] = nil \n      end\n   end\nend\n\nlocal MapRegionSystem = System(\"process\", 1, Query.All(Player))\n\nfunction MapRegionSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]\n      \n      if player.region == \"easy\" then\n         entity[HealthBuffMapRegion] = nil\n      else\n         local buff = entity[HealthBuffMapRegion]\n         if buff == nil then\n            buff = HealthBuffMapRegion(0)\n            entity:Set(buff)\n         end\n\n         if player.region == \"hard\" then\n            buff.value = 15\n         elseif player.region == \"hell\" then\n            buff.value = 40\n         end      \n      end\n   end\nend\n\nlocal HealthSystem = System(\"process\", 2, Query.All(Player).Any(HealthBuff))\n\nfunction HealthSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]\n\n      local healthTotal = player.health\n\n      local buffers = entity:GetAll(HealthBuff)\n      for i,buff in ipairs(buffers) do\n         healthTotal = healthTotal + buff.value\n      end\n\n      player.healthTotal = player.health\n   end\nend\n```\n\nOkay, in this new implementation, the `MapRegionSystem` system only cares about the `HealthBuffMapRegion` qualifier,\nwhile the `PlayerItemSystem` system only manages the `HealthBuffItem` qualifier. We can now create systems that \nspecialize in qualifiers and manage only this attribute of the entity. The `HealthSystem` gets and processes all \nentities that have any qualifier from the `HealthBuff` component.\n\n[Check the API](/api?id=component) other methods that can be useful when working with qualifiers.\n\n### FSM - Finite State Machines\n\n__UNDER_CONSTRUCTION__\n\n\n```lua\nlocal Movement = Component.Create({ Speed = 0 })\n\n--  [Standing] <--> [Walking] <--> [Running]\nMovement.States = {\n   Standing = {\"Walking\"},\n   Walking  = \"*\",\n   Running  = {\"Walking\"}\n}\n\nMovement.StateInitial = \"Standing\"\n\nMovement.Case = {\n   Standing = function(self, previous)\n      print(\"Transition from \"..previous..\" to Standing\")\n   end,\n   Walking = function(self, previous)\n      print(\"Transition from \"..previous..\" to Walking\")\n   end,\n   Running = function(self, previous)\n      print(\"Transition from \"..previous..\" to Running\")\n   end\n}\n\n\nlocal movement = Movement()\n\nmovement:SetState(\"Walking\")\nmovement:SetState(\"Running\")\n\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\nmovement:SetState(\"Standing\") -- invalid, Running -> Walking|Running\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\nmovement:SetState(nil)\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\nmovement:SetState(\"INVALID_STATE\")\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\n\n-- query\nlocal queryStanding = Query.All(Movement.In(\"Standing\"))\nlocal queryInMovement = Query.Any(Movement.In(\"Walking\", \"Running\"))\n\n\n-- qualifier\nlocal MovementB = Movement.Qualifier(\"Sub\")\n -- ignored, \"States\", \"StateInitial\" and \"Case\" only work in primary class\nMovementB.States = { Standing = {\"Walking\"} }\n```\n\n## Entity\n\n__UNDER_CONSTRUCTION__\n\n```lua\n--[[\n   [GET]\n   01) comp1 = entity[CompType1]\n   02) comp1 = entity:Get(CompType1)\n   03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n]]\n\n--[[\n   [SET]\n   01) entity[CompType1] = nil\n   02) entity[CompType1] = value\n   03) entity:Set(CompType1, nil)   \n   04) entity:Set(CompType1, value)\n   05) entity:Set(comp1)\n   06) entity:Set(comp1, comp2, ...)\n]]\n\n--[[\n   [UNSET]\n   01) enity:Unset(comp1)\n   02) entity[CompType1] = nil\n   03) enity:Unset(CompType1)\n   04) enity:Unset(comp1, comp1, ...)\n   05) enity:Unset(CompType1, CompType2, ...)\n]]\n\n--[[\n   [Utils]\n   01) comps = entity:GetAll()\n   01) qualifiers = entity:GetAll(PrimaryClass)\n]]\n```\n\n## Query\n\n__UNDER_CONSTRUCTION__\n\n## System\n\n__UNDER_CONSTRUCTION__\n\n## Task\n\n__UNDER_CONSTRUCTION__\n\n```lua\nlocal log = {}\n\nlocal Task_A = System.Create('task', function()\n   -- In this example, TASK_A takes time to execute, delaying its execution\n   local i = 0\n   while i <= 4000 do\n      i = i + 1\n      if i%1000 == 0 then\n         -- Processing is parallel, any time-consuming task must invoke coroutine.yield() after a period of time to \n         -- not block processing\n         coroutine.yield()\n      end\n   end\n   \n   table.insert(log, 'A')\nend)\n\nlocal Task_B = System.Create('task', function()\n   table.insert(log, 'B')\nend)\n\nlocal Task_C = System.Create('task', function()\n   table.insert(log, 'C')\nend)\n\nlocal Task_D = System.Create('task', function()\n   table.insert(log, 'D')\nend)\n\nlocal Task_E = System.Create('task', function()\n   table.insert(log, 'E')\nend)\n\nlocal Task_F = System.Create('task', function()\n   table.insert(log, 'F')\nend)\n\nlocal Task_G = System.Create('task', function()\n   table.insert(log, 'G')\nend)\n\nlocal Task_H = System.Create('task', function(self)\n   table.insert(log, 'H')\nend)\n\n--[[         \n   A<-------C<---+-----F<----+\n            |    |     |     |\n       +----+    E<----+     H\n       |         |           |\n   B<--+----D<---+------G<---+\n\n   A - has no dependency\n   B - has no dependency\n   C - Depends on A,B\n   D - Depends on B\n   E - Depends on A,B,C,D\n   F - Depends on A,B,C,D,E\n   G - Depends on B,D\n   H - Depends on A,B,C,D,E,F,G\n\n   Completion order will be B,D,G,A,C,E,F,H      \n\n   > In this example, TASK_A takes time to execute, delaying its execution\n]]\nTask_A.Before = {Task_C}\nTask_B.Before = {Task_D}\nTask_C.After = {Task_B}\nTask_D.Before = {Task_G}\nTask_F.After = {Task_E}\nTask_E.After = {Task_D, Task_C}\nTask_C.Before = {Task_F}\nTask_H.After = {Task_F, Task_G}\n```\n\n## World\n\n__UNDER_CONSTRUCTION__\n\n\n\n\n\n"
  },
  {
    "path": "docs/faq.md",
    "content": "# FAQ\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/favicon/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig><msapplication><tile><square70x70logo src=\"/ms-icon-70x70.png\"/><square150x150logo src=\"/ms-icon-150x150.png\"/><square310x310logo src=\"/ms-icon-310x310.png\"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>"
  },
  {
    "path": "docs/favicon/manifest.json",
    "content": "{\n \"name\": \"App\",\n \"icons\": [\n  {\n   \"src\": \"\\/android-icon-36x36.png\",\n   \"sizes\": \"36x36\",\n   \"type\": \"image\\/png\",\n   \"density\": \"0.75\"\n  },\n  {\n   \"src\": \"\\/android-icon-48x48.png\",\n   \"sizes\": \"48x48\",\n   \"type\": \"image\\/png\",\n   \"density\": \"1.0\"\n  },\n  {\n   \"src\": \"\\/android-icon-72x72.png\",\n   \"sizes\": \"72x72\",\n   \"type\": \"image\\/png\",\n   \"density\": \"1.5\"\n  },\n  {\n   \"src\": \"\\/android-icon-96x96.png\",\n   \"sizes\": \"96x96\",\n   \"type\": \"image\\/png\",\n   \"density\": \"2.0\"\n  },\n  {\n   \"src\": \"\\/android-icon-144x144.png\",\n   \"sizes\": \"144x144\",\n   \"type\": \"image\\/png\",\n   \"density\": \"3.0\"\n  },\n  {\n   \"src\": \"\\/android-icon-192x192.png\",\n   \"sizes\": \"192x192\",\n   \"type\": \"image\\/png\",\n   \"density\": \"4.0\"\n  }\n ]\n}"
  },
  {
    "path": "docs/getting-started.md",
    "content": "# Installation\n\n**ECS Lua** has no external dependencies, so just download the latest version available on \n[releases page](https://github.com/nidorx/ecs-lua/releases) of the project.\n\nThere are 3 options to use **ECS Lua**\n\n1. **ECS.lua** Minified version in a single file\n1. **ECS_concat.lua** Version concatenated with the original comments, which can be used for debugging during the\ndevelopment\n1. **ECS.zip** Version with files from the `src` directory.\n   > Important! All files do the `require` for dependencies that are in the same directory, if it is\n   using in a Lua project, register in `package.path`.\n\n   > These `require` do not work in Roblox Luau, due to the import format that Roblox uses.\n\nAfter importing **ECS Lua**, it is ready to be used. **ECS Lua** registers the global variable `_G.ECS` to\nmake it easy to use, so you can use the engine in both ways `local ECS = require(\"ECS\")`\n(in Roblox `local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))`) or simply `_G.ECS`.\n\n## LoopManager\n\nIn order for the world's systems to receive an update, the `World:Update(step, now)` method must be invoked on each \nframe. To automate this process, **ECS Lua** provides a functionality so that, at the time of instantiation of a \nnew world, it registers to receive the update automatically.\n\nThe implementation of this method is very simple and more details can be seen in the section\n[Architecture - World](/architecture?id=world).\n\n> If you use Roblox you don't need to worry, **ECS Lua** already has a default implementation when it runs on\nRoblox, more details below.\n\n## Roblox\n\nYou can install directly from Roblox Studio by searching the toolbox for `ECS-lua`, this is the \n[minified engine version](https://www.roblox.com/library/5887881675). When using **ECS Lua** in Roblox, the engine \nalready automatically identifies and registers a `LoopManager`, so no additional steps are needed.\n\n# General Concepts\n\nSome common terms in ECS engines are:\n- [Entities](/architecture?id=entity): An object with a unique ID that can have multiple components attached to it.\n- [Components](/architecture?id=component): Different characteristics of an entity. eg geometry, physics, hit points. Data is only stored in components.\n- [Systems](/architecture?id=system): It does the real work, applying the rules of the game, processing entities and modifying their components.\n- [Queries](/architecture?id=query): Used by systems to determine which entities they are interested in, based on the components the entities have.\n- [World](/architecture?id=world): A container for entities, components, systems and queries.\n\n![General Concepts](assets/diagram-1.png)\n\nThe normal workflow when building an ECS-based program:\n- Create the `Components` that shape the data you need to use in your game/application.\n- Create the `Systems` that will use these `Components` to read and transform the entity data.\n- Create `Entities` and attach `Components` to them.\n- Run all systems at each frame, perform `Query` in `World` to decide which entities will be modified.\n\n## Component\n\nComponents are objects that contain data. In **ECS Lua**, just call the `ECS.Component(template)` method to define a \n`Class` of a component.\n\nThe `template` parameter can be of any type, where:\n- When `table`, this template will be used for creating component instances;\n- When it's a `function`, it will be invoked on instantiation.\n- If the template type is different, **ECS Lua** will generate a template in the format `{ value = template }`, this is \nthe format used in the `Acceleration` component below.\n\n```lua\nlocal Position = ECS.Component({ \n   x = 0, y = 0, z = 0 \n})\n\n-- the same as:\n-- ECS.Component({ value = 0.1 })\nlocal Acceleration = ECS.Component(0.1) \n```\n[More information on creating components](/architecture?id=component).\n\n## Systems and Queries\n\nNow let's define a [system](/architecture?id=system) to process the components we just created. One system can implement\nseveral methods, in this exercise we will just use the `Update(Time)` method. This method will be invoked on every \nframe, in the world's `process` step.\n\nTo create a system, we use the `ECS.System(step, order, query, updateFn)` method. This method receives the following\nparameters:\n- **`step`** String, accepts the values `process`, `transform`, `render` or `task`\n- **`order`** Number (Optional) Allows you to set an execution order (for systems that are not `task`). Default 50\n- **`query`** Query (Optional) Filters the entities that will be processed by this system\n- **`updateFn`** Function (Optional) A shortcut for creating systems that only have the Update method\n\nWe're also going to define a `Query`, which is the query we'll use to get just the entities we're interested.\n\nTo create the query, we can use the `Query.All(Component)`, `Query.Any(Component)` or `Query.None(Component)`. When \ninvoking any of these methods a `QueryBuilder` is returned, so you can invoke the other methods in sequence \nEx. `Query.All(ComponentA).Any(ComponentB).None(ComponentC).Build()`.\n\n![Pipeline](assets/pipeline.png)\n\nLet's start by creating a system that will loop through all entities that have a `Position` component and record their \npositions.\n\n```lua\n-- a shortcut to methods\nlocal System, Query = ECS.System, ECS.Query \n\nlocal PositionLogSystem = System(\"process\", 2, Query.All(Position), function(self, Time)\n\n   -- Iterate through all entities in the query\n   self:Result():ForEach(function(entity)\n      -- Access the `Position` component in the current entity\n      local pos = entity[Position]\n\n      local msg = \"Entity with ID: %d has Position = {x: %0.2f, y: %0.2f, z: %0.2f}\"\n\t\tprint(msg:format(entity.id, pos.x, pos.y, pos.z))\n   end)\nend)\n```\n\nThe next system moves each entity that has a position and an acceleration.\n\n```lua\nlocal MovableSystem = System(\"process\", 1, Query.All(Acceleration, Position)) \n\n-- This method will be called on all frames by default.\nfunction MovableSystem:Update(Time)\n\n   local delta = Time.DeltaFixed\n\n   -- Iterate through all entities in the query\n   for i, entity in self:Result():Iterator() do\n      local acceleration = entity:Get(Acceleration).value\n\n      local position = entity[Position]\n      position.x = position.x + acceleration * delta\n      position.y = position.y + acceleration * delta\n      position.z = position.z + acceleration * delta\n   end\nend\n```\n\n> Note that we are accessing components in an entity in two different ways: `entity:Get(Acceleration)` has the\nsame result as `entity[Acceleration]`\n\nThe system query `MovableSystem` filters the entities that have the components `Acceleration` and\n`Position`. Note that, if necessary, we can create as many queries as we like and process them in the `Update` method, eg:\n\n```lua\nlocal SystemDemo =  System(\"process\", 1)  \n\nfunction SystemDemo:Initialize(config)\n   self.queryBoxes = Query.All(Box).Build()\n   self.queryBalls = Query.All(Ball).Build()\n   self.queryGeometries = Query.Any(Box, Ball).Build()\nend\n\nfunction SystemDemo:Update(Time)\n   local boxes = self:Result(self.queryBoxes):ToArray()\n   local balls = self.World:Exec(self.queryBalls):ToArray()\n   for i, entity in self:Result(self.queryGeometries):Iterator() do\n      print(entity[Box], entity[Ball])\n   end\nend\n```\n\nFor more information, check the architecture documentation: [Components](/architecture?id=component), \n[Entities](/architecture?id=entity) and [Queries](/architecture?id=query)\n\n\n## World\n\nA world is a container for `entities`, `components` and `systems`. Most games have only one `world`, however, you can \nhave multiple worlds running at the same time and enable or disable them as needed.\n\nCreating a world is done by the `ECS.World(systemClasses, frequency, disableAutoUpdate)` method, where:\n\n- **systemClasses** (optional) Array of system classes \n- **frequency** Number (optional) Define the frequency that the `process` step will be executed. Default 30\n- **disableAutoUpdate** Bool (optional) When `~= false`, the world automatically registers in the \n`LoopManager`, receiving the `World:Update()` method from it. Default false\n\nLet's start creating our world:\n\n```lua\nlocal world = ECS.World();\n```\n\nNow let's register our systems in the world so that they start up and run on every frame.\n\n```lua\nworld:AddSystem(PositionLogSystem)\nworld:AddSystem(MovableSystem)\n```\n\n## Entity\n\nHaving our world, some components and systems already defined, let's create [entities](/architecture?id=entity) and \nattach these components to them:\n\n```lua\nlocal entity1 = world:Entity(Position())\n\nlocal entity2 = world:Entity(\n   Position({x = 5}), \n   Acceleration(1)\n)\n\nlocal entity3 = world:Entity(\n   Position.New({x = 5}), \n   Acceleration.New(1)\n)\n\nlocal entity4 = world:Entity(\n   Position({x = 5}), \n   Acceleration({value = 1})\n)\n\nlocal entity5 = world:Entity()\nentity5[Position] = { y = 5 }\nentity5:Set(Acceleration())\n\nlocal entity6 = world:Entity()\nentity6[Position] = Position()\nentity6:Set(Acceleration())\n```\n\nWith that, we have just created 6 entities. 5 with the `Acceleration` and `Position` components, and one with just \nthe `Position` component.\n\nNote that there are several ways to instantiate and assign components to the entity, choose the one that best matches\nwith your programming style, the end result is the same. Also note that component classes can be used \n[as functions](http://lua-users.org/wiki/FuncTables), for example `Position()`. This format has the same effect \nthan `Position.New()`.\n\n## Putting everything together\n\nNow the world just needs to be updated (`world.Update(step, now)`) for everything to work. If you use roblox, just\ncreate a Local Script that the world will automatically update.\n\n\n```lua\n-- Roblox:\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- a shortcut to methods\nlocal Component, System, Query = ECS.Component, ECS.System, ECS.Query \n\n-- Components\nlocal Position = Component({ x = 0, y = 0, z = 0 })\nlocal Acceleration = Component(0.1)\n\n-- Sistems\nlocal PositionLogSystem = System(\"process\", 2, Query.All(Position), function(self, Time)\n\n   -- Iterate through all entities in the query\n   self:Result():ForEach(function(entity)\n      -- Access the `Position` component in the current entity\n      local pos = entity[Position]\n\n      local msg = \"Entity with ID: %d has Position = {x: %0.2f, y: %0.2f, z: %0.2f}\"\n\t\tprint(msg:format(entity.id, pos.x, pos.y, pos.z))\n   end)\nend)\n\nlocal MovableSystem = System(\"process\", 1, Query.All(Acceleration, Position)) \n\n-- This method will be called on all frames by default.\nfunction MovableSystem:Update(Time)\n\n   local delta = Time.DeltaFixed\n\n   -- Iterate through all entities in the query\n   for i, entity in self:Result():Iterator() do\n      local acceleration = entity:Get(Acceleration).value\n\n      local position = entity[Position]\n      position.x = position.x + acceleration * delta\n      position.y = position.y + acceleration * delta\n      position.z = position.z + acceleration * delta\n   end\nend\n\n-- World\nlocal world = ECS.World();\nworld:AddSystem(PositionLogSystem)\nworld:AddSystem(MovableSystem)\n\n-- Entities\nlocal entity1 = world:Entity(Position())\nlocal entity2 = world:Entity(Position({x = 5}), Acceleration(1))\nlocal entity3 = world:Entity(Position.New({x = 5}), Acceleration.New(1))\nlocal entity4 = world:Entity(Position({x = 5}), Acceleration({value = 1}))\n\nlocal entity5 = world:Entity()\nentity5[Position] = { y = 5 }\nentity5:Set(Acceleration())\n\nlocal entity6 = world:Entity()\nentity6[Position] = Position()\nentity6:Set(Acceleration())\n```\n\nResult of the above code in Roblox Studio\n\n![Result of the above code in Roblox Studio](assets/get-started-output.gif)\n\n## Next steps\nThis was a quick overview of how things are structured using **ECS Lua**,\n[read the architecture documentation](/architecture) for more detailed information.\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n   <meta charset=\"UTF-8\" />\n   <title>ECS-lua - Entity Component System in Lua</title>\n   <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n   <meta name=\"description\" content=\"Entity Component System in Lua\" />\n   <meta name=\"viewport\"\n      content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\" />\n   <link rel=\"stylesheet\" href=\"//cdn.jsdelivr.net/npm/docsify@4.12.1/themes/vue.css\">\n\n   <link rel=\"stylesheet\" href=\"style.css\" />\n\n   <link rel=\"apple-touch-icon\" sizes=\"57x57\" href=\"./favicon/apple-icon-57x57.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"60x60\" href=\"./favicon/apple-icon-60x60.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"./favicon/apple-icon-72x72.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"76x76\" href=\"./favicon/apple-icon-76x76.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"./favicon/apple-icon-114x114.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"120x120\" href=\"./favicon/apple-icon-120x120.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"144x144\" href=\"./favicon/apple-icon-144x144.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"152x152\" href=\"./favicon/apple-icon-152x152.png\">\n   <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"./favicon/apple-icon-180x180.png\">\n   <link rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"./favicon/android-icon-192x192.png\">\n   <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"./favicon/favicon-32x32.png\">\n   <link rel=\"icon\" type=\"image/png\" sizes=\"96x96\" href=\"./favicon/favicon-96x96.png\">\n   <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"./favicon/favicon-16x16.png\">\n   <link rel=\"manifest\" href=\"./favicon/manifest.json\">\n   <meta name=\"msapplication-TileColor\" content=\"#ffffff\">\n   <meta name=\"msapplication-TileImage\" content=\"./favicon/ms-icon-144x144.png\">\n   <meta name=\"theme-color\" content=\"#ffffff\">\n</head>\n\n<body>\n   <div id=\"app\"></div>\n   <script>\n      window.$docsify = {\n         repo: 'nidorx/ecs-lua',\n         name: \"ECS-lua\",\n         loadSidebar: true,\n         auto2top: true,\n         coverpage: ['/', '/pt-br/'],\n         executeScript: true,\n         loadNavbar: true,\n         search: {\n            paths: 'auto',\n            placeholder: {\n               '/': 'Type to search...',\n               '/pt-br/': 'Digite para buscar...',\n            },\n            noData: {\n               '/pt-br/': 'Nenhum resultado',\n               '/': 'No Results'\n            },\n         },\n         copyCode: {\n            buttonText: {\n               '/pt-br/': 'Clique para copiar',\n               '/': 'Copy to clipboard'\n            },\n            errorText: {\n               '/pt-br/': 'Erro',\n               '/': 'Error'\n            },\n            successText: {\n               '/pt-br/': 'Copiado',\n               '/': 'Copied'\n            }\n         },\n         relativePath: true,\n         // logo: './logo.png',\n         themeColor: '#FA880B',\n         fallbackLanguages: ['pt-br'],\n         plugins: [\n            function (hook, vm) {\n               var svg = '<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"/></svg>';\n               hook.beforeEach(function (markdown) {\n                  var text = 'edit this file';\n                  var underConstruction = '> **UNDER CONSTRUCTION!** You can contribute to this documentation at the link __EDIT_LINK__';\n                  if (vm.route.path.startsWith('/pt-br')) {\n                     text = 'edite este arquivo';\n                     underConstruction = '> **EM CONSTRUÇÃO!** Você pode contribuir com essa documentacao no link __EDIT_LINK__';\n                  }\n\n                  var url = 'https://github.com/nidorx/ecs-lua/edit/master/docs/' + vm.route.file;\n\n                  var editLink = '<a class=\"edit-button\" href=\"' + url + '\" target=\"_blank\">' + url + '</a>'\n                  underConstruction = underConstruction.replaceAll('__EDIT_LINK__', editLink)\n\n                  markdown = markdown.replaceAll('__UNDER_CONSTRUCTION__', underConstruction)\n\n                  return [\n                     markdown,\n                     '\\n\\n',\n                     '<div class=\"meta-container\">',\n                     '<a class=\"edit-button\" href=\"' + url + '\" target=\"_blank\">',\n                     svg,\n                     text,\n                     '</a>',\n                     '</div>\\n\\n'\n                  ].join(\"\")\n               })\n            }\n         ]\n      }\n\n      // remove sidebar's title and add new logo\n      let interval = setInterval(function () {\n         if (document.getElementsByTagName('aside')[0]) {\n            let parent = document.getElementsByTagName('aside')[0]\n            parent.removeChild(parent.getElementsByTagName('h1')[0])\n            let div = document.createElement('div')\n            div.setAttribute('class', 'logo')\n            let a = document.createElement('a')\n            a.setAttribute('href', 'https://nidorx.github.io/ecs-lua/#/')\n            let img = document.createElement('img')\n            img.setAttribute('src', './assets/logo.svg')\n\n            a.appendChild(img)\n            div.appendChild(a)\n            parent.prepend(div)\n            clearInterval(interval)\n         }\n      }, 0)\n   </script>\n   <script src=\"//cdn.jsdelivr.net/npm/docsify@4.12.1\"></script>\n   <script src=\"//unpkg.com/docsify-pagination/dist/docsify-pagination.min.js\"></script>\n   <script src=\"//cdn.jsdelivr.net/npm/docsify@4.12.1/lib/plugins/search.js\"></script>\n   <script src=\"//unpkg.com/prismjs/components/prism-lua.min.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "docs/pt-br/README.md",
    "content": "# O que é?\n\n**ECS Lua** é um motor ECS (Entity Component System) rápido e fácil de usar para o desenvolvimento de jogos.\n\n![](../assets/diagram-1-pt-br.png)\n\nA ideia básica desse padrão é deixar de fazer a definição de entidades usando uma [hierarquia](https://pt.wikipedia.org/wiki/Heran%C3%A7a_(programa%C3%A7%C3%A3o_orientada_a_objetos)) de classes e passar a fazer \nuso de [composição](https://pt.wikipedia.org/wiki/Composi%C3%A7%C3%A3o_de_objetos) em um paradigma de Programação Orientada a Dados.\n([Mais informações na Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)).\nA programação com um ECS pode resultar em um código mais eficiente e fácil de estender no longo do tempo.\n\n\n# Como ele funciona?\n\n![ECS Lua pipeline](../assets/pipeline.png)\n\n\n# Falar é fácil. Me mostre o código!\n\n```lua\nlocal World, System, Query, Component = ECS.World, ECS.System, ECS.Query, ECS.Component\n\nlocal Health = Component(100)\nlocal Position = Component({ x = 0, y = 0})\n\nlocal isInAcid = Query.Filter(function()\n   return true  -- it's wet season\nend)\n\nlocal InAcidSystem = System(\"process\", Query.All( Health, Position, isInAcid() ))\n\nfunction InAcidSystem:Update()\n   for i, entity in self:Result():Iterator() do\n      local health = entity[Health]\n      health.value = health.value - 0.01\n   end\nend\n\nlocal world = World({ InAcidSystem })\n\nworld.Entity(Position({ x: 5.0 }), Health())\n```\n    \n# Recursos\n\nO **ECS Lua** nao possui dependencias externas e é compativel e testada com [Lua 5.1], [Lua 5.2], [Lua 5.3], [Lua 5.4], \n[LuaJit] e [Roblox Luau](https://luau-lang.org/)\n\n- **Agnóstico de engine de jogo**: Pode ser usado em qualquer motor que tenha a linguagem de script Lua\n- **Ergonômico**: Focado em fornecer uma API simples, mas eficiente\n- **FSM**: Máquinas de Estados Finitos de maneira fácil e intuitiva\n- **JobSystem**: Para a execucao de sistemas em paralelo (por meio de [coroutines](http://www.lua.org/pil/9.1.html))\n- **Reativo**: Os sistemas podem ser informado quando uma entidade sofrer alteracao\n- **Previsível**:\n   - Os sistemas funcionarão na ordem em que foram registrados ou com base na prioridade definida ao registrá-los\n   - Os eventos reativos não geram um retorno de chamada aleatório quando emitidos, sao executados em um passo pre-definido\n\n# Objetivo\n\nSer uma biblioteca ECS leve, simples, ergonomica e de alto desempenho que pode ser facilmente estendida. O **ECS Lua** \nnão segue estritamente o \"design ECS puro\".\n\n# Próximos passos\n\nVocê pode navegar ou buscar assuntos específicos no menu lateral. A seguir, alguns links relevantes:\n\n<br>\n<br>\n\n<div class=\"home-row clearfix\" style=\"text-align:center\">\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Instalação](../assets/icon-download.png \":no-zoom\")](/pt-br/getting-started?id=instalação)\n\n   </div><div class=\"panel-heading\">\n\n   [Instalação](/pt-br/getting-started?id=instalação)\n\n   </div></div></div>\n\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Conceitos Gerais](../assets/icon-parts.png \":no-zoom\")](/pt-br/getting-started?id=conceitos-gerais)\n\n   </div><div class=\"panel-heading\">\n\n   [Conceitos Gerais](/pt-br/getting-started?id=conceitos-gerais)\n\n   </div></div></div>\n\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Arquitetura](../assets/icon-advanced.png \":no-zoom\")](/pt-br/architecture)\n\n   </div><div class=\"panel-heading\">\n\n   [Arquitetura](/pt-br/architecture)\n\n   </div></div></div>\n\n   <div class=\"home-col\"><div class=\"panel home-panel\"><div class=\"panel-body\">\n\n   [![Tutoriais](../assets/icon-tutorial.png \":no-zoom\")](/pt-br/tutorial)\n\n   </div><div class=\"panel-heading\">\n\n   [Tutoriais](/pt-br/tutorial)\n\n   </div></div></div>\n</div>\n\n[Lua 5.1]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.2]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.3]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[Lua 5.4]:https://app.travis-ci.com/github/nidorx/ecs-lua\n[LuaJit]:https://app.travis-ci.com/github/nidorx/ecs-lua\n"
  },
  {
    "path": "docs/pt-br/_coverpage.md",
    "content": "<div class=\"logo-container\">\n  <div class=\"heats\">\n    <div class=\"h r1 c1\"></div>\n    <div class=\"h r1 c2\"></div>\n    <div class=\"h r1 c3\"></div>\n    <div class=\"h r1 c4\"></div>\n    <div class=\"h r1 c5\"></div>\n    <div class=\"h r2 c1\"></div>\n    <div class=\"h r2 c2\"></div>\n    <div class=\"h r2 c3\"></div>\n    <div class=\"h r2 c4\"></div>\n    <div class=\"h r2 c5\"></div>\n    <div class=\"h r3 c1\"></div>\n    <div class=\"h r3 c2\"></div>\n    <div class=\"h r3 c3\"></div>\n    <div class=\"h r3 c4\"></div>\n    <div class=\"h r3 c5\"></div>\n    <div class=\"h r4 c1\"></div>\n    <div class=\"h r4 c2\"></div>\n    <div class=\"h r4 c3\"></div>\n    <div class=\"h r4 c4\"></div>\n    <div class=\"h r4 c5\"></div>\n    <div class=\"h r5 c1\"></div>\n    <div class=\"h r5 c2\"></div>\n    <div class=\"h r5 c3\"></div>\n    <div class=\"h r5 c4\"></div>\n    <div class=\"h r5 c5\"></div>\n    \n   <svg \n      class=\"ghost\"\n      width=\"50mm\" \n      height=\"50mm\" \n      version=\"1.1\" \n      viewBox=\"0 0 50 50\">\n      <path d=\"m4.8916 45.894-2e-6 -2.9104h-2.9104l-1e-6 -20.902h2.9104v-8.9958h3.175v-3.1992h3.4396v-2.8862l6.8792-9.1e-6v-2.9104h13.229v2.9104h6.4824v2.8862h3.7041v3.1992h-6.7469v3.199l-3.4396 1e-3 -2e-6 8.4414h3.4396l2e-6 2.9104 6.7469-1.7e-4v-5.5559l6.2178 2.3e-4v20.902h-3.175v2.9265l-6.7469-0.016v-2.9104h-3.0428v-3.1749h-3.4396v3.1749l-3.4396 0.0256v2.8848h-6.6146v-2.9104h-3.175v-3.1749h-3.175v3.1749h-3.7042c0.0062 0.97335 0 1.9371 0 2.9104-2.2049-2e-4 -4.4097 0-6.6146 0zm16.669-18.256-2e-6 -2.9104h3.4397v-8.4426h-3.4397v-3.199h-6.35v3.199h-3.7042l2e-6 8.4425 3.7042 1.5e-4v2.9103z\" fill=\"#fb860c\" class=\"body\"></path>\n      <g class=\"eye eye--left\">\n         <g class=\"pupil\">\n            <g class=\"inner\">\n               <path class=\"pupil-color\" d=\"m18.385 24.728v-5.5563h6.6147v5.5563z\" fill=\"#020202\"></path>\n               <path class=\"eyelid\" d=\"m18.385 24.728v-5.5563h6.6147v5.5563z\" fill=\"#edf4f7\"></path>\n            </g>\n         </g>\n      </g>\n      <g class=\"eye eye--right\">\n         <g class=\"pupil\">\n            <g class=\"inner\">\n               <path class=\"pupil-color\" d=\"m38.494 24.728v-5.5563h6.6147v5.5563z\" fill=\"#020202\"></path>\n               <path class=\"eyelid\"  d=\"m38.494 24.728v-5.5563h6.6147v5.5563z\" fill=\"#edf4f7\"></path>\n            </g>\n         </g>\n      </g>\n   </svg>\n  </div>\n</div>\n\n# ECS Lua\n[![Build Status](https://app.travis-ci.com/nidorx/ecs-lua.svg?branch=master)](https://app.travis-ci.com/nidorx/ecs-lua)\n\n[GitHub](https://github.com/nidorx/ecs-lua)\n[Começar](/pt-br/?id=o-que-é)\n\n<!-- background color -->\n![color](#ffffff)\n"
  },
  {
    "path": "docs/pt-br/_navbar.md",
    "content": "- [Início](/pt-br/) &nbsp; &nbsp;\n- Idiomas <span class=\"arrow\">&#x25BE;</span>\n  - [English](/)\n  - [Português do Brasil](/pt-br/)\n"
  },
  {
    "path": "docs/pt-br/_sidebar.md",
    "content": "- [Início](/pt-br/)\n- [Instalação](/pt-br/getting-started?id=instalação)\n- [Conceitos Gerais](/pt-br/getting-started?id=conceitos-gerais)\n   - [Componente](/pt-br/getting-started?id=componente)\n   - [Sistemas e Consultas](/pt-br/getting-started?id=sistemas-e-consultas)\n   - [Mundo](/pt-br/getting-started?id=mundo)\n   - [Entidade](/pt-br/getting-started?id=entidade)\n- [Exemplo](/pt-br/getting-started?id=juntando-tudo)\n- [Arquitetura](/pt-br/architecture)\n  - [Componente](/pt-br/architecture?id=componente)\n    - [Qualificadores](/pt-br/architecture?id=qualificadores)\n    - [FSM - Maquinas de Estado Finito](/pt-br/architecture?id=fsm-máquinas-de-estado-finito)\n  - [Entidade](/pt-br/architecture?id=entidade)\n  - [Consulta](/pt-br/architecture?id=consulta)  \n  - [Sistema](/pt-br/architecture?id=sistema)\n    - [Tarefas](/pt-br/architecture?id=tarefas)\n  - [Mundo](/pt-br/architecture?id=mundo)\n- [Tutoriais](/pt-br/tutorial)\n  - [Shoot](/pt-br/tutorial-shoot)\n  - [Pacman](/pt-br/tutorial-pacman)\n  - [Boids](/pt-br/tutorial-boids)\n- [API](/pt-br/api)\n  - [Archetype](/pt-br/api?id=archetype)\n  - [Component](/pt-br/api?id=component)\n  - [Entity](/pt-br/api?id=entity)\n  - [ECS](/pt-br/api?id=ecs)\n  - [Event](/pt-br/api?id=event)\n  - [LoopManager](/pt-br/api?id=loopmanager)\n  - [Query](/pt-br/api?id=query)\n  - [QueryResult](/pt-br/api?id=queryresult)\n  - [System](/pt-br/api?id=system)\n  - [Time](/pt-br/api?id=time)\n  - [World](/pt-br/api?id=world)\n\n"
  },
  {
    "path": "docs/pt-br/api.md",
    "content": "# API\n<div class=\"api-docs\">\n\n\n# ECS\n- `ECS.Query`\n   - _@type_ `QueryClass`\n- `ECS.Archetype`\n   - _@type_ `ArchetypeClass`\n- `ECS.World(systemClasses, frequency, disableAutoUpdate)`\n   - Create a new world instance\n   - _@param_ `systemClasses` `SystemClass[]` _optional_ Array of system classes\n   - _@param_ `frequency` `number` _optional_ Define the frequency that the `process` step will be executed. Default 30\n   - _@param_ `disableAutoUpdate` `bool` _optional_ When `~= false`, the world automatically registers in the `LoopManager`, \n   receiving the `World:Update()` method from it. Default false\n   - _@return_ `World`\n- `ECS.System(step, order, query, updateFn)`\n   - Create new System Class\n   - _@param_ `step` `process|transform|render|task`\n   - _@param_ `order` `number` _optional_ Allows you to set an execution order (for systems that are not `task`). Default 50\n   - _@param_ `query` `Query|QueryBuilder` _optional_ Filters the entities that will be processed by this system\n   - _@param_ `updateFn` `Function(self, Time)` _optional_ A shortcut for creating systems that only have the Update method\n   - _@return_ `SystemClass`\n- `ECS.Component(template)`\n   - Register a new ComponentClass\n   - _@param_ `template` `table|function(table):table|any` \n      - When `table`, this template will be used for creating component instances\n      - When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the \n      component is passed to template function\n      - If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format \n      `{ value = template }`.\n   - _@return_ `ComponentClass`\n- `ECS.SetLoopManager(manager)`\n   - Defines the LoopManager that will be used by the worlds to receive the automatic update\n   - _@param_ `manager` `LoopManager`\n\n# Archetype\n\nAn Archetype is a unique combination of component types. The EntityRepository uses the archetype to group all \nentities that have the same sets of components.\n\nAn entity can change archetype fluidly over its lifespan. For example, when you add or remove components, the archetype \nof the affected entity changes.\n\nAn archetype object is not a container; rather it is an identifier to each unique combination of component types that \nan application has created at run time, either directly or implicitly.\n\nYou can create archetypes directly using `ECS.Archetype.Of(Components[])`. You also implicitly create archetypes \nwhenever you add or remove a component from an entity. An Archetype object is an immutable singleton; creating an \narchetype with the same set of components, either directly or implicitly, results in the same archetype.\n\nThe ECS framework uses archetypes to group entities that have the same structure together. The ECS framework stores \ncomponent data in blocks of memory called chunks. A given chunk stores only entities having the same archetype. You can \nget the Archetype object for a chunk from its Archetype property.\n\nUse `ECS.Archetype.Of(Components[])` to get a Archetype reference.\n\n- `Archetype.EMPTY`\n   - Generic archetype, for entities that do not have components\n   - _@type_ `Archetype`\n- `Archetype.Of(componentClasses)`\n   - Gets the reference to an archetype from the informed components\n   - _@param_ `componentClasses` `ComponentClass[]` Component that define this archetype\n   - _@return_ `Archetype`\n- `Archetype.Version()`\n   - Get the version of archetype definitions\n   - _@return_ `number`\n- `Archetype:Has(componentClass)`\n   - Checks whether this archetype has the informed component\n   - _@param_ `componentClass` `ComponentClass`\n   - _@return_ `bool`\n- `Archetype:With(componentClass)`\n   - Gets the reference to an archetype that has the current components `+` the informed component\n   - _@param_ `componentClass` `ComponentClass`\n   - _@return_ `Archetype`\n- `Archetype:WithAll(componentClasses)`\n   - Gets the reference to an archetype that has the current components `+` the informed components\n   - _@param_ `componentClass` `ComponentClass[]`\n   - _@return_ `Archetype`\n- `Archetype:Without(componentClass)`\n   - Gets the reference to an archetype that has the current components `-` the informed component\n   - _@param_ `componentClass` `ComponentClass`\n   - _@return_ `Archetype`\n- `Archetype:WithoutAll(componentClasses)`\n   - Gets the reference to an archetype that has the current components `-` the informed components\n   - _@param_ `componentClass` `ComponentClass[]`\n   - _@return_ `Archetype`\n\n# Component\n- `ComponentClass.Id`\n   - Identifier of this component\n   - _@type_ `number`\n- `ComponentClass.IsCType`\n   - Indicates that this class is a Component\n   - _@type_ `true`\n- `ComponentClass.SuperClass`\n   - Used internally for Qualifiers, it indicates the base class of this Component (or primary component).\n   - _@type_ `ComponentClass`\n- `ComponentClass.HasQualifier`\n   - Indicates that this Component has qualifiers\n   - _@type_ `bool`\n- `ComponentClass.IsQualifier`\n   - Indicates that this specific class is a Component qualifier.\n   - _@type_ `bool`\n- `ComponentClass.IsFSM`\n   - Indicates that this Component is a [FSM - Finite State Machine][fsm]\n   - _@type_ `bool`\n- `ComponentClass.States`\n   - When set, this Component becomes a [FSM - Finite State Machine][fsm]\n      ```lua\n      local Movement = ECS.Component({ speed = 0 })\n      Movement.States = {\n         Standing = {\"Walking\"},\n         Walking  = \"*\",\n         Running  = {\"Walking\"}\n      }\n      ```\n   - _@type_ `table` _optional_\n- `ComponentClass.StateInitial`\n   - When the component is [FSM][fsm], it allows defining the initial state for new instances.\n   - _@type_ `string` _optional_\n- `ComponentClass.Case`\n   - When the component is [FSM][fsm], it allows the component to handle transactions between states.\n      ```lua\n      Movement.Case = {\n         Standing = function(self, previous)\n            self.speed = 0\n         end,\n         Walking = function(self, previous)\n            self.speed = 5\n         end,\n         Running = function(self, previous)\n            self.speed = 10\n         end\n      }\n      ```\n   - _@type_ `table` _optional_\n- `ComponentClass.Qualifier(qualifier)`\n   - Gets a qualifier for this type of component. If the qualifier does not exist, a new class will be created, \n   otherwise it brings the already registered class qualifier reference with the same name.\n   - _@param_ `qualifier` `string|ComponentClass`\n   - _@return_ `ComponentClass`\n- `ComponentClass.Qualifiers(...)`\n   - Get all qualified class\n   - _@param_ `...` `string|ComponentClass` _optional_ Allows to filter the specific qualifiers\n   - _@return_ `ComponentClass[]`\n- `ComponentClass(value)` | `ComponentClass.New(value)`\n   - Builder, instantiate a new component of this type\n   - _@param_ `value` `any` _optional_  If the value is not a table, it will be converted to the format `{ value = value}`\n   - _@return_ `Component`\n- `ComponentClass:GetType()`\n   - Get this component's class\n   - _@return_ `ComponentClass`\n- `ComponentClass:Is(componentClass)`\n   - Check if this component is of the type informed\n   - _@param_ `componentClass` `ComponentClass|ComponentSuperClass`\n   - _@return_ `bool`\n- `ComponentClass:Primary()`\n   - Get the instance for the primary qualifier of this class\n   - _@return_ `Component|nil`\n- `ComponentClass:Qualified(qualifier)`\n   - Get the instance for the given qualifier of this class\n   - _@param_ `qualifier` `string|ComponentClass`\n   - _@return_ `Component|nil`\n- `ComponentClass:QualifiedAll()`\n   - Get all instances for all qualifiers of that class\n   - _@return_ `Component[]`\n- `ComponentClass:Merge(other)`\n   - Merges data from the other component into the current component. **IMPORTANT!** This method should not be invoked, \n   it is used by the entity to ensure correct retrieval of a component's qualifiers.\n   - _@param_ `other` `Component`\n- `ComponentClass:Detach()`\n   - Unlink this component with the other qualifiers. **IMPORTANT!** This method should not be invoked, it is used by \n   the entity to ensure correct retrieval of a component's qualifiers.\n- `ComponentClass.In(...)`\n   - When the component is [FSM][fsm], creates a clause used to filter repository entities in a Query or QueryResult. \n      ```lua\n      ECS.Query.All(Movement.In(\"Walking\", \"Running\"))\n      ```\n   - _@param_ `...` `string[]`\n   - _@return_ `Clause`\n- `ComponentClass:SetState(newState)`\n   - When the component is [FSM][fsm], defines the current state\n   - _@param_ `newState` `string`\n- `ComponentClass:GetState()`\n   - When the component is [FSM][fsm], get the current state\n   - _@return_ `string`\n- `ComponentClass:GetPrevState()`\n   - When the component is [FSM][fsm], get the previous state\n   - _@return_ `string|nil`\n- `ComponentClass:GetStateTime()`\n   - When the component is [FSM][fsm], gets the time it changed to the current state. Whenever the state is changed, the \n   instant is persisted internally using `os.clock()`\n   - _@return_ `number`\n\n# Entity\n- `Entity.id`\n   - Identifier of this entity\n   - _@type_ `number`\n- `Entity.isAlive`\n   - The entity is created in _DEAD_ state (`entity.isAlive == false`) and will only be visible for queries after the \n   cleaning step _(`OnRemove`,`OnEnter`,`OnExit`)_ by the world\n   - _@type_ `bool`\n- `Entity.archetype`\n   - The entity archetype\n   - _@type_ `Archetype`\n- `Entity.New(onChange, components)`\n   - Creates an entity having components of the specified types.\n   - _@param_ `onChange` `Event`\n   - _@param_ `components` `Component[]` _optional_\n   - _@return_ `Entity`\n- `Entity:Get(componentClass)` | `entity[componentClass]`\n   - Gets an entity component\n   - _@param_ `componentClass` `ComponentClass` \n   - _@return_ `Component`\n- `Entity:Get(...)`\n   - Get multiple entity components at once\n      ```lua\n      local comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n      ```\n   - _@param_ `..` `ComponentClass[]` \n   - _@return_ `Component ...`\n- `Entity:Set(componentClass, value)` | `entity[componentClass] = value`\n   - Sets the value of a component\n   - _@param_ `componentClass` `ComponentClass` \n   - _@param_ `value` `any|nil` When nil, unset the component.\n- `Entity:Set(...)`\n   - Arrow one or more instances of a component\n   - _@param_ `...` `Component`\n- `Entity:Unset(componentClass|Component, ...)` | `entity[componentClass] = nil`\n   - Remove one or more components from the entity\n   - _@param_ `...` `componentClass|Component` \n\n# LoopManager\nIn order for the world's systems to receive an update, the `World:Update(step, now)` method must be invoked on each \nframe. To automate this process, **ECS Lua** provides a functionality so that, at the time of instantiation of a \nnew world, it registers to receive the update automatically.\n\n```lua\nlocal MyLoopManager = {\n   Register = function(world)\n      local beforePhysics = MyGameEngine.BeforePhysics(function()\n         world:Update(\"process\", os.clock())\n      end)\n\n      local afterPhysics = MyGameEngine.AfterPhysics(function()\n         world:Update(\"transform\", os.clock())\n      end)\n\n      local beforeRender\n      if (not MyGameEngine.IsServer()) then\n         beforeRender = MyGameEngine.BeforeRender(function()\n            world:Update(\"render\", os.clock())\n         end)\n      end\n\n      return function()\n         beforePhysics:Disconnect()\n         afterPhysics:Disconnect()\n         if beforeRender then\n            beforeRender:Disconnect()\n         end\n      end\n   end\n}\n\nECS.SetLoopManager(MyLoopManager)\n```\n\n- `LoopManager.Register(world)`\n   - Allows the world to register to be updated.\n   - _@param_ `world` `World` \n   - _@return_ `function` The world will invoke when destroyed\n\n# Query\n- `Query(all, any, none)` | `Query.New(all, any, none)`\n   - Create a new Query used to filter entities in the world. It makes use of local and global cache in order to \n   decrease the validation time (avoids looping in runtime of systems)\n   - _@param_ `all` `Array<ComponentClass|Clause>` _optional_\n   - _@param_ `any` `Array<ComponentClass|Clause>` _optional_\n   - _@param_ `none` `Array<ComponentClass|Clause>` _optional_\n   - _@return_ `Query`\n- `Query.All(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `Query.Any(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `Query.None(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `Query.Filter(filter)`\n   - Create custom filters that can be used in Queries. Its execution is delayed, invoked only in `QueryResult` methods.\n   The result of executing the clause depends on how it was used in the query.\n   Ex. If used in `Query.All()` the result is the inverse of using the same clause in `Query.None()`\n      ```lua\n      local Player = ECS.Component({ health = 100 })\n\n      local HealthPlayerFilter = ECS.Query.Filter(function(entity, config)\n         local player = entity[Player]\n         return player.health >= config.minHealth and player.health <= config.maxHealth\n      end)\n\n      local healthyClause = HealthPlayerFilter({\n         minHealth = 80,\n         maxHealth = 100,\n      })\n\n      local healthyQuery = ECS.Query.All(Player, healthyClause)\n      world:Exec(healthyQuery):ForEach(function(entity)\n         -- this player is very healthy\n      end)\n\n      local notHealthyQuery = ECS.Query.All(Player).None(healthyClause)\n      world:Exec(healthyQuery):ForEach(function(entity)\n         -- this player is NOT very healthy\n      end)\n\n      local dyingClause = HealthPlayerClause({\n         minHealth = 1,\n         maxHealth = 20,\n      })\n\n      local dyingQuery = ECS.Query.All(Player, dyingClause)\n      world:Exec(dyingQuery):ForEach(function(entity)\n         -- this player is about to die\n      end)\n\n      local notDyingQuery = ECS.Query.All(Player).None(dyingClause)\n      world:Exec(notDyingQuery):ForEach(function(entity)\n         -- this player is NOT about to die\n      end)\n      ```\n   - _@param_ `filter` `function(entity, config) -> bool`\n   - _@return_ `function(config) -> Clause`\n- `Query:Result(chunks)`\n   - Generate a `QueryResult` with the chunks entered and the clauses of the current query\n   - _@param_ `chunks` `Array<{ [Entity] = true }>` \n   - _@return_ `QueryResult`\n- `Query:Match(archetype)`\n   - Checks if the entered archetype is valid by the query definition\n   - _@param_ `archetype` `Archetype` \n   - _@return_ `bool`\n\n# QueryBuilder\n- `QueryBuilder.isQueryBuilder`\n   - Indicates that this is an instance of a QueryBuilder\n   - _@type_ `true`\n- `QueryBuilder.All(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `QueryBuilder.Any(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `QueryBuilder.None(...)`\n   - _@param_ `...` `Array<ComponentClass|Clause>` \n   - _@return_ `QueryBuilder`\n- `QueryBuilder.Build()`\n   - _@return_ `Query`\n\n# QueryResult\nThe result of a Query that was executed on an EntityStorage.\n\nQueryResult provides several methods to facilitate the filtering of entities resulting from the execution of the query.\n\n- **Intermediate Operations**\n   -  Intermediate operations return a new QueryResult. They are always lazy; executing an intermediate operation such as \n   `QueryResult:Filter()` does not actually perform any filtering, but instead creates a new QueryResult that, when traversed, \n   contains the elements of the initial QueryResult that match the given predicate. Traversal of the pipeline source \n   does not begin until the terminal operation of the pipeline is executed.\n- **Terminal Operations**\n   - Terminal operations, such as `QueryResult:ForEach` or `QueryResult.AllMatch`, may traverse the QueryResult to produce a \n   result or a side-effect.\n\n- `QueryResult.New(chunks, clauses)`\n   - Build a new QueryResult\n   - _@param_ `chunks` `Array<{ [Entity] = true }>`\n   - _@param_ `clauses` `Clause[]` _optional_\n   - _@return_ `QueryResult`\n- `QueryResult:With(operation, param)`\n   - Returns a QueryResult consisting of the elements of this QueryResult with a new pipeline operation\n   - _@param_ `operation` `function(param, value, count) -> newValue, acceptItem, continuesLoop`\n   - _@param_ `param` `any`\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:Filter(predicate)`\n   - Returns a QueryResult consisting of the elements of this QueryResult that match the given predicate.\n   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to each element to determine if it should be included\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:Map(mapper)`\n   - Returns a QueryResult consisting of the results of applying the given function to the elements of this QueryResult.\n   - _@param_ `mapper` `function(value) -> newValue` a function to apply to each element\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:Limit(maxSize)`\n   - Returns a QueryResult consisting of the elements of this QueryResult, truncated to be no longer than maxSize in length.\n   - _@param_ `maxSize` `number`\n   - _@return_ `QueryResult` the new QueryResult\n- `QueryResult:AnyMatch(predicate)`\n   - Returns whether any elements of this result match the provided predicate.\n   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to elements of this result\n   - _@return_ `true` if any elements of the result match the provided predicate, otherwise `false`\n- `QueryResult:AllMatch(predicate)`\n   - Returns whether all elements of this result match the provided predicate.\n   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to elements of this result\n   - _@return_ `true` if either all elements of the result match the provided predicate or the result is empty, otherwise `false`\n- `QueryResult:FindAny()`\n   - Returns some element of the result, or nil if the result is empty.\n\n   This is a short-circuiting terminal operation.\n\n   The behavior of this operation is explicitly nondeterministic; it is free to select any element in the result. \n   \n   Multiple invocations on the same result may not return the same value.\n   - _@return_ `any`\n- `QueryResult:ForEach(action)`\n   - Performs an action for each element of this QueryResult.\n\n   This is a terminal operation.\n\n   The behavior of this operation is explicitly nondeterministic. This operation does not guarantee to respect the \n   encounter order of the QueryResult.\n   - _@param_ `action` `function(value, count) -> bool` A action to perform on the elements, breaks execution case returns true\n- `QueryResult:ToArray()`\n   - Returns an array containing the elements of this QueryResult.\n   - _@return_ `Array<any>`\n- `QueryResult:Iterator()`\n   - Returns an Iterator, to use in for loop\n      ```lua\n      for count, entity in result:Iterator() do\n         print(entity.id)\n         break\n      end\n      ```  \n   - _@return_ `Iterator`\n\n# System\n\n- `System.Step`\n   - Step that this system will run\n   - _@type_ `string` `process|transform|render|task`\n- `System.Order`\n   - For systems that are not `task`, execution order\n   - _@type_ `number`\n- `System.Query`\n   - Filters the entities that will be processed by this system\n   - _@type_ `number`\n- `System.After`\n   - When the system is a task, it allows you to define that this system should run AFTER other specific systems.\n      ```lua\n      local log = {}\n\n      local Task_A = System.Create('task', function()\n         -- In this example, TASK_A takes time to execute, delaying its execution\n         local i = 0\n         while i <= 4000 do\n            i = i + 1\n            if i%1000 == 0 then\n               coroutine.yield()\n            end\n         end\n         \n         table.insert(log, 'A')\n      end)\n\n      local Task_B = System.Create('task', function()\n         table.insert(log, 'B')\n      end)\n\n      local Task_C = System.Create('task', function()\n         table.insert(log, 'C')\n      end)\n\n      local Task_D = System.Create('task', function()\n         table.insert(log, 'D')\n      end)\n\n      local Task_E = System.Create('task', function()\n         table.insert(log, 'E')\n      end)\n\n      local Task_F = System.Create('task', function()\n         table.insert(log, 'F')\n      end)\n\n      local Task_G = System.Create('task', function()\n         table.insert(log, 'G')\n      end)\n      \n      local Task_H = System.Create('task', function(self)\n         table.insert(log, 'H')\n      end)\n\n      --[[         \n         A<-------C<---+-----F<----+\n                  |    |     |     |\n             +----+    E<----+     H\n             |         |           |\n         B<--+----D<---+------G<---+\n\n         A - has no dependency\n         B - has no dependency\n         C - Depends on A,B\n         D - Depends on B\n         E - Depends on A,B,C,D\n         F - Depends on A,B,C,D,E\n         G - Depends on B,D\n         H - Depends on A,B,C,D,E,F,G\n\n         Completion order will be B,D,G,A,C,E,F,H      \n\n         > In this example, TASK_A takes time to execute, delaying its execution\n      ]]\n      Task_A.Before = {Task_C}\n      Task_B.Before = {Task_D}\n      Task_C.After = {Task_B}\n      Task_D.Before = {Task_G}\n      Task_F.After = {Task_E}\n      Task_E.After = {Task_D, Task_C}\n      Task_C.Before = {Task_F}\n      Task_H.After = {Task_F, Task_G}\n      ```\n   - _@type_ `SystemClass[]`\n- `System.Before`\n   - When the system is a task, it allows you to define that this system should run BEFORE other specific systems.\n   - _@see_ `System.After`\n   - _@type_ `SystemClass[]`\n- `System.version`\n   - System Version (GSV).\n   - _@see_ `World.version`\n   - _@type_ `Number`\n- `System._world`\n   - _@type_ `World`\n- `System._config`\n   - _@type_ `table`\n- `System.New(world, config)`\n   - Create an instance of this system\n   - _@param_ `world` `World`\n   - _@param_ `config` `table`\n   - _@return_ `System`\n- `System:GetType()`\n   - Get this system class\n   - _@return_ `SystemClass`\n- `System:Result(query)`\n   - Run a query in the world. A shortcut to `self._world:Exec(query)`\n   - _@param_ `query` `Query|QueryBuilder` _optional_ If nil, use default query\n   - _@return_ `QueryResult`\n- `System:Destroy()`\n   - Destroy this instance\n- `System:OnDestroy()`\n   - Allows you to perform some processing or cleaning when the instance is being destroyed\n- `System:ShouldUpdate(Time)`\n   - Invoked before 'Update', allows you to control the execution of the update\n   - _@param_ `Time` `Time`\n   - _@return_ `bool` If true, the Update method will be invoked.\n- `System:Update(Time)`\n   - Run the system's main method\n   - _@param_ `Time` `Time`\n- `System:OnRemove(Time, entity)`\n   - When it is a `QuerySystem`, it allows to be informed when an entity with the characteristics of the query is \n   removed from the world. This method is performed in the step cleanup process.\n   - _@param_ `Time` `Time`\n   - _@param_ `entity` `Entity`\n- `System:OnExit(Time, entity)`\n   - When it is a `QuerySystem`, it allows to be informed when an entity has lost the characteristics of that query \n   (has suffered an archetype change and the current query no longer applies). This method is performed in the step \n   cleanup process.\n   - _@param_ `Time` `Time`\n   - _@param_ `entity` `Entity`\n- `System:OnEnter(Time, entity)`\n   - When it is a QuerySystem, it allows to be informed when an entity received the characteristics expected by this \n   query (it suffered an archetype change and the current query now applies). This method is performed in the step \n   cleanup process.\n   - _@param_ `Time` `Time`\n   - _@param_ `entity` `Entity`\n\n\n# Time\nSingleton, reference to the world's global processing time.\n\n- `Time.Now`\n   - World Runtime\n   - _@type_ `number`\n- `Time.NowReal`\n   - Real time, received in `World:Update(step, now)` method\n   - _@type_ `number`\n- `Time.Frame`\n   - The time at the beginning of this frame (process). The world receives the current time at the beginning of each \n   frame, with the value increasing per frame.\n   - _@type_ `number`\n- `Time.FrameReal`\n   - The REAL time at the beginning of this frame (`World:Update(step, now)`).\n   - _@type_ `number`\n- `Time.Process`\n   - The time the latest process step has started.\n   - _@type_ `number`\n- `Time.Delta`\n   - The completion time in seconds since the last frame.\n   - _@type_ `number`\n- `Time.DeltaFixed`\n   - Based on world update frequency (`process` step). \n      ```lua\n      DeltaFixed = 1000/frequency/1000\n      ```\n   - _@see_ `World:SetFrequency()`\n   - _@type_ `number`\n- `Time.Interpolation`\n   - The proportion of time since the previous transform relative to processDeltaTime. Used to do interpolation during \n   the rendering step. Allows the `process` step to run at low frequency _(Ex. 30hz)_ and `render` at the maximum rate \n   of the player's device _(Ex. 60hz)_\n   - _@type_ `number`\n\n# World\n- `World.version`\n   - Global System Version (GSV).\n\n   Before executing the `Update()` method of each system, the world version is incremented, so at this point, the \n   world version will always be higher than the running system version.\n\n   Whenever an entity archetype is changed (received or lost component) the entity's version is updated to the current \n   version of the world.\n\n   After executing the System Update method, the version of this system is updated to the current world version.\n\n   This mechanism allows a system to know if an entity has been modified after the last execution of this same system, \n   as the entity's version is superior to the version of the last system execution. Thus, a system can contain logic if \n   it only operates on \"dirty\" entities, which have undergone changes. The code for this validation on a system is: \n      - `local isDirty = entity.version > self.version`\n   - _@type_ `number`\n- `World.maxTasksExecTime`\n   - Allows you to define the maximum time that the `JobSystem` can operate in each frame. The default value is \n   `0.011666666666666665` = `((1000/60/1000)*0.7)`\n      - A game that runs at 30fps has 0.0333 seconds to do all the processing for each frame, including rendering\n         - 30FPS = `(1000/30/1000)` = `0.03333333333333333`\n      - A game that runs at 60fps has 0.0166 seconds to do all the processing for each frame, including rendering\n         - 60FPS = `(1000/60/1000)` = `0.016666666666666666`\n   - _@type_ `number` Default `0.011666666666666665`\n- `World:SetFrequency(frequency)`\n   - Define the frequency that the `process` step will be executed\n   - _@param_ `frequency` `number` _optional_ Default 30\n- `World:GetFrequency()`\n   - Get the frequency of execution of the `process` step\n   - _@return_ `number`\n- `World:AddSystem(systemClass, config)`\n   - Add a new system to the world. Only one instance per type is accepted. If there is already another instance of this \n   system in the world, any new invocation of this method will be ignored.\n   - _@param_ `systemClass` `SystemClass` The system to be added in the world\n   - _@param_ `config` `table` _optional_ System instance configuration\n- `World:Entity(...)`\n   - Create a new entity. The entity is created in _DEAD_ state (`entity.isAlive == false`) and will only be visible for \n   queries after the cleaning step _(`OnRemove`,`OnEnter`,`OnExit`)_ of the current step\n   - _@param_ `...` `Component[]` _optional_ Instance of the components that this entity will have\n   - _@return_ `Entity`\n- `World:Remove(entity)`\n   - Performs immediate removal of an entity.\n\n   If the entity was created in this step and the cleanup process has not happened yet (therefore the entity is inactive, \n   `entity.isAlive == false`), the `OnRemove` event will never be fired.\n\n   If the entity is alive (`entity.isAlive == true`), even though it is removed immediately, the `OnRemove` event will \n   be fired at the end of the current step.\n   - _@param_ `entity` `Entity`\n- `World:Exec(query)`\n   - Run a query in this world\n   - _@param_ `query` `Query|QueryBuilder`\n   - _@return_ `QueryResult`\n- `World:Update(step, now)`\n   - Perform world update. When registered, the `LoopManager` will invoke World Update for each step in the sequence.\n      - `process` At the beginning of each frame\n      - `transform` After the game engine's physics engine runs\n      - `render` Before rendering the current frame\n   - _@param_ `step` `\"process\"|\"transform\"|\"render\"`\n   - _@param_ `now` `number` Usually os.clock()\n- `World:Destroy()`\n   - Destroy this instance, removing all entities, systems and events\n\n</div>\n\n[fsm]:https://en.wikipedia.org/wiki/Finite-state_machine\n"
  },
  {
    "path": "docs/pt-br/architecture.md",
    "content": "# Arquitetura\n\nEm Engenharia de Software, ECS é o acrônimo de Entity Component System (em português: Sistema de Componente e Entidade), \né um padrão de arquitetura de software usado principalmente no desenvolvimento de jogos eletrônicos. Um ECS segue o \nprincípio da \"composição ao invés de herança\" que permite uma flexibilidade maior na definição de entidades, onde cada \nobjeto em uma cena de um jogo é uma entidade (por exemplo inimigos, projéteis, veículos, etc.). Cada entidade consiste \nde um ou mais componentes que adicionam comportamento ou funcionalidade. Portanto, o comportamento de uma entidade pode \nser alterado durante o tempo de execução simplesmente adicionando ou removendo componentes. Isso elimina problemas de \nambiguidade com que sofrem as hierarquias de herança profunda e vasta, que são difíceis de entender, manter e estender.\n\nPara mais detalhes:\n- [Perguntas Freqüentes sobre ECS](https://github.com/SanderMertens/ecs-faq)\n- [Entity Systems Wiki](http://entity-systems.wikidot.com/)\n- [Evolua sua hierarquia](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/)\n- [ECS na Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)\n- [ECS no Elixir](https://yos.io/2016/09/17/entity-component-systems/)\n- [2017 GDC - Overwatch Gameplay Architecture e Netcode](https://www.youtube.com/watch?v=W3aieHjyNvw&ab_channel=GDC)\n\n\n## Componente\n\nRepresentam as diferentes características de uma entidade, como posição, velocidade, geometria, física e pontos de vida. \nOs componentes armazenam apenas dados brutos para um aspecto do objeto e como ele interage com o mundo. Em outras \npalavras, o componente rotula a entidade como tendo este aspecto particular.\n\nNo **ECS Lua**, a criacao de um componente é feito por meio do método `ECS.Component(template)`.\n\nO parametro `template` pode ser de qualquer tipo, onde: \n- Quando `table`, este template sera usado para a criacao de instancias de componentes\n   ```lua\n   local Componente = ECS.Component({\n      x = 0, y = 0, z = 0 \n   })\n\n   local comp = Componente({ x = 33, z = 80 })\n   print(comp.x, comp.y, comp.z) -- > 33, 0, 80\n\n   -- é o mesmo que \n   local comp = Componente.New({ x = 33, z = 80 })\n   print(comp.x, comp.y, comp.z) -- > 33, 0, 80\n   ```\n- Quando for uma `function`, essa sera invocada na instanciacao de um novo componente. O parametro de criacao do \ncomponente é passado para a funcao de template\n   ```lua\n   local Componente = ECS.Component(function(param)\n      return {\n         x = param.x or 1,\n         y = param.y or 1,\n         z = param.z or 1\n      }\n   end)\n\n   local comp = Componente({ x = 33, z = 80 })\n   print(comp.x, comp.y, comp.z) -- > 33, 1, 80\n   ```\n- Caso o tipo do template seja diferente de `table` e `function`, o **ECS Lua** ira gerar um template no formato `{ value = template }`.\n   ```lua\n   local Componente = ECS.Component(55)\n\n   local comp1 = Componente()\n   print(comp1.value) -- > 55\n\n   local comp2 = Componente({ value = 80 })\n   print(comp2.value) -- > 80\n\n   local comp3 = Componente(\"XPTO\")\n   print(comp3.value) -- > \"XPTO\"\n   ```\n\n### Métodos\n\nNo **ECS Lua**, os componentes são classes e podem portanto, possuir métodos auxiliares.\n\n> IMPORTANTE! Evite criar métodos que modifiquem os dados da instancia do componente diretamente ou que possuam regras de \nnegocio, o ideal é que essas lógicas fiquem dentro dos sistemas, que são, por definição, os responsáveis por alterar\nos dados das entidades e seus componentes.\n\n```lua\nlocal Pessoa = ECS.Component({\n   nome = \"\",\n   sobrenome = \"\",\n   nascimento = 0\n})\n\nfunction Pessoa:NomeCompleto()\n   return self.nome..\" \"..self.sobrenome\nend\n\nfunction Pessoa:Idade()\n   return tonumber(os.date(\"%Y\", os.time())) - self.nascimento\nend\n\n\nlocal pessoa = Componente({ nome = \"Joao\", sobrenome = \"Silva\", nascimento = 2000 })\n\nprint(pessoa:NomeCompleto()) -- Joao Silva\nprint(pessoa:Idade()) -- 21\n```\n\n### Qualificadores\n\nNas implementações \"ECS puras\" existe a premissa de que um componente só pode ser adicionado uma única vez em uma entidade. \nNa grande maioria dos cenários, isso é verdade. Como por exemplo, você náo deseja que a sua entidade possua duas \nposições, não faz sentido! Portanto, sua entidade só irá possuir um componente do tipo Posição. \n\nMas, as vezes voce irá construi alguma funcionalidade que precisa que sua entidade possua esse comportamento, possuir \nmais de um componente do mesmo **TIPO**. Quando o framework não dá suporte a esse tipo de implementação, voce acaba \nfazendo várias [gambiarras](https://pt.wikipedia.org/wiki/Gambiarra) para contornar o problema.\n\nO **ECS Lua** implementa o mecanismo de **Qualificadores** para que voce possa representar uma categoria de componentes.\n\nPara ilustrar o uso, vamos pensar no seguinte cenário: Eu quero adicionar no meu jogo um sistema de \n[Buff](https://en.wikipedia.org/wiki/Game_balance#Buff) para que meu personagem possa receber pontos extras de vida \nem situações específicas. Nós queremos ter a liberdade de aumentar ou diminuir a quantidade de buff de acordo com a \nregiao do mapa em que o jogador está.\n\nOlhando para o cenario acima, poderiamos criar, em um primeiro momento, a solucao abaixo. Um componente `HealthBuff` \npara registrar a quantidade de vida extra e dois sistemas. O primeiro `MapRegionSystem` decide quantos pontos de vida \nadicional o jogador tera para cada regiao, já o segundo `HealthSystem` representa alguma funcionalidade do sistema que \nprecisa obter o total de vida do jogador em certo momento.\n\n```lua\n-- components\nlocal Player = ECS.Component({ health = 100, region = \"easy\", healthTotal = 100 })\nlocal HealthBuff = ECS.Component({ value = 10 })\n\n-- systems\nlocal MapRegionSystem = System(\"process\", 1, Query.All(Player))\n\nfunction MapRegionSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]      \n      \n      if player.region == \"easy\" then\n         entity[HealthBuff] = nil -- remove o buff\n      else\n         local buff = entity[HealthBuff]\n         if buff == nil then\n            buff = HealthBuff(0)\n            entity:Set(buff)\n         end\n\n         if player.region == \"hard\" then\n            buff.value = 15\n         elseif player.region == \"hell\" then\n            buff.value = 40\n         end  \n      end    \n   end\nend\n\nlocal HealthSystem = System(\"process\", 2, Query.All(Player).Any(HealthBuff)) \n\nfunction HealthSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]\n\n      local buff = entity[HealthBuff]\n      if buff then \n         player.healthTotal = player.health + buff.value\n      else\n         player.healthTotal = player.health\n      end\n   end\nend\n```\n\nAté aqui tranquilo. Porém, imagine agora que meu jogador possa receber **VÁRIOS** buffers. \n   - Ele pode receber um buffer pelo personagem que esta usando;\n   - mais um buff quando desbloquear um item e;\n   - pode também comprar buff na loja do jogo.\n\nNeste novo cenário a nossa solução nao atende, pois o código do sistema `MapRegionSystem` não tem a informacao sobre os \noutros fatores, e para atender, deverá passar a conhecer ou gerenciar vários estados possíveis para decidir qual é a \nquantidade de vida que o jogador irá receber por estar em uma regiao específica. Os outros sistemas do jogo também \nprecisaram conhecer a regiao para decir quanto de buffer pode somar. Em uma solução \"ECS pura\", nós vamos comecar a: \n\n1. compartilhar estado entre sistemas\n1. criar \"Componentes TAGs\" para facilitar o gerenciamento desse estado distribuido, \n1. inflar os componentes com um atributo para cada tipo de sistema. \n\nEm um primeiro momento isso nao parece ser problema, mas com o tempo, vários sistemas serão executados de forma \ndesnecessária (apenas para fazer um if e nao processar aquela entidade). Estes sistemas passaram a ter \nresponsabilidades extras, aumentando a complexidade do codigo, dificultando a manutencao e facilitando o \naparecimento de bugs.\n\nNo **ECS Lua**  nós resolvemos este tipo de problema criando qualificadores, por meio do método estático \n`ComponentClass.Qualifier(qualifier)`. Ele aceita uma string como parametro e retorna a referencia para uma classe \nespecializada do nosso componente. Essa classe gerada mantém uma ligação forte com a classe base, permitindo a aplicacao \nde filtros de consulta mais complexas.\n\nVamos alterar o nosso exemplo fazendo uso de qualificadores. \n\n```lua\n-- components\nlocal Player = ECS.Component({ health = 100, region = \"easy\", healthTotal = 100 })\nlocal HealthBuff = ECS.Component({ value = 10 })\nlocal HealthBuffItem = HealthBuff.Qualifier(\"Item\")\nlocal HealthBuffMapRegion = HealthBuff.Qualifier(\"Region\")\nlocal Item = ECS.Component({ rarity = 0 })\n\n-- systems\nlocal PlayerItemSystem = System(\"process\", 1, Query.All(Player, Item))\n\nfunction PlayerItemSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local item = entity[Item]\n      local player = entity[Player]\n      \n      if item.rarity == \"legendary\" then\n         entity[HealthBuffItem] = 15 -- o mesmo que entity:Set(HealthBuffItem.New(15))\n      else\n         entity[HealthBuffItem] = nil \n      end\n   end\nend\n\nlocal MapRegionSystem = System(\"process\", 1, Query.All(Player))\n\nfunction MapRegionSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]\n      \n      if player.region == \"easy\" then\n         entity[HealthBuffMapRegion] = nil\n      else\n         local buff = entity[HealthBuffMapRegion]\n         if buff == nil then\n            buff = HealthBuffMapRegion(0)\n            entity:Set(buff)\n         end\n\n         if player.region == \"hard\" then\n            buff.value = 15\n         elseif player.Region == \"hell\" then\n            buff.value = 40\n         end      \n      end\n   end\nend\n\nlocal HealthSystem = System(\"process\", 2, Query.All(Player).Any(HealthBuff))\n\nfunction HealthSystem:Update(Time)\n   for i, entity in self:Result():Iterator() do\n      local player = entity[Player]\n\n      local healthTotal = player.health\n\n      local buffers = entity:GetAll(HealthBuff)\n      for i,buff in ipairs(buffers) do\n         healthTotal = healthTotal + buff.value\n      end\n\n      player.healthTotal = player.health\n   end\nend\n```\n\nPronto, nessa nova implementacao, o sistema `MapRegionSystem` só preocupa-se com o qualificador `HealthBuffMapRegion`, \nenquanto que o sistema `PlayerItemSystem` gerencia apenas o qualificador `HealthBuffItem`. Nos podemos agora criar \nsistemas especializados em qualificadores e gerenciar apenas este atributo da entidade. Já o `HealthSystem` obtém e \nprocessa todas as entidades que possuam qualquer qualificador do componente `HealthBuff`.\n\n[Verifique na API](/pt-br/api?id=component) outros métodos que podem ser úteis ao trabalhar com qualificadores.\n\n### FSM - Máquinas de Estado Finito\n\n__UNDER_CONSTRUCTION__\n\n\n```lua\nlocal Movement = Component.Create({ Speed = 0 })\n\n--  [Standing] <--> [Walking] <--> [Running]\nMovement.States = {\n   Standing = {\"Walking\"},\n   Walking  = \"*\",\n   Running  = {\"Walking\"}\n}\n\nMovement.StateInitial = \"Standing\"\n\nMovement.Case = {\n   Standing = function(self, previous)\n      print(\"Transition from \"..previous..\" to Standing\")\n   end,\n   Walking = function(self, previous)\n      print(\"Transition from \"..previous..\" to Walking\")\n   end,\n   Running = function(self, previous)\n      print(\"Transition from \"..previous..\" to Running\")\n   end\n}\n\n\nlocal movement = Movement()\n\nmovement:SetState(\"Walking\")\nmovement:SetState(\"Running\")\n\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\nmovement:SetState(\"Standing\") -- invalid, Running -> Walking|Running\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\nmovement:SetState(nil)\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\nmovement:SetState(\"INVALID_STATE\")\nprint(movement:GetState()) -- Running\nprint(movement:GetPrevState()) -- Walking\n\n\n-- query\nlocal queryStanding = Query.All(Movement.In(\"Standing\"))\nlocal queryInMovement = Query.Any(Movement.In(\"Walking\", \"Running\"))\n\n\n-- qualifier\nlocal MovementB = Movement.Qualifier(\"Sub\")\n -- ignored, \"States\", \"StateInitial\" and \"Case\" only work in primary class\nMovementB.States = { Standing = {\"Walking\"} }\n\n```\n\n## Entidade\n\n__UNDER_CONSTRUCTION__\n\n```lua\n--[[\n   [GET]\n   01) comp1 = entity[CompType1]\n   02) comp1 = entity:Get(CompType1)\n   03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n]]\n\n--[[\n   [SET]\n   01) entity[CompType1] = nil\n   02) entity[CompType1] = value\n   03) entity:Set(CompType1, nil)   \n   04) entity:Set(CompType1, value)\n   05) entity:Set(comp1)\n   06) entity:Set(comp1, comp2, ...)\n]]\n\n--[[\n   [UNSET]\n   01) enity:Unset(comp1)\n   02) entity[CompType1] = nil\n   03) enity:Unset(CompType1)\n   04) enity:Unset(comp1, comp1, ...)\n   05) enity:Unset(CompType1, CompType2, ...)\n]]\n\n--[[\n   [Utils]\n   01) comps = entity:GetAll()\n   01) qualifiers = entity:GetAll(PrimaryClass)\n]]\n```\n\n## Consulta\n\n__UNDER_CONSTRUCTION__\n\n## Sistema\n\n__UNDER_CONSTRUCTION__\n\n## Tarefas\n\n__UNDER_CONSTRUCTION__\n\n## Mundo\n\n__UNDER_CONSTRUCTION__\n\n\n\n\n\n"
  },
  {
    "path": "docs/pt-br/faq.md",
    "content": "# Perguntas Frequentes\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/pt-br/getting-started.md",
    "content": "# Instalação\n\nO **ECS Lua** nao possui dependencias externas, portanto, basta fazer o download da ultima versao disponível na \n[página de releases](https://github.com/nidorx/ecs-lua/releases) do projeto.\n\nExistem 3 opcoes de uso do **ECS Lua** \n\n1. **ECS.lua** Versao minificada em um único arquivo \n1. **ECS_concat.lua** Versao concatenada com os comentarios originais, que pode ser usada para depuracao durante o \ndesenvolvimento \n1. **ECS.zip** Versao com os arquivos do diretorio `src`. \n   > Importante! Todos os arquivos fazem o `require` para as dependencias que estão no mesmo diretorio, caso esteja \n   usando em um projeto Lua, registrar no `package.path`.\n\n   > Estes `require` nao funcionam no Roblox Luau, devido ao formato de importacao que o Roblox usa. \n\nApós importar o **ECS Lua**, ele está pronto para ser usado. O **ECS Lua** registra a variavel global `_G.ECS` para \nfacilitar o uso, portanto, voce pode usar o motor nas duas formas `local ECS = require(\"ECS\")` \n(no Roblox `local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))`) ou simplesmente `_G.ECS`.\n\n\n## LoopManager\n\nPara que os sistemas do mundo recebam atualizacao, é necessário que o método `World:Update(step, now)` seja invocado em \ncada frame. Para automatizar este processo, o **ECS** disponibiliza uma funcionalidade para que, no momento da \ninstanciacao de um novo mundo, este possa registrar-se para receber o update de forma automatica. \n\nA implementacao desse método é muito simples e mais detalhes pode ser visto na seção \n[Arquitetura - Ciclo De Vida](/pt-br/architecture?id=world).\n\n> Se você utiliza Roblox nao precisa preocupar-se, o **ECS Lua** já tem uma implementacao padrão quando é executado no \nRoblox, mais detalhes abaixo.\n\n\n## Roblox\n\nVocê pode fazer a instalação diretamente do Roblox Studio, através da busca na caixa de ferramentas por `ECS-lua`, esta \né a versão [minificada do motor](https://www.roblox.com/library/5887881675/ecs-lua). Ao usar o **ECS Lua** no Roblox, \na engine já identifica e registra automaticamente um `LoopManager`, nao sendo portanto necessario nenhum passo adicional.\n\n# Conceitos Gerais\n\nAlguns termos comuns nos motores ECS são:\n- [Entidades](/pt-br/architecture?id=entidade): Um objeto com um ID exclusivo que pode ter vários componentes anexados a ele.\n- [Componentes](/pt-br/architecture?id=componente): Diferentes caracteristicas de uma entidade. ex: geometria, física, pontos de vida. Os dados são armazenados apenas em componentes.\n- [Sistemas](/pt-br/architecture?id=sistema): Faz o trabalho real, aplica as regras do jogo, processando entidades e modificando seus componentes.\n- [Consultas](/pt-br/architecture?id=consulta): Usado pelos sistemas para determinar em quais entidades eles estão interessados, com base nos componentes que as entidades possuem.\n- [Mundo](/pt-br/architecture?id=mundo): Um contêiner para entidades, componentes, sistemas e consultas.\n\n<div align=center>\n\n![Arquitetura](../assets/diagram-1-pt-br.png)\n\n</div>\n\nO fluxo de trabalho normal ao construir um programa baseado em ECS:\n- Crie os `Componentes` que moldam os dados que você precisa usar em seu jogo/aplicativo.\n- Crie os `Sistemas` que usarão esses `Componentes` para ler e transformar os dados das entidades.\n- Crie `Entidades` e anexe `Componentes` a elas.\n- Execute todos os sistemas a cada frame, realize `Consultas` no `Mundo` para decidir quais entidades serão modificadas.\n\n## Componente\n\nOs componentes são objetos que contêm dados. No **ECS Lua**, basta invocar o método `ECS.Component(template)` para \ndefinir uma `Classe` de um componente. \n\nO parametro `template` pode ser de qualquer tipo, onde: \n- Quando `table`, este template sera usado para a criacao de instancias de componentes;\n- Quando for uma `function`, essa sera invocada na instanciacao. \n- Caso o tipo do template seja diferente, o **ECS Lua** ira gerar um template no formato `{ value = template }`, este é o \nformato usado no componente `Acceleration` abaixo.\n\n```lua\nlocal Position = ECS.Component({ \n   x = 0, y = 0, z = 0 \n})\n\n-- o mesmo que:\n-- ECS.Component({ value = 0.1 })\nlocal Acceleration = ECS.Component(0.1) \n```\n[Mais informações sobre como criar componentes](/pt-br/architecture?id=componentes).\n\n## Sistemas e Consultas\n\nAgora vamos definir um [sistema](/pt-br/architecture?id=systems) para processar os componentes que acabamos de criar. Um \nsistema pode implementar diversos métodos, neste exercício vamos usar apenas o método `Update(Time)`. Este método será\ninvocado em todo frame, no passo `process` do mundo.\n\nPara criar um sistema, usamos o método `ECS.System(step, order, query, updateFn)`. Este método recebe os seguintes \nparametros:\n- **`step`** String, aceita os valores `process`, `transform`, `render` ou `task`\n- **`order`** Number (Opcional, padrão 50), permite definir uma ordem de execucao para sistemas que não são do tipo `task`\n- **`query`** Query (Opcional), filtra as entidades que serão processadas por este sistema\n- **`updateFn`** Function (Opcional), um atalho para criacao de sistemas que so possuam o método Update\n\nNós também vamos definir uma `Query`, que é a consulta que usaremos para obter apenas as entidades que estamos \ninteressados.\n\nPra criar a query, nós podems usar os métodos `Query.All(Component)`, `Query.Any(Component)` e \n`Query.None(Component)`. Ao invocar qualquer um destes métodos um `QueryBuilder` é retornado, desse modo voce pode \ninvocar os outros métodos na sequencia Ex. `Query.All(ComponentA).Any(ComponentB).None(ComponentC).Build()`.\n\n![Pipeline](../assets/pipeline.png)\n\nVamos começar criando um sistema que fará um loop por todas as entidades que possuem um componente `Position` e \nregistrar suas posições.\n\n```lua\n-- um atalho para os métodos\nlocal System, Query = ECS.System, ECS.Query \n\nlocal PositionLogSystem = System(\"process\", 2, Query.All(Position), function(self, Time)\n\n   -- Iterar por todas as entidades na consulta\n   self:Result():ForEach(function(entity)\n      -- Acessa o componente `Position` na entidade atual\n      local pos = entity[Position]\n\n      local msg = \"Entidade com ID: %d tem o componente Position = {x: %0.2f, y: %0.2f, z: %0.2f}\"\n\t\tprint(msg:format(entity.id, pos.x, pos.y, pos.z))\n   end)\nend)\n```\n\nO próximo sistema move cada entidade que tem uma posição e uma aceleração.\n\n```lua\nlocal MovableSystem = System(\"process\", 1, Query.All(Acceleration, Position)) \n\n-- Este método será chamado em todos os quadros por padrão\nfunction MovableSystem:Update(Time)\n\n   local delta = Time.DeltaFixed\n\n   -- Iterar por todas as entidades na consulta\n   for i, entity in self:Result():Iterator() do\n      local acceleration = entity:Get(Acceleration).value\n\n      local position = entity[Position]\n      position.x = position.x + acceleration * delta\n      position.y = position.y + acceleration * delta\n      position.z = position.z + acceleration * delta\n   end\nend\n```\n\n> Observe que estamos acessando componentes em uma entidade de duas formas diferentes: `entity:Get(Acceleration)` tem o \nmesmo resultado que `entity[Acceleration]`\n\n\nA consulta do sistema sistema `MovableSystem` filtra as entidades que possuem os componentes `Acceleration` e \n`Position`. Observe que, se necessário, podemos criar quantas consultas desejarmos e processá-las no método `Update`, ex:\n\n```lua\nlocal SystemDemo =  System(\"process\", 1)  \n\nfunction SystemDemo:Initialize(config)\n   self.queryBoxes = Query.All(Box).Build()\n   self.queryBalls = Query.All(Ball).Build()\n   self.queryGeometries = Query.Any(Box, Ball).Build()\nend\n\nfunction SystemDemo:Update(Time)\n   local boxes = self:Result(self.queryBoxes):ToArray()\n   local balls = self.World:Exec(self.queryBalls):ToArray()\n   for i, entity in self:Result(self.queryGeometries):Iterator() do\n      print(entity[Box], entity[Ball])\n   end\nend\n```\n\nPara obter mais informações, verifique a documentação de arquitetura: [Acessando e modificando componentes](/manual/Architecture?id=accessing-components-and-modify-components) e [Queries](/manual/Architecture?id=reactive-queries)\n\n\n## Mundo\n\nUm mundo é um contêiner para `entidades`, `componentes` e `sistemas`. A maioria dos jogos tem apenas um `mundo`,\nentretanto, você pode ter vários mundos funcionando ao mesmo tempo e habilitá-los ou desabilitá-los conforme necessário.\n\nA criacao de um mundo é feita pelo método `ECS.World(systemClasses, frequency, disableAutoUpdate)`, onde:\n\n- **systemClasses** Array de classes de sistemas, opcional\n- **frequency** Number (Opcional, padrao 30), permite definir a frequencia que o passo `process` será executado no mundo.\n- **disableAutoUpdate** Bool (Opcional, padrao false), quando diferente de `false`, o mundo registra-se automaticamente \nno `LoopManager`, recebendo deste a invocacao do método `World:Update()`\n\nVamos começar a criar nosso primeiro mundo:\n\n```lua\nlocal world = ECS.World();\n```\n\nAgora vamos registrar nossos sistemas no mundo para que sejam inicializados e executados em cada quadro.\n\n```lua\nworld:AddSystem(PositionLogSystem)\nworld:AddSystem(MovableSystem)\n```\n\n## Entidade\n\nTendo nosso mundo, alguns componentes e sistemas já definidos, vamos criar [entidades](/pt-br/architecture?id=entidades) \ne anexar estes componentes a eles:\n\n```lua\nlocal entity1 = world:Entity(Position())\n\nlocal entity2 = world:Entity(\n   Position({x = 5}), \n   Acceleration(1)\n)\n\nlocal entity3 = world:Entity(\n   Position.New({x = 5}), \n   Acceleration.New(1)\n)\n\nlocal entity4 = world:Entity(\n   Position({x = 5}), \n   Acceleration({value = 1})\n)\n\nlocal entity5 = world:Entity()\nentity5[Position] = { y = 5 }\nentity5:Set(Acceleration())\n\nlocal entity6 = world:Entity()\nentity6[Position] = Position()\nentity6:Set(Acceleration())\n```\n\nCom isso, acabamos de criar 6 entidades. 5 com os componentes `Acceleration` e `Position`, e um apenas com o componente `Position`.\n\nObserve que existem varias formas de instanciar e atribuir os componentes para a entidade, escolha a que mais combina \ncom o seu estilo de programção, o resultado final é o mesmo. Perceba também que as classes dos componentes podem ser \nusadas [como funcoes](http://lua-users.org/wiki/FuncTables), por exemplo `Position()`. Este formato tem o mesmo efeito \nque `Position.New()`.\n\n\n## Juntando tudo\n\nAgora o mundo só precisa ser atualizado (`world.Update(step, now)`) para que tudo funcione. Se voce usa roblox, basta \ncriar um Local Script que o mundo já sera atualizado automaticamente.\n\n\n```lua\n-- No Roblox:\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- um atalho para os métodos\nlocal Component, System, Query = ECS.Component, ECS.System, ECS.Query \n\n--[[ Componentes ]]\nlocal Position = Component({ x = 0, y = 0, z = 0 })\nlocal Acceleration = Component(0.1)\n\n\n--[[ Sistemas ]]\nlocal PositionLogSystem = System(\"process\", 2, Query.All(Position), function(self, Time)\n\n   -- Iterar por todas as entidades na consulta\n   self:Result():ForEach(function(entity)\n      -- Acessa o componente `Position` na entidade atual\n      local pos = entity[Position]\n\n      local msg = \"Entidade com ID: %d tem o componente Position = {x: %0.2f, y: %0.2f, z: %0.2f}\"\n\t\tprint(msg:format(entity.id, pos.x, pos.y, pos.z))\n   end)\nend)\n\nlocal MovableSystem = System(\"process\", 1, Query.All(Acceleration, Position)) \n\n-- Este método será chamado em todos os quadros por padrão\nfunction MovableSystem:Update(Time)\n\n   local delta = Time.DeltaFixed\n\n   -- Iterar por todas as entidades na consulta\n   for i, entity in self:Result():Iterator() do\n      local acceleration = entity:Get(Acceleration).value\n\n      local position = entity[Position]\n      position.x = position.x + acceleration * delta\n      position.y = position.y + acceleration * delta\n      position.z = position.z + acceleration * delta\n   end\nend\n\n--[[ Mundo ]]\nlocal world = ECS.World();\nworld:AddSystem(PositionLogSystem)\nworld:AddSystem(MovableSystem)\n\n--[[ Entidades ]]\nlocal entity1 = world:Entity(Position())\nlocal entity2 = world:Entity(Position({x = 5}), Acceleration(1))\nlocal entity3 = world:Entity(Position.New({x = 5}), Acceleration.New(1))\nlocal entity4 = world:Entity(Position({x = 5}), Acceleration({value = 1}))\n\nlocal entity5 = world:Entity()\nentity5[Position] = { y = 5 }\nentity5:Set(Acceleration())\n\nlocal entity6 = world:Entity()\nentity6[Position] = Position()\nentity6:Set(Acceleration())\n```\n\nResultado do código acima no Roblox Studio\n\n![Resultado do código acima no Roblox Studio](../assets/get-started-output-pt-br.gif)\n\n## Próximos passos\nEsta foi uma visão geral rápida sobre como as coisas são estruturadas usando **ECS Lua**, \n[leia a documentação da arquitetura](/pt-br/architecture) para informações mais detalhadas.\n"
  },
  {
    "path": "docs/pt-br/tutorial-boids.md",
    "content": "# Tutorial - Boids\n\n__UNDER_CONSTRUCTION__\n\n> OBJETIVO: Aplicar o uso do Job System para atualizar algumas centenas (ou milhares) de entidades por meio de processamento paralelo\n"
  },
  {
    "path": "docs/pt-br/tutorial-pacman.md",
    "content": "# Tutorial - Pacman\n\n__UNDER_CONSTRUCTION__\n\n> OBJETIVO: Demonstrar o uso de maquinas de estado para criar a \"inteligencia\" dos fantasmas\n"
  },
  {
    "path": "docs/pt-br/tutorial-shoot.md",
    "content": "# Tutorial - Jogo de Tiro\n\n__UNDER_CONSTRUCTION__\n\n> OBJETIVO: Demonstrar o uso das principais funcionalidades do ECS Lua e interpolacao na renderizacao\n"
  },
  {
    "path": "docs/pt-br/tutorial.md",
    "content": "# Tutoriais\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/style.css",
    "content": ".meta-container {\n   text-align: right;\n}\n\n.meta-container>.edit-button {\n   text-decoration: none;\n   font-size      : 12px;\n}\n\n.meta-container>.edit-button svg {\n   top         : 2px;\n   width       : 14px;\n   margin-right: .5rem;\n   position    : relative;\n}\n\n.api-docs>ul>li {\n   list-style   : circle;\n   margin-bottom: 1.2em;\n}\n\n.api-docs>ul>li p {\n   margin: 0;\n}\n\n.api-docs>ul>li>ul>li {\n   list-style: none;\n}\n\n.cover-show {\n   z-index: 0;\n}\n\nmain {\n   z-index: 1;\n}\n\n.github-corner {\n   z-index: 2;\n}\n\n\n/* Interactive Logo  */\n/* Source: http://codepen.io/elrumordelaluz/pen/aWavEG */\n\n.logo-container {\n   position  : relative;\n   height    : 35vh;\n   max-height: 400px;\n   display   : block;\n   margin    : 0 auto;\n}\n\n.ghost,\n.heats {\n   position: absolute;\n   width   : 100%;\n   height  : 100%;\n}\n\n.heats {\n   display              : grid;\n   grid-template-columns: repeat(5, 1fr);\n}\n\n.ghost {\n   z-index  : 1;\n   animation: floating 6s ease-in-out infinite;\n}\n\n.eye {\n   transition      : 0.3s;\n   transform-origin: center center;\n}\n\n.eyelid {\n   transform: scaleY(0.0);\n   animation: blink 1.5s infinite alternate;\n}\n\n\n.eye--left .eyelid {\n   transform-origin: 40px 19px;\n}\n\n.eye--right .eyelid {\n   transform-origin: 40px 19px;\n}\n\n.eye .pupil,\n.eye .pupil .inner,\n.eye .eyelid {\n   transition: 0.3s;\n}\n\n.pupil {\n   transform-origin: center center;\n}\n\n.h {\n   z-index   : 2;\n   /* debug */\n   /* outline: 1px solid red; */\n}\n\n.h.r1:hover~.ghost .pupil {\n   transform: translateY(-7px);\n}\n\n.h.r2:hover~.ghost .pupil {\n   transform: translateY(-3px);\n}\n\n.h.r3:hover~.ghost .pupil {\n   transform: translateY(1px);\n}\n\n.h.r4:hover~.ghost .pupil {\n   transform: translateY(2px);\n}\n\n.h.r5:hover~.ghost .pupil {\n   transform: translateY(3px);\n}\n\n.h.c1:hover~.ghost .pupil .inner {\n   transform: translateX(-8px);\n}\n\n.h.c2:hover~.ghost .pupil .inner {\n   transform: translateX(-6px);\n}\n\n.h.c3:hover~.ghost .pupil .inner {\n   transform: translateX(-4px);\n}\n\n.h.c4:hover~.ghost .pupil .inner {\n   transform: translateX(0px);\n}\n\n.h.c5:hover~.ghost .pupil .inner {\n   transform: translateX(1px);\n}\n\n.h.r3.c3:hover~.ghost .body,\n.h.r2.c3:hover~.ghost .body {\n   animation: crazy-body 0.1s infinite alternate;\n}\n\n.h.r3.c3:hover~.ghost .eye--left .pupil .inner,\n.h.r2.c3:hover~.ghost .eye--left .pupil .inner {\n   animation: crazy-l 0.1s infinite alternate;\n}\n\n.h.r3.c3:hover~.ghost .eye--right .pupil .inner,\n.h.r2.c3:hover~.ghost .eye--right .pupil .inner {\n   animation: crazy-r 0.1s infinite alternate;\n}\n\n@keyframes crazy-body {\n\n   0%,\n   50% {\n      fill: #9cb1ff;\n   }\n\n   100% {\n      fill: #fb860c;\n   }\n}\n\n@keyframes crazy-l {\n\n   0%,\n   50% {\n      transform: translateX(0px);\n   }\n\n\n   100% {\n      transform: translateX(-5px);\n   }\n}\n\n@keyframes crazy-r {\n\n   0%,\n   50% {\n      transform: translateX(-7px);\n   }\n\n   100% {\n      transform: translateX(-2px);\n   }\n}\n\n@keyframes floating {\n\n   0% {\n      transform: translateY(0px);\n   }\n\n   50% {\n      transform: translateY(-20px);\n   }\n\n   100% {\n      transform: translateY(0px);\n   }\n}\n\n@keyframes blink {\n\n   0%,\n   95% {\n      opacity  : 0;\n      transform: scaleY(0.0);\n   }\n\n   100% {\n      opacity  : 1;\n      transform: scaleY(1.0);\n   }\n}\n\n\nsection.cover.show {\n   /* background: radial-gradient(circle farthest-side, #fff 20%, #9cb1ffcc 60%, #f69d3c 130%) !important; */\n   background: radial-gradient(circle farthest-side, #fff 20%, rgb(156, 177, 255, 0.4) 60%, #f69d3c 130%) !important;\n}\n\nsection.cover .cover-main {\n   margin: 0px 16px 0;\n}\n\naside.sidebar {\n   width     : 240px;\n   box-shadow: 0 0 10px 0 rgba(131, 142, 162, 0.22);\n}\n\naside.sidebar>h1 {\n   height    : 102px;\n   visibility: hidden;\n}\n\naside.sidebar .logo {\n   width     : 80px;\n   margin    : 40px auto 10px auto;\n   text-align: center;\n}\n\naside.sidebar .logo>p {\n   margin   : 0;\n   color    : #0f49b3;\n   font-size: 18px;\n}\n\naside.sidebar .search {\n   margin : 0 2.2em 1em !important;\n   padding: 0 !important;\n}\n\naside.sidebar .search input {\n   font-size: 12px;\n}\n\naside.sidebar .sidebar-nav {\n   margin-top: 38px;\n}\n\naside.sidebar ul {\n   margin: 0;\n}\n\naside.sidebar ul li {\n   margin  : 0;\n   position: relative;\n}\n\naside.sidebar ul li a {\n   height     : 42px;\n   line-height: 36px;\n   font-size  : 14px;\n   color      : #7285a1;\n   padding    : 3px 0 3px 40px;\n}\n\naside.sidebar ul li ul li a {\n   padding-left: 60px;\n}\n\naside.sidebar ul li ul li ul li a {\n   padding-left: 80px;\n}\n\naside.sidebar ul li.active>a {\n   border          : 0;\n   background-color: rgba(61, 120, 232, 0.05);\n}\n\naside.sidebar ul li.active>a:before {\n   content         : '';\n   position        : absolute;\n   margin          : 0;\n   width           : 10px;\n   height          : 10px;\n   top             : 15px;\n   left            : 20px;\n   border-radius   : 50%;\n   background-color: #9cb1ff;\n   box-shadow      : 0 0 0 3px rgb(156, 177, 255, 0.4);\n}\n\naside.sidebar ul li.active>a:after {\n   content         : '';\n   position        : absolute;\n   left            : 0;\n   width           : 4px;\n   height          : 36px;\n   border-radius   : 4px;\n   background-color: #9cb1ff;\n}\n\nsection.content .vuep {\n   height       : auto;\n   border       : 1px solid #ebebeb;\n   border-radius: 4px;\n}\n\nsection.content .vuep:hover {\n   box-shadow: 0 0 10px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .6);\n}\n\n/* section.content .vuep-editor,\nsection.content .vuep-preview {\n   position: relative;\n   width   : 50%;\n   height  : 432px;\n}\n\nsection.content .vuep-editor {\n   margin-right: 0;\n   border-right: 1px dashed #ebebeb;\n}\n\nsection.content .vuep-preview {\n   border : 0;\n   padding: 15px 25px;\n} */\n\nsection.content .markdown-section {\n   max-width   : 1200px;\n   /* min-width: 960px; */\n   /* padding  : 0 60px 30px 0; */\n}\n\n.markdown-section h1 {\n   font-size: 1.5rem;\n}\n\n.markdown-section h2 {\n   font-size: 1.25rem;\n   margin   : 2rem 0 1.5rem;\n}\n\n.markdown-section h3,\n.markdown-section h4 {\n   font-size: 1rem;\n   margin   : 1.5rem 0 .75rem;\n}\n\n.markdown-section table {\n   border-radius: 4px;\n}\n\n.markdown-section code {\n   padding: 3px 3px;\n}\n\n.markdown-section pre>code {\n   padding: 10px 5px;\n}\n\n@media screen and (max-width: 768px) {\n   .markdown-section pre {\n      padding: 0;\n   }\n}\n\n.markdown-section p.tip,\n.markdown-section tr:nth-child(2n) {\n   background-color: #fafcff;\n}\n\n.markdown-section iframe {\n   margin       : 0;\n   border       : 1px solid #ebebeb;\n   border-radius: 4px;\n}\n\n.markdown-section iframe:hover {\n   border    : 1px solid #ebebeb;\n   box-shadow: 0 0 10px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .6);\n}\n\n/* HOME PANEL */\n@keyframes box-enter {\n   from {\n      opacity: 0;\n   }\n\n   to {\n      opacity: 1;\n   }\n}\n\n.home-row {\n   display  : flex;\n   flex-wrap: wrap;\n}\n\n.home-col {\n   width             : 25%;\n   flex-basis        : 25%;\n   padding-left      : 12px;\n   padding-right     : 12px;\n   animation-name    : box-enter;\n   animation-duration: 2s;\n}\n\n@media screen and (max-width: 1160px) {\n   .home-col {\n      width     : 50%;\n      flex-basis: 50%;\n   }\n}\n\n@media screen and (max-width: 768px) {\n   .home-col {\n      width     : 100%;\n      flex-basis: 100%;\n   }\n}\n\n.home-panel {\n   box-shadow      : none;\n   border          : none;\n   border-radius   : 0;\n   margin-bottom   : 30px;\n   background-color: #fff;\n}\n\n.home-row .panel-heading {\n   border     : none;\n   background : #f69d3c !important;\n   color      : black !important;\n   font-weight: bold;\n   font-size  : 1.125rem;\n   padding    : 10px 13px;\n}\n\n.home-panel .panel-heading a,\n.home-panel .panel-heading a:hover {\n   color          : black;\n   text-decoration: none;\n}\n\n.home-panel .panel-body {\n   padding   : 24px;\n   border    : none;\n   background: #ffd6a8;\n   color     : white;\n   font-size : 1.875rem;\n   height    : 138px;\n}\n\n.home-panel p {\n   margin: 0 !important;\n}\n\n.home-panel .panel-body pre {\n   background: none;\n   border    : none;\n   color     : white;\n   font-size : 2.125rem;\n   text-align: center;\n   padding   : 0;\n}\n\n.home-panel .panel-body pre .fa {\n   margin-left: calc(-50% + 14px);\n}\n\n.home-panel .list-group {\n   border    : none;\n   background: #f2f2f2;\n   padding   : 10px !important;\n   box-shadow: none;\n   margin    : 0 !important;\n}\n\n.home-panel .list-group .list-group-item {\n   border     : none;\n   background : transparent;\n   text-align : left;\n   font-size  : 0.75rem;\n   font-weight: 600;\n   padding    : 2px 15px;\n}\n\n.home-panel .list-group .list-group-item::before {\n   display: none;\n}\n\n.home-panel .list-group .list-group-item p {\n   margin: 0;\n}\n\n.home-panel .list-group .list-group-item a {\n   text-decoration: none;\n}\n"
  },
  {
    "path": "docs/tutorial-boids.md",
    "content": "# Tutorial - Boids\n\n__UNDER_CONSTRUCTION__\n\n> OBJECTIVE: To apply the use of the Job System to update a few hundred (or thousands) of entities through parallel processing\n"
  },
  {
    "path": "docs/tutorial-pacman.md",
    "content": "# Tutorial - Pacman\n\n__UNDER_CONSTRUCTION__\n\n> OBJECTIVE: Demonstrate the use of state machines to create the \"intelligence\" of ghosts\n"
  },
  {
    "path": "docs/tutorial-shoot.md",
    "content": "# Tutorial - Shooting Game\n\n__UNDER_CONSTRUCTION__\n\n> OBJECTIVE: Demonstrate the use of the main features of ECS Lua and interpolation in rendering\n"
  },
  {
    "path": "docs/tutorial.md",
    "content": "# Tutorials\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/z_old_TECH_DETAILS.md",
    "content": "# Roblox-ECS - Technical implementation details\n\n## Roblox Pipeline\n\nBefore going into the details, let's review some important concepts about how the Roblox game engine works.\n\nMost likely you have seen the illustration below, made by zeuxcg and enriched by Fractality_alt. It describes the roblox rendering pipeline. Let's redraw it so that it is clearer what happens in each frame of a game in roblox\n\n[![](docs/pipeline_old.png)](https://devforum.roblox.com/t/runservice-heartbeat-switching-to-variable-frequency/23509/7)\n\nReady: In the new image, we have a clear separation (gap between CPU1 and CPU2) of the roblox rendering process, which occurs in parallel with the simulation and processing (game logic) of the next screen.\n\nThe green arrows indicate the start of processing of the new frame and the return of execution after the completion of the two processes that are being executed in parallel (rendering of the previous screen and processing of the current frame).\n\nThe complete information on the order of execution can be seen at https://developer.roblox.com/en-us/articles/task-scheduler\n\n[![](docs/pipeline.png)](https://developer.roblox.com/en-us/articles/task-scheduler)\n\nBased on this model, roblox-ecs-lib organizes the execution of the systems in the following events. We call them steps\n\n![](docs/pipeline_ecs_resume.png)\n\n![](docs/pipeline_ecs_steps.png)\n\n### processIn\nExecuted once per frame.\n\nThis is the first step to be executed in a frame. Use this step to run systems that translate the user's input or the current state of the workspace to entity components, which can be processed by specialized systems in the next steps\n\nEg. Use the UserInputService to register the player's inputs in the current frame in a pool of inputs, and, in the PROCESS_IN step, translate these commands to the player's components. Realize that the same logic can be used to receive entries from the server and update local entities that represent other players\n\n```lua\n\n-- InputHandlerUtils.lua\nlocal UserInputService = game:GetService(\"UserInputService\")\n\nlocal pool = {\n   FIRE = false\n}\n\n-- clear frame inputs\nfunction pool.clear()\n   pool = {\n      FIRE = false\n   }\nend\n\nUserInputService.InputBegan:Connect(function(input, gameProcessed)\n\tif input.UserInputType == Enum.UserInputType.MouseButton1 then\n\t\tpool.FIRE = true\n\tend\nend)\n\nreturn pool\n\n---------------------------------------------------------------------------------------\n\n-- InputMapSystem.lua\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\nlocal FiringComponent = require(game.ReplicatedStorage:WaitForChild(\"FiringComponent\"))\n\nlocal pool = require(game.ReplicatedStorage:WaitForChild(\"InputHandlerUtils\"))\n\nreturn ECS.RegisterSystem({\n   name = 'InputMap',\n   step = 'process',\n   order = 5,\n   requireAll = {\n      PlayerComponent\n   },\n   update = function (time, world, dirty, entity, index, players)\n      local changed = false\n\n      if pool.FIRE then\n         world.Set(entity, FiringComponent, { FiredAt = time.frame })\n         changed = true\n      end\n\n      pool.clear()\n\n      return changed\n   end\n})\n```\n\n### process\nExecuted 0 or more times per frame\n\nThis step allows the execution of systems for game logic independent of Frame-Rate, obtaining determinism in the simulation of the rules of the game\n\nIndependent Frame-Rate games are games that run at the same speed, no matter the frame rate. For example, a game can run at 30 FPS (frames per second) on a slow computer and 60 FPS on a fast one. A game independent of the frame rate progresses at the same speed on both computers (the objects appear to move at the same speed). On the other hand, a frame rate-dependent game advances at half the speed of the slow computer, in a kind of slow motion effect (read more at https://gafferongames.com/post/fix_your_timestep/).\n\nMaking frame rate independent games is important to ensure that your game is enjoyable and playable for everyone, no matter what type of computer they have. Games that slow down when the frame rate drops can seriously affect gameplay, making players frustrated and giving up! In addition, some systems have screens with different refresh rates, such as 120 Hz, so independence of the frame rate is important to ensure that the game does not accelerate and is impossibly fast on these devices.\n\nThis step can also be used to perform some physical simulations that are not met (or should not be performed) by the roblox internal physics engine.\n\nThe standard frequency for executing this step in a world is 30Hz, which can be configured when creating a world.\n\nIn the tutorial topic there is a demonstration of the use of interpolation for smooth rendering display even when updating the simulation in just 10Hz\n\n### processOut\nExecuted once for the frame\n\nUse this step when your systems make changes to the components and these changes imply the behavior of the roblox internal physics simulations, therefore, the workspace needs to receive the update for the correct physics engine simulation\n\n### transform\nExecuted once per frame.\n\nUse this step for systems that react to changes made by the roblox physics engine or to perform transformations on game objects based on entity components (ECS to Workspace).\n\nEx. In a soccer game, after running the physics engine, check if the ball touched the net, scoring a point\n\nEx2. In a game that is not based on the roblox physics engine, perform the interpolation of objects based on the positions calculated by the specialized systems that were executed in the PROCESS step\n\n### render\nExecuted once per frame.\n\nUse this step to run systems that perform updates on things related to the camera and user interface.\n\nIMPORTANT! Only run light systems here, as the screen design and the processing of the next frame will only happen after the completion of this step. If it is necessary to make transformations on world objects (interpolations, complex calculations), use the TRANSFORM step\n\n## Architectural decisions, Performance and Benchmarks\n\nTaking into account that on the Roblox platform the development of the logic of our game is done in an interpreted language, roblox-ecs-lib seeks to guarantee the maximum performance of its systems.\n\nWithout reinventing the wheel, roblox-ecs-lib mirrors the implementation of the Unity Engine in the following ways:\n\n1. Data oriented design\n2. Chunk data storage\n3. Efficient systems using version numbers\n\nOn the other hand, roblox-ecs-lib does not bring to its implementation all that complexity that exists in the Unity ECS Engine for creating Components or Systems.\n\nThe first reason that makes implementation simpler is that roblox-ecs-lib hides the complexities related to Archetypes, Chunks management and EntityManager from the developer.\n\nThe second reason for simplification is the fact that Roblox's script execution is Single Thread (there are promises for parallel execution for 2021, let's wait). Although the Lua language allows the use of coroutines, these are only performaticas for processes with high Input/Output consumption, (such as http calls, disk access, etc.), for heavy processing there is no advantage in using coroutines (see https://realpython.com/python-concurrency/).\n\nA third reason is the difference in the execution flow, which in Roblox is already predefined and roblox-ecs-lib makes use of these events, in its own way, as steps for running systems.\n\n### Data Oriented Design\n\nData-oriented design is an approach to optimising programs by carefully considering the memory layout of data structures, and their implications for auto-vectorisation and use of the CPU cache (see [An introduction to Data Oriented Design with Rust](http://jamesmcm.github.io/blog/2020/07/25/intro-dod/)).\n\nRoblox-ecs-lib takes into account the organization of data and good practices in order to achieve the maximum performance possible in the execution of the systems. Despite running in interpreted language, many of the performance rules applicable to a C program also have an effect on a Lua program (even if in a smaller proportion). For more details see the scripts available in the `benchmark` directory\n\n**Benchmark: Struct of Arrays vs. Array of Structs**\n![](docs/bench_soa.png)\n\n\n\n@TODO: more\n\n### Chunk data storage\n\n@TODO\n\nsee [The Chunk data structure in Unity](https://gametorrahod.com/the-chunk-data-structure/)\n\n### Efficient systems using version numbers\n\n@TODO\n\n![](docs/version.png)\n\nsee [Designing an efficient system with version numbers])(https://gametorrahod.com/designing-an-efficient-system-with-version-numbers/)\n\n\n\n\n\n\n- https://www.youtube.com/watch?v=W3aieHjyNvw&ab_channel=GDC\n- https://developer.roblox.com/en-us/articles/task-scheduler\n- https://medium.com/@timonpost/game-networking-1-interval-and-ticks-b39bb51ccca9\n- http://clintonbrennan.com/2013/12/lockstep-implementation-in-unity3d/\n- https://www.youtube.com/watch?v=W5lUCeAu_2k&feature=emb_logo&ab_channel=Battle%28non%29sense\n- https://www.raywenderlich.com/7630142-entity-component-system-for-unity-getting-started\n- https://levelup.gitconnected.com/a-simple-guide-to-get-started-with-unity-ecs-b0e6a036e707\n- https://www.raywenderlich.com/7630142-entity-component-system-for-unity-getting-started\n"
  },
  {
    "path": "docs/z_old_TUTORIAL.md",
    "content": "## Roblox-ECS Tutorial - Shooting Game\n\nIn this topic, we will see how to implement a simple shooting game, inspired by the [Unity ECS Tutorial - Player Shooting](https://www.youtube.com/watch?v=OQgmIHKXAdg&ab_channel=InfallibleCode)\n\nThe first step in using Roblox-ECS is to install the script. In roblox studio, in the Toolbox search field, type \"Roblox-ECS\". Install the script in `ReplicatedStorage> ECS`.\n\nNow, let's give our character a gun, let's do it via code. Create a `LocalScript` named `tutorial` in `StarterPlayer > StarterCharacterScripts` and add the code below.\n\n ```lua\nrepeat wait() until game.Players.LocalPlayer.Character\n\nlocal Players \t   = game:GetService(\"Players\")\nlocal Player \t   = Players.LocalPlayer\nlocal Character\t= Player.Character\n\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\nlocal ECSUtil = require(game.ReplicatedStorage:WaitForChild(\"ECSUtil\"))\n\n-- Our weapon\nlocal rightHand = Character:WaitForChild(\"RightHand\")\nlocal weapon = Instance.new(\"Part\", Character)\nweapon.CanCollide = false\nweapon.CastShadow = false\nweapon.Size       = Vector3.new(0.2, 0.2, 2)\nweapon.CFrame     = rightHand.CFrame + Vector3.new(0, 0, -1)\nweapon.Color      = Color3.fromRGB(255, 0, 255)\n\nlocal weldWeapon = Instance.new(\"WeldConstraint\", weapon)\nweldWeapon.Part0 = weapon\nweldWeapon.Part1 = rightHand\n\n-- weapon bullet spawn\nlocal BulletSpawnPart   = Instance.new(\"Part\", weapon)\nBulletSpawnPart.CanCollide = false\nBulletSpawnPart.CastShadow = false\nBulletSpawnPart.Color      = Color3.fromRGB(255, 255, 0)\nBulletSpawnPart.Size       = Vector3.new(0.6, 0.6, 0.6)\nBulletSpawnPart.Shape      = Enum.PartType.Ball\nBulletSpawnPart.CFrame     = weapon.CFrame + Vector3.new(0, 0, -1)\n\nlocal weldBulletSpawn = Instance.new(\"WeldConstraint\", BulletSpawnPart)\nweldBulletSpawn.Part0 = BulletSpawnPart\nweldBulletSpawn.Part1 = weapon\n```\n\nIn the code above we are just adding a weapon _(a cube)_ in the character's hands. We make the connection using a `WeldConstraint`, we also add a reference point to use as the initial position of the projectiles (`BulletSpawnPart`) and adjust the CFrame of the same to be on the correct side of the weapon (front).\n\nIf you run the code now you will see something like the image below.\n\n![](docs/tut_01.gif)\n\nAll ok, now, to have access to the position of `BulletSpawnPart` within an ECS world, we need to obtain the Position and Rotation of the object from the Workspace and save it as a component of an entity in the ECS world\n\nRoblox-ECS already offers a generic method, some components and systems that already do this synchronization, so let's use it to create our `bulletEntity`\n\nIn the script above, before the creation of our weapon, we will define our ECS world, and below, at the end of the script, we will use the Roblox-ECS utility components to synchronize the `BulletSpawnPart` position and rotation\n\n ```lua\nlocal world = ECS.CreateWorld()\nECSUtil.AddDefaultSystems(world)\n\n\nlocal bulletSpawnEntity = ECSUtil.NewBasePartEntity(world, BulletSpawnPart, true, false)\n```\n\nThe `ECSUtil.NewBasePartEntity` method is a facilitator that adds the `ECSUtil.BasePartComponent`, `ECSUtil.PositionComponent`, `ECSUtil.RotationComponent` components and can also add interpolation and sync tags, it has the following signature: `function ECSUtil.NewBasePartEntity(world, part, syncBasePartToEntity, syncEntityToBasePart, interpolate)`.\n\nIn our case, we only want it to sync the data from `BasePart` _(workspace)_ to our `Entity` _(ECS)_.\n\nIf you run the project now, you won't see any visual changes, because the systems that are running in this instance of the world don't have any logic that changes the behavior of our game yet.\n\nNow let's create our first component. Thinking about a solution that can be used both on the client and on the server, we will create our components and systems in the `ReplicatedStorage > tutorial` directory. Within this directory we can create two folders, `component` and` system`.\n\nIn `ReplicatedStorage > tutorial > component`, create a `ModuleScript` with the name `WeaponComponent` and the contents below\n\n ```lua\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Weapon')\n```\n \nThat’s it, there’s no logic, no data typing\n\n> **Note** Roblox-ECS does not validate the data handled by the systems, it is the responsibility of the developer to pay attention to the validations\n\nNow, in our `tutorial` script, we will add this feature to our entity. Change the script by adding the code snippets below.\n\n```lua\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal WeaponComponent = require(Components:WaitForChild(\"WeaponComponent\"))\n\n\nworld.Set(bulletSpawnEntity, WeaponComponent)\n```\n\nOk. We created the world, we created an entity, we added features but nothing happened on the screen yet. This is because we only add features (components) to our entity, we have not yet defined any behavior that must be performed for those features\n\nWith our components and entity defined, it's time to create our first system, let's call it `PlayerShootingSystem`\n\nFor a better separation of responsibilities, we will divide our weapon system into two distinct systems, the first, `FiringSystem` will be responsible only for creating new projectiles in the workpace whenever necessary. The `PlayerShootingSystem`, which we are creating now, will be the responsible for notifying the `FiringSystem` when it is time to create new projectiles. It does this by monitoring user input and whenever the mouse button is clicked, it adds a tag component to our entity, indicating that a projectile must be created\n\nBefore moving on, let's create this component now. Create a `ModuleScript` in` ReplicatedStorage > tutorial > component` with the name `FiringComponent` and add the content below\n\n ```lua\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Firing', nil, true)\n```\n\nNow, going back to our system, create a `ModuleScript` in `ReplicatedStorage > tutorial > system` with the name `PlayerShootingSystem` and the content below. This system is responsible for adding the `FiringComponent` tag to the entity that has the `WeaponComponent` component whenever the mouse button is pressed. Realize that when we make changes to the data currently being processed (entity or data array), it is necessary that our `update` method returns `true`, so that Roblox-ECS can inform other systems that this chunk has been changed, using dirty parameter\n\n ```lua\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal FiringComponent = require(Components:WaitForChild(\"FiringComponent\"))\nlocal WeaponComponent = require(Components:WaitForChild(\"WeaponComponent\"))\n\nreturn ECS.RegisterSystem({\n   name = 'PlayerShooting',\n   step = 'process',\n   order = 1,\n   requireAll = {\n      WeaponComponent\n   },\n   update = function (time, world, dirty, entity, index, weapons)\n\n      local isFiring = UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1)\n\n      if isFiring  then\n         world.Set(entity, FiringComponent)\n         return true\n      end\n\n      return false\n   end\n})\n```\n\nContinuing, we will now create the system responsible for creating the projectiles whenever our entity receives the tag `FiringComponent`, this will be the `FiringSystem`\n\nCreate a `ModuleScript` in `ReplicatedStorage > tutorial > system` with the name `FiringSystem` and the contents below. This system is responsible only for creating 3D objects in the scene that represent our projectiles. Realize that this system does not have the `update` method, as it is only interested in knowing when an entity with the expected characteristics appears in the world.\n\nTo correctly position our projectiles, this system uses data from the `ECSUtil.PositionComponent` and `ECSUtil.RotationComponent` components, which were added up there by the `ECSUtil.NewBasePartEntity` method during the creation of our entity. In order for our projectile to move, we added the `ECSUtil.MoveForwardComponent` and `ECSUtil.MoveSpeedComponent` components that are used by the `ECSUtil.MoveForwardSystem` system (Automatically added when creating the world)\n\nAlso note that our system has not made any changes to the current `chunk` or even the entity, so it always returns `false`\n\n```lua\n\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal FiringComponent = require(Components:WaitForChild(\"FiringComponent\"))\n\nreturn ECS.RegisterSystem({\n   name = 'Firing',\n   step = 'process', \n   requireAll = {      \n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      FiringComponent\n   },\n   onEnter = function(time, world, entity, index,  positions, rotations, firings)\n\n      local position = positions[index]\n      local rotation = rotations[index]\n      \n      if position ~= nil and rotation ~= nil then\n         -- can be made in a utility script, or clone a preexistece model\n         local bulletPart = Instance.new(\"Part\")\n         bulletPart.Anchored     = true\n         bulletPart.CanCollide   = false\n         bulletPart.Position     = position\n         bulletPart.CastShadow   = false\n         bulletPart.Shape        = Enum.PartType.Ball\n         bulletPart.Size         = Vector3.new(0.6, 0.6, 0.6)\n         bulletPart.CFrame       = CFrame.fromMatrix(position, rotation[1], rotation[2], rotation[3] * -1)\n         bulletPart.Parent       = game.Workspace\n\n         local bulletEntity = ECSUtil.NewBasePartEntity(world, bulletPart, false, true)\n         world.Set(bulletEntity, ECSUtil.MoveForwardComponent)\n         world.Set(bulletEntity, ECSUtil.MoveSpeedComponent, 0.1)\n      end\n\n      return false\n   end\n})\n```\n\nNow, let's add our systems to the world. Change the `tutorial` script by adding the codes below.\n\n```lua\nlocal Systems = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"system\")\nlocal FiringSystem         = require(Systems:WaitForChild(\"FiringSystem\"))\nlocal PlayerShootingSystem = require(Systems:WaitForChild(\"PlayerShootingSystem\"))\n\nworld.AddSystem(FiringSystem)\nworld.AddSystem(PlayerShootingSystem)\n```\n\nOkay, let's test our game.\n\n![](docs/tut_01a.gif)\n\nPerfect, everything went completely well, except for one thing. We can only shoot once. Let's understand what's wrong:\n\nOur `FiringSystem` is carrying out the expected behavior, creating projectiles whenever an entity with those characteristics appears in the world, `PlayerShootingSystem` is also carrying out what we expect, whenever we use the mouse click it defines that our entity has the `FiringComponent`, however, this `FiringComponent` feature never ceases to exist, it is being added only once, so the `onEnter` method of `FiringSystem` is only invoked once. Therefore, we need to remove the entity's `FiringComponent` after some time so that the `onEnter` method can be triggered more often.\n\nTo do this we will create a new system, its name will be `CleanupFiringSystem`, it will be responsible for removing the `FiringComponent` component from our entity after a period of time. In order for `CleanupFiringSystem` to do its job we need to change `FiringComponent`. It will stop being a component tag and start saving the moment of its creation, so that `CleanupFiringSystem` can validate this time and decide if it will remove it from the entity or not\n\nLet's change the `ReplicatedStorage > tutorial > component > FiringComponent.lua` script to the content below. Our component now has a constructor, used to validate the input data and is no longer a tag component\n\n```lua\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Firing', function(firedAt)\n   if firedAt == nil then\n      error(\"firedAt is required\")\n   end\n\n   return firedAt\nend)\n```\n\nIf you run the code now and try to shoot, you will see the following error in Roblox Studio's output:\n\n```log\n21:21:39.043 - ReplicatedStorage.tutorial.component.FiringComponent:5: firedAt is required\n21:21:39.044 - Stack Begin\n21:21:39.044 - Script 'ReplicatedStorage.tutorial.component.FiringComponent', Line 5\n21:21:39.045 - Script 'ReplicatedStorage.ECS', Line 1349\n21:21:39.045 - Script 'ReplicatedStorage.tutorial.system.PlayerShootingSystem', Line 22\n21:21:39.045 - Script 'ReplicatedStorage.ECS', Line 1096\n21:21:39.046 - Script 'ReplicatedStorage.ECS', Line 1635\n21:21:39.047 - Script 'ReplicatedStorage.ECS', Line 1787\n21:21:39.047 - Stack End\n```\n\nNote that `PlayerShootingSystem` is trying to add a `FiringComponent` to our entity, but the constructor method performed the validation and prevented the creation of the entity\n\nWe will update the `ReplicatedStorage > tutorial > system > PlayerShootingSystem.lua` script with the change below, when adding the component, we will pass to the constructor the current frame instant (`time.frame`)\n\n```lua\nif isFiring  then\n   world.Set(entity, FiringComponent, time.frame)\n   return true\nend\n```\n\nOk, now that we are correctly starting `FiringComponent` with a moment for validation, we will create `CleanupFiringSystem`\n\nCreate a `ModuleScript` in `ReplicatedStorage > tutorial > system` with the name `CleanupFiringSystem` and the contents below. This system is responsible for removing the `FiringComponent` component after some time. This will allow the `FiringSystem` `onEnter` method to be invoked more often. In our implementation, we define that after `0.5` seconds the information that the shot was taken is removed from our entity, allowing it to be fired again in the sequence\n\n```lua\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal FiringComponent = require(Components:WaitForChild(\"FiringComponent\"))\n\nreturn ECS.RegisterSystem({\n   name = 'CleanupFiring',\n   step = 'process',\n   requireAll = {\n      FiringComponent\n   },\n   update = function (time, world, dirty, entity, index, firings)\n\n      local firedAt = firings[index]\n      if firedAt ~= nil then\n         if time.frame - firedAt < 0.5 then\n            return false\n         end\n\n         world.Remove(entity, FiringComponent)\n\n         return true\n      end\n\n      return false\n   end\n})\n```\n\nWe will also change the `tutorial` script to add the new system to the world\n\n```lua\nlocal CleanupFiringSystem  = require(Systems:WaitForChild(\"CleanupFiringSystem\"))\n\n\nworld.AddSystem(CleanupFiringSystem)\n```\n\nOK, now we can shoot more than once, however, we still have another problem. Realize that by pressing and holding the mouse button, our weapon does not fire anymore, it is only firing if I click, wait `0.5` seconds and click again\n\nThis is happening because the `update` method of `PlayerShootingSystem` is being invoked with each new frame, updating the time of the `FiringComponent` of our entity in each update (`world.Set(entity, FiringComponent, time.frame)`) , this means that the logic of `CleanupFiringSystem` is not validated, since the elapsed time (`firedAt`) never exceeds 0.5 seconds. We need to filter this behavior.\n\nLet's change the `PlayerShootingSystem` to obtain the desired behavior. We want him to add the `FiringComponent` to any entity that does not yet have this component, so he will never make changes to the data for that component.\n\nLet's change the script `ReplicatedStorage > tutorial > system > PlayerShootingSystem.lua` with the code snippet below, applying a component filter, which at the moment only has `requireAll`, we will also add the `rejectAny` field, so that the method `Update` ignore entities that already have this component.\n\n```lua\nrequireAll = {\n   WeaponComponent\n},\nrejectAny = {\n   FiringComponent\n}\n```\n\nOkay, now we have the expected behavior, when pressing and holding the left mouse button, our weapon fires several projectiles respecting the interval defined in `CleanupFiringSystem`\n\n![](docs/tut_02.gif)\n\nHowever, you noticed one thing: The animation of our projectile is terrible, the projectiles are teleporting from one point to another, the animation of the movement is not smooth as expected\n\nThis happens due to **Fixed Timestep Jitter**, we will understand in the next topic\n\n\n### Fixed Timestep Jitter\n\nIn our project, the system responsible for the movement of our projectiles is `ECSUtil.MoveForwardSystem`. The `update` method of this system is invoked 30 times per second, which is the standard update frequency for the `process` step of Roblox-ECS. Therefore, even though our game is being rendered at more than 60FPS, the simulation performed by this system is limited, causing this unwanted effect in the animation\n\nTo work around the problem we have two solutions:\n\n**1 - Increase the frequency of our simulation**\n\nAt first glance, this seems to be the most suitable solution, just increase the frequency of our simulation to 60, 90 or 120Hz and our animation will be smooth\n\nFrom a technical point of view this is true, our animation will run smoothly, but in return we will be spending a lot more computational resources to run all the systems that are programmed to update in the `process` step, and that is not a good thing\n\nIn addition to spending unnecessary processing resources, this will increase the battery consumption of mobile devices and, if the player's device (whether computer or cell phone) does not have enough processing power the heavy simulation will cause the FPS to drop in rendering\n\nAnother problem is if you increase the frequency of the simulation on your server, which in addition to having limited processing power needs to process data from all players simultaneously, decreasing the overall quality of your game\n\nJust for the sake of experimentation, we will increase the frequency of execution of our world. Change the `tutorial` script to the following world boot configuration:\n\n```lua\nlocal world = ECS.CreateWorld(nil, { frequency = 60 })\n```\n\n![](docs/tut_03.gif)\n\nOkay, you already noticed that the animation of our projectiles were smooth, but this at an expensive computational cost (and unnecessary in our case). This change causes the `process` step of the world to be performed at a frequency of 60Hz (60 times per second)\n\nThis is not the best solution, let's use something more efficient\n\n**2 - Do Interpolation**\n\nInterpolation is a technique that allows, from two values ​​(A and B), to calculate a third value (C) that represents a ratio between A and B.\n\nExample:\n   - If `A = 0` and `B = 10`, for the ratio of `0.5` the value of `C = 5` _(C is between A and B exactly 0.5)_\n   - If our ratio were `0.95`, the `C` value would be `9.5`\n\nIn game development, we use interpolation to calculate a spatial position _(`Vector3`)_, or a rotation that is between two previously calculated values ​​_(position of the previous frame and position of the last simulation)_ using the elapsed time as a factor _(if the simulation takes 0.24 seconds and 0.12 seconds has passed since the last simulation, the factor is ~ 0.5)_\n\nWith that, we can reduce the frequency of the simulation _(heavy calculation)_, save the last two positions/rotations and apply the interpolation as we render the screen, in our case, doing this in the `transform` step _(which is running at a higher frequency, 60FPS for example)_\n\nRoblox-ECS already offers the interpolation factor _(interpolationAlpha)_ to be used in systems that wish to apply the interpolation. It also already provides a data synchronization system between the position and rotation of the entity to update the `BasePart` through this interpolation.\n\nWe will then make the changes to verify the use of interpolation and decrease the cost of processing our game.\n\nIn the `tutorial` script, we will decrease the execution frequency of the world, say for 10Hz\n\n```lua\nlocal world = ECS.CreateWorld(nil, { frequency = 10 })\n```\n\n![](docs/tut_04.gif)\n\nIf you run the game now you will see that the animation is horrible, we will now inform you that we want to use interpolation in the entities of our projectiles.\n\nChange the `ReplicatedStorage > tutorial > system > FiringSystem.lua` script, in the line where our bulletEntity is initializing, using the utility method, modify it\n\n```lua\nlocal bulletEntity = ECSUtil.NewBasePartEntity(world, bulletPart, false, true)\n```\n\nto\n\n```lua\nlocal bulletEntity = ECSUtil.NewBasePartEntity(world, bulletPart, false, true, true)\n```\n\nInforming that we want an entity that receives the tags and components used by the system if interpolated synchronization.\n\nThe result, as expected, is a totally smooth animation and using minimal CPU resources in the `process` step (only 10 times per second)\n\n![](docs/tut_05.gif)\n\nAnd we come to the end of the tutorial, for more information on these concepts, see\n- [Game Loop by Robert Nystrom](http://gameprogrammingpatterns.com/game-loop.html)\n- [Fix Your Timestep! by Glenn Fiedler](https://gafferongames.com/post/fix_your_timestep/)\n- [The Game Loop By Gilles Bellot](https://bell0bytes.eu/the-game-loop/)\n"
  },
  {
    "path": "examples/pong/.editorconfig",
    "content": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n[*.{lua,js,json,html,ts}]\ncharset = utf-8\nindent_size = 3\nindent_style = space\n"
  },
  {
    "path": "examples/pong/.gitignore",
    "content": "# IDE\n.idea\n*.iml\n.vscode\n\n# Roblox Studio\n# place-dev.rbxl\n**/*.rbxl.lock\n**/*.rbxlx.lock\n**/*.blend1\n\n# misc\nbuild\n"
  },
  {
    "path": "examples/pong/default.project.json",
    "content": "{\n\t\"name\": \"ecs-lua-pong\",\n\t\"tree\": {\n      \"$className\": \"DataModel\",\n      \"ReplicatedStorage\": {\n         \"$className\": \"ReplicatedStorage\",\n         \"$ignoreUnknownInstances\": true,\n         \"$path\": \"src/shared\"\n      },\n      \"ServerScriptService\": {\n         \"$className\": \"ServerScriptService\",\n         \"$ignoreUnknownInstances\": true,\n         \"$path\": \"src/server\"\n      },\n      \"StarterPlayer\": {\n         \"$className\": \"StarterPlayer\",\n         \"StarterPlayerScripts\": {\n            \"$className\": \"StarterPlayerScripts\",\n            \"$ignoreUnknownInstances\": true,\n            \"$path\": \"src/client\"\n         }\n      }\n\t}\n}\n"
  },
  {
    "path": "examples/pong/pong.rbxlx",
    "content": "<roblox xmlns:xmime=\"http://www.w3.org/2005/05/xmlmime\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://www.roblox.com/roblox.xsd\" version=\"4\">\n\t<External>null</External>\n\t<External>nil</External>\n\t<Item class=\"Workspace\" referent=\"RBX410F21D83960426483588B28BC333E4D\">\n\t\t<Properties>\n\t\t\t<bool name=\"AllowThirdPartySales\">false</bool>\n\t\t\t<token name=\"AnimationWeightedBlendFix\">0</token>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<token name=\"ClientAnimatorThrottling\">0</token>\n\t\t\t<string name=\"CollisionGroups\">Default^0^-1</string>\n\t\t\t<Ref name=\"CurrentCamera\">RBX49A913261C9245E695B612B56BEBFFA5</Ref>\n\t\t\t<double name=\"DistributedGameTime\">0</double>\n\t\t\t<bool name=\"ExplicitAutoJoints\">true</bool>\n\t\t\t<float name=\"FallenPartsDestroyHeight\">-500</float>\n\t\t\t<float name=\"Gravity\">196.199997</float>\n\t\t\t<token name=\"HumanoidOnlySetCollisionsOnStateChange\">0</token>\n\t\t\t<token name=\"InterpolationThrottling\">0</token>\n\t\t\t<token name=\"LevelOfDetail\">0</token>\n\t\t\t<token name=\"MeshPartHeadsAndAccessories\">0</token>\n\t\t\t<CoordinateFrame name=\"ModelMeshCFrame\">\n\t\t\t\t<X>0</X>\n\t\t\t\t<Y>0</Y>\n\t\t\t\t<Z>0</Z>\n\t\t\t\t<R00>1</R00>\n\t\t\t\t<R01>0</R01>\n\t\t\t\t<R02>0</R02>\n\t\t\t\t<R10>0</R10>\n\t\t\t\t<R11>1</R11>\n\t\t\t\t<R12>0</R12>\n\t\t\t\t<R20>0</R20>\n\t\t\t\t<R21>0</R21>\n\t\t\t\t<R22>1</R22>\n\t\t\t</CoordinateFrame>\n\t\t\t<SharedString name=\"ModelMeshData\">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>\n\t\t\t<Vector3 name=\"ModelMeshSize\">\n\t\t\t\t<X>0</X>\n\t\t\t\t<Y>0</Y>\n\t\t\t\t<Z>0</Z>\n\t\t\t</Vector3>\n\t\t\t<string name=\"Name\">Workspace</string>\n\t\t\t<bool name=\"NeedsPivotMigration\">false</bool>\n\t\t\t<token name=\"PhysicsSteppingMethod\">0</token>\n\t\t\t<Ref name=\"PrimaryPart\">null</Ref>\n\t\t\t<token name=\"Retargeting\">0</token>\n\t\t\t<token name=\"SignalBehavior\">0</token>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<token name=\"StreamOutBehavior\">0</token>\n\t\t\t<bool name=\"StreamingEnabled\">false</bool>\n\t\t\t<int name=\"StreamingMinRadius\">64</int>\n\t\t\t<token name=\"StreamingPauseMode\">0</token>\n\t\t\t<int name=\"StreamingTargetRadius\">1024</int>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<bool name=\"TerrainWeldsFixed\">true</bool>\n\t\t\t<bool name=\"TouchesUseCollisionGroups\">false</bool>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00017037</UniqueId>\n\t\t\t<OptionalCoordinateFrame name=\"WorldPivotData\">\n\t\t\t\t<CFrame>\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CFrame>\n\t\t\t</OptionalCoordinateFrame>\n\t\t</Properties>\n\t\t<Item class=\"Camera\" referent=\"RBX49A913261C9245E695B612B56BEBFFA5\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t<X>39.099472</X>\n\t\t\t\t\t<Y>31.9098797</Y>\n\t\t\t\t\t<Z>49.9157181</Z>\n\t\t\t\t\t<R00>0.902323961</R00>\n\t\t\t\t\t<R01>-0.183218867</R01>\n\t\t\t\t\t<R02>0.390182436</R02>\n\t\t\t\t\t<R10>-0</R10>\n\t\t\t\t\t<R11>0.905172706</R11>\n\t\t\t\t\t<R12>0.425044</R12>\n\t\t\t\t\t<R20>-0.431058586</R20>\n\t\t\t\t\t<R21>-0.383527398</R21>\n\t\t\t\t\t<R22>0.81675899</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<Ref name=\"CameraSubject\">null</Ref>\n\t\t\t\t<token name=\"CameraType\">0</token>\n\t\t\t\t<float name=\"FieldOfView\">70</float>\n\t\t\t\t<token name=\"FieldOfViewMode\">0</token>\n\t\t\t\t<CoordinateFrame name=\"Focus\">\n\t\t\t\t\t<X>38.3191071</X>\n\t\t\t\t\t<Y>31.0597916</Y>\n\t\t\t\t\t<Z>48.2821999</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<bool name=\"HeadLocked\">true</bool>\n\t\t\t\t<float name=\"HeadScale\">1</float>\n\t\t\t\t<string name=\"Name\">Camera</string>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001851d</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t\t<Item class=\"Part\" referent=\"RBXF1158A85558B42F4873C8C9FDF9707A9\">\n\t\t\t<Properties>\n\t\t\t\t<bool name=\"Anchored\">true</bool>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<float name=\"BackParamA\">-0.5</float>\n\t\t\t\t<float name=\"BackParamB\">0.5</float>\n\t\t\t\t<token name=\"BackSurface\">0</token>\n\t\t\t\t<token name=\"BackSurfaceInput\">0</token>\n\t\t\t\t<float name=\"BottomParamA\">-0.5</float>\n\t\t\t\t<float name=\"BottomParamB\">0.5</float>\n\t\t\t\t<token name=\"BottomSurface\">0</token>\n\t\t\t\t<token name=\"BottomSurfaceInput\">0</token>\n\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>-2</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<bool name=\"CanCollide\">true</bool>\n\t\t\t\t<bool name=\"CanQuery\">true</bool>\n\t\t\t\t<bool name=\"CanTouch\">true</bool>\n\t\t\t\t<bool name=\"CastShadow\">true</bool>\n\t\t\t\t<int name=\"CollisionGroupId\">0</int>\n\t\t\t\t<Color3uint8 name=\"Color3uint8\">4294506744</Color3uint8>\n\t\t\t\t<PhysicalProperties name=\"CustomPhysicalProperties\">\n\t\t\t\t\t<CustomPhysics>false</CustomPhysics>\n\t\t\t\t</PhysicalProperties>\n\t\t\t\t<float name=\"FrontParamA\">-0.5</float>\n\t\t\t\t<float name=\"FrontParamB\">0.5</float>\n\t\t\t\t<token name=\"FrontSurface\">0</token>\n\t\t\t\t<token name=\"FrontSurfaceInput\">0</token>\n\t\t\t\t<float name=\"LeftParamA\">-0.5</float>\n\t\t\t\t<float name=\"LeftParamB\">0.5</float>\n\t\t\t\t<token name=\"LeftSurface\">0</token>\n\t\t\t\t<token name=\"LeftSurfaceInput\">0</token>\n\t\t\t\t<bool name=\"Locked\">true</bool>\n\t\t\t\t<bool name=\"Massless\">false</bool>\n\t\t\t\t<token name=\"Material\">848</token>\n\t\t\t\t<string name=\"Name\">Ground</string>\n\t\t\t\t<CoordinateFrame name=\"PivotOffset\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<float name=\"Reflectance\">0</float>\n\t\t\t\t<float name=\"RightParamA\">-0.5</float>\n\t\t\t\t<float name=\"RightParamB\">0.5</float>\n\t\t\t\t<token name=\"RightSurface\">0</token>\n\t\t\t\t<token name=\"RightSurfaceInput\">0</token>\n\t\t\t\t<int name=\"RootPriority\">0</int>\n\t\t\t\t<Vector3 name=\"RotVelocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<float name=\"TopParamA\">-0.5</float>\n\t\t\t\t<float name=\"TopParamB\">0.5</float>\n\t\t\t\t<token name=\"TopSurface\">0</token>\n\t\t\t\t<token name=\"TopSurfaceInput\">0</token>\n\t\t\t\t<float name=\"Transparency\">0</float>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018523</UniqueId>\n\t\t\t\t<Vector3 name=\"Velocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<token name=\"formFactorRaw\">0</token>\n\t\t\t\t<token name=\"shape\">1</token>\n\t\t\t\t<Vector3 name=\"size\">\n\t\t\t\t\t<X>100</X>\n\t\t\t\t\t<Y>4</Y>\n\t\t\t\t\t<Z>32</Z>\n\t\t\t\t</Vector3>\n\t\t\t</Properties>\n\t\t\t<Item class=\"Decal\" referent=\"RBX76E9E5A12BFB4A57B7BB54CB2EC730DF\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<Color3 name=\"Color3\">\n\t\t\t\t\t\t<R>1</R>\n\t\t\t\t\t\t<G>1</G>\n\t\t\t\t\t\t<B>1</B>\n\t\t\t\t\t</Color3>\n\t\t\t\t\t<token name=\"Face\">1</token>\n\t\t\t\t\t<string name=\"Name\">Decal</string>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<Content name=\"Texture\"><url>rbxassetid://7796413907</url></Content>\n\t\t\t\t\t<float name=\"Transparency\">0</float>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00036a2f</UniqueId>\n\t\t\t\t\t<int name=\"ZIndex\">1</int>\n\t\t\t\t</Properties>\n\t\t\t</Item>\n\t\t</Item>\n\t\t<Item class=\"Terrain\" referent=\"RBXC72989D0705745C5A0B275C00421E0A5\">\n\t\t\t<Properties>\n\t\t\t\t<token name=\"AcquisitionMethod\">0</token>\n\t\t\t\t<bool name=\"Anchored\">true</bool>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<float name=\"BackParamA\">-0.5</float>\n\t\t\t\t<float name=\"BackParamB\">0.5</float>\n\t\t\t\t<token name=\"BackSurface\">0</token>\n\t\t\t\t<token name=\"BackSurfaceInput\">0</token>\n\t\t\t\t<float name=\"BottomParamA\">-0.5</float>\n\t\t\t\t<float name=\"BottomParamB\">0.5</float>\n\t\t\t\t<token name=\"BottomSurface\">4</token>\n\t\t\t\t<token name=\"BottomSurfaceInput\">0</token>\n\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<bool name=\"CanCollide\">true</bool>\n\t\t\t\t<bool name=\"CanQuery\">true</bool>\n\t\t\t\t<bool name=\"CanTouch\">true</bool>\n\t\t\t\t<bool name=\"CastShadow\">true</bool>\n\t\t\t\t<int name=\"CollisionGroupId\">0</int>\n\t\t\t\t<Color3uint8 name=\"Color3uint8\">4288914085</Color3uint8>\n\t\t\t\t<PhysicalProperties name=\"CustomPhysicalProperties\">\n\t\t\t\t\t<CustomPhysics>false</CustomPhysics>\n\t\t\t\t</PhysicalProperties>\n\t\t\t\t<bool name=\"Decoration\">true</bool>\n\t\t\t\t<float name=\"FrontParamA\">-0.5</float>\n\t\t\t\t<float name=\"FrontParamB\">0.5</float>\n\t\t\t\t<token name=\"FrontSurface\">0</token>\n\t\t\t\t<token name=\"FrontSurfaceInput\">0</token>\n\t\t\t\t<float name=\"LeftParamA\">-0.5</float>\n\t\t\t\t<float name=\"LeftParamB\">0.5</float>\n\t\t\t\t<token name=\"LeftSurface\">0</token>\n\t\t\t\t<token name=\"LeftSurfaceInput\">0</token>\n\t\t\t\t<bool name=\"Locked\">true</bool>\n\t\t\t\t<bool name=\"Massless\">false</bool>\n\t\t\t\t<token name=\"Material\">256</token>\n\t\t\t\t<BinaryString name=\"MaterialColors\"><![CDATA[AAAAAAAAan8/P39rf2Y/ilY+j35fi21PZmxvZbDqw8faiVpHOi4kHh4lZlw76JxKc3trhHta\ngcLgc4RKxr21zq2UlJSM]]></BinaryString>\n\t\t\t\t<string name=\"Name\">Terrain</string>\n\t\t\t\t<BinaryString name=\"PhysicsGrid\">AgMAAAAAAAAAAAAAAAA=</BinaryString>\n\t\t\t\t<CoordinateFrame name=\"PivotOffset\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<float name=\"Reflectance\">0</float>\n\t\t\t\t<float name=\"RightParamA\">-0.5</float>\n\t\t\t\t<float name=\"RightParamB\">0.5</float>\n\t\t\t\t<token name=\"RightSurface\">0</token>\n\t\t\t\t<token name=\"RightSurfaceInput\">0</token>\n\t\t\t\t<int name=\"RootPriority\">0</int>\n\t\t\t\t<Vector3 name=\"RotVelocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<BinaryString name=\"SmoothGrid\">AQU=</BinaryString>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<float name=\"TopParamA\">-0.5</float>\n\t\t\t\t<float name=\"TopParamB\">0.5</float>\n\t\t\t\t<token name=\"TopSurface\">3</token>\n\t\t\t\t<token name=\"TopSurfaceInput\">0</token>\n\t\t\t\t<float name=\"Transparency\">0</float>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001852b</UniqueId>\n\t\t\t\t<Vector3 name=\"Velocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<Color3 name=\"WaterColor\">\n\t\t\t\t\t<R>0.0470588282</R>\n\t\t\t\t\t<G>0.329411775</G>\n\t\t\t\t\t<B>0.360784322</B>\n\t\t\t\t</Color3>\n\t\t\t\t<float name=\"WaterReflectance\">1</float>\n\t\t\t\t<float name=\"WaterTransparency\">0.300000012</float>\n\t\t\t\t<float name=\"WaterWaveSize\">0.150000006</float>\n\t\t\t\t<float name=\"WaterWaveSpeed\">10</float>\n\t\t\t\t<Vector3 name=\"size\">\n\t\t\t\t\t<X>2044</X>\n\t\t\t\t\t<Y>252</Y>\n\t\t\t\t\t<Z>2044</Z>\n\t\t\t\t</Vector3>\n\t\t\t</Properties>\n\t\t</Item>\n\t\t<Item class=\"Model\" referent=\"RBXB772C9B898A14BCAB83DCC1DF34A1375\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<token name=\"LevelOfDetail\">0</token>\n\t\t\t\t<CoordinateFrame name=\"ModelMeshCFrame\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<SharedString name=\"ModelMeshData\">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>\n\t\t\t\t<Vector3 name=\"ModelMeshSize\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<string name=\"Name\">Wall</string>\n\t\t\t\t<bool name=\"NeedsPivotMigration\">false</bool>\n\t\t\t\t<Ref name=\"PrimaryPart\">null</Ref>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0009e4e8</UniqueId>\n\t\t\t\t<OptionalCoordinateFrame name=\"WorldPivotData\">\n\t\t\t\t\t<CFrame>\n\t\t\t\t\t\t<X>0.00240516663</X>\n\t\t\t\t\t\t<Y>0.52401495</Y>\n\t\t\t\t\t\t<Z>26.5799332</Z>\n\t\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t\t<R22>1</R22>\n\t\t\t\t\t</CFrame>\n\t\t\t\t</OptionalCoordinateFrame>\n\t\t\t</Properties>\n\t\t\t<Item class=\"Part\" referent=\"RBXE891A139D6F040948930F46B91AB342C\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<bool name=\"Anchored\">true</bool>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<float name=\"BackParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"BackParamB\">0.5</float>\n\t\t\t\t\t<token name=\"BackSurface\">0</token>\n\t\t\t\t\t<token name=\"BackSurfaceInput\">0</token>\n\t\t\t\t\t<float name=\"BottomParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"BottomParamB\">0.5</float>\n\t\t\t\t\t<token name=\"BottomSurface\">0</token>\n\t\t\t\t\t<token name=\"BottomSurfaceInput\">0</token>\n\t\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0.5</Y>\n\t\t\t\t\t\t<Z>15.5</Z>\n\t\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t\t<R22>1</R22>\n\t\t\t\t\t</CoordinateFrame>\n\t\t\t\t\t<bool name=\"CanCollide\">true</bool>\n\t\t\t\t\t<bool name=\"CanQuery\">true</bool>\n\t\t\t\t\t<bool name=\"CanTouch\">true</bool>\n\t\t\t\t\t<bool name=\"CastShadow\">true</bool>\n\t\t\t\t\t<int name=\"CollisionGroupId\">0</int>\n\t\t\t\t\t<Color3uint8 name=\"Color3uint8\">4294506744</Color3uint8>\n\t\t\t\t\t<PhysicalProperties name=\"CustomPhysicalProperties\">\n\t\t\t\t\t\t<CustomPhysics>false</CustomPhysics>\n\t\t\t\t\t</PhysicalProperties>\n\t\t\t\t\t<float name=\"FrontParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"FrontParamB\">0.5</float>\n\t\t\t\t\t<token name=\"FrontSurface\">0</token>\n\t\t\t\t\t<token name=\"FrontSurfaceInput\">0</token>\n\t\t\t\t\t<float name=\"LeftParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"LeftParamB\">0.5</float>\n\t\t\t\t\t<token name=\"LeftSurface\">0</token>\n\t\t\t\t\t<token name=\"LeftSurfaceInput\">0</token>\n\t\t\t\t\t<bool name=\"Locked\">false</bool>\n\t\t\t\t\t<bool name=\"Massless\">false</bool>\n\t\t\t\t\t<token name=\"Material\">848</token>\n\t\t\t\t\t<string name=\"Name\">back</string>\n\t\t\t\t\t<CoordinateFrame name=\"PivotOffset\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t\t<R22>1</R22>\n\t\t\t\t\t</CoordinateFrame>\n\t\t\t\t\t<float name=\"Reflectance\">0</float>\n\t\t\t\t\t<float name=\"RightParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"RightParamB\">0.5</float>\n\t\t\t\t\t<token name=\"RightSurface\">0</token>\n\t\t\t\t\t<token name=\"RightSurfaceInput\">0</token>\n\t\t\t\t\t<int name=\"RootPriority\">0</int>\n\t\t\t\t\t<Vector3 name=\"RotVelocity\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<float name=\"TopParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"TopParamB\">0.5</float>\n\t\t\t\t\t<token name=\"TopSurface\">0</token>\n\t\t\t\t\t<token name=\"TopSurfaceInput\">0</token>\n\t\t\t\t\t<float name=\"Transparency\">0</float>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0009c720</UniqueId>\n\t\t\t\t\t<Vector3 name=\"Velocity\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<token name=\"formFactorRaw\">1</token>\n\t\t\t\t\t<token name=\"shape\">1</token>\n\t\t\t\t\t<Vector3 name=\"size\">\n\t\t\t\t\t\t<X>100</X>\n\t\t\t\t\t\t<Y>1</Y>\n\t\t\t\t\t\t<Z>1</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t</Properties>\n\t\t\t</Item>\n\t\t\t<Item class=\"Part\" referent=\"RBX525E08FBF030488B9BA9426AB5418921\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<bool name=\"Anchored\">true</bool>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<float name=\"BackParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"BackParamB\">0.5</float>\n\t\t\t\t\t<token name=\"BackSurface\">0</token>\n\t\t\t\t\t<token name=\"BackSurfaceInput\">0</token>\n\t\t\t\t\t<float name=\"BottomParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"BottomParamB\">0.5</float>\n\t\t\t\t\t<token name=\"BottomSurface\">0</token>\n\t\t\t\t\t<token name=\"BottomSurfaceInput\">0</token>\n\t\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0.5</Y>\n\t\t\t\t\t\t<Z>-15.5</Z>\n\t\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t\t<R22>1</R22>\n\t\t\t\t\t</CoordinateFrame>\n\t\t\t\t\t<bool name=\"CanCollide\">true</bool>\n\t\t\t\t\t<bool name=\"CanQuery\">true</bool>\n\t\t\t\t\t<bool name=\"CanTouch\">true</bool>\n\t\t\t\t\t<bool name=\"CastShadow\">true</bool>\n\t\t\t\t\t<int name=\"CollisionGroupId\">0</int>\n\t\t\t\t\t<Color3uint8 name=\"Color3uint8\">4294506744</Color3uint8>\n\t\t\t\t\t<PhysicalProperties name=\"CustomPhysicalProperties\">\n\t\t\t\t\t\t<CustomPhysics>false</CustomPhysics>\n\t\t\t\t\t</PhysicalProperties>\n\t\t\t\t\t<float name=\"FrontParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"FrontParamB\">0.5</float>\n\t\t\t\t\t<token name=\"FrontSurface\">0</token>\n\t\t\t\t\t<token name=\"FrontSurfaceInput\">0</token>\n\t\t\t\t\t<float name=\"LeftParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"LeftParamB\">0.5</float>\n\t\t\t\t\t<token name=\"LeftSurface\">0</token>\n\t\t\t\t\t<token name=\"LeftSurfaceInput\">0</token>\n\t\t\t\t\t<bool name=\"Locked\">false</bool>\n\t\t\t\t\t<bool name=\"Massless\">false</bool>\n\t\t\t\t\t<token name=\"Material\">848</token>\n\t\t\t\t\t<string name=\"Name\">front</string>\n\t\t\t\t\t<CoordinateFrame name=\"PivotOffset\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t\t<R22>1</R22>\n\t\t\t\t\t</CoordinateFrame>\n\t\t\t\t\t<float name=\"Reflectance\">0</float>\n\t\t\t\t\t<float name=\"RightParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"RightParamB\">0.5</float>\n\t\t\t\t\t<token name=\"RightSurface\">0</token>\n\t\t\t\t\t<token name=\"RightSurfaceInput\">0</token>\n\t\t\t\t\t<int name=\"RootPriority\">0</int>\n\t\t\t\t\t<Vector3 name=\"RotVelocity\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<float name=\"TopParamA\">-0.5</float>\n\t\t\t\t\t<float name=\"TopParamB\">0.5</float>\n\t\t\t\t\t<token name=\"TopSurface\">0</token>\n\t\t\t\t\t<token name=\"TopSurfaceInput\">0</token>\n\t\t\t\t\t<float name=\"Transparency\">0</float>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0009e7e9</UniqueId>\n\t\t\t\t\t<Vector3 name=\"Velocity\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<token name=\"formFactorRaw\">1</token>\n\t\t\t\t\t<token name=\"shape\">1</token>\n\t\t\t\t\t<Vector3 name=\"size\">\n\t\t\t\t\t\t<X>100</X>\n\t\t\t\t\t\t<Y>1</Y>\n\t\t\t\t\t\t<Z>1</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t</Properties>\n\t\t\t</Item>\n\t\t</Item>\n\t\t<Item class=\"WedgePart\" referent=\"RBX83608662DF7B46538D287024252E478D\">\n\t\t\t<Properties>\n\t\t\t\t<bool name=\"Anchored\">true</bool>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<float name=\"BackParamA\">-0.5</float>\n\t\t\t\t<float name=\"BackParamB\">0.5</float>\n\t\t\t\t<token name=\"BackSurface\">0</token>\n\t\t\t\t<token name=\"BackSurfaceInput\">0</token>\n\t\t\t\t<float name=\"BottomParamA\">-0.5</float>\n\t\t\t\t<float name=\"BottomParamB\">0.5</float>\n\t\t\t\t<token name=\"BottomSurface\">0</token>\n\t\t\t\t<token name=\"BottomSurfaceInput\">0</token>\n\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t<X>-42</X>\n\t\t\t\t\t<Y>3</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>-4.37113883e-08</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>1</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>-1</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>-4.37113883e-08</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<bool name=\"CanCollide\">true</bool>\n\t\t\t\t<bool name=\"CanQuery\">true</bool>\n\t\t\t\t<bool name=\"CanTouch\">true</bool>\n\t\t\t\t<bool name=\"CastShadow\">false</bool>\n\t\t\t\t<int name=\"CollisionGroupId\">0</int>\n\t\t\t\t<Color3uint8 name=\"Color3uint8\">4280374457</Color3uint8>\n\t\t\t\t<PhysicalProperties name=\"CustomPhysicalProperties\">\n\t\t\t\t\t<CustomPhysics>false</CustomPhysics>\n\t\t\t\t</PhysicalProperties>\n\t\t\t\t<float name=\"FrontParamA\">-0.5</float>\n\t\t\t\t<float name=\"FrontParamB\">0.5</float>\n\t\t\t\t<token name=\"FrontSurface\">0</token>\n\t\t\t\t<token name=\"FrontSurfaceInput\">0</token>\n\t\t\t\t<float name=\"LeftParamA\">-0.5</float>\n\t\t\t\t<float name=\"LeftParamB\">0.5</float>\n\t\t\t\t<token name=\"LeftSurface\">0</token>\n\t\t\t\t<token name=\"LeftSurfaceInput\">0</token>\n\t\t\t\t<bool name=\"Locked\">false</bool>\n\t\t\t\t<bool name=\"Massless\">false</bool>\n\t\t\t\t<token name=\"Material\">288</token>\n\t\t\t\t<string name=\"Name\">goal_left</string>\n\t\t\t\t<CoordinateFrame name=\"PivotOffset\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<float name=\"Reflectance\">0</float>\n\t\t\t\t<float name=\"RightParamA\">-0.5</float>\n\t\t\t\t<float name=\"RightParamB\">0.5</float>\n\t\t\t\t<token name=\"RightSurface\">0</token>\n\t\t\t\t<token name=\"RightSurfaceInput\">0</token>\n\t\t\t\t<int name=\"RootPriority\">0</int>\n\t\t\t\t<Vector3 name=\"RotVelocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<float name=\"TopParamA\">-0.5</float>\n\t\t\t\t<float name=\"TopParamB\">0.5</float>\n\t\t\t\t<token name=\"TopSurface\">0</token>\n\t\t\t\t<token name=\"TopSurfaceInput\">0</token>\n\t\t\t\t<float name=\"Transparency\">0.5</float>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000ba532</UniqueId>\n\t\t\t\t<Vector3 name=\"Velocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<token name=\"formFactorRaw\">1</token>\n\t\t\t\t<Vector3 name=\"size\">\n\t\t\t\t\t<X>30</X>\n\t\t\t\t\t<Y>6</Y>\n\t\t\t\t\t<Z>12</Z>\n\t\t\t\t</Vector3>\n\t\t\t</Properties>\n\t\t\t<Item class=\"BillboardGui\" referent=\"RBXBFFC7DDAFAF64B6098501B285E516AB9\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<bool name=\"Active\">true</bool>\n\t\t\t\t\t<Ref name=\"Adornee\">null</Ref>\n\t\t\t\t\t<bool name=\"AlwaysOnTop\">false</bool>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<bool name=\"AutoLocalize\">true</bool>\n\t\t\t\t\t<float name=\"Brightness\">1</float>\n\t\t\t\t\t<bool name=\"ClipsDescendants\">false</bool>\n\t\t\t\t\t<float name=\"DistanceLowerLimit\">0</float>\n\t\t\t\t\t<float name=\"DistanceStep\">0</float>\n\t\t\t\t\t<float name=\"DistanceUpperLimit\">-1</float>\n\t\t\t\t\t<bool name=\"Enabled\">true</bool>\n\t\t\t\t\t<Vector3 name=\"ExtentsOffset\">\n\t\t\t\t\t\t<X>-1</X>\n\t\t\t\t\t\t<Y>0.5</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<Vector3 name=\"ExtentsOffsetWorldSpace\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<float name=\"LightInfluence\">0</float>\n\t\t\t\t\t<float name=\"MaxDistance\">INF</float>\n\t\t\t\t\t<string name=\"Name\">BillboardGui</string>\n\t\t\t\t\t<Ref name=\"PlayerToHideFrom\">null</Ref>\n\t\t\t\t\t<bool name=\"ResetOnSpawn\">true</bool>\n\t\t\t\t\t<Ref name=\"RootLocalizationTable\">null</Ref>\n\t\t\t\t\t<UDim2 name=\"Size\">\n\t\t\t\t\t\t<XS>0</XS>\n\t\t\t\t\t\t<XO>200</XO>\n\t\t\t\t\t\t<YS>0</YS>\n\t\t\t\t\t\t<YO>50</YO>\n\t\t\t\t\t</UDim2>\n\t\t\t\t\t<Vector2 name=\"SizeOffset\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t</Vector2>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<Vector3 name=\"StudsOffset\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<Vector3 name=\"StudsOffsetWorldSpace\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00d5bb66</UniqueId>\n\t\t\t\t\t<token name=\"ZIndexBehavior\">1</token>\n\t\t\t\t</Properties>\n\t\t\t\t<Item class=\"TextLabel\" referent=\"RBX3ACF9E26A08B4F32A4211ECE4A5010ED\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<bool name=\"Active\">false</bool>\n\t\t\t\t\t\t<Vector2 name=\"AnchorPoint\">\n\t\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t</Vector2>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<bool name=\"AutoLocalize\">false</bool>\n\t\t\t\t\t\t<token name=\"AutomaticSize\">0</token>\n\t\t\t\t\t\t<Color3 name=\"BackgroundColor3\">\n\t\t\t\t\t\t\t<R>1</R>\n\t\t\t\t\t\t\t<G>1</G>\n\t\t\t\t\t\t\t<B>1</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<float name=\"BackgroundTransparency\">1</float>\n\t\t\t\t\t\t<Color3 name=\"BorderColor3\">\n\t\t\t\t\t\t\t<R>0.105882362</R>\n\t\t\t\t\t\t\t<G>0.164705887</G>\n\t\t\t\t\t\t\t<B>0.207843155</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<token name=\"BorderMode\">2</token>\n\t\t\t\t\t\t<int name=\"BorderSizePixel\">0</int>\n\t\t\t\t\t\t<bool name=\"ClipsDescendants\">false</bool>\n\t\t\t\t\t\t<bool name=\"Draggable\">false</bool>\n\t\t\t\t\t\t<token name=\"Font\">13</token>\n\t\t\t\t\t\t<int name=\"LayoutOrder\">0</int>\n\t\t\t\t\t\t<float name=\"LineHeight\">1</float>\n\t\t\t\t\t\t<int name=\"MaxVisibleGraphemes\">-1</int>\n\t\t\t\t\t\t<string name=\"Name\">TextLabel</string>\n\t\t\t\t\t\t<Ref name=\"NextSelectionDown\">null</Ref>\n\t\t\t\t\t\t<Ref name=\"NextSelectionLeft\">null</Ref>\n\t\t\t\t\t\t<Ref name=\"NextSelectionRight\">null</Ref>\n\t\t\t\t\t\t<Ref name=\"NextSelectionUp\">null</Ref>\n\t\t\t\t\t\t<UDim2 name=\"Position\">\n\t\t\t\t\t\t\t<XS>0</XS>\n\t\t\t\t\t\t\t<XO>0</XO>\n\t\t\t\t\t\t\t<YS>0</YS>\n\t\t\t\t\t\t\t<YO>0</YO>\n\t\t\t\t\t\t</UDim2>\n\t\t\t\t\t\t<bool name=\"RichText\">false</bool>\n\t\t\t\t\t\t<Ref name=\"RootLocalizationTable\">null</Ref>\n\t\t\t\t\t\t<float name=\"Rotation\">0</float>\n\t\t\t\t\t\t<bool name=\"Selectable\">false</bool>\n\t\t\t\t\t\t<Ref name=\"SelectionImageObject\">null</Ref>\n\t\t\t\t\t\t<UDim2 name=\"Size\">\n\t\t\t\t\t\t\t<XS>0</XS>\n\t\t\t\t\t\t\t<XO>200</XO>\n\t\t\t\t\t\t\t<YS>0</YS>\n\t\t\t\t\t\t\t<YO>50</YO>\n\t\t\t\t\t\t</UDim2>\n\t\t\t\t\t\t<token name=\"SizeConstraint\">0</token>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<string name=\"Text\">0</string>\n\t\t\t\t\t\t<Color3 name=\"TextColor3\">\n\t\t\t\t\t\t\t<R>1</R>\n\t\t\t\t\t\t\t<G>1</G>\n\t\t\t\t\t\t\t<B>1</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<bool name=\"TextScaled\">false</bool>\n\t\t\t\t\t\t<float name=\"TextSize\">100</float>\n\t\t\t\t\t\t<Color3 name=\"TextStrokeColor3\">\n\t\t\t\t\t\t\t<R>0.129411772</R>\n\t\t\t\t\t\t\t<G>0.329411775</G>\n\t\t\t\t\t\t\t<B>0.725490212</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<float name=\"TextStrokeTransparency\">0</float>\n\t\t\t\t\t\t<float name=\"TextTransparency\">0</float>\n\t\t\t\t\t\t<token name=\"TextTruncate\">0</token>\n\t\t\t\t\t\t<bool name=\"TextWrapped\">false</bool>\n\t\t\t\t\t\t<token name=\"TextXAlignment\">2</token>\n\t\t\t\t\t\t<token name=\"TextYAlignment\">1</token>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00d5bd30</UniqueId>\n\t\t\t\t\t\t<bool name=\"Visible\">true</bool>\n\t\t\t\t\t\t<int name=\"ZIndex\">1</int>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t</Item>\n\t\t</Item>\n\t\t<Item class=\"WedgePart\" referent=\"RBXD7A0E9BA43EE407BA73087D2E116E84A\">\n\t\t\t<Properties>\n\t\t\t\t<bool name=\"Anchored\">true</bool>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<float name=\"BackParamA\">-0.5</float>\n\t\t\t\t<float name=\"BackParamB\">0.5</float>\n\t\t\t\t<token name=\"BackSurface\">0</token>\n\t\t\t\t<token name=\"BackSurfaceInput\">0</token>\n\t\t\t\t<float name=\"BottomParamA\">-0.5</float>\n\t\t\t\t<float name=\"BottomParamB\">0.5</float>\n\t\t\t\t<token name=\"BottomSurface\">0</token>\n\t\t\t\t<token name=\"BottomSurfaceInput\">0</token>\n\t\t\t\t<CoordinateFrame name=\"CFrame\">\n\t\t\t\t\t<X>42</X>\n\t\t\t\t\t<Y>3</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>0</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>-0.99999994</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>1</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>0</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<bool name=\"CanCollide\">true</bool>\n\t\t\t\t<bool name=\"CanQuery\">true</bool>\n\t\t\t\t<bool name=\"CanTouch\">true</bool>\n\t\t\t\t<bool name=\"CastShadow\">false</bool>\n\t\t\t\t<int name=\"CollisionGroupId\">0</int>\n\t\t\t\t<Color3uint8 name=\"Color3uint8\">4294924633</Color3uint8>\n\t\t\t\t<PhysicalProperties name=\"CustomPhysicalProperties\">\n\t\t\t\t\t<CustomPhysics>false</CustomPhysics>\n\t\t\t\t</PhysicalProperties>\n\t\t\t\t<float name=\"FrontParamA\">-0.5</float>\n\t\t\t\t<float name=\"FrontParamB\">0.5</float>\n\t\t\t\t<token name=\"FrontSurface\">0</token>\n\t\t\t\t<token name=\"FrontSurfaceInput\">0</token>\n\t\t\t\t<float name=\"LeftParamA\">-0.5</float>\n\t\t\t\t<float name=\"LeftParamB\">0.5</float>\n\t\t\t\t<token name=\"LeftSurface\">0</token>\n\t\t\t\t<token name=\"LeftSurfaceInput\">0</token>\n\t\t\t\t<bool name=\"Locked\">false</bool>\n\t\t\t\t<bool name=\"Massless\">false</bool>\n\t\t\t\t<token name=\"Material\">288</token>\n\t\t\t\t<string name=\"Name\">goal_right</string>\n\t\t\t\t<CoordinateFrame name=\"PivotOffset\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t<R00>1</R00>\n\t\t\t\t\t<R01>0</R01>\n\t\t\t\t\t<R02>0</R02>\n\t\t\t\t\t<R10>0</R10>\n\t\t\t\t\t<R11>1</R11>\n\t\t\t\t\t<R12>0</R12>\n\t\t\t\t\t<R20>0</R20>\n\t\t\t\t\t<R21>0</R21>\n\t\t\t\t\t<R22>1</R22>\n\t\t\t\t</CoordinateFrame>\n\t\t\t\t<float name=\"Reflectance\">0</float>\n\t\t\t\t<float name=\"RightParamA\">-0.5</float>\n\t\t\t\t<float name=\"RightParamB\">0.5</float>\n\t\t\t\t<token name=\"RightSurface\">0</token>\n\t\t\t\t<token name=\"RightSurfaceInput\">0</token>\n\t\t\t\t<int name=\"RootPriority\">0</int>\n\t\t\t\t<Vector3 name=\"RotVelocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<float name=\"TopParamA\">-0.5</float>\n\t\t\t\t<float name=\"TopParamB\">0.5</float>\n\t\t\t\t<token name=\"TopSurface\">0</token>\n\t\t\t\t<token name=\"TopSurfaceInput\">0</token>\n\t\t\t\t<float name=\"Transparency\">0.5</float>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000bc996</UniqueId>\n\t\t\t\t<Vector3 name=\"Velocity\">\n\t\t\t\t\t<X>0</X>\n\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t<Z>0</Z>\n\t\t\t\t</Vector3>\n\t\t\t\t<token name=\"formFactorRaw\">1</token>\n\t\t\t\t<Vector3 name=\"size\">\n\t\t\t\t\t<X>30</X>\n\t\t\t\t\t<Y>6</Y>\n\t\t\t\t\t<Z>12</Z>\n\t\t\t\t</Vector3>\n\t\t\t</Properties>\n\t\t\t<Item class=\"BillboardGui\" referent=\"RBXE48EA69614EE405E868677561CEE6844\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<bool name=\"Active\">true</bool>\n\t\t\t\t\t<Ref name=\"Adornee\">null</Ref>\n\t\t\t\t\t<bool name=\"AlwaysOnTop\">false</bool>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<bool name=\"AutoLocalize\">true</bool>\n\t\t\t\t\t<float name=\"Brightness\">1</float>\n\t\t\t\t\t<bool name=\"ClipsDescendants\">false</bool>\n\t\t\t\t\t<float name=\"DistanceLowerLimit\">0</float>\n\t\t\t\t\t<float name=\"DistanceStep\">0</float>\n\t\t\t\t\t<float name=\"DistanceUpperLimit\">-1</float>\n\t\t\t\t\t<bool name=\"Enabled\">true</bool>\n\t\t\t\t\t<Vector3 name=\"ExtentsOffset\">\n\t\t\t\t\t\t<X>1</X>\n\t\t\t\t\t\t<Y>0.5</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<Vector3 name=\"ExtentsOffsetWorldSpace\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<float name=\"LightInfluence\">0</float>\n\t\t\t\t\t<float name=\"MaxDistance\">INF</float>\n\t\t\t\t\t<string name=\"Name\">BillboardGui</string>\n\t\t\t\t\t<Ref name=\"PlayerToHideFrom\">null</Ref>\n\t\t\t\t\t<bool name=\"ResetOnSpawn\">true</bool>\n\t\t\t\t\t<Ref name=\"RootLocalizationTable\">null</Ref>\n\t\t\t\t\t<UDim2 name=\"Size\">\n\t\t\t\t\t\t<XS>0</XS>\n\t\t\t\t\t\t<XO>200</XO>\n\t\t\t\t\t\t<YS>0</YS>\n\t\t\t\t\t\t<YO>50</YO>\n\t\t\t\t\t</UDim2>\n\t\t\t\t\t<Vector2 name=\"SizeOffset\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t</Vector2>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<Vector3 name=\"StudsOffset\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<Vector3 name=\"StudsOffsetWorldSpace\">\n\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t<Z>0</Z>\n\t\t\t\t\t</Vector3>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00d84e6e</UniqueId>\n\t\t\t\t\t<token name=\"ZIndexBehavior\">1</token>\n\t\t\t\t</Properties>\n\t\t\t\t<Item class=\"TextLabel\" referent=\"RBX0F9F32CE94894B0A95B664D67D12F3CF\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<bool name=\"Active\">false</bool>\n\t\t\t\t\t\t<Vector2 name=\"AnchorPoint\">\n\t\t\t\t\t\t\t<X>0</X>\n\t\t\t\t\t\t\t<Y>0</Y>\n\t\t\t\t\t\t</Vector2>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<bool name=\"AutoLocalize\">false</bool>\n\t\t\t\t\t\t<token name=\"AutomaticSize\">0</token>\n\t\t\t\t\t\t<Color3 name=\"BackgroundColor3\">\n\t\t\t\t\t\t\t<R>1</R>\n\t\t\t\t\t\t\t<G>1</G>\n\t\t\t\t\t\t\t<B>1</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<float name=\"BackgroundTransparency\">1</float>\n\t\t\t\t\t\t<Color3 name=\"BorderColor3\">\n\t\t\t\t\t\t\t<R>0.105882362</R>\n\t\t\t\t\t\t\t<G>0.164705887</G>\n\t\t\t\t\t\t\t<B>0.207843155</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<token name=\"BorderMode\">2</token>\n\t\t\t\t\t\t<int name=\"BorderSizePixel\">0</int>\n\t\t\t\t\t\t<bool name=\"ClipsDescendants\">false</bool>\n\t\t\t\t\t\t<bool name=\"Draggable\">false</bool>\n\t\t\t\t\t\t<token name=\"Font\">13</token>\n\t\t\t\t\t\t<int name=\"LayoutOrder\">0</int>\n\t\t\t\t\t\t<float name=\"LineHeight\">1</float>\n\t\t\t\t\t\t<int name=\"MaxVisibleGraphemes\">-1</int>\n\t\t\t\t\t\t<string name=\"Name\">TextLabel</string>\n\t\t\t\t\t\t<Ref name=\"NextSelectionDown\">null</Ref>\n\t\t\t\t\t\t<Ref name=\"NextSelectionLeft\">null</Ref>\n\t\t\t\t\t\t<Ref name=\"NextSelectionRight\">null</Ref>\n\t\t\t\t\t\t<Ref name=\"NextSelectionUp\">null</Ref>\n\t\t\t\t\t\t<UDim2 name=\"Position\">\n\t\t\t\t\t\t\t<XS>0</XS>\n\t\t\t\t\t\t\t<XO>0</XO>\n\t\t\t\t\t\t\t<YS>0</YS>\n\t\t\t\t\t\t\t<YO>0</YO>\n\t\t\t\t\t\t</UDim2>\n\t\t\t\t\t\t<bool name=\"RichText\">false</bool>\n\t\t\t\t\t\t<Ref name=\"RootLocalizationTable\">null</Ref>\n\t\t\t\t\t\t<float name=\"Rotation\">0</float>\n\t\t\t\t\t\t<bool name=\"Selectable\">false</bool>\n\t\t\t\t\t\t<Ref name=\"SelectionImageObject\">null</Ref>\n\t\t\t\t\t\t<UDim2 name=\"Size\">\n\t\t\t\t\t\t\t<XS>0</XS>\n\t\t\t\t\t\t\t<XO>200</XO>\n\t\t\t\t\t\t\t<YS>0</YS>\n\t\t\t\t\t\t\t<YO>50</YO>\n\t\t\t\t\t\t</UDim2>\n\t\t\t\t\t\t<token name=\"SizeConstraint\">0</token>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<string name=\"Text\">0</string>\n\t\t\t\t\t\t<Color3 name=\"TextColor3\">\n\t\t\t\t\t\t\t<R>1</R>\n\t\t\t\t\t\t\t<G>1</G>\n\t\t\t\t\t\t\t<B>1</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<bool name=\"TextScaled\">false</bool>\n\t\t\t\t\t\t<float name=\"TextSize\">100</float>\n\t\t\t\t\t\t<Color3 name=\"TextStrokeColor3\">\n\t\t\t\t\t\t\t<R>1</R>\n\t\t\t\t\t\t\t<G>0.349019617</G>\n\t\t\t\t\t\t\t<B>0.349019617</B>\n\t\t\t\t\t\t</Color3>\n\t\t\t\t\t\t<float name=\"TextStrokeTransparency\">0</float>\n\t\t\t\t\t\t<float name=\"TextTransparency\">0</float>\n\t\t\t\t\t\t<token name=\"TextTruncate\">0</token>\n\t\t\t\t\t\t<bool name=\"TextWrapped\">false</bool>\n\t\t\t\t\t\t<token name=\"TextXAlignment\">2</token>\n\t\t\t\t\t\t<token name=\"TextYAlignment\">1</token>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00d84e6f</UniqueId>\n\t\t\t\t\t\t<bool name=\"Visible\">true</bool>\n\t\t\t\t\t\t<int name=\"ZIndex\">1</int>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t</Item>\n\t\t</Item>\n\t\t<Item class=\"ColorCorrectionEffect\" referent=\"RBXD73CE853D5D74FDAAC1462D306479898\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<float name=\"Brightness\">0.300000012</float>\n\t\t\t\t<float name=\"Contrast\">0</float>\n\t\t\t\t<bool name=\"Enabled\">true</bool>\n\t\t\t\t<string name=\"Name\">ColorCorrection</string>\n\t\t\t\t<float name=\"Saturation\">0</float>\n\t\t\t\t<int64 name=\"SourceAssetId\">5446998963</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<Color3 name=\"TintColor\">\n\t\t\t\t\t<R>1</R>\n\t\t\t\t\t<G>1</G>\n\t\t\t\t\t<B>1</B>\n\t\t\t\t</Color3>\n\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00b1e4f3</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t</Item>\n\t<Item class=\"SoundService\" referent=\"RBXE5949EC6FE2045749A7B268AAF0ABA30\">\n\t\t<Properties>\n\t\t\t<token name=\"AmbientReverb\">0</token>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<float name=\"DistanceFactor\">3.32999992</float>\n\t\t\t<float name=\"DopplerScale\">1</float>\n\t\t\t<string name=\"Name\">SoundService</string>\n\t\t\t<bool name=\"RespectFilteringEnabled\">true</bool>\n\t\t\t<float name=\"RolloffScale\">1</float>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018287</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"NonReplicatedCSGDictionaryService\" referent=\"RBX26484C62FA1F4A06AEBBFF2BC2176EA6\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">NonReplicatedCSGDictionaryService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018293</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"CSGDictionaryService\" referent=\"RBX20BD418A9E9844A49E5C90C1BDFC8365\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">CSGDictionaryService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018294</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Chat\" referent=\"RBX5BA112B07BC349FE9CBC0806A3A3B8D3\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"BubbleChatEnabled\">true</bool>\n\t\t\t<bool name=\"LoadDefaultChat\">true</bool>\n\t\t\t<string name=\"Name\">Chat</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018298</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"TimerService\" referent=\"RBXA360D83505BC49F9A52841A3F79A49DC\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Instance</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018299</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Players\" referent=\"RBX11000AF141734C20AEE631EAAECC8BC9\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"CharacterAutoLoads\">true</bool>\n\t\t\t<int name=\"MaxPlayersInternal\">30</int>\n\t\t\t<string name=\"Name\">Players</string>\n\t\t\t<int name=\"PreferredPlayersInternal\">30</int>\n\t\t\t<float name=\"RespawnTime\">3</float>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001829b</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ReplicatedFirst\" referent=\"RBX246243EEB6154E1D8DB74C3A0954EFF9\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">ReplicatedFirst</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001829f</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"TweenService\" referent=\"RBX3BBC5BFCC1A24EC592510AD2B07287D7\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">TweenService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182a1</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"PermissionsService\" referent=\"RBX4015366983DE4172B2D2C6333414A6FA\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">PermissionsService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182a5</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"PlayerEmulatorService\" referent=\"RBX00D4E8FA11574F67BE3691173A94E4B0\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"CustomPoliciesEnabled\">false</bool>\n\t\t\t<string name=\"EmulatedCountryCode\"></string>\n\t\t\t<string name=\"EmulatedGameLocale\"></string>\n\t\t\t<string name=\"Name\">PlayerEmulatorService</string>\n\t\t\t<bool name=\"PlayerEmulationEnabled\">false</bool>\n\t\t\t<BinaryString name=\"SerializedEmulatedPolicyInfo\"></BinaryString>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182a7</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"StudioData\" referent=\"RBXC7DCE67B720E4C29B5C489599497C1DF\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<int64 name=\"CommitInflightAuthorId\">0</int64>\n\t\t\t<string name=\"CommitInflightGuid\"></string>\n\t\t\t<int name=\"CommitInflightPlaceVersion\">0</int>\n\t\t\t<bool name=\"EnableScriptCollabByDefaultOnLoad\">true</bool>\n\t\t\t<bool name=\"EnableTeamCreateStreamingOnLoad\">false</bool>\n\t\t\t<string name=\"Name\">StudioData</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<int64 name=\"SrcPlaceId\">7782218223</int64>\n\t\t\t<int64 name=\"SrcUniverseId\">3016370887</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182a9</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"StarterPlayer\" referent=\"RBXF7E673CF6D1147968774AF1D9D0C0E52\">\n\t\t<Properties>\n\t\t\t<bool name=\"AllowCustomAnimations\">true</bool>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"AutoJumpEnabled\">true</bool>\n\t\t\t<float name=\"CameraMaxZoomDistance\">128</float>\n\t\t\t<float name=\"CameraMinZoomDistance\">0.5</float>\n\t\t\t<token name=\"CameraMode\">0</token>\n\t\t\t<float name=\"CharacterJumpHeight\">7.19999981</float>\n\t\t\t<float name=\"CharacterJumpPower\">50</float>\n\t\t\t<float name=\"CharacterMaxSlopeAngle\">89</float>\n\t\t\t<bool name=\"CharacterUseJumpPower\">false</bool>\n\t\t\t<float name=\"CharacterWalkSpeed\">16</float>\n\t\t\t<token name=\"DevCameraOcclusionMode\">0</token>\n\t\t\t<token name=\"DevComputerCameraMovementMode\">0</token>\n\t\t\t<token name=\"DevComputerMovementMode\">0</token>\n\t\t\t<token name=\"DevTouchCameraMovementMode\">0</token>\n\t\t\t<token name=\"DevTouchMovementMode\">0</token>\n\t\t\t<bool name=\"EnableMouseLockOption\">true</bool>\n\t\t\t<int64 name=\"GameSettingsAssetIDFace\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDHead\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDLeftArm\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDLeftLeg\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDPants\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDRightArm\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDRightLeg\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDShirt\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDTeeShirt\">0</int64>\n\t\t\t<int64 name=\"GameSettingsAssetIDTorso\">0</int64>\n\t\t\t<token name=\"GameSettingsAvatar\">1</token>\n\t\t\t<token name=\"GameSettingsR15Collision\">0</token>\n\t\t\t<NumberRange name=\"GameSettingsScaleRangeBodyType\">0 1 </NumberRange>\n\t\t\t<NumberRange name=\"GameSettingsScaleRangeHead\">0.95 1 </NumberRange>\n\t\t\t<NumberRange name=\"GameSettingsScaleRangeHeight\">0.9 1.05 </NumberRange>\n\t\t\t<NumberRange name=\"GameSettingsScaleRangeProportion\">0 1 </NumberRange>\n\t\t\t<NumberRange name=\"GameSettingsScaleRangeWidth\">0.7 1 </NumberRange>\n\t\t\t<float name=\"HealthDisplayDistance\">100</float>\n\t\t\t<bool name=\"LoadCharacterAppearance\">true</bool>\n\t\t\t<token name=\"LoadCharacterLayeredClothing\">0</token>\n\t\t\t<string name=\"Name\">StarterPlayer</string>\n\t\t\t<float name=\"NameDisplayDistance\">100</float>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182aa</UniqueId>\n\t\t\t<bool name=\"UserEmotesEnabled\">true</bool>\n\t\t</Properties>\n\t\t<Item class=\"StarterPlayerScripts\" referent=\"RBXC3D24CBCA3E9438FACFDA357A9E8878C\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<string name=\"Name\">StarterPlayerScripts</string>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018527</UniqueId>\n\t\t\t</Properties>\n\t\t\t<Item class=\"Folder\" referent=\"RBXACD7C1E3ABAD469192A58FC2593D0D64\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<string name=\"Name\">systems</string>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f85</UniqueId>\n\t\t\t\t</Properties>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXC600C1BF378844B8BD6A936DA05C33AB\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">CameraSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{08DEC31C-22BE-4373-9BCD-ACD671DAEB63}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal CFRAME = CFrame.new(Vector3.new(0, Constants.CAMERA_DISTANCE, 30), Vector3.new(0, 0, 0))\n\n-- sistema de camera\nlocal CameraSystem = ECS.System(\"render\", 1, function()\n   game.Workspace.CurrentCamera.CFrame = CFRAME\nend)\n\nreturn CameraSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f87</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXF8A95B0F21154AB0BC653AC5C9E69F16\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">PaddleSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{DBA66A71-31B9-4248-83DF-62C288362DAD}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Paddle = require(Components.Paddle)\nlocal Position = require(Components.Position)\nlocal BasePart = require(Components.BasePart)\n\nlocal PaddleSystem = ECS.System(\"process\", 2, ECS.Query.All(Paddle))\n\nfunction PaddleSystem:Update(Time)\n   self:Result():ForEach(function(entity)\n      local paddle = entity[Paddle]\n      if paddle.target ~= paddle.position then\n         paddle.position = Utility.lerp(paddle.position, paddle.target, Constants.PLAYER_SPEED * Time.DeltaFixed)\n         entity[Position].value = self:GetPaddlePosition(paddle)\n      end\n   end)\nend\n\nfunction PaddleSystem:OnEnter(Time, entity)\n   local paddle = entity[Paddle]\n   paddle.target = 0\n   paddle.position = 0\n\n   local positionVec3 = self:GetPaddlePosition(paddle)\n   entity[Position] = Position(positionVec3)\n\n   local part = Instance.new(\"Part\")\n   part.Name = \"Paddle_\"..paddle.side\n   part.Size = Vector3.new(Constants.PADDLE_WIDTH, 2, Constants.PADDLE_HEIGHT)\n   part.Shape = Enum.PartType.Block\n   part.Anchored = true\n   part.Position = positionVec3\n   part.Material = Enum.Material.SmoothPlastic\n\n   if paddle.side == \"left\" then\n      part.Color = Color3.fromRGB(33, 84, 185)\n   else\n      part.Color = Color3.fromRGB(255, 89, 89)\n   end\n   \n   part.Parent = game.Workspace\n   entity[BasePart] = BasePart(part)\nend\n\nfunction PaddleSystem:GetPaddlePosition(paddle)\n\n   local xPos = Constants.COURT_WIDTH/2\n   if paddle.side == \"left\" then\n      xPos = xPos * -1\n   end\n   \n   local zPosMax = Constants.COURT_HEIGHT/2 - Constants.PADDLE_HEIGHT/2\n   local zPos = Utility.map(paddle.position, -1, 1, -zPosMax, zPosMax)\n   return Vector3.new(xPos, 1, zPos)\nend\n\nreturn PaddleSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f88</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX0F4BE224B6BD439C9F5BDB855A8A8DB7\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">BallSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{946069F3-7725-4355-8D98-33EE940D19E7}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal BasePart = require(Components.BasePart)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal ballMaxX = Constants.COURT_WIDTH/2\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal BallSystem = ECS.System(\"transform\", 1, ECS.Query.All(Ball))\n\nfunction BallSystem:Update(Time)\n   local scored = false\n   local scoredSide\n\n   self:Result():ForEach(function(entity)\n      local ball = entity[Ball]\n      local position = entity[Position]\n      local velocity = entity[Velocity]\n   \n      local posValue = position.value\n   \n      if posValue.Z > ballMaxZ or posValue.Z < -ballMaxZ then\n         -- Reverse z velocity if ball hits a vertical wall\n         local v = velocity.value\n         velocity.value = Vector3.new(v.X, v.Y, v.Z*-1)\n\n         if posValue.Z > ballMaxZ then\n            posValue = Vector3.new(posValue.X, posValue.Y, ballMaxZ)\n         else\n            posValue = Vector3.new(posValue.X, posValue.Y, -ballMaxZ )\n         end\n         position.value = posValue\n\n         -- sound effect\n         self._world:Entity(\n            Position(posValue),\n            AudioSource({ clip = \"rbxassetid://4458219865\" })\n         )\n      end\n   end)\nend\n\nfunction BallSystem:OnEnter(Time, entity)\n   \n   local radius = Constants.BALL_RADIUS\n   local size = radius*2\n\n   local part = Instance.new(\"Part\")\n   part.Name = \"Ball\"\n   part.Anchored = true\n   part.Size = Vector3.new(size, size, size)\n   part.Shape = Enum.PartType.Ball\n   part.Color = Color3.fromRGB(255, 255, 255)\n   part.Material = Enum.Material.Neon\n   part.Parent = game.Workspace\n   entity[BasePart] = BasePart(part)\n\n\n   local speed = Constants.BALL_SERVE_SPEED\n   local position = Vector3.new(0, radius, 0)\n   \n   local ball = entity[Ball]\n\n   if ball.initialDirection then\n      position = Vector3.new(-ballMaxX, radius, 0)\n      if ball.initialDirection == \"left\" then\n         speed = speed * -1\n         position = Vector3.new(ballMaxX, radius, 0)\n      end\n   else\n      if math.random() > 0.5 then\n         speed = speed * -1\n      end\n   end\n\n   entity[Position] = Position(position)\n   entity[Velocity] = Velocity(Vector3.new(speed, 0, 0))\n\n   -- sound effect\n   self._world:Entity(\n      Position(entity[Position].value),\n      AudioSource({ clip = \"rbxassetid://1837831535\" })\n   )\nend\n\nfunction BallSystem:OnRemove(Time, entity)\n   local part = entity[BasePart].value\n   part.Parent = nil\n   entity[BasePart] = nil\nend\n\nreturn BallSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f89</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXAEB133864EF84D24A815A37F3522F9D8\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">RenderSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{B233C6E5-CCF2-40CD-BBBF-D2D51360A2CF}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal Position = require(Components.Position)\nlocal BasePart = require(Components.BasePart)\n\nlocal RenderSystem = ECS.System(\"render\", 2, ECS.Query.All(Position, BasePart))\n\nfunction RenderSystem:Update(Time)\n   self:Result():ForEach(function(entity)\n      local position = entity[Position]\n      local part = entity[BasePart].value\n   \n      if position.valueOld then\n         part.Position = position.valueOld:Lerp(position.value, Time.Interpolation)\n      else\n         part.Position = position.value\n      end\n   end)\nend\n\nreturn RenderSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af004bf7ed</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXCA90B4828D214730A6AF3792FBE6B274\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">MoveSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{636336A1-7EF8-466C-92B0-AA4F4DEDC877}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal Velocity = require(Components.Velocity)\nlocal Position = require(Components.Position)\n\nlocal MoveSystem = ECS.System(\"process\", 10, ECS.Query.All(Position, Velocity))\n\nfunction MoveSystem:Update(Time)\n   self:Result(self.queryBalls):ForEach(function(entity)\n      local position = entity[Position]\n      local velocity = entity[Velocity]\n   \n      -- position.valueOld = position.value\n      position.value = position.value + velocity.value * Time.DeltaFixed\n   end)\nend\n\nreturn MoveSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af004bf7ef</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXACB3F37D2F8A4220B62FF7ACF74552AC\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">PlayerAiThinkSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{69DFADC4-95AD-4491-B9D0-3F56B7267695}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\n\nlocal Utility = require(Client.Utility)\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Paddle = require(Components.Paddle)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal Player = require(Components.Player)\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal PlayerAiThinkSystem = ECS.System(\"process\", 1, ECS.Query.All(PlayerAI, Paddle, Position))\n\nfunction PlayerAiThinkSystem:Initialize(Time)\n   self.queryHuman = ECS.Query.All(PlayerHuman, Paddle, Position).Build()\n   self.queryBalls = ECS.Query.All(Ball, Position, Velocity).Build()\nend\n\nfunction PlayerAiThinkSystem:Update(Time)\n\n   local ettPaddleAI = self:Result():FindAny()\n   local paddle = ettPaddleAI[Paddle]\n   local paddlePos = ettPaddleAI[Position].value\n\n   -- Get the ball that is coming towards the AI and is closer\n   local tgBallPos\n\n   self:Result(self.queryBalls):ForEach(function(ettBall)\n      local ballPos = ettBall[Position].value\n      local ballVel = ettBall[Velocity].value\n      \n      local ballTowardsAI\n      if paddle.side == \"right\" then\n         ballTowardsAI = ballVel.X > 0\n      else\n         ballTowardsAI = ballVel.X < 0\n      end\n   \n      if ballTowardsAI then\n         if tgBallPos == nil then\n            tgBallPos = ballPos\n         else\n            -- o alvo é a bola que está mais proxima da raquete\n            if paddle.side == \"right\" then\n               if ballPos.X > tgBallPos.X then\n                  tgBallPos = ballPos\n               end\n            else\n               if ballPos.X < tgBallPos.X then\n                  tgBallPos = ballPos\n               end\n            end\n         end\n      end\n   end)\n\n   if tgBallPos then\n      paddle.target = Utility.map(tgBallPos.Z, -ballMaxZ, ballMaxZ, -1, 1)\n   end\nend\n\nreturn PlayerAiThinkSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00b861f3</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX19870299A5F743D388A563983C7FEE80\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">PlayerHumanInputSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{9422572F-580D-465B-B4AC-A422E0CA0AC7}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal CurrentCamera = game.workspace.CurrentCamera\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\n\nlocal Components = Client.components\nlocal Paddle = require(Components.Paddle)\nlocal Player = require(Components.Player)\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nlocal PlayerHumanInputSystem = ECS.System(\"process\", 1, ECS.Query.All(PlayerHuman, Paddle))\n\nfunction PlayerHumanInputSystem:Initialize()\n   UserInputService.MouseIconEnabled = false\nend\n\nfunction PlayerHumanInputSystem:Update(Time)\n   local screenSizeY = CurrentCamera.ViewportSize.Y\n   local mousePosY = UserInputService:GetMouseLocation().Y\n\n   local min = screenSizeY*0.2\n   local max = screenSizeY*0.8\n\n   mousePosY = math.max(math.min(mousePosY, max), min)\n\n   local entity = self:Result():FindAny()\n   local paddle = entity[Paddle]\n\n   paddle.target = Utility.map(mousePosY, min, max, -1, 1)\nend\n\nreturn PlayerHumanInputSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00b9a278</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXA75722F6BCC04B5097250033E28D735D\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">PaddleHitSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{866E63DF-FD3E-4F66-B58A-A6AE8447898B}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Paddle = require(Components.Paddle)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal PADDLE_AIM_C = 1.5\n\n-- compute outgoing angle depending on which point the ball hits the paddle\nlocal function computeBounce(ettBall, ettPaddle)\n   \n   local ball = ettBall[Ball]\n   local ballPos = ettBall[Position].value\n   local ballVel = ettBall[Velocity].value\n   local paddle = ettPaddle[Paddle]\n   local paddlePos = ettPaddle[Position].value\n\n   -- The sharpness of the angle is determined by where the ball hits the paddle\n   local angle = PADDLE_AIM_C * (ballPos.Z - paddlePos.Z)/Constants.PADDLE_HEIGHT\n\n   local spped = ball.secondary and Constants.BALL_SPEED_SECONDARY or Constants.BALL_SPEED\n\n   local ballVelZ = math.sin(angle) * spped\n   local ballVelX = math.cos(angle) * spped\n   \n   -- if the angle exceeds a magic value, the ball gets an extra speed boost\n   local angleAbs = math.abs(angle)\n   if (angleAbs > 0.6) then\n      local boost = (1 + angleAbs * Constants.BALL_BOOST)\n      ballVelX = ballVelX * boost\n      ballVelZ = ballVelZ * boost\n   end\n\n   -- Determine the direction in which the ball should go\n   if paddle.side == \"right\" then\n      ballVelX = ballVelX*-1\n   end\n   ettBall[Velocity].value = Vector3.new(ballVelX, 0, ballVelZ)\nend\n\nlocal function intersects(ettBall, ettPaddle)\n\n   local ball = ettBall[Ball]\n   local ballPos = ettBall[Position].value\n   local paddlePos = ettPaddle[Position].value\n   \n   -- circle\n   local cx, cz, radius = ballPos.X, ballPos.Z, Constants.BALL_RADIUS\n   -- rectangle\n   local rw, rh = Constants.PADDLE_WIDTH, Constants.PADDLE_HEIGHT\n   local rx, rz = paddlePos.X - rw/2, paddlePos.Z - rh/2\n   \n   -- temporary variables to set edges for testing\n   local testX = cx\n   local testZ = cz\n   \n   local xEdge, zEdge\n   -- which edge is closest?\n   if cx < rx then\n      testX = rx \n      xEdge = \"left\"\n   elseif cx > rx + rw then    \n      testX = rx+rw\n      xEdge = \"right\"\n   end \n\n   if cz < rz then\n      testZ = rz\n      zEdge = \"top\"\n   elseif cz > rz+rh then    \n      testZ = rz+rh\n      zEdge = \"bottom\"\n   end \n\n   -- get distance from closest edges\n\n   local distX = cx-testX\n   local distY = cz-testZ   \n   local distance = math.sqrt( (distX*distX) + (distY*distY) );\n \n   -- if the distance is less than the radius, collision!\n   if (distance <= radius) then\n      local normal \n      if distY < distX then\n         normal = (zEdge == \"top\") and Vector3.new(0, 0, 1) or Vector3.new(0, 0, -1)\n      else\n         normal = (xEdge == \"left\") and Vector3.new(1, 0, 0) or Vector3.new(-1, 0, 0)\n      end\n      return {\n         normal = normal,\n         distance = distance\n      }\n   end\n   return nil   \nend\n\nlocal PaddleHitSystem = ECS.System(\"transform\", 2, ECS.Query.All(Paddle, Position))\n\nfunction PaddleHitSystem:Initialize(Time)\n   self.queryBalls = ECS.Query.All(Ball, Position).Build()\nend\n\nfunction PaddleHitSystem:Update(Time)\n   local ettsBall = self:Result(self.queryBalls):ToArray()\n\n   local ballSpawned = false\n\n   self:Result():ForEach(function(ettPaddle)\n      local paddle = ettPaddle[Paddle]\n      local pPosition = ettPaddle[Position]\n   \n      -- collision detection\n      for i,ettBall in ipairs(ettsBall) do\n         local collistion = intersects(ettBall, ettPaddle)\n         if collistion then\n   \n            -- move the ball out of the paddle\n            local ballPos = ettBall[Position].value\n            ettBall[Position].value = ballPos - collistion.normal * collistion.distance\n           \n            computeBounce(ettBall, ettPaddle)\n\n            if #ettsBall < 2 then               \n               paddle.hits = paddle.hits + 1\n               if paddle.hits == 5 then\n                  -- create new ball\n                  local inverseDirection = (paddle.side == \"left\") and \"right\" or \"left\" \n                  self._world:Entity(Ball({ initialDirection = inverseDirection, secondary = true }))\n                  ballSpawned = true\n               end\n            end\n\n            -- sound effect\n            self._world:Entity(\n               Position(ballPos),\n               AudioSource({ clip = \"rbxassetid://4458219865\" })\n            )\n\n            -- break\n            return true\n         end\n      end\n   end)\n\n   if ballSpawned then\n      self:Result():ForEach(function(ettPaddle)\n         local paddle = ettPaddle[Paddle]\n         paddle.hits = 0\n      end)\n   end\nend\n\nreturn PaddleHitSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00bec1b2</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX02D9DE3A5DAA4DBEA8676F32F7BB5423\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">ScoreSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{2D906FD8-0371-482B-AEE6-C5E1345EC563}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Paddle = require(Components.Paddle)\nlocal Score = require(Components.Score)\nlocal Player = require(Components.Player)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal BasePart = require(Components.BasePart)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal ballMaxX = Constants.COURT_WIDTH/2\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal ScoreSystem = ECS.System(\"transform\", 2, ECS.Query.All(Score, Paddle))\n\nfunction ScoreSystem:Initialize(Time)\n   self.queryBalls = ECS.Query.All(Ball, Position).Build()\nend\n\nfunction ScoreSystem:Update(Time)\n   local ettScored = false\n   local scoredSide\n\n   local balls = self:Result(self.queryBalls):ToArray()\n\n   self:Result():ForEach(function(entity)\n      local paddle = entity[Paddle]\n      local score = entity[Score]\n\n      for i,ettBall in ipairs(balls) do         \n         local ball = ettBall[Ball]\n         local ballPos = ettBall[Position].value\n      \n         -- if ball hits horizontal wall, reset the game      \n         if (paddle.side == \"right\" and ballPos.X < -ballMaxX) then\n            ettScored = entity\n         elseif (paddle.side == \"left\" and ballPos.X > ballMaxX ) then\n            ettScored = entity\n         end\n   \n         if ettScored then\n            score.value = score.value + 1\n            score.TextLabel.Text = tostring(score.value)\n\n            -- sound effect\n            self._world:Entity(\n               Position(ballPos),\n               AudioSource({ clip = \"rbxassetid://1843023345\" })\n            )\n   \n            -- break\n            return true\n         end\n      end\n   end)\n\n   if ettScored then\n      -- remove all balls\n      for i,ettBall in ipairs(balls) do\n         self._world:Remove(ettBall)\n      end\n\n      -- create new ball\n      self._world:Entity(Ball())\n\n      self:Result(self.queryPlayers):ForEach(function(entity)\n         local paddle = entity[Paddle]\n         paddle.hits = 0\n      end)\n   end\nend\n\nfunction ScoreSystem:OnEnter(Time, entity)\n   local score = entity[Score]\n   local paddle = entity[Paddle]\n   score.goalPart = game.Workspace:FindFirstChild(\"goal_\"..paddle.side)\n   score.TextLabel = score.goalPart.BillboardGui.TextLabel   \nend\n\nreturn ScoreSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00daebbb</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX3045956FD6E04920AA9C021822B18402\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">AudioSystem</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{A78EFF7B-3F47-4101-831B-675BD9A94285}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal BasePart = require(Components.BasePart)\nlocal Position = require(Components.Position)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal ballMaxX = Constants.COURT_WIDTH/2\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal AudioSystem = ECS.System(\"transform\", 100, ECS.Query.All(AudioSource))\n\nfunction AudioSystem:Initialize(Time)\n   self.queryStopped = ECS.Query.All(AudioSource, AudioSource.In(\"Stopped\")).Build()\nend\n\nfunction AudioSystem:Update(Time)\n   self:Result(self.queryStopped):ForEach(function(entity)\n      self._world:Remove(entity)\n   end)\nend\n\nfunction AudioSystem:OnEnter(Time, entity)\n   local source = entity[AudioSource]\n   local position = entity[Position]\n   \n   -- create a sound\n   local sound = Instance.new(\"Sound\")\n   sound.SoundId = source.clip\n   sound.Looped = source.loop\n   source.sound = sound\n\n   if position then\n      -- create a part\n      local part = Instance.new(\"Part\")\n      part.Anchored = true\n      part.CanCollide = false\n      part.Transparency = 1\n      part.Position = position.value\n      part.Name = \"sound#\"..source.clip\n\n      sound.Parent = part\n      part.Parent = game.Workspace\n      entity[BasePart] = BasePart(part)\n   end\n\n   sound.Ended:Connect(function()\n      source:SetState(\"Stopped\")\n   end)\n\n   source:SetState(\"Playing\")\nend\n\nfunction AudioSystem:OnExit(Time, entity)\n   local part = entity[BasePart]\n   if part then\n      part.value.Parent = nil\n   end\nend\n\nfunction AudioSystem:OnRemove(Time, entity)\n   local part = entity[BasePart]\n   if part then\n      part.value.Parent = nil\n   end\nend\n\nreturn AudioSystem\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00e676fb</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t</Item>\n\t\t\t<Item class=\"LocalScript\" referent=\"RBX2F24BC263FA9489692B5EB8A9B40DA13\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<bool name=\"Disabled\">false</bool>\n\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t<string name=\"Name\">Main</string>\n\t\t\t\t\t<string name=\"ScriptGuid\">{2ACF1CF4-EF68-499C-AE31-8AD922804366}</string>\n\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nlocal Constants = require(script.Parent:WaitForChild(\"Constants\"))\n\nlocal Components = script.Parent:WaitForChild(\"components\")\nlocal Ball = require(Components.Ball)\nlocal Score = require(Components.Score)\nlocal Paddle = require(Components.Paddle)\nlocal Player = require(Components.Player)\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nlocal Systems = script.Parent.systems\nlocal MoveSystem = require(Systems.MoveSystem)\nlocal BallSystem = require(Systems.BallSystem)\nlocal AudioSystem = require(Systems.AudioSystem)\nlocal ScoreSystem = require(Systems.ScoreSystem)\nlocal PaddleSystem = require(Systems.PaddleSystem)\nlocal PaddleHitSystem = require(Systems.PaddleHitSystem)\nlocal PlayerAiThinkSystem = require(Systems.PlayerAiThinkSystem)\nlocal PlayerHumanInputSystem = require(Systems.PlayerHumanInputSystem)\n\nlocal RenderSystem = require(Systems.RenderSystem)\nlocal CameraSystem = require(Systems.CameraSystem)\n\nlocal world = ECS.World({\n   MoveSystem,\n   BallSystem,\n   AudioSystem,\n   ScoreSystem, \n   PaddleSystem, \n   PaddleHitSystem, \n   PlayerAiThinkSystem,\n   PlayerHumanInputSystem,\n   CameraSystem,\n   RenderSystem,\n}, 60)\n\n-- Ball\nworld:Entity(Ball())\n\n-- Player\nworld:Entity(\n   Score(),\n   PlayerHuman(),\n   Paddle({ side = \"left\" })\n)\n\n-- AI\nworld:Entity(\n   Score(),\n   PlayerAI(),\n   Paddle({ side = \"right\" })\n)\n]]></ProtectedString>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f8b</UniqueId>\n\t\t\t\t</Properties>\n\t\t\t</Item>\n\t\t\t<Item class=\"ModuleScript\" referent=\"RBXE15FB49653D349C192049CCEA6CCBCDB\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t<string name=\"Name\">Constants</string>\n\t\t\t\t\t<string name=\"ScriptGuid\">{04D89E38-9D37-4F2F-8910-F2E140D26783}</string>\n\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal BALL_SPEED = 50\n\nlocal constants = {\n   BALL_BOOST = 0.3,\n   BALL_RADIUS = 1,\n   BALL_SPEED = BALL_SPEED,\n   BALL_SERVE_SPEED = BALL_SPEED*0.7,\n   BALL_SPEED_SECONDARY = BALL_SPEED*0.5,\n   COURT_WIDTH = 72,\n   COURT_HEIGHT = 30,\n   PADDLE_WIDTH = 1,\n   PADDLE_HEIGHT = 10,\n   PLAYER_SPEED = 5,\n   CAMERA_DISTANCE = 30\n}\n\nreturn constants\n]]></ProtectedString>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f8d</UniqueId>\n\t\t\t\t</Properties>\n\t\t\t</Item>\n\t\t\t<Item class=\"Folder\" referent=\"RBX1915FDCA75BE4BC98C50A9B64179D939\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<string name=\"Name\">components</string>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f8f</UniqueId>\n\t\t\t\t</Properties>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX731666BE9B8E4151B6E8AB9201907441\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">Paddle</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{DB5C058C-4CD6-47E1-9B65-9238527C3456}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[local Paddle = _G.ECS.Component({\n   side = \"left\",\n   hits = 0,\n   target = 0,    -- -1 = bottom, 0 = middle, 1 = top\n   position = 0   -- -1 = bottom, 0 = middle, 1 = top\n})\n\nreturn Paddle\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f90</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX4D2BCEF9CC584FDD9C73F06E1F2C3C0C\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">Player</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{9628A125-E3F1-4B5A-88B3-651243F4A1D7}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal Player = _G.ECS.Component()\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nreturn Player\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f91</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXC14B2FBDD71E405F9FC1D4A346B275F9\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">Ball</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{C3E028B4-B3ED-4CA4-B0CF-3627BF8A161E}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal Ball = _G.ECS.Component({\n   secondary = false,\n   initialDirection = nil,\n})\n\nreturn Ball\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f92</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX0D2AD1B143A74C48A3D895201E886A5D\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">Position</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{D1094A76-FF25-4254-AFF9-1BFDB04A9B53}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal Position = _G.ECS.Component(Vector3.new(0, 0, 0))\n\nreturn Position\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0049760e</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXAD2CBAB299A7409B8E70C46C3620EA60\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">Velocity</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{C97B20B5-D53E-4CAF-B3ED-563B8C17198F}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal Velocity = _G.ECS.Component(Vector3.new(0, 0, 0))\n\nreturn Velocity\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00497610</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBXFAA2925496C74274BC7C69894EB769F5\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">BasePart</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{CD22ED03-3A99-400E-BC06-5E293A575342}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal BasePart = _G.ECS.Component()\n\nreturn BasePart\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af004bf7ee</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX94C97536316E4DBCA2E597A6D5A128FE\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">Score</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{87C823D7-7403-4393-BD3C-70DBC899E138}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal Score = _G.ECS.Component(0)\n\nreturn Score\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00dae7b8</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t\t<Item class=\"ModuleScript\" referent=\"RBX99A2DDB591AA42CEAF5455B784767761\">\n\t\t\t\t\t<Properties>\n\t\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t\t<string name=\"Name\">AudioSource</string>\n\t\t\t\t\t\t<string name=\"ScriptGuid\">{3E5DF6F2-A6F1-452B-8EE0-B53709BB312E}</string>\n\t\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal AudioSource = _G.ECS.Component({\n   clip = \"\",     -- sound asset\n   volume = 10,    -- playback volume between [0..10]\n   loop = false,  -- If true, the audio clip replays when it ends\n   sound = nil\n})\n\nAudioSource.States = {\n   [\"Playing\"] = { \"Stopped\" },\n   [\"Stopped\"] = { \"Playing\" }\n}\n\nAudioSource.StateInitial = \"Stopped\"\n\nAudioSource.Case = {\n   Playing = function(self, previous)\n      if self.sound then\n         self.sound:Play()\n      end\n   end,\n   Stopped = function(self, previous)\n      if self.sound then\n         self.sound:Stop()\n      end\n   end\n}\n\nreturn AudioSource\n]]></ProtectedString>\n\t\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00e676fd</UniqueId>\n\t\t\t\t\t</Properties>\n\t\t\t\t</Item>\n\t\t\t</Item>\n\t\t\t<Item class=\"ModuleScript\" referent=\"RBX481677E10F164DB08347112CF7900195\">\n\t\t\t\t<Properties>\n\t\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t\t<string name=\"Name\">Utility</string>\n\t\t\t\t\t<string name=\"ScriptGuid\">{37D1623F-A591-4656-A23A-20C1ADD07B85}</string>\n\t\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[\nlocal Utility = {}\n\nfunction Utility.map(x, inMin, inMax, outMin, outMax)\n   return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin\nend\n\nfunction Utility.lerp(v0, v1, t)\n   return (1-t)*v0 + t*v1\nend\n\nreturn Utility\n]]></ProtectedString>\n\t\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f94</UniqueId>\n\t\t\t\t</Properties>\n\t\t\t</Item>\n\t\t</Item>\n\t\t<Item class=\"StarterCharacterScripts\" referent=\"RBXFB5BB556864841E69B4510E6CDB76A78\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<string name=\"Name\">StarterCharacterScripts</string>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018526</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t</Item>\n\t<Item class=\"StarterPack\" referent=\"RBX2F6E2C871064497ABEDF80026EC0DDD0\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">StarterPack</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182ab</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"StarterGui\" referent=\"RBX5C4494074CD3423BACCDAB725D121272\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">StarterGui</string>\n\t\t\t<bool name=\"ResetPlayerGuiOnSpawn\">true</bool>\n\t\t\t<token name=\"ScreenOrientation\">4</token>\n\t\t\t<bool name=\"ShowDevelopmentGui\">true</bool>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182ac</UniqueId>\n\t\t\t<token name=\"VirtualCursorMode\">0</token>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"LocalizationService\" referent=\"RBX3EA7D398E29D4EE3BAF9688D05509DEF\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">LocalizationService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182ae</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"TeleportService\" referent=\"RBX2DDBCA1C4CF94BFD8F4D6C49C997B7EA\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Teleport Service</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182b2</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"CollectionService\" referent=\"RBX84D2FD6CCB834733987D39D709A0327C\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">CollectionService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182b4</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"PhysicsService\" referent=\"RBXBE1CA6E10B1A4C0A9140AC1D0B6DE8DC\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">PhysicsService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182b5</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Geometry\" referent=\"RBX2E5AAF76D7814C81A3ED1A0B272FD36A\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Geometry</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182b7</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"InsertService\" referent=\"RBX95DFEA2335CF4693A09EFA1482FAB261\">\n\t\t<Properties>\n\t\t\t<bool name=\"AllowClientInsertModels\">false</bool>\n\t\t\t<bool name=\"AllowInsertFreeModels\">false</bool>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">InsertService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182b9</UniqueId>\n\t\t</Properties>\n\t\t<Item class=\"StringValue\" referent=\"RBX064CBCC40B2C448CB2DE31E3DBD2F222\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<string name=\"Name\">InsertionHash</string>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018528</UniqueId>\n\t\t\t\t<string name=\"Value\">{C3FFDE2F-3CE5-41A4-B5E5-08BC30C81031}</string>\n\t\t\t</Properties>\n\t\t</Item>\n\t</Item>\n\t<Item class=\"GamePassService\" referent=\"RBX919E7E57FE0C4C0F9B26D7A6F21F7DA2\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">GamePassService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182ba</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Debris\" referent=\"RBXEE948B8CD3124B809B27B4D656929BDC\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<int name=\"MaxItems\">1000</int>\n\t\t\t<string name=\"Name\">Debris</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182bb</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"CookiesService\" referent=\"RBXA8B5F6EECD94433087BD9BDF22983214\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">CookiesService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182bc</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"VRService\" referent=\"RBXB5D786935BCC4FD084A53E05CDDF56A2\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">VRService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182c6</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ContextActionService\" referent=\"RBXD8918D179FF347A1B31427F7F2E88953\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">ContextActionService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182c7</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ScriptService\" referent=\"RBX2D39E24DB290430A964B2543E0C0E0F0\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Instance</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182c9</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"AssetService\" referent=\"RBXE6B97E98BA1545DB99E0FAF353EA8039\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">AssetService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182ca</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"TouchInputService\" referent=\"RBXBFAC66F8E5A4452DAD88376C31FD2EFC\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">TouchInputService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182cb</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"AnalyticsService\" referent=\"RBXC46723EFC7064E3ABBFEB096801FFE29\">\n\t\t<Properties>\n\t\t\t<string name=\"ApiKey\"></string>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">AnalyticsService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182ce</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Selection\" referent=\"RBX1606BB0FC5614C69A4FFEB0888FC08DF\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Selection</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182d1</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ServerScriptService\" referent=\"RBX53A646F0F8C241038B366A5820BFD969\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"LoadStringEnabled\">false</bool>\n\t\t\t<string name=\"Name\">ServerScriptService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182da</UniqueId>\n\t\t</Properties>\n\t\t<Item class=\"Script\" referent=\"RBX453CF3F8A8834BEA8C7D21CF8511A59B\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<bool name=\"Disabled\">false</bool>\n\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t<string name=\"Name\">Main</string>\n\t\t\t\t<string name=\"ScriptGuid\">{370D60B7-C5E9-4154-9170-329D73840A64}</string>\n\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[game.Players.CharacterAutoLoads = false\n]]></ProtectedString>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f95</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t</Item>\n\t<Item class=\"ServerStorage\" referent=\"RBX9B9A08C21D2941CD99AEEA06B3906461\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">ServerStorage</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182db</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ReplicatedStorage\" referent=\"RBXCF222E0A0202403BB4355E0C5140D429\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">ReplicatedStorage</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182dc</UniqueId>\n\t\t</Properties>\n\t\t<Item class=\"ModuleScript\" referent=\"RBX4330246125AE44CBBE0642184558BED9\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<Content name=\"LinkedSource\"><null></null></Content>\n\t\t\t\t<string name=\"Name\">ECS</string>\n\t\t\t\t<string name=\"ScriptGuid\">{DB0B7A5D-F8B8-4482-A6C9-2A8342B54807}</string>\n\t\t\t\t<ProtectedString name=\"Source\"><![CDATA[--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tThis is a minified version of ECS Lua, to see the full source code visit\n\thttps://github.com/nidorx/ecs-lua\n\n   Discussions about this script are at https://devforum.roblox.com/t/841175\n\n\t------------------------------------------------------------------------------\n\n\tMIT License\n\n\tCopyright (c) 2021 Alex Rodin\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.\n]]\nlocal a,b={},{}local function c(d)if(not a[d])then a[d]={r=b[d]()}end return a[d].r end b[\"Archetype\"]=function()local d={}local e={}local f={}local g=0 local h={}h.__index=h function h.Of(i)local j={}local k={}for m,n in ipairs(i)do if(n.IsCType and not n.isComponent)then if n.IsQualifier then if k[n]==nil then k[n]=true table.insert(j,n.Id)end n=n.SuperClass end if k[n]==nil then k[n]=true table.insert(j,n.Id)end end end table.sort(j)local l=\"_\"..table.concat(j,\"_\")if d[l]==nil then d[l]=setmetatable({id=l,_components=k},h)g=g+1 end return d[l]end function h.Version()return g end function h:Has(i)return(self._components[i]==true)end function h:With(i)if self._components[i]==true then return self end local j=e[self]if not j then j={}e[self]=j end local k=j[i]if k==nil then local l={i}for m,n in pairs(self._components)do table.insert(l,m)end k=h.Of(l)j[i]=k end return k end function h:WithAll(i)local j={}for k,l in pairs(self._components)do table.insert(j,k)end for k,l in ipairs(i)do if self._components[l]==nil then table.insert(j,l)end end return h.Of(j)end function h:Without(i)if self._components[i]==nil then return self end local j=f[self]if not j then j={}f[self]=j end local k=j[i]if k==nil then local l={}for m,n in pairs(self._components)do if m~=i then table.insert(l,m)end end k=h.Of(l)j[i]=k end return k end function h:WithoutAll(i)local j={}for l,m in ipairs(i)do j[m]=true end local k={}for l,m in pairs(self._components)do if j[l]==nil then table.insert(k,l)end end return h.Of(k)end h.EMPTY=h.Of({})return h end b[\"Component\"]=function()local d=c(\"Utility\")local e=c(\"ComponentFSM\")local f=d.copyDeep local g=d.mergeDeep local h=0 local function i(l,m)h=h+1 local n={Id=h,IsCType=true,SuperClass=m}n.__index=n if m==nil then m=n m._Qualifiers={[\"Primary\"]=n}m._QualifiersArr={n}m._Initializers={}else m.HasQualifier=true n.IsQualifier=true n.HasQualifier=true end local o=m._Qualifiers local p=m._QualifiersArr setmetatable(n,{__call=function(q,r)return n.New(r)end,__index=function(q,r)if(r==\"States\")then return m.__States end if(r==\"Case\"or r==\"StateInitial\")then return rawget(m,r)end end,__newindex=function(q,r,s)if(r==\"Case\"or r==\"States\"or r==\"StateInitial\")then if n==m then if(r==\"States\")then if not m.IsFSM then e.AddCapability(m,s)for t,u in pairs(o)do if u~=m then e.AddMethods(m,u)end end end else rawset(q,r,s)end end else rawset(q,r,s)end end})if m.IsFSM then e.AddMethods(m,n)end function n.Qualifier(q)if type(q)~=\"string\"then for s,t in ipairs(p)do if t==q then return q end end return nil end local r=o[q]if r==nil then r=i(l,m)o[q]=r table.insert(p,r)end return r end function n.Qualifiers(...)local q={...}if#q==0 then return p else local r={}local s={}for t,u in ipairs({...})do local v=n.Qualifier(u)if v and s[v]==nil then s[v]=true table.insert(r,v)end end return r end end function n.New(q)if(q~=nil and type(q)~=\"table\")then q={value=q}end local r=setmetatable(l(q)or{},n)for s,t in ipairs(m._Initializers)do t(r)end r.isComponent=true r._qualifiers={[n]=r}return r end function n:GetType()return n end function n:Is(q)return q==n or q==m end function n:Primary()return self._qualifiers[m]end function n:Qualified(q)return self._qualifiers[n.Qualifier(q)]end function n:QualifiedAll()local q={}for r,s in pairs(o)do q[r]=self._qualifiers[s]end return q end function n:Merge(q)if m.HasQualifier then if self==q then return end if self._qualifiers==q._qualifiers then return end if not q:Is(m)then return end local r=n local s=q:GetType()local t if r==m then t=self._qualifiers elseif s==m then t=q._qualifiers elseif self._qualifiers[m]~=nil then t=self._qualifiers[m]._qualifiers elseif q._qualifiers[m]~=nil then t=q._qualifiers[m]._qualifiers end if t~=nil then if self._qualifiers~=t then for u,v in pairs(self._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end if q._qualifiers~=t then for u,v in pairs(q._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end else for u,v in pairs(q._qualifiers)do if r~=u then self._qualifiers[u]=v v._qualifiers=self._qualifiers end end end end end function n:Detach()if not m.HasQualifier then return end self._qualifiers[n]=nil self._qualifiers={[n]=self}end return n end local function j(l)return l or{}end local k={}function k.Create(l)local m=j if l~=nil then local n=type(l)if(n==\"function\")then m=l else if(n~=\"table\")then l={value=l}end m=function(o)local p=f(l)if(o~=nil)then g(p,o)end return p end end end return i(m,nil)end return k end b[\"ComponentFSM\"]=function()local d=c(\"Query\")local e=d.Filter(function(g,h)local i=h.States local j=h.IsSuperClass local k=h.ComponentClass if j then local l=k.Qualifiers()for m,n in ipairs(l)do local o=g[n]if(o~=nil and i[o:GetState()]==true)then return true end end return false else local l=g[k]if l==nil then return false end return i[l:GetState()]==true end end)local f={}function f.AddCapability(g,h)g.IsFSM=true local i=setmetatable({},{__newindex=function(j,k,l)if(type(l)~=\"table\")then l={l}end if table.find(l,\"*\")then rawset(j,k,\"*\")else local m=table.find(l,k)if m~=nil then table.remove(l,m)if#l==0 then l=\"*\"end end rawset(j,k,l)end end})rawset(g,\"__States\",i)for j,k in pairs(h)do if g.StateInitial==nil then g.StateInitial=j end i[j]=k end f.AddMethods(g,g)table.insert(g._Initializers,function(j)j:SetState(g.StateInitial)end)end function f.AddMethods(g,h)h.IsFSM=true local i=g.States function h.In(...)local j={}local k=0 for l,m in ipairs({...})do if(i[m]~=nil and j[m]==nil)then k=k+1 j[m]=true end end if k==0 then return{}end return e({States=j,IsSuperClass=(h==g),ComponentClass=h,})end function h:SetState(j)if(j==nil or i[j]==nil)then return end local k=self:GetState()if(k==j)then return end if(k~=nil)then local m=i[k]if(m~=\"*\"and table.find(m,j)==nil)then return end end self._state=j self._statePrev=k self._stateTime=os.clock()local l=g.Case and g.Case[j]if l then l(self,k)end end function h:GetState()return self._state or g.StateInitial end function h:GetPrevState()return self._statePrev or nil end function h:GetStateTime()return self._stateTime or 0 end end return f end b[\"ECS\"]=function()local d=c(\"Query\")local e=c(\"World\")local f=c(\"System\")local g=c(\"Archetype\")local h=c(\"Component\")local function i(k)e.LoopManager=k end pcall(function()if(game and game.ClassName==\"DataModel\")then i(c(\"RobloxLoopManager\")())end end)local j={Query=d,World=e.New,System=f.Create,Archetype=g,Component=h.Create,SetLoopManager=i}if _G.ECS==nil then _G.ECS=j else local k=_G.warn or print k(\"ECS Lua was not registered in the global variables, there is already another object registered.\")end return j end b[\"Entity\"]=function()local d=c(\"Archetype\")local e=0 local function f(m,...)local n={...}local o=m._data if(#n==1)then local q=n[1]if(q.IsCType and not q.isComponent)then return o[q]else return nil end end local p={}for q,r in ipairs(n)do if(r.IsCType and not r.isComponent)then table.insert(p,o[r])end end return table.unpack(p)end local function g(m,n,o)local p=m._data local q for r,s in ipairs(o.Qualifiers())do if s~=o then q=p[s]if q then break end end end if q then q:Merge(n)end end local function h(m,...)local n={...}local o=m._data local p=m.archetype local q=p local r={}local s=n[1]if(s and s.IsCType and not s.isComponent)then local t=n[2]local u if t==nil then local v=o[s]if v then v:Detach()end o[s]=nil q=q:Without(s)elseif(type(t)==\"table\"and t.isComponent)then local v=o[s]if(v~=t)then if v then v:Detach()end s=t:GetType()o[s]=t q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,t,s)end end else local v=o[s]if v then v:Detach()end local w=s(t)o[s]=w q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,w,s)end end else for t,u in ipairs(n)do if(u.isComponent)then local v=u:GetType()local w=o[v]if(w~=u)then if w then w:Detach()end o[v]=u q=q:With(v)if(v.HasQualifier or v.IsQualifier)then g(m,u,v)end end end end end if(p~=q)then m.archetype=q m._onChange:Fire(m,p)end end local function i(m,...)local n=m._data local o=m.archetype local p=o for q,r in ipairs({...})do if r.isComponent then local s=r:GetType()local t=n[s]if t then t:Detach()end n[s]=nil p=p:Without(s)elseif r.IsCType then local s=n[r]if s then s:Detach()end n[r]=nil p=p:Without(r)end end if m.archetype~=p then m.archetype=p m._onChange:Fire(m,o)end end local function j(m,n)local o=m._data local p={}if(n~=nil and n.IsCType and not n.isComponent)then local q=n.Qualifiers()for r,s in ipairs(q)do local t=o[s]if t then table.insert(p,t)end end else for q,r in pairs(o)do table.insert(p,r)end end return p end local function k(m,n)if(n~=nil and n.IsCType and not n.isComponent)then local o=m._data local p=n.Qualifiers()for q,r in ipairs(p)do local s=o[r]if s then return s end end end end local l={__index=function(m,n)if(type(n)==\"table\")then return f(m,n)end end,__newindex=function(m,n,o)local p=true if(type(n)==\"table\"and(n.IsCType and not n.isComponent))then h(m,n,o)else rawset(m,n,o)end end}function l.New(m,n)local o=d.EMPTY local p={}if(n~=nil and#n>0)then local q={}for r,s in ipairs(n)do local t=s:GetType()table.insert(q,t)p[t]=s end o=d.Of(q)end e=e+1 return setmetatable({_data=p,_onChange=m,id=e,isAlive=false,archetype=o,Get=f,Set=h,Unset=i,GetAll=j,GetAny=k,},l)end return l end b[\"EntityRepository\"]=function()local d=c(\"Event\")local e={}e.__index=e function e.New()return setmetatable({_archetypes={},_entitiesArchetype={},},e)end function e:Insert(f)if(self._entitiesArchetype[f]==nil)then local g=f.archetype local h=self._archetypes[g]if(h==nil)then h={count=0,entities={}}self._archetypes[g]=h end h.entities[f]=true h.count=h.count+1 self._entitiesArchetype[f]=g else self:Update(f)end end function e:Remove(f)local g=self._entitiesArchetype[f]if g==nil then return end self._entitiesArchetype[f]=nil local h=self._archetypes[g]if(h~=nil and h.entities[f]==true)then h.entities[f]=nil h.count=h.count-1 if(h.count==0)then self._archetypes[g]=nil end end end function e:Update(f)local g=self._entitiesArchetype[f]if(g==nil or g==f.archetype)then return end self:Remove(f)self:Insert(f)end function e:Query(f)local g={}for h,i in pairs(self._archetypes)do if f:Match(h)then table.insert(g,i.entities)end end return f:Result(g),#g>0 end function e:FastCheck(f)for g,h in pairs(self._archetypes)do if f:Match(g)then return true end end return false end return e end b[\"Event\"]=function()local d={}d.__index=d function d.New(f,g)return setmetatable({_event=f,_handler=g},d)end function d:Disconnect()local f=self._event if(f and not f.destroyed)then local g=table.find(f._handlers,self._handler)if g~=nil then table.remove(f._handlers,g)end end setmetatable(self,nil)end local e={}e.__index=e function e.New()return setmetatable({_handlers={}},e)end function e:Connect(f)if(type(f)==\"function\")then table.insert(self._handlers,f)return d.New(self,f)end error((\"Event:Connect(%s)\"):format(typeof(f)),2)end function e:Fire(...)if not self.destroyed then for f,g in ipairs(self._handlers)do g(table.unpack({...}))end end end function e:Destroy()setmetatable(self,nil)self._handlers=nil self.destroyed=true end return e end b[\"Query\"]=function()local d=c(\"QueryResult\")local e={}local f={}f.__index=f setmetatable(f,{__call=function(i,j,k,l)return f.New(j,k,l)end,})local function g(i,j,k)local l={}local m={}local n={}for o,p in ipairs(i)do if(l[p]==nil)then if(p.IsCType and not p.isComponent)then l[p]=true table.insert(m,p)table.insert(n,p.Id)else if p.Filter then l[p]=true p[j]=true table.insert(k,p)end end end end if#m>0 then table.sort(n)local o=\"_\"..table.concat(n,\"_\")return m,o end end function f.New(i,j,k)local l={}local m,n,o if(j~=nil)then j,m=g(j,\"IsAnyFilter\",l)end if(i~=nil)then i,n=g(i,\"IsAllFilter\",l)end if(k~=nil)then k,o=g(k,\"IsNoneFilter\",l)end return setmetatable({isQuery=true,_any=j,_all=i,_none=k,_anyKey=m,_allKey=n,_noneKey=o,_cache={},_clauses=#l>0 and l or nil},f)end function f:Result(i)return d.New(i,self._clauses)end function f:Match(i)local j=self._cache local k=j[i]if k~=nil then return k else local l=e[i]if(l==nil)then l={Any={},All={},None={}}e[i]=l end local m=self._noneKey if m then local p=l.None[m]if(p==nil)then p=true for q,r in ipairs(self._none)do if i:Has(r)then p=false break end end l.None[m]=p end if(p==false)then j[i]=false return false end end local n=self._anyKey if n then local p=l.Any[n]if(p==nil)then p=false if(l.All[n]==true)then p=true else for q,r in ipairs(self._any)do if i:Has(r)then p=true break end end end l.Any[n]=p end if(p==false)then j[i]=false return false end end local o=self._allKey if o then local p=l.All[o]if(p==nil)then local q=true for r,s in ipairs(self._all)do if(not i:Has(s))then q=false break end end if q then p=true else p=false end l.All[o]=p end j[i]=p return p end j[i]=true return true end end local function h()local i={isQueryBuilder=true}local j function i.All(...)j=nil i._all={...}return i end function i.Any(...)j=nil i._any={...}return i end function i.None(...)j=nil i._none={...}return i end function i.Build()if j==nil then j=f.New(i._all,i._any,i._none)end return j end return i end function f.All(...)return h().All(...)end function f.Any(...)return h().Any(...)end function f.None(...)return h().None(...)end function f.Filter(i)return function(j)return{Filter=i,Config=j}end end return f end b[\"QueryResult\"]=function()local function d(l,m,n)return m,(l(m)==true),true end local function e(l,m,n)return l(m),true,true end local function f(l,m,n)local o=(n<=l)return m,o,o end local function g(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=false break end end return m,o,true end local function h(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==false)then o=false break end end return m,o,true end local function i(l,m,n)local o=false for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=true break end end return m,o,true end local j={}local k={}k.__index=k function k.New(l,m)local n=j if(m and#m>0)then local o={}local p={}local q={}n={}for r,s in ipairs(m)do if s.IsNoneFilter then table.insert(q,s)elseif s.IsAnyFilter then table.insert(p,s)else table.insert(o,s)end end if(#q>0)then table.insert(n,{g,q})end if(#o>0)then table.insert(n,{h,o})end if(#p>0)then table.insert(n,{i,p})end end return setmetatable({chunks=l,_pipeline=n,},k)end function k:With(l,m)local n={}for o,p in ipairs(self._pipeline)do table.insert(n,p)end table.insert(n,{l,m})return setmetatable({chunks=self.chunks,_pipeline=n,},k)end function k:Filter(l)return self:With(d,l)end function k:Map(l)return self:With(e,l)end function k:Limit(l)return self:With(f,l)end function k:AnyMatch(l)local m=false self:ForEach(function(n)if l(n)then m=true end return m end)return m end function k:AllMatch(l)local m=true self:ForEach(function(n)if(not l(n))then m=false end return m==false end)return m end function k:FindAny()local l self:ForEach(function(m)l=m return true end)return l end function k:ToArray()local l={}self:ForEach(function(m)table.insert(l,m)end)return l end function k:Iterator()local l=coroutine.create(function()self:ForEach(function(m,n)coroutine.yield(m,n)end)end)return function()local m,n,o=coroutine.resume(l)return o,n end end function k:ForEach(l)local m=1 local n=self._pipeline local o=#n>0 if(not o)then for p,q in ipairs(self.chunks)do for r,s in pairs(q)do if(l(r,m)==true)then return end m=m+1 end end else for p,q in ipairs(self.chunks)do for r,s in pairs(q)do local t=false local u=true local v=r if(u and o)then for w,x in ipairs(n)do local y,z,A=x[1](x[2],v,m)if(not A)then t=true end if z then v=y else u=false break end end end if u then if(l(v,m)==true)then return end m=m+1 end if t then return end end end end end return k end b[\"RobloxLoopManager\"]=function()local function d()local e=game:GetService(\"RunService\")return{Register=function(f)local g=e.Stepped:Connect(function()f:Update(\"process\",os.clock())end)local h=e.Heartbeat:Connect(function()f:Update(\"transform\",os.clock())end)local i if(not e:IsServer())then i=e.RenderStepped:Connect(function()f:Update(\"render\",os.clock())end)end return function()g:Disconnect()h:Disconnect()if i then i:Disconnect()end end end}end return d end b[\"System\"]=function()local d={\"task\",\"render\",\"process\",\"transform\"}local e={}function e.Create(f,g,h,i)if(f==nil or not table.find(d,f))then error(\"The step parameter must one of \",table.concat(d,\", \"))end if(g and type(g)==\"function\")then i=g g=nil elseif h and type(h)==\"function\"then i=h h=nil end if(g and type(g)==\"table\"and(g.isQuery or g.isQueryBuilder))then h=g g=nil end if(g==nil or g<0)then g=50 end if type(h)==\"function\"then i=h h=nil end if(h and h.isQueryBuilder)then h=h.Build()end local j={Step=f,Order=g,Query=h,}j.__index=j function j.New(k,l)local m=setmetatable({version=0,_world=k,_config=l,},j)if m.Initialize then m:Initialize(l)end return m end function j:GetType()return j end function j:Result(k)return self._world:Exec(k or j.Query)end function j:Publish(k,l)end function j:Receive(k)end function j:Destroy()if self.OnDestroy then self.OnDestroy()end setmetatable(self,nil)for k,l in pairs(self)do self[k]=nil end end if i and type(i)==\"function\"then j.Update=i end return j end return e end b[\"SystemExecutor\"]=function()local function d(i)local j={}local k={}for l,m in ipairs(i)do local n=m:GetType()if(m._TaskState==nil)then m._TaskState=\"suspended\"end if not k[n]then local o={Type=n,System=m,Depends={}}k[n]=o table.insert(j,o)end end for l,m in ipairs(j)do local n=m.Type.Before if(n~=nil and#n>0)then for p,q in ipairs(n)do local r=k[q]if r then r.Depends[m]=true end end end local o=m.Type.After if(o~=nil and#o>0)then for p,q in ipairs(o)do local r=k[q]if r then m.Depends[r]=true end end end end return j end local function e(i,j)return i.Order<j.Order end local f={}f.__index=f function f.New(i)local j=setmetatable({_world=i,_onExit={},_onEnter={},_onRemove={},_task={},_render={},_process={},_transform={},_schedulers={},_lastFrameMatchQueries={},_currentFrameMatchQueries={},},f)i:OnQueryMatch(function(k)j._currentFrameMatchQueries[k]=true end)return j end function f:SetSystems(i)local j={}local k={}local l={}local m={}local n={}local o={}local p={}for q,r in pairs(i)do local s=r.Step if r.Update then if s==\"task\"then table.insert(m,r)elseif s==\"process\"then table.insert(o,r)elseif s==\"transform\"then table.insert(p,r)elseif s==\"render\"then table.insert(n,r)end end if(r.Query and r.Query.isQuery and s~=\"task\")then if r.OnExit then table.insert(j,r)end if r.OnEnter then table.insert(k,r)end if r.OnRemove then table.insert(l,r)end end end m=d(m)table.sort(j,e)table.sort(k,e)table.sort(l,e)table.sort(n,e)table.sort(o,e)table.sort(p,e)self._onExit=j self._onEnter=k self._onRemove=l self._task=m self._render=n self._process=o self._transform=p end function f:ExecOnExitEnter(i,j)local k=true local l={}for m,n in pairs(j)do local o=l[n]if not o then o={}l[n]=o end local p=m.archetype local q=o[p]if not q then q={}o[p]=q end table.insert(q,m)k=false end if k then return end self:_ExecOnEnter(i,l)self:_ExecOnExit(i,l)end function f:_ExecOnEnter(i,j)local k=self._world for l,m in ipairs(self._onEnter)do local n=m.Query for o,p in pairs(j)do if not n:Match(o)then for q,r in pairs(p)do if n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnEnter(i,t)m.version=k.version end end end end end end end function f:_ExecOnExit(i,j)local k=self._world for l,m in ipairs(self._onExit)do local n=m.Query for o,p in pairs(j)do if n:Match(o)then for q,r in pairs(p)do if not n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnExit(i,t)m.version=k.version end end end end end end end function f:ExecOnRemove(i,j)local k=true local l={}for n,o in pairs(j)do local p=l[o]if not p then p={}l[o]=p end table.insert(p,n)k=false end if k then return end local m=self._world for n,o in ipairs(self._onRemove)do for p,q in pairs(l)do if o.Query:Match(p)then for r,s in ipairs(q)do m.version=m.version+1 o:OnRemove(i,s)o.version=m.version end end end end end local function g(i,j,k)local l=i._world local m=i._lastFrameMatchQueries local n=i._currentFrameMatchQueries for o,p in ipairs(j)do local q=true if p.Query then local r=p.Query if m[r]==true or n[r]==true then q=true else q=l:FastCheck(r)n[r]=q end end if q then if(p.ShouldUpdate==nil or p.ShouldUpdate(k))then l.version=l.version+1 p:Update(k)p.version=l.version end end end end function f:ExecProcess(i)self._currentFrameMatchQueries={}g(self,self._process,i)end function f:ExecTransform(i)g(self,self._transform,i)end function f:ExecRender(i)g(self,self._render,i)self._lastFrameMatchQueries=self._currentFrameMatchQueries end function f:ExecTasks(i)while i>0 do local j=false local k,l=0,#self._schedulers-1 while k<=l do k=k+1 local m=self._schedulers[k]local n,o=m.Resume(i)if o then j=true end i=i-(n+0.00001)if(i<=0)then break end end if not j then return end end end local function h(i,j,k,l)local m=i.System m._TaskState=\"running\"if(m.ShouldUpdate==nil or m.ShouldUpdate(j))then k.version=k.version+1 m:Update(j)m.version=k.version end m._TaskState=\"suspended\"l(i)end function f:ScheduleTasks(i)local j=self._world local k={}local l={}local m={}local n={}local o={}local p,q=0,#self._task-1 while p<=q do p=p+1 local s=self._task[p]if(s.System._TaskState==\"suspended\")then s.System._TaskState=\"scheduled\"local t=false for u,v in pairs(s.Depends)do t=true if o[u]==nil then o[u]={}end table.insert(o[u],s)end if(not t)then table.insert(k,s)end m[s]=true end end local function r(s)s.Thread=nil s.LastExecTime=nil n[s]=true if o[s]then local t=o[s]local u,v=0,#t-1 while u<=v do u=u+1 local w=t[u]if m[w]then local x=true for y,z in pairs(w.Depends)do if n[y]~=true then x=false break end end if x then m[w]=nil w.LastExecTime=0 w.Thread=coroutine.create(h)table.insert(l,w)end end end end end if#k>0 then local s,t=0,#k-1 while s<=t do s=s+1 local v=k[s]m[v]=nil v.LastExecTime=0 v.Thread=coroutine.create(h)table.insert(l,v)end local u u={Resume=function(v)table.sort(l,function(A,B)return A.LastExecTime<B.LastExecTime end)local w=0 local x,y=0,#l-1 while x<=y do x=x+1 local A=l[x]if A.Thread~=nil then local B=os.clock()A.LastExecTime=B coroutine.resume(A.Thread,A,i,j,r)w=w+(os.clock()-B)if(w>v)then break end end end for A,B in ipairs(l)do if B.Thread==nil then local C=table.find(l,B)if C~=nil then table.remove(l,C)end end end local z=#l>0 if(not z)then local A=table.find(self._schedulers,u)if A~=nil then table.remove(self._schedulers,A)end end return w,z end}table.insert(self._schedulers,u)end end return f end b[\"Timer\"]=function()local d=4 local function e(g)local h=0.0 local i=0.0 return function(j,k,l,m)local n=g.DeltaFixed local o=j-i if o>0.25 then o=0.25 end i=j g.Now=j h=h+o if k==\"process\"then if h>=n then g.Interpolation=1 l(g)local p=0 while(h>=n and p<d)do m(g)p=p+1 g.Process=g.Process+n h=h-n end end else g.Interpolation=math.min(math.max(h/n,0),1)l(g)m(g)end end end local f={}f.__index=f function f.New(g)local h={Now=0,Frame=0,Process=0,Delta=0,DeltaFixed=0,Interpolation=0}local i=setmetatable({Time=h,Frequency=0,_update=e(h)},f)i:SetFrequency(g)return i end function f:SetFrequency(g)if g==nil then g=30 end local h=math.floor(math.abs(g)/2)*2 if h<2 then h=2 end if g~=h then g=h end self.Frequency=g self.Time.DeltaFixed=1000/g/1000 end function f:Update(g,h,i,j)self._update(g,h,i,j)end return f end b[\"Utility\"]=function()local d={}if table.unpack==nil then table.unpack=unpack end if table.find==nil then table.find=function(g,h,i)local j=#g for k=i or 1,j,1 do if g[k]==h then return k end end return nil end end local function e(g)local h={}for i,j in pairs(g)do if type(j)==\"table\"then j=e(j)end h[i]=j end return h end d.copyDeep=e local function f(g,h)for i,j in pairs(h)do if(type(j)==\"table\")then local k=g[i]if(k==nil or type(k)~=\"table\")then g[i]=e(j)else g[i]=f(k,j)end else g[i]=j end end return g end d.mergeDeep=f return d end b[\"World\"]=function()local d=c(\"Timer\")local e=c(\"Event\")local f=c(\"Entity\")local g=c(\"Archetype\")local h=c(\"SystemExecutor\")local i=c(\"EntityRepository\")local j={}j.__index=j function j.New(k,l,m)local n=setmetatable({version=0,maxTasksExecTime=0.013333333333333334,_dirty=false,_timer=d.New(l),_systems={},_repository=i.New(),_entitiesCreated={},_entitiesRemoved={},_entitiesUpdated={},_onQueryMatch=e.New(),_onChangeArchetypeEvent=e.New(),},j)n._executor=h.New(n)n._onChangeArchetypeEvent:Connect(function(o,p,q)n:_OnChangeArchetype(o,p,q)end)if(k~=nil)then for o,p in ipairs(k)do n:AddSystem(p)end end if(not m and j.LoopManager)then n._loopCancel=j.LoopManager.Register(n)end return n end function j:SetFrequency(k)k=self._timer:SetFrequency(k)end function j:GetFrequency(k)return self._timer.Frequency end function j:AddSystem(k,l)if k then if l==nil then l={}end if self._systems[k]==nil then self._systems[k]=k.New(self,l)self._executor:SetSystems(self._systems)end end end function j:Entity(...)local k=f.New(self._onChangeArchetypeEvent,{...})self._dirty=true self._entitiesCreated[k]=true k.version=self.version k.isAlive=false return k end function j:Remove(k)if self._entitiesRemoved[k]==true then return end if self._entitiesCreated[k]==true then self._entitiesCreated[k]=nil else self._repository:Remove(k)self._entitiesRemoved[k]=true if self._entitiesUpdated[k]==nil then self._entitiesUpdated[k]=k.archetype end end self._dirty=true k.isAlive=false end function j:Exec(k)if(k.isQueryBuilder)then k=k.Build()end local l,m=self._repository:Query(k)if m then self._onQueryMatch:Fire(k)end return l end function j:FastCheck(k)if(k.isQueryBuilder)then k=k.Build()end return self._repository:FastCheck(k)end function j:OnQueryMatch(k)return self._onQueryMatch:Connect(k)end function j:Update(k,l)self._timer:Update(l,k,function(m)if k==\"process\"then self._executor:ScheduleTasks(m)end self._executor:ExecTasks(self.maxTasksExecTime)end,function(m)if k==\"process\"then self._executor:ExecProcess(m)elseif k==\"transform\"then self._executor:ExecTransform(m)else self._executor:ExecRender(m)end while self._dirty do self._dirty=false local n={}for q,r in pairs(self._entitiesRemoved)do n[q]=self._entitiesUpdated[q]self._entitiesUpdated[q]=nil end self._entitiesRemoved={}self._executor:ExecOnRemove(m,n)n=nil local o={}local p=false for q,r in pairs(self._entitiesUpdated)do if(r~=q.archetype)then p=true o[q]=r end end self._entitiesUpdated={}for q,r in pairs(self._entitiesCreated)do p=true o[q]=g.EMPTY q.isAlive=true self._repository:Insert(q)end self._entitiesCreated={}if p then self._executor:ExecOnExitEnter(m,o)o=nil end end end)end function j:Destroy()if self._loopCancel then self._loopCancel()self._loopCancel=nil end if self._onChangeArchetypeEvent then self._onChangeArchetypeEvent:Destroy()self._onChangeArchetypeEvent=nil end self._repository=nil if self._systems then for k,l in pairs(self._systems)do l:Destroy()end self._systems=nil end self._timer=nil self._ExecPlan=nil self._entitiesCreated=nil self._entitiesUpdated=nil self._entitiesRemoved=nil setmetatable(self,nil)end function j:_OnChangeArchetype(k,l,m)if k.isAlive then if self._entitiesUpdated[k]==nil then self._dirty=true self._entitiesUpdated[k]=l end self._repository:Update(k)k.version=self.version end end return j end return c(\"ECS\")\n]]></ProtectedString>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020f8c</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t</Item>\n\t<Item class=\"LuaWebService\" referent=\"RBXC9ED9E03516D447A890995FBFB27A2FC\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Instance</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182e4</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ProcessInstancePhysicsService\" referent=\"RBX5F1FBFACF45F4EA19A9CF2217A5F97BF\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">ProcessInstancePhysicsService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af000182e8</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Lighting\" referent=\"RBX4725CAF8161D47E3A86274F8683A413D\">\n\t\t<Properties>\n\t\t\t<Color3 name=\"Ambient\">\n\t\t\t\t<R>0.274509817</R>\n\t\t\t\t<G>0.274509817</G>\n\t\t\t\t<B>0.274509817</B>\n\t\t\t</Color3>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<float name=\"Brightness\">3</float>\n\t\t\t<Color3 name=\"ColorShift_Bottom\">\n\t\t\t\t<R>0</R>\n\t\t\t\t<G>0</G>\n\t\t\t\t<B>0</B>\n\t\t\t</Color3>\n\t\t\t<Color3 name=\"ColorShift_Top\">\n\t\t\t\t<R>0</R>\n\t\t\t\t<G>0</G>\n\t\t\t\t<B>0</B>\n\t\t\t</Color3>\n\t\t\t<float name=\"EnvironmentDiffuseScale\">1</float>\n\t\t\t<float name=\"EnvironmentSpecularScale\">1</float>\n\t\t\t<float name=\"ExposureCompensation\">0</float>\n\t\t\t<Color3 name=\"FogColor\">\n\t\t\t\t<R>0.752941251</R>\n\t\t\t\t<G>0.752941251</G>\n\t\t\t\t<B>0.752941251</B>\n\t\t\t</Color3>\n\t\t\t<float name=\"FogEnd\">100000</float>\n\t\t\t<float name=\"FogStart\">0</float>\n\t\t\t<float name=\"GeographicLatitude\">0</float>\n\t\t\t<bool name=\"GlobalShadows\">true</bool>\n\t\t\t<string name=\"Name\">Lighting</string>\n\t\t\t<Color3 name=\"OutdoorAmbient\">\n\t\t\t\t<R>0.274509817</R>\n\t\t\t\t<G>0.274509817</G>\n\t\t\t\t<B>0.274509817</B>\n\t\t\t</Color3>\n\t\t\t<bool name=\"Outlines\">false</bool>\n\t\t\t<float name=\"ShadowSoftness\">0.200000003</float>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<token name=\"Technology\">3</token>\n\t\t\t<string name=\"TimeOfDay\">14:30:00</string>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018303</UniqueId>\n\t\t</Properties>\n\t\t<Item class=\"SunRaysEffect\" referent=\"RBX03A6B34760B6477199BB49A06ADCD4C3\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<bool name=\"Enabled\">true</bool>\n\t\t\t\t<float name=\"Intensity\">0</float>\n\t\t\t\t<string name=\"Name\">SunRays</string>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<float name=\"Spread\">0.100000001</float>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018529</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t\t<Item class=\"Atmosphere\" referent=\"RBXD07C58A2F95A4BF191A57E43FB6DEECE\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<Color3 name=\"Color\">\n\t\t\t\t\t<R>1</R>\n\t\t\t\t\t<G>1</G>\n\t\t\t\t\t<B>1</B>\n\t\t\t\t</Color3>\n\t\t\t\t<Color3 name=\"Decay\">\n\t\t\t\t\t<R>0</R>\n\t\t\t\t\t<G>0.768627524</G>\n\t\t\t\t\t<B>1</B>\n\t\t\t\t</Color3>\n\t\t\t\t<float name=\"Density\">0</float>\n\t\t\t\t<float name=\"Glare\">0</float>\n\t\t\t\t<float name=\"Haze\">0</float>\n\t\t\t\t<string name=\"Name\">Atmosphere</string>\n\t\t\t\t<float name=\"Offset\">0</float>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001851b</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t\t<Item class=\"BloomEffect\" referent=\"RBXF8414CC0BCD1485CB9A251F82AE8EA76\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<bool name=\"Enabled\">true</bool>\n\t\t\t\t<float name=\"Intensity\">1</float>\n\t\t\t\t<string name=\"Name\">Bloom</string>\n\t\t\t\t<float name=\"Size\">24</float>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<float name=\"Threshold\">2</float>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001851c</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t\t<Item class=\"DepthOfFieldEffect\" referent=\"RBXC254F31ACDE14240891F667CBBCFD9D5\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<bool name=\"Enabled\">false</bool>\n\t\t\t\t<float name=\"FarIntensity\">0.100000001</float>\n\t\t\t\t<float name=\"FocusDistance\">0.0500000007</float>\n\t\t\t\t<float name=\"InFocusRadius\">30</float>\n\t\t\t\t<string name=\"Name\">DepthOfField</string>\n\t\t\t\t<float name=\"NearIntensity\">0.75</float>\n\t\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018520</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t\t<Item class=\"Sky\" referent=\"RBX31641C554FF24B268D89D38E6E8521F5\">\n\t\t\t<Properties>\n\t\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t\t<bool name=\"CelestialBodiesShown\">false</bool>\n\t\t\t\t<float name=\"MoonAngularSize\">11</float>\n\t\t\t\t<Content name=\"MoonTextureId\"><url>rbxasset://sky/moon.jpg</url></Content>\n\t\t\t\t<string name=\"Name\">Sky</string>\n\t\t\t\t<Content name=\"SkyboxBk\"><url>rbxassetid://1194117595</url></Content>\n\t\t\t\t<Content name=\"SkyboxDn\"><url>rbxassetid://724361046</url></Content>\n\t\t\t\t<Content name=\"SkyboxFt\"><url>rbxassetid://1194117232</url></Content>\n\t\t\t\t<Content name=\"SkyboxLf\"><url>rbxassetid://1194117595</url></Content>\n\t\t\t\t<Content name=\"SkyboxRt\"><url>rbxassetid://1194117232</url></Content>\n\t\t\t\t<Content name=\"SkyboxUp\"><url>rbxassetid://1194119030</url></Content>\n\t\t\t\t<int64 name=\"SourceAssetId\">5446998963</int64>\n\t\t\t\t<int name=\"StarCount\">3000</int>\n\t\t\t\t<float name=\"SunAngularSize\">21</float>\n\t\t\t\t<Content name=\"SunTextureId\"><url>rbxasset://sky/sun.jpg</url></Content>\n\t\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t\t<UniqueId name=\"UniqueId\">0241ee8883f291ef0181ac6c00b1e4f4</UniqueId>\n\t\t\t</Properties>\n\t\t</Item>\n\t</Item>\n\t<Item class=\"DataStoreService\" referent=\"RBX8F78A6280A5C4E60BED9DD2D50B3E625\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"AutomaticRetry\">true</bool>\n\t\t\t<bool name=\"LegacyNamingScheme\">false</bool>\n\t\t\t<string name=\"Name\">DataStoreService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001851e</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"HttpService\" referent=\"RBXB6E974B4254D4FA7887C4234900229C5\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"HttpEnabled\">false</bool>\n\t\t\t<string name=\"Name\">HttpService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018521</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"LanguageService\" referent=\"RBX2568EEE3F06340C982C8B73D60FA8AA9\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">LanguageService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00018522</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"Teams\" referent=\"RBX8689C418DE49464CB5D069A95FDF9E4A\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">Teams</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001852a</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"TestService\" referent=\"RBX4C89FB9B784B4C74A87D93D91C327565\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"AutoRuns\">true</bool>\n\t\t\t<string name=\"Description\"></string>\n\t\t\t<bool name=\"ExecuteWithStudioRun\">false</bool>\n\t\t\t<bool name=\"Is30FpsThrottleEnabled\">true</bool>\n\t\t\t<bool name=\"IsPhysicsEnvironmentalThrottled\">true</bool>\n\t\t\t<bool name=\"IsSleepAllowed\">true</bool>\n\t\t\t<string name=\"Name\">TestService</string>\n\t\t\t<int name=\"NumberOfPlayers\">0</int>\n\t\t\t<double name=\"SimulateSecondsLag\">0</double>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<double name=\"Timeout\">10</double>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001852c</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"VirtualInputManager\" referent=\"RBX35E0F67273514BF8AB9FB77F74D5B99D\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<string name=\"Name\">VirtualInputManager</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af0001852e</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<Item class=\"ProximityPromptService\" referent=\"RBX4F136E80680543DF9258D23CB2D0D95F\">\n\t\t<Properties>\n\t\t\t<BinaryString name=\"AttributesSerialize\"></BinaryString>\n\t\t\t<bool name=\"Enabled\">true</bool>\n\t\t\t<int name=\"MaxPromptsVisible\">16</int>\n\t\t\t<string name=\"Name\">ProximityPromptService</string>\n\t\t\t<int64 name=\"SourceAssetId\">-1</int64>\n\t\t\t<BinaryString name=\"Tags\"></BinaryString>\n\t\t\t<UniqueId name=\"UniqueId\">0cf7e2b366cacefc0180a4af00020ca6</UniqueId>\n\t\t</Properties>\n\t</Item>\n\t<SharedStrings>\n\t\t<SharedString md5=\"yuZpQdnvvUBOTYh1jqZ2cA==\"></SharedString>\n\t</SharedStrings>\n</roblox>"
  },
  {
    "path": "examples/pong/src/client/Constants.lua",
    "content": "\nlocal BALL_SPEED = 50\n\nlocal constants = {\n   BALL_BOOST = 0.3,\n   BALL_RADIUS = 1,\n   BALL_SPEED = BALL_SPEED,\n   BALL_SERVE_SPEED = BALL_SPEED*0.7,\n   BALL_SPEED_SECONDARY = BALL_SPEED*0.5,\n   COURT_WIDTH = 72,\n   COURT_HEIGHT = 30,\n   PADDLE_WIDTH = 1,\n   PADDLE_HEIGHT = 10,\n   PLAYER_SPEED = 5,\n   CAMERA_DISTANCE = 30\n}\n\nreturn constants\n"
  },
  {
    "path": "examples/pong/src/client/Main.client.lua",
    "content": "\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nlocal Constants = require(script.Parent:WaitForChild(\"Constants\"))\n\nlocal Components = script.Parent:WaitForChild(\"components\")\nlocal Ball = require(Components.Ball)\nlocal Score = require(Components.Score)\nlocal Paddle = require(Components.Paddle)\nlocal Player = require(Components.Player)\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nlocal Systems = script.Parent.systems\nlocal MoveSystem = require(Systems.MoveSystem)\nlocal BallSystem = require(Systems.BallSystem)\nlocal AudioSystem = require(Systems.AudioSystem)\nlocal ScoreSystem = require(Systems.ScoreSystem)\nlocal PaddleSystem = require(Systems.PaddleSystem)\nlocal PaddleHitSystem = require(Systems.PaddleHitSystem)\nlocal PlayerAiThinkSystem = require(Systems.PlayerAiThinkSystem)\nlocal PlayerHumanInputSystem = require(Systems.PlayerHumanInputSystem)\n\nlocal RenderSystem = require(Systems.RenderSystem)\nlocal CameraSystem = require(Systems.CameraSystem)\n\nlocal world = ECS.World({\n   MoveSystem,\n   BallSystem,\n   AudioSystem,\n   ScoreSystem, \n   PaddleSystem, \n   PaddleHitSystem, \n   PlayerAiThinkSystem,\n   PlayerHumanInputSystem,\n   CameraSystem,\n   RenderSystem,\n}, 60)\n\n-- Ball\nworld:Entity(Ball())\n\n-- Player\nworld:Entity(\n   Score(),\n   PlayerHuman(),\n   Paddle({ side = \"left\" })\n)\n\n-- AI\nworld:Entity(\n   Score(),\n   PlayerAI(),\n   Paddle({ side = \"right\" })\n)\n"
  },
  {
    "path": "examples/pong/src/client/Utility.lua",
    "content": "\nlocal Utility = {}\n\nfunction Utility.map(x, inMin, inMax, outMin, outMax)\n   return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin\nend\n\nfunction Utility.lerp(v0, v1, t)\n   return (1-t)*v0 + t*v1\nend\n\nreturn Utility\n"
  },
  {
    "path": "examples/pong/src/client/components/AudioSource.lua",
    "content": "\nlocal AudioSource = _G.ECS.Component({\n   clip = \"\",     -- sound asset\n   volume = 10,    -- playback volume between [0..10]\n   loop = false,  -- If true, the audio clip replays when it ends\n   sound = nil\n})\n\nAudioSource.States = {\n   [\"Playing\"] = { \"Stopped\" },\n   [\"Stopped\"] = { \"Playing\" }\n}\n\nAudioSource.StateInitial = \"Stopped\"\n\nAudioSource.Case = {\n   Playing = function(self, previous)\n      if self.sound then\n         self.sound:Play()\n      end\n   end,\n   Stopped = function(self, previous)\n      if self.sound then\n         self.sound:Stop()\n      end\n   end\n}\n\nreturn AudioSource\n"
  },
  {
    "path": "examples/pong/src/client/components/Ball.lua",
    "content": "\nlocal Ball = _G.ECS.Component({\n   secondary = false,\n   initialDirection = nil,\n})\n\nreturn Ball\n"
  },
  {
    "path": "examples/pong/src/client/components/BasePart.lua",
    "content": "\nlocal BasePart = _G.ECS.Component()\n\nreturn BasePart\n"
  },
  {
    "path": "examples/pong/src/client/components/Paddle.lua",
    "content": "local Paddle = _G.ECS.Component({\n   side = \"left\",\n   hits = 0,\n   target = 0,    -- -1 = bottom, 0 = middle, 1 = top\n   position = 0   -- -1 = bottom, 0 = middle, 1 = top\n})\n\nreturn Paddle\n"
  },
  {
    "path": "examples/pong/src/client/components/Player.lua",
    "content": "\nlocal Player = _G.ECS.Component()\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nreturn Player\n"
  },
  {
    "path": "examples/pong/src/client/components/Position.lua",
    "content": "\nlocal Position = _G.ECS.Component(Vector3.new(0, 0, 0))\n\nreturn Position\n"
  },
  {
    "path": "examples/pong/src/client/components/Score.lua",
    "content": "\nlocal Score = _G.ECS.Component(0)\n\nreturn Score\n"
  },
  {
    "path": "examples/pong/src/client/components/Velocity.lua",
    "content": "\nlocal Velocity = _G.ECS.Component(Vector3.new(0, 0, 0))\n\nreturn Velocity\n"
  },
  {
    "path": "examples/pong/src/client/systems/AudioSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal BasePart = require(Components.BasePart)\nlocal Position = require(Components.Position)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal AudioSystem = ECS.System(\"transform\", 100, ECS.Query.All(AudioSource))\n\nfunction AudioSystem:Initialize(Time)\n   self.queryStopped = ECS.Query.All(AudioSource, AudioSource.In(\"Stopped\")).Build()\nend\n\nfunction AudioSystem:Update(Time)\n   self:Result(self.queryStopped):ForEach(function(entity)\n      self._world:Remove(entity)\n   end)\nend\n\nfunction AudioSystem:OnEnter(Time, entity)\n   local source = entity[AudioSource]\n   local position = entity[Position]\n   \n   -- create a sound\n   local sound = Instance.new(\"Sound\")\n   sound.SoundId = source.clip\n   sound.Looped = source.loop\n   source.sound = sound\n\n   if position then\n      -- create a part\n      local part = Instance.new(\"Part\")\n      part.Anchored = true\n      part.CanCollide = false\n      part.Transparency = 1\n      part.Position = position.value\n      part.Name = \"sound#\"..source.clip\n\n      sound.Parent = part\n      part.Parent = game.Workspace\n      entity[BasePart] = BasePart(part)\n   end\n\n   sound.Ended:Connect(function()\n      source:SetState(\"Stopped\")\n   end)\n\n   source:SetState(\"Playing\")\nend\n\nfunction AudioSystem:OnExit(Time, entity)\n   local part = entity[BasePart]\n   if part then\n      part.value.Parent = nil\n   end\nend\n\nfunction AudioSystem:OnRemove(Time, entity)\n   local part = entity[BasePart]\n   if part then\n      part.value.Parent = nil\n   end\nend\n\nreturn AudioSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/BallSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal BasePart = require(Components.BasePart)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal ballMaxX = Constants.COURT_WIDTH/2\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal BallSystem = ECS.System(\"transform\", 1, ECS.Query.All(Ball))\n\nfunction BallSystem:Update(Time)\n   local scored = false\n   local scoredSide\n\n   self:Result():ForEach(function(entity)\n      local ball = entity[Ball]\n      local position = entity[Position]\n      local velocity = entity[Velocity]\n   \n      local posValue = position.value\n   \n      if posValue.Z > ballMaxZ or posValue.Z < -ballMaxZ then\n         -- Reverse z velocity if ball hits a vertical wall\n         local v = velocity.value\n         velocity.value = Vector3.new(v.X, v.Y, v.Z*-1)\n\n         if posValue.Z > ballMaxZ then\n            posValue = Vector3.new(posValue.X, posValue.Y, ballMaxZ)\n         else\n            posValue = Vector3.new(posValue.X, posValue.Y, -ballMaxZ )\n         end\n         position.value = posValue\n\n         -- sound effect\n         self._world:Entity(\n            Position(posValue),\n            AudioSource({ clip = \"rbxassetid://4458219865\" })\n         )\n      end\n   end)\nend\n\nfunction BallSystem:OnEnter(Time, entity)\n   \n   local radius = Constants.BALL_RADIUS\n   local size = radius*2\n\n   local part = Instance.new(\"Part\")\n   part.Name = \"Ball\"\n   part.Anchored = true\n   part.Size = Vector3.new(size, size, size)\n   part.Shape = Enum.PartType.Ball\n   part.Color = Color3.fromRGB(255, 255, 255)\n   part.Material = Enum.Material.Neon\n   part.Parent = game.Workspace\n   entity[BasePart] = BasePart(part)\n\n\n   local speed = Constants.BALL_SERVE_SPEED\n   local position = Vector3.new(0, radius, 0)\n   \n   local ball = entity[Ball]\n\n   if ball.initialDirection then\n      position = Vector3.new(-ballMaxX, radius, 0)\n      if ball.initialDirection == \"left\" then\n         speed = speed * -1\n         position = Vector3.new(ballMaxX, radius, 0)\n      end\n   else\n      if math.random() > 0.5 then\n         speed = speed * -1\n      end\n   end\n\n   entity[Position] = Position(position)\n   entity[Velocity] = Velocity(Vector3.new(speed, 0, 0))\n\n   -- sound effect\n   self._world:Entity(\n      Position(entity[Position].value),\n      AudioSource({ clip = \"rbxassetid://1837831535\" })\n   )\nend\n\nfunction BallSystem:OnRemove(Time, entity)\n   local part = entity[BasePart].value\n   part.Parent = nil\n   entity[BasePart] = nil\nend\n\nreturn BallSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/CameraSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal CFRAME = CFrame.new(Vector3.new(0, Constants.CAMERA_DISTANCE, 30), Vector3.new(0, 0, 0))\n\nlocal CameraSystem = ECS.System(\"render\", 1, function()\n   game.Workspace.CurrentCamera.CFrame = CFRAME\nend)\n\nreturn CameraSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/MoveSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal Velocity = require(Components.Velocity)\nlocal Position = require(Components.Position)\n\nlocal MoveSystem = ECS.System(\"process\", 10, ECS.Query.All(Position, Velocity))\n\nfunction MoveSystem:Update(Time)\n   self:Result(self.queryBalls):ForEach(function(entity)\n      local position = entity[Position]\n      local velocity = entity[Velocity]\n   \n      -- interpolation\n      -- position.valueOld = position.value\n      position.value = position.value + velocity.value * Time.DeltaFixed\n   end)\nend\n\nreturn MoveSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/PaddleHitSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Paddle = require(Components.Paddle)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal PADDLE_AIM_C = 1.5\n\n-- compute outgoing angle depending on which point the ball hits the paddle\nlocal function computeBounce(ettBall, ettPaddle)\n   \n   local ball = ettBall[Ball]\n   local ballPos = ettBall[Position].value\n   local ballVel = ettBall[Velocity].value\n   local paddle = ettPaddle[Paddle]\n   local paddlePos = ettPaddle[Position].value\n\n   -- The sharpness of the angle is determined by where the ball hits the paddle\n   local angle = PADDLE_AIM_C * (ballPos.Z - paddlePos.Z)/Constants.PADDLE_HEIGHT\n\n   local spped = ball.secondary and Constants.BALL_SPEED_SECONDARY or Constants.BALL_SPEED\n\n   local ballVelZ = math.sin(angle) * spped\n   local ballVelX = math.cos(angle) * spped\n   \n   -- if the angle exceeds a magic value, the ball gets an extra speed boost\n   local angleAbs = math.abs(angle)\n   if (angleAbs > 0.6) then\n      local boost = (1 + angleAbs * Constants.BALL_BOOST)\n      ballVelX = ballVelX * boost\n      ballVelZ = ballVelZ * boost\n   end\n\n   -- Determine the direction in which the ball should go\n   if paddle.side == \"right\" then\n      ballVelX = ballVelX*-1\n   end\n   ettBall[Velocity].value = Vector3.new(ballVelX, 0, ballVelZ)\nend\n\nlocal function intersects(ettBall, ettPaddle)\n\n   local ball = ettBall[Ball]\n   local ballPos = ettBall[Position].value\n   local paddlePos = ettPaddle[Position].value\n   \n   -- circle\n   local cx, cz, radius = ballPos.X, ballPos.Z, Constants.BALL_RADIUS\n   -- rectangle\n   local rw, rh = Constants.PADDLE_WIDTH, Constants.PADDLE_HEIGHT\n   local rx, rz = paddlePos.X - rw/2, paddlePos.Z - rh/2\n   \n   -- temporary variables to set edges for testing\n   local testX = cx\n   local testZ = cz\n   \n   local xEdge, zEdge\n   -- which edge is closest?\n   if cx < rx then\n      testX = rx \n      xEdge = \"left\"\n   elseif cx > rx + rw then    \n      testX = rx+rw\n      xEdge = \"right\"\n   end \n\n   if cz < rz then\n      testZ = rz\n      zEdge = \"top\"\n   elseif cz > rz+rh then    \n      testZ = rz+rh\n      zEdge = \"bottom\"\n   end \n\n   -- get distance from closest edges\n\n   local distX = cx-testX\n   local distY = cz-testZ   \n   local distance = math.sqrt( (distX*distX) + (distY*distY) );\n \n   -- if the distance is less than the radius, collision!\n   if (distance <= radius) then\n      local normal \n      if distY < distX then\n         normal = (zEdge == \"top\") and Vector3.new(0, 0, 1) or Vector3.new(0, 0, -1)\n      else\n         normal = (xEdge == \"left\") and Vector3.new(1, 0, 0) or Vector3.new(-1, 0, 0)\n      end\n      return {\n         normal = normal,\n         distance = distance\n      }\n   end\n   return nil   \nend\n\nlocal PaddleHitSystem = ECS.System(\"transform\", 2, ECS.Query.All(Paddle, Position))\n\nfunction PaddleHitSystem:Initialize(Time)\n   self.queryBalls = ECS.Query.All(Ball, Position).Build()\nend\n\nfunction PaddleHitSystem:Update(Time)\n   local ettsBall = self:Result(self.queryBalls):ToArray()\n\n   local ballSpawned = false\n\n   self:Result():ForEach(function(ettPaddle)\n      local paddle = ettPaddle[Paddle]\n      local pPosition = ettPaddle[Position]\n   \n      -- collision detection\n      for i,ettBall in ipairs(ettsBall) do\n         local collistion = intersects(ettBall, ettPaddle)\n         if collistion then\n   \n            -- move the ball out of the paddle\n            local ballPos = ettBall[Position].value\n            ettBall[Position].value = ballPos - collistion.normal * collistion.distance\n           \n            computeBounce(ettBall, ettPaddle)\n\n            if #ettsBall < 2 then               \n               paddle.hits = paddle.hits + 1\n               if paddle.hits == 5 then\n                  -- create new ball\n                  local inverseDirection = (paddle.side == \"left\") and \"right\" or \"left\" \n                  self._world:Entity(Ball({ initialDirection = inverseDirection, secondary = true }))\n                  ballSpawned = true\n               end\n            end\n\n            -- sound effect\n            self._world:Entity(\n               Position(ballPos),\n               AudioSource({ clip = \"rbxassetid://4458219865\" })\n            )\n\n            -- break\n            return true\n         end\n      end\n   end)\n\n   if ballSpawned then\n      self:Result():ForEach(function(ettPaddle)\n         local paddle = ettPaddle[Paddle]\n         paddle.hits = 0\n      end)\n   end\nend\n\nreturn PaddleHitSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/PaddleSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Paddle = require(Components.Paddle)\nlocal Position = require(Components.Position)\nlocal BasePart = require(Components.BasePart)\n\nlocal PaddleSystem = ECS.System(\"process\", 2, ECS.Query.All(Paddle))\n\nfunction PaddleSystem:Update(Time)\n   self:Result():ForEach(function(entity)\n      local paddle = entity[Paddle]\n      if paddle.target ~= paddle.position then\n         paddle.position = Utility.lerp(paddle.position, paddle.target, Constants.PLAYER_SPEED * Time.DeltaFixed)\n         entity[Position].value = self:GetPaddlePosition(paddle)\n      end\n   end)\nend\n\nfunction PaddleSystem:OnEnter(Time, entity)\n   local paddle = entity[Paddle]\n   paddle.target = 0\n   paddle.position = 0\n\n   local positionVec3 = self:GetPaddlePosition(paddle)\n   entity[Position] = Position(positionVec3)\n\n   local part = Instance.new(\"Part\")\n   part.Name = \"Paddle_\"..paddle.side\n   part.Size = Vector3.new(Constants.PADDLE_WIDTH, 2, Constants.PADDLE_HEIGHT)\n   part.Shape = Enum.PartType.Block\n   part.Anchored = true\n   part.Position = positionVec3\n   part.Material = Enum.Material.SmoothPlastic\n\n   if paddle.side == \"left\" then\n      part.Color = Color3.fromRGB(33, 84, 185)\n   else\n      part.Color = Color3.fromRGB(255, 89, 89)\n   end\n   \n   part.Parent = game.Workspace\n   entity[BasePart] = BasePart(part)\nend\n\nfunction PaddleSystem:GetPaddlePosition(paddle)\n\n   local xPos = Constants.COURT_WIDTH/2\n   if paddle.side == \"left\" then\n      xPos = xPos * -1\n   end\n   \n   local zPosMax = Constants.COURT_HEIGHT/2 - Constants.PADDLE_HEIGHT/2\n   local zPos = Utility.map(paddle.position, -1, 1, -zPosMax, zPosMax)\n   return Vector3.new(xPos, 1, zPos)\nend\n\nreturn PaddleSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/PlayerAiThinkSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\n\nlocal Utility = require(Client.Utility)\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Paddle = require(Components.Paddle)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal Player = require(Components.Player)\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal PlayerAiThinkSystem = ECS.System(\"process\", 1, ECS.Query.All(PlayerAI, Paddle, Position))\n\nfunction PlayerAiThinkSystem:Initialize(Time)\n   self.queryHuman = ECS.Query.All(PlayerHuman, Paddle, Position).Build()\n   self.queryBalls = ECS.Query.All(Ball, Position, Velocity).Build()\nend\n\nfunction PlayerAiThinkSystem:Update(Time)\n\n   local ettPaddleAI = self:Result():FindAny()\n   local paddle = ettPaddleAI[Paddle]\n   local paddlePos = ettPaddleAI[Position].value\n\n   -- Get the ball that is coming towards the AI and is closer\n   local tgBallPos\n\n   self:Result(self.queryBalls):ForEach(function(ettBall)\n      local ballPos = ettBall[Position].value\n      local ballVel = ettBall[Velocity].value\n      \n      local ballTowardsAI\n      if paddle.side == \"right\" then\n         ballTowardsAI = ballVel.X > 0\n      else\n         ballTowardsAI = ballVel.X < 0\n      end\n   \n      if ballTowardsAI then\n         if tgBallPos == nil then\n            tgBallPos = ballPos\n         else\n            -- the target is the ball that is closest to the racket\n            if paddle.side == \"right\" then\n               if ballPos.X > tgBallPos.X then\n                  tgBallPos = ballPos\n               end\n            else\n               if ballPos.X < tgBallPos.X then\n                  tgBallPos = ballPos\n               end\n            end\n         end\n      end\n   end)\n\n   if tgBallPos then\n      paddle.target = Utility.map(tgBallPos.Z, -ballMaxZ, ballMaxZ, -1, 1)\n   end\nend\n\nreturn PlayerAiThinkSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/PlayerHumanInputSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal CurrentCamera = game.workspace.CurrentCamera\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\n\nlocal Components = Client.components\nlocal Paddle = require(Components.Paddle)\nlocal Player = require(Components.Player)\nlocal PlayerHuman = Player.Qualifier(\"Human\")\n\nlocal PlayerHumanInputSystem = ECS.System(\"process\", 1, ECS.Query.All(PlayerHuman, Paddle))\n\nfunction PlayerHumanInputSystem:Initialize()\n   UserInputService.MouseIconEnabled = false\nend\n\nfunction PlayerHumanInputSystem:Update(Time)\n   local screenSizeY = CurrentCamera.ViewportSize.Y\n   local mousePosY = UserInputService:GetMouseLocation().Y\n\n   local min = screenSizeY*0.2\n   local max = screenSizeY*0.8\n\n   mousePosY = math.max(math.min(mousePosY, max), min)\n\n   local entity = self:Result():FindAny()\n   local paddle = entity[Paddle]\n\n   paddle.target = Utility.map(mousePosY, min, max, -1, 1)\nend\n\nreturn PlayerHumanInputSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/RenderSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal Position = require(Components.Position)\nlocal BasePart = require(Components.BasePart)\n\nlocal RenderSystem = ECS.System(\"render\", 2, ECS.Query.All(Position, BasePart))\n\nfunction RenderSystem:Update(Time)\n   self:Result():ForEach(function(entity)\n      local position = entity[Position]\n      local part = entity[BasePart].value\n   \n      if position.valueOld then\n         part.Position = position.valueOld:Lerp(position.value, Time.Interpolation)\n      else\n         part.Position = position.value\n      end\n   end)\nend\n\nreturn RenderSystem\n"
  },
  {
    "path": "examples/pong/src/client/systems/ScoreSystem.lua",
    "content": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = Client.components\nlocal Ball = require(Components.Ball)\nlocal Paddle = require(Components.Paddle)\nlocal Score = require(Components.Score)\nlocal Player = require(Components.Player)\nlocal Position = require(Components.Position)\nlocal Velocity = require(Components.Velocity)\nlocal BasePart = require(Components.BasePart)\nlocal AudioSource = require(Components.AudioSource)\n\nlocal ballMaxX = Constants.COURT_WIDTH/2\nlocal ballMaxZ = Constants.COURT_HEIGHT/2 - Constants.BALL_RADIUS\n\nlocal ScoreSystem = ECS.System(\"transform\", 2, ECS.Query.All(Score, Paddle))\n\nfunction ScoreSystem:Initialize(Time)\n   self.queryBalls = ECS.Query.All(Ball, Position).Build()\nend\n\nfunction ScoreSystem:Update(Time)\n   local ettScored = false\n   local scoredSide\n\n   local balls = self:Result(self.queryBalls):ToArray()\n\n   self:Result():ForEach(function(entity)\n      local paddle = entity[Paddle]\n      local score = entity[Score]\n\n      for i,ettBall in ipairs(balls) do         \n         local ball = ettBall[Ball]\n         local ballPos = ettBall[Position].value\n      \n         -- if ball hits horizontal wall, reset the game      \n         if (paddle.side == \"right\" and ballPos.X < -ballMaxX) then\n            ettScored = entity\n         elseif (paddle.side == \"left\" and ballPos.X > ballMaxX ) then\n            ettScored = entity\n         end\n   \n         if ettScored then\n            score.value = score.value + 1\n            score.TextLabel.Text = tostring(score.value)\n\n            -- sound effect\n            self._world:Entity(\n               Position(ballPos),\n               AudioSource({ clip = \"rbxassetid://1843023345\" })\n            )\n   \n            -- break\n            return true\n         end\n      end\n   end)\n\n   if ettScored then\n      -- remove all balls\n      for i,ettBall in ipairs(balls) do\n         self._world:Remove(ettBall)\n      end\n\n      -- create new ball\n      self._world:Entity(Ball())\n\n      self:Result(self.queryPlayers):ForEach(function(entity)\n         local paddle = entity[Paddle]\n         paddle.hits = 0\n      end)\n   end\nend\n\nfunction ScoreSystem:OnEnter(Time, entity)\n   local score = entity[Score]\n   local paddle = entity[Paddle]\n   score.goalPart = game.Workspace:FindFirstChild(\"goal_\"..paddle.side)\n   score.TextLabel = score.goalPart.BillboardGui.TextLabel   \nend\n\nreturn ScoreSystem\n"
  },
  {
    "path": "examples/pong/src/server/Main.server.lua",
    "content": "game.Players.CharacterAutoLoads = false\n"
  },
  {
    "path": "examples/pong/src/shared/ECS.lua",
    "content": "--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tThis is a minified version of ECS Lua, to see the full source code visit\n\thttps://github.com/nidorx/ecs-lua\n\n   Discussions about this script are at https://devforum.roblox.com/t/841175\n\n\t------------------------------------------------------------------------------\n\n\tMIT License\n\n\tCopyright (c) 2021 Alex Rodin\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.\n]]\nlocal a,b={},{}local function c(d)if(not a[d])then a[d]={r=b[d]()}end return a[d].r end b[\"Archetype\"]=function()local d={}local e={}local f={}local g=0 local h={}h.__index=h function h.Of(i)local j={}local k={}for m,n in ipairs(i)do if(n.IsCType and not n.isComponent)then if n.IsQualifier then if k[n]==nil then k[n]=true table.insert(j,n.Id)end n=n.SuperClass end if k[n]==nil then k[n]=true table.insert(j,n.Id)end end end table.sort(j)local l=\"_\"..table.concat(j,\"_\")if d[l]==nil then d[l]=setmetatable({id=l,_components=k},h)g=g+1 end return d[l]end function h.Version()return g end function h:Has(i)return(self._components[i]==true)end function h:With(i)if self._components[i]==true then return self end local j=e[self]if not j then j={}e[self]=j end local k=j[i]if k==nil then local l={i}for m,n in pairs(self._components)do table.insert(l,m)end k=h.Of(l)j[i]=k end return k end function h:WithAll(i)local j={}for k,l in pairs(self._components)do table.insert(j,k)end for k,l in ipairs(i)do if self._components[l]==nil then table.insert(j,l)end end return h.Of(j)end function h:Without(i)if self._components[i]==nil then return self end local j=f[self]if not j then j={}f[self]=j end local k=j[i]if k==nil then local l={}for m,n in pairs(self._components)do if m~=i then table.insert(l,m)end end k=h.Of(l)j[i]=k end return k end function h:WithoutAll(i)local j={}for l,m in ipairs(i)do j[m]=true end local k={}for l,m in pairs(self._components)do if j[l]==nil then table.insert(k,l)end end return h.Of(k)end h.EMPTY=h.Of({})return h end b[\"Component\"]=function()local d=c(\"Utility\")local e=c(\"ComponentFSM\")local f=d.copyDeep local g=d.mergeDeep local h=0 local function i(l,m)h=h+1 local n={Id=h,IsCType=true,SuperClass=m}n.__index=n if m==nil then m=n m._Qualifiers={[\"Primary\"]=n}m._QualifiersArr={n}m._Initializers={}else m.HasQualifier=true n.IsQualifier=true n.HasQualifier=true end local o=m._Qualifiers local p=m._QualifiersArr setmetatable(n,{__call=function(q,r)return n.New(r)end,__index=function(q,r)if(r==\"States\")then return m.__States end if(r==\"Case\"or r==\"StateInitial\")then return rawget(m,r)end end,__newindex=function(q,r,s)if(r==\"Case\"or r==\"States\"or r==\"StateInitial\")then if n==m then if(r==\"States\")then if not m.IsFSM then e.AddCapability(m,s)for t,u in pairs(o)do if u~=m then e.AddMethods(m,u)end end end else rawset(q,r,s)end end else rawset(q,r,s)end end})if m.IsFSM then e.AddMethods(m,n)end function n.Qualifier(q)if type(q)~=\"string\"then for s,t in ipairs(p)do if t==q then return q end end return nil end local r=o[q]if r==nil then r=i(l,m)o[q]=r table.insert(p,r)end return r end function n.Qualifiers(...)local q={...}if#q==0 then return p else local r={}local s={}for t,u in ipairs({...})do local v=n.Qualifier(u)if v and s[v]==nil then s[v]=true table.insert(r,v)end end return r end end function n.New(q)if(q~=nil and type(q)~=\"table\")then q={value=q}end local r=setmetatable(l(q)or{},n)for s,t in ipairs(m._Initializers)do t(r)end r.isComponent=true r._qualifiers={[n]=r}return r end function n:GetType()return n end function n:Is(q)return q==n or q==m end function n:Primary()return self._qualifiers[m]end function n:Qualified(q)return self._qualifiers[n.Qualifier(q)]end function n:QualifiedAll()local q={}for r,s in pairs(o)do q[r]=self._qualifiers[s]end return q end function n:Merge(q)if m.HasQualifier then if self==q then return end if self._qualifiers==q._qualifiers then return end if not q:Is(m)then return end local r=n local s=q:GetType()local t if r==m then t=self._qualifiers elseif s==m then t=q._qualifiers elseif self._qualifiers[m]~=nil then t=self._qualifiers[m]._qualifiers elseif q._qualifiers[m]~=nil then t=q._qualifiers[m]._qualifiers end if t~=nil then if self._qualifiers~=t then for u,v in pairs(self._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end if q._qualifiers~=t then for u,v in pairs(q._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end else for u,v in pairs(q._qualifiers)do if r~=u then self._qualifiers[u]=v v._qualifiers=self._qualifiers end end end end end function n:Detach()if not m.HasQualifier then return end self._qualifiers[n]=nil self._qualifiers={[n]=self}end return n end local function j(l)return l or{}end local k={}function k.Create(l)local m=j if l~=nil then local n=type(l)if(n==\"function\")then m=l else if(n~=\"table\")then l={value=l}end m=function(o)local p=f(l)if(o~=nil)then g(p,o)end return p end end end return i(m,nil)end return k end b[\"ComponentFSM\"]=function()local d=c(\"Query\")local e=d.Filter(function(g,h)local i=h.States local j=h.IsSuperClass local k=h.ComponentClass if j then local l=k.Qualifiers()for m,n in ipairs(l)do local o=g[n]if(o~=nil and i[o:GetState()]==true)then return true end end return false else local l=g[k]if l==nil then return false end return i[l:GetState()]==true end end)local f={}function f.AddCapability(g,h)g.IsFSM=true local i=setmetatable({},{__newindex=function(j,k,l)if(type(l)~=\"table\")then l={l}end if table.find(l,\"*\")then rawset(j,k,\"*\")else local m=table.find(l,k)if m~=nil then table.remove(l,m)if#l==0 then l=\"*\"end end rawset(j,k,l)end end})rawset(g,\"__States\",i)for j,k in pairs(h)do if g.StateInitial==nil then g.StateInitial=j end i[j]=k end f.AddMethods(g,g)table.insert(g._Initializers,function(j)j:SetState(g.StateInitial)end)end function f.AddMethods(g,h)h.IsFSM=true local i=g.States function h.In(...)local j={}local k=0 for l,m in ipairs({...})do if(i[m]~=nil and j[m]==nil)then k=k+1 j[m]=true end end if k==0 then return{}end return e({States=j,IsSuperClass=(h==g),ComponentClass=h,})end function h:SetState(j)if(j==nil or i[j]==nil)then return end local k=self:GetState()if(k==j)then return end if(k~=nil)then local m=i[k]if(m~=\"*\"and table.find(m,j)==nil)then return end end self._state=j self._statePrev=k self._stateTime=os.clock()local l=g.Case and g.Case[j]if l then l(self,k)end end function h:GetState()return self._state or g.StateInitial end function h:GetPrevState()return self._statePrev or nil end function h:GetStateTime()return self._stateTime or 0 end end return f end b[\"ECS\"]=function()local d=c(\"Query\")local e=c(\"World\")local f=c(\"System\")local g=c(\"Archetype\")local h=c(\"Component\")local function i(k)e.LoopManager=k end pcall(function()if(game and game.ClassName==\"DataModel\")then i(c(\"RobloxLoopManager\")())end end)local j={Query=d,World=e.New,System=f.Create,Archetype=g,Component=h.Create,SetLoopManager=i}if _G.ECS==nil then _G.ECS=j else local k=_G.warn or print k(\"ECS Lua was not registered in the global variables, there is already another object registered.\")end return j end b[\"Entity\"]=function()local d=c(\"Archetype\")local e=0 local function f(m,...)local n={...}local o=m._data if(#n==1)then local q=n[1]if(q.IsCType and not q.isComponent)then return o[q]else return nil end end local p={}for q,r in ipairs(n)do if(r.IsCType and not r.isComponent)then table.insert(p,o[r])end end return table.unpack(p)end local function g(m,n,o)local p=m._data local q for r,s in ipairs(o.Qualifiers())do if s~=o then q=p[s]if q then break end end end if q then q:Merge(n)end end local function h(m,...)local n={...}local o=m._data local p=m.archetype local q=p local r={}local s=n[1]if(s and s.IsCType and not s.isComponent)then local t=n[2]local u if t==nil then local v=o[s]if v then v:Detach()end o[s]=nil q=q:Without(s)elseif(type(t)==\"table\"and t.isComponent)then local v=o[s]if(v~=t)then if v then v:Detach()end s=t:GetType()o[s]=t q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,t,s)end end else local v=o[s]if v then v:Detach()end local w=s(t)o[s]=w q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,w,s)end end else for t,u in ipairs(n)do if(u.isComponent)then local v=u:GetType()local w=o[v]if(w~=u)then if w then w:Detach()end o[v]=u q=q:With(v)if(v.HasQualifier or v.IsQualifier)then g(m,u,v)end end end end end if(p~=q)then m.archetype=q m._onChange:Fire(m,p)end end local function i(m,...)local n=m._data local o=m.archetype local p=o for q,r in ipairs({...})do if r.isComponent then local s=r:GetType()local t=n[s]if t then t:Detach()end n[s]=nil p=p:Without(s)elseif r.IsCType then local s=n[r]if s then s:Detach()end n[r]=nil p=p:Without(r)end end if m.archetype~=p then m.archetype=p m._onChange:Fire(m,o)end end local function j(m,n)local o=m._data local p={}if(n~=nil and n.IsCType and not n.isComponent)then local q=n.Qualifiers()for r,s in ipairs(q)do local t=o[s]if t then table.insert(p,t)end end else for q,r in pairs(o)do table.insert(p,r)end end return p end local function k(m,n)if(n~=nil and n.IsCType and not n.isComponent)then local o=m._data local p=n.Qualifiers()for q,r in ipairs(p)do local s=o[r]if s then return s end end end end local l={__index=function(m,n)if(type(n)==\"table\")then return f(m,n)end end,__newindex=function(m,n,o)local p=true if(type(n)==\"table\"and(n.IsCType and not n.isComponent))then h(m,n,o)else rawset(m,n,o)end end}function l.New(m,n)local o=d.EMPTY local p={}if(n~=nil and#n>0)then local q={}for r,s in ipairs(n)do local t=s:GetType()table.insert(q,t)p[t]=s end o=d.Of(q)end e=e+1 return setmetatable({_data=p,_onChange=m,id=e,isAlive=false,archetype=o,Get=f,Set=h,Unset=i,GetAll=j,GetAny=k,},l)end return l end b[\"EntityRepository\"]=function()local d=c(\"Event\")local e={}e.__index=e function e.New()return setmetatable({_archetypes={},_entitiesArchetype={},},e)end function e:Insert(f)if(self._entitiesArchetype[f]==nil)then local g=f.archetype local h=self._archetypes[g]if(h==nil)then h={count=0,entities={}}self._archetypes[g]=h end h.entities[f]=true h.count=h.count+1 self._entitiesArchetype[f]=g else self:Update(f)end end function e:Remove(f)local g=self._entitiesArchetype[f]if g==nil then return end self._entitiesArchetype[f]=nil local h=self._archetypes[g]if(h~=nil and h.entities[f]==true)then h.entities[f]=nil h.count=h.count-1 if(h.count==0)then self._archetypes[g]=nil end end end function e:Update(f)local g=self._entitiesArchetype[f]if(g==nil or g==f.archetype)then return end self:Remove(f)self:Insert(f)end function e:Query(f)local g={}for h,i in pairs(self._archetypes)do if f:Match(h)then table.insert(g,i.entities)end end return f:Result(g),#g>0 end function e:FastCheck(f)for g,h in pairs(self._archetypes)do if f:Match(g)then return true end end return false end return e end b[\"Event\"]=function()local d={}d.__index=d function d.New(f,g)return setmetatable({_event=f,_handler=g},d)end function d:Disconnect()local f=self._event if(f and not f.destroyed)then local g=table.find(f._handlers,self._handler)if g~=nil then table.remove(f._handlers,g)end end setmetatable(self,nil)end local e={}e.__index=e function e.New()return setmetatable({_handlers={}},e)end function e:Connect(f)if(type(f)==\"function\")then table.insert(self._handlers,f)return d.New(self,f)end error((\"Event:Connect(%s)\"):format(typeof(f)),2)end function e:Fire(...)if not self.destroyed then for f,g in ipairs(self._handlers)do g(table.unpack({...}))end end end function e:Destroy()setmetatable(self,nil)self._handlers=nil self.destroyed=true end return e end b[\"Query\"]=function()local d=c(\"QueryResult\")local e={}local f={}f.__index=f setmetatable(f,{__call=function(i,j,k,l)return f.New(j,k,l)end,})local function g(i,j,k)local l={}local m={}local n={}for o,p in ipairs(i)do if(l[p]==nil)then if(p.IsCType and not p.isComponent)then l[p]=true table.insert(m,p)table.insert(n,p.Id)else if p.Filter then l[p]=true p[j]=true table.insert(k,p)end end end end if#m>0 then table.sort(n)local o=\"_\"..table.concat(n,\"_\")return m,o end end function f.New(i,j,k)local l={}local m,n,o if(j~=nil)then j,m=g(j,\"IsAnyFilter\",l)end if(i~=nil)then i,n=g(i,\"IsAllFilter\",l)end if(k~=nil)then k,o=g(k,\"IsNoneFilter\",l)end return setmetatable({isQuery=true,_any=j,_all=i,_none=k,_anyKey=m,_allKey=n,_noneKey=o,_cache={},_clauses=#l>0 and l or nil},f)end function f:Result(i)return d.New(i,self._clauses)end function f:Match(i)local j=self._cache local k=j[i]if k~=nil then return k else local l=e[i]if(l==nil)then l={Any={},All={},None={}}e[i]=l end local m=self._noneKey if m then local p=l.None[m]if(p==nil)then p=true for q,r in ipairs(self._none)do if i:Has(r)then p=false break end end l.None[m]=p end if(p==false)then j[i]=false return false end end local n=self._anyKey if n then local p=l.Any[n]if(p==nil)then p=false if(l.All[n]==true)then p=true else for q,r in ipairs(self._any)do if i:Has(r)then p=true break end end end l.Any[n]=p end if(p==false)then j[i]=false return false end end local o=self._allKey if o then local p=l.All[o]if(p==nil)then local q=true for r,s in ipairs(self._all)do if(not i:Has(s))then q=false break end end if q then p=true else p=false end l.All[o]=p end j[i]=p return p end j[i]=true return true end end local function h()local i={isQueryBuilder=true}local j function i.All(...)j=nil i._all={...}return i end function i.Any(...)j=nil i._any={...}return i end function i.None(...)j=nil i._none={...}return i end function i.Build()if j==nil then j=f.New(i._all,i._any,i._none)end return j end return i end function f.All(...)return h().All(...)end function f.Any(...)return h().Any(...)end function f.None(...)return h().None(...)end function f.Filter(i)return function(j)return{Filter=i,Config=j}end end return f end b[\"QueryResult\"]=function()local function d(l,m,n)return m,(l(m)==true),true end local function e(l,m,n)return l(m),true,true end local function f(l,m,n)local o=(n<=l)return m,o,o end local function g(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=false break end end return m,o,true end local function h(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==false)then o=false break end end return m,o,true end local function i(l,m,n)local o=false for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=true break end end return m,o,true end local j={}local k={}k.__index=k function k.New(l,m)local n=j if(m and#m>0)then local o={}local p={}local q={}n={}for r,s in ipairs(m)do if s.IsNoneFilter then table.insert(q,s)elseif s.IsAnyFilter then table.insert(p,s)else table.insert(o,s)end end if(#q>0)then table.insert(n,{g,q})end if(#o>0)then table.insert(n,{h,o})end if(#p>0)then table.insert(n,{i,p})end end return setmetatable({chunks=l,_pipeline=n,},k)end function k:With(l,m)local n={}for o,p in ipairs(self._pipeline)do table.insert(n,p)end table.insert(n,{l,m})return setmetatable({chunks=self.chunks,_pipeline=n,},k)end function k:Filter(l)return self:With(d,l)end function k:Map(l)return self:With(e,l)end function k:Limit(l)return self:With(f,l)end function k:AnyMatch(l)local m=false self:ForEach(function(n)if l(n)then m=true end return m end)return m end function k:AllMatch(l)local m=true self:ForEach(function(n)if(not l(n))then m=false end return m==false end)return m end function k:FindAny()local l self:ForEach(function(m)l=m return true end)return l end function k:ToArray()local l={}self:ForEach(function(m)table.insert(l,m)end)return l end function k:Iterator()local l=coroutine.create(function()self:ForEach(function(m,n)coroutine.yield(m,n)end)end)return function()local m,n,o=coroutine.resume(l)return o,n end end function k:ForEach(l)local m=1 local n=self._pipeline local o=#n>0 if(not o)then for p,q in ipairs(self.chunks)do for r,s in pairs(q)do if(l(r,m)==true)then return end m=m+1 end end else for p,q in ipairs(self.chunks)do for r,s in pairs(q)do local t=false local u=true local v=r if(u and o)then for w,x in ipairs(n)do local y,z,A=x[1](x[2],v,m)if(not A)then t=true end if z then v=y else u=false break end end end if u then if(l(v,m)==true)then return end m=m+1 end if t then return end end end end end return k end b[\"RobloxLoopManager\"]=function()local function d()local e=game:GetService(\"RunService\")return{Register=function(f)local g=e.Stepped:Connect(function()f:Update(\"process\",os.clock())end)local h=e.Heartbeat:Connect(function()f:Update(\"transform\",os.clock())end)local i if(not e:IsServer())then i=e.RenderStepped:Connect(function()f:Update(\"render\",os.clock())end)end return function()g:Disconnect()h:Disconnect()if i then i:Disconnect()end end end}end return d end b[\"System\"]=function()local d={\"task\",\"render\",\"process\",\"transform\"}local e={}function e.Create(f,g,h,i)if(f==nil or not table.find(d,f))then error(\"The step parameter must one of \",table.concat(d,\", \"))end if(g and type(g)==\"function\")then i=g g=nil elseif h and type(h)==\"function\"then i=h h=nil end if(g and type(g)==\"table\"and(g.isQuery or g.isQueryBuilder))then h=g g=nil end if(g==nil or g<0)then g=50 end if type(h)==\"function\"then i=h h=nil end if(h and h.isQueryBuilder)then h=h.Build()end local j={Step=f,Order=g,Query=h,}j.__index=j function j.New(k,l)local m=setmetatable({version=0,_world=k,_config=l,},j)if m.Initialize then m:Initialize(l)end return m end function j:GetType()return j end function j:Result(k)return self._world:Exec(k or j.Query)end function j:Publish(k,l)end function j:Receive(k)end function j:Destroy()if self.OnDestroy then self.OnDestroy()end setmetatable(self,nil)for k,l in pairs(self)do self[k]=nil end end if i and type(i)==\"function\"then j.Update=i end return j end return e end b[\"SystemExecutor\"]=function()local function d(i)local j={}local k={}for l,m in ipairs(i)do local n=m:GetType()if(m._TaskState==nil)then m._TaskState=\"suspended\"end if not k[n]then local o={Type=n,System=m,Depends={}}k[n]=o table.insert(j,o)end end for l,m in ipairs(j)do local n=m.Type.Before if(n~=nil and#n>0)then for p,q in ipairs(n)do local r=k[q]if r then r.Depends[m]=true end end end local o=m.Type.After if(o~=nil and#o>0)then for p,q in ipairs(o)do local r=k[q]if r then m.Depends[r]=true end end end end return j end local function e(i,j)return i.Order<j.Order end local f={}f.__index=f function f.New(i)local j=setmetatable({_world=i,_onExit={},_onEnter={},_onRemove={},_task={},_render={},_process={},_transform={},_schedulers={},_lastFrameMatchQueries={},_currentFrameMatchQueries={},},f)i:OnQueryMatch(function(k)j._currentFrameMatchQueries[k]=true end)return j end function f:SetSystems(i)local j={}local k={}local l={}local m={}local n={}local o={}local p={}for q,r in pairs(i)do local s=r.Step if r.Update then if s==\"task\"then table.insert(m,r)elseif s==\"process\"then table.insert(o,r)elseif s==\"transform\"then table.insert(p,r)elseif s==\"render\"then table.insert(n,r)end end if(r.Query and r.Query.isQuery and s~=\"task\")then if r.OnExit then table.insert(j,r)end if r.OnEnter then table.insert(k,r)end if r.OnRemove then table.insert(l,r)end end end m=d(m)table.sort(j,e)table.sort(k,e)table.sort(l,e)table.sort(n,e)table.sort(o,e)table.sort(p,e)self._onExit=j self._onEnter=k self._onRemove=l self._task=m self._render=n self._process=o self._transform=p end function f:ExecOnExitEnter(i,j)local k=true local l={}for m,n in pairs(j)do local o=l[n]if not o then o={}l[n]=o end local p=m.archetype local q=o[p]if not q then q={}o[p]=q end table.insert(q,m)k=false end if k then return end self:_ExecOnEnter(i,l)self:_ExecOnExit(i,l)end function f:_ExecOnEnter(i,j)local k=self._world for l,m in ipairs(self._onEnter)do local n=m.Query for o,p in pairs(j)do if not n:Match(o)then for q,r in pairs(p)do if n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnEnter(i,t)m.version=k.version end end end end end end end function f:_ExecOnExit(i,j)local k=self._world for l,m in ipairs(self._onExit)do local n=m.Query for o,p in pairs(j)do if n:Match(o)then for q,r in pairs(p)do if not n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnExit(i,t)m.version=k.version end end end end end end end function f:ExecOnRemove(i,j)local k=true local l={}for n,o in pairs(j)do local p=l[o]if not p then p={}l[o]=p end table.insert(p,n)k=false end if k then return end local m=self._world for n,o in ipairs(self._onRemove)do for p,q in pairs(l)do if o.Query:Match(p)then for r,s in ipairs(q)do m.version=m.version+1 o:OnRemove(i,s)o.version=m.version end end end end end local function g(i,j,k)local l=i._world local m=i._lastFrameMatchQueries local n=i._currentFrameMatchQueries for o,p in ipairs(j)do local q=true if p.Query then local r=p.Query if m[r]==true or n[r]==true then q=true else q=l:FastCheck(r)n[r]=q end end if q then if(p.ShouldUpdate==nil or p.ShouldUpdate(k))then l.version=l.version+1 p:Update(k)p.version=l.version end end end end function f:ExecProcess(i)self._currentFrameMatchQueries={}g(self,self._process,i)end function f:ExecTransform(i)g(self,self._transform,i)end function f:ExecRender(i)g(self,self._render,i)self._lastFrameMatchQueries=self._currentFrameMatchQueries end function f:ExecTasks(i)while i>0 do local j=false local k,l=0,#self._schedulers-1 while k<=l do k=k+1 local m=self._schedulers[k]local n,o=m.Resume(i)if o then j=true end i=i-(n+0.00001)if(i<=0)then break end end if not j then return end end end local function h(i,j,k,l)local m=i.System m._TaskState=\"running\"if(m.ShouldUpdate==nil or m.ShouldUpdate(j))then k.version=k.version+1 m:Update(j)m.version=k.version end m._TaskState=\"suspended\"l(i)end function f:ScheduleTasks(i)local j=self._world local k={}local l={}local m={}local n={}local o={}local p,q=0,#self._task-1 while p<=q do p=p+1 local s=self._task[p]if(s.System._TaskState==\"suspended\")then s.System._TaskState=\"scheduled\"local t=false for u,v in pairs(s.Depends)do t=true if o[u]==nil then o[u]={}end table.insert(o[u],s)end if(not t)then table.insert(k,s)end m[s]=true end end local function r(s)s.Thread=nil s.LastExecTime=nil n[s]=true if o[s]then local t=o[s]local u,v=0,#t-1 while u<=v do u=u+1 local w=t[u]if m[w]then local x=true for y,z in pairs(w.Depends)do if n[y]~=true then x=false break end end if x then m[w]=nil w.LastExecTime=0 w.Thread=coroutine.create(h)table.insert(l,w)end end end end end if#k>0 then local s,t=0,#k-1 while s<=t do s=s+1 local v=k[s]m[v]=nil v.LastExecTime=0 v.Thread=coroutine.create(h)table.insert(l,v)end local u u={Resume=function(v)table.sort(l,function(A,B)return A.LastExecTime<B.LastExecTime end)local w=0 local x,y=0,#l-1 while x<=y do x=x+1 local A=l[x]if A.Thread~=nil then local B=os.clock()A.LastExecTime=B coroutine.resume(A.Thread,A,i,j,r)w=w+(os.clock()-B)if(w>v)then break end end end for A,B in ipairs(l)do if B.Thread==nil then local C=table.find(l,B)if C~=nil then table.remove(l,C)end end end local z=#l>0 if(not z)then local A=table.find(self._schedulers,u)if A~=nil then table.remove(self._schedulers,A)end end return w,z end}table.insert(self._schedulers,u)end end return f end b[\"Timer\"]=function()local d=4 local function e(g)local h=0.0 local i=0.0 return function(j,k,l,m)local n=g.DeltaFixed local o=j-i if o>0.25 then o=0.25 end i=j g.Now=j h=h+o if k==\"process\"then if h>=n then g.Interpolation=1 l(g)local p=0 while(h>=n and p<d)do m(g)p=p+1 g.Process=g.Process+n h=h-n end end else g.Interpolation=math.min(math.max(h/n,0),1)l(g)m(g)end end end local f={}f.__index=f function f.New(g)local h={Now=0,Frame=0,Process=0,Delta=0,DeltaFixed=0,Interpolation=0}local i=setmetatable({Time=h,Frequency=0,_update=e(h)},f)i:SetFrequency(g)return i end function f:SetFrequency(g)if g==nil then g=30 end local h=math.floor(math.abs(g)/2)*2 if h<2 then h=2 end if g~=h then g=h end self.Frequency=g self.Time.DeltaFixed=1000/g/1000 end function f:Update(g,h,i,j)self._update(g,h,i,j)end return f end b[\"Utility\"]=function()local d={}if table.unpack==nil then table.unpack=unpack end if table.find==nil then table.find=function(g,h,i)local j=#g for k=i or 1,j,1 do if g[k]==h then return k end end return nil end end local function e(g)local h={}for i,j in pairs(g)do if type(j)==\"table\"then j=e(j)end h[i]=j end return h end d.copyDeep=e local function f(g,h)for i,j in pairs(h)do if(type(j)==\"table\")then local k=g[i]if(k==nil or type(k)~=\"table\")then g[i]=e(j)else g[i]=f(k,j)end else g[i]=j end end return g end d.mergeDeep=f return d end b[\"World\"]=function()local d=c(\"Timer\")local e=c(\"Event\")local f=c(\"Entity\")local g=c(\"Archetype\")local h=c(\"SystemExecutor\")local i=c(\"EntityRepository\")local j={}j.__index=j function j.New(k,l,m)local n=setmetatable({version=0,maxTasksExecTime=0.013333333333333334,_dirty=false,_timer=d.New(l),_systems={},_repository=i.New(),_entitiesCreated={},_entitiesRemoved={},_entitiesUpdated={},_onQueryMatch=e.New(),_onChangeArchetypeEvent=e.New(),},j)n._executor=h.New(n)n._onChangeArchetypeEvent:Connect(function(o,p,q)n:_OnChangeArchetype(o,p,q)end)if(k~=nil)then for o,p in ipairs(k)do n:AddSystem(p)end end if(not m and j.LoopManager)then n._loopCancel=j.LoopManager.Register(n)end return n end function j:SetFrequency(k)k=self._timer:SetFrequency(k)end function j:GetFrequency(k)return self._timer.Frequency end function j:AddSystem(k,l)if k then if l==nil then l={}end if self._systems[k]==nil then self._systems[k]=k.New(self,l)self._executor:SetSystems(self._systems)end end end function j:Entity(...)local k=f.New(self._onChangeArchetypeEvent,{...})self._dirty=true self._entitiesCreated[k]=true k.version=self.version k.isAlive=false return k end function j:Remove(k)if self._entitiesRemoved[k]==true then return end if self._entitiesCreated[k]==true then self._entitiesCreated[k]=nil else self._repository:Remove(k)self._entitiesRemoved[k]=true if self._entitiesUpdated[k]==nil then self._entitiesUpdated[k]=k.archetype end end self._dirty=true k.isAlive=false end function j:Exec(k)if(k.isQueryBuilder)then k=k.Build()end local l,m=self._repository:Query(k)if m then self._onQueryMatch:Fire(k)end return l end function j:FastCheck(k)if(k.isQueryBuilder)then k=k.Build()end return self._repository:FastCheck(k)end function j:OnQueryMatch(k)return self._onQueryMatch:Connect(k)end function j:Update(k,l)self._timer:Update(l,k,function(m)if k==\"process\"then self._executor:ScheduleTasks(m)end self._executor:ExecTasks(self.maxTasksExecTime)end,function(m)if k==\"process\"then self._executor:ExecProcess(m)elseif k==\"transform\"then self._executor:ExecTransform(m)else self._executor:ExecRender(m)end while self._dirty do self._dirty=false local n={}for q,r in pairs(self._entitiesRemoved)do n[q]=self._entitiesUpdated[q]self._entitiesUpdated[q]=nil end self._entitiesRemoved={}self._executor:ExecOnRemove(m,n)n=nil local o={}local p=false for q,r in pairs(self._entitiesUpdated)do if(r~=q.archetype)then p=true o[q]=r end end self._entitiesUpdated={}for q,r in pairs(self._entitiesCreated)do p=true o[q]=g.EMPTY q.isAlive=true self._repository:Insert(q)end self._entitiesCreated={}if p then self._executor:ExecOnExitEnter(m,o)o=nil end end end)end function j:Destroy()if self._loopCancel then self._loopCancel()self._loopCancel=nil end if self._onChangeArchetypeEvent then self._onChangeArchetypeEvent:Destroy()self._onChangeArchetypeEvent=nil end self._repository=nil if self._systems then for k,l in pairs(self._systems)do l:Destroy()end self._systems=nil end self._timer=nil self._ExecPlan=nil self._entitiesCreated=nil self._entitiesUpdated=nil self._entitiesRemoved=nil setmetatable(self,nil)end function j:_OnChangeArchetype(k,l,m)if k.isAlive then if self._entitiesUpdated[k]==nil then self._dirty=true self._entitiesUpdated[k]=l end self._repository:Update(k)k.version=self.version end end return j end return c(\"ECS\")\n"
  },
  {
    "path": "modules/bin/luacov",
    "content": "#!/usr/bin/env lua\nlocal runner = require(\"luacov.runner\")\n\nlocal patterns = {}\nlocal configfile\nlocal reporter\n\nlocal help_message = ([[\nLuaCov %s - coverage analyzer for Lua scripts\n\nUsage:\n   luacov [options] [pattern...]\n\n   Launch your Lua programs with -lluacov to perform accounting.\n   Launch this script to generate a report from collected stats.\n   By default it reports on every Lua file encountered running your\n   script. To filter filenames, pass one or more Lua patterns matching\n   files to be included in the command line, or use a config.\n\nOptions:\n   -c filename, --config filename\n\n      Use a config file, .luacov by default. For details see\n      luacov.defaults module.\n\n   -r name, --reporter name\n\n      Use a custom reporter - a module in luacov.reporter.* namespace.\n\n   -h, --help\n\n      Show this help message and exit.\n\nExamples:\n   luacov foo/bar\n\n      This will report only on modules in the foo/bar subtree.\n]]):format(runner.version)\n\nlocal function read_key(i)\n   if arg[i]:sub(1, 1) ~= \"-\" or #arg[i] == 1 then\n      return nil, arg[i], i + 1\n   end\n\n   if arg[i]:sub(2, 2) == \"-\" then\n      local key, value = arg[i]:match(\"^%-%-([^=]+)=(.*)$\")\n      if key then\n         return key, value, i + 1\n      else\n         return arg[i]:sub(3), arg[i + 1], i + 2\n      end\n   else\n      local key = arg[i]:sub(2, 2)\n      local value = arg[i]:sub(3)\n      if #value == 0 then\n         i = i + 1\n         value = arg[i]\n      elseif value:sub(1, 1) == \"=\" then\n         value = value:sub(2)\n      end\n      return key, value, i + 1\n   end\nend\n\nlocal function norm_pat(p)\n   return (p:gsub(\"\\\\\", \"/\"):gsub(\"%.lua$\", \"\"))\nend\n\nlocal i = 1\nwhile arg[i] do\n   local key, value\n   key, value, i = read_key(i)\n   if key then\n      if (key == \"h\") or (key == \"help\") then\n         print(help_message)\n         os.exit(0)\n      elseif (key == \"c\") or (key == \"config\") then\n         configfile = value\n      elseif (key == \"r\") or (key == \"reporter\") then\n         reporter = value\n      end\n   else\n      table.insert(patterns, norm_pat(value))\n   end\nend\n\n-- will load configfile specified, or defaults otherwise\nlocal configuration = runner.load_config(configfile)\n\nconfiguration.include = configuration.include or {}\nconfiguration.exclude = configuration.exclude or {}\n\n-- add elements specified on commandline to config\nfor _, patt in ipairs(patterns) do\n   table.insert(configuration.include, patt)\nend\n\nconfiguration.reporter = reporter or configuration.reporter\nrunner.run_report(configuration)\n"
  },
  {
    "path": "modules/luacov/defaults.lua",
    "content": "--- Default values for configuration options.\n-- For project specific configuration create '.luacov' file in your project\n-- folder. It should be a Lua script setting various options as globals\n-- or returning table of options.\n-- @class module\n-- @name luacov.defaults\nreturn {\n\n  --- Filename to store collected stats. Default: \"luacov.stats.out\".\n  statsfile = \"luacov.stats.out\",\n\n  --- Filename to store report. Default: \"luacov.report.out\".\n  reportfile = \"luacov.report.out\",\n\n  --- Enable saving coverage data after every `savestepsize` lines?\n  -- Setting this flag to `true` in config is equivalent to running LuaCov\n  -- using `luacov.tick` module. Default: false.\n  tick = false,\n\n  --- Stats file updating frequency for `luacov.tick`.\n  -- The lower this value - the more frequently results will be written out to the stats file.\n  -- You may want to reduce this value (to, for example, 2) to avoid losing coverage data in\n  -- case your program may terminate without triggering luacov exit hooks that are supposed\n  -- to save the data. Default: 100.\n  savestepsize = 100,\n\n  --- Run reporter on completion? Default: false.\n  runreport = false,\n\n  --- Delete stats file after reporting? Default: false.\n  deletestats = false,\n\n  --- Process Lua code loaded from raw strings?\n  -- That is, when the 'source' field in the debug info\n  -- does not start with '@'. Default: false.\n  codefromstrings = false,\n\n  --- Lua patterns for files to include when reporting.\n  -- All will be included if nothing is listed.\n  -- Do not include the '.lua' extension. Path separator is always '/'.\n  -- Overruled by `exclude`.\n  -- @usage\n  -- include = {\n  --    \"mymodule$\",      -- the main module\n  --    \"mymodule%/.+$\",  -- and everything namespaced underneath it\n  -- }\n  include = {},\n\n  --- Lua patterns for files to exclude when reporting.\n  -- Nothing will be excluded if nothing is listed.\n  -- Do not include the '.lua' extension. Path separator is always '/'.\n  -- Overrules `include`.\n  exclude = {},\n\n  --- Table mapping names of modules to be included to their filenames.\n  -- Has no effect if empty.\n  -- Real filenames mentioned here will be used for reporting\n  -- even if the modules have been installed elsewhere.\n  -- Module name can contain '*' wildcard to match groups of modules,\n  -- in this case corresponding path will be used as a prefix directory\n  -- where modules from the group are located.\n  -- @usage\n  -- modules = {\n  --    [\"some_rock\"] = \"src/some_rock.lua\",\n  --    [\"some_rock.*\"] = \"src\"\n  -- }\n  modules = {},\n\n  --- Enable including untested files in report.\n  -- If `true`, all untested files in \".\" will be included.\n  -- If it is a table with directory and file paths, all untested files in these paths will be included.\n  -- Note that you are not allowed to use patterns in these paths.\n  -- Default: false.\n  includeuntestedfiles = false,\n\n}\n"
  },
  {
    "path": "modules/luacov/hook.lua",
    "content": "------------------------\n-- Hook module, creates debug hook used by LuaCov.\n-- @class module\n-- @name luacov.hook\nlocal hook = {}\n\n----------------------------------------------------------------\nlocal dir_sep = package.config:sub(1, 1)\nif not dir_sep:find(\"[/\\\\]\") then\n   dir_sep = \"/\"\nend\n\n--- Creates a new debug hook.\n-- @param runner runner module.\n-- @return debug hook function that uses runner fields and functions\n-- and sets `runner.data`.\nfunction hook.new(runner)\n   local ignored_files = {}\n   local steps_after_save = 0\n\n   return function(_, line_nr, level)\n      -- Do not use string metamethods within the debug hook:\n      -- they may be absent if it's called from a sandboxed environment\n      -- or because of carelessly implemented monkey-patching.\n      level = level or 2\n      if not runner.initialized then\n         return\n      end\n\n      -- Get name of processed file.\n      local name = debug.getinfo(level, \"S\").source\n      local prefixed_name = string.match(name, \"^@(.*)\")\n      if prefixed_name then\n         name = prefixed_name:gsub(\"^%.[/\\\\]\", \"\"):gsub(\"[/\\\\]\", dir_sep)\n      elseif not runner.configuration.codefromstrings then\n         -- Ignore Lua code loaded from raw strings by default.\n         return\n      end\n\n      local data = runner.data\n      local file = data[name]\n\n      if not file then\n         -- New or ignored file.\n         if ignored_files[name] then\n            return\n         elseif runner.file_included(name) then\n            file = {max = 0, max_hits = 0}\n            data[name] = file\n         else\n            ignored_files[name] = true\n            return\n         end\n      end\n\n      if line_nr > file.max then\n         file.max = line_nr\n      end\n\n      local hits = (file[line_nr] or 0) + 1\n      file[line_nr] = hits\n\n      if hits > file.max_hits then\n         file.max_hits = hits\n      end\n\n      if runner.tick then\n         steps_after_save = steps_after_save + 1\n\n         if steps_after_save == runner.configuration.savestepsize then\n            steps_after_save = 0\n\n            if not runner.paused then\n               runner.save_stats()\n            end\n         end\n      end\n   end\nend\n\nreturn hook\n"
  },
  {
    "path": "modules/luacov/linescanner.lua",
    "content": "local LineScanner = {}\nLineScanner.__index = LineScanner\n\nfunction LineScanner:new()\n   return setmetatable({\n      first = true,\n      comment = false,\n      after_function = false,\n      enabled = true\n   }, self)\nend\n\n-- Raw version of string.gsub\nlocal function replace(s, old, new)\n   old = old:gsub(\"%p\", \"%%%0\")\n   new = new:gsub(\"%%\", \"%%%%\")\n   return (s:gsub(old, new))\nend\n\nlocal fixups = {\n   { \"=\", \" ?= ?\" }, -- '=' may be surrounded by spaces\n   { \"(\", \" ?%( ?\" }, -- '(' may be surrounded by spaces\n   { \")\", \" ?%) ?\" }, -- ')' may be surrounded by spaces\n   { \"<FULLID>\", \"x ?[%[%.]? ?[ntfx0']* ?%]?\" }, -- identifier, possibly indexed once\n   { \"<IDS>\", \"x ?, ?x[x, ]*\" }, -- at least two comma-separated identifiers\n   { \"<FIELDNAME>\", \"%[? ?[ntfx0']+ ?%]?\" }, -- field, possibly like [\"this\"]\n   { \"<PARENS>\", \"[ %(]*\" }, -- optional opening parentheses\n}\n\n-- Utility function to make patterns more readable\nlocal function fixup(pat)\n   for _, fixup_pair in ipairs(fixups) do\n      pat = replace(pat, fixup_pair[1], fixup_pair[2])\n   end\n\n   return pat\nend\n\n--- Lines that are always excluded from accounting\nlocal any_hits_exclusions = {\n   \"\", -- Empty line\n   \"end[,; %)]*\", -- Single \"end\"\n   \"else\", -- Single \"else\"\n   \"repeat\", -- Single \"repeat\"\n   \"do\", -- Single \"do\"\n   \"if\", -- Single \"if\"\n   \"then\", -- Single \"then\"\n   \"while t do\", -- \"while true do\" generates no code\n   \"if t then\", -- \"if true then\" generates no code\n   \"local x\", -- \"local var\"\n   fixup \"local x=\", -- \"local var =\"\n   fixup \"local <IDS>\", -- \"local var1, ..., varN\"\n   fixup \"local <IDS>=\", -- \"local var1, ..., varN =\"\n   \"local function x\", -- \"local function f (arg1, ..., argN)\"\n}\n\n--- Lines that are only excluded from accounting when they have 0 hits\nlocal zero_hits_exclusions = {\n   \"[ntfx0',= ]+,\", -- \"var1 var2,\" multi columns table stuff\n   \"{ ?} ?,\", -- Empty table before comma leaves no trace in tables and calls\n   fixup \"<FIELDNAME>=.+[,;]\", -- \"[123] = 23,\" \"['foo'] = \"asd\",\"\n   fixup \"<FIELDNAME>=function\", -- \"[123] = function(...)\"\n   fixup \"<FIELDNAME>=<PARENS>'\", -- \"[123] = [[\", possibly with opening parens\n   \"return function\", -- \"return function(arg1, ..., argN)\"\n   \"function\", -- \"function(arg1, ..., argN)\"\n   \"[ntfx0]\", -- Single token expressions leave no trace in tables, function calls and sometimes assignments\n   \"''\", -- Same for strings\n   \"{ ?}\", -- Same for empty tables\n   fixup \"<FULLID>\", -- Same for local variables indexed once\n   fixup \"local x=function\", -- \"local a = function(arg1, ..., argN)\"\n   fixup \"local x=<PARENS>'\", -- \"local a = [[\", possibly with opening parens\n   fixup \"local x=(<PARENS>\", -- \"local a = (\", possibly with several parens\n   fixup \"local <IDS>=(<PARENS>\", -- \"local a, b = (\", possibly with several parens\n   fixup \"local x=n\", -- \"local a = nil; local b = nil\" produces no trace for the second statement\n   fixup \"<FULLID>=<PARENS>'\", -- \"a.b = [[\", possibly with opening parens\n   fixup \"<FULLID>=function\", -- \"a = function(arg1, ..., argN)\"\n   \"} ?,\", -- \"},\" generates no trace if the table ends with a key-value pair\n   \"} ?, ?function\", -- same with \"}, function(...)\"\n   \"break\", -- \"break\" generates no trace in Lua 5.2+\n   \"{\", -- \"{\" opening table\n   \"}?[ %)]*\", -- optional closing paren, possibly with several closing parens\n   \"[ntf0']+ ?}[ %)]*\" -- a constant at the end of a table, possibly with closing parens (for LuaJIT)\n}\n\nlocal function excluded(exclusions, line)\n   for _, e in ipairs(exclusions) do\n      if line:match(\"^ *\"..e..\" *$\") then\n         return true\n      end\n   end\n\n   return false\nend\n\nfunction LineScanner:find(pattern)\n   return self.line:find(pattern, self.i)\nend\n\n-- Skips string literal with quote stored as self.quote.\n-- @return boolean indicating success.\nfunction LineScanner:skip_string()\n   -- Look for closing quote, possibly after even number of backslashes.\n   local _, quote_i = self:find(\"^(\\\\*)%1\"..self.quote)\n   if not quote_i then\n      _, quote_i = self:find(\"[^\\\\](\\\\*)%1\"..self.quote)\n   end\n\n   if quote_i then\n      self.i = quote_i + 1\n      self.quote = nil\n      table.insert(self.simple_line_buffer, \"'\")\n      return true\n   else\n      return false\n   end\nend\n\n-- Skips long string literal with equal signs stored as self.equals.\n-- @return boolean indicating success.\nfunction LineScanner:skip_long_string()\n   local _, bracket_i = self:find(\"%]\"..self.equals..\"%]\")\n\n   if bracket_i then\n      self.i = bracket_i + 1\n      self.equals = nil\n\n      if self.comment then\n         self.comment = false\n      else\n         table.insert(self.simple_line_buffer, \"'\")\n      end\n\n      return true\n   else\n      return false\n   end\nend\n\n-- Skips function arguments.\n-- @return boolean indicating success.\nfunction LineScanner:skip_args()\n   local _, paren_i = self:find(\"%)\")\n\n   if paren_i then\n      self.i = paren_i + 1\n      self.args = nil\n      return true\n   else\n      return false\n   end\nend\n\nfunction LineScanner:skip_whitespace()\n   local next_i = self:find(\"%S\") or #self.line + 1\n\n   if next_i ~= self.i then\n      self.i = next_i\n      table.insert(self.simple_line_buffer, \" \")\n   end\nend\n\nfunction LineScanner:skip_number()\n   if self:find(\"^0[xX]\") then\n      self.i = self.i + 2\n   end\n\n   local _\n   _, _, self.i = self:find(\"^[%x%.]*()\")\n\n   if self:find(\"^[eEpP][%+%-]\") then\n      -- Skip exponent, too.\n      self.i = self.i + 2\n      _, _, self.i = self:find(\"^[%x%.]*()\")\n   end\n\n   -- Skip LuaJIT number suffixes (i, ll, ull).\n   _, _, self.i = self:find(\"^[iull]*()\")\n   table.insert(self.simple_line_buffer, \"0\")\nend\n\nlocal keywords = {[\"nil\"] = \"n\", [\"true\"] = \"t\", [\"false\"] = \"f\"}\n\nfor _, keyword in ipairs({\n      \"and\", \"break\", \"do\", \"else\", \"elseif\", \"end\", \"for\", \"function\", \"goto\", \"if\",\n      \"in\", \"local\", \"not\", \"or\", \"repeat\", \"return\", \"then\", \"until\", \"while\"}) do\n   keywords[keyword] = keyword\nend\n\nfunction LineScanner:skip_name()\n   -- It is guaranteed that the first character matches \"%a_\".\n   local _, _, name = self:find(\"^([%w_]*)\")\n   self.i = self.i + #name\n\n   if keywords[name] then\n      name = keywords[name]\n   else\n      name = \"x\"\n   end\n\n   table.insert(self.simple_line_buffer, name)\n\n   if name == \"function\" then\n      -- This flag indicates that the next pair of parentheses (function args) must be skipped.\n      self.after_function = true\n   end\nend\n\n-- Source lines can be explicitly ignored using `enable` and `disable` inline options.\n-- An inline option is a simple comment: `-- luacov: enable` or `-- luacov: disable`.\n-- Inline option parsing is not whitespace sensitive.\n-- All lines starting from a line containing `disable` option and up to a line containing `enable`\n-- option (or end of file) are excluded.\n\nfunction LineScanner:check_inline_options(comment_body)\n   if comment_body:find(\"^%s*luacov:%s*enable%s*$\") then\n      self.enabled = true\n   elseif comment_body:find(\"^%s*luacov:%s*disable%s*$\") then\n      self.enabled = false\n   end\nend\n\n-- Consumes and analyzes a line.\n-- @return boolean indicating whether line must be excluded.\n-- @return boolean indicating whether line must be excluded if not hit.\nfunction LineScanner:consume(line)\n   if self.first then\n      self.first = false\n\n      if line:match(\"^#!\") then\n         -- Ignore Unix hash-bang magic line.\n         return true, true\n      end\n   end\n\n   self.line = line\n   -- As scanner goes through the line, it puts its simplified parts into buffer.\n   -- Punctuation is preserved. Whitespace is replaced with single space.\n   -- Literal strings are replaced with \"''\", so that a string literal\n   -- containing special characters does not confuse exclusion rules.\n   -- Numbers are replaced with \"0\".\n   -- Identifiers are replaced with \"x\".\n   -- Literal keywords (nil, true and false) are replaced with \"n\", \"t\" and \"f\",\n   -- other keywords are preserved.\n   -- Function declaration arguments are removed.\n   self.simple_line_buffer = {}\n   self.i = 1\n\n   while self.i <= #line do\n      -- One iteration of this loop handles one token, where\n      -- string literal start and end are considered distinct tokens.\n      if self.quote then\n         if not self:skip_string() then\n            -- String literal ends on another line.\n            break\n         end\n      elseif self.equals then\n         if not self:skip_long_string() then\n            -- Long string literal or comment ends on another line.\n            break\n         end\n      elseif self.args then\n         if not self:skip_args() then\n            -- Function arguments end on another line.\n            break\n         end\n      else\n         self:skip_whitespace()\n\n         if self:find(\"^%.%d\") then\n            self.i = self.i + 1\n         end\n\n         if self:find(\"^%d\") then\n            self:skip_number()\n         elseif self:find(\"^[%a_]\") then\n            self:skip_name()\n         else\n            if self:find(\"^%-%-\") then\n               self.comment = true\n               self.i = self.i + 2\n            end\n\n            local _, bracket_i, equals = self:find(\"^%[(=*)%[\")\n            if equals then\n               self.i = bracket_i + 1\n               self.equals = equals\n\n               if not self.comment then\n                  table.insert(self.simple_line_buffer, \"'\")\n               end\n            elseif self.comment then\n               -- Simple comment, check if it contains inline options and skip line.\n               self.comment = false\n               local comment_body = self.line:sub(self.i)\n               self:check_inline_options(comment_body)\n               break\n            else\n               local char = line:sub(self.i, self.i)\n\n               if char == \".\" then\n                  -- Dot can't be saved as one character because of\n                  -- \"..\" and \"...\" tokens and the fact that number literals\n                  -- can start with one.\n                  local _, _, dots = self:find(\"^(%.*)\")\n                  self.i = self.i + #dots\n                  table.insert(self.simple_line_buffer, dots)\n               else\n                  self.i = self.i + 1\n\n                  if char == \"'\" or char == '\"' then\n                     table.insert(self.simple_line_buffer, \"'\")\n                     self.quote = char\n                  elseif self.after_function and char == \"(\" then\n                     -- This is the opening parenthesis of function declaration args.\n                     self.after_function = false\n                     self.args = true\n                  else\n                     -- Save other punctuation literally.\n                     -- This inserts an empty string when at the end of line,\n                     -- which is fine.\n                     table.insert(self.simple_line_buffer, char)\n                  end\n               end\n            end\n         end\n      end\n   end\n\n   if not self.enabled then\n      -- Disabled by inline options, always exclude the line.\n      return true, true\n   end\n\n   local simple_line = table.concat(self.simple_line_buffer)\n   return excluded(any_hits_exclusions, simple_line), excluded(zero_hits_exclusions, simple_line)\nend\n\nreturn LineScanner\n"
  },
  {
    "path": "modules/luacov/reporter/default.lua",
    "content": "return require \"luacov.reporter\""
  },
  {
    "path": "modules/luacov/reporter.lua",
    "content": "------------------------\n-- Report module, will transform statistics file into a report.\n-- @class module\n-- @name luacov.reporter\nlocal reporter = {}\n\nlocal LineScanner = require(\"luacov.linescanner\")\nlocal luacov = require(\"luacov.runner\")\nlocal util = require(\"luacov.util\")\nlocal lfs_ok, lfs = pcall(require, \"lfs\")\n\n----------------------------------------------------------------\nlocal dir_sep = package.config:sub(1, 1)\nif not dir_sep:find(\"[/\\\\]\") then\n   dir_sep = \"/\"\nend\n\n\n--- returns all files inside dir\n--- @param dir directory to be listed\n--- @treturn table with filenames and attributes\nlocal function dirtree(dir)\n   assert(dir and dir ~= \"\", \"Please pass directory parameter\")\n   if dir:sub(-1):match(\"[/\\\\]\") then\n       dir=string.sub(dir, 1, -2)\n   end\n\n   dir = dir:gsub(\"[/\\\\]\", dir_sep)\n\n   local function yieldtree(directory)\n       for entry in lfs.dir(directory) do\n           if entry ~= \".\" and entry ~= \"..\" then\n               entry=directory..dir_sep..entry\n               local attr=lfs.attributes(entry)\n               coroutine.yield(entry,attr)\n               if attr.mode == \"directory\" then\n                   yieldtree(entry)\n               end\n           end\n       end\n   end\n\n   return coroutine.wrap(function() yieldtree(dir) end)\nend\n\n----------------------------------------------------------------\n--- checks if string 'filename' has pattern 'pattern'\n--- @param filename\n--- @param pattern\n--- @return boolean\nlocal function fileMatches(filename, pattern)\n   return string.find(filename, pattern)\nend\n\n----------------------------------------------------------------\n--- Basic reporter class stub.\n-- Implements 'new', 'run' and 'close' methods required by `report`.\n-- Provides some helper methods and stubs to be overridden by child classes.\n-- @usage\n-- local MyReporter = setmetatable({}, ReporterBase)\n-- MyReporter.__index = MyReporter\n-- function MyReporter:on_hit_line(...)\n--    self:write((\"File %s: hit line %s %d times\"):format(...))\n-- end\n-- @type ReporterBase\nlocal ReporterBase = {} do\nReporterBase.__index = ReporterBase\n\nfunction ReporterBase:new(conf)\n   local stats = require(\"luacov.stats\")\n   local data = stats.load(conf.statsfile)\n\n   if not data then\n      return nil, \"Could not load stats file \" .. conf.statsfile .. \".\"\n   end\n\n   local files = {}\n   local filtered_data = {}\n   local max_hits = 0\n\n   -- Several original paths can map to one real path,\n   -- their stats should be merged in this case.\n   for filename, file_stats in pairs(data) do\n      if luacov.file_included(filename) then\n         filename = luacov.real_name(filename)\n\n         if filtered_data[filename] then\n            luacov.update_stats(filtered_data[filename], file_stats)\n         else\n            table.insert(files, filename)\n            filtered_data[filename] = file_stats\n         end\n\n         max_hits = math.max(max_hits, filtered_data[filename].max_hits)\n      end\n   end\n\n   -- including files without tests\n   -- only .lua files\n   if conf.includeuntestedfiles then\n      if not lfs_ok then\n         print(\"The option includeuntestedfiles requires the lfs module (from luafilesystem) to be installed.\")\n         os.exit(1)\n      end\n\n      local function add_empty_file_coverage_data(file_path)\n\n         -- Leading \"./\" must be trimmed from the file paths because the paths of tested\n         -- files do not have a leading \"./\" either\n         if (file_path:match(\"^%.[/\\\\]\")) then\n            file_path = file_path:sub(3)\n         end\n\n         if luacov.file_included(file_path) then\n            local file_stats = {\n               max = 0,\n               max_hits = 0\n            }\n\n            local filename = luacov.real_name(file_path)\n\n            if not filtered_data[filename] then\n               table.insert(files, filename)\n               filtered_data[filename] = file_stats\n            end\n         end\n\n      end\n\n      local function add_empty_dir_coverage_data(directory_path)\n\n         for filename, attr in dirtree(directory_path) do\n            if attr.mode == \"file\" and fileMatches(filename, '.%.lua$') then\n               add_empty_file_coverage_data(filename)\n            end\n         end\n\n      end\n\n      if (conf.includeuntestedfiles == true) then\n        add_empty_dir_coverage_data(\".\" .. dir_sep)\n\n      elseif (type(conf.includeuntestedfiles) == \"table\" and conf.includeuntestedfiles[1]) then\n         for _, include_path in ipairs(conf.includeuntestedfiles) do\n            if (fileMatches(include_path, '.%.lua$')) then\n               add_empty_file_coverage_data(include_path)\n            else\n               add_empty_dir_coverage_data(include_path)\n            end\n         end\n      end\n\n   end\n\n   table.sort(files)\n\n   local out, err = io.open(conf.reportfile, \"w\")\n   if not out then return nil, err end\n\n   local o = setmetatable({\n      _out  = out,\n      _cfg  = conf,\n      _data = filtered_data,\n      _files = files,\n      _mhit = max_hits,\n   }, self)\n\n   return o\nend\n\n--- Returns configuration table.\n-- @see luacov.defaults\nfunction ReporterBase:config()\n   return self._cfg\nend\n\n--- Returns maximum number of hits per line in all coverage data.\nfunction ReporterBase:max_hits()\n   return self._mhit\nend\n\n--- Writes strings to report file.\n-- @param ... strings.\nfunction ReporterBase:write(...)\n   return self._out:write(...)\nend\n\nfunction ReporterBase:close()\n   self._out:close()\n   self._private = nil\nend\n\n--- Returns array of filenames to be reported.\nfunction ReporterBase:files()\n   return self._files\nend\n\n--- Returns coverage data for a file.\n-- @param filename name of the file.\n-- @see luacov.stats.load\nfunction ReporterBase:stats(filename)\n   return self._data[filename]\nend\n\n-- Stub methods follow.\n-- luacheck: push no unused args\n\n--- Stub method called before reporting.\nfunction ReporterBase:on_start()\nend\n\n--- Stub method called before processing a file.\n-- @param filename name of the file.\nfunction ReporterBase:on_new_file(filename)\nend\n\n--- Stub method called if a file couldn't be processed due to an error.\n-- @param filename name of the file.\n-- @param error_type \"open\", \"read\" or \"load\".\n-- @param message error message.\nfunction ReporterBase:on_file_error(filename, error_type, message)\nend\n\n--- Stub method called for each empty source line\n-- and other lines that can't be hit.\n-- @param filename name of the file.\n-- @param lineno line number.\n-- @param line the line itself as a string.\nfunction ReporterBase:on_empty_line(filename, lineno, line)\nend\n\n--- Stub method called for each missed source line.\n-- @param filename name of the file.\n-- @param lineno line number.\n-- @param line the line itself as a string.\nfunction ReporterBase:on_mis_line(filename, lineno, line)\nend\n\n--- Stub method called for each hit source line.\n-- @param filename name of the file.\n-- @param lineno line number.\n-- @param line the line itself as a string.\n-- @param hits number of times the line was hit. Should be positive.\nfunction ReporterBase:on_hit_line(filename, lineno, line, hits)\nend\n\n--- Stub method called after a file has been processed.\n-- @param filename name of the file.\n-- @param hits total number of hit lines in the file.\n-- @param miss total number of missed lines in the file.\nfunction ReporterBase:on_end_file(filename, hits, miss)\nend\n\n--- Stub method called after reporting.\nfunction ReporterBase:on_end()\nend\n\n-- luacheck: pop\n\nlocal cluacov_ok = pcall(require, \"cluacov.version\")\nlocal deepactivelines\n\nif cluacov_ok then\n   deepactivelines = require(\"cluacov.deepactivelines\")\nend\n\nfunction ReporterBase:_run_file(filename)\n   local file, open_err = io.open(filename)\n\n   if not file then\n      self:on_file_error(filename, \"open\", util.unprefix(open_err, filename .. \": \"))\n      return\n   end\n\n   local active_lines\n\n   if cluacov_ok then\n      local src, read_err = file:read(\"*a\")\n\n      if not src then\n         self:on_file_error(filename, \"read\", read_err)\n         return\n      end\n\n      src = src:gsub(\"^#![^\\n]*\", \"\")\n      local func, load_err = util.load_string(src, nil, \"@file\")\n\n      if not func then\n         self:on_file_error(filename, \"load\", \"line \" .. util.unprefix(load_err, \"file:\"))\n         return\n      end\n\n      active_lines = deepactivelines.get(func)\n      file:seek(\"set\")\n   end\n\n   self:on_new_file(filename)\n   local file_hits, file_miss = 0, 0\n   local filedata = self:stats(filename)\n\n   local line_nr = 1\n   local scanner = LineScanner:new()\n\n   while true do\n      local line = file:read(\"*l\")\n      if not line then break end\n\n      local always_excluded, excluded_when_not_hit = scanner:consume(line)\n      local hits = filedata[line_nr] or 0\n      local included = not always_excluded and (not excluded_when_not_hit or hits ~= 0)\n\n      if cluacov_ok then\n         included = included and active_lines[line_nr]\n      end\n\n      if included then\n         if hits == 0 then\n            self:on_mis_line(filename, line_nr, line)\n            file_miss = file_miss + 1\n         else\n            self:on_hit_line(filename, line_nr, line, hits)\n            file_hits = file_hits + 1\n         end\n      else\n         self:on_empty_line(filename, line_nr, line)\n      end\n\n      line_nr = line_nr + 1\n   end\n\n   file:close()\n   self:on_end_file(filename, file_hits, file_miss)\nend\n\nfunction ReporterBase:run()\n   self:on_start()\n\n   for _, filename in ipairs(self:files()) do\n      self:_run_file(filename)\n   end\n\n   self:on_end()\nend\n\nend\n--- @section end\n----------------------------------------------------------------\n\n----------------------------------------------------------------\nlocal DefaultReporter = setmetatable({}, ReporterBase) do\nDefaultReporter.__index = DefaultReporter\n\nfunction DefaultReporter:on_start()\n   local most_hits = self:max_hits()\n   local most_hits_length = #(\"%d\"):format(most_hits)\n\n   self._summary      = {}\n   self._empty_format = (\" \"):rep(most_hits_length + 1)\n   self._zero_format  = (\"*\"):rep(most_hits_length)..\"0\"\n   self._count_format = (\"%% %dd\"):format(most_hits_length+1)\n   self._printed_first_header = false\nend\n\nfunction DefaultReporter:on_new_file(filename)\n   self:write((\"=\"):rep(78), \"\\n\")\n   self:write(filename, \"\\n\")\n   self:write((\"=\"):rep(78), \"\\n\")\nend\n\nfunction DefaultReporter:on_file_error(filename, error_type, message) --luacheck: no self\n   io.stderr:write((\"Couldn't %s %s: %s\\n\"):format(error_type, filename, message))\nend\n\nfunction DefaultReporter:on_empty_line(_, _, line)\n   if line == \"\" then\n      self:write(\"\\n\")\n   else\n      self:write(self._empty_format, \" \", line, \"\\n\")\n   end\nend\n\nfunction DefaultReporter:on_mis_line(_, _, line)\n   self:write(self._zero_format, \" \", line, \"\\n\")\nend\n\nfunction DefaultReporter:on_hit_line(_, _, line, hits)\n   self:write(self._count_format:format(hits), \" \", line, \"\\n\")\nend\n\nfunction DefaultReporter:on_end_file(filename, hits, miss)\n   self._summary[filename] = { hits = hits, miss = miss }\n   self:write(\"\\n\")\nend\n\nlocal function coverage_to_string(hits, missed)\n   local total = hits + missed\n\n   if total == 0 then\n      total = 1\n   end\n\n   return (\"%.2f%%\"):format(hits/total*100.0)\nend\n\nfunction DefaultReporter:on_end()\n   self:write((\"=\"):rep(78), \"\\n\")\n   self:write(\"Summary\\n\")\n   self:write((\"=\"):rep(78), \"\\n\")\n   self:write(\"\\n\")\n\n   local lines = {{\"File\", \"Hits\", \"Missed\", \"Coverage\"}}\n   local total_hits, total_missed = 0, 0\n\n   for _, filename in ipairs(self:files()) do\n      local summary = self._summary[filename]\n\n      if summary then\n         local hits, missed = summary.hits, summary.miss\n\n         table.insert(lines, {\n            filename,\n            tostring(summary.hits),\n            tostring(summary.miss),\n            coverage_to_string(hits, missed)\n         })\n\n         total_hits = total_hits + hits\n         total_missed = total_missed + missed\n      end\n   end\n\n   table.insert(lines, {\n      \"Total\",\n      tostring(total_hits),\n      tostring(total_missed),\n      coverage_to_string(total_hits, total_missed)\n   })\n\n   local max_column_lengths = {}\n\n   for _, line in ipairs(lines) do\n      for column_nr, column in ipairs(line) do\n         max_column_lengths[column_nr] = math.max(max_column_lengths[column_nr] or -1, #column)\n      end\n   end\n\n   local table_width = #max_column_lengths - 1\n\n   for _, column_length in ipairs(max_column_lengths) do\n      table_width = table_width + column_length\n   end\n\n\n   for line_nr, line in ipairs(lines) do\n      if line_nr == #lines or line_nr == 2 then\n         self:write((\"-\"):rep(table_width), \"\\n\")\n      end\n\n      for column_nr, column in ipairs(line) do\n         self:write(column)\n\n         if column_nr == #line then\n            self:write(\"\\n\")\n         else\n            self:write((\" \"):rep(max_column_lengths[column_nr] - #column + 1))\n         end\n      end\n   end\nend\n\nend\n----------------------------------------------------------------\n\n--- Runs the report generator.\n-- To load a config, use `luacov.runner.load_config` first.\n-- @param[opt] reporter_class custom reporter class. Will be\n-- instantiated using 'new' method with configuration\n-- (see `luacov.defaults`) as the argument. It should\n-- return nil + error if something went wrong.\n-- After acquiring a reporter object its 'run' and 'close'\n-- methods will be called.\n-- The easiest way to implement a custom reporter class is to\n-- extend `ReporterBase`.\nfunction reporter.report(reporter_class)\n   local configuration = luacov.load_config()\n\n   reporter_class = reporter_class or DefaultReporter\n\n   local rep, err = reporter_class:new(configuration)\n\n   if not rep then\n      print(err)\n      print(\"Run your Lua program with -lluacov and then rerun luacov.\")\n      os.exit(1)\n   end\n\n   rep:run()\n\n   rep:close()\n\n   if configuration.deletestats then\n      os.remove(configuration.statsfile)\n   end\nend\n\nreporter.ReporterBase    = ReporterBase\n\nreporter.DefaultReporter = DefaultReporter\n\nreturn reporter\n"
  },
  {
    "path": "modules/luacov/runner.lua",
    "content": "---------------------------------------------------\n-- Statistics collecting module.\n-- Calling the module table is a shortcut to calling the `init` function.\n-- @class module\n-- @name luacov.runner\n\nlocal runner = {}\n--- LuaCov version in `MAJOR.MINOR.PATCH` format.\nrunner.version = \"0.15.0\"\n\nlocal stats = require(\"luacov.stats\")\nlocal util = require(\"luacov.util\")\nrunner.defaults = require(\"luacov.defaults\")\n\nlocal debug = require(\"debug\")\nlocal raw_os_exit = os.exit\n\nlocal new_anchor = newproxy or function() return {} end -- luacheck: compat\n\n-- Returns an anchor that runs fn when collected.\nlocal function on_exit_wrap(fn)\n   local anchor = new_anchor()\n   debug.setmetatable(anchor, {__gc = fn})\n   return anchor\nend\n\nrunner.data = {}\nrunner.paused = true\nrunner.initialized = false\nrunner.tick = false\n\n-- Checks if a string matches at least one of patterns.\n-- @param patterns array of patterns or nil\n-- @param str string to match\n-- @param on_empty return value in case of empty pattern array\nlocal function match_any(patterns, str, on_empty)\n   if not patterns or not patterns[1] then\n      return on_empty\n   end\n\n   for _, pattern in ipairs(patterns) do\n      if string.match(str, pattern) then\n         return true\n      end\n   end\n\n   return false\nend\n\n--------------------------------------------------\n-- Uses LuaCov's configuration to check if a file is included for\n-- coverage data collection.\n-- @param filename name of the file.\n-- @return true if file is included, false otherwise.\nfunction runner.file_included(filename)\n   -- Normalize file names before using patterns.\n   filename = string.gsub(filename, \"\\\\\", \"/\")\n   filename = string.gsub(filename, \"%.lua$\", \"\")\n\n   -- If include list is empty, everything is included by default.\n   -- If exclude list is empty, nothing is excluded by default.\n   return match_any(runner.configuration.include, filename, true) and\n      not match_any(runner.configuration.exclude, filename, false)\nend\n\n--------------------------------------------------\n-- Adds stats to an existing file stats table.\n-- @param old_stats stats to be updated.\n-- @param extra_stats another stats table, will be broken during update.\nfunction runner.update_stats(old_stats, extra_stats)\n   old_stats.max = math.max(old_stats.max, extra_stats.max)\n\n   -- Remove string keys so that they do not appear when iterating\n   -- over 'extra_stats'.\n   extra_stats.max = nil\n   extra_stats.max_hits = nil\n\n   for line_nr, run_nr in pairs(extra_stats) do\n      old_stats[line_nr] = (old_stats[line_nr] or 0) + run_nr\n      old_stats.max_hits = math.max(old_stats.max_hits, old_stats[line_nr])\n   end\nend\n\n-- Adds accumulated stats to existing stats file or writes a new one, then resets data.\nfunction runner.save_stats()\n   local loaded = stats.load(runner.configuration.statsfile) or {}\n\n   for name, file_data in pairs(runner.data) do\n      if loaded[name] then\n         runner.update_stats(loaded[name], file_data)\n      else\n         loaded[name] = file_data\n      end\n   end\n\n   stats.save(runner.configuration.statsfile, loaded)\n   runner.data = {}\nend\n\nlocal cluacov_ok = pcall(require, \"cluacov.version\")\n\n--------------------------------------------------\n-- Debug hook set by LuaCov.\n-- Acknowledges that a line is executed, but does nothing\n-- if called manually before coverage gathering is started.\n-- @param _ event type, should always be \"line\".\n-- @param line_nr line number.\n-- @param[opt] level passed to debug.getinfo to get name of processed file,\n-- 2 by default. Increase it if this function is called manually\n-- from another debug hook.\n-- @usage\n-- local function custom_hook(_, line)\n--    runner.debug_hook(_, line, 3)\n--    extra_processing(line)\n-- end\n-- @function debug_hook\nrunner.debug_hook = require(cluacov_ok and \"cluacov.hook\" or \"luacov.hook\").new(runner)\n\n------------------------------------------------------\n-- Runs the reporter specified in configuration.\n-- @param[opt] configuration if string, filename of config file (used to call `load_config`).\n-- If table then config table (see file `luacov.default.lua` for an example).\n-- If `configuration.reporter` is not set, runs the default reporter;\n-- otherwise, it must be a module name in 'luacov.reporter' namespace.\n-- The module must contain 'report' function, which is called without arguments.\nfunction runner.run_report(configuration)\n   configuration = runner.load_config(configuration)\n   local reporter = \"luacov.reporter\"\n\n   if configuration.reporter then\n      reporter = reporter .. \".\" .. configuration.reporter\n   end\n\n   require(reporter).report()\nend\n\nlocal on_exit_run_once = false\n\nlocal function on_exit()\n   -- Lua >= 5.2 could call __gc when user call os.exit\n   -- so this method could be called twice\n   if on_exit_run_once then return end\n   on_exit_run_once = true\n   runner.save_stats()\n\n   if runner.configuration.runreport then\n      runner.run_report(runner.configuration)\n   end\nend\n\nlocal dir_sep = package.config:sub(1, 1)\nlocal wildcard_expansion = \"[^/]+\"\n\nif not dir_sep:find(\"[/\\\\]\") then\n   dir_sep = \"/\"\nend\n\nlocal function escape_module_punctuation(ch)\n   if ch == \".\" then\n      return \"/\"\n   elseif ch == \"*\" then\n      return wildcard_expansion\n   else\n      return \"%\" .. ch\n   end\nend\n\nlocal function reversed_module_name_parts(name)\n   local parts = {}\n\n   for part in name:gmatch(\"[^%.]+\") do\n      table.insert(parts, 1, part)\n   end\n\n   return parts\nend\n\n-- This function is used for sorting module names.\n-- More specific names should come first.\n-- E.g. rule for 'foo.bar' should override rule for 'foo.*',\n-- rule for 'foo.*' should override rule for 'foo.*.*',\n-- and rule for 'a.b' should override rule for 'b'.\n-- To be more precise, because names become patterns that are matched\n-- from the end, the name that has the first (from the end) literal part\n-- (and the corresponding part for the other name is not literal)\n-- is considered more specific.\nlocal function compare_names(name1, name2)\n   local parts1 = reversed_module_name_parts(name1)\n   local parts2 = reversed_module_name_parts(name2)\n\n   for i = 1, math.max(#parts1, #parts2) do\n      if not parts1[i] then return false end\n      if not parts2[i] then return true end\n\n      local is_literal1 = not parts1[i]:find(\"%*\")\n      local is_literal2 = not parts2[i]:find(\"%*\")\n\n      if is_literal1 ~= is_literal2 then\n         return is_literal1\n      end\n   end\n\n   -- Names are at the same level of specificness,\n   -- fall back to lexicographical comparison.\n   return name1 < name2\nend\n\n-- Sets runner.modules using runner.configuration.modules.\n-- Produces arrays of module patterns and filenames and sets\n-- them as runner.modules.patterns and runner.modules.filenames.\n-- Appends these patterns to the include list.\nlocal function acknowledge_modules()\n   runner.modules = {patterns = {}, filenames = {}}\n\n   if not runner.configuration.modules then\n      return\n   end\n\n   if not runner.configuration.include then\n      runner.configuration.include = {}\n   end\n\n   local names = {}\n\n   for name in pairs(runner.configuration.modules) do\n      table.insert(names, name)\n   end\n\n   table.sort(names, compare_names)\n\n   for _, name in ipairs(names) do\n      local pattern = name:gsub(\"%p\", escape_module_punctuation) .. \"$\"\n      local filename = runner.configuration.modules[name]:gsub(\"[/\\\\]\", dir_sep)\n      table.insert(runner.modules.patterns, pattern)\n      table.insert(runner.configuration.include, pattern)\n      table.insert(runner.modules.filenames, filename)\n\n      if filename:match(\"init%.lua$\") then\n         pattern = pattern:gsub(\"$$\", \"/init$\")\n         table.insert(runner.modules.patterns, pattern)\n         table.insert(runner.configuration.include, pattern)\n         table.insert(runner.modules.filenames, filename)\n      end\n   end\nend\n\n--------------------------------------------------\n-- Returns real name for a source file name\n-- using `luacov.defaults.modules` option.\n-- @param filename name of the file.\nfunction runner.real_name(filename)\n   local orig_filename = filename\n   -- Normalize file names before using patterns.\n   filename = filename:gsub(\"\\\\\", \"/\"):gsub(\"%.lua$\", \"\")\n\n   for i, pattern in ipairs(runner.modules.patterns) do\n      local match = filename:match(pattern)\n\n      if match then\n         local new_filename = runner.modules.filenames[i]\n\n         if pattern:find(wildcard_expansion, 1, true) then\n            -- Given a prefix directory, join it\n            -- with matched part of source file name.\n            if not new_filename:match(\"/$\") then\n               new_filename = new_filename .. \"/\"\n            end\n\n            new_filename = new_filename .. match .. \".lua\"\n         end\n\n         -- Switch slashes back to native.\n         return (new_filename:gsub(\"^%.[/\\\\]\", \"\"):gsub(\"[/\\\\]\", dir_sep))\n      end\n   end\n\n   return orig_filename\nend\n\n-- Always exclude luacov's own files.\nlocal luacov_excludes = {\n   \"luacov$\",\n   \"luacov/hook$\",\n   \"luacov/reporter$\",\n   \"luacov/reporter/default$\",\n   \"luacov/defaults$\",\n   \"luacov/runner$\",\n   \"luacov/stats$\",\n   \"luacov/tick$\",\n   \"luacov/util$\",\n   \"cluacov/version$\"\n}\n\nlocal function is_absolute(path)\n   if path:sub(1, 1) == dir_sep or path:sub(1, 1) == \"/\" then\n      return true\n   end\n\n   if dir_sep == \"\\\\\" and path:find(\"^%a:\") then\n      return true\n   end\n\n   return false\nend\n\nlocal function get_cur_dir()\n   local pwd_cmd = dir_sep == \"\\\\\" and \"cd 2>nul\" or \"pwd 2>/dev/null\"\n   local handler = io.popen(pwd_cmd, \"r\")\n   local cur_dir = handler:read()\n   handler:close()\n   cur_dir = cur_dir:gsub(\"\\r?\\n$\", \"\")\n\n   if cur_dir:sub(-1) ~= dir_sep and cur_dir:sub(-1) ~= \"/\" then\n      cur_dir = cur_dir .. dir_sep\n   end\n\n   return cur_dir\nend\n\n-- Sets configuration. If some options are missing, default values are used instead.\nlocal function set_config(configuration)\n   runner.configuration = {}\n\n   for option, default_value in pairs(runner.defaults) do\n      runner.configuration[option] = default_value\n   end\n\n   for option, value in pairs(configuration) do\n      runner.configuration[option] = value\n   end\n\n   -- Program using LuaCov may change directory during its execution.\n   -- Convert path options to absolute paths to use correct paths anyway.\n   local cur_dir\n\n   for _, option in ipairs({\"statsfile\", \"reportfile\"}) do\n      local path = runner.configuration[option]\n\n      if not is_absolute(path) then\n         cur_dir = cur_dir or get_cur_dir()\n         runner.configuration[option] = cur_dir .. path\n      end\n   end\n\n   acknowledge_modules()\n\n   for _, patt in ipairs(luacov_excludes) do\n      table.insert(runner.configuration.exclude, patt)\n   end\n\n   runner.tick = runner.tick or runner.configuration.tick\nend\n\nlocal function load_config_file(name, is_default)\n   local conf = setmetatable({}, {__index = _G})\n\n   local ok, ret, error_msg = util.load_config(name, conf)\n\n   if ok then\n      if type(ret) == \"table\" then\n         for key, value in pairs(ret) do\n            if conf[key] == nil then\n               conf[key] = value\n            end\n         end\n      end\n\n      return conf\n   end\n\n   local error_type = ret\n\n   if error_type == \"read\" and is_default then\n      return nil\n   end\n\n   io.stderr:write((\"Error: couldn't %s config file %s: %s\\n\"):format(error_type, name, error_msg))\n   raw_os_exit(1)\nend\n\nlocal default_config_file = \".luacov\"\n\n------------------------------------------------------\n-- Loads a valid configuration.\n-- @param[opt] configuration user provided config (config-table or filename)\n-- @return existing configuration if already set, otherwise loads a new\n-- config from the provided data or the defaults.\n-- When loading a new config, if some options are missing, default values\n-- from `luacov.defaults` are used instead.\nfunction runner.load_config(configuration)\n   if not runner.configuration then\n      if not configuration then\n         -- Nothing provided, load from default location if possible.\n         set_config(load_config_file(default_config_file, true) or runner.defaults)\n      elseif type(configuration) == \"string\" then\n         set_config(load_config_file(configuration))\n      elseif type(configuration) == \"table\" then\n         set_config(configuration)\n      else\n         error(\"Expected filename, config table or nil. Got \" .. type(configuration))\n      end\n   end\n\n   return runner.configuration\nend\n\n--------------------------------------------------\n-- Pauses saving data collected by LuaCov's runner.\n-- Allows other processes to write to the same stats file.\n-- Data is still collected during pause.\nfunction runner.pause()\n   runner.paused = true\nend\n\n--------------------------------------------------\n-- Resumes saving data collected by LuaCov's runner.\nfunction runner.resume()\n   runner.paused = false\nend\n\nlocal hook_per_thread\n\n-- Determines whether debug hooks are separate for each thread.\nlocal function has_hook_per_thread()\n   if hook_per_thread == nil then\n      local old_hook, old_mask, old_count = debug.gethook()\n      local noop = function() end\n      debug.sethook(noop, \"l\")\n      local thread_hook = coroutine.wrap(function() return debug.gethook() end)()\n      hook_per_thread = thread_hook ~= noop\n      debug.sethook(old_hook, old_mask, old_count)\n   end\n\n   return hook_per_thread\nend\n\n--------------------------------------------------\n-- Wraps a function, enabling coverage gathering in it explicitly.\n-- LuaCov gathers coverage using a debug hook, and patches coroutine\n-- library to set it on created threads when under standard Lua, where each\n-- coroutine has its own hook. If a coroutine is created using Lua C API\n-- or before the monkey-patching, this wrapper should be applied to the\n-- main function of the coroutine. Under LuaJIT this function is redundant,\n-- as there is only one, global debug hook.\n-- @param f a function\n-- @return a function that enables coverage gathering and calls the original function.\n-- @usage\n-- local coro = coroutine.create(runner.with_luacov(func))\nfunction runner.with_luacov(f)\n   return function(...)\n      if has_hook_per_thread() then\n         debug.sethook(runner.debug_hook, \"l\")\n      end\n\n      return f(...)\n   end\nend\n\n--------------------------------------------------\n-- Initializes LuaCov runner to start collecting data.\n-- @param[opt] configuration if string, filename of config file (used to call `load_config`).\n-- If table then config table (see file `luacov.default.lua` for an example)\nfunction runner.init(configuration)\n   runner.configuration = runner.load_config(configuration)\n\n   -- metatable trick on filehandle won't work if Lua exits through\n   -- os.exit() hence wrap that with exit code as well\n   os.exit = function(...) -- luacheck: no global\n      on_exit()\n      raw_os_exit(...)\n   end\n\n   debug.sethook(runner.debug_hook, \"l\")\n\n   if has_hook_per_thread() then\n      -- debug must be set for each coroutine separately\n      -- hence wrap coroutine function to set the hook there\n      -- as well\n      local rawcoroutinecreate = coroutine.create\n      coroutine.create = function(...) -- luacheck: no global\n         local co = rawcoroutinecreate(...)\n         debug.sethook(co, runner.debug_hook, \"l\")\n         return co\n      end\n\n      -- Version of assert which handles non-string errors properly.\n      local function safeassert(ok, ...)\n         if ok then\n            return ...\n         else\n            error(..., 0)\n         end\n      end\n\n      coroutine.wrap = function(...) -- luacheck: no global\n         local co = rawcoroutinecreate(...)\n         debug.sethook(co, runner.debug_hook, \"l\")\n         return function(...)\n            return safeassert(coroutine.resume(co, ...))\n         end\n      end\n   end\n\n   if not runner.tick then\n      runner.on_exit_trick = on_exit_wrap(on_exit)\n   end\n\n   runner.initialized = true\n   runner.paused = false\nend\n\n--------------------------------------------------\n-- Shuts down LuaCov's runner.\n-- This should only be called from daemon processes or sandboxes which have\n-- disabled os.exit and other hooks that are used to determine shutdown.\nfunction runner.shutdown()\n   on_exit()\nend\n\n-- Gets the sourcefilename from a function.\n-- @param func function to lookup.\n-- @return sourcefilename or nil when not found.\nlocal function getsourcefile(func)\n   assert(type(func) == \"function\")\n   local d = debug.getinfo(func).source\n   if d and d:sub(1, 1) == \"@\" then\n      return d:sub(2)\n   end\nend\n\n-- Looks for a function inside a table.\n-- @param searched set of already checked tables.\nlocal function findfunction(t, searched)\n   if searched[t] then\n      return\n   end\n\n   searched[t] = true\n\n   for _, v in pairs(t) do\n      if type(v) == \"function\" then\n         return v\n      elseif type(v) == \"table\" then\n         local func = findfunction(v, searched)\n         if func then return func end\n      end\n   end\nend\n\n-- Gets source filename from a file name, module name, function or table.\n-- @param name string;   filename,\n--             string;   modulename as passed to require(),\n--             function; where containing file is looked up,\n--             table;    module table where containing file is looked up\n-- @raise error message if could not find source filename.\n-- @return source filename.\nlocal function getfilename(name)\n   if type(name) == \"function\" then\n      local sourcefile = getsourcefile(name)\n\n      if not sourcefile then\n         error(\"Could not infer source filename\")\n      end\n\n      return sourcefile\n   elseif type(name) == \"table\" then\n      local func = findfunction(name, {})\n\n      if not func then\n         error(\"Could not find a function within \" .. tostring(name))\n      end\n\n      return getfilename(func)\n   else\n      if type(name) ~= \"string\" then\n         error(\"Bad argument: \" .. tostring(name))\n      end\n\n      if util.file_exists(name) then\n         return name\n      end\n\n      local success, result = pcall(require, name)\n\n      if not success then\n         error(\"Module/file '\" .. name .. \"' was not found\")\n      end\n\n      if type(result) ~= \"table\" and type(result) ~= \"function\" then\n         error(\"Module '\" .. name .. \"' did not return a result to lookup its file name\")\n      end\n\n      return getfilename(result)\n   end\nend\n\n-- Escapes a filename.\n-- Escapes magic pattern characters, removes .lua extension\n-- and replaces dir seps by '/'.\nlocal function escapefilename(name)\n   return name:gsub(\"%.lua$\", \"\"):gsub(\"[%%%^%$%.%(%)%[%]%+%*%-%?]\",\"%%%0\"):gsub(\"\\\\\", \"/\")\nend\n\nlocal function addfiletolist(name, list)\n  local f = \"^\"..escapefilename(getfilename(name))..\"$\"\n  table.insert(list, f)\n  return f\nend\n\nlocal function addtreetolist(name, level, list)\n   local f = escapefilename(getfilename(name))\n\n   if level or f:match(\"/init$\") then\n      -- chop the last backslash and everything after it\n      f = f:match(\"^(.*)/\") or f\n   end\n\n   local t = \"^\"..f..\"/\"   -- the tree behind the file\n   f = \"^\"..f..\"$\"         -- the file\n   table.insert(list, f)\n   table.insert(list, t)\n   return f, t\nend\n\n-- Returns a pcall result, with the initial 'true' value removed\n-- and 'false' replaced with nil.\nlocal function checkresult(ok, ...)\n   if ok then\n      return ... -- success, strip 'true' value\n   else\n      return nil, ... -- failure; nil + error\n   end\nend\n\n-------------------------------------------------------------------\n-- Adds a file to the exclude list (see `luacov.defaults`).\n-- If passed a function, then through debuginfo the source filename is collected. In case of a table\n-- it will recursively search the table for a function, which is then resolved to a filename through debuginfo.\n-- If the parameter is a string, it will first check if a file by that name exists. If it doesn't exist\n-- it will call `require(name)` to load a module by that name, and the result of require (function or\n-- table expected) is used as described above to get the sourcefile.\n-- @param name\n-- * string;   literal filename,\n-- * string;   modulename as passed to require(),\n-- * function; where containing file is looked up,\n-- * table;    module table where containing file is looked up\n-- @return the pattern as added to the list, or nil + error\nfunction runner.excludefile(name)\n  return checkresult(pcall(addfiletolist, name, runner.configuration.exclude))\nend\n-------------------------------------------------------------------\n-- Adds a file to the include list (see `luacov.defaults`).\n-- @param name see `excludefile`\n-- @return the pattern as added to the list, or nil + error\nfunction runner.includefile(name)\n  return checkresult(pcall(addfiletolist, name, runner.configuration.include))\nend\n-------------------------------------------------------------------\n-- Adds a tree to the exclude list (see `luacov.defaults`).\n-- If `name = 'luacov'` and `level = nil` then\n-- module 'luacov' (luacov.lua) and the tree 'luacov' (containing `luacov/runner.lua` etc.) is excluded.\n-- If `name = 'pl.path'` and `level = true` then\n-- module 'pl' (pl.lua) and the tree 'pl' (containing `pl/path.lua` etc.) is excluded.\n-- NOTE: in case of an 'init.lua' file, the 'level' parameter will always be set\n-- @param name see `excludefile`\n-- @param level if truthy then one level up is added, including the tree\n-- @return the 2 patterns as added to the list (file and tree), or nil + error\nfunction runner.excludetree(name, level)\n  return checkresult(pcall(addtreetolist, name, level, runner.configuration.exclude))\nend\n-------------------------------------------------------------------\n-- Adds a tree to the include list (see `luacov.defaults`).\n-- @param name see `excludefile`\n-- @param level see `includetree`\n-- @return the 2 patterns as added to the list (file and tree), or nil + error\nfunction runner.includetree(name, level)\n  return checkresult(pcall(addtreetolist, name, level, runner.configuration.include))\nend\n\n\nreturn setmetatable(runner, {__call = function(_, configfile) runner.init(configfile) end})\n"
  },
  {
    "path": "modules/luacov/stats.lua",
    "content": "-----------------------------------------------------\n-- Manages the file with statistics (being) collected.\n-- @class module\n-- @name luacov.stats\nlocal stats = {}\n\n-----------------------------------------------------\n-- Loads the stats file.\n-- @param statsfile path to the stats file.\n-- @return table with data or nil if couldn't load.\n-- The table maps filenames to stats tables.\n-- Per-file tables map line numbers to hits or nils when there are no hits.\n-- Additionally, .max field contains maximum line number\n-- and .max_hits contains maximum number of hits in the file.\nfunction stats.load(statsfile)\n   local data = {}\n   local fd = io.open(statsfile, \"r\")\n   if not fd then\n      return nil\n   end\n   while true do\n      local max = fd:read(\"*n\")\n      if not max then\n         break\n      end\n      if fd:read(1) ~= \":\" then\n         break\n      end\n      local filename = fd:read(\"*l\")\n      if not filename then\n         break\n      end\n      data[filename] = {\n         max = max,\n         max_hits = 0\n      }\n      for i = 1, max do\n         local hits = fd:read(\"*n\")\n         if not hits then\n            break\n         end\n         if fd:read(1) ~= \" \" then\n            break\n         end\n         if hits > 0 then\n            data[filename][i] = hits\n            data[filename].max_hits = math.max(data[filename].max_hits, hits)\n         end\n      end\n   end\n   fd:close()\n   return data\nend\n\n-----------------------------------------------------\n-- Saves data to the stats file.\n-- @param statsfile path to the stats file.\n-- @param data data to store.\nfunction stats.save(statsfile, data)\n   local fd = assert(io.open(statsfile, \"w\"))\n\n   local filenames = {}\n   for filename in pairs(data) do\n      table.insert(filenames, filename)\n   end\n   table.sort(filenames)\n\n   for _, filename in ipairs(filenames) do\n      local filedata = data[filename]\n      fd:write(filedata.max, \":\", filename, \"\\n\")\n\n      for i = 1, filedata.max do\n         fd:write(tostring(filedata[i] or 0), \" \")\n      end\n      fd:write(\"\\n\")\n   end\n   fd:close()\nend\n\nreturn stats\n"
  },
  {
    "path": "modules/luacov/tick.lua",
    "content": "\n--- Load luacov using this if you want it to periodically\n-- save the stats file. This is useful if your script is\n-- a daemon (i.e., does not properly terminate).\n-- @class module\n-- @name luacov.tick\n-- @see luacov.defaults.savestepsize\nlocal runner = require(\"luacov.runner\")\nrunner.tick = true\nrunner.init()\nreturn {}\n"
  },
  {
    "path": "modules/luacov/util.lua",
    "content": "---------------------------------------------------\n-- Utility module.\n-- @class module\n-- @name luacov.util\nlocal util = {}\n\n--- Removes a prefix from a string if it's present.\n-- @param str a string.\n-- @param prefix a prefix string.\n-- @return original string if does not start with prefix\n-- or string without prefix.\nfunction util.unprefix(str, prefix)\n   if str:sub(1, #prefix) == prefix then\n      return str:sub(#prefix + 1)\n   else\n      return str\n   end\nend\n\n-- Returns contents of a file or nil + error message.\nlocal function read_file(name)\n   local f, open_err = io.open(name, \"rb\")\n\n   if not f then\n      return nil, util.unprefix(open_err, name .. \": \")\n   end\n\n   local contents, read_err = f:read(\"*a\")\n   f:close()\n\n   if contents then\n      return contents\n   else\n      return nil, read_err\n   end\nend\n\n--- Loads a string.\n-- @param str a string.\n-- @param[opt] env environment table.\n-- @param[opt] chunkname chunk name.\nfunction util.load_string(str, env, chunkname)\n   if _VERSION:find(\"5%.1\") then\n      local func, err = loadstring(str, chunkname) -- luacheck: compat\n\n      if not func then\n         return nil, err\n      end\n\n      if env then\n         setfenv(func, env) -- luacheck: compat\n      end\n\n      return func\n   else\n      return load(str, chunkname, \"bt\", env or _ENV) -- luacheck: compat\n   end\nend\n\n--- Load a config file.\n-- Reads, loads and runs a Lua file in an environment.\n-- @param name file name.\n-- @param env environment table.\n-- @return true and the first return value of config on success,\n-- nil + error type + error message on failure, where error type\n-- can be \"read\", \"load\" or \"run\".\nfunction util.load_config(name, env)\n   local src, read_err = read_file(name)\n\n   if not src then\n      return nil, \"read\", read_err\n   end\n\n   local func, load_err = util.load_string(src, env, \"@config\")\n\n   if not func then\n      return nil, \"load\", \"line \" .. util.unprefix(load_err, \"config:\")\n   end\n\n   local ok, ret = pcall(func)\n\n   if not ok then\n      return nil, \"run\", \"line \" .. util.unprefix(ret, \"config:\")\n   end\n\n   return true, ret\nend\n\n--- Checks if a file exists.\n-- @param name file name.\n-- @return true if file can be opened, false otherwise.\nfunction util.file_exists(name)\n   local f = io.open(name)\n\n   if f then\n      f:close()\n      return true\n   else\n      return false\n   end\nend\n\nreturn util\n"
  },
  {
    "path": "modules/luacov.lua",
    "content": "--- Loads `luacov.runner` and immediately starts it.\n-- Useful for launching scripts from the command-line. Returns the `luacov.runner` module.\n-- @class module\n-- @name luacov\n-- @usage lua -lluacov sometest.lua\nlocal runner = require(\"luacov.runner\")\nrunner.init()\nreturn runner\n"
  },
  {
    "path": "modules/luaunit.lua",
    "content": "--[[\n        luaunit.lua\n\nDescription: A unit testing framework\nHomepage: https://github.com/bluebird75/luaunit\nDevelopment by Philippe Fremy <phil@freehackers.org>\nBased on initial work of Ryu, Gwang (http://www.gpgstudy.com/gpgiki/LuaUnit)\nLicense: BSD License, see LICENSE.txt\n]]--\n\nrequire(\"math\")\nlocal M={}\n\n-- private exported functions (for testing)\nM.private = {}\n\nM.VERSION='3.4'\nM._VERSION=M.VERSION -- For LuaUnit v2 compatibility\n\n-- a version which distinguish between regular Lua and LuaJit\nM._LUAVERSION = (jit and jit.version) or _VERSION\n\n--[[ Some people like assertEquals( actual, expected ) and some people prefer\nassertEquals( expected, actual ).\n]]--\nM.ORDER_ACTUAL_EXPECTED = true\nM.PRINT_TABLE_REF_IN_ERROR_MSG = false\nM.LINE_LENGTH = 80\nM.TABLE_DIFF_ANALYSIS_THRESHOLD = 10    -- display deep analysis for more than 10 items\nM.LIST_DIFF_ANALYSIS_THRESHOLD  = 10    -- display deep analysis for more than 10 items\n\n-- this setting allow to remove entries from the stack-trace, for \n-- example to hide a call to a framework which would be calling luaunit\nM.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE = 0\n\n--[[ EPS is meant to help with Lua's floating point math in simple corner\ncases like almostEquals(1.1-0.1, 1), which may not work as-is (e.g. on numbers\nwith rational binary representation) if the user doesn't provide some explicit\nerror margin.\n\nThe default margin used by almostEquals() in such cases is EPS; and since\nLua may be compiled with different numeric precisions (single vs. double), we\ntry to select a useful default for it dynamically. Note: If the initial value\nis not acceptable, it can be changed by the user to better suit specific needs.\n\nSee also: https://en.wikipedia.org/wiki/Machine_epsilon\n]]\nM.EPS = 2^-52 -- = machine epsilon for \"double\", ~2.22E-16\nif math.abs(1.1 - 1 - 0.1) > M.EPS then\n    -- rounding error is above EPS, assume single precision\n    M.EPS = 2^-23 -- = machine epsilon for \"float\", ~1.19E-07\nend\n\n-- set this to false to debug luaunit\nlocal STRIP_LUAUNIT_FROM_STACKTRACE = true\n\nM.VERBOSITY_DEFAULT = 10\nM.VERBOSITY_LOW     = 1\nM.VERBOSITY_QUIET   = 0\nM.VERBOSITY_VERBOSE = 20\nM.DEFAULT_DEEP_ANALYSIS = nil\nM.FORCE_DEEP_ANALYSIS   = true\nM.DISABLE_DEEP_ANALYSIS = false\n\n-- set EXPORT_ASSERT_TO_GLOBALS to have all asserts visible as global values\n-- EXPORT_ASSERT_TO_GLOBALS = true\n\n-- we need to keep a copy of the script args before it is overriden\nlocal cmdline_argv = rawget(_G, \"arg\")\n\nM.FAILURE_PREFIX = 'LuaUnit test FAILURE: ' -- prefix string for failed tests\nM.SUCCESS_PREFIX = 'LuaUnit test SUCCESS: ' -- prefix string for successful tests finished early\nM.SKIP_PREFIX    = 'LuaUnit test SKIP:    ' -- prefix string for skipped tests\n\n\n\nM.USAGE=[[Usage: lua <your_test_suite.lua> [options] [testname1 [testname2] ... ]\nOptions:\n  -h, --help:             Print this help\n  --version:              Print version information\n  -v, --verbose:          Increase verbosity\n  -q, --quiet:            Set verbosity to minimum\n  -e, --error:            Stop on first error\n  -f, --failure:          Stop on first failure or error\n  -s, --shuffle:          Shuffle tests before running them\n  -o, --output OUTPUT:    Set output type to OUTPUT\n                          Possible values: text, tap, junit, nil\n  -n, --name NAME:        For junit only, mandatory name of xml file\n  -r, --repeat NUM:       Execute all tests NUM times, e.g. to trig the JIT\n  -p, --pattern PATTERN:  Execute all test names matching the Lua PATTERN\n                          May be repeated to include several patterns\n                          Make sure you escape magic chars like +? with %\n  -x, --exclude PATTERN:  Exclude all test names matching the Lua PATTERN\n                          May be repeated to exclude several patterns\n                          Make sure you escape magic chars like +? with %\n  testname1, testname2, ... : tests to run in the form of testFunction,\n                              TestClass or TestClass.testMethod\n\nYou may also control LuaUnit options with the following environment variables:\n* LUAUNIT_OUTPUT: same as --output\n* LUAUNIT_JUNIT_FNAME: same as --name ]]\n\n----------------------------------------------------------------\n--\n--                 general utility functions\n--\n----------------------------------------------------------------\n\n--[[ Note on catching exit\n\nI have seen the case where running a big suite of test cases and one of them would\nperform a os.exit(0), making the outside world think that the full test suite was executed\nsuccessfully.\n\nThis is an attempt to mitigate this problem: we override os.exit() to now let a test\nexit the framework while we are running. When we are not running, it behaves normally.\n]]\n\nM.oldOsExit = os.exit\nos.exit = function(...) \n    if M.LuaUnit and #M.LuaUnit.instances ~= 0 then\n        local msg = [[You are trying to exit but there is still a running instance of LuaUnit.\nLuaUnit expects to run until the end before exiting with a complete status of successful/failed tests.\n\nTo force exit LuaUnit while running, please call before os.exit (assuming lu is the luaunit module loaded):\n\n    lu.unregisterCurrentSuite() \n\n]]\n        M.private.error_fmt(2, msg)\n    end\n    M.oldOsExit(...)\nend\n\nlocal function pcall_or_abort(func, ...)\n    -- unpack is a global function for Lua 5.1, otherwise use table.unpack\n    local unpack = rawget(_G, \"unpack\") or table.unpack\n    local result = {pcall(func, ...)}\n    if not result[1] then\n        -- an error occurred\n        print(result[2]) -- error message\n        print()\n        print(M.USAGE)\n        os.exit(-1)\n    end\n    return unpack(result, 2)\nend\n\nlocal crossTypeOrdering = {\n    number = 1, boolean = 2, string = 3, table = 4, other = 5\n}\nlocal crossTypeComparison = {\n    number = function(a, b) return a < b end,\n    string = function(a, b) return a < b end,\n    other = function(a, b) return tostring(a) < tostring(b) end,\n}\n\nlocal function crossTypeSort(a, b)\n    local type_a, type_b = type(a), type(b)\n    if type_a == type_b then\n        local func = crossTypeComparison[type_a] or crossTypeComparison.other\n        return func(a, b)\n    end\n    type_a = crossTypeOrdering[type_a] or crossTypeOrdering.other\n    type_b = crossTypeOrdering[type_b] or crossTypeOrdering.other\n    return type_a < type_b\nend\n\nlocal function __genSortedIndex( t )\n    -- Returns a sequence consisting of t's keys, sorted.\n    local sortedIndex = {}\n\n    for key,_ in pairs(t) do\n        table.insert(sortedIndex, key)\n    end\n\n    table.sort(sortedIndex, crossTypeSort)\n    return sortedIndex\nend\nM.private.__genSortedIndex = __genSortedIndex\n\nlocal function sortedNext(state, control)\n    -- Equivalent of the next() function of table iteration, but returns the\n    -- keys in sorted order (see __genSortedIndex and crossTypeSort).\n    -- The state is a temporary variable during iteration and contains the\n    -- sorted key table (state.sortedIdx). It also stores the last index (into\n    -- the keys) used by the iteration, to find the next one quickly.\n    local key\n\n    --print(\"sortedNext: control = \"..tostring(control) )\n    if control == nil then\n        -- start of iteration\n        state.count = #state.sortedIdx\n        state.lastIdx = 1\n        key = state.sortedIdx[1]\n        return key, state.t[key]\n    end\n\n    -- normally, we expect the control variable to match the last key used\n    if control ~= state.sortedIdx[state.lastIdx] then\n        -- strange, we have to find the next value by ourselves\n        -- the key table is sorted in crossTypeSort() order! -> use bisection\n        local lower, upper = 1, state.count\n        repeat\n            state.lastIdx = math.modf((lower + upper) / 2)\n            key = state.sortedIdx[state.lastIdx]\n            if key == control then\n                break -- key found (and thus prev index)\n            end\n            if crossTypeSort(key, control) then\n                -- key < control, continue search \"right\" (towards upper bound)\n                lower = state.lastIdx + 1\n            else\n                -- key > control, continue search \"left\" (towards lower bound)\n                upper = state.lastIdx - 1\n            end\n        until lower > upper\n        if lower > upper then -- only true if the key wasn't found, ...\n            state.lastIdx = state.count -- ... so ensure no match in code below\n        end\n    end\n\n    -- proceed by retrieving the next value (or nil) from the sorted keys\n    state.lastIdx = state.lastIdx + 1\n    key = state.sortedIdx[state.lastIdx]\n    if key then\n        return key, state.t[key]\n    end\n\n    -- getting here means returning `nil`, which will end the iteration\nend\n\nlocal function sortedPairs(tbl)\n    -- Equivalent of the pairs() function on tables. Allows to iterate in\n    -- sorted order. As required by \"generic for\" loops, this will return the\n    -- iterator (function), an \"invariant state\", and the initial control value.\n    -- (see http://www.lua.org/pil/7.2.html)\n    return sortedNext, {t = tbl, sortedIdx = __genSortedIndex(tbl)}, nil\nend\nM.private.sortedPairs = sortedPairs\n\n-- seed the random with a strongly varying seed\nmath.randomseed(math.floor(os.clock()*1E11))\n\nlocal function randomizeTable( t )\n    -- randomize the item orders of the table t\n    for i = #t, 2, -1 do\n        local j = math.random(i)\n        if i ~= j then\n            t[i], t[j] = t[j], t[i]\n        end\n    end\nend\nM.private.randomizeTable = randomizeTable\n\nlocal function strsplit(delimiter, text)\n-- Split text into a list consisting of the strings in text, separated\n-- by strings matching delimiter (which may _NOT_ be a pattern).\n-- Example: strsplit(\", \", \"Anna, Bob, Charlie, Dolores\")\n    if delimiter == \"\" or delimiter == nil then -- this would result in endless loops\n        error(\"delimiter is nil or empty string!\")\n    end\n    if text == nil then\n        return nil\n    end\n\n    local list, pos, first, last = {}, 1\n    while true do\n        first, last = text:find(delimiter, pos, true)\n        if first then -- found?\n            table.insert(list, text:sub(pos, first - 1))\n            pos = last + 1\n        else\n            table.insert(list, text:sub(pos))\n            break\n        end\n    end\n    return list\nend\nM.private.strsplit = strsplit\n\nlocal function hasNewLine( s )\n    -- return true if s has a newline\n    return (string.find(s, '\\n', 1, true) ~= nil)\nend\nM.private.hasNewLine = hasNewLine\n\nlocal function prefixString( prefix, s )\n    -- Prefix all the lines of s with prefix\n    return prefix .. string.gsub(s, '\\n', '\\n' .. prefix)\nend\nM.private.prefixString = prefixString\n\nlocal function strMatch(s, pattern, start, final )\n    -- return true if s matches completely the pattern from index start to index end\n    -- return false in every other cases\n    -- if start is nil, matches from the beginning of the string\n    -- if final is nil, matches to the end of the string\n    start = start or 1\n    final = final or string.len(s)\n\n    local foundStart, foundEnd = string.find(s, pattern, start, false)\n    return foundStart == start and foundEnd == final\nend\nM.private.strMatch = strMatch\n\nlocal function patternFilter(patterns, expr)\n    -- Run `expr` through the inclusion and exclusion rules defined in patterns\n    -- and return true if expr shall be included, false for excluded.\n    -- Inclusion pattern are defined as normal patterns, exclusions \n    -- patterns start with `!` and are followed by a normal pattern\n\n    -- result: nil = UNKNOWN (not matched yet), true = ACCEPT, false = REJECT\n    -- default: true if no explicit \"include\" is found, set to false otherwise\n    local default, result = true, nil\n\n    if patterns ~= nil then\n        for _, pattern in ipairs(patterns) do\n            local exclude = pattern:sub(1,1) == '!'\n            if exclude then\n                pattern = pattern:sub(2)\n            else\n                -- at least one include pattern specified, a match is required\n                default = false\n            end\n            -- print('pattern: ',pattern)\n            -- print('exclude: ',exclude)\n            -- print('default: ',default)\n\n            if string.find(expr, pattern) then\n                -- set result to false when excluding, true otherwise\n                result = not exclude\n            end\n        end\n    end\n\n    if result ~= nil then\n        return result\n    end\n    return default\nend\nM.private.patternFilter = patternFilter\n\nlocal function xmlEscape( s )\n    -- Return s escaped for XML attributes\n    -- escapes table:\n    -- \"   &quot;\n    -- '   &apos;\n    -- <   &lt;\n    -- >   &gt;\n    -- &   &amp;\n\n    return string.gsub( s, '.', {\n        ['&'] = \"&amp;\",\n        ['\"'] = \"&quot;\",\n        [\"'\"] = \"&apos;\",\n        ['<'] = \"&lt;\",\n        ['>'] = \"&gt;\",\n    } )\nend\nM.private.xmlEscape = xmlEscape\n\nlocal function xmlCDataEscape( s )\n    -- Return s escaped for CData section, escapes: \"]]>\"\n    return string.gsub( s, ']]>', ']]&gt;' )\nend\nM.private.xmlCDataEscape = xmlCDataEscape\n\n\nlocal function lstrip( s )\n    --[[Return s with all leading white spaces and tabs removed]]\n    local idx = 0\n    while idx < s:len() do\n        idx = idx + 1\n        local c = s:sub(idx,idx)\n        if c ~= ' ' and c ~= '\\t' then\n            break\n        end\n    end\n    return s:sub(idx)\nend\nM.private.lstrip = lstrip\n\nlocal function extractFileLineInfo( s )\n    --[[ From a string in the form \"(leading spaces) dir1/dir2\\dir3\\file.lua:linenb: msg\"\n\n    Return the \"file.lua:linenb\" information\n    ]]\n    local s2 = lstrip(s)\n    local firstColon = s2:find(':', 1, true)\n    if firstColon == nil then\n        -- string is not in the format file:line:\n        return s\n    end\n    local secondColon = s2:find(':', firstColon+1, true)\n    if secondColon == nil then\n        -- string is not in the format file:line:\n        return s\n    end\n\n    return s2:sub(1, secondColon-1) \nend\nM.private.extractFileLineInfo = extractFileLineInfo\n\n\nlocal function stripLuaunitTrace2( stackTrace, errMsg )\n    --[[\n    -- Example of  a traceback:\n    <<stack traceback:\n        example_with_luaunit.lua:130: in function 'test2_withFailure'\n        ./luaunit.lua:1449: in function <./luaunit.lua:1449>\n        [C]: in function 'xpcall'\n        ./luaunit.lua:1449: in function 'protectedCall'\n        ./luaunit.lua:1508: in function 'execOneFunction'\n        ./luaunit.lua:1596: in function 'runSuiteByInstances'\n        ./luaunit.lua:1660: in function 'runSuiteByNames'\n        ./luaunit.lua:1736: in function 'runSuite'\n        example_with_luaunit.lua:140: in main chunk\n        [C]: in ?>>\n    error message: <<example_with_luaunit.lua:130: expected 2, got 1>>\n\n        Other example:\n    <<stack traceback:\n        ./luaunit.lua:545: in function 'assertEquals'\n        example_with_luaunit.lua:58: in function 'TestToto.test7'\n        ./luaunit.lua:1517: in function <./luaunit.lua:1517>\n        [C]: in function 'xpcall'\n        ./luaunit.lua:1517: in function 'protectedCall'\n        ./luaunit.lua:1578: in function 'execOneFunction'\n        ./luaunit.lua:1677: in function 'runSuiteByInstances'\n        ./luaunit.lua:1730: in function 'runSuiteByNames'\n        ./luaunit.lua:1806: in function 'runSuite'\n        example_with_luaunit.lua:140: in main chunk\n        [C]: in ?>>\n    error message: <<example_with_luaunit.lua:58:  expected 2, got 1>>\n\n    <<stack traceback:\n        luaunit2/example_with_luaunit.lua:124: in function 'test1_withFailure'\n        luaunit2/luaunit.lua:1532: in function <luaunit2/luaunit.lua:1532>\n        [C]: in function 'xpcall'\n        luaunit2/luaunit.lua:1532: in function 'protectedCall'\n        luaunit2/luaunit.lua:1591: in function 'execOneFunction'\n        luaunit2/luaunit.lua:1679: in function 'runSuiteByInstances'\n        luaunit2/luaunit.lua:1743: in function 'runSuiteByNames'\n        luaunit2/luaunit.lua:1819: in function 'runSuite'\n        luaunit2/example_with_luaunit.lua:140: in main chunk\n        [C]: in ?>>\n    error message: <<luaunit2/example_with_luaunit.lua:124:  expected 2, got 1>>\n\n\n    -- first line is \"stack traceback\": KEEP\n    -- next line may be luaunit line: REMOVE\n    -- next lines are call in the program under testOk: REMOVE\n    -- next lines are calls from luaunit to call the program under test: KEEP\n\n    -- Strategy:\n    -- keep first line\n    -- remove lines that are part of luaunit\n    -- kepp lines until we hit a luaunit line\n\n    The strategy for stripping is:\n    * keep first line \"stack traceback:\"\n    * part1:\n        * analyse all lines of the stack from bottom to top of the stack (first line to last line)\n        * extract the \"file:line:\" part of the line\n        * compare it with the \"file:line\" part of the error message\n        * if it does not match strip the line\n        * if it matches, keep the line and move to part 2\n    * part2:\n        * anything NOT starting with luaunit.lua is the interesting part of the stack trace\n        * anything starting again with luaunit.lua is part of the test launcher and should be stripped out\n    ]]\n\n    local function isLuaunitInternalLine( s )\n        -- return true if line of stack trace comes from inside luaunit\n        return s:find('[/\\\\]luaunit%.lua:%d+: ') ~= nil\n    end\n\n    -- print( '<<'..stackTrace..'>>' )\n\n    local t = strsplit( '\\n', stackTrace )\n    -- print( prettystr(t) )\n\n    local idx = 2\n\n    local errMsgFileLine = extractFileLineInfo(errMsg)\n    -- print('emfi=\"'..errMsgFileLine..'\"')\n\n    -- remove lines that are still part of luaunit\n    while t[idx] and extractFileLineInfo(t[idx]) ~= errMsgFileLine do\n        -- print('Removing : '..t[idx] )\n        table.remove(t, idx)\n    end\n\n    -- keep lines until we hit luaunit again\n    while t[idx] and (not isLuaunitInternalLine(t[idx])) do\n        -- print('Keeping : '..t[idx] )\n        idx = idx + 1\n    end\n\n    -- remove remaining luaunit lines\n    while t[idx] do\n        -- print('Removing2 : '..t[idx] )\n        table.remove(t, idx)\n    end\n\n    -- print( prettystr(t) )\n    return table.concat( t, '\\n')\n\nend\nM.private.stripLuaunitTrace2 = stripLuaunitTrace2\n\n\nlocal function prettystr_sub(v, indentLevel, printTableRefs, cycleDetectTable )\n    local type_v = type(v)\n    if \"string\" == type_v  then\n        -- use clever delimiters according to content:\n        -- enclose with single quotes if string contains \", but no '\n        if v:find('\"', 1, true) and not v:find(\"'\", 1, true) then\n            return \"'\" .. v .. \"'\"\n        end\n        -- use double quotes otherwise, escape embedded \"\n        return '\"' .. v:gsub('\"', '\\\\\"') .. '\"'\n\n    elseif \"table\" == type_v then\n        --if v.__class__ then\n        --    return string.gsub( tostring(v), 'table', v.__class__ )\n        --end\n        return M.private._table_tostring(v, indentLevel, printTableRefs, cycleDetectTable)\n\n    elseif \"number\" == type_v then\n        -- eliminate differences in formatting between various Lua versions\n        if v ~= v then\n            return \"#NaN\" -- \"not a number\"\n        end\n        if v == math.huge then\n            return \"#Inf\" -- \"infinite\"\n        end\n        if v == -math.huge then\n            return \"-#Inf\"\n        end\n        if _VERSION == \"Lua 5.3\" then\n            local i = math.tointeger(v)\n            if i then\n                return tostring(i)\n            end\n        end\n    end\n\n    return tostring(v)\nend\n\nlocal function prettystr( v )\n    --[[ Pretty string conversion, to display the full content of a variable of any type.\n\n    * string are enclosed with \" by default, or with ' if string contains a \"\n    * tables are expanded to show their full content, with indentation in case of nested tables\n    ]]--\n    local cycleDetectTable = {}\n    local s = prettystr_sub(v, 1, M.PRINT_TABLE_REF_IN_ERROR_MSG, cycleDetectTable)\n    if cycleDetectTable.detected and not M.PRINT_TABLE_REF_IN_ERROR_MSG then\n        -- some table contain recursive references,\n        -- so we must recompute the value by including all table references\n        -- else the result looks like crap\n        cycleDetectTable = {}\n        s = prettystr_sub(v, 1, true, cycleDetectTable)\n    end\n    return s\nend\nM.prettystr = prettystr\n\nfunction M.adjust_err_msg_with_iter( err_msg, iter_msg )\n    --[[ Adjust the error message err_msg: trim the FAILURE_PREFIX or SUCCESS_PREFIX information if needed, \n    add the iteration message if any and return the result.\n\n    err_msg:  string, error message captured with pcall\n    iter_msg: a string describing the current iteration (\"iteration N\") or nil\n              if there is no iteration in this test.\n\n    Returns: (new_err_msg, test_status)\n        new_err_msg: string, adjusted error message, or nil in case of success\n        test_status: M.NodeStatus.FAIL, SUCCESS or ERROR according to the information\n                     contained in the error message.\n    ]]\n    if iter_msg then\n        iter_msg = iter_msg..', '\n    else\n        iter_msg = ''\n    end\n\n    local RE_FILE_LINE = '.*:%d+: '\n\n    -- error message is not necessarily a string, \n    -- so convert the value to string with prettystr()\n    if type( err_msg ) ~= 'string' then\n        err_msg = prettystr( err_msg )\n    end\n\n    if (err_msg:find( M.SUCCESS_PREFIX ) == 1) or err_msg:match( '('..RE_FILE_LINE..')' .. M.SUCCESS_PREFIX .. \".*\" ) then\n        -- test finished early with success()\n        return nil, M.NodeStatus.SUCCESS\n    end\n\n    if (err_msg:find( M.SKIP_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.SKIP_PREFIX .. \".*\" ) ~= nil) then\n        -- substitute prefix by iteration message\n        err_msg = err_msg:gsub('.*'..M.SKIP_PREFIX, iter_msg, 1)\n        -- print(\"failure detected\")\n        return err_msg, M.NodeStatus.SKIP\n    end\n\n    if (err_msg:find( M.FAILURE_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.FAILURE_PREFIX .. \".*\" ) ~= nil) then\n        -- substitute prefix by iteration message\n        err_msg = err_msg:gsub(M.FAILURE_PREFIX, iter_msg, 1)\n        -- print(\"failure detected\")\n        return err_msg, M.NodeStatus.FAIL\n    end\n\n\n\n    -- print(\"error detected\")\n    -- regular error, not a failure\n    if iter_msg then\n        local match\n        -- \"./test\\\\test_luaunit.lua:2241: some error msg\n        match = err_msg:match( '(.*:%d+: ).*' ) \n        if match then\n            err_msg = err_msg:gsub( match, match .. iter_msg )\n        else\n            -- no file:line: infromation, just add the iteration info at the beginning of the line\n            err_msg = iter_msg .. err_msg\n        end\n    end\n    return err_msg, M.NodeStatus.ERROR\nend\n\nlocal function tryMismatchFormatting( table_a, table_b, doDeepAnalysis, margin )\n    --[[\n    Prepares a nice error message when comparing tables, performing a deeper \n    analysis.\n\n    Arguments:\n    * table_a, table_b: tables to be compared\n    * doDeepAnalysis:\n        M.DEFAULT_DEEP_ANALYSIS: (the default if not specified) perform deep analysis only for big lists and big dictionnaries\n        M.FORCE_DEEP_ANALYSIS  : always perform deep analysis\n        M.DISABLE_DEEP_ANALYSIS: never perform deep analysis\n    * margin: supplied only for almost equality\n\n    Returns: {success, result}\n    * success: false if deep analysis could not be performed \n               in this case, just use standard assertion message\n    * result: if success is true, a multi-line string with deep analysis of the two lists\n    ]]\n\n    -- check if table_a & table_b are suitable for deep analysis\n    if type(table_a) ~= 'table' or type(table_b) ~= 'table' then\n        return false\n    end\n\n    if doDeepAnalysis == M.DISABLE_DEEP_ANALYSIS then\n        return false\n    end\n\n    local len_a, len_b, isPureList = #table_a, #table_b, true\n\n    for k1, v1 in pairs(table_a) do\n        if type(k1) ~= 'number' or k1 > len_a then\n            -- this table a mapping\n            isPureList = false\n            break\n        end\n    end\n\n    if isPureList then\n        for k2, v2 in pairs(table_b) do\n            if type(k2) ~= 'number' or k2 > len_b then\n                -- this table a mapping\n                isPureList = false\n                break\n            end\n        end\n    end\n\n    if isPureList and math.min(len_a, len_b) < M.LIST_DIFF_ANALYSIS_THRESHOLD then\n        if not (doDeepAnalysis == M.FORCE_DEEP_ANALYSIS) then\n            return false\n        end\n    end\n\n    if isPureList then\n        return M.private.mismatchFormattingPureList( table_a, table_b, margin )\n    else\n        -- only work on mapping for the moment\n        -- return M.private.mismatchFormattingMapping( table_a, table_b, doDeepAnalysis )\n        return false\n    end\nend\nM.private.tryMismatchFormatting = tryMismatchFormatting\n\nlocal function getTaTbDescr()\n    if not M.ORDER_ACTUAL_EXPECTED then\n        return 'expected', 'actual'\n    end\n    return 'actual', 'expected'\nend\n\nlocal function extendWithStrFmt( res, ... )\n    table.insert( res, string.format( ... ) )\nend\n\nlocal function mismatchFormattingMapping( table_a, table_b, doDeepAnalysis )\n    --[[\n    Prepares a nice error message when comparing tables which are not pure lists, performing a deeper \n    analysis.\n\n    Returns: {success, result}\n    * success: false if deep analysis could not be performed \n               in this case, just use standard assertion message\n    * result: if success is true, a multi-line string with deep analysis of the two lists\n    ]]\n\n    -- disable for the moment\n    --[[\n    local result = {}\n    local descrTa, descrTb = getTaTbDescr()\n\n    local keysCommon = {}\n    local keysOnlyTa = {}\n    local keysOnlyTb = {}\n    local keysDiffTaTb = {}\n\n    local k, v\n\n    for k,v in pairs( table_a ) do\n        if is_equal( v, table_b[k] ) then\n            table.insert( keysCommon, k )\n        else \n            if table_b[k] == nil then\n                table.insert( keysOnlyTa, k )\n            else\n                table.insert( keysDiffTaTb, k )\n            end\n        end\n    end\n\n    for k,v in pairs( table_b ) do\n        if not is_equal( v, table_a[k] ) and table_a[k] == nil then\n            table.insert( keysOnlyTb, k )\n        end\n    end\n\n    local len_a = #keysCommon + #keysDiffTaTb + #keysOnlyTa\n    local len_b = #keysCommon + #keysDiffTaTb + #keysOnlyTb\n    local limited_display = (len_a < 5 or len_b < 5)\n\n    if math.min(len_a, len_b) < M.TABLE_DIFF_ANALYSIS_THRESHOLD then\n        return false\n    end\n\n    if not limited_display then\n        if len_a == len_b then\n            extendWithStrFmt( result, 'Table A (%s) and B (%s) both have %d items', descrTa, descrTb, len_a )\n        else\n            extendWithStrFmt( result, 'Table A (%s) has %d items and table B (%s) has %d items', descrTa, len_a, descrTb, len_b )\n            end\n\n        if #keysCommon == 0 and #keysDiffTaTb == 0 then\n            table.insert( result, 'Table A and B have no keys in common, they are totally different')\n        else\n            local s_other = 'other '\n            if #keysCommon then\n                extendWithStrFmt( result, 'Table A and B have %d identical items', #keysCommon )\n            else\n                table.insert( result, 'Table A and B have no identical items' )\n                s_other = ''\n            end\n\n            if #keysDiffTaTb ~= 0 then\n                result[#result] = string.format( '%s and %d items differing present in both tables', result[#result], #keysDiffTaTb)\n            else\n                result[#result] = string.format( '%s and no %sitems differing present in both tables', result[#result], s_other, #keysDiffTaTb)\n            end\n        end\n\n        extendWithStrFmt( result, 'Table A has %d keys not present in table B and table B has %d keys not present in table A', #keysOnlyTa, #keysOnlyTb ) \n    end\n\n    local function keytostring(k)\n        if \"string\" == type(k) and k:match(\"^[_%a][_%w]*$\") then\n            return k\n        end\n        return prettystr(k)\n    end\n\n    if #keysDiffTaTb ~= 0 then\n        table.insert( result, 'Items differing in A and B:')\n        for k,v in sortedPairs( keysDiffTaTb ) do\n            extendWithStrFmt( result, '  - A[%s]: %s', keytostring(v), prettystr(table_a[v]) )\n            extendWithStrFmt( result, '  + B[%s]: %s', keytostring(v), prettystr(table_b[v]) )\n        end\n    end    \n\n    if #keysOnlyTa ~= 0 then\n        table.insert( result, 'Items only in table A:' )\n        for k,v in sortedPairs( keysOnlyTa ) do\n            extendWithStrFmt( result, '  - A[%s]: %s', keytostring(v), prettystr(table_a[v]) )\n        end\n    end\n\n    if #keysOnlyTb ~= 0 then\n        table.insert( result, 'Items only in table B:' )\n        for k,v in sortedPairs( keysOnlyTb ) do\n            extendWithStrFmt( result, '  + B[%s]: %s', keytostring(v), prettystr(table_b[v]) )\n        end\n    end\n\n    if #keysCommon ~= 0 then\n        table.insert( result, 'Items common to A and B:')\n        for k,v in sortedPairs( keysCommon ) do\n            extendWithStrFmt( result, '  = A and B [%s]: %s', keytostring(v), prettystr(table_a[v]) )\n        end\n    end    \n\n    return true, table.concat( result, '\\n')\n    ]]\nend\nM.private.mismatchFormattingMapping = mismatchFormattingMapping\n\nlocal function mismatchFormattingPureList( table_a, table_b, margin )\n    --[[\n    Prepares a nice error message when comparing tables which are lists, performing a deeper \n    analysis.\n\n    margin is supplied only for almost equality\n\n    Returns: {success, result}\n    * success: false if deep analysis could not be performed \n               in this case, just use standard assertion message\n    * result: if success is true, a multi-line string with deep analysis of the two lists\n    ]]\n    local result, descrTa, descrTb = {}, getTaTbDescr()\n\n    local len_a, len_b, refa, refb = #table_a, #table_b, '', ''\n    if M.PRINT_TABLE_REF_IN_ERROR_MSG then\n        refa, refb = string.format( '<%s> ', M.private.table_ref(table_a)), string.format('<%s> ', M.private.table_ref(table_b) )\n    end\n    local longest, shortest = math.max(len_a, len_b), math.min(len_a, len_b)\n    local deltalv  = longest - shortest\n\n    local commonUntil = shortest\n    for i = 1, shortest do\n        if not M.private.is_table_equals(table_a[i], table_b[i], margin) then\n            commonUntil = i - 1\n            break\n        end\n    end\n\n    local commonBackTo = shortest - 1\n    for i = 0, shortest - 1 do\n        if not M.private.is_table_equals(table_a[len_a-i], table_b[len_b-i], margin) then\n            commonBackTo = i - 1\n            break\n        end\n    end\n\n\n    table.insert( result, 'List difference analysis:' )    \n    if len_a == len_b then\n        -- TODO: handle expected/actual naming\n        extendWithStrFmt( result, '* lists %sA (%s) and %sB (%s) have the same size', refa, descrTa, refb, descrTb )\n    else \n        extendWithStrFmt( result, '* list sizes differ: list %sA (%s) has %d items, list %sB (%s) has %d items', refa, descrTa, len_a, refb, descrTb, len_b )\n    end\n\n    extendWithStrFmt( result, '* lists A and B start differing at index %d', commonUntil+1 ) \n    if commonBackTo >= 0 then\n        if deltalv > 0 then\n            extendWithStrFmt( result, '* lists A and B are equal again from index %d for A, %d for B', len_a-commonBackTo, len_b-commonBackTo )\n        else\n            extendWithStrFmt( result, '* lists A and B are equal again from index %d', len_a-commonBackTo )\n        end\n    end\n\n    local function insertABValue(ai, bi)\n        bi = bi or ai\n        if M.private.is_table_equals( table_a[ai], table_b[bi], margin) then\n            return extendWithStrFmt( result, '  = A[%d], B[%d]: %s', ai, bi, prettystr(table_a[ai]) )\n        else\n            extendWithStrFmt( result, '  - A[%d]: %s', ai, prettystr(table_a[ai]))\n            extendWithStrFmt( result, '  + B[%d]: %s', bi, prettystr(table_b[bi]))\n        end\n    end\n\n    -- common parts to list A & B, at the beginning\n    if commonUntil > 0 then\n        table.insert( result, '* Common parts:' )\n        for i = 1, commonUntil do\n            insertABValue( i )\n        end\n    end\n\n    -- diffing parts to list A & B\n    if commonUntil < shortest - commonBackTo - 1 then\n        table.insert( result, '* Differing parts:' )\n        for i = commonUntil + 1, shortest - commonBackTo - 1 do\n            insertABValue( i )\n        end\n    end\n\n    -- display indexes of one list, with no match on other list\n    if shortest - commonBackTo <= longest - commonBackTo - 1 then\n        table.insert( result, '* Present only in one list:' )\n        for i = shortest - commonBackTo, longest - commonBackTo - 1 do\n            if len_a > len_b then\n                extendWithStrFmt( result, '  - A[%d]: %s', i, prettystr(table_a[i]) )\n                -- table.insert( result, '+ (no matching B index)')\n            else\n                -- table.insert( result, '- no matching A index')\n                extendWithStrFmt( result, '  + B[%d]: %s', i, prettystr(table_b[i]) )\n            end\n        end\n    end\n\n    -- common parts to list A & B, at the end\n    if commonBackTo >= 0 then\n        table.insert( result, '* Common parts at the end of the lists' )\n        for i = longest - commonBackTo, longest do\n            if len_a > len_b then\n                insertABValue( i, i-deltalv )\n            else\n                insertABValue( i-deltalv, i )\n            end\n        end\n    end\n\n    return true, table.concat( result, '\\n')\nend\nM.private.mismatchFormattingPureList = mismatchFormattingPureList\n\nlocal function prettystrPairs(value1, value2, suffix_a, suffix_b)\n    --[[\n    This function helps with the recurring task of constructing the \"expected\n    vs. actual\" error messages. It takes two arbitrary values and formats\n    corresponding strings with prettystr().\n\n    To keep the (possibly complex) output more readable in case the resulting\n    strings contain line breaks, they get automatically prefixed with additional\n    newlines. Both suffixes are optional (default to empty strings), and get\n    appended to the \"value1\" string. \"suffix_a\" is used if line breaks were\n    encountered, \"suffix_b\" otherwise.\n\n    Returns the two formatted strings (including padding/newlines).\n    ]]\n    local str1, str2 = prettystr(value1), prettystr(value2)\n    if hasNewLine(str1) or hasNewLine(str2) then\n        -- line break(s) detected, add padding\n        return \"\\n\" .. str1 .. (suffix_a or \"\"), \"\\n\" .. str2\n    end\n    return str1 .. (suffix_b or \"\"), str2\nend\nM.private.prettystrPairs = prettystrPairs\n\nlocal UNKNOWN_REF = 'table 00-unknown ref'\nlocal ref_generator = { value=1, [UNKNOWN_REF]=0 }\n\nlocal function table_ref( t )\n    -- return the default tostring() for tables, with the table ID, even if the table has a metatable\n    -- with the __tostring converter\n    local ref = ''\n    local mt = getmetatable( t )\n    if mt == nil then\n        ref = tostring(t)\n    else\n        local success, result\n        success, result = pcall(setmetatable, t, nil)\n        if not success then\n            -- protected table, if __tostring is defined, we can\n            -- not get the reference. And we can not know in advance.\n            ref = tostring(t) \n            if not ref:match( 'table: 0?x?[%x]+' ) then\n                return UNKNOWN_REF\n            end\n        else\n            ref = tostring(t)\n            setmetatable( t, mt )\n        end\n    end\n    -- strip the \"table: \" part\n    ref = ref:sub(8)\n    if ref ~= UNKNOWN_REF and ref_generator[ref] == nil then\n        -- Create a new reference number\n        ref_generator[ref] = ref_generator.value\n        ref_generator.value = ref_generator.value+1\n    end\n    if M.PRINT_TABLE_REF_IN_ERROR_MSG then\n        return string.format('table %02d-%s', ref_generator[ref], ref)\n    else\n        return string.format('table %02d', ref_generator[ref])\n    end\nend\nM.private.table_ref = table_ref\n\nlocal TABLE_TOSTRING_SEP = \", \"\nlocal TABLE_TOSTRING_SEP_LEN = string.len(TABLE_TOSTRING_SEP)\n\nlocal function _table_tostring( tbl, indentLevel, printTableRefs, cycleDetectTable )\n    printTableRefs = printTableRefs or M.PRINT_TABLE_REF_IN_ERROR_MSG\n    cycleDetectTable = cycleDetectTable or {}\n    cycleDetectTable[tbl] = true\n\n    local result, dispOnMultLines = {}, false\n\n    -- like prettystr but do not enclose with \"\" if the string is just alphanumerical\n    -- this is better for displaying table keys who are often simple strings\n    local function keytostring(k)\n        if \"string\" == type(k) and k:match(\"^[_%a][_%w]*$\") then\n            return k\n        end\n        return prettystr_sub(k, indentLevel+1, printTableRefs, cycleDetectTable)\n    end\n\n    local mt = getmetatable( tbl )\n\n    if mt and mt.__tostring then\n        -- if table has a __tostring() function in its metatable, use it to display the table\n        -- else, compute a regular table\n        result = tostring(tbl)\n        if type(result) ~= 'string' then\n            return string.format( '<invalid tostring() result: \"%s\" >', prettystr(result) )\n        end\n        result = strsplit( '\\n', result )\n        return M.private._table_tostring_format_multiline_string( result, indentLevel )\n\n    else\n        -- no metatable, compute the table representation\n\n        local entry, count, seq_index = nil, 0, 1\n        for k, v in sortedPairs( tbl ) do\n\n            -- key part\n            if k == seq_index then\n                -- for the sequential part of tables, we'll skip the \"<key>=\" output\n                entry = ''\n                seq_index = seq_index + 1\n            elseif cycleDetectTable[k] then\n                -- recursion in the key detected\n                cycleDetectTable.detected = true\n                entry = \"<\"..table_ref(k)..\">=\"\n            else\n                entry = keytostring(k) .. \"=\"\n            end\n\n            -- value part \n            if cycleDetectTable[v] then\n                -- recursion in the value detected!\n                cycleDetectTable.detected = true\n                entry = entry .. \"<\"..table_ref(v)..\">\"\n            else\n                entry = entry ..\n                    prettystr_sub( v, indentLevel+1, printTableRefs, cycleDetectTable )\n            end\n            count = count + 1\n            result[count] = entry\n        end\n        return M.private._table_tostring_format_result( tbl, result, indentLevel, printTableRefs )\n    end\n\nend\nM.private._table_tostring = _table_tostring -- prettystr_sub() needs it\n\nlocal function _table_tostring_format_multiline_string( tbl_str, indentLevel )\n    local indentString = '\\n'..string.rep(\"    \", indentLevel - 1)\n    return table.concat( tbl_str, indentString )\n\nend\nM.private._table_tostring_format_multiline_string = _table_tostring_format_multiline_string\n\n\nlocal function _table_tostring_format_result( tbl, result, indentLevel, printTableRefs )\n    -- final function called in _table_to_string() to format the resulting list of \n    -- string describing the table.\n\n    local dispOnMultLines = false\n\n    -- set dispOnMultLines to true if the maximum LINE_LENGTH would be exceeded with the values\n    local totalLength = 0\n    for k, v in ipairs( result ) do\n        totalLength = totalLength + string.len( v )\n        if totalLength >= M.LINE_LENGTH then\n            dispOnMultLines = true\n            break\n        end\n    end\n\n    -- set dispOnMultLines to true if the max LINE_LENGTH would be exceeded\n    -- with the values and the separators.\n    if not dispOnMultLines then\n        -- adjust with length of separator(s):\n        -- two items need 1 sep, three items two seps, ... plus len of '{}'\n        if #result > 0 then\n            totalLength = totalLength + TABLE_TOSTRING_SEP_LEN * (#result - 1)\n        end\n        dispOnMultLines = (totalLength + 2 >= M.LINE_LENGTH)\n    end\n\n    -- now reformat the result table (currently holding element strings)\n    if dispOnMultLines then\n        local indentString = string.rep(\"    \", indentLevel - 1)\n        result = {  \n                    \"{\\n    \", \n                    indentString,\n                    table.concat(result, \",\\n    \" .. indentString), \n                    \"\\n\",\n                    indentString, \n                    \"}\"\n                }\n    else\n        result = {\"{\", table.concat(result, TABLE_TOSTRING_SEP), \"}\"}\n    end\n    if printTableRefs then\n        table.insert(result, 1, \"<\"..table_ref(tbl)..\"> \") -- prepend table ref\n    end\n    return table.concat(result)\nend\nM.private._table_tostring_format_result = _table_tostring_format_result -- prettystr_sub() needs it\n\nlocal function table_findkeyof(t, element)\n    -- Return the key k of the given element in table t, so that t[k] == element\n    -- (or `nil` if element is not present within t). Note that we use our\n    -- 'general' is_equal comparison for matching, so this function should\n    -- handle table-type elements gracefully and consistently.\n    if type(t) == \"table\" then\n        for k, v in pairs(t) do\n            if M.private.is_table_equals(v, element) then\n                return k\n            end\n        end\n    end\n    return nil\nend\n\nlocal function _is_table_items_equals(actual, expected )\n    local type_a, type_e = type(actual), type(expected)\n\n    if type_a ~= type_e then\n        return false\n\n    elseif (type_a == 'table') --[[and (type_e == 'table')]] then\n        for k, v in pairs(actual) do\n            if table_findkeyof(expected, v) == nil then\n                return false -- v not contained in expected\n            end\n        end\n        for k, v in pairs(expected) do\n            if table_findkeyof(actual, v) == nil then\n                return false -- v not contained in actual\n            end\n        end\n        return true\n\n    elseif actual ~= expected then\n        return false\n    end\n\n    return true\nend\n\n--[[\nThis is a specialized metatable to help with the bookkeeping of recursions\nin _is_table_equals(). It provides an __index table that implements utility\nfunctions for easier management of the table. The \"cached\" method queries\nthe state of a specific (actual,expected) pair; and the \"store\" method sets\nthis state to the given value. The state of pairs not \"seen\" / visited is\nassumed to be `nil`.\n]]\nlocal _recursion_cache_MT = {\n    __index = {\n        -- Return the cached value for an (actual,expected) pair (or `nil`)\n        cached = function(t, actual, expected)\n            local subtable = t[actual] or {}\n            return subtable[expected]\n        end,\n\n        -- Store cached value for a specific (actual,expected) pair.\n        -- Returns the value, so it's easy to use for a \"tailcall\" (return ...).\n        store = function(t, actual, expected, value, asymmetric)\n            local subtable = t[actual]\n            if not subtable then\n                subtable = {}\n                t[actual] = subtable\n            end\n            subtable[expected] = value\n\n            -- Unless explicitly marked \"asymmetric\": Consider the recursion\n            -- on (expected,actual) to be equivalent to (actual,expected) by\n            -- default, and thus cache the value for both.\n            if not asymmetric then\n                t:store(expected, actual, value, true)\n            end\n\n            return value\n        end\n    }\n}\n\nlocal function _is_table_equals(actual, expected, cycleDetectTable, marginForAlmostEqual)\n    --[[Returns true if both table are equal.\n\n    If argument marginForAlmostEqual is suppied, number comparison is done using alomstEqual instead \n    of strict equality.\n\n    cycleDetectTable is an internal argument used during recursion on tables.\n    ]]\n    --print('_is_table_equals( \\n     '..prettystr(actual)..'\\n      , '..prettystr(expected)..\n    --                        '\\n     , '..prettystr(cycleDetectTable)..'\\n    , '..prettystr(marginForAlmostEqual)..' )')\n\n    local type_a, type_e = type(actual), type(expected)\n\n    if type_a ~= type_e then\n        return false -- different types won't match\n    end\n\n    if type_a == 'number' then\n        if marginForAlmostEqual ~= nil then\n            return M.almostEquals(actual, expected, marginForAlmostEqual)\n        else\n            return actual == expected\n        end\n    elseif type_a ~= 'table' then\n        -- other types compare directly\n        return actual == expected\n    end\n\n    cycleDetectTable = cycleDetectTable or { actual={}, expected={} }\n    if cycleDetectTable.actual[ actual ] then\n        -- oh, we hit a cycle in actual\n        if cycleDetectTable.expected[ expected ] then\n            -- uh, we hit a cycle at the same time in expected\n            -- so the two tables have similar structure\n            return true\n        end\n\n        -- cycle was hit only in actual, the structure differs from expected\n        return false\n    end\n\n    if cycleDetectTable.expected[ expected ] then\n        -- no cycle in actual, but cycle in expected\n        -- the structure differ\n        return false\n    end\n\n    -- at this point, no table cycle detected, we are\n    -- seeing this table for the first time\n\n    -- mark the cycle detection\n    cycleDetectTable.actual[ actual ] = true\n    cycleDetectTable.expected[ expected ] = true\n\n\n    local actualKeysMatched = {}\n    for k, v in pairs(actual) do\n        actualKeysMatched[k] = true -- Keep track of matched keys\n        if not _is_table_equals(v, expected[k], cycleDetectTable, marginForAlmostEqual) then\n            -- table differs on this key\n            -- clear the cycle detection before returning\n            cycleDetectTable.actual[ actual ] = nil\n            cycleDetectTable.expected[ expected ] = nil\n            return false\n        end\n    end\n\n    for k, v in pairs(expected) do\n        if not actualKeysMatched[k] then\n            -- Found a key that we did not see in \"actual\" -> mismatch\n            -- clear the cycle detection before returning\n            cycleDetectTable.actual[ actual ] = nil\n            cycleDetectTable.expected[ expected ] = nil\n            return false\n        end\n        -- Otherwise actual[k] was already matched against v = expected[k].\n    end\n\n    -- all key match, we have a match !\n    cycleDetectTable.actual[ actual ] = nil\n    cycleDetectTable.expected[ expected ] = nil\n    return true\nend\nM.private._is_table_equals = _is_table_equals\n\nlocal function failure(main_msg, extra_msg_or_nil, level)\n    -- raise an error indicating a test failure\n    -- for error() compatibility we adjust \"level\" here (by +1), to report the\n    -- calling context\n    local msg\n    if type(extra_msg_or_nil) == 'string' and extra_msg_or_nil:len() > 0 then\n        msg = extra_msg_or_nil .. '\\n' .. main_msg\n    else\n        msg = main_msg\n    end\n    error(M.FAILURE_PREFIX .. msg, (level or 1) + 1 + M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE)\nend\n\nlocal function is_table_equals(actual, expected, marginForAlmostEqual)\n    return _is_table_equals(actual, expected, nil, marginForAlmostEqual)\nend\nM.private.is_table_equals = is_table_equals\n\nlocal function fail_fmt(level, extra_msg_or_nil, ...)\n     -- failure with printf-style formatted message and given error level\n    failure(string.format(...), extra_msg_or_nil, (level or 1) + 1)\nend\nM.private.fail_fmt = fail_fmt\n\nlocal function error_fmt(level, ...)\n     -- printf-style error()\n    error(string.format(...), (level or 1) + 1 + M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE)\nend\nM.private.error_fmt = error_fmt\n\n----------------------------------------------------------------\n--\n--                     assertions\n--\n----------------------------------------------------------------\n\nlocal function errorMsgEquality(actual, expected, doDeepAnalysis, margin)\n    -- margin is supplied only for almost equal verification\n\n    if not M.ORDER_ACTUAL_EXPECTED then\n        expected, actual = actual, expected\n    end\n    if type(expected) == 'string' or type(expected) == 'table' then\n        local strExpected, strActual = prettystrPairs(expected, actual)\n        local result = string.format(\"expected: %s\\nactual: %s\", strExpected, strActual)\n        if margin then\n            result = result .. '\\nwere not equal by the margin of: '..prettystr(margin)\n        end\n\n        -- extend with mismatch analysis if possible:\n        local success, mismatchResult\n        success, mismatchResult = tryMismatchFormatting( actual, expected, doDeepAnalysis, margin )\n        if success then \n            result = table.concat( { result, mismatchResult }, '\\n' )\n        end\n        return result\n    end\n    return string.format(\"expected: %s, actual: %s\",\n                         prettystr(expected), prettystr(actual))\nend\n\nfunction M.assertError(f, ...)\n    -- assert that calling f with the arguments will raise an error\n    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error\n    if pcall( f, ... ) then\n        failure( \"Expected an error when calling function but no error generated\", nil, 2 )\n    end\nend\n\nfunction M.fail( msg )\n    -- stops a test due to a failure\n    failure( msg, nil, 2 )\nend\n\nfunction M.failIf( cond, msg )\n    -- Fails a test with \"msg\" if condition is true\n    if cond then\n        failure( msg, nil, 2 )\n    end\nend\n\nfunction M.skip(msg)\n    -- skip a running test\n    error_fmt(2, M.SKIP_PREFIX .. msg)\nend\n\nfunction M.skipIf( cond, msg )\n    -- skip a running test if condition is met\n    if cond then\n        error_fmt(2, M.SKIP_PREFIX .. msg)\n    end\nend\n\nfunction M.runOnlyIf( cond, msg )\n    -- continue a running test if condition is met, else skip it\n    if not cond then\n        error_fmt(2, M.SKIP_PREFIX .. prettystr(msg))\n    end\nend\n\nfunction M.success()\n    -- stops a test with a success\n    error_fmt(2, M.SUCCESS_PREFIX)\nend\n\nfunction M.successIf( cond )\n    -- stops a test with a success if condition is met\n    if cond then\n        error_fmt(2, M.SUCCESS_PREFIX)\n    end\nend\n\n\n------------------------------------------------------------------\n--                  Equality assertions\n------------------------------------------------------------------\n\nfunction M.assertEquals(actual, expected, extra_msg_or_nil, doDeepAnalysis)\n    if type(actual) == 'table' and type(expected) == 'table' then\n        if not is_table_equals(actual, expected) then\n            failure( errorMsgEquality(actual, expected, doDeepAnalysis), extra_msg_or_nil, 2 )\n        end\n    elseif type(actual) ~= type(expected) then\n        failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 )\n    elseif actual ~= expected then\n        failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 )\n    end\nend\n\nfunction M.almostEquals( actual, expected, margin )\n    if type(actual) ~= 'number' or type(expected) ~= 'number' or type(margin) ~= 'number' then\n        error_fmt(3, 'almostEquals: must supply only number arguments.\\nArguments supplied: %s, %s, %s',\n            prettystr(actual), prettystr(expected), prettystr(margin))\n    end\n    if margin < 0 then\n        error_fmt(3, 'almostEquals: margin must not be negative, current value is ' .. margin)\n    end\n    return math.abs(expected - actual) <= margin\nend\n\nfunction M.assertAlmostEquals( actual, expected, margin, extra_msg_or_nil )\n    -- check that two floats are close by margin\n    margin = margin or M.EPS\n    if type(margin) ~= 'number' then\n        error_fmt(2, 'almostEquals: margin must be a number, not %s', prettystr(margin))\n    end\n\n    if type(actual) == 'table' and type(expected) == 'table' then\n        -- handle almost equals for table\n        if not is_table_equals(actual, expected, margin) then\n            failure( errorMsgEquality(actual, expected, nil, margin), extra_msg_or_nil, 2 )\n        end\n    elseif type(actual) == 'number' and type(expected) == 'number' and type(margin) == 'number' then\n        if not M.almostEquals(actual, expected, margin) then\n            if not M.ORDER_ACTUAL_EXPECTED then\n                expected, actual = actual, expected\n            end\n            local delta = math.abs(actual - expected) \n            fail_fmt(2, extra_msg_or_nil, 'Values are not almost equal\\n' ..\n                        'Actual: %s, expected: %s, delta %s above margin of %s',\n                        actual, expected, delta, margin)\n        end\n    else\n        error_fmt(3, 'almostEquals: must supply only number or table arguments.\\nArguments supplied: %s, %s, %s',\n            prettystr(actual), prettystr(expected), prettystr(margin))\n    end\nend\n\nfunction M.assertNotEquals(actual, expected, extra_msg_or_nil)\n    if type(actual) ~= type(expected) then\n        return\n    end\n\n    if type(actual) == 'table' and type(expected) == 'table' then\n        if not is_table_equals(actual, expected) then\n            return\n        end\n    elseif actual ~= expected then\n        return\n    end\n    fail_fmt(2, extra_msg_or_nil, 'Received the not expected value: %s', prettystr(actual))\nend\n\nfunction M.assertNotAlmostEquals( actual, expected, margin, extra_msg_or_nil )\n    -- check that two floats are not close by margin\n    margin = margin or M.EPS\n    if M.almostEquals(actual, expected, margin) then\n        if not M.ORDER_ACTUAL_EXPECTED then\n            expected, actual = actual, expected\n        end\n        local delta = math.abs(actual - expected)\n        fail_fmt(2, extra_msg_or_nil, 'Values are almost equal\\nActual: %s, expected: %s' ..\n                    ', delta %s below margin of %s',\n                    actual, expected, delta, margin)\n    end\nend\n\nfunction M.assertItemsEquals(actual, expected, extra_msg_or_nil)\n    -- checks that the items of table expected\n    -- are contained in table actual. Warning, this function\n    -- is at least O(n^2)\n    if not _is_table_items_equals(actual, expected ) then\n        expected, actual = prettystrPairs(expected, actual)\n        fail_fmt(2, extra_msg_or_nil, 'Content of the tables are not identical:\\nExpected: %s\\nActual: %s',\n                 expected, actual)\n    end\nend\n\n------------------------------------------------------------------\n--                  String assertion\n------------------------------------------------------------------\n\nfunction M.assertStrContains( str, sub, isPattern, extra_msg_or_nil )\n    -- this relies on lua string.find function\n    -- a string always contains the empty string\n    -- assert( type(str) == 'string', 'Argument 1 of assertStrContains() should be a string.' ) )\n    -- assert( type(sub) == 'string', 'Argument 2 of assertStrContains() should be a string.' ) )\n    if not string.find(str, sub, 1, not isPattern) then\n        sub, str = prettystrPairs(sub, str, '\\n')\n        fail_fmt(2, extra_msg_or_nil, 'Could not find %s %s in string %s',\n                 isPattern and 'pattern' or 'substring', sub, str)\n    end\nend\n\nfunction M.assertStrIContains( str, sub, extra_msg_or_nil )\n    -- this relies on lua string.find function\n    -- a string always contains the empty string\n    if not string.find(str:lower(), sub:lower(), 1, true) then\n        sub, str = prettystrPairs(sub, str, '\\n')\n        fail_fmt(2, extra_msg_or_nil, 'Could not find (case insensitively) substring %s in string %s',\n                 sub, str)\n    end\nend\n\nfunction M.assertNotStrContains( str, sub, isPattern, extra_msg_or_nil )\n    -- this relies on lua string.find function\n    -- a string always contains the empty string\n    if string.find(str, sub, 1, not isPattern) then\n        sub, str = prettystrPairs(sub, str, '\\n')\n        fail_fmt(2, extra_msg_or_nil, 'Found the not expected %s %s in string %s',\n                 isPattern and 'pattern' or 'substring', sub, str)\n    end\nend\n\nfunction M.assertNotStrIContains( str, sub, extra_msg_or_nil )\n    -- this relies on lua string.find function\n    -- a string always contains the empty string\n    if string.find(str:lower(), sub:lower(), 1, true) then\n        sub, str = prettystrPairs(sub, str, '\\n')\n        fail_fmt(2, extra_msg_or_nil, 'Found (case insensitively) the not expected substring %s in string %s',\n                 sub, str)\n    end\nend\n\nfunction M.assertStrMatches( str, pattern, start, final, extra_msg_or_nil )\n    -- Verify a full match for the string\n    if not strMatch( str, pattern, start, final ) then\n        pattern, str = prettystrPairs(pattern, str, '\\n')\n        fail_fmt(2, extra_msg_or_nil, 'Could not match pattern %s with string %s',\n                 pattern, str)\n    end\nend\n\nlocal function _assertErrorMsgEquals( stripFileAndLine, expectedMsg, func, ... )\n    local no_error, error_msg = pcall( func, ... )\n    if no_error then\n        failure( 'No error generated when calling function but expected error: '..M.prettystr(expectedMsg), nil, 3 )\n    end\n    if type(expectedMsg) == \"string\" and type(error_msg) ~= \"string\" then\n        -- table are converted to string automatically\n        error_msg = tostring(error_msg)\n    end\n    local differ = false\n    if stripFileAndLine then\n        if error_msg:gsub(\"^.+:%d+: \", \"\") ~= expectedMsg then\n            differ = true\n        end\n    else\n        if error_msg ~= expectedMsg then\n            local tr = type(error_msg)\n            local te = type(expectedMsg)\n            if te == 'table' then\n                if tr ~= 'table' then\n                    differ = true\n                else\n                     local ok = pcall(M.assertItemsEquals, error_msg, expectedMsg)\n                     if not ok then\n                         differ = true\n                     end\n                end\n            else\n               differ = true\n            end\n        end\n    end\n\n    if differ then\n        error_msg, expectedMsg = prettystrPairs(error_msg, expectedMsg)\n        fail_fmt(3, nil, 'Error message expected: %s\\nError message received: %s\\n',\n                 expectedMsg, error_msg)\n    end\nend\n\nfunction M.assertErrorMsgEquals( expectedMsg, func, ... )\n    -- assert that calling f with the arguments will raise an error\n    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error\n    _assertErrorMsgEquals(false, expectedMsg, func, ...)\nend\n\nfunction M.assertErrorMsgContentEquals(expectedMsg, func, ...)\n     _assertErrorMsgEquals(true, expectedMsg, func, ...)\nend\n\nfunction M.assertErrorMsgContains( partialMsg, func, ... )\n    -- assert that calling f with the arguments will raise an error\n    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error\n    local no_error, error_msg = pcall( func, ... )\n    if no_error then\n        failure( 'No error generated when calling function but expected error containing: '..prettystr(partialMsg), nil, 2 )\n    end\n    if type(error_msg) ~= \"string\" then\n        error_msg = tostring(error_msg)\n    end\n    if not string.find( error_msg, partialMsg, nil, true ) then\n        error_msg, partialMsg = prettystrPairs(error_msg, partialMsg)\n        fail_fmt(2, nil, 'Error message does not contain: %s\\nError message received: %s\\n',\n                 partialMsg, error_msg)\n    end\nend\n\nfunction M.assertErrorMsgMatches( expectedMsg, func, ... )\n    -- assert that calling f with the arguments will raise an error\n    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error\n    local no_error, error_msg = pcall( func, ... )\n    if no_error then\n        failure( 'No error generated when calling function but expected error matching: \"'..expectedMsg..'\"', nil, 2 )\n    end\n    if type(error_msg) ~= \"string\" then\n        error_msg = tostring(error_msg)\n    end\n    if not strMatch( error_msg, expectedMsg ) then\n        expectedMsg, error_msg = prettystrPairs(expectedMsg, error_msg)\n        fail_fmt(2, nil, 'Error message does not match pattern: %s\\nError message received: %s\\n',\n                 expectedMsg, error_msg)\n    end\nend\n\n------------------------------------------------------------------\n--              Type assertions\n------------------------------------------------------------------\n\nfunction M.assertEvalToTrue(value, extra_msg_or_nil)\n    if not value then\n        failure(\"expected: a value evaluating to true, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertEvalToFalse(value, extra_msg_or_nil)\n    if value then\n        failure(\"expected: false or nil, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsTrue(value, extra_msg_or_nil)\n    if value ~= true then\n        failure(\"expected: true, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsTrue(value, extra_msg_or_nil)\n    if value == true then\n        failure(\"expected: not true, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsFalse(value, extra_msg_or_nil)\n    if value ~= false then\n        failure(\"expected: false, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsFalse(value, extra_msg_or_nil)\n    if value == false then\n        failure(\"expected: not false, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsNil(value, extra_msg_or_nil)\n    if value ~= nil then\n        failure(\"expected: nil, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsNil(value, extra_msg_or_nil)\n    if value == nil then\n        failure(\"expected: not nil, actual: nil\", extra_msg_or_nil, 2)\n    end\nend\n\n--[[\nAdd type assertion functions to the module table M. Each of these functions\ntakes a single parameter \"value\", and checks that its Lua type matches the\nexpected string (derived from the function name):\n\nM.assertIsXxx(value) -> ensure that type(value) conforms to \"xxx\"\n]]\nfor _, funcName in ipairs(\n    {'assertIsNumber', 'assertIsString', 'assertIsTable', 'assertIsBoolean',\n     'assertIsFunction', 'assertIsUserdata', 'assertIsThread'}\n) do\n    local typeExpected = funcName:match(\"^assertIs([A-Z]%a*)$\")\n    -- Lua type() always returns lowercase, also make sure the match() succeeded\n    typeExpected = typeExpected and typeExpected:lower()\n                   or error(\"bad function name '\"..funcName..\"' for type assertion\")\n\n    M[funcName] = function(value, extra_msg_or_nil)\n        if type(value) ~= typeExpected then\n            if type(value) == 'nil' then\n                fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: nil',\n                         typeExpected, type(value), prettystrPairs(value))\n            else\n                fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: type %s, value %s',\n                         typeExpected, type(value), prettystrPairs(value))\n            end\n        end\n    end\nend\n\n--[[\nAdd shortcuts for verifying type of a variable, without failure (luaunit v2 compatibility)\nM.isXxx(value) -> returns true if type(value) conforms to \"xxx\"\n]]\nfor _, typeExpected in ipairs(\n    {'Number', 'String', 'Table', 'Boolean',\n     'Function', 'Userdata', 'Thread', 'Nil' }\n) do\n    local typeExpectedLower = typeExpected:lower()\n    local isType = function(value)\n        return (type(value) == typeExpectedLower)\n    end\n    M['is'..typeExpected] = isType\n    M['is_'..typeExpectedLower] = isType\nend\n\n--[[\nAdd non-type assertion functions to the module table M. Each of these functions\ntakes a single parameter \"value\", and checks that its Lua type differs from the\nexpected string (derived from the function name):\n\nM.assertNotIsXxx(value) -> ensure that type(value) is not \"xxx\"\n]]\nfor _, funcName in ipairs(\n    {'assertNotIsNumber', 'assertNotIsString', 'assertNotIsTable', 'assertNotIsBoolean',\n     'assertNotIsFunction', 'assertNotIsUserdata', 'assertNotIsThread'}\n) do\n    local typeUnexpected = funcName:match(\"^assertNotIs([A-Z]%a*)$\")\n    -- Lua type() always returns lowercase, also make sure the match() succeeded\n    typeUnexpected = typeUnexpected and typeUnexpected:lower()\n                   or error(\"bad function name '\"..funcName..\"' for type assertion\")\n\n    M[funcName] = function(value, extra_msg_or_nil)\n        if type(value) == typeUnexpected then\n            fail_fmt(2, extra_msg_or_nil, 'expected: not a %s type, actual: value %s',\n                     typeUnexpected, prettystrPairs(value))\n        end\n    end\nend\n\nfunction M.assertIs(actual, expected, extra_msg_or_nil)\n    if actual ~= expected then\n        if not M.ORDER_ACTUAL_EXPECTED then\n            actual, expected = expected, actual\n        end\n        local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG\n        M.PRINT_TABLE_REF_IN_ERROR_MSG = true\n        expected, actual = prettystrPairs(expected, actual, '\\n', '')\n        M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg\n        fail_fmt(2, extra_msg_or_nil, 'expected and actual object should not be different\\nExpected: %s\\nReceived: %s',\n                 expected, actual)\n    end\nend\n\nfunction M.assertNotIs(actual, expected, extra_msg_or_nil)\n    if actual == expected then\n        local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG\n        M.PRINT_TABLE_REF_IN_ERROR_MSG = true\n        local s_expected\n        if not M.ORDER_ACTUAL_EXPECTED then\n            s_expected = prettystrPairs(actual)\n        else\n            s_expected = prettystrPairs(expected)\n        end\n        M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg\n        fail_fmt(2, extra_msg_or_nil, 'expected and actual object should be different: %s', s_expected )\n    end\nend\n\n\n------------------------------------------------------------------\n--              Scientific assertions\n------------------------------------------------------------------\n\n\nfunction M.assertIsNaN(value, extra_msg_or_nil)\n    if type(value) ~= \"number\" or value == value then\n        failure(\"expected: NaN, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsNaN(value, extra_msg_or_nil)\n    if type(value) == \"number\" and value ~= value then\n        failure(\"expected: not NaN, actual: NaN\", extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsInf(value, extra_msg_or_nil)\n    if type(value) ~= \"number\" or math.abs(value) ~= math.huge then\n        failure(\"expected: #Inf, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsPlusInf(value, extra_msg_or_nil)\n    if type(value) ~= \"number\" or value ~= math.huge then\n        failure(\"expected: #Inf, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsMinusInf(value, extra_msg_or_nil)\n    if type(value) ~= \"number\" or value ~= -math.huge then\n        failure(\"expected: -#Inf, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsPlusInf(value, extra_msg_or_nil)\n    if type(value) == \"number\" and value == math.huge then\n        failure(\"expected: not #Inf, actual: #Inf\", extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsMinusInf(value, extra_msg_or_nil)\n    if type(value) == \"number\" and value == -math.huge then\n        failure(\"expected: not -#Inf, actual: -#Inf\", extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsInf(value, extra_msg_or_nil)\n    if type(value) == \"number\" and math.abs(value) == math.huge then\n        failure(\"expected: not infinity, actual: \" .. prettystr(value), extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertIsPlusZero(value, extra_msg_or_nil)\n    if type(value) ~= 'number' or value ~= 0 then\n        failure(\"expected: +0.0, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    else if (1/value == -math.huge) then\n            -- more precise error diagnosis\n            failure(\"expected: +0.0, actual: -0.0\", extra_msg_or_nil, 2)\n        else if (1/value ~= math.huge) then\n                -- strange, case should have already been covered\n                failure(\"expected: +0.0, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n            end\n        end\n    end\nend\n\nfunction M.assertIsMinusZero(value, extra_msg_or_nil)\n    if type(value) ~= 'number' or value ~= 0 then\n        failure(\"expected: -0.0, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n    else if (1/value == math.huge) then\n            -- more precise error diagnosis\n            failure(\"expected: -0.0, actual: +0.0\", extra_msg_or_nil, 2)\n        else if (1/value ~= -math.huge) then\n                -- strange, case should have already been covered\n                failure(\"expected: -0.0, actual: \" ..prettystr(value), extra_msg_or_nil, 2)\n            end\n        end\n    end\nend\n\nfunction M.assertNotIsPlusZero(value, extra_msg_or_nil)\n    if type(value) == 'number' and (1/value == math.huge) then\n        failure(\"expected: not +0.0, actual: +0.0\", extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertNotIsMinusZero(value, extra_msg_or_nil)\n    if type(value) == 'number' and (1/value == -math.huge) then\n        failure(\"expected: not -0.0, actual: -0.0\", extra_msg_or_nil, 2)\n    end\nend\n\nfunction M.assertTableContains(t, expected, extra_msg_or_nil)\n    -- checks that table t contains the expected element\n    if table_findkeyof(t, expected) == nil then\n        t, expected = prettystrPairs(t, expected)\n        fail_fmt(2, extra_msg_or_nil, 'Table %s does NOT contain the expected element %s',\n                 t, expected)\n    end\nend\n\nfunction M.assertNotTableContains(t, expected, extra_msg_or_nil)\n    -- checks that table t doesn't contain the expected element\n    local k = table_findkeyof(t, expected)\n    if k ~= nil then\n        t, expected = prettystrPairs(t, expected)\n        fail_fmt(2, extra_msg_or_nil, 'Table %s DOES contain the unwanted element %s (at key %s)',\n                 t, expected, prettystr(k))\n    end\nend\n\n----------------------------------------------------------------\n--                     Compatibility layer\n----------------------------------------------------------------\n\n-- for compatibility with LuaUnit v2.x\nfunction M.wrapFunctions()\n    -- In LuaUnit version <= 2.1 , this function was necessary to include\n    -- a test function inside the global test suite. Nowadays, the functions\n    -- are simply run directly as part of the test discovery process.\n    -- so just do nothing !\n    io.stderr:write[[Use of WrapFunctions() is no longer needed.\nJust prefix your test function names with \"test\" or \"Test\" and they\nwill be picked up and run by LuaUnit.\n]]\nend\n\nlocal list_of_funcs = {\n    -- { official function name , alias }\n\n    -- general assertions\n    { 'assertEquals'            , 'assert_equals' },\n    { 'assertItemsEquals'       , 'assert_items_equals' },\n    { 'assertNotEquals'         , 'assert_not_equals' },\n    { 'assertAlmostEquals'      , 'assert_almost_equals' },\n    { 'assertNotAlmostEquals'   , 'assert_not_almost_equals' },\n    { 'assertEvalToTrue'        , 'assert_eval_to_true' },\n    { 'assertEvalToFalse'       , 'assert_eval_to_false' },\n    { 'assertStrContains'       , 'assert_str_contains' },\n    { 'assertStrIContains'      , 'assert_str_icontains' },\n    { 'assertNotStrContains'    , 'assert_not_str_contains' },\n    { 'assertNotStrIContains'   , 'assert_not_str_icontains' },\n    { 'assertStrMatches'        , 'assert_str_matches' },\n    { 'assertError'             , 'assert_error' },\n    { 'assertErrorMsgEquals'    , 'assert_error_msg_equals' },\n    { 'assertErrorMsgContains'  , 'assert_error_msg_contains' },\n    { 'assertErrorMsgMatches'   , 'assert_error_msg_matches' },\n    { 'assertErrorMsgContentEquals', 'assert_error_msg_content_equals' },\n    { 'assertIs'                , 'assert_is' },\n    { 'assertNotIs'             , 'assert_not_is' },\n    { 'assertTableContains'     , 'assert_table_contains' },\n    { 'assertNotTableContains'  , 'assert_not_table_contains' },\n    { 'wrapFunctions'           , 'WrapFunctions' },\n    { 'wrapFunctions'           , 'wrap_functions' },\n\n    -- type assertions: assertIsXXX -> assert_is_xxx\n    { 'assertIsNumber'          , 'assert_is_number' },\n    { 'assertIsString'          , 'assert_is_string' },\n    { 'assertIsTable'           , 'assert_is_table' },\n    { 'assertIsBoolean'         , 'assert_is_boolean' },\n    { 'assertIsNil'             , 'assert_is_nil' },\n    { 'assertIsTrue'            , 'assert_is_true' },\n    { 'assertIsFalse'           , 'assert_is_false' },\n    { 'assertIsNaN'             , 'assert_is_nan' },\n    { 'assertIsInf'             , 'assert_is_inf' },\n    { 'assertIsPlusInf'         , 'assert_is_plus_inf' },\n    { 'assertIsMinusInf'        , 'assert_is_minus_inf' },\n    { 'assertIsPlusZero'        , 'assert_is_plus_zero' },\n    { 'assertIsMinusZero'       , 'assert_is_minus_zero' },\n    { 'assertIsFunction'        , 'assert_is_function' },\n    { 'assertIsThread'          , 'assert_is_thread' },\n    { 'assertIsUserdata'        , 'assert_is_userdata' },\n\n    -- type assertions: assertIsXXX -> assertXxx\n    { 'assertIsNumber'          , 'assertNumber' },\n    { 'assertIsString'          , 'assertString' },\n    { 'assertIsTable'           , 'assertTable' },\n    { 'assertIsBoolean'         , 'assertBoolean' },\n    { 'assertIsNil'             , 'assertNil' },\n    { 'assertIsTrue'            , 'assertTrue' },\n    { 'assertIsFalse'           , 'assertFalse' },\n    { 'assertIsNaN'             , 'assertNaN' },\n    { 'assertIsInf'             , 'assertInf' },\n    { 'assertIsPlusInf'         , 'assertPlusInf' },\n    { 'assertIsMinusInf'        , 'assertMinusInf' },\n    { 'assertIsPlusZero'        , 'assertPlusZero' },\n    { 'assertIsMinusZero'       , 'assertMinusZero'},\n    { 'assertIsFunction'        , 'assertFunction' },\n    { 'assertIsThread'          , 'assertThread' },\n    { 'assertIsUserdata'        , 'assertUserdata' },\n\n    -- type assertions: assertIsXXX -> assert_xxx (luaunit v2 compat)\n    { 'assertIsNumber'          , 'assert_number' },\n    { 'assertIsString'          , 'assert_string' },\n    { 'assertIsTable'           , 'assert_table' },\n    { 'assertIsBoolean'         , 'assert_boolean' },\n    { 'assertIsNil'             , 'assert_nil' },\n    { 'assertIsTrue'            , 'assert_true' },\n    { 'assertIsFalse'           , 'assert_false' },\n    { 'assertIsNaN'             , 'assert_nan' },\n    { 'assertIsInf'             , 'assert_inf' },\n    { 'assertIsPlusInf'         , 'assert_plus_inf' },\n    { 'assertIsMinusInf'        , 'assert_minus_inf' },\n    { 'assertIsPlusZero'        , 'assert_plus_zero' },\n    { 'assertIsMinusZero'       , 'assert_minus_zero' },\n    { 'assertIsFunction'        , 'assert_function' },\n    { 'assertIsThread'          , 'assert_thread' },\n    { 'assertIsUserdata'        , 'assert_userdata' },\n\n    -- type assertions: assertNotIsXXX -> assert_not_is_xxx\n    { 'assertNotIsNumber'       , 'assert_not_is_number' },\n    { 'assertNotIsString'       , 'assert_not_is_string' },\n    { 'assertNotIsTable'        , 'assert_not_is_table' },\n    { 'assertNotIsBoolean'      , 'assert_not_is_boolean' },\n    { 'assertNotIsNil'          , 'assert_not_is_nil' },\n    { 'assertNotIsTrue'         , 'assert_not_is_true' },\n    { 'assertNotIsFalse'        , 'assert_not_is_false' },\n    { 'assertNotIsNaN'          , 'assert_not_is_nan' },\n    { 'assertNotIsInf'          , 'assert_not_is_inf' },\n    { 'assertNotIsPlusInf'      , 'assert_not_plus_inf' },\n    { 'assertNotIsMinusInf'     , 'assert_not_minus_inf' },\n    { 'assertNotIsPlusZero'     , 'assert_not_plus_zero' },\n    { 'assertNotIsMinusZero'    , 'assert_not_minus_zero' },\n    { 'assertNotIsFunction'     , 'assert_not_is_function' },\n    { 'assertNotIsThread'       , 'assert_not_is_thread' },\n    { 'assertNotIsUserdata'     , 'assert_not_is_userdata' },\n\n    -- type assertions: assertNotIsXXX -> assertNotXxx (luaunit v2 compat)\n    { 'assertNotIsNumber'       , 'assertNotNumber' },\n    { 'assertNotIsString'       , 'assertNotString' },\n    { 'assertNotIsTable'        , 'assertNotTable' },\n    { 'assertNotIsBoolean'      , 'assertNotBoolean' },\n    { 'assertNotIsNil'          , 'assertNotNil' },\n    { 'assertNotIsTrue'         , 'assertNotTrue' },\n    { 'assertNotIsFalse'        , 'assertNotFalse' },\n    { 'assertNotIsNaN'          , 'assertNotNaN' },\n    { 'assertNotIsInf'          , 'assertNotInf' },\n    { 'assertNotIsPlusInf'      , 'assertNotPlusInf' },\n    { 'assertNotIsMinusInf'     , 'assertNotMinusInf' },\n    { 'assertNotIsPlusZero'     , 'assertNotPlusZero' },\n    { 'assertNotIsMinusZero'    , 'assertNotMinusZero' },\n    { 'assertNotIsFunction'     , 'assertNotFunction' },\n    { 'assertNotIsThread'       , 'assertNotThread' },\n    { 'assertNotIsUserdata'     , 'assertNotUserdata' },\n\n    -- type assertions: assertNotIsXXX -> assert_not_xxx\n    { 'assertNotIsNumber'       , 'assert_not_number' },\n    { 'assertNotIsString'       , 'assert_not_string' },\n    { 'assertNotIsTable'        , 'assert_not_table' },\n    { 'assertNotIsBoolean'      , 'assert_not_boolean' },\n    { 'assertNotIsNil'          , 'assert_not_nil' },\n    { 'assertNotIsTrue'         , 'assert_not_true' },\n    { 'assertNotIsFalse'        , 'assert_not_false' },\n    { 'assertNotIsNaN'          , 'assert_not_nan' },\n    { 'assertNotIsInf'          , 'assert_not_inf' },\n    { 'assertNotIsPlusInf'      , 'assert_not_plus_inf' },\n    { 'assertNotIsMinusInf'     , 'assert_not_minus_inf' },\n    { 'assertNotIsPlusZero'     , 'assert_not_plus_zero' },\n    { 'assertNotIsMinusZero'    , 'assert_not_minus_zero' },\n    { 'assertNotIsFunction'     , 'assert_not_function' },\n    { 'assertNotIsThread'       , 'assert_not_thread' },\n    { 'assertNotIsUserdata'     , 'assert_not_userdata' },\n\n    -- all assertions with Coroutine duplicate Thread assertions\n    { 'assertIsThread'          , 'assertIsCoroutine' },\n    { 'assertIsThread'          , 'assertCoroutine' },\n    { 'assertIsThread'          , 'assert_is_coroutine' },\n    { 'assertIsThread'          , 'assert_coroutine' },\n    { 'assertNotIsThread'       , 'assertNotIsCoroutine' },\n    { 'assertNotIsThread'       , 'assertNotCoroutine' },\n    { 'assertNotIsThread'       , 'assert_not_is_coroutine' },\n    { 'assertNotIsThread'       , 'assert_not_coroutine' },\n}\n\n-- Create all aliases in M\nfor _,v in ipairs( list_of_funcs ) do\n    local funcname, alias = v[1], v[2]\n    M[alias] = M[funcname]\n\n    if EXPORT_ASSERT_TO_GLOBALS then\n        _G[funcname] = M[funcname]\n        _G[alias] = M[funcname]\n    end\nend\n\n----------------------------------------------------------------\n--\n--                     Outputters\n--\n----------------------------------------------------------------\n\n-- A common \"base\" class for outputters\n-- For concepts involved (class inheritance) see http://www.lua.org/pil/16.2.html\n\nlocal genericOutput = { __class__ = 'genericOutput' } -- class\nlocal genericOutput_MT = { __index = genericOutput } -- metatable\nM.genericOutput = genericOutput -- publish, so that custom classes may derive from it\n\nfunction genericOutput.new(runner, default_verbosity)\n    -- runner is the \"parent\" object controlling the output, usually a LuaUnit instance\n    local t = { runner = runner }\n    if runner then\n        t.result = runner.result\n        t.verbosity = runner.verbosity or default_verbosity\n        t.fname = runner.fname\n    else\n        t.verbosity = default_verbosity\n    end\n    return setmetatable( t, genericOutput_MT)\nend\n\n-- abstract (\"empty\") methods\nfunction genericOutput:startSuite() \n    -- Called once, when the suite is started\nend\n\nfunction genericOutput:startClass(className) \n    -- Called each time a new test class is started\nend\n\nfunction genericOutput:startTest(testName) \n    -- called each time a new test is started, right before the setUp()\n    -- the current test status node is already created and available in: self.result.currentNode\nend\n\nfunction genericOutput:updateStatus(node) \n    -- called with status failed or error as soon as the error/failure is encountered\n    -- this method is NOT called for a successful test because a test is marked as successful by default\n    -- and does not need to be updated\nend\n\nfunction genericOutput:endTest(node) \n    -- called when the test is finished, after the tearDown() method\nend\n\nfunction genericOutput:endClass() \n    -- called when executing the class is finished, before moving on to the next class of at the end of the test execution\nend\n\nfunction genericOutput:endSuite() \n    -- called at the end of the test suite execution\nend\n\n\n----------------------------------------------------------------\n--                     class TapOutput\n----------------------------------------------------------------\n\nlocal TapOutput = genericOutput.new() -- derived class\nlocal TapOutput_MT = { __index = TapOutput } -- metatable\nTapOutput.__class__ = 'TapOutput'\n\n    -- For a good reference for TAP format, check: http://testanything.org/tap-specification.html\n\n    function TapOutput.new(runner)\n        local t = genericOutput.new(runner, M.VERBOSITY_LOW)\n        return setmetatable( t, TapOutput_MT)\n    end\n    function TapOutput:startSuite()\n        print(\"1..\"..self.result.selectedCount)\n        print('# Started on '..self.result.startDate)\n    end\n    function TapOutput:startClass(className)\n        if className ~= '[TestFunctions]' then\n            print('# Starting class: '..className)\n        end\n    end\n\n    function TapOutput:updateStatus( node )\n        if node:isSkipped() then\n            io.stdout:write(\"ok \", self.result.currentTestNumber, \"\\t# SKIP \", node.msg, \"\\n\" )\n            return\n        end\n\n        io.stdout:write(\"not ok \", self.result.currentTestNumber, \"\\t\", node.testName, \"\\n\")\n        if self.verbosity > M.VERBOSITY_LOW then\n           print( prefixString( '#   ', node.msg ) )\n        end\n        if (node:isFailure() or node:isError()) and self.verbosity > M.VERBOSITY_DEFAULT then\n           print( prefixString( '#   ', node.stackTrace ) )\n        end\n    end\n\n    function TapOutput:endTest( node )\n        if node:isSuccess() then\n            io.stdout:write(\"ok     \", self.result.currentTestNumber, \"\\t\", node.testName, \"\\n\")\n        end\n    end\n\n    function TapOutput:endSuite()\n        print( '# '..M.LuaUnit.statusLine( self.result ) )\n        return self.result.notSuccessCount\n    end\n\n\n-- class TapOutput end\n\n----------------------------------------------------------------\n--                     class JUnitOutput\n----------------------------------------------------------------\n\n-- See directory junitxml for more information about the junit format\nlocal JUnitOutput = genericOutput.new() -- derived class\nlocal JUnitOutput_MT = { __index = JUnitOutput } -- metatable\nJUnitOutput.__class__ = 'JUnitOutput'\n\n    function JUnitOutput.new(runner)\n        local t = genericOutput.new(runner, M.VERBOSITY_LOW)\n        t.testList = {}\n        return setmetatable( t, JUnitOutput_MT )\n    end\n\n    function JUnitOutput:startSuite()\n        -- open xml file early to deal with errors\n        if self.fname == nil then\n            error('With Junit, an output filename must be supplied with --name!')\n        end\n        if string.sub(self.fname,-4) ~= '.xml' then\n            self.fname = self.fname..'.xml'\n        end\n        self.fd = io.open(self.fname, \"w\")\n        if self.fd == nil then\n            error(\"Could not open file for writing: \"..self.fname)\n        end\n\n        print('# XML output to '..self.fname)\n        print('# Started on '..self.result.startDate)\n    end\n    function JUnitOutput:startClass(className)\n        if className ~= '[TestFunctions]' then\n            print('# Starting class: '..className)\n        end\n    end\n    function JUnitOutput:startTest(testName)\n        print('# Starting test: '..testName)\n    end\n\n    function JUnitOutput:updateStatus( node )\n        if node:isFailure() then\n            print( '#   Failure: ' .. prefixString( '#   ', node.msg ):sub(4, nil) )\n            -- print('# ' .. node.stackTrace)\n        elseif node:isError() then\n            print( '#   Error: ' .. prefixString( '#   '  , node.msg ):sub(4, nil) )\n            -- print('# ' .. node.stackTrace)\n        end\n    end\n\n    function JUnitOutput:endSuite()\n        print( '# '..M.LuaUnit.statusLine(self.result))\n\n        -- XML file writing\n        self.fd:write('<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\\n')\n        self.fd:write('<testsuites>\\n')\n        self.fd:write(string.format(\n            '    <testsuite name=\"LuaUnit\" id=\"00001\" package=\"\" hostname=\"localhost\" tests=\"%d\" timestamp=\"%s\" time=\"%0.3f\" errors=\"%d\" failures=\"%d\" skipped=\"%d\">\\n',\n            self.result.runCount, self.result.startIsodate, self.result.duration, self.result.errorCount, self.result.failureCount, self.result.skippedCount ))\n        self.fd:write(\"        <properties>\\n\")\n        self.fd:write(string.format('            <property name=\"Lua Version\" value=\"%s\"/>\\n', _VERSION ) )\n        self.fd:write(string.format('            <property name=\"LuaUnit Version\" value=\"%s\"/>\\n', M.VERSION) )\n        -- XXX please include system name and version if possible\n        self.fd:write(\"        </properties>\\n\")\n\n        for i,node in ipairs(self.result.allTests) do\n            self.fd:write(string.format('        <testcase classname=\"%s\" name=\"%s\" time=\"%0.3f\">\\n',\n                node.className, node.testName, node.duration ) )\n            if node:isNotSuccess() then\n                self.fd:write(node:statusXML())\n            end\n            self.fd:write('        </testcase>\\n')\n        end\n\n        -- Next two lines are needed to validate junit ANT xsd, but really not useful in general:\n        self.fd:write('    <system-out/>\\n')\n        self.fd:write('    <system-err/>\\n')\n\n        self.fd:write('    </testsuite>\\n')\n        self.fd:write('</testsuites>\\n')\n        self.fd:close()\n        return self.result.notSuccessCount\n    end\n\n\n-- class TapOutput end\n\n----------------------------------------------------------------\n--                     class TextOutput\n----------------------------------------------------------------\n\n--[[    Example of other unit-tests suite text output\n\n-- Python Non verbose:\n\nFor each test: . or F or E\n\nIf some failed tests:\n    ==============\n    ERROR / FAILURE: TestName (testfile.testclass)\n    ---------\n    Stack trace\n\n\nthen --------------\nthen \"Ran x tests in 0.000s\"\nthen OK or FAILED (failures=1, error=1)\n\n-- Python Verbose:\ntestname (filename.classname) ... ok\ntestname (filename.classname) ... FAIL\ntestname (filename.classname) ... ERROR\n\nthen --------------\nthen \"Ran x tests in 0.000s\"\nthen OK or FAILED (failures=1, error=1)\n\n-- Ruby:\nStarted\n .\n Finished in 0.002695 seconds.\n\n 1 tests, 2 assertions, 0 failures, 0 errors\n\n-- Ruby:\n>> ruby tc_simple_number2.rb\nLoaded suite tc_simple_number2\nStarted\nF..\nFinished in 0.038617 seconds.\n\n  1) Failure:\ntest_failure(TestSimpleNumber) [tc_simple_number2.rb:16]:\nAdding doesn't work.\n<3> expected but was\n<4>.\n\n3 tests, 4 assertions, 1 failures, 0 errors\n\n-- Java Junit\n.......F.\nTime: 0,003\nThere was 1 failure:\n1) testCapacity(junit.samples.VectorTest)junit.framework.AssertionFailedError\n    at junit.samples.VectorTest.testCapacity(VectorTest.java:87)\n    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\nFAILURES!!!\nTests run: 8,  Failures: 1,  Errors: 0\n\n\n-- Maven\n\n# mvn test\n-------------------------------------------------------\n T E S T S\n-------------------------------------------------------\nRunning math.AdditionTest\nTests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed:\n0.03 sec <<< FAILURE!\n\nResults :\n\nFailed tests:\n  testLireSymbole(math.AdditionTest)\n\nTests run: 2, Failures: 1, Errors: 0, Skipped: 0\n\n\n-- LuaUnit\n---- non verbose\n* display . or F or E when running tests\n---- verbose\n* display test name + ok/fail\n----\n* blank line\n* number) ERROR or FAILURE: TestName\n   Stack trace\n* blank line\n* number) ERROR or FAILURE: TestName\n   Stack trace\n\nthen --------------\nthen \"Ran x tests in 0.000s (%d not selected, %d skipped)\"\nthen OK or FAILED (failures=1, error=1)\n\n\n]]\n\nlocal TextOutput = genericOutput.new() -- derived class\nlocal TextOutput_MT = { __index = TextOutput } -- metatable\nTextOutput.__class__ = 'TextOutput'\n\n    function TextOutput.new(runner)\n        local t = genericOutput.new(runner, M.VERBOSITY_DEFAULT)\n        t.errorList = {}\n        return setmetatable( t, TextOutput_MT )\n    end\n\n    function TextOutput:startSuite()\n        if self.verbosity > M.VERBOSITY_DEFAULT then\n            print( 'Started on '.. self.result.startDate )\n        end\n    end\n\n    function TextOutput:startTest(testName)\n        if self.verbosity > M.VERBOSITY_DEFAULT then\n            io.stdout:write( \"    \", self.result.currentNode.testName, \" ... \" )\n        end\n    end\n\n    function TextOutput:endTest( node )\n        if node:isSuccess() then\n            if self.verbosity > M.VERBOSITY_DEFAULT then\n                io.stdout:write(\"Ok\\n\")\n            else\n                io.stdout:write(\".\")\n                io.stdout:flush()\n            end\n        else\n            if self.verbosity > M.VERBOSITY_DEFAULT then\n                print( node.status )\n                print( node.msg )\n                --[[\n                -- find out when to do this:\n                if self.verbosity > M.VERBOSITY_DEFAULT then\n                    print( node.stackTrace )\n                end\n                ]]\n            else\n                -- write only the first character of status E, F or S\n                io.stdout:write(string.sub(node.status, 1, 1))\n                io.stdout:flush()\n            end\n        end\n    end\n\n    function TextOutput:displayOneFailedTest( index, fail )\n        print(index..\") \"..fail.testName )\n        print( fail.msg )\n        print( fail.stackTrace )\n        print()\n    end\n\n    function TextOutput:displayErroredTests()\n        if #self.result.errorTests ~= 0 then\n            print(\"Tests with errors:\")\n            print(\"------------------\")\n            for i, v in ipairs(self.result.errorTests) do\n                self:displayOneFailedTest(i, v)\n            end\n        end\n    end\n\n    function TextOutput:displayFailedTests()\n        if #self.result.failedTests ~= 0 then\n            print(\"Failed tests:\")\n            print(\"-------------\")\n            for i, v in ipairs(self.result.failedTests) do\n                self:displayOneFailedTest(i, v)\n            end\n        end\n    end\n\n    function TextOutput:endSuite()\n        if self.verbosity > M.VERBOSITY_DEFAULT then\n            print(\"=========================================================\")\n        else\n            print()\n        end\n        self:displayErroredTests()\n        self:displayFailedTests()\n        print( M.LuaUnit.statusLine( self.result ) )\n        if self.result.notSuccessCount == 0 then\n            print('OK')\n        end\n    end\n\n-- class TextOutput end\n\n\n----------------------------------------------------------------\n--                     class NilOutput\n----------------------------------------------------------------\n\nlocal function nopCallable()\n    --print(42)\n    return nopCallable\nend\n\nlocal NilOutput = { __class__ = 'NilOuptut' } -- class\nlocal NilOutput_MT = { __index = nopCallable } -- metatable\n\nfunction NilOutput.new(runner)\n    return setmetatable( { __class__ = 'NilOutput' }, NilOutput_MT )\nend\n\n----------------------------------------------------------------\n--\n--                     class LuaUnit\n--\n----------------------------------------------------------------\n\nM.LuaUnit = {\n    outputType = TextOutput,\n    verbosity = M.VERBOSITY_DEFAULT,\n    __class__ = 'LuaUnit',\n    instances = {}\n}\nlocal LuaUnit_MT = { __index = M.LuaUnit }\n\nif EXPORT_ASSERT_TO_GLOBALS then\n    LuaUnit = M.LuaUnit\nend\n\n    function M.LuaUnit.new()\n        local newInstance = setmetatable( {}, LuaUnit_MT )\n        return newInstance\n    end\n\n    -----------------[[ Utility methods ]]---------------------\n\n    function M.LuaUnit.asFunction(aObject)\n        -- return \"aObject\" if it is a function, and nil otherwise\n        if 'function' == type(aObject) then\n            return aObject\n        end\n    end\n\n    function M.LuaUnit.splitClassMethod(someName)\n        --[[\n        Return a pair of className, methodName strings for a name in the form\n        \"class.method\". If no class part (or separator) is found, will return\n        nil, someName instead (the latter being unchanged).\n\n        This convention thus also replaces the older isClassMethod() test:\n        You just have to check for a non-nil className (return) value.\n        ]]\n        local separator = string.find(someName, '.', 1, true)\n        if separator then\n            return someName:sub(1, separator - 1), someName:sub(separator + 1)\n        end\n        return nil, someName\n    end\n\n    function M.LuaUnit.isMethodTestName( s )\n        -- return true is the name matches the name of a test method\n        -- default rule is that is starts with 'Test' or with 'test'\n        return string.sub(s, 1, 4):lower() == 'test'\n    end\n\n    function M.LuaUnit.isTestName( s )\n        -- return true is the name matches the name of a test\n        -- default rule is that is starts with 'Test' or with 'test'\n        return string.sub(s, 1, 4):lower() == 'test'\n    end\n\n    function M.LuaUnit.collectTests()\n        -- return a list of all test names in the global namespace\n        -- that match LuaUnit.isTestName\n\n        local testNames = {}\n        for k, _ in pairs(_G) do\n            if type(k) == \"string\" and M.LuaUnit.isTestName( k ) then\n                table.insert( testNames , k )\n            end\n        end\n        table.sort( testNames )\n        return testNames\n    end\n\n    function M.LuaUnit.parseCmdLine( cmdLine )\n        -- parse the command line\n        -- Supported command line parameters:\n        -- --verbose, -v: increase verbosity\n        -- --quiet, -q: silence output\n        -- --error, -e: treat errors as fatal (quit program)\n        -- --output, -o, + name: select output type\n        -- --pattern, -p, + pattern: run test matching pattern, may be repeated\n        -- --exclude, -x, + pattern: run test not matching pattern, may be repeated\n        -- --shuffle, -s, : shuffle tests before reunning them\n        -- --name, -n, + fname: name of output file for junit, default to stdout\n        -- --repeat, -r, + num: number of times to execute each test\n        -- [testnames, ...]: run selected test names\n        --\n        -- Returns a table with the following fields:\n        -- verbosity: nil, M.VERBOSITY_DEFAULT, M.VERBOSITY_QUIET, M.VERBOSITY_VERBOSE\n        -- output: nil, 'tap', 'junit', 'text', 'nil'\n        -- testNames: nil or a list of test names to run\n        -- exeRepeat: num or 1\n        -- pattern: nil or a list of patterns\n        -- exclude: nil or a list of patterns\n\n        local result, state = {}, nil\n        local SET_OUTPUT = 1\n        local SET_PATTERN = 2\n        local SET_EXCLUDE = 3\n        local SET_FNAME = 4\n        local SET_REPEAT = 5\n\n        if cmdLine == nil then\n            return result\n        end\n\n        local function parseOption( option )\n            if option == '--help' or option == '-h' then\n                result['help'] = true\n                return\n            elseif option == '--version' then\n                result['version'] = true\n                return\n            elseif option == '--verbose' or option == '-v' then\n                result['verbosity'] = M.VERBOSITY_VERBOSE\n                return\n            elseif option == '--quiet' or option == '-q' then\n                result['verbosity'] = M.VERBOSITY_QUIET\n                return\n            elseif option == '--error' or option == '-e' then\n                result['quitOnError'] = true\n                return\n            elseif option == '--failure' or option == '-f' then\n                result['quitOnFailure'] = true\n                return\n            elseif option == '--shuffle' or option == '-s' then\n                result['shuffle'] = true\n                return\n            elseif option == '--output' or option == '-o' then\n                state = SET_OUTPUT\n                return state\n            elseif option == '--name' or option == '-n' then\n                state = SET_FNAME\n                return state\n            elseif option == '--repeat' or option == '-r' then\n                state = SET_REPEAT\n                return state\n            elseif option == '--pattern' or option == '-p' then\n                state = SET_PATTERN\n                return state\n            elseif option == '--exclude' or option == '-x' then\n                state = SET_EXCLUDE\n                return state\n            end\n            error('Unknown option: '..option,3)\n        end\n\n        local function setArg( cmdArg, state )\n            if state == SET_OUTPUT then\n                result['output'] = cmdArg\n                return\n            elseif state == SET_FNAME then\n                result['fname'] = cmdArg\n                return\n            elseif state == SET_REPEAT then\n                result['exeRepeat'] = tonumber(cmdArg)\n                                     or error('Malformed -r argument: '..cmdArg)\n                return\n            elseif state == SET_PATTERN then\n                if result['pattern'] then\n                    table.insert( result['pattern'], cmdArg )\n                else\n                    result['pattern'] = { cmdArg }\n                end\n                return\n            elseif state == SET_EXCLUDE then\n                local notArg = '!'..cmdArg\n                if result['pattern'] then\n                    table.insert( result['pattern'],  notArg )\n                else\n                    result['pattern'] = { notArg }\n                end\n                return\n            end\n            error('Unknown parse state: '.. state)\n        end\n\n\n        for i, cmdArg in ipairs(cmdLine) do\n            if state ~= nil then\n                setArg( cmdArg, state, result )\n                state = nil\n            else\n                if cmdArg:sub(1,1) == '-' then\n                    state = parseOption( cmdArg )\n                else\n                    if result['testNames'] then\n                        table.insert( result['testNames'], cmdArg )\n                    else\n                        result['testNames'] = { cmdArg }\n                    end\n                end\n            end\n        end\n\n        if result['help'] then\n            M.LuaUnit.help()\n        end\n\n        if result['version'] then\n            M.LuaUnit.version()\n        end\n\n        if state ~= nil then\n            error('Missing argument after '..cmdLine[ #cmdLine ],2 )\n        end\n\n        return result\n    end\n\n    function M.LuaUnit.help()\n        print(M.USAGE)\n        os.exit(0)\n    end\n\n    function M.LuaUnit.version()\n        print('LuaUnit v'..M.VERSION..' by Philippe Fremy <phil@freehackers.org>')\n        os.exit(0)\n    end\n\n----------------------------------------------------------------\n--                     class NodeStatus\n----------------------------------------------------------------\n\n    local NodeStatus = { __class__ = 'NodeStatus' } -- class\n    local NodeStatus_MT = { __index = NodeStatus } -- metatable\n    M.NodeStatus = NodeStatus\n\n    -- values of status\n    NodeStatus.SUCCESS  = 'SUCCESS'\n    NodeStatus.SKIP     = 'SKIP'\n    NodeStatus.FAIL     = 'FAIL'\n    NodeStatus.ERROR    = 'ERROR'\n\n    function NodeStatus.new( number, testName, className )\n        -- default constructor, test are PASS by default\n        local t = { number = number, testName = testName, className = className }\n        setmetatable( t, NodeStatus_MT )\n        t:success()\n        return t\n    end\n\n    function NodeStatus:success()\n        self.status = self.SUCCESS\n        -- useless because lua does this for us, but it helps me remembering the relevant field names\n        self.msg = nil\n        self.stackTrace = nil\n    end\n\n    function NodeStatus:skip(msg)\n        self.status = self.SKIP\n        self.msg = msg\n        self.stackTrace = nil\n    end\n\n    function NodeStatus:fail(msg, stackTrace)\n        self.status = self.FAIL\n        self.msg = msg\n        self.stackTrace = stackTrace\n    end\n\n    function NodeStatus:error(msg, stackTrace)\n        self.status = self.ERROR\n        self.msg = msg\n        self.stackTrace = stackTrace\n    end\n\n    function NodeStatus:isSuccess()\n        return self.status == NodeStatus.SUCCESS\n    end\n\n    function NodeStatus:isNotSuccess()\n        -- Return true if node is either failure or error or skip\n        return (self.status == NodeStatus.FAIL or self.status == NodeStatus.ERROR or self.status == NodeStatus.SKIP)\n    end\n\n    function NodeStatus:isSkipped()\n        return self.status == NodeStatus.SKIP\n    end\n\n    function NodeStatus:isFailure()\n        return self.status == NodeStatus.FAIL\n    end\n\n    function NodeStatus:isError()\n        return self.status == NodeStatus.ERROR\n    end\n\n    function NodeStatus:statusXML()\n        if self:isError() then\n            return table.concat(\n                {'            <error type=\"', xmlEscape(self.msg), '\">\\n',\n                 '                <![CDATA[', xmlCDataEscape(self.stackTrace),\n                 ']]></error>\\n'})\n        elseif self:isFailure() then\n            return table.concat(\n                {'            <failure type=\"', xmlEscape(self.msg), '\">\\n',\n                 '                <![CDATA[', xmlCDataEscape(self.stackTrace),\n                 ']]></failure>\\n'})\n        elseif self:isSkipped() then\n            return table.concat({'            <skipped>', xmlEscape(self.msg),'</skipped>\\n' } )\n        end\n        return '            <passed/>\\n' -- (not XSD-compliant! normally shouldn't get here)\n    end\n\n    --------------[[ Output methods ]]-------------------------\n\n    local function conditional_plural(number, singular)\n        -- returns a grammatically well-formed string \"%d <singular/plural>\"\n        local suffix = ''\n        if number ~= 1 then -- use plural\n            suffix = (singular:sub(-2) == 'ss') and 'es' or 's'\n        end\n        return string.format('%d %s%s', number, singular, suffix)\n    end\n\n    function M.LuaUnit.statusLine(result)\n        -- return status line string according to results\n        local s = {\n            string.format('Ran %d tests in %0.3f seconds',\n                          result.runCount, result.duration),\n            conditional_plural(result.successCount, 'success'),\n        }\n        if result.notSuccessCount > 0 then\n            if result.failureCount > 0 then\n                table.insert(s, conditional_plural(result.failureCount, 'failure'))\n            end\n            if result.errorCount > 0 then\n                table.insert(s, conditional_plural(result.errorCount, 'error'))\n            end\n        else\n            table.insert(s, '0 failures')\n        end\n        if result.skippedCount > 0 then\n            table.insert(s, string.format(\"%d skipped\", result.skippedCount))\n        end\n        if result.nonSelectedCount > 0 then\n            table.insert(s, string.format(\"%d non-selected\", result.nonSelectedCount))\n        end\n        return table.concat(s, ', ')\n    end\n\n    function M.LuaUnit:startSuite(selectedCount, nonSelectedCount)\n        self.result = {\n            selectedCount = selectedCount,\n            nonSelectedCount = nonSelectedCount,\n            successCount = 0,\n            runCount = 0,\n            currentTestNumber = 0,\n            currentClassName = \"\",\n            currentNode = nil,\n            suiteStarted = true,\n            startTime = os.clock(),\n            startDate = os.date(os.getenv('LUAUNIT_DATEFMT')),\n            startIsodate = os.date('%Y-%m-%dT%H:%M:%S'),\n            patternIncludeFilter = self.patternIncludeFilter,\n\n            -- list of test node status\n            allTests = {},\n            failedTests = {},\n            errorTests = {},\n            skippedTests = {},\n\n            failureCount = 0,\n            errorCount = 0,\n            notSuccessCount = 0,\n            skippedCount = 0,\n        }\n\n        self.outputType = self.outputType or TextOutput\n        self.output = self.outputType.new(self)\n        self.output:startSuite()\n    end\n\n    function M.LuaUnit:startClass( className, classInstance )\n        self.result.currentClassName = className\n        self.output:startClass( className )\n        self:setupClass( className, classInstance )\n    end\n\n    function M.LuaUnit:startTest( testName  )\n        self.result.currentTestNumber = self.result.currentTestNumber + 1\n        self.result.runCount = self.result.runCount + 1\n        self.result.currentNode = NodeStatus.new(\n            self.result.currentTestNumber,\n            testName,\n            self.result.currentClassName\n        )\n        self.result.currentNode.startTime = os.clock()\n        table.insert( self.result.allTests, self.result.currentNode )\n        self.output:startTest( testName )\n    end\n\n    function M.LuaUnit:updateStatus( err )\n        -- \"err\" is expected to be a table / result from protectedCall()\n        if err.status == NodeStatus.SUCCESS then\n            return\n        end\n\n        local node = self.result.currentNode\n\n        --[[ As a first approach, we will report only one error or one failure for one test.\n\n        However, we can have the case where the test is in failure, and the teardown is in error.\n        In such case, it's a good idea to report both a failure and an error in the test suite. This is\n        what Python unittest does for example. However, it mixes up counts so need to be handled carefully: for\n        example, there could be more (failures + errors) count that tests. What happens to the current node ?\n\n        We will do this more intelligent version later.\n        ]]\n\n        -- if the node is already in failure/error, just don't report the new error (see above)\n        if node.status ~= NodeStatus.SUCCESS then\n            return\n        end\n\n        if err.status == NodeStatus.FAIL then\n            node:fail( err.msg, err.trace )\n            table.insert( self.result.failedTests, node )\n        elseif err.status == NodeStatus.ERROR then\n            node:error( err.msg, err.trace )\n            table.insert( self.result.errorTests, node )\n        elseif err.status == NodeStatus.SKIP then\n            node:skip( err.msg )\n            table.insert( self.result.skippedTests, node )\n        else\n            error('No such status: ' .. prettystr(err.status))\n        end\n\n        self.output:updateStatus( node )\n    end\n\n    function M.LuaUnit:endTest()\n        local node = self.result.currentNode\n        -- print( 'endTest() '..prettystr(node))\n        -- print( 'endTest() '..prettystr(node:isNotSuccess()))\n        node.duration = os.clock() - node.startTime\n        node.startTime = nil\n        self.output:endTest( node )\n\n        if node:isSuccess() then\n            self.result.successCount = self.result.successCount + 1\n        elseif node:isError() then\n            if self.quitOnError or self.quitOnFailure then\n                -- Runtime error - abort test execution as requested by\n                -- \"--error\" option. This is done by setting a special\n                -- flag that gets handled in internalRunSuiteByInstances().\n                print(\"\\nERROR during LuaUnit test execution:\\n\" .. node.msg)\n                self.result.aborted = true\n            end\n        elseif node:isFailure() then\n            if self.quitOnFailure then\n                -- Failure - abort test execution as requested by\n                -- \"--failure\" option. This is done by setting a special\n                -- flag that gets handled in internalRunSuiteByInstances().\n                print(\"\\nFailure during LuaUnit test execution:\\n\" .. node.msg)\n                self.result.aborted = true\n            end\n        elseif node:isSkipped() then\n            self.result.runCount = self.result.runCount - 1\n        else\n            error('No such node status: ' .. prettystr(node.status))\n        end\n        self.result.currentNode = nil\n    end\n\n    function M.LuaUnit:endClass()\n        self:teardownClass( self.lastClassName, self.lastClassInstance )\n        self.output:endClass()\n    end\n\n    function M.LuaUnit:endSuite()\n        if self.result.suiteStarted == false then\n            error('LuaUnit:endSuite() -- suite was already ended' )\n        end\n        self.result.duration = os.clock()-self.result.startTime\n        self.result.suiteStarted = false\n\n        -- Expose test counts for outputter's endSuite(). This could be managed\n        -- internally instead by using the length of the lists of failed tests\n        -- but unit tests rely on these fields being present.\n        self.result.failureCount = #self.result.failedTests\n        self.result.errorCount = #self.result.errorTests\n        self.result.notSuccessCount = self.result.failureCount + self.result.errorCount\n        self.result.skippedCount = #self.result.skippedTests\n\n        self.output:endSuite()\n    end\n\n    function M.LuaUnit:setOutputType(outputType, fname)\n        -- Configures LuaUnit runner output\n        -- outputType is one of: NIL, TAP, JUNIT, TEXT\n        -- when outputType is junit, the additional argument fname is used to set the name of junit output file\n        -- for other formats, fname is ignored\n        if outputType:upper() == \"NIL\" then\n            self.outputType = NilOutput\n            return\n        end\n        if outputType:upper() == \"TAP\" then\n            self.outputType = TapOutput\n            return\n        end\n        if outputType:upper() == \"JUNIT\" then\n            self.outputType = JUnitOutput\n            if fname then\n                self.fname = fname\n            end\n            return\n        end\n        if outputType:upper() == \"TEXT\" then\n            self.outputType = TextOutput\n            return\n        end\n        error( 'No such format: '..outputType,2)\n    end\n\n    --------------[[ Runner ]]-----------------\n\n    function M.LuaUnit:protectedCall(classInstance, methodInstance, prettyFuncName)\n        -- if classInstance is nil, this is just a function call\n        -- else, it's method of a class being called.\n\n        local function err_handler(e)\n            -- transform error into a table, adding the traceback information\n            return {\n                status = NodeStatus.ERROR,\n                msg = e,\n                trace = string.sub(debug.traceback(\"\", 1), 2)\n            }\n        end\n\n        local ok, err\n        if classInstance then\n            -- stupid Lua < 5.2 does not allow xpcall with arguments so let's use a workaround\n            ok, err = xpcall( function () methodInstance(classInstance) end, err_handler )\n        else\n            ok, err = xpcall( function () methodInstance() end, err_handler )\n        end\n        if ok then\n            return {status = NodeStatus.SUCCESS}\n        end\n        -- print('ok=\"'..prettystr(ok)..'\" err=\"'..prettystr(err)..'\"')\n\n        local iter_msg\n        iter_msg = self.exeRepeat and 'iteration '..self.currentCount\n\n        err.msg, err.status = M.adjust_err_msg_with_iter( err.msg, iter_msg )\n\n        if err.status == NodeStatus.SUCCESS or err.status == NodeStatus.SKIP then\n            err.trace = nil\n            return err\n        end\n\n        -- reformat / improve the stack trace\n        if prettyFuncName then -- we do have the real method name\n            err.trace = err.trace:gsub(\"in (%a+) 'methodInstance'\", \"in %1 '\"..prettyFuncName..\"'\")\n        end\n        if STRIP_LUAUNIT_FROM_STACKTRACE then\n            err.trace = stripLuaunitTrace2(err.trace, err.msg)\n        end\n\n        return err -- return the error \"object\" (table)\n    end\n\n\n    function M.LuaUnit:execOneFunction(className, methodName, classInstance, methodInstance)\n        -- When executing a test function, className and classInstance must be nil\n        -- When executing a class method, all parameters must be set\n\n        if type(methodInstance) ~= 'function' then\n            self:unregisterSuite()\n            error( tostring(methodName)..' must be a function, not '..type(methodInstance))\n        end\n\n        local prettyFuncName\n        if className == nil then\n            className = '[TestFunctions]'\n            prettyFuncName = methodName\n        else\n            prettyFuncName = className..'.'..methodName\n        end\n\n        if self.lastClassName ~= className then\n            if self.lastClassName ~= nil then\n                self:endClass()\n            end\n            self:startClass( className, classInstance )\n            self.lastClassName = className\n            self.lastClassInstance = classInstance\n        end\n\n        self:startTest(prettyFuncName)\n\n        local node = self.result.currentNode\n        for iter_n = 1, self.exeRepeat or 1 do\n            if node:isNotSuccess() then\n                break\n            end\n            self.currentCount = iter_n\n\n            -- run setUp first (if any)\n            if classInstance then\n                local func = self.asFunction( classInstance.setUp ) or\n                             self.asFunction( classInstance.Setup ) or\n                             self.asFunction( classInstance.setup ) or\n                             self.asFunction( classInstance.SetUp )\n                if func then\n                    self:updateStatus(self:protectedCall(classInstance, func, className..'.setUp'))\n                end\n            end\n\n            -- run testMethod()\n            if node:isSuccess() then\n                self:updateStatus(self:protectedCall(classInstance, methodInstance, prettyFuncName))\n            end\n\n            -- lastly, run tearDown (if any)\n            if classInstance then\n                local func = self.asFunction( classInstance.tearDown ) or\n                             self.asFunction( classInstance.TearDown ) or\n                             self.asFunction( classInstance.teardown ) or\n                             self.asFunction( classInstance.Teardown )\n                if func then\n                    self:updateStatus(self:protectedCall(classInstance, func, className..'.tearDown'))\n                end\n            end\n        end\n\n        self:endTest()\n    end\n\n    function M.LuaUnit.expandOneClass( result, className, classInstance )\n        --[[\n        Input: a list of { name, instance }, a class name, a class instance\n        Ouptut: modify result to add all test method instance in the form:\n        { className.methodName, classInstance }\n        ]]\n        for methodName, methodInstance in sortedPairs(classInstance) do\n            if M.LuaUnit.asFunction(methodInstance) and M.LuaUnit.isMethodTestName( methodName ) then\n                table.insert( result, { className..'.'..methodName, classInstance } )\n            end\n        end\n    end\n\n    function M.LuaUnit.expandClasses( listOfNameAndInst )\n        --[[\n        -- expand all classes (provided as {className, classInstance}) to a list of {className.methodName, classInstance}\n        -- functions and methods remain untouched\n\n        Input: a list of { name, instance }\n\n        Output:\n        * { function name, function instance } : do nothing\n        * { class.method name, class instance }: do nothing\n        * { class name, class instance } : add all method names in the form of (className.methodName, classInstance)\n        ]]\n        local result = {}\n\n        for i,v in ipairs( listOfNameAndInst ) do\n            local name, instance = v[1], v[2]\n            if M.LuaUnit.asFunction(instance) then\n                table.insert( result, { name, instance } )\n            else\n                if type(instance) ~= 'table' then\n                    error( 'Instance must be a table or a function, not a '..type(instance)..' with value '..prettystr(instance))\n                end\n                local className, methodName = M.LuaUnit.splitClassMethod( name )\n                if className then\n                    local methodInstance = instance[methodName]\n                    if methodInstance == nil then\n                        error( \"Could not find method in class \"..tostring(className)..\" for method \"..tostring(methodName) )\n                    end\n                    table.insert( result, { name, instance } )\n                else\n                    M.LuaUnit.expandOneClass( result, name, instance )\n                end\n            end\n        end\n\n        return result\n    end\n\n    function M.LuaUnit.applyPatternFilter( patternIncFilter, listOfNameAndInst )\n        local included, excluded = {}, {}\n        for i, v in ipairs( listOfNameAndInst ) do\n            -- local name, instance = v[1], v[2]\n            if  patternFilter( patternIncFilter, v[1] ) then\n                table.insert( included, v )\n            else\n                table.insert( excluded, v )\n            end\n        end\n        return included, excluded\n    end\n\n    local function getKeyInListWithGlobalFallback( key,  listOfNameAndInst )\n        local result = nil\n        for i,v in ipairs( listOfNameAndInst ) do\n            if(listOfNameAndInst[i][1] == key) then\n                result = listOfNameAndInst[i][2]\n                break\n            end\n        end\n        if(not  M.LuaUnit.asFunction( result ) ) then\n            result = _G[key]\n        end\n        return result\n    end\n\n    function M.LuaUnit:setupSuite( listOfNameAndInst )\n        local setupSuite = getKeyInListWithGlobalFallback(\"setupSuite\", listOfNameAndInst)\n        if  self.asFunction( setupSuite ) then\n            self:updateStatus( self:protectedCall( nil, setupSuite, 'setupSuite' ) )\n        end\n    end\n\n    function M.LuaUnit:teardownSuite(listOfNameAndInst)\n        local teardownSuite = getKeyInListWithGlobalFallback(\"teardownSuite\", listOfNameAndInst)\n        if self.asFunction( teardownSuite ) then\n            self:updateStatus( self:protectedCall( nil, teardownSuite, 'teardownSuite') )\n        end\n    end\n\n    function  M.LuaUnit:setupClass( className, instance )\n        if type( instance ) == 'table' and self.asFunction( instance.setupClass ) then\n            self:updateStatus( self:protectedCall( instance, instance.setupClass, className..'.setupClass' ) )\n        end\n    end\n\n    function M.LuaUnit:teardownClass( className, instance )\n        if type( instance ) == 'table' and self.asFunction( instance.teardownClass ) then\n            self:updateStatus( self:protectedCall( instance, instance.teardownClass, className..'.teardownClass' ) )\n        end\n    end\n\n    function M.LuaUnit:internalRunSuiteByInstances( listOfNameAndInst )\n        --[[ Run an explicit list of tests. Each item of the list must be one of:\n        * { function name, function instance }\n        * { class name, class instance }\n        * { class.method name, class instance }\n\n        This function is internal to LuaUnit. The official API to perform this action is runSuiteByInstances()\n        ]]\n\n        local expandedList = self.expandClasses( listOfNameAndInst )\n        if self.shuffle then\n            randomizeTable( expandedList )\n        end\n        local filteredList, filteredOutList = self.applyPatternFilter(\n            self.patternIncludeFilter, expandedList )\n\n        self:startSuite( #filteredList, #filteredOutList )\n        self:setupSuite( listOfNameAndInst )\n\n        for i,v in ipairs( filteredList ) do\n            local name, instance = v[1], v[2]\n            if M.LuaUnit.asFunction(instance) then\n                self:execOneFunction( nil, name, nil, instance )\n            else\n                -- expandClasses() should have already taken care of sanitizing the input\n                assert( type(instance) == 'table' )\n                local className, methodName = M.LuaUnit.splitClassMethod( name )\n                assert( className ~= nil )\n                local methodInstance = instance[methodName]\n                assert(methodInstance ~= nil)\n                self:execOneFunction( className, methodName, instance, methodInstance )\n            end\n            if self.result.aborted then\n                break -- \"--error\" or \"--failure\" option triggered\n            end\n        end\n\n        if self.lastClassName ~= nil then\n            self:endClass()\n        end\n\n        self:teardownSuite( listOfNameAndInst )\n        self:endSuite()\n\n        if self.result.aborted then\n            print(\"LuaUnit ABORTED (as requested by --error or --failure option)\")\n            self:unregisterSuite()\n            os.exit(-2)\n        end\n    end\n\n    function M.LuaUnit:internalRunSuiteByNames( listOfName )\n        --[[ Run LuaUnit with a list of generic names, coming either from command-line or from global\n            namespace analysis. Convert the list into a list of (name, valid instances (table or function))\n            and calls internalRunSuiteByInstances.\n        ]]\n\n        local instanceName, instance\n        local listOfNameAndInst = {}\n\n        for i,name in ipairs( listOfName ) do\n            local className, methodName = M.LuaUnit.splitClassMethod( name )\n            if className then\n                instanceName = className\n                instance = _G[instanceName]\n\n                if instance == nil then\n                    self:unregisterSuite()\n                    error( \"No such name in global space: \"..instanceName )\n                end\n\n                if type(instance) ~= 'table' then\n                    self:unregisterSuite()\n                    error( 'Instance of '..instanceName..' must be a table, not '..type(instance))\n                end\n\n                local methodInstance = instance[methodName]\n                if methodInstance == nil then\n                    self:unregisterSuite()\n                    error( \"Could not find method in class \"..tostring(className)..\" for method \"..tostring(methodName) )\n                end\n\n            else\n                -- for functions and classes\n                instanceName = name\n                instance = _G[instanceName]\n            end\n\n            if instance == nil then\n                self:unregisterSuite()\n                error( \"No such name in global space: \"..instanceName )\n            end\n\n            if (type(instance) ~= 'table' and type(instance) ~= 'function') then\n                self:unregisterSuite()\n                error( 'Name must match a function or a table: '..instanceName )\n            end\n\n            table.insert( listOfNameAndInst, { name, instance } )\n        end\n\n        self:internalRunSuiteByInstances( listOfNameAndInst )\n    end\n\n    function M.LuaUnit.run(...)\n        -- Run some specific test classes.\n        -- If no arguments are passed, run the class names specified on the\n        -- command line. If no class name is specified on the command line\n        -- run all classes whose name starts with 'Test'\n        --\n        -- If arguments are passed, they must be strings of the class names\n        -- that you want to run or generic command line arguments (-o, -p, -v, ...)\n        local runner = M.LuaUnit.new()\n        return runner:runSuite(...)\n    end\n\n    function M.LuaUnit:registerSuite()\n        -- register the current instance into our global array of instances\n        -- print('-> Register suite')\n        M.LuaUnit.instances[ #M.LuaUnit.instances+1 ] = self\n    end\n\n    function M.unregisterCurrentSuite()\n        -- force unregister the last registered suite\n        table.remove(M.LuaUnit.instances, #M.LuaUnit.instances)\n    end\n\n    function M.LuaUnit:unregisterSuite()\n        -- print('<- Unregister suite')\n        -- remove our current instqances from the global array of instances\n        local instanceIdx = nil\n        for i, instance in ipairs(M.LuaUnit.instances) do\n            if instance == self then\n                instanceIdx = i\n                break\n            end\n        end\n\n        if instanceIdx ~= nil then\n            table.remove(M.LuaUnit.instances, instanceIdx)\n            -- print('Unregister done')\n        end\n\n    end\n\n    function M.LuaUnit:initFromArguments( ... )\n        --[[Parses all arguments from either command-line or direct call and set internal\n        flags of LuaUnit runner according to it.\n\n        Return the list of names which were possibly passed on the command-line or as arguments\n        ]]\n        local args = {...}\n        if type(args[1]) == 'table' and args[1].__class__ == 'LuaUnit' then\n            -- run was called with the syntax M.LuaUnit:runSuite()\n            -- we support both M.LuaUnit.run() and M.LuaUnit:run()\n            -- strip out the first argument self to make it a command-line argument list\n            table.remove(args,1)\n        end\n\n        if #args == 0 then\n            args = cmdline_argv\n        end\n\n        local options = pcall_or_abort( M.LuaUnit.parseCmdLine, args )\n\n        -- We expect these option fields to be either `nil` or contain\n        -- valid values, so it's safe to always copy them directly.\n        self.verbosity     = options.verbosity\n        self.quitOnError   = options.quitOnError\n        self.quitOnFailure = options.quitOnFailure\n\n        self.exeRepeat            = options.exeRepeat\n        self.patternIncludeFilter = options.pattern\n        self.shuffle              = options.shuffle\n\n        options.output     = options.output or os.getenv('LUAUNIT_OUTPUT')\n        options.fname      = options.fname  or os.getenv('LUAUNIT_JUNIT_FNAME')\n\n        if options.output then\n            if options.output:lower() == 'junit' and options.fname == nil then\n                print('With junit output, a filename must be supplied with -n or --name')\n                os.exit(-1)\n            end\n            pcall_or_abort(self.setOutputType, self, options.output, options.fname)\n        end\n\n        return options.testNames\n    end\n\n    function M.LuaUnit:runSuite( ... )\n        testNames = self:initFromArguments(...)\n        self:registerSuite()\n        self:internalRunSuiteByNames( testNames or M.LuaUnit.collectTests() )\n        self:unregisterSuite()\n        return self.result.notSuccessCount\n    end\n\n    function M.LuaUnit:runSuiteByInstances( listOfNameAndInst, commandLineArguments )\n        --[[\n        Run all test functions or tables provided as input.\n\n        Input: a list of { name, instance }\n            instance can either be a function or a table containing test functions starting with the prefix \"test\"\n\n        return the number of failures and errors, 0 meaning success\n        ]]\n        -- parse the command-line arguments\n        testNames = self:initFromArguments( commandLineArguments )\n        self:registerSuite()\n        self:internalRunSuiteByInstances( listOfNameAndInst )\n        self:unregisterSuite()\n        return self.result.notSuccessCount\n    end\n\n\n\n-- class LuaUnit\n\n-- For compatbility with LuaUnit v2\nM.run = M.LuaUnit.run\nM.Run = M.LuaUnit.run\n\nfunction M:setVerbosity( verbosity )\n    -- set the verbosity value (as integer)\n    M.LuaUnit.verbosity = verbosity\nend\nM.set_verbosity = M.setVerbosity\nM.SetVerbosity = M.setVerbosity\n\n\nreturn M\n\n"
  },
  {
    "path": "modules/minify.lua",
    "content": "--[[\n   MIT License\n\n   Copyright (c) 2017 Mark Langen\n\n   Permission is hereby granted, free of charge, to any person obtaining a copy\n   of this software and associated documentation files (the \"Software\"), to deal\n   in the Software without restriction, including without limitation the rights\n   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n   copies of the Software, and to permit persons to whom the Software is\n   furnished to do so, subject to the following conditions:\n\n   The above copyright notice and this permission notice shall be included in all\n   copies or substantial portions of the Software.\n\n   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n   SOFTWARE.\n\n   @link https://github.com/n1tehawk/lua-minify\n]]\n\n-- eyecandy to increase readability for empty if branches and keep luacheck happy\nlocal function do_nothing() end\n\nlocal function lookupify(tb)\n\tlocal t = {}\n\tfor _, v in pairs(tb) do\n\t\tt[v] = true\n\tend\n\treturn t\nend\n\nlocal function CountTable(tb, limit)\n\tlocal c, k = 0, next(tb, nil)\n\twhile k ~= nil do\n\t\tc = c + 1\n\t\tif limit and (c >= limit) then\n\t\t\tbreak\n\t\tend\n\t\tk = next(tb, k)\n\tend\n\treturn c\nend\n\nlocal indentation = '    ' -- normally either multiple spaces or \"\\t\"\nlocal function indentStr(level)\n\treturn string.rep(indentation, level or 0)\nend\n\nlocal function FormatTable(tb, atIndent, ignoreFunc)\n\t-- Note: This is currently unused,\n\t-- and it might be better to check for a __tostring metamethod instead\n\tif type(tb.Print) == 'function' then\n\t\treturn tb.Print()\n\tend\n\t-- set parameter defaults\n\tatIndent = atIndent or 0\n\tignoreFunc = ignoreFunc or function()\n\t\treturn false\n\tend\n\n\tlocal consecutiveIndex, useNewlines = 1, CountTable(tb, 2) > 1\n\tlocal baseIndent = indentStr(atIndent + 1)\n\tlocal out = {'{'} -- table of output strings\n\tif useNewlines then\n\t\ttable.insert(out, '\\n')\n\tend\n\n\tfor k, v in pairs(tb) do\n\t\tlocal type_k, type_v = type(k), type(v) -- cache types, used multiple times\n\t\tif type_v ~= 'function' and not ignoreFunc(k) then\n\t\t\tif useNewlines then\n\t\t\t\ttable.insert(out, baseIndent)\n\t\t\tend\n\n\t\t\t-- key\n\t\t\tif type_k == 'string' then\n\t\t\t\tif k:match(\"^[A-Za-z_][A-Za-z0-9_]*$\") then\n\t\t\t\t\ttable.insert(out, k) -- plain identifier key, no need to quote\n\t\t\t\telse\n\t\t\t\t\t-- bracket and quote key\n\t\t\t\t\ttable.insert(out, '[\"')\n\t\t\t\t\ttable.insert(out, k)\n\t\t\t\t\ttable.insert(out, '\"]')\n\t\t\t\tend\n\t\t\t\ttable.insert(out, \" = \")\n\t\t\telseif type_k == 'number' and k == consecutiveIndex then\n\t\t\t\t-- as long as a \"list\"-type table has consecutive entries,\n\t\t\t\t-- there's no need to output the key\n\t\t\t\tconsecutiveIndex = consecutiveIndex + 1\n\t\t\telse\n\t\t\t\t-- non-consecutive indices and non-string keys\n\t\t\t\ttable.insert(out, '[')\n\t\t\t\ttable.insert(out, tostring(k))\n\t\t\t\ttable.insert(out, '] = ')\n\t\t\tend\n\n\t\t\t-- value\n\t\t\tif type_v == 'string' then\n\t\t\t\ttable.insert(out, '\"')\n\t\t\t\ttable.insert(out, v)\n\t\t\t\ttable.insert(out, '\"')\n\t\t\telseif type_v == 'table' then\n\t\t\t\t-- recursive output of table-type values\n\t\t\t\ttable.insert(out,\n\t\t\t\t\tFormatTable(v, atIndent + (useNewlines and 1 or 0), ignoreFunc))\n\t\t\telse\n\t\t\t\ttable.insert(out, tostring(v))\n\t\t\tend\n\t\t\tif next(tb, k) then\n\t\t\t\ttable.insert(out, ',')\n\t\t\tend\n\t\t\tif useNewlines then\n\t\t\t\ttable.insert(out, '\\n')\n\t\t\tend\n\t\tend\n\tend\n\tif useNewlines then\n\t\ttable.insert(out, indentStr(atIndent))\n\tend\n\ttable.insert(out, '}')\n\treturn table.concat(out)\nend\n\nlocal WhiteChars = lookupify{' ', '\\n', '\\t', '\\r'}\n\nlocal EscapeForCharacter = {['\\r'] = '\\\\r', ['\\n'] = '\\\\n', ['\\t'] = '\\\\t', ['\"'] = '\\\\\"', [\"'\"] = \"\\\\'\", ['\\\\'] = '\\\\'}\n\nlocal CharacterForEscape = {['r'] = '\\r', ['n'] = '\\n', ['t'] = '\\t', ['\"'] = '\"', [\"'\"] = \"'\", ['\\\\'] = '\\\\'}\n\nlocal AllIdentStartChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',\n                                     'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',\n                                     's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n                                     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',\n                                     'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',\n                                     'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_'}\n\nlocal AllIdentChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',\n                                'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',\n                                's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n                                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',\n                                'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',\n                                'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_',\n                                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}\n\nlocal Digits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}\n\nlocal HexDigits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n                            'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f'}\n\nlocal Symbols = lookupify{'+', '-', '*', '/', '^', '%', ',', '{', '}', '[', ']', '(', ')', ';', '#', '.', ':'}\n\nlocal EqualSymbols = lookupify{'~', '=', '>', '<'}\n\nlocal Keywords = lookupify{\n    'and', 'break', 'do', 'else', 'elseif',\n    'end', 'false', 'for', 'function', 'goto', 'if',\n    'in', 'local', 'nil', 'not', 'or', 'repeat',\n    'return', 'then', 'true', 'until', 'while',\n}\n\nlocal BlockFollowKeyword = lookupify{'else', 'elseif', 'until', 'end'}\n\nlocal UnopSet = lookupify{'-', 'not', '#'}\n\nlocal BinopSet = lookupify{\n\t'+', '-', '*', '/', '%', '^', '#',\n\t'..', '.', ':',\n\t'>', '<', '<=', '>=', '~=', '==',\n\t'and', 'or'\n}\n\nlocal GlobalRenameIgnore = lookupify{\n\n}\n\nlocal BinaryPriority = {\n   ['+'] = {6, 6};\n   ['-'] = {6, 6};\n   ['*'] = {7, 7};\n   ['/'] = {7, 7};\n   ['%'] = {7, 7};\n   ['^'] = {10, 9};\n   ['..'] = {5, 4};\n   ['=='] = {3, 3};\n   ['~='] = {3, 3};\n   ['>'] = {3, 3};\n   ['<'] = {3, 3};\n   ['>='] = {3, 3};\n   ['<='] = {3, 3};\n   ['and'] = {2, 2};\n   ['or'] = {1, 1};\n}\nlocal UnaryPriority = 8\n\n-- Eof, Ident, Keyword, Number, String, Symbol\n\n-- decode string position to line and column number, with optional start values\nlocal function _decode_position(text, pos, line, col)\n\tline, col = line or 1, col or 0\n\tif pos > 0 then\n\t\tif pos > text:len() then\n\t\t\tpos = text:len()\n\t\tend\n\t\tfor i = 1, pos do\n\t\t\tcol = col + 1\n\t\t\tif text:sub(i, i) == '\\n' then\n\t\t\t\tline = line + 1\n\t\t\t\tcol = 0\n\t\t\tend\n\t\tend\n\t\tif col == 0 then\n\t\t\tcol = 1\n\t\tend\n\tend\n\treturn line, col\nend\n\nlocal function CreateLuaTokenStream(text)\n\t-- Tracking for the current position in the buffer, and\n\t-- the current line / character we are on.\n\tlocal p = 1\n\n\t-- Output buffer for tokens\n\tlocal tokenBuffer = {}\n\n\t-- Get a character, or '' if at eof\n\tlocal function look(n)\n\t\tn = p + (n or 0)\n\t\treturn text:sub(n, n)\n\tend\n\tlocal function get()\n\t\tlocal c = text:sub(p, p)\n\t\tp = p + 1\n\t\treturn c\n\tend\n\n\t-- Error\n\tlocal function _error(str)\n\t\tlocal line, col = _decode_position(text, p)\n\t\tfor _, token in pairs(tokenBuffer) do\n\t\t\tprint(token.Type..\"<\"..token.Source..\">\")\n\t\tend\n\t\terror(\"file<\"..line..\":\"..col..\">: \"..str)\n\tend\n\n\t-- Consume a long data with equals count of `eqcount'\n\tlocal function longdata(eqcount)\n\t\twhile true do\n\t\t\tlocal c = get()\n\t\t\tif c == '' then\n\t\t\t\t_error(\"Unfinished long string.\")\n\t\t\telseif c == ']' then\n\t\t\t\tlocal done = true -- Until contested\n\t\t\t\tfor _ = 1, eqcount do\n\t\t\t\t\tif look() == '=' then\n\t\t\t\t\t\tp = p + 1\n\t\t\t\t\telse\n\t\t\t\t\t\tdone = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tif done and get() == ']' then\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\t-- Get the opening part for a long data `[` `=`* `[`\n\t-- Precondition: The first `[` has been consumed\n\t-- Return: nil or the equals count\n\tlocal function getopen()\n\t\tlocal eqcount = 0\n\t\twhile look(eqcount) == '=' do\n\t\t\teqcount = eqcount + 1\n\t\tend\n\t\tif look(eqcount) == '[' then\n\t\t\tp = p + eqcount + 1\n\t\t\treturn eqcount\n\t\tend\n\t\treturn nil\n\tend\n\n\tlocal whiteStart, tokenStart -- used in main loop, upvalues for token()\n\n\t-- Add token\n\tlocal function token(type)\n\t\tlocal tk = {\n\t\t\tType = type;\n\t\t\tLeadingWhite = text:sub(whiteStart, tokenStart-1);\n\t\t\tSource = text:sub(tokenStart, p-1);\n\t\t}\n\t\ttable.insert(tokenBuffer, tk)\n\t\treturn tk\n\tend\n\n\t-- Parse tokens loop\n\twhile true do\n\t\t-- Mark the whitespace start\n\t\twhiteStart = p\n\n\t\t-- Get the leading whitespace + comments\n\t\twhile true do\n\t\t\tlocal c = look()\n\t\t\tif c == '' then\n\t\t\t\tbreak\n\t\t\telseif c == '-' then\n\t\t\t\tif look(1) == '-' then\n\t\t\t\t\tp = p + 2\n\t\t\t\t\t-- Consume comment body\n\t\t\t\t\tif look() == '[' then\n\t\t\t\t\t\tp = p + 1\n\t\t\t\t\t\tlocal eqcount = getopen()\n\t\t\t\t\t\tif eqcount then\n\t\t\t\t\t\t\t-- Long comment body\n\t\t\t\t\t\t\tlongdata(eqcount)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t-- Normal comment body\n\t\t\t\t\t\t\tlocal c2\n\t\t\t\t\t\t\trepeat\n\t\t\t\t\t\t\t\tc2 = get()\n\t\t\t\t\t\t\tuntil c2 == '' or c2 == '\\n'\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\t-- Normal comment body\n\t\t\t\t\t\tlocal c2\n\t\t\t\t\t\trepeat\n\t\t\t\t\t\t\tc2 = get()\n\t\t\t\t\t\tuntil c2 == '' or c2 == '\\n'\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\telseif WhiteChars[c] then\n\t\t\t\tp = p + 1\n\t\t\telse\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\t-- Mark the token start\n\t\ttokenStart = p\n\n\t\t-- Switch on token type\n\t\tlocal c1 = get()\n\t\tif c1 == '' then\n\t\t\t-- End of file\n\t\t\ttoken('Eof')\n\t\t\tbreak\n\t\telseif c1 == '\\'' or c1 == '\\\"' then\n\t\t\t-- String constant\n\t\t\tlocal c2\n\t\t\trepeat\n\t\t\t\tc2 = get()\n\t\t\t\tif c2 == '' then\n\t\t\t\t\t_error(\"Unfinished string.\")\n\t\t\t\telseif c2 == '\\\\' then\n\t\t\t\t\tlocal c3 = get()\n\t\t\t\t\tif not(Digits[c3] or CharacterForEscape[c3]) then\n\t\t\t\t\t\t_error(\"Invalid Escape Sequence `\"..c3..\"`.\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tuntil c2 == c1\n\t\t\ttoken('String')\n\t\telseif AllIdentStartChars[c1] then\n\t\t\t-- Ident or Keyword\n\t\t\twhile AllIdentChars[look()] do\n\t\t\t\tp = p + 1\n\t\t\tend\n\t\t\tif Keywords[text:sub(tokenStart, p-1)] then\n\t\t\t\ttoken('Keyword')\n\t\t\telse\n\t\t\t\ttoken('Ident')\n\t\t\tend\n\t\telseif Digits[c1] or (c1 == '.' and Digits[look()]) then\n\t\t\t-- Number\n\t\t\tif c1 == '0' and look() == 'x' then\n\t\t\t\tp = p + 1\n\t\t\t\t-- Hex number\n\t\t\t\twhile HexDigits[look()] do\n\t\t\t\t\tp = p + 1\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- Normal Number\n\t\t\t\twhile Digits[look()] do\n\t\t\t\t\tp = p + 1\n\t\t\t\tend\n\t\t\t\tif look() == '.' then\n\t\t\t\t\t-- With decimal point\n\t\t\t\t\tp = p + 1\n\t\t\t\t\twhile Digits[look()] do\n\t\t\t\t\t\tp = p + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tif look() == 'e' or look() == 'E' then\n\t\t\t\t\t-- With exponent\n\t\t\t\t\tp = p + 1\n\t\t\t\t\tif look() == '-' then\n\t\t\t\t\t\tp = p + 1\n\t\t\t\t\tend\n\t\t\t\t\twhile Digits[look()] do\n\t\t\t\t\t\tp = p + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\ttoken('Number')\n\t\telseif c1 == '[' then\n\t\t\t-- '[' Symbol or Long String\n\t\t\tlocal eqCount = getopen()\n\t\t\tif eqCount then\n\t\t\t\t-- Long string\n\t\t\t\tlongdata(eqCount)\n\t\t\t\ttoken('String')\n\t\t\telse\n\t\t\t\t-- Symbol\n\t\t\t\ttoken('Symbol')\n\t\t\tend\n\t\telseif c1 == '.' then\n\t\t\t-- Greedily consume up to 3 `.` for . / .. / ... tokens\n\t\t\tif look() == '.' then\n\t\t\t\tget()\n\t\t\t\tif look() == '.' then\n\t\t\t\t\tget()\n\t\t\t\tend\n\t\t\tend\n\t\t\ttoken('Symbol')\n\t\telseif EqualSymbols[c1] then\n\t\t\tif look() == '=' then\n\t\t\t\tp = p + 1\n\t\t\tend\n\t\t\ttoken('Symbol')\n\t\telseif Symbols[c1] then\n\t\t\ttoken('Symbol')\n\t\telse\n\t\t\t_error(\"Bad symbol `\"..c1..\"` in source.\")\n\t\tend\n\tend\n\treturn tokenBuffer\nend\n\nlocal function CreateLuaParser(tokens)\n\tif type(tokens) == \"string\" then\n\t\t-- tokenize from string first\n\t\ttokens = CreateLuaTokenStream(tokens)\n\tend\n\t-- Token stream and pointer into it\n\tassert(type(tokens) == \"table\")\n\t-- for _, tok in pairs(tokens) do\n\t-- \tprint(tok.Type..\": \"..tok.Source)\n\t-- end\n\tlocal p = 1\n\n\tlocal function get()\n\t\tlocal tok = tokens[p]\n\t\tif p < #tokens then\n\t\t\tp = p + 1\n\t\tend\n\t\treturn tok\n\tend\n\tlocal function peek(n)\n\t\tn = p + (n or 0)\n\t\treturn tokens[n] or tokens[#tokens]\n\tend\n\n\tlocal function getTokenStartPosition(token)\n\t\tlocal line, col = 1, 0\n\t\tlocal tkNum = 1\n\t\trepeat\n\t\t\tlocal tk = tokens[tkNum]\n\t\t\ttkNum = tkNum + 1\n\t\t\tlocal text = tk.LeadingWhite\n\t\t\tif tk ~= token then\n\t\t\t\ttext = text .. tk.Source\n\t\t\tend\n\t\t\tline, col = _decode_position(text, #text, line, col)\n\t\tuntil tk == token\n\t\treturn line..\":\"..(col+1)\n\tend\n\tlocal function debugMark()\n\t\tlocal tk = peek()\n\t\treturn \"<\"..tk.Type..\" `\"..tk.Source..\"`> at: \"..getTokenStartPosition(tk)\n\tend\n\n\tlocal function isBlockFollow()\n\t\tlocal tok = peek()\n\t\treturn tok.Type == 'Eof' or (tok.Type == 'Keyword' and BlockFollowKeyword[tok.Source])\n\tend\n\tlocal function isUnop()\n\t\treturn UnopSet[peek().Source] or false\n\tend\n\tlocal function isBinop()\n\t\treturn BinopSet[peek().Source] or false\n\tend\n\tlocal function expect(type, source, type2, source2)\n\t\tlocal tk = peek()\n\t\tif tk.Type == type and (source == nil or tk.Source == source) then\n\t\t\treturn get()\n\t\telseif tk.Type == type2 and (source2 == nil or tk.Source == source2) then\n\t\t\treturn get()\n\t\telse\n\t\t\tfor i = -3, 3 do\n\t\t\t\tprint(\"Tokens[\"..i..\"] = `\"..peek(i).Source..\"`\")\n\t\t\tend\n\t\t\tif source then\n\t\t\t\terror(getTokenStartPosition(tk)..\": `\"..source..\"` expected.\")\n\t\t\telse\n\t\t\t\terror(getTokenStartPosition(tk)..\": \"..type\n\t\t\t\t\t  ..(type2 and (\" or \"..type2) or \"\")\n\t\t\t\t\t  ..\" expected.\")\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function MkNode(node)\n\t\tlocal function _GetSelfToken(self)\n\t\t\treturn self.Token\n\t\tend\n\n\t\tlocal getf = node.GetFirstToken or _GetSelfToken\n\t\tlocal getl = node.GetLastToken or _GetSelfToken\n\t\tfunction node:GetFirstToken()\n\t\t\tlocal t = getf(self)\n\t\t\tassert(t)\n\t\t\treturn t\n\t\tend\n\t\tfunction node:GetLastToken()\n\t\t\tlocal t = getl(self)\n\t\t\tassert(t)\n\t\t\treturn t\n\t\tend\n\t\treturn node\n\tend\n\n\t-- Forward decls\n\tlocal block, expr\n\n\t-- Expression list\n\tlocal function exprlist()\n\t\tlocal exprList = {}\n\t\tlocal commaList = {}\n\t\ttable.insert(exprList, expr())\n\t\twhile peek().Source == ',' do\n\t\t\ttable.insert(commaList, get())\n\t\t\ttable.insert(exprList, expr())\n\t\tend\n\t\treturn exprList, commaList\n\tend\n\n\tlocal function prefixexpr()\n\t\tlocal tk = peek()\n\t\tif tk.Source == '(' then\n\t\t\tlocal oparenTk = get()\n\t\t\tlocal inner = expr()\n\t\t\tlocal cparenTk = expect('Symbol', ')')\n\t\t\treturn MkNode{\n\t\t\t\tType = 'ParenExpr';\n\t\t\t\tExpression = inner;\n\t\t\t\tToken_OpenParen = oparenTk;\n\t\t\t\tToken_CloseParen = cparenTk;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_OpenParen\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Token_CloseParen\n\t\t\t\tend;\n\t\t\t}\n\t\telseif tk.Type == 'Ident' then\n\t\t\treturn MkNode{\n\t\t\t\tType = 'VariableExpr';\n\t\t\t\tToken = get();\n\t\t\t}\n\t\telse\n\t\t\tprint(debugMark())\n\t\t\terror(getTokenStartPosition(tk)..\": Unexpected symbol\")\n\t\tend\n\tend\n\n\tlocal function tableexpr()\n\t\tlocal obrace = expect('Symbol', '{')\n\t\tlocal entries = {}\n\t\tlocal separators = {}\n\t\twhile peek().Source ~= '}' do\n\t\t\tif peek().Source == '[' then\n\t\t\t\t-- Index\n\t\t\t\tlocal obrac = get()\n\t\t\t\tlocal index = expr()\n\t\t\t\tlocal cbrac = expect('Symbol', ']')\n\t\t\t\tlocal eq = expect('Symbol', '=')\n\t\t\t\tlocal value = expr()\n\t\t\t\ttable.insert(entries, {\n\t\t\t\t\tEntryType = 'Index';\n\t\t\t\t\tIndex = index;\n\t\t\t\t\tValue = value;\n\t\t\t\t\tToken_OpenBracket = obrac;\n\t\t\t\t\tToken_CloseBracket = cbrac;\n\t\t\t\t\tToken_Equals = eq;\n\t\t\t\t})\n\t\t\telseif peek().Type == 'Ident' and peek(1).Source == '=' then\n\t\t\t\t-- Field\n\t\t\t\tlocal field = get()\n\t\t\t\tlocal eq = get()\n\t\t\t\tlocal value = expr()\n\t\t\t\ttable.insert(entries, {\n\t\t\t\t\tEntryType = 'Field';\n\t\t\t\t\tField = field;\n\t\t\t\t\tValue = value;\n\t\t\t\t\tToken_Equals = eq;\n\t\t\t\t})\n\t\t\telse\n\t\t\t\t-- Value\n\t\t\t\tlocal value = expr()\n\t\t\t\ttable.insert(entries, {\n\t\t\t\t\tEntryType = 'Value';\n\t\t\t\t\tValue = value;\n\t\t\t\t})\n\t\t\tend\n\n\t\t\t-- Comma or Semicolon separator\n\t\t\tif peek().Source == ',' or peek().Source == ';' then\n\t\t\t\ttable.insert(separators, get())\n\t\t\telse\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\t\tlocal cbrace = expect('Symbol', '}')\n\t\treturn MkNode{\n\t\t\tType = 'TableLiteral';\n\t\t\tEntryList = entries;\n\t\t\tToken_SeparatorList = separators;\n\t\t\tToken_OpenBrace = obrace;\n\t\t\tToken_CloseBrace = cbrace;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_OpenBrace\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Token_CloseBrace\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- List of identifiers\n\tlocal function varlist(isFuncDecl)\n\t\tlocal varList, commaList, token = {}, {}, peek()\n\t\tif token.Type == 'Ident' then\n\t\t\ttable.insert(varList, get())\n\t\telseif isFuncDecl and (token.Source == '...') then\n\t\t\ttable.insert(varList, get())\n\t\tend\n\t\twhile peek().Source == ',' do\n\t\t\ttable.insert(commaList, get())\n\t\t\tif isFuncDecl then\n\t\t\t\ttoken = expect('Ident', nil, 'Symbol', '...')\n\t\t\telse\n\t\t\t\ttoken = expect('Ident')\n\t\t\tend\n\t\t\ttable.insert(varList, token)\n\t\tend\n\t\treturn varList, commaList\n\tend\n\n\t-- Body\n\tlocal function blockbody(terminator)\n\t\tlocal body = block()\n\t\tlocal after = peek()\n\t\tif after.Type == 'Keyword' and after.Source == terminator then\n\t\t\tget()\n\t\t\treturn body, after\n\t\telse\n\t\t\tprint(after.Type, after.Source)\n\t\t\terror(getTokenStartPosition(after)..\": \"..terminator..\" expected.\")\n\t\tend\n\tend\n\n\t-- Function declaration\n\tlocal function funcdecl(isAnonymous)\n\t\tlocal functionKw = get()\n\t\t--\n\t\tlocal nameChain, nameChainSeparator\n\t\t--\n\t\tif not isAnonymous then\n\t\t\tnameChain = {}\n\t\t\tnameChainSeparator = {}\n\t\t\t--\n\t\t\ttable.insert(nameChain, expect('Ident'))\n\t\t\t--\n\t\t\twhile peek().Source == '.' do\n\t\t\t\ttable.insert(nameChainSeparator, get())\n\t\t\t\ttable.insert(nameChain, expect('Ident'))\n\t\t\tend\n\t\t\tif peek().Source == ':' then\n\t\t\t\ttable.insert(nameChainSeparator, get())\n\t\t\t\ttable.insert(nameChain, expect('Ident'))\n\t\t\tend\n\t\tend\n\t\t--\n\t\tlocal oparenTk = expect('Symbol', '(')\n\t\tlocal argList, argCommaList = varlist(true)\n\t\tlocal cparenTk = expect('Symbol', ')')\n\t\tlocal fbody, enTk = blockbody('end')\n\t\t--\n\t\treturn MkNode{\n\t\t\tType = (isAnonymous and 'FunctionLiteral' or 'FunctionStat');\n\t\t\tNameChain = nameChain;\n\t\t\tArgList = argList;\n\t\t\tBody = fbody;\n\t\t\t--\n\t\t\tToken_Function = functionKw;\n\t\t\tToken_NameChainSeparator = nameChainSeparator;\n\t\t\tToken_OpenParen = oparenTk;\n\t\t\tToken_ArgCommaList = argCommaList;\n\t\t\tToken_CloseParen = cparenTk;\n\t\t\tToken_End = enTk;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_Function\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Token_End;\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- Argument list passed to a function\n\tlocal function functionargs()\n\t\tlocal tk = peek()\n\t\tif tk.Source == '(' then\n\t\t\tlocal oparenTk = get()\n\t\t\tlocal argList = {}\n\t\t\tlocal argCommaList = {}\n\t\t\twhile peek().Source ~= ')' do\n\t\t\t\ttable.insert(argList, expr())\n\t\t\t\tif peek().Source == ',' then\n\t\t\t\t\ttable.insert(argCommaList, get())\n\t\t\t\telse\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\t\tlocal cparenTk = expect('Symbol', ')')\n\t\t\treturn MkNode{\n\t\t\t\tCallType = 'ArgCall';\n\t\t\t\tArgList = argList;\n\t\t\t\t--\n\t\t\t\tToken_CommaList = argCommaList;\n\t\t\t\tToken_OpenParen = oparenTk;\n\t\t\t\tToken_CloseParen = cparenTk;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_OpenParen\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Token_CloseParen\n\t\t\t\tend;\n\t\t\t}\n\t\telseif tk.Source == '{' then\n\t\t\treturn MkNode{\n\t\t\t\tCallType = 'TableCall';\n\t\t\t\tTableExpr = expr();\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.TableExpr:GetFirstToken()\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.TableExpr:GetLastToken()\n\t\t\t\tend;\n\t\t\t}\n\t\telseif tk.Type == 'String' then\n\t\t\treturn MkNode{\n\t\t\t\tCallType = 'StringCall';\n\t\t\t\tToken = get();\n\t\t\t}\n\t\telse\n\t\t\terror(\"Function arguments expected.\")\n\t\tend\n\tend\n\n\tlocal function callexpr(base)\n\t\treturn MkNode{\n\t\t\tType = 'CallExpr';\n\t\t\tBase = base;\n\t\t\tFunctionArguments = functionargs();\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Base:GetFirstToken()\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.FunctionArguments:GetLastToken()\n\t\t\tend;\n\t\t}\n\tend\n\n\tlocal function primaryexpr()\n\t\tlocal base = prefixexpr()\n\t\tassert(base, \"nil prefixexpr\")\n\t\twhile true do\n\t\t\tlocal tk = peek()\n\t\t\tif tk.Source == '.' then\n\t\t\t\tlocal dotTk = get()\n\t\t\t\tlocal fieldName = expect('Ident')\n\t\t\t\tbase = MkNode{\n\t\t\t\t\tType = 'FieldExpr';\n\t\t\t\t\tBase = base;\n\t\t\t\t\tField = fieldName;\n\t\t\t\t\tToken_Dot = dotTk;\n\t\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\t\treturn self.Base:GetFirstToken()\n\t\t\t\t\tend;\n\t\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\t\treturn self.Field\n\t\t\t\t\tend;\n\t\t\t\t}\n\t\t\telseif tk.Source == ':' then\n\t\t\t\tlocal colonTk = get()\n\t\t\t\tlocal methodName = expect('Ident')\n\t\t\t\tlocal fargs = functionargs()\n\t\t\t\tbase = MkNode{\n\t\t\t\t\tType = 'MethodExpr';\n\t\t\t\t\tBase = base;\n\t\t\t\t\tMethod = methodName;\n\t\t\t\t\tFunctionArguments = fargs;\n\t\t\t\t\tToken_Colon = colonTk;\n\t\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\t\treturn self.Base:GetFirstToken()\n\t\t\t\t\tend;\n\t\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\t\treturn self.FunctionArguments:GetLastToken()\n\t\t\t\t\tend;\n\t\t\t\t}\n\t\t\telseif tk.Source == '[' then\n\t\t\t\tlocal obrac = get()\n\t\t\t\tlocal index = expr()\n\t\t\t\tlocal cbrac = expect('Symbol', ']')\n\t\t\t\tbase = MkNode{\n\t\t\t\t\tType = 'IndexExpr';\n\t\t\t\t\tBase = base;\n\t\t\t\t\tIndex = index;\n\t\t\t\t\tToken_OpenBracket = obrac;\n\t\t\t\t\tToken_CloseBracket = cbrac;\n\t\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\t\treturn self.Base:GetFirstToken()\n\t\t\t\t\tend;\n\t\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\t\treturn self.Token_CloseBracket\n\t\t\t\t\tend;\n\t\t\t\t}\n\t\t\telseif tk.Source == '{' then\n\t\t\t\tbase = callexpr(base) -- TableCall\n\t\t\telseif tk.Source == '(' then\n\t\t\t\tbase = callexpr(base) -- ArgCall\n\t\t\telseif tk.Type == 'String' then\n\t\t\t\tbase = callexpr(base) -- StringCall\n\t\t\telse\n\t\t\t\treturn base\n\t\t\tend\n\t\tend\n\tend\n\n\t-- Create a \"literal\" node with a given type (string)\n\tlocal function literal(_type)\n\t\treturn MkNode{\n\t\t\tType = _type;\n\t\t\tToken = get();\n\t\t}\n\tend\n\n\tlocal function simpleexpr()\n\t\tlocal tk = peek()\n\t\tif tk.Type == 'Number' then\n\t\t\treturn literal('NumberLiteral')\n\t\telseif tk.Type == 'String' then\n\t\t\treturn literal('StringLiteral')\n\t\telseif tk.Source == 'nil' then\n\t\t\treturn literal('NilLiteral')\n\t\telseif tk.Source == 'true' or tk.Source == 'false' then\n\t\t\treturn literal('BooleanLiteral')\n\t\telseif tk.Source == '...' then\n\t\t\treturn literal('VargLiteral')\n\t\telseif tk.Source == '{' then\n\t\t\treturn tableexpr()\n\t\telseif tk.Source == 'function' then\n\t\t\treturn funcdecl(true)\n\t\telse\n\t\t\treturn primaryexpr()\n\t\tend\n\tend\n\n\tlocal function subexpr(limit)\n\t\tlocal curNode\n\n\t\t-- Initial Base Expression\n\t\tif isUnop() then\n\t\t\tlocal opTk = get()\n\t\t\tlocal ex = subexpr(UnaryPriority)\n\t\t\tcurNode = MkNode{\n\t\t\t\tType = 'UnopExpr';\n\t\t\t\tToken_Op = opTk;\n\t\t\t\tRhs = ex;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_Op\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Rhs:GetLastToken()\n\t\t\t\tend;\n\t\t\t}\n\t\telse\n\t\t\tcurNode = simpleexpr()\n\t\t\tassert(curNode, \"nil simpleexpr\")\n\t\tend\n\n\t\t-- Apply Precedence Recursion Chain\n\t\twhile isBinop() and BinaryPriority[peek().Source][1] > limit do\n\t\t\tlocal opTk = get()\n\t\t\tlocal rhs = subexpr(BinaryPriority[opTk.Source][2])\n\t\t\tassert(rhs, \"RhsNeeded\")\n\t\t\tcurNode = MkNode{\n\t\t\t\tType = 'BinopExpr';\n\t\t\t\tLhs = curNode;\n\t\t\t\tRhs = rhs;\n\t\t\t\tToken_Op = opTk;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Lhs:GetFirstToken()\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Rhs:GetLastToken()\n\t\t\t\tend;\n\t\t\t}\n\t\tend\n\n\t\t-- Return result\n\t\treturn curNode\n\tend\n\n\t-- Expression\n\texpr = function()\n\t\treturn subexpr(0)\n\tend\n\n\t-- Expression statement\n\tlocal function exprstat()\n\t\tlocal ex = primaryexpr()\n\t\tif ex.Type == 'MethodExpr' or ex.Type == 'CallExpr' then\n\t\t\t-- all good, calls can be statements\n\t\t\treturn MkNode{\n\t\t\t\tType = 'CallExprStat';\n\t\t\t\tExpression = ex;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Expression:GetFirstToken()\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Expression:GetLastToken()\n\t\t\t\tend;\n\t\t\t}\n\t\telse\n\t\t\t-- Assignment expr\n\t\t\tlocal lhs = {ex}\n\t\t\tlocal lhsSeparator = {}\n\t\t\twhile peek().Source == ',' do\n\t\t\t\ttable.insert(lhsSeparator, get())\n\t\t\t\tlocal lhsPart = primaryexpr()\n\t\t\t\tif lhsPart.Type == 'MethodExpr' or lhsPart.Type == 'CallExpr' then\n\t\t\t\t\terror(\"Bad left hand side of assignment\")\n\t\t\t\tend\n\t\t\t\ttable.insert(lhs, lhsPart)\n\t\t\tend\n\t\t\tlocal eq = expect('Symbol', '=')\n\t\t\tlocal rhs = {expr()}\n\t\t\tlocal rhsSeparator = {}\n\t\t\twhile peek().Source == ',' do\n\t\t\t\ttable.insert(rhsSeparator, get())\n\t\t\t\ttable.insert(rhs, expr())\n\t\t\tend\n\t\t\treturn MkNode{\n\t\t\t\tType = 'AssignmentStat';\n\t\t\t\tRhs = rhs;\n\t\t\t\tLhs = lhs;\n\t\t\t\tToken_Equals = eq;\n\t\t\t\tToken_LhsSeparatorList = lhsSeparator;\n\t\t\t\tToken_RhsSeparatorList = rhsSeparator;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Lhs[1]:GetFirstToken()\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Rhs[#self.Rhs]:GetLastToken()\n\t\t\t\tend;\n\t\t\t}\n\t\tend\n\tend\n\n\t-- If statement\n\tlocal function ifstat()\n\t\tlocal ifKw = get()\n\t\tlocal condition = expr()\n\t\tlocal thenKw = expect('Keyword', 'then')\n\t\tlocal ifBody = block()\n\t\tlocal elseClauses = {}\n\t\twhile peek().Source == 'elseif' or peek().Source == 'else' do\n\t\t\tlocal elseifKw = get()\n\t\t\tlocal elseifCondition, elseifThenKw\n\t\t\tif elseifKw.Source == 'elseif' then\n\t\t\t\telseifCondition = expr()\n\t\t\t\telseifThenKw = expect('Keyword', 'then')\n\t\t\tend\n\t\t\tlocal elseifBody = block()\n\t\t\ttable.insert(elseClauses, {\n\t\t\t\tCondition = elseifCondition;\n\t\t\t\tBody = elseifBody;\n\t\t\t\t--\n\t\t\t\tClauseType = elseifKw.Source;\n\t\t\t\tToken = elseifKw;\n\t\t\t\tToken_Then = elseifThenKw;\n\t\t\t})\n\t\t\tif elseifKw.Source == 'else' then\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\t\tlocal enKw = expect('Keyword', 'end')\n\t\treturn MkNode{\n\t\t\tType = 'IfStat';\n\t\t\tCondition = condition;\n\t\t\tBody = ifBody;\n\t\t\tElseClauseList = elseClauses;\n\t\t\t--\n\t\t\tToken_If = ifKw;\n\t\t\tToken_Then = thenKw;\n\t\t\tToken_End = enKw;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_If\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Token_End\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- Do statement\n\tlocal function dostat()\n\t\tlocal doKw = get()\n\t\tlocal body, enKw = blockbody('end')\n\t\t--\n\t\treturn MkNode{\n\t\t\tType = 'DoStat';\n\t\t\tBody = body;\n\t\t\t--\n\t\t\tToken_Do = doKw;\n\t\t\tToken_End = enKw;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_Do\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Token_End\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- While statement\n\tlocal function whilestat()\n\t\tlocal whileKw = get()\n\t\tlocal condition = expr()\n\t\tlocal doKw = expect('Keyword', 'do')\n\t\tlocal body, enKw = blockbody('end')\n\t\t--\n\t\treturn MkNode{\n\t\t\tType = 'WhileStat';\n\t\t\tCondition = condition;\n\t\t\tBody = body;\n\t\t\t--\n\t\t\tToken_While = whileKw;\n\t\t\tToken_Do = doKw;\n\t\t\tToken_End = enKw;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_While\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Token_End\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- For statement\n\tlocal function forstat()\n\t\tlocal forKw = get()\n\t\tlocal loopVars, loopVarCommas = varlist()\n\t\tif peek().Source == '=' then\n\t\t\tlocal eqTk = get()\n\t\t\tlocal exprList, exprCommaList = exprlist()\n\t\t\tif #exprList < 2 or #exprList > 3 then\n\t\t\t\terror(\"expected 2 or 3 values for range bounds\")\n\t\t\tend\n\t\t\tlocal doTk = expect('Keyword', 'do')\n\t\t\tlocal body, enTk = blockbody('end')\n\t\t\treturn MkNode{\n\t\t\t\tType = 'NumericForStat';\n\t\t\t\tVarList = loopVars;\n\t\t\t\tRangeList = exprList;\n\t\t\t\tBody = body;\n\t\t\t\t--\n\t\t\t\tToken_For = forKw;\n\t\t\t\tToken_VarCommaList = loopVarCommas;\n\t\t\t\tToken_Equals = eqTk;\n\t\t\t\tToken_RangeCommaList = exprCommaList;\n\t\t\t\tToken_Do = doTk;\n\t\t\t\tToken_End = enTk;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_For\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Token_End\n\t\t\t\tend;\n\t\t\t}\n\t\telseif peek().Source == 'in' then\n\t\t\tlocal inTk = get()\n\t\t\tlocal exprList, exprCommaList = exprlist()\n\t\t\tlocal doTk = expect('Keyword', 'do')\n\t\t\tlocal body, enTk = blockbody('end')\n\t\t\treturn MkNode{\n\t\t\t\tType = 'GenericForStat';\n\t\t\t\tVarList = loopVars;\n\t\t\t\tGeneratorList = exprList;\n\t\t\t\tBody = body;\n\t\t\t\t--\n\t\t\t\tToken_For = forKw;\n\t\t\t\tToken_VarCommaList = loopVarCommas;\n\t\t\t\tToken_In = inTk;\n\t\t\t\tToken_GeneratorCommaList = exprCommaList;\n\t\t\t\tToken_Do = doTk;\n\t\t\t\tToken_End = enTk;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_For\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.Token_End\n\t\t\t\tend;\n\t\t\t}\n\t\telse\n\t\t\terror(\"`=` or in expected\")\n\t\tend\n\tend\n\n\t-- Repeat statement\n\tlocal function repeatstat()\n\t\tlocal repeatKw = get()\n\t\tlocal body, untilTk = blockbody('until')\n\t\tlocal condition = expr()\n\t\treturn MkNode{\n\t\t\tType = 'RepeatStat';\n\t\t\tBody = body;\n\t\t\tCondition = condition;\n\t\t\t--\n\t\t\tToken_Repeat = repeatKw;\n\t\t\tToken_Until = untilTk;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_Repeat\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Condition:GetLastToken()\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- Local var declaration\n\tlocal function localdecl()\n\t\tlocal localKw = get()\n\t\tif peek().Source == 'function' then\n\t\t\t-- Local function def\n\t\t\tlocal funcStat = funcdecl(false)\n\t\t\tif #funcStat.NameChain > 1 then\n\t\t\t\terror(getTokenStartPosition(funcStat.Token_NameChainSeparator[1])..\": `(` expected.\")\n\t\t\tend\n\t\t\treturn MkNode{\n\t\t\t\tType = 'LocalFunctionStat';\n\t\t\t\tFunctionStat = funcStat;\n\t\t\t\tToken_Local = localKw;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_Local\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\treturn self.FunctionStat:GetLastToken()\n\t\t\t\tend;\n\t\t\t}\n\t\telseif peek().Type == 'Ident' then\n\t\t\t-- Local variable declaration\n\t\t\tlocal varList, varCommaList = varlist()\n\t\t\tlocal exprList, exprCommaList = {}, {}\n\t\t\tlocal eqToken\n\t\t\tif peek().Source == '=' then\n\t\t\t\teqToken = get()\n\t\t\t\texprList, exprCommaList = exprlist()\n\t\t\tend\n\t\t\treturn MkNode{\n\t\t\t\tType = 'LocalVarStat';\n\t\t\t\tVarList = varList;\n\t\t\t\tExprList = exprList;\n\t\t\t\tToken_Local = localKw;\n\t\t\t\tToken_Equals = eqToken;\n\t\t\t\tToken_VarCommaList = varCommaList;\n\t\t\t\tToken_ExprCommaList = exprCommaList;\n\t\t\t\tGetFirstToken = function(self)\n\t\t\t\t\treturn self.Token_Local\n\t\t\t\tend;\n\t\t\t\tGetLastToken = function(self)\n\t\t\t\t\tif #self.ExprList > 0 then\n\t\t\t\t\t\treturn self.ExprList[#self.ExprList]:GetLastToken()\n\t\t\t\t\telse\n\t\t\t\t\t\treturn self.VarList[#self.VarList]\n\t\t\t\t\tend\n\t\t\t\tend;\n\t\t\t}\n\t\telse\n\t\t\terror(\"`function` or ident expected\")\n\t\tend\n\tend\n\n\t-- Return statement\n\tlocal function retstat()\n\t\tlocal returnKw = get()\n\t\tlocal exprList, commaList\n\t\tif isBlockFollow() or peek().Source == ';' then\n\t\t\texprList = {}\n\t\t\tcommaList = {}\n\t\telse\n\t\t\texprList, commaList = exprlist()\n\t\tend\n\t\treturn {\n\t\t\tType = 'ReturnStat';\n\t\t\tExprList = exprList;\n\t\t\tToken_Return = returnKw;\n\t\t\tToken_CommaList = commaList;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_Return\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\tif #self.ExprList > 0 then\n\t\t\t\t\treturn self.ExprList[#self.ExprList]:GetLastToken()\n\t\t\t\telse\n\t\t\t\t\treturn self.Token_Return\n\t\t\t\tend\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- Break statement\n\tlocal function breakstat()\n\t\tlocal breakKw = get()\n\t\treturn {\n\t\t\tType = 'BreakStat';\n\t\t\tToken_Break = breakKw;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\treturn self.Token_Break\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\treturn self.Token_Break\n\t\t\tend;\n\t\t}\n\tend\n\n\t-- Expression\n\tlocal function statement()\n\t\tlocal tok = peek()\n\t\tif tok.Source == 'if' then\n\t\t\treturn false, ifstat()\n\t\telseif tok.Source == 'while' then\n\t\t\treturn false, whilestat()\n\t\telseif tok.Source == 'do' then\n\t\t\treturn false, dostat()\n\t\telseif tok.Source == 'for' then\n\t\t\treturn false, forstat()\n\t\telseif tok.Source == 'repeat' then\n\t\t\treturn false, repeatstat()\n\t\telseif tok.Source == 'function' then\n\t\t\treturn false, funcdecl(false)\n\t\telseif tok.Source == 'local' then\n\t\t\treturn false, localdecl()\n\t\telseif tok.Source == 'return' then\n\t\t\treturn true, retstat()\n\t\telseif tok.Source == 'break' then\n\t\t\treturn true, breakstat()\n\t\telse\n\t\t\treturn false, exprstat()\n\t\tend\n\tend\n\n\t-- Chunk\n\tblock = function()\n\t\tlocal statements = {}\n\t\tlocal semicolons = {}\n\t\tlocal isLast = false\n\t\twhile not isLast and not isBlockFollow() do\n\t\t\t-- Parse statement\n\t\t\tlocal stat\n\t\t\tisLast, stat = statement()\n\t\t\ttable.insert(statements, stat)\n\t\t\tlocal next = peek()\n\t\t\tif next.Type == 'Symbol' and next.Source == ';' then\n\t\t\t\tsemicolons[#statements] = get()\n\t\t\tend\n\t\tend\n\t\treturn {\n\t\t\tType = 'StatList';\n\t\t\tStatementList = statements;\n\t\t\tSemicolonList = semicolons;\n\t\t\tGetFirstToken = function(self)\n\t\t\t\tif #self.StatementList == 0 then\n\t\t\t\t\treturn nil\n\t\t\t\telse\n\t\t\t\t\treturn self.StatementList[1]:GetFirstToken()\n\t\t\t\tend\n\t\t\tend;\n\t\t\tGetLastToken = function(self)\n\t\t\t\tif #self.StatementList == 0 then\n\t\t\t\t\treturn nil\n\t\t\t\telseif self.SemicolonList[#self.StatementList] then\n\t\t\t\t\t-- Last token may be one of the semicolon separators\n\t\t\t\t\treturn self.SemicolonList[#self.StatementList]\n\t\t\t\telse\n\t\t\t\t\treturn self.StatementList[#self.StatementList]:GetLastToken()\n\t\t\t\tend\n\t\t\tend;\n\t\t}\n\tend\n\n\treturn block()\nend\n\nlocal function VisitAst(ast, visitors)\n\tlocal ExprType = lookupify{\n\t\t'BinopExpr'; 'UnopExpr';\n\t\t'NumberLiteral'; 'StringLiteral'; 'NilLiteral'; 'BooleanLiteral'; 'VargLiteral';\n\t\t'FieldExpr'; 'IndexExpr';\n\t\t'MethodExpr'; 'CallExpr';\n\t\t'FunctionLiteral';\n\t\t'VariableExpr';\n\t\t'ParenExpr';\n\t\t'TableLiteral';\n\t}\n\n\tlocal StatType = lookupify{\n\t\t'StatList';\n\t\t'BreakStat';\n\t\t'ReturnStat';\n\t\t'LocalVarStat';\n\t\t'LocalFunctionStat';\n\t\t'FunctionStat';\n\t\t'RepeatStat';\n\t\t'GenericForStat';\n\t\t'NumericForStat';\n\t\t'WhileStat';\n\t\t'DoStat';\n\t\t'IfStat';\n\t\t'CallExprStat';\n\t\t'AssignmentStat';\n\t}\n\n\t-- Check for typos in visitor construction\n\tfor visitorSubject, _ in pairs(visitors) do\n\t\tif not StatType[visitorSubject] and not ExprType[visitorSubject] then\n\t\t\terror(\"Invalid visitor target: `\"..visitorSubject..\"`\")\n\t\tend\n\tend\n\n\t-- Helpers to call visitors on a node\n\tlocal function preVisit(exprOrStat)\n\t\tlocal visitor = visitors[exprOrStat.Type]\n\t\tif type(visitor) == 'function' then\n\t\t\treturn visitor(exprOrStat)\n\t\telseif visitor and visitor.Pre then\n\t\t\treturn visitor.Pre(exprOrStat)\n\t\tend\n\tend\n\tlocal function postVisit(exprOrStat)\n\t\tlocal visitor = visitors[exprOrStat.Type]\n\t\tif visitor and type(visitor) == 'table' and visitor.Post then\n\t\t\treturn visitor.Post(exprOrStat)\n\t\tend\n\tend\n\n\tlocal visitExpr, visitStat\n\n\tvisitExpr = function(expr)\n\t\tif preVisit(expr) then\n\t\t\t-- Handler did custom child iteration or blocked child iteration\n\t\t\treturn\n\t\tend\n\t\tif expr.Type == 'BinopExpr' then\n\t\t\tvisitExpr(expr.Lhs)\n\t\t\tvisitExpr(expr.Rhs)\n\t\telseif expr.Type == 'UnopExpr' then\n\t\t\tvisitExpr(expr.Rhs)\n\t\telseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\n\t\t\texpr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\n\t\t\texpr.Type == 'VargLiteral'\n\t\tthen\n\t\t\tdo_nothing() -- No children to visit, single token literals\n\t\telseif expr.Type == 'FieldExpr' then\n\t\t\tvisitExpr(expr.Base)\n\t\telseif expr.Type == 'IndexExpr' then\n\t\t\tvisitExpr(expr.Base)\n\t\t\tvisitExpr(expr.Index)\n\t\telseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\n\t\t\tvisitExpr(expr.Base)\n\t\t\tif expr.FunctionArguments.CallType == 'ArgCall' then\n\t\t\t\tfor _, argExpr in pairs(expr.FunctionArguments.ArgList) do\n\t\t\t\t\tvisitExpr(argExpr)\n\t\t\t\tend\n\t\t\telseif expr.FunctionArguments.CallType == 'TableCall' then\n\t\t\t\tvisitExpr(expr.FunctionArguments.TableExpr)\n\t\t\tend\n\t\telseif expr.Type == 'FunctionLiteral' then\n\t\t\tvisitStat(expr.Body)\n\t\telseif expr.Type == 'VariableExpr' then\n\t\t\tdo_nothing() -- No children to visit\n\t\telseif expr.Type == 'ParenExpr' then\n\t\t\tvisitExpr(expr.Expression)\n\t\telseif expr.Type == 'TableLiteral' then\n\t\t\tfor _, entry in pairs(expr.EntryList) do\n\t\t\t\tif entry.EntryType == 'Field' then\n\t\t\t\t\tvisitExpr(entry.Value)\n\t\t\t\telseif entry.EntryType == 'Index' then\n\t\t\t\t\tvisitExpr(entry.Index)\n\t\t\t\t\tvisitExpr(entry.Value)\n\t\t\t\telseif entry.EntryType == 'Value' then\n\t\t\t\t\tvisitExpr(entry.Value)\n\t\t\t\telse\n\t\t\t\t\tassert(false, \"unreachable\")\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tassert(false, \"unreachable, type: \"..expr.Type..\":\"..FormatTable(expr))\n\t\tend\n\t\tpostVisit(expr)\n\tend\n\n\tvisitStat = function(stat)\n\t\tif preVisit(stat) then\n\t\t\t-- Handler did custom child iteration or blocked child iteration\n\t\t\treturn\n\t\tend\n\t\tif stat.Type == 'StatList' then\n\t\t\tfor _, ch in pairs(stat.StatementList) do\n\t\t\t\tvisitStat(ch)\n\t\t\tend\n\t\telseif stat.Type == 'BreakStat' then\n\t\t\tdo_nothing() -- No children to visit\n\t\telseif stat.Type == 'ReturnStat' then\n\t\t\tfor _, expr in pairs(stat.ExprList) do\n\t\t\t\tvisitExpr(expr)\n\t\t\tend\n\t\telseif stat.Type == 'LocalVarStat' then\n\t\t\tif stat.Token_Equals then\n\t\t\t\tfor _, expr in pairs(stat.ExprList) do\n\t\t\t\t\tvisitExpr(expr)\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'LocalFunctionStat' then\n\t\t\tvisitStat(stat.FunctionStat.Body)\n\t\telseif stat.Type == 'FunctionStat' then\n\t\t\tvisitStat(stat.Body)\n\t\telseif stat.Type == 'RepeatStat' then\n\t\t\tvisitStat(stat.Body)\n\t\t\tvisitExpr(stat.Condition)\n\t\telseif stat.Type == 'GenericForStat' then\n\t\t\tfor _, expr in pairs(stat.GeneratorList) do\n\t\t\t\tvisitExpr(expr)\n\t\t\tend\n\t\t\tvisitStat(stat.Body)\n\t\telseif stat.Type == 'NumericForStat' then\n\t\t\tfor _, expr in pairs(stat.RangeList) do\n\t\t\t\tvisitExpr(expr)\n\t\t\tend\n\t\t\tvisitStat(stat.Body)\n\t\telseif stat.Type == 'WhileStat' then\n\t\t\tvisitExpr(stat.Condition)\n\t\t\tvisitStat(stat.Body)\n\t\telseif stat.Type == 'DoStat' then\n\t\t\tvisitStat(stat.Body)\n\t\telseif stat.Type == 'IfStat' then\n\t\t\tvisitExpr(stat.Condition)\n\t\t\tvisitStat(stat.Body)\n\t\t\tfor _, clause in pairs(stat.ElseClauseList) do\n\t\t\t\tif clause.Condition then\n\t\t\t\t\tvisitExpr(clause.Condition)\n\t\t\t\tend\n\t\t\t\tvisitStat(clause.Body)\n\t\t\tend\n\t\telseif stat.Type == 'CallExprStat' then\n\t\t\tvisitExpr(stat.Expression)\n\t\telseif stat.Type == 'AssignmentStat' then\n\t\t\tfor _, ex in pairs(stat.Lhs) do\n\t\t\t\tvisitExpr(ex)\n\t\t\tend\n\t\t\tfor _, ex in pairs(stat.Rhs) do\n\t\t\t\tvisitExpr(ex)\n\t\t\tend\n\t\telse\n\t\t\tassert(false, \"unreachable\")\n\t\tend\n\t\tpostVisit(stat)\n\tend\n\n\tif StatType[ast.Type] then\n\t\tvisitStat(ast)\n\telse\n\t\tvisitExpr(ast)\n\tend\nend\n\nlocal function AddVariableInfo(ast)\n\tlocal globalVars = {}\n\tlocal currentScope = nil\n\n\t-- Numbering generator for variable lifetimes\n\tlocal locationGenerator = 0\n\tlocal function markLocation()\n\t\tlocationGenerator = locationGenerator + 1\n\t\treturn locationGenerator\n\tend\n\n\t-- Scope management\n\tlocal function pushScope()\n\t\tcurrentScope = {\n\t\t\tParentScope = currentScope;\n\t\t\tChildScopeList = {};\n\t\t\tVariableList = {};\n\t\t\tBeginLocation = markLocation();\n\t\t}\n\t\tif currentScope.ParentScope then\n\t\t\tcurrentScope.Depth = currentScope.ParentScope.Depth + 1\n\t\t\ttable.insert(currentScope.ParentScope.ChildScopeList, currentScope)\n\t\telse\n\t\t\tcurrentScope.Depth = 1\n\t\tend\n\t\tfunction currentScope:GetVar(varName)\n\t\t\tfor _, var in pairs(self.VariableList) do\n\t\t\t\tif var.Name == varName then\n\t\t\t\t\treturn var\n\t\t\t\tend\n\t\t\tend\n\t\t\tif self.ParentScope then\n\t\t\t\treturn self.ParentScope:GetVar(varName)\n\t\t\telse\n\t\t\t\tfor _, var in pairs(globalVars) do\n\t\t\t\t\tif var.Name == varName then\n\t\t\t\t\t\treturn var\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tlocal function popScope()\n\t\tlocal scope = currentScope\n\n\t\t-- Mark where this scope ends\n\t\tscope.EndLocation = markLocation()\n\n\t\t-- Mark all of the variables in the scope as ending there\n\t\tfor _, var in pairs(scope.VariableList) do\n\t\t\tvar.ScopeEndLocation = scope.EndLocation\n\t\tend\n\n\t\t-- Move to the parent scope\n\t\tcurrentScope = scope.ParentScope\n\n\t\treturn scope\n\tend\n\tpushScope() -- push initial scope\n\n\t-- Add / reference variables\n\tlocal function addLocalVar(name, setNameFunc, localInfo)\n\t\tassert(localInfo, \"Missing localInfo\")\n\t\tassert(name, \"Missing local var name\")\n\t\tlocal var = {\n\t\t\tType = 'Local';\n\t\t\tName = name;\n\t\t\tRenameList = {setNameFunc};\n\t\t\tAssignedTo = false;\n\t\t\tInfo = localInfo;\n\t\t\tUseCount = 0;\n\t\t\tScope = currentScope;\n\t\t\tBeginLocation = markLocation();\n\t\t\tEndLocation = markLocation();\n\t\t\tReferenceLocationList = {markLocation()};\n\t\t}\n\t\tfunction var:Rename(newName)\n\t\t\tself.Name = newName\n\t\t\tfor _, renameFunc in pairs(self.RenameList) do\n\t\t\t\trenameFunc(newName)\n\t\t\tend\n\t\tend\n\t\tfunction var:Reference()\n\t\t\tself.UseCount = self.UseCount + 1\n\t\tend\n\t\ttable.insert(currentScope.VariableList, var)\n\t\treturn var\n\tend\n\tlocal function getGlobalVar(name)\n\t\tfor _, var in pairs(globalVars) do\n\t\t\tif var.Name == name then\n\t\t\t\treturn var\n\t\t\tend\n\t\tend\n\t\tlocal var = {\n\t\t\tType = 'Global';\n\t\t\tName = name;\n\t\t\tRenameList = {};\n\t\t\tAssignedTo = false;\n\t\t\tUseCount = 0;\n\t\t\tScope = nil; -- Globals have no scope\n\t\t\tBeginLocation = markLocation();\n\t\t\tEndLocation = markLocation();\n\t\t\tReferenceLocationList = {};\n\t\t}\n\t\tfunction var:Rename(newName)\n\t\t\tself.Name = newName\n\t\t\tfor _, renameFunc in pairs(self.RenameList) do\n\t\t\t\trenameFunc(newName)\n\t\t\tend\n\t\tend\n\t\tfunction var:Reference()\n\t\t\tself.UseCount = self.UseCount + 1\n\t\tend\n\t\ttable.insert(globalVars, var)\n\t\treturn var\n\tend\n\tlocal function addGlobalReference(name, setNameFunc)\n\t\tassert(name, \"Missing var name\")\n\t\tlocal var = getGlobalVar(name)\n\t\ttable.insert(var.RenameList, setNameFunc)\n\t\treturn var\n\tend\n\tlocal function getLocalVar(scope, name)\n\t\t-- First search this scope\n\t\t-- Note: Reverse iterate here because Lua does allow shadowing a local\n\t\t--       within the same scope, and the later defined variable should\n\t\t--       be the one referenced.\n\t\tfor i = #scope.VariableList, 1, -1 do\n\t\t\tif scope.VariableList[i].Name == name then\n\t\t\t\treturn scope.VariableList[i]\n\t\t\tend\n\t\tend\n\n\t\t-- Then search parent scope\n\t\tif scope.ParentScope then\n\t\t\tlocal var = getLocalVar(scope.ParentScope, name)\n\t\t\tif var then\n\t\t\t\treturn var\n\t\t\tend\n\t\tend\n\n\t\t-- Then \n\t\treturn nil\n\tend\n\tlocal function referenceVariable(name, setNameFunc)\n\t\tassert(name, \"Missing var name\")\n\t\tlocal var = getLocalVar(currentScope, name)\n\t\tif var then\n\t\t\ttable.insert(var.RenameList, setNameFunc)\n\t\telse\n\t\t\tvar = addGlobalReference(name, setNameFunc)\n\t\tend\n\t\t-- Update the end location of where this variable is used, and\n\t\t-- add this location to the list of references to this variable.\n\t\tlocal curLocation = markLocation()\n\t\tvar.EndLocation = curLocation\n\t\ttable.insert(var.ReferenceLocationList, var.EndLocation)\n\t\treturn var\n\tend\n\n\tlocal visitor = {}\n\tvisitor.FunctionLiteral = {\n\t\t-- Function literal adds a new scope and adds the function literal arguments\n\t\t-- as local variables in the scope.\n\t\tPre = function(expr)\n\t\t\tpushScope()\n\t\t\tfor index, ident in pairs(expr.ArgList) do\n\t\t\t\t-- Note: Beware ident.Type == 'Symbol', it may be \"...\" here!\n\t\t\t\tif ident.Type == 'Ident' then\n\t\t\t\t\taddLocalVar(ident.Source,\n\t\t\t\t\t\tfunction(name)\n\t\t\t\t\t\t\tident.Source = name\n\t\t\t\t\t\tend,\n\t\t\t\t\t\t{ Type = 'Argument';  Index = index; })\n\t\t\t\tend\n\t\t\tend\n\t\tend;\n\t\tPost = function()\n\t\t\tpopScope()\n\t\tend;\n\t}\n\tvisitor.VariableExpr = function(expr)\n\t\t-- Variable expression references from existing local variables\n\t\t-- in the current scope, annotating the variable usage with variable\n\t\t-- information.\n\t\texpr.Variable = referenceVariable(expr.Token.Source, function(newName)\n\t\t\texpr.Token.Source = newName\n\t\tend)\n\tend\n\tvisitor.StatList = {\n\t\t-- StatList adds a new scope\n\t\tPre = function()\n\t\t\tpushScope()\n\t\tend;\n\t\tPost = function()\n\t\t\tpopScope()\n\t\tend;\n\t}\n\tvisitor.LocalVarStat = {\n\t\tPost = function(stat)\n\t\t\t-- Local var stat adds the local variables to the current scope as locals\n\t\t\t-- We need to visit the subexpressions first, because these new locals\n\t\t\t-- will not be in scope for the initialization value expressions. That is:\n\t\t\t--  `local bar = bar + 1`\n\t\t\t-- Is valid code\n\t\t\tfor varNum, ident in pairs(stat.VarList) do\n\t\t\t\taddLocalVar(ident.Source, function(name)\n\t\t\t\t\tstat.VarList[varNum].Source = name\n\t\t\t\tend, {\n\t\t\t\t\tType = 'Local';\n\t\t\t\t})\n\t\t\tend\n\t\tend;\n\t}\n\tvisitor.LocalFunctionStat = {\n\t\tPre = function(stat)\n\t\t\t-- Local function stat adds the function itself to the current scope as\n\t\t\t-- a local variable, and creates a new scope with the function arguments\n\t\t\t-- as local variables.\n\t\t\taddLocalVar(stat.FunctionStat.NameChain[1].Source, function(name)\n\t\t\t\tstat.FunctionStat.NameChain[1].Source = name\n\t\t\tend, {\n\t\t\t\tType = 'LocalFunction';\n\t\t\t})\n\t\t\tpushScope()\n\t\t\tfor index, ident in pairs(stat.FunctionStat.ArgList) do\n\t\t\t\t-- Note: Beware ident.Type == 'Symbol', it may be \"...\" here!\n\t\t\t\tif ident.Type == 'Ident' then\n\t\t\t\t\taddLocalVar(ident.Source, function(name)\n\t\t\t\t\t\tident.Source = name\n\t\t\t\t\tend, {\n\t\t\t\t\t\tType = 'Argument';\n\t\t\t\t\t\tIndex = index;\n\t\t\t\t\t})\n\t\t\t\tend\n\t\t\tend\n\t\tend;\n\t\tPost = function()\n\t\t\tpopScope()\n\t\tend;\n\t}\n\tvisitor.FunctionStat = {\n\t\tPre = function(stat)\n\t\t\t-- Function stat adds a new scope containing the function arguments\n\t\t\t-- as local variables.\n\t\t\t-- A function stat may also assign to a global variable if it is in\n\t\t\t-- the form `function foo()` with no additional dots/colons in the \n\t\t\t-- name chain.\n\t\t\tlocal nameChain = stat.NameChain\n\t\t\tlocal var\n\t\t\tif #nameChain == 1 then\n\t\t\t\t-- If there is only one item in the name chain, then the first item\n\t\t\t\t-- is a reference to a global variable.\n\t\t\t\tvar = addGlobalReference(nameChain[1].Source, function(name)\n\t\t\t\t\tnameChain[1].Source = name\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\tvar = referenceVariable(nameChain[1].Source, function(name)\n\t\t\t\t\tnameChain[1].Source = name\n\t\t\t\tend)\n\t\t\tend\n\t\t\tvar.AssignedTo = true\n\t\t\tpushScope()\n\t\t\tfor index, ident in pairs(stat.ArgList) do\n\t\t\t\t-- Note: Beware ident.Type == 'Symbol', it may be \"...\" here!\n\t\t\t\tif ident.Type == 'Ident' then\n\t\t\t\t\taddLocalVar(ident.Source, function(name)\n\t\t\t\t\t\tident.Source = name\n\t\t\t\t\tend, {\n\t\t\t\t\t\tType = 'Argument';\n\t\t\t\t\t\tIndex = index;\n\t\t\t\t\t})\n\t\t\t\tend\n\t\t\tend\n\t\tend;\n\t\tPost = function()\n\t\t\tpopScope()\n\t\tend;\n\t}\n\tvisitor.GenericForStat = {\n\t\tPre = function(stat)\n\t\t\t-- Generic fors need an extra scope holding the range variables\n\t\t\t-- Need a custom visitor so that the generator expressions can be\n\t\t\t-- visited before we push a scope, but the body can be visited\n\t\t\t-- after we push a scope.\n\t\t\tfor _, ex in pairs(stat.GeneratorList) do\n\t\t\t\tVisitAst(ex, visitor)\n\t\t\tend\n\t\t\tpushScope()\n\t\t\tfor index, ident in pairs(stat.VarList) do\n\t\t\t\taddLocalVar(ident.Source, function(name)\n\t\t\t\t\tident.Source = name\n\t\t\t\tend, {\n\t\t\t\t\tType = 'ForRange';\n\t\t\t\t\tIndex = index;\n\t\t\t\t})\n\t\t\tend\n\t\t\tVisitAst(stat.Body, visitor)\n\t\t\tpopScope()\n\t\t\treturn true -- Custom visit\n\t\tend;\n\t}\n\tvisitor.NumericForStat = {\n\t\tPre = function(stat)\n\t\t\t-- Numeric fors need an extra scope holding the range variables\n\t\t\t-- Need a custom visitor so that the generator expressions can be\n\t\t\t-- visited before we push a scope, but the body can be visited\n\t\t\t-- after we push a scope.\n\t\t\tfor _, ex in pairs(stat.RangeList) do\n\t\t\t\tVisitAst(ex, visitor)\n\t\t\tend\n\t\t\tpushScope()\n\t\t\tfor index, ident in pairs(stat.VarList) do\n\t\t\t\taddLocalVar(ident.Source, function(name)\n\t\t\t\t\tident.Source = name\n\t\t\t\tend, {\n\t\t\t\t\tType = 'ForRange';\n\t\t\t\t\tIndex = index;\n\t\t\t\t})\n\t\t\tend\n\t\t\tVisitAst(stat.Body, visitor)\n\t\t\tpopScope()\n\t\t\treturn true\t-- Custom visit\n\t\tend;\n\t}\n\tvisitor.AssignmentStat = {\n\t\tPost = function(stat)\n\t\t\t-- For an assignment statement we need to mark the\n\t\t\t-- \"assigned to\" flag on variables.\n\t\t\tfor _, ex in pairs(stat.Lhs) do\n\t\t\t\tif ex.Variable then\n\t\t\t\t\tex.Variable.AssignedTo = true\n\t\t\t\tend\n\t\t\tend\n\t\tend;\n\t}\n\n\tVisitAst(ast, visitor)\n\n\treturn globalVars, popScope()\nend\n\n-- Prints out an AST to stdout, or emits it by appending to a table\nlocal function PrintAst(ast, tbl_out)\n\n\tlocal printStat, printExpr\n\n\tlocal function printt(tk)\n\t\tif not tk.LeadingWhite or not tk.Source then\n\t\t\terror(\"Bad token: \"..FormatTable(tk))\n\t\tend\n\t\tif tbl_out then\n\t\t\ttable.insert(tbl_out, tk.LeadingWhite)\n\t\t\ttable.insert(tbl_out, tk.Source)\n\t\telse\n\t\t\tio.write(tk.LeadingWhite)\n\t\t\tio.write(tk.Source)\n\t\tend\n\tend\n\n\tprintExpr = function(expr)\n\t\tif expr.Type == 'BinopExpr' then\n\t\t\tprintExpr(expr.Lhs)\n\t\t\tprintt(expr.Token_Op)\n\t\t\tprintExpr(expr.Rhs)\n\t\telseif expr.Type == 'UnopExpr' then\n\t\t\tprintt(expr.Token_Op)\n\t\t\tprintExpr(expr.Rhs)\n\t\telseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\n\t\t\texpr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\n\t\t\texpr.Type == 'VargLiteral'\n\t\tthen\n\t\t\t-- Just print the token\n\t\t\tprintt(expr.Token)\n\t\telseif expr.Type == 'FieldExpr' then\n\t\t\tprintExpr(expr.Base)\n\t\t\tprintt(expr.Token_Dot)\n\t\t\tprintt(expr.Field)\n\t\telseif expr.Type == 'IndexExpr' then\n\t\t\tprintExpr(expr.Base)\n\t\t\tprintt(expr.Token_OpenBracket)\n\t\t\tprintExpr(expr.Index)\n\t\t\tprintt(expr.Token_CloseBracket)\n\t\telseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\n\t\t\tprintExpr(expr.Base)\n\t\t\tif expr.Type == 'MethodExpr' then\n\t\t\t\tprintt(expr.Token_Colon)\n\t\t\t\tprintt(expr.Method)\n\t\t\tend\n\t\t\tif expr.FunctionArguments.CallType == 'StringCall' then\n\t\t\t\tprintt(expr.FunctionArguments.Token)\n\t\t\telseif expr.FunctionArguments.CallType == 'ArgCall' then\n\t\t\t\tprintt(expr.FunctionArguments.Token_OpenParen)\n\t\t\t\tfor index, argExpr in pairs(expr.FunctionArguments.ArgList) do\n\t\t\t\t\tprintExpr(argExpr)\n\t\t\t\t\tlocal sep = expr.FunctionArguments.Token_CommaList[index]\n\t\t\t\t\tif sep then\n\t\t\t\t\t\tprintt(sep)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tprintt(expr.FunctionArguments.Token_CloseParen)\n\t\t\telseif expr.FunctionArguments.CallType == 'TableCall' then\n\t\t\t\tprintExpr(expr.FunctionArguments.TableExpr)\n\t\t\tend\n\t\telseif expr.Type == 'FunctionLiteral' then\n\t\t\tprintt(expr.Token_Function)\n\t\t\tprintt(expr.Token_OpenParen)\n\t\t\tfor index, arg in pairs(expr.ArgList) do\n\t\t\t\tprintt(arg)\n\t\t\t\tlocal comma = expr.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tprintt(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(expr.Token_CloseParen)\n\t\t\tprintStat(expr.Body)\n\t\t\tprintt(expr.Token_End)\n\t\telseif expr.Type == 'VariableExpr' then\n\t\t\tprintt(expr.Token)\n\t\telseif expr.Type == 'ParenExpr' then\n\t\t\tprintt(expr.Token_OpenParen)\n\t\t\tprintExpr(expr.Expression)\n\t\t\tprintt(expr.Token_CloseParen)\n\t\telseif expr.Type == 'TableLiteral' then\n\t\t\tprintt(expr.Token_OpenBrace)\n\t\t\tfor index, entry in pairs(expr.EntryList) do\n\t\t\t\tif entry.EntryType == 'Field' then\n\t\t\t\t\tprintt(entry.Field)\n\t\t\t\t\tprintt(entry.Token_Equals)\n\t\t\t\t\tprintExpr(entry.Value)\n\t\t\t\telseif entry.EntryType == 'Index' then\n\t\t\t\t\tprintt(entry.Token_OpenBracket)\n\t\t\t\t\tprintExpr(entry.Index)\n\t\t\t\t\tprintt(entry.Token_CloseBracket)\n\t\t\t\t\tprintt(entry.Token_Equals)\n\t\t\t\t\tprintExpr(entry.Value)\n\t\t\t\telseif entry.EntryType == 'Value' then\n\t\t\t\t\tprintExpr(entry.Value)\n\t\t\t\telse\n\t\t\t\t\tassert(false, \"unreachable\")\n\t\t\t\tend\n\t\t\t\tlocal sep = expr.Token_SeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(expr.Token_CloseBrace)\n\t\telse\n\t\t\tassert(false, \"unreachable, type: \"..expr.Type..\":\"..FormatTable(expr))\n\t\tend\n\tend\n\n\tprintStat = function(stat)\n\t\tif stat.Type == 'StatList' then\n\t\t\tfor index, ch in pairs(stat.StatementList) do\n\t\t\t\tprintStat(ch)\n\t\t\t\tif stat.SemicolonList[index] then\n\t\t\t\t\tprintt(stat.SemicolonList[index])\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'BreakStat' then\n\t\t\tprintt(stat.Token_Break)\n\t\telseif stat.Type == 'ReturnStat' then\n\t\t\tprintt(stat.Token_Return)\n\t\t\tfor index, expr in pairs(stat.ExprList) do\n\t\t\t\tprintExpr(expr)\n\t\t\t\tif stat.Token_CommaList[index] then\n\t\t\t\t\tprintt(stat.Token_CommaList[index])\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'LocalVarStat' then\n\t\t\tprintt(stat.Token_Local)\n\t\t\tfor index, var in pairs(stat.VarList) do\n\t\t\t\tprintt(var)\n\t\t\t\tlocal comma = stat.Token_VarCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tprintt(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tif stat.Token_Equals then\n\t\t\t\tprintt(stat.Token_Equals)\n\t\t\t\tfor index, expr in pairs(stat.ExprList) do\n\t\t\t\t\tprintExpr(expr)\n\t\t\t\t\tlocal comma = stat.Token_ExprCommaList[index]\n\t\t\t\t\tif comma then\n\t\t\t\t\t\tprintt(comma)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'LocalFunctionStat' then\n\t\t\tprintt(stat.Token_Local)\n\t\t\tprintt(stat.FunctionStat.Token_Function)\n\t\t\tprintt(stat.FunctionStat.NameChain[1])\n\t\t\tprintt(stat.FunctionStat.Token_OpenParen)\n\t\t\tfor index, arg in pairs(stat.FunctionStat.ArgList) do\n\t\t\t\tprintt(arg)\n\t\t\t\tlocal comma = stat.FunctionStat.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tprintt(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.FunctionStat.Token_CloseParen)\n\t\t\tprintStat(stat.FunctionStat.Body)\n\t\t\tprintt(stat.FunctionStat.Token_End)\n\t\telseif stat.Type == 'FunctionStat' then\n\t\t\tprintt(stat.Token_Function)\n\t\t\tfor index, part in pairs(stat.NameChain) do\n\t\t\t\tprintt(part)\n\t\t\t\tlocal sep = stat.Token_NameChainSeparator[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_OpenParen)\n\t\t\tfor index, arg in pairs(stat.ArgList) do\n\t\t\t\tprintt(arg)\n\t\t\t\tlocal comma = stat.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tprintt(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_CloseParen)\n\t\t\tprintStat(stat.Body)\n\t\t\tprintt(stat.Token_End)\n\t\telseif stat.Type == 'RepeatStat' then\n\t\t\tprintt(stat.Token_Repeat)\n\t\t\tprintStat(stat.Body)\n\t\t\tprintt(stat.Token_Until)\n\t\t\tprintExpr(stat.Condition)\n\t\telseif stat.Type == 'GenericForStat' then\n\t\t\tprintt(stat.Token_For)\n\t\t\tfor index, var in pairs(stat.VarList) do\n\t\t\t\tprintt(var)\n\t\t\t\tlocal sep = stat.Token_VarCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_In)\n\t\t\tfor index, expr in pairs(stat.GeneratorList) do\n\t\t\t\tprintExpr(expr)\n\t\t\t\tlocal sep = stat.Token_GeneratorCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_Do)\n\t\t\tprintStat(stat.Body)\n\t\t\tprintt(stat.Token_End)\n\t\telseif stat.Type == 'NumericForStat' then\n\t\t\tprintt(stat.Token_For)\n\t\t\tfor index, var in pairs(stat.VarList) do\n\t\t\t\tprintt(var)\n\t\t\t\tlocal sep = stat.Token_VarCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_Equals)\n\t\t\tfor index, expr in pairs(stat.RangeList) do\n\t\t\t\tprintExpr(expr)\n\t\t\t\tlocal sep = stat.Token_RangeCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_Do)\n\t\t\tprintStat(stat.Body)\n\t\t\tprintt(stat.Token_End)\n\t\telseif stat.Type == 'WhileStat' then\n\t\t\tprintt(stat.Token_While)\n\t\t\tprintExpr(stat.Condition)\n\t\t\tprintt(stat.Token_Do)\n\t\t\tprintStat(stat.Body)\n\t\t\tprintt(stat.Token_End)\n\t\telseif stat.Type == 'DoStat' then\n\t\t\tprintt(stat.Token_Do)\n\t\t\tprintStat(stat.Body)\n\t\t\tprintt(stat.Token_End)\n\t\telseif stat.Type == 'IfStat' then\n\t\t\tprintt(stat.Token_If)\n\t\t\tprintExpr(stat.Condition)\n\t\t\tprintt(stat.Token_Then)\n\t\t\tprintStat(stat.Body)\n\t\t\tfor _, clause in pairs(stat.ElseClauseList) do\n\t\t\t\tprintt(clause.Token)\n\t\t\t\tif clause.Condition then\n\t\t\t\t\tprintExpr(clause.Condition)\n\t\t\t\t\tprintt(clause.Token_Then)\n\t\t\t\tend\n\t\t\t\tprintStat(clause.Body)\n\t\t\tend\n\t\t\tprintt(stat.Token_End)\n\t\telseif stat.Type == 'CallExprStat' then\n\t\t\tprintExpr(stat.Expression)\n\t\telseif stat.Type == 'AssignmentStat' then\n\t\t\tfor index, ex in pairs(stat.Lhs) do\n\t\t\t\tprintExpr(ex)\n\t\t\t\tlocal sep = stat.Token_LhsSeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tprintt(stat.Token_Equals)\n\t\t\tfor index, ex in pairs(stat.Rhs) do\n\t\t\t\tprintExpr(ex)\n\t\t\t\tlocal sep = stat.Token_RhsSeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tprintt(sep)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tassert(false, \"unreachable\")\n\t\tend\n\tend\n\n\tprintStat(ast)\nend\n\n-- Get an actual string representation of the AST\nlocal function AstToString(ast)\n\tlocal output = {}\n\tPrintAst(ast, output)\n\treturn table.concat(output)\nend\n\n-- Adds / removes whitespace in an AST to put it into a \"standard formatting\"\nlocal function FormatAst(ast)\n\tlocal formatStat, formatExpr\n\n\tlocal currentIndent = 0\n\n\tlocal function applyIndent(token)\n\t\tlocal indentString = '\\n'..('\\t'):rep(currentIndent)\n\t\tif token.LeadingWhite == '' or (token.LeadingWhite:sub(-#indentString, -1) ~= indentString) then\n\t\t\t-- Trim existing trailing whitespace on LeadingWhite\n\t\t\t-- Trim trailing tabs and spaces, and up to one newline\n\t\t\ttoken.LeadingWhite = token.LeadingWhite:gsub(\"\\n?[\\t ]*$\", \"\")\n\t\t\ttoken.LeadingWhite = token.LeadingWhite..indentString\n\t\tend\n\tend\n\n\tlocal function indent()\n\t\tcurrentIndent = currentIndent + 1\n\tend\n\n\tlocal function undent()\n\t\tcurrentIndent = currentIndent - 1\n\t\tassert(currentIndent >= 0, \"Undented too far\")\n\tend\n\n\tlocal function leadingChar(tk)\n\t\tif #tk.LeadingWhite > 0 then\n\t\t\treturn tk.LeadingWhite:sub(1,1)\n\t\telse\n\t\t\treturn tk.Source:sub(1,1)\n\t\tend\n\tend\n\n\tlocal function padToken(tk)\n\t\tif not WhiteChars[leadingChar(tk)] then\n\t\t\ttk.LeadingWhite = ' '..tk.LeadingWhite\n\t\tend\n\tend\n\n\tlocal function padExpr(expr)\n\t\tpadToken(expr:GetFirstToken())\n\tend\n\n\tlocal function formatBody(openToken, bodyStat, closeToken) -- luacheck: ignore 212\n\t\tindent()\n\t\tformatStat(bodyStat)\n\t\tundent()\n\t\tapplyIndent(closeToken)\n\tend\n\n\tformatExpr = function(expr)\n\t\tif expr.Type == 'BinopExpr' then\n\t\t\tformatExpr(expr.Lhs)\n\t\t\tformatExpr(expr.Rhs)\n\t\t\tif expr.Token_Op.Source ~= '..' then -- No padding on ..\n\t\t\t\tpadExpr(expr.Rhs)\n\t\t\t\tpadToken(expr.Token_Op)\n\t\t\tend\n\t\telseif expr.Type == 'UnopExpr' then\n\t\t\tformatExpr(expr.Rhs)\n\t\t\t--(expr.Token_Op)\n\t\telseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\n\t\t\texpr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\n\t\t\texpr.Type == 'VargLiteral'\n\t\tthen\n\t\t\tdo_nothing()\n\t\t\t--(expr.Token)\n\t\telseif expr.Type == 'FieldExpr' then\n\t\t\tformatExpr(expr.Base)\n\t\t\t--(expr.Token_Dot)\n\t\t\t--(expr.Field)\n\t\telseif expr.Type == 'IndexExpr' then\n\t\t\tformatExpr(expr.Base)\n\t\t\tformatExpr(expr.Index)\n\t\t\t--(expr.Token_OpenBracket)\n\t\t\t--(expr.Token_CloseBracket)\n\t\telseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\n\t\t\tformatExpr(expr.Base)\n\t\t\tif expr.Type == 'MethodExpr' then\n\t\t\t\tdo_nothing()\n\t\t\t\t--(expr.Token_Colon)\n\t\t\t\t--(expr.Method)\n\t\t\tend\n\t\t\tif expr.FunctionArguments.CallType == 'StringCall' then\n\t\t\t\tdo_nothing() --(expr.FunctionArguments.Token)\n\t\t\telseif expr.FunctionArguments.CallType == 'ArgCall' then\n\t\t\t\t--(expr.FunctionArguments.Token_OpenParen)\n\t\t\t\tfor index, argExpr in pairs(expr.FunctionArguments.ArgList) do\n\t\t\t\t\tformatExpr(argExpr)\n\t\t\t\t\tif index > 1 then\n\t\t\t\t\t\tpadExpr(argExpr)\n\t\t\t\t\tend\n\t\t\t\t\t--[[\n\t\t\t\t\tlocal sep = expr.FunctionArguments.Token_CommaList[index]\n\t\t\t\t\tif sep then\n\t\t\t\t\t\t--(sep)\n\t\t\t\t\tend\n\t\t\t\t\t--]]\n\t\t\t\tend\n\t\t\t\t--(expr.FunctionArguments.Token_CloseParen)\n\t\t\telseif expr.FunctionArguments.CallType == 'TableCall' then\n\t\t\t\tformatExpr(expr.FunctionArguments.TableExpr)\n\t\t\tend\n\t\telseif expr.Type == 'FunctionLiteral' then\n\t\t\t--(expr.Token_Function)\n\t\t\t--(expr.Token_OpenParen)\n\t\t\tfor index, arg in pairs(expr.ArgList) do\n\t\t\t\t--(arg)\n\t\t\t\tif index > 1 then\n\t\t\t\t\tpadToken(arg)\n\t\t\t\tend\n\t\t\t\t--[[\n\t\t\t\tlocal comma = expr.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\t--(comma)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\t--(expr.Token_CloseParen)\n\t\t\tformatBody(expr.Token_CloseParen, expr.Body, expr.Token_End)\n\t\telseif expr.Type == 'VariableExpr' then\n\t\t\tdo_nothing() --(expr.Token)\n\t\telseif expr.Type == 'ParenExpr' then\n\t\t\tformatExpr(expr.Expression)\n\t\t\t--(expr.Token_OpenParen)\n\t\t\t--(expr.Token_CloseParen)\n\t\telseif expr.Type == 'TableLiteral' then\n\t\t\t--(expr.Token_OpenBrace)\n\t\t\tif #expr.EntryList > 0 then\n\t\t\t\tindent()\n\t\t\t\tfor _, entry in pairs(expr.EntryList) do\n\t\t\t\t\tif entry.EntryType == 'Field' then\n\t\t\t\t\t\tapplyIndent(entry.Field)\n\t\t\t\t\t\tpadToken(entry.Token_Equals)\n\t\t\t\t\t\tformatExpr(entry.Value)\n\t\t\t\t\t\tpadExpr(entry.Value)\n\t\t\t\t\telseif entry.EntryType == 'Index' then\n\t\t\t\t\t\tapplyIndent(entry.Token_OpenBracket)\n\t\t\t\t\t\tformatExpr(entry.Index)\n\t\t\t\t\t\t--(entry.Token_CloseBracket)\n\t\t\t\t\t\tpadToken(entry.Token_Equals)\n\t\t\t\t\t\tformatExpr(entry.Value)\n\t\t\t\t\t\tpadExpr(entry.Value)\n\t\t\t\t\telseif entry.EntryType == 'Value' then\n\t\t\t\t\t\tformatExpr(entry.Value)\n\t\t\t\t\t\tapplyIndent(entry.Value:GetFirstToken())\n\t\t\t\t\telse\n\t\t\t\t\t\tassert(false, \"unreachable\")\n\t\t\t\t\tend\n\t\t\t\t\t--[[\n\t\t\t\t\tlocal sep = expr.Token_SeparatorList[index]\n\t\t\t\t\tif sep then\n\t\t\t\t\t\t--(sep)\n\t\t\t\t\tend\n\t\t\t\t\t--]]\n\t\t\t\tend\n\t\t\t\tundent()\n\t\t\t\tapplyIndent(expr.Token_CloseBrace)\n\t\t\tend\n\t\t\t--(expr.Token_CloseBrace)\n\t\telse\n\t\t\tassert(false, \"unreachable, type: \"..expr.Type..\":\"..FormatTable(expr))\n\t\tend\n\tend\n\n\tformatStat = function(stat)\n\t\tif stat.Type == 'StatList' then\n\t\t\tfor _, _stat in pairs(stat.StatementList) do\n\t\t\t\tformatStat(_stat)\n\t\t\t\tapplyIndent(_stat:GetFirstToken())\n\t\t\tend\n\n\t\telseif stat.Type == 'BreakStat' then\n\t\t\tdo_nothing() --(stat.Token_Break)\n\n\t\telseif stat.Type == 'ReturnStat' then\n\t\t\t--(stat.Token_Return)\n\t\t\tfor index, expr in pairs(stat.ExprList) do\n\t\t\t\tformatExpr(expr)\n\t\t\t\tpadExpr(expr)\n\t\t\t\tif stat.Token_CommaList[index] then\n\t\t\t\t\tdo_nothing() --(stat.Token_CommaList[index])\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'LocalVarStat' then\n\t\t\t--(stat.Token_Local)\n\t\t\tfor _, var in pairs(stat.VarList) do\n\t\t\t\tpadToken(var)\n\t\t\t\t--[[\n\t\t\t\tlocal comma = stat.Token_VarCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\t--(comma)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\tif stat.Token_Equals then\n\t\t\t\tpadToken(stat.Token_Equals)\n\t\t\t\tfor _, expr in pairs(stat.ExprList) do\n\t\t\t\t\tformatExpr(expr)\n\t\t\t\t\tpadExpr(expr)\n\t\t\t\t\t--[[\n\t\t\t\t\tlocal comma = stat.Token_ExprCommaList[index]\n\t\t\t\t\tif comma then\n\t\t\t\t\t\t--(comma)\n\t\t\t\t\tend\n\t\t\t\t\t--]]\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'LocalFunctionStat' then\n\t\t\t--(stat.Token_Local)\n\t\t\tpadToken(stat.FunctionStat.Token_Function)\n\t\t\tpadToken(stat.FunctionStat.NameChain[1])\n\t\t\t--(stat.FunctionStat.Token_OpenParen)\n\t\t\tfor index, arg in pairs(stat.FunctionStat.ArgList) do\n\t\t\t\tif index > 1 then\n\t\t\t\t\tpadToken(arg)\n\t\t\t\tend\n\t\t\t\t--[[\n\t\t\t\tlocal comma = stat.FunctionStat.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\t--(comma)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\t--(stat.FunctionStat.Token_CloseParen)\n\t\t\tformatBody(stat.FunctionStat.Token_CloseParen, stat.FunctionStat.Body, stat.FunctionStat.Token_End)\n\t\telseif stat.Type == 'FunctionStat' then\n\t\t\t--(stat.Token_Function)\n\t\t\tfor index, part in pairs(stat.NameChain) do\n\t\t\t\tif index == 1 then\n\t\t\t\t\tpadToken(part)\n\t\t\t\tend\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_NameChainSeparator[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\t--(stat.Token_OpenParen)\n\t\t\tfor index, arg in pairs(stat.ArgList) do\n\t\t\t\tif index > 1 then\n\t\t\t\t\tpadToken(arg)\n\t\t\t\tend\n\t\t\t\t--[[\n\t\t\t\tlocal comma = stat.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\t--(comma)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\t--(stat.Token_CloseParen)\n\t\t\tformatBody(stat.Token_CloseParen, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'RepeatStat' then\n\t\t\t--(stat.Token_Repeat)\n\t\t\tformatBody(stat.Token_Repeat, stat.Body, stat.Token_Until)\n\t\t\tformatExpr(stat.Condition)\n\t\t\tpadExpr(stat.Condition)\n\t\telseif stat.Type == 'GenericForStat' then\n\t\t\t--(stat.Token_For)\n\t\t\tfor _, var in pairs(stat.VarList) do\n\t\t\t\tpadToken(var)\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_VarCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\tpadToken(stat.Token_In)\n\t\t\tfor _, expr in pairs(stat.GeneratorList) do\n\t\t\t\tformatExpr(expr)\n\t\t\t\tpadExpr(expr)\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_GeneratorCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\tpadToken(stat.Token_Do)\n\t\t\tformatBody(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'NumericForStat' then\n\t\t\t--(stat.Token_For)\n\t\t\tfor _, var in pairs(stat.VarList) do\n\t\t\t\tpadToken(var)\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_VarCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\tpadToken(stat.Token_Equals)\n\t\t\tfor _, expr in pairs(stat.RangeList) do\n\t\t\t\tformatExpr(expr)\n\t\t\t\tpadExpr(expr)\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_RangeCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\tpadToken(stat.Token_Do)\n\t\t\tformatBody(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'WhileStat' then\n\t\t\t--(stat.Token_While)\n\t\t\tformatExpr(stat.Condition)\n\t\t\tpadExpr(stat.Condition)\n\t\t\tpadToken(stat.Token_Do)\n\t\t\tformatBody(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'DoStat' then\n\t\t\t--(stat.Token_Do)\n\t\t\tformatBody(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'IfStat' then\n\t\t\t--(stat.Token_If)\n\t\t\tformatExpr(stat.Condition)\n\t\t\tpadExpr(stat.Condition)\n\t\t\tpadToken(stat.Token_Then)\n\t\t\t--\n\t\t\tlocal lastBodyOpen = stat.Token_Then\n\t\t\tlocal lastBody = stat.Body\n\t\t\t--\n\t\t\tfor _, clause in pairs(stat.ElseClauseList) do\n\t\t\t\tformatBody(lastBodyOpen, lastBody, clause.Token)\n\t\t\t\tlastBodyOpen = clause.Token\n\t\t\t\t--\n\t\t\t\tif clause.Condition then\n\t\t\t\t\tformatExpr(clause.Condition)\n\t\t\t\t\tpadExpr(clause.Condition)\n\t\t\t\t\tpadToken(clause.Token_Then)\n\t\t\t\t\tlastBodyOpen = clause.Token_Then\n\t\t\t\tend\n\t\t\t\tlastBody = clause.Body\n\t\t\tend\n\t\t\t--\n\t\t\tformatBody(lastBodyOpen, lastBody, stat.Token_End)\n\n\t\telseif stat.Type == 'CallExprStat' then\n\t\t\tformatExpr(stat.Expression)\n\t\telseif stat.Type == 'AssignmentStat' then\n\t\t\tfor index, ex in pairs(stat.Lhs) do\n\t\t\t\tformatExpr(ex)\n\t\t\t\tif index > 1 then\n\t\t\t\t\tpadExpr(ex)\n\t\t\t\tend\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_LhsSeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\t\tpadToken(stat.Token_Equals)\n\t\t\tfor _, ex in pairs(stat.Rhs) do\n\t\t\t\tformatExpr(ex)\n\t\t\t\tpadExpr(ex)\n\t\t\t\t--[[\n\t\t\t\tlocal sep = stat.Token_RhsSeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\t--(sep)\n\t\t\t\tend\n\t\t\t\t--]]\n\t\t\tend\n\t\telse\n\t\t\tassert(false, \"unreachable\")\n\t\tend\n\tend\n\n\tformatStat(ast)\nend\n\n-- Strips as much whitespace off of tokens in an AST as possible without causing problems\nlocal function StripAst(ast)\n\tlocal stripStat, stripExpr\n\n\tlocal function stript(token)\n\t\ttoken.LeadingWhite = ''\n\tend\n\n\t-- Make to adjacent tokens as close as possible\n\tlocal function joint(tokenA, tokenB)\n\t\t-- Strip the second token's whitespace\n\t\tstript(tokenB)\n\n\t\t-- Get the trailing A <-> leading B character pair\n\t\tlocal lastCh = tokenA.Source:sub(-1, -1)\n\t\tlocal firstCh = tokenB.Source:sub(1, 1)\n\n\t\t-- Cases to consider:\n\t\t--  Touching minus signs -> comment: `- -42` -> `--42' is invalid\n\t\t--  Touching words: `a b` -> `ab` is invalid\n\t\t--  Touching digits: `2 3`, can't occur in the Lua syntax as number literals aren't a primary expression\n\t\t--  Ambiguous syntax: `f(x)\\n(x)()` is already disallowed, we can't cause a problem by removing newlines\n\n\t\t-- Figure out what separation is needed\n\t\tif\n\t\t\t(lastCh == '-' and firstCh == '-') or\n\t\t\t(AllIdentChars[lastCh] and AllIdentChars[firstCh])\n\t\tthen\n\t\t\ttokenB.LeadingWhite = ' ' -- Use a separator\n\t\telse\n\t\t\ttokenB.LeadingWhite = '' -- Don't use a separator\n\t\tend\n\tend\n\n\t-- Join up a statement body and it's opening / closing tokens\n\tlocal function bodyjoint(open, body, close)\n\t\tstripStat(body)\n\t\tstript(close)\n\t\tlocal bodyFirst = body:GetFirstToken()\n\t\tlocal bodyLast = body:GetLastToken()\n\t\tif bodyFirst then\n\t\t\t-- Body is non-empty, join body to open / close\n\t\t\tjoint(open, bodyFirst)\n\t\t\tjoint(bodyLast, close)\n\t\telse\n\t\t\t-- Body is empty, just join open and close token together\n\t\t\tjoint(open, close)\n\t\tend\n\tend\n\n\tstripExpr = function(expr)\n\t\tif expr.Type == 'BinopExpr' then\n\t\t\tstripExpr(expr.Lhs)\n\t\t\tstript(expr.Token_Op)\n\t\t\tstripExpr(expr.Rhs)\n\t\t\t-- Handle the `a - -b` -/-> `a--b` case which would otherwise incorrectly generate a comment\n\t\t\t-- Also handles operators \"or\" / \"and\" which definitely need joining logic in a bunch of cases\n\t\t\tjoint(expr.Token_Op, expr.Rhs:GetFirstToken())\n\t\t\tjoint(expr.Lhs:GetLastToken(), expr.Token_Op)\n\t\telseif expr.Type == 'UnopExpr' then\n\t\t\tstript(expr.Token_Op)\n\t\t\tstripExpr(expr.Rhs)\n\t\t\t-- Handle the `- -b` -/-> `--b` case which would otherwise incorrectly generate a comment\n\t\t\tjoint(expr.Token_Op, expr.Rhs:GetFirstToken())\n\t\telseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\n\t\t\texpr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\n\t\t\texpr.Type == 'VargLiteral'\n\t\tthen\n\t\t\t-- Just print the token\n\t\t\tstript(expr.Token)\n\t\telseif expr.Type == 'FieldExpr' then\n\t\t\tstripExpr(expr.Base)\n\t\t\tstript(expr.Token_Dot)\n\t\t\tstript(expr.Field)\n\t\telseif expr.Type == 'IndexExpr' then\n\t\t\tstripExpr(expr.Base)\n\t\t\tstript(expr.Token_OpenBracket)\n\t\t\tstripExpr(expr.Index)\n\t\t\tstript(expr.Token_CloseBracket)\n\t\telseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\n\t\t\tstripExpr(expr.Base)\n\t\t\tif expr.Type == 'MethodExpr' then\n\t\t\t\tstript(expr.Token_Colon)\n\t\t\t\tstript(expr.Method)\n\t\t\tend\n\t\t\tif expr.FunctionArguments.CallType == 'StringCall' then\n\t\t\t\tstript(expr.FunctionArguments.Token)\n\t\t\telseif expr.FunctionArguments.CallType == 'ArgCall' then\n\t\t\t\tstript(expr.FunctionArguments.Token_OpenParen)\n\t\t\t\tfor index, argExpr in pairs(expr.FunctionArguments.ArgList) do\n\t\t\t\t\tstripExpr(argExpr)\n\t\t\t\t\tlocal sep = expr.FunctionArguments.Token_CommaList[index]\n\t\t\t\t\tif sep then\n\t\t\t\t\t\tstript(sep)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tstript(expr.FunctionArguments.Token_CloseParen)\n\t\t\telseif expr.FunctionArguments.CallType == 'TableCall' then\n\t\t\t\tstripExpr(expr.FunctionArguments.TableExpr)\n\t\t\tend\n\t\telseif expr.Type == 'FunctionLiteral' then\n\t\t\tstript(expr.Token_Function)\n\t\t\tstript(expr.Token_OpenParen)\n\t\t\tfor index, arg in pairs(expr.ArgList) do\n\t\t\t\tstript(arg)\n\t\t\t\tlocal comma = expr.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tstript(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tstript(expr.Token_CloseParen)\n\t\t\tbodyjoint(expr.Token_CloseParen, expr.Body, expr.Token_End)\n\t\telseif expr.Type == 'VariableExpr' then\n\t\t\tstript(expr.Token)\n\t\telseif expr.Type == 'ParenExpr' then\n\t\t\tstript(expr.Token_OpenParen)\n\t\t\tstripExpr(expr.Expression)\n\t\t\tstript(expr.Token_CloseParen)\n\t\telseif expr.Type == 'TableLiteral' then\n\t\t\tstript(expr.Token_OpenBrace)\n\t\t\tfor index, entry in pairs(expr.EntryList) do\n\t\t\t\tif entry.EntryType == 'Field' then\n\t\t\t\t\tstript(entry.Field)\n\t\t\t\t\tstript(entry.Token_Equals)\n\t\t\t\t\tstripExpr(entry.Value)\n\t\t\t\telseif entry.EntryType == 'Index' then\n\t\t\t\t\tstript(entry.Token_OpenBracket)\n\t\t\t\t\tstripExpr(entry.Index)\n\t\t\t\t\tstript(entry.Token_CloseBracket)\n\t\t\t\t\tstript(entry.Token_Equals)\n\t\t\t\t\tstripExpr(entry.Value)\n\t\t\t\telseif entry.EntryType == 'Value' then\n\t\t\t\t\tstripExpr(entry.Value)\n\t\t\t\telse\n\t\t\t\t\tassert(false, \"unreachable\")\n\t\t\t\tend\n\t\t\t\tlocal sep = expr.Token_SeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tstript(expr.Token_CloseBrace)\n\t\telse\n\t\t\tassert(false, \"unreachable, type: \"..expr.Type..\":\"..FormatTable(expr))\n\t\tend\n\tend\n\n\tstripStat = function(stat)\n\t\tif stat.Type == 'StatList' then\n\t\t\t-- Strip all surrounding whitespace on statement lists along with separating whitespace\n\t\t\tfor i = 1, #stat.StatementList do\n\t\t\t\tlocal chStat = stat.StatementList[i]\n\n\t\t\t\t-- Strip the statement and it's whitespace\n\t\t\t\tstripStat(chStat)\n\t\t\t\tstript(chStat:GetFirstToken())\n\n\t\t\t\t-- If there was a last statement, join them appropriately\n\t\t\t\tlocal lastChStat = stat.StatementList[i-1]\n\t\t\t\tif lastChStat then\n\t\t\t\t\t-- See if we can remove a semi-colon, the only case where we can't is if\n\t\t\t\t\t-- this and the last statement have a `);(` pair, where removing the semi-colon\n\t\t\t\t\t-- would introduce ambiguous syntax.\n\t\t\t\t\tif stat.SemicolonList[i-1] and\n\t\t\t\t\t\t(lastChStat:GetLastToken().Source ~= ')' or chStat:GetFirstToken().Source ~= '(')\n\t\t\t\t\tthen\n\t\t\t\t\t\tstat.SemicolonList[i-1] = nil\n\t\t\t\t\tend\n\n\t\t\t\t\t-- If there isn't a semi-colon, we should safely join the two statements\n\t\t\t\t\t-- (If there is one, then no whitespace leading chStat is always okay)\n\t\t\t\t\tif not stat.SemicolonList[i-1] then\n\t\t\t\t\t\tjoint(lastChStat:GetLastToken(), chStat:GetFirstToken())\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- A semi-colon is never needed on the last stat in a statlist:\n\t\t\tstat.SemicolonList[#stat.StatementList] = nil\n\n\t\t\t-- The leading whitespace on the statlist should be stripped\n\t\t\tif #stat.StatementList > 0 then\n\t\t\t\tstript(stat.StatementList[1]:GetFirstToken())\n\t\t\tend\n\n\t\telseif stat.Type == 'BreakStat' then\n\t\t\tstript(stat.Token_Break)\n\n\t\telseif stat.Type == 'ReturnStat' then\n\t\t\tstript(stat.Token_Return)\n\t\t\tfor index, expr in pairs(stat.ExprList) do\n\t\t\t\tstripExpr(expr)\n\t\t\t\tif stat.Token_CommaList[index] then\n\t\t\t\t\tstript(stat.Token_CommaList[index])\n\t\t\t\tend\n\t\t\tend\n\t\t\tif #stat.ExprList > 0 then\n\t\t\t\tjoint(stat.Token_Return, stat.ExprList[1]:GetFirstToken())\n\t\t\tend\n\t\telseif stat.Type == 'LocalVarStat' then\n\t\t\tstript(stat.Token_Local)\n\t\t\tfor index, var in pairs(stat.VarList) do\n\t\t\t\tif index == 1 then\n\t\t\t\t\tjoint(stat.Token_Local, var)\n\t\t\t\telse\n\t\t\t\t\tstript(var)\n\t\t\t\tend\n\t\t\t\tlocal comma = stat.Token_VarCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tstript(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tif stat.Token_Equals then\n\t\t\t\tstript(stat.Token_Equals)\n\t\t\t\tfor index, expr in pairs(stat.ExprList) do\n\t\t\t\t\tstripExpr(expr)\n\t\t\t\t\tlocal comma = stat.Token_ExprCommaList[index]\n\t\t\t\t\tif comma then\n\t\t\t\t\t\tstript(comma)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telseif stat.Type == 'LocalFunctionStat' then\n\t\t\tstript(stat.Token_Local)\n\t\t\tjoint(stat.Token_Local, stat.FunctionStat.Token_Function)\n\t\t\tjoint(stat.FunctionStat.Token_Function, stat.FunctionStat.NameChain[1])\n\t\t\tjoint(stat.FunctionStat.NameChain[1], stat.FunctionStat.Token_OpenParen)\n\t\t\tfor index, arg in pairs(stat.FunctionStat.ArgList) do\n\t\t\t\tstript(arg)\n\t\t\t\tlocal comma = stat.FunctionStat.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tstript(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tstript(stat.FunctionStat.Token_CloseParen)\n\t\t\tbodyjoint(stat.FunctionStat.Token_CloseParen, stat.FunctionStat.Body, stat.FunctionStat.Token_End)\n\t\telseif stat.Type == 'FunctionStat' then\n\t\t\tstript(stat.Token_Function)\n\t\t\tfor index, part in pairs(stat.NameChain) do\n\t\t\t\tif index == 1 then\n\t\t\t\t\tjoint(stat.Token_Function, part)\n\t\t\t\telse\n\t\t\t\t\tstript(part)\n\t\t\t\tend\n\t\t\t\tlocal sep = stat.Token_NameChainSeparator[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tstript(stat.Token_OpenParen)\n\t\t\tfor index, arg in pairs(stat.ArgList) do\n\t\t\t\tstript(arg)\n\t\t\t\tlocal comma = stat.Token_ArgCommaList[index]\n\t\t\t\tif comma then\n\t\t\t\t\tstript(comma)\n\t\t\t\tend\n\t\t\tend\n\t\t\tstript(stat.Token_CloseParen)\n\t\t\tbodyjoint(stat.Token_CloseParen, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'RepeatStat' then\n\t\t\tstript(stat.Token_Repeat)\n\t\t\tbodyjoint(stat.Token_Repeat, stat.Body, stat.Token_Until)\n\t\t\tstripExpr(stat.Condition)\n\t\t\tjoint(stat.Token_Until, stat.Condition:GetFirstToken())\n\t\telseif stat.Type == 'GenericForStat' then\n\t\t\tstript(stat.Token_For)\n\t\t\tfor index, var in pairs(stat.VarList) do\n\t\t\t\tif index == 1 then\n\t\t\t\t\tjoint(stat.Token_For, var)\n\t\t\t\telse\n\t\t\t\t\tstript(var)\n\t\t\t\tend\n\t\t\t\tlocal sep = stat.Token_VarCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tjoint(stat.VarList[#stat.VarList], stat.Token_In)\n\t\t\tfor index, expr in pairs(stat.GeneratorList) do\n\t\t\t\tstripExpr(expr)\n\t\t\t\tif index == 1 then\n\t\t\t\t\tjoint(stat.Token_In, expr:GetFirstToken())\n\t\t\t\tend\n\t\t\t\tlocal sep = stat.Token_GeneratorCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tjoint(stat.GeneratorList[#stat.GeneratorList]:GetLastToken(), stat.Token_Do)\n\t\t\tbodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'NumericForStat' then\n\t\t\tstript(stat.Token_For)\n\t\t\tfor index, var in pairs(stat.VarList) do\n\t\t\t\tif index == 1 then\n\t\t\t\t\tjoint(stat.Token_For, var)\n\t\t\t\telse\n\t\t\t\t\tstript(var)\n\t\t\t\tend\n\t\t\t\tlocal sep = stat.Token_VarCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tjoint(stat.VarList[#stat.VarList], stat.Token_Equals)\n\t\t\tfor index, expr in pairs(stat.RangeList) do\n\t\t\t\tstripExpr(expr)\n\t\t\t\tif index == 1 then\n\t\t\t\t\tjoint(stat.Token_Equals, expr:GetFirstToken())\n\t\t\t\tend\n\t\t\t\tlocal sep = stat.Token_RangeCommaList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tjoint(stat.RangeList[#stat.RangeList]:GetLastToken(), stat.Token_Do)\n\t\t\tbodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'WhileStat' then\n\t\t\tstript(stat.Token_While)\n\t\t\tstripExpr(stat.Condition)\n\t\t\tstript(stat.Token_Do)\n\t\t\tjoint(stat.Token_While, stat.Condition:GetFirstToken())\n\t\t\tjoint(stat.Condition:GetLastToken(), stat.Token_Do)\n\t\t\tbodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'DoStat' then\n\t\t\tstript(stat.Token_Do)\n\t\t\tstript(stat.Token_End)\n\t\t\tbodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\n\t\telseif stat.Type == 'IfStat' then\n\t\t\tstript(stat.Token_If)\n\t\t\tstripExpr(stat.Condition)\n\t\t\tjoint(stat.Token_If, stat.Condition:GetFirstToken())\n\t\t\tjoint(stat.Condition:GetLastToken(), stat.Token_Then)\n\t\t\t--\n\t\t\tlocal lastBodyOpen = stat.Token_Then\n\t\t\tlocal lastBody = stat.Body\n\t\t\t--\n\t\t\tfor _, clause in pairs(stat.ElseClauseList) do\n\t\t\t\tbodyjoint(lastBodyOpen, lastBody, clause.Token)\n\t\t\t\tlastBodyOpen = clause.Token\n\t\t\t\t--\n\t\t\t\tif clause.Condition then\n\t\t\t\t\tstripExpr(clause.Condition)\n\t\t\t\t\tjoint(clause.Token, clause.Condition:GetFirstToken())\n\t\t\t\t\tjoint(clause.Condition:GetLastToken(), clause.Token_Then)\n\t\t\t\t\tlastBodyOpen = clause.Token_Then\n\t\t\t\tend\n\t\t\t\tstripStat(clause.Body)\n\t\t\t\tlastBody = clause.Body\n\t\t\tend\n\t\t\t--\n\t\t\tbodyjoint(lastBodyOpen, lastBody, stat.Token_End)\n\n\t\telseif stat.Type == 'CallExprStat' then\n\t\t\tstripExpr(stat.Expression)\n\t\telseif stat.Type == 'AssignmentStat' then\n\t\t\tfor index, ex in pairs(stat.Lhs) do\n\t\t\t\tstripExpr(ex)\n\t\t\t\tlocal sep = stat.Token_LhsSeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\t\tstript(stat.Token_Equals)\n\t\t\tfor index, ex in pairs(stat.Rhs) do\n\t\t\t\tstripExpr(ex)\n\t\t\t\tlocal sep = stat.Token_RhsSeparatorList[index]\n\t\t\t\tif sep then\n\t\t\t\t\tstript(sep)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tassert(false, \"unreachable\")\n\t\tend\n\tend\n\n\tstripStat(ast)\nend\n\nlocal VarDigits = {}\nfor i = ('a'):byte(), ('z'):byte() do table.insert(VarDigits, string.char(i)) end\nfor i = ('A'):byte(), ('Z'):byte() do table.insert(VarDigits, string.char(i)) end\nfor i = ('0'):byte(), ('9'):byte() do table.insert(VarDigits, string.char(i)) end\ntable.insert(VarDigits, '_')\nlocal VarStartDigits = {}\nfor i = ('a'):byte(), ('z'):byte() do table.insert(VarStartDigits, string.char(i)) end\nfor i = ('A'):byte(), ('Z'):byte() do table.insert(VarStartDigits, string.char(i)) end\nlocal function indexToVarName(index)\n\tlocal id = ''\n\tlocal d = index % #VarStartDigits\n\tindex = (index - d) / #VarStartDigits\n\tid = id..VarStartDigits[d+1]\n\twhile index > 0 do\n\t\td = index % #VarDigits\n\t\tindex = (index - d) / #VarDigits\n\t\tid = id..VarDigits[d+1]\n\tend\n\treturn id\nend\nlocal function MinifyVariables(globalScope, rootScope)\n\t-- externalGlobals is a set of global variables that have not been assigned to, that is\n\t-- global variables defined \"externally to the script\". We are not going to be renaming \n\t-- those, and we have to make sure that we don't collide with them when renaming \n\t-- things so we keep track of them in this set.\n\tlocal externalGlobals = {}\n\n\t-- First we want to rename all of the variables to unique temporaries, so that we can\n\t-- easily use the scope::GetVar function to check whether renames are valid.\n\tlocal temporaryIndex = 0\n\tfor _, var in pairs(globalScope) do\n\t\tif var.AssignedTo then\n\t\t\tvar:Rename('_TMP_'..temporaryIndex..'_')\n\t\t\ttemporaryIndex = temporaryIndex + 1\n\t\telse\n\t\t\t-- Not assigned to, external global\n\t\t\texternalGlobals[var.Name] = true\n\t\tend\n\tend\n\n\t-- Now we go through renaming, first do globals, we probably want them\n\t-- to have shorter names in general.\n\t-- TODO: Rename all vars based on frequency patterns, giving variables\n\t--       used more shorter names.\n\tlocal nextFreeNameIndex = 0\n\tfor _, var in pairs(globalScope) do\n\t\tif var.AssignedTo then\n\t\t\tlocal varName\n\t\t\trepeat\n\t\t\t\tvarName = indexToVarName(nextFreeNameIndex)\n\t\t\t\tnextFreeNameIndex = nextFreeNameIndex + 1\n\t\t\tuntil not Keywords[varName] and not externalGlobals[varName]\n\t\t\tvar:Rename(varName)\n\t\tend\n\tend\n\n\t-- Now rename all local vars\n\trootScope.FirstFreeName = nextFreeNameIndex\n\tlocal function doRenameScope(scope)\n\t\tfor _, var in pairs(scope.VariableList) do\n\t\t\tlocal varName\n\t\t\trepeat\n\t\t\t\tvarName = indexToVarName(scope.FirstFreeName)\n\t\t\t\tscope.FirstFreeName = scope.FirstFreeName + 1\n\t\t\tuntil not Keywords[varName] and not externalGlobals[varName]\n\t\t\tvar:Rename(varName)\n\t\tend\n\t\tfor _, childScope in pairs(scope.ChildScopeList) do\n\t\t\tchildScope.FirstFreeName = scope.FirstFreeName\n\t\t\tdoRenameScope(childScope)\n\t\tend\n\tend\n\tdoRenameScope(rootScope)\nend\n\n--[[\nlocal function MinifyVariables_2(globalScope, rootScope)\n\t-- Variable names and other names that are fixed, that we cannot use\n\t-- Either these are Lua keywords, or globals that are not assigned to,\n\t-- that is environmental globals that are assigned elsewhere beyond our \n\t-- control.\n\tlocal globalUsedNames = {}\n\tfor kw, _ in pairs(Keywords) do\n\t\tglobalUsedNames[kw] = true\n\tend\n\n\t-- Gather a list of all of the variables that we will rename\n\tlocal allVariables = {}\n\tlocal allLocalVariables = {}\n\tdo\n\t\t-- Add applicable globals\n\t\tfor _, var in pairs(globalScope) do\n\t\t\tif var.AssignedTo then\n\t\t\t\t-- We can try to rename this global since it was assigned to\n\t\t\t\t-- (and thus presumably initialized) in the script we are \n\t\t\t\t-- minifying.\n\t\t\t\ttable.insert(allVariables, var)\n\t\t\telse\n\t\t\t\t-- We can't rename this global, mark it as an unusable name\n\t\t\t\t-- and don't add it to the rename list\n\t\t\t\tglobalUsedNames[var.Name] = true\n\t\t\tend\n\t\tend\n\n\t\t-- Recursively add locals, we can rename all of those\n\t\tlocal function addFrom(scope)\n\t\t\tfor _, var in pairs(scope.VariableList) do\n\t\t\t\ttable.insert(allVariables, var)\n\t\t\t\ttable.insert(allLocalVariables, var)\n\t\t\tend\n\t\t\tfor _, childScope in pairs(scope.ChildScopeList) do\n\t\t\t\taddFrom(childScope)\n\t\t\tend\n\t\tend\n\t\taddFrom(rootScope)\n\tend\n\n\t-- Add used name arrays to variables\n\tfor _, var in pairs(allVariables) do\n\t\tvar.UsedNameArray = {}\n\tend\n\n\t-- Sort the least used variables first\n\ttable.sort(allVariables, function(a, b)\n\t\treturn #a.RenameList < #b.RenameList\n\tend)\n\n\t-- Lazy generator for valid names to rename to\n\tlocal nextValidNameIndex = 0\n\tlocal varNamesLazy = {}\n\tlocal function varIndexToValidVarName(i)\n\t\tlocal name = varNamesLazy[i] \n\t\tif not name then\n\t\t\trepeat\n\t\t\t\tname = indexToVarName(nextValidNameIndex)\n\t\t\t\tnextValidNameIndex = nextValidNameIndex + 1\n\t\t\tuntil not globalUsedNames[name]\n\t\t\tvarNamesLazy[i] = name\n\t\tend\n\t\treturn name\n\tend\n\n\t-- For each variable, go to rename it\n\tfor _, var in pairs(allVariables) do\n\t\t-- Lazy... todo: Make this pair a proper for-each-pair-like set of loops\n\t\t-- rather than using a renamed flag.\n\t\tvar.Renamed = true\n\n\t\t-- Find the first unused name\n\t\tlocal i = 1\n\t\twhile var.UsedNameArray[i] do\n\t\t\ti = i + 1\n\t\tend\n\n\t\t-- Rename the variable to that name\n\t\tvar:Rename(varIndexToValidVarName(i))\n\n\t\tif var.Scope then\n\t\t\t-- Now we need to mark the name as unusable by any variables:\n\t\t\t--  1) At the same depth that overlap lifetime with this one\n\t\t\t--  2) At a deeper level, which have a reference to this variable in their lifetimes\n\t\t\t--  3) At a shallower level, which are referenced during this variable's lifetime\n\t\t\tfor _, otherVar in pairs(allVariables) do\n\t\t\t\tif not otherVar.Renamed then\n\t\t\t\t\tif not otherVar.Scope or otherVar.Scope.Depth < var.Scope.Depth then\n\t\t\t\t\t\t-- Check Global variable (Which is always at a shallower level)\n\t\t\t\t\t\t--  or\n\t\t\t\t\t\t-- Check case 3\n\t\t\t\t\t\t-- The other var is at a shallower depth, is there a reference to it\n\t\t\t\t\t\t-- during this variable's lifetime?\n\t\t\t\t\t\tfor _, refAt in pairs(otherVar.ReferenceLocationList) do\n\t\t\t\t\t\t\tif refAt >= var.BeginLocation and refAt <= var.ScopeEndLocation then\n\t\t\t\t\t\t\t\t-- Collide\n\t\t\t\t\t\t\t\totherVar.UsedNameArray[i] = true\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\telseif otherVar.Scope.Depth > var.Scope.Depth then\n\t\t\t\t\t\t-- Check Case 2\n\t\t\t\t\t\t-- The other var is at a greater depth, see if any of the references\n\t\t\t\t\t\t-- to this variable are in the other var's lifetime.\n\t\t\t\t\t\tfor _, refAt in pairs(var.ReferenceLocationList) do\n\t\t\t\t\t\t\tif refAt >= otherVar.BeginLocation and refAt <= otherVar.ScopeEndLocation then\n\t\t\t\t\t\t\t\t-- Collide\n\t\t\t\t\t\t\t\totherVar.UsedNameArray[i] = true\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\telse --otherVar.Scope.Depth must be equal to var.Scope.Depth\n\t\t\t\t\t\t-- Check case 1\n\t\t\t\t\t\t-- The two locals are in the same scope\n\t\t\t\t\t\t-- Just check if the usage lifetimes overlap within that scope. That is, we\n\t\t\t\t\t\t-- can shadow a local variable within the same scope as long as the usages\n\t\t\t\t\t\t-- of the two locals do not overlap.\n\t\t\t\t\t\tif var.BeginLocation < otherVar.EndLocation and\n\t\t\t\t\t\t\tvar.EndLocation > otherVar.BeginLocation\n\t\t\t\t\t\tthen\n\t\t\t\t\t\t\totherVar.UsedNameArray[i] = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- This is a global var, all other globals can't collide with it, and\n\t\t\t-- any local variable with a reference to this global in it's lifetime\n\t\t\t-- can't collide with it.\n\t\t\tfor _, otherVar in pairs(allVariables) do\n\t\t\t\tif not otherVar.Renamed then\n\t\t\t\t\tif otherVar.Type == 'Global' then\n\t\t\t\t\t\totherVar.UsedNameArray[i] = true\n\t\t\t\t\telseif otherVar.Type == 'Local' then\n\t\t\t\t\t\t-- Other var is a local, see if there is a reference to this global within\n\t\t\t\t\t\t-- that local's lifetime.\n\t\t\t\t\t\tfor _, refAt in pairs(var.ReferenceLocationList) do\n\t\t\t\t\t\t\tif refAt >= otherVar.BeginLocation and refAt <= otherVar.ScopeEndLocation then\n\t\t\t\t\t\t\t\t-- Collide\n\t\t\t\t\t\t\t\totherVar.UsedNameArray[i] = true\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tassert(false, \"unreachable\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\n\t-- -- \n\t-- print(\"Total Variables: \"..#allVariables)\n\t-- print(\"Total Range: \"..rootScope.BeginLocation..\"-\"..rootScope.EndLocation)\n\t-- print(\"\")\n\t-- for _, var in pairs(allVariables) do\n\t-- \tio.write(\"`\"..var.Name..\"':\\n\\t#symbols: \"..#var.RenameList..\n\t-- \t\t\"\\n\\tassigned to: \"..tostring(var.AssignedTo))\n\t-- \tif var.Type == 'Local' then\n\t-- \t\tio.write(\"\\n\\trange: \"..var.BeginLocation..\"-\"..var.EndLocation)\n\t-- \t\tio.write(\"\\n\\tlocal type: \"..var.Info.Type)\n\t-- \tend\n\t-- \tio.write(\"\\n\\n\")\n\t-- end\n\n\t-- -- First we want to rename all of the variables to unique temporaries, so that we can\n\t-- -- easily use the scope::GetVar function to check whether renames are valid.\n\t-- local temporaryIndex = 0\n\t-- for _, var in pairs(allVariables) do\n\t-- \tvar:Rename('_TMP_'..temporaryIndex..'_')\n\t-- \ttemporaryIndex = temporaryIndex + 1\n\t-- end\n\n\t-- For each variable, we need to build a list of names that collide with it\n\n\t--\n\t--error()\nend\n--]]\n\nlocal function BeautifyVariables(globalScope, rootScope)\n\t-- externalGlobals isn't used anywhere, so this setup seems pointless\n\t--[[\n\tlocal externalGlobals = {}\n\tfor _, var in pairs(globalScope) do\n\t\tif not var.AssignedTo then\n\t\t\texternalGlobals[var.Name] = true\n\t\tend\n\tend\n\t--]]\n\n\tlocal localNumber = 1\n\tlocal globalNumber = 1\n\n\tlocal function setVarName(var, name)\n\t\tvar.Name = name\n\t\tfor _, setter in pairs(var.RenameList) do\n\t\t\tsetter(name)\n\t\tend\n\tend\n\n\tfor _, var in pairs(globalScope) do\n\t\tif var.AssignedTo then\n\t\t\tsetVarName(var, 'G_'..globalNumber)\n\t\t\tglobalNumber = globalNumber + 1\n\t\tend\n\tend\n\n\tlocal function modify(scope)\n\t\tfor _, var in pairs(scope.VariableList) do\n\t\t\tlocal name = 'L_'..localNumber..'_'\n\t\t\tif var.Info.Type == 'Argument' then\n\t\t\t\tname = name..'arg'..var.Info.Index\n\t\t\telseif var.Info.Type == 'LocalFunction' then\n\t\t\t\tname = name..'func'\n\t\t\telseif var.Info.Type == 'ForRange' then\n\t\t\t\tname = name..'forvar'..var.Info.Index\n\t\t\tend\n\t\t\tsetVarName(var, name)\n\t\t\tlocalNumber = localNumber + 1\n\t\tend\n\t\tfor _, child in pairs(scope.ChildScopeList) do\n\t\t\tmodify(child)\n\t\tend\n\tend\n\tmodify(rootScope)\nend\n\nlocal function usageError()\n\terror(\n\t\t\t\"\\nusage: minify <file> or unminify <file>\\n\" ..\n\t\t\t\"  The modified code will be printed to the stdout, pipe it to a file, the\\n\" ..\n\t\t\t\"  lua interpreter, or something else as desired EG:\\n\\n\" ..\n\t\t\t\"        lua minify.lua minify input.lua > output.lua\\n\\n\" ..\n\t\t\t\"  * minify will minify the code in the file.\\n\" ..\n\t\t\t\"  * unminify will beautify the code and replace the variable names with easily\\n\" ..\n\t\t\t\"    find-replacable ones to aide in reverse engineering minified code.\\n\", 0)\nend\n\n-- Enable this file to be used as a module:\n-- If it gets loaded via require(), return a module table with exported entities\nif debug.getinfo(2, \"n\").name == \"require\" then\n\treturn {\n\t\tAddVariableInfo = AddVariableInfo,\n\t\tAstToString = AstToString,\n\t\tBeautifyVariables = BeautifyVariables,\n\t\tCountTable = CountTable,\n\t\tCreateLuaParser = CreateLuaParser,\n\t\tCreateLuaTokenStream = CreateLuaTokenStream,\n\t\tFormatAst = FormatAst,\n\t\tFormatTable = FormatTable,\n\t\tGlobalRenameIgnore = GlobalRenameIgnore,\n\t\tMinifyVariables = MinifyVariables,\n\t\tPrintAst = PrintAst,\n\t\tStripAst = StripAst,\n\t}\nend\n\nlocal args = {...}\nif #args ~= 2 then\n\tusageError()\nend\n\nlocal sourceFile = io.open(args[2], 'r')\nif not sourceFile then\n\terror(\"Could not open the input file `\" .. args[2] .. \"`\", 0)\nend\n\nlocal function minify(ast, global_scope, root_scope)\n\tMinifyVariables(global_scope, root_scope)\n\tStripAst(ast)\n\tPrintAst(ast)\nend\n\nlocal function beautify(ast, global_scope, root_scope)\n\tBeautifyVariables(global_scope, root_scope)\n\tFormatAst(ast)\n\tPrintAst(ast)\nend\n\nlocal data = sourceFile:read('*all')\nlocal ast = CreateLuaParser(data)\nlocal global_scope, root_scope = AddVariableInfo(ast)\n\nif args[1] == 'minify' then\n\tminify(ast, global_scope, root_scope)\nelseif args[1] == 'unminify' then\n\tbeautify(ast, global_scope, root_scope)\nelse\n\tusageError()\nend\n"
  },
  {
    "path": "roblox/README.md",
    "content": "# Roblox-ECS\n\nLINK: https://github.com/nidorx/ecs-lua/blob/master/src/shared/ECSUtil.lua\n\n## Utility Systems and Components\n\nECSUtil provides some basic systems and components, described below\n\n### Components\n- _ECSUtil._**BasePartComponent**\n   - A component that facilitates access to BasePart\n- _ECSUtil._**PositionComponent**\n   - Component that works with a position `Vector3`\n- _ECSUtil._**RotationComponent**\n   - Rotational vectors _(right, up, look)_ that represents the object in the 3d world. To transform into a CFrame use `CFrame.fromMatrix(pos, rot[1], rot[2], rot[3] * -1)`\n- _ECSUtil._**PositionInterpolationComponent**\n   - Allows to register two last positions (`Vector3`) to allow interpolation\n- _ECSUtil._**RotationInterpolationComponent**\n   - Allows to record two last rotations (`rightVector`, `upVector`, `lookVector`) to allow interpolation\n- _ECSUtil._**BasePartToEntitySyncComponent**\n   - Tag, indicates that the `Entity` _(ECS)_ must be synchronized with the data from the `BasePart` _(workspace)_\n- _ECSUtil._**EntityToBasePartSyncComponent**\n   - Tag, indicates that the `BasePart` _(workspace)_ must be synchronized with the existing data in the `Entity` _(ECS)_\n- _ECSUtil._**MoveForwardComponent**\n   - Tag, indicates that the forward movement system must act on this entity\n- _ECSUtil._**MoveSpeedComponent**\n   - Allows you to define a movement speed for specialized handling systems\n\n\n### Systems\n- _ECSUtil._**BasePartToEntityProcessInSystem**\n   - Synchronizes the `Entity` _(ECS)_ with the data of a `BasePart` _(workspace)_ at the beginning of the `process` step\n   -  ```lua\n      step  = 'process',\n      order = 10,\n      requireAll = {\n         ECSUtil.BasePartComponent,\n         ECSUtil.PositionComponent,\n         ECSUtil.RotationComponent,\n         ECSUtil.BasePartToEntitySyncComponent\n      },\n      rejectAny = {\n         ECSUtil.PositionInterpolationComponent,\n         ECSUtil.RotationInterpolationComponent\n      }\n      ```\n- _ECSUtil._**BasePartToEntityTransformSystem**\n   - Synchronizes the `Entity` _(ECS)_ with the data of a `BasePart` _(workspace)_ at the beginning of the `transform` step _(After running the Roblox physics engine)_\n   -  ```lua\n      step  = 'transform',\n      order = 10,\n      requireAll = {\n         ECSUtil.BasePartComponent,\n         ECSUtil.PositionComponent,\n         ECSUtil.RotationComponent,\n         ECSUtil.BasePartToEntitySyncComponent\n      },\n      rejectAny = {\n         ECSUtil.PositionInterpolationComponent,\n         ECSUtil.RotationInterpolationComponent\n      }\n      ```\n- _ECSUtil._**EntityToBasePartProcessOutSystem**\n   - Synchronizes the `BasePart` _(workspace)_ with the `Entity` _(ECS)_ data at the end of the `processOut` step _(before Roblox's physics engine runs)_\n   -  ```lua\n      step  = 'process',\n      order = 100,\n      requireAll = {\n         ECSUtil.BasePartComponent,\n         ECSUtil.PositionComponent,\n         ECSUtil.RotationComponent,\n         ECSUtil.EntityToBasePartSyncComponent\n      }\n      ```\n- _ECSUtil._**EntityToBasePartTransformSystem**\n   - Synchronizes the `BasePart` _(workspace)_ with the `Entity` _(ECS)_ data at the end of the `transform` step _(last step of the current frame in multi-thread execution)_\n   -  ```lua\n      step  = 'transform',\n      order = 100,\n      requireAll = {\n         ECSUtil.BasePartComponent,\n         ECSUtil.PositionComponent,\n         ECSUtil.RotationComponent,\n         ECSUtil.EntityToBasePartSyncComponent\n      },\n      rejectAny = {\n         ECSUtil.PositionInterpolationComponent,\n         ECSUtil.RotationInterpolationComponent\n      }\n      ```\n- _ECSUtil._**EntityToBasePartInterpolationTransformSystem**\n   - Interpolates the position and rotation of a BasePart in the `transform` step. Allows the `process` step to be performed at low frequency with smooth rendering\n   -  ```lua\n      step  = 'transform',\n      order = 100,\n      requireAll = {\n         ECSUtil.BasePartComponent,\n         ECSUtil.PositionComponent,\n         ECSUtil.RotationComponent,\n         ECSUtil.PositionInterpolationComponent,\n         ECSUtil.RotationInterpolationComponent,\n         ECSUtil.EntityToBasePartSyncComponent\n      }\n      ```\n- _ECSUtil._**MoveForwardSystem**\n   - Simple forward movement system (position = position + speed * lookVector)\n   -  ```lua\n      step = 'process',\n      requireAll = {\n         ECSUtil.MoveSpeedComponent,\n         ECSUtil.PositionComponent,\n         ECSUtil.RotationComponent,\n         ECSUtil.MoveForwardComponent,\n      }\n      ```\n"
  },
  {
    "path": "roblox/RobloxUtils.lua",
    "content": "local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- precision\nlocal EPSILON = 0.000000001\n\nlocal function floatEQ(n0, n1)\n   if n0 == n1 then\n      return true\n   end\n\n   return math.abs(n1 - n0) < EPSILON\nend\n\n\nlocal function vectorEQ(v0, v1)\n   if v0 == v1 then\n      return true\n   end\n\n   if not floatEQ(v0.X, v1.X) or not floatEQ(v0.Y, v1.Y) or not floatEQ(v0.Z, v1.Z) then\n      return false\n   else\n      return true\n   end\nend\n\n----------------------------------------------------------------------------------------------------------------------\n-- UTILITY COMPONENTS & SYSTEMS\n----------------------------------------------------------------------------------------------------------------------\n\nlocal ECSUtil = {}\n\n\n-- A component that facilitates access to BasePart\nECSUtil.BasePartComponent = ECS.Component('BasePart', function(object)\n   if object == nil or object['IsA'] == nil or object:IsA('BasePart') == false then\n      error(\"This component only works with BasePart objects\")\n   end\n\n   return object\nend)\n\n-- Tag, indicates that the entity must be synchronized with the data from the BasePart (workspace)\nECSUtil.BasePartToEntitySyncComponent = ECS.Component('BasePartToEntitySync', nil, true)\n\n-- Tag, indicates that the BasePart (workspace) must be synchronized with the existing data in the Entity (ECS)\nECSUtil.EntityToBasePartSyncComponent = ECS.Component('EntityToBasePartSync', nil, true)\n\n-- Component that works with a position Vector3\nECSUtil.PositionComponent = ECS.Component('Position', function(position)\n   if position ~= nil and typeof(position) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects\")\n   end\n\n   if position == nil then\n      position = Vector3.new(0, 0, 0)\n   end\n\n   return position\nend)\n\n-- Allows to register two last positions (Vector3) to allow interpolation\nECSUtil.PositionInterpolationComponent = ECS.Component('PositionInterpolation', function(position)\n   if position ~= nil and typeof(position) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects\")\n   end\n\n   if position == nil then\n      position = Vector3.new(0, 0, 0)\n   end\n\n   return {position, position}\nend)\n\n-- {avgDelta, lastUpdate, position, rightVector, upVector, lookVector}\nECSUtil.InterpolationCustomComponent = ECS.Component('InterpolationCustom', function(avgDelta, lastUpdate, lastPosition, lastRightVector, lastUpVector, lastLookVector)\n   if avgDelta == nil then\n      return nil\n   end\n   return {avgDelta, lastUpdate, lastPosition, lastRightVector, lastUpVector, lastLookVector}\nend)\n\nlocal VEC3_R = Vector3.new(1, 0, 0)\nlocal VEC3_U = Vector3.new(0, 1, 0)\nlocal VEC3_F = Vector3.new(0, 0, 1)\n\n--[[\n   Rotational vectors that represents the object in the 3d world.\n   To transform into a CFrame use CFrame.fromMatrix(pos, rot[1], rot[2], rot[3] * -1)\n\n   Params\n      lookVector  {Vector3}   @See CFrame.LookVector\n      rightVector {Vector3}   @See CFrame.RightVector\n      upVector    {Vector3}   @See CFrame.UpVector\n\n   @See\n      https://devforum.roblox.com/t/understanding-cframe-frommatrix-the-replacement-for-cframe-new/593742\n      https://devforum.roblox.com/t/handling-the-edge-cases-of-cframe-frommatrix/632465\n]]\nECSUtil.RotationComponent = ECS.Component('Rotation', function(rightVector, upVector, lookVector)\n\n   if rightVector ~= nil and typeof(rightVector) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects [param=rightVector]\")\n   end\n\n   if upVector ~= nil and typeof(upVector) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects [param=upVector]\")\n   end\n\n   if lookVector ~= nil and typeof(lookVector) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects [param=lookVector]\")\n   end\n\n   if rightVector == nil then\n      rightVector = VEC3_R\n   end\n\n   if upVector == nil then\n      upVector = VEC3_U\n   end\n\n   if lookVector == nil then\n      lookVector = VEC3_F\n   end\n\n   return {rightVector, upVector, lookVector}\nend)\n\n-- Allows to record two last rotations (rightVector, upVector, lookVector) to allow interpolation\nECSUtil.RotationInterpolationComponent = ECS.Component('RotationInterpolation', function(rightVector, upVector, lookVector)\n\n   if rightVector ~= nil and typeof(rightVector) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects [param=rightVector]\")\n   end\n\n   if upVector ~= nil and typeof(upVector) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects [param=upVector]\")\n   end\n\n   if lookVector ~= nil and typeof(lookVector) ~= 'Vector3' then\n      error(\"This component only works with Vector3 objects [param=lookVector]\")\n   end\n\n   if rightVector == nil then\n      rightVector = VEC3_R\n   end\n\n   if upVector == nil then\n      upVector = VEC3_U\n   end\n\n   if lookVector == nil then\n      lookVector = VEC3_F\n   end\n\n   return {{rightVector, upVector, lookVector}, {rightVector, upVector, lookVector}}\nend)\n\n-- Tag, indicates that the forward movement system must act on this entity\nECSUtil.MoveForwardComponent = ECS.Component('MoveForward', nil, true)\n\n-- Allows you to define a movement speed for specialized handling systems\nECSUtil.MoveSpeedComponent = ECS.Component('MoveSpeed', function(speed)\n   if speed == nil or typeof(speed) ~= 'number' then\n      error(\"This component only works with number value\")\n   end\n\n   return speed\nend)\n\n------------------------------------------\n--[[\n   Utility system that copies the direction and position of a Roblox BasePart to the ECS entity\n\n   Executed in two moments: At the beginning of the \"process\" step and at the beginning of the \"transform\" step\n]]\n---------------------------------------->>\nlocal function BasePartToEntityUpdate(time, world, dirty, entity, index, parts, positions, rotations)\n\n   local changed = false\n   local part = parts[index]\n\n   if part ~= nil then\n\n      local position = positions[index]\n      local basePos = part.CFrame.Position\n      if position == nil or not vectorEQ(basePos, position) then\n         positions[index] = basePos\n         changed = true\n      end\n\n      local rotation    = rotations[index]\n      local rightVector =  part.CFrame.RightVector\n      local upVector    =  part.CFrame.UpVector\n      local lookVector  =  part.CFrame.LookVector\n      if rotation == nil or not vectorEQ(rightVector, rotation[1]) or not vectorEQ(upVector, rotation[2]) or not vectorEQ(lookVector, rotation[3]) then\n         rotations[index] = {rightVector, upVector, lookVector}\n         changed = true\n      end\n   end\n\n   return changed\nend\n\n-- copia dados de basepart para entidade no inicio do processamento, ignora entidades marcadas com Interpolation\nECSUtil.BasePartToEntityProcessInSystem = ECS.RegisterSystem({\n   Name  = 'BasePartToEntityProcessIn',\n   Step  = 'process',\n   Order = 10,\n   RequireAll = {\n      ECSUtil.BasePartComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.BasePartToEntitySyncComponent\n   },\n   RejectAny = {\n      ECSUtil.PositionInterpolationComponent,\n      ECSUtil.RotationInterpolationComponent\n   },\n   Update = BasePartToEntityUpdate\n})\n\n-- copia dados de um BasePart para entidade no inicio do passo transform\nECSUtil.BasePartToEntityTransformSystem = ECS.RegisterSystem({\n   Name  = 'BasePartToEntityTransform',\n   Step  = 'transform',\n   Order = 10,\n   RequireAll = {\n      ECSUtil.BasePartComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.BasePartToEntitySyncComponent\n   },\n   RejectAny = {\n      ECSUtil.PositionInterpolationComponent,\n      ECSUtil.RotationInterpolationComponent\n   },\n   Update = BasePartToEntityUpdate\n})\n----------------------------------------<<\n\n------------------------------------------\n--[[\n   Utility system that copies the direction and position from ECS entity to a Roblox BasePart\n\n   Executed in two moments: At the end of the \"process\" step and at the end of the \"transform\" step\n]]\n---------------------------------------->>\n\nlocal function EntityToBasePartUpdate(time, world, dirty, entity, index, parts, positions, rotations)\n\n   if not dirty then\n      return false\n   end\n\n   local changed  = false\n   local part     = parts[index]\n   local position = positions[index]\n   local rotation = rotations[index]\n   if part ~= nil then\n      local basePos     = part.CFrame.Position\n      local rightVector = part.CFrame.RightVector\n      local upVector    = part.CFrame.UpVector\n      local lookVector  = part.CFrame.LookVector\n\n      -- goal cframe, allow interpolation\n      local cframe = part.CFrame\n\n      if position ~= nil and not vectorEQ(basePos, position) then\n         cframe = CFrame.fromMatrix(position, rightVector, upVector, lookVector * -1)\n         changed = true\n      end\n\n      if rotation ~= nil then\n         if not vectorEQ(rightVector, rotation[1]) or not vectorEQ(upVector, rotation[2]) or not vectorEQ(lookVector, rotation[3]) then\n            cframe = CFrame.fromMatrix(cframe.Position, rotation[1], rotation[2], rotation[3] * -1)\n            changed = true\n         end\n      end\n\n      if changed then\n         part.CFrame = cframe\n      end\n   end\n\n   return changed\nend\n\n-- copia dados da entidade para um BaseParte no fim do processamento\nECSUtil.EntityToBasePartProcessOutSystem = ECS.RegisterSystem({\n   Name  = 'EntityToBasePartProcess',\n   Step  = 'process',\n   Order = 100,\n   RequireAll = {\n      ECSUtil.BasePartComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.EntityToBasePartSyncComponent\n   },\n   Update = EntityToBasePartUpdate\n})\n\n-- copia dados de uma entidade para um BsePart no passo de transformação, ignora entidades com interpolação\nECSUtil.EntityToBasePartTransformSystem = ECS.RegisterSystem({\n   Name  = 'EntityToBasePartTransform',\n   Step  = 'transform',\n   Order = 100,\n   RequireAll = {\n      ECSUtil.BasePartComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.EntityToBasePartSyncComponent\n   },\n   RejectAny = {\n      ECSUtil.PositionInterpolationComponent,\n      ECSUtil.RotationInterpolationComponent\n   },\n   Update = EntityToBasePartUpdate\n})\n\n-- Interpolates the position and rotation of a BasePart in the transform step.\n-- Allows the process step to be performed at low frequency and with smooth rendering\nlocal interpolationFactor = 1\nECSUtil.EntityToBasePartInterpolationTransformSystem = ECS.RegisterSystem({\n   Name  = 'EntityToBasePartInterpolationTransform',\n   Step  = 'transform',\n   Order = 100,\n   RequireAll = {\n      ECSUtil.BasePartComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.PositionInterpolationComponent,\n      ECSUtil.RotationInterpolationComponent,\n      ECSUtil.EntityToBasePartSyncComponent\n   },\n   RejectAny ={\n      ECSUtil.InterpolationCustomComponent\n   },\n   BeforeUpdate = function(time, interpolation, world, system)\n      interpolationFactor = interpolation\n   end,\n   Update = function(time, world, dirty, entity, index, parts, positions, rotations, positionsInt, rotationsInt)\n\n      local part     = parts[index]\n      local position = positions[index]\n      local rotation = rotations[index]\n\n      if part ~= nil then\n         -- goal cframe, allow interpolation\n         local cframe = part.CFrame\n\n         -- swap old and new position, if changed\n         if position ~= nil then\n            local rightVector = part.CFrame.RightVector\n            local upVector    = part.CFrame.UpVector\n            local lookVector  = part.CFrame.LookVector\n\n            if not vectorEQ(positionsInt[index][1], position) then\n               positionsInt[index][2] = positionsInt[index][1]\n               positionsInt[index][1] = position\n            end\n\n            local oldPosition = positionsInt[index][2]\n            cframe = CFrame.fromMatrix(oldPosition:Lerp(position, interpolationFactor), rightVector, upVector, lookVector * -1)\n         end\n\n         -- swap old and new rotation, if changed\n         if rotation ~= nil then\n            if not vectorEQ(rotationsInt[index][1][1], rotation[1])\n               or not vectorEQ(rotationsInt[index][1][2], rotation[2])\n               or not vectorEQ(rotationsInt[index][1][3], rotation[3])\n            then\n               rotationsInt[index][2] = rotationsInt[index][1]\n               rotationsInt[index][1] = rotation\n            end\n\n            local oldRotation = rotationsInt[index][2]\n            cframe = CFrame.fromMatrix(\n               cframe.Position,\n               oldRotation[1]:Lerp(rotation[1], interpolationFactor),\n               oldRotation[2]:Lerp(rotation[2], interpolationFactor),\n               (oldRotation[3] * -1):Lerp((rotation[3] * -1), interpolationFactor)\n            )\n         end\n\n         part.CFrame = cframe\n      end\n\n      -- readonly\n      return false\n   end\n})\n\n-- Customized interpolation, the developer is responsible for indicating the necessary parameters for calculating the interpolation \nECSUtil.EntityToBasePartInterpolationCustomTransformSystem = ECS.RegisterSystem({\n   Name  = 'EntityToBasePartInterpolationCustomTransform',\n   Step  = 'transform',\n   Order = 100,\n   RequireAll = {\n      ECSUtil.BasePartComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.InterpolationCustomComponent,\n      ECSUtil.EntityToBasePartSyncComponent\n   },\n   Update = function(time, world, dirty, entity, index, parts, positions, rotations, interpolations)\n\n      local part     = parts[index]\n      local position = positions[index]\n      local rotation = rotations[index]\n      -- {avgDelta, lastUpdate, position, rightVector, upVector, lookVector}\n      local interp = interpolations[index]\n\n      if part ~= nil and position ~= nil and rotation ~= nil and interp ~= nil then\n         local avgDelta    = interp[1]\n         local lastUpdate  = interp[2]\n         local alpha = (time.frame-lastUpdate)/avgDelta\n         local cframe = CFrame.fromMatrix(interp[3], interp[4], interp[5], interp[6] * -1)\n         part.CFrame = cframe:Lerp ( CFrame.fromMatrix(position, rotation[1], rotation[2], rotation[3] * -1), alpha )\n      end\n\n      -- readonly\n      return false\n   end\n})\n----------------------------------------<<\n\n-- Simple forward movement system (position = position + speed * lookVector)\nlocal moveForwardSpeedFactor = 1\nECSUtil.MoveForwardSystem = ECS.RegisterSystem({\n   Name = 'MoveForward',\n   Step = 'process',\n   RequireAll = {\n      ECSUtil.MoveSpeedComponent,\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      ECSUtil.MoveForwardComponent,\n   },\n   BeforeUpdate = function(time, interpolation, world, system)\n      moveForwardSpeedFactor = world.GetFrequency()/60\n   end,\n   Update = function (time, world, dirty, entity, index, speeds, positions, rotations, forwards)\n\n      local position = positions[index]\n      if position ~= nil then\n\n         local rotation = rotations[index]\n         if rotation ~= nil then\n\n            local speed = speeds[index]\n            if speed ~= nil then\n               -- speed/2 = 1 studs per second (120 = frequency)\n               positions[index] = position + speed/moveForwardSpeedFactor  * rotation[3]\n               return true\n            end\n         end\n      end\n\n      return false\n   end\n})\n\n-- Creates an entity related to a BasePart\nfunction ECSUtil.NewBasePartEntity(world, part, syncBasePartToEntity, syncEntityToBasePart, interpolate)\n   local entityID = world.Create()\n\n   world.Set(entityID, ECSUtil.BasePartComponent, part)\n   world.Set(entityID, ECSUtil.PositionComponent, part.CFrame.Position)\n   world.Set(entityID, ECSUtil.RotationComponent, part.CFrame.RightVector, part.CFrame.UpVector, part.CFrame.LookVector)\n\n   if syncBasePartToEntity then\n      world.Set(entityID, ECSUtil.BasePartToEntitySyncComponent)\n   end\n\n   if syncEntityToBasePart then\n      world.Set(entityID, ECSUtil.EntityToBasePartSyncComponent)\n   end\n\n   if interpolate then\n      world.Set(entityID, ECSUtil.PositionInterpolationComponent, part.CFrame.Position)\n      world.Set(entityID, ECSUtil.RotationInterpolationComponent, part.CFrame.RightVector, part.CFrame.UpVector, part.CFrame.LookVector)\n   end\n\n   return entityID\nend\n\n-- add default systems\nfunction  ECSUtil.AddDefaultSystems(world)\n   world.AddSystem(ECSUtil.BasePartToEntityProcessInSystem)\n   world.AddSystem(ECSUtil.MoveForwardSystem)\n   world.AddSystem(ECSUtil.EntityToBasePartProcessOutSystem)\n\n   -- transform\n   world.AddSystem(ECSUtil.BasePartToEntityTransformSystem)\n   world.AddSystem(ECSUtil.EntityToBasePartTransformSystem)\n   world.AddSystem(ECSUtil.EntityToBasePartInterpolationTransformSystem)\n   world.AddSystem(ECSUtil.EntityToBasePartInterpolationCustomTransformSystem)   \nend\n\n-- export ECS lib\nreturn ECSUtil\n"
  },
  {
    "path": "roblox/tutorial/default.project.json",
    "content": "{\n   \"name\": \"dat.GUI\",\n   \"tree\": {\n      \"$className\": \"DataModel\",\n      \"ReplicatedStorage\": {\n         \"$className\": \"ReplicatedStorage\",\n         \"$path\": \"src/shared\"\n      },\n      \"ServerScriptService\": {\n         \"$className\": \"ServerScriptService\",\n         \"$path\": \"src/server\"\n      },\n      \"StarterPlayer\": {\n         \"$className\": \"StarterPlayer\",\n         \"StarterCharacterScripts\": {\n            \"$className\": \"StarterCharacterScripts\",\n            \"$path\": \"src/client\"\n         }\n      }\n   }\n}"
  },
  {
    "path": "roblox/tutorial/src/client/benchmark/init.client.lua",
    "content": "repeat wait() until game:GetService('Players').LocalPlayer.Character\n\nlocal DISABLED = true\n\n--[[\n   Benchmark, data oriented design\n]]\n\n-- “Script timeout: exhausted allowed execution time”\n-- disable the timeout in Studio with the following command\n-- settings().Studio.ScriptTimeoutLength = -1\n\nlocal case = require(script:WaitForChild('soa'))\n\nspawn(function()\n\n   if DISABLED then\n      return\n   end\n\n   wait(5)\n\n   print('[Benchmark] '..case.name)\n      \n   local sizes = {1000, 2000, 5000, 10000, 50000, 100000, 200000, 300000, 500000}\n   local sufs = {'OOP', 'DOP'}\n   local samples = 20\n   local groups = {}\n\n   for it  = 1, samples do\n      for _,size in ipairs(sizes) do\n         print('[Benchmark] it '..it, ', size ', size)\n         if groups['t'..size] == nil then\n            groups['t'..size] = {}\n         end\n      \n         for _,suffix in ipairs(sufs) do\n            -- ignoring creation\n            local input = case['gen'..suffix](size)\n\n            if groups['t'..size][suffix] == nil then\n               groups['t'..size][suffix] = 0\n            end\n\n            local start_time = os.clock()\n\n            -- runing test\n            case['run'..suffix](input)\n\n            local duration = os.clock() - start_time\n            groups['t'..size][suffix] = groups['t'..size][suffix]  + duration\n         end\n      end\n   end\n\n   -- print raw data, to use in excel\n   print('[Benchmark] '..case.name)\n   for i,size in ipairs(sizes) do\n      if i == 1 then\n         print('size', 'oop_avg_time', 'dop_avg_time')\n      end\n      local oop = groups['t'..size]['OOP']\n      local dop = groups['t'..size]['DOP']\n      print(size,'\\t', oop/samples,'\\t', dop/samples,'\\t')\n   end\nend)"
  },
  {
    "path": "roblox/tutorial/src/client/benchmark/soa.lua",
    "content": "\nlocal case = {\n   name = 'Struct of Arrays vs. Array of Structs'\n}\n\n-- produce equal sequences of numbers for both tests\nlocal RANDOM_SEED = os.time()\n\nlocal Player = {}\nPlayer.__index = Player\n\nfunction Player.new(name, health, location, velocity, acceleration)\n   return setmetatable({\n      name = name,\n      health = health,\n      location = location,\n      velocity = velocity,\n      acceleration = acceleration,\n   }, Player)\nend\n\nfunction case.genOOP(size)\n   math.randomseed(RANDOM_SEED)\n   local players = table.create(size)\n\n   for i = 1, size do\n      players[i] = Player.new(\n         string.format(\"player_name_%s\", i),\n         100.0,\n         {x = math.random(0, 10.0), y = math.random(0, 10.0)},\n         {x = math.random(0, 10.0), y = math.random(0, 10.0)},\n         {x = math.random(0, 10.0), y = math.random(0, 10.0)}\n      )\n   end\n\n   return players\nend\n\nfunction case.genDOP(size)\n   math.randomseed(RANDOM_SEED)\n\n   local names = table.create(size)\n   local health = table.create(size)\n   local locations = table.create(size)\n   local velocities = table.create(size)\n   local accelerations = table.create(size)\n\n   for i = 1, size do\n      names[i]          = string.format(\"player_name_%s\", i)\n      health[i]         = 100.0\n      locations[i]      = {math.random(0, 10.0), math.random(0, 10.0)}\n      velocities[i]     = {math.random(0, 10.0), math.random(0, 10.0)}\n      accelerations[i]  = {math.random(0, 10.0), math.random(0, 10.0)}\n   end\n\n   return {\n      names = names,\n      health = health,\n      locations = locations,\n      velocities = velocities,\n      accelerations = accelerations\n  }\nend\n\nfunction case.runOOP(players)\n   for i, player in ipairs(players) do\n\n      if player.location.x > 100 or  player.location.y > 100 then\n         player.location = {x = 0, y = 0}\n      end\n\n      player.location = {\n         x = player.location.x + player.velocity.x,\n         y = player.location.y + player.velocity.y\n      }\n\n      if player.velocity.x > 100 or  player.velocity.y > 100 then\n         player.velocity = {x = 0, y = 0}\n      end\n      \n      player.velocity = {\n         x = player.velocity.x + player.acceleration.x,\n         y = player.velocity.y + player.acceleration.y\n      }\n\n   end\nend\n\nfunction case.runDOP(world)\n   local locations = world.locations\n   local velocities = world.velocities\n   local accelerations = world.accelerations\n   for i, location in ipairs(locations) do\n\n      if location[1] > 100 or location[2] > 100 then\n         locations[i] = {0, 0}\n      end\n\n      locations[i]  = {\n         location[1] + velocities[i][1],\n         location[2] + velocities[i][2]\n      }\n\n      if velocities[i][1] > 100 or velocities[i][2] > 100 then\n         velocities[i] = {0, 0}\n      end\n\n      velocities[i]  = {\n         velocities[i][1] + accelerations[i][1],\n         velocities[i][2] + accelerations[i][2]\n      }\n   end\nend\n\nreturn case\n"
  },
  {
    "path": "roblox/tutorial/src/client/tutorial/init.client.lua",
    "content": "repeat wait() until game.Players.LocalPlayer.Character\n\nlocal Players \t   = game:GetService(\"Players\")\nlocal Player \t   = Players.LocalPlayer\nlocal Character\t= Player.Character\n\n   \n-- services\nlocal ECS      = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\nlocal ECSUtil  = require(game.ReplicatedStorage:WaitForChild(\"ECSUtil\"))\n\n-- Components\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal WeaponComponent = require(Components:WaitForChild(\"WeaponComponent\"))\n\n-- Systems\nlocal Systems = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"system\")\nlocal FiringSystem         = require(Systems:WaitForChild(\"FiringSystem\"))\nlocal PlayerShootingSystem = require(Systems:WaitForChild(\"PlayerShootingSystem\"))\nlocal CleanupFiringSystem  = require(Systems:WaitForChild(\"CleanupFiringSystem\"))\n\n-- Our world\nlocal world = ECS.CreateWorld(nil, { Frequency = 10 })\nworld.AddSystem(FiringSystem)\nworld.AddSystem(PlayerShootingSystem)\nworld.AddSystem(CleanupFiringSystem)\nECSUtil.AddDefaultSystems(world)\n\n-- Our weapon\nlocal rightHand = Character:WaitForChild(\"RightHand\")\nlocal weapon = Instance.new(\"Part\", Character)\nweapon.CanCollide = false\nweapon.CastShadow = false\nweapon.Size       = Vector3.new(0.2, 0.2, 2)\nweapon.CFrame     = rightHand.CFrame + Vector3.new(0, 0, -1)\nweapon.Color      = Color3.fromRGB(255, 0, 255)\n\nlocal weldWeapon = Instance.new(\"WeldConstraint\", weapon)\nweldWeapon.Part0 = weapon\nweldWeapon.Part1 = rightHand\n\n-- weapon bullet spawn\nlocal BulletSpawnPart   = Instance.new(\"Part\", weapon)\nBulletSpawnPart.CanCollide = false\nBulletSpawnPart.CastShadow = false\nBulletSpawnPart.Color      = Color3.fromRGB(255, 255, 0)\nBulletSpawnPart.Size       = Vector3.new(0.6, 0.6, 0.6)\nBulletSpawnPart.Shape      = Enum.PartType.Ball\nBulletSpawnPart.CFrame     = weapon.CFrame + Vector3.new(0, 0, -1)\n\nlocal weldBulletSpawn = Instance.new(\"WeldConstraint\", BulletSpawnPart)\nweldBulletSpawn.Part0 = BulletSpawnPart\nweldBulletSpawn.Part1 = weapon\n\n-- Create our entity\nlocal bulletSpawnEntity = ECSUtil.NewBasePartEntity(world, BulletSpawnPart, true, false)\n\n-- Mark as weapon\nworld.Set(bulletSpawnEntity, WeaponComponent)"
  },
  {
    "path": "roblox/tutorial/src/server/tutorial/init.server.lua",
    "content": ""
  },
  {
    "path": "roblox/tutorial/src/shared/teste.lua",
    "content": "local ECS = {}\n\nlocal Query, System, Component,  = ECS.Query, ECS.System, ECS.Component,\n\n\nlocal Transform = Component({\n   Position = Vector3.new(),\n   Rotation = Vector3.new()\n})\n\nlocal IsActive = Component(nil, true)\n\nlocal MovementSystem = System('proccess', 100, Query.All(Transform, IsActive))\n\nMovementSystem.After = {OtherSystem}\nMovementSystem.Before = {TransformSystem}\n\nfunction MovementSystem:Initialize()\n   print(\"Initialized\")\nend\n\nfunction MovementSystem:ShouldUpdate(Time)\n   return true\nend\n\nfunction MovementSystem:PreUpdate(Time)\n   return true\nend\n\nfunction MovementSystem:Update(Time, dirty)\n   self:ForEach(function(entity)\n      \n   end)\nend\n\nfunction MovementSystem:PostUpdate(Time, interpolation)\n   \nend\n\nfunction MovementSystem:OnEnter(time)\n   local world = self.world\n   local query = Query.All(IsActive)\n   \n   world:Exec(query):ForEach(function(entity)\n      local transform = Transform()\n      transform.Position = Vector3.new(50, 50, 50)\n\n      entity:Set(transform)\n      entity:Set(Transform, transform)\n      entity[Transform] = transform\n\n      local isActive = entity:Get(IsActive)\n      local isActive = entity[IsActive]\n   end)\nend\n\n\nlocal world = ECS.World({MovementSystem}, 60, false)\nworld:AddSystem(MovementSystem, 10, { types = 'xto' })\nworld:SetFrequency(30)\n\nworld:Destroy()\n\n\n-- ShouldUpdate(time: number, interpolation:number): void\n--    It allows informing if the update methods of this system should be invoked\n-- BeforeUpdate(time: number, interpolation:number): void\n--    Invoked before updating entities available for this system.\n--    It is only invoked when there are entities with the characteristics\n--    expected by this system\n-- Update: function(time, dirty, entity, index, [component_N_items...]) -> boolean\n--    Invoked in updates, limited to the value set in the \"frequency\" attribute\n-- AfterUpdate(time: number, interpolation:number): void\n-- OnEnter(time, entity, index, [component_N_items...]) -> boolean\n-- OnExit(time, entity, index, [component_N_items...]) -> boolean\n-- OnRemove(time, enity, index, [component_N_items...])\n\n\n\n\n\n\n\nlocal Archetype1 = {}\nlocal ComponentName = {}\nlocal ComponentTransform = {}\n\nlocal entity1 = {}\nlocal entity2 = {}\n\n\n\nlocal Entity = {}\n\nfunction Entity.Create(world, ...)\n   local components = {...}\nend\n\nfunction Entity:Get(cType)\n   return self._data[cType]\nend\n\nfunction Entity:Set(cType, value)\n   return self._data[cType]\nend\n\n\nentity1[ComponentName]\n"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/component/FiringComponent.lua",
    "content": "local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Firing', function(firedAt)\n   if firedAt == nil then\n      error(\"firedAt is required\")\n   end\n\n   return firedAt\nend)\n"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/component/WeaponComponent.lua",
    "content": "local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Weapon')\n"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/system/CleanupFiringSystem.lua",
    "content": "\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- Components\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal FiringComponent = require(Components:WaitForChild(\"FiringComponent\"))\n\nreturn ECS.RegisterSystem({\n   Name = 'CleanupFiring',\n   Step = 'transform',\n   RequireAll = {\n      FiringComponent\n   },\n   Update = function (time, world, dirty, entity, index, firings)\n\n      local firedAt = firings[index]\n      if firedAt ~= nil then\n         if time.frame - firedAt < 0.5 then\n            return false\n         end\n\n         world.Remove(entity, FiringComponent)\n\n         return true\n      end\n\n      return false\n   end\n})"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/system/FiringSystem.lua",
    "content": "\nlocal ECS      = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\nlocal ECSUtil  = require(game.ReplicatedStorage:WaitForChild(\"ECSUtil\"))\n\n-- Components\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal FiringComponent = require(Components:WaitForChild(\"FiringComponent\"))\n\nreturn ECS.RegisterSystem({\n   Name = 'Firing',\n   Step = 'process0',\n   RequireAll = {\n      ECSUtil.PositionComponent,\n      ECSUtil.RotationComponent,\n      FiringComponent\n   },\n   OnEnter = function(time, world, entity, index,  positions, rotations, firings)\n\n      local position = positions[index]\n      local rotation = rotations[index]\n      \n      if position ~= nil and rotation ~= nil then\n\n         -- can be made in a utility script, or clone a preexistece model\n         local bulletPart = Instance.new(\"Part\")\n         bulletPart.Anchored     = true\n         bulletPart.CanCollide   = false\n         bulletPart.Position     = position\n         bulletPart.CastShadow   = false\n         bulletPart.Shape        = Enum.PartType.Ball\n         bulletPart.Size         = Vector3.new(0.6, 0.6, 0.6)\n         bulletPart.CFrame       = CFrame.fromMatrix(position, rotation[1], rotation[2], rotation[3] * -1)\n         bulletPart.Parent       = game.Workspace\n\n         local bulletEntity = ECSUtil.NewBasePartEntity(world, bulletPart, false, true, true)\n         world.Set(bulletEntity, ECSUtil.MoveForwardComponent)\n         world.Set(bulletEntity, ECSUtil.MoveSpeedComponent, 1.0)\n      end\n\n      return false\n   end\n})\n"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/system/PlayerShootingSystem.lua",
    "content": "\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- Components\nlocal Components = game.ReplicatedStorage:WaitForChild(\"tutorial\"):WaitForChild(\"component\")\nlocal FiringComponent = require(Components:WaitForChild(\"FiringComponent\"))\nlocal WeaponComponent = require(Components:WaitForChild(\"WeaponComponent\"))\n\nreturn ECS.RegisterSystem({\n   Name = 'PlayerShooting',\n   Step = 'process',\n   Order = 1,\n   RequireAll = {\n      WeaponComponent\n   },\n   RejectAny = {\n      FiringComponent\n   },\n   Update = function (time, world, dirty, entity, index, weapons)\n\n      local isFiring = UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1)\n\n      if isFiring  then\n         world.Set(entity, FiringComponent, time.frame)\n         return true\n      end\n\n      return false\n   end\n})\n"
  },
  {
    "path": "src/Archetype.lua",
    "content": "\nlocal archetypes = {}\n\nlocal CACHE_WITH = {}\nlocal CACHE_WITHOUT = {}\n\n-- Version of the last registered archetype. Used to cache the systems execution plan\nlocal Version = 0\n\n--[[\n   An Archetype is a unique combination of component types. The EntityRepository uses the archetype to group all \n   entities that have the same sets of components.\n\n   An entity can change archetype fluidly over its lifespan. For example, when you add or remove components, \n   the archetype of the affected entity changes.\n\n   An archetype object is not a container; rather it is an identifier to each unique combination of component \n   types that an application has created at run time, either directly or implicitly.\n\n   You can create archetypes directly using ECS.Archetype.Of(Components[]). You also implicitly create archetypes \n   whenever you add or remove a component from an entity. An Archetype object is an immutable singleton; \n   creating an archetype with the same set of components, either directly or implicitly, results in the same \n   archetype.\n\n   The ECS framework uses archetypes to group entities that have the same structure together. The ECS framework stores \n   component data in blocks of memory called chunks. A given chunk stores only entities having the same archetype. \n   You can get the Archetype object for a chunk from its Archetype property.\n\n   Use ECS.Archetype.Of(Components[]) to get a Archetype reference.\n]]\nlocal Archetype  = {}\nArchetype.__index = Archetype\n\n--[[\n   Gets the reference to an archetype from the informed components\n\n   @param componentClasses {ComponentClass[]} Component that define this archetype\n   @return Archetype\n]]\nfunction Archetype.Of(componentClasses)\n\n   local ids = {}\n   local cTypes = {}\n   for _, cType in ipairs(componentClasses) do\n      if (cType.IsCType and not cType.isComponent) then\n         if cType.IsQualifier then\n            if cTypes[cType] == nil then    \n               cTypes[cType] = true\n               table.insert(ids, cType.Id)\n            end\n            cType = cType.SuperClass\n         end\n         if cTypes[cType] == nil then    \n            cTypes[cType] = true\n            table.insert(ids, cType.Id)\n         end\n      end\n   end\n\n   table.sort(ids)\n   local id = \"_\" .. table.concat(ids, \"_\")\n\n   if archetypes[id] == nil then\n      archetypes[id] = setmetatable({\n         id = id,\n         _components = cTypes\n      }, Archetype)\n      Version = Version + 1\n   end\n\n   return archetypes[id]\nend\n\n--[[\n   Get the version of archetype definitions\n\n   @return number\n]]\nfunction Archetype.Version()\n   return Version\nend\n\n--[[\n   Checks whether this archetype has the informed component\n\n   @param componentClass {ComponentClass}\n   @return bool\n]]\nfunction Archetype:Has(componentClass)\n   return (self._components[componentClass] == true)\nend\n\n--[[\n   Gets the reference to an archetype that has the current components + the informed component\n\n   @param componentClass {ComponentClass}\n   @return Archetype\n]]\nfunction Archetype:With(componentClass)\n   if self._components[componentClass] == true then\n      -- component exists in that list, returns the archetype itself\n      return self\n   end\n\n   local cache = CACHE_WITH[self]\n   if not cache then\n      cache =  {}\n      CACHE_WITH[self] = cache\n   end\n\n   local other = cache[componentClass]\n   if other == nil then\n      local componentTs = {componentClass}\n      for component,_ in pairs(self._components) do\n         table.insert(componentTs, component)\n      end\n      other = Archetype.Of(componentTs)\n      cache[componentClass] = other\n   end\n   return other\nend\n\n--[[\n   Gets the reference to an archetype that has the current components + the informed components\n\n   @param componentClasses {ComponentClass[]}\n   @return Archetype\n]]\nfunction Archetype:WithAll(componentClasses)\n\n   local cTypes = {}\n   for component,_ in pairs(self._components) do\n      table.insert(cTypes, component)\n   end\n   \n   for _,component in ipairs(componentClasses) do\n      if self._components[component] == nil then\n         table.insert(cTypes, component)\n      end\n   end\n\n   return Archetype.Of(cTypes)\nend\n\n--[[\n   Gets the reference to an archetype that has the current components - the informed component\n   \n   @param componentClass {ComponentClass}\n   @return Archetype\n]]\nfunction Archetype:Without(componentClass)\n\n   if self._components[componentClass] == nil then\n      -- component does not exist in this list, returns the archetype itself\n      return self\n   end\n\n   local cache = CACHE_WITHOUT[self]\n   if not cache then\n      cache =  {}\n      CACHE_WITHOUT[self] = cache\n   end\n\n   local other = cache[componentClass]\n   if other == nil then      \n      local componentTs = {}\n      for component,_ in pairs(self._components) do\n         if component ~= componentClass then\n            table.insert(componentTs, component)\n         end\n      end\n      other =  Archetype.Of(componentTs)\n      cache[componentClass] = other\n   end\n      \n   return other\nend\n\n--[[\n   Gets the reference to an archetype that has the current components - the informed components\n\n   @param componentClasses {ComponentClass[]}\n   @return Archetype\n]]\nfunction Archetype:WithoutAll(componentClasses)\n\n   local toIgnoreIdx = {}\n   for _,component in ipairs(componentClasses) do\n      toIgnoreIdx[component] = true\n   end\n   \n   local cTypes = {}\n   for component,_ in pairs(self._components) do\n      if toIgnoreIdx[component] == nil then\n         table.insert(cTypes, component)\n      end\n   end\n\n   return Archetype.Of(cTypes)\nend\n\n-- Generic archetype, for entities that do not have components\nArchetype.EMPTY = Archetype.Of({})\n\nreturn Archetype\n"
  },
  {
    "path": "src/Component.lua",
    "content": "local Utility = require(\"Utility\")\nlocal ComponentFSM = require(\"ComponentFSM\")\n\nlocal copyDeep = Utility.copyDeep\nlocal mergeDeep = Utility.mergeDeep\n\nlocal CLASS_SEQ = 0\n\n--[[\n   @param initializer {function(table) => table}\n   @param superClass {ComponentClass}\n   @return ComponentClass\n]]\nlocal function createComponentClass(initializer, superClass)\n   CLASS_SEQ = CLASS_SEQ + 1\n\n   local ComponentClass = {\n      Id = CLASS_SEQ,\n      IsCType = true,\n      -- Primary component\n      SuperClass = superClass\n   }\n   ComponentClass.__index = ComponentClass\n\n   if superClass == nil then\n      superClass = ComponentClass\n      superClass._Qualifiers = { [\"Primary\"] = ComponentClass }\n      superClass._QualifiersArr = { ComponentClass }\n      superClass._Initializers = {}\n   else\n      superClass.HasQualifier = true\n      ComponentClass.IsQualifier = true\n      ComponentClass.HasQualifier = true\n   end\n\n   local Qualifiers = superClass._Qualifiers\n   local QualifiersArr = superClass._QualifiersArr\n\n   setmetatable(ComponentClass, {\n      __call = function(t, value)\n         return ComponentClass.New(value)\n      end,\n      __index = function(t, key)\n         if (key == \"States\") then\n            return superClass.__States       \n         end\n         if (key == \"Case\" or key == \"StateInitial\") then\n            return rawget(superClass, key)       \n         end\n      end,\n      __newindex = function(t, key, value)\n         if (key == \"Case\" or key == \"States\" or key == \"StateInitial\") then\n            -- (FMS) Finite State Machine\n            if ComponentClass == superClass then\n               if (key == \"States\") then\n                  if not superClass.IsFSM then\n                     ComponentFSM.AddCapability(superClass, value)\n                     for _, qualifiedClass in pairs(Qualifiers) do\n                        if qualifiedClass ~= superClass then\n                           ComponentFSM.AddMethods(superClass, qualifiedClass)               \n                        end\n                     end\n                  end\n               else\n                  rawset(t, key, value)\n               end\n            end\n         else\n            rawset(t, key, value)\n         end\n      end\n   })\n\n   if superClass.IsFSM then\n      ComponentFSM.AddMethods(superClass, ComponentClass)               \n   end\n\n   --[[\n      Gets a qualifier for this type of component. If the qualifier does not exist, a new class will be created, \n      otherwise it brings the already registered class qualifier reference with the same name.\n\n      @param qualifier {string|ComponentClass}\n      @return ComponentClass\n   ]]\n   function ComponentClass.Qualifier(qualifier)\n      if type(qualifier) ~= \"string\" then\n         for _, qualifiedClass in ipairs(QualifiersArr) do\n            if qualifiedClass == qualifier then\n               return qualifier\n            end\n         end\n         return nil\n      end\n\n      local qualifiedClass = Qualifiers[qualifier]\n      if qualifiedClass == nil then\n         qualifiedClass = createComponentClass(initializer, superClass)\n         Qualifiers[qualifier] = qualifiedClass\n         table.insert(QualifiersArr, qualifiedClass)\n      end\n      return qualifiedClass\n   end\n\n   --[[\n      Get all qualified class\n\n      @param ... {string|ComponentClass} (Optional) Allows to filter the specific qualifiers\n      @return ComponentClass[]\n   ]]\n   function ComponentClass.Qualifiers(...)\n      local filter = {...}\n      if #filter == 0 then\n         return QualifiersArr\n      else\n         local qualifiers = {}\n         local cTypes = {}\n         for _,qualifier in ipairs({...}) do\n            local qualifiedClass = ComponentClass.Qualifier(qualifier)\n            if qualifiedClass and cTypes[qualifiedClass] == nil then\n               cTypes[qualifiedClass] = true\n               table.insert(qualifiers, qualifiedClass)\n            end\n         end\n         return qualifiers      \n      end\n   end\n\n   --[[\n      Constructor\n\n      @param value {any} If the value is not a table, it will be converted to the format \"{ value = value}\"\n      @return Component\n   ]]\n   function ComponentClass.New(value)\n      if (value ~= nil and type(value) ~= \"table\") then\n         -- local MyComponent = Component({ value = Vector3.new(0, 0, 0) })\n         -- local component = MyComponent(Vector3.new(10, 10, 10))\n         value = { value = value }\n      end\n      local component = setmetatable(initializer(value) or {}, ComponentClass)\n      for _, fn in ipairs(superClass._Initializers) do\n         fn(component)\n      end\n      component.isComponent = true\n      component._qualifiers = { [ComponentClass] = component }\n      return component\n   end\n\n   --[[\n      Get this component's class\n\n      @return ComponentClass\n   ]]\n   function ComponentClass:GetType()\n      return ComponentClass\n   end\n\n   --[[\n      Check if this component is of the type informed\n\n      @param componentClass {ComponentClass}\n      @return bool\n   ]]\n   function ComponentClass:Is(componentClass)\n      return componentClass == ComponentClass or componentClass == superClass\n   end\n\n   --[[\n      Get the instance for the primary qualifier of this class\n\n      @return Component|nil\n   ]]\n   function ComponentClass:Primary()\n      return self._qualifiers[superClass]\n   end\n\n   --[[\n      Get the instance for the given qualifier of this class\n\n      @param name {string|ComponentClass}\n      @return Component|nil\n   ]]\n   function ComponentClass:Qualified(qualifier)\n      return self._qualifiers[ComponentClass.Qualifier(qualifier)]\n   end\n\n   --[[\n      Get all instances for all qualifiers of that class\n\n      @return Component[]\n   ]]\n   function ComponentClass:QualifiedAll()\n      local qualifiedAll = {}\n      for name, qualifiedClass in pairs(Qualifiers) do\n         qualifiedAll[name] = self._qualifiers[qualifiedClass]\n      end\n      return qualifiedAll\n   end\n\n   --[[\n      Merges data from the other component into the current component. This method should not be invoked, it is used\n      by the entity to ensure correct retrieval of a component's qualifiers.\n\n      @param other {Component}\n   ]]\n   function ComponentClass:Merge(other)\n      if superClass.HasQualifier then\n         if self == other then\n            return\n         end\n   \n         if self._qualifiers == other._qualifiers then\n            return\n         end\n   \n         if not other:Is(superClass) then\n            return\n         end\n   \n         local selfClass = ComponentClass\n         local otherClass = other:GetType()\n   \n         -- does anyone know the reference to the primary entity?\n         local primaryQualifiers\n         if selfClass == superClass then\n            primaryQualifiers = self._qualifiers\n         elseif otherClass == superClass then\n            primaryQualifiers = other._qualifiers\n         elseif self._qualifiers[superClass] ~= nil then\n            primaryQualifiers = self._qualifiers[superClass]._qualifiers\n         elseif other._qualifiers[superClass] ~= nil then\n            primaryQualifiers = other._qualifiers[superClass]._qualifiers\n         end\n   \n         if primaryQualifiers ~= nil then\n            if self._qualifiers ~= primaryQualifiers then\n               for qualifiedClass, component in pairs(self._qualifiers) do\n                  if superClass ~= qualifiedClass then\n                     primaryQualifiers[qualifiedClass] = component\n                     component._qualifiers = primaryQualifiers\n                  end\n               end\n            end\n   \n            if other._qualifiers ~= primaryQualifiers then\n               for qualifiedClass, component in pairs(other._qualifiers) do\n                  if superClass ~= qualifiedClass then\n                     primaryQualifiers[qualifiedClass] = component\n                     component._qualifiers = primaryQualifiers\n                  end\n               end\n            end\n         else\n            -- none of the instances know the Primary, use the current object reference\n            for qualifiedClass, component in pairs(other._qualifiers) do\n               if selfClass ~= qualifiedClass then\n                  self._qualifiers[qualifiedClass] = component\n                  component._qualifiers = self._qualifiers\n               end\n            end\n         end\n      end\n   end\n\n   --[[\n      Unlink this component with the other qualifiers\n   ]]\n   function ComponentClass:Detach()\n      if not superClass.HasQualifier then\n         return\n      end\n\n      -- remove old unlink\n      self._qualifiers[ComponentClass] = nil\n\n      -- new link\n      self._qualifiers = { [ComponentClass] = self }\n   end\n\n   return ComponentClass\nend\n\nlocal function defaultInitializer(value)\n   return value or {}\nend\n\n--[[\n   A Component is an object that can store data but should have not behaviour (As that should be handled by systems). \n]]\nlocal Component = {}\n\n--[[\n   Register a new ComponentClass\n\n   @param template {table|function(table?) -> table} \n      When `table`, this template will be used for creating component instances\n      When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the \n         component is passed to template function\n      If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format \n         `{ value = template }`.\n   @return ComponentClass  \n]]\nfunction Component.Create(template)\n\n   local initializer = defaultInitializer\n\n   if template ~= nil then\n      local ttype = type(template)\n      if (ttype == \"function\") then\n         initializer = template\n      else\n         if (ttype ~= \"table\") then\n            template = { value = template }\n         end\n\n         initializer = function(value)\n            local data = copyDeep(template)\n            if (value ~= nil) then\n               mergeDeep(data, value)\n            end\n            return data\n         end\n      end\n   end\n   \n   return createComponentClass(initializer, nil)\nend\n\nreturn Component\n"
  },
  {
    "path": "src/ComponentFSM.lua",
    "content": "--[[\n   Facilitate the construction and use of a Finite State Machine (FSM) using ECS\n\n   Example:\n      local Movement = ECS.Component({ Speed = 0 })\n\n      Movement.States = {\n         Standing = \"*\",\n         Walking  = {\"Standing\", \"Running\"},\n         Running  = {\"Walking\"}\n      }\n\n      Movement.StateInitial = \"Standing\"\n\n      Movement.Case = {\n         Standing = function(self, previous)\n            self.Speed = 0\n         end,\n         Walking = function(self, previous)\n            self.Speed = 5\n         end,\n         Running = function(self, previous)\n            self.Speed = 10\n         end\n      }\n      \n      local movement = entity[Movement]\n      print(movement:GetState()) -- \"Standing\"\n      movement:SetState(\"Walking\")\n      print(movement:GetPrevState()) -- \"Standing\"\n      movement:GetStateTime()\n\n      if (movement:GetState() == \"Standing\") then\n         movement.Speed = 0\n      end\n]]\n\n\nlocal Query = require(\"Query\")\n\n--[[\n   Filter used in Query and QueryResult\n\n   @see QueryResult.lua\n\n   Ex. ECS.Query.All(Movement.In(\"Standing\", \"Walking\"))\n]]\nlocal queryFilterCTypeStateIn = Query.Filter(function(entity, config)\n   local states = config.States\n   local isSuperClass = config.IsSuperClass\n   local componentClass = config.ComponentClass\n\n   if isSuperClass then\n      local qualifiers = componentClass.Qualifiers()\n      for _, qualifier in ipairs(qualifiers) do\n         local component = entity[qualifier]\n         if (component ~= nil and states[component:GetState()] == true) then\n            return true\n         end\n      end\n      return false\n   else\n      local component = entity[componentClass]\n      if component == nil then\n         return false\n      end\n      return states[component:GetState()] == true\n   end\nend)\n\nlocal ComponentFSM = {}\n\n--[[\n   Adds FSM capability to a ComponentClass\n\n   @param superClass {ComonentClass}\n   @param states { {[key=string] => string|string[]}}\n\n   @see Component.lua - createComponentClass() - ComponentClass__newindex\n]]\nfunction ComponentFSM.AddCapability(superClass, states)\n   \n   superClass.IsFSM = true\n\n   local cTypeStates = setmetatable({}, {\n      __newindex = function(states, newState, value)   \n\n         if (type(value) ~= \"table\") then\n            value = {value}\n         end\n\n         if table.find(value, \"*\") then\n            rawset(states, newState, \"*\")\n         else\n            local idxSelf = table.find(value, newState)\n            if idxSelf ~= nil then\n               table.remove(value, idxSelf)\n               if #value == 0 then\n                  value = \"*\"\n               end\n            end\n            rawset(states, newState, value)\n         end\n      end\n   })\n   rawset(superClass, \"__States\", cTypeStates)\n\n   for state,value in pairs(states) do\n      if superClass.StateInitial == nil then\n         superClass.StateInitial = state\n      end\n      cTypeStates[state] = value\n   end\n\n   ComponentFSM.AddMethods(superClass, superClass)\n\n   table.insert(superClass._Initializers, function(component)\n      component:SetState(superClass.StateInitial)\n   end)\nend\n\n\n\n--[[\n   Adds FSM state change methods to a ComponentClass\n\n   @param superClass {ComponentClass}\n   @param componentClass {ComponentClass}\n]]\nfunction ComponentFSM.AddMethods(superClass, componentClass)\n\n   componentClass.IsFSM = true\n   local cTypeStates = superClass.States\n\n   --[[\n      Creates a clause used to filter repository entities in a Query or QueryResult\n\n      @param ... {string[]} \n      @return Clause\n\n      Ex. ECS.Query.All(Movement.In(\"Walking\", \"Running\"))\n   ]]\n   function componentClass.In(...)\n      \n      local states = {}\n      local count = 0\n      for _,state in ipairs({...}) do\n         if (cTypeStates[state] ~= nil and states[state] == nil) then\n            count = count + 1\n            states[state] = true\n         end\n      end\n\n      if count == 0 then\n         -- In any state\n         return {}\n      end\n\n      return queryFilterCTypeStateIn({\n         States = states,\n         IsSuperClass = (componentClass == superClass),\n         ComponentClass = componentClass, \n      })\n   end   \n\n   --[[\n      Defines the current state of the FSM\n\n      @param newState {string}\n   ]]\n   function componentClass:SetState(newState)      \n      if (newState == nil or cTypeStates[newState] == nil) then\n         return\n      end\n\n      local actual = self:GetState()\n      if (actual == newState) then\n         return\n      end\n\n      if (actual ~= nil ) then\n         local transtions = cTypeStates[actual]\n         if (transtions ~= \"*\" and table.find(transtions, newState) == nil) then\n            -- not allowed\n            return\n         end\n      end\n\n      self._state = newState\n      self._statePrev = actual\n      self._stateTime = os.clock()\n\n      local action = superClass.Case and superClass.Case[newState]\n      if action then\n         action(self, actual)\n      end\n   end\n\n   --[[\n      Get the current state of the FSM\n\n      @return string\n   ]]\n   function componentClass:GetState()\n      return self._state or superClass.StateInitial\n   end\n\n   --[[\n      Get the previous state of the FSM\n\n      @return string|nil\n   ]]\n   function componentClass:GetPrevState()\n      return self._statePrev or nil\n   end\n\n   --[[\n      Gets the time it changed to the current state\n   ]]\n   function componentClass:GetStateTime()\n      return self._stateTime or 0\n   end\nend\n\nreturn ComponentFSM\n"
  },
  {
    "path": "src/ECS.lua",
    "content": "--[[\n   ECS Lua v2.2.0\n\n   ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n   https://github.com/nidorx/ecs-lua\n\n   Discussions about this script are at https://devforum.roblox.com/t/841175\n\n   ------------------------------------------------------------------------------\n\n   MIT License\n\n   Copyright (c) 2020 Alex Rodin\n\n   Permission is hereby granted, free of charge, to any person obtaining a copy\n   of this software and associated documentation files (the \"Software\"), to deal\n   in the Software without restriction, including without limitation the rights\n   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n   copies of the Software, and to permit persons to whom the Software is\n   furnished to do so, subject to the following conditions:\n\n   The above copyright notice and this permission notice shall be included in all\n   copies or substantial portions of the Software.\n\n   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n   SOFTWARE.\n]]\n\nlocal Query = require(\"Query\")\nlocal World = require(\"World\")\nlocal System = require(\"System\")\nlocal Archetype = require(\"Archetype\")\nlocal Component = require(\"Component\")\n\nlocal function setLoopManager(manager)\n   World.LoopManager = manager\nend\n\npcall(function()\n   if (game and game.ClassName == \"DataModel\") then\n      -- is roblox\n      setLoopManager(require(\"RobloxLoopManager\")())\n   end\nend)\n\n--[[\n  @TODO\n   - Server entities\n   - Client - Server sincronization (snapshot, delta, spatial index, grid manhatham distance)\n   - Table pool (avoid GC)\n   - System readonly? Paralel execution\n   - Debugging?\n   - Benchmark (Local Script vs ECS implementation)\n   - Basic physics (managed)\n   - SharedComponent?\n   - Serializaton\n      - world:Serialize()\n      - world:Serialize(entity)\n      - entity:Serialize()\n      - component:Serialize()\n]]\nlocal ECS = {\n   Query = Query,\n   World = World.New,\n   System = System.Create,\n   Archetype = Archetype,\n   Component = Component.Create,\n   SetLoopManager = setLoopManager\n}\n\nif _G.ECS == nil then\n   _G.ECS = ECS\nelse\n   local warn = _G.warn or print\n   warn(\"ECS Lua was not registered in the global variables, there is already another object registered.\")\nend\n\nreturn ECS\n"
  },
  {
    "path": "src/Entity.lua",
    "content": "--[[\n   The entity is a fundamental part of the Entity Component System. Everything in your game that has data or an \n   identity of its own is an entity. However, an entity does not contain either data or behavior itself. Instead, \n   the data is stored in the components and the behavior is provided by the systems that process those components. \n]]\n\nlocal Archetype = require(\"Archetype\")\n\nlocal SEQ  = 0\n\n--[[\n   [GET]\n   01) comp1 = entity[CompType1]\n   02) comp1 = entity:Get(CompType1)\n   03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n]]\nlocal function getComponent(entity, ...)\n\n   local values = {...}\n   local data = entity._data\n   \n   if (#values == 1) then\n      local cType = values[1]\n      if (cType.IsCType and not cType.isComponent) then\n         -- 01) comp1 = entity[CompType1]\n         -- 02) comp1 = entity:Get(CompType1)\n         return data[cType]\n      else\n         return nil\n      end\n   end\n   \n   -- 03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n   local components = {}   \n   for i,cType in ipairs(values) do\n      if (cType.IsCType and not cType.isComponent) then         \n         table.insert(components, data[cType])\n      end\n   end\n   return table.unpack(components)\nend\n\n--[[\n   Merges the qualifiers of a new added component.\n\n   @param entity {Entity}\n   @param newComponent {Component}\n   @param newComponentClass {ComponentClass}\n]]\nlocal function mergeComponents(entity, newComponent, newComponentClass)\n   local data = entity._data\n   local otherComponent\n   -- get first instance\n   for _,oCType in ipairs(newComponentClass.Qualifiers()) do\n      if oCType ~= newComponentClass then\n         otherComponent = data[oCType]\n         if otherComponent then\n            break\n         end\n      end\n   end\n\n   if otherComponent then\n      otherComponent:Merge(newComponent)\n   end\nend\n\n--[[\n   [SET]\n   01) entity[CompType1] = nil\n   02) entity[CompType1] = value\n   03) entity:Set(CompType1, nil)   \n   04) entity:Set(CompType1, value)\n   05) entity:Set(comp1)\n   06) entity:Set(comp1, comp2, ...)\n]]\nlocal function setComponent(entity, ...)\n\n   local values = {...}\n   local data = entity._data\n   local archetypeOld = entity.archetype\n   local archetypeNew = archetypeOld\n\n   local toMerge = {}\n\n   local cType = values[1]   \n   if (cType and cType.IsCType and not cType.isComponent) then\n      local value = values[2]\n      local component\n      -- 01) entity[CompType1] = nil\n      -- 02) entity[CompType1] = value\n      -- 03) entity:Set(CompType1, nil)   \n      -- 04) entity:Set(CompType1, value)\n      if value == nil then\n         local old = data[cType]\n         if old then\n            old:Detach()\n         end\n\n         data[cType] = nil\n         archetypeNew = archetypeNew:Without(cType)\n\n      elseif (type(value) == \"table\" and value.isComponent) then\n         local old = data[cType]         \n         if (old ~= value) then\n            if old then\n               old:Detach()\n            end\n\n            cType = value:GetType()\n            data[cType] = value\n            archetypeNew = archetypeNew:With(cType)\n\n            -- merge components\n            if (cType.HasQualifier or cType.IsQualifier) then\n               mergeComponents(entity, value, cType)\n            end\n         end\n      else\n         local old = data[cType]\n         if old then\n            old:Detach()\n         end\n\n         local component = cType(value)\n         data[cType] = component\n         archetypeNew = archetypeNew:With(cType)\n\n         -- merge components\n         if (cType.HasQualifier or cType.IsQualifier) then\n            mergeComponents(entity, component, cType)\n         end\n      end\n   else\n      -- 05) entity:Set(comp1)\n      -- 06) entity:Set(comp1, comp2, ...)\n      for i,component in ipairs(values) do\n         if (component.isComponent) then\n            local cType = component:GetType()       \n            local old = data[cType]\n            if (old ~= component) then\n               if old then\n                  old:Detach()\n               end\n\n               data[cType] = component\n               archetypeNew = archetypeNew:With(cType)\n               \n               -- merge components\n               if (cType.HasQualifier or cType.IsQualifier) then\n                  mergeComponents(entity, component, cType)\n               end\n            end              \n         end\n      end\n   end\n\n   if (archetypeOld ~= archetypeNew) then\n      entity.archetype = archetypeNew\n      entity._onChange:Fire(entity, archetypeOld)\n   end\nend\n\n--[[\n   [UNSET]\n   01) enity:Unset(comp1)\n   02) entity[CompType1] = nil\n   03) enity:Unset(CompType1)\n   04) enity:Unset(comp1, comp1, ...)\n   05) enity:Unset(CompType1, CompType2, ...)\n]]\nlocal function unsetComponent(entity, ...)\n\n   local data = entity._data\n   local archetypeOld = entity.archetype\n   local archetypeNew = archetypeOld\n\n   for _,value in ipairs({...}) do\n      if value.isComponent then\n         -- 01) enity:Unset(comp1)\n         -- 04) enity:Unset(comp1, comp1, ...)\n         local cType = value:GetType()  \n         local old = data[cType]\n         if old then\n            old:Detach()\n         end\n         data[cType] = nil\n         archetypeNew = archetypeNew:Without(cType)\n         \n      elseif value.IsCType then\n         -- 02) entity[CompType1] = nil\n         -- 03) enity:Unset(CompType1)\n         -- 05) enity:Unset(CompType1, CompType2, ...)\n         local old = data[value]\n         if old then\n            old:Detach()\n         end\n         data[value] = nil\n         archetypeNew = archetypeNew:Without(value)\n      end\n   end\n\n   if entity.archetype ~= archetypeNew then\n      entity.archetype = archetypeNew\n      entity._onChange:Fire(entity, archetypeOld)\n   end\nend\n\n--[[\n   01) comps = entity:GetAll()\n   01) qualifiers = entity:GetAll(PrimaryClass)\n]]\nlocal function getAll(entity, qualifier)\n   local data = entity._data\n   local components = {}\n   if (qualifier ~= nil and qualifier.IsCType and not qualifier.isComponent) then\n      local ctypes = qualifier.Qualifiers()\n      for _,cType in ipairs(ctypes) do\n         local component = data[cType]\n         if component then\n            table.insert(components, component)\n         end\n      end\n   else\n      for _, component in pairs(data) do\n         table.insert(components, component)\n      end\n   end\n\n   return components\nend\n\n--[[\n   01) comp = entity:GetAny(PrimaryClass)\n]]\nlocal function getAny(entity, qualifier)\n   if (qualifier ~= nil and qualifier.IsCType and not qualifier.isComponent) then\n      local data = entity._data\n      local ctypes = qualifier.Qualifiers()\n      for _,cType in ipairs(ctypes) do\n         local component = data[cType]\n         if component then\n            return component\n         end\n      end\n   end\nend\n\nlocal Entity = {\n   __index = function(e, key)\n      if (type(key) == \"table\") then \n         -- 01) local comp1 = entity[CompType1]\n         -- 01) local comps = entity[{CompType1, CompType2, ...}]\n         return getComponent(e, key)\n      end\n   end,\n   __newindex = function(e, key, value)\n      local isComponentSet = true\n      if (type(key) == \"table\" and (key.IsCType and not key.isComponent)) then\n         -- 01) entity[CompType1] = nil\n         -- 02) entity[CompType1] = value\n         setComponent(e, key, value)\n      else\n         rawset(e, key, value)\n      end\n   end\n}\n\n--[[\n   Creates an entity having components of the specified types.\n\n   @param onChange {Event}\n   @param components {Component[]} (Optional)\n]]\nfunction Entity.New(onChange, components)\n\n   local archetype = Archetype.EMPTY\n   local data = {}\n   if (components ~= nil and #components > 0) then\n      local cTypes = {}\n      for _, component in ipairs(components) do\n         local cType = component:GetType()\n         table.insert(cTypes, cType)\n         data[cType] = component\n      end\n      archetype = Archetype.Of(cTypes)\n   end\n\n   SEQ = SEQ + 1\n\n   return setmetatable({\n      _data = data,\n      _onChange = onChange,\n      id = SEQ,\n      isAlive = false,\n      archetype = archetype,\n      Get = getComponent,\n      Set = setComponent,\n      Unset = unsetComponent,\n      GetAll = getAll,\n      GetAny = getAny,\n   }, Entity)\nend\n\nreturn Entity\n"
  },
  {
    "path": "src/EntityRepository.lua",
    "content": "local Event = require(\"Event\")\n\n--[[\n   The repository (database) of entities in a world.\n\n   The repository indexes entities by archetype. Whenever the entity's archetype is changed, the entity is \n   transported to the correct storage.\n]]\nlocal EntityRepository = {}\nEntityRepository.__index = EntityRepository\n\n--[[\n   Create a new repository\n\n   @return EntityRepository\n]]\nfunction EntityRepository.New()\n   return setmetatable({\n      _archetypes = {},\n      _entitiesArchetype = {},\n      \n   }, EntityRepository)\nend\n\n--[[\n   Insert an entity into this repository\n\n   @param entity {Entity}\n]]\nfunction EntityRepository:Insert(entity)\n   if (self._entitiesArchetype[entity] == nil) then\n      local archetype = entity.archetype\n      local storage = self._archetypes[archetype]\n      if (storage == nil) then\n         storage = { count = 0, entities = {} }\n         self._archetypes[archetype] = storage\n      end\n   \n      storage.entities[entity] = true\n      storage.count = storage.count + 1\n      \n      self._entitiesArchetype[entity] = archetype\n   else\n      self:Update(entity)\n   end\nend\n\n--[[\n   Remove an entity from this repository\n\n   @param entity {Entity}\n]]\nfunction EntityRepository:Remove(entity)\n   local archetypeOld = self._entitiesArchetype[entity]\n   if archetypeOld == nil then\n      return\n   end\n   self._entitiesArchetype[entity] = nil\n\n   local storage = self._archetypes[archetypeOld]\n   if (storage ~= nil and storage.entities[entity] == true) then\n      storage.entities[entity] = nil\n      storage.count = storage.count - 1\n      if (storage.count == 0) then\n         self._archetypes[archetypeOld] = nil\n      end\n   end\nend\n\n--[[\n   Updates the entity in the repository, if necessary, moves the entity from one storage to another\n\n   @param entity {Entity}\n]]\nfunction EntityRepository:Update(entity)\n   local archetypeOld = self._entitiesArchetype[entity]\n   if (archetypeOld == nil or archetypeOld == entity.archetype) then\n      return\n   end\n\n   self:Remove(entity)\n   self:Insert(entity)\nend\n\n--[[\n   Execute the query entered in this repository\n\n   @param query {Query}\n   @return QueryResult\n]]\nfunction EntityRepository:Query(query)\n   local chunks = {}\n   for archetype, storage in pairs(self._archetypes) do\n      if query:Match(archetype) then\n         table.insert(chunks, storage.entities)\n      end\n   end\n   return query:Result(chunks), #chunks > 0\nend\n\n--[[\n   Quick check to find out if a query is applicable.\n\n   @param query {Query}\n   @return bool\n]]\nfunction EntityRepository:FastCheck(query)\n   for archetype, storage in pairs(self._archetypes) do\n      if query:Match(archetype) then\n         return true\n      end\n   end\n   return false\nend\n\nreturn EntityRepository\n"
  },
  {
    "path": "src/Event.lua",
    "content": "\n--[[\n   Subscription\n]]\nlocal Connection = {}\nConnection.__index = Connection\n\nfunction Connection.New(event, handler)\n   return setmetatable({ _event = event, _handler = handler }, Connection)\nend\n\n-- Unsubscribe\nfunction Connection:Disconnect()\n   local event = self._event\n   if (event and not event.destroyed) then\n      local idx = table.find(event._handlers, self._handler)\n      if idx ~= nil then\n         table.remove(event._handlers, idx)\n      end\n   end\n   setmetatable(self, nil)\nend \n\n--[[\n   Observer Pattern\n\n   Allows the application to fire events of a particular type.\n]]\nlocal Event = {}\nEvent.__index = Event\n\nfunction Event.New()\n\treturn setmetatable({ _handlers = {} }, Event)\nend\n\nfunction Event:Connect(handler)\n\tif (type(handler) == \"function\") then\n      table.insert(self._handlers, handler)\n      return Connection.New(self, handler)\n\tend\n\n   error((\"Event:Connect(%s)\"):format(typeof(handler)), 2)\nend\n\nfunction Event:Fire(...)\n\tif not self.destroyed then\n      for i,handler in ipairs(self._handlers) do\n         handler(table.unpack({...}))\n      end\n\tend\nend\n\nfunction Event:Destroy()\n\tsetmetatable(self, nil)\n   self._handlers = nil\n   self.destroyed = true\nend\n\nreturn Event\n"
  },
  {
    "path": "src/Query.lua",
    "content": "\nlocal QueryResult = require(\"QueryResult\")\n\n--[[\n   Global cache result.\n\n   The validated components are always the same (reference in memory, except within the archetypes),\n   in this way, you can save the result of a query in an archetype, reducing the overall execution\n   time (since we don't need to iterate all the time)\n\n   @type KEY string = concat(Array<ComponentClass.Id>, \"_\")\n   @Type {\n      [Archetype] = {\n         Any  = { [KEY] = bool },\n         All  = { [KEY] = bool },\n         None = { [KEY] = bool },\n      }\n   }\n]]\nlocal CACHE = {}\n\n--[[\n   Interface for creating filters for existing entities in the ECS world\n]]\nlocal Query = {}\nQuery.__index = Query\nsetmetatable(Query, {\n   __call = function(t, all, any, none)\n      return Query.New(all, any, none)\n   end,\n})\n\nlocal function parseFilters(list, clauseGroup, clauses)\n   local indexed = {}\n   local cTypes = {}\n   local cTypeIds = {}\n   \n   for i,item in ipairs(list) do\n      if (indexed[item] == nil) then\n         if (item.IsCType and not item.isComponent) then\n            indexed[item] = true\n            table.insert(cTypes, item)\n            table.insert(cTypeIds, item.Id)\n         else\n            -- clauses\n            if item.Filter then\n               indexed[item] = true\n               item[clauseGroup] = true\n               table.insert(clauses, item)\n            end\n         end\n      end\n   end\n\n   if #cTypes > 0 then\n      table.sort(cTypeIds)\n      local cTypesKey = \"_\" .. table.concat(cTypeIds, \"_\")   \n      return cTypes, cTypesKey\n   end\nend\n\n--[[\n   Create a new Query used to filter entities in the world. It makes use of local and global cache in order to \n   decrease the validation time (avoids looping in runtime of systems)\n\n   @param all {Array<ComponentClass|Clause>} Optional All component types in this array must exist in the archetype\n   @param any {Array<ComponentClass|Clause>} Optional At least one of the component types in this array must exist in the archetype\n   @param none {Array<ComponentClass|Clause>} Optional None of the component types in this array can exist in the archetype\n]]\nfunction Query.New(all, any, none)\n\n   -- used by QueryResult\n   local clauses = {}\n\n   local anyKey, allKey, noneKey\n\n   if (any ~= nil) then\n      any, anyKey = parseFilters(any, \"IsAnyFilter\", clauses)\n   end\n\n   if (all ~= nil) then\n      all, allKey = parseFilters(all, \"IsAllFilter\", clauses)\n   end\n\n   if (none ~= nil) then\n      none, noneKey = parseFilters(none, \"IsNoneFilter\", clauses)\n   end\n\n   return setmetatable({\n      isQuery = true,\n      _any = any,\n      _all = all,\n      _none = none,\n      _anyKey = anyKey,\n      _allKey = allKey,\n      _noneKey = noneKey,\n      _cache = {}, -- local cache (L1)\n      _clauses = #clauses > 0 and clauses or nil\n   }, Query)\nend\n\n--[[\n   Generate a QueryResult with the chunks entered and the clauses of the current query\n\n   @param chunks {Chunk}\n   @return QueryResult\n]]\nfunction Query:Result(chunks)\n   return QueryResult.New(chunks, self._clauses)\nend\n\n--[[\n   Checks if the entered archetype is valid by the query definition\n\n   @param archetype {Archetype}\n   @return bool\n]]\nfunction Query:Match(archetype)\n\n   -- cache L1\n   local localCache = self._cache\n   \n   -- check local cache (L1)\n   local cacheResult = localCache[archetype]\n   if cacheResult ~= nil then\n      return cacheResult\n   else\n      -- check global cache (executed by other filter instance)\n      local globalCache = CACHE[archetype]\n      if (globalCache == nil) then\n         globalCache = { Any = {}, All = {}, None = {} }\n         CACHE[archetype] = globalCache\n      end\n      \n      -- check if these combinations exist in this component array\n\n      local noneKey = self._noneKey\n      if noneKey then\n         local isNoneValid = globalCache.None[noneKey]\n         if (isNoneValid == nil) then\n            isNoneValid = true\n            for _, cType in ipairs(self._none) do\n               if archetype:Has(cType) then\n                  isNoneValid = false\n                  break\n               end\n            end\n            globalCache.None[noneKey] = isNoneValid\n         end\n\n         if (isNoneValid == false) then\n            localCache[archetype] = false\n            return false\n         end     \n      end\n\n      local anyKey = self._anyKey\n      if anyKey then\n         local isAnyValid = globalCache.Any[anyKey]\n         if (isAnyValid == nil) then\n            isAnyValid = false\n            if (globalCache.All[anyKey] == true) then\n               isAnyValid = true\n            else\n               for _, cType in ipairs(self._any) do\n                  if archetype:Has(cType) then\n                     isAnyValid = true\n                     break\n                  end\n               end\n            end\n            globalCache.Any[anyKey] = isAnyValid\n         end\n\n         if (isAnyValid == false) then\n            localCache[archetype] = false\n            return false\n         end\n      end\n\n      local allKey = self._allKey\n      if allKey then\n         local isAllValid = globalCache.All[allKey]\n         if (isAllValid == nil) then\n            local haveAll = true\n            for _, cType in ipairs(self._all) do\n               if (not archetype:Has(cType)) then\n                  haveAll = false\n                  break\n               end\n            end\n\n            if haveAll then\n               isAllValid = true\n            else\n               isAllValid = false\n            end\n\n            globalCache.All[allKey] = isAllValid\n         end\n\n         localCache[archetype] = isAllValid\n         return isAllValid\n      end\n\n      -- empty query = SELECT * FROM\n      localCache[archetype] = true\n      return true\n   end\nend\n\nlocal function builder()\n   local builder = {\n      isQueryBuilder = true\n   }\n   local query\n\n   function builder.All(...)\n      query = nil\n      builder._all = {...}\n      return builder\n   end\n   \n   function builder.Any(...)\n      query = nil\n      builder._any = {...}\n      return builder\n   end\n   \n   function builder.None(...)\n      query = nil\n      builder._none = {...}\n      return builder\n   end\n\n   function builder.Build()\n      if query == nil then\n         query = Query.New(builder._all, builder._any, builder._none)\n      end\n      return query\n   end\n\n   return builder\nend\n\nfunction Query.All(...)\n   return builder().All(...)\nend\n\nfunction Query.Any(...)\n   return builder().Any(...)\nend\n\nfunction Query.None(...)\n   return builder().None(...)\nend\n\n--[[\n   Create custom filters that can be used in Queries. Its execution is delayed, invoked only in QueryResult methods\n\n   The result of executing the clause depends on how it was used in the query.\n\n   Ex. If used in Query.All() the result is the inverse of using the same clause in Query.None()\n\n      local Player = ECS.Component({ health = 100 })\n\n      local HealthPlayerFilter = ECS.Query.Filter(function(entity, config)\n         local player = entity[Player]\n         return player.health >= config.minHealth and player.health <= config.maxHealth\n      end)\n\n      local healthyClause = HealthPlayerFilter({\n         minHealth = 80,\n         maxHealth = 100,\n      })\n\n      local healthyQuery = ECS.Query.All(Player, healthyClause)\n      world:Exec(healthyQuery):ForEach(function(entity)\n         -- this player is very healthy\n      end)\n\n      local notHealthyQuery = ECS.Query.All(Player).None(healthyClause)\n      world:Exec(healthyQuery):ForEach(function(entity)\n         -- this player is NOT very healthy\n      end)\n\n      local dyingClause = HealthPlayerClause({\n         minHealth = 1,\n         maxHealth = 20,\n      })\n\n      local dyingQuery = ECS.Query.All(Player, dyingClause)\n      world:Exec(dyingQuery):ForEach(function(entity)\n         -- this player is about to die\n      end)\n\n      local notDyingQuery = ECS.Query.All(Player).None(dyingClause)\n      world:Exec(notDyingQuery):ForEach(function(entity)\n         -- this player is NOT about to die\n      end)\n\n   @param filter {function(entity, config):bool} \n   @return function(config):Clause\n]]\nfunction Query.Filter(filter)\n   return function (config)\n      return {\n         Filter = filter,\n         Config = config\n      }\n   end\nend\n\nreturn Query\n"
  },
  {
    "path": "src/QueryResult.lua",
    "content": "\n--[[\n   OperatorFunction = function(param, value, count) => newValue, acceptItem, mustContinue\n]]\n\nlocal function operatorFilter(predicate, value, count)\n   return value, (predicate(value) == true), true\nend\n\nlocal function operatorMap(mapper, value, count)\n   return mapper(value), true, true\nend\n\nlocal function operatorLimit(limit, value, count)\n   local accept = (count <= limit)\n   return value, accept, accept\nend\n\nlocal function operatorClauseNone(clauses, value, count)\n   local acceptItem = true\n   for _,clause in ipairs(clauses) do\n      if (clause.Filter(value, clause.Config) == true) then\n         acceptItem = false\n         break\n      end \n   end\n   return value, acceptItem, true\nend\n\nlocal function operatorClauseAll(clauses, value, count)\n   local acceptItem = true\n   for _,clause in ipairs(clauses) do\n      if (clause.Filter(value, clause.Config) == false) then\n         acceptItem = false\n         break\n      end\n   end\n   return value, acceptItem, true\nend\n\nlocal function operatorClauseAny(clauses, value, count)\n   local acceptItem = false\n   for _,clause in ipairs(clauses) do\n      if (clause.Filter(value, clause.Config) == true) then\n         acceptItem = true\n         break\n      end\n   end\n   return value, acceptItem, true\nend\n\nlocal EMPTY_OBJECT = {}\n\n--[[\n   The result of a Query that was executed on an EntityStorage.\n\n   QueryResult provides several methods to facilitate the filtering of entities resulting from the execution of the \n   query.\n]]\nlocal QueryResult = {}\nQueryResult.__index = QueryResult\n\n--[[\n   Build a new QueryResult\n\n   @param chunks { Array<{ [Entity] = true }> }\n   @clauses {Clause[]}\n\n   @see Query.lua\n   @see EntityRepository:Query(query)\n   @return QueryResult\n]]\nfunction QueryResult.New(chunks, clauses)\n\n   local pipeline = EMPTY_OBJECT\n   if (clauses and #clauses > 0) then\n      local all = {}\n      local any = {}\n      local none = {}\n\n      pipeline = {}\n      \n      for i,clause in ipairs(clauses) do\n         if clause.IsNoneFilter then\n            table.insert(none, clause)\n         elseif clause.IsAnyFilter then\n            table.insert(any, clause)\n         else\n            table.insert(all, clause)\n         end \n      end\n\n      if (#none > 0) then\n         table.insert(pipeline, {operatorClauseNone, none})\n      end\n      \n      if (#all > 0) then\n         table.insert(pipeline, {operatorClauseAll, all})\n      end\n\n      if (#any > 0) then\n         table.insert(pipeline, {operatorClauseAny, any})\n      end\n     \n   end\n\n   return setmetatable({\n      chunks = chunks,\n      _pipeline = pipeline,\n   }, QueryResult)\nend\n\n--[[ -------------------------------------------------------------------------------------------------------------------\n   Intermediate Operations\n\n   Intermediate operations return a new QueryResult. They are always lazy; executing an intermediate operation such as \n   QueryResult:Filter() does not actually perform any filtering, but instead creates a new QueryResult that, when traversed, \n   contains the elements of the initial QueryResult that match the given predicate. Traversal of the pipeline source \n   does not begin until the terminal operation of the pipeline is executed.\n]] ---------------------------------------------------------------------------------------------------------------------\n\n--[[\n   Returns a QueryResult consisting of the elements of this QueryResult with a new pipeline operation\n\n   @param operation {function(param, value, count) -> newValue, accept, continues}\n   @param param {any}\n   @return the new QueryResult\n]]\nfunction QueryResult:With(operation, param)\n   local pipeline = {}\n   for _,operator in ipairs(self._pipeline) do\n      table.insert(pipeline, operator)\n   end\n   table.insert(pipeline, { operation, param })\n\n   return setmetatable({\n      chunks = self.chunks,\n      _pipeline = pipeline,\n   }, QueryResult)\nend\n\n--[[\n   Returns a QueryResult consisting of the elements of this QueryResult that match the given predicate.\n\n   @param predicate {function(value) -> bool} a predicate to apply to each element to determine if it should be included\n   @return the new QueryResult\n]]\nfunction QueryResult:Filter(predicate)\n   return self:With(operatorFilter, predicate)\nend\n\n--[[\n   Returns a QueryResult consisting of the results of applying the given function to the elements of this QueryResult.\n\n   @param mapper {function(value) -> newValue} a function to apply to each element\n   @return the new QueryResult\n]]\nfunction QueryResult:Map(mapper)\n   return self:With(operatorMap, mapper)\nend\n\n--[[\n   Returns a QueryResult consisting of the elements of this QueryResult, truncated to be no longer than maxSize in length.\n   \n   This is a short-circuiting stateful intermediate operation.\n\n   @param maxSize {number}\n   @return the new QueryResult\n]]\nfunction QueryResult:Limit(maxSize)\n   return self:With(operatorLimit, maxSize)\nend\n\n--[[ -------------------------------------------------------------------------------------------------------------------\n   Terminal Operations\n\n   Terminal operations, such as QueryResult:ForEach or QueryResult.AllMatch, may traverse the QueryResult to produce a \n   result or a side-effect. After the terminal operation is performed, the pipeline is considered consumed, and can no \n   longer be used; if you need to traverse the same data source again, you must return to the data source to get a new \n   QueryResult.\n]] ---------------------------------------------------------------------------------------------------------------------\n\n--[[\n   Returns whether any elements of this result match the provided predicate.\n\n   @param predicate { function(value) -> bool} a predicate to apply to elements of this result\n   @returns true if any elements of the result match the provided predicate, otherwise false\n]]\nfunction QueryResult:AnyMatch(predicate)\n   local anyMatch = false\n   self:ForEach(function(value)\n      if predicate(value) then\n         anyMatch = true\n      end\n      -- break if true\n      return anyMatch\n   end)\n   return anyMatch\nend\n\n--[[\n   Returns whether all elements of this result match the provided predicate.\n\n   @param predicate { function(value) -> bool} a predicate to apply to elements of this result\n   @returns true if either all elements of the result match the provided predicate or the result is empty, otherwise false\n]]\nfunction QueryResult:AllMatch(predicate)\n   local allMatch = true\n   self:ForEach(function(value)\n      if (not predicate(value)) then\n         allMatch = false\n      end\n      -- break if false\n      return allMatch == false\n   end)\n   return allMatch\nend\n\n--[[\n   Returns some element of the result, or nil if the result is empty.\n\n   This is a short-circuiting terminal operation.\n\n   The behavior of this operation is explicitly nondeterministic; it is free to select any element in the result. \n   \n   Multiple invocations on the same result may not return the same value.\n\n   @return {any}\n]]\nfunction QueryResult:FindAny()\n   local out\n   self:ForEach(function(value)\n      out = value\n      -- break\n      return true\n   end)\n   return out\nend\n\n--[[\n   Returns an array containing the elements of this QueryResult.\n\n   This is a terminal operation.\n]]\nfunction QueryResult:ToArray()\n   local array = {}\n   self:ForEach(function(value)\n      table.insert(array, value)\n   end)\n   return array\nend\n\n--[[\n   Returns an Iterator, to use in for loop\n\n   for count, entity in result:Iterator() do\n      print(entity.id)\n   end\n]]\nfunction QueryResult:Iterator()\n   local thread = coroutine.create(function()\n      self:ForEach(function(value, count)\n         -- These will be passed back again next iteration\n         coroutine.yield(value, count)\n      end)\n   end)\n\n   return function()\n      local success, item, index = coroutine.resume(thread)\n      return index, item\n   end\nend\n\n--[[\n   Performs an action for each element of this QueryResult.\n\n   This is a terminal operation.\n\n   The behavior of this operation is explicitly nondeterministic. This operation does not guarantee to respect the \n   encounter order of the QueryResult.\n\n   @param action {function(value, count) -> bool} A action to perform on the elements, breaks execution case returns true\n]]\nfunction QueryResult:ForEach(action)\n   local count = 1\n   local pipeline = self._pipeline\n\n   local hasPipeline = #pipeline > 0 \n   if (not hasPipeline) then\n      -- faster\n      for _, entities in ipairs(self.chunks) do\n         for entity, _ in pairs(entities) do\n            if (action(entity, count) == true) then\n               return\n            end\n            count = count + 1  \n         end\n      end\n   else\n      -- Pipeline this QueryResult, applying callback to each value\n      for i, entities in ipairs(self.chunks) do\n         for entity,_ in pairs(entities) do\n            local mustStop = false\n            local itemAccepted = true\n\n            local value = entity\n            if (itemAccepted and hasPipeline) then               \n               for _, operator in ipairs(pipeline) do\n                  local newValue, acceptItem, canContinue = operator[1](operator[2], value, count)\n                  if (not canContinue) then\n                     mustStop = true\n                  end\n   \n                  if acceptItem then\n                     value = newValue\n                  else\n                     itemAccepted = false\n                     break\n                  end\n               end\n            end\n            \n            if itemAccepted then\n               if (action(value, count) == true) then\n                  return\n               end\n               count = count + 1\n            end\n   \n            if mustStop then\n               return\n            end\n         end\n      end\n   end\nend\n\nreturn QueryResult\n"
  },
  {
    "path": "src/RobloxLoopManager.lua",
    "content": "local function InitManager()\n   local RunService = game:GetService(\"RunService\")\n   \n   return {\n      Register = function(world)         \n         -- if not RunService:IsRunning() then\n         --    return\n         -- end\n         local beforePhysics = RunService.Stepped:Connect(function()\n            world:Update(\"process\", os.clock())\n         end)\n   \n         local afterPhysics = RunService.Heartbeat:Connect(function()\n            world:Update(\"transform\", os.clock())\n         end)\n   \n         local beforeRender\n         if (not RunService:IsServer()) then\n            beforeRender = RunService.RenderStepped:Connect(function()\n               world:Update(\"render\", os.clock())\n            end)\n         end\n   \n         return function()\n            beforePhysics:Disconnect()\n            afterPhysics:Disconnect()\n            if beforeRender then\n               beforeRender:Disconnect()\n            end\n         end\n      end\n   }\nend\n\nreturn InitManager\n"
  },
  {
    "path": "src/System.lua",
    "content": "\nlocal STEPS = { \"task\", \"render\", \"process\", \"transform\" }\n\nlocal System = {}\n\n--[[\n   Create new System Class\n\n   @param step {process|transform|render|task}\n   @param order {number} (Optional) Allows you to set an execution order (for systems that are not `task`). Default 50\n   @param query {Query|QueryBuilder} (Optional) Filters the entities that will be processed by this system\n   @param updateFn {function(self, Time)} (Optional) A shortcut for creating systems that only have the Update method\n   @return SystemClass\n]]\nfunction System.Create(step, order, query, updateFn)\n\n   if (step == nil or not table.find(STEPS, step)) then\n      error(\"The step parameter must one of \", table.concat(STEPS, \", \"))\n   end\n\n   if (order and type(order) == \"function\") then\n      updateFn = order\n      order = nil\n   elseif query and type(query) == \"function\" then\n      updateFn = query\n      query = nil\n   end\n\n   if (order and type(order) == \"table\" and (order.isQuery or order.isQueryBuilder)) then\n      query = order\n      order = nil\n   end\n\n   if (order == nil or order < 0) then\n      order = 50\n   end\n\n   if type(query) == \"function\" then\n      updateFn = query\n      query = nil\n   end\n\n   if (query and query.isQueryBuilder) then\n      query = query.Build()\n   end\n\n   local SystemClass = {\n      Step = step,\n      -- Allows you to define the execution priority level for this system\n      Order = order,\n      Query = query,\n      -- After = {SystemC, SystemD}, When the system is a task, it allows you to define that this system should run AFTER other specific systems.\n      -- Before = {SystemA, SystemB}, When the system is a task, it allows you to define that this system should run BEFORE other specific systems.\n      --[[\n\n         ShouldUpdate(Time) -> bool - Invoked before 'Update', allows you to control the execution of the update\n         Update(Time)\n\n         [QuerySystem]\n            OnRemove(Time, enity)\n            OnExit(Time, entity)\n            OnEnter(Time, entity)\n      ]]\n   }\n   SystemClass.__index = SystemClass\n\n   --[[\n      Create an instance of this system\n\n      @param world {World}\n      @param config {table}\n   ]]\n   function SystemClass.New(world, config)\n      local system = setmetatable({\n         version = 0,\n         _world = world,\n         _config = config,\n      }, SystemClass)\n\n      if system.Initialize then\n         system:Initialize(config)\n      end\n\n      return system\n   end\n\n    --[[\n      Get this system class\n\n      @return SystemClass\n   ]]\n   function SystemClass:GetType()\n      return SystemClass\n   end\n\n   --[[\n      Run a query in the world. A shortcut to `self._world:Exec(query)`\n\n      @query {Query|QueryBuilder} Optional If nil, use default query\n      @return QueryResult\n   ]]\n   function SystemClass:Result(query)\n      return self._world:Exec(query or SystemClass.Query)\n   end\n\n   --[[\n      destroy this instance\n   ]]\n   function SystemClass:Destroy() \n      if self.OnDestroy then\n         self.OnDestroy()\n      end\n      setmetatable(self, nil)\n      for k,v in pairs(self) do\n         self[k] = nil\n      end\n   end\n\n   if updateFn and type(updateFn) == \"function\" then\n      SystemClass.Update = updateFn\n   end\n\n   return SystemClass\nend\n\nreturn System\n"
  },
  {
    "path": "src/SystemExecutor.lua",
    "content": "\n--[[\n   After = {SystemC, SystemD}, An update order that requests ECS update this system after it updates another specified system.\n   Before = {SystemA, SystemB}, An update order that requests ECS update this system before it updates another specified system.\n]]\nlocal function mapTaskDependencies(systems)\n\n   local nodes = {}\n   local nodesByType = {}\n\n   for i,system in ipairs(systems) do\n      local sType = system:GetType()\n\n      if (system._TaskState == nil) then\n         -- suspended, scheduled, running\n         system._TaskState = \"suspended\"\n      end\n\n      if not nodesByType[sType] then\n         local node = {\n            Type = sType,\n            System = system,\n            -- @type {[Node]=true}\n            Depends = {}\n         }\n         nodesByType[sType] = node\n         table.insert(nodes, node)        \n      end\n   end\n\n   for _, node in ipairs(nodes) do\n       -- this system will update Before another specified system\n       local before = node.Type.Before\n       if (before ~= nil and #before > 0) then\n          for _,sTypeOther in ipairs(before) do\n             local otherNode = nodesByType[sTypeOther]\n             if otherNode then\n                otherNode.Depends[node] = true\n             end\n          end\n       end\n\n      -- this system will update After another specified system\n      local after = node.Type.After\n      if (after ~= nil and #after > 0) then\n         for _,sTypeOther in ipairs(after) do\n            local otherNode = nodesByType[sTypeOther]\n            if otherNode then\n               node.Depends[otherNode] = true\n            end\n         end\n      end\n   end\n\n   return nodes\nend\n\nlocal function orderSystems(a, b)\n   return a.Order < b.Order\nend\n\n--[[\n   Responsible for coordinating and executing the systems methods\n]]\nlocal SystemExecutor = {}\nSystemExecutor.__index = SystemExecutor\n\nfunction SystemExecutor.New(world)   \n   local executor =  setmetatable({\n      _world = world,\n      _onExit = {},\n      _onEnter = {},\n      _onRemove = {},\n      _task = {},\n      _render = {},\n      _process = {},\n      _transform = {},\n      _schedulers = {},\n      _lastFrameMatchQueries = {},\n      _currentFrameMatchQueries = {},\n   }, SystemExecutor)\n\n   world:OnQueryMatch(function(query)\n      executor._currentFrameMatchQueries[query] = true\n   end)\n\n   return executor\nend\n\nfunction SystemExecutor:SetSystems(systems)\n   local onExit = {}\n   local onEnter = {}\n   local onRemove = {}\n   -- system:Update()\n   local updateTask = {}\n   local updateRender = {}\n   local updateProcess = {}\n   local updateTransform = {}\n\n   for _, system in pairs(systems) do      \n      local step = system.Step\n      if system.Update then\n         if step == \"task\" then\n            table.insert(updateTask, system)\n            \n         elseif step == \"process\" then\n            table.insert(updateProcess, system) \n\n         elseif step == \"transform\" then\n            table.insert(updateTransform, system)\n\n         elseif step == \"render\" then\n            table.insert(updateRender, system)\n\n         end\n      end\n\n      if (system.Query and system.Query.isQuery and step ~= \"task\") then\n         if system.OnExit then\n            table.insert(onExit, system)\n         end\n\n         if system.OnEnter then\n            table.insert(onEnter, system)\n         end\n   \n         if system.OnRemove then\n            table.insert(onRemove, system)\n         end\n      end\n   end\n\n   updateTask = mapTaskDependencies(updateTask)\n   \n   table.sort(onExit, orderSystems)\n   table.sort(onEnter, orderSystems)\n   table.sort(onRemove, orderSystems)\n   table.sort(updateRender, orderSystems)\n   table.sort(updateProcess, orderSystems)\n   table.sort(updateTransform, orderSystems)\n\n   self._onExit = onExit\n   self._onEnter = onEnter\n   self._onRemove = onRemove\n   self._task = updateTask\n   self._render = updateRender\n   self._process = updateProcess\n   self._transform = updateTransform\nend\n\n--[[\n      \n   @param Time\n   @param changedEntities { { [Entity] = Old<Archetype> } }\n]]\nfunction SystemExecutor:ExecOnExitEnter(Time, changedEntities)\n   local isEmpty = true\n\n   -- { [Old<Archetype>] = { [New<Archetype>] = {Entity, Entity, ...} } }\n   local oldIndexed = {}\n   for entity, archetypeOld in pairs(changedEntities) do\n      local newIndexed = oldIndexed[archetypeOld]\n      if not newIndexed then\n         newIndexed = {}\n         oldIndexed[archetypeOld] = newIndexed\n      end\n      local archetypeNew = entity.archetype\n\n      local entities = newIndexed[archetypeNew]\n      if not entities then\n         entities = {}\n         newIndexed[archetypeNew] = entities\n      end\n      table.insert(entities, entity)\n      isEmpty = false\n   end\n   if isEmpty then\n      return\n   end\n   self:_ExecOnEnter(Time, oldIndexed)\n   self:_ExecOnExit(Time, oldIndexed)\nend\n\n--[[\n   Executes the systems' OnEnter method\n\n   @param Time {Time}\n   @param entities {{[Key=Entity] => Archetype}}\n   ]]\nfunction SystemExecutor:_ExecOnEnter(Time, oldIndexed)\n   local world = self._world\n   for _, system in ipairs(self._onEnter) do\n      local query = system.Query\n      for archetypeOld, newIndexed in pairs(oldIndexed) do\n         if not query:Match(archetypeOld) then\n            for archetypeNew, entities in pairs(newIndexed) do\n               if query:Match(archetypeNew) then\n                  for i,entity in ipairs(entities) do                  \n                     world.version = world.version + 1   -- increment Global System Version (GSV)\n                     system:OnEnter(Time, entity)        -- local dirty = entity.version > system.version\n                     system.version = world.version      -- update last system version with GSV\n                  end\n               end\n            end\n         end         \n      end\n   end\nend\n\n--[[\n   Executes the systems' OnExit method\n\n   @param Time {Time}\n   @param entities {{[Key=Entity] => Archetype}}\n]]\nfunction SystemExecutor:_ExecOnExit(Time, oldIndexed)\n   local world = self._world\n   for _, system in ipairs(self._onExit) do\n      local query = system.Query\n      for archetypeOld, newIndexed in pairs(oldIndexed) do\n         if query:Match(archetypeOld) then\n            for archetypeNew, entities in pairs(newIndexed) do\n               if not query:Match(archetypeNew) then\n                  for i,entity in ipairs(entities) do                  \n                     world.version = world.version + 1   -- increment Global System Version (GSV)\n                     system:OnExit(Time, entity)         -- local dirty = entity.version > system.version\n                     system.version = world.version      -- update last system version with GSV\n                  end\n               end\n            end\n         end         \n      end\n   end\nend\n\n--[[\n   Executes the systems' OnRemove method\n\n   @param Time {Time}\n   @param entities {{[Key=Entity] => Archetype}}\n]]\nfunction SystemExecutor:ExecOnRemove(Time, removedEntities)\n   \n   local isEmpty = true\n   local oldIndexed = {}\n   for entity, archetypeOld in pairs(removedEntities) do\n      local entities = oldIndexed[archetypeOld]\n      if not entities then\n         entities = {}\n         oldIndexed[archetypeOld] = entities\n      end\n      table.insert(entities, entity)\n      isEmpty = false\n   end\n   if isEmpty then\n      return\n   end\n   \n   local world = self._world\n   for _, system in ipairs(self._onRemove) do \n      for archetypeOld, entities in pairs(oldIndexed) do\n         if system.Query:Match(archetypeOld) then\n            for i,entity in ipairs(entities) do  \n               world.version = world.version + 1   -- increment Global System Version (GSV)\n               system:OnRemove(Time, entity)       -- local dirty = entity.version > system.version\n               system.version = world.version      -- update last system version with GSV\n            end\n         end\n      end\n   end\nend\n\nlocal function execUpdate(executor, systems, Time)\n   local world = executor._world\n   local lastFrameMatchQueries = executor._lastFrameMatchQueries\n   local currentFrameMatchQueries = executor._currentFrameMatchQueries\n   for j, system in ipairs(systems) do\n      local canExec = true\n      if system.Query then\n         local query = system.Query\n         if lastFrameMatchQueries[query] == true or currentFrameMatchQueries[query] == true then\n            -- If the query ran in the last frame, it is likely to run successfully on this\n            canExec = true\n         else\n            -- Always revalidates, the repository undergoes constant change\n            canExec = world:FastCheck(query)\n            currentFrameMatchQueries[query] = canExec\n         end\n      end\n      if canExec then\n         if (system.ShouldUpdate == nil or system.ShouldUpdate(Time)) then\n            world.version = world.version + 1   -- increment Global System Version (GSV)\n            system:Update(Time)                 -- local dirty = entity.version > system.version\n            system.version = world.version      -- update last system version with GSV\n         end\n      end\n   end\nend\n\nfunction SystemExecutor:ExecProcess(Time)\n   self._currentFrameMatchQueries = {}\n   execUpdate(self, self._process, Time)   \nend\n\nfunction SystemExecutor:ExecTransform(Time)\n   execUpdate(self, self._transform, Time)\nend\n\nfunction SystemExecutor:ExecRender(Time)\n   execUpdate(self, self._render, Time)\n   self._lastFrameMatchQueries = self._currentFrameMatchQueries\nend\n\n--[[\n   Starts the execution of Jobs.\n\n   Each Job is performed in an individual coroutine\n\n   @param maxExecTime {number} limits the amount of time jobs can run\n]]\nfunction SystemExecutor:ExecTasks(maxExecTime)\n   while maxExecTime > 0 do\n      local hasMore = false\n\n      -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785\n      local i, len = 0, #self._schedulers-1\n      while i <= len do\n         i = i + 1\n\n         local scheduler = self._schedulers[i]\n         local tasksTime, hasMoreTask = scheduler.Resume(maxExecTime)\n         \n         if hasMoreTask then\n            hasMore = true\n         end\n   \n         maxExecTime = maxExecTime - (tasksTime + 0.00001)\n         \n         if (maxExecTime <= 0) then\n            break\n         end\n      end\n\n      if not hasMore then\n         return\n      end\n   end\nend\n\nlocal function execTask(node, Time, world, onComplete)\n   local system = node.System\n   system._TaskState = \"running\"\n   if (system.ShouldUpdate == nil or system.ShouldUpdate(Time)) then\n      world.version = world.version + 1   -- increment Global System Version (GSV)\n      system:Update(Time)                 -- local dirty = entity.version > system.version\n      system.version = world.version      -- update last system version with GSV\n   end\n   system._TaskState = \"suspended\"\n   onComplete(node)\nend\n\n--[[\n   Invoked at the beginning of each frame, it schedules the execution of the next tasks\n]]\nfunction SystemExecutor:ScheduleTasks(Time)\n   local world = self._world\n\n   local rootNodes = {}    -- Node[]\n   local runningNodes = {} -- Node[]\n   local scheduled = {}    -- { [Node] = true }\n   local completed = {}    -- { [Node] = true }\n   local dependents = {}   -- { [Node] = Node[] }\n\n   local i, len = 0, #self._task-1\n   while i <= len do\n      i = i + 1\n      local node = self._task[i]\n      \n      if (node.System._TaskState == \"suspended\") then\n         -- will be executed\n         node.System._TaskState = \"scheduled\"\n\n         local hasDependencies = false\n         for other,_ in pairs(node.Depends) do\n            hasDependencies = true\n            if dependents[other] == nil then\n               dependents[other] = {}\n            end\n            table.insert(dependents[other], node)\n         end\n         \n         if (not hasDependencies) then\n            table.insert(rootNodes, node)\n         end\n\n         scheduled[node] = true\n      end\n   end\n\n   -- suspended, scheduled, running\n   local function onComplete(node)\n\n      node.Thread = nil\n      node.LastExecTime = nil\n      completed[node] = true\n\n      -- alguma outra tarefa depende da execucao deste no para executar?\n      if dependents[node] then\n         local dependentsFromNode = dependents[node]\n\n         local i, len = 0, #dependentsFromNode-1\n         while i <= len do\n            i = i + 1\n            local dependent = dependentsFromNode[i]\n            if scheduled[dependent] then\n               local allDependenciesCompleted = true\n               for otherNode,_ in pairs(dependent.Depends) do\n                  if completed[otherNode] ~= true then\n                     allDependenciesCompleted = false\n                     break\n                  end\n               end\n   \n               if allDependenciesCompleted then\n                  scheduled[dependent] = nil\n                  dependent.LastExecTime = 0\n                  dependent.Thread = coroutine.create(execTask)\n                  table.insert(runningNodes, dependent)\n               end\n            end\n         end\n      end\n   end\n\n   if #rootNodes > 0 then\n      local i, len = 0, #rootNodes-1\n      while i <= len do\n         i = i + 1\n         local node = rootNodes[i]\n         scheduled[node] = nil\n         node.LastExecTime = 0\n         node.Thread = coroutine.create(execTask)\n         table.insert(runningNodes, node)\n      end\n\n      local scheduler\n      scheduler = {\n         Resume = function(maxExecTime)\n\n            -- orders the threads, executing the ones with the least execution time first this prevents long tasks \n            -- from taking up all the processing time\n            table.sort(runningNodes, function(nodeA, nodeB)\n               return nodeA.LastExecTime < nodeB.LastExecTime\n            end)\n\n            local totalTime = 0\n\n            -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785\n            local i, len = 0, #runningNodes-1\n            while i <= len do\n               i = i + 1\n               local node = runningNodes[i]\n\n               if node.Thread ~= nil then\n                  local execTime = os.clock()\n                  node.LastExecTime = execTime\n   \n                  coroutine.resume(node.Thread, node, Time, world, onComplete)\n   \n                  totalTime = totalTime + (os.clock() - execTime)\n   \n                  if (totalTime > maxExecTime) then\n                     break\n                  end\n               end\n            end\n\n            -- remove completed\n            for i,node in ipairs(runningNodes) do\n               if node.Thread == nil then                  \n                  local idx = table.find(runningNodes, node)\n                  if idx ~= nil then\n                     table.remove(runningNodes, idx)\n                  end\n               end\n            end\n\n            local hasMore = #runningNodes > 0\n   \n            if (not hasMore) then\n               local idx = table.find(self._schedulers, scheduler)\n               if idx ~= nil then\n                  table.remove(self._schedulers, idx)\n               end\n            end\n\n            return totalTime, hasMore\n         end\n      }\n\n      table.insert(self._schedulers, scheduler)\n   end\nend\n\nreturn SystemExecutor\n"
  },
  {
    "path": "src/Timer.lua",
    "content": "\n-- if execution is slow, perform a maximum of 4 simultaneous updates in order to keep the fixrate\nlocal MAX_SKIP_FRAMES = 4\n\nlocal function loop(Time)\n\n   local accumulator = 0.0\n   local lastStepTime = 0.0\n\n   return function (newTime, stepName, beforeUpdateFn, updateFn)\n      local dtFixed = Time.DeltaFixed\n      local stepTime = newTime - lastStepTime\n      if stepTime > 0.25 then\n         stepTime = 0.25\n      end\n      lastStepTime = newTime\n\n      Time.Now = newTime\n\n      -- 1000/30/1000 = 0.03333333333333333\n      accumulator = accumulator + stepTime\n      \n      --[[\n         Adjusting the framerate, the world must run on the same frequency,\n         this ensures determinism in the execution of the scripts\n\n         Each system in \"transform\" step is executed at a predetermined frequency (in Hz).\n\n         Ex. If the game is running on the client at 30FPS but a system needs to be run at\n         120Hz or 240Hz, this logic will ensure that this frequency is reached\n\n         @see https://gafferongames.com/post/fix_your_timestep/\n         @see https://gameprogrammingpatterns.com/game-loop.html\n         @see https://bell0bytes.eu/the-game-loop/\n      ]]\n      if stepName == \"process\" then\n         if accumulator >= dtFixed then       \n            Time.Interpolation = 1\n\n            beforeUpdateFn(Time)\n            local nLoops = 0\n            while (accumulator >= dtFixed and nLoops < MAX_SKIP_FRAMES) do\n               updateFn(Time)\n               nLoops = nLoops + 1\n               Time.Process = Time.Process + dtFixed\n               accumulator = accumulator - dtFixed\n            end\n         end\n      else\n         Time.Interpolation = math.min(math.max(accumulator/dtFixed, 0), 1)\n         beforeUpdateFn(Time)\n         updateFn(Time)\n      end\n   end\nend\n\nlocal Timer = {}\nTimer.__index = Timer\n\nfunction Timer.New(frequency)\n   local Time = {\n      Now = 0,\n      -- The time at the beginning of this frame. The world receives the current time at the beginning\n      -- of each frame, with the value increasing per frame.\n      Frame = 0,         \n      Process = 0, -- The time the latest process step has started.\n      Delta = 0, -- The completion time in seconds since the last frame.\n      DeltaFixed = 0,\n      -- INTERPOLATION: The proportion of time since the previous transform relative to processDeltaTime\n      Interpolation = 0\n   }\n\n   local timer = setmetatable({\n      -- Public, visible by systems\n      Time = Time,\n      Frequency = 0,\n      _update = loop(Time)\n   }, Timer)\n\n   timer:SetFrequency(frequency)\n\n   return timer\nend\n\n--[[\n   Changes the frequency of execution of the \"process\" step\n\n   @param frequency {number}\n]]\nfunction Timer:SetFrequency(frequency)\n\n   -- frequency: number,\n   -- The maximum times per second this system should be updated. Defaults 30\n   if frequency == nil then\n      frequency = 30\n   end\n\n   local safeFrequency  = math.floor(math.abs(frequency)/2)*2\n   if safeFrequency < 2 then\n      safeFrequency = 2\n   end\n\n   if frequency ~= safeFrequency then\n      frequency = safeFrequency\n   end\n\n   self.Frequency = frequency\n   self.Time.DeltaFixed = 1000/frequency/1000\nend\n\nfunction Timer:Update(now, step, beforeUpdate, update)\n   self._update(now, step, beforeUpdate, update)\nend\n\nreturn Timer\n"
  },
  {
    "path": "src/Utility.lua",
    "content": "--[[\n   Utility library.\n]]\nlocal Utility = {}\n\nif table.unpack == nil then\n\ttable.unpack = unpack\nend\n\nif table.find == nil then\n   --[[\n      Within the given array-like table haystack, find the first occurrence of value needle, starting from index init \n      or the beginning if not provided. If the value is not found, nil is returned.\n\n      A linear search algorithm is performed.\n   ]]\n   table.find = function(haystack, needle, init)\n      local len = #haystack\n      for i = init or 1, len, 1 do\n         if haystack[i] == needle then\n            return i\n         end\n      end\n\n      return nil\n   end\nend\n\nlocal function copyDeep(src)\n\tlocal copy = {}\n   for k, v in pairs(src) do\n      if type(v) == \"table\" then\n         v = copyDeep(v)\n      end\n      copy[k] = v\n   end\n\t\n\treturn copy\nend\nUtility.copyDeep = copyDeep\n\n--[[\n   Faz o merge dos atributos src com o dest\n   Quando o um atributo do segundo é um \"table\", faz uma copia do valor\n]]\nlocal function mergeDeep(dest, src)\n   for k,valueSrc in pairs(src) do\n      if (type(valueSrc) == \"table\") then\n         local valueDest = dest[k]\n         if (valueDest == nil or type(valueDest) ~= \"table\") then\n            dest[k] = copyDeep(valueSrc)\n         else\n            dest[k] = mergeDeep(valueDest, valueSrc)\n         end\n      else\n         dest[k] = valueSrc\n      end\n   end\n\treturn dest\nend\nUtility.mergeDeep = mergeDeep\n\nreturn Utility\n"
  },
  {
    "path": "src/World.lua",
    "content": "local Timer = require(\"Timer\")\nlocal Event = require(\"Event\")\nlocal Entity = require(\"Entity\")\nlocal Archetype = require(\"Archetype\")\nlocal SystemExecutor = require(\"SystemExecutor\")\nlocal EntityRepository = require(\"EntityRepository\")\n\nlocal World = {}\nWorld.__index = World\n\n--[[  \n   Create a new world instance\n\n   @param systemClasses {SystemClass[]} (Optional) Array of system classes\n   @param frequency {number} (Optional) define the frequency that the `process` step will be executed. Default 30\n   @param disableAutoUpdate {bool} (Optional) when `~= false`, the world automatically registers in the `LoopManager`, \n   receiving the `World:Update()` method from it. Default false\n]]\nfunction World.New(systemClasses, frequency, disableAutoUpdate)   \n   local world = setmetatable({\n      --[[\n         Global System Version (GSV).\n\n         Before executing the Update method of each system, the world version is incremented, so at this point, the \n         world version will always be higher than the running system version.\n\n         Whenever an entity archetype is changed (received or lost component) the entity's version is updated to the \n         current version of the world.\n\n         After executing the System Update method, the version of this system is updated to the current world version.\n\n         This mechanism allows a system to know if an entity has been modified after the last execution of this same \n         system, as the entity's version is superior to the version of the last system execution. Thus, a system can \n         contain logic if it only operates on \"dirty\" entities, which have undergone changes. The code for this \n         validation on a system is `local isDirty = entity.version > self.version`\n      ]]\n      version = 0,\n      --[[\n         Allows you to define the maximum time that the JobSystem can operate in each frame.\n\n         The default value is 0.011666666666666665 = ((1000/60/1000)*0.7)\n\n         A game that runs at 30fps has 0.0333 seconds to do all the processing for each frame, including rendering\n            - 30FPS = ((1000/30/1000)*0.7)/3 = 0.007777777777777777\n\n         A game that runs at 60fps has 0.0166 seconds to do all the processing for each frame, including rendering\n            - 60FPS = ((1000/60/1000)*0.7)/3 = 0.0038888888888888883\n      ]]\n      maxTasksExecTime = 0.013333333333333334,\n      _dirty = false, -- True when create/remove entity, add/remove entity component (change archetype)\n      _timer = Timer.New(frequency),\n      _systems = {}, -- systems in this world\n      _repository = EntityRepository.New(),\n      _entitiesCreated = {}, -- created during the execution of the Update\n      _entitiesRemoved = {}, -- removed during execution (only removed after the last execution step)\n      _entitiesUpdated = {}, -- changed during execution (received or lost components, therefore, changed the archetype)\n      _onQueryMatch = Event.New(),\n      _onChangeArchetypeEvent = Event.New(),\n   }, World)\n\n   -- System execution plan\n   world._executor = SystemExecutor.New(world)\n\n   world._onChangeArchetypeEvent:Connect(function(entity, archetypeOld, archetypeNew)      \n      world:_OnChangeArchetype(entity, archetypeOld, archetypeNew)\n   end)\n\n   -- add systems\n   if (systemClasses ~= nil) then\n      for _, systemClass in ipairs(systemClasses) do\n         world:AddSystem(systemClass)\n      end\n   end\n\n   if (not disableAutoUpdate and World.LoopManager) then\n      world._loopCancel = World.LoopManager.Register(world)\n   end\n\n   return world\nend\n\n--[[\n   Changes the frequency of execution of the \"process\" step\n\n   @param frequency {number}\n]]\nfunction World:SetFrequency(frequency) \n   frequency = self._timer:SetFrequency(frequency) \nend\n\n--[[\n   Get the frequency of execution of the \"process\" step\n\n   @return number\n]]\nfunction World:GetFrequency(frequency) \n   return self._timer.Frequency\nend\n\n--[[\n   Add a new system to the world.\n\n   Only one instance per type is accepted. If there is already another instance of this system in the world, any new \n   invocation of this method will be ignored.\n\n   @param systemClass {SystemClass} The system to be added in the world\n   @param config {table} (Optional) System instance configuration\n]]\nfunction World:AddSystem(systemClass, config)\n   if systemClass then\n      if config == nil then\n         config = {}\n      end\n     \n      if self._systems[systemClass] == nil then\n         self._systems[systemClass] = systemClass.New(self, config)\n\n         self._executor:SetSystems(self._systems)\n      end\n   end\nend\n\n--[[\n   Create a new entity.\n\n   The entity is created in DEAD state (entity.isAlive == false) and will only be visible for queries after the \n   cleaning step (OnRemove, OnEnter, OnExit) of the current step\n\n   @param args {Component[]}\n   @return Entity\n]]\nfunction World:Entity(...)\n   local entity = Entity.New(self._onChangeArchetypeEvent, {...})\n\n   self._dirty = true\n   self._entitiesCreated[entity] = true\n   \n   entity.version = self.version -- update entity version using current Global System Version (GSV)\n   entity.isAlive = false\n\n   return entity\nend\n\n--[[\n   Performs immediate removal of an entity.\n\n   If the entity was created in this step and the cleanup process has not happened yet (therefore the entity is \n   inactive, entity.isAlive == false), the `OnRemove` event will never be fired.\n\n   If the entity is alive (entity.isAlive == true), even though it is removed immediately, the `OnRemove` event will be \n   fired at the end of the current step.\n\n   @param entity {Entity}\n]]\nfunction World:Remove(entity)\n\n   if self._entitiesRemoved[entity] == true then\n      return\n   end\n\n   if self._entitiesCreated[entity] == true then\n      self._entitiesCreated[entity] = nil\n   else\n      self._repository:Remove(entity)\n      self._entitiesRemoved[entity] = true\n\n      if self._entitiesUpdated[entity] == nil then\n         self._entitiesUpdated[entity] = entity.archetype\n      end\n   end\n\n   self._dirty = true\n   entity.isAlive = false\nend\n\n--[[\n   Run a query in this world\n\n   @param query {Query|QueryBuilder}\n   @return QueryResult\n]]\nfunction World:Exec(query)\n   if (query.isQueryBuilder) then\n      query = query.Build()\n   end\n\n   local result, match = self._repository:Query(query)\n\n   if match then\n      self._onQueryMatch:Fire(query)\n   end\n\n   return result\nend\n\n--[[\n   Quick check to find out if a query is applicable.\n\n   @param query {Query|QueryBuilder}\n   @return QueryResult\n]]\nfunction World:FastCheck(query)\n   if (query.isQueryBuilder) then\n      query = query.Build()\n   end\n\n   return self._repository:FastCheck(query)\nend\n\n--[[\n   Add a callback that is reported whenever a query has been successfully executed. Used internally \n   to quickly find out if a QuerySystem will run.\n]]\nfunction World:OnQueryMatch(callback)\n   return self._onQueryMatch:Connect(callback)\nend\n\n--[[\n   Perform world update.\n\n   When registered, LoopManager will invoke World Update for each step in the sequence.\n\n   - process At the beginning of each frame\n   - transform After the game engine's physics engine runs\n   - render Before rendering the current frame\n\n   @param step {\"process\"|\"transform\"|\"render\"}\n   @param now {number} Usually os.clock()\n]]\nfunction World:Update(step, now)\n\n   \n   self._timer:Update(\n      now, step,\n      function(Time)\n         --[[\n            JobSystem\n            .------------------.\n            |     pipeline     |\n            |------------------| \n            | s:ShouldUpdate() |\n            | s:Update()       |\n            '------------------'\n         ]]\n         if step == \"process\" then\n            self._executor:ScheduleTasks(Time)\n         end\n         -- run suspended Tasks\n         self._executor:ExecTasks(self.maxTasksExecTime)\n      end,\n      function(Time)\n         --[[\n            .------------------.\n            |     pipeline     |\n            |------------------| \n            | s:ShouldUpdate() |\n            | s:Update()       |\n            |                  |\n            |-- CLEAR ---------|\n            | s:OnRemove()     |\n            | s:OnExit()       |\n            | s:OnEnter()      |\n            '------------------'\n         ]]\n         if step == \"process\" then\n            self._executor:ExecProcess(Time)\n         elseif step == \"transform\" then\n            self._executor:ExecTransform(Time)\n         else\n            self._executor:ExecRender(Time)\n         end\n\n         -- cleans up after running scripts\n         while self._dirty do\n            self._dirty = false\n         \n            -- 1: remove entities\n            local entitiesRemoved = {}\n            for entity,_ in pairs(self._entitiesRemoved) do\n               entitiesRemoved[entity] = self._entitiesUpdated[entity]\n               self._entitiesUpdated[entity] = nil\n            end\n            self._entitiesRemoved = {}\n            self._executor:ExecOnRemove(Time, entitiesRemoved)\n            entitiesRemoved = nil\n         \n            local changed = {}\n            local hasChange = false\n         \n            -- 2: Update entities in memory\n            for entity, archetypeOld in pairs(self._entitiesUpdated) do\n               if (archetypeOld ~= entity.archetype) then\n                  hasChange = true\n                  changed[entity] = archetypeOld\n               end\n            end\n            self._entitiesUpdated = {}\n         \n            -- 3: Add new entities\n            for entity, _ in pairs(self._entitiesCreated) do\n               hasChange = true\n               changed[entity] = Archetype.EMPTY\n         \n               entity.isAlive = true\n               self._repository:Insert(entity) \n            end\n            self._entitiesCreated = {}\n         \n            if hasChange then\n               self._executor:ExecOnExitEnter(Time, changed)\n               changed = nil\n            end\n         end\n      end\n   )\nend\n\n--[[\n   Destroy this instance, removing all entities, systems and events\n]]\nfunction World:Destroy()\n\n   if self._loopCancel then\n      self._loopCancel()\n      self._loopCancel = nil\n   end\n\n   if self._onChangeArchetypeEvent then\n      self._onChangeArchetypeEvent:Destroy()\n      self._onChangeArchetypeEvent = nil\n   end\n\n   self._repository = nil\n\n   if self._systems then\n      for _,system in pairs(self._systems) do\n         system:Destroy()\n      end\n      self._systems = nil\n   end\n   \n   self._timer = nil\n   self._ExecPlan = nil\n   self._entitiesCreated = nil\n   self._entitiesUpdated = nil\n   self._entitiesRemoved = nil\n\n   setmetatable(self, nil)\nend\n\nfunction World:_OnChangeArchetype(entity, archetypeOld, archetypeNew)\n   if entity.isAlive then\n\n      if self._entitiesUpdated[entity] == nil then\n         self._dirty = true\n         self._entitiesUpdated[entity] = archetypeOld\n      end\n   \n      self._repository:Update(entity)\n\n      -- update entity version using current Global System Version (GSV)\n      entity.version = self.version\n   end\nend\n\nreturn World\n"
  },
  {
    "path": "test/README.md",
    "content": "see [CONTRIBUTING.md](/CONTRIBUTING.md)\n"
  },
  {
    "path": "test/test_Archetype.lua",
    "content": "local lu = require('luaunit')\n\nlocal Archetype = require('Archetype')\n\nTestArchetype = {}\n\nlocal SEQ = 1\nlocal function newCompId()\n   SEQ = SEQ+1\n   return SEQ\nend\n\nfunction TestArchetype:test_Should_ReturnSame()\n\n   local version = Archetype.Version()\n\n   local ComponentA = { Id = newCompId(), IsCType = true }\n   local ComponentB = { Id = newCompId(), IsCType = true }\n   local ComponentC = { Id = newCompId(), }\n   \n   local ComponentS = { Id = newCompId(), IsCType = true }\n   local ComponentQ = { Id = newCompId(), IsCType = true, IsQualifier = true, SuperClass = ComponentS }\n   \n   local archetypeA = Archetype.Of({ ComponentA, ComponentQ, ComponentB, ComponentC })\n   local archetypeB = Archetype.Of({ ComponentC, ComponentA, ComponentQ, ComponentB })\n\n   lu.assertNotIsNil(archetypeA)\n   lu.assertNotIsNil(archetypeB)\n\n   lu.assertEquals(archetypeA, archetypeB)\n\n   lu.assertIsTrue(archetypeA:Has(ComponentA))\n   lu.assertIsTrue(archetypeA:Has(ComponentB))\n\n   lu.assertIsTrue(archetypeA:Has(ComponentS))\n   lu.assertIsTrue(archetypeA:Has(ComponentQ))\n\n   lu.assertIsFalse(archetypeA:Has(ComponentC))\n   \n   lu.assertNotEquals(version, Archetype.Version())\nend\n\nfunction TestArchetype:test_Should_Create_With_Component()\n\n   local ComponentA = { Id = newCompId(), IsCType = true }\n   local ComponentB = { Id = newCompId(), IsCType = true }\n   local ComponentC = { Id = newCompId(), }\n   local ComponentD = { Id = newCompId(), IsCType = true }\n\n   local archetypeA = Archetype.Of({ ComponentA, ComponentB, ComponentC })\n   local archetypeB = Archetype.Of({ ComponentA, ComponentB, ComponentC, ComponentD })\n   local archetypeC = archetypeA:With(ComponentD)\n   local archetypeD = archetypeB:With(ComponentD)\n\n   lu.assertNotIsNil(archetypeA)\n   lu.assertNotIsNil(archetypeB)\n   lu.assertNotIsNil(archetypeC)\n   lu.assertNotIsNil(archetypeD)\n\n   lu.assertEquals(archetypeB, archetypeC)\n   lu.assertEquals(archetypeB, archetypeD)\n\n   \n   lu.assertIsTrue(archetypeB:Has(ComponentD))\n   lu.assertIsFalse(archetypeA:Has(ComponentD))\nend\n\nfunction TestArchetype:test_Should_Create_WithAll_Components()\n\n   local ComponentA = { Id = newCompId(), IsCType = true }\n   local ComponentB = { Id = newCompId(), IsCType = true }\n   local ComponentC = { Id = newCompId(), }\n   local ComponentD = { Id = newCompId(), IsCType = true }\n   local ComponentE = { Id = newCompId(), IsCType = true }\n\n   local archetypeA = Archetype.Of({ ComponentA, ComponentB, ComponentC })\n   local archetypeB = Archetype.Of({ ComponentA, ComponentB, ComponentC, ComponentD, ComponentE })\n   local archetypeC = archetypeA:WithAll({ ComponentD, ComponentE })\n   local archetypeD = archetypeB:WithAll({ ComponentD, ComponentE })\n\n   lu.assertNotIsNil(archetypeA)\n   lu.assertNotIsNil(archetypeB)\n   lu.assertNotIsNil(archetypeC)\n   lu.assertNotIsNil(archetypeD)\n\n   lu.assertEquals(archetypeB, archetypeC)\n   lu.assertEquals(archetypeB, archetypeD)\n\n   lu.assertIsTrue(archetypeB:Has(ComponentD))\n   lu.assertIsTrue(archetypeB:Has(ComponentE))\n   lu.assertIsFalse(archetypeA:Has(ComponentD))\n   lu.assertIsFalse(archetypeA:Has(ComponentE))\nend\n\n\nfunction TestArchetype:test_Should_Create_Without_Component()\n\n   local ComponentA = { Id = newCompId(), IsCType = true }\n   local ComponentB = { Id = newCompId(), IsCType = true }\n   local ComponentC = { Id = newCompId(), }\n   local ComponentD = { Id = newCompId(), IsCType = true }\n\n   local archetypeA = Archetype.Of({ ComponentA, ComponentB, ComponentC })\n   local archetypeB = Archetype.Of({ ComponentA, ComponentB, ComponentC, ComponentD })\n   local archetypeC = archetypeB:Without(ComponentD)\n   local archetypeD = archetypeA:Without(ComponentD)\n\n   lu.assertNotIsNil(archetypeA)\n   lu.assertNotIsNil(archetypeB)\n   lu.assertNotIsNil(archetypeC)\n   lu.assertNotIsNil(archetypeD)\n\n   lu.assertEquals(archetypeA, archetypeC)\n   lu.assertEquals(archetypeA, archetypeD)\n\n   lu.assertIsTrue(archetypeB:Has(ComponentD))\n   lu.assertIsFalse(archetypeA:Has(ComponentD))\nend\n\nfunction TestArchetype:test_Should_Create_WithoutAll_Components()\n\n   local ComponentA = { Id = newCompId(), IsCType = true }\n   local ComponentB = { Id = newCompId(), IsCType = true }\n   local ComponentC = { Id = newCompId(), }\n   local ComponentD = { Id = newCompId(), IsCType = true }\n   local ComponentE = { Id = newCompId(), IsCType = true }\n\n   local archetypeA = Archetype.Of({ ComponentA, ComponentB, ComponentC })\n   local archetypeB = Archetype.Of({ ComponentA, ComponentB, ComponentC, ComponentD, ComponentE })\n   local archetypeC = archetypeB:WithoutAll({ ComponentD, ComponentE })\n   local archetypeD = archetypeA:WithoutAll({ ComponentD, ComponentE })\n\n   lu.assertNotIsNil(archetypeA)\n   lu.assertNotIsNil(archetypeB)\n   lu.assertNotIsNil(archetypeC)\n   lu.assertNotIsNil(archetypeD)\n\n   lu.assertEquals(archetypeA, archetypeC)\n   lu.assertEquals(archetypeA, archetypeD)\n\n   lu.assertIsTrue(archetypeB:Has(ComponentD))\n   lu.assertIsTrue(archetypeB:Has(ComponentE))\n   lu.assertIsFalse(archetypeA:Has(ComponentD))\n   lu.assertIsFalse(archetypeA:Has(ComponentE))\nend\n"
  },
  {
    "path": "test/test_Component.lua",
    "content": "local lu = require('luaunit')\n\nfunction sleep(a)\n   local sec = tonumber(os.clock() + a); \n   while (os.clock() < sec) do \n   end \nend\n\nlocal Component = require('Component')\n\nTestComponent = {}\n\nfunction TestComponent:test_GetType()\n   local Comp_A = Component.Create()\n   local Comp_B = Component.Create()\n   local Comp_C = Component.Create()\n\n   local comp_a = Comp_A()\n   local comp_b = Comp_B()\n   local comp_c = Comp_C()\n\n   lu.assertEquals(comp_a:GetType(), Comp_A)\n   lu.assertEquals(comp_b:GetType(), Comp_B)\n   lu.assertEquals(comp_c:GetType(), Comp_C)\nend\n\nfunction TestComponent:test_Value()\n\n   local ComponentClass = Component.Create(\"foo\")\n\n   local component1 = ComponentClass()\n   local component2 = ComponentClass(\"bar\")\n\n   lu.assertEquals(component1.value, \"foo\")\n   lu.assertEquals(component2.value, \"bar\")\nend\n\nfunction TestComponent:test_Should_UseTemplate_Table()\n\n   local ComponentClass = Component.Create({\n      Level1 = {\n         Level2 = {\n            Level3 = {\n               value = 00\n            }\n         }\n      }\n   })\n\n   local component1 = ComponentClass()\n   local component2 = ComponentClass()\n\n   lu.assertEquals(component1.Level1.Level2.Level3.value, 00)\n   lu.assertEquals(component2.Level1.Level2.Level3.value, 00)\n\n   component1.Level1.Level2.Level3.value = 11\n   component2.Level1.Level2.Level3.value = 22\n\n   lu.assertEquals(component1.Level1.Level2.Level3.value, 11)\n   lu.assertEquals(component2.Level1.Level2.Level3.value, 22)\n\n\n   lu.assertEquals(component1:GetType(), ComponentClass)\nend\n\nfunction TestComponent:test_Should_UseTemplate_Function()\n\n   local ComponentClass = Component.Create(function()\n      return {\n         Level1 = {\n            Level2 = {\n               Level3 = {\n                  value = 00\n               }\n            }\n         }\n      }\n   end)\n\n   local component1 = ComponentClass()\n   local component2 = ComponentClass()\n\n   lu.assertEquals(component1.Level1.Level2.Level3.value, 00)\n   lu.assertEquals(component2.Level1.Level2.Level3.value, 00)\n\n   component1.Level1.Level2.Level3.value = 11\n   component2.Level1.Level2.Level3.value = 22\n\n   lu.assertEquals(component1.Level1.Level2.Level3.value, 11)\n   lu.assertEquals(component2.Level1.Level2.Level3.value, 22)\nend\n\nfunction TestComponent:test_Should_CreateQualifier()\n\n   local HealthBuff = Component.Create({ Percent = 0 })\n   local HealthBuffLevel = HealthBuff.Qualifier(\"Level\")\n   local HealthBuffMission = HealthBuff.Qualifier(\"Mission\")\n   \n   -- same object\n   local HealthBuffMissionCopy1 = HealthBuff.Qualifier(\"Mission\")\n   local HealthBuffMissionCopy2 = HealthBuffMission.Qualifier(\"Mission\")\n   local HealthBuffMissionCopy3 = HealthBuff.Qualifier(HealthBuffMissionCopy1)\n   lu.assertEquals(HealthBuffMission, HealthBuffMissionCopy1)\n   lu.assertEquals(HealthBuffMission, HealthBuffMissionCopy2)\n   lu.assertEquals(HealthBuffMission, HealthBuffMissionCopy3)\n\n\n   local OtherComponent = Component.Create({ Percent = 0 })\n   lu.assertIsNil(HealthBuff.Qualifier(OtherComponent))\n   lu.assertIsNil(HealthBuff.Qualifier({}))\n\n   lu.assertItemsEquals(HealthBuff.Qualifiers(), {HealthBuff, HealthBuffLevel, HealthBuffMission})\n   lu.assertItemsEquals(HealthBuffLevel.Qualifiers(), {HealthBuff, HealthBuffLevel, HealthBuffMission})\n   lu.assertItemsEquals(HealthBuffMission.Qualifiers(), {HealthBuffLevel, HealthBuffMission, HealthBuff })\n\n   lu.assertItemsEquals(HealthBuff.Qualifiers(\"Level\", \"Mission\"), {HealthBuffLevel, HealthBuffMission})\n   lu.assertItemsEquals(HealthBuffLevel.Qualifiers(\"Primary\", \"Mission\"), {HealthBuff, HealthBuffMission})\n   lu.assertItemsEquals(HealthBuffMission.Qualifiers(\"Primary\", \"Level\"), {HealthBuff, HealthBuffLevel })\n\n   local function mergeCase(mergeFn)      \n      local buff = HealthBuff()\n      local buffLevel = HealthBuffLevel()\n      local buffMission = HealthBuffMission()\n\n      lu.assertEquals(buff:GetType(), HealthBuff)\n      lu.assertEquals(buffLevel:GetType(), HealthBuffLevel)\n      lu.assertEquals(buffMission:GetType(), HealthBuffMission)\n\n      lu.assertIsTrue(buff:Is(HealthBuff))\n      lu.assertIsTrue(buffLevel:Is(HealthBuff))\n      lu.assertIsTrue(buffLevel:Is(HealthBuffLevel))\n      lu.assertIsTrue(buffMission:Is(HealthBuff))\n      lu.assertIsTrue(buffMission:Is(HealthBuffMission))\n      \n      -- merge all\n      mergeFn(buff, buffLevel, buffMission)\n      \n      -- tem de ignorar esse merge\n      local OtherComponent = Component.Create({ value = 0 })\n      local other = OtherComponent()\n      buff:Merge(other)\n\n      -- get primary\n      lu.assertEquals(buff:Primary(), buff)\n      lu.assertEquals(buffLevel:Primary(), buff)\n      lu.assertEquals(buffMission:Primary(), buff)\n\n      -- get qualified\n      lu.assertEquals(buff:Qualified(\"Primary\"), buff)\n      lu.assertEquals(buffLevel:Qualified(\"Primary\"), buff)\n      lu.assertEquals(buffMission:Qualified(\"Primary\"), buff)\n\n      lu.assertEquals(buff:Qualified(\"Level\"), buffLevel)\n      lu.assertEquals(buffLevel:Qualified(\"Level\"), buffLevel)\n      lu.assertEquals(buffMission:Qualified(\"Level\"), buffLevel)\n\n      lu.assertEquals(buff:Qualified(\"Mission\"), buffMission)\n      lu.assertEquals(buffLevel:Qualified(\"Mission\"), buffMission)\n      lu.assertEquals(buffMission:Qualified(\"Mission\"), buffMission)\n\n      -- all\n      lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n   end\n\n   mergeCase(function(buff, buffLevel, buffMission)\n      buff:Merge(buff)\n      buff:Merge(buffLevel)\n      buff:Merge(buffMission)\n   end)\n\n   mergeCase(function(buff, buffLevel, buffMission)\n      buffLevel:Merge(buff)\n      buffMission:Merge(buffLevel)\n      buff:Merge(buffMission)\n   end)\n\n   mergeCase(function(buff, buffLevel, buffMission)\n      buffLevel:Merge(buffMission)\n      buffLevel:Merge(buff)\n   end)\n\n   mergeCase(function(buff, buffLevel, buffMission)\n      buffLevel:Merge(buff)\n      buffLevel:Merge(buffMission)\n   end)   \nend\n\n----------------------------------------------------------------------------\n-- FSM - Finite State Machine\n----------------------------------------------------------------------------\n\nfunction TestComponent:test_Should_CreateFSM()\n\n   local Movement = Component.Create({ Speed = 0 })\n\n   local MovementB = Movement.Qualifier(\"Sub\")\n\n   --  [Standing] <---> [Walking] <---> [Running]\n   Movement.States = {\n      Standing = {\"Walking\"},\n      Walking  = \"*\",\n      Running  = {\"Walking\", \"Running\"},\n      Other    = {\"Other\"}\n   }\n\n   -- ignored\n   MovementB.States = { Standing = {\"Walking\"} }\n\n   lu.assertEquals(Movement.States, {\n      Standing = {\"Walking\"},\n      Walking  = \"*\",\n      Running  = {\"Walking\"},\n      Other    = \"*\"\n   })\n\n   lu.assertEquals(MovementB.States, Movement.States)\n\n   Movement.StateInitial = \"Standing\"\n\n   local CountCall\n\n   Movement.Case = {\n      Standing = function(self, previous)\n         CountCall.Standing = CountCall.Standing + 1\n         CountCall.From[previous] = CountCall.From[previous] + 1\n      end,\n      Walking = function(self, previous)\n         CountCall.Walking = CountCall.Walking + 1\n         CountCall.From[previous] = CountCall.From[previous] + 1\n      end,\n      Running = function(self, previous)\n         CountCall.Running = CountCall.Running + 1\n         CountCall.From[previous] = CountCall.From[previous] + 1\n      end\n   }\n\n   local function resetCountCaseCall()\n      CountCall = {\n         Standing = 0,\n         Walking  = 0,\n         Running  = 0,\n         From = {\n            Standing = 0,\n            Walking  = 0,\n            Running  = 0 \n         }\n      }\n      sleep(0.1)\n   end\n\n   -- ECS.Query.All(Movement.In(\"Standing\"))\n\n   local movement = Movement()\n\n   lu.assertEquals(movement:GetState(), \"Standing\")\n   lu.assertEquals(movement:GetPrevState(), nil)\n\n   local oldStateTime = 0\n\n   resetCountCaseCall()\n   movement:SetState(\"Walking\")\n   lu.assertEquals(movement:GetState(), \"Walking\")\n   lu.assertEquals(movement:GetPrevState(), \"Standing\")\n   local newStateTime = movement:GetStateTime()\n   lu.assertNotEquals(oldStateTime, newStateTime)\n   oldStateTime = newStateTime\n\n   lu.assertEquals(CountCall.Standing, 0)\n   lu.assertEquals(CountCall.Walking, 1)\n   lu.assertEquals(CountCall.Running, 0)\n   lu.assertEquals(CountCall.From.Standing, 1)\n   lu.assertEquals(CountCall.From.Walking, 0)\n   lu.assertEquals(CountCall.From.Running, 0)\n\n   resetCountCaseCall()\n   movement:SetState(\"Running\")\n   lu.assertEquals(movement:GetState(), \"Running\")\n   lu.assertEquals(movement:GetPrevState(), \"Walking\")\n   newStateTime = movement:GetStateTime()\n   lu.assertNotEquals(oldStateTime, newStateTime)\n   oldStateTime = newStateTime\n   lu.assertEquals(CountCall.Standing, 0)\n   lu.assertEquals(CountCall.Walking, 0)\n   lu.assertEquals(CountCall.Running, 1)\n   lu.assertEquals(CountCall.From.Standing, 0)\n   lu.assertEquals(CountCall.From.Walking, 1)\n   lu.assertEquals(CountCall.From.Running, 0)\n\n   resetCountCaseCall()\n   movement:SetState(\"Walking\")\n   lu.assertEquals(movement:GetState(), \"Walking\")\n   lu.assertEquals(movement:GetPrevState(), \"Running\")\n   newStateTime = movement:GetStateTime()\n   lu.assertNotEquals(oldStateTime, newStateTime)\n   oldStateTime = newStateTime\n   lu.assertEquals(CountCall.Standing, 0)\n   lu.assertEquals(CountCall.Walking, 1)\n   lu.assertEquals(CountCall.Running, 0)\n   lu.assertEquals(CountCall.From.Standing, 0)\n   lu.assertEquals(CountCall.From.Walking, 0)\n   lu.assertEquals(CountCall.From.Running, 1)\n\n   resetCountCaseCall()\n   movement:SetState(\"Standing\")\n   lu.assertEquals(movement:GetState(), \"Standing\")\n   lu.assertEquals(movement:GetPrevState(), \"Walking\")\n   newStateTime = movement:GetStateTime()\n   lu.assertNotEquals(oldStateTime, newStateTime)\n   oldStateTime = newStateTime\n   lu.assertEquals(CountCall.Standing, 1)\n   lu.assertEquals(CountCall.Walking, 0)\n   lu.assertEquals(CountCall.Running, 0)\n   lu.assertEquals(CountCall.From.Standing, 0)\n   lu.assertEquals(CountCall.From.Walking, 1)\n   lu.assertEquals(CountCall.From.Running, 0)\n\n   resetCountCaseCall()\n   movement:SetState(\"Running\")\n   lu.assertEquals(movement:GetState(), \"Standing\")\n   lu.assertEquals(movement:GetPrevState(), \"Walking\")\n   lu.assertEquals(oldStateTime, movement:GetStateTime())\n   lu.assertEquals(CountCall.Standing, 0)\n   lu.assertEquals(CountCall.Walking, 0)\n   lu.assertEquals(CountCall.Running, 0)\n   lu.assertEquals(CountCall.From.Standing, 0)\n   lu.assertEquals(CountCall.From.Walking, 0)\n   lu.assertEquals(CountCall.From.Running, 0)\n\n   resetCountCaseCall()\n   movement:SetState(nil)\n   lu.assertEquals(movement:GetState(), \"Standing\")\n   lu.assertEquals(movement:GetPrevState(), \"Walking\")\n   lu.assertEquals(oldStateTime, movement:GetStateTime())\n   lu.assertEquals(CountCall.Standing, 0)\n   lu.assertEquals(CountCall.Walking, 0)\n   lu.assertEquals(CountCall.Running, 0)\n   lu.assertEquals(CountCall.From.Standing, 0)\n   lu.assertEquals(CountCall.From.Walking, 0)\n   lu.assertEquals(CountCall.From.Running, 0)\n\n   resetCountCaseCall()\n   movement:SetState(\"INVALID_STATE\")\n   lu.assertEquals(movement:GetState(), \"Standing\")\n   lu.assertEquals(movement:GetPrevState(), \"Walking\")\n   lu.assertEquals(oldStateTime, movement:GetStateTime())\n   lu.assertEquals(CountCall.Standing, 0)\n   lu.assertEquals(CountCall.Walking, 0)\n   lu.assertEquals(CountCall.Running, 0)\n   lu.assertEquals(CountCall.From.Standing, 0)\n   lu.assertEquals(CountCall.From.Walking, 0)\n   lu.assertEquals(CountCall.From.Running, 0)\nend\n\nfunction TestComponent:test_Should_QueryFSM_InState()\n   \n   local function execClause(clause, entity)\n      return clause.Filter(entity, clause.Config)\n   end\n\n   local Movement = Component.Create({ Speed = 0 })\n   Movement.States = {\n      Standing = \"*\",\n      Walking  = \"*\",\n      Running  = \"*\"\n   }\n   local MovementB = Movement.Qualifier(\"Specialized\")\n   lu.assertNotEquals(Movement, MovementB)\n\n   local ett_Standing = {\n      [Movement] = Movement()\n   }  \n   ett_Standing[Movement]:SetState(\"Standing\")\n\n   local ett_Standing_Walking = {\n      [Movement] = Movement(),\n      [MovementB] = MovementB(),\n   }   \n   ett_Standing_Walking[Movement]:Merge(ett_Standing_Walking[MovementB])\n   ett_Standing_Walking[Movement]:SetState(\"Standing\")\n   ett_Standing_Walking[MovementB]:SetState(\"Walking\")\n   \n   local ett_Running = {\n      [Movement] = Movement(),\n   }  \n   ett_Running[Movement]:SetState(\"Running\")\n\n   -- clause superClass\n   local clause_Walking = Movement.In(\"Walking\")\n   local clause_Running = Movement.In(\"Running\")\n   local clause_Walking_Running = Movement.In(\"Walking\", \"Running\")\n\n   lu.assertIsTrue(execClause(clause_Walking, ett_Standing_Walking))\n   lu.assertIsTrue(execClause(clause_Running, ett_Running))\n   lu.assertIsTrue(execClause(clause_Walking_Running, ett_Running))\n\n   lu.assertIsFalse(execClause(clause_Walking, ett_Standing))\n   lu.assertIsFalse(execClause(clause_Walking, ett_Running))\n   lu.assertIsFalse(execClause(clause_Walking_Running, ett_Standing))\n\n   -- clause qualifiedClass\n   local clause_b_Walking = MovementB.In(\"Walking\")\n   local clause_b_Running = MovementB.In(\"Running\")\n   local clause_b_Walking_Running = MovementB.In(\"Walking\", \"Running\")\n\n   lu.assertIsTrue(execClause(clause_b_Walking, ett_Standing_Walking))\n   lu.assertIsTrue(execClause(clause_b_Walking_Running, ett_Standing_Walking))\n\n   lu.assertIsFalse(execClause(clause_b_Running, ett_Running))\n   lu.assertIsFalse(execClause(clause_b_Walking_Running, ett_Running))\n   lu.assertIsFalse(execClause(clause_b_Walking, ett_Standing))\n   lu.assertIsFalse(execClause(clause_b_Walking, ett_Running))\n   lu.assertIsFalse(execClause(clause_b_Walking_Running, ett_Standing))\n\n   -- clause all\n   lu.assertEquals(Movement.In(), {})\n   lu.assertEquals(MovementB.In(), {})\n\n   -- ignores OtherComponent\n   local OtherComponent = Component.Create({ Percent = 0 })\n   OtherComponent.States = {\n      Standing = \"*\",\n      Walking  = \"*\",\n      Running  = \"*\"\n   }\n   local ett_other_co = {\n      [OtherComponent] = OtherComponent(),\n   }  \n   ett_other_co[OtherComponent]:SetState(\"Walking\")\n   lu.assertIsFalse(execClause(clause_b_Walking, ett_other_co))\n   lu.assertIsFalse(execClause(clause_b_Walking_Running, ett_other_co))\nend\n\n-- local Movement = ECS.Component({ Speed = 0 })\n-- Movement.States = {\n--    Standing = \"*\",\n--    Walking  = {\"Standing\", \"Running\"},\n--    Running  = {\"Walking\"}\n-- }\n\n-- Movement.Case = {\n--    Standing = function(self, previous)\n--       self.Speed = 0\n--    end,\n--    Walking = function(self, previous)\n--       self.Speed = 5\n--    end,\n--    Running = function(self, previous)\n--       self.Speed = 10\n--    end\n-- }\n\n-- ECS.Query.All(Movement.In(\"Standing\"))\n\n-- local movement = entity[Movement]\n-- movement:GetState() -> \"Running\"\n-- movement:SetState(\"Walking\")\n-- movement:GetPrevState()\n-- movement:GetStateTime()\n\n-- if movement:GetState() == \"Standing\" then\n--    movement.Speed = 0\n-- end\n"
  },
  {
    "path": "test/test_Entity.lua",
    "content": "local lu = require('luaunit')\n\nlocal Event = require('Event')\nlocal Entity = require('Entity')\nlocal Component = require('Component')\nlocal Archetype = require('Archetype')\n\n\nTestEntity = {}\n\nlocal Comp_A = Component.Create({ Name = 'a' })\nComp_A.Name = \"A\"\n\nlocal Comp_B = Component.Create({ Name = 'b' })\nComp_B.Name = \"B\"\n\nlocal Comp_C = Component.Create({ Name = 'c' })\nComp_C.Name = \"C\"\n\nlocal comp_a = Comp_A()\nlocal comp_b = Comp_B()\nlocal comp_c = Comp_C()\n\nlocal archetype_A = Archetype.Of({ Comp_A })\nlocal archetype_B = Archetype.Of({ Comp_B })\nlocal archetype_C = Archetype.Of({ Comp_C })\nlocal archetype_A_B = archetype_A:With(Comp_B)\nlocal archetype_A_C = archetype_A:With(Comp_C)\nlocal archetype_B_C = archetype_B:With(Comp_C)\nlocal archetype_A_B_C = archetype_A_B:With(Comp_C)\n\nfunction TestEntity:test_Constructor()\n   lu.assertEquals(Entity.New(nil).archetype, Archetype.EMPTY)\n   lu.assertEquals(Entity.New(nil, {comp_a}).archetype, archetype_A)\n   lu.assertEquals(Entity.New(nil, {comp_b}).archetype, archetype_B)\n   lu.assertEquals(Entity.New(nil, {comp_c}).archetype, archetype_C)\n   lu.assertEquals(Entity.New(nil, {comp_a, comp_b}).archetype, archetype_A_B)\n   lu.assertEquals(Entity.New(nil, {comp_a, comp_c}).archetype, archetype_A_C)\n   lu.assertEquals(Entity.New(nil, {comp_b, comp_c}).archetype, archetype_B_C)\n   lu.assertEquals(Entity.New(nil, {comp_a, comp_b, comp_c}).archetype, archetype_A_B_C)\nend\n\n--[[\n   [GET]\n   01) comp1 = entity[CompType1]\n   02) comp1 = entity:Get(CompType1)\n   03) comps = entity[{CompType1, CompType2, ...}]\n   04) comps = entity:Get({CompType1, CompType2, ...})\n]]\nfunction TestEntity:test_Get()\n\n   local Object = {}\n\n   local entity = Entity.New(nil, {comp_a, comp_b, comp_c})\n\n   -- 01) comp1 = entity[CompType1]\n   lu.assertEquals(entity[Comp_A], comp_a)\n   lu.assertEquals(entity[Comp_B], comp_b)\n   lu.assertEquals(entity[Comp_C], comp_c)\n\n   -- 02) comp1 = entity:Get(CompType1)\n   lu.assertEquals(entity:Get(Comp_A), comp_a)\n   lu.assertEquals(entity:Get(Comp_B), comp_b)\n   lu.assertEquals(entity:Get(Comp_C), comp_c)\n\n   -- 03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)\n   lu.assertEquals(entity:Get({}), nil)\n   lu.assertEquals(entity:Get(Object, \"XPTO\"), nil)\n   lu.assertEquals({entity:Get(Comp_A)}, {comp_a})\n   lu.assertEquals({entity:Get(Comp_B)}, {comp_b})\n   lu.assertEquals({entity:Get(Comp_C)}, {comp_c})\n   lu.assertEquals({entity:Get(Comp_A, Comp_B)}, {comp_a, comp_b})\n   lu.assertEquals({entity:Get(Comp_A, Comp_C)}, {comp_a, comp_c})\n   lu.assertEquals({entity:Get(Comp_B, Comp_C)}, {comp_b, comp_c})\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n   lu.assertEquals({entity:Get(Comp_A, Comp_C, Comp_B)}, {comp_a, comp_c, comp_b})\n   lu.assertEquals({entity:Get(Comp_A, Comp_C, Comp_B)}, {comp_a, comp_c, comp_b})\n   lu.assertEquals({entity:Get(Comp_A, Comp_C, Comp_B, Object)}, {comp_a, comp_c, comp_b})\n   lu.assertEquals({entity:Get(Comp_A, Comp_C, Comp_B, \"XPTO\")}, {comp_a, comp_c, comp_b})\nend\n\n--[[\n   [UNSET]\n   01) enity:Unset(comp1)\n   02) entity[CompType1] = nil\n   03) enity:Unset(CompType1)\n   04) enity:Unset(comp1, comp1, ...)\n   05) enity:Unset(CompType1, CompType2, ...)\n]]\nfunction TestEntity:test_Unset()\n\n   local Object = {}   \n   local event = Event.New()\n\n   local eventEntity = nil\n   local eventArchetypeOld = nil\n   event:Connect(function(entity, old)\n      eventEntity = entity\n      eventArchetypeOld = old\n   end)\n   \n   -- 01) enity:Unset(comp1)\n   local entity = Entity.New(event, {comp_a, comp_b, comp_c})\n   entity:Unset(Object)\n   lu.assertEquals(eventEntity, nil)\n   lu.assertEquals(eventArchetypeOld, nil)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity:Unset(comp_a)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B_C)\n   lu.assertEquals(entity.archetype, archetype_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_b, comp_c})\n\n   entity:Unset(comp_b)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_B_C)\n   lu.assertEquals(entity.archetype, archetype_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_c})\n\n   entity:Unset(comp_c)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_C)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   -- 02) entity[CompType1] = nil\n   entity = Entity.New(event, {comp_a, comp_b, comp_c})\n   entity[Object] = nil\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity[Comp_A] = nil\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B_C)\n   lu.assertEquals(entity.archetype, archetype_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_b, comp_c})\n\n   entity[Comp_B] = nil\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_B_C)\n   lu.assertEquals(entity.archetype, archetype_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_c})\n\n   entity[Comp_C] = nil\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_C)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   -- 03) enity:Unset(CompType1)\n   entity = Entity.New(event, {comp_a, comp_b, comp_c})\n   entity:Unset(Object)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity:Unset(Comp_A)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B_C)\n   lu.assertEquals(entity.archetype, archetype_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_b, comp_c})\n\n   entity:Unset(Comp_B)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_B_C)\n   lu.assertEquals(entity.archetype, archetype_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_c})\n\n   entity:Unset(Comp_C)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_C)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   -- 04) enity:Unset(comp1, comp1, ...)\n   entity = Entity.New(event, {comp_a, comp_b, comp_c})\n   entity:Unset(Object)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity:Unset(comp_a)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B_C)\n   lu.assertEquals(entity.archetype, archetype_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_b, comp_c})\n\n   entity:Unset(comp_b)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_B_C)\n   lu.assertEquals(entity.archetype, archetype_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_c})\n\n   entity:Unset(comp_c)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_C)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   -- 05) enity:Unset(CompType1, CompType2, ...)\n   entity = Entity.New(event, {comp_a, comp_b, comp_c})\n   entity:Unset(Comp_A)\n   lu.assertEquals(entity.archetype, archetype_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_b, comp_c})\n\n   entity:Unset(Comp_B)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_B_C)\n   lu.assertEquals(entity.archetype, archetype_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_c})\n\n   entity[Comp_C] = nil\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_C)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\nend\n\n--[[\n   [SET]\n   01) entity[CompType1] = nil\n   02) entity[CompType1] = value\n   03) entity:Set(CompType1, nil)   \n   04) entity:Set(CompType1, value)\n   05) entity:Set(comp1)\n   06) entity:Set(comp1, comp2, ...)\n]]\nfunction TestEntity:test_Set()\n\n   local Object = {}\n\n   local event = Event.New()\n\n   local eventEntity = nil\n   local eventArchetypeOld = nil\n   event:Connect(function(entity, old)\n      eventEntity = entity\n      eventArchetypeOld = old\n   end)\n   \n   --  05) entity:Set(comp1)\n   local entity = Entity.New(event)\n   entity:Set(Object)\n   lu.assertEquals(eventEntity, nil)\n   lu.assertEquals(eventArchetypeOld, nil)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   entity:Set(comp_a)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, Archetype.EMPTY)\n   lu.assertEquals(entity.archetype, archetype_A)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a})\n\n   entity:Set(comp_b)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A)\n   lu.assertEquals(entity.archetype, archetype_A_B)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b})\n\n   entity:Set(comp_c)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   -- 01) entity[CompType1] = nil\n   -- @see TestEntity:test_Unset()\n\n   -- 03) entity:Set(CompType1, nil)   \n   entity = Entity.New(event, {comp_a, comp_b, comp_c})\n   entity:Set(Object, nil)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity:Set(Comp_A, nil)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B_C)\n   lu.assertEquals(entity.archetype, archetype_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_b, comp_c})\n\n   entity:Set(Comp_B, nil)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_B_C)\n   lu.assertEquals(entity.archetype, archetype_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_c})\n\n   entity:Set(Comp_C, nil)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_C)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   -- 02) entity[CompType1] = value\n   entity = Entity.New(event)\n   entity[Object] = \"XPTO\"\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   entity[Comp_A] = comp_a\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, Archetype.EMPTY)\n   lu.assertEquals(entity.archetype, archetype_A)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a})\n\n   entity[Comp_B] = comp_b\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A)\n   lu.assertEquals(entity.archetype, archetype_A_B)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b})\n\n   entity[Comp_C] = comp_c\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity[Comp_C] = { Name = 'NEW C' }\n   lu.assertEquals({entity:Get(Comp_A, Comp_B)}, {comp_a, comp_b})\n   lu.assertEquals(entity[Comp_C]:GetType(), Comp_C)\n   lu.assertEquals(entity[Comp_C].Name, 'NEW C')\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   -- NO EVENT CALL (SAME ARCHETYPE archetype_A_B_C)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B)\n\n   -- 04) entity:Set(CompType1, value)\n   entity = Entity.New(event)\n   entity:Set(Object, \"XPTO\")\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   entity:Set(Comp_A, comp_a)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, Archetype.EMPTY)\n   lu.assertEquals(entity.archetype, archetype_A)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a})\n\n   entity:Set(Comp_B, comp_b)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A)\n   lu.assertEquals(entity.archetype, archetype_A_B)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b})\n\n   entity:Set(Comp_C, comp_c)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\n\n   entity:Set(Comp_C, { Name = 'NEW C' })\n   lu.assertEquals({entity:Get(Comp_A, Comp_B)}, {comp_a, comp_b})\n   lu.assertEquals(entity[Comp_C]:GetType(), Comp_C)\n   lu.assertEquals(entity[Comp_C].Name, 'NEW C')\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   -- NO EVENT CALL (SAME ARCHETYPE archetype_A_B_C)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A_B)\n\n   -- 06) entity:Set(comp1, comp2, ...)\n   entity = Entity.New(event)\n   entity:Set(Object)\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {})\n\n   entity:Set(comp_a)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, Archetype.EMPTY)\n   lu.assertEquals(entity.archetype, archetype_A)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a})\n\n   entity:Set(comp_b, comp_c)\n   lu.assertEquals(eventEntity, entity)\n   lu.assertEquals(eventArchetypeOld, archetype_A)\n   lu.assertEquals(entity.archetype, archetype_A_B_C)\n   lu.assertEquals({entity:Get(Comp_A, Comp_B, Comp_C)}, {comp_a, comp_b, comp_c})\nend\n\nfunction TestEntity:test_RawSet()\n   local Object = {}\n\n   local event = Event.New()\n   \n   --  05) entity:Set(comp1)\n   local entity = Entity.New(event)\n   entity.Name = \"Player\"\n   entity[\"NetworkId\"] = 33\n   entity[Object] = \"XPTO\"\n\n   lu.assertEquals(entity.Name, \"Player\")\n   lu.assertEquals(entity.NetworkId, 33)\n   lu.assertEquals(entity[Object], \"XPTO\")\n   lu.assertEquals(entity.archetype, Archetype.EMPTY)\nend\n\nfunction TestEntity:test_Qualifiers()\n\n   local event = Event.New()\n\n   local HealthBuff = Component.Create({ percent = 0 })\n   local HealthBuffLevel = HealthBuff.Qualifier(\"Level\")\n   local HealthBuffMission = HealthBuff.Qualifier(\"Mission\")\n   local OtherComp = Component.Create({ value = 0 })\n\n\n   local function doTest(instanciate)\n\n      local entity = Entity.New(event)\n      \n      local buff, buffLevel, buffMission, other = instanciate(entity)\n\n      lu.assertItemsEquals(entity:GetAll(HealthBuff), {buff, buffLevel, buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffLevel), {buff, buffLevel, buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffMission), {buff, buffLevel, buffMission})\n      lu.assertItemsEquals(entity:GetAll(OtherComp), {other})\n      lu.assertItemsEquals(entity:GetAll(), {other, buff, buffLevel, buffMission})\n\n\n      -- get primary\n      lu.assertEquals(buff:Primary(), buff)\n      lu.assertEquals(buffLevel:Primary(), buff)\n      lu.assertEquals(buffMission:Primary(), buff)\n\n      -- get qualified\n      lu.assertEquals(buff:Qualified(\"Primary\"), buff)\n      lu.assertEquals(buffLevel:Qualified(\"Primary\"), buff)\n      lu.assertEquals(buffMission:Qualified(\"Primary\"), buff)\n\n      lu.assertEquals(buff:Qualified(\"Level\"), buffLevel)\n      lu.assertEquals(buffLevel:Qualified(\"Level\"), buffLevel)\n      lu.assertEquals(buffMission:Qualified(\"Level\"), buffLevel)\n\n      lu.assertEquals(buff:Qualified(\"Mission\"), buffMission)\n      lu.assertEquals(buffLevel:Qualified(\"Mission\"), buffMission)\n      lu.assertEquals(buffMission:Qualified(\"Mission\"), buffMission)\n\n      -- all\n      lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      \n      -- unset\n      entity[HealthBuffLevel] = nil\n      lu.assertItemsEquals(entity:GetAll(HealthBuff), {buff, buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffLevel), {buff, buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffMission), {buff, buffMission})\n      lu.assertItemsEquals(entity:GetAll(OtherComp), {other})\n      lu.assertItemsEquals(entity:GetAll(), {other, buff, buffMission})\n      lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Level\"] = buffLevel\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Mission\"] = buffMission\n      })\n\n      -- unset\n      entity:Unset(HealthBuff)\n      lu.assertItemsEquals(entity:GetAll(HealthBuff), {buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffLevel), {buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffMission), {buffMission})\n      lu.assertItemsEquals(entity:GetAll(OtherComp), {other})\n      lu.assertItemsEquals(entity:GetAll(), {other, buffMission})      \n      lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Level\"] = buffLevel\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Mission\"] = buffMission\n      })\n\n      -- again \n      local buff, buffLevel, buffMission, other = instanciate(entity)\n       -- all\n       lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Level\"] = buffLevel, [\"Mission\"] = buffMission\n      })\n      \n      -- unset\n      entity[HealthBuffLevel] = nil\n      lu.assertItemsEquals(entity:GetAll(HealthBuff), {buff, buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffLevel), {buff, buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffMission), {buff, buffMission})\n      lu.assertItemsEquals(entity:GetAll(OtherComp), {other})\n      lu.assertItemsEquals(entity:GetAll(), {other, buff, buffMission})\n      lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Mission\"] = buffMission\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Level\"] = buffLevel\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Primary\"] = buff, [\"Mission\"] = buffMission\n      })\n\n      -- unset\n      entity:Unset(HealthBuff)\n      lu.assertItemsEquals(entity:GetAll(HealthBuff), {buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffLevel), {buffMission})\n      lu.assertItemsEquals(entity:GetAll(HealthBuffMission), {buffMission})\n      lu.assertItemsEquals(entity:GetAll(OtherComp), {other})\n      lu.assertItemsEquals(entity:GetAll(), {other, buffMission})      \n      lu.assertEquals(buff:QualifiedAll(), {\n         [\"Primary\"] = buff\n      })\n      lu.assertEquals(buffLevel:QualifiedAll(), {\n         [\"Level\"] = buffLevel\n      })\n      lu.assertEquals(buffMission:QualifiedAll(), {\n         [\"Mission\"] = buffMission\n      })\n   end\n\n   doTest(function(entity)\n      local buff = HealthBuff()\n      local buffLevel = HealthBuffLevel()\n      local buffMission = HealthBuffMission()\n      local other = OtherComp()\n\n      entity:Set(buff)\n      entity:Set(buffLevel)\n      entity:Set(buffMission)\n      entity:Set(other)\n\n      return buff, buffLevel, buffMission, other\n   end)\n\n   doTest(function(entity)\n      local buff = HealthBuff()\n      local buffLevel = HealthBuffLevel()\n      local buffMission = HealthBuffMission()\n      local other = OtherComp()\n\n      entity[HealthBuff] = buff\n      entity[HealthBuffLevel] = buffLevel\n      entity[HealthBuffMission] = buffMission\n      entity[OtherComp] = other\n\n      return buff, buffLevel, buffMission, other\n   end)\n\n   doTest(function(entity)\n\n      entity[HealthBuff] = {}\n      entity[HealthBuffLevel] = {}\n      entity[HealthBuffMission] = {}\n      entity[OtherComp] = {}\n\n      return entity[HealthBuff], entity[HealthBuffLevel] , entity[HealthBuffMission], entity[OtherComp]\n   end)\n   \nend\n"
  },
  {
    "path": "test/test_EntityRepository.lua",
    "content": "local lu = require('luaunit')\n\nlocal EntityRepository = require('EntityRepository')\n\nlocal function query(...)\n   local archetypes = {...}\n   local Query = {}\n   function Query:Match(archetype)\n      return table.find(archetypes, archetype) ~= nil\n   end\n   function Query:Result(chunks)\n      return chunks\n   end\n   return Query\nend\n\nlocal function result(...)\n   -- { ARCHETYPE_STORAGE<{[ENTITY]=true}>, ... }\n   -- { { [ENTITY]=true } }\n   local result = {}\n   local archetypes = {}\n   for i, entity in ipairs({...}) do\n      if archetypes[entity.archetype] == nil then\n         archetypes[entity.archetype] = {}\n         table.insert(result, archetypes[entity.archetype])\n      end\n      archetypes[entity.archetype][entity] = true\n   end\n   return result\nend\n\nTestEntityRepository = {}\n\nfunction TestEntityRepository:test_InsertRemoveUpdateQuery()\n\n   local repo = EntityRepository.New()\n\n   local ett_Foo_1 = { archetype = 'foo' }\n   local ett_Foo_2 = { archetype = 'foo' }\n   local ett_Bar_1 = { archetype = 'bar' }\n   local ett_Bar_2 = { archetype = 'bar' }\n   local ett_Baz_1 = { archetype = 'baz' }\n   local ett_Baz_2 = { archetype = 'baz' }\n\n   local q_Foo = query('foo')\n   local q_Bar = query('bar')\n   local q_Baz = query('baz')\n   local q_Foo_Bar = query('foo', 'bar')\n   local q_Foo_Baz = query('foo', 'baz')\n   local q_Bar_Baz = query('bar', 'baz')\n   local q_Foo_Bar_Baz = query('foo', 'bar', 'baz')\n\n   repo:Insert(ett_Foo_1)\n   repo:Insert(ett_Bar_1)\n   repo:Insert(ett_Bar_2)\n   repo:Insert(ett_Baz_1)\n   lu.assertItemsEquals(repo:Query(q_Foo), result(ett_Foo_1))\n   lu.assertItemsEquals(repo:Query(q_Bar), result(ett_Bar_1, ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Baz), result(ett_Baz_1))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar), result(ett_Foo_1, ett_Bar_1, ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Baz), result(ett_Foo_1, ett_Baz_1))\n   lu.assertItemsEquals(repo:Query(q_Bar_Baz), result(ett_Bar_1, ett_Bar_2, ett_Baz_1))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar_Baz), result(ett_Foo_1, ett_Bar_1, ett_Bar_2, ett_Baz_1))\n\n   repo:Insert(ett_Foo_1)\n   repo:Insert(ett_Foo_2)\n   repo:Insert(ett_Bar_1)\n   repo:Insert(ett_Bar_2)\n   repo:Insert(ett_Baz_1)\n   repo:Insert(ett_Baz_2)\n   lu.assertItemsEquals(repo:Query(q_Foo), result(ett_Foo_1, ett_Foo_2))\n   lu.assertItemsEquals(repo:Query(q_Bar), result(ett_Bar_1, ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Baz), result(ett_Baz_1, ett_Baz_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar), result(ett_Foo_1, ett_Foo_2, ett_Bar_1, ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Baz), result(ett_Foo_1, ett_Foo_2, ett_Baz_1, ett_Baz_2))\n   lu.assertItemsEquals(repo:Query(q_Bar_Baz), result(ett_Bar_1, ett_Bar_2, ett_Baz_1, ett_Baz_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar_Baz), result(ett_Foo_1, ett_Foo_2, ett_Bar_1, ett_Bar_2, ett_Baz_1, ett_Baz_2))\n\n   repo:Remove(ett_Foo_1)\n   repo:Remove(ett_Bar_1)\n   repo:Remove(ett_Baz_1)\n   repo:Remove(ett_Baz_2)\n   repo:Remove('XPTO')\n   lu.assertItemsEquals(repo:Query(q_Foo), result(ett_Foo_2))\n   lu.assertItemsEquals(repo:Query(q_Bar), result(ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Baz), result())\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar), result(ett_Foo_2, ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Baz), result(ett_Foo_2))\n   lu.assertItemsEquals(repo:Query(q_Bar_Baz), result(ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar_Baz), result(ett_Foo_2, ett_Bar_2))\n\n   -- update\n   ett_Foo_2.archetype = 'bar'\n   ett_Bar_2.archetype = 'baz'\n   repo:Insert(ett_Foo_2)\n   repo:Update(ett_Bar_2)\n   lu.assertItemsEquals(repo:Query(q_Foo), result())\n   lu.assertItemsEquals(repo:Query(q_Bar), result(ett_Foo_2))\n   lu.assertItemsEquals(repo:Query(q_Baz), result(ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar), result(ett_Foo_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Baz), result(ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Bar_Baz), result(ett_Foo_2, ett_Bar_2))\n   lu.assertItemsEquals(repo:Query(q_Foo_Bar_Baz), result(ett_Foo_2, ett_Bar_2))\nend\n"
  },
  {
    "path": "test/test_Event.lua",
    "content": "local lu = require('luaunit')\n\nlocal Event = require('Event')\n\nTestEvent = {}\n\nfunction TestEvent:test_ConnectFireDisconnectDestroy()\n\n   local event = Event.New()\n\n   local Object1 = {}\n   local Object2 = {Object1}\n\n   local Calls = { [1] = 0, [2] = 0 }\n\n   local conn1 = event:Connect(function(obj1, obj2)\n      lu.assertEquals(obj1, Object1)\n      lu.assertEquals(obj2, Object2)\n      Calls[1] = Calls[1] + 1\n   end)\n\n   local conn2 = event:Connect(function(obj1, obj2)\n      lu.assertIsTrue(obj1 == Object1)\n      lu.assertIsTrue(obj2 == Object2)\n      Calls[2] = Calls[2] + 1\n   end)   \n\n   event:Fire(Object1, Object2)\n   lu.assertEquals(Calls[1], 1)\n   lu.assertEquals(Calls[2], 1)\n\n\n   -- Disconnect\n   conn1:Disconnect()\n   event:Fire(Object1, Object2)\n   lu.assertEquals(Calls[1], 1)\n   lu.assertEquals(Calls[2], 2)\n\n   lu.assertError(function()\n      event:Connect(\"INVALID\")   \n   end)\n\n   event:Destroy()\n   conn2:Disconnect()\nend\n"
  },
  {
    "path": "test/test_Query.lua",
    "content": "local lu = require('luaunit')\n\nlocal Query = require('Query')\nlocal Archetype = require('Archetype')\nlocal Component = require('Component')\n\nlocal Comp_A = Component.Create()\nlocal Comp_B = Component.Create()\nlocal Comp_B_QL = Comp_B.Qualifier(\"Specialized\")\nlocal Comp_FSM = Component.Create()\nlocal Comp_FSM_QL = Comp_FSM.Qualifier(\"Specialized\")\n\nComp_FSM.States = { Standing = \"*\", Walking  = \"*\", Running  = \"*\" }\n\nlocal archetype_A = Archetype.Of({Comp_A})\nlocal archetype_B = Archetype.Of({Comp_B})\nlocal archetype_A_B = Archetype.Of({Comp_A, Comp_B})\nlocal archetype_B_QL = Archetype.Of({Comp_B_QL})\nlocal archetype_FSM = Archetype.Of({Comp_FSM})\nlocal archetype_FSM_QL = Archetype.Of({Comp_FSM_QL})\n\nTestQuery = {}\n\nfunction TestQuery:test_Match()\n   \n   -- all\n   local all_A = Query({ Comp_A })\n   local all_A_B = Query({ Comp_A, Comp_B })\n   local all_B_QL = Query.All(Comp_B_QL).Build()\n   lu.assertIsTrue(all_A:Match(archetype_A))\n   lu.assertIsTrue(all_A_B:Match(archetype_A_B))\n   lu.assertIsTrue(all_B_QL:Match(archetype_B_QL))\n   lu.assertIsFalse(all_A_B:Match(archetype_FSM))\n   -- all (cache result)\n   lu.assertIsTrue(all_A:Match(archetype_A))\n   lu.assertIsTrue(all_A_B:Match(archetype_A_B))\n   lu.assertIsTrue(all_B_QL:Match(archetype_B_QL))\n   lu.assertIsFalse(all_A_B:Match(archetype_FSM))\n\n   -- any\n   local any_A = Query(nil, { Comp_A })\n   local any_B = Query(nil, { Comp_B })\n   local any_A_B = Query.Any(Comp_A, Comp_B).Build()\n   lu.assertIsTrue(any_A:Match(archetype_A_B))\n   lu.assertIsTrue(any_B:Match(archetype_A_B))\n   lu.assertIsTrue(any_A_B:Match(archetype_A_B))\n   lu.assertIsFalse(any_A_B:Match(archetype_FSM))\n   -- any (cache result)\n   lu.assertIsTrue(any_A:Match(archetype_A_B))\n   lu.assertIsTrue(any_B:Match(archetype_A_B))\n   lu.assertIsTrue(any_A_B:Match(archetype_A_B))\n   lu.assertIsFalse(any_A_B:Match(archetype_FSM))\n\n   -- none\n   local none_A = Query(nil, nil, { Comp_A })\n   local none_B = Query.None(Comp_B).Build()\n   lu.assertIsFalse(none_A:Match(archetype_A_B))\n   lu.assertIsFalse(none_B:Match(archetype_A_B))\n   -- none (cache result)\n   lu.assertIsFalse(none_A:Match(archetype_A_B))\n   lu.assertIsFalse(none_B:Match(archetype_A_B))\n\n   -- clause\n   local all_A = Query({ Comp_FSM.In(\"Standing\") })\n   lu.assertIsTrue(all_A:Match(archetype_FSM))\n   lu.assertIsTrue(all_A:Match(archetype_FSM_QL))\n\n\n   -- result\n   lu.assertNotIsNil(all_A:Result({}))\nend\n"
  },
  {
    "path": "test/test_QueryResult.lua",
    "content": "local lu = require('luaunit')\n\nlocal Entity = require('Entity')\nlocal Archetype = require('Archetype')\nlocal Component = require('Component')\nlocal QueryResult = require('QueryResult')\n\n\nlocal Comp_A = Component.Create()\nlocal Comp_B = Component.Create()\nlocal Comp_B_Ql = Comp_B.Qualifier(\"Specialized\")\nlocal Comp_FSM = Component.Create()\nlocal Comp_FSM_2 = Component.Create()\nlocal Comp_FSM_2_Ql = Comp_FSM_2.Qualifier(\"Specialized\")\nlocal Comp_Other = Component.Create()\n\nComp_FSM.States = { Standing = \"*\", Walking  = \"*\", Running  = \"*\" }\nComp_FSM_2.States = { Standing = \"*\", Walking  = \"*\", Running  = \"*\" }\n\nlocal comp_a = Comp_A()\nlocal comp_b = Comp_B()\nlocal comp_b_ql = Comp_B_Ql()\nlocal comp_fsm_Standing = Comp_FSM()\nlocal comp_fsm_Walking = Comp_FSM()\nlocal comp_fsm_Running = Comp_FSM()\nlocal comp_fsm_2_Standing = Comp_FSM_2()\nlocal comp_fsm_2_Walking = Comp_FSM_2()\nlocal comp_fsm_2_Running = Comp_FSM_2()\nlocal comp_fsm_2_ql_Standing = Comp_FSM_2_Ql()\nlocal comp_fsm_2_ql_Walking = Comp_FSM_2_Ql()\nlocal comp_fsm_2_ql_Running = Comp_FSM_2_Ql()\n\ncomp_fsm_Standing:SetState(\"Standing\")\ncomp_fsm_Walking:SetState(\"Walking\")\ncomp_fsm_Running:SetState(\"Running\")\ncomp_fsm_2_Standing:SetState(\"Standing\")\ncomp_fsm_2_Walking:SetState(\"Walking\")\ncomp_fsm_2_Running:SetState(\"Running\")\ncomp_fsm_2_ql_Standing:SetState(\"Standing\")\ncomp_fsm_2_ql_Walking:SetState(\"Walking\")\ncomp_fsm_2_ql_Running:SetState(\"Running\")\n\nlocal entity_A = Entity.New(nil, {comp_a})\nlocal entity_B = Entity.New(nil, {comp_b})\nlocal entity_B_QL = Entity.New(nil, {comp_b_ql})\nlocal entity_FSM_Standing = Entity.New(nil, {comp_fsm_Standing})\nlocal entity_FSM_Walking = Entity.New(nil, {comp_fsm_Walking})\nlocal entity_FSM_Running = Entity.New(nil, {comp_fsm_Running})\nlocal entity_FSM_2_Standing = Entity.New(nil, {comp_fsm_2_Standing})\nlocal entity_FSM_2_Walking = Entity.New(nil, {comp_fsm_2_Walking})\nlocal entity_FSM_2_Running = Entity.New(nil, {comp_fsm_2_Running})\nlocal entity_FSM_2_ql_Standing = Entity.New(nil, {comp_fsm_2_ql_Standing})\nlocal entity_FSM_2_ql_Walking = Entity.New(nil, {comp_fsm_2_ql_Walking})\nlocal entity_FSM_2_ql_Running = Entity.New(nil, {comp_fsm_2_ql_Running})\n\n-- TO DEBUG\nentity_A.Name = \"entity_A\"\nentity_B.Name = \"entity_B\"\nentity_B_QL.Name = \"entity_B_QL\"\nentity_FSM_Standing.Name = \"entity_FSM_Standing\"\nentity_FSM_Walking.Name = \"entity_FSM_Walking\"\nentity_FSM_Running.Name = \"entity_FSM_Running\"\nentity_FSM_2_Standing.Name = \"entity_FSM_2_Standing\"\nentity_FSM_2_Walking.Name = \"entity_FSM_2_Walking\"\nentity_FSM_2_Running.Name = \"entity_FSM_2_Running\"\nentity_FSM_2_ql_Standing.Name = \"entity_FSM_2_ql_Standing\"\nentity_FSM_2_ql_Walking.Name = \"entity_FSM_2_ql_Walking\"\nentity_FSM_2_ql_Running.Name = \"entity_FSM_2_ql_Running\"\n\n-- { ARCHETYPE_STORAGE<{[ENTITY]=true}>, ... }\nlocal chunks = {\n   { [entity_A] = true },\n   { [entity_B] = true },\n   { [entity_B_QL] = true },\n   { [entity_FSM_Standing] = true, [entity_FSM_Walking] = true, [entity_FSM_Running] = true },\n   { [entity_FSM_2_Standing] = true, [entity_FSM_2_Walking] = true, [entity_FSM_2_Running] = true },\n   { [entity_FSM_2_ql_Standing] = true, [entity_FSM_2_ql_Walking] = true, [entity_FSM_2_ql_Running] = true }\n}\n\nTestQueryResult = {}\n\nfunction TestQueryResult:test_ToArray()\n   local result = QueryResult.New(chunks)\n\n   local array = result:ToArray()\n   lu.assertItemsEquals(array, {\n      entity_A,\n      entity_B,\n      entity_B_QL,\n      entity_FSM_Standing,\n      entity_FSM_Walking,\n      entity_FSM_Running,\n      entity_FSM_2_Standing,\n      entity_FSM_2_Walking,\n      entity_FSM_2_Running,\n      entity_FSM_2_ql_Standing,\n      entity_FSM_2_ql_Walking,\n      entity_FSM_2_ql_Running\n   })\nend\n\nfunction TestQueryResult:test_Iterator()\n   local result = QueryResult.New(chunks)\n\n   local entities = {}\n   local indexes = {}\n\n   for count, entity in result:Iterator() do\n      table.insert(entities, entity)\n      table.insert(indexes, count)\n   end\n\n   lu.assertEquals(indexes, {1,2,3,4,5,6,7,8,9,10,11,12})\n   lu.assertItemsEquals(entities, {\n      entity_A,\n      entity_B,\n      entity_B_QL,\n      entity_FSM_Standing,\n      entity_FSM_Walking,\n      entity_FSM_Running,\n      entity_FSM_2_Standing,\n      entity_FSM_2_Walking,\n      entity_FSM_2_Running,\n      entity_FSM_2_ql_Standing,\n      entity_FSM_2_ql_Walking,\n      entity_FSM_2_ql_Running\n   })\nend\n\nlocal function spy(method, callback)\n   return function(...)\n      return callback(method, table.unpack({...}))\n   end   \nend\n\nfunction TestQueryResult:test_AnyMatch_AllMatch_FindAny()\n   local result = QueryResult.New(chunks)\n\n   lu.assertIsTrue(result:AnyMatch(function(entity)\n      return entity.archetype == entity_FSM_Standing.archetype\n   end))\n\n   local count = 0\n   lu.assertIsFalse(result:AnyMatch(function(entity)\n      count = count + 1\n      return entity.archetype == Archetype.EMPTY\n   end))\n   lu.assertEquals(count, 12)\n\n\n   lu.assertIsFalse(result:AllMatch(function(entity)\n      return entity.archetype == entity_FSM_Standing.archetype\n   end))\n\n   -- short-circuiting terminal\n   local count = 0\n   lu.assertIsFalse(result:AllMatch(function(entity)\n      count = count + 1\n      return entity.archetype == Archetype.EMPTY\n   end))\n   lu.assertEquals(count, 1)\n\n   -- short-circuiting terminal\n   local count = 0\n   result.ForEach = spy(result.ForEach, function(forEachOriginal, result, callback)\n      return forEachOriginal(result, function(value)\n         count = count + 1\n         return callback(value, count)\n      end)\n   end)\n   \n   lu.assertNotIsNil(result:FindAny())\n   lu.assertEquals(count, 1)\nend\n\n\n\nfunction TestQueryResult:test_ForEach()\n   local result = QueryResult.New(chunks)\n\n   local count = 0\n   result:ForEach(function(entity)\n      count = count + 1\n   end)\n   lu.assertEquals(count, 12)\n\n   -- break\n   count = 0\n   result:ForEach(function(entity)\n      count = count + 1\n      if count == 5 then\n         return true\n      end\n   end)\n   lu.assertEquals(count, 5)   \nend\n\nfunction TestQueryResult:test_Filter_Map_Limit()\n   local result = QueryResult.New(chunks)\n\n   lu.assertIsFalse(\n      result\n         :Filter(function(entity)\n            return entity.archetype == Archetype.EMPTY\n         end)\n         :AnyMatch(function(entity)\n            return entity.archetype == entity_FSM_Standing.archetype\n         end)\n   )\n\n   lu.assertIsTrue(\n      result\n         :Filter(function(entity)\n            return entity.archetype == entity_FSM_Standing.archetype\n         end)\n         :AnyMatch(function(entity)\n            return entity == entity_FSM_Walking\n         end)\n   )\n\n   lu.assertItemsEquals(\n      result\n         :Filter(function(entity)\n            return entity.archetype == entity_FSM_Standing.archetype\n         end)\n         :ToArray(), \n      {\n         entity_FSM_Standing,\n         entity_FSM_Walking,\n         entity_FSM_Running\n      }\n   )\n\n   lu.assertEquals(\n      result\n         :Filter(function(entity)\n            return entity.archetype == Archetype.EMPTY\n         end)\n         :ToArray(), \n      {}\n   )\n\n   lu.assertItemsEquals(\n      result\n         :Filter(function(entity)\n            return entity.archetype == entity_FSM_Standing.archetype\n         end)\n         :Map(function(entity)            \n            return entity[Comp_FSM]:GetState()\n         end)\n         :ToArray(), \n      { \"Standing\", \"Walking\", \"Running\" }\n   )\n\n   lu.assertEquals(\n      #(\n         result\n            :Filter(function(entity)\n               return entity.archetype == entity_FSM_Standing.archetype\n            end)\n            :Limit(2)\n            :ToArray()\n      ), \n      2\n   )\n\n   lu.assertEquals(\n      #(result:Limit(8):ToArray()), \n      8\n   )\n\n   -- total = 12\n   lu.assertEquals(\n      #(result:Limit(20):ToArray()), \n      12\n   )\nend\n\nfunction TestQueryResult:test_Clauses()\n\n   local clause_none_walking_running = Comp_FSM.In(\"Walking\", \"Running\")\n   clause_none_walking_running.IsNoneFilter = true\n\n   local result = QueryResult.New(chunks, {clause_none_walking_running})\n   lu.assertItemsEquals(result:ToArray(), {\n      entity_A,\n      entity_B,\n      entity_B_QL,\n      entity_FSM_Standing,\n      entity_FSM_2_Standing,\n      entity_FSM_2_Walking,\n      entity_FSM_2_Running,\n      entity_FSM_2_ql_Standing,\n      entity_FSM_2_ql_Walking,\n      entity_FSM_2_ql_Running\n   })\n\n   local clause_any_standing_running = Comp_FSM.In(\"Standing\", \"Running\")\n   clause_any_standing_running.IsAnyFilter = true\n\n   local clause_any_walking_running_2 = Comp_FSM_2.In(\"Walking\", \"Running\")\n   clause_any_walking_running_2.IsAnyFilter = true\n\n   local result = QueryResult.New(chunks, { clause_any_standing_running, clause_any_walking_running_2 })\n   lu.assertItemsEquals(result:ToArray(), {\n      entity_FSM_Standing,\n      entity_FSM_Running,\n      entity_FSM_2_Walking,\n      entity_FSM_2_Running,\n      entity_FSM_2_ql_Walking,\n      entity_FSM_2_ql_Running,\n   })\n\n   local clause_any_standing_running = Comp_FSM.In(\"Standing\", \"Running\")\n   clause_any_standing_running.IsAnyFilter = true\n\n   local clause_any_walking_running_2 = Comp_FSM_2.In(\"Walking\", \"Running\")\n   clause_any_walking_running_2.IsAnyFilter = true\n\n   local clause_none_walking_running_2_ql = Comp_FSM_2_Ql.In(\"Walking\", \"Running\")\n   clause_none_walking_running_2_ql.IsNoneFilter = true\n\n   local result = QueryResult.New(chunks, { clause_any_standing_running, clause_any_walking_running_2, clause_none_walking_running_2_ql })\n   lu.assertItemsEquals(result:ToArray(), {\n      entity_FSM_Standing,\n      entity_FSM_Running,\n      entity_FSM_2_Walking,\n      entity_FSM_2_Running\n   })\n\n   local clause_any_walking_running_2_ql = Comp_FSM_2_Ql.In(\"Walking\", \"Running\")\n   clause_any_walking_running_2_ql.IsAnyFilter = true\n\n   local result = QueryResult.New(chunks, { clause_any_standing_running, clause_any_walking_running_2_ql })\n   lu.assertItemsEquals(result:ToArray(), {\n      entity_FSM_Standing,\n      entity_FSM_Running,\n      entity_FSM_2_ql_Walking,\n      entity_FSM_2_ql_Running,\n   })\n   \n   local clause_all_standing_running = Comp_FSM.In(\"Standing\", \"Running\")\n   clause_all_standing_running.IsAllFilter = true\n\n   local result = QueryResult.New(chunks, {clause_all_standing_running})\n   lu.assertItemsEquals(result:ToArray(), {\n      entity_FSM_Standing,\n      entity_FSM_Running\n   })   \nend\n"
  },
  {
    "path": "test/test_SystemExecutor.lua",
    "content": "local lu = require('luaunit')\n\nlocal World = require('World')\nlocal Query = require('Query')\nlocal System = require('System')\nlocal SystemExecutor = require('SystemExecutor')\n\nTestSystemExecutor = {}\n\nfunction TestSystemExecutor:test_ExecProcessTransformRender()\n   local steps = {\n      render = 'ExecRender', \n      process = 'ExecProcess',\n      transform = 'ExecTransform'\n   }\n\n   for step, method in pairs(steps) do      \n      local world = World.New()   \n      world.version = 10\n\n      local log = {}\n   \n      local system1 = {\n         Step = step,\n         Order = 1,\n         Update = function()\n            table.insert(log, 1)\n         end,\n         version = 0,\n         ShouldUpdate = function()\n            return true\n         end\n      }\n\n      local system2 = {\n         Step = step,\n         Order = 2,\n         Update = function()\n            table.insert(log, 2)\n         end,\n         version = 0,\n         ShouldUpdate = function()\n            return false\n         end\n      }\n      \n      local system3 = {\n         Step = step,\n         Order = 3,\n         Update = function()\n            table.insert(log, 3)\n         end,\n         version = 0,\n         ShouldUpdate = function()\n            return true\n         end\n      }\n   \n      local executor = SystemExecutor.New(world)   \n      executor:SetSystems({system1, system2, system3})\n\n      executor[method](executor, {})\n   \n      lu.assertEquals(log, {1, 3})  \n      lu.assertEquals(system1.version, 11)  \n      lu.assertEquals(system2.version, 0)  \n      lu.assertEquals(system3.version, 12)  \n\n      world:Destroy()\n   end\nend\n\nfunction TestSystemExecutor:test_Coroutine()\n\n   \n   local log = {}\n   local value = 0\n\n   local co = coroutine.create(function ()\t\n      -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785\n      local i, len = 0, 10000-1\n      -- while i <= len do\n      --    i = i + 1\n      --    if i%1000 == 0 then\n      --       print( \"Before yield\", i, value)\n      --       coroutine.yield()\t\n      --       print( \"After yield\", i, value)\n      --    end\t\n      -- end\n      for i=1,3000 do\t\t\n         if i%1000 == 0 then\n            table.insert(log, i)\n            table.insert(log, value)\n            coroutine.yield()\t\n            table.insert(log, value)\n         end\t\t\n      end\n   end)\n   \n   coroutine.resume(co)\n   lu.assertEquals(log, {1000, 0})\n   value = 1\n\n   coroutine.resume(co)\n   lu.assertEquals(log, {1000, 0, 1, 2000, 1})\n   value = 2\n\n   coroutine.resume(co)\n   lu.assertEquals(log, {1000, 0, 1, 2000, 1, 2, 3000, 2})\n   value = 3\n\n   coroutine.resume(co)\n   lu.assertEquals(log, {1000, 0, 1, 2000, 1, 2, 3000, 2, 3})\n   value = 3\nend\n\nfunction TestSystemExecutor:test_ExecTasks()\n   local steps = {\n      render = 'ExecRender', \n      process = 'ExecProcess',\n      transform = 'ExecTransform'\n   }\n\n   local world = World.New()   \n   world.version = 10\n   local log = {}\n   local logBeforeYield = {}\n   local logAfterYield = {}\n\n   local Task_A = System.Create('task', function()\n      -- delay execution to ensure test flow\n      local i = 0\n      while i <= 4000 do\n         i = i + 1\n         if i%1000 == 0 then\n            table.insert(logBeforeYield, i)\n            coroutine.yield()\n            table.insert(logAfterYield, i+1)\n         end\n      end\n      \n      table.insert(log, 'A')\n      lu.assertEquals(logBeforeYield, {1000, 2000, 3000, 4000})\n      lu.assertEquals(logAfterYield, {1001, 2001, 3001, 4001})\n   end)\n\n   local Task_B = System.Create('task', function()\n      table.insert(log, 'B')\n   end)\n\n   local Task_C = System.Create('task', function()\n      table.insert(log, 'C')\n   end)\n\n   local Task_D = System.Create('task', function()\n      table.insert(log, 'D')\n   end)\n\n   local Task_E = System.Create('task', function()\n      table.insert(log, 'E')\n   end)\n\n   local Task_F = System.Create('task', function()\n      table.insert(log, 'F')\n   end)\n\n   local Task_G = System.Create('task', function()\n      table.insert(log, 'G')\n   end)\n   \n\n   local Task_H = System.Create('task', function(self)\n      table.insert(log, 'H')\n   end)\n\n   --[[\n      ┌─┐      ┌─┐            ┌─┐\n      │A│◄─────┤C│◄────┬──────┤F│◄────┐\n      └─┘      └┬┘     │      └┬┘     │\n                │     ┌┴┐      │     ┌┴┐\n           ┌────┘     │E│◄─────┘     │H│\n           │          └┬┘            └┬┘\n           │           │              │\n      ┌─┐  │   ┌─┐     │      ┌─┐     │\n      │B│◄─┴───┤D│◄────┴──────┤G│◄────┘\n      └─┘      └─┘            └─┘\n\n      A - has no dependency\n      B - has no dependency\n      C - Depends on A,B\n      D - Depends on B\n      E - Depends on A,B,C,D\n      F - Depends on A,B,C,D,E\n      G - Depends on B,D\n      H - Depends on A,B,C,D,E,F,G\n\n      Completion order will be B,D,G,A,C,E,F,H      \n   ]]\n   Task_A.Before = {Task_C}\n   Task_B.Before = {Task_D}\n   Task_C.After = {Task_B}\n   Task_D.Before = {Task_G}\n   Task_F.After = {Task_E}\n   Task_E.After = {Task_D, Task_C}\n   Task_C.Before = {Task_F}\n   Task_H.After = {Task_F, Task_G}\n   \n   local task_a = Task_A.New(world, {})\n   local task_b = Task_B.New(world, {})\n   local task_c = Task_C.New(world, {})\n   local task_d = Task_D.New(world, {})\n   local task_e = Task_E.New(world, {})\n   local task_f = Task_F.New(world, {})\n   local task_g = Task_G.New(world, {})\n   local task_h = Task_H.New(world, {})\n\n   local executor = SystemExecutor.New(world)\n   executor:SetSystems({task_a, task_b, task_c, task_d, task_e, task_f, task_g, task_h})\n\n   executor:ScheduleTasks({})\n   \n   local MaxExecutionTime = 0.5\n   executor:ExecTasks(MaxExecutionTime)\n\n   lu.assertEquals(log, {'B','D','G','A','C','E','F','H'}) \n   lu.assertEquals(logBeforeYield, {1000, 2000, 3000, 4000})  \n   lu.assertEquals(logAfterYield, {1001, 2001, 3001, 4001})   \n\n   world:Destroy()\nend\n\nfunction TestSystemExecutor:test_ExecOnEnter()\n   local world = World.New()   \n   world.version = 10   \n      \n   local changedEntities = {\n      [{id = 1, archetype='Arch_1'}] = 'Arch_0',\n      [{id = 2, archetype='Arch_1'}] = 'Arch_0',\n      [{id = 3, archetype='Arch_2'}] = 'Arch_1',\n      [{id = 4, archetype='Arch_3'}] = 'Arch_1',\n      [{id = 5, archetype='Arch_3'}] = 'Arch_3',\n      [{id = 6, archetype='Arch_3'}] = 'Arch_0',\n   }\n\n   local log = {\n      Arch_1 = {},\n      Arch_2 = {},\n      Arch_3 = {},\n   }\n   local logExpected = {\n      Arch_1 = { [\"1_1\"] = true, [\"1_2\"] = true},\n      Arch_2 = {},\n      Arch_3 = { [\"3_4\"] = true, [\"3_6\"] = true},\n   }\n\n   local systemArch1 = {\n      Step = 'process',\n      Order = 1,\n      OnEnter = function(s, Time, entity)\n         log.Arch_1[\"1_\"..entity.id] = true\n      end,\n      version = 0,\n      Query = {\n         isQuery = true,\n         Match = function(s, archetypeNew)\n            return archetypeNew == 'Arch_1'\n         end\n      }\n   }\n\n   local systemNoQuery = {\n      Step = 'process',\n      Order = 2,\n      OnEnter = function(s, Time, entity)\n         log.Arch_1[\"2_\"..entity.id] = true\n      end,\n      version = 0\n   }\n\n   local systemArch3 = {\n      Step = 'process',\n      Order = 3,\n      OnEnter = function(s, Time, entity)\n         log.Arch_3[\"3_\"..entity.id] = true\n      end,\n      version = 0,\n      Query = {\n         isQuery = true,\n         Match = function(s, archetypeNew)\n            return archetypeNew == 'Arch_3'\n         end\n      }\n   }\n\n   local executor = SystemExecutor.New(world)   \n   executor:SetSystems({systemArch1, systemNoQuery, systemArch3})\n\n   executor:ExecOnExitEnter({}, changedEntities)\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 12)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 14)  \n   lu.assertEquals(world.version, 14)  \n\n\n   executor:ExecOnExitEnter({}, changedEntities)\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 16)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 18)  \n   lu.assertEquals(world.version, 18)  \n\n   executor:ExecOnExitEnter({}, {})\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 16)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 18)  \n   lu.assertEquals(world.version, 18)\n\n   world:Destroy()\nend\n\nfunction TestSystemExecutor:test_ExecOnExit()\n   local world = World.New()   \n   world.version = 10  \n      \n   local changedEntities = {\n      [{id = 1, archetype='Arch_1'}] = 'Arch_0',\n      [{id = 2, archetype='Arch_1'}] = 'Arch_3',\n      [{id = 3, archetype='Arch_2'}] = 'Arch_1',\n      [{id = 4, archetype='Arch_3'}] = 'Arch_1',\n      [{id = 5, archetype='Arch_2'}] = 'Arch_3',\n      [{id = 6, archetype='Arch_3'}] = 'Arch_3',\n   }\n\n   local log = {\n      Arch_1 = {},\n      Arch_2 = {},\n      Arch_3 = {},\n   }\n   local logExpected = {\n      Arch_1 = { [\"1_3\"] = true, [\"1_4\"] = true},\n      Arch_2 = {},\n      Arch_3 = { [\"3_2\"] = true, [\"3_5\"] = true},\n   }\n\n   local systemArch1 = {\n      Step = 'process',\n      Order = 1,\n      OnExit = function(s, Time, entity)\n         log.Arch_1[\"1_\"..entity.id] = true\n      end,\n      version = 0,\n      Query = {\n         isQuery = true,\n         Match = function(s, archetypeNew)\n            return archetypeNew == 'Arch_1'\n         end\n      }\n   }\n\n   local systemNoQuery = {\n      Step = 'process',\n      Order = 2,\n      OnExit = function(s, Time, entity)\n         log.Arch_1[\"2_\"..entity.id] = true\n      end,\n      version = 0\n   }\n\n   local systemArch3 = {\n      Step = 'process',\n      Order = 3,\n      OnExit = function(s, Time, entity)\n         log.Arch_3[\"3_\"..entity.id] = true\n      end,\n      version = 0,\n      Query = {\n         isQuery = true,\n         Match = function(s, archetypeNew)\n            return archetypeNew == 'Arch_3'\n         end\n      }\n   }\n\n   local executor = SystemExecutor.New(world)   \n   executor:SetSystems({systemArch1, systemNoQuery, systemArch3})\n\n   executor:ExecOnExitEnter({}, changedEntities)\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 12)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 14)  \n   lu.assertEquals(world.version, 14)  \n\n\n   executor:ExecOnExitEnter({}, changedEntities)\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 16)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 18)  \n   lu.assertEquals(world.version, 18)  \n\n   executor:ExecOnExitEnter({}, {})\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 16)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 18)  \n   lu.assertEquals(world.version, 18)\n\n   world:Destroy()\nend\n\nfunction TestSystemExecutor:test_ExecOnRemove()\n   local world = World.New()   \n   world.version = 10  \n      \n   local removedEntities = {\n      [{id = 1, archetype='Arch_1'}] = 'Arch_0',\n      [{id = 2, archetype='Arch_1'}] = 'Arch_0',\n      [{id = 3, archetype='Arch_2'}] = 'Arch_1',\n      [{id = 4, archetype='Arch_2'}] = 'Arch_1',\n      [{id = 5, archetype='Arch_0'}] = 'Arch_3',\n      [{id = 6, archetype='Arch_0'}] = 'Arch_3',\n   }\n\n   local log = {\n      Arch_1 = {},\n      Arch_2 = {},\n      Arch_3 = {},\n   }\n   local logExpected = {\n      Arch_1 = { [\"1_3\"] = true, [\"1_4\"] = true},\n      Arch_2 = {},\n      Arch_3 = { [\"3_5\"] = true, [\"3_6\"] = true},\n   }\n\n   local systemArch1 = {\n      Step = 'process',\n      Order = 1,\n      OnRemove = function(s, Time, entity)\n         log.Arch_1[\"1_\"..entity.id] = true\n      end,\n      version = 0,\n      Query = {\n         isQuery = true,\n         Match = function(s, archetypeNew)\n            return archetypeNew == 'Arch_1'\n         end\n      }\n   }\n\n   local systemNoQuery = {\n      Step = 'process',\n      Order = 2,\n      OnRemove = function(s, Time, entity)\n         log.Arch_1[\"2_\"..entity.id] = true\n      end,\n      version = 0\n   }\n\n   local systemArch3 = {\n      Step = 'process',\n      Order = 3,\n      OnRemove = function(s, Time, entity)\n         log.Arch_3[\"3_\"..entity.id] = true\n      end,\n      version = 0,\n      Query = {\n         isQuery = true,\n         Match = function(s, archetypeNew)\n            return archetypeNew == 'Arch_3'\n         end\n      }\n   }\n\n   local executor = SystemExecutor.New(world)   \n   executor:SetSystems({systemArch1, systemNoQuery, systemArch3})\n\n   executor:ExecOnRemove({}, removedEntities)\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 12)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 14)  \n   lu.assertEquals(world.version, 14)  \n\n\n   executor:ExecOnRemove({}, removedEntities)\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 16)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 18)  \n   lu.assertEquals(world.version, 18)  \n\n   executor:ExecOnRemove({}, {})\n   lu.assertEquals(log, logExpected)  \n   lu.assertEquals(systemArch1.version, 16)  \n   lu.assertEquals(systemNoQuery.version, 0)  \n   lu.assertEquals(systemArch3.version, 18)  \n   lu.assertEquals(world.version, 18)\n\n   world:Destroy()\nend\n"
  },
  {
    "path": "test/test_World.lua",
    "content": "local lu = require('luaunit')\n\nlocal World = require('World')\nlocal Query = require('Query')\nlocal System = require('System')\nlocal Component = require('Component')\n\n-- mock\nWorld.LoopManager = {\n   Register = function(world)\n      return function ()\n         \n      end\n   end\n}\n\n\nTestWorld = {}\n\nfunction TestWorld:test_SystemWhitoutQuery()\n   local Comp_A = Component.Create(0)\n   local Comp_B = Component.Create(0)\n   local Comp_C = Component.Create(0)\n   local Comp_D = Component.Create(0)\n\n   local calledInitialize = 0\n   local calledShouldUpdate = 0\n   local calledUpdate = 0\n   local calledOnEnter = 0\n   local calledOnExit = 0\n   local calledOnRemove = 0\n   local calledOnDestroy = 0\n   \n   local systemConfig = { value = 1 }\n\n\n   lu.assertError(function()\n      -- empty step\n      System.Create()\n   end)\n\n   lu.assertError(function()\n      -- invalid step\n      System.Create('xpto')\n   end)\n\n   local System_A = System.Create('process', 1, function()\n      calledUpdate = calledUpdate + 1\n   end)\n\n   function System_A:Initialize()\n      calledInitialize = calledInitialize + 1\n      lu.assertEquals(self._config, systemConfig)\n      lu.assertEquals(self.GetType(), System_A)\n   end\n\n   function System_A:ShouldUpdate(config)\n      calledShouldUpdate = calledShouldUpdate + 1\n      return true\n   end\n\n   function System_A:OnEnter(Time, entity)\n      calledOnEnter = calledOnEnter + 1\n   end\n\n   function System_A:OnExit(Time, entity)\n      calledOnExit = calledOnExit + 1\n   end\n\n   function System_A:OnRemove(Time, entity)\n      calledOnRemove = calledOnRemove + 1\n   end\n\n   function System_A:OnDestroy()\n      calledOnDestroy = calledOnDestroy + 1\n   end\n\n\n   local System_B = System.Create('transform', -100, Query.All(Comp_B))\n\n   function System_B:OnEnter(Time, entity)\n      calledOnEnter = calledOnEnter + 1\n   end\n\n   function System_B:OnExit(Time, entity)\n      calledOnExit = calledOnExit + 1\n   end\n\n   function System_B:OnRemove(Time, entity)\n      calledOnRemove = calledOnRemove + 1\n   end\n\n   function System_B:OnDestroy()\n      calledOnDestroy = calledOnDestroy + 1\n   end\n   \n   local world = World.New({System_B}, 60)\n   world:AddSystem(System_A, systemConfig)\n\n\n   local entityC = world:Entity(Comp_C(1))\n\n   local System_C = System.Create('transform', 20, Query.All(Comp_C))\n\n   function System_C:Update(Time)\n      calledUpdate = calledUpdate + 1\n      lu.assertItemsEquals(self:Result():ToArray(), {entityC})\n   end\n\n   world:AddSystem(System_C)\n   \n\n   lu.assertEquals(world:GetFrequency() , 60)\n   \n   world:SetFrequency(30) \n   lu.assertEquals(world:GetFrequency() , 30)\n\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 0)\n   lu.assertEquals(calledUpdate, 0)\n   lu.assertEquals(calledOnEnter, 0)\n   lu.assertEquals(calledOnExit, 0)\n   lu.assertEquals(calledOnRemove, 0)\n\n   world:Update('process', 0.0334 * 1)\n   world:Update('transform', 0.0335 * 1 + 0.001)\n   world:Update('render', 0.0336 * 1 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 1)\n   lu.assertEquals(calledUpdate, 2)\n   lu.assertEquals(calledOnEnter, 0)\n   lu.assertEquals(calledOnExit, 0)\n   lu.assertEquals(calledOnRemove, 0)\n   \n\n   local entityA = world:Entity(Comp_A(1))\n   world:Update('process', 0.0334 * 2)\n   world:Update('transform', 0.0335 * 2 + 0.001)\n   world:Update('render', 0.0336 * 2 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 2)\n   lu.assertEquals(calledUpdate, 4)\n   lu.assertEquals(calledOnEnter, 0)\n   lu.assertEquals(calledOnExit, 0)\n   lu.assertEquals(calledOnRemove, 0)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {entityA})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {})\n\n\n   local entityB = world:Entity(Comp_B(1))\n   world:Update('process', 0.0334 * 3)\n   local entityD = world:Entity(Comp_D(1))\n   world:Update('transform', 0.0335 * 3 + 0.001)\n   world:Update('render', 0.0336 * 3 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 3)\n   lu.assertEquals(calledUpdate, 6)\n   lu.assertEquals(calledOnEnter, 1)\n   lu.assertEquals(calledOnExit, 0)\n   lu.assertEquals(calledOnRemove, 0)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {entityA})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {entityB})\n  \n   entityA[Comp_B] = {value = 2}\n   world:Update('process', 0.0334 * 4)\n   world:Update('transform', 0.0335 * 4 + 0.001)\n   world:Update('render', 0.0336 * 4 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 4)\n   lu.assertEquals(calledUpdate, 8)\n   lu.assertEquals(calledOnEnter, 2)\n   lu.assertEquals(calledOnExit, 0)\n   lu.assertEquals(calledOnRemove, 0)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {entityA})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {entityA, entityB})\n\n   entityA[Comp_B] = nil\n   world:Update('process', 0.0334 * 5)\n   world:Update('transform', 0.0335 * 5 + 0.001)\n   world:Update('render', 0.0336 * 5 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 5)\n   lu.assertEquals(calledUpdate, 10)\n   lu.assertEquals(calledOnEnter, 2)\n   lu.assertEquals(calledOnExit, 1)\n   lu.assertEquals(calledOnRemove, 0)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {entityA})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {entityB})\n\n   world:Remove(entityA)\n   world:Remove(entityA) -- no result\n   world:Update('process', 0.0334 * 6)\n   world:Update('transform', 0.0335 * 6 + 0.001)\n   world:Update('render', 0.0336 * 6 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 6)\n   lu.assertEquals(calledUpdate, 12)\n   lu.assertEquals(calledOnEnter, 2)\n   lu.assertEquals(calledOnExit, 1)\n   lu.assertEquals(calledOnRemove, 0)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {entityB})\n\n   world:Remove(entityB)\n   world:Update('process', 0.0334 * 7)\n   world:Update('transform', 0.0335 * 7 + 0.001)\n   world:Update('render', 0.0336 * 7 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 7)\n   lu.assertEquals(calledUpdate, 14)\n   lu.assertEquals(calledOnEnter, 2)\n   lu.assertEquals(calledOnExit, 1)\n   lu.assertEquals(calledOnRemove, 1)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {})\n\n   -- add and remove before update\n   local entityB = world:Entity(Comp_B(1))\n   world:Remove(entityB)\n   world:Update('process', 0.0334 * 8)\n   world:Update('transform', 0.0335 * 8 + 0.001)\n   world:Update('render', 0.0336 * 8 + 0.002)\n   lu.assertEquals(calledInitialize, 1)\n   lu.assertEquals(calledShouldUpdate, 8)\n   lu.assertEquals(calledUpdate, 16)\n   lu.assertEquals(calledOnEnter, 2)\n   lu.assertEquals(calledOnExit, 1)\n   lu.assertEquals(calledOnRemove, 1)\n   lu.assertEquals(calledOnDestroy, 0)\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_A)):ToArray(), {})\n   lu.assertItemsEquals(world:Exec(Query.All(Comp_B)):ToArray(), {})\n   \n   \n   world:Destroy()\n   lu.assertEquals(calledOnDestroy, 2)\n\nend\n\nfunction TestWorld:test_ExecTasks()\n \n   local log = {}\n   local logBeforeYield = {}\n   local logAfterYield = {}\n\n   local Task_A = System.Create('task', function()\n      -- delay execution to ensure test flow\n      local i = 0\n      while i <= 4000 do\n         i = i + 1\n         if i%1000 == 0 then\n            table.insert(logBeforeYield, i)\n            coroutine.yield()\n            table.insert(logAfterYield, i+1)\n         end\n      end\n      \n      table.insert(log, 'A')\n      lu.assertEquals(logBeforeYield, {1000, 2000, 3000, 4000})\n      lu.assertEquals(logAfterYield, {1001, 2001, 3001, 4001})\n   end)\n\n   local Task_B = System.Create('task', function()\n      table.insert(log, 'B')\n   end)\n\n   local Task_C = System.Create('task', function()\n      table.insert(log, 'C')\n   end)\n\n   local Task_D = System.Create('task', function()\n      table.insert(log, 'D')\n   end)\n\n   local Task_E = System.Create('task', function()\n      table.insert(log, 'E')\n   end)\n\n   local Task_F = System.Create('task', function()\n      table.insert(log, 'F')\n   end)\n\n   local Task_G = System.Create('task', function()\n      table.insert(log, 'G')\n   end)\n   \n\n   local Task_H = System.Create('task', function(self)\n      table.insert(log, 'H')\n   end)\n\n   --[[\n      ┌─┐      ┌─┐            ┌─┐\n      │A│◄─────┤C│◄────┬──────┤F│◄────┐\n      └─┘      └┬┘     │      └┬┘     │\n                │     ┌┴┐      │     ┌┴┐\n           ┌────┘     │E│◄─────┘     │H│\n           │          └┬┘            └┬┘\n           │           │              │\n      ┌─┐  │   ┌─┐     │      ┌─┐     │\n      │B│◄─┴───┤D│◄────┴──────┤G│◄────┘\n      └─┘      └─┘            └─┘\n\n      A - has no dependency\n      B - has no dependency\n      C - Depends on A,B\n      D - Depends on B\n      E - Depends on A,B,C,D\n      F - Depends on A,B,C,D,E\n      G - Depends on B,D\n      H - Depends on A,B,C,D,E,F,G\n\n      Completion order will be B,D,G,A,C,E,F,H      \n   ]]\n   Task_A.Before = {Task_C}\n   Task_B.Before = {Task_D}\n   Task_C.After = {Task_B}\n   Task_D.Before = {Task_G}\n   Task_F.After = {Task_E}\n   Task_E.After = {Task_D, Task_C}\n   Task_C.Before = {Task_F}\n   Task_H.After = {Task_F, Task_G}\n\n\n   local world = World.New({ Task_A, Task_B, Task_C, Task_D, Task_E, Task_F, Task_G, Task_H})   \n   world:SetFrequency(30) \n   -- world.version = 10\n   \n   world:Update('process', 0.0334 * 1)\n   world:Update('transform', 0.0335 * 1 + 0.001)\n   world:Update('transform', 0.0335 * 2 + 0.001)\n   world:Update('transform', 0.0335 * 4 + 0.001)\n   world:Update('transform', 0.0335 * 5 + 0.001)\n   world:Update('transform', 0.0335 * 6 + 0.001)\n\n   lu.assertEquals(log, {'B','D','G','A','C','E','F','H'}) \n   lu.assertEquals(logBeforeYield, {1000, 2000, 3000, 4000})  \n   lu.assertEquals(logAfterYield, {1001, 2001, 3001, 4001})   \n\n   world:Destroy()\nend\n"
  },
  {
    "path": "test.lua",
    "content": "\npackage.path = package.path .. \";modules/?.lua\"\npackage.path = package.path .. \";src/?.lua\"\nlocal lu = require(\"luaunit\")\nlocal luacov = require(\"luacov\")\n\n-- tests\nrequire(\"test/test_Archetype\")\nrequire(\"test/test_Component\")\n-- require(\"test/test_ECS\")\nrequire(\"test/test_Entity\")\nrequire(\"test/test_EntityRepository\")\nrequire(\"test/test_Event\")\nrequire(\"test/test_Query\")\nrequire(\"test/test_QueryResult\")\n-- require(\"test/test_RobloxLoopManager\")\n-- require(\"test/test_System\")\nrequire(\"test/test_SystemExecutor\")\n-- require(\"test/test_Timer\")\n-- require(\"test/test_Utility\")\nrequire(\"test/test_World\")\n\nos.exit(lu.LuaUnit.run())\n"
  }
]