mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 02:49:18 +00:00 
			
		
		
		
	Fix line endings on demo (#232)
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	
This commit is contained in:
		
							parent
							
								
									3be946db0c
								
							
						
					
					
						commit
						803616a005
					
				
					 15 changed files with 1128 additions and 1128 deletions
				
			
		| 
						 | 
				
			
			@ -1,49 +1,49 @@
 | 
			
		|||
--!optimize 2
 | 
			
		||||
--!native
 | 
			
		||||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
 | 
			
		||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
 | 
			
		||||
local jecs = require(ReplicatedStorage.Lib)
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local ecs = jecs.World.new()
 | 
			
		||||
local mirror = require(ReplicatedStorage.mirror)
 | 
			
		||||
local mcs = mirror.World.new()
 | 
			
		||||
 | 
			
		||||
local C1 = ecs:component()
 | 
			
		||||
local C2 = ecs:entity()
 | 
			
		||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local C3 = ecs:entity()
 | 
			
		||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local C4 = ecs:entity()
 | 
			
		||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E1 = mcs:component()
 | 
			
		||||
local E2 = mcs:entity()
 | 
			
		||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E3 = mcs:entity()
 | 
			
		||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E4 = mcs:entity()
 | 
			
		||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	ParameterGenerator = function()
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	Functions = {
 | 
			
		||||
		Mirror = function()
 | 
			
		||||
			local m = mcs:entity()
 | 
			
		||||
			for i = 1, 100 do
 | 
			
		||||
				mcs:add(m, E3)
 | 
			
		||||
				mcs:remove(m, E3)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
 | 
			
		||||
		Jecs = function()
 | 
			
		||||
			local j = ecs:entity()
 | 
			
		||||
			for i = 1, 100 do
 | 
			
		||||
				ecs:add(j, C3)
 | 
			
		||||
				ecs:remove(j, C3)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
--!optimize 2
 | 
			
		||||
--!native
 | 
			
		||||
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
 | 
			
		||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
 | 
			
		||||
local jecs = require(ReplicatedStorage.Lib)
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local ecs = jecs.World.new()
 | 
			
		||||
local mirror = require(ReplicatedStorage.mirror)
 | 
			
		||||
local mcs = mirror.World.new()
 | 
			
		||||
 | 
			
		||||
local C1 = ecs:component()
 | 
			
		||||
local C2 = ecs:entity()
 | 
			
		||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local C3 = ecs:entity()
 | 
			
		||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local C4 = ecs:entity()
 | 
			
		||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E1 = mcs:component()
 | 
			
		||||
local E2 = mcs:entity()
 | 
			
		||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E3 = mcs:entity()
 | 
			
		||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
local E4 = mcs:entity()
 | 
			
		||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	ParameterGenerator = function()
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	Functions = {
 | 
			
		||||
		Mirror = function()
 | 
			
		||||
			local m = mcs:entity()
 | 
			
		||||
			for i = 1, 100 do
 | 
			
		||||
				mcs:add(m, E3)
 | 
			
		||||
				mcs:remove(m, E3)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
 | 
			
		||||
		Jecs = function()
 | 
			
		||||
			local j = ecs:entity()
 | 
			
		||||
			for i = 1, 100 do
 | 
			
		||||
				ecs:add(j, C3)
 | 
			
		||||
				ecs:remove(j, C3)
 | 
			
		||||
			end
 | 
			
		||||
		end,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,28 +1,28 @@
 | 
			
		|||
local function collect<T...>(
 | 
			
		||||
	signal: {
 | 
			
		||||
		Connect: (RBXScriptSignal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
 | 
			
		||||
	}
 | 
			
		||||
): () -> (T...)
 | 
			
		||||
	local enqueued = {}
 | 
			
		||||
 | 
			
		||||
	local i = 0
 | 
			
		||||
 | 
			
		||||
	local connection = (signal :: any):Connect(function(...)
 | 
			
		||||
		table.insert(enqueued, { ... })
 | 
			
		||||
		i += 1
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	return function(): any
 | 
			
		||||
		if i == 0 then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		i -= 1
 | 
			
		||||
 | 
			
		||||
		local args: any = table.remove(enqueued, 1)
 | 
			
		||||
 | 
			
		||||
		return unpack(args)
 | 
			
		||||
	end, connection
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return collect
 | 
			
		||||
local function collect<T...>(
 | 
			
		||||
	signal: {
 | 
			
		||||
		Connect: (RBXScriptSignal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
 | 
			
		||||
	}
 | 
			
		||||
): () -> (T...)
 | 
			
		||||
	local enqueued = {}
 | 
			
		||||
 | 
			
		||||
	local i = 0
 | 
			
		||||
 | 
			
		||||
	local connection = (signal :: any):Connect(function(...)
 | 
			
		||||
		table.insert(enqueued, { ... })
 | 
			
		||||
		i += 1
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	return function(): any
 | 
			
		||||
		if i == 0 then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		i -= 1
 | 
			
		||||
 | 
			
		||||
		local args: any = table.remove(enqueued, 1)
 | 
			
		||||
 | 
			
		||||
		return unpack(args)
 | 
			
		||||
	end, connection
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return collect
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,36 +1,36 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
local types = require("./types")
 | 
			
		||||
 | 
			
		||||
local Networked = jecs.tag()
 | 
			
		||||
local NetworkedPair = jecs.tag()
 | 
			
		||||
 | 
			
		||||
local Renderable = jecs.component() :: jecs.Id<Instance>
 | 
			
		||||
jecs.meta(Renderable, Networked)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 components = {
 | 
			
		||||
	Renderable = Renderable,
 | 
			
		||||
	Player = Player,
 | 
			
		||||
	Poison = Poison,
 | 
			
		||||
	Health = Health,
 | 
			
		||||
 | 
			
		||||
	Networked = Networked,
 | 
			
		||||
	NetworkedPair = NetworkedPair,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for name, component in components do
 | 
			
		||||
	jecs.meta(component, jecs.Name, name)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return components
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
local types = require("./types")
 | 
			
		||||
 | 
			
		||||
local Networked = jecs.tag()
 | 
			
		||||
local NetworkedPair = jecs.tag()
 | 
			
		||||
 | 
			
		||||
local Renderable = jecs.component() :: jecs.Id<Instance>
 | 
			
		||||
jecs.meta(Renderable, Networked)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 components = {
 | 
			
		||||
	Renderable = Renderable,
 | 
			
		||||
	Player = Player,
 | 
			
		||||
	Poison = Poison,
 | 
			
		||||
	Health = Health,
 | 
			
		||||
 | 
			
		||||
	Networked = Networked,
 | 
			
		||||
	NetworkedPair = NetworkedPair,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for name, component in components do
 | 
			
		||||
	jecs.meta(component, jecs.Name, name)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return components
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,190 +1,190 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
type Observer<T...> = {
 | 
			
		||||
	callback: (jecs.Entity) -> (),
 | 
			
		||||
	query: jecs.Query<T...>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type PatchedWorld = jecs.World & {
 | 
			
		||||
	added: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
 | 
			
		||||
	removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
 | 
			
		||||
	changed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
 | 
			
		||||
	-- deleted: (PatchedWorld, () -> ()) -> () -> (),
 | 
			
		||||
	observer: (PatchedWorld, Observer<any>) -> (),
 | 
			
		||||
	monitor: (PatchedWorld, Observer<any>) -> (),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function observers_new(world, description)
 | 
			
		||||
	local query = description.query
 | 
			
		||||
	local callback = description.callback
 | 
			
		||||
	local terms = query.filter_with :: { jecs.Id }
 | 
			
		||||
	if not terms then
 | 
			
		||||
		local ids = query.ids
 | 
			
		||||
		query.filter_with = ids
 | 
			
		||||
		terms = ids
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local entity_index = world.entity_index :: any
 | 
			
		||||
	local function emplaced(entity: jecs.Entity)
 | 
			
		||||
		local r = jecs.entity_index_try_get_fast(
 | 
			
		||||
			entity_index, entity :: any)
 | 
			
		||||
 | 
			
		||||
		if not r then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
 | 
			
		||||
		if jecs.query_match(query, archetype) then
 | 
			
		||||
			callback(entity)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, term in terms do
 | 
			
		||||
		world:added(term, emplaced)
 | 
			
		||||
		world:changed(term, emplaced)
 | 
			
		||||
 	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function monitors_new(world, description)
 | 
			
		||||
	local query = description.query
 | 
			
		||||
	local callback = description.callback
 | 
			
		||||
	local terms = query.filter_with :: { jecs.Id }
 | 
			
		||||
	if not terms then
 | 
			
		||||
		local ids = query.ids
 | 
			
		||||
		query.filter_with = ids
 | 
			
		||||
		terms = ids
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local entity_index = world.entity_index :: any
 | 
			
		||||
	local function emplaced(entity: jecs.Entity)
 | 
			
		||||
		local r = jecs.entity_index_try_get_fast(
 | 
			
		||||
			entity_index, entity :: any)
 | 
			
		||||
 | 
			
		||||
		if not r then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
 | 
			
		||||
		if jecs.query_match(query, archetype) then
 | 
			
		||||
			callback(entity, jecs.OnAdd)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function removed(entity: jecs.Entity, component: jecs.Id)
 | 
			
		||||
		local r = jecs.entity_index_try_get_fast(
 | 
			
		||||
			entity_index, entity :: any)
 | 
			
		||||
 | 
			
		||||
		if not r then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
 | 
			
		||||
		if jecs.query_match(query, archetype) then
 | 
			
		||||
			callback(entity, jecs.OnRemove)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, term in terms do
 | 
			
		||||
		world:added(term, emplaced)
 | 
			
		||||
		world:removed(term, removed)
 | 
			
		||||
 	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function observers_add(world: jecs.World): PatchedWorld
 | 
			
		||||
	local signals = {
 | 
			
		||||
		added = {},
 | 
			
		||||
		emplaced = {},
 | 
			
		||||
		removed = {},
 | 
			
		||||
		deleted = {}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	world = world :: jecs.World & {[string]: any}
 | 
			
		||||
 | 
			
		||||
	world.added = function(_, component, fn)
 | 
			
		||||
		local listeners = signals.added[component]
 | 
			
		||||
		if not listeners then
 | 
			
		||||
			listeners = {}
 | 
			
		||||
			signals.added[component] = listeners
 | 
			
		||||
 | 
			
		||||
			local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
			
		||||
			local rw = jecs.pair(component, jecs.Wildcard)
 | 
			
		||||
			local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
			
		||||
			local function on_add(entity: number, id: number, value: any)
 | 
			
		||||
				for _, listener in listeners do
 | 
			
		||||
					listener(entity, id, value)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			world:set(component, jecs.OnAdd, on_add)
 | 
			
		||||
			idr.hooks.on_add = on_add :: any
 | 
			
		||||
			idr_r.hooks.on_add = on_add :: any
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(listeners, fn)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	world.changed = function(_, component, fn)
 | 
			
		||||
		local listeners = signals.emplaced[component]
 | 
			
		||||
		if not listeners then
 | 
			
		||||
			listeners = {}
 | 
			
		||||
			signals.emplaced[component] = listeners
 | 
			
		||||
			local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
			
		||||
			local rw = jecs.pair(component, jecs.Wildcard)
 | 
			
		||||
			local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
			
		||||
			local function on_change(entity: number, id: number, value: any)
 | 
			
		||||
				for _, listener in listeners do
 | 
			
		||||
					listener(entity, id, value)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			world:set(component, jecs.OnChange, on_change)
 | 
			
		||||
			idr.hooks.on_change = on_change :: any
 | 
			
		||||
			idr_r.hooks.on_change = on_change :: any
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(listeners, fn)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	world.removed = function(_, component, fn)
 | 
			
		||||
		local listeners = signals.removed[component]
 | 
			
		||||
		if not listeners then
 | 
			
		||||
			listeners = {}
 | 
			
		||||
			signals.removed[component] = listeners
 | 
			
		||||
			local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
			
		||||
			local rw = jecs.pair(component, jecs.Wildcard)
 | 
			
		||||
			local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
			
		||||
			local function on_remove(entity: number, id: number, value: any)
 | 
			
		||||
				for _, listener in listeners do
 | 
			
		||||
					listener(entity, id, value)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			world:set(component, jecs.OnRemove, on_remove)
 | 
			
		||||
			idr.hooks.on_remove = on_remove :: any
 | 
			
		||||
			idr_r.hooks.on_remove = on_remove :: any
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(listeners, fn)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	world.signals = signals
 | 
			
		||||
 | 
			
		||||
	world.observer = observers_new
 | 
			
		||||
 | 
			
		||||
	world.monitor = monitors_new
 | 
			
		||||
 | 
			
		||||
	-- local world_delete = world.delete
 | 
			
		||||
 | 
			
		||||
	-- world.deleted = function(_, fn)
 | 
			
		||||
	-- 	local listeners = signals.deleted
 | 
			
		||||
	-- 	table.insert(listeners, fn)
 | 
			
		||||
	-- end
 | 
			
		||||
	-- world.delete = function(world, entity)
 | 
			
		||||
	-- 	world_delete(world, entity)
 | 
			
		||||
	-- 	for _, fn in signals.deleted do
 | 
			
		||||
	-- 		fn(entity)
 | 
			
		||||
	-- 	end
 | 
			
		||||
	-- end
 | 
			
		||||
 | 
			
		||||
	return world :: PatchedWorld
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return observers_add
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
type Observer<T...> = {
 | 
			
		||||
	callback: (jecs.Entity) -> (),
 | 
			
		||||
	query: jecs.Query<T...>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type PatchedWorld = jecs.World & {
 | 
			
		||||
	added: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
 | 
			
		||||
	removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
 | 
			
		||||
	changed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
 | 
			
		||||
	-- deleted: (PatchedWorld, () -> ()) -> () -> (),
 | 
			
		||||
	observer: (PatchedWorld, Observer<any>) -> (),
 | 
			
		||||
	monitor: (PatchedWorld, Observer<any>) -> (),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function observers_new(world, description)
 | 
			
		||||
	local query = description.query
 | 
			
		||||
	local callback = description.callback
 | 
			
		||||
	local terms = query.filter_with :: { jecs.Id }
 | 
			
		||||
	if not terms then
 | 
			
		||||
		local ids = query.ids
 | 
			
		||||
		query.filter_with = ids
 | 
			
		||||
		terms = ids
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local entity_index = world.entity_index :: any
 | 
			
		||||
	local function emplaced(entity: jecs.Entity)
 | 
			
		||||
		local r = jecs.entity_index_try_get_fast(
 | 
			
		||||
			entity_index, entity :: any)
 | 
			
		||||
 | 
			
		||||
		if not r then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
 | 
			
		||||
		if jecs.query_match(query, archetype) then
 | 
			
		||||
			callback(entity)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, term in terms do
 | 
			
		||||
		world:added(term, emplaced)
 | 
			
		||||
		world:changed(term, emplaced)
 | 
			
		||||
 	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function monitors_new(world, description)
 | 
			
		||||
	local query = description.query
 | 
			
		||||
	local callback = description.callback
 | 
			
		||||
	local terms = query.filter_with :: { jecs.Id }
 | 
			
		||||
	if not terms then
 | 
			
		||||
		local ids = query.ids
 | 
			
		||||
		query.filter_with = ids
 | 
			
		||||
		terms = ids
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local entity_index = world.entity_index :: any
 | 
			
		||||
	local function emplaced(entity: jecs.Entity)
 | 
			
		||||
		local r = jecs.entity_index_try_get_fast(
 | 
			
		||||
			entity_index, entity :: any)
 | 
			
		||||
 | 
			
		||||
		if not r then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
 | 
			
		||||
		if jecs.query_match(query, archetype) then
 | 
			
		||||
			callback(entity, jecs.OnAdd)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function removed(entity: jecs.Entity, component: jecs.Id)
 | 
			
		||||
		local r = jecs.entity_index_try_get_fast(
 | 
			
		||||
			entity_index, entity :: any)
 | 
			
		||||
 | 
			
		||||
		if not r then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local archetype = r.archetype
 | 
			
		||||
 | 
			
		||||
		if jecs.query_match(query, archetype) then
 | 
			
		||||
			callback(entity, jecs.OnRemove)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, term in terms do
 | 
			
		||||
		world:added(term, emplaced)
 | 
			
		||||
		world:removed(term, removed)
 | 
			
		||||
 	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function observers_add(world: jecs.World): PatchedWorld
 | 
			
		||||
	local signals = {
 | 
			
		||||
		added = {},
 | 
			
		||||
		emplaced = {},
 | 
			
		||||
		removed = {},
 | 
			
		||||
		deleted = {}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	world = world :: jecs.World & {[string]: any}
 | 
			
		||||
 | 
			
		||||
	world.added = function(_, component, fn)
 | 
			
		||||
		local listeners = signals.added[component]
 | 
			
		||||
		if not listeners then
 | 
			
		||||
			listeners = {}
 | 
			
		||||
			signals.added[component] = listeners
 | 
			
		||||
 | 
			
		||||
			local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
			
		||||
			local rw = jecs.pair(component, jecs.Wildcard)
 | 
			
		||||
			local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
			
		||||
			local function on_add(entity: number, id: number, value: any)
 | 
			
		||||
				for _, listener in listeners do
 | 
			
		||||
					listener(entity, id, value)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			world:set(component, jecs.OnAdd, on_add)
 | 
			
		||||
			idr.hooks.on_add = on_add :: any
 | 
			
		||||
			idr_r.hooks.on_add = on_add :: any
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(listeners, fn)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	world.changed = function(_, component, fn)
 | 
			
		||||
		local listeners = signals.emplaced[component]
 | 
			
		||||
		if not listeners then
 | 
			
		||||
			listeners = {}
 | 
			
		||||
			signals.emplaced[component] = listeners
 | 
			
		||||
			local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
			
		||||
			local rw = jecs.pair(component, jecs.Wildcard)
 | 
			
		||||
			local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
			
		||||
			local function on_change(entity: number, id: number, value: any)
 | 
			
		||||
				for _, listener in listeners do
 | 
			
		||||
					listener(entity, id, value)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			world:set(component, jecs.OnChange, on_change)
 | 
			
		||||
			idr.hooks.on_change = on_change :: any
 | 
			
		||||
			idr_r.hooks.on_change = on_change :: any
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(listeners, fn)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	world.removed = function(_, component, fn)
 | 
			
		||||
		local listeners = signals.removed[component]
 | 
			
		||||
		if not listeners then
 | 
			
		||||
			listeners = {}
 | 
			
		||||
			signals.removed[component] = listeners
 | 
			
		||||
			local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
			
		||||
			local rw = jecs.pair(component, jecs.Wildcard)
 | 
			
		||||
			local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
			
		||||
			local function on_remove(entity: number, id: number, value: any)
 | 
			
		||||
				for _, listener in listeners do
 | 
			
		||||
					listener(entity, id, value)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			world:set(component, jecs.OnRemove, on_remove)
 | 
			
		||||
			idr.hooks.on_remove = on_remove :: any
 | 
			
		||||
			idr_r.hooks.on_remove = on_remove :: any
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(listeners, fn)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	world.signals = signals
 | 
			
		||||
 | 
			
		||||
	world.observer = observers_new
 | 
			
		||||
 | 
			
		||||
	world.monitor = monitors_new
 | 
			
		||||
 | 
			
		||||
	-- local world_delete = world.delete
 | 
			
		||||
 | 
			
		||||
	-- world.deleted = function(_, fn)
 | 
			
		||||
	-- 	local listeners = signals.deleted
 | 
			
		||||
	-- 	table.insert(listeners, fn)
 | 
			
		||||
	-- end
 | 
			
		||||
	-- world.delete = function(world, entity)
 | 
			
		||||
	-- 	world_delete(world, entity)
 | 
			
		||||
	-- 	for _, fn in signals.deleted do
 | 
			
		||||
	-- 		fn(entity)
 | 
			
		||||
	-- 	end
 | 
			
		||||
	-- end
 | 
			
		||||
 | 
			
		||||
	return world :: PatchedWorld
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return observers_add
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,50 +1,50 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local types = require("../ReplicatedStorage/types")
 | 
			
		||||
 | 
			
		||||
type Signal<T...> = {
 | 
			
		||||
	Connect: (Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
 | 
			
		||||
}
 | 
			
		||||
type Remote<T...> = {
 | 
			
		||||
	FireClient: (Remote<T...>, T...) -> (),
 | 
			
		||||
	FireAllClients: (Remote<T...>, T...) -> (),
 | 
			
		||||
	FireServer: (Remote<T...>) -> (),
 | 
			
		||||
	OnServerEvent: {
 | 
			
		||||
		Connect: (any, fn: (Player, T...) -> () ) -> ()
 | 
			
		||||
	},
 | 
			
		||||
	OnClientEvent: {
 | 
			
		||||
		Connect: (any, fn: (T...) -> () ) -> ()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function stream_ensure(name): Remote<any>
 | 
			
		||||
	local remote = ReplicatedStorage:FindFirstChild(name)
 | 
			
		||||
	if not remote then
 | 
			
		||||
		remote = Instance.new("RemoteEvent")
 | 
			
		||||
		remote.Name = name
 | 
			
		||||
		remote.Parent = ReplicatedStorage
 | 
			
		||||
	end
 | 
			
		||||
	return remote :: any
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function datagram_ensure(name): Remote<any>
 | 
			
		||||
	local remote = ReplicatedStorage:FindFirstChild(name)
 | 
			
		||||
	if not remote then
 | 
			
		||||
		remote = Instance.new("UnreliableRemoteEvent")
 | 
			
		||||
		remote.Name = name
 | 
			
		||||
		remote.Parent = ReplicatedStorage
 | 
			
		||||
	end
 | 
			
		||||
	return remote :: any
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	input = datagram_ensure("input") :: Remote<string>,
 | 
			
		||||
	replication = stream_ensure("replication") :: Remote<{
 | 
			
		||||
		[string]: {
 | 
			
		||||
			set: { types.Entity }?,
 | 
			
		||||
			values: { any }?,
 | 
			
		||||
			removed: { types.Entity }?
 | 
			
		||||
		}
 | 
			
		||||
	}>,
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local types = require("../ReplicatedStorage/types")
 | 
			
		||||
 | 
			
		||||
type Signal<T...> = {
 | 
			
		||||
	Connect: (Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
 | 
			
		||||
}
 | 
			
		||||
type Remote<T...> = {
 | 
			
		||||
	FireClient: (Remote<T...>, T...) -> (),
 | 
			
		||||
	FireAllClients: (Remote<T...>, T...) -> (),
 | 
			
		||||
	FireServer: (Remote<T...>) -> (),
 | 
			
		||||
	OnServerEvent: {
 | 
			
		||||
		Connect: (any, fn: (Player, T...) -> () ) -> ()
 | 
			
		||||
	},
 | 
			
		||||
	OnClientEvent: {
 | 
			
		||||
		Connect: (any, fn: (T...) -> () ) -> ()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function stream_ensure(name): Remote<any>
 | 
			
		||||
	local remote = ReplicatedStorage:FindFirstChild(name)
 | 
			
		||||
	if not remote then
 | 
			
		||||
		remote = Instance.new("RemoteEvent")
 | 
			
		||||
		remote.Name = name
 | 
			
		||||
		remote.Parent = ReplicatedStorage
 | 
			
		||||
	end
 | 
			
		||||
	return remote :: any
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function datagram_ensure(name): Remote<any>
 | 
			
		||||
	local remote = ReplicatedStorage:FindFirstChild(name)
 | 
			
		||||
	if not remote then
 | 
			
		||||
		remote = Instance.new("UnreliableRemoteEvent")
 | 
			
		||||
		remote.Name = name
 | 
			
		||||
		remote.Parent = ReplicatedStorage
 | 
			
		||||
	end
 | 
			
		||||
	return remote :: any
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	input = datagram_ensure("input") :: Remote<string>,
 | 
			
		||||
	replication = stream_ensure("replication") :: Remote<{
 | 
			
		||||
		[string]: {
 | 
			
		||||
			set: { types.Entity }?,
 | 
			
		||||
			values: { any }?,
 | 
			
		||||
			removed: { types.Entity }?
 | 
			
		||||
		}
 | 
			
		||||
	}>,
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,136 +1,136 @@
 | 
			
		|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jabby = require(ReplicatedStorage.Packages.jabby)
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
jabby.set_check_function(function() return true end)
 | 
			
		||||
 | 
			
		||||
local scheduler = jabby.scheduler.create("jabby scheduler")
 | 
			
		||||
 | 
			
		||||
jabby.register({
 | 
			
		||||
	applet = jabby.applets.scheduler,
 | 
			
		||||
	name = "Scheduler",
 | 
			
		||||
	configuration = {
 | 
			
		||||
		scheduler = scheduler,
 | 
			
		||||
	},
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
local ContextActionService = game:GetService("ContextActionService")
 | 
			
		||||
 | 
			
		||||
local function create_widget(_, state: Enum.UserInputState)
 | 
			
		||||
	local client = jabby.obtain_client()
 | 
			
		||||
    if state ~= Enum.UserInputState.Begin then return end
 | 
			
		||||
    client.spawn_app(client.apps.home, nil)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local RunService = game:GetService("RunService")
 | 
			
		||||
 | 
			
		||||
local System = jecs.component() :: jecs.Id<{
 | 
			
		||||
	fn: () -> (),
 | 
			
		||||
	name: string,
 | 
			
		||||
}>
 | 
			
		||||
local DependsOn = jecs.component()
 | 
			
		||||
local Phase = jecs.tag()
 | 
			
		||||
local Event = jecs.component() :: jecs.Id<RBXScriptSignal>
 | 
			
		||||
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
 | 
			
		||||
local function ECS_PHASE(world, after: types.Entity)
 | 
			
		||||
	local phase = world:entity()
 | 
			
		||||
	world:add(phase, Phase)
 | 
			
		||||
	if after then
 | 
			
		||||
		local dependency = pair(DependsOn, after)
 | 
			
		||||
		world:add(phase, dependency)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return phase
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local Heartbeat = jecs.tag()
 | 
			
		||||
jecs.meta(Heartbeat, Phase)
 | 
			
		||||
jecs.meta(Heartbeat, Event, RunService.Heartbeat)
 | 
			
		||||
 | 
			
		||||
local PreSimulation = jecs.tag()
 | 
			
		||||
jecs.meta(PreSimulation, Phase)
 | 
			
		||||
jecs.meta(PreSimulation, Event, RunService.PreSimulation)
 | 
			
		||||
 | 
			
		||||
local PreAnimation = jecs.tag()
 | 
			
		||||
jecs.meta(PreAnimation, Phase)
 | 
			
		||||
jecs.meta(PreAnimation, Event, RunService.PreAnimation)
 | 
			
		||||
 | 
			
		||||
local PreRender = jecs.tag()
 | 
			
		||||
jecs.meta(PreRender, Phase)
 | 
			
		||||
jecs.meta(PreRender, Event, RunService.PreRender)
 | 
			
		||||
 | 
			
		||||
local function ECS_SYSTEM(world: types.World, mod: ModuleScript, phase: types.Entity?)
 | 
			
		||||
	local system = world:entity()
 | 
			
		||||
	local p = phase or Heartbeat
 | 
			
		||||
	local fn = require(mod) :: (...any) -> ()
 | 
			
		||||
	world:set(system, System, {
 | 
			
		||||
		fn = fn(world, 0) or fn,
 | 
			
		||||
		name = mod.Name,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	local depends_on = DependsOn :: jecs.Entity
 | 
			
		||||
	world:add(system, pair(depends_on, p))
 | 
			
		||||
end
 | 
			
		||||
local function find_systems_w_phase(world: types.World, systems, phase: types.Entity)
 | 
			
		||||
	local phase_name = world:get(phase, jecs.Name) :: string
 | 
			
		||||
	for _, s in world:query(System):with(pair(DependsOn, phase)) do
 | 
			
		||||
		table.insert(systems, {
 | 
			
		||||
			id = scheduler:register_system({
 | 
			
		||||
				phase = phase_name,
 | 
			
		||||
				name = s.name,
 | 
			
		||||
			}),
 | 
			
		||||
			fn = s.fn
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
	for after in world:query(Phase, pair(DependsOn, phase)) do
 | 
			
		||||
		find_systems_w_phase(world, systems, after)
 | 
			
		||||
	end
 | 
			
		||||
	return systems
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_RUN(world: types.World)
 | 
			
		||||
 | 
			
		||||
	jabby.register({
 | 
			
		||||
		applet = jabby.applets.world,
 | 
			
		||||
		name = "MyWorld",
 | 
			
		||||
		configuration = {
 | 
			
		||||
			world = world,
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if RunService:IsClient() then
 | 
			
		||||
		ContextActionService:BindAction("Open Jabby Home", create_widget, false, Enum.KeyCode.F4)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for phase, event in world:query(Event, Phase) do
 | 
			
		||||
		local systems = find_systems_w_phase(world, {}, phase)
 | 
			
		||||
		event:Connect(function(...)
 | 
			
		||||
			for _, system in systems do
 | 
			
		||||
				scheduler:run(system.id, system.fn, world, ...)
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	PHASE = ECS_PHASE,
 | 
			
		||||
	SYSTEM = ECS_SYSTEM,
 | 
			
		||||
	RUN = ECS_RUN,
 | 
			
		||||
	phases = {
 | 
			
		||||
		Heartbeat = Heartbeat,
 | 
			
		||||
		PreSimulation = PreSimulation,
 | 
			
		||||
		PreAnimation = PreAnimation,
 | 
			
		||||
		PreRender = PreRender
 | 
			
		||||
	},
 | 
			
		||||
	components = {
 | 
			
		||||
		System = System,
 | 
			
		||||
		DependsOn = DependsOn,
 | 
			
		||||
		Phase = Phase,
 | 
			
		||||
		Event = Event,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
local jabby = require(ReplicatedStorage.Packages.jabby)
 | 
			
		||||
local jecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
jabby.set_check_function(function() return true end)
 | 
			
		||||
 | 
			
		||||
local scheduler = jabby.scheduler.create("jabby scheduler")
 | 
			
		||||
 | 
			
		||||
jabby.register({
 | 
			
		||||
	applet = jabby.applets.scheduler,
 | 
			
		||||
	name = "Scheduler",
 | 
			
		||||
	configuration = {
 | 
			
		||||
		scheduler = scheduler,
 | 
			
		||||
	},
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
local ContextActionService = game:GetService("ContextActionService")
 | 
			
		||||
 | 
			
		||||
local function create_widget(_, state: Enum.UserInputState)
 | 
			
		||||
	local client = jabby.obtain_client()
 | 
			
		||||
    if state ~= Enum.UserInputState.Begin then return end
 | 
			
		||||
    client.spawn_app(client.apps.home, nil)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local RunService = game:GetService("RunService")
 | 
			
		||||
 | 
			
		||||
local System = jecs.component() :: jecs.Id<{
 | 
			
		||||
	fn: () -> (),
 | 
			
		||||
	name: string,
 | 
			
		||||
}>
 | 
			
		||||
local DependsOn = jecs.component()
 | 
			
		||||
local Phase = jecs.tag()
 | 
			
		||||
local Event = jecs.component() :: jecs.Id<RBXScriptSignal>
 | 
			
		||||
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
 | 
			
		||||
local types = require(ReplicatedStorage.types)
 | 
			
		||||
 | 
			
		||||
local function ECS_PHASE(world, after: types.Entity)
 | 
			
		||||
	local phase = world:entity()
 | 
			
		||||
	world:add(phase, Phase)
 | 
			
		||||
	if after then
 | 
			
		||||
		local dependency = pair(DependsOn, after)
 | 
			
		||||
		world:add(phase, dependency)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	return phase
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local Heartbeat = jecs.tag()
 | 
			
		||||
jecs.meta(Heartbeat, Phase)
 | 
			
		||||
jecs.meta(Heartbeat, Event, RunService.Heartbeat)
 | 
			
		||||
 | 
			
		||||
local PreSimulation = jecs.tag()
 | 
			
		||||
jecs.meta(PreSimulation, Phase)
 | 
			
		||||
jecs.meta(PreSimulation, Event, RunService.PreSimulation)
 | 
			
		||||
 | 
			
		||||
local PreAnimation = jecs.tag()
 | 
			
		||||
jecs.meta(PreAnimation, Phase)
 | 
			
		||||
jecs.meta(PreAnimation, Event, RunService.PreAnimation)
 | 
			
		||||
 | 
			
		||||
local PreRender = jecs.tag()
 | 
			
		||||
jecs.meta(PreRender, Phase)
 | 
			
		||||
jecs.meta(PreRender, Event, RunService.PreRender)
 | 
			
		||||
 | 
			
		||||
local function ECS_SYSTEM(world: types.World, mod: ModuleScript, phase: types.Entity?)
 | 
			
		||||
	local system = world:entity()
 | 
			
		||||
	local p = phase or Heartbeat
 | 
			
		||||
	local fn = require(mod) :: (...any) -> ()
 | 
			
		||||
	world:set(system, System, {
 | 
			
		||||
		fn = fn(world, 0) or fn,
 | 
			
		||||
		name = mod.Name,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	local depends_on = DependsOn :: jecs.Entity
 | 
			
		||||
	world:add(system, pair(depends_on, p))
 | 
			
		||||
end
 | 
			
		||||
local function find_systems_w_phase(world: types.World, systems, phase: types.Entity)
 | 
			
		||||
	local phase_name = world:get(phase, jecs.Name) :: string
 | 
			
		||||
	for _, s in world:query(System):with(pair(DependsOn, phase)) do
 | 
			
		||||
		table.insert(systems, {
 | 
			
		||||
			id = scheduler:register_system({
 | 
			
		||||
				phase = phase_name,
 | 
			
		||||
				name = s.name,
 | 
			
		||||
			}),
 | 
			
		||||
			fn = s.fn
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
	for after in world:query(Phase, pair(DependsOn, phase)) do
 | 
			
		||||
		find_systems_w_phase(world, systems, after)
 | 
			
		||||
	end
 | 
			
		||||
	return systems
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_RUN(world: types.World)
 | 
			
		||||
 | 
			
		||||
	jabby.register({
 | 
			
		||||
		applet = jabby.applets.world,
 | 
			
		||||
		name = "MyWorld",
 | 
			
		||||
		configuration = {
 | 
			
		||||
			world = world,
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if RunService:IsClient() then
 | 
			
		||||
		ContextActionService:BindAction("Open Jabby Home", create_widget, false, Enum.KeyCode.F4)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for phase, event in world:query(Event, Phase) do
 | 
			
		||||
		local systems = find_systems_w_phase(world, {}, phase)
 | 
			
		||||
		event:Connect(function(...)
 | 
			
		||||
			for _, system in systems do
 | 
			
		||||
				scheduler:run(system.id, system.fn, world, ...)
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
	PHASE = ECS_PHASE,
 | 
			
		||||
	SYSTEM = ECS_SYSTEM,
 | 
			
		||||
	RUN = ECS_RUN,
 | 
			
		||||
	phases = {
 | 
			
		||||
		Heartbeat = Heartbeat,
 | 
			
		||||
		PreSimulation = PreSimulation,
 | 
			
		||||
		PreAnimation = PreAnimation,
 | 
			
		||||
		PreRender = PreRender
 | 
			
		||||
	},
 | 
			
		||||
	components = {
 | 
			
		||||
		System = System,
 | 
			
		||||
		DependsOn = DependsOn,
 | 
			
		||||
		Phase = Phase,
 | 
			
		||||
		Event = Event,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,86 +1,86 @@
 | 
			
		|||
local types = require("../types")
 | 
			
		||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
			
		||||
local remotes = require("../remotes")
 | 
			
		||||
local collect = require("../collect")
 | 
			
		||||
local client_ids = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local function ecs_map_get(world, id)
 | 
			
		||||
	local deserialised_id = client_ids[id]
 | 
			
		||||
 | 
			
		||||
	if not deserialised_id then
 | 
			
		||||
		if world:has(id, jecs.Name) then
 | 
			
		||||
			deserialised_id = world:entity(id)
 | 
			
		||||
		else
 | 
			
		||||
			deserialised_id = world:entity()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		client_ids[id] = deserialised_id
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	return jecs.pair(rel, tgt)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local snapshots = collect(remotes.replication.OnClientEvent)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World)
 | 
			
		||||
    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)
 | 
			
		||||
            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)
 | 
			
		||||
                  		world:add(entity, id)
 | 
			
		||||
                   	end
 | 
			
		||||
                else
 | 
			
		||||
	                local values = map.values
 | 
			
		||||
	                for i, entity in set do
 | 
			
		||||
						entity = ecs_map_get(world, entity)
 | 
			
		||||
                  		world:set(entity, id, values[i])
 | 
			
		||||
                   	end
 | 
			
		||||
			    end
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            local removed = map.removed
 | 
			
		||||
 | 
			
		||||
            if removed then
 | 
			
		||||
                for i, e in removed do
 | 
			
		||||
                    if not world:contains(e) then
 | 
			
		||||
                        continue
 | 
			
		||||
                    end
 | 
			
		||||
                    world:remove(e, id)
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
local types = require("../types")
 | 
			
		||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
			
		||||
local remotes = require("../remotes")
 | 
			
		||||
local collect = require("../collect")
 | 
			
		||||
local client_ids = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local function ecs_map_get(world, id)
 | 
			
		||||
	local deserialised_id = client_ids[id]
 | 
			
		||||
 | 
			
		||||
	if not deserialised_id then
 | 
			
		||||
		if world:has(id, jecs.Name) then
 | 
			
		||||
			deserialised_id = world:entity(id)
 | 
			
		||||
		else
 | 
			
		||||
			deserialised_id = world:entity()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		client_ids[id] = deserialised_id
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	return jecs.pair(rel, tgt)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local snapshots = collect(remotes.replication.OnClientEvent)
 | 
			
		||||
 | 
			
		||||
return function(world: types.World)
 | 
			
		||||
    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)
 | 
			
		||||
            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)
 | 
			
		||||
                  		world:add(entity, id)
 | 
			
		||||
                   	end
 | 
			
		||||
                else
 | 
			
		||||
	                local values = map.values
 | 
			
		||||
	                for i, entity in set do
 | 
			
		||||
						entity = ecs_map_get(world, entity)
 | 
			
		||||
                  		world:set(entity, id, values[i])
 | 
			
		||||
                   	end
 | 
			
		||||
			    end
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            local removed = map.removed
 | 
			
		||||
 | 
			
		||||
            if removed then
 | 
			
		||||
                for i, e in removed do
 | 
			
		||||
                    if not world:contains(e) then
 | 
			
		||||
                        continue
 | 
			
		||||
                    end
 | 
			
		||||
                    world:remove(e, id)
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,15 @@
 | 
			
		|||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
			
		||||
local observers_add = require("../ReplicatedStorage/observers_add")
 | 
			
		||||
 | 
			
		||||
export type World = typeof(observers_add(jecs.world()))
 | 
			
		||||
export type Entity = jecs.Entity
 | 
			
		||||
export type Id<T> = jecs.Id<T>
 | 
			
		||||
export type Snapshot = {
 | 
			
		||||
	[string]: {
 | 
			
		||||
		set: { jecs.Entity }?,
 | 
			
		||||
		values: { any }?,
 | 
			
		||||
		removed: { jecs.Entity }?
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
return {}
 | 
			
		||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
			
		||||
local observers_add = require("../ReplicatedStorage/observers_add")
 | 
			
		||||
 | 
			
		||||
export type World = typeof(observers_add(jecs.world()))
 | 
			
		||||
export type Entity = jecs.Entity
 | 
			
		||||
export type Id<T> = jecs.Id<T>
 | 
			
		||||
export type Snapshot = {
 | 
			
		||||
	[string]: {
 | 
			
		||||
		set: { jecs.Entity }?,
 | 
			
		||||
		values: { any }?,
 | 
			
		||||
		removed: { jecs.Entity }?
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
return {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +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 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)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
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 in world:query(ct.Player, ct.Health):without(ct.Poison) do
 | 
			
		||||
		world:set(e, ct.Poison, 10)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,20 @@
 | 
			
		|||
local collect = require("../../ReplicatedStorage/collect")
 | 
			
		||||
local types = require("../../ReplicatedStorage/types")
 | 
			
		||||
local ct = require("../../ReplicatedStorage/components")
 | 
			
		||||
local Players = game:GetService("Players")
 | 
			
		||||
 | 
			
		||||
local player_added = collect(Players.PlayerAdded)
 | 
			
		||||
return function(world: types.World, dt: number)
 | 
			
		||||
	for player in player_added do
 | 
			
		||||
		local entity = world:entity()
 | 
			
		||||
		world:set(entity, ct.Player, player)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for entity, player in world:query(ct.Player):without(ct.Renderable) do
 | 
			
		||||
		local character = player.Character
 | 
			
		||||
		if character then
 | 
			
		||||
		if not character.Parent then
 | 
			
		||||
			world:set(entity, ct.Renderable, character)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
local collect = require("../../ReplicatedStorage/collect")
 | 
			
		||||
local types = require("../../ReplicatedStorage/types")
 | 
			
		||||
local ct = require("../../ReplicatedStorage/components")
 | 
			
		||||
local Players = game:GetService("Players")
 | 
			
		||||
 | 
			
		||||
local player_added = collect(Players.PlayerAdded)
 | 
			
		||||
return function(world: types.World, dt: number)
 | 
			
		||||
	for player in player_added do
 | 
			
		||||
		local entity = world:entity()
 | 
			
		||||
		world:set(entity, ct.Player, player)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for entity, player in world:query(ct.Player):without(ct.Renderable) do
 | 
			
		||||
		local character = player.Character
 | 
			
		||||
		if character then
 | 
			
		||||
		if not character.Parent then
 | 
			
		||||
			world:set(entity, ct.Renderable, character)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,12 @@
 | 
			
		|||
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)
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		world:set(e, ct.Health, health_after_tick)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
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)
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		world:set(e, ct.Health, health_after_tick)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,190 +1,190 @@
 | 
			
		|||
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 components = ct :: {[string]: jecs.Entity }
 | 
			
		||||
 | 
			
		||||
return function(world: ty.World)
 | 
			
		||||
 | 
			
		||||
	--- integration test
 | 
			
		||||
 | 
			
		||||
 --    for _ = 1, 10 do
 | 
			
		||||
 --    	local e = world:entity()
 | 
			
		||||
 --     	world:set(e, ct.TestA, true)
 | 
			
		||||
 --    end
 | 
			
		||||
 | 
			
		||||
    local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
 | 
			
		||||
    local networked_components = {}
 | 
			
		||||
    local networked_pairs = {}
 | 
			
		||||
 | 
			
		||||
    for component in world:each(ct.Networked) do
 | 
			
		||||
    	local name = world:get(component, jecs.Name) :: string
 | 
			
		||||
		if components[name] == nil then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        storages[component] = {}
 | 
			
		||||
 | 
			
		||||
        table.insert(networked_components, component)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
	for relation in world:each(ct.NetworkedPair) do
 | 
			
		||||
		local name = world:get(relation, jecs.Name) :: string
 | 
			
		||||
		if not components[name] then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(networked_pairs, relation)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, component in networked_components do
 | 
			
		||||
		local name = world:get(component, jecs.Name) :: string
 | 
			
		||||
		if not components[name] then
 | 
			
		||||
			error(`Networked Component (%id{component}%name{name})`)
 | 
			
		||||
		end
 | 
			
		||||
		local is_tag = jecs.is_tag(world, component)
 | 
			
		||||
		local storage = storages[component]
 | 
			
		||||
		if is_tag then
 | 
			
		||||
		    world:added(component, function(entity)
 | 
			
		||||
		        storage[entity] = true
 | 
			
		||||
		    end)
 | 
			
		||||
		else
 | 
			
		||||
		    world:added(component, function(entity, _, value)
 | 
			
		||||
		        storage[entity] = value
 | 
			
		||||
		    end)
 | 
			
		||||
		    world:changed(component, function(entity, _, value)
 | 
			
		||||
		        storage[entity] = value
 | 
			
		||||
		    end)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        world:removed(component, function(entity)
 | 
			
		||||
            storage[entity] = "jecs.Remove"
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, relation in networked_pairs do
 | 
			
		||||
		world:added(relation, function(entity, id, value)
 | 
			
		||||
	        local is_tag = jecs.is_tag(world, id)
 | 
			
		||||
	        local storage = storages[id]
 | 
			
		||||
	        if not storage then
 | 
			
		||||
	            storage = {}
 | 
			
		||||
	            storages[id] = storage
 | 
			
		||||
	        end
 | 
			
		||||
	        if is_tag then
 | 
			
		||||
	            storage[entity] = true
 | 
			
		||||
	        else
 | 
			
		||||
	            storage[entity] = value
 | 
			
		||||
	        end
 | 
			
		||||
	    end)
 | 
			
		||||
 | 
			
		||||
	    world:changed(relation, function(entity, id, value)
 | 
			
		||||
	        local is_tag = jecs.is_tag(world, id)
 | 
			
		||||
	        if is_tag then
 | 
			
		||||
	            return
 | 
			
		||||
	        end
 | 
			
		||||
 | 
			
		||||
	        local storage = storages[id]
 | 
			
		||||
	        if not storage then
 | 
			
		||||
	            storage = {}
 | 
			
		||||
	            storages[id] = storage
 | 
			
		||||
	        end
 | 
			
		||||
 | 
			
		||||
	        storage[entity] = value
 | 
			
		||||
	    end)
 | 
			
		||||
 | 
			
		||||
	    world:removed(relation, function(entity, id)
 | 
			
		||||
	        local storage = storages[id]
 | 
			
		||||
	        if not storage then
 | 
			
		||||
	            storage = {}
 | 
			
		||||
	            storages[id] = storage
 | 
			
		||||
	        end
 | 
			
		||||
 | 
			
		||||
	        storage[entity] = "jecs.Remove"
 | 
			
		||||
	    end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local players_added = collect(Players.PlayerAdded)
 | 
			
		||||
 | 
			
		||||
    return function()
 | 
			
		||||
        local snapshot_lazy: ty.Snapshot
 | 
			
		||||
        local set_ids_lazy: { jecs.Entity }
 | 
			
		||||
 | 
			
		||||
		for player in players_added do
 | 
			
		||||
			if not snapshot_lazy then
 | 
			
		||||
				snapshot_lazy, set_ids_lazy =  {}, {}
 | 
			
		||||
 | 
			
		||||
				for component, storage in storages do
 | 
			
		||||
					local set_values = {}
 | 
			
		||||
					local set_n = 0
 | 
			
		||||
 | 
			
		||||
					local q = world:query(component)
 | 
			
		||||
					local is_tag = jecs.is_tag(world, component)
 | 
			
		||||
					for _, archetype in q:archetypes() do
 | 
			
		||||
						local entities = archetype.entities
 | 
			
		||||
						local entities_len = #entities
 | 
			
		||||
						table.move(entities, 1, entities_len, set_n + 1, set_ids_lazy)
 | 
			
		||||
						if is_tag then
 | 
			
		||||
							set_values = table.create(entities_len, true)
 | 
			
		||||
						else
 | 
			
		||||
							local column = archetype.columns[archetype.records[component]]
 | 
			
		||||
							table.move(column, 1, entities_len, set_n + 1, set_values)
 | 
			
		||||
						end
 | 
			
		||||
 | 
			
		||||
						set_n += entities_len
 | 
			
		||||
					end
 | 
			
		||||
 | 
			
		||||
					local set = table.move(set_ids_lazy, 1, set_n, 1, {})
 | 
			
		||||
 | 
			
		||||
					snapshot_lazy[tostring(component)] = {
 | 
			
		||||
					    set = if set_n > 0 then set else nil,
 | 
			
		||||
					    values = if set_n > 0 then set_values else nil,
 | 
			
		||||
					}
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			remotes.replication:FireClient(player, snapshot_lazy)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local snapshot = {} :: ty.Snapshot
 | 
			
		||||
 | 
			
		||||
		local set_ids = {}
 | 
			
		||||
		local removed_ids = {}
 | 
			
		||||
 | 
			
		||||
		for component, storage in storages do
 | 
			
		||||
		    local set_values = {} :: { any }
 | 
			
		||||
		    local set_n = 0
 | 
			
		||||
		    local removed_n = 0
 | 
			
		||||
		    for e, v in storage do
 | 
			
		||||
		        if v ~= "jecs.Remove" then
 | 
			
		||||
		            set_n += 1
 | 
			
		||||
		            set_ids[set_n] = e
 | 
			
		||||
		            set_values[set_n] = v or true
 | 
			
		||||
		        elseif not world:contains(e) then
 | 
			
		||||
		            removed_n += 1
 | 
			
		||||
		            removed_ids[removed_n] = e
 | 
			
		||||
		        end
 | 
			
		||||
		    end
 | 
			
		||||
 | 
			
		||||
		    table.clear(storage)
 | 
			
		||||
 | 
			
		||||
            local dirty = false
 | 
			
		||||
 | 
			
		||||
            if set_n > 0 or removed_n > 0 then
 | 
			
		||||
            	dirty = true
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            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)] = {
 | 
			
		||||
                    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
 | 
			
		||||
                }
 | 
			
		||||
	        end
 | 
			
		||||
        end
 | 
			
		||||
        if next(snapshot) ~= nil then
 | 
			
		||||
        	remotes.replication:FireAllClients(snapshot)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
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 components = ct :: {[string]: jecs.Entity }
 | 
			
		||||
 | 
			
		||||
return function(world: ty.World)
 | 
			
		||||
 | 
			
		||||
	--- integration test
 | 
			
		||||
 | 
			
		||||
 --    for _ = 1, 10 do
 | 
			
		||||
 --    	local e = world:entity()
 | 
			
		||||
 --     	world:set(e, ct.TestA, true)
 | 
			
		||||
 --    end
 | 
			
		||||
 | 
			
		||||
    local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
 | 
			
		||||
    local networked_components = {}
 | 
			
		||||
    local networked_pairs = {}
 | 
			
		||||
 | 
			
		||||
    for component in world:each(ct.Networked) do
 | 
			
		||||
    	local name = world:get(component, jecs.Name) :: string
 | 
			
		||||
		if components[name] == nil then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        storages[component] = {}
 | 
			
		||||
 | 
			
		||||
        table.insert(networked_components, component)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
	for relation in world:each(ct.NetworkedPair) do
 | 
			
		||||
		local name = world:get(relation, jecs.Name) :: string
 | 
			
		||||
		if not components[name] then
 | 
			
		||||
			continue
 | 
			
		||||
		end
 | 
			
		||||
		table.insert(networked_pairs, relation)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, component in networked_components do
 | 
			
		||||
		local name = world:get(component, jecs.Name) :: string
 | 
			
		||||
		if not components[name] then
 | 
			
		||||
			error(`Networked Component (%id{component}%name{name})`)
 | 
			
		||||
		end
 | 
			
		||||
		local is_tag = jecs.is_tag(world, component)
 | 
			
		||||
		local storage = storages[component]
 | 
			
		||||
		if is_tag then
 | 
			
		||||
		    world:added(component, function(entity)
 | 
			
		||||
		        storage[entity] = true
 | 
			
		||||
		    end)
 | 
			
		||||
		else
 | 
			
		||||
		    world:added(component, function(entity, _, value)
 | 
			
		||||
		        storage[entity] = value
 | 
			
		||||
		    end)
 | 
			
		||||
		    world:changed(component, function(entity, _, value)
 | 
			
		||||
		        storage[entity] = value
 | 
			
		||||
		    end)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        world:removed(component, function(entity)
 | 
			
		||||
            storage[entity] = "jecs.Remove"
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, relation in networked_pairs do
 | 
			
		||||
		world:added(relation, function(entity, id, value)
 | 
			
		||||
	        local is_tag = jecs.is_tag(world, id)
 | 
			
		||||
	        local storage = storages[id]
 | 
			
		||||
	        if not storage then
 | 
			
		||||
	            storage = {}
 | 
			
		||||
	            storages[id] = storage
 | 
			
		||||
	        end
 | 
			
		||||
	        if is_tag then
 | 
			
		||||
	            storage[entity] = true
 | 
			
		||||
	        else
 | 
			
		||||
	            storage[entity] = value
 | 
			
		||||
	        end
 | 
			
		||||
	    end)
 | 
			
		||||
 | 
			
		||||
	    world:changed(relation, function(entity, id, value)
 | 
			
		||||
	        local is_tag = jecs.is_tag(world, id)
 | 
			
		||||
	        if is_tag then
 | 
			
		||||
	            return
 | 
			
		||||
	        end
 | 
			
		||||
 | 
			
		||||
	        local storage = storages[id]
 | 
			
		||||
	        if not storage then
 | 
			
		||||
	            storage = {}
 | 
			
		||||
	            storages[id] = storage
 | 
			
		||||
	        end
 | 
			
		||||
 | 
			
		||||
	        storage[entity] = value
 | 
			
		||||
	    end)
 | 
			
		||||
 | 
			
		||||
	    world:removed(relation, function(entity, id)
 | 
			
		||||
	        local storage = storages[id]
 | 
			
		||||
	        if not storage then
 | 
			
		||||
	            storage = {}
 | 
			
		||||
	            storages[id] = storage
 | 
			
		||||
	        end
 | 
			
		||||
 | 
			
		||||
	        storage[entity] = "jecs.Remove"
 | 
			
		||||
	    end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local players_added = collect(Players.PlayerAdded)
 | 
			
		||||
 | 
			
		||||
    return function()
 | 
			
		||||
        local snapshot_lazy: ty.Snapshot
 | 
			
		||||
        local set_ids_lazy: { jecs.Entity }
 | 
			
		||||
 | 
			
		||||
		for player in players_added do
 | 
			
		||||
			if not snapshot_lazy then
 | 
			
		||||
				snapshot_lazy, set_ids_lazy =  {}, {}
 | 
			
		||||
 | 
			
		||||
				for component, storage in storages do
 | 
			
		||||
					local set_values = {}
 | 
			
		||||
					local set_n = 0
 | 
			
		||||
 | 
			
		||||
					local q = world:query(component)
 | 
			
		||||
					local is_tag = jecs.is_tag(world, component)
 | 
			
		||||
					for _, archetype in q:archetypes() do
 | 
			
		||||
						local entities = archetype.entities
 | 
			
		||||
						local entities_len = #entities
 | 
			
		||||
						table.move(entities, 1, entities_len, set_n + 1, set_ids_lazy)
 | 
			
		||||
						if is_tag then
 | 
			
		||||
							set_values = table.create(entities_len, true)
 | 
			
		||||
						else
 | 
			
		||||
							local column = archetype.columns[archetype.records[component]]
 | 
			
		||||
							table.move(column, 1, entities_len, set_n + 1, set_values)
 | 
			
		||||
						end
 | 
			
		||||
 | 
			
		||||
						set_n += entities_len
 | 
			
		||||
					end
 | 
			
		||||
 | 
			
		||||
					local set = table.move(set_ids_lazy, 1, set_n, 1, {})
 | 
			
		||||
 | 
			
		||||
					snapshot_lazy[tostring(component)] = {
 | 
			
		||||
					    set = if set_n > 0 then set else nil,
 | 
			
		||||
					    values = if set_n > 0 then set_values else nil,
 | 
			
		||||
					}
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			remotes.replication:FireClient(player, snapshot_lazy)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local snapshot = {} :: ty.Snapshot
 | 
			
		||||
 | 
			
		||||
		local set_ids = {}
 | 
			
		||||
		local removed_ids = {}
 | 
			
		||||
 | 
			
		||||
		for component, storage in storages do
 | 
			
		||||
		    local set_values = {} :: { any }
 | 
			
		||||
		    local set_n = 0
 | 
			
		||||
		    local removed_n = 0
 | 
			
		||||
		    for e, v in storage do
 | 
			
		||||
		        if v ~= "jecs.Remove" then
 | 
			
		||||
		            set_n += 1
 | 
			
		||||
		            set_ids[set_n] = e
 | 
			
		||||
		            set_values[set_n] = v or true
 | 
			
		||||
		        elseif not world:contains(e) then
 | 
			
		||||
		            removed_n += 1
 | 
			
		||||
		            removed_ids[removed_n] = e
 | 
			
		||||
		        end
 | 
			
		||||
		    end
 | 
			
		||||
 | 
			
		||||
		    table.clear(storage)
 | 
			
		||||
 | 
			
		||||
            local dirty = false
 | 
			
		||||
 | 
			
		||||
            if set_n > 0 or removed_n > 0 then
 | 
			
		||||
            	dirty = true
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            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)] = {
 | 
			
		||||
                    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
 | 
			
		||||
                }
 | 
			
		||||
	        end
 | 
			
		||||
        end
 | 
			
		||||
        if next(snapshot) ~= nil then
 | 
			
		||||
        	remotes.replication:FireAllClients(snapshot)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										316
									
								
								test/lol.luau
									
									
									
									
									
								
							
							
						
						
									
										316
									
								
								test/lol.luau
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,158 +1,158 @@
 | 
			
		|||
local c = {
 | 
			
		||||
	white_underline = function(s: any)
 | 
			
		||||
		return `\27[1;4m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	white = function(s: any)
 | 
			
		||||
		return `\27[37;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	green = function(s: any)
 | 
			
		||||
		return `\27[32;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	red = function(s: any)
 | 
			
		||||
		return `\27[31;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	yellow = function(s: any)
 | 
			
		||||
		return `\27[33;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	red_highlight = function(s: any)
 | 
			
		||||
		return `\27[41;1;30m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	green_highlight = function(s: any)
 | 
			
		||||
		return `\27[42;1;30m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	gray = function(s: any)
 | 
			
		||||
		return `\27[30;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local ECS_PAIR_FLAG =                      0x8
 | 
			
		||||
local ECS_ID_FLAGS_MASK =                 0x10
 | 
			
		||||
local ECS_ENTITY_MASK =     bit32.lshift(1, 24)
 | 
			
		||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
 | 
			
		||||
 | 
			
		||||
type i53 = number
 | 
			
		||||
type i24 = number
 | 
			
		||||
 | 
			
		||||
local function ECS_ENTITY_T_LO(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_GENERATION(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local ECS_ID = ECS_ENTITY_T_LO
 | 
			
		||||
 | 
			
		||||
local function ECS_COMBINE(source: number, target: number): i53
 | 
			
		||||
	return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_GENERATION_INC(e: i53)
 | 
			
		||||
	if e > ECS_ENTITY_MASK then
 | 
			
		||||
		local flags = e // ECS_ID_FLAGS_MASK
 | 
			
		||||
		local id = flags // ECS_ENTITY_MASK
 | 
			
		||||
		local generation = flags % ECS_GENERATION_MASK
 | 
			
		||||
 | 
			
		||||
		local next_gen = generation + 1
 | 
			
		||||
		if next_gen > ECS_GENERATION_MASK then
 | 
			
		||||
			return id
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		return ECS_COMBINE(id, next_gen) + flags
 | 
			
		||||
	end
 | 
			
		||||
	return ECS_COMBINE(e, 1)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function bl()
 | 
			
		||||
	print("")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function pe(e)
 | 
			
		||||
	local gen = ECS_GENERATION(e)
 | 
			
		||||
	return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function dprint(tbl: {  [number]: number })
 | 
			
		||||
	bl()
 | 
			
		||||
	print("--------")
 | 
			
		||||
	for i, e in tbl do
 | 
			
		||||
		print("| "..pe(e).." |")
 | 
			
		||||
		print("--------")
 | 
			
		||||
	end
 | 
			
		||||
	bl()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local max_id = 0
 | 
			
		||||
local alive_count = 0
 | 
			
		||||
local dense = {}
 | 
			
		||||
local sparse = {}
 | 
			
		||||
local function alloc()
 | 
			
		||||
	if alive_count ~= #dense then
 | 
			
		||||
		alive_count += 1
 | 
			
		||||
		print("*recycled", pe(dense[alive_count]))
 | 
			
		||||
		return dense[alive_count]
 | 
			
		||||
	end
 | 
			
		||||
	max_id += 1
 | 
			
		||||
	local id = max_id
 | 
			
		||||
	alive_count += 1
 | 
			
		||||
	dense[alive_count] = id
 | 
			
		||||
	sparse[id] = {
 | 
			
		||||
		dense = alive_count
 | 
			
		||||
	}
 | 
			
		||||
	print("*allocated", pe(id))
 | 
			
		||||
	return id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function remove(entity)
 | 
			
		||||
	local id = ECS_ID(entity)
 | 
			
		||||
	local r = sparse[id]
 | 
			
		||||
	local index_of_deleted_entity = r.dense
 | 
			
		||||
	local last_entity_alive_at_index = alive_count -- last entity alive
 | 
			
		||||
	alive_count -= 1
 | 
			
		||||
	local last_alive_entity = dense[last_entity_alive_at_index]
 | 
			
		||||
	local r_swap = sparse[ECS_ID(last_alive_entity)]
 | 
			
		||||
	r_swap.dense = r.dense
 | 
			
		||||
	r.dense = last_entity_alive_at_index
 | 
			
		||||
	dense[index_of_deleted_entity] = last_alive_entity
 | 
			
		||||
	dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
 | 
			
		||||
	print("*dellocated", pe(id))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function alive(e)
 | 
			
		||||
	local r = sparse[ECS_ID(e)]
 | 
			
		||||
 | 
			
		||||
	return dense[r.dense] == e
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function pa(e)
 | 
			
		||||
	print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local tprint = require("@testkit").print
 | 
			
		||||
local e1v0 = alloc()
 | 
			
		||||
local e2v0 = alloc()
 | 
			
		||||
local e3v0 = alloc()
 | 
			
		||||
local e4v0 = alloc()
 | 
			
		||||
local e5v0 = alloc()
 | 
			
		||||
pa(e1v0)
 | 
			
		||||
pa(e4v0)
 | 
			
		||||
remove(e5v0)
 | 
			
		||||
pa(e5v0)
 | 
			
		||||
 | 
			
		||||
local e5v1 = alloc()
 | 
			
		||||
pa(e5v0)
 | 
			
		||||
pa(e5v1)
 | 
			
		||||
pa(e2v0)
 | 
			
		||||
print(ECS_ID(e2v0))
 | 
			
		||||
 | 
			
		||||
dprint(dense)
 | 
			
		||||
remove(e2v0)
 | 
			
		||||
dprint(dense)
 | 
			
		||||
local c = {
 | 
			
		||||
	white_underline = function(s: any)
 | 
			
		||||
		return `\27[1;4m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	white = function(s: any)
 | 
			
		||||
		return `\27[37;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	green = function(s: any)
 | 
			
		||||
		return `\27[32;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	red = function(s: any)
 | 
			
		||||
		return `\27[31;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	yellow = function(s: any)
 | 
			
		||||
		return `\27[33;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	red_highlight = function(s: any)
 | 
			
		||||
		return `\27[41;1;30m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	green_highlight = function(s: any)
 | 
			
		||||
		return `\27[42;1;30m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
 | 
			
		||||
	gray = function(s: any)
 | 
			
		||||
		return `\27[30;1m{s}\27[0m`
 | 
			
		||||
	end,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local ECS_PAIR_FLAG =                      0x8
 | 
			
		||||
local ECS_ID_FLAGS_MASK =                 0x10
 | 
			
		||||
local ECS_ENTITY_MASK =     bit32.lshift(1, 24)
 | 
			
		||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
 | 
			
		||||
 | 
			
		||||
type i53 = number
 | 
			
		||||
type i24 = number
 | 
			
		||||
 | 
			
		||||
local function ECS_ENTITY_T_LO(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_GENERATION(e: i53): i24
 | 
			
		||||
	return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local ECS_ID = ECS_ENTITY_T_LO
 | 
			
		||||
 | 
			
		||||
local function ECS_COMBINE(source: number, target: number): i53
 | 
			
		||||
	return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function ECS_GENERATION_INC(e: i53)
 | 
			
		||||
	if e > ECS_ENTITY_MASK then
 | 
			
		||||
		local flags = e // ECS_ID_FLAGS_MASK
 | 
			
		||||
		local id = flags // ECS_ENTITY_MASK
 | 
			
		||||
		local generation = flags % ECS_GENERATION_MASK
 | 
			
		||||
 | 
			
		||||
		local next_gen = generation + 1
 | 
			
		||||
		if next_gen > ECS_GENERATION_MASK then
 | 
			
		||||
			return id
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		return ECS_COMBINE(id, next_gen) + flags
 | 
			
		||||
	end
 | 
			
		||||
	return ECS_COMBINE(e, 1)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function bl()
 | 
			
		||||
	print("")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function pe(e)
 | 
			
		||||
	local gen = ECS_GENERATION(e)
 | 
			
		||||
	return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function dprint(tbl: {  [number]: number })
 | 
			
		||||
	bl()
 | 
			
		||||
	print("--------")
 | 
			
		||||
	for i, e in tbl do
 | 
			
		||||
		print("| "..pe(e).." |")
 | 
			
		||||
		print("--------")
 | 
			
		||||
	end
 | 
			
		||||
	bl()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local max_id = 0
 | 
			
		||||
local alive_count = 0
 | 
			
		||||
local dense = {}
 | 
			
		||||
local sparse = {}
 | 
			
		||||
local function alloc()
 | 
			
		||||
	if alive_count ~= #dense then
 | 
			
		||||
		alive_count += 1
 | 
			
		||||
		print("*recycled", pe(dense[alive_count]))
 | 
			
		||||
		return dense[alive_count]
 | 
			
		||||
	end
 | 
			
		||||
	max_id += 1
 | 
			
		||||
	local id = max_id
 | 
			
		||||
	alive_count += 1
 | 
			
		||||
	dense[alive_count] = id
 | 
			
		||||
	sparse[id] = {
 | 
			
		||||
		dense = alive_count
 | 
			
		||||
	}
 | 
			
		||||
	print("*allocated", pe(id))
 | 
			
		||||
	return id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function remove(entity)
 | 
			
		||||
	local id = ECS_ID(entity)
 | 
			
		||||
	local r = sparse[id]
 | 
			
		||||
	local index_of_deleted_entity = r.dense
 | 
			
		||||
	local last_entity_alive_at_index = alive_count -- last entity alive
 | 
			
		||||
	alive_count -= 1
 | 
			
		||||
	local last_alive_entity = dense[last_entity_alive_at_index]
 | 
			
		||||
	local r_swap = sparse[ECS_ID(last_alive_entity)]
 | 
			
		||||
	r_swap.dense = r.dense
 | 
			
		||||
	r.dense = last_entity_alive_at_index
 | 
			
		||||
	dense[index_of_deleted_entity] = last_alive_entity
 | 
			
		||||
	dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
 | 
			
		||||
	print("*dellocated", pe(id))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function alive(e)
 | 
			
		||||
	local r = sparse[ECS_ID(e)]
 | 
			
		||||
 | 
			
		||||
	return dense[r.dense] == e
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function pa(e)
 | 
			
		||||
	print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local tprint = require("@testkit").print
 | 
			
		||||
local e1v0 = alloc()
 | 
			
		||||
local e2v0 = alloc()
 | 
			
		||||
local e3v0 = alloc()
 | 
			
		||||
local e4v0 = alloc()
 | 
			
		||||
local e5v0 = alloc()
 | 
			
		||||
pa(e1v0)
 | 
			
		||||
pa(e4v0)
 | 
			
		||||
remove(e5v0)
 | 
			
		||||
pa(e5v0)
 | 
			
		||||
 | 
			
		||||
local e5v1 = alloc()
 | 
			
		||||
pa(e5v0)
 | 
			
		||||
pa(e5v1)
 | 
			
		||||
pa(e2v0)
 | 
			
		||||
print(ECS_ID(e2v0))
 | 
			
		||||
 | 
			
		||||
dprint(dense)
 | 
			
		||||
remove(e2v0)
 | 
			
		||||
dprint(dense)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,122 +1,122 @@
 | 
			
		|||
local RunService = game:GetService("RunService")
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
_G.__JECS_HI_COMPONENT_ID = 300
 | 
			
		||||
local ecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
-- 500 entities
 | 
			
		||||
-- 2-30 components on each entity
 | 
			
		||||
-- 300 unique components
 | 
			
		||||
-- 200 systems
 | 
			
		||||
-- 1-10 components to query per system
 | 
			
		||||
 | 
			
		||||
local startTime = os.clock()
 | 
			
		||||
 | 
			
		||||
local world = ecs.World.new()
 | 
			
		||||
 | 
			
		||||
local components = {}
 | 
			
		||||
 | 
			
		||||
for i = 1, 300 do -- 300 components
 | 
			
		||||
	components[i] = world:component()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local archetypes = {}
 | 
			
		||||
for i = 1, 50 do -- 50 archetypes
 | 
			
		||||
	local archetype = {}
 | 
			
		||||
 | 
			
		||||
	for _ = 1, math.random(2, 30) do
 | 
			
		||||
		local componentId = math.random(1, #components)
 | 
			
		||||
 | 
			
		||||
		table.insert(archetype, components[componentId])
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	archetypes[i] = archetype
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
for _ = 1, 1000 do -- 1000 entities in the world
 | 
			
		||||
	local componentsToAdd = {}
 | 
			
		||||
 | 
			
		||||
	local archetypeId = math.random(1, #archetypes)
 | 
			
		||||
	local e = world:entity()
 | 
			
		||||
	for _, component in ipairs(archetypes[archetypeId]) do
 | 
			
		||||
		world:set(e, component, {
 | 
			
		||||
			DummyData = math.random(1, 5000),
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function values(t)
 | 
			
		||||
	local array = {}
 | 
			
		||||
	for _, v in t do
 | 
			
		||||
		table.insert(array, v)
 | 
			
		||||
	end
 | 
			
		||||
	return array
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local contiguousComponents = values(components)
 | 
			
		||||
local systemComponentsToQuery = {}
 | 
			
		||||
 | 
			
		||||
for _ = 1, 200 do -- 200 systems
 | 
			
		||||
	local numComponentsToQuery = math.random(1, 10)
 | 
			
		||||
	local componentsToQuery = {}
 | 
			
		||||
 | 
			
		||||
	for _ = 1, numComponentsToQuery do
 | 
			
		||||
		table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	table.insert(systemComponentsToQuery, componentsToQuery)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local worldCreateTime = os.clock() - startTime
 | 
			
		||||
local results = {}
 | 
			
		||||
startTime = os.clock()
 | 
			
		||||
 | 
			
		||||
RunService.Heartbeat:Connect(function()
 | 
			
		||||
	local added = 0
 | 
			
		||||
	local systemStartTime = os.clock()
 | 
			
		||||
	debug.profilebegin("systems")
 | 
			
		||||
	for _, componentsToQuery in ipairs(systemComponentsToQuery) do
 | 
			
		||||
		debug.profilebegin("system")
 | 
			
		||||
		for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
 | 
			
		||||
			world:set(
 | 
			
		||||
				entityId,
 | 
			
		||||
				{
 | 
			
		||||
					DummyData = firstComponent.DummyData + 1,
 | 
			
		||||
				}
 | 
			
		||||
			)
 | 
			
		||||
			added += 1
 | 
			
		||||
		end
 | 
			
		||||
		debug.profileend()
 | 
			
		||||
	end
 | 
			
		||||
	debug.profileend()
 | 
			
		||||
 | 
			
		||||
	if os.clock() - startTime < 4 then
 | 
			
		||||
		-- discard first 4 seconds
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if results == nil then
 | 
			
		||||
		return
 | 
			
		||||
	elseif #results < 1000 then
 | 
			
		||||
		table.insert(results, os.clock() - systemStartTime)
 | 
			
		||||
	else
 | 
			
		||||
		print("added", added)
 | 
			
		||||
		print("World created in", worldCreateTime * 1000, "ms")
 | 
			
		||||
		local sum = 0
 | 
			
		||||
		for _, result in ipairs(results) do
 | 
			
		||||
			sum += result
 | 
			
		||||
		end
 | 
			
		||||
		print(("Average frame time: %fms"):format((sum / #results) * 1000))
 | 
			
		||||
 | 
			
		||||
		results = nil
 | 
			
		||||
 | 
			
		||||
		local n = #world.archetypes
 | 
			
		||||
 | 
			
		||||
		print(
 | 
			
		||||
			("X entities\n%d components\n%d systems\n%d archetypes"):format(
 | 
			
		||||
				#components,
 | 
			
		||||
				#systemComponentsToQuery,
 | 
			
		||||
				n
 | 
			
		||||
			)
 | 
			
		||||
		)
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
local RunService = game:GetService("RunService")
 | 
			
		||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
			
		||||
_G.__JECS_HI_COMPONENT_ID = 300
 | 
			
		||||
local ecs = require(ReplicatedStorage.ecs)
 | 
			
		||||
 | 
			
		||||
-- 500 entities
 | 
			
		||||
-- 2-30 components on each entity
 | 
			
		||||
-- 300 unique components
 | 
			
		||||
-- 200 systems
 | 
			
		||||
-- 1-10 components to query per system
 | 
			
		||||
 | 
			
		||||
local startTime = os.clock()
 | 
			
		||||
 | 
			
		||||
local world = ecs.World.new()
 | 
			
		||||
 | 
			
		||||
local components = {}
 | 
			
		||||
 | 
			
		||||
for i = 1, 300 do -- 300 components
 | 
			
		||||
	components[i] = world:component()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local archetypes = {}
 | 
			
		||||
for i = 1, 50 do -- 50 archetypes
 | 
			
		||||
	local archetype = {}
 | 
			
		||||
 | 
			
		||||
	for _ = 1, math.random(2, 30) do
 | 
			
		||||
		local componentId = math.random(1, #components)
 | 
			
		||||
 | 
			
		||||
		table.insert(archetype, components[componentId])
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	archetypes[i] = archetype
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
for _ = 1, 1000 do -- 1000 entities in the world
 | 
			
		||||
	local componentsToAdd = {}
 | 
			
		||||
 | 
			
		||||
	local archetypeId = math.random(1, #archetypes)
 | 
			
		||||
	local e = world:entity()
 | 
			
		||||
	for _, component in ipairs(archetypes[archetypeId]) do
 | 
			
		||||
		world:set(e, component, {
 | 
			
		||||
			DummyData = math.random(1, 5000),
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function values(t)
 | 
			
		||||
	local array = {}
 | 
			
		||||
	for _, v in t do
 | 
			
		||||
		table.insert(array, v)
 | 
			
		||||
	end
 | 
			
		||||
	return array
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local contiguousComponents = values(components)
 | 
			
		||||
local systemComponentsToQuery = {}
 | 
			
		||||
 | 
			
		||||
for _ = 1, 200 do -- 200 systems
 | 
			
		||||
	local numComponentsToQuery = math.random(1, 10)
 | 
			
		||||
	local componentsToQuery = {}
 | 
			
		||||
 | 
			
		||||
	for _ = 1, numComponentsToQuery do
 | 
			
		||||
		table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	table.insert(systemComponentsToQuery, componentsToQuery)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local worldCreateTime = os.clock() - startTime
 | 
			
		||||
local results = {}
 | 
			
		||||
startTime = os.clock()
 | 
			
		||||
 | 
			
		||||
RunService.Heartbeat:Connect(function()
 | 
			
		||||
	local added = 0
 | 
			
		||||
	local systemStartTime = os.clock()
 | 
			
		||||
	debug.profilebegin("systems")
 | 
			
		||||
	for _, componentsToQuery in ipairs(systemComponentsToQuery) do
 | 
			
		||||
		debug.profilebegin("system")
 | 
			
		||||
		for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
 | 
			
		||||
			world:set(
 | 
			
		||||
				entityId,
 | 
			
		||||
				{
 | 
			
		||||
					DummyData = firstComponent.DummyData + 1,
 | 
			
		||||
				}
 | 
			
		||||
			)
 | 
			
		||||
			added += 1
 | 
			
		||||
		end
 | 
			
		||||
		debug.profileend()
 | 
			
		||||
	end
 | 
			
		||||
	debug.profileend()
 | 
			
		||||
 | 
			
		||||
	if os.clock() - startTime < 4 then
 | 
			
		||||
		-- discard first 4 seconds
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if results == nil then
 | 
			
		||||
		return
 | 
			
		||||
	elseif #results < 1000 then
 | 
			
		||||
		table.insert(results, os.clock() - systemStartTime)
 | 
			
		||||
	else
 | 
			
		||||
		print("added", added)
 | 
			
		||||
		print("World created in", worldCreateTime * 1000, "ms")
 | 
			
		||||
		local sum = 0
 | 
			
		||||
		for _, result in ipairs(results) do
 | 
			
		||||
			sum += result
 | 
			
		||||
		end
 | 
			
		||||
		print(("Average frame time: %fms"):format((sum / #results) * 1000))
 | 
			
		||||
 | 
			
		||||
		results = nil
 | 
			
		||||
 | 
			
		||||
		local n = #world.archetypes
 | 
			
		||||
 | 
			
		||||
		print(
 | 
			
		||||
			("X entities\n%d components\n%d systems\n%d archetypes"):format(
 | 
			
		||||
				#components,
 | 
			
		||||
				#systemComponentsToQuery,
 | 
			
		||||
				n
 | 
			
		||||
			)
 | 
			
		||||
		)
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,24 +1,24 @@
 | 
			
		|||
local jecs = require("@jecs")
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local ChildOf = jecs.ChildOf
 | 
			
		||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
			
		||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
 | 
			
		||||
local FriendsWith = world:component()
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
local e1 = world:entity()
 | 
			
		||||
local e2 = world:entity()
 | 
			
		||||
world:delete(e2)
 | 
			
		||||
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
local e3 = world:entity()
 | 
			
		||||
world:add(e3, pair(ChildOf, e1))
 | 
			
		||||
local e4 = world:entity()
 | 
			
		||||
world:add(e4, pair(FriendsWith, e3))
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
world:delete(e1)
 | 
			
		||||
world:delete(e3)
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
world:print_entity_index()
 | 
			
		||||
world:entity()
 | 
			
		||||
world:entity()
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
local jecs = require("@jecs")
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local ChildOf = jecs.ChildOf
 | 
			
		||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
			
		||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
 | 
			
		||||
local FriendsWith = world:component()
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
local e1 = world:entity()
 | 
			
		||||
local e2 = world:entity()
 | 
			
		||||
world:delete(e2)
 | 
			
		||||
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
local e3 = world:entity()
 | 
			
		||||
world:add(e3, pair(ChildOf, e1))
 | 
			
		||||
local e4 = world:entity()
 | 
			
		||||
world:add(e4, pair(FriendsWith, e3))
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
world:delete(e1)
 | 
			
		||||
world:delete(e3)
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
world:print_entity_index()
 | 
			
		||||
world:entity()
 | 
			
		||||
world:entity()
 | 
			
		||||
world:print_snapshot()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue