1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
math.randomseed(os.time())
local module = {}
setmetatable(module, {__index=_G})
setfenv(1, module)
--===== Components =====--
-- Components provide a kind-of-type-safe way of storing values, since they are
-- guaranteed to have a specific set of keys and no other values in them. They also serialize
-- nicely for storage.
Component = {}
function Component.newFactory(name, params, serialize)
-- create the metatable for this component type
local metatable = {}
metatable.__tostring = serialize or Component.serialize -- nice serialization
metatable.__newindex = function(tbl, index, value)
if params[index] ~= nil then
tbl[index] = value
else
-- throw an error if we try to set a key that is not in the valid set
error(string.format("%q is not a valid key for a component of type %q", index, name))
end
end
-- the factory function for creating components
return function(tbl)
local tbl = tbl or {}
local self = { __type=name }
-- ensure that all keys are present
for k, v in pairs(params) do
self[k] = v
end
setmetatable(self, metatable)
-- set the keys from the factory call
for k, v in pairs(tbl) do
self[k] = v
end
return self
end
end
function Component.serialize(self)
local str = "{"
for k, v in pairs(self) do
if type(v) == "string" then
str = str .. string.format("%s=%q,", k, v)
else
str = str .. string.format("%s=%s,", k, tostring(v))
end
end
str = string.sub(str, 1, -2) .. "}"
return str
end
--===== EntityDb =====--
-- EntityDb is a database of entities and their associated components
-- it should be quite efficient to query for all entities with a given component, and reasonably
-- efficient to query for all components of a given entity
EntityDb = {}
EntityDb.__index = EntityDb
function EntityDb.new(_)
local self = {
entities = {},
components = {},
}
setmetatable(self, EntityDb)
return self
end
setmetatable(EntityDb, {__call=EntityDb.new})
function EntityDb.checkIsValid(self, id)
if not self.entities[id] then
error(string.format("invalid entity id: %s", tostring(id)))
end
end
local function uid()
local template ='xxxx:xxxx:xxxx'
return string.gsub(template, 'x', function (c)
local v = math.random(0, 0xf)
return string.format('%x', v)
end)
end
function EntityDb.createEntity(self)
local id = uid()
self.entities[id] = true
return id
end
function EntityDb.addComponent(self, id, name, value)
self:checkIsValid(id)
if not self.components[name] then
self.components[name] = { count=0 }
end
local component = self.components[name]
component[id] = value
component.count = component.count + 1
end
local function shallowCopy(tbl)
local copy = {}
for k, v in pairs(tbl) do copy[k] = v end
return copy
end
function EntityDb.queryComponent(self, name)
local query = shallowCopy(self.components[name])
query.count = nil
return query
end
function EntityDb.queryEntity(self, id)
self:checkIsValid(id)
local query = {}
for name, component in pairs(self.components) do
query[name] = component[id]
end
return query
end
function EntityDb.removeComponent(self, id, name)
self:checkIsValid(id)
local component = self.components[name]
if component[id] ~= nil then
component[id] = nil
component.count = component.count - 1
if component.count == 0 then
self.components[name] = nil
end
end
end
function EntityDb.deleteEntity(self, id)
self:checkIsValid(id)
for name in pairs(self.components) do
self:removeComponent(id, name)
end
self.entities[id] = nil
end
return module
|