Update replication demo
Some checks failed
analysis / Run Luau Analyze (push) Has been cancelled
deploy-docs / build (push) Has been cancelled
publish-npm / publish (push) Has been cancelled
unit-testing / Run Luau Tests (push) Has been cancelled
deploy-docs / Deploy (push) Has been cancelled

This commit is contained in:
Ukendio 2025-09-29 23:02:54 +02:00
parent 698854d11b
commit 57e653fa78
2 changed files with 117 additions and 85 deletions

View file

@ -40,11 +40,18 @@ end
-- local rel_render = `e{jecs.ECS_ID(rel)}v{jecs.ECS_GENERATION(rel)}`
-- local tgt_render = `e{jecs.ECS_ID(tgt)}v{jecs.ECS_GENERATION(tgt)}`
local function ecs_deser_pairs(world, token)
local tokens = string.split(token, ",")
local rel = tonumber(tokens[1])
local tgt = tonumber(tokens[2])
-- local function ecs_deser_pairs_str(world, token)
-- local tokens = string.split(token, ",")
-- local rel = tonumber(tokens[1]) :: jecs.Entity
-- local tgt = tonumber(tokens[2]) :: jecs.Entity
-- rel = ecs_ensure_entity(world, rel)
-- tgt = ecs_ensure_entity(world, tgt)
-- return jecs.pair(rel, tgt)
-- end
local function ecs_deser_pairs(world, rel, tgt)
rel = ecs_ensure_entity(world, rel)
tgt = ecs_ensure_entity(world, tgt)
@ -53,18 +60,24 @@ end
local snapshots = collect(remotes.replication.OnClientEvent)
return function(world: types.World)
return function(world: jecs.World)
for entity in world:each(components.Destroy) do
client_ids[entity] = nil
end
for snapshot in snapshots do
for ser_id, map in snapshot do
local id = tonumber(ser_id)
if not id then
id = ecs_deser_pairs(world, ser_id)
else
local id = (tonumber(ser_id) :: any) :: jecs.Entity
if jecs.IS_PAIR(id) and map.pair then
id = ecs_deser_pairs(world, map.relation, map.target)
elseif id then
id = ecs_ensure_entity(world, id)
end
-- if not id then
-- id = ecs_deser_pairs_str(world, ser_id)
-- else
-- id = ecs_ensure_entity(world, id)
-- end
local members = world:get(id, components.NetworkedMembers)
local set = map.set
if set then
@ -74,11 +87,23 @@ return function(world: types.World)
world:add(entity, id)
end
else
local t = os.clock()
local values = map.values
local values = map.values :: { any }
for i, entity in set do
entity = ecs_ensure_entity(world, entity)
world:set(entity, id, values[i])
local value = values[i]
if members then
for _, member in members do
local data = value[member] :: {jecs.Entity} | jecs.Entity -- targets
if typeof(data) == "table" then
for pos, tgt in data :: { jecs.Entity } do
data[pos] = ecs_ensure_entity(world, tgt)
end
else
value[member] = ecs_ensure_entity(world, data :: any)
end
end
end
world:set(entity, id, value)
end
end
end

View file

@ -8,24 +8,16 @@ local jecs = require(ReplicatedStorage.ecs)
local collect = require(ReplicatedStorage.collect)
local ty = require(ReplicatedStorage.types)
return function(world: ty.World)
--- integration test
-- for _ = 1, 10 do
-- local e = world:entity()
-- world:set(e, ct.TestA, true)
-- end
return function(world: jecs.World)
local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
local networked_components = {}
local networked_pairs = {}
for component in world:each(ct.Networked) do
local name = assert(world:get(component, jecs.Name), "Invalid component")
local name = world:get(component, jecs.Name)
assert(name)
if components[name] == nil then
error("Invalid component:"..name)
end
storages[component] = {}
@ -34,7 +26,8 @@ return function(world: ty.World)
end
for relation in world:each(ct.NetworkedPair) do
local name = world:get(relation, jecs.Name) :: string
local name = world:get(relation, jecs.Name)
assert(name)
if not components[name] then
error("Invalid component")
end
@ -43,7 +36,7 @@ return function(world: ty.World)
for _, component in networked_components do
local name = world:get(component, jecs.Name)
if not components[name] then
if not name or not components[name] then
-- error("Invalid component")
error(`Networked Component (%id{component}%name{name})`)
end
@ -68,7 +61,7 @@ return function(world: ty.World)
end
for _, relation in networked_pairs do
world:added(relation, function(entity, id, value)
world:added(relation, function(entity: jecs.Entity, id: jecs.Id, value)
local is_tag = jecs.is_tag(world, id)
local storage = storages[id]
if not storage then
@ -82,7 +75,7 @@ return function(world: ty.World)
end
end)
world:changed(relation, function(entity, id, value)
world:changed(relation, function(entity: jecs.Id, id: jecs.Id, value)
local is_tag = jecs.is_tag(world, id)
if is_tag then
return
@ -111,8 +104,8 @@ return function(world: ty.World)
-- local requested_snapshots = collect(remotes.request_snapshot.OnServerEvent)
local players_added = collect(Players.PlayerAdded)
return function()
local snapshot_lazy: ty.Snapshot
return function(_, dt: number)
local snapshot_lazy: ty.snapshot
local set_ids_lazy: { jecs.Entity }
-- In the future maybe it should be requested by the player instead when they
@ -132,9 +125,7 @@ return function(world: ty.World)
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
if not is_tag then
local column = archetype.columns_map[component]
table.move(column, 1, entities_len, set_n + 1, set_values)
end
@ -144,25 +135,31 @@ return function(world: ty.World)
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
local ser_id: string = nil :: any
if jecs.IS_PAIR(component) then
ser_id = `{jecs.pair_first(world, component)},{jecs.pair_first(world, component)}`
else
ser_id = tostring(component)
end
snapshot_lazy[ser_id] = {
local map = {
set = if set_n > 0 then set else nil,
values = if set_n > 0 then set_values else nil,
}
if jecs.IS_PAIR(component) then
map.relation = jecs.pair_first(world, component)
map.target = jecs.pair_second(world, component)
map.pair = true
end
snapshot_lazy[tostring(component)] = map
end
end
remotes.replication:FireClient(player, snapshot_lazy)
end
local snapshot = {} :: ty.Snapshot
-- accumulator += dt
-- Purposely sending less diffs of the world because doing it at 60hz
-- gets expensive. But this requires interpolated elements in the scene.
-- if accumulator > 1/60 then
-- accumulator = 0
local snapshot = {} :: ty.snapshot
local set_ids = {}
local removed_ids = {}
@ -193,23 +190,33 @@ return function(world: ty.World)
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 }
local ser_id: string = nil :: any
if jecs.IS_PAIR(component) then
ser_id = `{jecs.pair_first(world, component)},{jecs.pair_second(world, component)}`
else
ser_id = tostring(component)
end
-- local ser_id: string = nil :: any
snapshot[ser_id] = {
-- if jecs.IS_PAIR(component) then
-- ser_id = `{jecs.pair_first(world, component)},{jecs.pair_second(world, component)}`
-- else
-- ser_id = tostring(component)
-- end
local map = {
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
removed = if removed_n > 0 then removed else nil,
}
if jecs.IS_PAIR(component) then
map.relation = jecs.pair_first(world, component)
map.target = jecs.pair_second(world, component)
map.pair = true
end
snapshot[tostring(component)] = map
end
end
if next(snapshot) ~= nil then
remotes.replication:FireAllClients(snapshot)
-- print(snapshot)
end
end
end