模块:Block metadata table

来自Minecraft Wiki
Chixvv留言 | 贡献2025年2月8日 (六) 07:07的版本
跳转到导航 跳转到搜索
[ 创建 | 刷新 ]文档页面
此模块没有文档页面。如果你知道此模块的使用方法,请帮助为其创建文档页面。
local p = {}
local Bit32 = require('bit32')
local Autovalue = require('Module:Autovalue')
local BlockReplaceMap = mw.loadJsonData('Module:Sandbox/BlockReplaceDataMap.json')
local PropertyList = BlockReplaceMap['properties']
local OriginalMapping = BlockReplaceMap['originalMapping']
local NewMappings = BlockReplaceMap['newMappings']
local BlockAlias = mw.loadJsonData('Module:Sandbox/BlockAlias.json')
local SimpleAlias = BlockAlias['simple']
local ComplexAlias = BlockAlias['complex']
local ComplexAliasReverted = BlockAlias['complexReverted']
local VanillaBlockUpdater = mw.loadJsonData('Module:Sandbox/VanillaBlockUpdater.json')
local NBTSprites = {
	int = mw.getCurrentFrame():preprocess('{{nbt|int}}'),
	number = mw.getCurrentFrame():preprocess('{{nbt|int}}'),
	bool = mw.getCurrentFrame():preprocess('{{nbt|bool}}'),
	boolean = mw.getCurrentFrame():preprocess('{{nbt|bool}}'),
	string = mw.getCurrentFrame():preprocess('{{nbt|string}}'),
}
local Utils = {
	contains = function(array, element)
		for _, v in ipairs(array) do
			if v == element then return true end
		end
		return false
	end,
	
	count = function(table)
		local count = 0
		for _, _ in pairs(table) do count = count + 1 end
		return count
	end,
	
	stringToArray = function(str, splitter)
		local rawSplit = mw.text.split(str, splitter)
		local results = {}
		for _,value in ipairs( rawSplit ) do
			local stripped = (string.gsub(value, '^[%s\n]*(.-)[%s\n]*$', '%1')) -- StripSpaceAndLineAtBothEnds
			if stripped ~= '' then
				table.insert(results, stripped)
			end
		end
		return results
	end,
	
	mapValues = function(originalValues, from, to)
		local newValues = {}
		for k,v in ipairs(originalValues) do
			for k2, v2 in ipairs(from) do
				if v == v2 then newValues[k] = to[k2] end
			end
		end
		return newValues
	end,
}
local AliasUtils = {}
	AliasUtils.getOlderSimpleAliasArray = function(blockID)
		local older = SimpleAlias[blockID]
		if type(older)=='string' then
			return {older}
		elseif older then
			local result={} for _,v in ipairs(older) do table.insert(result, v) end --array copy
			return result
		end
		return nil
	end
	AliasUtils.getNewerSimpleAlias = function(blockID)
		for newer, older in pairs(SimpleAlias) do
			if older == blockID then return newer end
		end
		return nil
	end
	AliasUtils.addSimpleAliasAndGetOlder = function(map, block)
		map[block] = true
		local newer = AliasUtils.getNewerSimpleAlias(block)
		if newer then map[newer] = true end
		local older = AliasUtils.getOlderSimpleAliasArray(block)
		if older and #older == 1 then
			map[older[1]] = true
			return older[1]
		end
		return block
	end
	AliasUtils.getAllAliases = function(blockID)
		local toSplit = (ComplexAlias[blockID] and blockID) or ComplexAliasReverted[blockID]
		local older = AliasUtils.getOlderSimpleAliasArray(blockID)
		if not(older) and not(toSplit) then
			return {blockID}, {blockID}, nil
		elseif older and #older == 1 then
			toSplit = toSplit or (ComplexAlias[older[1]] and older[1]) or ComplexAliasReverted[older[1]]
			if not(toSplit) then return {blockID, older[1]}, older, nil end
		elseif older and #older > 1 then --cauldron only
			assert(not(ComplexAliasReverted[blockID])); assert(older[1] == blockID); assert(not(older[3]))
			return older, older, nil
		end
		local oldest = {}
		local results = {}
		oldest = {AliasUtils.addSimpleAliasAndGetOlder(results, toSplit)}
		local splitIDs = ComplexAlias[toSplit]['renameTo']
		if type(splitIDs[1]) == 'string' then
			for _,v in ipairs(splitIDs) do
				AliasUtils.addSimpleAliasAndGetOlder(results, v)
			end
		else
			for _,v in ipairs(splitIDs) do
				for _,v1 in ipairs(v['renameTo']) do
					assert(type(v1)=='string')
					AliasUtils.addSimpleAliasAndGetOlder(results, v1)
				end
			end
		end
		local resultArray = {}
		for k,_ in pairs(results) do table.insert(resultArray, k) end
		table.sort(resultArray)
		return resultArray, oldest, toSplit
	end

local function countBits(propertyInfoList)
	local bitCount = 0
	local overlapping = false
	for _, propertyInfo in ipairs(propertyInfoList) do
		if propertyInfo['overlappingBits'] then
			if not(overlapping) then
				bitCount = bitCount + propertyInfo['overlappingBits']
			end
			overlapping = true
		else
			overlapping = false
			bitCount = bitCount + propertyInfo['bits']
		end
	end
	return bitCount
end

local function getMetadataMappingForOldestBlock(block)
	local mapping = OriginalMapping[block]
	local overwriteFlag =false
	for _, newMapping in ipairs(NewMappings) do
		local new = newMapping['new']
		local overwriting = newMapping['overwriting']
		local newFlag = false
		if new and new[block] then
			if mapping then error('Duplicate blocks "'..block..'" in BlockReplaceMap') end
			mapping = new[block]
			newFlag = true
		end
		if overwriting and overwriting[block] then
			if newFlag or not(mapping) then error('Cannot overwrite "'..block..'" in BlockReplaceMap') end
			mapping =overwriting[block]
			overwriteFlag = true
		end
	end
	if type(mapping) == 'string' then
		local mapping2, maxValue2, overwriteFlag2 = getMetadataMappingForOldestBlock(mapping)
		if overwriteFlag2 then error('Cannot redirect "'..block..'" to "'..mapping..'", because the latter is overwritten') end
		if not(mapping2) then error('Cannot redirect "'..block..'" to "'..mapping..'"') end
		return mapping2, maxValue2, overwriteFlag
	end
	if mapping then
		local maxValue = mapping['max'] -- coral only
		if maxValue then mapping = mapping['mapping'] end
		return mapping, maxValue, overwriteFlag
	end
	return nil
end

local function getPropertyInfoFromMetadataMapping(mapping)
	local propertyNameList = {}; local propertyInfoList = {}
	for _, property in ipairs(mapping) do
		if type(property) == 'string' then
			table.insert(propertyNameList, property)
			table.insert(propertyInfoList, PropertyList[property] or error('Missing property '..property..' in BlockReplaceMap'))
		elseif property['property'] then
			table.insert(propertyNameList, property['property'])
			local propertyInfo = PropertyList[property['property']] or error('Missing property '..property['property']..' in BlockReplaceMap')
			if Utils.contains(propertyInfo['bits'], property['bits']) then
				table.insert(propertyInfoList, {type=propertyInfo['type'], bits=property['bits']})
			else
				error('Missing property '..property..'['..tostring(property['bits'])..' bits] in BlockReplaceMap')
			end
		else
			assert(property['overlapping']) -- doors only
			for _,v in ipairs(property['overlapping']) do
				table.insert(propertyNameList, v)
				local propertyInfo = PropertyList[v] or error('Missing property '..property..' in BlockReplaceMap')
				table.insert(propertyInfoList, {overlappingBits = true, type=propertyInfo['type'], values=propertyInfo['values'], bits=propertyInfo['bits'], max=propertyInfo['max']})
			end
		end
	end
	--copy and normalize propertyInfo to {type=*,bits=*,values=array[*],overlapping=*}
	local overlappingBits
	for k, info in ipairs(propertyInfoList) do
		local normalizedInfo = {}
		local propertyType = info['type']
		normalizedInfo['type'] = propertyType
		if propertyType == 'bool' then
			normalizedInfo['bits'] = 1
			normalizedInfo['values'] = {false, true}
		elseif propertyType == 'int' then
			normalizedInfo['bits'] = info['bits']
			local max = info['max'] or ( math.ldexp( 1, info['bits'] ) - 1 ) --(2^n - 1)
			normalizedInfo['values'] = {0, 1}
			for i=2, max do table.insert(normalizedInfo['values'], i) end
		else
			normalizedInfo['values'] = info['values']
			normalizedInfo['bits'] = math.floor(math.log( Utils.count(info['values']) ) / math.log(2) + 0.1)
		end
		if info['overlappingBits'] then
			overlappingBits = overlappingBits or normalizedInfo['bits']
			normalizedInfo['overlappingBits'] = overlappingBits
		end
		propertyInfoList[k] = normalizedInfo
	end
	return propertyNameList, propertyInfoList
end

local function genSimpleTable(propertyNameList, propertyInfoList)
	local result = {}
	local allRows = 0
	local bitMap = {'0x1','0x2','0x4','0x8','0x10','0x20','0x40','0x80','0x100','0x200','0x400'}
	local currentBit = 1
	for k, propertyName in ipairs(propertyNameList) do
		if propertyInfoList[k]['bits'] == 0 then
			table.insert(result, '| — || '..NBTSprites[PropertyInfoList[k]['type']]..propertyName..' || '..propertyInfoList[k]['value'])
		else
			local bitString = {}
			for i=currentBit, (currentBit + propertyInfoList[k]['bits']-1) do
				table.insert(bitString,bitMap[i])
			end
			if not(propertyInfoList[k]['overlappingBits']) then
				currentBit = currentBit + propertyInfoList[k]['bits']
			elseif (propertyInfoList[k+1] and not(propertyInfoList[k+1]['overlappingBits'])) then
				currentBit = currentBit + propertyInfoList[k]['overlappingBits']
			end
			if propertyName == '(Unused)' then
				assert(not(propertyInfoList[k]['overlappingBits']))
				allRows = allRows + 1
				table.insert(result, '| ' .. table.concat(bitString,'<br>'))
				table.insert(result, '| colspan = 3 | (未使用)')
				table.insert(result, '|-')
			else
				local rowspan = tostring(math.max(Utils.count(propertyInfoList[k]['values']),1))
				allRows = allRows + rowspan
				table.insert(result, '| rowspan = '.. rowspan .. ' | ' .. table.concat(bitString,'<br>'))
				for index, value in ipairs(propertyInfoList[k]['values']) do
					table.insert(result, '| '..tostring(index-1))
					if index == 1 then
						table.insert(result, '| rowspan = '.. rowspan .. ' | ' .. NBTSprites[propertyInfoList[k]['type']]..propertyName)
					end
					table.insert(result, '| '..tostring(value))
					table.insert(result, '|-')
				end
			end
		end
	end
	return table.concat(result,'\n'), allRows
end

local function genComplexTable(states)
	local result = {}
	local allRows = 0
	for i=0, #states do
		local state = states[i]
		local name = AliasUtils.getNewerSimpleAlias(state['name']) or state['name']
		local propertyCount = Utils.count(state['states'])
		local rowspan = tostring(math.max(propertyCount, 1))
		allRows = allRows + rowspan
		table.insert(result,'| rowspan = '..rowspan..' | '..tostring(i))
		table.insert(result,'| rowspan = '..rowspan..' | '..name)
		if propertyCount == 0 then
			table.insert(result, '| — || —')
			table.insert(result, '|-')
		else
			for k,v in pairs(state['states']) do
				if k ~= '(Unused)' then
					table.insert(result, '| '..NBTSprites[type(v)]..k..' || '..tostring(v))
					table.insert(result, '|-')
				end
			end
		end
	end
	return table.concat(result, '\n'), allRows
end

local function applySimpleMappingEntry(mappingEntry, block, propertyNameList, propertyInfoList)
	if mappingEntry['removeProperties'] then
		for propertyToRemove, infoToRemove in pairs(mappingEntry['removeProperties']) do
			for k, propertyName in ipairs(propertyNameList) do
				if propertyName == propertyToRemove then
					assert(propertyInfoList[k]['type'] == infoToRemove['type'])
					assert(propertyInfoList[k]['overlappingBits'] == nil)
					propertyNameList[k] = '(Unused)'
				end
			end
		end
	end
	if mappingEntry['addProperties'] then
		for propertyToAdd, infoToAdd in pairs(mappingEntry['addProperties']) do
			if not(Utils.contains(propertyNameList, propertyToAdd)) then
				table.insert(propertyNameList, propertyToAdd)
				table.insert(propertyInfoList, {type=infoToAdd['type'],bits=0,value=infoToAdd['value']})
			end
		end
	end
	if mappingEntry['renameTo'] then
		block = mappingEntry['renameTo']
	end
	if mappingEntry['mapValues'] then
		local property = mappingEntry['mapValues']['property']
		local propertyType = mappingEntry['mapValues']['type']
		local old = mappingEntry['mapValues']['old']
		local new = mappingEntry['mapValues']['new']
		for index, propertyName in ipairs(propertyNameList) do
			if propertyName == property then
				assert(propertyInfoList[index]['type'] == propertyType)
				if old then
					propertyInfoList[index]['values'] = Utils.mapValues(propertyInfoList[index]['values'], old, new)
				else
					assert(type(new)~='table')
					if propertyInfoList[index]['value'] then
						propertyInfoList[index]['value'] = new
					else
						for k,_ in propertyInfoList[index]['values'] do
							propertyInfoList[index]['values'][k] = new
						end
					end
				end
			end
		end
	end
	if mappingEntry['mapProperty'] then
		local old = mappingEntry['mapProperty']['old']
		local new = mappingEntry['mapProperty']['new']
		for index, propertyName in ipairs(propertyNameList) do
			if propertyName == old['property'] then
				assert(propertyInfoList[index]['type'] == old['type'])
				if old['values'] then
					propertyInfoList[index]['values'] = Utils.mapValues(propertyInfoList[index]['values'], old['values'], new['values'])
				end
				propertyNameList[index] = new['property']
				propertyInfoList[index]['type'] = new['type']
			end
		end
	end
	return block
end

local function applyMappingEntryOnState(mappingEntry, state)
	if mappingEntry['stateFilter'] then 
		for property, info in pairs(mappingEntry['stateFilter']) do
			assert(state['states'][property] ~= nil)
			if info['value'] then
				if state['states'][property] ~= info['value'] then return end
			elseif not(Utils.contains(info['values'], state['states'][property])) then
				return
			end
		end
	end
	if mappingEntry['complexAlias'] then
		local complexAliasInfo = ComplexAlias[state['name']]
								or ComplexAlias[AliasUtils.getNewerSimpleAlias(state['name'])]
								or ComplexAlias[AliasUtils.getOlderSimpleAliasArray(state['name'])[1]]
		local value = state['states'][complexAliasInfo['property']]
		assert(value ~= nil)
		state['states'][complexAliasInfo['property']] = nil
		for k, v in ipairs(complexAliasInfo['values']) do
			if v == value then
				state['name'] = complexAliasInfo['renameTo'][k]
			end
		end
		if type(state['name']) == 'table' then
			local value = state['states'][state['name']['property']]
			state['states'][state['name']['property']] = nil
			for k, v in ipairs(state['name']['values']) do
				if v == value then state['name'] = state['name']['renameTo'][k] end
			end
		end
		assert(type(state['name']) == 'string')
	end
	if mappingEntry['removeProperties'] then
		for propertyToRemove, infoToRemove in pairs(mappingEntry['removeProperties']) do
			assert(state['states'][propertyToRemove] == nil or (type(state['states'][propertyToRemove]) == (infoToRemove['type']=='bool' and 'boolean' or (infoToRemove['type']=='int' and 'number' or infoToRemove['type']))))
			state['states'][propertyToRemove] = nil
		end
	end
	if mappingEntry['addProperties'] then
		for propertyToAdd, infoToAdd in pairs(mappingEntry['addProperties']) do
			if(state['states'][propertyToAdd] == nil) then
				state['states'][propertyToAdd] = infoToAdd['value']
			end
		end
	end
	if mappingEntry['renameTo'] then
		state['name'] = mappingEntry['renameTo']
	end
	if mappingEntry['mapValues'] then
		local property = mappingEntry['mapValues']['property']
		local propertyType = mappingEntry['mapValues']['type']
		local old = mappingEntry['mapValues']['old']
		local new = mappingEntry['mapValues']['new']
		if not(old) then
			state['states'][property] = new
		else
			local original = state['states'][property]
			for k, value in ipairs(old) do
				if value == original then
					state['states'][property] = new[k]
				end
			end
		end
	end
	if mappingEntry['mapProperty'] then
		local old = mappingEntry['mapProperty']['old']
		local new = mappingEntry['mapProperty']['new']
		-- assert (state['states'][old['property']] ~= nil) -- wood[pillar_axis]
		if old['values'] then
			local newValue
			for k, value in ipairs(old['values']) do
				if value == state['states'][old['property']] then
					newValue = new['values'][k]
				end
			end
			state['states'][new['property']] = newValue
		else
			state['states'][new['property']] = state['states'][old['property']]
		end
		if(old['property'] ~= new['property']) then
			state['states'][old['property']] = nil
		end
	end
end

local function getStateFromMetadata(metadata, block, propertyNameList, propertyInfoList)
	local state = {}
	local metadataTemp = metadata
	for k, v in ipairs(propertyNameList) do
		local bits = propertyInfoList[k]['bits']
		state[v] = propertyInfoList[k]['values'][Bit32.band(metadataTemp, math.ldexp( 1, bits ) - 1) + 1]
		if not(propertyInfoList[k]['overlappingBits']) then
			metadataTemp = Bit32.rshift(metadataTemp, bits)
		elseif not(propertyInfoList[k+1]['overlappingBits']) then
			metadataTemp = Bit32.rshift(metadataTemp, propertyInfoList[k]['overlappingBits'])
		end
	end
	assert(metadataTemp == 0)
	return {name = block, states = state}
end

local function allMetadataToStates(maxValue, block, propertyNameList, propertyInfoList)
	local result = {}
	for i=0, maxValue do
		result[i] = getStateFromMetadata(i, block, propertyNameList, propertyInfoList)
	end
	return result
end

local function getMappingEntry(updaterMapping, block)
	local alias = {}; AliasUtils.addSimpleAliasAndGetOlder(alias,block)
	local mappingEntry
	for k, _ in pairs(alias) do
		mappingEntry = mappingEntry or updaterMapping[k]
	end
	if type(mappingEntry) == 'string' then
		mappingEntry = updaterMapping[mappingEntry]
		assert(type(mappingEntry) ~= 'string')
	end
	if updaterMapping['#default'] then
		local newMappingEntry = {}
		if mappingEntry and mappingEntry[1] then
			for _, v in ipairs(mappingEntry) do table.insert(newMappingEntry, v) end
		else
			newMappingEntry[1] = mappingEntry
		end
		if updaterMapping['#default'][1] then
			for _, v in ipairs(updaterMapping['#default']) do table.insert(newMappingEntry, v) end
		else
			table.insert(newMappingEntry, updaterMapping['#default'])
		end
		mappingEntry = newMappingEntry
	end
	return mappingEntry
end

local function tryGetSimpleTable(block, propertyNameList, propertyInfoList)
	local filtered = false
	for _, updaterMapping in ipairs(VanillaBlockUpdater) do
		if not(filtered) then
			local mappingEntry = getMappingEntry(updaterMapping, block)
			if mappingEntry then
				if mappingEntry[1] then
					local originID = block
					for _,v in ipairs(mappingEntry) do
						if not(filtered) and not(v['removedIn']) and (originID == block) then
							if v['stateFilter'] or mappingEntry['complexAlias'] then
								filtered = true
							else
								block = applySimpleMappingEntry(v, block, propertyNameList, propertyInfoList)
							end
						end
					end
				else
					if not(mappingEntry['removedIn']) then
						if mappingEntry['stateFilter'] or mappingEntry['complexAlias'] then
							filtered = true
						else
							block = applySimpleMappingEntry(mappingEntry, block, propertyNameList, propertyInfoList)
						end
					end
				end
			end
		end
	end
	if not(filtered) then
		local body, rows = genSimpleTable(propertyNameList, propertyInfoList)
		return body, rows, false
	else
		return nil, nil, true
	end
end

local function getComplexTable(oldestAliases, propertyNameList, propertyInfoList, maxValue)
	local states = allMetadataToStates(maxValue, oldestAliases, propertyNameList, propertyInfoList)
	for i=0, maxValue do
		for _, updaterMapping in pairs(VanillaBlockUpdater) do
			local mappingEntry = getMappingEntry(updaterMapping, states[i]['name'])
			if mappingEntry then
				if mappingEntry[1] then
					local originID = states[i]['name']
					for _,v in ipairs(mappingEntry) do
						if not(v['removedIn']) and (originID == states[i]['name']) then
							applyMappingEntryOnState(v,states[i])
						end
					end
				else
					if not(mappingEntry['removedIn']) then
						applyMappingEntryOnState(mappingEntry,states[i])
					end
				end
			end
		end
	end
	return genComplexTable(states)
end

local function getMetadataTable(oldestAliases, shouldSplit)
	if oldestAliases[2] then --cauldron only
		local lastBody, body, rowspan, isComplex
		for _, v in ipairs(oldestAliases) do
			body, rowspan, isComplex = getMetadataTable({v},nil)
			assert(not(lastBody) or lastBody == body)
			lastBody = body
		end
		return body, rowspan, isComplex
	else
		local mapping, maxValue = getMetadataMappingForOldestBlock(oldestAliases[1])
		if mapping == nil then
			return nil
		end
		local propertyNameList, propertyInfoList = getPropertyInfoFromMetadataMapping(mapping)
		local body, rowspan
		local filtered = false
		if not(shouldSplit) then
			body, rowspan, filtered = tryGetSimpleTable(oldestAliases[1], propertyNameList, propertyInfoList)
		end
		if shouldSplit or filtered then
			shouldSplit = true
			if not(maxValue) then
				if #propertyInfoList == 1 then
					maxValue = Utils.count(propertyInfoList[1]['values']) - 1
				else
					maxValue = math.ldexp( 1, countBits(propertyInfoList) ) - 1
				end
			end
			body, rowspan = getComplexTable(oldestAliases[1], propertyNameList, propertyInfoList, maxValue)
		end
		return body, rowspan, shouldSplit
	end
end

local function getMetadataTableForBlocks(idArray)
	local dedupByToSplit = {}
	local complexBodys = {}; local simpleBodys = {}; local simpleBodyMap = {}; local notSupport = {}
	for _, id in pairs(idArray) do
		local noDup = true
		local allAliases, oldestAliases, toSplit = AliasUtils.getAllAliases(id)
		if toSplit then
			if dedupByToSplit[toSplit] == true then noDup = false
			else dedupByToSplit[toSplit] = true end
		end
		if noDup then
			local body, rowspan, isComplex = getMetadataTable(oldestAliases, (toSplit ~= nil))
			if body == nil then
				for _, alias in ipairs(allAliases) do table.insert(notSupport, alias) end
			elseif isComplex then
				table.insert(complexBodys, '| rowspan = '..tostring(rowspan)..' | <code>'..table.concat(allAliases,'</code><br><code>')..'</code>')
				table.insert(complexBodys, body)
			else --合并内容相同的简单表
				if (simpleBodyMap[body]) then
					for _, alias in ipairs(allAliases) do table.insert(simpleBodyMap[body]['aliases'],alias) end
				else
					simpleBodyMap[body] = {aliases=allAliases, rowspan=rowspan}
				end
			end
		end
	end
	for body, info in pairs(simpleBodyMap) do
		table.insert(simpleBodys, '| rowspan = '..tostring(info['rowspan'])..' | <code>'..table.concat(info['aliases'],'</code><br><code>')..'</code>')
		table.insert(simpleBodys, body)
	end
	local result = {}
	if complexBodys[1] then
		table.insert(result, '{| class = "wikitable collapsible"\n! rowspan=2 | 指定的方块ID !! rowspan=2 | 指定的数据值 !! rowspan=2 | 实际方块ID !! colspan = 2 | 实际方块状态\n|-')
		table.insert(result, '! 方块属性 !! 值\n|-')
		table.insert(result, table.concat(complexBodys,'\n'))
		table.insert(result, '|}')
	end
	if simpleBodys[1] or notSupport[1] then
		table.insert(result, '{| class = "wikitable collapsible"\n! rowspan=2 | 方块ID !! colspan = 2 | 指定的数据值 !! colspan = 2 | 对应的方块属性\n|-')
		table.insert(result, '! 二进制位 !! 值 !! 方块属性 !! 值\n|-')
		table.insert(result, table.concat(simpleBodys,'\n'))
		table.insert(result, '| <code>'..table.concat(notSupport,'</code><br><code>')..'</code>')
		table.insert(result, '| colspan=4 | 此方块不支持数据值\n|-')
		table.insert(result, '|}')
	end
	return table.concat(result, '\n')
end

function p.blockMetadataTable (f)
	local args = f
	local frame = mw.getCurrentFrame()
	if f == frame then
		args = require('Module:ProcessArgs').merge(true)
	end
	local targetNames = Utils.stringToArray(mw.text.trim(args[2] or args[1] or mw.text.trim(mw.title.getCurrentTitle().rootText)), ',')
	targetNames = Autovalue.expandGroups(targetNames, 'block', 'be')
	local targetIDs = {}
	for _, name in ipairs(targetNames) do
		table.insert(targetIDs, Autovalue.getRawValue(name, 'block id', 'be'))
	end
	return getMetadataTableForBlocks(targetIDs)
end

function p.allBlockMetadataTable()
	return getMetadataTableForBlocks(mw.loadData('Module:Block id values BE'))
end

return p