mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 10:59:18 +00:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "0064f249247cbf5f80cf3e33ff1dcc16df1a6357" and "1d1b7b099aa0bd798ca0d6f6cb7d8c6354c12cf7" have entirely different histories.
		
	
	
		
			0064f24924
			...
			1d1b7b099a
		
	
		
					 6 changed files with 49 additions and 220 deletions
				
			
		| 
						 | 
				
			
			@ -1,67 +0,0 @@
 | 
			
		|||
--!optimize 2
 | 
			
		||||
--!native
 | 
			
		||||
--!strict
 | 
			
		||||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
local __ = jecs.Wildcard
 | 
			
		||||
local std = ReplicatedStorage.std
 | 
			
		||||
 | 
			
		||||
local world = require(std.world)
 | 
			
		||||
 | 
			
		||||
local Position = world:component() :: jecs.Entity<vector>
 | 
			
		||||
local Previous = jecs.Rest
 | 
			
		||||
local pre = jecs.pair(Position, Previous)
 | 
			
		||||
 | 
			
		||||
local added = world
 | 
			
		||||
	:query(Position)
 | 
			
		||||
	:without(pre)
 | 
			
		||||
	:cached()
 | 
			
		||||
local changed = world
 | 
			
		||||
	:query(Position, pre)
 | 
			
		||||
	:cached()
 | 
			
		||||
local removed = world
 | 
			
		||||
	:query(pre)
 | 
			
		||||
	:without(Position)
 | 
			
		||||
	:cached()
 | 
			
		||||
 | 
			
		||||
local children = {}
 | 
			
		||||
for i = 1, 10 do
 | 
			
		||||
	local e = world:entity()
 | 
			
		||||
	world:set(e, Position, vector.create(i, i, i))
 | 
			
		||||
	table.insert(children, e)
 | 
			
		||||
end
 | 
			
		||||
local function flip()
 | 
			
		||||
	return math.random() > 0.5
 | 
			
		||||
end
 | 
			
		||||
local function system()
 | 
			
		||||
	for i, child in children do
 | 
			
		||||
		world:set(child, Position, vector.create(i,i,i))
 | 
			
		||||
	end
 | 
			
		||||
	for e, p in added:iter() do
 | 
			
		||||
		world:set(e, pre, p)
 | 
			
		||||
	end
 | 
			
		||||
	for i, child in children do
 | 
			
		||||
		if flip() then
 | 
			
		||||
			world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	for e, new, old in changed:iter() do
 | 
			
		||||
		if new ~= old then
 | 
			
		||||
			world:set(e, pre, new)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for i, child in children do
 | 
			
		||||
		world:remove(child, Position)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for e in removed:iter() do
 | 
			
		||||
		world:remove(e, pre)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
local scheduler = require(std.scheduler)
 | 
			
		||||
 | 
			
		||||
scheduler.SYSTEM(system)
 | 
			
		||||
 | 
			
		||||
return 0
 | 
			
		||||
| 
						 | 
				
			
			@ -1,90 +0,0 @@
 | 
			
		|||
--!optimize 2
 | 
			
		||||
--!native
 | 
			
		||||
--!strict
 | 
			
		||||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
local __ = jecs.Wildcard
 | 
			
		||||
local std = ReplicatedStorage.std
 | 
			
		||||
 | 
			
		||||
local world = require(std.world)
 | 
			
		||||
 | 
			
		||||
local Position = world:component() :: jecs.Entity<vector>
 | 
			
		||||
local Previous = jecs.Rest
 | 
			
		||||
local pre = jecs.pair(Position, Previous)
 | 
			
		||||
 | 
			
		||||
local added = world
 | 
			
		||||
	:query(Position)
 | 
			
		||||
	:without(pre)
 | 
			
		||||
	:cached()
 | 
			
		||||
local changed = world
 | 
			
		||||
	:query(Position, pre)
 | 
			
		||||
	:cached()
 | 
			
		||||
local removed = world
 | 
			
		||||
	:query(pre)
 | 
			
		||||
	:without(Position)
 | 
			
		||||
	:cached()
 | 
			
		||||
 | 
			
		||||
local children = {}
 | 
			
		||||
for i = 1, 10 do
 | 
			
		||||
	local e = world:entity()
 | 
			
		||||
	world:set(e, Position, vector.create(i, i, i))
 | 
			
		||||
	table.insert(children, e)
 | 
			
		||||
end
 | 
			
		||||
local function flip()
 | 
			
		||||
	return math.random() > 0.5
 | 
			
		||||
end
 | 
			
		||||
local entity_index = world.entity_index
 | 
			
		||||
local function copy(archetypes, id)
 | 
			
		||||
	for _, archetype in archetypes do
 | 
			
		||||
 | 
			
		||||
		local to = jecs.archetype_traverse_add(world, pre, archetype)
 | 
			
		||||
		local columns = to.columns
 | 
			
		||||
		local records = to.records
 | 
			
		||||
		local old = columns[records[pre].column]
 | 
			
		||||
		local new =	columns[records[id].column]
 | 
			
		||||
 | 
			
		||||
		if to ~= archetype then
 | 
			
		||||
			for _, entity in archetype.entities do
 | 
			
		||||
				local r = jecs.entity_index_try_get_fast(entity_index, entity)
 | 
			
		||||
				jecs.entity_move(entity_index, entity, r, to)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		table.move(new, 1, #new, 1, old)
 | 
			
		||||
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
local function system2()
 | 
			
		||||
	for i, child in children do
 | 
			
		||||
		world:set(child, Position, vector.create(i,i,i))
 | 
			
		||||
	end
 | 
			
		||||
	for e, p in added:iter() do
 | 
			
		||||
	end
 | 
			
		||||
	copy(added:archetypes(), Position)
 | 
			
		||||
	for i, child in children do
 | 
			
		||||
		if flip() then
 | 
			
		||||
			world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for e, new, old in changed:iter() do
 | 
			
		||||
		if new ~= old then
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	copy(changed:archetypes(), Position)
 | 
			
		||||
 | 
			
		||||
	for i, child in children do
 | 
			
		||||
		world:remove(child, Position)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for e in removed:iter() do
 | 
			
		||||
		world:remove(e, pre)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
local scheduler = require(std.scheduler)
 | 
			
		||||
 | 
			
		||||
scheduler.SYSTEM(system2)
 | 
			
		||||
 | 
			
		||||
return 0
 | 
			
		||||
| 
						 | 
				
			
			@ -9,23 +9,15 @@ local cts = require(std.components)
 | 
			
		|||
local Model = cts.Model
 | 
			
		||||
local Transform = cts.Transform
 | 
			
		||||
 | 
			
		||||
local moved_models = world:query(Model, Transform):cached()
 | 
			
		||||
local updated_models = {}
 | 
			
		||||
local i = 0
 | 
			
		||||
local function processed(n)
 | 
			
		||||
	i += 1
 | 
			
		||||
	if i > n then
 | 
			
		||||
		i = 0
 | 
			
		||||
		return true
 | 
			
		||||
	end
 | 
			
		||||
	return false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local moving_models = world:query(Transform, Model):cached()
 | 
			
		||||
local function move(dt: number)
 | 
			
		||||
	for entity, model in moved_models do
 | 
			
		||||
		if updated_models[entity] then
 | 
			
		||||
			updated_models[entity] = nil
 | 
			
		||||
			model.PrimaryPart.CFrame = transform
 | 
			
		||||
	for _, transform, model in moving_models do
 | 
			
		||||
		local cf = transform.new
 | 
			
		||||
		if cf ~= transform.old then
 | 
			
		||||
			local part = model.PrimaryPart :: BasePart
 | 
			
		||||
			local origo = part.CFrame
 | 
			
		||||
			part.CFrame = origo:Lerp(cf, 1)
 | 
			
		||||
			transform.old = cf
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +25,11 @@ end
 | 
			
		|||
local function syncTransforms()
 | 
			
		||||
	for _, id, cf in blink.UpdateTransform.Iter() do
 | 
			
		||||
		local e = ref("server-" .. tostring(id))
 | 
			
		||||
		world:set(e, Transform, cf)
 | 
			
		||||
		moved_models[e] = true
 | 
			
		||||
		local transform = world:get(e, Transform)
 | 
			
		||||
		if not transform then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		transform.new = cf
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,4 +38,4 @@ local scheduler = require(std.scheduler)
 | 
			
		|||
scheduler.SYSTEM(move)
 | 
			
		||||
scheduler.SYSTEM(syncTransforms)
 | 
			
		||||
 | 
			
		||||
return 0
 | 
			
		||||
return 0
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
local jecs = require("@jecs")
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
 | 
			
		||||
local world = jecs.World.new()
 | 
			
		||||
local Name = world:component()
 | 
			
		||||
| 
						 | 
				
			
			@ -13,37 +12,39 @@ local function name(e)
 | 
			
		|||
	return world:get(e, Name)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local Position = named(world.component, "Position") :: jecs.Entity<vector>
 | 
			
		||||
local Position = named(world.component, "Position") :: jecs.Entity<Vector3>
 | 
			
		||||
local Previous = jecs.Rest
 | 
			
		||||
local PreviousPosition = jecs.pair(Previous, Position)
 | 
			
		||||
 | 
			
		||||
local added = world
 | 
			
		||||
	:query(Position)
 | 
			
		||||
	:without(pair(Previous, Position))
 | 
			
		||||
	:without(PreviousPosition)
 | 
			
		||||
	:cached()
 | 
			
		||||
local changed = world
 | 
			
		||||
	:query(Position, pair(Previous, Position))
 | 
			
		||||
	:query(Position, PreviousPosition)
 | 
			
		||||
	:cached()
 | 
			
		||||
local removed = world
 | 
			
		||||
	:query(pair(Previous, Position))
 | 
			
		||||
	:query(PreviousPosition)
 | 
			
		||||
	:without(Position)
 | 
			
		||||
	:cached()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local e1 = named(world.entity, "e1")
 | 
			
		||||
world:set(e1, Position, vector.create(10, 20, 30))
 | 
			
		||||
world:set(e1, Position, Vector3.new(10, 20, 30))
 | 
			
		||||
 | 
			
		||||
local e2 = named(world.entity, "e2")
 | 
			
		||||
world:set(e2, Position, vector.create(10, 20, 30))
 | 
			
		||||
for entity, p in added do
 | 
			
		||||
	print(`Added {name(entity)}: \{{p.x}, {p.y}, {p.z}}`)
 | 
			
		||||
	world:set(entity, pair(Previous, Position), p)
 | 
			
		||||
world:set(e2, Position, Vector3.new(10, 20, 30))
 | 
			
		||||
 | 
			
		||||
for e, p in added:iter() do
 | 
			
		||||
	print(`Added {e}: \{{p.X}, {p.Y}, {p.Z}}`)
 | 
			
		||||
	world:set(e, PreviousPosition, p)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
world:set(e1, Position, vector.create(999, 999, 1998))
 | 
			
		||||
world:set(e1, Position, "")
 | 
			
		||||
 | 
			
		||||
for _, archetype in changed:archetypes() do
 | 
			
		||||
for e, new, old in changed:iter() do
 | 
			
		||||
	if new ~= old then
 | 
			
		||||
		print(`{name(e)}'s Position changed from \{{old.x}, {old.y}, {old.z}\} to \{{new.x}, {new.y}, {new.z}\}`)
 | 
			
		||||
		world:set(e, pair(Previous, Position), new)
 | 
			
		||||
		print(`{name(new)}'s Position changed from \{{old.X}, {old.Y}, {old.Z}\} to \{{new.X}, {new.Y}, {new.Z}\}`)
 | 
			
		||||
		world:set(e, PreviousPosition, new)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +52,6 @@ world:remove(e2, Position)
 | 
			
		|||
 | 
			
		||||
for e in removed:iter() do
 | 
			
		||||
	print(`Position was removed from {name(e)}`)
 | 
			
		||||
	world:remove(e, pair(Previous, Position))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Output:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1144,9 +1144,6 @@ do
 | 
			
		|||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local sparse_array = entity_index.sparse_array
 | 
			
		||||
		local dense_array = entity_index.dense_array
 | 
			
		||||
 | 
			
		||||
		if idr_t then
 | 
			
		||||
			for archetype_id in idr_t.cache do
 | 
			
		||||
				local children = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -1177,14 +1174,24 @@ do
 | 
			
		|||
						else
 | 
			
		||||
							local on_remove = id_record.hooks.on_remove
 | 
			
		||||
							local to = archetype_traverse_remove(world, id, idr_t_archetype)
 | 
			
		||||
							local empty = #to.types == 0
 | 
			
		||||
							for i = n, 1, -1 do
 | 
			
		||||
								local child = children[i]
 | 
			
		||||
								if on_remove then
 | 
			
		||||
									on_remove(child)
 | 
			
		||||
							if on_remove then
 | 
			
		||||
								if to then
 | 
			
		||||
									for i = n, 1, -1 do
 | 
			
		||||
										local child = children[i]
 | 
			
		||||
										on_remove(children[i])
 | 
			
		||||
										local r = entity_index_try_get_fast(entity_index, child) :: Record
 | 
			
		||||
										entity_move(entity_index, child, r, to)
 | 
			
		||||
									end
 | 
			
		||||
								else
 | 
			
		||||
									for i = n, 1, -1 do
 | 
			
		||||
										local child = children[i]
 | 
			
		||||
										on_remove(child)
 | 
			
		||||
									end
 | 
			
		||||
								end
 | 
			
		||||
								local r = sparse_array[ECS_ENTITY_T_LO(child)]
 | 
			
		||||
								if not empty then
 | 
			
		||||
							elseif to then
 | 
			
		||||
								for i = n, 1, -1 do
 | 
			
		||||
									local child = children[i]
 | 
			
		||||
									local r = entity_index_try_get_fast(entity_index, child) :: Record
 | 
			
		||||
									entity_move(entity_index, child, r, to)
 | 
			
		||||
								end
 | 
			
		||||
							end
 | 
			
		||||
| 
						 | 
				
			
			@ -1196,6 +1203,7 @@ do
 | 
			
		|||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local dense_array = entity_index.dense_array
 | 
			
		||||
		local index_of_deleted_entity = record.dense
 | 
			
		||||
		local index_of_last_alive_entity = entity_index.alive_count
 | 
			
		||||
		entity_index.alive_count = index_of_last_alive_entity - 1
 | 
			
		||||
| 
						 | 
				
			
			@ -2188,7 +2196,7 @@ function World.new()
 | 
			
		|||
	return self
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
export type Entity<T = unknown> = {__T: T}
 | 
			
		||||
export type Entity<T = nil> = number & { __T: T }
 | 
			
		||||
 | 
			
		||||
export type Id<T = nil> =
 | 
			
		||||
    | Entity<T>
 | 
			
		||||
| 
						 | 
				
			
			@ -2354,8 +2362,6 @@ return {
 | 
			
		|||
	archetype_traverse_add = archetype_traverse_add,
 | 
			
		||||
	archetype_traverse_remove = archetype_traverse_remove,
 | 
			
		||||
 | 
			
		||||
	entity_move = entity_move,
 | 
			
		||||
 | 
			
		||||
	entity_index_try_get = entity_index_try_get,
 | 
			
		||||
	entity_index_try_get_any = entity_index_try_get_any,
 | 
			
		||||
	entity_index_try_get_fast = entity_index_try_get_fast,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1045,21 +1045,6 @@ TEST("world:component()", function()
 | 
			
		|||
end)
 | 
			
		||||
 | 
			
		||||
TEST("world:delete", function()
 | 
			
		||||
	do CASE "delete recycled entity id used as component"
 | 
			
		||||
		local world = world_new()
 | 
			
		||||
		local id = world:entity()
 | 
			
		||||
		world:add(id, jecs.Component)
 | 
			
		||||
 | 
			
		||||
		local e = world:entity()
 | 
			
		||||
		world:set(e, id, 1)
 | 
			
		||||
		CHECK(world:get(e, id) == 1)
 | 
			
		||||
		world:delete(id)
 | 
			
		||||
		local recycled = world:entity()
 | 
			
		||||
		world:add(recycled, jecs.Component)
 | 
			
		||||
		world:set(e, recycled, 1)
 | 
			
		||||
		CHECK(world:has(recycled, jecs.Component))
 | 
			
		||||
		CHECK(world:get(e, recycled) == 1)
 | 
			
		||||
	end
 | 
			
		||||
	do
 | 
			
		||||
		CASE("bug: Empty entity does not respect cleanup policy")
 | 
			
		||||
		local world = world_new()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue