You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
4.9 KiB
Lua
207 lines
4.9 KiB
Lua
local ecs = require "ecs.core"
|
|
|
|
local function get_attrib(opt, inout)
|
|
local desc = {}
|
|
if opt == "?" then
|
|
desc.opt = true
|
|
else
|
|
assert(opt == ":")
|
|
end
|
|
if inout == "in" then
|
|
desc.r = true
|
|
elseif inout == "out" then
|
|
desc.w = true
|
|
elseif inout == "update" then
|
|
desc.r = true
|
|
desc.w = true
|
|
else
|
|
assert(inout == "temp")
|
|
end
|
|
return desc
|
|
end
|
|
|
|
local function cache_world(obj, k)
|
|
local c = {
|
|
typenames = {},
|
|
id = 0,
|
|
select = {},
|
|
}
|
|
|
|
local function gen_select_pat(pat)
|
|
local typenames = c.typenames
|
|
local desc = {}
|
|
local idx = 1
|
|
for key, opt, inout in pat:gmatch "([_%w]+)([:?])(%l+)" do
|
|
local tc = assert(typenames[key])
|
|
local a = get_attrib(opt, inout)
|
|
a.name = tc.name
|
|
a.id = tc.id
|
|
a.type = tc.type
|
|
local n = #tc
|
|
for i = 1, #tc do
|
|
a[i] = tc[i]
|
|
end
|
|
desc[idx] = a
|
|
idx = idx + 1
|
|
end
|
|
return desc
|
|
end
|
|
|
|
local function cache_select(cache, pat)
|
|
local pat_desc = gen_select_pat(pat)
|
|
cache[pat] = k:_groupiter(pat_desc)
|
|
return cache[pat]
|
|
end
|
|
|
|
setmetatable(c.select, {
|
|
__mode = "kv",
|
|
__index = cache_select,
|
|
})
|
|
|
|
obj[k] = c
|
|
return c
|
|
end
|
|
|
|
local context = setmetatable({}, {
|
|
__index = cache_world,
|
|
})
|
|
local typeid = {
|
|
int = assert(ecs._TYPEINT),
|
|
float = assert(ecs._TYPEFLOAT),
|
|
bool = assert(ecs._TYPEBOOL),
|
|
}
|
|
local typesize = {
|
|
[typeid.int] = 4,
|
|
[typeid.float] = 4,
|
|
[typeid.bool] = 1,
|
|
}
|
|
|
|
local typepack = {
|
|
[typeid.int] = 'i4',
|
|
[typeid.float] = 'f',
|
|
[typeid.bool] = 'B',
|
|
}
|
|
|
|
local M = ecs._METHODS
|
|
|
|
do -- newtype
|
|
local function parse(s)
|
|
-- s is "name:typename"
|
|
local name, typename = s:match "([%w_]+):(%l+)"
|
|
local tid = assert(typeid[typename])
|
|
return {tid, name}
|
|
end
|
|
|
|
local function align(c, field)
|
|
local t = field[1]
|
|
local size = c.size
|
|
if t == typeid.int or t == typeid.float then
|
|
local offset = ((size + 3) & ~3)
|
|
c.size = offset + 4
|
|
field[3] = offset
|
|
elseif t == typeid.bool then
|
|
c.size = size + 1
|
|
field[3] = size
|
|
else
|
|
error("Invalid type " .. t)
|
|
end
|
|
return field
|
|
end
|
|
|
|
local function align_struct(c, t)
|
|
if t == typeid.int or t == typeid.float then
|
|
c.size = ((c.size + 3) & ~3)
|
|
end
|
|
end
|
|
|
|
function M:register(typeclass)
|
|
local name = assert(typeclass.name)
|
|
local ctx = context[self]
|
|
local typenames = ctx.typenames
|
|
local id = ctx.id + 1
|
|
assert(typenames[name] == nil and id <= ecs._MAXTYPE)
|
|
ctx.id = id
|
|
local c = {
|
|
id = id,
|
|
name = name,
|
|
size = 0,
|
|
}
|
|
for i, v in ipairs(typeclass) do
|
|
c[i] = align(c, parse(v))
|
|
end
|
|
if typeclass.type == "lua" then
|
|
assert(c.size == 0)
|
|
c.size = ecs._LUAOBJECT
|
|
c.islua = true
|
|
elseif c.size > 0 then
|
|
align_struct(c, typeclass[1][1])
|
|
local pack = "!4="
|
|
for i = 1, #c do
|
|
pack = pack .. typepack[c[i][1]]
|
|
end
|
|
c.pack = pack
|
|
else
|
|
-- size == 0, one value
|
|
if typeclass.type then
|
|
local t = assert(typeid[typeclass.type])
|
|
c.type = t
|
|
c.size = typesize[t]
|
|
c.pack = typepack[t]
|
|
c[1] = {t, "v", 0}
|
|
else
|
|
c.tag = true
|
|
end
|
|
end
|
|
typenames[name] = c
|
|
self:_newtype(id, c.size)
|
|
end
|
|
end
|
|
|
|
local mapbool = {
|
|
[true] = 1,
|
|
[false] = 0,
|
|
}
|
|
|
|
function M:new(obj)
|
|
local eid = self:_newentity()
|
|
local typenames = context[self].typenames
|
|
for k, v in pairs(obj) do
|
|
local tc = typenames[k]
|
|
if not tc then
|
|
error("Invalid key : " .. k)
|
|
end
|
|
if tc.islua then
|
|
self:_addcomponent(eid, tc.id, v)
|
|
elseif tc.tag then
|
|
self:_addcomponent(eid, tc.id)
|
|
elseif tc.type then
|
|
self:_addcomponent(eid, tc.id, string.pack(tc.pack, mapbool[v] or v))
|
|
else
|
|
local tmp = {}
|
|
for i, f in ipairs(tc) do
|
|
tmp[i] = v[f[2]]
|
|
end
|
|
self:_addcomponent(eid, tc.id, string.pack(tc.pack, table.unpack(tmp)))
|
|
end
|
|
end
|
|
end
|
|
|
|
function M:context(t)
|
|
local typenames = context[self].typenames
|
|
local id = {}
|
|
for i, name in ipairs(t) do
|
|
local tc = typenames[name]
|
|
if not tc then
|
|
error("Invalid component name " .. name)
|
|
end
|
|
id[i] = tc.id
|
|
end
|
|
return self:_context(id)
|
|
end
|
|
|
|
function M:select(pat)
|
|
return context[self].select[pat]()
|
|
end
|
|
|
|
return ecs
|