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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
win = require("window")
log = require("log")
world = require("world")
Component = require("ecs.component")
GraphicsComponent = require("ecs.graphics")
NetworkedComponent = require("ecs.networked")
PredictedComponent = require("ecs.predicted")
ScriptComponent = require("ecs.script")
CharacterControllerComponent = require("ecs.char_controller")
shader_world = require("shaders.world")
--win.scene:append(shader_world)
--pp = am.postprocess({
--clear_color: vec4(0.2, 0.2, 0.3, 1),
--depth_buffer: true,
----stencil_buffer: true,
--width: win.width,
--height: win.height,
--})
pp = am.group!
pp\append(shader_world.node)
GraphicsComponent.node = pp
ScriptComponent.node = pp
PredictedComponent.node = pp
NetworkedComponent.node = pp
-- Base entity of our ECS
class Entity
id: 1
new: (id, components) => -- [id], [components]
ent_reg = world.domain == "client" and world.level.entities or world.level_sync.ents
id_cur = id or #ent_reg + 1
@created_at = debug.traceback!
assert(id_cur < 10, "Got more then 10 entities")
while ent_reg[id_cur] != nil
id_cur += 1
log.debug("Want to create an entity with id" .. id_cur .. ", registry is:" .. tostring(ent_reg) .. "\n" .. debug.traceback(), {"ecs"})
assert(ent_reg[id_cur] == nil, "Attempted to create entity with the same id as one that already exists: " .. tostring(id_cur) .. ", entities were:" .. tostring(ent_reg))
log.info("Creating entity " .. id_cur .. " domain is: " .. world.domain, {"ecs"})
ent_reg[id_cur] = @
@reg = ent_reg
@id = id_cur
-- Bookkeeping for O(1) access for components
@c_by_type = {}
@c_by_name = {}
--Entity is responsible for the component -> entity link
@components = components or {}
for name, component in pairs(@components)
component.entity = @
@c_by_type[component.__class] = @c_by_type[component.__class] or {}
@c_by_type[component.__class][name] = component
for name, component in pairs(@components)
assert(component.join, "Component " .. name .. " does not have a join method.")
component\join(@)
component\post_join(@)
add: (component, cid) =>
assert(component, "Failed to find component:" .. tostring(component))
assert(type(component) == "table", "Component must be a table")
assert(component.__class, "Component must have a class")
component.entity = @
component\join(@, cid)
component\post_join(@, cid)
if cid == nil
cid = #@components + 1
while @components[cid]
cid += 1
assert(@components[cid] == nil, "Already had a component with id" .. tostring(cid))
@components[cid] = component
@c_by_type[component.__class] = @c_by_type[component.__class] or {}
@c_by_type[component.__class][cid] = component
assert(@c_by_name[component.name] == nil, "Duplicate components named" .. component.name)
@c_by_name[component.name] = component
@
remove: (cid) =>
component = @components[cid]
component.entity = nil
component\leave(@)
component
@components[cid] = nil
@c_by_type[component.__class][cid] = nil
@c_by_name[component.name] = nil
destroy: () =>
for name, component in pairs(@components)
@remove(name)
@reg[@id] = nil
get: (cid) =>
@components[cid]
serialize: () =>
components = {}
for i, component in pairs(@components)
component_name = component.__class.__name
if not component.serialize
log.warn("Component:" .. component_name .. " does not have a .serialize()", {"ecs"})
continue
component_data = component\serialize!
components[i] = string.format(
"%d\0%s%d\0%s",
#component_name, component_name,
#component_data, component_data
)
return table.concat(components, "\0")
--@deserialize: (data) =>
--error("Deserialize called")
--log.info("Deserializing entity from data of length " .. tostring(data and #data or 0), {"ecs"})
--ent = @!
---- Empty payload -> empty entity
--if not data or #data == 0
--return ent
---- Map serialized component type names back to their classes
--component_types = {
--[GraphicsComponent.__name]: GraphicsComponent
--[NetworkedComponent.__name]: NetworkedComponent
--[PredictedComponent.__name]: PredictedComponent
--[ScriptComponent.__name]: ScriptComponent
--[CharacterControllerComponent.__name]: CharacterControllerComponent
--[PhysicsComponent.__name]: PhysicsComponent
--}
--pointer = 1
--len = #data
--while pointer <= len
---- Read component name length (digits up to first NUL)
--name_len_end = string.find(data, "\0", pointer)
--if not name_len_end
--break
--name_len_str = string.sub(data, pointer, name_len_end - 1)
--name_len = tonumber(name_len_str)
--if not name_len or name_len <= 0
--break
--pointer = name_len_end + 1
---- Read component type name
--component_name = string.sub(data, pointer, pointer + name_len - 1)
--pointer += name_len
---- Read component data length (digits up to next NUL)
--data_len_end = string.find(data, "\0", pointer)
--if not data_len_end
--break
--data_len_str = string.sub(data, pointer, data_len_end - 1)
--data_len = tonumber(data_len_str)
--if not data_len or data_len < 0
--break
--pointer = data_len_end + 1
---- Read component payload
--component_data = string.sub(data, pointer, pointer + data_len - 1)
--pointer += data_len
---- Skip inter-component separator if present
--if string.sub(data, pointer, pointer) == "\0"
--pointer += 1
--ctype = component_types[component_name]
--if not ctype
--log.warn("Unknown component type during deserialize: " .. tostring(component_name), {"ecs"})
--continue
--if not ctype.deserialize
--log.warn("Component type " .. tostring(component_name) .. " does not implement .deserialize()", {"ecs"})
--continue
---- Let the component class decide how to interpret its payload
--component = ctype.deserialize(component_data)
--if component
--ent\add(component)
--return ent
__tostring: () =>
components_str = tostring(@components)
return string.format("%s : {\n%s\n}\t%q",@@__name, components_str\gsub("^","\t"), @created_at)
class PhysicsComponent extends Component
new: (name, properties) =>
assert(properties and properties.shape, "Failed to find a shape for physics component")
super(name, properties)
{
:Component
:Entity
:NetworkedComponent
:PredictedComponent
:GraphicsComponent
:PhysicsComponent
:ScriptComponent
:CharacterControllerComponent
node: pp
}
|