mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 18:39:19 +00:00 
			
		
		
		
	Update networking example
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	
This commit is contained in:
		
							parent
							
								
									53f705ac2e
								
							
						
					
					
						commit
						a6ba9f4bd5
					
				
					 13 changed files with 252 additions and 129 deletions
				
			
		| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
--!native
 | 
			
		||||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local Matter = require(ReplicatedStorage.DevPackages.matter)
 | 
			
		||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
 | 
			
		||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
 | 
			
		||||
local newWorld = Matter.World.new()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
--!strict
 | 
			
		||||
local RunService = game:GetService("RunService")
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
local types = require("./types")
 | 
			
		||||
| 
						 | 
				
			
			@ -5,31 +7,58 @@ local types = require("./types")
 | 
			
		|||
local Networked = jecs.tag()
 | 
			
		||||
local NetworkedPair = jecs.tag()
 | 
			
		||||
 | 
			
		||||
local Renderable = jecs.component() :: jecs.Id<Instance>
 | 
			
		||||
jecs.meta(Renderable, Networked)
 | 
			
		||||
local InstanceMapping = jecs.component() :: jecs.Id<Instance>
 | 
			
		||||
jecs.meta(InstanceMapping, jecs.OnAdd, function(component)
 | 
			
		||||
	jecs.meta(component, jecs.OnAdd, function(entity, _, instance)
 | 
			
		||||
		if RunService:IsServer() then
 | 
			
		||||
			instance:SetAttribute("entity_server")
 | 
			
		||||
		else
 | 
			
		||||
			instance:SetAttribute("entity_client")
 | 
			
		||||
		end
 | 
			
		||||
	end)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
local function networked_id(ct)
 | 
			
		||||
	jecs.meta(ct, Networked)
 | 
			
		||||
	return ct
 | 
			
		||||
end
 | 
			
		||||
local function networked_pair(ct)
 | 
			
		||||
	jecs.meta(ct, NetworkedPair)
 | 
			
		||||
	return ct
 | 
			
		||||
end
 | 
			
		||||
local function instance_mapping_id(ct)
 | 
			
		||||
	jecs.meta(ct, InstanceMapping)
 | 
			
		||||
	return ct
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local Poison = jecs.component() :: jecs.Id<number>
 | 
			
		||||
jecs.meta(Poison, Networked)
 | 
			
		||||
 | 
			
		||||
local Health = jecs.component() :: jecs.Id<number>
 | 
			
		||||
jecs.meta(Health, Networked)
 | 
			
		||||
 | 
			
		||||
local Player = jecs.component() :: jecs.Id<Player>
 | 
			
		||||
jecs.meta(Player, Networked)
 | 
			
		||||
 | 
			
		||||
local Renderable = jecs.component() :: types.Id<Instance>
 | 
			
		||||
local Poison = jecs.component() :: types.Id<number>
 | 
			
		||||
local Health = jecs.component() :: types.Id<number>
 | 
			
		||||
local Player = jecs.component() :: types.Id<Player>
 | 
			
		||||
local Debuff = jecs.tag() :: types.Entity
 | 
			
		||||
local Lifetime = jecs.component() :: types.Id<{
 | 
			
		||||
	duration: number,
 | 
			
		||||
	created: number
 | 
			
		||||
}>
 | 
			
		||||
local Destroy = jecs.tag()
 | 
			
		||||
 | 
			
		||||
local components = {
 | 
			
		||||
	Renderable = Renderable,
 | 
			
		||||
	Player = Player,
 | 
			
		||||
	Poison = Poison,
 | 
			
		||||
	Health = Health,
 | 
			
		||||
	Renderable = networked_id(instance_mapping_id(Renderable)),
 | 
			
		||||
	Player = networked_id(Player),
 | 
			
		||||
	Poison = networked_id(Poison),
 | 
			
		||||
	Health = networked_id(Health),
 | 
			
		||||
	Lifetime = networked_id(Lifetime),
 | 
			
		||||
	Debuff = networked_id(Debuff),
 | 
			
		||||
	Destroy = networked_id(Destroy),
 | 
			
		||||
 | 
			
		||||
	-- We have to define that some builtin IDs can also be networked
 | 
			
		||||
	ChildOf = networked_pair(jecs.ChildOf),
 | 
			
		||||
 | 
			
		||||
	Networked = Networked,
 | 
			
		||||
	NetworkedPair = NetworkedPair,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for name, component in components do
 | 
			
		||||
for name, component in components :: {[string]: types.Id<any> } do
 | 
			
		||||
	jecs.meta(component, jecs.Name, name)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,4 +10,5 @@ local world = observers_add(jecs.world())
 | 
			
		|||
 | 
			
		||||
local systems = ReplicatedStorage.systems
 | 
			
		||||
SYSTEM(world, systems.receive_replication)
 | 
			
		||||
SYSTEM(world, systems.entities_delete)
 | 
			
		||||
RUN(world)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								demo/src/ReplicatedStorage/systems/entities_delete.luau
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										13
									
								
								demo/src/ReplicatedStorage/systems/entities_delete.luau
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
 | 
			
		||||
--!strict
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
local ct = require(ReplicatedStorage.components)
 | 
			
		||||
 | 
			
		||||
local function entities_delete(world: types.World, dt: number)
 | 
			
		||||
	for e in world:each(ct.Destroy) do
 | 
			
		||||
		world:delete(e)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return entities_delete
 | 
			
		||||
| 
						 | 
				
			
			@ -2,45 +2,51 @@ local types = require("../types")
 | 
			
		|||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
			
		||||
local remotes = require("../remotes")
 | 
			
		||||
local collect = require("../collect")
 | 
			
		||||
local client_ids = {}
 | 
			
		||||
local components = require("../components")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local function ecs_map_get(world, id)
 | 
			
		||||
	local deserialised_id = client_ids[id]
 | 
			
		||||
local client_ids: {[jecs.Entity]: jecs.Entity } = {}
 | 
			
		||||
 | 
			
		||||
	if not deserialised_id then
 | 
			
		||||
		if world:has(id, jecs.Name) then
 | 
			
		||||
			deserialised_id = world:entity(id)
 | 
			
		||||
local function ecs_ensure_entity(world: jecs.World, id: jecs.Entity)
 | 
			
		||||
	local e = 0
 | 
			
		||||
 | 
			
		||||
	local ser_id = id
 | 
			
		||||
	local deser_id = client_ids[ser_id]
 | 
			
		||||
	if deser_id  then
 | 
			
		||||
		if deser_id == 0 then
 | 
			
		||||
			local new_id = world:entity()
 | 
			
		||||
			client_ids[ser_id] = new_id
 | 
			
		||||
			deser_id = new_id
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
			deserialised_id = world:entity()
 | 
			
		||||
		if not world:exists(ser_id)
 | 
			
		||||
			or (world:contains(ser_id) and not world:get(ser_id, jecs.Name))
 | 
			
		||||
		then
 | 
			
		||||
			deser_id = world:entity()
 | 
			
		||||
		else
 | 
			
		||||
			if world:contains(ser_id) and world:get(ser_id, jecs.Name) then
 | 
			
		||||
				deser_id = ser_id
 | 
			
		||||
			else
 | 
			
		||||
				deser_id = world:entity()
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		client_ids[ser_id] = deser_id
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
		client_ids[id] = deserialised_id
 | 
			
		||||
	e = deser_id
 | 
			
		||||
 | 
			
		||||
	return e
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
	-- local deserialised_id = client_ids[id]
 | 
			
		||||
	-- if not deserialised_id then
 | 
			
		||||
	-- 	if world:has(id, jecs.Name) then
 | 
			
		||||
	-- 		deserialised_id = world:entity(id)
 | 
			
		||||
	-- 	else
 | 
			
		||||
	-- 		if world:exists(id) then
 | 
			
		||||
	-- 			deserialised_id = world:entity()
 | 
			
		||||
	-- 		else
 | 
			
		||||
	-- 			deserialised_id = world:entity(id)
 | 
			
		||||
	-- 		end
 | 
			
		||||
	-- 	end
 | 
			
		||||
	-- 	client_ids[id] = deserialised_id
 | 
			
		||||
	-- 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])
 | 
			
		||||
 | 
			
		||||
	return deserialised_id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ecs_make_alive_id(world, id)
 | 
			
		||||
	local rel = jecs.ECS_PAIR_FIRST(id)
 | 
			
		||||
	local tgt = jecs.ECS_PAIR_SECOND(id)
 | 
			
		||||
 | 
			
		||||
	rel = ecs_map_get(world, rel)
 | 
			
		||||
	tgt = ecs_map_get(world, tgt)
 | 
			
		||||
	rel = ecs_ensure_entity(world, rel)
 | 
			
		||||
	tgt = ecs_ensure_entity(world, tgt)
 | 
			
		||||
 | 
			
		||||
	return jecs.pair(rel, tgt)
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -48,24 +54,30 @@ end
 | 
			
		|||
local snapshots = collect(remotes.replication.OnClientEvent)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World)
 | 
			
		||||
	for entity in world:each(components.Destroy) do
 | 
			
		||||
		client_ids[entity] = nil
 | 
			
		||||
	end
 | 
			
		||||
    for snapshot in snapshots do
 | 
			
		||||
        for id, map in snapshot do
 | 
			
		||||
        	id = tonumber(id)
 | 
			
		||||
            if jecs.IS_PAIR(id) then
 | 
			
		||||
            	id = ecs_make_alive_id(world, id)
 | 
			
		||||
        for ser_id, map in snapshot do
 | 
			
		||||
        	local id = tonumber(ser_id)
 | 
			
		||||
            if not id then
 | 
			
		||||
            	id = ecs_deser_pairs(world, ser_id)
 | 
			
		||||
            else
 | 
			
		||||
            	id = ecs_ensure_entity(world, id)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            local set = map.set
 | 
			
		||||
            if set then
 | 
			
		||||
                if jecs.is_tag(world, id) then
 | 
			
		||||
                   	for _, entity in set do
 | 
			
		||||
                       	entity = ecs_map_get(world, entity)
 | 
			
		||||
                       	entity = ecs_ensure_entity(world, entity)
 | 
			
		||||
                  		world:add(entity, id)
 | 
			
		||||
                   	end
 | 
			
		||||
                else
 | 
			
		||||
                	local t = os.clock()
 | 
			
		||||
	                local values = map.values
 | 
			
		||||
	                for i, entity in set do
 | 
			
		||||
						entity = ecs_map_get(world, entity)
 | 
			
		||||
						entity = ecs_ensure_entity(world, entity)
 | 
			
		||||
						world:set(entity, id, values[i])
 | 
			
		||||
                   	end
 | 
			
		||||
			    end
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +87,7 @@ return function(world: types.World)
 | 
			
		|||
 | 
			
		||||
            if removed then
 | 
			
		||||
                for _, entity in removed do
 | 
			
		||||
                    entity = ecs_map_get(world, entity)
 | 
			
		||||
                    entity = ecs_ensure_entity(world, entity)
 | 
			
		||||
                    if not world:contains(entity) then
 | 
			
		||||
                        continue
 | 
			
		||||
                    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,5 +15,8 @@ local systems = ServerScriptService.systems
 | 
			
		|||
SYSTEM(world, systems.replication)
 | 
			
		||||
SYSTEM(world, systems.players_added)
 | 
			
		||||
SYSTEM(world, systems.poison_hurts)
 | 
			
		||||
SYSTEM(world, systems.health_regen)
 | 
			
		||||
SYSTEM(world, systems.lifetimes_expire)
 | 
			
		||||
SYSTEM(world, systems.life_is_painful)
 | 
			
		||||
SYSTEM(world, ReplicatedStorage.systems.entities_delete)
 | 
			
		||||
RUN(world, 0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								demo/src/ServerScriptService/systems/health_regen.luau
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								demo/src/ServerScriptService/systems/health_regen.luau
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local ct = require(ReplicatedStorage.components)
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World, dt: number)
 | 
			
		||||
	for e in world:query(ct.Player):without(ct.Health) do
 | 
			
		||||
		world:set(e, ct.Health, 100)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for e, health in world:query(ct.Health) do
 | 
			
		||||
		if math.random() < 1 / 60 / 30 then
 | 
			
		||||
			world:set(e, ct.Health, 100)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +1,19 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local ct = require(ReplicatedStorage.components)
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World, dt: number)
 | 
			
		||||
	for e in world:query(ct.Player):without(ct.Health) do
 | 
			
		||||
		world:set(e, ct.Health, 100)
 | 
			
		||||
	end
 | 
			
		||||
	for e in world:query(ct.Player, ct.Health):without(ct.Poison) do
 | 
			
		||||
		world:set(e, ct.Poison, 10)
 | 
			
		||||
	if math.random() < (1 / 60 / 7) then
 | 
			
		||||
		for e in world:each(ct.Health) do
 | 
			
		||||
			local poison = world:entity()
 | 
			
		||||
			world:add(poison, ct.Debuff)
 | 
			
		||||
			world:add(poison, jecs.pair(jecs.ChildOf, e))
 | 
			
		||||
			world:set(poison, ct.Poison, 10)
 | 
			
		||||
			world:set(poison, ct.Lifetime, {
 | 
			
		||||
				duration = 3,
 | 
			
		||||
				created = os.clock()
 | 
			
		||||
			})
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								demo/src/ServerScriptService/systems/lifetimes_expire.luau
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								demo/src/ServerScriptService/systems/lifetimes_expire.luau
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local ct = require(ReplicatedStorage.components)
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World, dt: number)
 | 
			
		||||
	for e, lifetime in world:query(ct.Lifetime) do
 | 
			
		||||
		if os.clock() > lifetime.created + lifetime.duration then
 | 
			
		||||
			world:add(e, ct.Destroy)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -12,9 +12,13 @@ return function(world: types.World, dt: number)
 | 
			
		|||
 | 
			
		||||
	for entity, player in world:query(ct.Player):without(ct.Renderable) do
 | 
			
		||||
		local character = player.Character
 | 
			
		||||
		if character then
 | 
			
		||||
		if not character then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		if not character.Parent then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		world:set(entity, ct.Renderable, character)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,18 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local ct = require(ReplicatedStorage.components)
 | 
			
		||||
return function(world, dt)
 | 
			
		||||
	for e, poison, health in world:query(ct.Poison, ct.Health) do
 | 
			
		||||
		local health_after_tick = health - poison * dt * 0.05
 | 
			
		||||
		if health_after_tick < 0 then
 | 
			
		||||
			world:remove(e, ct.Health)
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World, dt: number)
 | 
			
		||||
	for e, poison_tick in world:query(ct.Poison, jecs.pair(jecs.ChildOf, jecs.w)) do
 | 
			
		||||
		local tgt = world:target(e, jecs.ChildOf)
 | 
			
		||||
		local health = world:get(tgt, ct.Health)
 | 
			
		||||
		if not health then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		world:set(e, ct.Health, health_after_tick)
 | 
			
		||||
 | 
			
		||||
		if math.random() < 1 / 60 / 1 and health > 1 then
 | 
			
		||||
			world:set(tgt, ct.Health, health - 1)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,13 @@
 | 
			
		|||
 | 
			
		||||
--!strict
 | 
			
		||||
local Players = game:GetService("Players")
 | 
			
		||||
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 ct = require(ReplicatedStorage.components)
 | 
			
		||||
local components = ct :: { [string]: jecs.Entity }
 | 
			
		||||
local remotes = require(ReplicatedStorage.remotes)
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
local collect = require(ReplicatedStorage.collect)
 | 
			
		||||
local ty = require(ReplicatedStorage.types)
 | 
			
		||||
 | 
			
		||||
return function(world: ty.World)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,9 +23,10 @@ return function(world: ty.World)
 | 
			
		|||
    local networked_pairs = {}
 | 
			
		||||
 | 
			
		||||
    for component in world:each(ct.Networked) do
 | 
			
		||||
    	local name = world:get(component, jecs.Name) :: string
 | 
			
		||||
		local name = assert(world:get(component, jecs.Name), "Invalid component")
 | 
			
		||||
		if components[name] == nil then
 | 
			
		||||
			continue
 | 
			
		||||
			error("Invalid component:"..name)
 | 
			
		||||
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        storages[component] = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,14 +37,15 @@ return function(world: ty.World)
 | 
			
		|||
	for relation in world:each(ct.NetworkedPair) do
 | 
			
		||||
		local name = world:get(relation, jecs.Name) :: string
 | 
			
		||||
		if not components[name] then
 | 
			
		||||
			continue
 | 
			
		||||
			error("Invalid component")
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(networked_pairs, relation)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, component in networked_components do
 | 
			
		||||
		local name = world:get(component, jecs.Name) :: string
 | 
			
		||||
		local name = world:get(component, jecs.Name)
 | 
			
		||||
		if not components[name] then
 | 
			
		||||
			-- error("Invalid component")
 | 
			
		||||
			error(`Networked Component (%id{component}%name{name})`)
 | 
			
		||||
		end
 | 
			
		||||
		local is_tag = jecs.is_tag(world, component)
 | 
			
		||||
| 
						 | 
				
			
			@ -103,12 +109,16 @@ return function(world: ty.World)
 | 
			
		|||
	end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- local requested_snapshots = collect(remotes.request_snapshot.OnServerEvent)
 | 
			
		||||
    local players_added = collect(Players.PlayerAdded)
 | 
			
		||||
 | 
			
		||||
    return function()
 | 
			
		||||
        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
 | 
			
		||||
        -- are ready to receive the replication. Otherwise streaming could be complicated
 | 
			
		||||
        -- with intances references being nil.
 | 
			
		||||
		for player in players_added do
 | 
			
		||||
			if not snapshot_lazy then
 | 
			
		||||
				snapshot_lazy, set_ids_lazy =  {}, {}
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +136,7 @@ return function(world: ty.World)
 | 
			
		|||
						if is_tag then
 | 
			
		||||
							set_values = table.create(entities_len, true)
 | 
			
		||||
						else
 | 
			
		||||
							local column = archetype.columns[archetype.records[component]]
 | 
			
		||||
							local column = archetype.columns_map[component]
 | 
			
		||||
							table.move(column, 1, entities_len, set_n + 1, set_values)
 | 
			
		||||
						end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +145,15 @@ return function(world: ty.World)
 | 
			
		|||
 | 
			
		||||
					local set = table.move(set_ids_lazy, 1, set_n, 1, {})
 | 
			
		||||
 | 
			
		||||
					snapshot_lazy[tostring(component)] = {
 | 
			
		||||
					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] = {
 | 
			
		||||
	                    set = if set_n > 0 then set else nil,
 | 
			
		||||
	                    values = if set_n > 0 then set_values else nil,
 | 
			
		||||
	                }
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +177,7 @@ return function(world: ty.World)
 | 
			
		|||
		            set_n += 1
 | 
			
		||||
		            set_ids[set_n] = e
 | 
			
		||||
		            set_values[set_n] = v or true
 | 
			
		||||
		        elseif world:contains(e) then
 | 
			
		||||
		        elseif not world:contains(e) then
 | 
			
		||||
		            removed_n += 1
 | 
			
		||||
		            removed_ids[removed_n] = e
 | 
			
		||||
		        end
 | 
			
		||||
| 
						 | 
				
			
			@ -176,7 +194,15 @@ 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 }
 | 
			
		||||
	            snapshot[tostring(component)] = {
 | 
			
		||||
	            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
 | 
			
		||||
 | 
			
		||||
	            snapshot[ser_id] = {
 | 
			
		||||
                    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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,11 +24,6 @@ type Id<T=unknown> = jecs.Id<T>
 | 
			
		|||
local entity_visualiser = require("@tools/entity_visualiser")
 | 
			
		||||
local dwi = entity_visualiser.stringify
 | 
			
		||||
 | 
			
		||||
TEST("repro", function()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
TEST("bulk", function()
 | 
			
		||||
	local world = jecs.world()
 | 
			
		||||
	local A = world:component()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue