2025-06-01 13:47:58 +00:00
|
|
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
|
|
local types = require("../../ReplicatedStorage/types")
|
|
|
|
local ct = require("../../ReplicatedStorage/components")
|
|
|
|
local jecs = require(ReplicatedStorage.ecs)
|
|
|
|
local remotes = require("../../ReplicatedStorage/remotes")
|
|
|
|
local components = ct :: {[string]: jecs.Entity }
|
|
|
|
|
|
|
|
return function(world: ty.World)
|
|
|
|
|
|
|
|
--- integration test
|
|
|
|
|
|
|
|
-- for _ = 1, 10 do
|
|
|
|
-- local e = world:entity()
|
|
|
|
-- world:set(e, ct.TestA, true)
|
|
|
|
-- end
|
|
|
|
|
|
|
|
local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
|
|
|
|
local networked_components = {}
|
|
|
|
local networked_pairs = {}
|
|
|
|
|
|
|
|
for component in world:each(ct.Networked) do
|
|
|
|
local name = world:get(component, jecs.Name) :: string
|
|
|
|
if components[name] == nil then
|
|
|
|
continue
|
|
|
|
end
|
|
|
|
|
|
|
|
storages[component] = {}
|
|
|
|
|
|
|
|
table.insert(networked_components, component)
|
|
|
|
end
|
|
|
|
|
|
|
|
for relation in world:each(ct.NetworkedPair) do
|
|
|
|
local name = world:get(relation, jecs.Name) :: string
|
|
|
|
if not components[name] then
|
|
|
|
continue
|
|
|
|
end
|
|
|
|
table.insert(networked_pairs, relation)
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, component in networked_components do
|
|
|
|
local name = world:get(component, jecs.Name) :: string
|
|
|
|
if not components[name] then
|
|
|
|
error(`Networked Component (%id{component}%name{name})`)
|
|
|
|
end
|
|
|
|
local is_tag = jecs.is_tag(world, component)
|
|
|
|
local storage = storages[component]
|
|
|
|
if is_tag then
|
|
|
|
world:added(component, function(entity)
|
|
|
|
storage[entity] = true
|
|
|
|
end)
|
|
|
|
else
|
|
|
|
world:added(component, function(entity, _, value)
|
|
|
|
storage[entity] = value
|
|
|
|
end)
|
|
|
|
world:changed(component, function(entity, _, value)
|
|
|
|
storage[entity] = value
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
world:removed(component, function(entity)
|
|
|
|
storage[entity] = "jecs.Remove"
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, relation in networked_pairs do
|
|
|
|
world:added(relation, function(entity, id, value)
|
|
|
|
local is_tag = jecs.is_tag(world, id)
|
|
|
|
local storage = storages[id]
|
|
|
|
if not storage then
|
|
|
|
storage = {}
|
|
|
|
storages[id] = storage
|
|
|
|
end
|
|
|
|
if is_tag then
|
|
|
|
storage[entity] = true
|
|
|
|
else
|
|
|
|
storage[entity] = value
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
world:changed(relation, function(entity, id, value)
|
|
|
|
local is_tag = jecs.is_tag(world, id)
|
|
|
|
if is_tag then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local storage = storages[id]
|
|
|
|
if not storage then
|
|
|
|
storage = {}
|
|
|
|
storages[id] = storage
|
|
|
|
end
|
|
|
|
|
|
|
|
storage[entity] = value
|
|
|
|
end)
|
|
|
|
|
|
|
|
world:removed(relation, function(entity, id)
|
|
|
|
local storage = storages[id]
|
|
|
|
if not storage then
|
|
|
|
storage = {}
|
|
|
|
storages[id] = storage
|
|
|
|
end
|
|
|
|
|
|
|
|
storage[entity] = "jecs.Remove"
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
local players_added = collect(Players.PlayerAdded)
|
|
|
|
|
|
|
|
return function()
|
|
|
|
local snapshot_lazy: ty.Snapshot
|
|
|
|
local set_ids_lazy: { jecs.Entity }
|
|
|
|
|
|
|
|
for player in players_added do
|
|
|
|
if not snapshot_lazy then
|
|
|
|
snapshot_lazy, set_ids_lazy = {}, {}
|
|
|
|
|
|
|
|
for component, storage in storages do
|
|
|
|
local set_values = {}
|
|
|
|
local set_n = 0
|
|
|
|
|
|
|
|
local q = world:query(component)
|
|
|
|
local is_tag = jecs.is_tag(world, component)
|
|
|
|
for _, archetype in q:archetypes() do
|
|
|
|
local entities = archetype.entities
|
|
|
|
local entities_len = #entities
|
|
|
|
table.move(entities, 1, entities_len, set_n + 1, set_ids_lazy)
|
|
|
|
if is_tag then
|
|
|
|
set_values = table.create(entities_len, true)
|
|
|
|
else
|
|
|
|
local column = archetype.columns[archetype.records[component]]
|
|
|
|
table.move(column, 1, entities_len, set_n + 1, set_values)
|
|
|
|
end
|
|
|
|
|
|
|
|
set_n += entities_len
|
|
|
|
end
|
|
|
|
|
|
|
|
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
|
|
|
|
|
|
|
|
snapshot_lazy[tostring(component)] = {
|
|
|
|
set = if set_n > 0 then set else nil,
|
|
|
|
values = if set_n > 0 then set_values else nil,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
remotes.replication:FireClient(player, snapshot_lazy)
|
|
|
|
end
|
|
|
|
|
|
|
|
local snapshot = {} :: ty.Snapshot
|
|
|
|
|
|
|
|
local set_ids = {}
|
|
|
|
local removed_ids = {}
|
|
|
|
|
|
|
|
for component, storage in storages do
|
|
|
|
local set_values = {} :: { any }
|
|
|
|
local set_n = 0
|
|
|
|
local removed_n = 0
|
|
|
|
for e, v in storage do
|
|
|
|
if v ~= "jecs.Remove" then
|
|
|
|
set_n += 1
|
|
|
|
set_ids[set_n] = e
|
|
|
|
set_values[set_n] = v or true
|
|
|
|
elseif not world:contains(e) then
|
|
|
|
removed_n += 1
|
|
|
|
removed_ids[removed_n] = e
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
table.clear(storage)
|
|
|
|
|
|
|
|
local dirty = false
|
|
|
|
|
|
|
|
if set_n > 0 or removed_n > 0 then
|
|
|
|
dirty = true
|
|
|
|
end
|
|
|
|
|
|
|
|
if dirty then
|
|
|
|
local removed = table.move(removed_ids, 1, removed_n, 1, {}) :: { jecs.Entity }
|
|
|
|
local set = table.move(set_ids, 1, set_n, 1, {}) :: { jecs.Entity }
|
|
|
|
snapshot[tostring(component)] = {
|
|
|
|
set = if set_n > 0 then set else nil,
|
|
|
|
values = if set_n > 0 then set_values else nil,
|
|
|
|
removed = if removed_n > 0 then removed else nil
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if next(snapshot) ~= nil then
|
|
|
|
remotes.replication:FireAllClients(snapshot)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|