aboutsummaryrefslogtreecommitdiff
path: root/src/ecs.moon
diff options
context:
space:
mode:
Diffstat (limited to 'src/ecs.moon')
-rw-r--r--src/ecs.moon184
1 files changed, 184 insertions, 0 deletions
diff --git a/src/ecs.moon b/src/ecs.moon
new file mode 100644
index 0000000..61b73a5
--- /dev/null
+++ b/src/ecs.moon
@@ -0,0 +1,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
+}