mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-10-31 09:09:18 +00:00 
			
		
		
		
	Fix export
This commit is contained in:
		
							parent
							
								
									4c105fa72c
								
							
						
					
					
						commit
						77bbe3e285
					
				
					 4 changed files with 31 additions and 337 deletions
				
			
		
							
								
								
									
										43
									
								
								lib/init.lua
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								lib/init.lua
									
									
									
									
									
								
							|  | @ -48,7 +48,8 @@ local HI_COMPONENT_ID = 256 | |||
| local ON_ADD = HI_COMPONENT_ID + 1 | ||||
| local ON_REMOVE = HI_COMPONENT_ID + 2 | ||||
| local ON_SET = HI_COMPONENT_ID + 3 | ||||
| local REST = HI_COMPONENT_ID + 4 | ||||
| local WILDCARD = HI_COMPONENT_ID + 4 | ||||
| local REST = HI_COMPONENT_ID + 5 | ||||
| 
 | ||||
| local function transitionArchetype( | ||||
| 	entityIndex: EntityIndex, | ||||
|  | @ -205,7 +206,7 @@ end | |||
| 
 | ||||
| local FLAGS_PAIR = 0x8 | ||||
| 
 | ||||
| local function addFlags(flags)  | ||||
| local function ADD_FLAGS(flags)  | ||||
|     local typeFlags = 0x0 | ||||
|     if flags.isPair then | ||||
|         typeFlags = bit32.bor(typeFlags, FLAGS_PAIR) -- HIGHEST bit in the ID. | ||||
|  | @ -231,17 +232,16 @@ local ECS_ENTITY_MASK = bit32.lshift(1, 24) | |||
| -- ECS_GENERATION_MASK           (0xFFFFull << 24) | ||||
| local ECS_GENERATION_MASK = bit32.lshift(1, 16) | ||||
| 
 | ||||
| local function newId(source: number, target: number)  | ||||
| local function NEW_ID(source: number, target: number)  | ||||
|     local e = source * 2^28 + target * ECS_ID_FLAGS_MASK | ||||
|     return e | ||||
| end | ||||
| 
 | ||||
| local function isPair(e: number)  | ||||
| local function ECS_IS_PAIR(e: number)  | ||||
|     return (e % 2^4) // FLAGS_PAIR ~= 0 | ||||
| end | ||||
| 
 | ||||
| function separate(entity: number) | ||||
|     local _typeFlags = entity % 0x10 | ||||
| function SEPARATE(entity: number) local _typeFlags = entity % 0x10 | ||||
|     entity //= ECS_ID_FLAGS_MASK | ||||
|     return entity // ECS_ENTITY_MASK, entity % ECS_GENERATION_MASK, _typeFlags | ||||
| end | ||||
|  | @ -258,9 +258,9 @@ local function ECS_ID(e: i53) | |||
| end | ||||
| 
 | ||||
| local function ECS_GENERATION_INC(e: i53) | ||||
|     local id, generation, flags = separate(e)     | ||||
|     local id, generation, flags = SEPARATE(e)     | ||||
| 
 | ||||
|     return newId(id, generation + 1) + flags | ||||
|     return NEW_ID(id, generation + 1) + flags | ||||
| end | ||||
| 
 | ||||
| -- gets the high ID | ||||
|  | @ -274,7 +274,7 @@ end | |||
| local ECS_PAIR_SECOND = ECS_ID | ||||
| 
 | ||||
| local function ECS_PAIR(source: number, target: number) | ||||
|     local id = newId(ECS_PAIR_SECOND(target), ECS_PAIR_SECOND(source)) + addFlags({ isPair = true }) | ||||
|     local id = NEW_ID(ECS_PAIR_SECOND(target), ECS_PAIR_SECOND(source)) + ADD_FLAGS({ isPair = true }) | ||||
|     return id | ||||
| end | ||||
| 
 | ||||
|  | @ -283,16 +283,16 @@ local function getAlive(entityIndex: EntityIndex, id: i53) | |||
| end | ||||
| 
 | ||||
| local function ecs_get_source(entityIndex, e)  | ||||
|     assert(isPair(e)) | ||||
|     assert(ECS_IS_PAIR(e)) | ||||
|     return getAlive(entityIndex, ECS_PAIR_FIRST(e)) | ||||
| end | ||||
| local function ecs_get_target(entityIndex, e)  | ||||
|     assert(isPair(e)) | ||||
|     assert(ECS_IS_PAIR(e)) | ||||
|     return getAlive(entityIndex, ECS_PAIR_SECOND(e)) | ||||
| end | ||||
| 
 | ||||
| local function nextEntityId(entityIndex, index: i24)  | ||||
| 	local id = newId(index, 0) | ||||
| 	local id = NEW_ID(index, 0) | ||||
| 	entityIndex.sparse[id] = { | ||||
| 		dense = index | ||||
| 	} :: Record	 | ||||
|  | @ -583,6 +583,7 @@ function World.query(world: World, ...: i53): Query | |||
| 
 | ||||
| 	local firstArchetypeMap | ||||
| 	local componentIndex = world.componentIndex | ||||
| 	local entityIndex = world.entityIndex | ||||
| 
 | ||||
| 	for _, componentId in components do | ||||
| 		local map = componentIndex[componentId] | ||||
|  | @ -602,6 +603,22 @@ function World.query(world: World, ...: i53): Query | |||
| 		local skip = false | ||||
| 
 | ||||
| 		for i, componentId in components do | ||||
| 			if ECS_IS_PAIR(componentId) then  | ||||
| 				local source = ecs_get_source(entityIndex, componentId)	 | ||||
| 				local target = ecs_get_target(entityIndex, componentId) | ||||
| 
 | ||||
| 				if target == WILDCARD then | ||||
| 					local matched = false | ||||
| 					for c in archetypeRecords do  | ||||
| 						if ecs_get_source(entityIndex, c) == source then  | ||||
| 							matched = true | ||||
| 						end | ||||
| 					end	 | ||||
| 					if matched then  | ||||
| 						fr | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 			local index = archetypeRecords[componentId] | ||||
| 			if not index then | ||||
| 				skip = true | ||||
|  | @ -768,7 +785,7 @@ return table.freeze({ | |||
| 	ON_REMOVE = ON_REMOVE; | ||||
| 	ON_SET = ON_SET; | ||||
| 	ECS_ID = ECS_ID, | ||||
| 	IS_PAIR = isPair, | ||||
| 	ECS_IS_PAIR = ECS_IS_PAIR, | ||||
| 	ECS_PAIR = ECS_PAIR, | ||||
| 	ECS_GENERATION = ECS_GENERATION, | ||||
| 	ECS_GENERATION_INC = ECS_GENERATION_INC, | ||||
|  |  | |||
|  | @ -1,320 +0,0 @@ | |||
| local jecs = require(script.Parent) | ||||
| local world = jecs.World.new() | ||||
| 
 | ||||
| local A, B, C, D = world:entity(), world:entity(), world:entity(), world:entity() | ||||
| local E, F, G, H = world:entity(), world:entity(), world:entity(), world:entity() | ||||
| print("A", A) | ||||
| print("B", B) | ||||
| print("C", C) | ||||
| print("D", D) | ||||
| print("E", E) | ||||
| print("F", F) | ||||
| print("G", G) | ||||
| print("H", H) | ||||
| 
 | ||||
| local common = 0 | ||||
| local N = 2^16-2 | ||||
| local archetypes = {} | ||||
| local function flip()  | ||||
| 	return math.random() >= 0.5 | ||||
| end | ||||
| 
 | ||||
| local amountOfCombination = 0 | ||||
| for i = 1, N do  | ||||
| 	local entity = world:entity() | ||||
| 	local combination = "" | ||||
| 
 | ||||
| 	if flip() then  | ||||
| 		combination ..= "2_" | ||||
| 		world:set(entity, B, { value = true}) | ||||
| 	end | ||||
| 	if flip() then  | ||||
| 		combination ..= "3_" | ||||
| 		world:set(entity, C, { value = true}) | ||||
| 	end | ||||
| 	if flip() then  | ||||
| 		combination ..= "4_" | ||||
| 		world:set(entity, D, { value = true}) | ||||
| 	end | ||||
| 	if flip() then  | ||||
| 		combination ..= "5_" | ||||
| 		world:set(entity, E, { value = true}) | ||||
| 	end | ||||
| 	if flip() then  | ||||
| 		combination ..= "6_" | ||||
| 		world:set(entity, F, { value = true}) | ||||
| 	end | ||||
| 	if flip() then  | ||||
| 		combination ..= "7_" | ||||
| 		world:set(entity, G, { value = true}) | ||||
| 	end | ||||
| 	if flip() then  | ||||
| 		combination ..= "8" | ||||
| 		world:set(entity, H, { value = true}) | ||||
| 	end | ||||
| 
 | ||||
| 	if #combination == 7 then  | ||||
| 		combination = "1_" .. combination | ||||
| 		common += 1 | ||||
| 		world:set(entity, A, { value = true}) | ||||
| 	end | ||||
| 
 | ||||
| 	if combination:find("2")  | ||||
| 		and combination:find("3")  | ||||
| 		and combination:find("4") | ||||
| 		and combination:find("6") | ||||
| 	then  | ||||
| 		amountOfCombination += 1 | ||||
| 	end | ||||
| 	archetypes[combination] = true | ||||
| end | ||||
| 
 | ||||
| return function() | ||||
| 	describe("World", function() | ||||
| 		it("should add component", function() | ||||
| 			local id = world:entity() | ||||
| 			world:set(id, A, true) | ||||
| 			world:set(id, B, 1) | ||||
| 
 | ||||
| 			local id1 = world:entity() | ||||
| 			world:set(id1, A, "hello") | ||||
| 			expect(world:get(id, A)).to.equal(true) | ||||
| 			expect(world:get(id, B)).to.equal(1) | ||||
| 			expect(world:get(id1, A)).to.equal("hello") | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should remove component", function()  | ||||
| 			local Tag = world:entity() | ||||
| 			local entities = {} | ||||
| 			for i = 1, 10 do  | ||||
| 				local entity = world:entity() | ||||
| 				entities[i] = entity | ||||
| 				world:set(entity, Tag) | ||||
| 			end | ||||
| 
 | ||||
| 			for i = 1, 10 do  | ||||
| 				local entity = entities[i] | ||||
| 				expect(world:get(entity, Tag)).to.equal(nil) | ||||
| 				world:remove(entity, Tag) | ||||
| 			end | ||||
| 			 | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should override component data", function()  | ||||
| 		 | ||||
| 			local id = world:entity() | ||||
| 			world:set(id, A, true) | ||||
| 			expect(world:get(id, A)).to.equal(true) | ||||
| 
 | ||||
| 			world:set(id, A, false) | ||||
| 			expect(world:get(id, A)).to.equal(false) | ||||
| 
 | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should not query a removed component", function()  | ||||
| 			local Tag = world:entity() | ||||
| 			local AnotherTag = world:entity() | ||||
| 
 | ||||
| 			local entity = world:entity() | ||||
| 			world:set(entity, Tag) | ||||
| 			world:set(entity, AnotherTag) | ||||
| 			world:remove(entity, AnotherTag) | ||||
| 
 | ||||
| 			local added = 0 | ||||
| 			for e, t, a in world:query(Tag, AnotherTag) do  | ||||
| 				added += 1 | ||||
| 			end | ||||
| 			expect(added).to.equal(0) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should query correct number of compatible archetypes", function() | ||||
| 			local added = 0 | ||||
| 			for _ in world:query(B, C, D, F) do | ||||
| 				added += 1 | ||||
| 			end         | ||||
| 			expect(added).to.equal(amountOfCombination) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should not query poisoned players", function()  | ||||
| 			local Player = world:entity() | ||||
| 			local Health = world:entity() | ||||
| 			local Poison = world:entity() | ||||
| 
 | ||||
| 			local one = world:entity() | ||||
| 			world:set(one, Player, { name = "alice"}) | ||||
| 			world:set(one, Health, 100) | ||||
| 			world:set(one, Poison) | ||||
| 
 | ||||
| 			local two = world:entity() | ||||
| 			world:set(two, Player, { name = "bob"}) | ||||
| 			world:set(two, Health, 90) | ||||
| 
 | ||||
| 			local withoutCount = 0 | ||||
| 			for _id, _player in world:query(Player):without(Poison) do | ||||
| 				withoutCount += 1 | ||||
| 			end | ||||
| 
 | ||||
| 			expect(withoutCount).to.equal(1) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should allow calling world:entity before world:component", function()  | ||||
| 			for _ = 1, 256 do  | ||||
| 				world:entity() | ||||
| 			end	 | ||||
| 			expect(world:component()).to.be.ok() | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should skip iteration", function()  | ||||
| 			local Position, Velocity = world:entity(), world:entity() | ||||
| 			local e = world:entity() | ||||
| 			world:set(e, Position, Vector3.zero) | ||||
| 			world:set(e, Velocity, Vector3.one) | ||||
| 			local added = 0 | ||||
| 			for i in world:query(Position):without(Velocity) do | ||||
| 				added += 1 | ||||
| 			end         | ||||
| 			expect(added).to.equal(0) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should query all matching entities", function() | ||||
| 
 | ||||
| 			local world = jecs.World.new() | ||||
| 			local A = world:component() | ||||
| 			local B = world:component() | ||||
| 
 | ||||
| 			local entities = {} | ||||
| 			for i = 1, N do | ||||
| 				local id = world:entity() | ||||
| 
 | ||||
| 
 | ||||
| 				world:set(id, A, true) | ||||
| 				if i > 5 then world:set(id, B, true) end | ||||
| 				entities[i] = id | ||||
| 			end | ||||
| 
 | ||||
| 			for id in world:query(A) do | ||||
| 				local i = table.find(entities, id) | ||||
| 				expect(i).to.be.ok() | ||||
| 				table.remove(entities, i) | ||||
| 			end | ||||
| 
 | ||||
| 			expect(#entities).to.equal(0) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should query all matching entities when irrelevant component is removed", function() | ||||
| 
 | ||||
| 			 | ||||
| 			local world = jecs.World.new() | ||||
| 			local A = world:component() | ||||
| 			local B = world:component() | ||||
| 	 | ||||
| 			local entities = {} | ||||
| 			for i = 1, N do | ||||
| 				local id = world:entity() | ||||
| 	 | ||||
| 				world:set(id, A, true) | ||||
| 				world:set(id, B, true) | ||||
| 				if i > 5 then world:remove(id, B, true) end | ||||
| 				entities[i] = id | ||||
| 			end | ||||
| 	 | ||||
| 			local added = 0 | ||||
| 			for id in world:query(A) do | ||||
| 				added += 1 | ||||
| 				local i = table.find(entities, id) | ||||
| 				expect(i).to.be.ok() | ||||
| 				table.remove(entities, i) | ||||
| 			end | ||||
| 	 | ||||
| 			expect(added).to.equal(N) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should query all entities without B", function()  | ||||
| 			local world = jecs.World.new() | ||||
| 			local A = world:component() | ||||
| 			local B = world:component() | ||||
| 	 | ||||
| 			local entities = {} | ||||
| 			for i = 1, N do | ||||
| 				local id = world:entity() | ||||
| 	 | ||||
| 				world:set(id, A, true) | ||||
| 				if i < 5 then | ||||
| 					entities[i] = id | ||||
| 				else | ||||
| 					world:set(id, B, true) | ||||
| 				end | ||||
| 				 | ||||
| 			end | ||||
| 	 | ||||
| 			for id in world:query(A):without(B) do | ||||
| 				local i = table.find(entities, id) | ||||
| 				expect(i).to.be.ok() | ||||
| 				table.remove(entities, i) | ||||
| 			end | ||||
| 	 | ||||
| 			expect(#entities).to.equal(0) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should allow setting components in arbitrary order", function()  | ||||
| 			local world = jecs.World.new() | ||||
| 
 | ||||
| 			local Health = world:entity() | ||||
| 			local Poison = world:component() | ||||
| 
 | ||||
| 			local id = world:entity() | ||||
| 			world:set(id, Poison, 5) | ||||
| 			world:set(id, Health, 50) | ||||
| 
 | ||||
| 			expect(world:get(id, Poison)).to.equal(5) | ||||
| 		end) | ||||
| 
 | ||||
| 		it("Should allow deleting components", function()  | ||||
| 			local world = jecs.World.new() | ||||
| 
 | ||||
| 			local Health = world:entity() | ||||
| 			local Poison = world:component() | ||||
| 
 | ||||
| 			local id = world:entity() | ||||
| 			world:set(id, Poison, 5) | ||||
| 			world:set(id, Health, 50) | ||||
| 			world:delete(id) | ||||
| 
 | ||||
| 			expect(world:get(id, Poison)).to.never.be.ok() | ||||
| 			expect(world:get(id, Health)).to.never.be.ok() | ||||
| 		end) | ||||
| 
 | ||||
| 		it("should allow iterating the whole world", function()  | ||||
| 			local world = jecs.World.new() | ||||
| 
 | ||||
| 			local A, B = world:entity(), world:entity() | ||||
| 
 | ||||
| 			local eA = world:entity() | ||||
| 			world:set(eA, A, true) | ||||
| 			local eB = world:entity() | ||||
| 			world:set(eB, B, true) | ||||
| 			local eAB = world:entity() | ||||
| 			world:set(eAB, A, true) | ||||
| 			world:set(eAB, B, true) | ||||
| 
 | ||||
| 			local count = 0 | ||||
| 			for id, data in world do | ||||
| 				count += 1 | ||||
| 				if id == eA then | ||||
| 					expect(data[A]).to.be.ok() | ||||
| 					expect(data[B]).to.never.be.ok() | ||||
| 				elseif id == eB then | ||||
| 					expect(data[B]).to.be.ok() | ||||
| 					expect(data[A]).to.never.be.ok() | ||||
| 				elseif id == eAB then | ||||
| 					expect(data[A]).to.be.ok() | ||||
| 					expect(data[B]).to.be.ok() | ||||
| 				else | ||||
| 					error("unknown entity", id) | ||||
| 				end | ||||
| 			end | ||||
| 
 | ||||
| 			expect(count).to.equal(3) | ||||
| 		end) | ||||
| 	end) | ||||
| end | ||||
|  | @ -11,9 +11,6 @@ | |||
|       }, | ||||
|       "ReplicatedStorage": { | ||||
|         "$className": "ReplicatedStorage", | ||||
|         "DevPackages": { | ||||
|             "$path": "DevPackages" | ||||
|         }, | ||||
|         "Lib": { | ||||
|           "$path": "lib" | ||||
|         }, | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ local testkit = require("../testkit") | |||
| local jecs = require("../lib/init") | ||||
| local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION | ||||
| local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC | ||||
| local IS_PAIR = jecs.IS_PAIR | ||||
| local IS_PAIR = jecs.ECS_IS_PAIR | ||||
| local ECS_PAIR = jecs.ECS_PAIR | ||||
| local getAlive = jecs.getAlive | ||||
| local ecs_get_source = jecs.ecs_get_source | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue