mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 10:59:18 +00:00 
			
		
		
		
	Allow pre existing hooks for observer
This commit is contained in:
		
							parent
							
								
									48a43d4ff8
								
							
						
					
					
						commit
						150afd784a
					
				
					 5 changed files with 108 additions and 90 deletions
				
			
		| 
						 | 
					@ -6,9 +6,9 @@ type Observer<T...> = {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type PatchedWorld = jecs.World & {
 | 
					export type PatchedWorld = jecs.World & {
 | 
				
			||||||
	added: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id, value: any) -> ()) -> (),
 | 
						added: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id, value: T) -> ()) -> () -> (),
 | 
				
			||||||
	removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
 | 
						removed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id) -> ()) -> () -> (),
 | 
				
			||||||
	changed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
 | 
						changed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id, value: T) -> ()) -> () -> (),
 | 
				
			||||||
	observer: (PatchedWorld, Observer<any>) -> (),
 | 
						observer: (PatchedWorld, Observer<any>) -> (),
 | 
				
			||||||
	monitor: (PatchedWorld, Observer<any>) -> (),
 | 
						monitor: (PatchedWorld, Observer<any>) -> (),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,7 @@ local function join(world, component)
 | 
				
			||||||
		sparse_array[entity] = nil
 | 
							sparse_array[entity] = nil
 | 
				
			||||||
		dense_array[max_id] = nil
 | 
							dense_array[max_id] = nil
 | 
				
			||||||
		values[max_id] = nil
 | 
							values[max_id] = nil
 | 
				
			||||||
 | 
							max_id -= 1
 | 
				
			||||||
	end)
 | 
						end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world:changed(component, function(entity, id, value)
 | 
						world:changed(component, function(entity, id, value)
 | 
				
			||||||
| 
						 | 
					@ -89,62 +90,6 @@ local function join(world, component)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function query_changed(world, component)
 | 
					 | 
				
			||||||
	assert(jecs.IS_PAIR(component) == false)
 | 
					 | 
				
			||||||
	local callerid = debug.info(2, "sl")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local tracker = world.trackers[callerid]
 | 
					 | 
				
			||||||
	if not tracker then
 | 
					 | 
				
			||||||
		local records = {}
 | 
					 | 
				
			||||||
		local connections = {}
 | 
					 | 
				
			||||||
		tracker = {
 | 
					 | 
				
			||||||
			records = records,
 | 
					 | 
				
			||||||
			connections = connections
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		world.trackers[callerid] = tracker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		table.insert(connections, world:added(component, function(entity, id, v)
 | 
					 | 
				
			||||||
			tracker[entity] = {
 | 
					 | 
				
			||||||
				new = v
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		end))
 | 
					 | 
				
			||||||
		table.insert(connections, world:changed(component, function(entity, id, v)
 | 
					 | 
				
			||||||
			local record = tracker[entity]
 | 
					 | 
				
			||||||
			record.old = record.new
 | 
					 | 
				
			||||||
			record.new = v
 | 
					 | 
				
			||||||
		end))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		table.insert(connections, world:removed(component, function(entity, id)
 | 
					 | 
				
			||||||
			local record = tracker[entity]
 | 
					 | 
				
			||||||
			record.old = record.new
 | 
					 | 
				
			||||||
			record.new = nil
 | 
					 | 
				
			||||||
		end))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local entity = nil
 | 
					 | 
				
			||||||
	local record = nil
 | 
					 | 
				
			||||||
	return function()
 | 
					 | 
				
			||||||
		entity, record = next(tracker, entity)
 | 
					 | 
				
			||||||
		if entity == nil then
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		return entity, record
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function spy_on_world_delete(world)
 | 
					 | 
				
			||||||
	local world_delete = world.delete
 | 
					 | 
				
			||||||
	world.delete = function(world, entity)
 | 
					 | 
				
			||||||
		world_delete(world, entity)
 | 
					 | 
				
			||||||
		for _, tracker in world.trackers do
 | 
					 | 
				
			||||||
			tracker.records[entity] = nil
 | 
					 | 
				
			||||||
			for _, connection in tracker.connections do
 | 
					 | 
				
			||||||
				connection()
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function monitors_new(world, description)
 | 
					local function monitors_new(world, description)
 | 
				
			||||||
	local query = description.query
 | 
						local query = description.query
 | 
				
			||||||
	local callback = description.callback
 | 
						local callback = description.callback
 | 
				
			||||||
| 
						 | 
					@ -192,18 +137,23 @@ local function monitors_new(world, description)
 | 
				
			||||||
 	end
 | 
					 	end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function observers_add(world: jecs.World & { [string]: any }): PatchedWorld
 | 
					local function observers_add(world: jecs.World): PatchedWorld
 | 
				
			||||||
	type Signal = { [jecs.Entity]: { (...any) -> () } }
 | 
						type Signal = { [jecs.Entity]: { (...any) -> () } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local world_mut = world :: jecs.World & {[string]: any}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local signals = {
 | 
						local signals = {
 | 
				
			||||||
		added = {} :: Signal,
 | 
							added = {} :: Signal,
 | 
				
			||||||
		emplaced = {} :: Signal,
 | 
							emplaced = {} :: Signal,
 | 
				
			||||||
		removed = {} :: Signal
 | 
							removed = {} :: Signal
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.added = function(_, component, fn)
 | 
						world_mut.added = function<T>(
 | 
				
			||||||
 | 
							_: jecs.World,
 | 
				
			||||||
 | 
							component: jecs.Id<T>,
 | 
				
			||||||
 | 
							fn: (e: jecs.Entity, id: jecs.Id, value: T) -> ()
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
		local listeners = signals.added[component]
 | 
							local listeners = signals.added[component]
 | 
				
			||||||
		local component_index = world.component_index :: jecs.ComponentIndex
 | 
					 | 
				
			||||||
		assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
 | 
					 | 
				
			||||||
		if not listeners then
 | 
							if not listeners then
 | 
				
			||||||
			listeners = {}
 | 
								listeners = {}
 | 
				
			||||||
			signals.added[component] = listeners
 | 
								signals.added[component] = listeners
 | 
				
			||||||
| 
						 | 
					@ -213,7 +163,16 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
					listener(entity, id, value)
 | 
										listener(entity, id, value)
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			world:set(component, jecs.OnAdd, on_add)
 | 
								local idr = world.component_index[component]
 | 
				
			||||||
 | 
								if idr then
 | 
				
			||||||
 | 
									local idr_hook_existing = idr.hooks.on_add
 | 
				
			||||||
 | 
									if idr_hook_existing then
 | 
				
			||||||
 | 
										table.insert(listeners, idr_hook_existing)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
									idr.hooks.on_add = on_add :: any
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									world:set(component, jecs.OnAdd, on_add)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		table.insert(listeners, fn)
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
		return function()
 | 
							return function()
 | 
				
			||||||
| 
						 | 
					@ -224,10 +183,12 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.changed = function(_, component, fn)
 | 
						world_mut.changed = function<T>(
 | 
				
			||||||
 | 
							_: jecs.World,
 | 
				
			||||||
 | 
							component: jecs.Id<T>,
 | 
				
			||||||
 | 
							fn: (e: jecs.Entity, id: jecs.Id, value: T) -> ()
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
		local listeners = signals.emplaced[component]
 | 
							local listeners = signals.emplaced[component]
 | 
				
			||||||
		local component_index = world.component_index :: jecs.ComponentIndex
 | 
					 | 
				
			||||||
		assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
 | 
					 | 
				
			||||||
		if not listeners then
 | 
							if not listeners then
 | 
				
			||||||
			listeners = {}
 | 
								listeners = {}
 | 
				
			||||||
			signals.emplaced[component] = listeners
 | 
								signals.emplaced[component] = listeners
 | 
				
			||||||
| 
						 | 
					@ -236,7 +197,16 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
					listener(entity, id, value)
 | 
										listener(entity, id, value)
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			world:set(component, jecs.OnChange, on_change)
 | 
								local idr = world.component_index[component]
 | 
				
			||||||
 | 
								if idr then
 | 
				
			||||||
 | 
									local idr_hook_existing = idr.hooks.on_change
 | 
				
			||||||
 | 
									if idr_hook_existing then
 | 
				
			||||||
 | 
										table.insert(listeners, idr_hook_existing)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
									idr.hooks.on_change = on_change :: any
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									world:set(component, jecs.OnChange, on_change)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		table.insert(listeners, fn)
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
		return function()
 | 
							return function()
 | 
				
			||||||
| 
						 | 
					@ -247,10 +217,12 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.removed = function(_, component, fn)
 | 
						world_mut.removed = function<T>(
 | 
				
			||||||
 | 
							_: jecs.World,
 | 
				
			||||||
 | 
							component: jecs.Id<T>,
 | 
				
			||||||
 | 
							fn: (e: jecs.Entity, id: jecs.Id) -> ()
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
		local listeners = signals.removed[component]
 | 
							local listeners = signals.removed[component]
 | 
				
			||||||
		local component_index = world.component_index :: jecs.ComponentIndex
 | 
					 | 
				
			||||||
		assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
 | 
					 | 
				
			||||||
		if not listeners then
 | 
							if not listeners then
 | 
				
			||||||
			listeners = {}
 | 
								listeners = {}
 | 
				
			||||||
			signals.removed[component] = listeners
 | 
								signals.removed[component] = listeners
 | 
				
			||||||
| 
						 | 
					@ -259,7 +231,16 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
					listener(entity, id, value)
 | 
										listener(entity, id, value)
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			world:set(component, jecs.OnRemove, on_remove)
 | 
								local idr = world.component_index[component]
 | 
				
			||||||
 | 
								if idr then
 | 
				
			||||||
 | 
									local idr_hook_existing = idr.hooks.on_remove
 | 
				
			||||||
 | 
									if idr_hook_existing then
 | 
				
			||||||
 | 
										table.insert(listeners, idr_hook_existing)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
									idr.hooks.on_remove = on_remove :: any
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									world:set(component, jecs.OnRemove, on_remove)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		table.insert(listeners, fn)
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
		return function()
 | 
							return function()
 | 
				
			||||||
| 
						 | 
					@ -270,15 +251,15 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.signals = signals
 | 
						world_mut.signals = signals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.observer = observers_new
 | 
						world_mut.observer = observers_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.monitor = monitors_new
 | 
						world_mut.monitor = monitors_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	world.trackers = {}
 | 
						world_mut.trackers = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return world :: PatchedWorld
 | 
						return world_mut :: PatchedWorld
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return observers_add
 | 
					return observers_add
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					local collect = require("../../ReplicatedStorage/collect")
 | 
				
			||||||
local collect = require(ReplicatedStorage.collect)
 | 
					local types = require("../../ReplicatedStorage/types")
 | 
				
			||||||
local types = require(ReplicatedStorage.types)
 | 
					local ct = require("../../ReplicatedStorage/components")
 | 
				
			||||||
local ct = require(ReplicatedStorage.components)
 | 
					 | 
				
			||||||
local Players = game:GetService("Players")
 | 
					local Players = game:GetService("Players")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local player_added = collect(Players.PlayerAdded)
 | 
					local player_added = collect(Players.PlayerAdded)
 | 
				
			||||||
| 
						 | 
					@ -13,7 +12,8 @@ return function(world: types.World, dt: number)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for entity, player in world:query(ct.Player):without(ct.Renderable) do
 | 
						for entity, player in world:query(ct.Player):without(ct.Renderable) do
 | 
				
			||||||
		local character = player.Character
 | 
							local character = player.Character
 | 
				
			||||||
		if character and character.Parent ~= nil then
 | 
							if character then
 | 
				
			||||||
 | 
							if not character.Parent then
 | 
				
			||||||
			world:set(entity, ct.Renderable, character)
 | 
								world:set(entity, ct.Renderable, character)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,24 @@ local observers_add = require("@addons/observers")
 | 
				
			||||||
TEST("addons/observers", function()
 | 
					TEST("addons/observers", function()
 | 
				
			||||||
	local world = observers_add(jecs.world())
 | 
						local world = observers_add(jecs.world())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do CASE "Should not override hook"
 | 
				
			||||||
 | 
							local A = world:component()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local count = 0
 | 
				
			||||||
 | 
							local function counter()
 | 
				
			||||||
 | 
								count += 1
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							world:set(A, jecs.OnAdd, counter)
 | 
				
			||||||
 | 
							world:set(world:entity(), A, true)
 | 
				
			||||||
 | 
							CHECK(count == 1)
 | 
				
			||||||
 | 
							world:added(A, counter)
 | 
				
			||||||
 | 
							world:set(world:entity(), A, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							CHECK(count == 3)
 | 
				
			||||||
 | 
							print(count)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do CASE "Ensure ordering between signals and observers"
 | 
						do CASE "Ensure ordering between signals and observers"
 | 
				
			||||||
		local A = world:component()
 | 
							local A = world:component()
 | 
				
			||||||
		local B = world:component()
 | 
							local B = world:component()
 | 
				
			||||||
| 
						 | 
					@ -24,17 +42,12 @@ TEST("addons/observers", function()
 | 
				
			||||||
		world:added(A, counter)
 | 
							world:added(A, counter)
 | 
				
			||||||
		world:added(A, counter)
 | 
							world:added(A, counter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		world:removed(A, counter)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local e = world:entity()
 | 
							local e = world:entity()
 | 
				
			||||||
		world:add(e, A)
 | 
							world:add(e, A)
 | 
				
			||||||
		CHECK(count == 2)
 | 
							CHECK(count == 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		world:add(e, B)
 | 
							world:add(e, B)
 | 
				
			||||||
		CHECK(count == 3)
 | 
							CHECK(count == 3)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		world:remove(e, A)
 | 
					 | 
				
			||||||
		CHECK(count == 4)
 | 
					 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do CASE "Rematch entities in observers"
 | 
						do CASE "Rematch entities in observers"
 | 
				
			||||||
| 
						 | 
					@ -87,10 +100,10 @@ TEST("addons/observers", function()
 | 
				
			||||||
		local A = world:component()
 | 
							local A = world:component()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local callcount = 0
 | 
							local callcount = 0
 | 
				
			||||||
		world:added(A, function(entity) 
 | 
							world:added(A, function(entity)
 | 
				
			||||||
			callcount += 1
 | 
								callcount += 1
 | 
				
			||||||
		end)
 | 
							end)
 | 
				
			||||||
		world:added(A, function(entity) 
 | 
							world:added(A, function(entity)
 | 
				
			||||||
			callcount += 1
 | 
								callcount += 1
 | 
				
			||||||
		end)
 | 
							end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,30 @@ local entity_visualiser = require("@tools/entity_visualiser")
 | 
				
			||||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
					local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
				
			||||||
local dwi = entity_visualiser.stringify
 | 
					local dwi = entity_visualiser.stringify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST("repro", function()
 | 
				
			||||||
 | 
						local Model = jecs.component()
 | 
				
			||||||
 | 
						local Relation = jecs.component()
 | 
				
			||||||
 | 
						local Relation2 = jecs.component()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local world = jecs.world()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local e1 = world:entity()
 | 
				
			||||||
 | 
						world:set(e1, Model, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local e2 = world:entity()
 | 
				
			||||||
 | 
						world:set(e2, Model, 2)
 | 
				
			||||||
 | 
						world:set(e2, jecs.pair(Relation, e1), 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local e3 = world:entity()
 | 
				
			||||||
 | 
						world:set(e3, Model, 2)
 | 
				
			||||||
 | 
						world:set(e3, jecs.pair(Relation, e1), 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world:delete(e1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _ in world:query(Model) do end
 | 
				
			||||||
 | 
						jecs.ECS_META_RESET()
 | 
				
			||||||
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:add()", function()
 | 
					TEST("world:add()", function()
 | 
				
			||||||
	do CASE "idempotent"
 | 
						do CASE "idempotent"
 | 
				
			||||||
		local world = jecs.world()
 | 
							local world = jecs.world()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "ukendio/jecs"
 | 
					name = "ukendio/jecs"
 | 
				
			||||||
version = "0.5.5"
 | 
					version = "0.6.0-rc.1"
 | 
				
			||||||
registry = "https://github.com/UpliftGames/wally-index"
 | 
					registry = "https://github.com/UpliftGames/wally-index"
 | 
				
			||||||
realm = "shared"
 | 
					realm = "shared"
 | 
				
			||||||
license = "MIT"
 | 
					license = "MIT"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue