mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-10-31 01:09:16 +00:00 
			
		
		
		
	Merge branch 'main' of https://github.com/Ukendio/jecs into add-docs
This commit is contained in:
		
						commit
						09ae7794ea
					
				
					 9 changed files with 117 additions and 781 deletions
				
			
		
							
								
								
									
										73
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| name: Release | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     tags: ["v*"] | ||||
| 
 | ||||
| jobs: | ||||
|   build: | ||||
|     name: Build | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout Project | ||||
|         uses: actions/checkout@v3 | ||||
| 
 | ||||
|       - name: Install Aftman | ||||
|         uses: ok-nick/setup-aftman@v0.3.0 | ||||
| 
 | ||||
|       - name: Install Dependencies | ||||
|         run: wally install | ||||
| 
 | ||||
|       - name: Build | ||||
|         run: rojo build --output build.rbxm default.project.json | ||||
| 
 | ||||
|       - name: Upload Build Artifact | ||||
|         uses: actions/upload-artifact@v3 | ||||
|         with: | ||||
|           name: build | ||||
|           path: build.rbxm | ||||
| 
 | ||||
|   release: | ||||
|     name: Release | ||||
|     needs: [build] | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: write | ||||
|     steps: | ||||
|       - name: Checkout Project | ||||
|         uses: actions/checkout@v3 | ||||
| 
 | ||||
|       - name: Download Jecs Build | ||||
|         uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: build | ||||
|           path: build | ||||
| 
 | ||||
|       - name: Rename Build | ||||
|         run: mv build/build.rbxm jecs.rbxm | ||||
| 
 | ||||
|       - name: Create Release | ||||
|         uses: softprops/action-gh-release@v1 | ||||
|         with: | ||||
|           name: Matter ${{ github.ref_name }} | ||||
|           body: | | ||||
|             Matter ${{ github.ref_name }} is now available! | ||||
|           files: | | ||||
|             jecs.rbxm | ||||
| 
 | ||||
|   publish: | ||||
|     name: Publish | ||||
|     needs: [release] | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout Project | ||||
|         uses: actions/checkout@v3 | ||||
| 
 | ||||
|       - name: Install Aftman | ||||
|         uses: ok-nick/setup-aftman@v0.3.0 | ||||
| 
 | ||||
|       - name: Wally Login | ||||
|         run: wally login --token ${{ secrets.WALLY_AUTH_TOKEN }} | ||||
| 
 | ||||
|       - name: Publish | ||||
|         run: wally publish | ||||
							
								
								
									
										31
									
								
								bench.project.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								bench.project.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| { | ||||
|     "name": "jecs-test", | ||||
|     "tree": { | ||||
|         "$className": "DataModel", | ||||
|         "StarterPlayer": { | ||||
|             "$className": "StarterPlayer", | ||||
|             "StarterPlayerScripts": { | ||||
|             "$className": "StarterPlayerScripts", | ||||
|             "$path": "tests" | ||||
|             } | ||||
|         }, | ||||
|         "ReplicatedStorage": { | ||||
|             "$className": "ReplicatedStorage", | ||||
|             "Lib": { | ||||
|                 "$path": "lib" | ||||
|             }, | ||||
|             "rgb": { | ||||
|                 "$path": "rgb.lua" | ||||
|             }, | ||||
|             "benches": { | ||||
|                 "$path": "benches" | ||||
|             }, | ||||
|             "mirror": { | ||||
|                 "$path": "mirror" | ||||
|             }, | ||||
|             "DevPackages": { | ||||
|                 "$path": "benches/visual/DevPackages" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,372 +0,0 @@ | |||
| local testkit = require("../testkit") | ||||
| local jecs = require("../lib/init") | ||||
| local ecr = require("../DevPackages/_Index/centau_ecr@0.8.0/ecr/src/ecr") | ||||
| 
 | ||||
| 
 | ||||
| local BENCH, START = testkit.benchmark() | ||||
| 
 | ||||
| local function TITLE(title: string) | ||||
| 	print() | ||||
| 	print(testkit.color.white(title)) | ||||
| end | ||||
| 
 | ||||
| local N = 2^16-2 | ||||
| 
 | ||||
| type i53 = number | ||||
| 
 | ||||
| do TITLE "create" | ||||
| 	BENCH("entity", function() | ||||
| 		local world = jecs.World.new() | ||||
| 		for i = 1, START(N) do | ||||
| 			world:entity() | ||||
| 		end | ||||
| 	end) | ||||
| end | ||||
| 
 | ||||
| --- component benchmarks | ||||
| 
 | ||||
| --todo: perform the same benchmarks for multiple components.? | ||||
| -- these kind of operations only support 1 component at a time, which is | ||||
| -- a shame, especially for archetypes where moving components is expensive. | ||||
| 
 | ||||
| do TITLE "set" | ||||
| 	BENCH("add 1 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		 | ||||
| 		for i = 1, N do | ||||
| 			entities[i] = world:entity() | ||||
| 		end | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			world:set(entities[i], A, i) | ||||
| 		end | ||||
| 	end) | ||||
| 
 | ||||
| 	BENCH("change 1 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		local e = world:entity() | ||||
| 		world:set(e, A, 1) | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			world:set(e, A, 2) | ||||
| 		end | ||||
| 	end) | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| do TITLE "remove" | ||||
| 	BENCH("1 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			entities[i] = id | ||||
| 			world:set(id, A, true) | ||||
| 		end | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			world:remove(entities[i], A) | ||||
| 		end | ||||
| 
 | ||||
| 	end) | ||||
| end | ||||
| 
 | ||||
| do TITLE "get" | ||||
| 	BENCH("1 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 
 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			entities[i] = id | ||||
| 			world:set(id, A, true) | ||||
| 		end | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			-- ? curious why the overhead is roughly 80 ns. | ||||
| 			world:get(entities[i], A) | ||||
| 		end | ||||
| 
 | ||||
| 	end) | ||||
| 
 | ||||
| 	BENCH("2 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		local B = world:component() | ||||
| 		 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			entities[i] = id | ||||
| 			world:set(id, A, true) | ||||
| 			world:set(id, B, true) | ||||
| 		end | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			world:get(entities[i], A, B) | ||||
| 		end | ||||
| 
 | ||||
| 	end) | ||||
| 
 | ||||
| 	BENCH("3 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		local B = world:component() | ||||
| 		local C = world:component() | ||||
| 		 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			entities[i] = id | ||||
| 			world:set(id, A, true) | ||||
| 			world:set(id, B, true) | ||||
| 			world:set(id, C, true) | ||||
| 		end | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			world:get(entities[i], A, B, C) | ||||
| 		end | ||||
| 
 | ||||
| 	end) | ||||
| 
 | ||||
| 	BENCH("4 component", function()  | ||||
| 		local world = jecs.World.new() | ||||
| 		local entities = {} | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		local B = world:component() | ||||
| 		local C = world:component() | ||||
| 		local D = world:component() | ||||
| 		 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			entities[i] = id | ||||
| 			world:set(id, A, true) | ||||
| 			world:set(id, B, true) | ||||
| 			world:set(id, C, true) | ||||
| 			world:set(id, D, true) | ||||
| 		end | ||||
| 
 | ||||
| 		for i = 1, START(N) do | ||||
| 			world:get(entities[i], A, B, C, D) | ||||
| 		end | ||||
| 
 | ||||
| 	end) | ||||
| end | ||||
| 
 | ||||
| do TITLE (testkit.color.white_underline("Jecs query")) | ||||
| 
 | ||||
| 	local function count(query: () -> ()) | ||||
| 		local n = 0 | ||||
| 		for _ in query do | ||||
| 			n += 1 | ||||
| 		end | ||||
| 		return n | ||||
| 	end | ||||
| 
 | ||||
| 	local function flip() | ||||
| 		return math.random() > 0.5 | ||||
| 	end | ||||
| 
 | ||||
| 	local function view_bench( | ||||
| 		world: jecs.World, | ||||
| 		A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53, I: i53 | ||||
| 	) | ||||
| 
 | ||||
| 		BENCH("1 component", function() | ||||
| 			START(count(world:query(A))) | ||||
| 			for _ in world:query(A) do end | ||||
| 		end) | ||||
| 
 | ||||
| 		BENCH("2 component", function() | ||||
| 			START(count(world:query(A, B))) | ||||
| 			for _ in world:query(A, B) do end | ||||
| 		end) | ||||
| 
 | ||||
| 		BENCH("4 component", function() | ||||
| 			START(count(world:query(A, B, C, D))) | ||||
| 			for _ in world:query(A, B, C, D) do end | ||||
| 		end) | ||||
| 
 | ||||
| 		BENCH("8 component", function() | ||||
| 			START(count(world:query(A, B, C, D, E, F, G, H))) | ||||
| 			for _ in world:query(A, B, C, D, E, F, G, H) do end | ||||
| 		end) | ||||
| 	end | ||||
| 
 | ||||
| 	do TITLE "random components" | ||||
| 
 | ||||
| 		local world = jecs.World.new() | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		local B = world:component() | ||||
| 		local C = world:component() | ||||
| 		local D = world:component() | ||||
| 		local E = world:component() | ||||
| 		local F = world:component() | ||||
| 		local G = world:component() | ||||
| 		local H = world:component() | ||||
| 		local I = world:component() | ||||
| 
 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			if flip() then world:set(id, A, true) end | ||||
| 			if flip() then world:set(id, B, true) end | ||||
| 			if flip() then world:set(id, C, true) end | ||||
| 			if flip() then world:set(id, D, true) end | ||||
| 			if flip() then world:set(id, E, true) end | ||||
| 			if flip() then world:set(id, F, true) end | ||||
| 			if flip() then world:set(id, G, true) end | ||||
| 			if flip() then world:set(id, H, true) end | ||||
| 			if flip() then world:set(id, I, true) end | ||||
| 			 | ||||
| 		end | ||||
| 
 | ||||
| 		view_bench(world, A, B, C, D, E, F, G, H, I) | ||||
| 
 | ||||
| 	end | ||||
| 
 | ||||
| 	do TITLE "one component in common" | ||||
| 
 | ||||
| 		local world = jecs.World.new() | ||||
| 
 | ||||
| 		local A = world:component() | ||||
| 		local B = world:component() | ||||
| 		local C = world:component() | ||||
| 		local D = world:component() | ||||
| 		local E = world:component() | ||||
| 		local F = world:component() | ||||
| 		local G = world:component() | ||||
| 		local H = world:component() | ||||
| 		local I = world:component() | ||||
| 
 | ||||
| 		for i = 1, N do | ||||
| 			local id = world:entity() | ||||
| 			local a = true | ||||
| 			if flip() then world:set(id, B, true) else a = false end | ||||
| 			if flip() then world:set(id, C, true) else a = false end | ||||
| 			if flip() then world:set(id, D, true) else a = false end | ||||
| 			if flip() then world:set(id, E, true) else a = false end | ||||
| 			if flip() then world:set(id, F, true) else a = false end | ||||
| 			if flip() then world:set(id, G, true) else a = false end | ||||
| 			if flip() then world:set(id, H, true) else a = false end | ||||
| 			if flip() then world:set(id, I, true) else a = false end | ||||
| 			if a then world:set(id, A, true) end | ||||
| 			 | ||||
| 		end | ||||
| 
 | ||||
| 		view_bench(world, A, B, C, D, E, F, G, H, I) | ||||
| 
 | ||||
| 	end | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| do TITLE (testkit.color.white_underline("ECR query")) | ||||
| 
 | ||||
|     local A = ecr.component() | ||||
|     local B = ecr.component() | ||||
|     local C = ecr.component() | ||||
|     local D = ecr.component() | ||||
|     local E = ecr.component() | ||||
|     local F = ecr.component() | ||||
|     local G = ecr.component() | ||||
|     local H = ecr.component() | ||||
|     local I = ecr.component() | ||||
| 
 | ||||
| 	local function count(query: () -> ()) | ||||
| 		local n = 0 | ||||
| 		for _ in query do | ||||
| 			n += 1 | ||||
| 		end | ||||
| 		return n | ||||
| 	end | ||||
| 
 | ||||
| 	local function flip() | ||||
| 		return math.random() > 0.5 | ||||
| 	end | ||||
| 
 | ||||
| 	local function view_bench( | ||||
| 		world: ecr.Registry, | ||||
| 		A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53, I: i53 | ||||
| 	) | ||||
| 
 | ||||
| 		BENCH("1 component", function() | ||||
| 			START(count(world:view(A))) | ||||
| 			for _ in world:view(A) do end | ||||
| 		end) | ||||
| 
 | ||||
| 		BENCH("2 component", function() | ||||
| 			START(count(world:view(A, B))) | ||||
| 			for _ in world:view(A, B) do end | ||||
| 		end) | ||||
| 
 | ||||
| 		BENCH("4 component", function() | ||||
| 			START(count(world:view(A, B, C, D))) | ||||
| 			for _ in world:view(A, B, C, D) do end | ||||
| 		end) | ||||
| 
 | ||||
| 		BENCH("8 component", function() | ||||
| 			START(count(world:view(A, B, C, D, E, F, G, H))) | ||||
| 			for _ in world:view(A, B, C, D, E, F, G, H) do end | ||||
| 		end) | ||||
| 	end | ||||
| 
 | ||||
| 
 | ||||
| 	do TITLE "random components" | ||||
|         local world = ecr.registry() | ||||
| 
 | ||||
| 		for i = 1, N do | ||||
| 			local id = world.create() | ||||
| 			if flip() then world:set(id, A, true) end | ||||
| 			if flip() then world:set(id, B, true) end | ||||
| 			if flip() then world:set(id, C, true) end | ||||
| 			if flip() then world:set(id, D, true) end | ||||
| 			if flip() then world:set(id, E, true) end | ||||
| 			if flip() then world:set(id, F, true) end | ||||
| 			if flip() then world:set(id, G, true) end | ||||
| 			if flip() then world:set(id, H, true) end | ||||
| 			if flip() then world:set(id, I, true) end | ||||
| 			 | ||||
| 		end | ||||
| 
 | ||||
| 		view_bench(world, A, B, C, D, E, F, G, H, I) | ||||
| 
 | ||||
| 	end | ||||
| 
 | ||||
| 	do TITLE "one component in common" | ||||
| 
 | ||||
| 		local world = ecr.registry() | ||||
| 
 | ||||
| 		for i = 1, N do | ||||
| 			local id = world.create() | ||||
| 			local a = true | ||||
| 			if flip() then world:set(id, B, true) else a = false end | ||||
| 			if flip() then world:set(id, C, true) else a = false end | ||||
| 			if flip() then world:set(id, D, true) else a = false end | ||||
| 			if flip() then world:set(id, E, true) else a = false end | ||||
| 			if flip() then world:set(id, F, true) else a = false end | ||||
| 			if flip() then world:set(id, G, true) else a = false end | ||||
| 			if flip() then world:set(id, H, true) else a = false end | ||||
| 			if flip() then world:set(id, I, true) else a = false end | ||||
| 			if a then world:set(id, A, true) end | ||||
| 			 | ||||
| 		end | ||||
| 
 | ||||
| 		view_bench(world, A, B, C, D, E, F, G, H, I) | ||||
| 
 | ||||
| 	end | ||||
| 
 | ||||
| end | ||||
|  | @ -2,7 +2,6 @@ | |||
| --!native | ||||
| 
 | ||||
| local ReplicatedStorage = game:GetService("ReplicatedStorage") | ||||
| local rgb = require(ReplicatedStorage.rgb) | ||||
| local Matter = require(ReplicatedStorage.DevPackages.Matter) | ||||
| local jecs = require(ReplicatedStorage.Lib) | ||||
| local ecr = require(ReplicatedStorage.DevPackages.ecr) | ||||
|  |  | |||
							
								
								
									
										11
									
								
								benches/visual/wally.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								benches/visual/wally.toml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| [package] | ||||
| name = "private/private" | ||||
| version = "0.1.0-rc.6" | ||||
| registry = "https://github.com/UpliftGames/wally-index" | ||||
| realm = "shared" | ||||
| include = ["default.project.json", "lib/**", "lib", "wally.toml", "README.md"] | ||||
| exclude = ["**"] | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| Matter = "matter-ecs/matter@0.8.0" | ||||
| ecr = "centau/ecr@0.8.0" | ||||
|  | @ -1,382 +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() | ||||
| 				end | ||||
| 			end | ||||
| 
 | ||||
| 			expect(count).to.equal(5) | ||||
| 		end) | ||||
| 
 | ||||
|         it("should allow querying for relations", function() | ||||
|             local world = jecs.World.new() | ||||
|             local Eats = world:entity() | ||||
|             local Apples = world:entity() | ||||
|             local bob = world:entity() | ||||
|              | ||||
|             world:set(bob, jecs.pair(Eats, Apples), true) | ||||
|             for e, bool in world:query(jecs.pair(Eats, Apples)) do  | ||||
|                 expect(e).to.equal(bob) | ||||
|                 expect(bool).to.equal(bool) | ||||
|             end | ||||
|         end) | ||||
|          | ||||
|         it("should allow wildcards in queries", function() | ||||
|             local world = jecs.World.new() | ||||
|             local Eats = world:entity() | ||||
|             local Apples = world:entity() | ||||
|             local bob = world:entity() | ||||
|              | ||||
|             world:set(bob, jecs.pair(Eats, Apples), "bob eats apples") | ||||
|             for e, data in world:query(jecs.pair(Eats, jecs.w)) do  | ||||
|                 expect(e).to.equal(bob) | ||||
|                 expect(data).to.equal("bob eats apples") | ||||
|             end | ||||
|             for e, data in world:query(jecs.pair(jecs.w, Apples)) do  | ||||
|                 expect(e).to.equal(bob) | ||||
|                 expect(data).to.equal("bob eats apples") | ||||
|             end | ||||
|         end) | ||||
| 
 | ||||
|         it("should match against multiple pairs", function() | ||||
|             local world = jecs.World.new() | ||||
|             local pair = jecs.pair | ||||
|             local Eats = world:entity() | ||||
|             local Apples = world:entity() | ||||
|             local Oranges =world:entity() | ||||
|             local bob = world:entity() | ||||
|             local alice = world:entity() | ||||
|              | ||||
|             world:set(bob, pair(Eats, Apples), "bob eats apples") | ||||
|             world:set(alice, pair(Eats, Oranges), "alice eats oranges") | ||||
|              | ||||
|             local w = jecs.Wildcard | ||||
|              | ||||
|             local count = 0 | ||||
|             for e, data in world:query(pair(Eats, w)) do  | ||||
|                 count += 1 | ||||
|                 if e == bob then  | ||||
|                     expect(data).to.equal("bob eats apples") | ||||
|                 else | ||||
|                     expect(data).to.equal("alice eats oranges") | ||||
|                 end | ||||
|             end | ||||
| 
 | ||||
|             expect(count).to.equal(2) | ||||
|             count = 0 | ||||
| 
 | ||||
|             for e, data in world:query(pair(w, Apples)) do  | ||||
|                 count += 1 | ||||
|                 expect(data).to.equal("bob eats apples") | ||||
|             end | ||||
|             expect(count).to.equal(1) | ||||
|         end) | ||||
| 	end) | ||||
| end | ||||
|  | @ -22,18 +22,6 @@ | |||
|         }, | ||||
|         "mirror": { | ||||
|           "$path": "mirror" | ||||
|         }, | ||||
|         "DevPackages": { | ||||
|           "$path": "DevPackages" | ||||
|         } | ||||
|       }, | ||||
|       "TestService": { | ||||
|         "$properties": { | ||||
|           "ExecuteWithStudioRun": true | ||||
|         }, | ||||
|         "$className": "TestService", | ||||
|         "run": { | ||||
|           "$path": "tests.server.lua" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| local ReplicatedStorage = game:GetService("ReplicatedStorage") | ||||
| 
 | ||||
| require(ReplicatedStorage.DevPackages.TestEZ).TestBootstrap:run({ | ||||
| 	ReplicatedStorage.Lib, | ||||
| 	nil, | ||||
| 	{ | ||||
| 		noXpcallByDefault = true, | ||||
| 	}, | ||||
| }) | ||||
|  | @ -1,10 +1,7 @@ | |||
| [package] | ||||
| name = "ukendio/jecs" | ||||
| version = "0.1.0-rc.6" | ||||
| version = "0.1.0" | ||||
| registry = "https://github.com/UpliftGames/wally-index" | ||||
| realm = "shared" | ||||
| include = ["default.project.json", "lib/**", "lib", "wally.toml", "README.md"] | ||||
| exclude = ["**"] | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| TestEZ = "roblox/testez@0.4.1" | ||||
| exclude = ["**"] | ||||
		Loading…
	
		Reference in a new issue