sorcery  Artifact [d98f045dad]

Artifact d98f045dad6f58e1bfc84a080a1c1c125de33f9fb39ca807be160294c4ded82e:

  • File registration.lua — part of check-in [3f6a913e4e] at 2020-09-29 12:40:28 on branch trunk — * remove former hacky registration system, replace with consistent and flexible API; rewrite metal/gem generation to take advantage of this new API; tweaks to init system to enable world-local tweaks to lore and sorcery behavior * initial documentation commit * initial steps towards calendar - add default date format, astrolabe; prepare infra for division/melding/transmutation spells, various tweaks and fixes (user: lexi, size: 3785) [annotate] [blame] [check-ins using]

if not sorcery then sorcery = {DEBUG=true} end
sorcery.register = {}
sorcery.registry = {
	defercheck = function() return true end
	-- used to warn about deferments that have not been discharged by a certain threshold
}
sorcery.registry.mk = function(name,db)
	local reg = {}
	if db == nil then
		-- predefined db
		db = sorcery.data[name]
	elseif db == false then -- auxiliary db, stored in registry itself
		reg.db = {}
		db = reg.db
	end
	local dat = {
		iters = {};
		state = {};
		defer = {};
	}
	reg.invoke = function(fnid,tgtid)
		local fno = dat.iters[fnid]
		if not fno then return false end

		local runid = fnid .. '@' ..tgtid
		if dat.state[runid] then return true end

		if fno.deps then for k,f in pairs(fno.deps) do
			if reg.invoke(f,tgtid) == false then return false end
		end end
		
		fno.fn(tgtid, db[tgtid])

		dat.state[runid] = true
		return true
	end
	reg.foreach = function(ident,deps,fn)
		dat.iters[ident] = {deps = deps, fn = fn}
		for k in pairs(db) do
			if reg.invoke(ident,k) == false then
				-- not all dependencies are available
				-- to run yet; must defer until then
				dat.defer[#dat.defer+1] = ident
				return false
			end
		end
		for i,dfn in pairs(dat.defer) do
			local deferred = dat.iters[dfn]
			for _,d in pairs(deferred.deps) do
				if not dat.iters[d] then goto skipdfmt end
			end
			-- a deferred function can now be run, do so
			table.remove(dat.defer,i)
			reg.foreach(dfn,deferred.deps,deferred.fn)
		::skipdfmt::end

		return true
	end
	reg.link = function(key,value)
		-- support simple arrays as well as kv stores
		if value == nil then
			value = key
			key = #db+1
		end
		db[key] = value
		for id in pairs(dat.iters) do 
			reg.invoke(id,key)
		end
	end
	reg.meld = function(tbl)
		for k,v in pairs(tbl) do reg.add(k,v) end
	end
	sorcery.register[name] = reg

	local nextfn = sorcery.registry.defercheck
	sorcery.registry.defercheck = function()
		if #dat.defer ~= 0 then
			print('WARNING: ' .. tostring(#dat.defer) .. ' deferred iterator(s) have not yet been discharged for registry “' .. name .. '”')
			local log = sorcery.log and function(text)
				sorcery.log('registry',text)
			end or print
			for i,v in pairs(dat.defer) do
				log('\t' .. tostring(i) .. ') ' .. v)
			end
			log('there is likely a missing dependency or dependency ordering problem. also make sure you have spelled the names of the iterator dependencies correctly')
			return false
		end
		nextfn()
	end
	return reg
end

if sorcery.DEBUG then
	local function dump(tbl,indent)
		indent = indent or 0
		local space = string.rep(' ',indent*4)
		for k,v in pairs(tbl) do
			if type(v) == 'table' then
				print(string.format('%s%s = {',space,k))
				dump(v,indent + 1)
				print(string.format('%s}', space))
			else
				print(string.format('%s%s = %q',space,k,tostring(v)))
			end
		end
	end 

	local metals = {
		oregonium = {desc = 'Oregonium'};
		nevadite = {desc = 'Nevadite'};
	}
	local myreg = sorcery.registry.mk('metals',metals)

	sorcery.register.metals.link('californium', {
		desc = "Californium";
	})

	sorcery.register.metals.foreach('sorcery:mkingot',{'sorcery:mkfrag'}, function(k,v)
		local ingot = 'sorcery:' .. k .. '_ingot'
		print('registered',ingot)
		v.parts = v.parts or {}
		v.parts.ingot = ingot;
	end)

	sorcery.registry.defercheck()

	sorcery.register.metals.link('washingtonium', {
		desc = "Washingtonium";
	})

	sorcery.register.metals.foreach('sorcery:mkfrag',{}, function(k,v)
		local fragment = 'sorcery:' .. k .. '_fragment'
		print('registered',fragment)
		v.parts = v.parts or {}
		v.parts.fragment = fragment;
	end)

	sorcery.register.metals.link('biloxite', {
		desc = "Biloxite";
	})
	
	dump(metals)
	if sorcery.registry.defercheck() then
		print('lingering deferments!')
	else
		print('all good!')
	end
end