==FILE== mods/dumpnodes/init.lua
local function nd_get_tiles(nd)
	return nd.tiles or nd.tile_images
end

local function nd_get_tile(nd, n)
	local tile = nd_get_tiles(nd)[n]
	if type(tile) == 'table' then
		tile = tile.name
	end
	return tile
end

local function pairs_s(dict)
	local keys = {}
	for k in pairs(dict) do
		table.insert(keys, k)
	end
	table.sort(keys)
	return ipairs(keys)
end

minetest.register_chatcommand("dumpnodes", {
	params = "",
	description = "",
	func = function(player, param)
		local n = 0
		local ntbl = {}
		for _, nn in pairs_s(minetest.registered_nodes) do
			local nd = minetest.registered_nodes[nn]
			local prefix, name = nn:match('(.*):(.*)')
			if prefix == nil or name == nil then
				print("ignored(1): " .. nn)
			else
				if ntbl[prefix] == nil then
					ntbl[prefix] = {}
				end
				ntbl[prefix][name] = true
			end
		end
		local out, err = io.open('nodes.txt', 'wb')
		if not out then
			return true, "io.open(): " .. err
		end
		for _, prefix in pairs_s(ntbl) do
			out:write('# ' .. prefix .. '\n')
			for _, name in pairs_s(ntbl[prefix]) do
				local nn = prefix .. ":" .. name
				local nd = minetest.registered_nodes[nn]
				if nd.drawtype == 'airlike' or nd_get_tiles(nd) == nil then
					print("ignored(2): " .. nn)
				else
					local tl = nd_get_tile(nd, 1)
					tl = (tl .. '^'):match('(.-)^') -- strip modifiers
					out:write(nn .. ' ' .. tl .. '\n')
					n = n + 1
				end
			end
			out:write('\n')
		end
		out:close()
		return true, n .. " nodes dumped."
	end,
})
==FILE== avgcolor.py
#!/usr/bin/env python
import sys
from math import sqrt
from PIL import Image

if len(sys.argv) < 2:
	print("Prints average color (RGB) of input image")
	print("Usage: %s <input>" % sys.argv[0])
	exit(1)

inp = Image.open(sys.argv[1]).convert('RGBA')
ind = inp.load()

cl = ([], [], [])
for x in range(inp.size[0]):
	for y in range(inp.size[1]):
		px = ind[x, y]
		if px[3] < 128: continue # alpha
		cl[0].append(px[0]**2)
		cl[1].append(px[1]**2)
		cl[2].append(px[2]**2)

if len(cl[0]) == 0:
	print("Didn't find average color for %s" % sys.argv[1], file=sys.stderr)
	print("0 0 0")
else:
	cl = tuple(sqrt(sum(x)/len(x)) for x in cl)
	print("%d %d %d" % cl)
==SCRIPT==
#!/bin/bash -e
AVGCOLOR_PATH=/path/to/avgcolor.py
GAME_PATH=/path/to/minetest_game
MODS_PATH= # path to "mods" folder, only set if you have loaded mods
NODESTXT_PATH=./nodes.txt
COLORSTXT_PATH=./colors.txt

while read -r line; do
	set -- junk $line; shift
	if [[ -z "$1" || $1 == "#" ]]; then
		echo "$line"; continue
	fi
	tex=$(find $GAME_PATH -type f -name "$2")
	[[ -z "$tex" && -n "$MODS_PATH" ]] && tex=$(find $MODS_PATH -type f -name "$2")
	if [ -z "$tex" ]; then
		echo "skip $1: texture not found" >&2
		continue
	fi
	echo "$1" $(python $AVGCOLOR_PATH "$tex")
	echo "ok $1" >&2
done < $NODESTXT_PATH > $COLORSTXT_PATH
# Use nicer colors for water and lava:
sed -re 's/^default:((river_)?water_(flowing|source)) [0-9 ]+$/default:\1 39 66 106 128 224/g' $COLORSTXT_PATH -i
sed -re 's/^default:(lava_(flowing|source)) [0-9 ]+$/default:\1 255 100 0/g' $COLORSTXT_PATH -i
# Add transparency to glass nodes and xpanes:
sed -re 's/^default:(.*glass) ([0-9 ]+)$/default:\1 \2 64 16/g' $COLORSTXT_PATH -i
sed -re 's/^doors:(.*glass[^ ]*) ([0-9 ]+)$/doors:\1 \2 64 16/g' $COLORSTXT_PATH -i
sed -re 's/^xpanes:(.*(pane|bar)[^ ]*) ([0-9 ]+)$/xpanes:\1 \3 64 16/g' $COLORSTXT_PATH -i
# Delete some usually hidden nodes:
sed '/^doors:hidden /d' $COLORSTXT_PATH -i
sed '/^fireflies:firefly /d' $COLORSTXT_PATH -i
sed '/^butterflies:butterfly_/d' $COLORSTXT_PATH -i
==INSTRUCTIONS==
1) Make sure avgcolors.py works (outputs the usage instructions when run)
2) Add the dumpnodes mod to Minetest
3) Create a world and load dumpnodes & all mods you want to generate colors for
4) Execute /dumpnodes ingame
5) Run the script to generate colors.txt (make sure to adjust the PATH variables at the top)
