mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-10-31 17:20:32 +00:00 
			
		
		
		
	Compare commits
	
		
			3 commits
		
	
	
		
			9d83c3bc13
			...
			045408af37
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 045408af37 | ||
|  | 59e7fd1f41 | ||
|  | f3befa3adb | 
					 4 changed files with 601 additions and 422 deletions
				
			
		|  | @ -5,21 +5,21 @@ local lifetime_tracker_add = require("@tools/lifetime_tracker") | ||||||
| local pe = require("@tools/entity_visualiser").prettify | local pe = require("@tools/entity_visualiser").prettify | ||||||
| local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false}) | local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false}) | ||||||
| local FriendsWith = world:component() | local FriendsWith = world:component() | ||||||
| local _1 = world:print_snapshot() | world:print_snapshot() | ||||||
| local e1 = world:entity() | local e1 = world:entity() | ||||||
| local e2 = world:entity() | local e2 = world:entity() | ||||||
| world:delete(e2) | world:delete(e2) | ||||||
| 
 | 
 | ||||||
| local _2 = world:print_snapshot() | world:print_snapshot() | ||||||
| local e3 = world:entity() | local e3 = world:entity() | ||||||
| world:add(e3, pair(ChildOf, e1)) | world:add(e3, pair(ChildOf, e1)) | ||||||
| local e4 = world:entity() | local e4 = world:entity() | ||||||
| world:add(e4, pair(FriendsWith, e3)) | world:add(e4, pair(FriendsWith, e3)) | ||||||
| local _3 = world:print_snapshot() | world:print_snapshot() | ||||||
| world:delete(e1) | world:delete(e1) | ||||||
| world:delete(e3) | world:delete(e3) | ||||||
| local _4 = world:print_snapshot() | world:print_snapshot() | ||||||
| world:print_entity_index() | world:print_entity_index() | ||||||
| world:entity() | world:entity() | ||||||
| world:entity() | world:entity() | ||||||
| local _5 = world:print_snapshot() | world:print_snapshot() | ||||||
|  |  | ||||||
|  | @ -117,6 +117,62 @@ local function name(world, e) | ||||||
| 	return world:get(e, jecs.Name) | 	return world:get(e, jecs.Name) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | TEST("#repro2", function() | ||||||
|  | 	local world = world_new() | ||||||
|  | 	local Lifetime = world:component() :: jecs.Id<number> | ||||||
|  | 	local Particle = world:entity() | ||||||
|  | 	local Beam = world:entity() | ||||||
|  | 
 | ||||||
|  | 	local entity = world:entity() | ||||||
|  | 	world:set(entity, pair(Lifetime, Particle), 1) | ||||||
|  | 	world:set(entity, pair(Lifetime, Beam), 2) | ||||||
|  | 	world:set(entity, pair(4, 5), 6) -- noise | ||||||
|  | 
 | ||||||
|  | 	local entity_visualizer = require("@tools/entity_visualiser") | ||||||
|  | 	entity_visualizer.components(world, entity) | ||||||
|  | 
 | ||||||
|  | 	for e in world:each(pair(Lifetime, __)) do | ||||||
|  | 		local i = 0 | ||||||
|  | 		local nth = world:target(e, Lifetime, i) | ||||||
|  | 		while nth do | ||||||
|  | 			entity_visualizer.components(world, e) | ||||||
|  | 
 | ||||||
|  | 			local data = world:get(e, pair(Lifetime, nth)) | ||||||
|  | 			data -= 1 | ||||||
|  | 		 	if data <= 0 then | ||||||
|  |                 world:remove(e, pair(Lifetime, nth)) | ||||||
|  |             else | ||||||
|  |                 world:set(e, pair(Lifetime, nth), data) | ||||||
|  |             end | ||||||
|  | 			i += 1 | ||||||
|  | 			nth = world:target(e, Lifetime, i) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	CHECK(not world:has(entity, pair(Lifetime, Particle))) | ||||||
|  | 	CHECK(world:get(entity, pair(Lifetime, Beam)) == 1) | ||||||
|  | end) | ||||||
|  | 
 | ||||||
|  | local lifetime_tracker_add = require("@tools/lifetime_tracker") | ||||||
|  | 
 | ||||||
|  | TEST("another", function() | ||||||
|  | 	local world = world_new() | ||||||
|  | 	world = lifetime_tracker_add(world, {padding_enabled=false}) | ||||||
|  | 	local e1 = world:entity() | ||||||
|  | 	local e2 = world:entity() | ||||||
|  | 	local e3 = world:entity() | ||||||
|  | 	world:delete(e2) | ||||||
|  | 	world:print_entity_index() | ||||||
|  | 	print(pair(e1, e2)) | ||||||
|  | 	print(pair(e2, e3)) | ||||||
|  | 	local e2_e3 = pair(e2, e3) | ||||||
|  | 	CHECK(jecs.pair_first(world, e2_e3) == 0) | ||||||
|  | 	CHECK(jecs.pair_second(world, e2_e3) == e3) | ||||||
|  | 	CHECK_EXPECT_ERR(function() | ||||||
|  | 		world:add(e1, pair(e2, e3)) | ||||||
|  | 	end) | ||||||
|  | end) | ||||||
|  | 
 | ||||||
| TEST("#repro", function() | TEST("#repro", function() | ||||||
| 	local world = world_new() | 	local world = world_new() | ||||||
| 
 | 
 | ||||||
|  | @ -186,9 +242,9 @@ end) | ||||||
| 
 | 
 | ||||||
| TEST("world:cleanup()", function() | TEST("world:cleanup()", function() | ||||||
| 	local world = world_new() | 	local world = world_new() | ||||||
| 	local A = world:component() | 	local A = world:component() :: jecs.Id<boolean> | ||||||
| 	local B = world:component() | 	local B = world:component() :: jecs.Id<boolean> | ||||||
| 	local C = world:component() | 	local C = world:component() :: jecs.Id<boolean> | ||||||
| 
 | 
 | ||||||
| 	local e1 = world:entity() | 	local e1 = world:entity() | ||||||
| 	local e2 = world:entity() | 	local e2 = world:entity() | ||||||
|  | @ -299,7 +355,7 @@ TEST("world:entity()", function() | ||||||
| 
 | 
 | ||||||
| 	do CASE "Recycling max generation" | 	do CASE "Recycling max generation" | ||||||
| 		local world = world_new() | 		local world = world_new() | ||||||
| 		local pin = jecs.Rest::number + 1 | 		local pin = (jecs.Rest :: any) :: number + 1 | ||||||
| 		for i = 1, 2^16-1 do | 		for i = 1, 2^16-1 do | ||||||
| 			local e = world:entity() | 			local e = world:entity() | ||||||
| 			world:delete(e) | 			world:delete(e) | ||||||
|  | @ -513,21 +569,22 @@ TEST("world:query()", function() | ||||||
| 	do CASE "pairs" | 	do CASE "pairs" | ||||||
| 		local world = jecs.World.new() | 		local world = jecs.World.new() | ||||||
| 
 | 
 | ||||||
| 		local C1 = world:component() | 		local C1 = world:component() :: jecs.Id<boolean> | ||||||
| 		local C2 = world:component() | 		local C2 = world:component() :: jecs.Id<boolean> | ||||||
| 		local T1 = world:entity() | 		local T1 = world:entity() | ||||||
| 		local T2 = world:entity() | 		local T2 = world:entity() | ||||||
| 
 | 
 | ||||||
| 		local e = world:entity() | 		local e = world:entity() | ||||||
| 
 | 
 | ||||||
| 		world:set(e, pair(C1, C2), true) | 		local C1_C2 = pair(C1, C2) | ||||||
|  | 		world:set(e, C1_C2, true) | ||||||
| 		world:set(e, pair(C1, T1), true) | 		world:set(e, pair(C1, T1), true) | ||||||
| 		world:set(e, pair(T1, C1), true) | 		world:set(e, pair(T1, C1), true) | ||||||
| 		CHECK_EXPECT_ERR(function() | 		CHECK_EXPECT_ERR(function() | ||||||
| 			world:set(e, pair(T1, T2), true :: any) | 			world:set(e, pair(T1, T2), true :: any) | ||||||
| 		end) | 		end) | ||||||
| 
 | 
 | ||||||
| 		for id, a, b, c, d in world:query(pair(C1, C2), pair(C1, T1), pair(T1, C1), pair(T1, T2)) :: any do | 		for id, a, b, c, d in world:query(pair(C1, C2), pair(C1, T1), pair(T1, C1), pair(T1, T2)):iter() do | ||||||
| 			CHECK(a == true) | 			CHECK(a == true) | ||||||
| 			CHECK(b == true) | 			CHECK(b == true) | ||||||
| 			CHECK(c == true) | 			CHECK(c == true) | ||||||
|  | @ -823,8 +880,9 @@ TEST("world:query()", function() | ||||||
| 		local bob = world:entity() | 		local bob = world:entity() | ||||||
| 
 | 
 | ||||||
| 		world:delete(Apples) | 		world:delete(Apples) | ||||||
| 
 | 		CHECK_EXPECT_ERR(function() | ||||||
| 			world:set(bob, pair(Eats, Apples), "bob eats apples") | 			world:set(bob, pair(Eats, Apples), "bob eats apples") | ||||||
|  | 		end) | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	do | 	do | ||||||
|  | @ -1394,24 +1452,10 @@ TEST("world:target", function() | ||||||
| 		CHECK(world:target(e, C, 0) == D) | 		CHECK(world:target(e, C, 0) == D) | ||||||
| 		CHECK(world:target(e, C, 1) == nil) | 		CHECK(world:target(e, C, 1) == nil) | ||||||
| 
 | 
 | ||||||
| 		-- for id in archetype.records do |  | ||||||
| 		-- 	local f = world:get(ecs_pair_first(world, id), jecs.Name) |  | ||||||
| 		-- 	local s = world:get(ecs_pair_second(world, id), jecs.Name) |  | ||||||
| 		-- 	print(`({f}, {s})`) |  | ||||||
| 		-- end |  | ||||||
| 		-- |  | ||||||
| 
 |  | ||||||
| 		CHECK(archetype.records[pair(A, B)] == 1) | 		CHECK(archetype.records[pair(A, B)] == 1) | ||||||
| 		CHECK(archetype.records[pair(A, C)] == 2) | 		CHECK(archetype.records[pair(A, C)] == 2) | ||||||
| 		CHECK(archetype.records[pair(A, D)] == 3) | 		CHECK(archetype.records[pair(A, D)] == 3) | ||||||
| 		CHECK(archetype.records[pair(A, E)] == 4) | 		CHECK(archetype.records[pair(A, E)] == 4) | ||||||
| 		-- print("(A, B)", archetype.records[pair(A, B)]) |  | ||||||
| 		-- print("(A, C)", archetype.records[pair(A, C)]) |  | ||||||
| 		-- print("(A, D)", archetype.records[pair(A, D)]) |  | ||||||
| 		-- print("(A, E)", archetype.records[pair(A, E)]) |  | ||||||
| 
 |  | ||||||
| 		-- print(pair(A, D), pair(B, C)) |  | ||||||
| 		-- print("(B, C)", archetype.records[pair(B, C)]) |  | ||||||
| 
 | 
 | ||||||
| 		CHECK(world:target(e, C, 0) == D) | 		CHECK(world:target(e, C, 0) == D) | ||||||
| 		CHECK(world:target(e, C, 1) == nil) | 		CHECK(world:target(e, C, 1) == nil) | ||||||
|  |  | ||||||
							
								
								
									
										108
									
								
								tools/runtime_lints.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								tools/runtime_lints.luau
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,108 @@ | ||||||
|  | local function dbg_info(n: number): any | ||||||
|  | 	return debug.info(n, "s") | ||||||
|  | end | ||||||
|  | local function throw(msg: string) | ||||||
|  | 	local s = 1 | ||||||
|  | 	local root = dbg_info(1) | ||||||
|  | 	repeat | ||||||
|  | 		s += 1 | ||||||
|  | 	until dbg_info(s) ~= root | ||||||
|  | 	if warn then | ||||||
|  | 		error(msg, s) | ||||||
|  | 	else | ||||||
|  | 		print(`[jecs] error: {msg}\n`) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function ASSERT<T>(v: T, msg: string) | ||||||
|  | 	if v then | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  | 	throw(msg) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function runtime_lints_add(world) | ||||||
|  | 	local function get_name(id) | ||||||
|  | 		return world_get_one_inline(world, id, EcsName) | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	local function bname(id): string | ||||||
|  | 		local name: string | ||||||
|  | 		if ECS_IS_PAIR(id) then | ||||||
|  | 			local first = get_name(world, ecs_pair_first(world, id)) | ||||||
|  | 			local second = get_name(world, ecs_pair_second(world, id)) | ||||||
|  | 			name = `pair({first}, {second})` | ||||||
|  | 		else | ||||||
|  | 			return get_name(world, id) | ||||||
|  | 		end | ||||||
|  | 		if name then | ||||||
|  | 			return name | ||||||
|  | 		else | ||||||
|  | 			return `${id}` | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	local function ID_IS_TAG(world: World, id) | ||||||
|  | 		if ECS_IS_PAIR(id) then | ||||||
|  | 			id = ecs_pair_first(world, id) | ||||||
|  | 		end | ||||||
|  | 		return not world_has_one_inline(world, id, EcsComponent) | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	World.query = function(world: World, ...) | ||||||
|  | 		ASSERT((...), "Requires at least a single component") | ||||||
|  | 		return world_query(world, ...) | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	World.set = function(world: World, entity: i53, id: i53, value: any): () | ||||||
|  | 		local is_tag = ID_IS_TAG(world, id) | ||||||
|  | 		if is_tag and value == nil then | ||||||
|  | 			local _1 = bname(world, entity) | ||||||
|  | 			local _2 = bname(world, id) | ||||||
|  | 			local why = "cannot set component value to nil" | ||||||
|  | 			throw(why) | ||||||
|  | 			return | ||||||
|  | 		elseif value ~= nil and is_tag then | ||||||
|  | 			local _1 = bname(world, entity) | ||||||
|  | 			local _2 = bname(world, id) | ||||||
|  | 			local why = `cannot set a component value because {_2} is a tag` | ||||||
|  | 			why ..= `\n[jecs] note: consider using "world:add({_1}, {_2})" instead` | ||||||
|  | 			throw(why) | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 
 | ||||||
|  | 		world_set(world, entity, id, value) | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	World.add = function(world: World, entity: i53, id: i53, value: any) | ||||||
|  | 		if value ~= nil then | ||||||
|  | 			local _1 = bname(world, entity) | ||||||
|  | 			local _2 = bname(world, id) | ||||||
|  | 			throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`) | ||||||
|  | 		end | ||||||
|  | 
 | ||||||
|  | 		world_add(world, entity, id) | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	World.get = function(world: World, entity: i53, ...) | ||||||
|  | 		local length = select("#", ...) | ||||||
|  | 		ASSERT(length < 5, "world:get does not support more than 4 components") | ||||||
|  | 		local _1 | ||||||
|  | 		for i = 1, length do | ||||||
|  | 			local id = select(i, ...) | ||||||
|  | 			local id_is_tag = not world_has(world, id, EcsComponent) | ||||||
|  | 			if id_is_tag then | ||||||
|  | 				local name = get_name(world, id) | ||||||
|  | 				if not _1 then | ||||||
|  | 					_1 = get_name(world, entity) | ||||||
|  | 				end | ||||||
|  | 				throw( | ||||||
|  | 					`cannot get (#{i}) component {name} value because it is a tag.` | ||||||
|  | 						.. `\n[jecs] note: If this was intentional, use "world:has({_1}, {name}) instead"` | ||||||
|  | 				) | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 
 | ||||||
|  | 		return world_get(world, entity, ...) | ||||||
|  | 	end | ||||||
|  | end | ||||||
		Loading…
	
		Reference in a new issue